OSDN Git Service

Add VC++ Project files for PuTTY DLL without exported functions.
[ffftp/ffftp.git] / putty / DIALOG.C
1 /*\r
2  * dialog.c - a reasonably platform-independent mechanism for\r
3  * describing dialog boxes.\r
4  */\r
5 \r
6 #include <assert.h>\r
7 #include <limits.h>\r
8 #include <stdarg.h>\r
9 #include <stdlib.h>\r
10 \r
11 #define DEFINE_INTORPTR_FNS\r
12 \r
13 #include "putty.h"\r
14 #include "dialog.h"\r
15 \r
16 int ctrl_path_elements(char *path)\r
17 {\r
18     int i = 1;\r
19     while (*path) {\r
20         if (*path == '/') i++;\r
21         path++;\r
22     }\r
23     return i;\r
24 }\r
25 \r
26 /* Return the number of matching path elements at the starts of p1 and p2,\r
27  * or INT_MAX if the paths are identical. */\r
28 int ctrl_path_compare(char *p1, char *p2)\r
29 {\r
30     int i = 0;\r
31     while (*p1 || *p2) {\r
32         if ((*p1 == '/' || *p1 == '\0') &&\r
33             (*p2 == '/' || *p2 == '\0'))\r
34             i++;                       /* a whole element matches, ooh */\r
35         if (*p1 != *p2)\r
36             return i;                  /* mismatch */\r
37         p1++, p2++;\r
38     }\r
39     return INT_MAX;                    /* exact match */\r
40 }\r
41 \r
42 struct controlbox *ctrl_new_box(void)\r
43 {\r
44     struct controlbox *ret = snew(struct controlbox);\r
45 \r
46     ret->nctrlsets = ret->ctrlsetsize = 0;\r
47     ret->ctrlsets = NULL;\r
48     ret->nfrees = ret->freesize = 0;\r
49     ret->frees = NULL;\r
50 \r
51     return ret;\r
52 }\r
53 \r
54 void ctrl_free_box(struct controlbox *b)\r
55 {\r
56     int i;\r
57 \r
58     for (i = 0; i < b->nctrlsets; i++) {\r
59         ctrl_free_set(b->ctrlsets[i]);\r
60     }\r
61     for (i = 0; i < b->nfrees; i++)\r
62         sfree(b->frees[i]);\r
63     sfree(b->ctrlsets);\r
64     sfree(b->frees);\r
65     sfree(b);\r
66 }\r
67 \r
68 void ctrl_free_set(struct controlset *s)\r
69 {\r
70     int i;\r
71 \r
72     sfree(s->pathname);\r
73     sfree(s->boxname);\r
74     sfree(s->boxtitle);\r
75     for (i = 0; i < s->ncontrols; i++) {\r
76         ctrl_free(s->ctrls[i]);\r
77     }\r
78     sfree(s->ctrls);\r
79     sfree(s);\r
80 }\r
81 \r
82 /*\r
83  * Find the index of first controlset in a controlbox for a given\r
84  * path. If that path doesn't exist, return the index where it\r
85  * should be inserted.\r
86  */\r
87 static int ctrl_find_set(struct controlbox *b, char *path, int start)\r
88 {\r
89     int i, last, thisone;\r
90 \r
91     last = 0;\r
92     for (i = 0; i < b->nctrlsets; i++) {\r
93         thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);\r
94         /*\r
95          * If `start' is true and there exists a controlset with\r
96          * exactly the path we've been given, we should return the\r
97          * index of the first such controlset we find. Otherwise,\r
98          * we should return the index of the first entry in which\r
99          * _fewer_ path elements match than they did last time.\r
100          */\r
101         if ((start && thisone == INT_MAX) || thisone < last)\r
102             return i;\r
103         last = thisone;\r
104     }\r
105     return b->nctrlsets;               /* insert at end */\r
106 }\r
107 \r
108 /*\r
109  * Find the index of next controlset in a controlbox for a given\r
110  * path, or -1 if no such controlset exists. If -1 is passed as\r
111  * input, finds the first.\r
112  */\r
113 int ctrl_find_path(struct controlbox *b, char *path, int index)\r
114 {\r
115     if (index < 0)\r
116         index = ctrl_find_set(b, path, 1);\r
117     else\r
118         index++;\r
119 \r
120     if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))\r
121         return index;\r
122     else\r
123         return -1;\r
124 }\r
125 \r
126 /* Set up a panel title. */\r
127 struct controlset *ctrl_settitle(struct controlbox *b,\r
128                                  char *path, char *title)\r
129 {\r
130     \r
131     struct controlset *s = snew(struct controlset);\r
132     int index = ctrl_find_set(b, path, 1);\r
133     s->pathname = dupstr(path);\r
134     s->boxname = NULL;\r
135     s->boxtitle = dupstr(title);\r
136     s->ncontrols = s->ctrlsize = 0;\r
137     s->ncolumns = 0;                   /* this is a title! */\r
138     s->ctrls = NULL;\r
139     if (b->nctrlsets >= b->ctrlsetsize) {\r
140         b->ctrlsetsize = b->nctrlsets + 32;\r
141         b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
142     }\r
143     if (index < b->nctrlsets)\r
144         memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
145                 (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
146     b->ctrlsets[index] = s;\r
147     b->nctrlsets++;\r
148     return s;\r
149 }\r
150 \r
151 /* Retrieve a pointer to a controlset, creating it if absent. */\r
152 struct controlset *ctrl_getset(struct controlbox *b,\r
153                                char *path, char *name, char *boxtitle)\r
154 {\r
155     struct controlset *s;\r
156     int index = ctrl_find_set(b, path, 1);\r
157     while (index < b->nctrlsets &&\r
158            !strcmp(b->ctrlsets[index]->pathname, path)) {\r
159         if (b->ctrlsets[index]->boxname &&\r
160             !strcmp(b->ctrlsets[index]->boxname, name))\r
161             return b->ctrlsets[index];\r
162         index++;\r
163     }\r
164     s = snew(struct controlset);\r
165     s->pathname = dupstr(path);\r
166     s->boxname = dupstr(name);\r
167     s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;\r
168     s->ncolumns = 1;\r
169     s->ncontrols = s->ctrlsize = 0;\r
170     s->ctrls = NULL;\r
171     if (b->nctrlsets >= b->ctrlsetsize) {\r
172         b->ctrlsetsize = b->nctrlsets + 32;\r
173         b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
174     }\r
175     if (index < b->nctrlsets)\r
176         memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
177                 (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
178     b->ctrlsets[index] = s;\r
179     b->nctrlsets++;\r
180     return s;\r
181 }\r
182 \r
183 /* Allocate some private data in a controlbox. */\r
184 void *ctrl_alloc(struct controlbox *b, size_t size)\r
185 {\r
186     void *p;\r
187     /*\r
188      * This is an internal allocation routine, so it's allowed to\r
189      * use smalloc directly.\r
190      */\r
191     p = smalloc(size);\r
192     if (b->nfrees >= b->freesize) {\r
193         b->freesize = b->nfrees + 32;\r
194         b->frees = sresize(b->frees, b->freesize, void *);\r
195     }\r
196     b->frees[b->nfrees++] = p;\r
197     return p;\r
198 }\r
199 \r
200 static union control *ctrl_new(struct controlset *s, int type,\r
201                                intorptr helpctx, handler_fn handler,\r
202                                intorptr context)\r
203 {\r
204     union control *c = snew(union control);\r
205     if (s->ncontrols >= s->ctrlsize) {\r
206         s->ctrlsize = s->ncontrols + 32;\r
207         s->ctrls = sresize(s->ctrls, s->ctrlsize, union control *);\r
208     }\r
209     s->ctrls[s->ncontrols++] = c;\r
210     /*\r
211      * Fill in the standard fields.\r
212      */\r
213     c->generic.type = type;\r
214     c->generic.tabdelay = 0;\r
215     c->generic.column = COLUMN_FIELD(0, s->ncolumns);\r
216     c->generic.helpctx = helpctx;\r
217     c->generic.handler = handler;\r
218     c->generic.context = context;\r
219     c->generic.label = NULL;\r
220     return c;\r
221 }\r
222 \r
223 /* `ncolumns' is followed by that many percentages, as integers. */\r
224 union control *ctrl_columns(struct controlset *s, int ncolumns, ...)\r
225 {\r
226     union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));\r
227     assert(s->ncolumns == 1 || ncolumns == 1);\r
228     c->columns.ncols = ncolumns;\r
229     s->ncolumns = ncolumns;\r
230     if (ncolumns == 1) {\r
231         c->columns.percentages = NULL;\r
232     } else {\r
233         va_list ap;\r
234         int i;\r
235         c->columns.percentages = snewn(ncolumns, int);\r
236         va_start(ap, ncolumns);\r
237         for (i = 0; i < ncolumns; i++)\r
238             c->columns.percentages[i] = va_arg(ap, int);\r
239         va_end(ap);\r
240     }\r
241     return c;\r
242 }\r
243 \r
244 union control *ctrl_editbox(struct controlset *s, char *label, char shortcut,\r
245                             int percentage,\r
246                             intorptr helpctx, handler_fn handler,\r
247                             intorptr context, intorptr context2)\r
248 {\r
249     union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
250     c->editbox.label = label ? dupstr(label) : NULL;\r
251     c->editbox.shortcut = shortcut;\r
252     c->editbox.percentwidth = percentage;\r
253     c->editbox.password = 0;\r
254     c->editbox.has_list = 0;\r
255     c->editbox.context2 = context2;\r
256     return c;\r
257 }\r
258 \r
259 union control *ctrl_combobox(struct controlset *s, char *label, char shortcut,\r
260                              int percentage,\r
261                              intorptr helpctx, handler_fn handler,\r
262                              intorptr context, intorptr context2)\r
263 {\r
264     union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
265     c->editbox.label = label ? dupstr(label) : NULL;\r
266     c->editbox.shortcut = shortcut;\r
267     c->editbox.percentwidth = percentage;\r
268     c->editbox.password = 0;\r
269     c->editbox.has_list = 1;\r
270     c->editbox.context2 = context2;\r
271     return c;\r
272 }\r
273 \r
274 /*\r
275  * `ncolumns' is followed by (alternately) radio button titles and\r
276  * intorptrs, until a NULL in place of a title string is seen. Each\r
277  * title is expected to be followed by a shortcut _iff_ `shortcut'\r
278  * is NO_SHORTCUT.\r
279  */\r
280 union control *ctrl_radiobuttons(struct controlset *s, char *label,\r
281                                  char shortcut, int ncolumns, intorptr helpctx,\r
282                                  handler_fn handler, intorptr context, ...)\r
283 {\r
284     va_list ap;\r
285     int i;\r
286     union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);\r
287     c->radio.label = label ? dupstr(label) : NULL;\r
288     c->radio.shortcut = shortcut;\r
289     c->radio.ncolumns = ncolumns;\r
290     /*\r
291      * Initial pass along variable argument list to count the\r
292      * buttons.\r
293      */\r
294     va_start(ap, context);\r
295     i = 0;\r
296     while (va_arg(ap, char *) != NULL) {\r
297         i++;\r
298         if (c->radio.shortcut == NO_SHORTCUT)\r
299             (void)va_arg(ap, int);     /* char promotes to int in arg lists */\r
300         (void)va_arg(ap, intorptr);\r
301     }\r
302     va_end(ap);\r
303     c->radio.nbuttons = i;\r
304     if (c->radio.shortcut == NO_SHORTCUT)\r
305         c->radio.shortcuts = snewn(c->radio.nbuttons, char);\r
306     else\r
307         c->radio.shortcuts = NULL;\r
308     c->radio.buttons = snewn(c->radio.nbuttons, char *);\r
309     c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);\r
310     /*\r
311      * Second pass along variable argument list to actually fill in\r
312      * the structure.\r
313      */\r
314     va_start(ap, context);\r
315     for (i = 0; i < c->radio.nbuttons; i++) {\r
316         c->radio.buttons[i] = dupstr(va_arg(ap, char *));\r
317         if (c->radio.shortcut == NO_SHORTCUT)\r
318             c->radio.shortcuts[i] = va_arg(ap, int);\r
319                                        /* char promotes to int in arg lists */\r
320         c->radio.buttondata[i] = va_arg(ap, intorptr);\r
321     }\r
322     va_end(ap);\r
323     return c;\r
324 }\r
325 \r
326 union control *ctrl_pushbutton(struct controlset *s,char *label,char shortcut,\r
327                                intorptr helpctx, handler_fn handler,\r
328                                intorptr context)\r
329 {\r
330     union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);\r
331     c->button.label = label ? dupstr(label) : NULL;\r
332     c->button.shortcut = shortcut;\r
333     c->button.isdefault = 0;\r
334     c->button.iscancel = 0;\r
335     return c;\r
336 }\r
337 \r
338 union control *ctrl_listbox(struct controlset *s,char *label,char shortcut,\r
339                             intorptr helpctx, handler_fn handler,\r
340                             intorptr context)\r
341 {\r
342     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
343     c->listbox.label = label ? dupstr(label) : NULL;\r
344     c->listbox.shortcut = shortcut;\r
345     c->listbox.height = 5;             /* *shrug* a plausible default */\r
346     c->listbox.draglist = 0;\r
347     c->listbox.multisel = 0;\r
348     c->listbox.percentwidth = 100;\r
349     c->listbox.ncols = 0;\r
350     c->listbox.percentages = NULL;\r
351     return c;\r
352 }\r
353 \r
354 union control *ctrl_droplist(struct controlset *s, char *label, char shortcut,\r
355                              int percentage, intorptr helpctx,\r
356                              handler_fn handler, intorptr context)\r
357 {\r
358     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
359     c->listbox.label = label ? dupstr(label) : NULL;\r
360     c->listbox.shortcut = shortcut;\r
361     c->listbox.height = 0;             /* means it's a drop-down list */\r
362     c->listbox.draglist = 0;\r
363     c->listbox.multisel = 0;\r
364     c->listbox.percentwidth = percentage;\r
365     c->listbox.ncols = 0;\r
366     c->listbox.percentages = NULL;\r
367     return c;\r
368 }\r
369 \r
370 union control *ctrl_draglist(struct controlset *s,char *label,char shortcut,\r
371                              intorptr helpctx, handler_fn handler,\r
372                              intorptr context)\r
373 {\r
374     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
375     c->listbox.label = label ? dupstr(label) : NULL;\r
376     c->listbox.shortcut = shortcut;\r
377     c->listbox.height = 5;             /* *shrug* a plausible default */\r
378     c->listbox.draglist = 1;\r
379     c->listbox.multisel = 0;\r
380     c->listbox.percentwidth = 100;\r
381     c->listbox.ncols = 0;\r
382     c->listbox.percentages = NULL;\r
383     return c;\r
384 }\r
385 \r
386 union control *ctrl_filesel(struct controlset *s,char *label,char shortcut,\r
387                             char const *filter, int write, char *title,\r
388                             intorptr helpctx, handler_fn handler,\r
389                             intorptr context)\r
390 {\r
391     union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);\r
392     c->fileselect.label = label ? dupstr(label) : NULL;\r
393     c->fileselect.shortcut = shortcut;\r
394     c->fileselect.filter = filter;\r
395     c->fileselect.for_writing = write;\r
396     c->fileselect.title = dupstr(title);\r
397     return c;\r
398 }\r
399 \r
400 union control *ctrl_fontsel(struct controlset *s,char *label,char shortcut,\r
401                             intorptr helpctx, handler_fn handler,\r
402                             intorptr context)\r
403 {\r
404     union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);\r
405     c->fontselect.label = label ? dupstr(label) : NULL;\r
406     c->fontselect.shortcut = shortcut;\r
407     return c;\r
408 }\r
409 \r
410 union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)\r
411 {\r
412     union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));\r
413     c->tabdelay.ctrl = ctrl;\r
414     return c;\r
415 }\r
416 \r
417 union control *ctrl_text(struct controlset *s, char *text, intorptr helpctx)\r
418 {\r
419     union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));\r
420     c->text.label = dupstr(text);\r
421     return c;\r
422 }\r
423 \r
424 union control *ctrl_checkbox(struct controlset *s, char *label, char shortcut,\r
425                              intorptr helpctx, handler_fn handler,\r
426                              intorptr context)\r
427 {\r
428     union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);\r
429     c->checkbox.label = label ? dupstr(label) : NULL;\r
430     c->checkbox.shortcut = shortcut;\r
431     return c;\r
432 }\r
433 \r
434 void ctrl_free(union control *ctrl)\r
435 {\r
436     int i;\r
437 \r
438     sfree(ctrl->generic.label);\r
439     switch (ctrl->generic.type) {\r
440       case CTRL_RADIO:\r
441         for (i = 0; i < ctrl->radio.nbuttons; i++)\r
442             sfree(ctrl->radio.buttons[i]);\r
443         sfree(ctrl->radio.buttons);\r
444         sfree(ctrl->radio.shortcuts);\r
445         sfree(ctrl->radio.buttondata);\r
446         break;\r
447       case CTRL_COLUMNS:\r
448         sfree(ctrl->columns.percentages);\r
449         break;\r
450       case CTRL_LISTBOX:\r
451         sfree(ctrl->listbox.percentages);\r
452         break;\r
453       case CTRL_FILESELECT:\r
454         sfree(ctrl->fileselect.title);\r
455         break;\r
456     }\r
457     sfree(ctrl);\r
458 }\r
459 \r
460 void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,\r
461                                 void *data, int event)\r
462 {\r
463     int button;\r
464     /*\r
465      * For a standard radio button set, the context parameter gives\r
466      * offsetof(targetfield, Config), and the extra data per button\r
467      * gives the value the target field should take if that button\r
468      * is the one selected.\r
469      */\r
470     if (event == EVENT_REFRESH) {\r
471         for (button = 0; button < ctrl->radio.nbuttons; button++)\r
472             if (*(int *)ATOFFSET(data, ctrl->radio.context.i) ==\r
473                 ctrl->radio.buttondata[button].i)\r
474                 break;\r
475         /* We expected that `break' to happen, in all circumstances. */\r
476         assert(button < ctrl->radio.nbuttons);\r
477         dlg_radiobutton_set(ctrl, dlg, button);\r
478     } else if (event == EVENT_VALCHANGE) {\r
479         button = dlg_radiobutton_get(ctrl, dlg);\r
480         assert(button >= 0 && button < ctrl->radio.nbuttons);\r
481         *(int *)ATOFFSET(data, ctrl->radio.context.i) =\r
482             ctrl->radio.buttondata[button].i;\r
483     }\r
484 }\r
485 \r
486 void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,\r
487                                 void *data, int event)\r
488 {\r
489     int offset, invert;\r
490 \r
491     /*\r
492      * For a standard checkbox, the context parameter gives\r
493      * offsetof(targetfield, Config), optionally ORed with\r
494      * CHECKBOX_INVERT.\r
495      */\r
496     offset = ctrl->checkbox.context.i;\r
497     if (offset & CHECKBOX_INVERT) {\r
498         offset &= ~CHECKBOX_INVERT;\r
499         invert = 1;\r
500     } else\r
501         invert = 0;\r
502 \r
503     /*\r
504      * C lacks a logical XOR, so the following code uses the idiom\r
505      * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1\r
506      * iff exactly one of a and b is nonzero, otherwise 0.)\r
507      */\r
508 \r
509     if (event == EVENT_REFRESH) {\r
510         dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert));\r
511     } else if (event == EVENT_VALCHANGE) {\r
512         *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert;\r
513     }\r
514 }\r
515 \r
516 void dlg_stdeditbox_handler(union control *ctrl, void *dlg,\r
517                             void *data, int event)\r
518 {\r
519     /*\r
520      * The standard edit-box handler expects the main `context'\r
521      * field to contain the `offsetof' a field in the structure\r
522      * pointed to by `data'. The secondary `context2' field\r
523      * indicates the type of this field:\r
524      *\r
525      *  - if context2 > 0, the field is a char array and context2\r
526      *    gives its size.\r
527      *  - if context2 == -1, the field is an int and the edit box\r
528      *    is numeric.\r
529      *  - if context2 < -1, the field is an int and the edit box is\r
530      *    _floating_, and (-context2) gives the scale. (E.g. if\r
531      *    context2 == -1000, then typing 1.2 into the box will set\r
532      *    the field to 1200.)\r
533      */\r
534     int offset = ctrl->editbox.context.i;\r
535     int length = ctrl->editbox.context2.i;\r
536 \r
537     if (length > 0) {\r
538         char *field = (char *)ATOFFSET(data, offset);\r
539         if (event == EVENT_REFRESH) {\r
540             dlg_editbox_set(ctrl, dlg, field);\r
541         } else if (event == EVENT_VALCHANGE) {\r
542             dlg_editbox_get(ctrl, dlg, field, length);\r
543         }\r
544     } else if (length < 0) {\r
545         int *field = (int *)ATOFFSET(data, offset);\r
546         char data[80];\r
547         if (event == EVENT_REFRESH) {\r
548             if (length == -1)\r
549                 sprintf(data, "%d", *field);\r
550             else\r
551                 sprintf(data, "%g", (double)*field / (double)(-length));\r
552             dlg_editbox_set(ctrl, dlg, data);\r
553         } else if (event == EVENT_VALCHANGE) {\r
554             dlg_editbox_get(ctrl, dlg, data, lenof(data));\r
555             if (length == -1)\r
556                 *field = atoi(data);\r
557             else\r
558                 *field = (int)((-length) * atof(data));\r
559         }\r
560     }\r
561 }\r
562 \r
563 void dlg_stdfilesel_handler(union control *ctrl, void *dlg,\r
564                             void *data, int event)\r
565 {\r
566     /*\r
567      * The standard file-selector handler expects the `context'\r
568      * field to contain the `offsetof' a Filename field in the\r
569      * structure pointed to by `data'.\r
570      */\r
571     int offset = ctrl->fileselect.context.i;\r
572 \r
573     if (event == EVENT_REFRESH) {\r
574         dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset));\r
575     } else if (event == EVENT_VALCHANGE) {\r
576         dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset));\r
577     }\r
578 }\r
579 \r
580 void dlg_stdfontsel_handler(union control *ctrl, void *dlg,\r
581                             void *data, int event)\r
582 {\r
583     /*\r
584      * The standard file-selector handler expects the `context'\r
585      * field to contain the `offsetof' a FontSpec field in the\r
586      * structure pointed to by `data'.\r
587      */\r
588     int offset = ctrl->fontselect.context.i;\r
589 \r
590     if (event == EVENT_REFRESH) {\r
591         dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset));\r
592     } else if (event == EVENT_VALCHANGE) {\r
593         dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset));\r
594     }\r
595 }\r