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