OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / DIALOG.C
diff --git a/contrib/putty/DIALOG.C b/contrib/putty/DIALOG.C
new file mode 100644 (file)
index 0000000..55bece8
--- /dev/null
@@ -0,0 +1,595 @@
+/*\r
+ * dialog.c - a reasonably platform-independent mechanism for\r
+ * describing dialog boxes.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <limits.h>\r
+#include <stdarg.h>\r
+#include <stdlib.h>\r
+\r
+#define DEFINE_INTORPTR_FNS\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+\r
+int ctrl_path_elements(char *path)\r
+{\r
+    int i = 1;\r
+    while (*path) {\r
+       if (*path == '/') i++;\r
+       path++;\r
+    }\r
+    return i;\r
+}\r
+\r
+/* Return the number of matching path elements at the starts of p1 and p2,\r
+ * or INT_MAX if the paths are identical. */\r
+int ctrl_path_compare(char *p1, char *p2)\r
+{\r
+    int i = 0;\r
+    while (*p1 || *p2) {\r
+       if ((*p1 == '/' || *p1 == '\0') &&\r
+           (*p2 == '/' || *p2 == '\0'))\r
+           i++;                       /* a whole element matches, ooh */\r
+       if (*p1 != *p2)\r
+           return i;                  /* mismatch */\r
+       p1++, p2++;\r
+    }\r
+    return INT_MAX;                   /* exact match */\r
+}\r
+\r
+struct controlbox *ctrl_new_box(void)\r
+{\r
+    struct controlbox *ret = snew(struct controlbox);\r
+\r
+    ret->nctrlsets = ret->ctrlsetsize = 0;\r
+    ret->ctrlsets = NULL;\r
+    ret->nfrees = ret->freesize = 0;\r
+    ret->frees = NULL;\r
+\r
+    return ret;\r
+}\r
+\r
+void ctrl_free_box(struct controlbox *b)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+       ctrl_free_set(b->ctrlsets[i]);\r
+    }\r
+    for (i = 0; i < b->nfrees; i++)\r
+       sfree(b->frees[i]);\r
+    sfree(b->ctrlsets);\r
+    sfree(b->frees);\r
+    sfree(b);\r
+}\r
+\r
+void ctrl_free_set(struct controlset *s)\r
+{\r
+    int i;\r
+\r
+    sfree(s->pathname);\r
+    sfree(s->boxname);\r
+    sfree(s->boxtitle);\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       ctrl_free(s->ctrls[i]);\r
+    }\r
+    sfree(s->ctrls);\r
+    sfree(s);\r
+}\r
+\r
+/*\r
+ * Find the index of first controlset in a controlbox for a given\r
+ * path. If that path doesn't exist, return the index where it\r
+ * should be inserted.\r
+ */\r
+static int ctrl_find_set(struct controlbox *b, char *path, int start)\r
+{\r
+    int i, last, thisone;\r
+\r
+    last = 0;\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+       thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);\r
+       /*\r
+        * If `start' is true and there exists a controlset with\r
+        * exactly the path we've been given, we should return the\r
+        * index of the first such controlset we find. Otherwise,\r
+        * we should return the index of the first entry in which\r
+        * _fewer_ path elements match than they did last time.\r
+        */\r
+       if ((start && thisone == INT_MAX) || thisone < last)\r
+           return i;\r
+       last = thisone;\r
+    }\r
+    return b->nctrlsets;              /* insert at end */\r
+}\r
+\r
+/*\r
+ * Find the index of next controlset in a controlbox for a given\r
+ * path, or -1 if no such controlset exists. If -1 is passed as\r
+ * input, finds the first.\r
+ */\r
+int ctrl_find_path(struct controlbox *b, char *path, int index)\r
+{\r
+    if (index < 0)\r
+       index = ctrl_find_set(b, path, 1);\r
+    else\r
+       index++;\r
+\r
+    if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))\r
+       return index;\r
+    else\r
+       return -1;\r
+}\r
+\r
+/* Set up a panel title. */\r
+struct controlset *ctrl_settitle(struct controlbox *b,\r
+                                char *path, char *title)\r
+{\r
+    \r
+    struct controlset *s = snew(struct controlset);\r
+    int index = ctrl_find_set(b, path, 1);\r
+    s->pathname = dupstr(path);\r
+    s->boxname = NULL;\r
+    s->boxtitle = dupstr(title);\r
+    s->ncontrols = s->ctrlsize = 0;\r
+    s->ncolumns = 0;                  /* this is a title! */\r
+    s->ctrls = NULL;\r
+    if (b->nctrlsets >= b->ctrlsetsize) {\r
+       b->ctrlsetsize = b->nctrlsets + 32;\r
+       b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
+    }\r
+    if (index < b->nctrlsets)\r
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
+    b->ctrlsets[index] = s;\r
+    b->nctrlsets++;\r
+    return s;\r
+}\r
+\r
+/* Retrieve a pointer to a controlset, creating it if absent. */\r
+struct controlset *ctrl_getset(struct controlbox *b,\r
+                              char *path, char *name, char *boxtitle)\r
+{\r
+    struct controlset *s;\r
+    int index = ctrl_find_set(b, path, 1);\r
+    while (index < b->nctrlsets &&\r
+          !strcmp(b->ctrlsets[index]->pathname, path)) {\r
+       if (b->ctrlsets[index]->boxname &&\r
+           !strcmp(b->ctrlsets[index]->boxname, name))\r
+           return b->ctrlsets[index];\r
+       index++;\r
+    }\r
+    s = snew(struct controlset);\r
+    s->pathname = dupstr(path);\r
+    s->boxname = dupstr(name);\r
+    s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;\r
+    s->ncolumns = 1;\r
+    s->ncontrols = s->ctrlsize = 0;\r
+    s->ctrls = NULL;\r
+    if (b->nctrlsets >= b->ctrlsetsize) {\r
+       b->ctrlsetsize = b->nctrlsets + 32;\r
+       b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
+    }\r
+    if (index < b->nctrlsets)\r
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
+    b->ctrlsets[index] = s;\r
+    b->nctrlsets++;\r
+    return s;\r
+}\r
+\r
+/* Allocate some private data in a controlbox. */\r
+void *ctrl_alloc(struct controlbox *b, size_t size)\r
+{\r
+    void *p;\r
+    /*\r
+     * This is an internal allocation routine, so it's allowed to\r
+     * use smalloc directly.\r
+     */\r
+    p = smalloc(size);\r
+    if (b->nfrees >= b->freesize) {\r
+       b->freesize = b->nfrees + 32;\r
+       b->frees = sresize(b->frees, b->freesize, void *);\r
+    }\r
+    b->frees[b->nfrees++] = p;\r
+    return p;\r
+}\r
+\r
+static union control *ctrl_new(struct controlset *s, int type,\r
+                              intorptr helpctx, handler_fn handler,\r
+                              intorptr context)\r
+{\r
+    union control *c = snew(union control);\r
+    if (s->ncontrols >= s->ctrlsize) {\r
+       s->ctrlsize = s->ncontrols + 32;\r
+       s->ctrls = sresize(s->ctrls, s->ctrlsize, union control *);\r
+    }\r
+    s->ctrls[s->ncontrols++] = c;\r
+    /*\r
+     * Fill in the standard fields.\r
+     */\r
+    c->generic.type = type;\r
+    c->generic.tabdelay = 0;\r
+    c->generic.column = COLUMN_FIELD(0, s->ncolumns);\r
+    c->generic.helpctx = helpctx;\r
+    c->generic.handler = handler;\r
+    c->generic.context = context;\r
+    c->generic.label = NULL;\r
+    return c;\r
+}\r
+\r
+/* `ncolumns' is followed by that many percentages, as integers. */\r
+union control *ctrl_columns(struct controlset *s, int ncolumns, ...)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));\r
+    assert(s->ncolumns == 1 || ncolumns == 1);\r
+    c->columns.ncols = ncolumns;\r
+    s->ncolumns = ncolumns;\r
+    if (ncolumns == 1) {\r
+       c->columns.percentages = NULL;\r
+    } else {\r
+       va_list ap;\r
+       int i;\r
+       c->columns.percentages = snewn(ncolumns, int);\r
+       va_start(ap, ncolumns);\r
+       for (i = 0; i < ncolumns; i++)\r
+           c->columns.percentages[i] = va_arg(ap, int);\r
+       va_end(ap);\r
+    }\r
+    return c;\r
+}\r
+\r
+union control *ctrl_editbox(struct controlset *s, char *label, char shortcut,\r
+                           int percentage,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context, intorptr context2)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
+    c->editbox.label = label ? dupstr(label) : NULL;\r
+    c->editbox.shortcut = shortcut;\r
+    c->editbox.percentwidth = percentage;\r
+    c->editbox.password = 0;\r
+    c->editbox.has_list = 0;\r
+    c->editbox.context2 = context2;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_combobox(struct controlset *s, char *label, char shortcut,\r
+                            int percentage,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context, intorptr context2)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
+    c->editbox.label = label ? dupstr(label) : NULL;\r
+    c->editbox.shortcut = shortcut;\r
+    c->editbox.percentwidth = percentage;\r
+    c->editbox.password = 0;\r
+    c->editbox.has_list = 1;\r
+    c->editbox.context2 = context2;\r
+    return c;\r
+}\r
+\r
+/*\r
+ * `ncolumns' is followed by (alternately) radio button titles and\r
+ * intorptrs, until a NULL in place of a title string is seen. Each\r
+ * title is expected to be followed by a shortcut _iff_ `shortcut'\r
+ * is NO_SHORTCUT.\r
+ */\r
+union control *ctrl_radiobuttons(struct controlset *s, char *label,\r
+                                char shortcut, int ncolumns, intorptr helpctx,\r
+                                handler_fn handler, intorptr context, ...)\r
+{\r
+    va_list ap;\r
+    int i;\r
+    union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);\r
+    c->radio.label = label ? dupstr(label) : NULL;\r
+    c->radio.shortcut = shortcut;\r
+    c->radio.ncolumns = ncolumns;\r
+    /*\r
+     * Initial pass along variable argument list to count the\r
+     * buttons.\r
+     */\r
+    va_start(ap, context);\r
+    i = 0;\r
+    while (va_arg(ap, char *) != NULL) {\r
+       i++;\r
+       if (c->radio.shortcut == NO_SHORTCUT)\r
+           (void)va_arg(ap, int);     /* char promotes to int in arg lists */\r
+       (void)va_arg(ap, intorptr);\r
+    }\r
+    va_end(ap);\r
+    c->radio.nbuttons = i;\r
+    if (c->radio.shortcut == NO_SHORTCUT)\r
+       c->radio.shortcuts = snewn(c->radio.nbuttons, char);\r
+    else\r
+       c->radio.shortcuts = NULL;\r
+    c->radio.buttons = snewn(c->radio.nbuttons, char *);\r
+    c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);\r
+    /*\r
+     * Second pass along variable argument list to actually fill in\r
+     * the structure.\r
+     */\r
+    va_start(ap, context);\r
+    for (i = 0; i < c->radio.nbuttons; i++) {\r
+       c->radio.buttons[i] = dupstr(va_arg(ap, char *));\r
+       if (c->radio.shortcut == NO_SHORTCUT)\r
+           c->radio.shortcuts[i] = va_arg(ap, int);\r
+                                      /* char promotes to int in arg lists */\r
+       c->radio.buttondata[i] = va_arg(ap, intorptr);\r
+    }\r
+    va_end(ap);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_pushbutton(struct controlset *s,char *label,char shortcut,\r
+                              intorptr helpctx, handler_fn handler,\r
+                              intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);\r
+    c->button.label = label ? dupstr(label) : NULL;\r
+    c->button.shortcut = shortcut;\r
+    c->button.isdefault = 0;\r
+    c->button.iscancel = 0;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_listbox(struct controlset *s,char *label,char shortcut,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 5;            /* *shrug* a plausible default */\r
+    c->listbox.draglist = 0;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = 100;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_droplist(struct controlset *s, char *label, char shortcut,\r
+                            int percentage, intorptr helpctx,\r
+                            handler_fn handler, intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 0;            /* means it's a drop-down list */\r
+    c->listbox.draglist = 0;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = percentage;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_draglist(struct controlset *s,char *label,char shortcut,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 5;            /* *shrug* a plausible default */\r
+    c->listbox.draglist = 1;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = 100;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_filesel(struct controlset *s,char *label,char shortcut,\r
+                           char const *filter, int write, char *title,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);\r
+    c->fileselect.label = label ? dupstr(label) : NULL;\r
+    c->fileselect.shortcut = shortcut;\r
+    c->fileselect.filter = filter;\r
+    c->fileselect.for_writing = write;\r
+    c->fileselect.title = dupstr(title);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_fontsel(struct controlset *s,char *label,char shortcut,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);\r
+    c->fontselect.label = label ? dupstr(label) : NULL;\r
+    c->fontselect.shortcut = shortcut;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));\r
+    c->tabdelay.ctrl = ctrl;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_text(struct controlset *s, char *text, intorptr helpctx)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));\r
+    c->text.label = dupstr(text);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_checkbox(struct controlset *s, char *label, char shortcut,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);\r
+    c->checkbox.label = label ? dupstr(label) : NULL;\r
+    c->checkbox.shortcut = shortcut;\r
+    return c;\r
+}\r
+\r
+void ctrl_free(union control *ctrl)\r
+{\r
+    int i;\r
+\r
+    sfree(ctrl->generic.label);\r
+    switch (ctrl->generic.type) {\r
+      case CTRL_RADIO:\r
+       for (i = 0; i < ctrl->radio.nbuttons; i++)\r
+           sfree(ctrl->radio.buttons[i]);\r
+       sfree(ctrl->radio.buttons);\r
+       sfree(ctrl->radio.shortcuts);\r
+       sfree(ctrl->radio.buttondata);\r
+       break;\r
+      case CTRL_COLUMNS:\r
+       sfree(ctrl->columns.percentages);\r
+       break;\r
+      case CTRL_LISTBOX:\r
+       sfree(ctrl->listbox.percentages);\r
+       break;\r
+      case CTRL_FILESELECT:\r
+       sfree(ctrl->fileselect.title);\r
+       break;\r
+    }\r
+    sfree(ctrl);\r
+}\r
+\r
+void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    int button;\r
+    /*\r
+     * For a standard radio button set, the context parameter gives\r
+     * offsetof(targetfield, Config), and the extra data per button\r
+     * gives the value the target field should take if that button\r
+     * is the one selected.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       for (button = 0; button < ctrl->radio.nbuttons; button++)\r
+           if (*(int *)ATOFFSET(data, ctrl->radio.context.i) ==\r
+               ctrl->radio.buttondata[button].i)\r
+               break;\r
+       /* We expected that `break' to happen, in all circumstances. */\r
+       assert(button < ctrl->radio.nbuttons);\r
+       dlg_radiobutton_set(ctrl, dlg, button);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       button = dlg_radiobutton_get(ctrl, dlg);\r
+       assert(button >= 0 && button < ctrl->radio.nbuttons);\r
+       *(int *)ATOFFSET(data, ctrl->radio.context.i) =\r
+           ctrl->radio.buttondata[button].i;\r
+    }\r
+}\r
+\r
+void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    int offset, invert;\r
+\r
+    /*\r
+     * For a standard checkbox, the context parameter gives\r
+     * offsetof(targetfield, Config), optionally ORed with\r
+     * CHECKBOX_INVERT.\r
+     */\r
+    offset = ctrl->checkbox.context.i;\r
+    if (offset & CHECKBOX_INVERT) {\r
+       offset &= ~CHECKBOX_INVERT;\r
+       invert = 1;\r
+    } else\r
+       invert = 0;\r
+\r
+    /*\r
+     * C lacks a logical XOR, so the following code uses the idiom\r
+     * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1\r
+     * iff exactly one of a and b is nonzero, otherwise 0.)\r
+     */\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert;\r
+    }\r
+}\r
+\r
+void dlg_stdeditbox_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard edit-box handler expects the main `context'\r
+     * field to contain the `offsetof' a field in the structure\r
+     * pointed to by `data'. The secondary `context2' field\r
+     * indicates the type of this field:\r
+     *\r
+     *  - if context2 > 0, the field is a char array and context2\r
+     *    gives its size.\r
+     *  - if context2 == -1, the field is an int and the edit box\r
+     *    is numeric.\r
+     *  - if context2 < -1, the field is an int and the edit box is\r
+     *    _floating_, and (-context2) gives the scale. (E.g. if\r
+     *    context2 == -1000, then typing 1.2 into the box will set\r
+     *    the field to 1200.)\r
+     */\r
+    int offset = ctrl->editbox.context.i;\r
+    int length = ctrl->editbox.context2.i;\r
+\r
+    if (length > 0) {\r
+       char *field = (char *)ATOFFSET(data, offset);\r
+       if (event == EVENT_REFRESH) {\r
+           dlg_editbox_set(ctrl, dlg, field);\r
+       } else if (event == EVENT_VALCHANGE) {\r
+           dlg_editbox_get(ctrl, dlg, field, length);\r
+       }\r
+    } else if (length < 0) {\r
+       int *field = (int *)ATOFFSET(data, offset);\r
+       char data[80];\r
+       if (event == EVENT_REFRESH) {\r
+           if (length == -1)\r
+               sprintf(data, "%d", *field);\r
+           else\r
+               sprintf(data, "%g", (double)*field / (double)(-length));\r
+           dlg_editbox_set(ctrl, dlg, data);\r
+       } else if (event == EVENT_VALCHANGE) {\r
+           dlg_editbox_get(ctrl, dlg, data, lenof(data));\r
+           if (length == -1)\r
+               *field = atoi(data);\r
+           else\r
+               *field = (int)((-length) * atof(data));\r
+       }\r
+    }\r
+}\r
+\r
+void dlg_stdfilesel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard file-selector handler expects the `context'\r
+     * field to contain the `offsetof' a Filename field in the\r
+     * structure pointed to by `data'.\r
+     */\r
+    int offset = ctrl->fileselect.context.i;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset));\r
+    }\r
+}\r
+\r
+void dlg_stdfontsel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard file-selector handler expects the `context'\r
+     * field to contain the `offsetof' a FontSpec field in the\r
+     * structure pointed to by `data'.\r
+     */\r
+    int offset = ctrl->fontselect.context.i;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset));\r
+    }\r
+}\r