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