]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/macctrls.c
ea182c0f615bf51d6849bfa46771e526c0c14b7a
[PuTTY.git] / mac / macctrls.c
1 /* $Id$ */
2 /*
3  * Copyright (c) 2003 Ben Harris
4  * All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or
11  * sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  * 
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  * 
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28 #include <MacTypes.h>
29 #include <Appearance.h>
30 #include <ColorPicker.h>
31 #include <Controls.h>
32 #include <ControlDefinitions.h>
33 #include <Events.h>
34 #include <Lists.h>
35 #include <Menus.h>
36 #include <Resources.h>
37 #include <Script.h>
38 #include <Sound.h>
39 #include <TextEdit.h>
40 #include <TextUtils.h>
41 #include <ToolUtils.h>
42 #include <Windows.h>
43
44 #include <assert.h>
45 #include <string.h>
46
47 #include "putty.h"
48 #include "mac.h"
49 #include "macresid.h"
50 #include "dialog.h"
51 #include "tree234.h"
52
53 /* Range of menu IDs for popup menus */
54 #define MENU_MIN        1024
55 #define MENU_MAX        2048
56
57
58 union macctrl {
59     struct macctrl_generic {
60         enum {
61             MACCTRL_TEXT,
62             MACCTRL_EDITBOX,
63             MACCTRL_RADIO,
64             MACCTRL_CHECKBOX,
65             MACCTRL_BUTTON,
66             MACCTRL_LISTBOX,
67             MACCTRL_POPUP,
68             MACCTRL_GROUPBOX
69         } type;
70         /* Template from which this was generated */
71         union control *ctrl;
72         /* Next control in this panel */
73         union macctrl *next;
74         void *privdata;
75         int freeprivdata;
76     } generic;
77     struct {
78         struct macctrl_generic generic;
79         ControlRef tbctrl;
80     } text;
81     struct {
82         struct macctrl_generic generic;
83         ControlRef tbctrl;
84         ControlRef tblabel;
85         ControlRef tbbutton;
86         MenuRef menu;
87         int menuid;
88         unsigned int nids;
89         int *ids;
90     } editbox;
91     struct {
92         struct macctrl_generic generic;
93         ControlRef *tbctrls;
94         ControlRef tblabel;
95     } radio;
96     struct {
97         struct macctrl_generic generic;
98         ControlRef tbctrl;
99     } checkbox;
100     struct {
101         struct macctrl_generic generic;
102         ControlRef tbctrl;
103         ControlRef tbring;
104     } button;
105     struct {
106         struct macctrl_generic generic;
107         ControlRef tbctrl;
108         ControlRef tbup;
109         ControlRef tbdown;
110         ListHandle list;
111         unsigned int nids;
112         int *ids;
113     } listbox;
114     struct {
115         struct macctrl_generic generic;
116         ControlRef tbctrl;
117         MenuRef menu;
118         int menuid;
119         unsigned int nids;
120         int *ids;
121     } popup;
122     struct {
123         struct macctrl_generic generic;
124         ControlRef tbctrl;
125     } groupbox;
126 };
127
128 struct mac_layoutstate {
129     Point pos;
130     Point boxpos;
131     char *boxname;
132     unsigned int width;
133     unsigned int panelnum;
134 };
135
136 #define ctrlevent(mcs, mc, event) do {                                  \
137     if ((mc)->generic.ctrl->generic.handler != NULL)                    \
138         (*(mc)->generic.ctrl->generic.handler)((mc)->generic.ctrl, (mcs),\
139                                                (mcs)->data, (event));   \
140 } while (0)
141
142 #define findbyctrl(mcs, ctrl)                                           \
143     find234((mcs)->byctrl, (ctrl), macctrl_cmp_byctrl_find)
144
145 static void macctrl_layoutset(struct mac_layoutstate *, struct controlset *, 
146                               WindowPtr, struct macctrls *);
147 static void macctrl_hideshowpanel(struct macctrls *, unsigned int, int);
148 static void macctrl_switchtopanel(struct macctrls *, unsigned int);
149 static void macctrl_setfocus(struct macctrls *, union macctrl *);
150 static void macctrl_text(struct macctrls *, WindowPtr,
151                          struct mac_layoutstate *, union control *);
152 static void macctrl_editbox(struct macctrls *, WindowPtr,
153                             struct mac_layoutstate *, union control *);
154 static void macctrl_radio(struct macctrls *, WindowPtr,
155                           struct mac_layoutstate *, union control *);
156 static void macctrl_checkbox(struct macctrls *, WindowPtr,
157                              struct mac_layoutstate *, union control *);
158 static void macctrl_button(struct macctrls *, WindowPtr,
159                            struct mac_layoutstate *, union control *);
160 static void macctrl_listbox(struct macctrls *, WindowPtr,
161                             struct mac_layoutstate *, union control *);
162 static void macctrl_popup(struct macctrls *, WindowPtr,
163                           struct mac_layoutstate *, union control *);
164 static void macctrl_groupbox(struct macctrls *, WindowPtr,
165                              struct mac_layoutstate *, union control *);
166 static void draglist_up(union macctrl *, struct macctrls *);
167 static void draglist_down(union macctrl *, struct macctrls *);
168
169 #if !TARGET_API_MAC_CARBON
170 static pascal SInt32 macctrl_sys7_editbox_cdef(SInt16, ControlRef,
171                                                ControlDefProcMessage, SInt32);
172 static pascal SInt32 macctrl_sys7_default_cdef(SInt16, ControlRef,
173                                                ControlDefProcMessage, SInt32);
174 static pascal SInt32 macctrl_sys7_listbox_cdef(SInt16, ControlRef,
175                                                ControlDefProcMessage, SInt32);
176 static pascal SInt32 macctrl_sys7_groupbox_cdef(SInt16, ControlRef,
177                                                 ControlDefProcMessage, SInt32);
178 #endif
179
180 #if !TARGET_API_MAC_CARBON
181 /*
182  * This trick enables us to keep all the CDEF code in the main
183  * application, which makes life easier.  For details, see
184  * <http://developer.apple.com/technotes/tn/tn2003.html#custom_code_base>.
185  */
186
187 #pragma options align=mac68k
188 typedef struct {
189     short               jmpabs; /* 4EF9 */
190     ControlDefUPP       theUPP;
191 } **PatchCDEF;
192 #pragma options align=reset
193 #endif
194
195 static void macctrl_init()
196 {
197 #if !TARGET_API_MAC_CARBON
198     static int inited = 0;
199     PatchCDEF cdef;
200
201     if (inited) return;
202     cdef = (PatchCDEF)GetResource(kControlDefProcResourceType, CDEF_EditBox);
203     (*cdef)->theUPP = NewControlDefProc(macctrl_sys7_editbox_cdef);
204     cdef = (PatchCDEF)GetResource(kControlDefProcResourceType, CDEF_Default);
205     (*cdef)->theUPP = NewControlDefProc(macctrl_sys7_default_cdef);
206     cdef = (PatchCDEF)GetResource(kControlDefProcResourceType, CDEF_ListBox);
207     (*cdef)->theUPP = NewControlDefProc(macctrl_sys7_listbox_cdef);
208     cdef = (PatchCDEF)GetResource(kControlDefProcResourceType, CDEF_GroupBox);
209     (*cdef)->theUPP = NewControlDefProc(macctrl_sys7_groupbox_cdef);
210     inited = 1;
211 #endif
212 }
213
214
215 static int macctrl_cmp_byctrl(void *av, void *bv)
216 {
217     union macctrl *a = (union macctrl *)av;
218     union macctrl *b = (union macctrl *)bv;
219
220     if (a->generic.ctrl < b->generic.ctrl)
221         return -1;
222     else if (a->generic.ctrl > b->generic.ctrl)
223         return +1;
224     else
225         return 0;
226 }
227
228 static int macctrl_cmp_byctrl_find(void *av, void *bv)
229 {
230     union control *a = (union control *)av;
231     union macctrl *b = (union macctrl *)bv;
232
233     if (a < b->generic.ctrl)
234         return -1;
235     else if (a > b->generic.ctrl)
236         return +1;
237     else
238         return 0;
239 }
240
241 static union control panellist;
242
243 static void panellist_handler(union control *ctrl, void *dlg, void *data,
244                               int event)
245 {
246     struct macctrls *mcs = dlg;
247
248     /* XXX what if there's no selection? */
249     if (event == EVENT_SELCHANGE)
250         macctrl_switchtopanel(mcs, dlg_listbox_index(ctrl, dlg) + 1);
251 }
252
253 void macctrl_layoutbox(struct controlbox *cb, WindowPtr window,
254                        struct macctrls *mcs)
255 {
256     int i;
257     struct mac_layoutstate curstate;
258     ControlRef root;
259     Rect rect;
260
261     macctrl_init();
262     if (mac_gestalts.apprvers >= 0x100)
263         CreateRootControl(window, &root);
264 #if TARGET_API_MAC_CARBON
265     GetPortBounds(GetWindowPort(window), &rect);
266 #else
267     rect = window->portRect;
268 #endif
269     mcs->window = window;
270     mcs->byctrl = newtree234(macctrl_cmp_byctrl);
271     mcs->focus = NULL;
272     mcs->defbutton = NULL;
273     mcs->canbutton = NULL;
274     mcs->curpanel = 1;
275     /* Count the number of panels */
276     mcs->npanels = 1;
277     for (i = 1; i < cb->nctrlsets; i++)
278         if (strcmp(cb->ctrlsets[i]->pathname, cb->ctrlsets[i-1]->pathname))
279             mcs->npanels++;
280     mcs->panels = snewn(mcs->npanels, union macctrl *);
281     memset(mcs->panels, 0, sizeof(*mcs->panels) * mcs->npanels);
282     curstate.panelnum = 0;
283
284     curstate.pos.h = rect.left + 13;
285     curstate.pos.v = rect.top + 13;
286     curstate.width = 160;
287     panellist.listbox.type = CTRL_LISTBOX;
288     panellist.listbox.handler = &panellist_handler;
289     panellist.listbox.height = 20;
290     panellist.listbox.percentwidth = 100;
291     macctrl_listbox(mcs, window, &curstate, &panellist);
292     /* XXX Start with panel 1 active */
293
294     curstate.pos.h = rect.left + 13 + 160 + 13;
295     curstate.pos.v = rect.bottom - 33;
296     curstate.width = rect.right - (rect.left + 13 + 160) - (13 * 2);
297     for (i = 0; i < cb->nctrlsets; i++) {
298         if (i > 0 && strcmp(cb->ctrlsets[i]->pathname,
299                             cb->ctrlsets[i-1]->pathname)) {
300             curstate.pos.v = rect.top + 13;
301             curstate.panelnum++;
302             assert(curstate.panelnum < mcs->npanels);
303             dlg_listbox_add(&panellist, mcs, cb->ctrlsets[i]->pathname);
304         }
305         macctrl_layoutset(&curstate, cb->ctrlsets[i], window, mcs);
306     }
307     macctrl_switchtopanel(mcs, 1);
308     macctrl_hideshowpanel(mcs, 0, TRUE);
309     /* 14 = proxies, 19 = portfwd, 20 = SSH bugs */
310 }
311
312
313
314 #define MAXCOLS 16
315
316 static void macctrl_layoutset(struct mac_layoutstate *curstate,
317                               struct controlset *s,
318                               WindowPtr window, struct macctrls *mcs)
319 {
320     unsigned int i, j, ncols, colstart, colspan;
321     struct mac_layoutstate cols[MAXCOLS], pos;
322
323     /* Start a containing box, if we have a boxname. */
324     if (s->boxname && *s->boxname) {
325         curstate->boxpos = curstate->pos;
326         if (s->boxtitle) {
327             curstate->boxname = s->boxtitle;
328             curstate->pos.v += 10; /* XXX determine font height */
329         } else {
330             curstate->boxname = NULL;
331         }
332         curstate->pos.v += 6;
333         curstate->pos.h += 12;
334         curstate->width -= 24;
335     }
336
337     /* Draw a title, if we have one. */
338     if (!s->boxname && s->boxtitle) {
339         union control *ctrl = snew(union control);
340         ctrl->generic.handler = NULL;
341         ctrl->text.label = dupstr(s->boxtitle);
342         macctrl_text(mcs, window, curstate, ctrl);
343         /* FIXME: should be highlighted, centred or boxed */
344     }
345
346     cols[0] = *curstate;
347     ncols = 1;
348
349     for (i = 0; i < s->ncontrols; i++) {
350         union control *ctrl = s->ctrls[i];
351
352         colstart = COLUMN_START(ctrl->generic.column);
353         colspan = COLUMN_SPAN(ctrl->generic.column);
354         if (ctrl->generic.type == CTRL_COLUMNS) {
355             if (ctrl->columns.ncols != 1) {
356                 ncols = ctrl->columns.ncols;
357                 assert(ncols <= MAXCOLS);
358                 for (j = 0; j < ncols; j++) {
359                     cols[j] = cols[0];
360                     if (j > 0)
361                         cols[j].pos.h = cols[j-1].pos.h + cols[j-1].width + 6;
362                     if (j == ncols - 1)
363                         cols[j].width = curstate->width -
364                             (cols[j].pos.h - curstate->pos.h);
365                     else
366                         cols[j].width = (curstate->width + 6) * 
367                             ctrl->columns.percentages[j] / 100 - 6;
368                 }
369             } else {
370                 for (j = 0; j < ncols; j++)
371                     if (cols[j].pos.v > cols[0].pos.v)
372                         cols[0].pos.v = cols[j].pos.v;
373                 cols[0].width = curstate->width;
374                 ncols = 1;
375             }
376         } else {
377             pos = cols[colstart];
378             pos.width = cols[colstart + colspan - 1].width +
379                 (cols[colstart + colspan - 1].pos.h - cols[colstart].pos.h);
380
381             for (j = colstart; j < colstart + colspan; j++)
382                 if (pos.pos.v < cols[j].pos.v)
383                     pos.pos.v = cols[j].pos.v;
384
385             switch (ctrl->generic.type) {
386               case CTRL_TEXT:
387                 macctrl_text(mcs, window, &pos, ctrl);
388                 break;
389               case CTRL_EDITBOX:
390                 macctrl_editbox(mcs, window, &pos, ctrl);
391                 break;
392               case CTRL_RADIO:
393                 macctrl_radio(mcs, window, &pos, ctrl);
394                 break;
395               case CTRL_CHECKBOX:
396                 macctrl_checkbox(mcs, window, &pos, ctrl);
397                 break;
398               case CTRL_BUTTON:
399                 macctrl_button(mcs, window, &pos, ctrl);
400                 break;
401               case CTRL_LISTBOX:
402                 if (ctrl->listbox.height == 0)
403                     macctrl_popup(mcs, window, &pos, ctrl);
404                 else
405                     macctrl_listbox(mcs, window, &pos, ctrl);
406                 break;
407             }
408             for (j = colstart; j < colstart + colspan; j++)
409                 cols[j].pos.v = pos.pos.v;
410         }
411     }
412     for (j = 0; j < ncols; j++)
413         if (cols[j].pos.v > curstate->pos.v)
414             curstate->pos.v = cols[j].pos.v;
415
416     if (s->boxname && *s->boxname) {
417         union control *ctrl = snew(union control);
418         /* We're coming out of a box, so set the width back */
419         curstate->pos.h -= 12;
420         curstate->width += 24;
421         /* And draw the box to the original width */
422         macctrl_groupbox(mcs, window, curstate, ctrl);
423     }
424 }
425
426 static void macctrl_hideshowpanel(struct macctrls *mcs, unsigned int panel,
427                                   int showit)
428 {
429     union macctrl *mc;
430     int j;
431
432 #define hideshow(c) do {                                                \
433     if (showit) ShowControl(c); else HideControl(c);                    \
434 } while (0)
435
436     for (mc = mcs->panels[panel]; mc != NULL; mc = mc->generic.next) {
437 #if !TARGET_API_MAC_CARBON
438         if (mcs->focus == mc)
439             macctrl_setfocus(mcs, NULL);
440 #endif
441         switch (mc->generic.type) {
442           case MACCTRL_TEXT:
443             hideshow(mc->text.tbctrl);
444             break;
445           case MACCTRL_EDITBOX:
446             hideshow(mc->editbox.tbctrl);
447             if (mc->editbox.tblabel != NULL)
448                 hideshow(mc->editbox.tblabel);
449             if (mc->editbox.tbbutton != NULL)
450                 hideshow(mc->editbox.tbbutton);
451             break;
452           case MACCTRL_RADIO:
453             for (j = 0; j < mc->generic.ctrl->radio.nbuttons; j++)
454                 hideshow(mc->radio.tbctrls[j]);
455             if (mc->radio.tblabel != NULL)
456                 hideshow(mc->radio.tblabel);
457             break;
458           case MACCTRL_CHECKBOX:
459             hideshow(mc->checkbox.tbctrl);
460             break;
461           case MACCTRL_BUTTON:
462             hideshow(mc->button.tbctrl);
463             if (mc->button.tbring != NULL)
464                 hideshow(mc->button.tbring);
465             break;
466           case MACCTRL_LISTBOX:
467             hideshow(mc->listbox.tbctrl);
468             if (mc->listbox.tbup != NULL)
469                 hideshow(mc->listbox.tbup);
470             if (mc->listbox.tbdown != NULL)
471                 hideshow(mc->listbox.tbdown);
472             /*
473              * At least under Mac OS 8.1, hiding a list box
474              * doesn't hide its scroll bars.
475              */
476 #if TARGET_API_MAC_CARBON
477             hideshow(GetListVerticalScrollBar(mc->listbox.list));
478 #else
479             hideshow((*mc->listbox.list)->vScroll);
480 #endif
481             break;
482           case MACCTRL_POPUP:
483             hideshow(mc->popup.tbctrl);
484             break;
485           case MACCTRL_GROUPBOX:
486             hideshow(mc->groupbox.tbctrl);
487             break;
488         }
489     }
490 }
491
492 static void macctrl_switchtopanel(struct macctrls *mcs, unsigned int which)
493 {
494
495     macctrl_hideshowpanel(mcs, mcs->curpanel, FALSE);
496     macctrl_hideshowpanel(mcs, which, TRUE);
497     mcs->curpanel = which;
498 }
499
500 #if !TARGET_API_MAC_CARBON
501 /*
502  * System 7 focus manipulation
503  */
504 static void macctrl_defocus(union macctrl *mc)
505 {
506
507     assert(mac_gestalts.apprvers < 0x100);
508     switch (mc->generic.type) {
509       case MACCTRL_EDITBOX:
510         TEDeactivate((TEHandle)(*mc->editbox.tbctrl)->contrlData);
511         break;
512     }
513 }
514
515 static void macctrl_enfocus(union macctrl *mc)
516 {
517
518     assert(mac_gestalts.apprvers < 0x100);
519     switch (mc->generic.type) {
520       case MACCTRL_EDITBOX:
521         TEActivate((TEHandle)(*mc->editbox.tbctrl)->contrlData);
522         break;
523     }
524 }
525
526 static void macctrl_setfocus(struct macctrls *mcs, union macctrl *mc)
527 {
528
529     if (mcs->focus == mc)
530         return;
531     if (mcs->focus != NULL)
532         macctrl_defocus(mcs->focus);
533     mcs->focus = mc;
534     if (mc != NULL)
535         macctrl_enfocus(mc);
536 }
537 #endif
538
539 static void macctrl_text(struct macctrls *mcs, WindowPtr window,
540                          struct mac_layoutstate *curstate,
541                          union control *ctrl)
542 {
543     union macctrl *mc = snew(union macctrl);
544     Rect bounds;
545     SInt16 height;
546
547     assert(ctrl->text.label != NULL);
548     mc->generic.type = MACCTRL_TEXT;
549     mc->generic.ctrl = ctrl;
550     mc->generic.privdata = NULL;
551     bounds.left = curstate->pos.h;
552     bounds.right = bounds.left + curstate->width;
553     bounds.top = curstate->pos.v;
554     bounds.bottom = bounds.top + 16;
555     if (mac_gestalts.apprvers >= 0x100) {
556         Size olen;
557
558         mc->text.tbctrl = NewControl(window, &bounds, NULL, FALSE, 0, 0, 0,
559                                      kControlStaticTextProc, (long)mc);
560         SetControlData(mc->text.tbctrl, kControlEntireControl,
561                        kControlStaticTextTextTag,
562                        strlen(ctrl->text.label), ctrl->text.label);
563         GetControlData(mc->text.tbctrl, kControlEntireControl,
564                        kControlStaticTextTextHeightTag,
565                        sizeof(height), &height, &olen);
566     }
567 #if !TARGET_API_MAC_CARBON
568     else {
569         TEHandle te;
570
571         mc->text.tbctrl = NewControl(window, &bounds, NULL, FALSE, 0, 0, 0,
572                                      SYS7_TEXT_PROC, (long)mc);
573         te = (TEHandle)(*mc->text.tbctrl)->contrlData;
574         TESetText(ctrl->text.label, strlen(ctrl->text.label), te);
575         height = TEGetHeight(1, (*te)->nLines, te);
576     }
577 #endif
578     SizeControl(mc->text.tbctrl, curstate->width, height);
579     curstate->pos.v += height + 6;
580     add234(mcs->byctrl, mc);
581     mc->generic.next = mcs->panels[curstate->panelnum];
582     mcs->panels[curstate->panelnum] = mc;
583 }
584
585 static void macctrl_editbox(struct macctrls *mcs, WindowPtr window,
586                             struct mac_layoutstate *curstate,
587                             union control *ctrl)
588 {
589     union macctrl *mc = snew(union macctrl);
590     Rect lbounds, bounds, butbounds;
591     static int nextmenuid = MENU_MIN;
592     int menuid;
593     MenuRef menu;
594
595     mc->generic.type = MACCTRL_EDITBOX;
596     mc->generic.ctrl = ctrl;
597     mc->generic.privdata = NULL;
598     lbounds.left = curstate->pos.h;
599     lbounds.top = curstate->pos.v;
600     if (ctrl->editbox.percentwidth == 100) {
601         if (ctrl->editbox.label != NULL) {
602             lbounds.right = lbounds.left + curstate->width;
603             lbounds.bottom = lbounds.top + 16;
604             curstate->pos.v += 18;
605         }
606         bounds.left = curstate->pos.h;
607         bounds.right = bounds.left + curstate->width;
608     } else {
609         lbounds.right = lbounds.left +
610             curstate->width * (100 - ctrl->editbox.percentwidth) / 100;
611         lbounds.bottom = lbounds.top + 22;
612         bounds.left = lbounds.right;
613         bounds.right = lbounds.left + curstate->width;
614     }
615     bounds.top = curstate->pos.v;
616     bounds.bottom = bounds.top + 22;
617
618     if (ctrl->editbox.has_list) {
619         butbounds = bounds;
620         butbounds.left = butbounds.right - 20;
621         bounds.right -= 26; /* enough for 6 px gap and a button */
622     }
623
624     if (mac_gestalts.apprvers >= 0x100) {
625         if (ctrl->editbox.label == NULL)
626             mc->editbox.tblabel = NULL;
627         else {
628             mc->editbox.tblabel = NewControl(window, &lbounds, NULL, FALSE,
629                                              0, 0, 0, kControlStaticTextProc,
630                                              (long)mc);
631             SetControlData(mc->editbox.tblabel, kControlEntireControl,
632                            kControlStaticTextTextTag,
633                            strlen(ctrl->editbox.label), ctrl->editbox.label);
634         }
635         InsetRect(&bounds, 3, 3);
636         mc->editbox.tbctrl = NewControl(window, &bounds, NULL, FALSE, 0, 0, 0,
637                                         ctrl->editbox.password ?
638                                         kControlEditTextPasswordProc :
639                                         kControlEditTextProc, (long)mc);
640     }
641 #if !TARGET_API_MAC_CARBON
642     else {
643         if (ctrl->editbox.label == NULL)
644             mc->editbox.tblabel = NULL;
645         else {
646             mc->editbox.tblabel = NewControl(window, &lbounds, NULL, FALSE,
647                                              0, 0, 0, SYS7_TEXT_PROC,
648                                              (long)mc);
649             TESetText(ctrl->editbox.label, strlen(ctrl->editbox.label),
650                       (TEHandle)(*mc->editbox.tblabel)->contrlData);
651         }
652         mc->editbox.tbctrl = NewControl(window, &bounds, NULL, FALSE, 0, 0, 0,
653                                         SYS7_EDITBOX_PROC, (long)mc);
654     }
655 #endif
656
657     if (ctrl->editbox.has_list) {
658         while (GetMenuHandle(nextmenuid) != NULL)
659             if (++nextmenuid >= MENU_MAX) nextmenuid = MENU_MIN;
660         menuid = nextmenuid++;
661         menu = NewMenu(menuid, "\pdummy");
662         if (menu == NULL) goto nomenu;
663         mc->editbox.menu = menu;
664         mc->editbox.menuid = menuid;
665         InsertMenu(menu, kInsertHierarchicalMenu);
666         mc->editbox.nids = 0;
667         mc->editbox.ids = NULL;
668
669         mc->editbox.tbbutton = NewControl(window, &butbounds, NULL, FALSE,
670                                           popupTitleLeftJust, menuid, 0,
671                                           popupMenuProc + popupFixedWidth,
672                                           (long)mc);
673     }
674
675   nomenu:
676
677     curstate->pos.v += 28;
678     add234(mcs->byctrl, mc);
679     mc->generic.next = mcs->panels[curstate->panelnum];
680     mcs->panels[curstate->panelnum] = mc;
681     ctrlevent(mcs, mc, EVENT_REFRESH);
682 }
683
684 #if !TARGET_API_MAC_CARBON
685 static pascal SInt32 macctrl_sys7_editbox_cdef(SInt16 variant,
686                                                ControlRef control,
687                                                ControlDefProcMessage msg,
688                                                SInt32 param)
689 {
690     RgnHandle rgn;
691     Rect rect;
692     TEHandle te;
693     long ssfs;
694     Point mouse;
695
696     switch (msg) {
697       case initCntl:
698         rect = (*control)->contrlRect;
699         if (variant == SYS7_EDITBOX_VARIANT)
700             InsetRect(&rect, 3, 3); /* 2 if it's 20 pixels high */
701         te = TENew(&rect, &rect);
702         ssfs = GetScriptVariable(smSystemScript, smScriptSysFondSize);
703         (*te)->txSize = LoWord(ssfs);
704         (*te)->txFont = HiWord(ssfs);
705         (*control)->contrlData = (Handle)te;
706         return noErr;
707       case dispCntl:
708         TEDispose((TEHandle)(*control)->contrlData);
709         return 0;
710       case drawCntl:
711         if ((*control)->contrlVis) {
712             rect = (*control)->contrlRect;
713             if (variant == SYS7_EDITBOX_VARIANT) {
714                 PenNormal();
715                 FrameRect(&rect);
716                 InsetRect(&rect, 3, 3);
717             }
718             EraseRect(&rect);
719             (*(TEHandle)(*control)->contrlData)->viewRect = rect;
720             TEUpdate(&rect, (TEHandle)(*control)->contrlData);
721         }
722         return 0;
723       case testCntl:
724         if (variant == SYS7_TEXT_VARIANT)
725             return kControlNoPart;
726         mouse.h = LoWord(param);
727         mouse.v = HiWord(param);
728         rect = (*control)->contrlRect;
729         InsetRect(&rect, 3, 3);
730         return PtInRect(mouse, &rect) ? kControlEditTextPart : kControlNoPart;
731       case calcCRgns:
732         if (param & (1 << 31)) {
733             param &= ~(1 << 31);
734             goto calcthumbrgn;
735         }
736         /* FALLTHROUGH */
737       case calcCntlRgn:
738         rgn = (RgnHandle)param;
739         RectRgn(rgn, &(*control)->contrlRect);
740         return 0;
741       case calcThumbRgn:
742       calcthumbrgn:
743         rgn = (RgnHandle)param;
744         SetEmptyRgn(rgn);
745         return 0;
746     }
747
748     return 0;
749 }
750 #endif
751                              
752 static void macctrl_radio(struct macctrls *mcs, WindowPtr window,
753                           struct mac_layoutstate *curstate,
754                           union control *ctrl)
755 {
756     union macctrl *mc = snew(union macctrl);
757     Rect bounds;
758     Str255 title;
759     unsigned int i, colwidth;
760
761     mc->generic.type = MACCTRL_RADIO;
762     mc->generic.ctrl = ctrl;
763     mc->generic.privdata = NULL;
764     mc->radio.tbctrls = snewn(ctrl->radio.nbuttons, ControlRef);
765     colwidth = (curstate->width + 13) / ctrl->radio.ncolumns;
766     bounds.top = curstate->pos.v;
767     bounds.bottom = bounds.top + 16;
768     bounds.left = curstate->pos.h;
769     bounds.right = bounds.left + curstate->width;
770     if (ctrl->radio.label == NULL)
771         mc->radio.tblabel = NULL;
772     else {
773         if (mac_gestalts.apprvers >= 0x100) {
774             mc->radio.tblabel = NewControl(window, &bounds, NULL, FALSE,
775                                            0, 0, 0, kControlStaticTextProc,
776                                            (long)mc);
777             SetControlData(mc->radio.tblabel, kControlEntireControl,
778                            kControlStaticTextTextTag,
779                            strlen(ctrl->radio.label), ctrl->radio.label);
780         }
781 #if !TARGET_API_MAC_CARBON
782         else {
783             mc->radio.tblabel = NewControl(window, &bounds, NULL, FALSE,
784                                            0, 0, 0, SYS7_TEXT_PROC, (long)mc);
785             TESetText(ctrl->radio.label, strlen(ctrl->radio.label),
786                       (TEHandle)(*mc->radio.tblabel)->contrlData);
787         }
788 #endif
789         curstate->pos.v += 18;
790     }
791     for (i = 0; i < ctrl->radio.nbuttons; i++) {
792         bounds.top = curstate->pos.v - 2;
793         bounds.bottom = bounds.top + 18;
794         bounds.left = curstate->pos.h + colwidth * (i % ctrl->radio.ncolumns);
795         if (i == ctrl->radio.nbuttons - 1 ||
796             i % ctrl->radio.ncolumns == ctrl->radio.ncolumns - 1) {
797             bounds.right = curstate->pos.h + curstate->width;
798             curstate->pos.v += 18;
799         } else
800             bounds.right = bounds.left + colwidth - 13;
801         c2pstrcpy(title, ctrl->radio.buttons[i]);
802         mc->radio.tbctrls[i] = NewControl(window, &bounds, title, FALSE,
803                                           0, 0, 1, radioButProc, (long)mc);
804     }
805     curstate->pos.v += 4;
806     add234(mcs->byctrl, mc);
807     mc->generic.next = mcs->panels[curstate->panelnum];
808     mcs->panels[curstate->panelnum] = mc;
809     ctrlevent(mcs, mc, EVENT_REFRESH);
810 }
811
812 static void macctrl_checkbox(struct macctrls *mcs, WindowPtr window,
813                              struct mac_layoutstate *curstate,
814                              union control *ctrl)
815 {
816     union macctrl *mc = snew(union macctrl);
817     Rect bounds;
818     Str255 title;
819
820     assert(ctrl->checkbox.label != NULL);
821     mc->generic.type = MACCTRL_CHECKBOX;
822     mc->generic.ctrl = ctrl;
823     mc->generic.privdata = NULL;
824     bounds.left = curstate->pos.h;
825     bounds.right = bounds.left + curstate->width;
826     bounds.top = curstate->pos.v;
827     bounds.bottom = bounds.top + 16;
828     c2pstrcpy(title, ctrl->checkbox.label);
829     mc->checkbox.tbctrl = NewControl(window, &bounds, title, FALSE, 0, 0, 1,
830                                      checkBoxProc, (long)mc);
831     add234(mcs->byctrl, mc);
832     curstate->pos.v += 22;
833     mc->generic.next = mcs->panels[curstate->panelnum];
834     mcs->panels[curstate->panelnum] = mc;
835     ctrlevent(mcs, mc, EVENT_REFRESH);
836 }
837
838 static void macctrl_button(struct macctrls *mcs, WindowPtr window,
839                            struct mac_layoutstate *curstate,
840                            union control *ctrl)
841 {
842     union macctrl *mc = snew(union macctrl);
843     Rect bounds;
844     Str255 title;
845
846     assert(ctrl->button.label != NULL);
847     mc->generic.type = MACCTRL_BUTTON;
848     mc->generic.ctrl = ctrl;
849     mc->generic.privdata = NULL;
850     bounds.left = curstate->pos.h;
851     bounds.right = bounds.left + curstate->width;
852     bounds.top = curstate->pos.v;
853     bounds.bottom = bounds.top + 20;
854     c2pstrcpy(title, ctrl->button.label);
855     mc->button.tbctrl = NewControl(window, &bounds, title, FALSE, 0, 0, 1,
856                                    pushButProc, (long)mc);
857     mc->button.tbring = NULL;
858     if (mac_gestalts.apprvers >= 0x100) {
859         Boolean isdefault = ctrl->button.isdefault;
860
861         SetControlData(mc->button.tbctrl, kControlEntireControl,
862                        kControlPushButtonDefaultTag,
863                        sizeof(isdefault), &isdefault);
864     } else if (ctrl->button.isdefault) {
865         InsetRect(&bounds, -4, -4);
866         mc->button.tbring = NewControl(window, &bounds, title, FALSE, 0, 0, 1,
867                                        SYS7_DEFAULT_PROC, (long)mc);
868     }
869     if (mac_gestalts.apprvers >= 0x110) {
870         Boolean iscancel = ctrl->button.iscancel;
871
872         SetControlData(mc->button.tbctrl, kControlEntireControl,
873                        kControlPushButtonCancelTag,
874                        sizeof(iscancel), &iscancel);
875     }
876     if (ctrl->button.isdefault)
877         mcs->defbutton = mc;
878     if (ctrl->button.iscancel)
879         mcs->canbutton = mc;
880     add234(mcs->byctrl, mc);
881     mc->generic.next = mcs->panels[curstate->panelnum];
882     mcs->panels[curstate->panelnum] = mc;
883     curstate->pos.v += 26;
884 }
885
886 #if !TARGET_API_MAC_CARBON
887 static pascal SInt32 macctrl_sys7_default_cdef(SInt16 variant,
888                                                ControlRef control,
889                                                ControlDefProcMessage msg,
890                                                SInt32 param)
891 {
892     RgnHandle rgn;
893     Rect rect;
894     int oval;
895     PenState savestate;
896
897     switch (msg) {
898       case drawCntl:
899         if ((*control)->contrlVis) {
900             rect = (*control)->contrlRect;
901             GetPenState(&savestate);
902             PenNormal();
903             PenSize(3, 3);
904             if ((*control)->contrlHilite == kControlInactivePart)
905                 PenPat(&qd.gray);
906             oval = (rect.bottom - rect.top) / 2 + 2;
907             FrameRoundRect(&rect, oval, oval);
908             SetPenState(&savestate);
909         }
910         return 0;
911       case calcCRgns:
912         if (param & (1 << 31)) {
913             param &= ~(1 << 31);
914             goto calcthumbrgn;
915         }
916         /* FALLTHROUGH */
917       case calcCntlRgn:
918         rgn = (RgnHandle)param;
919         RectRgn(rgn, &(*control)->contrlRect);
920         return 0;
921       case calcThumbRgn:
922       calcthumbrgn:
923         rgn = (RgnHandle)param;
924         SetEmptyRgn(rgn);
925         return 0;
926     }
927
928     return 0;
929 }
930 #endif
931
932 static void macctrl_listbox(struct macctrls *mcs, WindowPtr window,
933                             struct mac_layoutstate *curstate,
934                             union control *ctrl)
935 {
936     union macctrl *mc = snew(union macctrl);
937     Rect bounds, upbounds, downbounds;
938     Size olen;
939
940     /* XXX Use label */
941     assert(ctrl->listbox.percentwidth == 100);
942     mc->generic.type = MACCTRL_LISTBOX;
943     mc->generic.ctrl = ctrl;
944     mc->generic.privdata = NULL;
945     /* The list starts off empty */
946     mc->listbox.nids = 0;
947     mc->listbox.ids = NULL;
948     bounds.left = curstate->pos.h;
949     bounds.right = bounds.left + curstate->width;
950     bounds.top = curstate->pos.v;
951     bounds.bottom = bounds.top + 16 * ctrl->listbox.height + 2;
952
953     if (ctrl->listbox.draglist) {
954         upbounds = downbounds = bounds;
955         upbounds.left = upbounds.right - 58;
956         upbounds.bottom = upbounds.top + 20;
957         downbounds.left = downbounds.right - 58;
958         downbounds.top = upbounds.bottom + 6;
959         downbounds.bottom = downbounds.top + 20;
960         bounds.right -= 64; /* enough for 6 px gap and a button */
961     }
962
963     if (mac_gestalts.apprvers >= 0x100) {
964         InsetRect(&bounds, 3, 3);
965         mc->listbox.tbctrl = NewControl(window, &bounds, NULL, FALSE,
966                                         ldes_Default, 0, 0,
967                                         kControlListBoxProc, (long)mc);
968         if (GetControlData(mc->listbox.tbctrl, kControlEntireControl,
969                            kControlListBoxListHandleTag,
970                            sizeof(mc->listbox.list), &mc->listbox.list,
971                            &olen) != noErr) {
972             DisposeControl(mc->listbox.tbctrl);
973             sfree(mc);
974             return;
975         }
976     }
977 #if !TARGET_API_MAC_CARBON
978     else {
979         InsetRect(&bounds, -3, -3);
980         mc->listbox.tbctrl = NewControl(window, &bounds, NULL, FALSE,
981                                         0, 0, 0,
982                                         SYS7_LISTBOX_PROC, (long)mc);
983         mc->listbox.list = (ListHandle)(*mc->listbox.tbctrl)->contrlData;
984         (*mc->listbox.list)->refCon = (long)mc;
985     }
986 #endif
987     if (!ctrl->listbox.multisel) {
988 #if TARGET_API_MAC_CARBON
989         SetListSelectionFlags(mc->listbox.list, lOnlyOne);
990 #else
991         (*mc->listbox.list)->selFlags = lOnlyOne;
992 #endif
993     }
994
995     if (ctrl->listbox.draglist) {
996         mc->listbox.tbup = NewControl(window, &upbounds, "\pUp", FALSE, 0, 0, 1,
997                                       pushButProc, (long)mc);
998         mc->listbox.tbdown = NewControl(window, &downbounds, "\pDown", FALSE, 0, 0, 1,
999                                       pushButProc, (long)mc);
1000     }
1001
1002     add234(mcs->byctrl, mc);
1003     curstate->pos.v += 6 + 16 * ctrl->listbox.height + 2;
1004     mc->generic.next = mcs->panels[curstate->panelnum];
1005     mcs->panels[curstate->panelnum] = mc;
1006     ctrlevent(mcs, mc, EVENT_REFRESH);
1007 #if TARGET_API_MAC_CARBON
1008     HideControl(GetListVerticalScrollBar(mc->listbox.list));
1009 #else
1010     HideControl((*mc->listbox.list)->vScroll);
1011 #endif
1012 }
1013
1014 #if !TARGET_API_MAC_CARBON
1015 static pascal SInt32 macctrl_sys7_listbox_cdef(SInt16 variant,
1016                                                ControlRef control,
1017                                                ControlDefProcMessage msg,
1018                                                SInt32 param)
1019 {
1020     RgnHandle rgn;
1021     Rect rect;
1022     ListHandle list;
1023     long ssfs;
1024     Point mouse;
1025     ListBounds bounds;
1026     Point csize;
1027     short savefont;
1028     short savesize;
1029     GrafPtr curport;
1030
1031     switch (msg) {
1032       case initCntl:
1033         rect = (*control)->contrlRect;
1034         InsetRect(&rect, 4, 4);
1035         rect.right -= 15; /* scroll bar */
1036         bounds.top = bounds.bottom = bounds.left = 0;
1037         bounds.right = 1;
1038         csize.h = csize.v = 0;
1039         GetPort(&curport);
1040         savefont = curport->txFont;
1041         savesize = curport->txSize;
1042         ssfs = GetScriptVariable(smSystemScript, smScriptSysFondSize);
1043         TextFont(HiWord(ssfs));
1044         TextSize(LoWord(ssfs));
1045         list = LNew(&rect, &bounds, csize, 0, (*control)->contrlOwner,
1046                     TRUE, FALSE, FALSE, TRUE);
1047         SetControlReference((*list)->vScroll, (long)list);
1048         (*control)->contrlData = (Handle)list;
1049         TextFont(savefont);
1050         TextSize(savesize);
1051         return noErr;
1052       case dispCntl:
1053         /*
1054          * If the dialogue box is being destroyed, the scroll bar
1055          * might have gone already.  In our situation, this is the
1056          * only time we destroy a control, so NULL out the scroll bar
1057          * handle to prevent LDispose trying to free it.
1058          */
1059         list = (ListHandle)(*control)->contrlData;
1060         (*list)->vScroll = NULL;
1061         LDispose(list);
1062         return 0;
1063       case drawCntl:
1064         if ((*control)->contrlVis) {
1065             rect = (*control)->contrlRect;
1066             /* XXX input focus highlighting? */
1067             InsetRect(&rect, 3, 3);
1068             PenNormal();
1069             FrameRect(&rect);
1070             list = (ListHandle)(*control)->contrlData;
1071             LActivate((*control)->contrlHilite != kControlInactivePart, list);
1072             GetPort(&curport);
1073             LUpdate(curport->visRgn, list);
1074         }
1075         return 0;
1076       case testCntl:
1077         mouse.h = LoWord(param);
1078         mouse.v = HiWord(param);
1079         rect = (*control)->contrlRect;
1080         InsetRect(&rect, 4, 4);
1081         /*
1082          * We deliberately exclude the scrollbar so that LClick() can see it.
1083          */
1084         rect.right -= 15;
1085         return PtInRect(mouse, &rect) ? kControlListBoxPart : kControlNoPart;
1086       case calcCRgns:
1087         if (param & (1 << 31)) {
1088             param &= ~(1 << 31);
1089             goto calcthumbrgn;
1090         }
1091         /* FALLTHROUGH */
1092       case calcCntlRgn:
1093         rgn = (RgnHandle)param;
1094         RectRgn(rgn, &(*control)->contrlRect);
1095         return 0;
1096       case calcThumbRgn:
1097       calcthumbrgn:
1098         rgn = (RgnHandle)param;
1099         SetEmptyRgn(rgn);
1100         return 0;
1101     }
1102
1103     return 0;
1104 }
1105 #endif
1106
1107 #if !TARGET_API_MAC_CARBON
1108 static pascal SInt32 macctrl_sys7_groupbox_cdef(SInt16 variant,
1109                                                 ControlRef control,
1110                                                 ControlDefProcMessage msg,
1111                                                 SInt32 param)
1112 {
1113     RgnHandle rgn;
1114     Rect rect;
1115     PenState savestate;
1116
1117     switch (msg) {
1118       case drawCntl:
1119         if ((*control)->contrlVis) {
1120             rect = (*control)->contrlRect;
1121             GetPenState(&savestate);
1122             PenNormal();
1123             PenSize(3, 3);
1124             PenPat(&qd.gray);
1125             FrameRect(&rect);
1126             SetPenState(&savestate);
1127         }
1128         return 0;
1129       case calcCRgns:
1130         if (param & (1 << 31)) {
1131             param &= ~(1 << 31);
1132             goto calcthumbrgn;
1133         }
1134         /* FALLTHROUGH */
1135       case calcCntlRgn:
1136         rgn = (RgnHandle)param;
1137         RectRgn(rgn, &(*control)->contrlRect);
1138         return 0;
1139       case calcThumbRgn:
1140       calcthumbrgn:
1141         rgn = (RgnHandle)param;
1142         SetEmptyRgn(rgn);
1143         return 0;
1144     }
1145     return 0;
1146 }
1147 #endif
1148
1149 static void macctrl_popup(struct macctrls *mcs, WindowPtr window,
1150                           struct mac_layoutstate *curstate,
1151                           union control *ctrl)
1152 {
1153     union macctrl *mc = snew(union macctrl);
1154     Rect bounds;
1155     Str255 title;
1156     unsigned int labelwidth;
1157     static int nextmenuid = MENU_MIN;
1158     int menuid;
1159     MenuRef menu;
1160
1161     /* 
1162      * <http://developer.apple.com/qa/tb/tb42.html> explains how to
1163      * create a popup menu with dynamic content.
1164      */
1165     assert(ctrl->listbox.height == 0);
1166     assert(!ctrl->listbox.draglist);
1167     assert(!ctrl->listbox.multisel);
1168
1169     mc->generic.type = MACCTRL_POPUP;
1170     mc->generic.ctrl = ctrl;
1171     mc->generic.privdata = NULL;
1172     c2pstrcpy(title, ctrl->button.label == NULL ? "" : ctrl->button.label);
1173
1174     /* Find a spare menu ID and create the menu */
1175     while (GetMenuHandle(nextmenuid) != NULL)
1176         if (++nextmenuid >= MENU_MAX) nextmenuid = MENU_MIN;
1177     menuid = nextmenuid++;
1178     menu = NewMenu(menuid, "\pdummy");
1179     if (menu == NULL) return;
1180     mc->popup.menu = menu;
1181     mc->popup.menuid = menuid;
1182     InsertMenu(menu, kInsertHierarchicalMenu);
1183
1184     /* The menu starts off empty */
1185     mc->popup.nids = 0;
1186     mc->popup.ids = NULL;
1187
1188     bounds.left = curstate->pos.h;
1189     bounds.right = bounds.left + curstate->width;
1190     bounds.top = curstate->pos.v;
1191     bounds.bottom = bounds.top + 20;
1192     /* XXX handle percentwidth == 100 */
1193     labelwidth = curstate->width * (100 - ctrl->listbox.percentwidth) / 100;
1194     mc->popup.tbctrl = NewControl(window, &bounds, title, FALSE,
1195                                   popupTitleLeftJust, menuid, labelwidth,
1196                                   popupMenuProc + popupFixedWidth, (long)mc);
1197     add234(mcs->byctrl, mc);
1198     curstate->pos.v += 26;
1199     mc->generic.next = mcs->panels[curstate->panelnum];
1200     mcs->panels[curstate->panelnum] = mc;
1201     ctrlevent(mcs, mc, EVENT_REFRESH);
1202 }
1203
1204 static void macctrl_groupbox(struct macctrls *mcs, WindowPtr window,
1205                              struct mac_layoutstate *curstate,
1206                              union control *ctrl)
1207 {
1208     union macctrl *mc = snew (union macctrl);
1209     Str255 ptitle;
1210     Rect r;
1211
1212     r.top = curstate->boxpos.v;
1213     r.left = curstate->boxpos.h;
1214     r.bottom = curstate->pos.v;
1215     r.right = curstate->boxpos.h + curstate->width;
1216
1217     mc->generic.type = MACCTRL_GROUPBOX;
1218     mc->generic.privdata = NULL;
1219     mc->generic.ctrl = ctrl;
1220     mc->generic.ctrl->generic.handler = NULL;
1221
1222     if (curstate->boxname)
1223         c2pstrcpy(ptitle, curstate->boxname);
1224     else
1225         c2pstrcpy(ptitle, "");
1226     if (mac_gestalts.apprvers >= 0x100) { /* Appearance Manager */
1227         mc->groupbox.tbctrl = NewControl(window, &r, ptitle, FALSE, 0, 0, 1,
1228                                          kControlGroupBoxTextTitleProc, (long)mc);
1229     } else {
1230         mc->groupbox.tbctrl = NewControl(window, &r, ptitle, FALSE, 0, 0, 1,
1231                                          SYS7_GROUPBOX_PROC, (long)mc);
1232     }
1233     add234(mcs->byctrl, mc);
1234     mc->generic.next = mcs->panels[curstate->panelnum];
1235     mcs->panels[curstate->panelnum] = mc;
1236 }
1237
1238 void macctrl_activate(WindowPtr window, EventRecord *event)
1239 {
1240     struct macctrls *mcs = mac_winctrls(window);
1241     Boolean active = (event->modifiers & activeFlag) != 0;
1242     GrafPtr saveport;
1243     int i, j;
1244     ControlPartCode state;
1245     union macctrl *mc;
1246
1247     GetPort(&saveport);
1248     SetPort((GrafPtr)GetWindowPort(window));
1249     if (mac_gestalts.apprvers >= 0x100)
1250         SetThemeWindowBackground(window, active ?
1251                                  kThemeBrushModelessDialogBackgroundActive :
1252                                  kThemeBrushModelessDialogBackgroundInactive,
1253                                  TRUE);
1254     state = active ? kControlNoPart : kControlInactivePart;
1255     for (i = 0; i <= mcs->curpanel; i += mcs->curpanel)
1256         for (mc = mcs->panels[i]; mc != NULL; mc = mc->generic.next) {
1257             switch (mc->generic.type) {
1258               case MACCTRL_TEXT:
1259                 HiliteControl(mc->text.tbctrl, state);
1260                 break;
1261               case MACCTRL_EDITBOX:
1262                 HiliteControl(mc->editbox.tbctrl, state);
1263                 if (mc->editbox.tblabel != NULL)
1264                     HiliteControl(mc->editbox.tblabel, state);
1265                 if (mc->editbox.tbbutton != NULL)
1266                     HiliteControl(mc->editbox.tbbutton, state);
1267                 break;
1268               case MACCTRL_RADIO:
1269                 for (j = 0; j < mc->generic.ctrl->radio.nbuttons; j++)
1270                     HiliteControl(mc->radio.tbctrls[j], state);
1271                 if (mc->radio.tblabel != NULL)
1272                     HiliteControl(mc->radio.tblabel, state);
1273                 break;
1274               case MACCTRL_CHECKBOX:
1275                 HiliteControl(mc->checkbox.tbctrl, state);
1276                 break;
1277               case MACCTRL_BUTTON:
1278                 HiliteControl(mc->button.tbctrl, state);
1279                 if (mc->button.tbring != NULL)
1280                     HiliteControl(mc->button.tbring, state);                
1281                 break;
1282               case MACCTRL_LISTBOX:
1283                 HiliteControl(mc->listbox.tbctrl, state);
1284                 if (mc->listbox.tbup != NULL)
1285                     HiliteControl(mc->listbox.tbup, state);
1286                 if (mc->listbox.tbdown != NULL)
1287                     HiliteControl(mc->listbox.tbdown, state);
1288                 break;
1289               case MACCTRL_POPUP:
1290                 HiliteControl(mc->popup.tbctrl, state);
1291                 break;
1292               case MACCTRL_GROUPBOX:
1293                 HiliteControl(mc->popup.tbctrl, state);
1294             }
1295 #if !TARGET_API_MAC_CARBON
1296             if (mcs->focus == mc) {
1297                 if (active)
1298                     macctrl_enfocus(mc);
1299                 else
1300                     macctrl_defocus(mc);
1301             }
1302 #endif
1303         }
1304     SetPort(saveport);
1305 }
1306
1307 void macctrl_click(WindowPtr window, EventRecord *event)
1308 {
1309     Point mouse;
1310     ControlHandle control, oldfocus;
1311     int part, trackresult;
1312     GrafPtr saveport;
1313     union macctrl *mc;
1314     struct macctrls *mcs = mac_winctrls(window);
1315     int i;
1316     UInt32 features;
1317
1318     GetPort(&saveport);
1319     SetPort((GrafPtr)GetWindowPort(window));
1320     mouse = event->where;
1321     GlobalToLocal(&mouse);
1322     part = FindControl(mouse, window, &control);
1323     if (control != NULL) {
1324 #if !TARGET_API_MAC_CARBON
1325         /*
1326          * Special magic for scroll bars in list boxes, whose refcon
1327          * is the list.
1328          */
1329         if (part == kControlUpButtonPart || part == kControlDownButtonPart ||
1330             part == kControlPageUpPart || part == kControlPageDownPart ||
1331             part == kControlIndicatorPart)
1332             mc = (union macctrl *)
1333                 (*(ListHandle)GetControlReference(control))->refCon;
1334        else
1335 #endif
1336             mc = (union macctrl *)GetControlReference(control);
1337         if (mac_gestalts.apprvers >= 0x100) {
1338             if (GetControlFeatures(control, &features) == noErr &&
1339                 (features & kControlSupportsFocus) &&
1340                 (features & kControlGetsFocusOnClick) &&
1341                 GetKeyboardFocus(window, &oldfocus) == noErr &&
1342                 control != oldfocus)
1343                 SetKeyboardFocus(window, control, part);
1344             trackresult = HandleControlClick(control, mouse, event->modifiers,
1345                                              (ControlActionUPP)-1);
1346         } else {
1347 #if !TARGET_API_MAC_CARBON
1348             if (mc->generic.type == MACCTRL_EDITBOX &&
1349                 control == mc->editbox.tbctrl) {
1350                 TEHandle te = (TEHandle)(*control)->contrlData;
1351
1352                 macctrl_setfocus(mcs, mc);
1353                 TEClick(mouse, !!(event->modifiers & shiftKey), te);
1354                 goto done;
1355             }
1356             if (mc->generic.type == MACCTRL_EDITBOX &&
1357                 control == mc->editbox.tbbutton) {
1358                 dlg_editbox_set(mc->generic.ctrl, mcs,
1359                                 cp_enumerate(dlg_listbox_index(mc->generic.ctrl, mcs)));
1360                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1361             }
1362             if (mc->generic.type == MACCTRL_LISTBOX &&
1363                 (control == mc->listbox.tbctrl ||
1364                  control == (*mc->listbox.list)->vScroll)) {
1365
1366                 macctrl_setfocus(mcs, mc);
1367                 if (LClick(mouse, event->modifiers, mc->listbox.list))
1368                     /* double-click */
1369                     ctrlevent(mcs, mc, EVENT_ACTION);
1370                 else
1371                     ctrlevent(mcs, mc, EVENT_SELCHANGE);
1372                 goto done;
1373             }
1374             if (mc->generic.type == MACCTRL_LISTBOX &&
1375                 control == mc->listbox.tbup)
1376                 draglist_up(mc, mcs);
1377             if (mc->generic.type == MACCTRL_LISTBOX &&
1378                 control == mc->listbox.tbdown)
1379                 draglist_down(mc, mcs);
1380 #endif
1381             trackresult = TrackControl(control, mouse, (ControlActionUPP)-1);
1382         }
1383         switch (mc->generic.type) {
1384           case MACCTRL_RADIO:
1385             if (trackresult != 0) {
1386                 for (i = 0; i < mc->generic.ctrl->radio.nbuttons; i++)
1387                     if (mc->radio.tbctrls[i] == control)
1388                         SetControlValue(mc->radio.tbctrls[i],
1389                                         kControlRadioButtonCheckedValue);
1390                     else
1391                         SetControlValue(mc->radio.tbctrls[i],
1392                                         kControlRadioButtonUncheckedValue);
1393                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1394             }
1395             break;
1396           case MACCTRL_CHECKBOX:
1397             if (trackresult != 0) {
1398                 SetControlValue(control, !GetControlValue(control));
1399                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1400             }
1401             break;
1402           case MACCTRL_BUTTON:
1403             if (trackresult != 0)
1404                 ctrlevent(mcs, mc, EVENT_ACTION);
1405             break;
1406           case MACCTRL_EDITBOX:
1407             if (control == mc->editbox.tbbutton) {
1408                 dlg_editbox_set(mc->generic.ctrl, mcs,
1409                                 cp_enumerate(dlg_listbox_index(mc->generic.ctrl, mcs)));
1410                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1411             }
1412             break;
1413           case MACCTRL_LISTBOX:
1414             if (control == mc->listbox.tbup)
1415                 draglist_up(mc, mcs);
1416             if (control == mc->listbox.tbdown)
1417                 draglist_down(mc, mcs);
1418
1419             /* FIXME spot double-click */
1420             ctrlevent(mcs, mc, EVENT_SELCHANGE);
1421             break;
1422           case MACCTRL_POPUP:
1423             ctrlevent(mcs, mc, EVENT_SELCHANGE);
1424             break;
1425         }
1426     }
1427   done:
1428     SetPort(saveport);
1429 }
1430
1431 void macctrl_key(WindowPtr window, EventRecord *event)
1432 {
1433     ControlRef control;
1434     struct macctrls *mcs = mac_winctrls(window);
1435     union macctrl *mc;
1436     unsigned long dummy;
1437
1438     switch (event->message & charCodeMask) {
1439       case kEnterCharCode:
1440       case kReturnCharCode:
1441         if (mcs->defbutton != NULL) {
1442             assert(mcs->defbutton->generic.type == MACCTRL_BUTTON);
1443             HiliteControl(mcs->defbutton->button.tbctrl, kControlButtonPart);
1444             /*
1445              * I'd like to delay unhilighting the button until after
1446              * the event has been processed, but by them the entire
1447              * dialgue box might have been destroyed.
1448              */
1449             Delay(6, &dummy);
1450             HiliteControl(mcs->defbutton->button.tbctrl, kControlNoPart);
1451             ctrlevent(mcs, mcs->defbutton, EVENT_ACTION);
1452         }
1453         return;
1454       case kEscapeCharCode:
1455         if (mcs->canbutton != NULL) {
1456             assert(mcs->canbutton->generic.type == MACCTRL_BUTTON);
1457             HiliteControl(mcs->canbutton->button.tbctrl, kControlButtonPart);
1458             Delay(6, &dummy);
1459             HiliteControl(mcs->defbutton->button.tbctrl, kControlNoPart);
1460             ctrlevent(mcs, mcs->canbutton, EVENT_ACTION);
1461         }
1462         return;
1463     }
1464     if (mac_gestalts.apprvers >= 0x100) {
1465         if (GetKeyboardFocus(window, &control) == noErr && control != NULL) {
1466             HandleControlKey(control, (event->message & keyCodeMask) >> 8,
1467                              event->message & charCodeMask, event->modifiers);
1468             mc = (union macctrl *)GetControlReference(control);
1469             switch (mc->generic.type) {
1470               case MACCTRL_LISTBOX:
1471                 ctrlevent(mcs, mc, EVENT_SELCHANGE);
1472                 break;
1473               default:
1474                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1475                 break;
1476             }
1477         }
1478     }
1479 #if !TARGET_API_MAC_CARBON
1480     else {
1481         TEHandle te;
1482
1483         if (mcs->focus != NULL) {
1484             mc = mcs->focus;
1485             switch (mc->generic.type) {
1486               case MACCTRL_EDITBOX:
1487                 te = (TEHandle)(*mc->editbox.tbctrl)->contrlData;
1488                 TEKey(event->message & charCodeMask, te);
1489                 ctrlevent(mcs, mc, EVENT_VALCHANGE);
1490                 break;
1491             }
1492         }
1493     }
1494 #endif
1495 }
1496
1497 void macctrl_update(WindowPtr window)
1498 {
1499 #if TARGET_API_MAC_CARBON
1500     RgnHandle visrgn;
1501 #endif
1502     Rect rect;
1503     GrafPtr saveport;
1504
1505     BeginUpdate(window);
1506     GetPort(&saveport);
1507     SetPort((GrafPtr)GetWindowPort(window));
1508     if (mac_gestalts.apprvers >= 0x101) {
1509 #if TARGET_API_MAC_CARBON
1510         GetPortBounds(GetWindowPort(window), &rect);
1511 #else
1512         rect = window->portRect;
1513 #endif
1514         InsetRect(&rect, -1, -1);
1515         DrawThemeModelessDialogFrame(&rect, mac_frontwindow() == window ?
1516                                      kThemeStateActive : kThemeStateInactive);
1517     }
1518 #if TARGET_API_MAC_CARBON
1519     visrgn = NewRgn();
1520     GetPortVisibleRegion(GetWindowPort(window), visrgn);
1521     UpdateControls(window, visrgn);
1522     DisposeRgn(visrgn);
1523 #else
1524     UpdateControls(window, window->visRgn);
1525 #endif
1526     SetPort(saveport);
1527     EndUpdate(window);
1528 }
1529
1530 #if TARGET_API_MAC_CARBON
1531 #define EnableItem EnableMenuItem
1532 #define DisableItem DisableMenuItem
1533 #endif
1534 void macctrl_adjustmenus(WindowPtr window)
1535 {
1536     MenuHandle menu;
1537
1538     menu = GetMenuHandle(mFile);
1539     DisableItem(menu, iSave); /* XXX enable if modified */
1540     EnableItem(menu, iSaveAs);
1541     EnableItem(menu, iDuplicate);
1542
1543     menu = GetMenuHandle(mEdit);
1544     DisableItem(menu, 0);
1545 }
1546
1547 void macctrl_close(WindowPtr window)
1548 {
1549     struct macctrls *mcs = mac_winctrls(window);
1550     union macctrl *mc;
1551
1552     /*
1553      * Mostly, we don't bother disposing of the Toolbox controls,
1554      * since that will happen automatically when the window is
1555      * disposed of.  Popup menus are an exception, because we have to
1556      * dispose of the menu ourselves, and doing that while the control
1557      * still holds a reference to it seems rude.
1558      */
1559     while ((mc = index234(mcs->byctrl, 0)) != NULL) {
1560         if (mc->generic.privdata != NULL && mc->generic.freeprivdata)
1561             sfree(mc->generic.privdata);
1562         switch (mc->generic.type) {
1563           case MACCTRL_POPUP:
1564             DisposeControl(mc->popup.tbctrl);
1565             DeleteMenu(mc->popup.menuid);
1566             DisposeMenu(mc->popup.menu);
1567             break;
1568         }
1569         del234(mcs->byctrl, mc);
1570         sfree(mc);
1571     }
1572
1573     freetree234(mcs->byctrl);
1574     mcs->byctrl = NULL;
1575     sfree(mcs->panels);
1576     mcs->panels = NULL;
1577 }
1578
1579 void dlg_update_start(union control *ctrl, void *dlg)
1580 {
1581
1582     /* No-op for now */
1583 }
1584
1585 void dlg_update_done(union control *ctrl, void *dlg)
1586 {
1587
1588     /* No-op for now */
1589 }
1590
1591 void dlg_set_focus(union control *ctrl, void *dlg)
1592 {
1593
1594     if (mac_gestalts.apprvers >= 0x100) {
1595         /* Use SetKeyboardFocus() */
1596     } else {
1597         /* Do our own mucking around */
1598     }
1599 }
1600
1601 union control *dlg_last_focused(union control *ctrl, void *dlg)
1602 {
1603
1604     return NULL;
1605 }
1606
1607 void dlg_beep(void *dlg)
1608 {
1609
1610     SysBeep(30);
1611 }
1612
1613 void dlg_error_msg(void *dlg, char *msg)
1614 {
1615     Str255 pmsg;
1616
1617     c2pstrcpy(pmsg, msg);
1618     ParamText(pmsg, NULL, NULL, NULL);
1619     StopAlert(128, NULL);
1620 }
1621
1622 void dlg_end(void *dlg, int value)
1623 {
1624     struct macctrls *mcs = dlg;
1625
1626     if (mcs->end != NULL)
1627         (*mcs->end)(mcs->window, value);
1628 };
1629
1630 void dlg_refresh(union control *ctrl, void *dlg)
1631 {
1632     struct macctrls *mcs = dlg;
1633     union macctrl *mc;
1634     int i;
1635
1636     if (ctrl == NULL) {
1637         /* NULL means refresh every control */
1638         for (i = 0 ; i < mcs->npanels; i++) {
1639             for (mc = mcs->panels[i]; mc != NULL; mc = mc->generic.next) {
1640                 ctrlevent(mcs, mc, EVENT_REFRESH);
1641             }
1642         }
1643         return;
1644     }
1645     /* Just refresh a specific control */
1646     mc = findbyctrl(mcs, ctrl);
1647     assert(mc != NULL);
1648     ctrlevent(mcs, mc, EVENT_REFRESH);
1649 };
1650
1651 void *dlg_get_privdata(union control *ctrl, void *dlg)
1652 {
1653     struct macctrls *mcs = dlg;
1654     union macctrl *mc = findbyctrl(mcs, ctrl);
1655
1656     assert(mc != NULL);
1657     return mc->generic.privdata;
1658 }
1659
1660 void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)
1661 {
1662     struct macctrls *mcs = dlg;
1663     union macctrl *mc = findbyctrl(mcs, ctrl);
1664
1665     assert(mc != NULL);
1666     mc->generic.privdata = ptr;
1667     mc->generic.freeprivdata = FALSE;
1668 }
1669
1670 void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)
1671 {
1672     struct macctrls *mcs = dlg;
1673     union macctrl *mc = findbyctrl(mcs, ctrl);
1674
1675     assert(mc != NULL);
1676     mc->generic.privdata = smalloc(size);
1677     mc->generic.freeprivdata = TRUE;
1678     return mc->generic.privdata;
1679 }
1680
1681
1682 /*
1683  * Radio Button control
1684  */
1685
1686 void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)
1687 {
1688     struct macctrls *mcs = dlg;
1689     union macctrl *mc = findbyctrl(mcs, ctrl);
1690     int i;
1691
1692     if (mc == NULL) return;
1693     for (i = 0; i < ctrl->radio.nbuttons; i++) {
1694         if (i == whichbutton)
1695             SetControlValue(mc->radio.tbctrls[i],
1696                             kControlRadioButtonCheckedValue);
1697         else
1698             SetControlValue(mc->radio.tbctrls[i],
1699                             kControlRadioButtonUncheckedValue);
1700     }
1701
1702 };
1703
1704 int dlg_radiobutton_get(union control *ctrl, void *dlg)
1705 {
1706     struct macctrls *mcs = dlg;
1707     union macctrl *mc = findbyctrl(mcs, ctrl);
1708     int i;
1709
1710     assert(mc != NULL);
1711     for (i = 0; i < ctrl->radio.nbuttons; i++) {
1712         if (GetControlValue(mc->radio.tbctrls[i])  ==
1713             kControlRadioButtonCheckedValue)
1714             return i;
1715     }
1716     return -1;
1717 };
1718
1719
1720 /*
1721  * Check Box control
1722  */
1723
1724 void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
1725 {
1726     struct macctrls *mcs = dlg;
1727     union macctrl *mc = findbyctrl(mcs, ctrl);
1728
1729     if (mc == NULL) return;
1730     SetControlValue(mc->checkbox.tbctrl,
1731                     checked ? kControlCheckBoxCheckedValue :
1732                               kControlCheckBoxUncheckedValue);
1733 }
1734
1735 int dlg_checkbox_get(union control *ctrl, void *dlg)
1736 {
1737     struct macctrls *mcs = dlg;
1738     union macctrl *mc = findbyctrl(mcs, ctrl);
1739
1740     assert(mc != NULL);
1741     return GetControlValue(mc->checkbox.tbctrl);
1742 }
1743
1744
1745 /*
1746  * Edit Box control
1747  */
1748
1749 void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
1750 {
1751     struct macctrls *mcs = dlg;
1752     union macctrl *mc = findbyctrl(mcs, ctrl);
1753     GrafPtr saveport;
1754
1755     if (mc == NULL) return;
1756     assert(mc->generic.type == MACCTRL_EDITBOX);
1757     GetPort(&saveport);
1758     SetPort((GrafPtr)(GetWindowPort(mcs->window)));
1759     if (mac_gestalts.apprvers >= 0x100)
1760         SetControlData(mc->editbox.tbctrl, kControlEntireControl,
1761                        ctrl->editbox.password ?
1762                        kControlEditTextPasswordTag :
1763                        kControlEditTextTextTag,
1764                        strlen(text), text);
1765 #if !TARGET_API_MAC_CARBON
1766     else
1767         TESetText(text, strlen(text),
1768                   (TEHandle)(*mc->editbox.tbctrl)->contrlData);
1769 #endif
1770     DrawOneControl(mc->editbox.tbctrl);
1771     SetPort(saveport);
1772 }
1773
1774 void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
1775 {
1776     struct macctrls *mcs = dlg;
1777     union macctrl *mc = findbyctrl(mcs, ctrl);
1778     Size olen;
1779
1780     assert(mc != NULL);
1781     assert(mc->generic.type == MACCTRL_EDITBOX);
1782     if (mac_gestalts.apprvers >= 0x100) {
1783         if (GetControlData(mc->editbox.tbctrl, kControlEntireControl,
1784                            ctrl->editbox.password ?
1785                            kControlEditTextPasswordTag :
1786                            kControlEditTextTextTag,
1787                            length - 1, buffer, &olen) != noErr)
1788             olen = 0;
1789         if (olen > length - 1)
1790             olen = length - 1;
1791     }
1792 #if !TARGET_API_MAC_CARBON
1793     else {
1794         TEHandle te = (TEHandle)(*mc->editbox.tbctrl)->contrlData;
1795
1796         olen = (*te)->teLength;
1797         if (olen > length - 1)
1798             olen = length - 1;
1799         memcpy(buffer, *(*te)->hText, olen);
1800     }
1801 #endif
1802     buffer[olen] = '\0';
1803 }
1804
1805
1806 /*
1807  * List Box control
1808  */
1809
1810 static void dlg_macpopup_clear(union control *ctrl, void *dlg)
1811 {
1812     struct macctrls *mcs = dlg;
1813     union macctrl *mc = findbyctrl(mcs, ctrl);
1814     MenuRef menu = mc->popup.menu;
1815     unsigned int i, n;
1816
1817     if (mc == NULL) return;
1818     n = CountMenuItems(menu);
1819     for (i = 0; i < n; i++)
1820         DeleteMenuItem(menu, n - i);
1821     mc->popup.nids = 0;
1822     sfree(mc->popup.ids);
1823     mc->popup.ids = NULL;
1824     SetControlMaximum(mc->popup.tbctrl, CountMenuItems(menu));
1825 }
1826
1827 static void dlg_macedit_clear(union control *ctrl, void *dlg)
1828 {
1829     struct macctrls *mcs = dlg;
1830     union macctrl *mc = findbyctrl(mcs, ctrl);
1831     MenuRef menu = mc->editbox.menu;
1832     unsigned int i, n;
1833
1834     if (mc == NULL) return;
1835     n = CountMenuItems(menu);
1836     for (i = 0; i < n; i++)
1837         DeleteMenuItem(menu, n - i);
1838     mc->editbox.nids = 0;
1839     sfree(mc->editbox.ids);
1840     mc->editbox.ids = NULL;
1841     SetControlMaximum(mc->editbox.tbbutton, CountMenuItems(menu));
1842 }
1843
1844 static void dlg_maclist_clear(union control *ctrl, void *dlg)
1845 {
1846     struct macctrls *mcs = dlg;
1847     union macctrl *mc = findbyctrl(mcs, ctrl);
1848
1849     if (mc == NULL) return;
1850     LDelRow(0, 0, mc->listbox.list);
1851     mc->listbox.nids = 0;
1852     sfree(mc->listbox.ids);
1853     mc->listbox.ids = NULL;
1854     DrawOneControl(mc->listbox.tbctrl);
1855 }
1856
1857 void dlg_listbox_clear(union control *ctrl, void *dlg)
1858 {
1859
1860     switch (ctrl->generic.type) {
1861       case CTRL_LISTBOX:
1862         if (ctrl->listbox.height == 0)
1863             dlg_macpopup_clear(ctrl, dlg);
1864         else
1865             dlg_maclist_clear(ctrl, dlg);
1866         break;
1867       case CTRL_EDITBOX:
1868         dlg_macedit_clear(ctrl, dlg);
1869     }
1870 }
1871
1872 static void dlg_macpopup_del(union control *ctrl, void *dlg, int index)
1873 {
1874     struct macctrls *mcs = dlg;
1875     union macctrl *mc = findbyctrl(mcs, ctrl);
1876     MenuRef menu = mc->popup.menu;
1877
1878     if (mc == NULL) return;
1879     DeleteMenuItem(menu, index + 1);
1880     if (mc->popup.ids != NULL)
1881         memcpy(mc->popup.ids + index, mc->popup.ids + index + 1,
1882                (mc->popup.nids - index - 1) * sizeof(*mc->popup.ids));
1883     SetControlMaximum(mc->popup.tbctrl, CountMenuItems(menu));
1884 }
1885
1886 static void dlg_macedit_del(union control *ctrl, void *dlg, int index)
1887 {
1888     struct macctrls *mcs = dlg;
1889     union macctrl *mc = findbyctrl(mcs, ctrl);
1890     MenuRef menu = mc->editbox.menu;
1891
1892     if (mc == NULL) return;
1893     DeleteMenuItem(menu, index + 1);
1894     if (mc->editbox.ids != NULL)
1895         memcpy(mc->editbox.ids + index, mc->editbox.ids + index + 1,
1896                (mc->editbox.nids - index - 1) * sizeof(*mc->editbox.ids));
1897     SetControlMaximum(mc->editbox.tbbutton, CountMenuItems(menu));
1898 }
1899
1900 static void dlg_maclist_del(union control *ctrl, void *dlg, int index)
1901 {
1902     struct macctrls *mcs = dlg;
1903     union macctrl *mc = findbyctrl(mcs, ctrl);
1904
1905     if (mc == NULL) return;
1906     LDelRow(1, index, mc->listbox.list);
1907     if (mc->listbox.ids != NULL)
1908         memcpy(mc->listbox.ids + index, mc->listbox.ids + index + 1,
1909                (mc->listbox.nids - index - 1) * sizeof(*mc->listbox.ids));
1910     DrawOneControl(mc->listbox.tbctrl);
1911 }
1912
1913 void dlg_listbox_del(union control *ctrl, void *dlg, int index)
1914 {
1915
1916     switch (ctrl->generic.type) {
1917       case CTRL_LISTBOX:
1918         if (ctrl->listbox.height == 0)
1919             dlg_macpopup_del(ctrl, dlg, index);
1920         else
1921             dlg_maclist_del(ctrl, dlg, index);
1922         break;
1923       case CTRL_EDITBOX:
1924         dlg_macedit_del(ctrl, dlg, index);
1925     }
1926 }
1927
1928 static void dlg_macpopup_add(union control *ctrl, void *dlg, char const *text)
1929 {
1930     struct macctrls *mcs = dlg;
1931     union macctrl *mc = findbyctrl(mcs, ctrl);
1932     MenuRef menu = mc->popup.menu;
1933     Str255 itemstring;
1934
1935     if (mc == NULL) return;
1936     assert(text[0] != '\0');
1937     c2pstrcpy(itemstring, text);
1938     AppendMenu(menu, "\pdummy");
1939     SetMenuItemText(menu, CountMenuItems(menu), itemstring);
1940     SetControlMaximum(mc->popup.tbctrl, CountMenuItems(menu));
1941 }
1942
1943 static void dlg_macedit_add(union control *ctrl, void *dlg, char const *text)
1944 {
1945     struct macctrls *mcs = dlg;
1946     union macctrl *mc = findbyctrl(mcs, ctrl);
1947     MenuRef menu = mc->editbox.menu;
1948     Str255 itemstring;
1949
1950     if (mc == NULL) return;
1951     assert(text[0] != '\0');
1952     c2pstrcpy(itemstring, text);
1953     AppendMenu(menu, "\pdummy");
1954     SetMenuItemText(menu, CountMenuItems(menu), itemstring);
1955     SetControlMaximum(mc->editbox.tbbutton, CountMenuItems(menu));
1956 }
1957
1958 static void dlg_maclist_add(union control *ctrl, void *dlg, char const *text)
1959 {
1960     struct macctrls *mcs = dlg;
1961     union macctrl *mc = findbyctrl(mcs, ctrl);
1962     ListBounds bounds;
1963     Cell cell = { 0, 0 };
1964
1965     if (mc == NULL) return;
1966 #if TARGET_API_MAC_CARBON
1967     GetListDataBounds(mc->listbox.list, &bounds);
1968 #else
1969     bounds = (*mc->listbox.list)->dataBounds;
1970 #endif
1971     cell.v = bounds.bottom;
1972     LAddRow(1, cell.v, mc->listbox.list);
1973     LSetCell(text, strlen(text), cell, mc->listbox.list);
1974     DrawOneControl(mc->listbox.tbctrl);
1975 }
1976
1977 void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
1978 {
1979
1980     switch (ctrl->generic.type) {
1981       case CTRL_LISTBOX:
1982         if (ctrl->listbox.height == 0)
1983             dlg_macpopup_add(ctrl, dlg, text);
1984         else
1985             dlg_maclist_add(ctrl, dlg, text);
1986         break;
1987       case CTRL_EDITBOX:
1988         dlg_macedit_add(ctrl, dlg, text);
1989         break;
1990     }
1991 }
1992
1993 static void dlg_macpopup_addwithid(union control *ctrl, void *dlg,
1994                                    char const *text, int id)
1995 {
1996     struct macctrls *mcs = dlg;
1997     union macctrl *mc = findbyctrl(mcs, ctrl);
1998     MenuRef menu = mc->popup.menu;
1999     unsigned int index;
2000
2001     if (mc == NULL) return;
2002     dlg_macpopup_add(ctrl, dlg, text);
2003     index = CountMenuItems(menu) - 1;
2004     if (mc->popup.nids <= index) {
2005         mc->popup.nids = index + 1;
2006         mc->popup.ids = sresize(mc->popup.ids, mc->popup.nids, int);
2007     }
2008     mc->popup.ids[index] = id;
2009 }
2010
2011 static void dlg_macedit_addwithid(union control *ctrl, void *dlg,
2012                                    char const *text, int id)
2013 {
2014     struct macctrls *mcs = dlg;
2015     union macctrl *mc = findbyctrl(mcs, ctrl);
2016     MenuRef menu = mc->editbox.menu;
2017     unsigned int index;
2018
2019     if (mc == NULL) return;
2020     dlg_macedit_add(ctrl, dlg, text);
2021     index = CountMenuItems(menu) - 1;
2022     if (mc->editbox.nids <= index) {
2023         mc->editbox.nids = index + 1;
2024         mc->editbox.ids = sresize(mc->editbox.ids, mc->editbox.nids, int);
2025     }
2026     mc->editbox.ids[index] = id;
2027 }
2028
2029 static void dlg_maclist_addwithid(union control *ctrl, void *dlg,
2030                                   char const *text, int id)
2031 {
2032     struct macctrls *mcs = dlg;
2033     union macctrl *mc = findbyctrl(mcs, ctrl);
2034     ListBounds bounds;
2035     int index;
2036
2037     if (mc == NULL) return;
2038     dlg_maclist_add(ctrl, dlg, text);
2039 #if TARGET_API_MAC_CARBON
2040     GetListDataBounds(mc->listbox.list, &bounds);
2041 #else
2042     bounds = (*mc->listbox.list)->dataBounds;
2043 #endif
2044     index = bounds.bottom;
2045     if (mc->listbox.nids <= index) {
2046         mc->listbox.nids = index + 1;
2047         mc->listbox.ids = sresize(mc->listbox.ids, mc->listbox.nids, int);
2048     }
2049     mc->listbox.ids[index] = id;
2050 }
2051
2052 void dlg_listbox_addwithid(union control *ctrl, void *dlg,
2053                            char const *text, int id)
2054 {
2055
2056     switch (ctrl->generic.type) {
2057       case CTRL_LISTBOX:
2058         if (ctrl->listbox.height == 0)
2059             dlg_macpopup_addwithid(ctrl, dlg, text, id);
2060         else
2061             dlg_maclist_addwithid(ctrl, dlg, text, id);
2062         break;
2063       case CTRL_EDITBOX:
2064         dlg_macedit_addwithid(ctrl, dlg, text, id);
2065         break;
2066     }
2067 }
2068
2069 int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
2070 {
2071     struct macctrls *mcs = dlg;
2072     union macctrl *mc = findbyctrl(mcs, ctrl);
2073
2074     assert(mc != NULL);
2075     switch (ctrl->generic.type) {
2076       case CTRL_LISTBOX:
2077         if (ctrl->listbox.height == 0) {
2078             assert(mc->popup.ids != NULL && mc->popup.nids > index);
2079             return mc->popup.ids[index];
2080         } else {
2081             assert(mc->listbox.ids != NULL && mc->listbox.nids > index);
2082             return mc->listbox.ids[index];
2083         }
2084       case CTRL_EDITBOX:
2085         assert(mc->editbox.ids != NULL && mc->editbox.nids > index);
2086         return mc->editbox.ids[index];
2087     }
2088     return -1;
2089 }
2090
2091 int dlg_listbox_index(union control *ctrl, void *dlg)
2092 {
2093     struct macctrls *mcs = dlg;
2094     union macctrl *mc = findbyctrl(mcs, ctrl);
2095     Cell cell = { 0, 0 };
2096
2097     assert(mc != NULL);
2098     switch (ctrl->generic.type) {
2099       case CTRL_LISTBOX:
2100         if (ctrl->listbox.height == 0)
2101             return GetControlValue(mc->popup.tbctrl) - 1;
2102         else {
2103             if (LGetSelect(TRUE, &cell, mc->listbox.list))
2104                 return cell.v;
2105             else
2106                 return -1;
2107         }
2108       case CTRL_EDITBOX:
2109         return GetControlValue(mc->editbox.tbbutton) - 1;
2110     }
2111     return -1;
2112 }
2113
2114 int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
2115 {
2116     struct macctrls *mcs = dlg;
2117     union macctrl *mc = findbyctrl(mcs, ctrl);
2118     Cell cell = { 0, 0 };
2119
2120     assert(mc != NULL);
2121     switch (ctrl->generic.type) {
2122       case CTRL_LISTBOX:
2123         if (ctrl->listbox.height == 0)
2124             return GetControlValue(mc->popup.tbctrl) - 1 == index;
2125         else {
2126             cell.v = index;
2127             return LGetSelect(FALSE, &cell, mc->listbox.list);
2128         }
2129       case CTRL_EDITBOX:
2130         return GetControlValue(mc->editbox.tbbutton) - 1 == index;
2131     }
2132     return FALSE;
2133 }
2134
2135 void dlg_listbox_select(union control *ctrl, void *dlg, int index)
2136 {
2137     struct macctrls *mcs = dlg;
2138     union macctrl *mc = findbyctrl(mcs, ctrl);
2139
2140     if (mc == NULL) return;
2141     switch (ctrl->generic.type) {
2142       case CTRL_LISTBOX:
2143         if (ctrl->listbox.height == 0)
2144             SetControlValue(mc->popup.tbctrl, index + 1);
2145         break;
2146       case CTRL_EDITBOX:
2147         SetControlValue(mc->editbox.tbbutton, index + 1);
2148     }
2149 }
2150
2151 static void draglist_move(union macctrl *mc, struct macctrls *mcs,
2152                           int direction)
2153 {
2154     ListBounds bounds;
2155     Cell cell = {0, 0};
2156     char current[255];
2157     char new[255];
2158     short curlength = 255;
2159     short newlength = 255;
2160     int curid, newid;
2161
2162     int index = dlg_listbox_index(mc->generic.ctrl, mcs);
2163
2164 #if TARGET_API_MAC_CARBON
2165     GetListDataBounds(mc->listbox.list, &bounds);
2166 #else
2167     bounds = (*mc->listbox.list)->dataBounds;
2168 #endif
2169
2170     if ((index < 0) ||
2171         (index == 0 && direction < 0) ||
2172         (index == bounds.bottom-1 && direction > 0)) {
2173         SysBeep(30);
2174         return;
2175     }
2176
2177     /* Swap the contents of the selected and target list cells */
2178
2179     cell.v = index;
2180     LGetCell(current, &curlength, cell, mc->listbox.list);
2181     current[curlength] = '\0';
2182     cell.v += direction;
2183     LGetCell(new, &newlength, cell, mc->listbox.list);
2184     new[newlength] = '\0';
2185
2186     cell.v = index;
2187     LSetCell(new, newlength, cell, mc->listbox.list);
2188     cell.v += direction;
2189     LSetCell(current, curlength, cell, mc->listbox.list);
2190
2191     /* Move the selection to the target list cell */
2192
2193     cell.v = index;
2194     LSetSelect(FALSE, cell, mc->listbox.list);
2195     cell.v += direction;
2196     LSetSelect(TRUE, cell, mc->listbox.list);
2197     DrawOneControl(mc->listbox.tbctrl);
2198
2199     /* Swap the ids of the list cells */
2200
2201     curid = mc->listbox.ids[index];
2202     newid = mc->listbox.ids[index + direction];
2203     mc->listbox.ids[index] = newid;
2204     mc->listbox.ids[index + direction] = curid;
2205                             
2206     ctrlevent(mcs, mc, EVENT_VALCHANGE);
2207 }
2208
2209 static void draglist_up(union macctrl *mc, struct macctrls *mcs)
2210 {
2211     draglist_move(mc, mcs, -1);
2212 }
2213
2214 static void draglist_down(union macctrl *mc, struct macctrls *mcs)
2215 {
2216     draglist_move(mc, mcs, +1);
2217 }
2218
2219 /*
2220  * Text control
2221  */
2222
2223 void dlg_text_set(union control *ctrl, void *dlg, char const *text)
2224 {
2225     struct macctrls *mcs = dlg;
2226     union macctrl *mc = findbyctrl(mcs, ctrl);
2227
2228     if (mc == NULL) return;
2229     if (mac_gestalts.apprvers >= 0x100)
2230         SetControlData(mc->text.tbctrl, kControlEntireControl,
2231                        kControlStaticTextTextTag, strlen(text), text);
2232 #if !TARGET_API_MAC_CARBON
2233     else
2234         TESetText(text, strlen(text),
2235                   (TEHandle)(*mc->text.tbctrl)->contrlData);
2236 #endif
2237 }
2238
2239
2240 /*
2241  * File Selector control
2242  */
2243
2244 void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
2245 {
2246
2247 }
2248
2249 void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)
2250 {
2251
2252 }
2253
2254
2255 /*
2256  * Font Selector control
2257  */
2258
2259 void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fn)
2260 {
2261
2262 }
2263
2264 void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fn)
2265 {
2266
2267 }
2268
2269
2270 /*
2271  * Printer enumeration
2272  */
2273
2274 printer_enum *printer_start_enum(int *nprinters)
2275 {
2276
2277     *nprinters = 0;
2278     return NULL;
2279 }
2280
2281 char *printer_get_name(printer_enum *pe, int thing)
2282 {
2283
2284     return "<none>";
2285 }
2286
2287 void printer_finish_enum(printer_enum *pe)
2288 {
2289
2290 }
2291
2292
2293 /*
2294  * Colour selection stuff
2295  */
2296
2297 void dlg_coloursel_start(union control *ctrl, void *dlg,
2298                          int r, int g, int b)
2299 {
2300     struct macctrls *mcs = dlg;
2301     union macctrl *mc = findbyctrl(mcs, ctrl);
2302     Point where = {-1, -1}; /* Screen with greatest colour depth */
2303     RGBColor incolour;
2304
2305     if (HAVE_COLOR_QD()) {
2306         incolour.red = r * 0x0101;
2307         incolour.green = g * 0x0101;
2308         incolour.blue = b * 0x0101;
2309         mcs->gotcolour = GetColor(where, "\pModify Colour:", &incolour,
2310                                   &mcs->thecolour);
2311         ctrlevent(mcs, mc, EVENT_CALLBACK);
2312     } else
2313         dlg_beep(dlg);
2314 }
2315
2316 int dlg_coloursel_results(union control *ctrl, void *dlg,
2317                           int *r, int *g, int *b)
2318 {
2319     struct macctrls *mcs = dlg;
2320
2321     if (mcs->gotcolour) {
2322         *r = mcs->thecolour.red >> 8;
2323         *g = mcs->thecolour.green >> 8;
2324         *b = mcs->thecolour.blue >> 8;
2325         return 1;
2326     } else
2327         return 0;
2328 }
2329
2330 /*
2331  * Local Variables:
2332  * c-file-style: "simon"
2333  * End:
2334  */