Event handling in the GTK dialog engine is now basically present and
[sgt/putty] / unix / gtkdlg.c
index 2b1f136..d48c422 100644 (file)
@@ -5,13 +5,24 @@
 /*
  * TODO:
  * 
- *  - event handling, in general!
- * 
  *  - keyboard stuff
  *     + accelerators
- *     + tab order
  *     + default button
  * 
+ *  - focus stuff
+ *     + `last focused' for nasty sessionsaver hack
+ *     + set focus into a sensible control to start with
+ *     + perhaps we need uc->primary as the right widget to
+ *       forcibly focus?
+ * 
+ *  - dlg_error_msg
+ * 
+ *  - must return a value from the dialog box!
+ * 
+ *  - font selection hiccup: the default `fixed' is not
+ *    automatically translated into its expanded XLFD form when the
+ *    font selector is started. It should be.
+ * 
  *  - cosmetics:
  *     + can't we _somehow_ have less leading between radio buttons?
  *     + wrapping text widgets, the horror, the horror
 
 #include "putty.h"
 #include "dialog.h"
+#include "tree234.h"
+
+struct uctrl {
+    union control *ctrl;
+    GtkWidget *toplevel;
+    void *privdata;
+    int privdata_needs_free;
+    GtkWidget **buttons; int nbuttons; /* for radio buttons */
+    GtkWidget *entry;         /* for editbox, combobox, filesel, fontsel */
+    GtkWidget *list;         /* for combobox, listbox */
+    GtkWidget *menu;         /* for optionmenu (==droplist) */
+    GtkWidget *optmenu;              /* also for optionmenu */
+    GtkWidget *text;         /* for text */
+};
+
+struct dlgparam {
+    tree234 *byctrl, *bywidget;
+    void *data;
+    struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */
+    /* `flags' are set to indicate when a GTK signal handler is being called
+     * due to automatic processing and should not flag a user event. */
+    int flags;
+};
+#define FLAG_UPDATING_COMBO_LIST 1
+
+/*
+ * Forward references.
+ */
+static void listitem_button(GtkWidget *item, GdkEventButton *event,
+                           gpointer data);
+static void menuitem_activate(GtkMenuItem *item, gpointer data);
+static void coloursel_ok(GtkButton *button, gpointer data);
+static void coloursel_cancel(GtkButton *button, gpointer data);
+
+static int uctrl_cmp_byctrl(void *av, void *bv)
+{
+    struct uctrl *a = (struct uctrl *)av;
+    struct uctrl *b = (struct uctrl *)bv;
+    if (a->ctrl < b->ctrl)
+       return -1;
+    else if (a->ctrl > b->ctrl)
+       return +1;
+    return 0;
+}
+
+static int uctrl_cmp_byctrl_find(void *av, void *bv)
+{
+    union control *a = (union control *)av;
+    struct uctrl *b = (struct uctrl *)bv;
+    if (a < b->ctrl)
+       return -1;
+    else if (a > b->ctrl)
+       return +1;
+    return 0;
+}
+
+static int uctrl_cmp_bywidget(void *av, void *bv)
+{
+    struct uctrl *a = (struct uctrl *)av;
+    struct uctrl *b = (struct uctrl *)bv;
+    if (a->toplevel < b->toplevel)
+       return -1;
+    else if (a->toplevel > b->toplevel)
+       return +1;
+    return 0;
+}
+
+static int uctrl_cmp_bywidget_find(void *av, void *bv)
+{
+    GtkWidget *a = (GtkWidget *)av;
+    struct uctrl *b = (struct uctrl *)bv;
+    if (a < b->toplevel)
+       return -1;
+    else if (a > b->toplevel)
+       return +1;
+    return 0;
+}
+
+static void dlg_init(struct dlgparam *dp)
+{
+    dp->byctrl = newtree234(uctrl_cmp_byctrl);
+    dp->bywidget = newtree234(uctrl_cmp_bywidget);
+    dp->coloursel_result.ok = FALSE;
+}
+
+static void dlg_cleanup(struct dlgparam *dp)
+{
+    struct uctrl *uc;
+
+    freetree234(dp->byctrl);          /* doesn't free the uctrls inside */
+    while ( (uc = index234(dp->bywidget, 0)) != NULL) {
+       del234(dp->bywidget, uc);
+       if (uc->privdata_needs_free)
+           sfree(uc->privdata);
+       sfree(uc->buttons);
+       sfree(uc);
+    }
+    freetree234(dp->bywidget);
+}
+
+static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc)
+{
+    add234(dp->byctrl, uc);
+    add234(dp->bywidget, uc);
+}
+
+static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl)
+{
+    return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find);
+}
+
+static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)
+{
+    struct uctrl *ret = NULL;
+    do {
+       ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find);
+       if (ret)
+           return ret;
+       w = w->parent;
+    } while (w);
+    return ret;
+}
 
 void *dlg_get_privdata(union control *ctrl, void *dlg)
 {
-    return NULL;                      /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    return uc->privdata;
 }
 
 void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    uc->privdata = ptr;
+    uc->privdata_needs_free = FALSE;
 }
 
 void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)
 {
-    return NULL;                      /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    uc->privdata = smalloc(size);
+    uc->privdata_needs_free = FALSE;
+    return uc->privdata;
 }
 
 union control *dlg_last_focused(void *dlg)
 {
+    struct dlgparam *dp = (struct dlgparam *)dlg;
     return NULL;                       /* FIXME */
 }
 
-void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int which)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_RADIO);
+    assert(uc->buttons != NULL);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), TRUE);
 }
 
 int dlg_radiobutton_get(union control *ctrl, void *dlg)
 {
-    return 0;                          /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    int i;
+
+    assert(uc->ctrl->generic.type == CTRL_RADIO);
+    assert(uc->buttons != NULL);
+    for (i = 0; i < uc->nbuttons; i++)
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i])))
+           return i;
+    return 0;                         /* got to return something */
 }
 
 void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);
 }
 
 int dlg_checkbox_get(union control *ctrl, void *dlg)
 {
-    return 0;                          /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
+    return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));
 }
 
 void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX);
+    assert(uc->entry != NULL);
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), text);
 }
 
 void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX);
+    assert(uc->entry != NULL);
+    strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
+           length);
+    buffer[length-1] = '\0';
+}
+
+static void container_remove_and_destroy(GtkWidget *w, gpointer data)
+{
+    GtkContainer *cont = GTK_CONTAINER(data);
+    /* gtk_container_remove will unref the widget for us; we need not. */
+    gtk_container_remove(cont, w);
 }
 
 /* The `listbox' functions can also apply to combo boxes. */
 void dlg_listbox_clear(union control *ctrl, void *dlg)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    GtkContainer *cont;
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    cont = (uc->menu ? GTK_CONTAINER(uc->menu) : GTK_CONTAINER(uc->list));
+
+    gtk_container_foreach(cont, container_remove_and_destroy, cont);
 }
 
 void dlg_listbox_del(union control *ctrl, void *dlg, int index)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    if (uc->menu) {
+       gtk_container_remove
+           (GTK_CONTAINER(uc->menu),
+            g_list_nth_data(GTK_MENU_SHELL(uc->menu)->children, index));
+    } else {
+       gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);
+    }
 }
 
 void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
 {
-    /* FIXME */
+    dlg_listbox_addwithindex(ctrl, dlg, text, 0);
 }
 
 /*
@@ -108,53 +310,235 @@ void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
 void dlg_listbox_addwithindex(union control *ctrl, void *dlg,
                              char const *text, int id)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    dp->flags |= FLAG_UPDATING_COMBO_LIST;
+
+    if (uc->menu) {
+       /*
+        * List item in a drop-down (but non-combo) list. Tabs are
+        * ignored; we just provide a standard menu item with the
+        * text.
+        */
+       GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
+
+       gtk_container_add(GTK_CONTAINER(uc->menu), menuitem);
+       gtk_widget_show(menuitem);
+
+       gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", (gpointer)id);
+       gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                          GTK_SIGNAL_FUNC(menuitem_activate), dp);
+    } else if (!uc->entry) {
+       /*
+        * List item in a non-combo-box list box. We make all of
+        * these Columns containing GtkLabels. This allows us to do
+        * the nasty force_left hack irrespective of whether there
+        * are tabs in the thing.
+        */
+       GtkWidget *listitem = gtk_list_item_new();
+       GtkWidget *cols = columns_new(0);
+       gint *percents;
+       int i, ncols;
+
+       /* Count the tabs in the text, and hence determine # of columns. */
+       ncols = 1;
+       for (i = 0; text[i]; i++)
+           if (text[i] == '\t')
+               ncols++;
+
+       assert(ncols <=
+              (uc->ctrl->listbox.ncols ? uc->ctrl->listbox.ncols : 1));
+       percents = smalloc(ncols * sizeof(gint));
+       percents[ncols-1] = 100;
+       for (i = 0; i < ncols-1; i++) {
+           percents[i] = uc->ctrl->listbox.percentages[i];
+           percents[ncols-1] -= percents[i];
+       }
+       columns_set_cols(COLUMNS(cols), ncols, percents);
+       sfree(percents);
+
+       for (i = 0; i < ncols; i++) {
+           int len = strcspn(text, "\t");
+           char *dup = dupprintf("%.*s", len, text);
+           GtkWidget *label;
+
+           text += len;
+           if (*text) text++;
+           label = gtk_label_new(dup);
+           sfree(dup);
+
+           columns_add(COLUMNS(cols), label, i, 1);
+           columns_force_left_align(COLUMNS(cols), label);
+           gtk_widget_show(label);
+       }
+       gtk_container_add(GTK_CONTAINER(listitem), cols);
+       gtk_widget_show(cols);
+       gtk_container_add(GTK_CONTAINER(uc->list), listitem);
+       gtk_widget_show(listitem);
+
+       gtk_signal_connect(GTK_OBJECT(listitem), "button_press_event",
+                          GTK_SIGNAL_FUNC(listitem_button), dp);
+       gtk_object_set_data(GTK_OBJECT(listitem), "user-data", (gpointer)id);
+    } else {
+       /*
+        * List item in a combo-box list, which means the sensible
+        * thing to do is make it a perfectly normal label. Hence
+        * tabs are disregarded.
+        */
+       GtkWidget *listitem = gtk_list_item_new_with_label(text);
+
+       gtk_container_add(GTK_CONTAINER(uc->list), listitem);
+       gtk_widget_show(listitem);
+
+       gtk_object_set_data(GTK_OBJECT(listitem), "user-data", (gpointer)id);
+    }
+
+    dp->flags &= ~FLAG_UPDATING_COMBO_LIST;
 }
 
 int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
 {
-    return -1;                         /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    GList *children;
+    GtkObject *item;
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
+                                                   uc->list));
+    item = GTK_OBJECT(g_list_nth_data(children, index));
+
+    return (int)gtk_object_get_data(GTK_OBJECT(item), "user-data");
 }
 
 /* dlg_listbox_index returns <0 if no single element is selected. */
 int dlg_listbox_index(union control *ctrl, void *dlg)
 {
-    return -1;                         /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    GList *children;
+    GtkWidget *item, *activeitem;
+    int i;
+    int selected = -1;
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    if (uc->menu)
+       activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));
+
+    children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
+                                                   uc->list));
+    for (i = 0; children!=NULL && (item = GTK_WIDGET(children->data))!=NULL;
+        i++, children = children->next) {
+       if (uc->menu ? activeitem == item :
+           GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED) {
+           if (selected < 0)
+               selected = i;
+           else
+               return -1;
+       }
+    }
+    return selected;
 }
 
 int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
 {
-    return 0;                          /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    GList *children;
+    GtkWidget *item, *activeitem;
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->menu != NULL || uc->list != NULL);
+
+    children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :
+                                                   uc->list));
+    item = GTK_WIDGET(g_list_nth_data(children, index));
+
+    if (uc->menu) {
+       activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));
+       return item == activeitem;
+    } else {
+       return GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED;
+    }
 }
 
 void dlg_listbox_select(union control *ctrl, void *dlg, int index)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
+          uc->ctrl->generic.type == CTRL_LISTBOX);
+    assert(uc->optmenu != NULL || uc->list != NULL);
+
+    if (uc->optmenu) {
+       gtk_option_menu_set_history(GTK_OPTION_MENU(uc->optmenu), index);
+    } else {
+       gtk_list_select_item(GTK_LIST(uc->list), index);
+    }
 }
 
 void dlg_text_set(union control *ctrl, void *dlg, char const *text)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+
+    assert(uc->ctrl->generic.type == CTRL_TEXT);
+    assert(uc->text != NULL);
+
+    gtk_label_set_text(GTK_LABEL(uc->text), text);
 }
 
 void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_FILESELECT);
+    assert(uc->entry != NULL);
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), fn.path);
 }
 
 void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_FILESELECT);
+    assert(uc->entry != NULL);
+    strncpy(fn->path, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
+           lenof(fn->path));
+    fn->path[lenof(fn->path)-1] = '\0';
 }
 
 void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
+    assert(uc->entry != NULL);
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), fs.name);
 }
 
 void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
+    assert(uc->entry != NULL);
+    strncpy(fs->name, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
+           lenof(fs->name));
+    fs->name[lenof(fs->name)-1] = '\0';
 }
 
 /*
@@ -164,16 +548,23 @@ void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)
  */
 void dlg_update_start(union control *ctrl, void *dlg)
 {
-    /* FIXME */
+    /*
+     * Apparently we can't do this at all in GTK. GtkCList supports
+     * freeze and thaw, but not GtkList. Bah.
+     */
 }
 
 void dlg_update_done(union control *ctrl, void *dlg)
 {
-    /* FIXME */
+    /*
+     * Apparently we can't do this at all in GTK. GtkCList supports
+     * freeze and thaw, but not GtkList. Bah.
+     */
 }
 
 void dlg_set_focus(union control *ctrl, void *dlg)
 {
+    struct dlgparam *dp = (struct dlgparam *)dlg;
     /* FIXME */
 }
 
@@ -184,11 +575,12 @@ void dlg_set_focus(union control *ctrl, void *dlg)
  */
 void dlg_beep(void *dlg)
 {
-    /* FIXME */
+    gdk_beep();
 }
 
 void dlg_error_msg(void *dlg, char *msg)
 {
+    struct dlgparam *dp = (struct dlgparam *)dlg;
     /* FIXME */
 }
 
@@ -199,26 +591,282 @@ void dlg_error_msg(void *dlg, char *msg)
  */
 void dlg_end(void *dlg, int value)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    gtk_main_quit();
+    /* FIXME: don't forget to faff about with returning a value */
 }
 
 void dlg_refresh(union control *ctrl, void *dlg)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc;
+
+    if (ctrl) {
+       if (ctrl->generic.handler != NULL)
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
+    } else {
+       int i;
+
+       for (i = 0; (uc = index234(dp->byctrl, i)) != NULL; i++) {
+           assert(uc->ctrl != NULL);
+           if (uc->ctrl->generic.handler != NULL)
+               uc->ctrl->generic.handler(uc->ctrl, dp,
+                                         dp->data, EVENT_REFRESH);
+       }
+    }
 }
 
 void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
 {
-    /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
+    gdouble cvals[4];
+
+    GtkWidget *coloursel =
+       gtk_color_selection_dialog_new("Select a colour");
+    GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);
+
+    dp->coloursel_result.ok = FALSE;
+
+    gtk_window_set_modal(GTK_WINDOW(coloursel), TRUE);
+    gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs->colorsel), FALSE);
+    cvals[0] = r / 255.0;
+    cvals[1] = g / 255.0;
+    cvals[2] = b / 255.0;
+    cvals[3] = 1.0;                   /* fully opaque! */
+    gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs->colorsel), cvals);
+
+    gtk_object_set_data(GTK_OBJECT(ccs->ok_button), "user-data",
+                       (gpointer)coloursel);
+    gtk_object_set_data(GTK_OBJECT(ccs->cancel_button), "user-data",
+                       (gpointer)coloursel);
+    gtk_object_set_data(GTK_OBJECT(coloursel), "user-data", (gpointer)uc);
+    gtk_signal_connect(GTK_OBJECT(ccs->ok_button), "clicked",
+                      GTK_SIGNAL_FUNC(coloursel_ok), (gpointer)dp);
+    gtk_signal_connect(GTK_OBJECT(ccs->cancel_button), "clicked",
+                      GTK_SIGNAL_FUNC(coloursel_cancel), (gpointer)dp);
+    gtk_signal_connect_object(GTK_OBJECT(ccs->ok_button), "clicked",
+                             GTK_SIGNAL_FUNC(gtk_widget_destroy),
+                             (gpointer)coloursel);
+    gtk_signal_connect_object(GTK_OBJECT(ccs->cancel_button), "clicked",
+                             GTK_SIGNAL_FUNC(gtk_widget_destroy),
+                             (gpointer)coloursel);
+    gtk_widget_show(coloursel);
 }
 
 int dlg_coloursel_results(union control *ctrl, void *dlg,
                          int *r, int *g, int *b)
 {
-    return 0;                          /* FIXME */
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    if (dp->coloursel_result.ok) {
+       *r = dp->coloursel_result.r;
+       *g = dp->coloursel_result.g;
+       *b = dp->coloursel_result.b;
+       return 1;
+    } else
+       return 0;
 }
 
-/*
+/* ----------------------------------------------------------------------
+ * Signal handlers while the dialog box is active.
+ */
+
+static void button_clicked(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
+}
+
+static void button_toggled(GtkToggleButton *tb, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tb));
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
+}
+
+static void editbox_changed(GtkEditable *ed, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    if (!(dp->flags & FLAG_UPDATING_COMBO_LIST)) {
+       struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
+    }
+}
+
+static void editbox_lostfocus(GtkWidget *ed, GdkEventFocus *event,
+                             gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH);
+}
+
+static void listitem_button(GtkWidget *item, GdkEventButton *event,
+                           gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    if (event->type == GDK_2BUTTON_PRESS ||
+       event->type == GDK_3BUTTON_PRESS) {
+       struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
+    }
+}
+
+static void list_selchange(GtkList *list, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(list));
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
+}
+
+static void menuitem_activate(GtkMenuItem *item, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    GtkWidget *menushell = GTK_WIDGET(item)->parent;
+    gpointer optmenu = gtk_object_get_data(GTK_OBJECT(menushell), "user-data");
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(optmenu));
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
+}
+
+static void draglist_move(struct dlgparam *dp, struct uctrl *uc, int direction)
+{
+    int index = dlg_listbox_index(uc->ctrl, dp);
+    GList *children = gtk_container_children(GTK_CONTAINER(uc->list));
+    GtkWidget *child;
+
+    if ((index < 0) ||
+       (index == 0 && direction < 0) ||
+       (index == g_list_length(children)-1 && direction > 0)) {
+       gdk_beep();
+       return;
+    }
+
+    child = g_list_nth_data(children, index);
+    gtk_widget_ref(child);
+    gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);
+    children = NULL;
+    children = g_list_append(children, child);
+    gtk_list_insert_items(GTK_LIST(uc->list), children, index + direction);
+    gtk_list_select_item(GTK_LIST(uc->list), index + direction);
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
+}
+
+static void draglist_up(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
+    draglist_move(dp, uc, -1);
+}
+
+static void draglist_down(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
+    draglist_move(dp, uc, +1);
+}
+
+static void filesel_ok(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(filesel), "user-data");
+    char *name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
+}
+
+static void fontsel_ok(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    gpointer fontsel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(fontsel), "user-data");
+    char *name = gtk_font_selection_dialog_get_font_name
+       (GTK_FONT_SELECTION_DIALOG(fontsel));
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
+}
+
+static void coloursel_ok(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");
+    gdouble cvals[4];
+    gtk_color_selection_get_color
+       (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel)->colorsel),
+        cvals);
+    dp->coloursel_result.r = (int) (255 * cvals[0]);
+    dp->coloursel_result.g = (int) (255 * cvals[1]);
+    dp->coloursel_result.b = (int) (255 * cvals[2]);
+    dp->coloursel_result.ok = TRUE;
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
+}
+
+static void coloursel_cancel(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");
+    dp->coloursel_result.ok = FALSE;
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
+}
+
+static void filefont_clicked(GtkButton *button, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
+
+    if (uc->ctrl->generic.type == CTRL_FILESELECT) {
+       GtkWidget *filesel =
+           gtk_file_selection_new(uc->ctrl->fileselect.title);
+       gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);
+       gtk_object_set_data
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
+            (gpointer)filesel);
+       gtk_object_set_data(GTK_OBJECT(filesel), "user-data", (gpointer)uc);
+       gtk_signal_connect
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
+            GTK_SIGNAL_FUNC(filesel_ok), (gpointer)dp);
+       gtk_signal_connect_object
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
+            GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
+       gtk_signal_connect_object
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
+            GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
+       gtk_widget_show(filesel);
+    }
+
+    if (uc->ctrl->generic.type == CTRL_FONTSELECT) {
+       gchar *spacings[] = { "c", "m", NULL };
+       GtkWidget *fontsel =
+           gtk_font_selection_dialog_new("Select a font");
+       gtk_window_set_modal(GTK_WINDOW(fontsel), TRUE);
+       gtk_font_selection_dialog_set_filter
+           (GTK_FONT_SELECTION_DIALOG(fontsel),
+            GTK_FONT_FILTER_BASE, GTK_FONT_ALL,
+            NULL, NULL, NULL, NULL, spacings, NULL);
+       gtk_font_selection_dialog_set_font_name
+           (GTK_FONT_SELECTION_DIALOG(fontsel),
+            gtk_entry_get_text(GTK_ENTRY(uc->entry)));
+       gtk_object_set_data
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
+            "user-data", (gpointer)fontsel);
+       gtk_object_set_data(GTK_OBJECT(fontsel), "user-data", (gpointer)uc);
+       gtk_signal_connect
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
+            "clicked", GTK_SIGNAL_FUNC(fontsel_ok), (gpointer)dp);
+       gtk_signal_connect_object
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),
+            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+            (gpointer)fontsel);
+       gtk_signal_connect_object
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button),
+            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+            (gpointer)fontsel);
+       gtk_widget_show(fontsel);
+    }
+}
+
+/* ----------------------------------------------------------------------
  * This function does the main layout work: it reads a controlset,
  * it creates the relevant GTK controls, and returns a GtkWidget
  * containing the result. (This widget might be a title of some
@@ -227,7 +875,8 @@ int dlg_coloursel_results(union control *ctrl, void *dlg,
  * definitely a GtkWidget and should probably be added to a
  * GtkVbox.)
  */
-GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
+GtkWidget *layout_ctrls(struct dlgparam *dp, struct controlset *s,
+                       int listitemheight)
 {
     Columns *cols;
     GtkWidget *ret;
@@ -262,8 +911,15 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
      */
     for (i = 0; i < s->ncontrols; i++) {
        union control *ctrl = s->ctrls[i];
+       struct uctrl uc;
         GtkWidget *w = NULL;
 
+       uc.ctrl = ctrl;
+       uc.privdata = NULL;
+       uc.privdata_needs_free = FALSE;
+       uc.buttons = NULL;
+       uc.entry = uc.list = uc.menu = uc.optmenu = uc.text = NULL;
+
         switch (ctrl->generic.type) {
           case CTRL_COLUMNS:
             {
@@ -274,15 +930,21 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
             }
             continue;                  /* no actual control created */
           case CTRL_TABDELAY:
-            /* FIXME: we can do columns_taborder_last easily enough, but
-             * we need to be able to remember which GtkWidget(s) correspond
-             * to ctrl->tabdelay.ctrl. */
+           {
+               struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl);
+               if (uc)
+                   columns_taborder_last(cols, uc->toplevel);
+           }
             continue;                  /* no actual control created */
           case CTRL_BUTTON:
             w = gtk_button_new_with_label(ctrl->generic.label);
+           gtk_signal_connect(GTK_OBJECT(w), "clicked",
+                              GTK_SIGNAL_FUNC(button_clicked), dp);
             break;
           case CTRL_CHECKBOX:
             w = gtk_check_button_new_with_label(ctrl->generic.label);
+           gtk_signal_connect(GTK_OBJECT(w), "toggled",
+                              GTK_SIGNAL_FUNC(button_toggled), dp);
             break;
           case CTRL_RADIO:
             /*
@@ -310,29 +972,42 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
                                  percentages);
                 g_free(percentages);
                 group = NULL;
+
+               uc.nbuttons = ctrl->radio.nbuttons;
+               uc.buttons = smalloc(uc.nbuttons * sizeof(GtkWidget *));
+
                 for (i = 0; i < ctrl->radio.nbuttons; i++) {
                     GtkWidget *b;
                     gint colstart;
 
                     b = (gtk_radio_button_new_with_label
                          (group, ctrl->radio.buttons[i]));
+                   uc.buttons[i] = b;
                     group = gtk_radio_button_group(GTK_RADIO_BUTTON(b));
                     colstart = i % ctrl->radio.ncolumns;
                     columns_add(COLUMNS(w), b, colstart,
                                 (i == ctrl->radio.nbuttons-1 ?
                                  ctrl->radio.ncolumns - colstart : 1));
                     gtk_widget_show(b);
+                   gtk_signal_connect(GTK_OBJECT(b), "toggled",
+                                      GTK_SIGNAL_FUNC(button_toggled), dp);
                 }
             }
             break;
           case CTRL_EDITBOX:
             if (ctrl->editbox.has_list) {
                 w = gtk_combo_new();
+               gtk_combo_set_value_in_list(GTK_COMBO(w), FALSE, TRUE);
+               uc.entry = GTK_COMBO(w)->entry;
+                uc.list = GTK_COMBO(w)->list;
             } else {
                 w = gtk_entry_new();
                 if (ctrl->editbox.password)
                     gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
+               uc.entry = w;
             }
+           gtk_signal_connect(GTK_OBJECT(uc.entry), "changed",
+                              GTK_SIGNAL_FUNC(editbox_changed), dp);
             /*
              * Edit boxes, for some strange reason, have a minimum
              * width of 150 in GTK 1.2. We don't want this - we'd
@@ -368,6 +1043,8 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
 
                 w = container;
             }
+           gtk_signal_connect(GTK_OBJECT(uc.entry), "focus_out_event",
+                              GTK_SIGNAL_FUNC(editbox_lostfocus), dp);
             break;
           case CTRL_FILESELECT:
           case CTRL_FONTSELECT:
@@ -389,7 +1066,7 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
                     gtk_widget_show(ww);
                 }
 
-                ww = gtk_entry_new();
+                uc.entry = ww = gtk_entry_new();
                 gtk_widget_size_request(ww, &req);
                 gtk_widget_set_usize(ww, 10, req.height);
                 columns_add(COLUMNS(w), ww, 0, 1);
@@ -398,25 +1075,35 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
                 ww = gtk_button_new_with_label(browsebtn);
                 columns_add(COLUMNS(w), ww, 1, 1);
                 gtk_widget_show(ww);
+
+               gtk_signal_connect(GTK_OBJECT(uc.entry), "changed",
+                                  GTK_SIGNAL_FUNC(editbox_changed), dp);
+               gtk_signal_connect(GTK_OBJECT(ww), "clicked",
+                                  GTK_SIGNAL_FUNC(filefont_clicked), dp);
             }
             break;
           case CTRL_LISTBOX:
             if (ctrl->listbox.height == 0) {
-                w = gtk_option_menu_new();
+                uc.optmenu = w = gtk_option_menu_new();
+               uc.menu = gtk_menu_new();
+               gtk_option_menu_set_menu(GTK_OPTION_MENU(w), uc.menu);
+               gtk_object_set_data(GTK_OBJECT(uc.menu), "user-data",
+                                   (gpointer)uc.optmenu);
             } else {
-                GtkWidget *list;
-                list = gtk_list_new();
-                gtk_list_set_selection_mode(GTK_LIST(list),
+                uc.list = gtk_list_new();
+                gtk_list_set_selection_mode(GTK_LIST(uc.list),
                                             (ctrl->listbox.multisel ?
                                              GTK_SELECTION_MULTIPLE :
                                              GTK_SELECTION_SINGLE));
                 w = gtk_scrolled_window_new(NULL, NULL);
                 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w),
-                                                      list);
+                                                      uc.list);
                 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w),
                                                GTK_POLICY_NEVER,
                                                GTK_POLICY_AUTOMATIC);
-                gtk_widget_show(list);
+                gtk_widget_show(uc.list);
+               gtk_signal_connect(GTK_OBJECT(uc.list), "selection-changed",
+                                  GTK_SIGNAL_FUNC(list_selchange), dp);
 
                 /*
                  * Adjust the height of the scrolled window to the
@@ -431,34 +1118,11 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
                  * upgrades, I'd be grateful.
                  */
                {
-                   int edge = GTK_WIDGET(list)->style->klass->ythickness;
+                   int edge = GTK_WIDGET(uc.list)->style->klass->ythickness;
                     gtk_widget_set_usize(w, 10,
                                          2*edge + (ctrl->listbox.height *
                                                   listitemheight));
                }
-#if 1
-/* here is an example of a percentage-based tabbed list item */
-{ int i; for (i=0; i<10; i++) {
-    GtkWidget *listitem = gtk_list_item_new();
-    GtkWidget *cols = columns_new(4);
-    GtkWidget *label1 = gtk_label_new("left");
-    GtkWidget *label2 = gtk_label_new("right");
-    GList *itemlist;
-    static const gint percents[] = { 50, 50 };
-    columns_set_cols(COLUMNS(cols), 2, percents);
-    columns_add(COLUMNS(cols), label1, 0, 1);
-    columns_force_left_align(COLUMNS(cols), label1);
-    columns_add(COLUMNS(cols), label2, 1, 1);
-    columns_force_left_align(COLUMNS(cols), label2);
-    gtk_widget_show(label1);
-    gtk_widget_show(label2);
-    gtk_widget_show(cols);
-    gtk_container_add(GTK_CONTAINER(listitem), cols);
-    itemlist = g_list_append(NULL, listitem);
-    gtk_list_append_items(GTK_LIST(list), itemlist);
-    gtk_widget_show(listitem);
-} }
-#endif
 
                 if (ctrl->listbox.draglist) {
                     /*
@@ -478,9 +1142,13 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
                     button = gtk_button_new_with_label("Up");
                     columns_add(COLUMNS(cols), button, 1, 1);
                     gtk_widget_show(button);
+                   gtk_signal_connect(GTK_OBJECT(button), "clicked",
+                                      GTK_SIGNAL_FUNC(draglist_up), dp);
                     button = gtk_button_new_with_label("Down");
                     columns_add(COLUMNS(cols), button, 1, 1);
                     gtk_widget_show(button);
+                   gtk_signal_connect(GTK_OBJECT(button), "clicked",
+                                      GTK_SIGNAL_FUNC(draglist_down), dp);
 
                     w = cols;
                 }
@@ -512,16 +1180,23 @@ GtkWidget *layout_ctrls(struct controlset *s, int listitemheight)
             }
             break;
           case CTRL_TEXT:
-            w = gtk_label_new(ctrl->generic.label);
+            uc.text = w = gtk_label_new(ctrl->generic.label);
             gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
             /* FIXME: deal with wrapping! */
             break;
         }
         if (w) {
+           struct uctrl *ucptr;
+
             columns_add(cols, w,
                         COLUMN_START(ctrl->generic.column),
                         COLUMN_SPAN(ctrl->generic.column));
             gtk_widget_show(w);
+
+           ucptr = smalloc(sizeof(struct uctrl));
+           *ucptr = uc;               /* structure copy */
+           ucptr->toplevel = w;
+           dlg_add_uctrl(dp, ucptr);
         }
     }
 
@@ -555,12 +1230,16 @@ void do_config_box(void)
     GtkTreeItem *treeitemlevels[8];
     GtkTree *treelevels[8];
     Config cfg;
+    struct dlgparam dp;
+    struct sesslist sl;
 
     struct selparam *selparams = NULL;
     int nselparams = 0, selparamsize = 0;
 
     do_defaults(NULL, &cfg);
 
+    dlg_init(&dp);
+
     {
         GtkWidget *listitem = gtk_list_item_new_with_label("foo");
         GtkRequisition req;
@@ -569,8 +1248,10 @@ void do_config_box(void)
         gtk_widget_unref(listitem);
     }
 
+    sl.nsessions = 0;
+
     ctrlbox = ctrl_new_box();
-    setup_config_box(ctrlbox, NULL, FALSE, 0);
+    setup_config_box(ctrlbox, &sl, FALSE, 0);
     unix_setup_config_box(ctrlbox, FALSE);
 
     window = gtk_dialog_new();
@@ -609,7 +1290,7 @@ void do_config_box(void)
     level = 0;
     for (index = 0; index < ctrlbox->nctrlsets; index++) {
        struct controlset *s = ctrlbox->ctrlsets[index];
-       GtkWidget *w = layout_ctrls(s, listitemheight);
+       GtkWidget *w = layout_ctrls(&dp, s, listitemheight);
 
        if (!*s->pathname) {
            gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area),
@@ -693,6 +1374,9 @@ void do_config_box(void)
                           &selparams[index]);
     }
 
+    dp.data = &cfg;
+    dlg_refresh(NULL, &dp);
+
     gtk_widget_show(window);
 
     gtk_signal_connect(GTK_OBJECT(window), "destroy",
@@ -700,6 +1384,7 @@ void do_config_box(void)
 
     gtk_main();
 
+    dlg_cleanup(&dp);
     sfree(selparams);
 }
 
@@ -776,14 +1461,20 @@ int platform_default_i(const char *name, int def)
 FontSpec platform_default_fontspec(const char *name)
 {
     FontSpec ret;
-    *ret.name = '\0';
+    if (!strcmp(name, "Font"))
+       strcpy(ret.name, "fixed");
+    else
+       *ret.name = '\0';
     return ret;
 }
 
 Filename platform_default_filename(const char *name)
 {
     Filename ret;
-    *ret.path = '\0';
+    if (!strcmp(name, "LogFileName"))
+       strcpy(ret.path, "putty.log");
+    else
+       *ret.path = '\0';
     return ret;
 }