]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
The long-awaited config box revamp! I've taken the whole config box
authorSimon Tatham <anakin@pobox.com>
Wed, 5 Mar 2003 22:07:40 +0000 (22:07 +0000)
committerSimon Tatham <anakin@pobox.com>
Wed, 5 Mar 2003 22:07:40 +0000 (22:07 +0000)
to pieces, and put it back together in a new table-driven form.
config.c sets up a data structure describing most of the config box;
wincfg.c adds in the Windows-specific options (so that config.c can
also form the basis for Mac and Unix config boxes). Then winctrls.c
contains a shiny new layout engine which consumes that data
structure, and windlg.c passes all WM_COMMAND and similar messages
to a driver alongside that layout engine. In the process I've sorted
out nicer-looking panel titles and finally fixed the list-boxes-are-
never-the-right-size bug (turned out to be Windows's fault, of
course). I _believe_ it should do everything the old config box did,
including context help. Now everyone has to test it thoroughly...

[originally from svn r2908]

Recipe
config.c [new file with mode: 0644]
dialog.c [new file with mode: 0644]
dialog.h [new file with mode: 0644]
doc/config.but
win_res.rc
wincfg.c [new file with mode: 0644]
winctrls.c
windlg.c
winhelp.h [new file with mode: 0644]
winstuff.h

diff --git a/Recipe b/Recipe
index 07bd56e025e3368567ce7279184cc7504ba0bc00..08399e4d1992e17c519d53f401a482c0144914d5 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -95,7 +95,7 @@
 
 # GUI front end and terminal emulator (putty, puttytel).
 GUITERM  = window windlg winctrls terminal sizetip wcwidth unicode ldiscucs
 
 # GUI front end and terminal emulator (putty, puttytel).
 GUITERM  = window windlg winctrls terminal sizetip wcwidth unicode ldiscucs
-         + logging printing winutils
+         + logging printing winutils dialog config wincfg tree234
 
 # Non-SSH back ends (putty, puttytel, plink).
 NONSSH   = telnet raw rlogin ldisc
 
 # Non-SSH back ends (putty, puttytel, plink).
 NONSSH   = telnet raw rlogin ldisc
@@ -146,7 +146,7 @@ pageant  : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234
 
 puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand noise sshsha winstore misc winctrls sshrsa sshdss winmisc
 
 puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand noise sshsha winstore misc winctrls sshrsa sshdss winmisc
-         + sshpubk sshaes sshsh512 import winutils puttygen.res LIBS
+         + sshpubk sshaes sshsh512 import winutils puttygen.res tree234 LIBS
 
 pterm    : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
          + logging uxprint settings pty be_none uxstore signal CHARSET
 
 pterm    : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
          + logging uxprint settings pty be_none uxstore signal CHARSET
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..ebcd607
--- /dev/null
+++ b/config.c
@@ -0,0 +1,1526 @@
+/*
+ * config.c - the platform-independent parts of the PuTTY
+ * configuration box.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "dialog.h"
+#include "storage.h"
+
+#define PRINTER_DISABLED_STRING "None (printing disabled)"
+
+static void protocolbuttons_handler(union control *ctrl, void *dlg,
+                                   void *data, int event)
+{
+    int button, defport;
+    Config *cfg = (Config *)data;
+    /*
+     * This function works just like the standard radio-button
+     * handler, except that it also has to change the setting of
+     * the port box. We expect the context parameter to point at
+     * the `union control' structure for the port box.
+     */
+    if (event == EVENT_REFRESH) {
+       for (button = 0; button < ctrl->radio.nbuttons; button++)
+           if (cfg->protocol == ctrl->radio.buttondata[button].i)
+               break;
+       /* We expected that `break' to happen, in all circumstances. */
+       assert(button < ctrl->radio.nbuttons);
+       dlg_radiobutton_set(ctrl, dlg, button);
+    } else if (event == EVENT_VALCHANGE) {
+       int oldproto = cfg->protocol;
+       button = dlg_radiobutton_get(ctrl, dlg);
+       assert(button >= 0 && button < ctrl->radio.nbuttons);
+       cfg->protocol = ctrl->radio.buttondata[button].i;
+       if (oldproto != cfg->protocol) {
+           defport = -1;
+           switch (cfg->protocol) {
+             case PROT_SSH: defport = 22; break;
+             case PROT_TELNET: defport = 23; break;
+             case PROT_RLOGIN: defport = 513; break;
+           }
+           if (defport > 0 && cfg->port != defport) {
+               cfg->port = defport;
+               dlg_refresh((union control *)ctrl->radio.context.p, dlg);
+           }
+       }
+    }
+}
+
+static void numeric_keypad_handler(union control *ctrl, void *dlg,
+                                  void *data, int event)
+{
+    int button;
+    Config *cfg = (Config *)data;
+    /*
+     * This function works much like the standard radio button
+     * handler, but it has to handle two fields in Config.
+     */
+    if (event == EVENT_REFRESH) {
+       if (cfg->nethack_keypad)
+           button = 2;
+       else if (cfg->app_keypad)
+           button = 1;
+       else
+           button = 0;
+       assert(button < ctrl->radio.nbuttons);
+       dlg_radiobutton_set(ctrl, dlg, button);
+    } else if (event == EVENT_VALCHANGE) {
+       button = dlg_radiobutton_get(ctrl, dlg);
+       assert(button >= 0 && button < ctrl->radio.nbuttons);
+       if (button == 2) {
+           cfg->app_keypad = FALSE;
+           cfg->nethack_keypad = TRUE;
+       } else {
+           cfg->app_keypad = (button != 0);
+           cfg->nethack_keypad = FALSE;
+       }
+    }
+}
+
+static void cipherlist_handler(union control *ctrl, void *dlg,
+                              void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    if (event == EVENT_REFRESH) {
+       int i;
+
+       static const struct { char *s; int c; } ciphers[] = {
+           { "3DES",                   CIPHER_3DES },
+           { "Blowfish",               CIPHER_BLOWFISH },
+           { "DES",                    CIPHER_DES },
+           { "AES (SSH 2 only)",       CIPHER_AES },
+           { "-- warn below here --",  CIPHER_WARN }
+       };
+
+       /* Set up the "selected ciphers" box. */
+       /* (cipherlist assumed to contain all ciphers) */
+       dlg_update_start(ctrl, dlg);
+       dlg_listbox_clear(ctrl, dlg);
+       for (i = 0; i < CIPHER_MAX; i++) {
+           int c = cfg->ssh_cipherlist[i];
+           int j;
+           char *cstr = NULL;
+           for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
+               if (ciphers[j].c == c) {
+                   cstr = ciphers[j].s;
+                   break;
+               }
+           }
+           dlg_listbox_addwithindex(ctrl, dlg, cstr, c);
+       }
+       dlg_update_done(ctrl, dlg);
+
+    } else if (event == EVENT_VALCHANGE) {
+       int i;
+
+       /* Update array to match the list box. */
+       for (i=0; i < CIPHER_MAX; i++)
+           cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i);
+
+    }
+}
+
+static void printerbox_handler(union control *ctrl, void *dlg,
+                              void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    if (event == EVENT_REFRESH) {
+       int nprinters, i;
+       printer_enum *pe;
+
+       dlg_update_start(ctrl, dlg);
+       dlg_listbox_clear(ctrl, dlg);
+       dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
+       pe = printer_start_enum(&nprinters);
+       for (i = 0; i < nprinters; i++)
+           dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
+       printer_finish_enum(pe);
+       dlg_editbox_set(ctrl, dlg,
+                       (*cfg->printer ? cfg->printer :
+                        PRINTER_DISABLED_STRING));
+       dlg_update_done(ctrl, dlg);
+    } else if (event == EVENT_VALCHANGE) {
+       dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer));
+       if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING))
+           *cfg->printer = '\0';
+    }
+}
+
+static void codepage_handler(union control *ctrl, void *dlg,
+                            void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    if (event == EVENT_REFRESH) {
+       int i;
+       char *cp;
+       dlg_update_start(ctrl, dlg);
+       strcpy(cfg->line_codepage,
+              cp_name(decode_codepage(cfg->line_codepage)));
+       dlg_listbox_clear(ctrl, dlg);
+       for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
+           dlg_listbox_add(ctrl, dlg, cp);
+       dlg_editbox_set(ctrl, dlg, cfg->line_codepage);
+       dlg_update_done(ctrl, dlg);
+    } else if (event == EVENT_VALCHANGE) {
+       dlg_editbox_get(ctrl, dlg, cfg->line_codepage,
+                       sizeof(cfg->line_codepage));
+       strcpy(cfg->line_codepage,
+              cp_name(decode_codepage(cfg->line_codepage)));
+    }
+}
+
+static void sshbug_handler(union control *ctrl, void *dlg,
+                          void *data, int event)
+{
+    if (event == EVENT_REFRESH) {
+       dlg_update_start(ctrl, dlg);
+       dlg_listbox_clear(ctrl, dlg);
+       dlg_listbox_addwithindex(ctrl, dlg, "Auto", AUTO);
+       dlg_listbox_addwithindex(ctrl, dlg, "Off", FORCE_OFF);
+       dlg_listbox_addwithindex(ctrl, dlg, "On", FORCE_ON);
+       switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {
+         case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;
+         case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
+         case FORCE_ON:  dlg_listbox_select(ctrl, dlg, 2); break;
+       }
+       dlg_update_done(ctrl, dlg);
+    } else if (event == EVENT_SELCHANGE) {
+       int i = dlg_listbox_index(ctrl, dlg);
+       if (i < 0)
+           i = AUTO;
+       else
+           i = dlg_listbox_getid(ctrl, dlg, i);
+       *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i;
+    }
+}
+
+struct sessionsaver_data {
+    union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
+    union control *okbutton, *cancelbutton;
+    char savedsession[2048];
+    struct sesslist *sesslist;
+};
+
+/* 
+ * Helper function to load the session selected in the list box, if
+ * any, as this is done in more than one place below. Returns 0 for
+ * failure.
+ */
+static int load_selected_session(struct sessionsaver_data *ssd,
+                                void *dlg, Config *cfg)
+{
+    int i = dlg_listbox_index(ssd->listbox, dlg);
+    int isdef;
+    if (i < 0) {
+       dlg_beep(dlg);
+       return 0;
+    }
+    isdef = !strcmp(ssd->sesslist->sessions[i], "Default Settings");
+    load_settings(ssd->sesslist->sessions[i], !isdef, cfg);
+    if (!isdef) {
+       strncpy(ssd->savedsession, ssd->sesslist->sessions[i],
+               sizeof(ssd->savedsession));
+       ssd->savedsession[sizeof(ssd->savedsession)-1] = '\0';
+    } else {
+       ssd->savedsession[0] = '\0';
+    }
+    dlg_refresh(NULL, dlg);
+    /* Restore the selection, which might have been clobbered by
+     * changing the value of the edit box. */
+    dlg_listbox_select(ssd->listbox, dlg, i);
+    return 1;
+}
+
+static void sessionsaver_handler(union control *ctrl, void *dlg,
+                                void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    struct sessionsaver_data *ssd =
+       (struct sessionsaver_data *)ctrl->generic.context.p;
+
+    if (event == EVENT_REFRESH) {
+       if (ctrl == ssd->editbox) {
+           dlg_editbox_set(ctrl, dlg, ssd->savedsession);
+       } else if (ctrl == ssd->listbox) {
+           int i;
+           dlg_update_start(ctrl, dlg);
+           dlg_listbox_clear(ctrl, dlg);
+           for (i = 0; i < ssd->sesslist->nsessions; i++)
+               dlg_listbox_add(ctrl, dlg, ssd->sesslist->sessions[i]);
+           dlg_update_done(ctrl, dlg);
+       }
+    } else if (event == EVENT_VALCHANGE) {
+       if (ctrl == ssd->editbox) {
+           dlg_editbox_get(ctrl, dlg, ssd->savedsession,
+                           sizeof(ssd->savedsession));
+       }
+    } else if (event == EVENT_ACTION) {
+       if (ctrl == ssd->listbox || ctrl == ssd->loadbutton) {
+           /*
+            * The user has double-clicked a session, or hit Load.
+            * We must load the selected session, and then
+            * terminate the configuration dialog _if_ there was a
+            * double-click on the list box _and_ that session
+            * contains a hostname.
+            */
+           if (load_selected_session(ssd, dlg, cfg) &&
+               (ctrl == ssd->listbox && cfg->host[0])) {
+               dlg_end(dlg, 1);       /* it's all over, and succeeded */
+           }
+       } else if (ctrl == ssd->savebutton) {
+           int isdef = !strcmp(ssd->savedsession, "Default Settings");
+           if (!ssd->savedsession[0]) {
+               int i = dlg_listbox_index(ctrl, dlg);
+               if (i < 0) {
+                   dlg_beep(dlg);
+                   return;
+               }
+               isdef = !strcmp(ssd->sesslist->sessions[i], "Default Settings");
+               if (!isdef) {
+                   strncpy(ssd->savedsession, ssd->sesslist->sessions[i],
+                           sizeof(ssd->savedsession));
+                   ssd->savedsession[sizeof(ssd->savedsession)-1] = '\0';
+               } else {
+                   ssd->savedsession[0] = '\0';
+               }
+           }
+           save_settings(ssd->savedsession, isdef, cfg);
+           get_sesslist(ssd->sesslist, FALSE);
+           get_sesslist(ssd->sesslist, TRUE);
+           dlg_refresh(ssd->editbox, dlg);
+           dlg_refresh(ssd->listbox, dlg);
+       } else if (ctrl == ssd->delbutton) {
+           int i = dlg_listbox_index(ctrl, dlg);
+           if (i <= 0) {
+               dlg_beep(dlg);
+           } else {
+               del_settings(ssd->sesslist->sessions[i]);
+               get_sesslist(ssd->sesslist, FALSE);
+               get_sesslist(ssd->sesslist, TRUE);
+               dlg_refresh(ssd->listbox, dlg);
+           }
+       } else if (ctrl == ssd->okbutton) {
+           /*
+            * Annoying special case. If the `Open' button is
+            * pressed while no host name is currently set, _and_
+            * the session list previously had the focus, _and_
+            * there was a session selected in that which had a
+            * valid host name in it, then load it and go.
+            */
+           if (dlg_last_focused(dlg) == ssd->listbox && !*cfg->host) {
+               Config cfg2;
+               if (!load_selected_session(ssd, dlg, &cfg2)) {
+                   dlg_beep(dlg);
+                   return;
+               }
+               /* If at this point we have a valid session, go! */
+               if (*cfg2.host) {
+                   *cfg = cfg2;       /* structure copy */
+                   dlg_end(dlg, 1);
+               } else
+                   dlg_beep(dlg);
+           }
+
+           /*
+            * Otherwise, do the normal thing: if we have a valid
+            * session, get going.
+            */
+           if (*cfg->host) {
+               dlg_end(dlg, 1);
+           } else
+               dlg_beep(dlg);
+       } else if (ctrl == ssd->cancelbutton) {
+           dlg_end(dlg, 0);
+       }
+    }
+}
+
+struct charclass_data {
+    union control *listbox, *editbox, *button;
+};
+
+static void charclass_handler(union control *ctrl, void *dlg,
+                             void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    struct charclass_data *ccd =
+       (struct charclass_data *)ctrl->generic.context.p;
+
+    if (event == EVENT_REFRESH) {
+       if (ctrl == ccd->listbox) {
+           int i;
+           dlg_update_start(ctrl, dlg);
+           dlg_listbox_clear(ctrl, dlg);
+           for (i = 0; i < 128; i++) {
+               char str[100];
+               sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
+                       (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]);
+               dlg_listbox_add(ctrl, dlg, str);
+           }
+           dlg_update_done(ctrl, dlg);
+       }
+    } else if (event == EVENT_ACTION) {
+       if (ctrl == ccd->button) {
+           char str[100];
+           int i, n;
+           dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str));
+           n = atoi(str);
+           for (i = 0; i < 128; i++) {
+               if (dlg_listbox_issel(ccd->listbox, dlg, i))
+                   cfg->wordness[i] = n;
+           }
+           dlg_refresh(ccd->listbox, dlg);
+       }
+    }
+}
+
+struct colour_data {
+    union control *listbox, *rgbtext, *button;
+};
+
+static const char *const colours[] = {
+    "Default Foreground", "Default Bold Foreground",
+    "Default Background", "Default Bold Background",
+    "Cursor Text", "Cursor Colour",
+    "ANSI Black", "ANSI Black Bold",
+    "ANSI Red", "ANSI Red Bold",
+    "ANSI Green", "ANSI Green Bold",
+    "ANSI Yellow", "ANSI Yellow Bold",
+    "ANSI Blue", "ANSI Blue Bold",
+    "ANSI Magenta", "ANSI Magenta Bold",
+    "ANSI Cyan", "ANSI Cyan Bold",
+    "ANSI White", "ANSI White Bold"
+};
+
+static void colour_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    struct colour_data *cd =
+       (struct colour_data *)ctrl->generic.context.p;
+    int update = FALSE, r, g, b;
+
+    if (event == EVENT_REFRESH) {
+       if (ctrl == cd->listbox) {
+           int i;
+           dlg_update_start(ctrl, dlg);
+           dlg_listbox_clear(ctrl, dlg);
+           for (i = 0; i < lenof(colours); i++)
+               dlg_listbox_add(ctrl, dlg, colours[i]);
+           dlg_update_done(ctrl, dlg);
+           dlg_text_set(cd->rgbtext, dlg, "");
+       }
+    } else if (event == EVENT_SELCHANGE) {
+       if (ctrl == cd->listbox) {
+           /* The user has selected a colour. Update the RGB text. */
+           int i = dlg_listbox_index(ctrl, dlg);
+           if (i < 0) {
+               dlg_beep(dlg);
+               return;
+           }
+           r = cfg->colours[i][0];
+           g = cfg->colours[i][1];
+           b = cfg->colours[i][2];
+           update = TRUE;
+       }
+    } else if (event == EVENT_ACTION) {
+       if (ctrl == cd->button) {
+           int i = dlg_listbox_index(cd->listbox, dlg);
+           if (i < 0) {
+               dlg_beep(dlg);
+               return;
+           }
+           /*
+            * Start a colour selector, which will send us an
+            * EVENT_CALLBACK when it's finished and allow us to
+            * pick up the results.
+            */
+           dlg_coloursel_start(ctrl, dlg,
+                               cfg->colours[i][0],
+                               cfg->colours[i][1],
+                               cfg->colours[i][2]);
+       }
+    } else if (event == EVENT_CALLBACK) {
+       if (ctrl == cd->button) {
+           int i = dlg_listbox_index(cd->listbox, dlg);
+           /*
+            * Collect the results of the colour selector. Will
+            * return nonzero on success, or zero if the colour
+            * selector did nothing (user hit Cancel, for example).
+            */
+           if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
+               cfg->colours[i][0] = r;
+               cfg->colours[i][1] = g;
+               cfg->colours[i][2] = b;
+               update = TRUE;
+           }
+       }
+    }
+
+    if (update) {
+       char buf[40];
+       sprintf(buf, "%02x/%02x/%02x", r, g, b);
+       dlg_text_set(cd->rgbtext, dlg, buf);
+    }
+}
+
+struct environ_data {
+    union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
+};
+
+static void environ_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    struct environ_data *ed =
+       (struct environ_data *)ctrl->generic.context.p;
+
+    if (event == EVENT_REFRESH) {
+       if (ctrl == ed->listbox) {
+           char *p = cfg->environmt;
+           dlg_update_start(ctrl, dlg);
+           dlg_listbox_clear(ctrl, dlg);
+           while (*p) {
+               dlg_listbox_add(ctrl, dlg, p);
+               p += strlen(p) + 1;
+           }
+           dlg_update_done(ctrl, dlg);
+       }
+    } else if (event == EVENT_ACTION) {
+       if (ctrl == ed->addbutton) {
+           char str[sizeof(cfg->environmt)];
+           char *p;
+           dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);
+           if (!*str) {
+               dlg_beep(dlg);
+               return;
+           }
+           p = str + strlen(str);
+           *p++ = '\t';
+           dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));
+           if (!*p) {
+               dlg_beep(dlg);
+               return;
+           }
+           p = cfg->environmt;
+           while (*p) {
+               while (*p)
+                   p++;
+               p++;
+           }
+           if ((p - cfg->environmt) + strlen(str) + 2 <
+               sizeof(cfg->environmt)) {
+               strcpy(p, str);
+               p[strlen(str) + 1] = '\0';
+               dlg_listbox_add(ed->listbox, dlg, str);
+               dlg_editbox_set(ed->varbox, dlg, "");
+               dlg_editbox_set(ed->valbox, dlg, "");
+           } else {
+               dlg_error_msg(dlg, "Environment too big");
+           }
+       } else if (ctrl == ed->rembutton) {
+           int i = dlg_listbox_index(ed->listbox, dlg);
+           if (i < 0) {
+               dlg_beep(dlg);
+           } else {
+               char *p, *q;
+
+               dlg_listbox_del(ed->listbox, dlg, i);
+               p = cfg->environmt;
+               while (i > 0) {
+                   if (!*p)
+                       goto disaster;
+                   while (*p)
+                       p++;
+                   p++;
+                   i--;
+               }
+               q = p;
+               if (!*p)
+                   goto disaster;
+               while (*p)
+                   p++;
+               p++;
+               while (*p) {
+                   while (*p)
+                       *q++ = *p++;
+                   *q++ = *p++;
+               }
+               *q = '\0';
+               disaster:;
+           }
+       }
+    }
+}
+
+struct portfwd_data {
+    union control *addbutton, *rembutton, *listbox;
+    union control *sourcebox, *destbox, *direction;
+};
+
+static void portfwd_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    struct portfwd_data *pfd =
+       (struct portfwd_data *)ctrl->generic.context.p;
+
+    if (event == EVENT_REFRESH) {
+       if (ctrl == pfd->listbox) {
+           char *p = cfg->portfwd;
+           dlg_update_start(ctrl, dlg);
+           dlg_listbox_clear(ctrl, dlg);
+           while (*p) {
+               dlg_listbox_add(ctrl, dlg, p);
+               p += strlen(p) + 1;
+           }
+           dlg_update_done(ctrl, dlg);
+       }
+    } else if (event == EVENT_ACTION) {
+       if (ctrl == pfd->addbutton) {
+           char str[sizeof(cfg->portfwd)];
+           char *p;
+           if (dlg_radiobutton_get(pfd->direction, dlg) == 0)
+               str[0] = 'L';
+           else
+               str[0] = 'R';
+           dlg_editbox_get(pfd->sourcebox, dlg, str+1, sizeof(str) - 2);
+           if (!str[1]) {
+               dlg_error_msg(dlg, "You need to specify a source port number");
+               return;
+           }
+           p = str + strlen(str);
+           *p++ = '\t';
+           dlg_editbox_get(pfd->destbox, dlg, p, sizeof(str)-1 - (p - str));
+           if (!*p || !strchr(p, ':')) {
+               dlg_error_msg(dlg,
+                             "You need to specify a destination address\n"
+                             "in the form \"host.name:port\"");
+               return;
+           }
+           p = cfg->portfwd;
+           while (*p) {
+               while (*p)
+                   p++;
+               p++;
+           }
+           if ((p - cfg->portfwd) + strlen(str) + 2 <
+               sizeof(cfg->portfwd)) {
+               strcpy(p, str);
+               p[strlen(str) + 1] = '\0';
+               dlg_listbox_add(pfd->listbox, dlg, str);
+               dlg_editbox_set(pfd->sourcebox, dlg, "");
+               dlg_editbox_set(pfd->destbox, dlg, "");
+           } else {
+               dlg_error_msg(dlg, "Too many forwardings");
+           }
+       } else if (ctrl == pfd->rembutton) {
+           int i = dlg_listbox_index(pfd->listbox, dlg);
+           if (i < 0)
+               dlg_beep(dlg);
+           else {
+               char *p, *q;
+
+               dlg_listbox_del(pfd->listbox, dlg, i);
+               p = cfg->portfwd;
+               while (i > 0) {
+                   if (!*p)
+                       goto disaster2;
+                   while (*p)
+                       p++;
+                   p++;
+                   i--;
+               }
+               q = p;
+               if (!*p)
+                   goto disaster2;
+               while (*p)
+                   p++;
+               p++;
+               while (*p) {
+                   while (*p)
+                       *q++ = *p++;
+                   *q++ = *p++;
+               }
+               *q = '\0';
+               disaster2:;
+           }
+       }
+    }
+}
+
+void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
+                     int midsession, int protocol)
+{
+    struct controlset *s;
+    struct sessionsaver_data *ssd;
+    struct charclass_data *ccd;
+    struct colour_data *cd;
+    struct environ_data *ed;
+    struct portfwd_data *pfd;
+    union control *c;
+
+    ssd = (struct sessionsaver_data *)
+       ctrl_alloc(b, sizeof(struct sessionsaver_data));
+    ssd->sesslist = (midsession ? NULL : sesslist);
+
+    /*
+     * The standard panel that appears at the bottom of all panels:
+     * Open, Cancel, Apply etc.
+     */
+    s = ctrl_getset(b, "", "", "");
+    ctrl_columns(s, 5, 20, 20, 20, 20, 20);
+    ssd->okbutton = ctrl_pushbutton(s,
+                                   (midsession ? "Apply" : "Open"),
+                                   (char)(midsession ? 'a' : 'o'),
+                                   HELPCTX(no_help),
+                                   sessionsaver_handler, P(ssd));
+    ssd->okbutton->button.isdefault = TRUE;
+    ssd->okbutton->generic.column = 3;
+    ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
+                                       sessionsaver_handler, P(ssd));
+    ssd->cancelbutton->generic.column = 4;
+    /* We carefully don't close the 5-column part, so that platform-
+     * specific add-ons can put extra buttons alongside Open and Cancel. */
+
+    /*
+     * The Session panel.
+     */
+    ctrl_settitle(b, "Session", "Basic options for your PuTTY session");
+
+    if (!midsession) {
+       s = ctrl_getset(b, "Session", "hostport",
+                       "Specify your connection by host name or IP address");
+       ctrl_columns(s, 2, 75, 25);
+       c = ctrl_editbox(s, "Host Name (or IP address)", 'n', 100,
+                        HELPCTX(session_hostname),
+                        dlg_stdeditbox_handler, I(offsetof(Config,host)),
+                        I(sizeof(((Config *)0)->host)));
+       c->generic.column = 0;
+       c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(session_hostname),
+                        dlg_stdeditbox_handler,
+                        I(offsetof(Config,port)), I(-1));
+       c->generic.column = 1;
+       ctrl_columns(s, 1, 100);
+       if (backends[3].backend == NULL) {
+           ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
+                             HELPCTX(session_hostname),
+                             protocolbuttons_handler, P(c),
+                             "Raw", 'r', I(PROT_RAW),
+                             "Telnet", 't', I(PROT_TELNET),
+                             "Rlogin", 'i', I(PROT_RLOGIN),
+                             NULL);
+       } else {
+           ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 4,
+                             HELPCTX(session_hostname),
+                             protocolbuttons_handler, P(c),
+                             "Raw", 'r', I(PROT_RAW),
+                             "Telnet", 't', I(PROT_TELNET),
+                             "Rlogin", 'i', I(PROT_RLOGIN),
+                             "SSH", 's', I(PROT_SSH),
+                             NULL);
+       }
+
+       s = ctrl_getset(b, "Session", "savedsessions",
+                       "Load, save or delete a stored session");
+       ctrl_columns(s, 2, 75, 25);
+       ssd->savedsession[0] = '\0';
+       ssd->sesslist = sesslist;
+       ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
+                                   HELPCTX(session_saved),
+                                   sessionsaver_handler, P(ssd), P(NULL));
+       ssd->editbox->generic.column = 0;
+       /* Reset columns so that the buttons are alongside the list, rather
+        * than alongside that edit box. */
+       ctrl_columns(s, 1, 100);
+       ctrl_columns(s, 2, 75, 25);
+       ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
+                                         HELPCTX(session_saved),
+                                         sessionsaver_handler, P(ssd));
+       ssd->loadbutton->generic.column = 1;
+       ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
+                                         HELPCTX(session_saved),
+                                         sessionsaver_handler, P(ssd));
+       ssd->savebutton->generic.column = 1;
+       ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
+                                        HELPCTX(session_saved),
+                                        sessionsaver_handler, P(ssd));
+       ssd->delbutton->generic.column = 1;
+       ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+                                   HELPCTX(session_saved),
+                                   sessionsaver_handler, P(ssd));
+       ssd->listbox->generic.column = 0;
+       ssd->listbox->listbox.height = 7;
+       ctrl_columns(s, 1, 100);
+    }
+
+    s = ctrl_getset(b, "Session", "otheropts", NULL);
+    c = ctrl_radiobuttons(s, "Close window on exit:", 'w', 4,
+                         HELPCTX(session_coe),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, close_on_exit)),
+                         "Always", I(FORCE_ON),
+                         "Never", I(FORCE_OFF),
+                         "Only on clean exit", I(AUTO), NULL);
+
+    /*
+     * The Session/Logging panel.
+     */
+    ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
+
+    s = ctrl_getset(b, "Session/Logging", "main", NULL);
+    ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 1,
+                     HELPCTX(logging_main),
+                     dlg_stdradiobutton_handler, I(offsetof(Config, logtype)),
+                     "Logging turned off completely", 't', I(LGTYP_NONE),
+                     "Log printable output only", 'p', I(LGTYP_ASCII),
+                     "Log all session output", 'l', I(LGTYP_DEBUG),
+                     "Log SSH packet data", 's', I(LGTYP_PACKETS),
+                     NULL);
+    ctrl_filesel(s, "Log file name:", 'f',
+                NULL, TRUE, "Select session log file name",
+                HELPCTX(logging_filename),
+                dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
+    ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
+             " &T for time, and &H for host name)",
+             HELPCTX(logging_filename));
+    ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
+                     HELPCTX(logging_exists),
+                     dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
+                     "Always overwrite it", I(LGXF_OVR),
+                     "Always append to the end of it", I(LGXF_APN),
+                     "Ask the user every time", I(LGXF_ASK), NULL);
+
+    /*
+     * The Terminal panel.
+     */
+    ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
+
+    s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
+    ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
+                 HELPCTX(terminal_autowrap),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
+    ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
+                 HELPCTX(terminal_decom),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
+    ctrl_checkbox(s, "Implicit CR in every LF", 'r',
+                 HELPCTX(terminal_lfhascr),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
+    ctrl_checkbox(s, "Use background colour to erase screen", 'e',
+                 HELPCTX(terminal_bce),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
+    ctrl_checkbox(s, "Enable blinking text", 'n',
+                 HELPCTX(terminal_blink),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
+    ctrl_editbox(s, "Answerback to ^E:", 's', 100,
+                HELPCTX(terminal_answerback),
+                dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
+                I(sizeof(((Config *)0)->answerback)));
+
+    s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
+    ctrl_radiobuttons(s, "Local echo:", 'l', 3,
+                     HELPCTX(terminal_localecho),
+                     dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
+                     "Auto", I(AUTO),
+                     "Force on", I(FORCE_ON),
+                     "Force off", I(FORCE_OFF), NULL);
+    ctrl_radiobuttons(s, "Local line editing:", 't', 3,
+                     HELPCTX(terminal_localedit),
+                     dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
+                     "Auto", I(AUTO),
+                     "Force on", I(FORCE_ON),
+                     "Force off", I(FORCE_OFF), NULL);
+
+    s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
+    ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
+                 HELPCTX(terminal_printing),
+                 printerbox_handler, P(NULL), P(NULL));
+
+    /*
+     * The Terminal/Keyboard panel.
+     */
+    ctrl_settitle(b, "Terminal/Keyboard",
+                 "Options controlling the effects of keys");
+
+    s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
+                   "Change the sequences sent by:");
+    ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
+                     HELPCTX(keyboard_backspace),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, bksp_is_delete)),
+                     "Control-H", I(0), "Control-? (127)", I(1), NULL);
+    ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
+                     HELPCTX(keyboard_homeend),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, rxvt_homeend)),
+                     "Standard", I(0), "rxvt", I(1), NULL);
+    ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
+                     HELPCTX(keyboard_funkeys),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, funky_type)),
+                     "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
+                     "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
+
+    s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
+                   "Application keypad settings:");
+    ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
+                     HELPCTX(keyboard_appcursor),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, app_cursor)),
+                     "Normal", I(0), "Application", I(1), NULL);
+    ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
+                     HELPCTX(keyboard_appkeypad),
+                     numeric_keypad_handler, P(NULL),
+                     "Normal", I(0), "Application", I(1), "NetHack", I(2),
+                     NULL);
+
+    /*
+     * The Terminal/Bell panel.
+     */
+    ctrl_settitle(b, "Terminal/Bell",
+                 "Options controlling the terminal bell");
+
+    s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
+    ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
+                     HELPCTX(bell_style),
+                     dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
+                     "None (bell disabled)", I(BELL_DISABLED),
+                     "Make default system alert sound", I(BELL_DEFAULT),
+                     "Visual bell (flash window)", I(BELL_VISUAL), NULL);
+
+    s = ctrl_getset(b, "Terminal/Bell", "overload",
+                   "Control the bell overload behaviour");
+    ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
+                 HELPCTX(bell_overload),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
+    ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
+                HELPCTX(bell_overload),
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
+    ctrl_editbox(s, "... in this many seconds", 't', 20,
+                HELPCTX(bell_overload),
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
+                I(-1000));
+    ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
+             HELPCTX(bell_overload));
+    ctrl_editbox(s, "Seconds of silence required", 's', 20,
+                HELPCTX(bell_overload),
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
+                I(-1000));
+
+    /*
+     * The Terminal/Features panel.
+     */
+    ctrl_settitle(b, "Terminal/Features",
+                 "Enabling and disabling advanced terminal features");
+
+    s = ctrl_getset(b, "Terminal/Features", "main", NULL);
+    ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
+                 HELPCTX(features_application),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
+    ctrl_checkbox(s, "Disable application keypad mode", 'k',
+                 HELPCTX(features_application),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
+    ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
+                 HELPCTX(features_mouse),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
+    ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
+                 HELPCTX(features_resize),
+                 dlg_stdcheckbox_handler,
+                 I(offsetof(Config,no_remote_resize)));
+    ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
+                 HELPCTX(features_altscreen),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
+    ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
+                 HELPCTX(features_retitle),
+                 dlg_stdcheckbox_handler,
+                 I(offsetof(Config,no_remote_wintitle)));
+    ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
+                 HELPCTX(features_dbackspace),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
+    ctrl_checkbox(s, "Disable remote-controlled character set configuration",
+                 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
+                 I(offsetof(Config,no_remote_charset)));
+
+    /*
+     * The Window panel.
+     */
+    ctrl_settitle(b, "Window", "Options controlling PuTTY's window");
+
+    s = ctrl_getset(b, "Window", "size", "Set the size of the window");
+    ctrl_columns(s, 2, 50, 50);
+    c = ctrl_editbox(s, "Rows", 'r', 100,
+                    HELPCTX(window_size),
+                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
+    c->generic.column = 0;
+    c = ctrl_editbox(s, "Columns", 'm', 100,
+                    HELPCTX(window_size),
+                    dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
+    c->generic.column = 1;
+    ctrl_columns(s, 1, 100);
+
+    s = ctrl_getset(b, "Window", "scrollback",
+                   "Control the scrollback in the window");
+    ctrl_editbox(s, "Lines of scrollback", 's', 50,
+                HELPCTX(window_scrollback),
+                dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
+    ctrl_checkbox(s, "Display scrollbar", 'd',
+                 HELPCTX(window_scrollback),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
+    ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i',
+                 HELPCTX(window_scrollback),
+                 dlg_stdcheckbox_handler,
+                 I(offsetof(Config,scrollbar_in_fullscreen)));
+    ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
+                 HELPCTX(window_scrollback),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
+    ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
+                 HELPCTX(window_scrollback),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
+
+    /*
+     * The Window/Appearance panel.
+     */
+    ctrl_settitle(b, "Window/Appearance",
+                 "Configure the appearance of PuTTY's window");
+
+    s = ctrl_getset(b, "Window/Appearance", "cursor",
+                   "Adjust the use of the cursor");
+    ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
+                     HELPCTX(appearance_cursor),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, cursor_type)),
+                     "Block", 'l', I(0),
+                     "Underline", 'u', I(1),
+                     "Vertical line", 'v', I(2), NULL);
+    ctrl_checkbox(s, "Cursor blinks", 'b',
+                 HELPCTX(appearance_cursor),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
+
+    s = ctrl_getset(b, "Window/Appearance", "font",
+                   "Font settings");
+    ctrl_fontsel(s, "Font used in the terminal window", 'n',
+                HELPCTX(appearance_font),
+                dlg_stdfontsel_handler, I(offsetof(Config, font)));
+
+    s = ctrl_getset(b, "Window/Appearance", "mouse",
+                   "Adjust the use of the mouse pointer");
+    ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
+                 HELPCTX(appearance_hidemouse),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
+
+    s = ctrl_getset(b, "Window/Appearance", "border",
+                   "Adjust the window border");
+    ctrl_editbox(s, "Gap between text and window edge:", NO_SHORTCUT, 20,
+                HELPCTX(appearance_border),
+                dlg_stdeditbox_handler,
+                I(offsetof(Config,window_border)), I(-1));
+
+    /*
+     * The Window/Behaviour panel.
+     */
+    ctrl_settitle(b, "Window/Behaviour",
+                 "Configure the behaviour of PuTTY's window");
+
+    s = ctrl_getset(b, "Window/Behaviour", "title",
+                   "Adjust the behaviour of the window title");
+    ctrl_editbox(s, "Window title:", 't', 100,
+                HELPCTX(appearance_title),
+                dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
+                I(sizeof(((Config *)0)->wintitle)));
+    ctrl_checkbox(s, "Separate window and icon titles", 'i',
+                 HELPCTX(appearance_title),
+                 dlg_stdcheckbox_handler,
+                 I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
+
+    s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
+    ctrl_checkbox(s, "Warn before closing window", 'w',
+                 HELPCTX(behaviour_closewarn),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
+
+    /*
+     * The Window/Translation panel.
+     */
+    ctrl_settitle(b, "Window/Translation",
+                 "Options controlling character set translation");
+
+    s = ctrl_getset(b, "Window/Translation", "trans",
+                   "Character set translation on received data");
+    ctrl_combobox(s, "Received data assumed to be in which character set:",
+                 'r', 100, HELPCTX(translation_codepage),
+                 codepage_handler, P(NULL), P(NULL));
+
+    s = ctrl_getset(b, "Window/Translation", "linedraw",
+                   "Adjust how PuTTY displays line drawing characters");
+    ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
+                     HELPCTX(translation_linedraw),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, vtmode)),
+                     "Font has XWindows encoding", 'x', I(VT_XWINDOWS),
+                     "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
+                     "Unicode mode", 'u', I(VT_UNICODE), NULL);
+
+    /*
+     * The Window/Selection panel.
+     */
+    ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
+
+    s = ctrl_getset(b, "Window/Selection", "trans",
+                   "Translation of pasted characters");
+    ctrl_checkbox(s, "Don't translate line drawing chars into +, - and |",'d',
+                 HELPCTX(selection_linedraw),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
+       
+    s = ctrl_getset(b, "Window/Selection", "mouse",
+                   "Control use of mouse");
+    ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
+                 HELPCTX(selection_shiftdrag),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
+    ctrl_radiobuttons(s,
+                     "Default selection mode (Alt+drag does the other one):",
+                     NO_SHORTCUT, 2,
+                     HELPCTX(selection_rect),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, rect_select)),
+                     "Normal", 'n', I(0),
+                     "Rectangular block", 'r', I(1), NULL);
+
+    s = ctrl_getset(b, "Window/Selection", "charclass",
+                   "Control the select-one-word-at-a-time mode");
+    ccd = (struct charclass_data *)
+       ctrl_alloc(b, sizeof(struct charclass_data));
+    ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
+                               HELPCTX(selection_charclasses),
+                               charclass_handler, P(ccd));
+    ccd->listbox->listbox.multisel = 1;
+    ccd->listbox->listbox.ncols = 4;
+    ccd->listbox->listbox.percentages = smalloc(4*sizeof(int));
+    ccd->listbox->listbox.percentages[0] = 15;
+    ccd->listbox->listbox.percentages[1] = 25;
+    ccd->listbox->listbox.percentages[2] = 20;
+    ccd->listbox->listbox.percentages[3] = 40;
+    ctrl_columns(s, 2, 67, 33);
+    ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
+                               HELPCTX(selection_charclasses),
+                               charclass_handler, P(ccd), P(NULL));
+    ccd->editbox->generic.column = 0;
+    ccd->button = ctrl_pushbutton(s, "Set", 's',
+                                 HELPCTX(selection_charclasses),
+                                 charclass_handler, P(ccd));
+    ccd->button->generic.column = 1;
+    ctrl_columns(s, 1, 100);
+
+    /*
+     * The Window/Colours panel.
+     */
+    ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
+
+    s = ctrl_getset(b, "Window/Colours", "general",
+                   "General options for colour usage");
+    ctrl_checkbox(s, "Bolded text is a different colour", 'b',
+                 HELPCTX(colours_bold),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
+
+    s = ctrl_getset(b, "Window/Colours", "adjust",
+                   "Adjust the precise colours PuTTY displays");
+    ctrl_text(s, "Select a colour from the list, and then click the"
+             " Modify button to change its appearance.",
+             HELPCTX(colours_config));
+    ctrl_columns(s, 2, 67, 33);
+    cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
+    cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
+                              HELPCTX(colours_config), colour_handler, P(cd));
+    cd->listbox->generic.column = 0;
+    c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
+    c->generic.column = 1;
+    cd->rgbtext = ctrl_text(s, "00/00/00", HELPCTX(colours_config));
+    cd->rgbtext->generic.column = 1;
+    cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
+                                colour_handler, P(cd));
+    cd->button->generic.column = 1;
+    ctrl_columns(s, 1, 100);
+
+    /*
+     * The Connection panel.
+     */
+    ctrl_settitle(b, "Connection", "Options controlling the connection");
+
+    if (!midsession) {
+       s = ctrl_getset(b, "Connection", "data", "Data to send to the server");
+       ctrl_editbox(s, "Terminal-type string", 't', 50,
+                    HELPCTX(connection_termtype),
+                    dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
+                    I(sizeof(((Config *)0)->termtype)));
+       ctrl_editbox(s, "Auto-login username", 'u', 50,
+                    HELPCTX(connection_username),
+                    dlg_stdeditbox_handler, I(offsetof(Config,username)),
+                    I(sizeof(((Config *)0)->username)));
+    }
+
+    s = ctrl_getset(b, "Connection", "keepalive",
+                   "Sending of null packets to keep session active");
+    ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
+                HELPCTX(connection_keepalive),
+                dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
+                I(-1));
+
+    if (!midsession) {
+       s = ctrl_getset(b, "Connection", "tcp",
+                       "Low-level TCP connection options");
+       ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n',
+                     HELPCTX(connection_nodelay),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,tcp_nodelay)));
+    }
+
+    if (!midsession) {
+       /*
+        * The Connection/Proxy panel.
+        */
+       ctrl_settitle(b, "Connection/Proxy",
+                     "Options controlling proxy usage");
+
+       s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics");
+       ctrl_radiobuttons(s, "Proxy type:", NO_SHORTCUT, 4,
+                         HELPCTX(proxy_type),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, proxy_type)),
+                         "None", 'n', I(PROXY_NONE),
+                         "HTTP", 't', I(PROXY_HTTP),
+                         "SOCKS", 's', I(PROXY_SOCKS),
+                         "Telnet", 'l', I(PROXY_TELNET),
+                         NULL);
+       ctrl_columns(s, 2, 80, 20);
+       c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
+                        HELPCTX(proxy_main),
+                        dlg_stdeditbox_handler,
+                        I(offsetof(Config,proxy_host)),
+                        I(sizeof(((Config *)0)->proxy_host)));
+       c->generic.column = 0;
+       c = ctrl_editbox(s, "Port", 'p', 100,
+                        HELPCTX(proxy_main),
+                        dlg_stdeditbox_handler,
+                        I(offsetof(Config,proxy_port)),
+                        I(-1));
+       c->generic.column = 1;
+       ctrl_columns(s, 1, 100);
+       ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
+                    HELPCTX(proxy_exclude),
+                    dlg_stdeditbox_handler,
+                    I(offsetof(Config,proxy_exclude_list)),
+                    I(sizeof(((Config *)0)->proxy_exclude_list)));
+       ctrl_checkbox(s, "Consider proxying local host connections", 'x',
+                     HELPCTX(proxy_exclude),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,even_proxy_localhost)));
+       ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
+                         HELPCTX(proxy_dns),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, proxy_dns)),
+                         "No", I(FORCE_OFF),
+                         "Auto", I(AUTO),
+                         "Yes", I(FORCE_ON), NULL);
+       ctrl_editbox(s, "Username", 'u', 60,
+                    HELPCTX(proxy_auth),
+                    dlg_stdeditbox_handler,
+                    I(offsetof(Config,proxy_username)),
+                    I(sizeof(((Config *)0)->proxy_username)));
+       c = ctrl_editbox(s, "Password", 'w', 60,
+                        HELPCTX(proxy_auth),
+                        dlg_stdeditbox_handler,
+                        I(offsetof(Config,proxy_password)),
+                        I(sizeof(((Config *)0)->proxy_password)));
+       c->editbox.password = 1;
+
+       s = ctrl_getset(b, "Connection/Proxy", "misc",
+                       "Miscellaneous proxy settings");
+       ctrl_editbox(s, "Telnet command", 'm', 100,
+                    HELPCTX(proxy_command),
+                    dlg_stdeditbox_handler,
+                    I(offsetof(Config,proxy_telnet_command)),
+                    I(sizeof(((Config *)0)->proxy_telnet_command)));
+       ctrl_radiobuttons(s, "SOCKS Version", 'v', 2,
+                         HELPCTX(proxy_socksver),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, proxy_socks_version)),
+                         "Version 5", I(5), "Version 4", I(4), NULL);
+    }
+
+    /*
+     * The Telnet panel exists in the base config box, and in a
+     * mid-session reconfig box _if_ we're using Telnet.
+     */
+    if (!midsession || protocol == PROT_TELNET) {
+       /*
+        * The Connection/Telnet panel.
+        */
+       ctrl_settitle(b, "Connection/Telnet",
+                     "Options controlling Telnet connections");
+
+       if (!midsession) {
+           s = ctrl_getset(b, "Connection/Telnet", "data",
+                           "Data to send to the server");
+           ctrl_editbox(s, "Terminal-speed string", 's', 50,
+                        HELPCTX(telnet_termspeed),
+                        dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
+                        I(sizeof(((Config *)0)->termspeed)));
+           ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
+           ctrl_columns(s, 2, 80, 20);
+           ed = (struct environ_data *)
+               ctrl_alloc(b, sizeof(struct environ_data));
+           ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
+                                     HELPCTX(telnet_environ),
+                                     environ_handler, P(ed), P(NULL));
+           ed->varbox->generic.column = 0;
+           ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
+                                     HELPCTX(telnet_environ),
+                                     environ_handler, P(ed), P(NULL));
+           ed->valbox->generic.column = 0;
+           ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
+                                           HELPCTX(telnet_environ),
+                                           environ_handler, P(ed));
+           ed->addbutton->generic.column = 1;
+           ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
+                                           HELPCTX(telnet_environ),
+                                           environ_handler, P(ed));
+           ed->rembutton->generic.column = 1;
+           ctrl_columns(s, 1, 100);
+           ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+                                      HELPCTX(telnet_environ),
+                                      environ_handler, P(ed));
+           ed->listbox->listbox.height = 3;
+       }
+
+       s = ctrl_getset(b, "Connection/Telnet", "protocol",
+                       "Telnet protocol adjustments");
+
+       if (!midsession) {
+           ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
+                             NO_SHORTCUT, 2,
+                             HELPCTX(telnet_oldenviron),
+                             dlg_stdradiobutton_handler,
+                             I(offsetof(Config, rfc_environ)),
+                             "BSD (commonplace)", 'b', I(0),
+                             "RFC 1408 (unusual)", 'f', I(1), NULL);
+           ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
+                             HELPCTX(telnet_passive),
+                             dlg_stdradiobutton_handler,
+                             I(offsetof(Config, passive_telnet)),
+                             "Passive", I(1), "Active", I(0), NULL);
+       }
+       ctrl_checkbox(s, "Keyboard sends telnet Backspace and Interrupt", 'k',
+                     HELPCTX(telnet_specialkeys),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,telnet_keyboard)));
+       ctrl_checkbox(s, "Return key sends telnet New Line instead of ^M",
+                     NO_SHORTCUT, HELPCTX(telnet_newline),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,telnet_newline)));
+    }
+
+    if (!midsession) {
+
+       /*
+        * The Connection/Rlogin panel.
+        */
+       ctrl_settitle(b, "Connection/Rlogin",
+                     "Options controlling Rlogin connections");
+
+       s = ctrl_getset(b, "Connection/Rlogin", "data",
+                       "Data to send to the server");
+       ctrl_editbox(s, "Terminal-speed string", 's', 50,
+                    HELPCTX(rlogin_termspeed),
+                    dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
+                    I(sizeof(((Config *)0)->termspeed)));
+       ctrl_editbox(s, "Local username:", 'l', 50,
+                    HELPCTX(rlogin_localuser),
+                    dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
+                    I(sizeof(((Config *)0)->localusername)));
+
+    }
+
+    /*
+     * All the SSH stuff is omitted in PuTTYtel.
+     */
+
+    if (!midsession && backends[3].backend != NULL) {
+
+       /*
+        * The Connection/SSH panel.
+        */
+       ctrl_settitle(b, "Connection/SSH",
+                     "Options controlling SSH connections");
+
+       s = ctrl_getset(b, "Connection/SSH", "data",
+                       "Data to send to the server");
+       ctrl_editbox(s, "Remote command:", 'r', 100,
+                    HELPCTX(ssh_command),
+                    dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
+                    I(sizeof(((Config *)0)->remote_cmd)));
+
+       s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
+       ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
+                     HELPCTX(ssh_nopty),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,nopty)));
+       ctrl_checkbox(s, "Enable compression", 'e',
+                     HELPCTX(ssh_compress),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,compression)));
+       ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
+                         HELPCTX(ssh_protocol),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, sshprot)),
+                         "1 only", 'l', I(0),
+                         "1", '1', I(1),
+                         "2", '2', I(2),
+                         "2 only", 'n', I(3), NULL);
+
+       s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
+       ctrl_draglist(s, "Encryption cipher selection policy:", 's',
+                     HELPCTX(ssh_ciphers),
+                     cipherlist_handler, P(NULL));
+       ctrl_checkbox(s, "Enable non-standard use of single-DES in SSH 2", 'i',
+                     HELPCTX(ssh_ciphers),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,ssh2_des_cbc)));
+
+       /*
+        * The Connection/SSH/Auth panel.
+        */
+       ctrl_settitle(b, "Connection/SSH/Auth",
+                     "Options controlling SSH authentication");
+
+       s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
+                       "Authentication methods");
+       ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
+                     HELPCTX(ssh_auth_tis),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,try_tis_auth)));
+       ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
+                     'i', HELPCTX(ssh_auth_ki),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,try_ki_auth)));
+
+       s = ctrl_getset(b, "Connection/SSH/Auth", "params",
+                       "Authentication parameters");
+       ctrl_checkbox(s, "Allow agent forwarding", 'f',
+                     HELPCTX(ssh_auth_agentfwd),
+                     dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
+       ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
+                     HELPCTX(ssh_auth_changeuser),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,change_username)));
+       ctrl_filesel(s, "Private key file for authentication:", 'k',
+                    FILTER_KEY_FILES, FALSE, "Select private key file",
+                    HELPCTX(ssh_auth_privkey),
+                    dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+
+       /*
+        * The Connection/SSH/Tunnels panel.
+        */
+       ctrl_settitle(b, "Connection/SSH/Tunnels",
+                     "Options controlling SSH tunnelling");
+
+       s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
+       ctrl_checkbox(s, "Enable X11 forwarding", 'e',
+                     HELPCTX(ssh_tunnels_x11),
+                     dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
+       ctrl_editbox(s, "X display location", 'x', 50,
+                    HELPCTX(ssh_tunnels_x11),
+                    dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
+                    I(sizeof(((Config *)0)->x11_display)));
+       ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
+                         HELPCTX(ssh_tunnels_x11auth),
+                         dlg_stdradiobutton_handler,
+                         I(offsetof(Config, x11_auth)),
+                         "MIT-Magic-Cookie-1", I(X11_MIT),
+                         "XDM-Authorization-1", I(X11_XDM), NULL);
+
+       s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
+                       "Port forwarding");
+       ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
+                     HELPCTX(ssh_tunnels_portfwd_localhost),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,lport_acceptall)));
+       ctrl_checkbox(s, "Remote ports do the same (SSH v2 only)", 'p',
+                     HELPCTX(ssh_tunnels_portfwd_localhost),
+                     dlg_stdcheckbox_handler,
+                     I(offsetof(Config,rport_acceptall)));
+
+       ctrl_columns(s, 3, 55, 20, 25);
+       c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
+       c->generic.column = COLUMN_FIELD(0,2);
+       /* You want to select from the list, _then_ hit Remove. So tab order
+        * should be that way round. */
+       pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
+       pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
+                                        HELPCTX(ssh_tunnels_portfwd),
+                                        portfwd_handler, P(pfd));
+       pfd->rembutton->generic.column = 2;
+       pfd->rembutton->generic.tabdelay = 1;
+       pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+                                   HELPCTX(ssh_tunnels_portfwd),
+                                   portfwd_handler, P(pfd));
+       pfd->listbox->listbox.height = 3;
+       ctrl_tabdelay(s, pfd->rembutton);
+       ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
+       /* You want to enter source, destination and type, _then_ hit Add.
+        * Again, we adjust the tab order to reflect this. */
+       pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
+                                        HELPCTX(ssh_tunnels_portfwd),
+                                        portfwd_handler, P(pfd));
+       pfd->addbutton->generic.column = 2;
+       pfd->addbutton->generic.tabdelay = 1;
+       pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
+                                     HELPCTX(ssh_tunnels_portfwd),
+                                     portfwd_handler, P(pfd), P(NULL));
+       pfd->sourcebox->generic.column = 0;
+       pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
+                                   HELPCTX(ssh_tunnels_portfwd),
+                                   portfwd_handler, P(pfd), P(NULL));
+       pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
+                                          HELPCTX(ssh_tunnels_portfwd),
+                                          portfwd_handler, P(pfd),
+                                          "Local", 'l', P(NULL),
+                                          "Remote", 'm', P(NULL), NULL);
+       ctrl_tabdelay(s, pfd->addbutton);
+       ctrl_columns(s, 1, 100);
+
+       /*
+        * The Connection/SSH/Bugs panel.
+        */
+       ctrl_settitle(b, "Connection/SSH/Bugs",
+                     "Workarounds for SSH server bugs");
+
+       s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
+                       "Detection of known bugs in SSH servers");
+       ctrl_droplist(s, "Chokes on SSH1 ignore messages", 'i', 20,
+                     HELPCTX(ssh_bugs_ignore1),
+                     sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
+       ctrl_droplist(s, "Refuses all SSH1 password camouflage", 's', 20,
+                     HELPCTX(ssh_bugs_plainpw1),
+                     sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
+       ctrl_droplist(s, "Chokes on SSH1 RSA authentication", 'r', 20,
+                     HELPCTX(ssh_bugs_rsa1),
+                     sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
+       ctrl_droplist(s, "Miscomputes SSH2 HMAC keys", 'm', 20,
+                     HELPCTX(ssh_bugs_hmac2),
+                     sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
+       ctrl_droplist(s, "Miscomputes SSH2 encryption keys", 'e', 20,
+                     HELPCTX(ssh_bugs_derivekey2),
+                     sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
+       ctrl_droplist(s, "Requires padding on SSH2 RSA signatures", 'p', 20,
+                     HELPCTX(ssh_bugs_rsapad2),
+                     sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
+       ctrl_droplist(s, "Chokes on Diffie-Hellman group exchange", 'd', 20,
+                     HELPCTX(ssh_bugs_dhgex2),
+                     sshbug_handler, I(offsetof(Config,sshbug_dhgex2)));
+       ctrl_droplist(s, "Misuses the session ID in PK auth", 'n', 20,
+                     HELPCTX(ssh_bugs_pksessid2),
+                     sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
+    }
+}
diff --git a/dialog.c b/dialog.c
new file mode 100644 (file)
index 0000000..d9d6aaf
--- /dev/null
+++ b/dialog.c
@@ -0,0 +1,587 @@
+/*
+ * dialog.c - a reasonably platform-independent mechanism for
+ * describing dialog boxes.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#define DEFINE_INTORPTR_FNS
+
+#include "putty.h"
+#include "dialog.h"
+
+int ctrl_path_elements(char *path)
+{
+    int i = 1;
+    while (*path) {
+       if (*path == '/') i++;
+       path++;
+    }
+    return i;
+}
+
+/* Return the number of matching path elements at the starts of p1 and p2,
+ * or INT_MAX if the paths are identical. */
+int ctrl_path_compare(char *p1, char *p2)
+{
+    int i = 0;
+    while (*p1 || *p2) {
+       if ((*p1 == '/' || *p1 == '\0') &&
+           (*p2 == '/' || *p2 == '\0'))
+           i++;                       /* a whole element matches, ooh */
+       if (*p1 != *p2)
+           return i;                  /* mismatch */
+       p1++, p2++;
+    }
+    return INT_MAX;                   /* exact match */
+}
+
+struct controlbox *ctrl_new_box(void)
+{
+    struct controlbox *ret = smalloc(sizeof(struct controlbox));
+
+    ret->nctrlsets = ret->ctrlsetsize = 0;
+    ret->ctrlsets = NULL;
+    ret->nfrees = ret->freesize = 0;
+    ret->frees = NULL;
+
+    return ret;
+}
+
+void ctrl_free_box(struct controlbox *b)
+{
+    int i;
+
+    for (i = 0; i < b->nctrlsets; i++) {
+       ctrl_free_set(b->ctrlsets[i]);
+    }
+    for (i = 0; i < b->nfrees; i++)
+       sfree(b->frees[i]);
+    sfree(b->ctrlsets);
+    sfree(b->frees);
+    sfree(b);
+}
+
+void ctrl_free_set(struct controlset *s)
+{
+    int i;
+
+    sfree(s->pathname);
+    sfree(s->boxname);
+    sfree(s->boxtitle);
+    for (i = 0; i < s->ncontrols; i++) {
+       ctrl_free(s->ctrls[i]);
+    }
+    sfree(s->ctrls);
+    sfree(s);
+}
+
+/*
+ * Find the index of first controlset in a controlbox for a given
+ * path. If that path doesn't exist, return the index where it
+ * should be inserted.
+ */
+static int ctrl_find_set(struct controlbox *b, char *path, int start)
+{
+    int i, last, thisone;
+
+    last = 0;
+    for (i = 0; i < b->nctrlsets; i++) {
+       thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);
+       /*
+        * If `start' is true and there exists a controlset with
+        * exactly the path we've been given, we should return the
+        * index of the first such controlset we find. Otherwise,
+        * we should return the index of the first entry in which
+        * _fewer_ path elements match than they did last time.
+        */
+       if ((start && thisone == INT_MAX) || thisone < last)
+           return i;
+       last = thisone;
+    }
+    return b->nctrlsets;              /* insert at end */
+}
+
+/*
+ * Find the index of next controlset in a controlbox for a given
+ * path, or -1 if no such controlset exists. If -1 is passed as
+ * input, finds the first.
+ */
+int ctrl_find_path(struct controlbox *b, char *path, int index)
+{
+    if (index < 0)
+       index = ctrl_find_set(b, path, 1);
+    else
+       index++;
+
+    if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))
+       return index;
+    else
+       return -1;
+}
+
+/* Set up a panel title. */
+struct controlset *ctrl_settitle(struct controlbox *b,
+                                char *path, char *title)
+{
+    
+    struct controlset *s = smalloc(sizeof(struct controlset));
+    int index = ctrl_find_set(b, path, 1);
+    s->pathname = dupstr(path);
+    s->boxname = NULL;
+    s->boxtitle = dupstr(title);
+    s->ncontrols = s->ctrlsize = 0;
+    s->ncolumns = 0;                  /* this is a title! */
+    s->ctrls = NULL;
+    if (b->nctrlsets >= b->ctrlsetsize) {
+       b->ctrlsetsize = b->nctrlsets + 32;
+       b->ctrlsets = srealloc(b->ctrlsets,
+                              b->ctrlsetsize*sizeof(*b->ctrlsets));
+    }
+    if (index < b->nctrlsets)
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));
+    b->ctrlsets[index] = s;
+    b->nctrlsets++;
+    return s;
+}
+
+/* Retrieve a pointer to a controlset, creating it if absent. */
+struct controlset *ctrl_getset(struct controlbox *b,
+                              char *path, char *name, char *boxtitle)
+{
+    struct controlset *s;
+    int index = ctrl_find_set(b, path, 1);
+    while (index < b->nctrlsets &&
+          !strcmp(b->ctrlsets[index]->pathname, path)) {
+       if (b->ctrlsets[index]->boxname &&
+           !strcmp(b->ctrlsets[index]->boxname, name))
+           return b->ctrlsets[index];
+       index++;
+    }
+    s = smalloc(sizeof(struct controlset));
+    s->pathname = dupstr(path);
+    s->boxname = dupstr(name);
+    s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;
+    s->ncolumns = 1;
+    s->ncontrols = s->ctrlsize = 0;
+    s->ctrls = NULL;
+    if (b->nctrlsets >= b->ctrlsetsize) {
+       b->ctrlsetsize = b->nctrlsets + 32;
+       b->ctrlsets = srealloc(b->ctrlsets,
+                              b->ctrlsetsize*sizeof(*b->ctrlsets));
+    }
+    if (index < b->nctrlsets)
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));
+    b->ctrlsets[index] = s;
+    b->nctrlsets++;
+    return s;
+}
+
+/* Allocate some private data in a controlbox. */
+void *ctrl_alloc(struct controlbox *b, size_t size)
+{
+    void *p;
+    p = smalloc(size);
+    if (b->nfrees >= b->freesize) {
+       b->freesize = b->nfrees + 32;
+       b->frees = srealloc(b->frees, b->freesize*sizeof(*b->frees));
+    }
+    b->frees[b->nfrees++] = p;
+    return p;
+}
+
+static union control *ctrl_new(struct controlset *s, int type,
+                              intorptr helpctx, handler_fn handler,
+                              intorptr context)
+{
+    union control *c = smalloc(sizeof(union control));
+    if (s->ncontrols >= s->ctrlsize) {
+       s->ctrlsize = s->ncontrols + 32;
+       s->ctrls = srealloc(s->ctrls, s->ctrlsize * sizeof(*s->ctrls));
+    }
+    s->ctrls[s->ncontrols++] = c;
+    /*
+     * Fill in the standard fields.
+     */
+    c->generic.type = type;
+    c->generic.tabdelay = 0;
+    c->generic.column = COLUMN_FIELD(0, s->ncolumns);
+    c->generic.helpctx = helpctx;
+    c->generic.handler = handler;
+    c->generic.context = context;
+    c->generic.label = NULL;
+    return c;
+}
+
+/* `ncolumns' is followed by that many percentages, as integers. */
+union control *ctrl_columns(struct controlset *s, int ncolumns, ...)
+{
+    union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));
+    assert(s->ncolumns == 1 || ncolumns == 1);
+    c->columns.ncols = ncolumns;
+    s->ncolumns = ncolumns;
+    if (ncolumns == 1) {
+       c->columns.percentages = NULL;
+    } else {
+       va_list ap;
+       int i;
+       c->columns.percentages = smalloc(ncolumns * sizeof(int));
+       va_start(ap, ncolumns);
+       for (i = 0; i < ncolumns; i++)
+           c->columns.percentages[i] = va_arg(ap, int);
+       va_end(ap);
+    }
+    return c;
+}
+
+union control *ctrl_editbox(struct controlset *s, char *label, char shortcut,
+                           int percentage,
+                           intorptr helpctx, handler_fn handler,
+                           intorptr context, intorptr context2)
+{
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
+    c->editbox.label = label ? dupstr(label) : NULL;
+    c->editbox.shortcut = shortcut;
+    c->editbox.percentwidth = percentage;
+    c->editbox.password = 0;
+    c->editbox.has_list = 0;
+    c->editbox.context2 = context2;
+    return c;
+}
+
+union control *ctrl_combobox(struct controlset *s, char *label, char shortcut,
+                            int percentage,
+                            intorptr helpctx, handler_fn handler,
+                            intorptr context, intorptr context2)
+{
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
+    c->editbox.label = label ? dupstr(label) : NULL;
+    c->editbox.shortcut = shortcut;
+    c->editbox.percentwidth = percentage;
+    c->editbox.password = 0;
+    c->editbox.has_list = 1;
+    c->editbox.context2 = context2;
+    return c;
+}
+
+/*
+ * `ncolumns' is followed by (alternately) radio button titles and
+ * intorptrs, until a NULL in place of a title string is seen. Each
+ * title is expected to be followed by a shortcut _iff_ `shortcut'
+ * is NO_SHORTCUT.
+ */
+union control *ctrl_radiobuttons(struct controlset *s, char *label,
+                                char shortcut, int ncolumns, intorptr helpctx,
+                                handler_fn handler, intorptr context, ...)
+{
+    va_list ap;
+    int i;
+    union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);
+    c->radio.label = label ? dupstr(label) : NULL;
+    c->radio.shortcut = shortcut;
+    c->radio.ncolumns = ncolumns;
+    /*
+     * Initial pass along variable argument list to count the
+     * buttons.
+     */
+    va_start(ap, context);
+    i = 0;
+    while (va_arg(ap, char *) != NULL) {
+       i++;
+       if (c->radio.shortcut == NO_SHORTCUT)
+           va_arg(ap, int);           /* char promotes to int in arg lists */
+       va_arg(ap, intorptr);
+    }
+    va_end(ap);
+    c->radio.nbuttons = i;
+    if (c->radio.shortcut == NO_SHORTCUT)
+       c->radio.shortcuts = smalloc(c->radio.nbuttons * sizeof(char));
+    else
+       c->radio.shortcuts = NULL;
+    c->radio.buttons = smalloc(c->radio.nbuttons * sizeof(char *));
+    c->radio.buttondata = smalloc(c->radio.nbuttons * sizeof(intorptr));
+    /*
+     * Second pass along variable argument list to actually fill in
+     * the structure.
+     */
+    va_start(ap, context);
+    for (i = 0; i < c->radio.nbuttons; i++) {
+       c->radio.buttons[i] = dupstr(va_arg(ap, char *));
+       if (c->radio.shortcut == NO_SHORTCUT)
+           c->radio.shortcuts[i] = va_arg(ap, int);
+                                      /* char promotes to int in arg lists */
+       c->radio.buttondata[i] = va_arg(ap, intorptr);
+    }
+    va_end(ap);
+    return c;
+}
+
+union control *ctrl_pushbutton(struct controlset *s,char *label,char shortcut,
+                              intorptr helpctx, handler_fn handler,
+                              intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);
+    c->button.label = label ? dupstr(label) : NULL;
+    c->button.shortcut = shortcut;
+    c->button.isdefault = 0;
+    return c;
+}
+
+union control *ctrl_listbox(struct controlset *s,char *label,char shortcut,
+                           intorptr helpctx, handler_fn handler,
+                           intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
+    c->listbox.label = label ? dupstr(label) : NULL;
+    c->listbox.shortcut = shortcut;
+    c->listbox.height = 5;            /* *shrug* a plausible default */
+    c->listbox.draglist = 0;
+    c->listbox.multisel = 0;
+    c->listbox.percentwidth = 100;
+    c->listbox.ncols = 0;
+    c->listbox.percentages = NULL;
+    return c;
+}
+
+union control *ctrl_droplist(struct controlset *s, char *label, char shortcut,
+                            int percentage, intorptr helpctx,
+                            handler_fn handler, intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
+    c->listbox.label = label ? dupstr(label) : NULL;
+    c->listbox.shortcut = shortcut;
+    c->listbox.height = 0;            /* means it's a drop-down list */
+    c->listbox.draglist = 0;
+    c->listbox.multisel = 0;
+    c->listbox.percentwidth = percentage;
+    return c;
+}
+
+union control *ctrl_draglist(struct controlset *s,char *label,char shortcut,
+                            intorptr helpctx, handler_fn handler,
+                            intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
+    c->listbox.label = label ? dupstr(label) : NULL;
+    c->listbox.shortcut = shortcut;
+    c->listbox.height = 5;            /* *shrug* a plausible default */
+    c->listbox.draglist = 1;
+    c->listbox.multisel = 0;
+    c->listbox.percentwidth = 100;
+    return c;
+}
+
+union control *ctrl_filesel(struct controlset *s,char *label,char shortcut,
+                           char const *filter, int write, char *title,
+                           intorptr helpctx, handler_fn handler,
+                           intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);
+    c->fileselect.label = label ? dupstr(label) : NULL;
+    c->fileselect.shortcut = shortcut;
+    c->fileselect.filter = filter;
+    c->fileselect.for_writing = write;
+    c->fileselect.title = dupstr(title);
+    return c;
+}
+
+union control *ctrl_fontsel(struct controlset *s,char *label,char shortcut,
+                           intorptr helpctx, handler_fn handler,
+                           intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);
+    c->fontselect.label = label ? dupstr(label) : NULL;
+    c->fontselect.shortcut = shortcut;
+    return c;
+}
+
+union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)
+{
+    union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));
+    c->tabdelay.ctrl = ctrl;
+    return c;
+}
+
+union control *ctrl_text(struct controlset *s, char *text, intorptr helpctx)
+{
+    union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));
+    c->text.label = dupstr(text);
+    return c;
+}
+
+union control *ctrl_checkbox(struct controlset *s, char *label, char shortcut,
+                            intorptr helpctx, handler_fn handler,
+                            intorptr context)
+{
+    union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);
+    c->checkbox.label = label ? dupstr(label) : NULL;
+    c->checkbox.shortcut = shortcut;
+    return c;
+}
+
+void ctrl_free(union control *ctrl)
+{
+    int i;
+
+    sfree(ctrl->generic.label);
+    switch (ctrl->generic.type) {
+      case CTRL_RADIO:
+       for (i = 0; i < ctrl->radio.nbuttons; i++)
+           sfree(ctrl->radio.buttons[i]);
+       sfree(ctrl->radio.buttons);
+       sfree(ctrl->radio.shortcuts);
+       sfree(ctrl->radio.buttondata);
+       break;
+      case CTRL_COLUMNS:
+       sfree(ctrl->columns.percentages);
+       break;
+      case CTRL_LISTBOX:
+       sfree(ctrl->listbox.percentages);
+       break;
+      case CTRL_FILESELECT:
+       sfree(ctrl->fileselect.title);
+       break;
+    }
+    sfree(ctrl);
+}
+
+void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,
+                               void *data, int event)
+{
+    int button;
+    /*
+     * For a standard radio button set, the context parameter gives
+     * offsetof(targetfield, Config), and the extra data per button
+     * gives the value the target field should take if that button
+     * is the one selected.
+     */
+    if (event == EVENT_REFRESH) {
+       for (button = 0; button < ctrl->radio.nbuttons; button++)
+           if (*(int *)ATOFFSET(data, ctrl->radio.context.i) ==
+               ctrl->radio.buttondata[button].i)
+               break;
+       /* We expected that `break' to happen, in all circumstances. */
+       assert(button < ctrl->radio.nbuttons);
+       dlg_radiobutton_set(ctrl, dlg, button);
+    } else if (event == EVENT_VALCHANGE) {
+       button = dlg_radiobutton_get(ctrl, dlg);
+       assert(button >= 0 && button < ctrl->radio.nbuttons);
+       *(int *)ATOFFSET(data, ctrl->radio.context.i) =
+           ctrl->radio.buttondata[button].i;
+    }
+}
+
+void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,
+                               void *data, int event)
+{
+    int offset, invert;
+
+    /*
+     * For a standard checkbox, the context parameter gives
+     * offsetof(targetfield, Config), optionally ORed with
+     * CHECKBOX_INVERT.
+     */
+    offset = ctrl->checkbox.context.i;
+    if (offset & CHECKBOX_INVERT) {
+       offset &= ~CHECKBOX_INVERT;
+       invert = 1;
+    } else
+       invert = 0;
+
+    /*
+     * C lacks a logical XOR, so the following code uses the idiom
+     * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
+     * iff exactly one of a and b is nonzero, otherwise 0.)
+     */
+
+    if (event == EVENT_REFRESH) {
+       dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert));
+    } else if (event == EVENT_VALCHANGE) {
+       *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert;
+    }
+}
+
+void dlg_stdeditbox_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    /*
+     * The standard edit-box handler expects the main `context'
+     * field to contain the `offsetof' a field in the structure
+     * pointed to by `data'. The secondary `context2' field
+     * indicates the type of this field:
+     *
+     *  - if context2 > 0, the field is a char array and context2
+     *    gives its size.
+     *  - if context2 == -1, the field is an int and the edit box
+     *    is numeric.
+     *  - if context2 < -1, the field is an int and the edit box is
+     *    _floating_, and (-context2) gives the scale. (E.g. if
+     *    context2 == -1000, then typing 1.2 into the box will set
+     *    the field to 1200.)
+     */
+    int offset = ctrl->editbox.context.i;
+    int length = ctrl->editbox.context2.i;
+
+    if (length > 0) {
+       char *field = (char *)ATOFFSET(data, offset);
+       if (event == EVENT_REFRESH) {
+           dlg_editbox_set(ctrl, dlg, field);
+       } else if (event == EVENT_VALCHANGE) {
+           dlg_editbox_get(ctrl, dlg, field, length);
+       }
+    } else if (length < 0) {
+       int *field = (int *)ATOFFSET(data, offset);
+       char data[80];
+       if (event == EVENT_REFRESH) {
+           if (length == -1)
+               sprintf(data, "%d", *field);
+           else
+               sprintf(data, "%g", (double)*field / (double)(-length));
+           dlg_editbox_set(ctrl, dlg, data);
+       } else if (event == EVENT_VALCHANGE) {
+           dlg_editbox_get(ctrl, dlg, data, lenof(data));
+           if (length == -1)
+               *field = atoi(data);
+           else
+               *field = (int)((-length) * atof(data));
+       }
+    }
+}
+
+void dlg_stdfilesel_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    /*
+     * The standard file-selector handler expects the `context'
+     * field to contain the `offsetof' a Filename field in the
+     * structure pointed to by `data'.
+     */
+    int offset = ctrl->fileselect.context.i;
+
+    if (event == EVENT_REFRESH) {
+       dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset));
+    } else if (event == EVENT_VALCHANGE) {
+       dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset));
+    }
+}
+
+void dlg_stdfontsel_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    /*
+     * The standard file-selector handler expects the `context'
+     * field to contain the `offsetof' a FontSpec field in the
+     * structure pointed to by `data'.
+     */
+    int offset = ctrl->fontselect.context.i;
+
+    if (event == EVENT_REFRESH) {
+       dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset));
+    } else if (event == EVENT_VALCHANGE) {
+       dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset));
+    }
+}
diff --git a/dialog.h b/dialog.h
new file mode 100644 (file)
index 0000000..6b9f5e3
--- /dev/null
+++ b/dialog.h
@@ -0,0 +1,665 @@
+/*
+ * Exports and types from dialog.c.
+ */
+
+/*
+ * This will come in handy for generic control handlers. Anyone
+ * knows how to make this more portable, let me know :-)
+ */
+#define ATOFFSET(data, offset) ( (void *) ( (char *)(data) + (offset) ) )
+
+/*
+ * This is the big union which defines a single control, of any
+ * type.
+ * 
+ * General principles:
+ *  - _All_ pointers in this structure are expected to point to
+ *    dynamically allocated things, unless otherwise indicated.
+ *  - `char' fields giving keyboard shortcuts are expected to be
+ *    NO_SHORTCUT if no shortcut is desired for a particular control.
+ *  - The `label' field can often be NULL, which will cause the
+ *    control to not have a label at all. This doesn't apply to
+ *    checkboxes and push buttons, in which the label is not
+ *    separate from the control.
+ */
+
+#define NO_SHORTCUT '\0'
+
+enum {
+    CTRL_TEXT,                        /* just a static line of text */
+    CTRL_EDITBOX,                     /* label plus edit box */
+    CTRL_RADIO,                               /* label plus radio buttons */
+    CTRL_CHECKBOX,                    /* checkbox (contains own label) */
+    CTRL_BUTTON,                      /* simple push button (no label) */
+    CTRL_LISTBOX,                     /* label plus list box */
+    CTRL_COLUMNS,                     /* divide window into columns */
+    CTRL_FILESELECT,                  /* label plus filename selector */
+    CTRL_FONTSELECT,                  /* label plus font selector */
+    CTRL_TABDELAY                     /* see `tabdelay' below */
+};
+
+/*
+ * Many controls have `intorptr' unions for storing user data,
+ * since the user might reasonably want to store either an integer
+ * or a void * pointer. Here I define a union, and two convenience
+ * functions to create that union from actual integers or pointers.
+ * 
+ * The convenience functions are declared as inline if possible.
+ * Otherwise, they're declared here and defined when this header is
+ * included with DEFINE_INTORPTR_FNS defined. This is a total pain,
+ * but such is life.
+ */
+typedef union { void *p; int i; } intorptr;
+
+#if defined DEFINE_INTORPTR_FNS || defined INLINE
+#ifdef INLINE
+#define PREFIX INLINE
+#else
+#define PREFIX
+#endif
+PREFIX intorptr I(int i) { intorptr ret; ret.i = i; return ret; }
+PREFIX intorptr P(void *p) { intorptr ret; ret.p = p; return ret; }
+#undef PREFIX
+#else
+intorptr I(int i);
+intorptr P(void *p);
+#endif
+
+/*
+ * Each control has an `int' field specifying which columns it
+ * occupies in a multi-column part of the dialog box. These macros
+ * pack and unpack that field.
+ * 
+ * If a control belongs in exactly one column, just specifying the
+ * column number is perfectly adequate.
+ */
+#define COLUMN_FIELD(start, span) ( (((span)-1) << 16) + (start) )
+#define COLUMN_START(field) ( (field) & 0xFFFF )
+#define COLUMN_SPAN(field) ( (((field) >> 16) & 0xFFFF) + 1 )
+
+union control;
+
+/*
+ * The number of event types is being deliberately kept small, on
+ * the grounds that not all platforms might be able to report a
+ * large number of subtle events. We have:
+ *  - the special REFRESH event, called when a control's value
+ *    needs setting
+ *  - the ACTION event, called when the user does something that
+ *    positively requests action (double-clicking a list box item,
+ *    or pushing a push-button)
+ *  - the VALCHANGE event, called when the user alters the setting
+ *    of the control in a way that is usually considered to alter
+ *    the underlying data (toggling a checkbox or radio button,
+ *    moving the items around in a drag-list, editing an edit
+ *    control)
+ *  - the SELCHANGE event, called when the user alters the setting
+ *    of the control in a more minor way (changing the selected
+ *    item in a list box).
+ *  - the CALLBACK event, which happens after the handler routine
+ *    has requested a subdialog (file selector, font selector,
+ *    colour selector) and it has come back with information.
+ */
+enum {
+    EVENT_REFRESH,
+    EVENT_ACTION,
+    EVENT_VALCHANGE,
+    EVENT_SELCHANGE,
+    EVENT_CALLBACK
+};
+typedef void (*handler_fn)(union control *ctrl, void *dlg,
+                          void *data, int event);
+
+#define STANDARD_PREFIX \
+       int type; \
+       char *label; \
+       int tabdelay; \
+       int column; \
+        handler_fn handler; \
+       intorptr context; \
+        intorptr helpctx
+
+union control {
+    /*
+     * The first possibility in this union is the generic header
+     * shared by all the structures, which we are therefore allowed
+     * to access through any one of them.
+     */
+    struct {
+       int type;
+       /*
+        * Every control except CTRL_COLUMNS has _some_ sort of
+        * label. By putting it in the `generic' union as well as
+        * everywhere else, we avoid having to have an irritating
+        * switch statement when we go through and deallocate all
+        * the memory in a config-box structure.
+        * 
+        * Yes, this does mean that any non-NULL value in this
+        * field is expected to be dynamically allocated and
+        * freeable.
+        * 
+        * For CTRL_COLUMNS, this field MUST be NULL.
+        */
+       char *label;
+       /*
+        * If `tabdelay' is non-zero, it indicates that this
+        * particular control should not yet appear in the tab
+        * order. A subsequent CTRL_TABDELAY entry will place it.
+        */
+       int tabdelay;
+       /*
+        * Indicate which column(s) this control occupies. This can
+        * be unpacked into starting column and column span by the
+        * COLUMN macros above.
+        */
+       int column;
+       /*
+        * Most controls need to provide a function which gets
+        * called when that control's setting is changed, or when
+        * the control's setting needs initialising.
+        * 
+        * The `data' parameter points to the writable data being
+        * modified as a result of the configuration activity; for
+        * example, the PuTTY `Config' structure, although not
+        * necessarily.
+        * 
+        * The `dlg' parameter is passed back to the platform-
+        * specific routines to read and write the actual control
+        * state.
+        */
+       handler_fn handler;
+       /*
+        * Almost all of the above functions will find it useful to
+        * be able to store a piece of `void *' or `int' data.
+        */
+       intorptr context;
+       /*
+        * For any control, we also allow the storage of a piece of
+        * data for use by context-sensitive help. For example, on
+        * Windows you can click the magic question mark and then
+        * click a control, and help for that control should spring
+        * up. Hence, here is a slot in which to store per-control
+        * data that a particular platform-specific driver can use
+        * to ensure it brings up the right piece of help text.
+        */
+       intorptr helpctx;
+    } generic;
+    struct {
+       STANDARD_PREFIX;
+       union control *ctrl;
+    } tabdelay;
+    struct {
+       STANDARD_PREFIX;
+    } text;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;                 /* keyboard shortcut */
+       /*
+        * Percentage of the dialog-box width used by the edit box.
+        * If this is set to 100, the label is on its own line;
+        * otherwise the label is on the same line as the box
+        * itself.
+        */
+       int percentwidth;
+       int password;                  /* details of input are hidden */
+       /*
+        * A special case of the edit box is the combo box, which
+        * has a drop-down list built in. (Note that a _non_-
+        * editable drop-down list is done as a special case of a
+        * list box.)
+        */
+       int has_list;
+       /*
+        * Edit boxes tend to need two items of context, so here's
+        * a spare.
+        */
+       intorptr context2;
+    } editbox;
+    struct {
+       STANDARD_PREFIX;
+       /*
+        * `shortcut' here is a single keyboard shortcut which is
+        * expected to select the whole group of radio buttons. It
+        * can be NO_SHORTCUT if required, and there is also a way
+        * to place individual shortcuts on each button; see below.
+        */
+       char shortcut;
+       /*
+        * There are separate fields for `ncolumns' and `nbuttons'
+        * for several reasons.
+        * 
+        * Firstly, we sometimes want the last of a set of buttons
+        * to have a longer label than the rest; we achieve this by
+        * setting `ncolumns' higher than `nbuttons', and the
+        * layout code is expected to understand that the final
+        * button should be given all the remaining space on the
+        * line. This sounds like a ludicrously specific special
+        * case (if we're doing this sort of thing, why not have
+        * the general ability to have a particular button span
+        * more than one column whether it's the last one or not?)
+        * but actually it's reasonably common for the sort of
+        * three-way control you get a lot of in PuTTY: `yes'
+        * versus `no' versus `some more complex way to decide'.
+        * 
+        * Secondly, setting `nbuttons' higher than `ncolumns' lets
+        * us have more than one line of radio buttons for a single
+        * setting. A very important special case of this is
+        * setting `ncolumns' to 1, so that each button is on its
+        * own line.
+        */
+       int ncolumns;
+       int nbuttons;
+       /*
+        * This points to a dynamically allocated array of `char *'
+        * pointers, each of which points to a dynamically
+        * allocated string.
+        */
+       char **buttons;                /* `nbuttons' button labels */
+       /*
+        * This points to a dynamically allocated array of `char'
+        * giving the individual keyboard shortcuts for each radio
+        * button. The array may be NULL if none are required.
+        */
+       char *shortcuts;               /* `nbuttons' shortcuts; may be NULL */
+       /*
+        * This points to a dynamically allocated array of
+        * intorptr, giving helpful data for each button.
+        */
+       intorptr *buttondata;          /* `nbuttons' entries; may be NULL */
+    } radio;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;
+    } checkbox;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;
+       /*
+        * At least Windows has the concept of a `default push
+        * button', which gets implicitly pressed when you hit
+        * Return even if it doesn't have the input focus.
+        */
+       int isdefault;
+    } button;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;                 /* keyboard shortcut */
+       /*
+        * Height of the list box, in approximate number of lines.
+        * If this is zero, the list is a drop-down list.
+        */
+       int height;                    /* height in lines */
+       /*
+        * If this is set, the list elements can be reordered by
+        * the user (by drag-and-drop or by Up and Down buttons,
+        * whatever the per-platform implementation feels
+        * comfortable with). This is not guaranteed to work on a
+        * drop-down list, so don't try it!
+        */
+       int draglist;
+       /*
+        * If this is set, the list can have more than one element
+        * selected at a time. This is not guaranteed to work on a
+        * drop-down list, so don't try it!
+        */
+       int multisel;
+       /*
+        * Percentage of the dialog-box width used by the list box.
+        * If this is set to 100, the label is on its own line;
+        * otherwise the label is on the same line as the box
+        * itself. Setting this to anything other than 100 is not
+        * guaranteed to work on a _non_-drop-down list, so don't
+        * try it!
+        */
+       int percentwidth;
+       /*
+        * Some list boxes contain strings that contain tab
+        * characters. If `ncols' is greater than 0, then
+        * `percentages' is expected to be non-zero and to contain
+        * the respective widths of `ncols' columns, which together
+        * will exactly fit the width of the list box. Otherwise
+        * `percentages' must be NULL.
+        */
+       int ncols;                     /* number of columns */
+       int *percentages;              /* % width of each column */
+    } listbox;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;
+       /*
+        * `filter' dictates what type of files will be selected by
+        * default; for example, when selecting private key files
+        * the file selector would do well to only show .PPK files
+        * (on those systems where this is the chosen extension).
+        * 
+        * The precise contents of `filter' are platform-defined,
+        * unfortunately. The special value NULL means `all files'
+        * and is always a valid fallback.
+        * 
+        * Unlike almost all strings in this structure, this value
+        * is NOT expected to require freeing (although of course
+        * you can always use ctrl_alloc if you do need to create
+        * one on the fly). This is because the likely mode of use
+        * is to define string constants in a platform-specific
+        * header file, and directly reference those. Or worse, a
+        * particular platform might choose to cast integers into
+        * this pointer type...
+        */
+       char const *filter;
+       /*
+        * Some systems like to know whether a file selector is
+        * choosing a file to read or one to write (and possibly
+        * create).
+        */
+       int for_writing;
+       /*
+        * On at least some platforms, the file selector is a
+        * separate dialog box, and contains a user-settable title.
+        * 
+        * This value _is_ expected to require freeing.
+        */
+       char *title;
+    } fileselect;
+    struct {
+       /* In this variant, `label' MUST be NULL. */
+       STANDARD_PREFIX;
+       int ncols;                     /* number of columns */
+       int *percentages;              /* % width of each column */
+       /*
+        * Every time this control type appears, exactly one of
+        * `ncols' and the previous number of columns MUST be one.
+        * Attempting to allow a seamless transition from a four-
+        * to a five-column layout, for example, would be way more
+        * trouble than it was worth. If you must lay things out
+        * like that, define eight unevenly sized columns and use
+        * column-spanning a lot. But better still, just don't.
+        * 
+        * `percentages' may be NULL if ncols==1, to save space.
+        */
+    } columns;
+    struct {
+       STANDARD_PREFIX;
+       char shortcut;
+    } fontselect;
+};
+
+#undef STANDARD_PREFIX
+
+/*
+ * `controlset' is a container holding an array of `union control'
+ * structures, together with a panel name and a title for the whole
+ * set. In Windows and any similar-looking GUI, each `controlset'
+ * in the config will be a container box within a panel.
+ * 
+ * Special case: if `boxname' is NULL, the control set gives an
+ * overall title for an entire panel of controls.
+ */
+struct controlset {
+    char *pathname;                   /* panel path, e.g. "SSH/Tunnels" */
+    char *boxname;                    /* internal short name of controlset */
+    char *boxtitle;                   /* title of container box */
+    int ncolumns;                     /* current no. of columns at bottom */
+    int ncontrols;                    /* number of `union control' in array */
+    int ctrlsize;                     /* allocated size of array */
+    union control **ctrls;            /* actual array */
+};
+
+/*
+ * This is the container structure which holds a complete set of
+ * controls.
+ */
+struct controlbox {
+    int nctrlsets;                    /* number of ctrlsets */
+    int ctrlsetsize;                  /* ctrlset size */
+    struct controlset **ctrlsets;      /* actual array of ctrlsets */
+    int nfrees;
+    int freesize;
+    void **frees;                     /* array of aux data areas to free */
+};
+
+struct controlbox *ctrl_new_box(void);
+void ctrl_free_box(struct controlbox *);
+
+/*
+ * Standard functions used for populating a controlbox structure.
+ */
+
+/* Set up a panel title. */
+struct controlset *ctrl_settitle(struct controlbox *,
+                                char *path, char *title);
+/* Retrieve a pointer to a controlset, creating it if absent. */
+struct controlset *ctrl_getset(struct controlbox *,
+                              char *path, char *name, char *boxtitle);
+void ctrl_free_set(struct controlset *);
+
+void ctrl_free(union control *);
+
+/*
+ * This function works like `malloc', but the memory it returns
+ * will be automatically freed when the controlbox is freed. Note
+ * that a controlbox is a dialog-box _template_, not an instance,
+ * and so data allocated through this function is better not used
+ * to hold modifiable per-instance things. It's mostly here for
+ * allocating structures to be passed as control handler params.
+ */
+void *ctrl_alloc(struct controlbox *b, size_t size);
+
+/*
+ * Individual routines to create `union control' structures in a controlset.
+ * 
+ * Most of these routines allow the most common fields to be set
+ * directly, and put default values in the rest. Each one returns a
+ * pointer to the `union control' it created, so that final tweaks
+ * can be made.
+ */
+
+/* `ncolumns' is followed by that many percentages, as integers. */
+union control *ctrl_columns(struct controlset *, int ncolumns, ...);
+union control *ctrl_editbox(struct controlset *, char *label, char shortcut,
+                           int percentage, intorptr helpctx,
+                           handler_fn handler,
+                           intorptr context, intorptr context2);
+union control *ctrl_combobox(struct controlset *, char *label, char shortcut,
+                            int percentage, intorptr helpctx,
+                            handler_fn handler,
+                            intorptr context, intorptr context2);
+/*
+ * `ncolumns' is followed by (alternately) radio button titles and
+ * intorptrs, until a NULL in place of a title string is seen. Each
+ * title is expected to be followed by a shortcut _iff_ `shortcut'
+ * is NO_SHORTCUT.
+ */
+union control *ctrl_radiobuttons(struct controlset *, char *label,
+                                char shortcut, int ncolumns,
+                                intorptr helpctx,
+                                handler_fn handler, intorptr context, ...);
+union control *ctrl_pushbutton(struct controlset *,char *label,char shortcut,
+                              intorptr helpctx,
+                              handler_fn handler, intorptr context);
+union control *ctrl_listbox(struct controlset *,char *label,char shortcut,
+                           intorptr helpctx,
+                           handler_fn handler, intorptr context);
+union control *ctrl_droplist(struct controlset *, char *label, char shortcut,
+                            int percentage, intorptr helpctx,
+                            handler_fn handler, intorptr context);
+union control *ctrl_draglist(struct controlset *,char *label,char shortcut,
+                            intorptr helpctx,
+                            handler_fn handler, intorptr context);
+union control *ctrl_filesel(struct controlset *,char *label,char shortcut,
+                           char const *filter, int write, char *title,
+                           intorptr helpctx,
+                           handler_fn handler, intorptr context);
+union control *ctrl_fontsel(struct controlset *,char *label,char shortcut,
+                           intorptr helpctx,
+                           handler_fn handler, intorptr context);
+union control *ctrl_text(struct controlset *, char *text, intorptr helpctx);
+union control *ctrl_checkbox(struct controlset *, char *label, char shortcut,
+                            intorptr helpctx,
+                            handler_fn handler, intorptr context);
+union control *ctrl_tabdelay(struct controlset *, union control *);
+
+/*
+ * Standard handler routines to cover most of the common cases in
+ * the config box.
+ */
+/*
+ * The standard radio-button handler expects the main `context'
+ * field to contain the `offsetof' of an int field in the structure
+ * pointed to by `data', and expects each of the individual button
+ * data to give a value for that int field.
+ */
+void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,
+                               void *data, int event);
+/*
+ * The standard checkbox handler expects the main `context' field
+ * to contain the `offsetof' an int field in the structure pointed
+ * to by `data', optionally ORed with CHECKBOX_INVERT to indicate
+ * that the sense of the datum is opposite to the sense of the
+ * checkbox.
+ */
+#define CHECKBOX_INVERT (1<<30)
+void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,
+                            void *data, int event);
+/*
+ * The standard edit-box handler expects the main `context' field
+ * to contain the `offsetof' a field in the structure pointed to by
+ * `data'. The secondary `context2' field indicates the type of
+ * this field:
+ * 
+ *  - if context2 > 0, the field is a char array and context2 gives
+ *    its size.
+ *  - if context2 == -1, the field is an int and the edit box is
+ *    numeric.
+ *  - if context2 < -1, the field is an int and the edit box is
+ *    _floating_, and (-context2) gives the scale. (E.g. if
+ *    context2 == -1000, then typing 1.2 into the box will set the
+ *    field to 1200.)
+ */
+void dlg_stdeditbox_handler(union control *ctrl, void *dlg,
+                           void *data, int event);
+/*
+ * The standard file-selector handler expects the main `context'
+ * field to contain the `offsetof' a Filename field in the
+ * structure pointed to by `data'.
+ */
+void dlg_stdfilesel_handler(union control *ctrl, void *dlg,
+                           void *data, int event);
+/*
+ * The standard font-selector handler expects the main `context'
+ * field to contain the `offsetof' a Font field in the structure
+ * pointed to by `data'.
+ */
+void dlg_stdfontsel_handler(union control *ctrl, void *dlg,
+                           void *data, int event);
+
+/*
+ * Routines the platform-independent dialog code can call to read
+ * and write the values of controls.
+ */
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton);
+int dlg_radiobutton_get(union control *ctrl, void *dlg);
+void dlg_checkbox_set(union control *ctrl, void *dlg, int checked);
+int dlg_checkbox_get(union control *ctrl, void *dlg);
+void dlg_editbox_set(union control *ctrl, void *dlg, char const *text);
+void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length);
+/* The `listbox' functions can also apply to combo boxes. */
+void dlg_listbox_clear(union control *ctrl, void *dlg);
+void dlg_listbox_del(union control *ctrl, void *dlg, int index);
+void dlg_listbox_add(union control *ctrl, void *dlg, char const *text);
+/*
+ * Each listbox entry may have a numeric id associated with it.
+ * Note that some front ends only permit a string to be stored at
+ * each position, which means that _if_ you put two identical
+ * strings in any listbox then you MUST not assign them different
+ * IDs and expect to get meaningful results back.
+ */
+void dlg_listbox_addwithindex(union control *ctrl, void *dlg,
+                             char const *text, int id);
+int dlg_listbox_getid(union control *ctrl, void *dlg, int index);
+/* dlg_listbox_index returns <0 if no single element is selected. */
+int dlg_listbox_index(union control *ctrl, void *dlg);
+int dlg_listbox_issel(union control *ctrl, void *dlg, int index);
+void dlg_listbox_select(union control *ctrl, void *dlg, int index);
+void dlg_text_set(union control *ctrl, void *dlg, char const *text);
+void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn);
+void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn);
+void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fn);
+void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fn);
+/*
+ * Bracketing a large set of updates in these two functions will
+ * cause the front end (if possible) to delay updating the screen
+ * until it's all complete, thus avoiding flicker.
+ */
+void dlg_update_start(union control *ctrl, void *dlg);
+void dlg_update_done(union control *ctrl, void *dlg);
+/*
+ * Set input focus into a particular control.
+ */
+void dlg_set_focus(union control *ctrl, void *dlg);
+/*
+ * Return the `ctrl' structure for the control that had the input
+ * focus before this one. This is NOT GUARANTEED to work on all
+ * platforms, so don't base any critical functionality on it!
+ */
+union control *dlg_last_focused(void *dlg);
+/*
+ * During event processing, you might well want to give an error
+ * indication to the user. dlg_beep() is a quick and easy generic
+ * error; dlg_error() puts up a message-box or equivalent.
+ */
+void dlg_beep(void *dlg);
+void dlg_error_msg(void *dlg, char *msg);
+/*
+ * This function signals to the front end that the dialog's
+ * processing is completed, and passes an integer value (typically
+ * a success status).
+ */
+void dlg_end(void *dlg, int value);
+
+/*
+ * Routines to manage a (per-platform) colour selector.
+ * dlg_coloursel_start() is called in an event handler, and
+ * schedules the running of a colour selector after the event
+ * handler returns. The colour selector will send EVENT_CALLBACK to
+ * the control that spawned it, when it's finished;
+ * dlg_coloursel_results() fetches the results, as integers from 0
+ * to 255; it returns nonzero on success, or zero if the colour
+ * selector was dismissed by hitting Cancel or similar.
+ * 
+ * dlg_coloursel_start() accepts an RGB triple which is used to
+ * initialise the colour selector to its starting value.
+ */
+void dlg_coloursel_start(union control *ctrl, void *dlg,
+                        int r, int g, int b);
+int dlg_coloursel_results(union control *ctrl, void *dlg,
+                         int *r, int *g, int *b);
+
+/*
+ * This routine is used by the platform-independent code to
+ * indicate that the value of a particular control is likely to
+ * have changed. It triggers a call of the handler for that control
+ * with `event' set to EVENT_REFRESH.
+ * 
+ * If `ctrl' is NULL, _all_ controls in the dialog get refreshed
+ * (for loading or saving entire sets of settings).
+ */
+void dlg_refresh(union control *ctrl, void *dlg);
+
+/*
+ * Standard helper functions for reading a controlbox structure.
+ */
+
+/*
+ * Find the index of next controlset in a controlbox for a given
+ * path, or -1 if no such controlset exists. If -1 is passed as
+ * input, finds the first. Intended usage is something like
+ * 
+ *     for (index=-1; (index=ctrl_find_path(ctrlbox, index, path)) >= 0 ;) {
+ *          ... process this controlset ...
+ *      }
+ */
+int ctrl_find_path(struct controlbox *b, char *path, int index);
+int ctrl_path_elements(char *path);
+/* Return the number of matching path elements at the starts of p1 and p2,
+ * or INT_MAX if the paths are identical. */
+int ctrl_path_compare(char *p1, char *p2);
index f96d25765bd6fb4da99499b203c22ae17d90ed61..4bf4c251a9ce7491d8b2a448f657667c8dbd9ae0 100644 (file)
@@ -1,4 +1,4 @@
-\versionid $Id: config.but,v 1.56 2003/02/19 09:54:45 jacob Exp $
+\versionid $Id: config.but,v 1.57 2003/03/05 22:07:40 simon Exp $
 
 \C{config} Configuring PuTTY
 
 
 \C{config} Configuring PuTTY
 
@@ -622,10 +622,14 @@ on a terminal bell:
 the server can send as many Control-G characters as it likes and
 nothing at all will happen.
 
 the server can send as many Control-G characters as it likes and
 nothing at all will happen.
 
-\b \q{Play Windows Default Sound} is the default setting. It causes
-the Windows \q{Default Beep} sound to be played. To change what this
-sound is, or to test it if nothing seems to be happening, use the
-Sound configurer in the Windows Control Panel.
+\b \q{Make default system alert sound} is the default setting. It
+causes the Windows \q{Default Beep} sound to be played. To change
+what this sound is, or to test it if nothing seems to be happening,
+use the Sound configurer in the Windows Control Panel.
+
+\b \q{Visual bell} is a silent alternative to a beeping computer. In
+this mode, when the server sends a Control-G, the whole PuTTY window
+will flash white for a fraction of a second.
 
 \b \q{Play a custom sound file} allows you to specify a particular
 sound file to be used by PuTTY alone, or even by a particular
 
 \b \q{Play a custom sound file} allows you to specify a particular
 sound file to be used by PuTTY alone, or even by a particular
@@ -634,10 +638,6 @@ beeps from any other beeps on the system. If you select this option,
 you will also need to enter the name of your sound file in the edit
 control \q{Custom sound file to play as a bell}.
 
 you will also need to enter the name of your sound file in the edit
 control \q{Custom sound file to play as a bell}.
 
-\b \q{Visual bell} is a silent alternative to a beeping computer. In
-this mode, when the server sends a Control-G, the whole PuTTY window
-will flash white for a fraction of a second.
-
 \S{config-belltaskbar} \q{Taskbar/caption indication on bell}
 
 \cfg{winhelp-topic}{bell.taskbar}
 \S{config-belltaskbar} \q{Taskbar/caption indication on bell}
 
 \cfg{winhelp-topic}{bell.taskbar}
@@ -883,32 +883,6 @@ offered a choice from all the fixed-width fonts installed on the
 system. (VT100-style terminal handling can only deal with fixed-
 width fonts.)
 
 system. (VT100-style terminal handling can only deal with fixed-
 width fonts.)
 
-\S{config-title} Controlling the window title
-
-\cfg{winhelp-topic}{appearance.title}
-
-The \q{Window title} edit box allows you to set the title of the
-PuTTY window. By default the window title will contain the host name
-followed by \q{PuTTY}, for example \c{server1.example.com - PuTTY}.
-If you want a different window title, this is where to set it.
-
-PuTTY allows the server to send \c{xterm} control sequences which
-modify the title of the window in mid-session. There is also an
-\c{xterm} sequence to modify the title of the window's \e{icon}.
-This makes sense in a windowing system where the window becomes an
-icon when minimised, such as Windows 3.1 or most X Window System
-setups; but in the Windows 95-like user interface it isn't as
-applicable.
-
-By default, PuTTY only uses the server-supplied \e{window} title, and
-ignores the icon title entirely. If for some reason you want to see
-both titles, check the box marked \q{Separate window and icon titles}.
-If you do this, PuTTY's window title and Taskbar caption will
-change into the server-supplied icon title if you minimise the PuTTY
-window, and change back to the server-supplied window title if you
-restore it. (If the server has not bothered to supply a window or
-icon title, none of this will happen.)
-
 \S{config-mouseptr} \q{Hide mouse pointer when typing in window}
 
 \cfg{winhelp-topic}{appearance.hidemouse}
 \S{config-mouseptr} \q{Hide mouse pointer when typing in window}
 
 \cfg{winhelp-topic}{appearance.hidemouse}
@@ -944,6 +918,32 @@ it to zero, or increase it further.
 The Behaviour configuration panel allows you to control aspects of
 the behaviour of PuTTY's window.
 
 The Behaviour configuration panel allows you to control aspects of
 the behaviour of PuTTY's window.
 
+\S{config-title} Controlling the window title
+
+\cfg{winhelp-topic}{appearance.title}
+
+The \q{Window title} edit box allows you to set the title of the
+PuTTY window. By default the window title will contain the host name
+followed by \q{PuTTY}, for example \c{server1.example.com - PuTTY}.
+If you want a different window title, this is where to set it.
+
+PuTTY allows the server to send \c{xterm} control sequences which
+modify the title of the window in mid-session. There is also an
+\c{xterm} sequence to modify the title of the window's \e{icon}.
+This makes sense in a windowing system where the window becomes an
+icon when minimised, such as Windows 3.1 or most X Window System
+setups; but in the Windows 95-like user interface it isn't as
+applicable.
+
+By default, PuTTY only uses the server-supplied \e{window} title, and
+ignores the icon title entirely. If for some reason you want to see
+both titles, check the box marked \q{Separate window and icon titles}.
+If you do this, PuTTY's window title and Taskbar caption will
+change into the server-supplied icon title if you minimise the PuTTY
+window, and change back to the server-supplied window title if you
+restore it. (If the server has not bothered to supply a window or
+icon title, none of this will happen.)
+
 \S{config-warnonclose} \q{Warn before closing window}
 
 \cfg{winhelp-topic}{behaviour.closewarn}
 \S{config-warnonclose} \q{Warn before closing window}
 
 \cfg{winhelp-topic}{behaviour.closewarn}
index 28e36c7daa843c24fe052773d63f3c2c5cdce691..4c424711ad8fc070a13839085e3d7693712477c5 100644 (file)
@@ -48,20 +48,6 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "PuTTY Configuration"
 FONT 8, "MS Shell Dlg"
 BEGIN
 CAPTION "PuTTY Configuration"
 FONT 8, "MS Shell Dlg"
 BEGIN
-    DEFPUSHBUTTON "&Open", IDOK, 184, 235, 44, 14
-    PUSHBUTTON "&Cancel", IDCANCEL, 231, 235, 44, 14
-    PUSHBUTTON "&About", IDC_ABOUT, 3, 235, 44, 14, NOT WS_TABSTOP
-    PUSHBUTTON "&Help", IDC_HELPBTN, 50, 235, 44, 14, NOT WS_TABSTOP
-END
-
-/* Accelerators used: ac */
-IDD_RECONF DIALOG DISCARDABLE 0, 0, 280, 252
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "PuTTY Reconfiguration"
-FONT 8, "MS Shell Dlg"
-BEGIN
-    DEFPUSHBUTTON "&Apply", IDOK, 184, 235, 44, 14
-    PUSHBUTTON "&Cancel", IDCANCEL, 231, 235, 44, 14
 END
 
 /* Accelerators used: co */
 END
 
 /* Accelerators used: co */
diff --git a/wincfg.c b/wincfg.c
new file mode 100644 (file)
index 0000000..5b5bf9a
--- /dev/null
+++ b/wincfg.c
@@ -0,0 +1,275 @@
+/*
+ * wincfg.c - the Windows-specific parts of the PuTTY configuration
+ * box.
+ */
+
+#include <windows.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "dialog.h"
+#include "storage.h"
+
+static void about_handler(union control *ctrl, void *dlg,
+                         void *data, int event)
+{
+    HWND *hwndp = (HWND *)ctrl->generic.context.p;
+
+    if (event == EVENT_ACTION) {
+       modal_about_box(*hwndp);
+    }
+}
+
+static void help_handler(union control *ctrl, void *dlg,
+                        void *data, int event)
+{
+    HWND *hwndp = (HWND *)ctrl->generic.context.p;
+
+    if (event == EVENT_ACTION) {
+       show_help(*hwndp);
+    }
+}
+
+void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
+                         int midsession)
+{
+    struct controlset *s;
+    union control *c;
+
+    if (!midsession) {
+       /*
+        * Add the About and Help buttons to the standard panel.
+        */
+       s = ctrl_getset(b, "", "", "");
+       c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help),
+                           about_handler, P(hwndp));
+       c->generic.column = 0;
+       if (has_help) {
+           c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help),
+                               help_handler, P(hwndp));
+           c->generic.column = 1;
+       }
+    }
+
+    /*
+     * Windows has the AltGr key, which has various Windows-
+     * specific options.
+     */
+    s = ctrl_getset(b, "Terminal/Keyboard", "features",
+                   "Enable extra keyboard features:");
+    ctrl_checkbox(s, "AltGr acts as Compose key", 't',
+                 HELPCTX(keyboard_compose),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,compose_key)));
+    ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd',
+                 HELPCTX(keyboard_ctrlalt),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys)));
+
+    /*
+     * Windows allows an arbitrary .WAV to be played as a bell. For
+     * this we must search the existing controlset for the
+     * radio-button set controlling the `beep' option, and add an
+     * extra button to it.
+     * 
+     * Note that although this _looks_ like a hideous hack, it's
+     * actually all above board. The well-defined interface to the
+     * per-platform dialog box code is the _data structures_ `union
+     * control', `struct controlset' and so on; so code like this
+     * that reaches into those data structures and changes bits of
+     * them is perfectly legitimate and crosses no boundaries. All
+     * the ctrl_* routines that create most of the controls are
+     * convenient shortcuts provided on the cross-platform side of
+     * the interface, and template creation code is under no actual
+     * obligation to use them.
+     */
+    s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
+    {
+       int i;
+       for (i = 0; i < s->ncontrols; i++) {
+           c = s->ctrls[i];
+           if (c->generic.type == CTRL_RADIO &&
+               c->generic.context.i == offsetof(Config, beep)) {
+               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->radio.nbuttons++;
+               c->radio.buttons =
+                   srealloc(c->radio.buttons,
+                            c->radio.nbuttons * sizeof(*c->radio.buttons));
+               c->radio.buttons[c->radio.nbuttons-1] =
+                   dupstr("Play a custom sound file");
+               c->radio.buttondata =
+                   srealloc(c->radio.buttondata,
+                            c->radio.nbuttons * sizeof(*c->radio.buttondata));
+               c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE);
+               if (c->radio.shortcuts) {
+                   c->radio.shortcuts =
+                       srealloc(c->radio.shortcuts,
+                                (c->radio.nbuttons *
+                                 sizeof(*c->radio.shortcuts)));
+                   c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT;
+               }
+               break;
+           }
+       }
+    }
+    ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT,
+                FILTER_WAVE_FILES, FALSE, "Select bell sound file",
+                HELPCTX(bell_style),
+                dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile)));
+
+    /*
+     * While we've got this box open, taskbar flashing on a bell is
+     * also Windows-specific.
+     */
+    ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3,
+                     HELPCTX(bell_taskbar),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, beep_ind)),
+                     "Disabled", I(B_IND_DISABLED),
+                     "Flashing", I(B_IND_FLASH),
+                     "Steady", I(B_IND_STEADY), NULL);
+
+    /*
+     * The sunken-edge border is a Windows GUI feature.
+     */
+    s = ctrl_getset(b, "Window/Appearance", "border",
+                   "Adjust the window border");
+    ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's',
+                 HELPCTX(appearance_border),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge)));
+
+    /*
+     * Cyrillic Lock is a horrid misfeature even on Windows, and
+     * the least we can do is ensure it never makes it to any other
+     * platform (at least unless someone fixes it!).
+     */
+    s = ctrl_getset(b, "Window/Translation", "input",
+                   "Enable character set translation on input data");
+    ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's',
+                 HELPCTX(translation_cyrillic),
+                 dlg_stdcheckbox_handler,
+                 I(offsetof(Config,xlat_capslockcyr)));
+
+    /*
+     * Windows has the weird OEM font mode, which gives us some
+     * additional options when working with line-drawing
+     * characters.
+     */
+    s = ctrl_getset(b, "Window/Translation", "linedraw",
+                   "Adjust how PuTTY displays line drawing characters");
+    {
+       int i;
+       for (i = 0; i < s->ncontrols; i++) {
+           c = s->ctrls[i];
+           if (c->generic.type == CTRL_RADIO &&
+               c->generic.context.i == offsetof(Config, vtmode)) {
+               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->radio.nbuttons += 2;
+               c->radio.buttons =
+                   srealloc(c->radio.buttons,
+                            c->radio.nbuttons * sizeof(*c->radio.buttons));
+               c->radio.buttons[c->radio.nbuttons-2] =
+                   dupstr("Use font in both ANSI and OEM modes");
+               c->radio.buttons[c->radio.nbuttons-1] =
+                   dupstr("Use font in OEM mode only");
+               c->radio.buttondata =
+                   srealloc(c->radio.buttondata,
+                            c->radio.nbuttons * sizeof(*c->radio.buttondata));
+               c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI);
+               c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY);
+               if (!c->radio.shortcuts) {
+                   int j;
+                   c->radio.shortcuts =
+                       smalloc((c->radio.nbuttons *
+                                sizeof(*c->radio.shortcuts)));
+                   for (j = 0; j < c->radio.nbuttons; j++)
+                       c->radio.shortcuts[j] = NO_SHORTCUT;
+               } else {
+                   c->radio.shortcuts =
+                       srealloc(c->radio.shortcuts,
+                                (c->radio.nbuttons *
+                                 sizeof(*c->radio.shortcuts)));
+               }
+               c->radio.shortcuts[c->radio.nbuttons-2] = 'b';
+               c->radio.shortcuts[c->radio.nbuttons-1] = 'e';
+               break;
+           }
+       }
+    }
+
+    /*
+     * RTF paste is Windows-specific.
+     */
+    s = ctrl_getset(b, "Window/Selection", "trans",
+                   "Translation of pasted characters");
+    ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f',
+                 HELPCTX(selection_rtf),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste)));
+
+    /*
+     * Windows often has no middle button, so we supply a selection
+     * mode in which the more critical Paste action is available on
+     * the right button instead.
+     */
+    s = ctrl_getset(b, "Window/Selection", "mouse",
+                   "Control use of mouse");
+    ctrl_radiobuttons(s, "Action of mouse buttons:", NO_SHORTCUT, 1,
+                     HELPCTX(selection_buttons),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, mouse_is_xterm)),
+                     "Windows (Right pastes, Middle extends)", 'w', I(0),
+                     "xterm (Right extends, Middle pastes)", 'x', I(1), NULL);
+    /*
+     * This really ought to go at the _top_ of its box, not the
+     * bottom, so we'll just do some shuffling now we've set it
+     * up...
+     */
+    c = s->ctrls[s->ncontrols-1];      /* this should be the new control */
+    memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *));
+    s->ctrls[0] = c;
+
+    /*
+     * Logical palettes don't even make sense anywhere except Windows.
+     */
+    s = ctrl_getset(b, "Window/Colours", "general",
+                   "General options for colour usage");
+    ctrl_checkbox(s, "Attempt to use logical palettes", 'l',
+                 HELPCTX(colours_logpal),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,try_palette)));
+
+    /*
+     * Resize-by-changing-font is a Windows insanity.
+     */
+    s = ctrl_getset(b, "Window", "size", "Set the size of the window");
+    ctrl_radiobuttons(s, "When window is resized:", 'z', 1,
+                     HELPCTX(window_resize),
+                     dlg_stdradiobutton_handler,
+                     I(offsetof(Config, resize_action)),
+                     "Change the number of rows and columns", I(RESIZE_TERM),
+                     "Change the size of the font", I(RESIZE_FONT),
+                     "Change font size only when maximised", I(RESIZE_EITHER),
+                     "Forbid resizing completely", I(RESIZE_DISABLED), NULL);
+
+    /*
+     * Most of the Window/Behaviour stuff is there to mimic Windows
+     * conventions which PuTTY can optionally disregard. Hence,
+     * most of these options are Windows-specific.
+     */
+    s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
+    ctrl_checkbox(s, "Window closes on ALT-F4", '4',
+                 HELPCTX(behaviour_altf4),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4)));
+    ctrl_checkbox(s, "System menu appears on ALT-Space", 'y',
+                 HELPCTX(behaviour_altspace),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_space)));
+    ctrl_checkbox(s, "System menu appears on ALT alone", 'l',
+                 HELPCTX(behaviour_altonly),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_only)));
+    ctrl_checkbox(s, "Ensure window is always on top", 'e',
+                 HELPCTX(behaviour_alwaysontop),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop)));
+    ctrl_checkbox(s, "Full screen on Alt-Enter", 'f',
+                 HELPCTX(behaviour_altenter),
+                 dlg_stdcheckbox_handler,
+                 I(offsetof(Config,fullscreenonaltenter)));
+}
index 53228236e58dcfa622074b7db51afc13dffec509..4d055f4618de7d46bccfb63f8da6ff490e13d2b6 100644 (file)
@@ -3,10 +3,23 @@
  * box.
  */
 
  * box.
  */
 
+/*
+ * Possible TODO in new cross-platform config box stuff:
+ *
+ *  - When lining up two controls alongside each other, I wonder if
+ *    we could conveniently arrange to centre them vertically?
+ *    Particularly ugly in the current setup is the `Add new
+ *    forwarded port:' static next to the rather taller `Remove'
+ *    button.
+ */
+
 #include <windows.h>
 #include <commctrl.h>
 #include <windows.h>
 #include <commctrl.h>
+#include <assert.h>
 
 #include "winstuff.h"
 
 #include "winstuff.h"
+#include "misc.h"
+#include "dialog.h"
 #include "puttymem.h"
 
 #include "putty.h"
 #include "puttymem.h"
 
 #include "putty.h"
 #define GAPYBOX 4
 #define DLGWIDTH 168
 #define STATICHEIGHT 8
 #define GAPYBOX 4
 #define DLGWIDTH 168
 #define STATICHEIGHT 8
+#define TITLEHEIGHT 12
 #define CHECKBOXHEIGHT 8
 #define RADIOHEIGHT 8
 #define EDITHEIGHT 12
 #define CHECKBOXHEIGHT 8
 #define RADIOHEIGHT 8
 #define EDITHEIGHT 12
+#define LISTHEIGHT 11
+#define LISTINCREMENT 8
 #define COMBOHEIGHT 12
 #define PUSHBTNHEIGHT 14
 #define PROGBARHEIGHT 14
 #define COMBOHEIGHT 12
 #define PUSHBTNHEIGHT 14
 #define PROGBARHEIGHT 14
@@ -55,10 +71,30 @@ HWND doctl(struct ctlpos *cp, RECT r,
     r.left += cp->xoff;
     MapDialogRect(cp->hwnd, &r);
 
     r.left += cp->xoff;
     MapDialogRect(cp->hwnd, &r);
 
-    ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
-                        r.left, r.top, r.right, r.bottom,
-                        cp->hwnd, (HMENU) wid, hinst, NULL);
-    SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
+    /*
+     * We can pass in cp->hwnd == NULL, to indicate a dry run
+     * without creating any actual controls.
+     */
+    if (cp->hwnd) {
+       ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
+                            r.left, r.top, r.right, r.bottom,
+                            cp->hwnd, (HMENU) wid, hinst, NULL);
+       SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
+
+       if (!strcmp(wclass, "LISTBOX")) {
+           /*
+            * Bizarre Windows bug: the list box calculates its
+            * number of lines based on the font it has at creation
+            * time, but sending it WM_SETFONT doesn't cause it to
+            * recalculate. So now, _after_ we've sent it
+            * WM_SETFONT, we explicitly resize it (to the same
+            * size it was already!) to force it to reconsider.
+            */
+           SetWindowPos(ctl, NULL, 0, 0, r.right, r.bottom,
+                        SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                        SWP_NOMOVE | SWP_NOZORDER);
+       }
+    }
     return ctl;
 }
 
     return ctl;
 }
 
@@ -116,14 +152,14 @@ void endbox(struct ctlpos *cp)
  * Some edit boxes. Each one has a static above it. The percentages
  * of the horizontal space are provided.
  */
  * Some edit boxes. Each one has a static above it. The percentages
  * of the horizontal space are provided.
  */
-void multiedit(struct ctlpos *cp, ...)
+void multiedit(struct ctlpos *cp, int password, ...)
 {
     RECT r;
     va_list ap;
     int percent, xpos;
 
     percent = xpos = 0;
 {
     RECT r;
     va_list ap;
     int percent, xpos;
 
     percent = xpos = 0;
-    va_start(ap, cp);
+    va_start(ap, password);
     while (1) {
        char *text;
        int staticid, editid, pcwidth;
     while (1) {
        char *text;
        int staticid, editid, pcwidth;
@@ -145,7 +181,8 @@ void multiedit(struct ctlpos *cp, ...)
        r.top = cp->ypos + 8 + GAPWITHIN;
        r.bottom = EDITHEIGHT;
        doctl(cp, r, "EDIT",
        r.top = cp->ypos + 8 + GAPWITHIN;
        r.bottom = EDITHEIGHT;
        doctl(cp, r, "EDIT",
-             WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
+             WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL |
+             (password ? ES_PASSWORD : 0),
              WS_EX_CLIENTEDGE, "", editid);
     }
     va_end(ap);
              WS_EX_CLIENTEDGE, "", editid);
     }
     va_end(ap);
@@ -174,29 +211,37 @@ void combobox(struct ctlpos *cp, char *text, int staticid, int listid)
     cp->ypos += STATICHEIGHT + GAPWITHIN + COMBOHEIGHT + GAPBETWEEN;
 }
 
     cp->ypos += STATICHEIGHT + GAPWITHIN + COMBOHEIGHT + GAPBETWEEN;
 }
 
-static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
+struct radio { char *text; int id; };
+
+static void radioline_common(struct ctlpos *cp, char *text, int id,
+                            int nacross, struct radio *buttons, int nbuttons)
 {
     RECT r;
     int group;
     int i;
 {
     RECT r;
     int group;
     int i;
-    char *btext;
+    int j;
+
+    if (text) {
+       r.left = GAPBETWEEN;
+       r.top = cp->ypos;
+       r.right = cp->width;
+       r.bottom = STATICHEIGHT;
+       cp->ypos += r.bottom + GAPWITHIN;
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
+    }
 
     group = WS_GROUP;
     i = 0;
 
     group = WS_GROUP;
     i = 0;
-    btext = va_arg(ap, char *);
-    while (1) {
-       char *nextbtext;
-       int bid;
-       if (!btext)
-           break;
+    for (j = 0; j < nbuttons; j++) {
+       char *btext = buttons[j].text;
+       int bid = buttons[j].id;
+
        if (i == nacross) {
        if (i == nacross) {
-           cp->ypos += r.bottom + GAPBETWEEN;
+           cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN);
            i = 0;
        }
            i = 0;
        }
-       bid = va_arg(ap, int);
-       nextbtext = va_arg(ap, char *);
        r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross;
        r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross;
-       if (nextbtext)
+       if (j < nbuttons-1)
            r.right =
                (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left;
        else
            r.right =
                (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left;
        else
@@ -204,11 +249,10 @@ static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
        r.top = cp->ypos;
        r.bottom = RADIOHEIGHT;
        doctl(cp, r, "BUTTON",
        r.top = cp->ypos;
        r.bottom = RADIOHEIGHT;
        doctl(cp, r, "BUTTON",
-             BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
-             group, 0, btext, bid);
+             BS_NOTIFY | BS_AUTORADIOBUTTON | WS_CHILD |
+             WS_VISIBLE | WS_TABSTOP | group, 0, btext, bid);
        group = 0;
        i++;
        group = 0;
        i++;
-       btext = nextbtext;
     }
     cp->ypos += r.bottom + GAPBETWEEN;
 }
     }
     cp->ypos += r.bottom + GAPBETWEEN;
 }
@@ -229,18 +273,29 @@ static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
  */
 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
 {
  */
 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
 {
-    RECT r;
     va_list ap;
     va_list ap;
+    struct radio *buttons;
+    int i, nbuttons;
 
 
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
     va_start(ap, nacross);
     va_start(ap, nacross);
-    radioline_common(cp, nacross, ap);
+    nbuttons = 0;
+    while (1) {
+       char *btext = va_arg(ap, char *);
+       int bid;
+       if (!btext)
+           break;
+       bid = va_arg(ap, int);
+    }
+    va_end(ap);
+    buttons = smalloc(nbuttons * sizeof(struct radio));
+    va_start(ap, nacross);
+    for (i = 0; i < nbuttons; i++) {
+       buttons[i].text = va_arg(ap, char *);
+       buttons[i].id = va_arg(ap, int);
+    }
     va_end(ap);
     va_end(ap);
+    radioline_common(cp, text, id, nacross, buttons, nbuttons);
+    sfree(buttons);
 }
 
 /*
 }
 
 /*
@@ -250,10 +305,28 @@ void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
 void bareradioline(struct ctlpos *cp, int nacross, ...)
 {
     va_list ap;
 void bareradioline(struct ctlpos *cp, int nacross, ...)
 {
     va_list ap;
+    struct radio *buttons;
+    int i, nbuttons;
 
     va_start(ap, nacross);
 
     va_start(ap, nacross);
-    radioline_common(cp, nacross, ap);
+    nbuttons = 0;
+    while (1) {
+       char *btext = va_arg(ap, char *);
+       int bid;
+       if (!btext)
+           break;
+       bid = va_arg(ap, int);
+    }
+    va_end(ap);
+    buttons = smalloc(nbuttons * sizeof(struct radio));
+    va_start(ap, nacross);
+    for (i = 0; i < nbuttons; i++) {
+       buttons[i].text = va_arg(ap, char *);
+       buttons[i].id = va_arg(ap, int);
+    }
     va_end(ap);
     va_end(ap);
+    radioline_common(cp, NULL, 0, nacross, buttons, nbuttons);
+    sfree(buttons);
 }
 
 /*
 }
 
 /*
@@ -262,37 +335,29 @@ void bareradioline(struct ctlpos *cp, int nacross, ...)
  */
 void radiobig(struct ctlpos *cp, char *text, int id, ...)
 {
  */
 void radiobig(struct ctlpos *cp, char *text, int id, ...)
 {
-    RECT r;
     va_list ap;
     va_list ap;
-    int group;
+    struct radio *buttons;
+    int i, nbuttons;
 
 
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
     va_start(ap, id);
     va_start(ap, id);
-    group = WS_GROUP;
+    nbuttons = 0;
     while (1) {
     while (1) {
-       char *btext;
+       char *btext = va_arg(ap, char *);
        int bid;
        int bid;
-       btext = va_arg(ap, char *);
        if (!btext)
            break;
        bid = va_arg(ap, int);
        if (!btext)
            break;
        bid = va_arg(ap, int);
-       r.left = GAPBETWEEN;
-       r.top = cp->ypos;
-       r.right = cp->width;
-       r.bottom = STATICHEIGHT;
-       cp->ypos += r.bottom + GAPWITHIN;
-       doctl(cp, r, "BUTTON",
-             BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
-             group, 0, btext, bid);
-       group = 0;
     }
     va_end(ap);
     }
     va_end(ap);
-    cp->ypos += GAPBETWEEN - GAPWITHIN;
+    buttons = smalloc(nbuttons * sizeof(struct radio));
+    va_start(ap, id);
+    for (i = 0; i < nbuttons; i++) {
+       buttons[i].text = va_arg(ap, char *);
+       buttons[i].id = va_arg(ap, int);
+    }
+    va_end(ap);
+    radioline_common(cp, text, id, 1, buttons, nbuttons);
+    sfree(buttons);
 }
 
 /*
 }
 
 /*
@@ -308,10 +373,92 @@ void checkbox(struct ctlpos *cp, char *text, int id)
     r.bottom = CHECKBOXHEIGHT;
     cp->ypos += r.bottom + GAPBETWEEN;
     doctl(cp, r, "BUTTON",
     r.bottom = CHECKBOXHEIGHT;
     cp->ypos += r.bottom + GAPBETWEEN;
     doctl(cp, r, "BUTTON",
-         BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
+         BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
          text, id);
 }
 
          text, id);
 }
 
+/*
+ * Wrap a piece of text for a static text control. Returns the
+ * wrapped text (a malloc'ed string containing \ns), and also
+ * returns the number of lines required.
+ */
+char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines)
+{
+    HFONT font = (HFONT) cp->font;
+    HDC hdc = GetDC(hwnd);
+    int lpx = GetDeviceCaps(hdc, LOGPIXELSX);
+    int width, nlines, j;
+    INT *pwidths, nfit;
+    SIZE size;
+    char *ret, *p, *q;
+    RECT r;
+
+    ret = smalloc(1+strlen(text));
+    p = text;
+    q = ret;
+    pwidths = smalloc(sizeof(INT)*(1+strlen(text)));
+
+    /*
+     * Work out the width the text will need to fit in, by doing
+     * the same adjustment that the `statictext' function itself
+     * will perform.
+     * 
+     * We must first convert from dialog-box units into pixels, and
+     * then from pixels into the `logical units' that Windows uses
+     * within GDI. You can't make this stuff up.
+     */
+    r.left = r.top = r.bottom = 0;
+    r.right = cp->width;
+    MapDialogRect(hwnd, &r);
+    width = MulDiv(r.right, lpx, 72);
+
+    nlines = 1;
+
+    while (*p) {
+       if (!GetTextExtentExPoint(hdc, p, strlen(p), width,
+                                 &nfit, pwidths, &size) ||
+           (size_t)nfit >= strlen(p)) {
+           /*
+            * Either GetTextExtentExPoint returned failure, or the
+            * whole of the rest of the text fits on this line.
+            * Either way, we stop wrapping, copy the remainder of
+            * the input string unchanged to the output, and leave.
+            */
+           strcpy(q, p);
+           break;
+       }
+
+       /*
+        * Now we search backwards along the string from `nfit',
+        * looking for a space at which to break the line. If we
+        * don't find one at all, that's fine - we'll just break
+        * the line at `nfit'.
+        */
+       for (j = nfit; j > 0; j--) {
+           if (isspace((unsigned char)p[j])) {
+               nfit = j;
+               break;
+           }
+       }
+
+       strncpy(q, p, nfit);
+       q[nfit] = '\n';
+       q += nfit+1;
+
+       p += nfit;
+       while (*p && isspace((unsigned char)*p))
+           p++;
+
+       nlines++;
+    }
+
+    ReleaseDC(cp->hwnd, hdc);
+
+    if (lines) *lines = nlines;
+
+    return ret;
+}
+
 /*
  * A single standalone static text control.
  */
 /*
  * A single standalone static text control.
  */
@@ -324,7 +471,25 @@ void statictext(struct ctlpos *cp, char *text, int lines, int id)
     r.right = cp->width;
     r.bottom = STATICHEIGHT * lines;
     cp->ypos += r.bottom + GAPBETWEEN;
     r.right = cp->width;
     r.bottom = STATICHEIGHT * lines;
     cp->ypos += r.bottom + GAPBETWEEN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
+    doctl(cp, r, "STATIC",
+         WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,
+         0, text, id);
+}
+
+/*
+ * An owner-drawn static text control for a panel title.
+ */
+void paneltitle(struct ctlpos *cp, int id)
+{
+    RECT r;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos;
+    r.right = cp->width;
+    r.bottom = TITLEHEIGHT;
+    cp->ypos += r.bottom + GAPBETWEEN;
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_OWNERDRAW,
+         0, NULL, id);
 }
 
 /*
 }
 
 /*
@@ -353,12 +518,37 @@ void staticbtn(struct ctlpos *cp, char *stext, int sid,
     r.right = rwid;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
     r.right = rwid;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
          0, btext, bid);
 
     cp->ypos += height + GAPBETWEEN;
 }
 
          0, btext, bid);
 
     cp->ypos += height + GAPBETWEEN;
 }
 
+/*
+ * A simple push button.
+ */
+void button(struct ctlpos *cp, char *btext, int bid, int defbtn)
+{
+    RECT r;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos;
+    r.right = cp->width;
+    r.bottom = PUSHBTNHEIGHT;
+
+    /* Q67655: the _dialog box_ must know which button is default
+     * as well as the button itself knowing */
+    if (defbtn && cp->hwnd)
+       SendMessage(cp->hwnd, DM_SETDEFID, bid, 0);
+
+    doctl(cp, r, "BUTTON",
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+         (defbtn ? BS_DEFPUSHBUTTON : 0) | BS_PUSHBUTTON,
+         0, btext, bid);
+
+    cp->ypos += PUSHBTNHEIGHT + GAPBETWEEN;
+}
+
 /*
  * Like staticbtn, but two buttons.
  */
 /*
  * Like staticbtn, but two buttons.
  */
@@ -387,7 +577,7 @@ void static2btn(struct ctlpos *cp, char *stext, int sid,
     r.right = rwid1;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
     r.right = rwid1;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
          0, btext1, bid1);
 
     r.left = rpos2;
          0, btext1, bid1);
 
     r.left = rpos2;
@@ -395,7 +585,7 @@ void static2btn(struct ctlpos *cp, char *stext, int sid,
     r.right = rwid2;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
     r.right = rwid2;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
          0, btext2, bid2);
 
     cp->ypos += height + GAPBETWEEN;
          0, btext2, bid2);
 
     cp->ypos += height + GAPBETWEEN;
@@ -481,6 +671,64 @@ void staticddl(struct ctlpos *cp, char *stext,
     cp->ypos += height + GAPBETWEEN;
 }
 
     cp->ypos += height + GAPBETWEEN;
 }
 
+/*
+ * A combo box on the right hand side, with a static to its left.
+ */
+void staticcombo(struct ctlpos *cp, char *stext,
+                int sid, int lid, int percentlist)
+{
+    const int height = (COMBOHEIGHT > STATICHEIGHT ?
+                       COMBOHEIGHT : STATICHEIGHT);
+    RECT r;
+    int lwid, rwid, rpos;
+
+    rpos =
+       GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100;
+    lwid = rpos - 2 * GAPBETWEEN;
+    rwid = cp->width + GAPBETWEEN - rpos;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;
+    r.right = lwid;
+    r.bottom = STATICHEIGHT;
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+
+    r.left = rpos;
+    r.top = cp->ypos + (height - EDITHEIGHT) / 2;
+    r.right = rwid;
+    r.bottom = COMBOHEIGHT*10;
+    doctl(cp, r, "COMBOBOX",
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
+         CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);
+
+    cp->ypos += height + GAPBETWEEN;
+}
+
+/*
+ * A static, with a full-width drop-down list box below it.
+ */
+void staticddlbig(struct ctlpos *cp, char *stext,
+                 int sid, int lid)
+{
+    RECT r;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos;
+    r.right = cp->width;
+    r.bottom = STATICHEIGHT;
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+    cp->ypos += STATICHEIGHT;
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos;
+    r.right = cp->width;
+    r.bottom = COMBOHEIGHT*4;
+    doctl(cp, r, "COMBOBOX",
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+         CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);
+    cp->ypos += COMBOHEIGHT + GAPBETWEEN;
+}
+
 /*
  * A big multiline edit control with a static labelling it.
  */
 /*
  * A big multiline edit control with a static labelling it.
  */
@@ -506,6 +754,35 @@ void bigeditctrl(struct ctlpos *cp, char *stext,
          WS_EX_CLIENTEDGE, "", eid);
 }
 
          WS_EX_CLIENTEDGE, "", eid);
 }
 
+/*
+ * A list box with a static labelling it.
+ */
+void listbox(struct ctlpos *cp, char *stext,
+            int sid, int lid, int lines, int multi)
+{
+    RECT r;
+
+    if (stext != NULL) {
+       r.left = GAPBETWEEN;
+       r.top = cp->ypos;
+       r.right = cp->width;
+       r.bottom = STATICHEIGHT;
+       cp->ypos += r.bottom + GAPWITHIN;
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+    }
+
+    r.left = GAPBETWEEN;
+    r.top = cp->ypos;
+    r.right = cp->width;
+    r.bottom = LISTHEIGHT + (lines - 1) * LISTINCREMENT;
+    cp->ypos += r.bottom + GAPBETWEEN;
+    doctl(cp, r, "LISTBOX",
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
+         LBS_NOTIFY | LBS_HASSTRINGS | LBS_USETABSTOPS |
+         (multi ? LBS_MULTIPLESEL : 0),
+         WS_EX_CLIENTEDGE, "", lid);
+}
+
 /*
  * A tab-control substitute when a real tab control is unavailable.
  */
 /*
  * A tab-control substitute when a real tab control is unavailable.
  */
@@ -584,320 +861,48 @@ void editbutton(struct ctlpos *cp, char *stext, int sid,
     r.right = rwid;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
     r.right = rwid;
     r.bottom = PUSHBTNHEIGHT;
     doctl(cp, r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
          0, btext, bid);
 
     cp->ypos += height + GAPBETWEEN;
 }
 
 /*
          0, btext, bid);
 
     cp->ypos += height + GAPBETWEEN;
 }
 
 /*
- * Special control which was hard to describe generically: the
- * session-saver assembly. A static; below that an edit box; below
- * that a list box. To the right of the list box, a column of
- * buttons.
+ * A special control for manipulating an ordered preference list
+ * (eg. for cipher selection).
+ * XXX: this is a rough hack and could be improved.
  */
  */
-void sesssaver(struct ctlpos *cp, char *text,
-              int staticid, int editid, int listid, ...)
+void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,
+              char *stext, int sid, int listid, int upbid, int dnbid)
 {
 {
+    const static int percents[] = { 5, 75, 20 };
     RECT r;
     RECT r;
-    va_list ap;
-    int lwid, rwid, rpos;
-    int y;
-    const int LISTDEFHEIGHT = 66;
-
-    rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
-    lwid = rpos - 2 * GAPBETWEEN;
-    rwid = cp->width + GAPBETWEEN - rpos;
-
-    /* The static control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = lwid;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
+    int xpos, percent = 0, i;
+    int listheight = LISTHEIGHT + (lines - 1) * LISTINCREMENT;
+    const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;
+    int totalheight, buttonpos;
 
 
-    /* The edit control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = lwid;
-    r.bottom = EDITHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "EDIT",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
-         WS_EX_CLIENTEDGE, "", editid);
+    /* Squirrel away IDs. */
+    hdl->listid = listid;
+    hdl->upbid  = upbid;
+    hdl->dnbid  = dnbid;
 
 
-    /*
-     * The buttons (we should hold off on the list box until we
-     * know how big the buttons are).
-     */
-    va_start(ap, listid);
-    y = cp->ypos;
-    while (1) {
-       char *btext = va_arg(ap, char *);
-       int bid;
-       if (!btext)
-           break;
-       bid = va_arg(ap, int);
-       r.left = rpos;
-       r.top = y;
-       r.right = rwid;
-       r.bottom = PUSHBTNHEIGHT;
-       y += r.bottom + GAPWITHIN;
-       doctl(cp, r, "BUTTON",
-             WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
-             0, btext, bid);
+    /* The static label. */
+    if (stext != NULL) {
+       r.left = GAPBETWEEN;
+       r.top = cp->ypos;
+       r.right = cp->width;
+       r.bottom = STATICHEIGHT;
+       cp->ypos += r.bottom + GAPWITHIN;
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
     }
 
     }
 
-    /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
-    y -= cp->ypos;
-    y -= GAPWITHIN;
-    if (y < LISTDEFHEIGHT)
-       y = LISTDEFHEIGHT;
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = lwid;
-    r.bottom = y;
-    cp->ypos += y + GAPBETWEEN;
-    doctl(cp, r, "LISTBOX",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
-         LBS_NOTIFY | LBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);
-}
-
-/*
- * Another special control: the environment-variable setter. A
- * static line first; then a pair of edit boxes with associated
- * statics, and two buttons; then a list box.
- */
-void envsetter(struct ctlpos *cp, char *stext, int sid,
-              char *e1stext, int e1sid, int e1id,
-              char *e2stext, int e2sid, int e2id,
-              int listid, char *b1text, int b1id, char *b2text, int b2id)
-{
-    RECT r;
-    const int height = (STATICHEIGHT > EDITHEIGHT
-                       && STATICHEIGHT >
-                       PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
-                       PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
-    const static int percents[] = { 20, 35, 10, 25 };
-    int i, j, xpos, percent;
-    const int LISTHEIGHT = 42;
-
-    /* The static control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
-
-    /* The statics+edits+buttons. */
-    for (j = 0; j < 2; j++) {
-       percent = 10;
-       for (i = 0; i < 4; i++) {
-           xpos = (cp->width + GAPBETWEEN) * percent / 100;
-           r.left = xpos + GAPBETWEEN;
-           percent += percents[i];
-           xpos = (cp->width + GAPBETWEEN) * percent / 100;
-           r.right = xpos - r.left;
-           r.top = cp->ypos;
-           r.bottom = (i == 0 ? STATICHEIGHT :
-                       i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
-           r.top += (height - r.bottom) / 2;
-           if (i == 0) {
-               doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
-                     j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
-           } else if (i == 1) {
-               doctl(cp, r, "EDIT",
-                     WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
-                     WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
-           } else if (i == 3) {
-               doctl(cp, r, "BUTTON",
-                     WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
-                     0, j == 0 ? b1text : b2text, j == 0 ? b1id : b2id);
-           }
-       }
-       cp->ypos += height + GAPWITHIN;
-    }
-
-    /* The list box. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = LISTHEIGHT;
-    cp->ypos += r.bottom + GAPBETWEEN;
-    doctl(cp, r, "LISTBOX",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
-         | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
-}
-
-/*
- * Yet another special control: the character-class setter. A
- * static, then a list, then a line containing a
- * button-and-static-and-edit. 
- */
-void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
-              char *btext, int bid, int eid, char *s2text, int s2id)
-{
-    RECT r;
-    const int height = (STATICHEIGHT > EDITHEIGHT
-                       && STATICHEIGHT >
-                       PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
-                       PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
-    const static int percents[] = { 30, 40, 30 };
-    int i, xpos, percent;
-    const int LISTHEIGHT = 52;
-
-    /* The static control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
-
-    /* The list box. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = LISTHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "LISTBOX",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
-         | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
-
-    /* The button+static+edit. */
-    percent = xpos = 0;
-    for (i = 0; i < 3; i++) {
-       r.left = xpos + GAPBETWEEN;
-       percent += percents[i];
-       xpos = (cp->width + GAPBETWEEN) * percent / 100;
-       r.right = xpos - r.left;
-       r.top = cp->ypos;
-       r.bottom = (i == 0 ? PUSHBTNHEIGHT :
-                   i == 1 ? STATICHEIGHT : EDITHEIGHT);
-       r.top += (height - r.bottom) / 2;
-       if (i == 0) {
-           doctl(cp, r, "BUTTON",
-                 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
-                 0, btext, bid);
-       } else if (i == 1) {
-           doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER,
-                 0, s2text, s2id);
-       } else if (i == 2) {
-           doctl(cp, r, "EDIT",
-                 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
-                 WS_EX_CLIENTEDGE, "", eid);
-       }
-    }
-    cp->ypos += height + GAPBETWEEN;
-}
-
-/*
- * A special control (horrors!). The colour editor. A static line;
- * then on the left, a list box, and on the right, a sequence of
- * two-part statics followed by a button.
- */
-void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
-               char *btext, int bid, ...)
-{
-    RECT r;
-    int y;
-    va_list ap;
-    int lwid, rwid, rpos;
-    const int LISTHEIGHT = 66;
-
-    /* The static control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
-
-    rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3;
-    lwid = rpos - 2 * GAPBETWEEN;
-    rwid = cp->width + GAPBETWEEN - rpos;
-
-    /* The list box. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = lwid;
-    r.bottom = LISTHEIGHT;
-    doctl(cp, r, "LISTBOX",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
-         | LBS_USETABSTOPS | LBS_NOTIFY, WS_EX_CLIENTEDGE, "", listid);
-
-    /* The statics. */
-    y = cp->ypos;
-    va_start(ap, bid);
-    while (1) {
-       char *ltext;
-       int lid, rid;
-       ltext = va_arg(ap, char *);
-       if (!ltext)
-           break;
-       lid = va_arg(ap, int);
-       rid = va_arg(ap, int);
-       r.top = y;
-       r.bottom = STATICHEIGHT;
-       y += r.bottom + GAPWITHIN;
-       r.left = rpos;
-       r.right = rwid / 2;
-       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid);
-       r.left = rpos + r.right;
-       r.right = rwid - r.right;
-       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "",
-             rid);
-    }
-    va_end(ap);
-
-    /* The button. */
-    r.top = y + 2 * GAPWITHIN;
-    r.bottom = PUSHBTNHEIGHT;
-    r.left = rpos;
-    r.right = rwid;
-    doctl(cp, r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
-         0, btext, bid);
-
-    cp->ypos += LISTHEIGHT + GAPBETWEEN;
-}
-
-/*
- * A special control for manipulating an ordered preference list
- * (eg. for cipher selection).
- * XXX: this is a rough hack and could be improved.
- */
-void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
-               int sid, int listid, int upbid, int dnbid)
-{
-    const static int percents[] = { 5, 75, 20 };
-    RECT r;
-    int xpos, percent = 0, i;
-    const int DEFLISTHEIGHT = 52;      /* XXX configurable? */
-    const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;
-    int totalheight;
-
-    /* Squirrel away IDs. */
-    hdl->listid = listid;
-    hdl->upbid  = upbid;
-    hdl->dnbid  = dnbid;
-
-    /* The static label. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
-
-    /* XXX it'd be nice to centre the buttons wrt the listbox
-     * but we'd have to find out how high the latter actually is. */
-    if (DEFLISTHEIGHT > BTNSHEIGHT) {
-        totalheight = DEFLISTHEIGHT;
-    } else {
-        totalheight = BTNSHEIGHT;
+    if (listheight > BTNSHEIGHT) {
+        totalheight = listheight;
+       buttonpos = (listheight - BTNSHEIGHT) / 2;
+    } else {
+        totalheight = BTNSHEIGHT;
+       buttonpos = 0;
     }
 
     for (i=0; i<3; i++) {
     }
 
     for (i=0; i<3; i++) {
@@ -912,12 +917,12 @@ void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
           case 1:
             /* The drag list box. */
             r.left = left; r.right = wid;
           case 1:
             /* The drag list box. */
             r.left = left; r.right = wid;
-            r.top = cp->ypos; r.bottom = totalheight;
+            r.top = cp->ypos; r.bottom = listheight;
             {
                 HWND ctl;
                 ctl = doctl(cp, r, "LISTBOX",
                             WS_CHILD | WS_VISIBLE | WS_TABSTOP |
             {
                 HWND ctl;
                 ctl = doctl(cp, r, "LISTBOX",
                             WS_CHILD | WS_VISIBLE | WS_TABSTOP |
-                           WS_VSCROLL | LBS_HASSTRINGS,
+                           WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS,
                             WS_EX_CLIENTEDGE,
                             "", listid);
                MakeDragList(ctl);
                             WS_EX_CLIENTEDGE,
                             "", listid);
                MakeDragList(ctl);
@@ -929,16 +934,18 @@ void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
            /* XXX worry about accelerators if we have more than one
             * prefslist on a panel */
             r.left = left; r.right = wid;
            /* XXX worry about accelerators if we have more than one
             * prefslist on a panel */
             r.left = left; r.right = wid;
-            r.top = cp->ypos; r.bottom = PUSHBTNHEIGHT;
+            r.top = cp->ypos + buttonpos; r.bottom = PUSHBTNHEIGHT;
             doctl(cp, r, "BUTTON",
             doctl(cp, r, "BUTTON",
-                  WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+                  BS_NOTIFY | WS_CHILD | WS_VISIBLE |
+                 WS_TABSTOP | BS_PUSHBUTTON,
                   0, "&Up", upbid);
 
             r.left = left; r.right = wid;
                   0, "&Up", upbid);
 
             r.left = left; r.right = wid;
-            r.top = cp->ypos + PUSHBTNHEIGHT + GAPBETWEEN;
+            r.top = cp->ypos + buttonpos + PUSHBTNHEIGHT + GAPBETWEEN;
             r.bottom = PUSHBTNHEIGHT;
             doctl(cp, r, "BUTTON",
             r.bottom = PUSHBTNHEIGHT;
             doctl(cp, r, "BUTTON",
-                  WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+                  BS_NOTIFY | WS_CHILD | WS_VISIBLE |
+                 WS_TABSTOP | BS_PUSHBUTTON,
                   0, "&Down", dnbid);
 
             break;
                   0, "&Down", dnbid);
 
             break;
@@ -1016,6 +1023,10 @@ int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
 
 /*
  * Handler for prefslist above.
 
 /*
  * Handler for prefslist above.
+ * 
+ * Return value has bit 0 set if the dialog box procedure needs to
+ * return TRUE from handling this message; it has bit 1 set if a
+ * change may have been made in the contents of the list.
  */
 int handle_prefslist(struct prefslist *hdl,
                      int *array, int maxmemb,
  */
 int handle_prefslist(struct prefslist *hdl,
                      int *array, int maxmemb,
@@ -1023,7 +1034,7 @@ int handle_prefslist(struct prefslist *hdl,
                     WPARAM wParam, LPARAM lParam)
 {
     int i;
                     WPARAM wParam, LPARAM lParam)
 {
     int i;
-    int ret;
+    int ret = 0;
 
     if (is_dlmsg) {
 
 
     if (is_dlmsg) {
 
@@ -1040,13 +1051,13 @@ int handle_prefslist(struct prefslist *hdl,
                hdl->dragging = 0;
                /* XXX hack Q183115 */
                SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
                hdl->dragging = 0;
                /* XXX hack Q183115 */
                SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
-                ret = 1; break;
+                ret |= 1; break;
               case DL_CANCELDRAG:
                DrawInsert(hwnd, dlm->hWnd, -1);     /* Clear arrow */
                SendDlgItemMessage(hwnd, hdl->listid,
                                   LB_DELETESTRING, hdl->dummyitem, 0);
                hdl->dragging = 0;
               case DL_CANCELDRAG:
                DrawInsert(hwnd, dlm->hWnd, -1);     /* Clear arrow */
                SendDlgItemMessage(hwnd, hdl->listid,
                                   LB_DELETESTRING, hdl->dummyitem, 0);
                hdl->dragging = 0;
-                ret = 1; break;
+                ret |= 1; break;
               case DL_DRAGGING:
                hdl->dragging = 1;
                dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
               case DL_DRAGGING:
                hdl->dragging = 1;
                dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
@@ -1056,7 +1067,7 @@ int handle_prefslist(struct prefslist *hdl,
                    SetWindowLong(hwnd, DWL_MSGRESULT, DL_MOVECURSOR);
                else
                    SetWindowLong(hwnd, DWL_MSGRESULT, DL_STOPCURSOR);
                    SetWindowLong(hwnd, DWL_MSGRESULT, DL_MOVECURSOR);
                else
                    SetWindowLong(hwnd, DWL_MSGRESULT, DL_STOPCURSOR);
-                ret = 1; break;
+                ret |= 1; break;
               case DL_DROPPED:
                if (hdl->dragging) {
                    dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
               case DL_DROPPED:
                if (hdl->dragging) {
                    dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
@@ -1072,14 +1083,14 @@ int handle_prefslist(struct prefslist *hdl,
                        if (dest > hdl->srcitem) dest--;
                        pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);
                    }
                        if (dest > hdl->srcitem) dest--;
                        pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);
                    }
+                   ret |= 2;
                }
                }
-                ret = 1; break;
+                ret |= 1; break;
             }
         }
 
     } else {
 
             }
         }
 
     } else {
 
-        ret = 0;
         if (((LOWORD(wParam) == hdl->upbid) ||
              (LOWORD(wParam) == hdl->dnbid)) &&
             ((HIWORD(wParam) == BN_CLICKED) ||
         if (((LOWORD(wParam) == hdl->upbid) ||
              (LOWORD(wParam) == hdl->dnbid)) &&
             ((HIWORD(wParam) == BN_CLICKED) ||
@@ -1098,19 +1109,21 @@ int handle_prefslist(struct prefslist *hdl,
                    pl_moveitem(hwnd, hdl->listid, selection, selection - 1);
                else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))
                    pl_moveitem(hwnd, hdl->listid, selection, selection + 1);
                    pl_moveitem(hwnd, hdl->listid, selection, selection - 1);
                else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))
                    pl_moveitem(hwnd, hdl->listid, selection, selection + 1);
+               ret |= 2;
             }
 
         }
 
     }
 
             }
 
         }
 
     }
 
-    /* Update array to match the list box. */
-    for (i=0; i < maxmemb; i++)
-        array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,
-                                       i, 0);
+    if (array) {
+       /* Update array to match the list box. */
+       for (i=0; i < maxmemb; i++)
+           array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,
+                                          i, 0);
+    }
 
     return ret;
 
     return ret;
-
 }
 
 /*
 }
 
 /*
@@ -1135,82 +1148,1267 @@ void progressbar(struct ctlpos *cp, int id)
          , WS_EX_CLIENTEDGE, "", id);
 }
 
          , WS_EX_CLIENTEDGE, "", id);
 }
 
+/* ----------------------------------------------------------------------
+ * Platform-specific side of portable dialog-box mechanism.
+ */
+
 /*
 /*
- * Another special control: the forwarding options setter. First a
- * list box; next a static header line, introducing a pair of edit
- * boxes with associated statics, another button, and a radio
- * button pair. Then we have a bareradioline, which is included in
- * this control group because it belongs before the `Add' button in
- * the tab order.
+ * This function takes a string, escapes all the ampersands, and
+ * places a single (unescaped) ampersand in front of the first
+ * occurrence of the given shortcut character (which may be
+ * NO_SHORTCUT).
+ * 
+ * Return value is a malloc'ed copy of the processed version of the
+ * string.
  */
  */
-void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
-              char *e1stext, int e1sid, int e1id,
-              char *e2stext, int e2sid, int e2id,
-              char *btext, int bid,
-              char *r1text, int r1id, char *r2text, int r2id)
-{
-    RECT r, button_r;
-    const int height = (STATICHEIGHT > EDITHEIGHT
-                       && STATICHEIGHT >
-                       PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
-                       PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
-    const static int percents[] = { 25, 35, 15, 25 };
-    int i, j, xpos, percent;
-    const int LISTHEIGHT = 42;
-
-    /* The list box. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = LISTHEIGHT;
-    cp->ypos += r.bottom + GAPBETWEEN;
-    doctl(cp, r, "LISTBOX",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
-         | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
+static char *shortcut_escape(char *text, char shortcut)
+{
+    char *ret;
+    char *p, *q;
+
+    if (!text)
+       return NULL;                   /* sfree won't choke on this */
+
+    ret = smalloc(2*strlen(text)+1);   /* size potentially doubles! */
+    shortcut = tolower((unsigned char)shortcut);
+
+    p = text;
+    q = ret;
+    while (*p) {
+       if (shortcut != NO_SHORTCUT &&
+           tolower((unsigned char)*p) == shortcut) {
+           *q++ = '&';
+           shortcut = NO_SHORTCUT;    /* stop it happening twice */
+       } else if (*p == '&') {
+           *q++ = '&';
+       }
+       *q++ = *p++;
+    }
+    *q = '\0';
+    return ret;
+}
 
 
-    /* The static control. */
-    r.left = GAPBETWEEN;
-    r.top = cp->ypos;
-    r.right = cp->width;
-    r.bottom = STATICHEIGHT;
-    cp->ypos += r.bottom + GAPWITHIN;
-    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+void winctrl_add_shortcuts(struct dlgparam *dp, struct winctrl *c)
+{
+    int i;
+    for (i = 0; i < lenof(c->shortcuts); i++)
+       if (c->shortcuts[i] != NO_SHORTCUT) {
+           unsigned char s = tolower((unsigned char)c->shortcuts[i]);
+           assert(!dp->shortcuts[s]);
+           dp->shortcuts[s] = TRUE;
+       }
+}
+
+void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c)
+{
+    int i;
+    for (i = 0; i < lenof(c->shortcuts); i++)
+       if (c->shortcuts[i] != NO_SHORTCUT) {
+           unsigned char s = tolower((unsigned char)c->shortcuts[i]);
+           assert(dp->shortcuts[s]);
+           dp->shortcuts[s] = FALSE;
+       }
+}
+
+static int winctrl_cmp_byctrl(void *av, void *bv)
+{
+    struct winctrl *a = (struct winctrl *)av;
+    struct winctrl *b = (struct winctrl *)bv;
+    if (a->ctrl < b->ctrl)
+       return -1;
+    else if (a->ctrl > b->ctrl)
+       return +1;
+    else
+       return 0;
+}
+static int winctrl_cmp_byid(void *av, void *bv)
+{
+    struct winctrl *a = (struct winctrl *)av;
+    struct winctrl *b = (struct winctrl *)bv;
+    if (a->base_id < b->base_id)
+       return -1;
+    else if (a->base_id > b->base_id)
+       return +1;
+    else
+       return 0;
+}
+static int winctrl_cmp_byctrl_find(void *av, void *bv)
+{
+    union control *a = (union control *)av;
+    struct winctrl *b = (struct winctrl *)bv;
+    if (a < b->ctrl)
+       return -1;
+    else if (a > b->ctrl)
+       return +1;
+    else
+       return 0;
+}
+static int winctrl_cmp_byid_find(void *av, void *bv)
+{
+    int *a = (int *)av;
+    struct winctrl *b = (struct winctrl *)bv;
+    if (*a < b->base_id)
+       return -1;
+    else if (*a >= b->base_id + b->num_ids)
+       return +1;
+    else
+       return 0;
+}
+
+void winctrl_init(struct winctrls *wc)
+{
+    wc->byctrl = newtree234(winctrl_cmp_byctrl);
+    wc->byid = newtree234(winctrl_cmp_byid);
+}
+void winctrl_cleanup(struct winctrls *wc)
+{
+    struct winctrl *c;
+
+    while ((c = index234(wc->byid, 0)) != NULL) {
+       winctrl_remove(wc, c);
+       sfree(c->data);
+       sfree(c);
+    }
 
 
-    /* The statics+edits+buttons. */
-    for (j = 0; j < 2; j++) {
-       percent = 0;
-       for (i = 0; i < (j ? 2 : 4); i++) {
-           xpos = (cp->width + GAPBETWEEN) * percent / 100;
-           r.left = xpos + GAPBETWEEN;
-           percent += percents[i];
-           if (j==1 && i==1) percent = 100;
-           xpos = (cp->width + GAPBETWEEN) * percent / 100;
-           r.right = xpos - r.left;
-           r.top = cp->ypos;
-           r.bottom = (i == 0 ? STATICHEIGHT :
-                       i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
-           r.top += (height - r.bottom) / 2;
-           if (i == 0) {
-               doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
-                     j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
-           } else if (i == 1) {
-               doctl(cp, r, "EDIT",
-                     WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
-                     WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
-           } else if (i == 3) {
+    freetree234(wc->byctrl);
+    freetree234(wc->byid);
+    wc->byctrl = wc->byid = NULL;
+}
+
+void winctrl_add(struct winctrls *wc, struct winctrl *c)
+{
+    struct winctrl *ret;
+    if (c->ctrl) {
+       ret = add234(wc->byctrl, c);
+       assert(ret == c);
+    }
+    ret = add234(wc->byid, c);
+    assert(ret == c);
+}
+
+void winctrl_remove(struct winctrls *wc, struct winctrl *c)
+{
+    struct winctrl *ret;
+    ret = del234(wc->byctrl, c);
+    ret = del234(wc->byid, c);
+    assert(ret == c);
+}
+
+struct winctrl *winctrl_findbyctrl(struct winctrls *wc, union control *ctrl)
+{
+    return find234(wc->byctrl, ctrl, winctrl_cmp_byctrl_find);
+}
+
+struct winctrl *winctrl_findbyid(struct winctrls *wc, int id)
+{
+    return find234(wc->byid, &id, winctrl_cmp_byid_find);
+}
+
+struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index)
+{
+    return index234(wc->byid, index);
+}
+
+void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
+                   struct ctlpos *cp, struct controlset *s, int *id)
+{
+    struct ctlpos columns[16];
+    int ncols, colstart, colspan;
+
+    struct ctlpos tabdelays[16];
+    union control *tabdelayed[16];
+    int ntabdelays;
+
+    struct ctlpos pos;
+
+    char shortcuts[MAX_SHORTCUTS_PER_CTRL], nshortcuts;
+    char *escaped;
+    int i, base_id, num_ids, orig_tabdelay;
+    void *data;
+
+    base_id = *id;
+
+    /* Start a containing box, if we have a boxname. */
+    if (s->boxname && *s->boxname) {
+       struct winctrl *c = smalloc(sizeof(struct winctrl));
+       c->ctrl = NULL;
+       c->base_id = base_id;
+       c->num_ids = 1;
+       c->data = NULL;
+       memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));
+       winctrl_add(wc, c);
+       beginbox(cp, s->boxtitle, base_id);
+       base_id++;
+    }
+
+    /* Draw a title, if we have one. */
+    if (!s->boxname && s->boxtitle) {
+       struct winctrl *c = smalloc(sizeof(struct winctrl));
+       c->ctrl = NULL;
+       c->base_id = base_id;
+       c->num_ids = 1;
+       c->data = dupstr(s->boxtitle);
+       memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));
+       winctrl_add(wc, c);
+       paneltitle(cp, base_id);
+       base_id++;
+    }
+
+    /* Initially we have just one column. */
+    ncols = 1;
+    columns[0] = *cp;                 /* structure copy */
+
+    /* And initially, there are no pending tab-delayed controls. */
+    ntabdelays = 0;
+
+    /* Loop over each control in the controlset. */
+    for (i = 0; i < s->ncontrols; i++) {
+       union control *ctrl = s->ctrls[i];
+
+       orig_tabdelay = FALSE;
+
+       /*
+        * Generic processing that pertains to all control types.
+        * At the end of this if statement, we'll have produced
+        * `ctrl' (a pointer to the control we have to create, or
+        * think about creating, in this iteration of the loop),
+        * `pos' (a suitable ctlpos with which to position it), and
+        * `c' (a winctrl structure to receive details of the
+        * dialog IDs). Or we'll have done a `continue', if it was
+        * CTRL_COLUMNS and doesn't require any control creation at
+        * all.
+        */
+       if (ctrl->generic.type == CTRL_COLUMNS) {
+           assert((ctrl->columns.ncols == 1) ^ (ncols == 1));
+
+           if (ncols == 1) {
+               /*
+                * We're splitting into multiple columns.
+                */
+               int lpercent, rpercent, lx, rx, i;
+
+               ncols = ctrl->columns.ncols;
+               assert(ncols <= lenof(columns));
+               for (i = 1; i < ncols; i++)
+                   columns[i] = columns[0];   /* structure copy */
+
+               lpercent = 0;
+               for (i = 0; i < ncols; i++) {
+                   rpercent = lpercent + ctrl->columns.percentages[i];
+                   lx = columns[i].xoff + lpercent *
+                       (columns[i].width + GAPBETWEEN) / 100;
+                   rx = columns[i].xoff + rpercent *
+                       (columns[i].width + GAPBETWEEN) / 100;
+                   columns[i].xoff = lx;
+                   columns[i].width = rx - lx - GAPBETWEEN;
+                   lpercent = rpercent;
+               }
+           } else {
                /*
                /*
-                * We postpone creation of the button until we've
-                * done everything else, since it belongs last in
-                * the tab order.
+                * We're recombining the various columns into one.
                 */
                 */
-               button_r = r;          /* structure copy */
+               int maxy = columns[0].ypos;
+               int i;
+               for (i = 1; i < ncols; i++)
+                   if (maxy < columns[i].ypos)
+                       maxy = columns[i].ypos;
+               ncols = 1;
+               columns[0] = *cp;      /* structure copy */
+               columns[0].ypos = maxy;
+           }
+
+           continue;
+       } else if (ctrl->generic.type == CTRL_TABDELAY) {
+           int i;
+
+           assert(!ctrl->generic.tabdelay);
+           ctrl = ctrl->tabdelay.ctrl;
+           orig_tabdelay = TRUE;
+
+           for (i = 0; i < ntabdelays; i++)
+               if (tabdelayed[i] == ctrl)
+                   break;
+           assert(i < ntabdelays);    /* we have to have found it */
+
+           pos = tabdelays[i];        /* structure copy */
+
+       } else {
+           /*
+            * If it wasn't one of those, it's a genuine control;
+            * so we'll have to compute a position for it now, by
+            * checking its column span.
+            */
+           int col;
+
+           colstart = COLUMN_START(ctrl->generic.column);
+           colspan = COLUMN_SPAN(ctrl->generic.column);
+
+           pos = columns[colstart];   /* structure copy */
+           pos.width = columns[colstart+colspan-1].width +
+               (columns[colstart+colspan-1].xoff - columns[colstart].xoff);
+
+           for (col = colstart; col < colstart+colspan; col++)
+               if (pos.ypos < columns[col].ypos)
+                   pos.ypos = columns[col].ypos;
+
+           /*
+            * If this control is to be tabdelayed, add it to the
+            * tabdelay list, and unset pos.hwnd to inhibit actual
+            * control creation.
+            */
+           if (ctrl->generic.tabdelay) {
+               assert(ntabdelays < lenof(tabdelays));
+               tabdelays[ntabdelays] = pos;   /* structure copy */
+               tabdelayed[ntabdelays] = ctrl;
+               ntabdelays++;
+               pos.hwnd = NULL;
            }
        }
            }
        }
-       cp->ypos += height + GAPWITHIN;
+
+       /* Most controls don't need anything in c->data. */
+       data = NULL;
+
+       /* And they all start off with no shortcuts registered. */
+       memset(shortcuts, NO_SHORTCUT, lenof(shortcuts));
+       nshortcuts = 0;
+
+       /*
+        * Now we're ready to actually create the control, by
+        * switching on its type.
+        */
+       switch (ctrl->generic.type) {
+         case CTRL_TEXT:
+           {
+               char *wrapped, *escaped;
+               int lines;
+               num_ids = 1;
+               wrapped = staticwrap(&pos, cp->hwnd,
+                                    ctrl->generic.label, &lines);
+               escaped = shortcut_escape(wrapped, NO_SHORTCUT);
+               statictext(&pos, escaped, lines, base_id);
+               sfree(escaped);
+               sfree(wrapped);
+           }
+           break;
+         case CTRL_EDITBOX:
+           num_ids = 2;               /* static, edit */
+           escaped = shortcut_escape(ctrl->editbox.label,
+                                     ctrl->editbox.shortcut);
+           shortcuts[nshortcuts++] = ctrl->editbox.shortcut;
+           if (ctrl->editbox.percentwidth == 100) {
+               if (ctrl->editbox.has_list)
+                   combobox(&pos, escaped,
+                            base_id, base_id+1);
+               else
+                   multiedit(&pos, ctrl->editbox.password, escaped,
+                             base_id, base_id+1, 100, NULL);
+           } else {
+               if (ctrl->editbox.has_list) {
+                   staticcombo(&pos, escaped, base_id, base_id+1,
+                               ctrl->editbox.percentwidth);
+               } else {
+                   (ctrl->editbox.password ? staticpassedit : staticedit)
+                       (&pos, escaped, base_id, base_id+1,
+                        ctrl->editbox.percentwidth);
+               }
+           }
+           sfree(escaped);
+           break;
+         case CTRL_RADIO:
+           num_ids = ctrl->radio.nbuttons + 1;   /* label as well */
+           {
+               struct radio *buttons;
+               int i;
+
+               escaped = shortcut_escape(ctrl->radio.label,
+                                         ctrl->radio.shortcut);
+               shortcuts[nshortcuts++] = ctrl->radio.shortcut;
+
+               buttons = smalloc(ctrl->radio.nbuttons * sizeof(struct radio));
+
+               for (i = 0; i < ctrl->radio.nbuttons; i++) {
+                   buttons[i].text =
+                       shortcut_escape(ctrl->radio.buttons[i],
+                                       (char)(ctrl->radio.shortcuts ?
+                                              ctrl->radio.shortcuts[i] :
+                                              NO_SHORTCUT));
+                   buttons[i].id = base_id + 1 + i;
+                   if (ctrl->radio.shortcuts) {
+                       assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL);
+                       shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i];
+                   }
+               }
+
+               radioline_common(&pos, escaped, base_id,
+                                ctrl->radio.ncolumns,
+                                buttons, ctrl->radio.nbuttons);
+
+               for (i = 0; i < ctrl->radio.nbuttons; i++) {
+                   sfree(buttons[i].text);
+               }
+               sfree(buttons);
+               sfree(escaped);
+           }
+           break;
+         case CTRL_CHECKBOX:
+           num_ids = 1;
+           escaped = shortcut_escape(ctrl->checkbox.label,
+                                     ctrl->checkbox.shortcut);
+           shortcuts[nshortcuts++] = ctrl->checkbox.shortcut;
+           checkbox(&pos, escaped, base_id);
+           sfree(escaped);
+           break;
+         case CTRL_BUTTON:
+           escaped = shortcut_escape(ctrl->button.label,
+                                     ctrl->button.shortcut);
+           shortcuts[nshortcuts++] = ctrl->button.shortcut;
+           num_ids = 1;
+           button(&pos, escaped, base_id, ctrl->button.isdefault);
+           sfree(escaped);
+           break;
+         case CTRL_LISTBOX:
+           num_ids = 2;
+           escaped = shortcut_escape(ctrl->listbox.label,
+                                     ctrl->listbox.shortcut);
+           shortcuts[nshortcuts++] = ctrl->listbox.shortcut;
+           if (ctrl->listbox.draglist) {
+               data = smalloc(sizeof(struct prefslist));
+               num_ids = 4;
+               prefslist(data, &pos, ctrl->listbox.height, escaped,
+                         base_id, base_id+1, base_id+2, base_id+3);
+               shortcuts[nshortcuts++] = 'u';   /* Up */
+               shortcuts[nshortcuts++] = 'd';   /* Down */
+           } else if (ctrl->listbox.height == 0) {
+               /* Drop-down list. */
+               if (ctrl->listbox.percentwidth == 100) {
+                   staticddlbig(&pos, escaped,
+                                base_id, base_id+1);
+               } else {
+                   staticddl(&pos, escaped, base_id,
+                             base_id+1, ctrl->listbox.percentwidth);
+               }
+           } else {
+               /* Ordinary list. */
+               listbox(&pos, escaped, base_id, base_id+1,
+                       ctrl->listbox.height, ctrl->listbox.multisel);
+           }
+           if (ctrl->listbox.ncols) {
+               /*
+                * This method of getting the box width is a bit of
+                * a hack; we'd do better to try to retrieve the
+                * actual width in dialog units from doctl() just
+                * before MapDialogRect. But that's going to be no
+                * fun, and this should be good enough accuracy.
+                */
+               int width = cp->width * ctrl->listbox.percentwidth;
+               int *tabarray;
+               int i, percent;
+
+               tabarray = smalloc((ctrl->listbox.ncols-1) * sizeof(int));
+               percent = 0;
+               for (i = 0; i < ctrl->listbox.ncols-1; i++) {
+                   percent += ctrl->listbox.percentages[i];
+                   tabarray[i] = width * percent / 10000;
+               }
+               SendDlgItemMessage(cp->hwnd, base_id+1, LB_SETTABSTOPS,
+                                  ctrl->listbox.ncols-1, (LPARAM)tabarray);
+               sfree(tabarray);
+           }
+           sfree(escaped);
+           break;
+         case CTRL_FILESELECT:
+           num_ids = 3;
+           escaped = shortcut_escape(ctrl->fileselect.label,
+                                     ctrl->fileselect.shortcut);
+           shortcuts[nshortcuts++] = ctrl->fileselect.shortcut;
+           editbutton(&pos, escaped, base_id, base_id+1,
+                      "Bro&wse...", base_id+2);
+           shortcuts[nshortcuts++] = 'w';
+           sfree(escaped);
+           break;
+         case CTRL_FONTSELECT:
+           num_ids = 3;
+           escaped = shortcut_escape(ctrl->fontselect.label,
+                                     ctrl->fontselect.shortcut);
+           shortcuts[nshortcuts++] = ctrl->fontselect.shortcut;
+           statictext(&pos, escaped, 1, base_id);
+           staticbtn(&pos, "", base_id+1, "Change...", base_id+2);
+           sfree(escaped);
+           data = smalloc(sizeof(FontSpec));
+           break;
+         default:
+           assert(!"Can't happen");
+           break;
+       }
+
+       /*
+        * Create a `struct winctrl' for this control, and advance
+        * the dialog ID counter, if it's actually been created
+        * (and isn't tabdelayed).
+        */
+       if (pos.hwnd) {
+           struct winctrl *c = smalloc(sizeof(struct winctrl));
+
+           c->ctrl = ctrl;
+           c->base_id = base_id;
+           c->num_ids = num_ids;
+           c->data = data;
+           memcpy(c->shortcuts, shortcuts, sizeof(shortcuts));
+           winctrl_add(wc, c);
+           winctrl_add_shortcuts(dp, c);
+           base_id += num_ids;
+       }
+
+       if (!orig_tabdelay) {
+           /*
+            * Update the ypos in all columns crossed by this
+            * control.
+            */
+           int i;
+           for (i = colstart; i < colstart+colspan; i++)
+               columns[i].ypos = pos.ypos;
+       }
     }
     }
-    bareradioline(cp, 2, r1text, r1id, r2text, r2id, NULL);
-    /* Create the postponed button. */
-    doctl(cp, button_r, "BUTTON",
-         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
-         0, btext, bid);
+
+    /*
+     * We've now finished laying out the controls; so now update
+     * the ctlpos and control ID that were passed in, terminate
+     * any containing box, and return.
+     */
+    for (i = 0; i < ncols; i++)
+       if (cp->ypos < columns[i].ypos)
+           cp->ypos = columns[i].ypos;
+    *id = base_id;
+
+    if (s->boxname && *s->boxname)
+       endbox(cp);
+}
+
+static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp,
+                             int has_focus)
+{
+    if (has_focus) {
+       if (dp->focused)
+           dp->lastfocused = dp->focused;
+       dp->focused = ctrl;
+    } else if (!has_focus && dp->focused == ctrl) {
+       dp->lastfocused = dp->focused;
+       dp->focused = NULL;
+    }
+}
+
+union control *dlg_last_focused(void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    return dp->lastfocused;
+}
+
+/*
+ * The dialog-box procedure calls this function to handle Windows
+ * messages on a control we manage.
+ */
+int winctrl_handle_command(struct dlgparam *dp, UINT msg,
+                          WPARAM wParam, LPARAM lParam)
+{
+    struct winctrl *c;
+    union control *ctrl;
+    int i, id, ret;
+    static UINT draglistmsg = WM_NULL;
+
+    /*
+     * Filter out pointless window messages. Our interest is in
+     * WM_COMMAND and the drag list message, and nothing else.
+     */
+    if (draglistmsg == WM_NULL)
+       draglistmsg = RegisterWindowMessage (DRAGLISTMSGSTRING);
+
+    if (msg != draglistmsg && msg != WM_COMMAND && msg != WM_DRAWITEM)
+       return 0;
+
+    /*
+     * Look up the control ID in our data.
+     */
+    for (i = 0; i < dp->nctrltrees; i++) {
+       c = winctrl_findbyid(dp->controltrees[i], LOWORD(wParam));
+       if (c)
+           break;
+    }
+    if (!c)
+       return 0;                      /* we have nothing to do */
+
+    if (msg == WM_DRAWITEM) {
+       /*
+        * Owner-draw request for a panel title.
+        */
+       LPDRAWITEMSTRUCT di = (LPDRAWITEMSTRUCT) lParam;
+       HDC hdc = di->hDC;
+       RECT r = di->rcItem;
+       SIZE s;
+
+       GetTextExtentPoint32(hdc, (char *)c->data,
+                                strlen((char *)c->data), &s);
+       DrawEdge(hdc, &r, EDGE_ETCHED, BF_ADJUST | BF_RECT);
+       TextOut(hdc,
+               r.left + (r.right-r.left-s.cx)/2,
+               r.top + (r.bottom-r.top-s.cy)/2,
+               (char *)c->data, strlen((char *)c->data));
+
+       return TRUE;
+    }
+
+    ctrl = c->ctrl;
+    id = LOWORD(wParam) - c->base_id;
+
+    if (!ctrl || !ctrl->generic.handler)
+       return 0;                      /* nothing we can do here */
+
+    /*
+     * From here on we do not issue `return' statements until the
+     * very end of the dialog box: any event handler is entitled to
+     * ask for a colour selector, so we _must_ always allow control
+     * to reach the end of this switch statement so that the
+     * subsequent code can test dp->coloursel_wanted().
+     */
+    ret = 0;
+    dp->coloursel_wanted = FALSE;
+
+    /*
+     * Now switch on the control type and the message.
+     */
+    switch (ctrl->generic.type) {
+      case CTRL_EDITBOX:
+       if (msg == WM_COMMAND && !ctrl->editbox.has_list &&
+           (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS);
+       if (msg == WM_COMMAND && ctrl->editbox.has_list &&
+           (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS);
+
+       if (msg == WM_COMMAND && !ctrl->editbox.has_list &&
+           HIWORD(wParam) == EN_CHANGE)
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+       if (msg == WM_COMMAND &&
+           ctrl->editbox.has_list) {
+           if (HIWORD(wParam) == CBN_SELCHANGE) {
+               int index, len;
+               char *text;
+
+               index = SendDlgItemMessage(dp->hwnd, c->base_id+1,
+                                          CB_GETCURSEL, 0, 0);
+               len = SendDlgItemMessage(dp->hwnd, c->base_id+1,
+                                        CB_GETLBTEXTLEN, index, 0);
+               text = smalloc(len+1);
+               SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETLBTEXT,
+                                  index, (LPARAM)text);
+               SetDlgItemText(dp->hwnd, c->base_id+1, text);
+               sfree(text);
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+           } else if (HIWORD(wParam) == CBN_EDITCHANGE) {
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+           } else if (HIWORD(wParam) == CBN_KILLFOCUS) {
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
+           }
+
+       }
+       break;
+      case CTRL_RADIO:
+       if (msg == WM_COMMAND &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       /*
+        * We sometimes get spurious BN_CLICKED messages for the
+        * radio button that is just about to _lose_ selection, if
+        * we're switching using the arrow keys. Therefore we
+        * double-check that the button in wParam is actually
+        * checked before generating an event.
+        */
+       if (msg == WM_COMMAND &&
+           HIWORD(wParam) == BN_CLICKED ||
+           HIWORD(wParam) == BN_DOUBLECLICKED &&
+           IsDlgButtonChecked(dp->hwnd, LOWORD(wParam))) {
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+       }
+       break;
+      case CTRL_CHECKBOX:
+       if (msg == WM_COMMAND &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       if (msg == WM_COMMAND &&
+           (HIWORD(wParam) == BN_CLICKED ||
+            HIWORD(wParam) == BN_DOUBLECLICKED)) {
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+       }
+       break;
+      case CTRL_BUTTON:
+       if (msg == WM_COMMAND &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       if (msg == WM_COMMAND &&
+           (HIWORD(wParam) == BN_CLICKED ||
+            HIWORD(wParam) == BN_DOUBLECLICKED)) {
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION);
+       }
+       break;
+      case CTRL_LISTBOX:
+       if (msg == WM_COMMAND && ctrl->listbox.height != 0 &&
+           (HIWORD(wParam)==LBN_SETFOCUS || HIWORD(wParam)==LBN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == LBN_SETFOCUS);
+       if (msg == WM_COMMAND && ctrl->listbox.height == 0 &&
+           (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS);
+       if (msg == WM_COMMAND && id >= 2 &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       if (ctrl->listbox.draglist) {
+           int pret;
+           pret = handle_prefslist(c->data, NULL, 0, (msg != WM_COMMAND),
+                                   dp->hwnd, wParam, lParam);
+           if (pret & 2)
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+           ret = pret & 1;
+       } else {
+           if (msg == WM_COMMAND && HIWORD(wParam) == LBN_DBLCLK) {
+               SetCapture(dp->hwnd);
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION);
+           } else if (msg == WM_COMMAND && HIWORD(wParam) == LBN_SELCHANGE) {
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_SELCHANGE);
+           }
+       }
+       break;
+      case CTRL_FILESELECT:
+       if (msg == WM_COMMAND && id == 1 &&
+           (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS);
+       if (msg == WM_COMMAND && id == 2 &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       if (id == 2 &&
+           (msg == WM_COMMAND &&
+            (HIWORD(wParam) == BN_CLICKED ||
+             HIWORD(wParam) == BN_DOUBLECLICKED))) {
+           OPENFILENAME of;
+           char filename[FILENAME_MAX];
+           int ret;
+
+           memset(&of, 0, sizeof(of));
+#ifdef OPENFILENAME_SIZE_VERSION_400
+           of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+           of.lStructSize = sizeof(of);
+#endif
+           of.hwndOwner = dp->hwnd;
+           if (ctrl->fileselect.filter)
+               of.lpstrFilter = ctrl->fileselect.filter;
+           else
+               of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
+           of.lpstrCustomFilter = NULL;
+           of.nFilterIndex = 1;
+           of.lpstrFile = filename;
+           GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename));
+           filename[lenof(filename)-1] = '\0';
+           of.nMaxFile = lenof(filename);
+           of.lpstrFileTitle = NULL;
+           of.lpstrInitialDir = NULL;
+           of.lpstrTitle = ctrl->fileselect.title;
+           of.Flags = 0;
+           if (ctrl->fileselect.for_writing)
+               ret = GetSaveFileName(&of);
+           else
+               ret = GetOpenFileName(&of);
+           if (ret) {
+               SetDlgItemText(dp->hwnd, c->base_id + 1, filename);
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+           }
+       }
+       break;
+      case CTRL_FONTSELECT:
+       if (msg == WM_COMMAND && id == 2 &&
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);
+       if (id == 2 &&
+           (msg == WM_COMMAND &&
+            (HIWORD(wParam) == BN_CLICKED ||
+             HIWORD(wParam) == BN_DOUBLECLICKED))) {
+           CHOOSEFONT cf;
+           LOGFONT lf;
+           HDC hdc;
+           FontSpec fs = *(FontSpec *)c->data;
+           
+           hdc = GetDC(0);
+           lf.lfHeight = -MulDiv(fs.height,
+                                 GetDeviceCaps(hdc, LOGPIXELSY), 72);
+           ReleaseDC(0, hdc);
+           lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
+           lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
+           lf.lfWeight = (fs.isbold ? FW_BOLD : 0);
+           lf.lfCharSet = fs.charset;
+           lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+           lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+           lf.lfQuality = DEFAULT_QUALITY;
+           lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
+           strncpy(lf.lfFaceName, fs.name,
+                   sizeof(lf.lfFaceName) - 1);
+           lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0';
+
+           cf.lStructSize = sizeof(cf);
+           cf.hwndOwner = dp->hwnd;
+           cf.lpLogFont = &lf;
+           cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
+               CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
+
+           if (ChooseFont(&cf)) {
+               strncpy(fs.name, lf.lfFaceName,
+                       sizeof(fs.name) - 1);
+               fs.name[sizeof(fs.name) - 1] = '\0';
+               fs.isbold = (lf.lfWeight == FW_BOLD);
+               fs.charset = lf.lfCharSet;
+               fs.height = cf.iPointSize / 10;
+               dlg_fontsel_set(ctrl, dp, fs);
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+           }
+       }
+       break;
+    }
+
+    /*
+     * If the above event handler has asked for a colour selector,
+     * now is the time to generate one.
+     */
+    if (dp->coloursel_wanted) {
+       static CHOOSECOLOR cc;
+       static DWORD custom[16] = { 0 };    /* zero initialisers */
+       cc.lStructSize = sizeof(cc);
+       cc.hwndOwner = dp->hwnd;
+       cc.hInstance = (HWND) hinst;
+       cc.lpCustColors = custom;
+       cc.rgbResult = RGB(dp->coloursel_result.r,
+                          dp->coloursel_result.g,
+                          dp->coloursel_result.b);
+       cc.Flags = CC_FULLOPEN | CC_RGBINIT;
+       if (ChooseColor(&cc)) {
+           dp->coloursel_result.r =
+               (unsigned char) (cc.rgbResult & 0xFF);
+           dp->coloursel_result.g =
+               (unsigned char) (cc.rgbResult >> 8) & 0xFF;
+           dp->coloursel_result.b =
+               (unsigned char) (cc.rgbResult >> 16) & 0xFF;
+           dp->coloursel_result.ok = TRUE;
+       } else
+           dp->coloursel_result.ok = FALSE;
+       ctrl->generic.handler(ctrl, dp, dp->data, EVENT_CALLBACK);
+    }
+
+    return ret;
+}
+
+/*
+ * This function can be called to produce context help on a
+ * control. Returns TRUE if it has actually launched WinHelp.
+ */
+int winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id)
+{
+    int i;
+    struct winctrl *c;
+    char *cmd;
+
+    /*
+     * Look up the control ID in our data.
+     */
+    for (i = 0; i < dp->nctrltrees; i++) {
+       c = winctrl_findbyid(dp->controltrees[i], id);
+       if (c)
+           break;
+    }
+    if (!c)
+       return 0;                      /* we have nothing to do */
+
+    /*
+     * This is the Windows front end, so we're allowed to assume
+     * `helpctx.p' is a context string.
+     */
+    if (!c->ctrl || !c->ctrl->generic.helpctx.p)
+       return 0;                      /* no help available for this ctrl */
+
+    cmd = dupprintf("JI(`',`%s')", c->ctrl->generic.helpctx.p);
+    WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
+    sfree(cmd);
+    return 1;
+}
+
+/*
+ * Now the various functions that the platform-independent
+ * mechanism can call to access the dialog box entries.
+ */
+
+static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl)
+{
+    int i;
+
+    for (i = 0; i < dp->nctrltrees; i++) {
+       struct winctrl *c = winctrl_findbyctrl(dp->controltrees[i], ctrl);
+       if (c)
+           return c;
+    }
+    return NULL;
+}
+
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_RADIO);
+    CheckRadioButton(dp->hwnd,
+                    c->base_id + 1,
+                    c->base_id + c->ctrl->radio.nbuttons,
+                    c->base_id + 1 + whichbutton);
+}
+
+int dlg_radiobutton_get(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int i;
+    assert(c && c->ctrl->generic.type == CTRL_RADIO);
+    for (i = 0; i < c->ctrl->radio.nbuttons; i++)
+       if (IsDlgButtonChecked(dp->hwnd, c->base_id + 1 + i))
+           return i;
+    assert(!"No radio button was checked?!");
+    return 0;
+}
+
+void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
+    CheckDlgButton(dp->hwnd, c->base_id, (checked != 0));
+}
+
+int dlg_checkbox_get(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
+    return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id);
+}
+
+void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
+    SetDlgItemText(dp->hwnd, c->base_id+1, text);
+}
+
+void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
+    GetDlgItemText(dp->hwnd, c->base_id+1, buffer, length);
+    buffer[length-1] = '\0';
+}
+
+/* The `listbox' functions can also apply to combo boxes. */
+void dlg_listbox_clear(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg;
+    assert(c &&
+          (c->ctrl->generic.type == CTRL_LISTBOX ||
+           c->ctrl->generic.type == CTRL_EDITBOX &&
+           c->ctrl->editbox.has_list));
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+          LB_RESETCONTENT : CB_RESETCONTENT);
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);
+}
+
+void dlg_listbox_del(union control *ctrl, void *dlg, int index)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg;
+    assert(c &&
+          (c->ctrl->generic.type == CTRL_LISTBOX ||
+           c->ctrl->generic.type == CTRL_EDITBOX &&
+           c->ctrl->editbox.has_list));
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+          LB_DELETESTRING : CB_DELETESTRING);
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);
+}
+
+void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg;
+    assert(c &&
+          (c->ctrl->generic.type == CTRL_LISTBOX ||
+           c->ctrl->generic.type == CTRL_EDITBOX &&
+           c->ctrl->editbox.has_list));
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+          LB_ADDSTRING : CB_ADDSTRING);
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text);
+}
+
+/*
+ * Each listbox entry may have a numeric id associated with it.
+ * Note that some front ends only permit a string to be stored at
+ * each position, which means that _if_ you put two identical
+ * strings in any listbox then you MUST not assign them different
+ * IDs and expect to get meaningful results back.
+ */
+void dlg_listbox_addwithindex(union control *ctrl, void *dlg,
+                             char const *text, int id)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg, msg2, index;
+    assert(c &&
+          (c->ctrl->generic.type == CTRL_LISTBOX ||
+           c->ctrl->generic.type == CTRL_EDITBOX &&
+           c->ctrl->editbox.has_list));
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+          LB_ADDSTRING : CB_ADDSTRING);
+    msg2 = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+          LB_SETITEMDATA : CB_SETITEMDATA);
+    index = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text);
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg2, index, (LPARAM)id);
+}
+
+int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg;
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
+    msg = (c->ctrl->listbox.height != 0 ? LB_GETITEMDATA : CB_GETITEMDATA);
+    return
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);
+}
+
+/* dlg_listbox_index returns <0 if no single element is selected. */
+int dlg_listbox_index(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg, ret;
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
+          !c->ctrl->listbox.multisel);
+    msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL);
+    ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);
+    if (ret == LB_ERR)
+       return -1;
+    else
+       return ret;
+}
+
+int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
+          c->ctrl->listbox.multisel &&
+          c->ctrl->listbox.height != 0);
+    return
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSEL, index, 0);
+}
+
+void dlg_listbox_select(union control *ctrl, void *dlg, int index)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int msg;
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
+          !c->ctrl->listbox.multisel);
+    msg = (c->ctrl->listbox.height != 0 ? LB_SETCURSEL : CB_SETCURSEL);
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);
+}
+
+void dlg_text_set(union control *ctrl, void *dlg, char const *text)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_TEXT);
+    SetDlgItemText(dp->hwnd, c->base_id, text);
+}
+
+void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_FILESELECT);
+    SetDlgItemText(dp->hwnd, c->base_id+1, fn.path);
+}
+
+void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_FILESELECT);
+    GetDlgItemText(dp->hwnd, c->base_id+1, fn->path, lenof(fn->path));
+    fn->path[lenof(fn->path)-1] = '\0';
+}
+
+void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs)
+{
+    char *buf, *boldstr;
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
+
+    *(FontSpec *)c->data = fs;        /* structure copy */
+
+    boldstr = (fs.isbold ? "bold, " : "");
+    if (fs.height == 0)
+       buf = dupprintf("Font: %s, %sdefault height", fs.name, boldstr);
+    else
+       buf = dupprintf("Font: %s, %s%d-point", fs.name, boldstr,
+                       (fs.height < 0 ? -fs.height : fs.height));
+    SetDlgItemText(dp->hwnd, c->base_id+1, buf);
+    sfree(buf);
+}
+
+void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
+    *fs = *(FontSpec *)c->data;               /* structure copy */
+}
+
+/*
+ * Bracketing a large set of updates in these two functions will
+ * cause the front end (if possible) to delay updating the screen
+ * until it's all complete, thus avoiding flicker.
+ */
+void dlg_update_start(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, FALSE, 0);
+    }
+}
+
+void dlg_update_done(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
+       HWND hw = GetDlgItem(dp->hwnd, c->base_id+1);
+       SendMessage(hw, WM_SETREDRAW, TRUE, 0);
+       InvalidateRect(hw, NULL, TRUE);
+    }
+}
+
+void dlg_set_focus(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    int id;
+    HWND ctl;
+    switch (ctrl->generic.type) {
+      case CTRL_EDITBOX: id = c->base_id + 1; break;
+      case CTRL_RADIO:
+       for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--)
+           if (IsDlgButtonChecked(dp->hwnd, id))
+               break;
+       /*
+        * In the theoretically-unlikely case that no button was
+        * selected, id should come out of this as 1, which is a
+        * reasonable enough choice.
+        */
+       break;
+      case CTRL_CHECKBOX: id = c->base_id; break;
+      case CTRL_BUTTON: id = c->base_id; break;
+      case CTRL_LISTBOX: id = c->base_id + 1; break;
+      case CTRL_FILESELECT: id = c->base_id + 1; break;
+      case CTRL_FONTSELECT: id = c->base_id + 2; break;
+      default: id = c->base_id; break;
+    }
+    ctl = GetDlgItem(dp->hwnd, id);
+    SetFocus(ctl);
+}
+
+/*
+ * During event processing, you might well want to give an error
+ * indication to the user. dlg_beep() is a quick and easy generic
+ * error; dlg_error() puts up a message-box or equivalent.
+ */
+void dlg_beep(void *dlg)
+{
+    /* struct dlgparam *dp = (struct dlgparam *)dlg; */
+    MessageBeep(0);
+}
+
+void dlg_error_msg(void *dlg, char *msg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    MessageBox(dp->hwnd, msg,
+              dp->errtitle ? dp->errtitle : NULL,
+              MB_OK | MB_ICONERROR);
+}
+
+/*
+ * This function signals to the front end that the dialog's
+ * processing is completed, and passes an integer value (typically
+ * a success status).
+ */
+void dlg_end(void *dlg, int value)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    dp->ended = TRUE;
+    dp->endresult = value;
+}
+
+void dlg_refresh(union control *ctrl, void *dlg)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    int i, j;
+    struct winctrl *c;
+
+    if (!ctrl) {
+       /*
+        * Send EVENT_REFRESH to absolutely everything.
+        */
+       for (j = 0; j < dp->nctrltrees; j++) {
+           for (i = 0;
+                (c = winctrl_findbyindex(dp->controltrees[j], i)) != NULL;
+                i++) {
+               if (c->ctrl && c->ctrl->generic.handler != NULL)
+                   c->ctrl->generic.handler(c->ctrl, dp,
+                                            dp->data, EVENT_REFRESH);
+           }
+       }
+    } else {
+       /*
+        * Send EVENT_REFRESH to a specific control.
+        */
+       if (ctrl->generic.handler != NULL)
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
+    }
+}
+
+void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
+{
+    struct dlgparam *dp = (struct dlgparam *)dlg;
+    dp->coloursel_wanted = TRUE;
+    dp->coloursel_result.r = r;
+    dp->coloursel_result.g = g;
+    dp->coloursel_result.b = b;
+}
+
+int dlg_coloursel_results(union control *ctrl, void *dlg,
+                         int *r, int *g, int *b)
+{
+    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;
 }
 }
index 9299a0060258c0e30fe9976f9d765ae41e37fe83..ca1890c5162113532aeea2fa773a787cabb44883 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -3,6 +3,8 @@
 #include <commdlg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <commdlg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
 #include <ctype.h>
 #include <time.h>
 
 #include <ctype.h>
 #include <time.h>
 
@@ -11,6 +13,7 @@
 #include "winstuff.h"
 #include "win_res.h"
 #include "storage.h"
 #include "winstuff.h"
 #include "win_res.h"
 #include "storage.h"
+#include "dialog.h"
 
 #ifdef MSVC4
 #define TVINSERTSTRUCT  TV_INSERTSTRUCT
 
 #ifdef MSVC4
 #define TVINSERTSTRUCT  TV_INSERTSTRUCT
 #define ICON_BIG        1
 #endif
 
 #define ICON_BIG        1
 #endif
 
+/*
+ * These are the various bits of data required to handle the
+ * portable-dialog stuff in the config box. Having them at file
+ * scope in here isn't too bad a place to put them; if we were ever
+ * to need more than one config box per process we could always
+ * shift them to a per-config-box structure stored in GWL_USERDATA.
+ */
+static struct controlbox *ctrlbox;
+/*
+ * ctrls_base holds the OK and Cancel buttons: the controls which
+ * are present in all dialog panels. ctrls_panel holds the ones
+ * which change from panel to panel.
+ */
+static struct winctrls ctrls_base, ctrls_panel;
+static struct dlgparam dp;
+
 static char **events = NULL;
 static int nevents = 0, negsize = 0;
 
 static char **events = NULL;
 static int nevents = 0, negsize = 0;
 
-static int readytogo;
-static int sesslist_has_focus;
 static int requested_help;
 
 static int requested_help;
 
-static struct prefslist cipherlist;
-
 extern Config cfg;                    /* defined in window.c */
 
 struct sesslist sesslist;             /* exported to window.c */
 extern Config cfg;                    /* defined in window.c */
 
 struct sesslist sesslist;             /* exported to window.c */
@@ -51,31 +66,6 @@ void force_normal(HWND hwnd)
     recurse = 0;
 }
 
     recurse = 0;
 }
 
-static void MyGetDlgItemInt(HWND hwnd, int id, int *result)
-{
-    BOOL ok;
-    int n;
-    n = GetDlgItemInt(hwnd, id, &ok, FALSE);
-    if (ok)
-       *result = n;
-}
-
-static void MyGetDlgItemFlt(HWND hwnd, int id, int *result, int scale)
-{
-    char text[80];
-    BOOL ok;
-    ok = GetDlgItemText(hwnd, id, text, sizeof(text) - 1);
-    if (ok && text[0])
-       *result = (int) (scale * atof(text));
-}
-
-static void MySetDlgItemFlt(HWND hwnd, int id, double value)
-{
-    char text[80];
-    sprintf(text, "%g", value);
-    SetDlgItemText(hwnd, id, text);
-}
-
 static int CALLBACK LogProc(HWND hwnd, UINT msg,
                            WPARAM wParam, LPARAM lParam)
 {
 static int CALLBACK LogProc(HWND hwnd, UINT msg,
                            WPARAM wParam, LPARAM lParam)
 {
@@ -231,1222 +221,21 @@ static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
     return 0;
 }
 
     return 0;
 }
 
-static char savedsession[2048];
-
-enum { IDCX_ABOUT =
-       IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
-
-    sessionpanelstart,
-    IDC_TITLE_SESSION,
-    IDC_BOX_SESSION1,
-    IDC_BOX_SESSION2,
-    IDC_BOX_SESSION3,
-    IDC_HOSTSTATIC,
-    IDC_HOST,
-    IDC_PORTSTATIC,
-    IDC_PORT,
-    IDC_PROTSTATIC,
-    IDC_PROTRAW,
-    IDC_PROTTELNET,
-    IDC_PROTRLOGIN,
-    IDC_PROTSSH,
-    IDC_SESSSTATIC,
-    IDC_SESSEDIT,
-    IDC_SESSLIST,
-    IDC_SESSLOAD,
-    IDC_SESSSAVE,
-    IDC_SESSDEL,
-    IDC_CLOSEEXIT,
-    IDC_COEALWAYS,
-    IDC_COENEVER,
-    IDC_COENORMAL,
-    sessionpanelend,
-
-    loggingpanelstart,
-    IDC_TITLE_LOGGING,
-    IDC_BOX_LOGGING1,
-    IDC_LSTATSTATIC,
-    IDC_LSTATOFF,
-    IDC_LSTATASCII,
-    IDC_LSTATRAW,
-    IDC_LSTATPACKET,
-    IDC_LGFSTATIC,
-    IDC_LGFEDIT,
-    IDC_LGFBUTTON,
-    IDC_LGFEXPLAIN,
-    IDC_LSTATXIST,
-    IDC_LSTATXOVR,
-    IDC_LSTATXAPN,
-    IDC_LSTATXASK,
-    loggingpanelend,
-
-    keyboardpanelstart,
-    IDC_TITLE_KEYBOARD,
-    IDC_BOX_KEYBOARD1,
-    IDC_BOX_KEYBOARD2,
-    IDC_BOX_KEYBOARD3,
-    IDC_DELSTATIC,
-    IDC_DEL008,
-    IDC_DEL127,
-    IDC_HOMESTATIC,
-    IDC_HOMETILDE,
-    IDC_HOMERXVT,
-    IDC_FUNCSTATIC,
-    IDC_FUNCTILDE,
-    IDC_FUNCLINUX,
-    IDC_FUNCXTERM,
-    IDC_FUNCVT400,
-    IDC_FUNCVT100P,
-    IDC_FUNCSCO,
-    IDC_KPSTATIC,
-    IDC_KPNORMAL,
-    IDC_KPAPPLIC,
-    IDC_KPNH,
-    IDC_CURSTATIC,
-    IDC_CURNORMAL,
-    IDC_CURAPPLIC,
-    IDC_COMPOSEKEY,
-    IDC_CTRLALTKEYS,
-    keyboardpanelend,
-
-    terminalpanelstart,
-    IDC_TITLE_TERMINAL,
-    IDC_BOX_TERMINAL1,
-    IDC_BOX_TERMINAL2,
-    IDC_BOX_TERMINAL3,
-    IDC_WRAPMODE,
-    IDC_DECOM,
-    IDC_LFHASCR,
-    IDC_BCE,
-    IDC_BLINKTEXT,
-    IDC_ANSWERBACK,
-    IDC_ANSWEREDIT,
-    IDC_ECHOSTATIC,
-    IDC_ECHOBACKEND,
-    IDC_ECHOYES,
-    IDC_ECHONO,
-    IDC_EDITSTATIC,
-    IDC_EDITBACKEND,
-    IDC_EDITYES,
-    IDC_EDITNO,
-    IDC_PRINTERSTATIC,
-    IDC_PRINTER,
-    terminalpanelend,
-
-    featurespanelstart,
-    IDC_TITLE_FEATURES,
-    IDC_BOX_FEATURES1,
-    IDC_NOAPPLICK,
-    IDC_NOAPPLICC,
-    IDC_NOMOUSEREP,
-    IDC_NORESIZE,
-    IDC_NOALTSCREEN,
-    IDC_NOWINTITLE,
-    IDC_NODBACKSPACE,
-    IDC_NOCHARSET,
-    featurespanelend,
-
-    bellpanelstart,
-    IDC_TITLE_BELL,
-    IDC_BOX_BELL1,
-    IDC_BOX_BELL2,
-    IDC_BELLSTATIC,
-    IDC_BELL_DISABLED,
-    IDC_BELL_DEFAULT,
-    IDC_BELL_WAVEFILE,
-    IDC_BELL_VISUAL,
-    IDC_BELL_WAVESTATIC,
-    IDC_BELL_WAVEEDIT,
-    IDC_BELL_WAVEBROWSE,
-    IDC_B_IND_STATIC,
-    IDC_B_IND_DISABLED,
-    IDC_B_IND_FLASH,
-    IDC_B_IND_STEADY,
-    IDC_BELLOVL,
-    IDC_BELLOVLNSTATIC,
-    IDC_BELLOVLN,
-    IDC_BELLOVLTSTATIC,
-    IDC_BELLOVLT,
-    IDC_BELLOVLEXPLAIN,
-    IDC_BELLOVLSSTATIC,
-    IDC_BELLOVLS,
-    bellpanelend,
-
-    windowpanelstart,
-    IDC_TITLE_WINDOW,
-    IDC_BOX_WINDOW1,
-    IDC_BOX_WINDOW2,
-    IDC_BOX_WINDOW3,
-    IDC_ROWSSTATIC,
-    IDC_ROWSEDIT,
-    IDC_COLSSTATIC,
-    IDC_COLSEDIT,
-    IDC_RESIZESTATIC,
-    IDC_RESIZETERM,
-    IDC_RESIZEFONT,
-    IDC_RESIZENONE,
-    IDC_RESIZEEITHER,
-    IDC_SCROLLBAR,
-    IDC_SCROLLBARFULLSCREEN,
-    IDC_SAVESTATIC,
-    IDC_SAVEEDIT,
-    IDC_SCROLLKEY,
-    IDC_SCROLLDISP,
-    windowpanelend,
-
-    behaviourpanelstart,
-    IDC_TITLE_BEHAVIOUR,
-    IDC_BOX_BEHAVIOUR1,
-    IDC_CLOSEWARN,
-    IDC_ALTF4,
-    IDC_ALTSPACE,
-    IDC_ALTONLY,
-    IDC_ALWAYSONTOP,
-    IDC_FULLSCREENONALTENTER,
-    behaviourpanelend,
-
-    appearancepanelstart,
-    IDC_TITLE_APPEARANCE,
-    IDC_BOX_APPEARANCE1,
-    IDC_BOX_APPEARANCE2,
-    IDC_BOX_APPEARANCE3,
-    IDC_BOX_APPEARANCE4,
-    IDC_BOX_APPEARANCE5,
-    IDC_CURSORSTATIC,
-    IDC_CURBLOCK,
-    IDC_CURUNDER,
-    IDC_CURVERT,
-    IDC_BLINKCUR,
-    IDC_FONTSTATIC,
-    IDC_CHOOSEFONT,
-    IDC_WINTITLE,
-    IDC_WINEDIT,
-    IDC_WINNAME,
-    IDC_HIDEMOUSE,
-    IDC_SUNKENEDGE,
-    IDC_WINBSTATIC,
-    IDC_WINBEDIT,
-    appearancepanelend,
-
-    connectionpanelstart,
-    IDC_TITLE_CONNECTION,
-    IDC_BOX_CONNECTION1,
-    IDC_BOX_CONNECTION2,
-    IDC_BOX_CONNECTION3,
-    IDC_TTSTATIC,
-    IDC_TTEDIT,
-    IDC_LOGSTATIC,
-    IDC_LOGEDIT,
-    IDC_PINGSTATIC,
-    IDC_PINGEDIT,
-    IDC_NODELAY,
-    connectionpanelend,
-
-    proxypanelstart,
-    IDC_TITLE_PROXY,
-    IDC_BOX_PROXY1,
-    IDC_PROXYTYPESTATIC,
-    IDC_PROXYTYPENONE,
-    IDC_PROXYTYPEHTTP,
-    IDC_PROXYTYPESOCKS,
-    IDC_PROXYTYPETELNET,
-    IDC_PROXYHOSTSTATIC,
-    IDC_PROXYHOSTEDIT,
-    IDC_PROXYPORTSTATIC,
-    IDC_PROXYPORTEDIT,
-    IDC_PROXYEXCLUDESTATIC,
-    IDC_PROXYEXCLUDEEDIT,
-    IDC_PROXYLOCALHOST,
-    IDC_PROXYDNSSTATIC,
-    IDC_PROXYDNSNO,
-    IDC_PROXYDNSAUTO,
-    IDC_PROXYDNSYES,
-    IDC_PROXYUSERSTATIC,
-    IDC_PROXYUSEREDIT,
-    IDC_PROXYPASSSTATIC,
-    IDC_PROXYPASSEDIT,
-    IDC_BOX_PROXY2,
-    IDC_PROXYTELNETCMDSTATIC,
-    IDC_PROXYTELNETCMDEDIT,
-    IDC_PROXYSOCKSVERSTATIC,
-    IDC_PROXYSOCKSVER5,
-    IDC_PROXYSOCKSVER4,
-    proxypanelend,
-
-    telnetpanelstart,
-    IDC_TITLE_TELNET,
-    IDC_BOX_TELNET1,
-    IDC_BOX_TELNET2,
-    IDC_TSSTATIC,
-    IDC_TSEDIT,
-    IDC_ENVSTATIC,
-    IDC_VARSTATIC,
-    IDC_VAREDIT,
-    IDC_VALSTATIC,
-    IDC_VALEDIT,
-    IDC_ENVLIST,
-    IDC_ENVADD,
-    IDC_ENVREMOVE,
-    IDC_EMSTATIC,
-    IDC_EMBSD,
-    IDC_EMRFC,
-    IDC_ACTSTATIC,
-    IDC_TPASSIVE,
-    IDC_TACTIVE,
-    IDC_TELNETKEY,
-    IDC_TELNETRET,
-    telnetpanelend,
-
-    rloginpanelstart,
-    IDC_TITLE_RLOGIN,
-    IDC_BOX_RLOGIN1,
-    IDC_BOX_RLOGIN2,
-    IDC_R_TSSTATIC,
-    IDC_R_TSEDIT,
-    IDC_RLLUSERSTATIC,
-    IDC_RLLUSEREDIT,
-    rloginpanelend,
-
-    sshpanelstart,
-    IDC_TITLE_SSH,
-    IDC_BOX_SSH1,
-    IDC_BOX_SSH2,
-    IDC_BOX_SSH3,
-    IDC_NOPTY,
-    IDC_BOX_SSHCIPHER,
-    IDC_CIPHERSTATIC2,
-    IDC_CIPHERLIST,
-    IDC_CIPHERUP,
-    IDC_CIPHERDN,
-    IDC_SSH2DES,
-    IDC_SSHPROTSTATIC,
-    IDC_SSHPROT1ONLY,
-    IDC_SSHPROT1,
-    IDC_SSHPROT2,
-    IDC_SSHPROT2ONLY,
-    IDC_CMDSTATIC,
-    IDC_CMDEDIT,
-    IDC_COMPRESS,
-    sshpanelend,
-
-    sshauthpanelstart,
-    IDC_TITLE_SSHAUTH,
-    IDC_BOX_SSHAUTH1,
-    IDC_BOX_SSHAUTH2,
-    IDC_PKSTATIC,
-    IDC_PKEDIT,
-    IDC_PKBUTTON,
-    IDC_AGENTFWD,
-    IDC_CHANGEUSER,
-    IDC_AUTHTIS,
-    IDC_AUTHKI,
-    sshauthpanelend,
-
-    sshbugspanelstart,
-    IDC_TITLE_SSHBUGS,
-    IDC_BOX_SSHBUGS1,
-    IDC_BUGS_IGNORE1,
-    IDC_BUGD_IGNORE1,
-    IDC_BUGS_PLAINPW1,
-    IDC_BUGD_PLAINPW1,
-    IDC_BUGS_RSA1,
-    IDC_BUGD_RSA1,
-    IDC_BUGS_HMAC2,
-    IDC_BUGD_HMAC2,
-    IDC_BUGS_DERIVEKEY2,
-    IDC_BUGD_DERIVEKEY2,
-    IDC_BUGS_RSAPAD2,
-    IDC_BUGD_RSAPAD2,
-    IDC_BUGS_DHGEX2,
-    IDC_BUGD_DHGEX2,
-    IDC_BUGS_PKSESSID2,
-    IDC_BUGD_PKSESSID2,
-    sshbugspanelend,
-
-    selectionpanelstart,
-    IDC_TITLE_SELECTION,
-    IDC_BOX_SELECTION1,
-    IDC_BOX_SELECTION2,
-    IDC_BOX_SELECTION3,
-    IDC_MBSTATIC,
-    IDC_MBWINDOWS,
-    IDC_MBXTERM,
-    IDC_MOUSEOVERRIDE,
-    IDC_SELTYPESTATIC,
-    IDC_SELTYPELEX,
-    IDC_SELTYPERECT,
-    IDC_CCSTATIC,
-    IDC_CCLIST,
-    IDC_CCSET,
-    IDC_CCSTATIC2,
-    IDC_CCEDIT,
-    IDC_RAWCNP,
-    IDC_RTFPASTE,
-    selectionpanelend,
-
-    colourspanelstart,
-    IDC_TITLE_COLOURS,
-    IDC_BOX_COLOURS1,
-    IDC_BOX_COLOURS2,
-    IDC_BOLDCOLOUR,
-    IDC_PALETTE,
-    IDC_COLOURSTATIC,
-    IDC_COLOURLIST,
-    IDC_RSTATIC,
-    IDC_GSTATIC,
-    IDC_BSTATIC,
-    IDC_RVALUE,
-    IDC_GVALUE,
-    IDC_BVALUE,
-    IDC_CHANGE,
-    colourspanelend,
-
-    translationpanelstart,
-    IDC_TITLE_TRANSLATION,
-    IDC_BOX_TRANSLATION1,
-    IDC_BOX_TRANSLATION2,
-    IDC_BOX_TRANSLATION3,
-    IDC_CODEPAGESTATIC,
-    IDC_CODEPAGE,
-    IDC_CAPSLOCKCYR,
-    IDC_VTSTATIC,
-    IDC_VTXWINDOWS,
-    IDC_VTOEMANSI,
-    IDC_VTOEMONLY,
-    IDC_VTPOORMAN,
-    IDC_VTUNICODE,
-    translationpanelend,
-
-    tunnelspanelstart,
-    IDC_TITLE_TUNNELS,
-    IDC_BOX_TUNNELS1,
-    IDC_BOX_TUNNELS2,
-    IDC_X11_FORWARD,
-    IDC_X11_DISPSTATIC,
-    IDC_X11_DISPLAY,
-    IDC_X11AUTHSTATIC,
-    IDC_X11MIT,
-    IDC_X11XDM,
-    IDC_LPORT_ALL,
-    IDC_RPORT_ALL,
-    IDC_PFWDSTATIC,
-    IDC_PFWDSTATIC2,
-    IDC_PFWDREMOVE,
-    IDC_PFWDLIST,
-    IDC_PFWDADD,
-    IDC_SPORTSTATIC,
-    IDC_SPORTEDIT,
-    IDC_DPORTSTATIC,
-    IDC_DPORTEDIT,
-    IDC_PFWDLOCAL,
-    IDC_PFWDREMOTE,
-
-    tunnelspanelend,
-
-    controlendvalue
-};
-
-static const char *const colours[] = {
-    "Default Foreground", "Default Bold Foreground",
-    "Default Background", "Default Bold Background",
-    "Cursor Text", "Cursor Colour",
-    "ANSI Black", "ANSI Black Bold",
-    "ANSI Red", "ANSI Red Bold",
-    "ANSI Green", "ANSI Green Bold",
-    "ANSI Yellow", "ANSI Yellow Bold",
-    "ANSI Blue", "ANSI Blue Bold",
-    "ANSI Magenta", "ANSI Magenta Bold",
-    "ANSI Cyan", "ANSI Cyan Bold",
-    "ANSI White", "ANSI White Bold"
+enum {
+    IDCX_ABOUT = IDC_ABOUT,
+    IDCX_TVSTATIC,
+    IDCX_TREEVIEW,
+    IDCX_STDBASE,
+    IDCX_PANELBASE = IDCX_STDBASE + 32
 };
 
 };
 
-static void fmtfont(char *buf)
-{
-    sprintf(buf, "Font: %s, ", cfg.font.name);
-    if (cfg.font.isbold)
-       strcat(buf, "bold, ");
-    if (cfg.font.height == 0)
-       strcat(buf, "default height");
-    else
-       sprintf(buf + strlen(buf), "%d-point",
-               (cfg.font.height < 0 ? -cfg.font.height : cfg.font.height));
-}
-
-char *help_context_cmd(int id)
-{
-    switch (id) {
-      case IDC_HOSTSTATIC:
-      case IDC_HOST:
-      case IDC_PORTSTATIC:
-      case IDC_PORT:
-      case IDC_PROTSTATIC:
-      case IDC_PROTRAW:
-      case IDC_PROTTELNET:
-      case IDC_PROTRLOGIN:
-      case IDC_PROTSSH:
-        return "JI(`',`session.hostname')";
-      case IDC_SESSSTATIC:
-      case IDC_SESSEDIT:
-      case IDC_SESSLIST:
-      case IDC_SESSLOAD:
-      case IDC_SESSSAVE:
-      case IDC_SESSDEL:
-        return "JI(`',`session.saved')";
-      case IDC_CLOSEEXIT:
-      case IDC_COEALWAYS:
-      case IDC_COENEVER:
-      case IDC_COENORMAL:
-        return "JI(`',`session.coe')";
-      case IDC_LSTATSTATIC:
-      case IDC_LSTATOFF:
-      case IDC_LSTATASCII:
-      case IDC_LSTATRAW:
-      case IDC_LSTATPACKET:
-        return "JI(`',`logging.main')";
-      case IDC_LGFSTATIC:
-      case IDC_LGFEDIT:
-      case IDC_LGFBUTTON:
-      case IDC_LGFEXPLAIN:
-        return "JI(`',`logging.filename')";
-      case IDC_LSTATXIST:
-      case IDC_LSTATXOVR:
-      case IDC_LSTATXAPN:
-      case IDC_LSTATXASK:
-        return "JI(`',`logging.exists')";
-
-      case IDC_DELSTATIC:
-      case IDC_DEL008:
-      case IDC_DEL127:
-        return "JI(`',`keyboard.backspace')";
-      case IDC_HOMESTATIC:
-      case IDC_HOMETILDE:
-      case IDC_HOMERXVT:
-        return "JI(`',`keyboard.homeend')";
-      case IDC_FUNCSTATIC:
-      case IDC_FUNCTILDE:
-      case IDC_FUNCLINUX:
-      case IDC_FUNCXTERM:
-      case IDC_FUNCVT400:
-      case IDC_FUNCVT100P:
-      case IDC_FUNCSCO:
-        return "JI(`',`keyboard.funkeys')";
-      case IDC_KPSTATIC:
-      case IDC_KPNORMAL:
-      case IDC_KPAPPLIC:
-        return "JI(`',`keyboard.appkeypad')";
-      case IDC_CURSTATIC:
-      case IDC_CURNORMAL:
-      case IDC_CURAPPLIC:
-        return "JI(`',`keyboard.appcursor')";
-      case IDC_KPNH:
-        return "JI(`',`keyboard.nethack')";
-      case IDC_COMPOSEKEY:
-        return "JI(`',`keyboard.compose')";
-      case IDC_CTRLALTKEYS:
-        return "JI(`',`keyboard.ctrlalt')";
-
-      case IDC_NOAPPLICK:
-      case IDC_NOAPPLICC:
-        return "JI(`',`features.application')";
-      case IDC_NOMOUSEREP:
-        return "JI(`',`features.mouse')";
-      case IDC_NORESIZE:
-        return "JI(`',`features.resize')";
-      case IDC_NOALTSCREEN:
-        return "JI(`',`features.altscreen')";
-      case IDC_NOWINTITLE:
-        return "JI(`',`features.retitle')";
-      case IDC_NODBACKSPACE:
-        return "JI(`',`features.dbackspace')";
-      case IDC_NOCHARSET:
-        return "JI(`',`features.charset')";
-
-      case IDC_WRAPMODE:
-        return "JI(`',`terminal.autowrap')";
-      case IDC_DECOM:
-        return "JI(`',`terminal.decom')";
-      case IDC_LFHASCR:
-        return "JI(`',`terminal.lfhascr')";
-      case IDC_BCE:
-        return "JI(`',`terminal.bce')";
-      case IDC_BLINKTEXT:
-        return "JI(`',`terminal.blink')";
-      case IDC_ANSWERBACK:
-      case IDC_ANSWEREDIT:
-        return "JI(`',`terminal.answerback')";
-      case IDC_ECHOSTATIC:
-      case IDC_ECHOBACKEND:
-      case IDC_ECHOYES:
-      case IDC_ECHONO:
-        return "JI(`',`terminal.localecho')";
-      case IDC_EDITSTATIC:
-      case IDC_EDITBACKEND:
-      case IDC_EDITYES:
-      case IDC_EDITNO:
-        return "JI(`',`terminal.localedit')";
-      case IDC_PRINTERSTATIC:
-      case IDC_PRINTER:
-       return "JI(`',`terminal.printing')";
-
-      case IDC_BELLSTATIC:
-      case IDC_BELL_DISABLED:
-      case IDC_BELL_DEFAULT:
-      case IDC_BELL_WAVEFILE:
-      case IDC_BELL_VISUAL:
-      case IDC_BELL_WAVESTATIC:
-      case IDC_BELL_WAVEEDIT:
-      case IDC_BELL_WAVEBROWSE:
-        return "JI(`',`bell.style')";
-      case IDC_B_IND_STATIC:
-      case IDC_B_IND_DISABLED:
-      case IDC_B_IND_FLASH:
-      case IDC_B_IND_STEADY:
-        return "JI(`',`bell.taskbar')";
-      case IDC_BELLOVL:
-      case IDC_BELLOVLNSTATIC:
-      case IDC_BELLOVLN:
-      case IDC_BELLOVLTSTATIC:
-      case IDC_BELLOVLT:
-      case IDC_BELLOVLEXPLAIN:
-      case IDC_BELLOVLSSTATIC:
-      case IDC_BELLOVLS:
-        return "JI(`',`bell.overload')";
-
-      case IDC_ROWSSTATIC:
-      case IDC_ROWSEDIT:
-      case IDC_COLSSTATIC:
-      case IDC_COLSEDIT:
-        return "JI(`',`window.size')";
-      case IDC_RESIZESTATIC:
-      case IDC_RESIZETERM:
-      case IDC_RESIZEFONT:
-      case IDC_RESIZENONE:
-      case IDC_RESIZEEITHER:
-        return "JI(`',`window.resize')";
-      case IDC_SCROLLBAR:
-      case IDC_SCROLLBARFULLSCREEN:
-      case IDC_SAVESTATIC:
-      case IDC_SAVEEDIT:
-      case IDC_SCROLLKEY:
-      case IDC_SCROLLDISP:
-        return "JI(`',`window.scrollback')";
-
-      case IDC_CLOSEWARN:
-        return "JI(`',`behaviour.closewarn')";
-      case IDC_ALTF4:
-        return "JI(`',`behaviour.altf4')";
-      case IDC_ALTSPACE:
-        return "JI(`',`behaviour.altspace')";
-      case IDC_ALTONLY:
-        return "JI(`',`behaviour.altonly')";
-      case IDC_ALWAYSONTOP:
-        return "JI(`',`behaviour.alwaysontop')";
-      case IDC_FULLSCREENONALTENTER:
-        return "JI(`',`behaviour.altenter')";
-
-      case IDC_CURSORSTATIC:
-      case IDC_CURBLOCK:
-      case IDC_CURUNDER:
-      case IDC_CURVERT:
-      case IDC_BLINKCUR:
-        return "JI(`',`appearance.cursor')";
-      case IDC_FONTSTATIC:
-      case IDC_CHOOSEFONT:
-        return "JI(`',`appearance.font')";
-      case IDC_WINTITLE:
-      case IDC_WINEDIT:
-      case IDC_WINNAME:
-        return "JI(`',`appearance.title')";
-      case IDC_HIDEMOUSE:
-        return "JI(`',`appearance.hidemouse')";
-      case IDC_SUNKENEDGE:
-      case IDC_WINBSTATIC:
-      case IDC_WINBEDIT:
-        return "JI(`',`appearance.border')";
-
-      case IDC_TTSTATIC:
-      case IDC_TTEDIT:
-        return "JI(`',`connection.termtype')";
-      case IDC_LOGSTATIC:
-      case IDC_LOGEDIT:
-        return "JI(`',`connection.username')";
-      case IDC_PINGSTATIC:
-      case IDC_PINGEDIT:
-        return "JI(`',`connection.keepalive')";
-      case IDC_NODELAY:
-        return "JI(`',`connection.nodelay')";
-
-      case IDC_PROXYTYPESTATIC:
-      case IDC_PROXYTYPENONE:
-      case IDC_PROXYTYPEHTTP:
-      case IDC_PROXYTYPESOCKS:
-      case IDC_PROXYTYPETELNET:
-        return "JI(`',`proxy.type')";
-      case IDC_PROXYHOSTSTATIC:
-      case IDC_PROXYHOSTEDIT:
-      case IDC_PROXYPORTSTATIC:
-      case IDC_PROXYPORTEDIT:
-        return "JI(`',`proxy.main')";
-      case IDC_PROXYEXCLUDESTATIC:
-      case IDC_PROXYEXCLUDEEDIT:
-      case IDC_PROXYLOCALHOST:
-        return "JI(`',`proxy.exclude')";
-      case IDC_PROXYDNSSTATIC:
-      case IDC_PROXYDNSNO:
-      case IDC_PROXYDNSAUTO:
-      case IDC_PROXYDNSYES:
-       return "JI(`',`proxy.dns')";
-      case IDC_PROXYUSERSTATIC:
-      case IDC_PROXYUSEREDIT:
-      case IDC_PROXYPASSSTATIC:
-      case IDC_PROXYPASSEDIT:
-        return "JI(`',`proxy.auth')";
-      case IDC_PROXYTELNETCMDSTATIC:
-      case IDC_PROXYTELNETCMDEDIT:
-        return "JI(`',`proxy.command')";
-      case IDC_PROXYSOCKSVERSTATIC:
-      case IDC_PROXYSOCKSVER5:
-      case IDC_PROXYSOCKSVER4:
-        return "JI(`',`proxy.socksver')";
-
-      case IDC_TSSTATIC:
-      case IDC_TSEDIT:
-        return "JI(`',`telnet.termspeed')";
-      case IDC_ENVSTATIC:
-      case IDC_VARSTATIC:
-      case IDC_VAREDIT:
-      case IDC_VALSTATIC:
-      case IDC_VALEDIT:
-      case IDC_ENVLIST:
-      case IDC_ENVADD:
-      case IDC_ENVREMOVE:
-        return "JI(`',`telnet.environ')";
-      case IDC_EMSTATIC:
-      case IDC_EMBSD:
-      case IDC_EMRFC:
-        return "JI(`',`telnet.oldenviron')";
-      case IDC_ACTSTATIC:
-      case IDC_TPASSIVE:
-      case IDC_TACTIVE:
-        return "JI(`',`telnet.passive')";
-      case IDC_TELNETKEY:
-        return "JI(`',`telnet.specialkeys')";
-      case IDC_TELNETRET:
-        return "JI(`',`telnet.newline')";
-
-      case IDC_R_TSSTATIC:
-      case IDC_R_TSEDIT:
-        return "JI(`',`rlogin.termspeed')";
-      case IDC_RLLUSERSTATIC:
-      case IDC_RLLUSEREDIT:
-        return "JI(`',`rlogin.localuser')";
-
-      case IDC_NOPTY:
-        return "JI(`',`ssh.nopty')";
-      case IDC_CIPHERSTATIC2:
-      case IDC_CIPHERLIST:
-      case IDC_CIPHERUP:
-      case IDC_CIPHERDN:
-      case IDC_SSH2DES:
-        return "JI(`',`ssh.ciphers')";
-      case IDC_SSHPROTSTATIC:
-      case IDC_SSHPROT1ONLY:
-      case IDC_SSHPROT1:
-      case IDC_SSHPROT2:
-      case IDC_SSHPROT2ONLY:
-        return "JI(`',`ssh.protocol')";
-      case IDC_CMDSTATIC:
-      case IDC_CMDEDIT:
-        return "JI(`',`ssh.command')";
-      case IDC_COMPRESS:
-        return "JI(`',`ssh.compress')";
-
-      case IDC_PKSTATIC:
-      case IDC_PKEDIT:
-      case IDC_PKBUTTON:
-        return "JI(`',`ssh.auth.privkey')";
-      case IDC_AGENTFWD:
-        return "JI(`',`ssh.auth.agentfwd')";
-      case IDC_CHANGEUSER:
-        return "JI(`',`ssh.auth.changeuser')";
-      case IDC_AUTHTIS:
-        return "JI(`',`ssh.auth.tis')";
-      case IDC_AUTHKI:
-        return "JI(`',`ssh.auth.ki')";
-
-      case IDC_MBSTATIC:
-      case IDC_MBWINDOWS:
-      case IDC_MBXTERM:
-        return "JI(`',`selection.buttons')";
-      case IDC_MOUSEOVERRIDE:
-        return "JI(`',`selection.shiftdrag')";
-      case IDC_SELTYPESTATIC:
-      case IDC_SELTYPELEX:
-      case IDC_SELTYPERECT:
-        return "JI(`',`selection.rect')";
-      case IDC_CCSTATIC:
-      case IDC_CCLIST:
-      case IDC_CCSET:
-      case IDC_CCSTATIC2:
-      case IDC_CCEDIT:
-        return "JI(`',`selection.charclasses')";
-      case IDC_RAWCNP:
-        return "JI(`',`selection.linedraw')";
-      case IDC_RTFPASTE:
-        return "JI(`',`selection.rtf')";
-
-      case IDC_BOLDCOLOUR:
-        return "JI(`',`colours.bold')";
-      case IDC_PALETTE:
-        return "JI(`',`colours.logpal')";
-      case IDC_COLOURSTATIC:
-      case IDC_COLOURLIST:
-      case IDC_RSTATIC:
-      case IDC_GSTATIC:
-      case IDC_BSTATIC:
-      case IDC_RVALUE:
-      case IDC_GVALUE:
-      case IDC_BVALUE:
-      case IDC_CHANGE:
-        return "JI(`',`colours.config')";
-
-      case IDC_CODEPAGESTATIC:
-      case IDC_CODEPAGE:
-        return "JI(`',`translation.codepage')";
-      case IDC_CAPSLOCKCYR:
-        return "JI(`',`translation.cyrillic')";
-      case IDC_VTSTATIC:
-      case IDC_VTXWINDOWS:
-      case IDC_VTOEMANSI:
-      case IDC_VTOEMONLY:
-      case IDC_VTPOORMAN:
-      case IDC_VTUNICODE:
-        return "JI(`',`translation.linedraw')";
-
-      case IDC_X11_FORWARD:
-      case IDC_X11_DISPSTATIC:
-      case IDC_X11_DISPLAY:
-        return "JI(`',`ssh.tunnels.x11')";
-      case IDC_X11AUTHSTATIC:
-      case IDC_X11MIT:
-      case IDC_X11XDM:
-       return "JI(`',`ssh.tunnels.x11auth')";
-      case IDC_PFWDSTATIC:
-      case IDC_PFWDSTATIC2:
-      case IDC_PFWDREMOVE:
-      case IDC_PFWDLIST:
-      case IDC_PFWDADD:
-      case IDC_SPORTSTATIC:
-      case IDC_SPORTEDIT:
-      case IDC_DPORTSTATIC:
-      case IDC_DPORTEDIT:
-      case IDC_PFWDLOCAL:
-      case IDC_PFWDREMOTE:
-        return "JI(`',`ssh.tunnels.portfwd')";
-      case IDC_LPORT_ALL:
-      case IDC_RPORT_ALL:
-        return "JI(`',`ssh.tunnels.portfwd.localhost')";
-
-      case IDC_BUGS_IGNORE1:
-      case IDC_BUGD_IGNORE1:
-       return "JI(`',`ssh.bugs.ignore1')";
-      case IDC_BUGS_PLAINPW1:
-      case IDC_BUGD_PLAINPW1:
-       return "JI(`',`ssh.bugs.plainpw1')";
-      case IDC_BUGS_RSA1:
-      case IDC_BUGD_RSA1:
-       return "JI(`',`ssh.bugs.rsa1')";
-      case IDC_BUGS_HMAC2:
-      case IDC_BUGD_HMAC2:
-       return "JI(`',`ssh.bugs.hmac2')";
-      case IDC_BUGS_DERIVEKEY2:
-      case IDC_BUGD_DERIVEKEY2:
-       return "JI(`',`ssh.bugs.derivekey2')";
-      case IDC_BUGS_RSAPAD2:
-      case IDC_BUGD_RSAPAD2:
-       return "JI(`',`ssh.bugs.rsapad2')";
-      case IDC_BUGS_DHGEX2:
-      case IDC_BUGD_DHGEX2:
-       return "JI(`',`ssh.bugs.dhgex2')";
-      case IDC_BUGS_PKSESSID2:
-      case IDC_BUGD_PKSESSID2:
-       return "JI(`',`ssh.bugs.pksessid2')";
-
-      default:
-        return NULL;
-    }
-}
-
-/* 2nd arg: NZ => don't redraw session list (use when loading
- * a new session) */
-static void init_dlg_ctrls(HWND hwnd, int keepsess)
-{
-    int i;
-    char fontstatic[256];
-
-    SetDlgItemText(hwnd, IDC_HOST, cfg.host);
-    SetDlgItemText(hwnd, IDC_SESSEDIT, savedsession);
-    if (!keepsess) {
-       int i, n;
-       n = SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_GETCOUNT, 0, 0);
-       for (i = n; i-- > 0;)
-           SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_DELETESTRING, i, 0);
-       for (i = 0; i < sesslist.nsessions; i++)
-           SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_ADDSTRING,
-                              0, (LPARAM) (sesslist.sessions[i]));
-    }
-    SetDlgItemInt(hwnd, IDC_PORT, cfg.port, FALSE);
-    CheckRadioButton(hwnd, IDC_PROTRAW, IDC_PROTSSH,
-                    cfg.protocol == PROT_SSH ? IDC_PROTSSH :
-                    cfg.protocol == PROT_TELNET ? IDC_PROTTELNET :
-                    cfg.protocol ==
-                    PROT_RLOGIN ? IDC_PROTRLOGIN : IDC_PROTRAW);
-    SetDlgItemInt(hwnd, IDC_PINGEDIT, cfg.ping_interval, FALSE);
-    CheckDlgButton(hwnd, IDC_NODELAY, cfg.tcp_nodelay);
-
-    CheckRadioButton(hwnd, IDC_DEL008, IDC_DEL127,
-                    cfg.bksp_is_delete ? IDC_DEL127 : IDC_DEL008);
-    CheckRadioButton(hwnd, IDC_HOMETILDE, IDC_HOMERXVT,
-                    cfg.rxvt_homeend ? IDC_HOMERXVT : IDC_HOMETILDE);
-    CheckRadioButton(hwnd, IDC_FUNCTILDE, IDC_FUNCSCO,
-                    cfg.funky_type == 0 ? IDC_FUNCTILDE :
-                    cfg.funky_type == 1 ? IDC_FUNCLINUX :
-                    cfg.funky_type == 2 ? IDC_FUNCXTERM :
-                    cfg.funky_type == 3 ? IDC_FUNCVT400 :
-                    cfg.funky_type == 4 ? IDC_FUNCVT100P :
-                    cfg.funky_type == 5 ? IDC_FUNCSCO : IDC_FUNCTILDE);
-    CheckDlgButton(hwnd, IDC_NOAPPLICC, cfg.no_applic_c);
-    CheckDlgButton(hwnd, IDC_NOAPPLICK, cfg.no_applic_k);
-    CheckDlgButton(hwnd, IDC_NOMOUSEREP, cfg.no_mouse_rep);
-    CheckDlgButton(hwnd, IDC_NORESIZE, cfg.no_remote_resize);
-    CheckDlgButton(hwnd, IDC_NOALTSCREEN, cfg.no_alt_screen);
-    CheckDlgButton(hwnd, IDC_NOWINTITLE, cfg.no_remote_wintitle);
-    CheckDlgButton(hwnd, IDC_NODBACKSPACE, cfg.no_dbackspace);
-    CheckDlgButton(hwnd, IDC_NOCHARSET, cfg.no_remote_charset);
-    CheckRadioButton(hwnd, IDC_CURNORMAL, IDC_CURAPPLIC,
-                    cfg.app_cursor ? IDC_CURAPPLIC : IDC_CURNORMAL);
-    CheckRadioButton(hwnd, IDC_KPNORMAL, IDC_KPNH,
-                    cfg.nethack_keypad ? IDC_KPNH :
-                    cfg.app_keypad ? IDC_KPAPPLIC : IDC_KPNORMAL);
-    CheckDlgButton(hwnd, IDC_ALTF4, cfg.alt_f4);
-    CheckDlgButton(hwnd, IDC_ALTSPACE, cfg.alt_space);
-    CheckDlgButton(hwnd, IDC_ALTONLY, cfg.alt_only);
-    CheckDlgButton(hwnd, IDC_COMPOSEKEY, cfg.compose_key);
-    CheckDlgButton(hwnd, IDC_CTRLALTKEYS, cfg.ctrlaltkeys);
-    CheckDlgButton(hwnd, IDC_TELNETKEY, cfg.telnet_keyboard);
-    CheckDlgButton(hwnd, IDC_TELNETRET, cfg.telnet_newline);
-    CheckRadioButton(hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
-                    cfg.localecho == AUTO ? IDC_ECHOBACKEND :
-                    cfg.localecho == FORCE_ON ? IDC_ECHOYES : IDC_ECHONO);
-    CheckRadioButton(hwnd, IDC_EDITBACKEND, IDC_EDITNO,
-                    cfg.localedit == AUTO ? IDC_EDITBACKEND :
-                    cfg.localedit == FORCE_ON ? IDC_EDITYES : IDC_EDITNO);
-    SetDlgItemText(hwnd, IDC_ANSWEREDIT, cfg.answerback);
-    CheckDlgButton(hwnd, IDC_ALWAYSONTOP, cfg.alwaysontop);
-    CheckDlgButton(hwnd, IDC_FULLSCREENONALTENTER, cfg.fullscreenonaltenter);
-    CheckDlgButton(hwnd, IDC_SCROLLKEY, cfg.scroll_on_key);
-    CheckDlgButton(hwnd, IDC_SCROLLDISP, cfg.scroll_on_disp);
-
-    CheckDlgButton(hwnd, IDC_WRAPMODE, cfg.wrap_mode);
-    CheckDlgButton(hwnd, IDC_DECOM, cfg.dec_om);
-    CheckDlgButton(hwnd, IDC_LFHASCR, cfg.lfhascr);
-    SetDlgItemInt(hwnd, IDC_ROWSEDIT, cfg.height, FALSE);
-    SetDlgItemInt(hwnd, IDC_COLSEDIT, cfg.width, FALSE);
-    SetDlgItemInt(hwnd, IDC_SAVEEDIT, cfg.savelines, FALSE);
-    fmtfont(fontstatic);
-    SetDlgItemText(hwnd, IDC_FONTSTATIC, fontstatic);
-    CheckRadioButton(hwnd, IDC_BELL_DISABLED, IDC_BELL_VISUAL,
-                    cfg.beep == BELL_DISABLED ? IDC_BELL_DISABLED :
-                    cfg.beep == BELL_DEFAULT ? IDC_BELL_DEFAULT :
-                    cfg.beep == BELL_WAVEFILE ? IDC_BELL_WAVEFILE :
-                    cfg.beep ==
-                    BELL_VISUAL ? IDC_BELL_VISUAL : IDC_BELL_DEFAULT);
-    CheckRadioButton(hwnd, IDC_B_IND_DISABLED, IDC_B_IND_STEADY,
-                    cfg.beep_ind ==
-                    B_IND_DISABLED ? IDC_B_IND_DISABLED : cfg.beep_ind ==
-                    B_IND_FLASH ? IDC_B_IND_FLASH : cfg.beep_ind ==
-                    B_IND_STEADY ? IDC_B_IND_STEADY : IDC_B_IND_DISABLED);
-    SetDlgItemText(hwnd, IDC_BELL_WAVEEDIT, cfg.bell_wavefile.path);
-    CheckDlgButton(hwnd, IDC_BELLOVL, cfg.bellovl);
-    SetDlgItemInt(hwnd, IDC_BELLOVLN, cfg.bellovl_n, FALSE);
-    MySetDlgItemFlt(hwnd, IDC_BELLOVLT, cfg.bellovl_t / 1000.0);
-    MySetDlgItemFlt(hwnd, IDC_BELLOVLS, cfg.bellovl_s / 1000.0);
-
-    CheckDlgButton(hwnd, IDC_BCE, cfg.bce);
-    CheckDlgButton(hwnd, IDC_BLINKTEXT, cfg.blinktext);
-
-    SetDlgItemText(hwnd, IDC_WINEDIT, cfg.wintitle);
-    CheckDlgButton(hwnd, IDC_WINNAME, !cfg.win_name_always);
-    CheckDlgButton(hwnd, IDC_HIDEMOUSE, cfg.hide_mouseptr);
-    CheckDlgButton(hwnd, IDC_SUNKENEDGE, cfg.sunken_edge);
-    SetDlgItemInt(hwnd, IDC_WINBEDIT, cfg.window_border, FALSE);
-    CheckRadioButton(hwnd, IDC_CURBLOCK, IDC_CURVERT,
-                    cfg.cursor_type == 0 ? IDC_CURBLOCK :
-                    cfg.cursor_type == 1 ? IDC_CURUNDER : IDC_CURVERT);
-    CheckDlgButton(hwnd, IDC_BLINKCUR, cfg.blink_cur);
-    CheckDlgButton(hwnd, IDC_SCROLLBAR, cfg.scrollbar);
-    CheckDlgButton(hwnd, IDC_SCROLLBARFULLSCREEN, cfg.scrollbar_in_fullscreen);
-    CheckRadioButton(hwnd, IDC_RESIZETERM, IDC_RESIZEEITHER,
-                    cfg.resize_action == RESIZE_TERM ? IDC_RESIZETERM :
-                    cfg.resize_action == RESIZE_FONT ? IDC_RESIZEFONT :
-                    cfg.resize_action == RESIZE_EITHER ? IDC_RESIZEEITHER :
-                    IDC_RESIZENONE);
-    CheckRadioButton(hwnd, IDC_COEALWAYS, IDC_COENORMAL,
-                    cfg.close_on_exit == AUTO ? IDC_COENORMAL :
-                    cfg.close_on_exit ==
-                    FORCE_OFF ? IDC_COENEVER : IDC_COEALWAYS);
-    CheckDlgButton(hwnd, IDC_CLOSEWARN, cfg.warn_on_close);
-
-    SetDlgItemText(hwnd, IDC_TTEDIT, cfg.termtype);
-    SetDlgItemText(hwnd, IDC_TSEDIT, cfg.termspeed);
-    SetDlgItemText(hwnd, IDC_R_TSEDIT, cfg.termspeed);
-    SetDlgItemText(hwnd, IDC_RLLUSEREDIT, cfg.localusername);
-    SetDlgItemText(hwnd, IDC_LOGEDIT, cfg.username);
-    SetDlgItemText(hwnd, IDC_LGFEDIT, cfg.logfilename.path);
-    CheckRadioButton(hwnd, IDC_LSTATOFF, IDC_LSTATPACKET,
-                    cfg.logtype == LGTYP_NONE ? IDC_LSTATOFF :
-                    cfg.logtype == LGTYP_ASCII ? IDC_LSTATASCII :
-                    cfg.logtype == LGTYP_DEBUG ? IDC_LSTATRAW :
-                    IDC_LSTATPACKET);
-    CheckRadioButton(hwnd, IDC_LSTATXOVR, IDC_LSTATXASK,
-                    cfg.logxfovr == LGXF_OVR ? IDC_LSTATXOVR :
-                    cfg.logxfovr == LGXF_ASK ? IDC_LSTATXASK :
-                    IDC_LSTATXAPN);
-    {
-       char *p = cfg.environmt;
-       SendDlgItemMessage(hwnd, IDC_ENVLIST, LB_RESETCONTENT, 0, 0);
-       while (*p) {
-           SendDlgItemMessage(hwnd, IDC_ENVLIST, LB_ADDSTRING, 0,
-                              (LPARAM) p);
-           p += strlen(p) + 1;
-       }
-       p = cfg.portfwd;
-       while (*p) {
-           SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_ADDSTRING, 0,
-                              (LPARAM) p);
-           p += strlen(p) + 1;
-       }
-    }
-    CheckRadioButton(hwnd, IDC_EMBSD, IDC_EMRFC,
-                    cfg.rfc_environ ? IDC_EMRFC : IDC_EMBSD);
-    CheckRadioButton(hwnd, IDC_TPASSIVE, IDC_TACTIVE,
-                    cfg.passive_telnet ? IDC_TPASSIVE : IDC_TACTIVE);
-
-    SetDlgItemText(hwnd, IDC_TTEDIT, cfg.termtype);
-    SetDlgItemText(hwnd, IDC_LOGEDIT, cfg.username);
-    CheckDlgButton(hwnd, IDC_NOPTY, cfg.nopty);
-    CheckDlgButton(hwnd, IDC_COMPRESS, cfg.compression);
-    CheckDlgButton(hwnd, IDC_SSH2DES, cfg.ssh2_des_cbc);
-    CheckDlgButton(hwnd, IDC_AGENTFWD, cfg.agentfwd);
-    CheckDlgButton(hwnd, IDC_CHANGEUSER, cfg.change_username);
-    CheckRadioButton(hwnd, IDC_SSHPROT1ONLY, IDC_SSHPROT2ONLY,
-                    cfg.sshprot == 1 ? IDC_SSHPROT1 :
-                    cfg.sshprot == 2 ? IDC_SSHPROT2 :
-                    cfg.sshprot == 3 ? IDC_SSHPROT2ONLY : IDC_SSHPROT1ONLY);
-    CheckDlgButton(hwnd, IDC_AUTHTIS, cfg.try_tis_auth);
-    CheckDlgButton(hwnd, IDC_AUTHKI, cfg.try_ki_auth);
-    SetDlgItemText(hwnd, IDC_PKEDIT, cfg.keyfile.path);
-    SetDlgItemText(hwnd, IDC_CMDEDIT, cfg.remote_cmd);
-
-    {
-       int i;
-       static const struct { char *s; int c; } ciphers[] = {
-           { "3DES",                   CIPHER_3DES },
-           { "Blowfish",               CIPHER_BLOWFISH },
-           { "DES",                    CIPHER_DES },
-           { "AES (SSH 2 only)",       CIPHER_AES },
-           { "-- warn below here --",  CIPHER_WARN }
-       };
-
-       /* Set up the "selected ciphers" box. */
-       /* (cipherlist assumed to contain all ciphers) */
-       SendDlgItemMessage(hwnd, IDC_CIPHERLIST, LB_RESETCONTENT, 0, 0);
-       for (i = 0; i < CIPHER_MAX; i++) {
-           int c = cfg.ssh_cipherlist[i];
-           int j, pos;
-           char *cstr = NULL;
-           for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
-               if (ciphers[j].c == c) {
-                   cstr = ciphers[j].s;
-                   break;
-               }
-           }
-           pos = SendDlgItemMessage(hwnd, IDC_CIPHERLIST, LB_ADDSTRING,
-                                    0, (LPARAM) cstr);
-           SendDlgItemMessage(hwnd, IDC_CIPHERLIST, LB_SETITEMDATA,
-                              pos, (LPARAM) c);
-       }
-
-    }
-
-    CheckRadioButton(hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
-                    cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS);
-    CheckRadioButton(hwnd, IDC_SELTYPELEX, IDC_SELTYPERECT,
-                    cfg.rect_select == 0 ? IDC_SELTYPELEX : IDC_SELTYPERECT);
-    CheckDlgButton(hwnd, IDC_MOUSEOVERRIDE, cfg.mouse_override);
-    CheckDlgButton(hwnd, IDC_RAWCNP, cfg.rawcnp);
-    CheckDlgButton(hwnd, IDC_RTFPASTE, cfg.rtf_paste);
-    {
-       static int tabs[4] = { 25, 61, 96, 128 };
-       SendDlgItemMessage(hwnd, IDC_CCLIST, LB_SETTABSTOPS, 4,
-                          (LPARAM) tabs);
-    }
-    for (i = 0; i < 128; i++) {
-       char str[100];
-       sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
-               (i >= 0x21 && i != 0x7F) ? i : ' ', cfg.wordness[i]);
-       SendDlgItemMessage(hwnd, IDC_CCLIST, LB_ADDSTRING, 0,
-                          (LPARAM) str);
-    }
-
-    CheckDlgButton(hwnd, IDC_BOLDCOLOUR, cfg.bold_colour);
-    CheckDlgButton(hwnd, IDC_PALETTE, cfg.try_palette);
-    {
-       int i, n;
-       n = SendDlgItemMessage(hwnd, IDC_COLOURLIST, LB_GETCOUNT, 0, 0);
-       for (i = n; i-- > 0;)
-           SendDlgItemMessage(hwnd, IDC_COLOURLIST,
-                              LB_DELETESTRING, i, 0);
-       for (i = 0; i < 22; i++)
-           SendDlgItemMessage(hwnd, IDC_COLOURLIST, LB_ADDSTRING, 0,
-                              (LPARAM) colours[i]);
-    }
-    SendDlgItemMessage(hwnd, IDC_COLOURLIST, LB_SETCURSEL, 0, 0);
-    SetDlgItemInt(hwnd, IDC_RVALUE, cfg.colours[0][0], FALSE);
-    SetDlgItemInt(hwnd, IDC_GVALUE, cfg.colours[0][1], FALSE);
-    SetDlgItemInt(hwnd, IDC_BVALUE, cfg.colours[0][2], FALSE);
-
-    {
-       int i;
-       char *cp;
-       strcpy(cfg.line_codepage, cp_name(decode_codepage(cfg.line_codepage)));
-       SendDlgItemMessage(hwnd, IDC_CODEPAGE, CB_RESETCONTENT, 0, 0);
-       CheckDlgButton (hwnd, IDC_CAPSLOCKCYR, cfg.xlat_capslockcyr);
-       for (i = 0; (cp = cp_enumerate(i)) != NULL; i++) {
-           SendDlgItemMessage(hwnd, IDC_CODEPAGE, CB_ADDSTRING,
-                              0, (LPARAM) cp);
-       }
-       SetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage);
-    }
-
-    {
-       int i, nprinters;
-       printer_enum *pe;
-       pe = printer_start_enum(&nprinters);
-       SendDlgItemMessage(hwnd, IDC_PRINTER, CB_RESETCONTENT, 0, 0);
-       SendDlgItemMessage(hwnd, IDC_PRINTER, CB_ADDSTRING,
-                          0, (LPARAM) PRINTER_DISABLED_STRING);
-       for (i = 0; i < nprinters; i++) {
-           char *printer_name = printer_get_name(pe, i);
-           SendDlgItemMessage(hwnd, IDC_PRINTER, CB_ADDSTRING,
-                              0, (LPARAM) printer_name);
-       }
-       printer_finish_enum(pe);
-       SetDlgItemText(hwnd, IDC_PRINTER,
-                      *cfg.printer ? cfg.printer : PRINTER_DISABLED_STRING);
-    }
-
-    CheckRadioButton(hwnd, IDC_VTXWINDOWS, IDC_VTUNICODE,
-                    cfg.vtmode == VT_XWINDOWS ? IDC_VTXWINDOWS :
-                    cfg.vtmode == VT_OEMANSI ? IDC_VTOEMANSI :
-                    cfg.vtmode == VT_OEMONLY ? IDC_VTOEMONLY :
-                    cfg.vtmode == VT_UNICODE ? IDC_VTUNICODE :
-                    IDC_VTPOORMAN);
-
-    CheckDlgButton(hwnd, IDC_X11_FORWARD, cfg.x11_forward);
-    SetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display);
-    CheckRadioButton(hwnd, IDC_X11MIT, IDC_X11XDM,
-                    cfg.x11_auth == X11_MIT ? IDC_X11MIT : IDC_X11XDM);
-
-    CheckDlgButton(hwnd, IDC_LPORT_ALL, cfg.lport_acceptall);
-    CheckDlgButton(hwnd, IDC_RPORT_ALL, cfg.rport_acceptall);
-    CheckRadioButton(hwnd, IDC_PFWDLOCAL, IDC_PFWDREMOTE, IDC_PFWDLOCAL);
-
-    /* proxy config */
-    CheckRadioButton(hwnd, IDC_PROXYTYPENONE, IDC_PROXYTYPETELNET,
-                    cfg.proxy_type == PROXY_HTTP ? IDC_PROXYTYPEHTTP :
-                    cfg.proxy_type == PROXY_SOCKS ? IDC_PROXYTYPESOCKS :
-                    cfg.proxy_type == PROXY_TELNET ? IDC_PROXYTYPETELNET : IDC_PROXYTYPENONE);
-    SetDlgItemText(hwnd, IDC_PROXYHOSTEDIT, cfg.proxy_host);
-    SetDlgItemInt(hwnd, IDC_PROXYPORTEDIT, cfg.proxy_port, FALSE);
-    SetDlgItemText(hwnd, IDC_PROXYEXCLUDEEDIT, cfg.proxy_exclude_list);
-    CheckDlgButton(hwnd, IDC_PROXYLOCALHOST, cfg.even_proxy_localhost);
-    CheckRadioButton(hwnd, IDC_PROXYDNSNO, IDC_PROXYDNSYES,
-                    cfg.proxy_dns == FORCE_OFF ? IDC_PROXYDNSNO :
-                    cfg.proxy_dns == FORCE_ON ? IDC_PROXYDNSYES :
-                    IDC_PROXYDNSAUTO);
-    SetDlgItemText(hwnd, IDC_PROXYTELNETCMDEDIT, cfg.proxy_telnet_command);
-    SetDlgItemText(hwnd, IDC_PROXYUSEREDIT, cfg.proxy_username);
-    SetDlgItemText(hwnd, IDC_PROXYPASSEDIT, cfg.proxy_password);
-    CheckRadioButton(hwnd, IDC_PROXYSOCKSVER5, IDC_PROXYSOCKSVER4,
-                    cfg.proxy_socks_version == 4 ? IDC_PROXYSOCKSVER4 : IDC_PROXYSOCKSVER5);
-
-    /* SSH bugs config */
-    SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1, CB_SETCURSEL,
-                      cfg.sshbug_ignore1 == FORCE_ON ? 2 :
-                      cfg.sshbug_ignore1 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1, CB_SETCURSEL,
-                      cfg.sshbug_plainpw1 == FORCE_ON ? 2 :
-                      cfg.sshbug_plainpw1 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSA1, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSA1, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSA1, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSA1, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSA1, CB_SETCURSEL,
-                      cfg.sshbug_rsa1 == FORCE_ON ? 2 :
-                      cfg.sshbug_rsa1 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2, CB_SETCURSEL,
-                      cfg.sshbug_hmac2 == FORCE_ON ? 2 :
-                      cfg.sshbug_hmac2 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2, CB_SETCURSEL,
-                      cfg.sshbug_derivekey2 == FORCE_ON ? 2 :
-                      cfg.sshbug_derivekey2 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2, CB_SETCURSEL,
-                      cfg.sshbug_rsapad2 == FORCE_ON ? 2 :
-                      cfg.sshbug_rsapad2 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2, CB_SETCURSEL,
-                      cfg.sshbug_dhgex2 == FORCE_ON ? 2 :
-                      cfg.sshbug_dhgex2 == FORCE_OFF ? 1 : 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2, CB_RESETCONTENT, 0, 0);
-    SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2, CB_ADDSTRING, 0, (LPARAM)"Auto");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2, CB_ADDSTRING, 0, (LPARAM)"Off");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2, CB_ADDSTRING, 0, (LPARAM)"On");
-    SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2, CB_SETCURSEL,
-                      cfg.sshbug_pksessid2 == FORCE_ON ? 2 :
-                      cfg.sshbug_pksessid2 == FORCE_OFF ? 1 : 0, 0);
-}
-
 struct treeview_faff {
     HWND treeview;
     HTREEITEM lastat[4];
 };
 
 static HTREEITEM treeview_insert(struct treeview_faff *faff,
 struct treeview_faff {
     HWND treeview;
     HTREEITEM lastat[4];
 };
 
 static HTREEITEM treeview_insert(struct treeview_faff *faff,
-                                int level, char *text)
+                                int level, char *text, char *path)
 {
     TVINSERTSTRUCT ins;
     int i;
 {
     TVINSERTSTRUCT ins;
     int i;
@@ -1458,8 +247,10 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
 #else
 #define INSITEM item
 #endif
 #else
 #define INSITEM item
 #endif
-    ins.INSITEM.mask = TVIF_TEXT;
+    ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
     ins.INSITEM.pszText = text;
     ins.INSITEM.pszText = text;
+    ins.INSITEM.cchTextMax = strlen(text)+1;
+    ins.INSITEM.lParam = (LPARAM)path;
     newitem = TreeView_InsertItem(faff->treeview, &ins);
     if (level > 0)
        TreeView_Expand(faff->treeview, faff->lastat[level - 1],
     newitem = TreeView_InsertItem(faff->treeview, &ins);
     if (level > 0)
        TreeView_Expand(faff->treeview, faff->lastat[level - 1],
@@ -1473,675 +264,50 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
 /*
  * Create the panelfuls of controls in the configuration box.
  */
 /*
  * Create the panelfuls of controls in the configuration box.
  */
-static void create_controls(HWND hwnd, int dlgtype, int panel)
+static void create_controls(HWND hwnd, char *path)
 {
 {
-    if (panel == sessionpanelstart) {
-       /* The Session panel. Accelerators used: [acgoh] nprtis elvd w */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Basic options for your PuTTY session",
-                IDC_TITLE_SESSION);
-       if (dlgtype == 0) {
-           beginbox(&cp, "Specify your connection by host name or IP address",
-                    IDC_BOX_SESSION1);
-           multiedit(&cp,
-                     "Host &Name (or IP address)",
-                     IDC_HOSTSTATIC, IDC_HOST, 75,
-                     "&Port", IDC_PORTSTATIC, IDC_PORT, 25, NULL);
-           if (backends[3].backend == NULL) {
-               /* this is PuTTYtel, so only three protocols available */
-               radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3,
-                         "&Raw", IDC_PROTRAW,
-                         "&Telnet", IDC_PROTTELNET,
-                         "Rlog&in", IDC_PROTRLOGIN, NULL);
-           } else {
-               radioline(&cp, "Protocol:", IDC_PROTSTATIC, 4,
-                         "&Raw", IDC_PROTRAW,
-                         "&Telnet", IDC_PROTTELNET,
-                         "Rlog&in", IDC_PROTRLOGIN,
-#ifdef FWHACK
-                         "&SSH/hack",
-#else
-                         "&SSH",
-#endif
-                         IDC_PROTSSH, NULL);
-           }
-           endbox(&cp);
-           beginbox(&cp, "Load, save or delete a stored session",
-                    IDC_BOX_SESSION2);
-           sesssaver(&cp, "Sav&ed Sessions",
-                     IDC_SESSSTATIC, IDC_SESSEDIT, IDC_SESSLIST,
-                     "&Load", IDC_SESSLOAD,
-                     "Sa&ve", IDC_SESSSAVE, "&Delete", IDC_SESSDEL, NULL);
-           endbox(&cp);
-       }
-       beginbox(&cp, NULL, IDC_BOX_SESSION3);
-       radioline(&cp, "Close &window on exit:", IDC_CLOSEEXIT, 4,
-                 "Always", IDC_COEALWAYS,
-                 "Never", IDC_COENEVER,
-                 "Only on clean exit", IDC_COENORMAL, NULL);
-       endbox(&cp);
-    }
+    struct ctlpos cp;
+    int index;
+    int base_id;
+    struct winctrls *wc;
 
 
-    if (panel == loggingpanelstart) {
-       /* The Logging panel. Accelerators used: [acgoh] tplsfwe */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling session logging",
-                IDC_TITLE_LOGGING);
-       beginbox(&cp, NULL, IDC_BOX_LOGGING1);
-       radiobig(&cp,
-                "Session logging:", IDC_LSTATSTATIC,
-                "Logging &turned off completely", IDC_LSTATOFF,
-                "Log &printable output only", IDC_LSTATASCII,
-                "&Log all session output", IDC_LSTATRAW,
-                "Log &SSH packet data", IDC_LSTATPACKET,
-                NULL);
-       editbutton(&cp, "Log &file name:",
-                  IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...",
-                  IDC_LGFBUTTON);
-       statictext(&cp, "(Log file name can contain &&Y, &&M, &&D for date,"
-                  " &&T for time, and &&H for host name)", 2, IDC_LGFEXPLAIN);
-       radiobig(&cp,
-                "What to do if the log file already &exists:",
-                IDC_LSTATXIST, "Always overwrite it", IDC_LSTATXOVR,
-                "Always append to the end of it", IDC_LSTATXAPN,
-                "Ask the user every time", IDC_LSTATXASK, NULL);
-       endbox(&cp);
-    }
-
-    if (panel == terminalpanelstart) {
-       /* The Terminal panel. Accelerators used: [acgoh] wdren lts p */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling the terminal emulation",
-                IDC_TITLE_TERMINAL);
-       beginbox(&cp, "Set various terminal options", IDC_BOX_TERMINAL1);
-       checkbox(&cp, "Auto &wrap mode initially on", IDC_WRAPMODE);
-       checkbox(&cp, "&DEC Origin Mode initially on", IDC_DECOM);
-       checkbox(&cp, "Implicit C&R in every LF", IDC_LFHASCR);
-       checkbox(&cp, "Use background colour to &erase screen", IDC_BCE);
-       checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
-       multiedit(&cp,
-                 "An&swerback to ^E:", IDC_ANSWERBACK,
-                 IDC_ANSWEREDIT, 100, NULL);
-       endbox(&cp);
-
-       beginbox(&cp, "Line discipline options", IDC_BOX_TERMINAL2);
-       radioline(&cp, "&Local echo:", IDC_ECHOSTATIC, 3,
-                 "Auto", IDC_ECHOBACKEND,
-                 "Force on", IDC_ECHOYES, "Force off", IDC_ECHONO, NULL);
-       radioline(&cp, "Local line edi&ting:", IDC_EDITSTATIC, 3,
-                 "Auto", IDC_EDITBACKEND,
-                 "Force on", IDC_EDITYES, "Force off", IDC_EDITNO, NULL);
-       endbox(&cp);
-
-       beginbox(&cp, "Remote-controlled printing", IDC_BOX_TERMINAL3);
-       combobox(&cp, "&Printer to send ANSI printer output to:",
-                IDC_PRINTERSTATIC, IDC_PRINTER);
-       endbox(&cp);
-    }
-
-    if (panel == featurespanelstart) {
-       /* The Features panel. Accelerators used: [acgoh] ukswtbrx */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Enabling and disabling advanced terminal features ",
-                IDC_TITLE_FEATURES);
-       beginbox(&cp, NULL, IDC_BOX_FEATURES1);
-       checkbox(&cp, "Disable application c&ursor keys mode", IDC_NOAPPLICC);
-       checkbox(&cp, "Disable application &keypad mode", IDC_NOAPPLICK);
-       checkbox(&cp, "Disable &xterm-style mouse reporting", IDC_NOMOUSEREP);
-       checkbox(&cp, "Disable remote-controlled terminal re&sizing",
-                IDC_NORESIZE);
-       checkbox(&cp, "Disable s&witching to alternate terminal screen",
-                IDC_NOALTSCREEN);
-       checkbox(&cp, "Disable remote-controlled window &title changing",
-                IDC_NOWINTITLE);
-       checkbox(&cp, "Disable destructive &backspace on server sending ^?",
-                IDC_NODBACKSPACE);
-       checkbox(&cp, "Disable remote-controlled cha&racter set configuration",
-                IDC_NOCHARSET);
-       endbox(&cp);
-    }
-
-    if (panel == bellpanelstart) {
-       /* The Bell panel. Accelerators used: [acgoh] bdsm wit */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling the terminal bell",
-                IDC_TITLE_BELL);
-       beginbox(&cp, "Set the style of bell", IDC_BOX_BELL1);
-       radiobig(&cp,
-                "Action to happen when a &bell occurs:", IDC_BELLSTATIC,
-                "None (bell disabled)", IDC_BELL_DISABLED,
-                "Play Windows Default Sound", IDC_BELL_DEFAULT,
-                "Play a custom sound file", IDC_BELL_WAVEFILE,
-                "Visual bell (flash window)", IDC_BELL_VISUAL, NULL);
-       editbutton(&cp, "Custom sound file to play as a bell:",
-                  IDC_BELL_WAVESTATIC, IDC_BELL_WAVEEDIT,
-                  "Bro&wse...", IDC_BELL_WAVEBROWSE);
-       radioline(&cp, "Taskbar/caption &indication on bell:",
-                 IDC_B_IND_STATIC, 3, "Disabled", IDC_B_IND_DISABLED,
-                 "Flashing", IDC_B_IND_FLASH, "Steady", IDC_B_IND_STEADY,
-                 NULL);
-       endbox(&cp);
-       beginbox(&cp, "Control the bell overload behaviour",
-                IDC_BOX_BELL2);
-       checkbox(&cp, "Bell is temporarily &disabled when over-used",
-                IDC_BELLOVL);
-       staticedit(&cp, "Over-use means this &many bells...",
-                  IDC_BELLOVLNSTATIC, IDC_BELLOVLN, 20);
-       staticedit(&cp, "... in &this many seconds",
-                  IDC_BELLOVLTSTATIC, IDC_BELLOVLT, 20);
-       statictext(&cp,
-                  "The bell is re-enabled after a few seconds of silence.",
-                  1, IDC_BELLOVLEXPLAIN);
-       staticedit(&cp, "Seconds of &silence required", IDC_BELLOVLSSTATIC,
-                  IDC_BELLOVLS, 20);
-       endbox(&cp);
-    }
-
-    if (panel == keyboardpanelstart) {
-       /* The Keyboard panel. Accelerators used: [acgoh] bef rntd */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling the effects of keys",
-                IDC_TITLE_KEYBOARD);
-       beginbox(&cp, "Change the sequences sent by:", IDC_BOX_KEYBOARD1);
-       radioline(&cp, "The &Backspace key", IDC_DELSTATIC, 2,
-                 "Control-H", IDC_DEL008,
-                 "Control-? (127)", IDC_DEL127, NULL);
-       radioline(&cp, "The Home and &End keys", IDC_HOMESTATIC, 2,
-                 "Standard", IDC_HOMETILDE, "rxvt", IDC_HOMERXVT, NULL);
-       radioline(&cp, "The &Function keys and keypad", IDC_FUNCSTATIC, 3,
-                 "ESC[n~", IDC_FUNCTILDE,
-                 "Linux", IDC_FUNCLINUX,
-                 "Xterm R6", IDC_FUNCXTERM,
-                 "VT400", IDC_FUNCVT400,
-                 "VT100+", IDC_FUNCVT100P, "SCO", IDC_FUNCSCO, NULL);
-       endbox(&cp);
-       beginbox(&cp, "Application keypad settings:", IDC_BOX_KEYBOARD2);
-       radioline(&cp, "Initial state of cu&rsor keys:", IDC_CURSTATIC, 2,
-                 "Normal", IDC_CURNORMAL,
-                 "Application", IDC_CURAPPLIC, NULL);
-       radioline(&cp, "Initial state of &numeric keypad:", IDC_KPSTATIC,
-                 3, "Normal", IDC_KPNORMAL, "Application", IDC_KPAPPLIC,
-                 "NetHack", IDC_KPNH, NULL);
-       endbox(&cp);
-       beginbox(&cp, "Enable extra keyboard features:",
-                IDC_BOX_KEYBOARD3);
-       checkbox(&cp, "AltGr ac&ts as Compose key", IDC_COMPOSEKEY);
-       checkbox(&cp, "Control-Alt is &different from AltGr",
-                IDC_CTRLALTKEYS);
-       endbox(&cp);
-    }
-
-    if (panel == windowpanelstart) {
-       /* The Window panel. Accelerators used: [acgoh] rmz sdikp */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling PuTTY's window",
-                IDC_TITLE_WINDOW);
-       beginbox(&cp, "Set the size of the window", IDC_BOX_WINDOW1);
-       multiedit(&cp,
-                 "&Rows", IDC_ROWSSTATIC, IDC_ROWSEDIT, 50,
-                 "Colu&mns", IDC_COLSSTATIC, IDC_COLSEDIT, 50, NULL);
-       radiobig(&cp, "When window is resi&zed:", IDC_RESIZESTATIC,
-                "Change the number of rows and columns", IDC_RESIZETERM,
-                "Change the size of the font", IDC_RESIZEFONT,
-                "Change font size only when maximised", IDC_RESIZEEITHER,
-                "Forbid resizing completely", IDC_RESIZENONE, NULL);
-       endbox(&cp);
-       beginbox(&cp, "Control the scrollback in the window",
-                IDC_BOX_WINDOW2);
-       staticedit(&cp, "Lines of &scrollback",
-                  IDC_SAVESTATIC, IDC_SAVEEDIT, 50);
-       checkbox(&cp, "&Display scrollbar", IDC_SCROLLBAR);
-       checkbox(&cp, "D&isplay scrollbar in full screen mode", IDC_SCROLLBARFULLSCREEN);
-       checkbox(&cp, "Reset scrollback on &keypress", IDC_SCROLLKEY);
-       checkbox(&cp, "Reset scrollback on dis&play activity",
-                IDC_SCROLLDISP);
-       endbox(&cp);
-    }
-
-    if (panel == appearancepanelstart) {
-       /* The Appearance panel. Accelerators used: [acgoh] luvb n ti p s */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Configure the appearance of PuTTY's window",
-                IDC_TITLE_APPEARANCE);
-       beginbox(&cp, "Adjust the use of the cursor", IDC_BOX_APPEARANCE1);
-       radioline(&cp, "Cursor appearance:", IDC_CURSORSTATIC, 3,
-                 "B&lock", IDC_CURBLOCK,
-                 "&Underline", IDC_CURUNDER,
-                 "&Vertical line", IDC_CURVERT, NULL);
-       checkbox(&cp, "Cursor &blinks", IDC_BLINKCUR);
-       endbox(&cp);
-       beginbox(&cp, "Set the font used in the terminal window",
-                IDC_BOX_APPEARANCE2);
-       staticbtn(&cp, "", IDC_FONTSTATIC, "Cha&nge...", IDC_CHOOSEFONT);
-       endbox(&cp);
-       beginbox(&cp, "Adjust the use of the window title",
-                IDC_BOX_APPEARANCE3);
-       multiedit(&cp,
-                 "Window &title:", IDC_WINTITLE, IDC_WINEDIT, 100, NULL);
-       checkbox(&cp, "Separate window and &icon titles", IDC_WINNAME);
-       endbox(&cp);
-       beginbox(&cp, "Adjust the use of the mouse pointer",
-                IDC_BOX_APPEARANCE4);
-       checkbox(&cp, "Hide mouse &pointer when typing in window",
-                IDC_HIDEMOUSE);
-       endbox(&cp);
-       beginbox(&cp, "Adjust the window border", IDC_BOX_APPEARANCE5);
-       checkbox(&cp, "&Sunken-edge border (slightly thicker)",
-                IDC_SUNKENEDGE);
-       staticedit(&cp, "Gap between text and window edge",
-                  IDC_WINBSTATIC, IDC_WINBEDIT, 20);
-       endbox(&cp);
-    }
-
-    if (panel == behaviourpanelstart) {
-       /* The Behaviour panel. Accelerators used: [acgoh] w4yltf */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Configure the behaviour of PuTTY's window",
-                IDC_TITLE_WINDOW);
-       beginbox(&cp, NULL, IDC_BOX_BEHAVIOUR1);
-       checkbox(&cp, "&Warn before closing window", IDC_CLOSEWARN);
-       checkbox(&cp, "Window closes on ALT-F&4", IDC_ALTF4);
-       checkbox(&cp, "S&ystem menu appears on ALT-Space", IDC_ALTSPACE);
-       checkbox(&cp, "System menu appears on A&LT alone", IDC_ALTONLY);
-       checkbox(&cp, "Ensure window is always on &top", IDC_ALWAYSONTOP);
-       checkbox(&cp, "&Full screen on Alt-Enter", IDC_FULLSCREENONALTENTER);
-       endbox(&cp);
-    }
-
-    if (panel == translationpanelstart) {
-       /* The Translation panel. Accelerators used: [acgoh] rxbepus */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling character set translation",
-                IDC_TITLE_TRANSLATION);
-       beginbox(&cp, "Character set translation on received data",
-                IDC_BOX_TRANSLATION1);
-       combobox(&cp, "&Received data assumed to be in which character set:",
-                IDC_CODEPAGESTATIC, IDC_CODEPAGE);
-       endbox(&cp);
-        beginbox(&cp, "Enable character set translation on input data",
-                 IDC_BOX_TRANSLATION2);
-        checkbox(&cp, "Cap&s Lock acts as Cyrillic switch",
-                 IDC_CAPSLOCKCYR);
-        endbox(&cp);
-       beginbox(&cp, "Adjust how PuTTY displays line drawing characters",
-                IDC_BOX_TRANSLATION3);
-       radiobig(&cp,
-                "Handling of line drawing characters:", IDC_VTSTATIC,
-                "Font has &XWindows encoding", IDC_VTXWINDOWS,
-                "Use font in &both ANSI and OEM modes", IDC_VTOEMANSI,
-                "Use font in O&EM mode only", IDC_VTOEMONLY,
-                "&Poor man's line drawing (" "+" ", " "-" " and " "|" ")",
-                IDC_VTPOORMAN, "&Unicode mode", IDC_VTUNICODE, NULL);
-       endbox(&cp);
-    }
-
-    if (panel == selectionpanelstart) {
-       /* The Selection panel. Accelerators used: [acgoh] df wxp est nr */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling copy and paste",
-                IDC_TITLE_SELECTION);
-       beginbox(&cp, "Translation of pasted characters",
-                IDC_BOX_SELECTION1);
-       checkbox(&cp,
-                "&Don't translate line drawing chars into +, - and |",
-                IDC_RAWCNP);
-       checkbox(&cp,
-                "Paste to clipboard in RT&F as well as plain text",
-                IDC_RTFPASTE);
-       endbox(&cp);
-       beginbox(&cp, "Control which mouse button does which thing",
-                IDC_BOX_SELECTION2);
-       radiobig(&cp, "Action of mouse buttons:", IDC_MBSTATIC,
-                "&Windows (Right pastes, Middle extends)", IDC_MBWINDOWS,
-                "&xterm (Right extends, Middle pastes)", IDC_MBXTERM,
-                NULL);
-       checkbox(&cp,
-                "Shift overrides a&pplication's use of mouse",
-                IDC_MOUSEOVERRIDE);
-        radioline(&cp,
-                  "Default selection mode (Alt+drag does the other one):",
-                  IDC_SELTYPESTATIC, 2,
-                 "&Normal", IDC_SELTYPELEX,
-                 "&Rectangular block", IDC_SELTYPERECT, NULL);
-       endbox(&cp);
-       beginbox(&cp, "Control the select-one-word-at-a-time mode",
-                IDC_BOX_SELECTION3);
-       charclass(&cp, "Charact&er classes:", IDC_CCSTATIC, IDC_CCLIST,
-                 "&Set", IDC_CCSET, IDC_CCEDIT,
-                 "&to class", IDC_CCSTATIC2);
-       endbox(&cp);
-    }
-
-    if (panel == colourspanelstart) {
-       /* The Colours panel. Accelerators used: [acgoh] blum */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling use of colours",
-                IDC_TITLE_COLOURS);
-       beginbox(&cp, "General options for colour usage",
-                IDC_BOX_COLOURS1);
-       checkbox(&cp, "&Bolded text is a different colour",
-                IDC_BOLDCOLOUR);
-       checkbox(&cp, "Attempt to use &logical palettes", IDC_PALETTE);
-       endbox(&cp);
-       beginbox(&cp, "Adjust the precise colours PuTTY displays",
-                IDC_BOX_COLOURS2);
-       colouredit(&cp, "Select a colo&ur and then click to modify it:",
-                  IDC_COLOURSTATIC, IDC_COLOURLIST,
-                  "&Modify...", IDC_CHANGE,
-                  "Red:", IDC_RSTATIC, IDC_RVALUE,
-                  "Green:", IDC_GSTATIC, IDC_GVALUE,
-                  "Blue:", IDC_BSTATIC, IDC_BVALUE, NULL);
-       endbox(&cp);
-    }
-
-    if (panel == connectionpanelstart) {
-       /* The Connection panel. Accelerators used: [acgoh] tukn */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       bartitle(&cp, "Options controlling the connection",
-                IDC_TITLE_CONNECTION);
-       if (dlgtype == 0) {
-           beginbox(&cp, "Data to send to the server",
-                    IDC_BOX_CONNECTION1);
-           staticedit(&cp, "Terminal-&type string", IDC_TTSTATIC,
-                      IDC_TTEDIT, 50);
-           staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC,
-                      IDC_LOGEDIT, 50);
-           endbox(&cp);
-       } else {
-           beginbox(&cp, "Adjust telnet session.", IDC_BOX_CONNECTION1);
-           checkbox(&cp, "Keyboard sends telnet Backspace and Interrupt",
-                    IDC_TELNETKEY);
-           checkbox(&cp, "Return key sends telnet New Line instead of ^M",
-                    IDC_TELNETRET);
-           endbox(&cp);
-       }
-       beginbox(&cp, "Sending of null packets to keep session active",
-                IDC_BOX_CONNECTION2);
-       staticedit(&cp, "Seconds between &keepalives (0 to turn off)",
-                  IDC_PINGSTATIC, IDC_PINGEDIT, 20);
-       endbox(&cp);
-       if (dlgtype == 0) {
-           beginbox(&cp, "Low-level TCP connection options",
-                    IDC_BOX_CONNECTION3);
-           checkbox(&cp, "Disable &Nagle's algorithm (TCP_NODELAY option)",
-                    IDC_NODELAY);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == proxypanelstart) {
-       /* The Proxy panel. Accelerators used: [acgoh] ntslypeuwmvxd */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling proxy usage",
-                    IDC_TITLE_PROXY);
-           beginbox(&cp, "Proxy basics", IDC_BOX_PROXY1);
-           radioline(&cp, "Proxy type:", IDC_PROXYTYPESTATIC, 4,
-                     "&None", IDC_PROXYTYPENONE,
-                     "H&TTP", IDC_PROXYTYPEHTTP,
-                     "&SOCKS", IDC_PROXYTYPESOCKS,
-                     "Te&lnet", IDC_PROXYTYPETELNET, NULL);
-           multiedit(&cp,
-                     "Prox&y Host", IDC_PROXYHOSTSTATIC, IDC_PROXYHOSTEDIT, 80,
-                     "&Port", IDC_PROXYPORTSTATIC, IDC_PROXYPORTEDIT, 20, NULL);
-           multiedit(&cp,
-                     "&Exclude Hosts/IPs", IDC_PROXYEXCLUDESTATIC,
-                     IDC_PROXYEXCLUDEEDIT, 100, NULL);
-           checkbox(&cp, "Consider pro&xying local host connections",
-                    IDC_PROXYLOCALHOST);
-           radioline(&cp, "Do &DNS name lookup at proxy end:",
-                     IDC_PROXYDNSSTATIC, 3,
-                     "No", IDC_PROXYDNSNO,
-                     "Auto", IDC_PROXYDNSAUTO,
-                     "Yes", IDC_PROXYDNSYES, NULL);
-           staticedit(&cp, "&Username", IDC_PROXYUSERSTATIC,
-                      IDC_PROXYUSEREDIT, 60);
-           staticpassedit(&cp, "Pass&word", IDC_PROXYPASSSTATIC,
-                          IDC_PROXYPASSEDIT, 60);
-           endbox(&cp);
-           beginbox(&cp, "Misc. proxy settings", IDC_BOX_PROXY2);
-           multiedit(&cp,
-                     "Telnet co&mmand", IDC_PROXYTELNETCMDSTATIC,
-                     IDC_PROXYTELNETCMDEDIT, 100, NULL);
-           radioline(&cp, "SOCKS &Version", IDC_PROXYSOCKSVERSTATIC,
-                     2, "Version 5", IDC_PROXYSOCKSVER5, "Version 4",
-                     IDC_PROXYSOCKSVER4, NULL);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == telnetpanelstart) {
-       /* The Telnet panel. Accelerators used: [acgoh] svldr bftk */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling Telnet connections",
-                    IDC_TITLE_TELNET);
-           beginbox(&cp, "Data to send to the server", IDC_BOX_TELNET1);
-           staticedit(&cp, "Terminal-&speed string", IDC_TSSTATIC,
-                      IDC_TSEDIT, 50);
-           envsetter(&cp, "Environment variables:", IDC_ENVSTATIC,
-                     "&Variable", IDC_VARSTATIC, IDC_VAREDIT, "Va&lue",
-                     IDC_VALSTATIC, IDC_VALEDIT, IDC_ENVLIST, "A&dd",
-                     IDC_ENVADD, "&Remove", IDC_ENVREMOVE);
-           endbox(&cp);
-           beginbox(&cp, "Telnet protocol adjustments", IDC_BOX_TELNET2);
-           radioline(&cp, "Handling of OLD_ENVIRON ambiguity:",
-                     IDC_EMSTATIC, 2, "&BSD (commonplace)", IDC_EMBSD,
-                     "R&FC 1408 (unusual)", IDC_EMRFC, NULL);
-           radioline(&cp, "&Telnet negotiation mode:", IDC_ACTSTATIC, 2,
-                     "Passive", IDC_TPASSIVE, "Active",
-                     IDC_TACTIVE, NULL);
-           checkbox(&cp, "&Keyboard sends telnet Backspace and Interrupt",
-                    IDC_TELNETKEY);
-           checkbox(&cp, "Return key sends telnet New Line instead of ^M",
-                    IDC_TELNETRET);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == rloginpanelstart) {
-       /* The Rlogin panel. Accelerators used: [acgoh] sl */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling Rlogin connections",
-                    IDC_TITLE_RLOGIN);
-           beginbox(&cp, "Data to send to the server", IDC_BOX_RLOGIN1);
-           staticedit(&cp, "Terminal-&speed string", IDC_R_TSSTATIC,
-                      IDC_R_TSEDIT, 50);
-           staticedit(&cp, "&Local username:", IDC_RLLUSERSTATIC,
-                      IDC_RLLUSEREDIT, 50);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == sshpanelstart) {
-       /* The SSH panel. Accelerators used: [acgoh] r pel12n sud i */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling SSH connections",
-                    IDC_TITLE_SSH);
-           beginbox(&cp, "Data to send to the server", IDC_BOX_SSH1);
-           multiedit(&cp,
-                     "&Remote command:", IDC_CMDSTATIC, IDC_CMDEDIT, 100,
-                     NULL);
-           endbox(&cp);
-           beginbox(&cp, "Protocol options", IDC_BOX_SSH2);
-           checkbox(&cp, "Don't allocate a &pseudo-terminal", IDC_NOPTY);
-           checkbox(&cp, "Enable compr&ession", IDC_COMPRESS);
-           radioline(&cp, "Preferred SSH protocol version:",
-                     IDC_SSHPROTSTATIC, 4,
-                     "1 on&ly", IDC_SSHPROT1ONLY,
-                     "&1", IDC_SSHPROT1, "&2", IDC_SSHPROT2,
-                     "2 o&nly", IDC_SSHPROT2ONLY, NULL);
-           endbox(&cp);
-           beginbox(&cp, "Encryption options", IDC_BOX_SSH3);
-           /* Adds accelerators: ud */
-           prefslist(&cipherlist, &cp, "Encryption cipher &selection policy:",
-                     IDC_CIPHERSTATIC2, IDC_CIPHERLIST, IDC_CIPHERUP,
-                     IDC_CIPHERDN);
-           checkbox(&cp, "Enable non-standard use of s&ingle-DES in SSH 2",
-                    IDC_SSH2DES);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == sshauthpanelstart) {
-       /* The SSH authentication panel. Accelerators used: [acgoh] m fkiuw */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling SSH authentication",
-                    IDC_TITLE_SSHAUTH);
-           beginbox(&cp, "Authentication methods",
-                    IDC_BOX_SSHAUTH1);
-           checkbox(&cp, "Atte&mpt TIS or CryptoCard authentication (SSH1)",
-                    IDC_AUTHTIS);
-           checkbox(&cp, "Attempt \"keyboard-&interactive\" authentication"
-                    " (SSH2)", IDC_AUTHKI);
-           endbox(&cp);
-           beginbox(&cp, "Authentication parameters",
-                    IDC_BOX_SSHAUTH2);
-           checkbox(&cp, "Allow agent &forwarding", IDC_AGENTFWD);
-           checkbox(&cp, "Allow attempted changes of &username in SSH2",
-                    IDC_CHANGEUSER);
-           editbutton(&cp, "Private &key file for authentication:",
-                      IDC_PKSTATIC, IDC_PKEDIT, "Bro&wse...",
-                      IDC_PKBUTTON);
-           endbox(&cp);
-       }
-    }
-
-    if (panel == sshbugspanelstart) {
-       /* The SSH bugs panel. Accelerators used: [acgoh] isrmepd */
-       struct ctlpos cp;
+    if (!path[0]) {
+       /*
+        * Here we must create the basic standard controls.
+        */
+       ctlposinit(&cp, hwnd, 3, 3, 235);
+       wc = &ctrls_base;
+       base_id = IDCX_STDBASE;
+    } else {
+       /*
+        * Otherwise, we're creating the controls for a particular
+        * panel.
+        */
        ctlposinit(&cp, hwnd, 80, 3, 13);
        ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Workarounds for SSH server bugs",
-                    IDC_TITLE_SSHBUGS);
-           beginbox(&cp, "Detection of known bugs in SSH servers",
-                    IDC_BOX_SSHBUGS1);
-           staticddl(&cp, "Chokes on SSH1 &ignore messages",
-                     IDC_BUGS_IGNORE1, IDC_BUGD_IGNORE1, 20);
-           staticddl(&cp, "Refuses all SSH1 pa&ssword camouflage",
-                     IDC_BUGS_PLAINPW1, IDC_BUGD_PLAINPW1, 20);
-           staticddl(&cp, "Chokes on SSH1 &RSA authentication",
-                     IDC_BUGS_RSA1, IDC_BUGD_RSA1, 20);
-           staticddl(&cp, "Miscomputes SSH2 H&MAC keys",
-                     IDC_BUGS_HMAC2, IDC_BUGD_HMAC2, 20);
-           staticddl(&cp, "Miscomputes SSH2 &encryption keys",
-                     IDC_BUGS_DERIVEKEY2, IDC_BUGD_DERIVEKEY2, 20);
-           staticddl(&cp, "Requires &padding on SSH2 RSA signatures",
-                     IDC_BUGS_RSAPAD2, IDC_BUGD_RSAPAD2, 20);
-           staticddl(&cp, "Chokes on &Diffie-Hellman group exchange",
-                     IDC_BUGS_DHGEX2, IDC_BUGD_DHGEX2, 20);
-           staticddl(&cp, "Misuses the sessio&n ID in PK auth",
-                     IDC_BUGS_PKSESSID2, IDC_BUGD_PKSESSID2, 20);
-           endbox(&cp);
-       }
+       wc = &ctrls_panel;
+       base_id = IDCX_PANELBASE;
     }
 
     }
 
-    if (panel == tunnelspanelstart) {
-       /* The Tunnels panel. Accelerators used: [acgoh] exu tprsdilm */
-       struct ctlpos cp;
-       ctlposinit(&cp, hwnd, 80, 3, 13);
-       if (dlgtype == 0) {
-           bartitle(&cp, "Options controlling SSH tunnelling",
-                    IDC_TITLE_TUNNELS);
-           beginbox(&cp, "X11 forwarding", IDC_BOX_TUNNELS1);
-           checkbox(&cp, "&Enable X11 forwarding", IDC_X11_FORWARD);
-           staticedit(&cp, "&X display location", IDC_X11_DISPSTATIC,
-                     IDC_X11_DISPLAY, 50);
-           radioline(&cp, "Remote X11 a&uthentication protocol",
-                     IDC_X11AUTHSTATIC, 2,
-                     "MIT-Magic-Cookie-1", IDC_X11MIT,
-                     "XDM-Authorization-1", IDC_X11XDM, NULL);
-           endbox(&cp);
-           beginbox(&cp, "Port forwarding", IDC_BOX_TUNNELS2);
-           checkbox(&cp, "Local ports accept connections from o&ther hosts",
-                    IDC_LPORT_ALL);
-           checkbox(&cp, "Remote &ports do the same (SSH v2 only)",
-                    IDC_RPORT_ALL);
-           staticbtn(&cp, "Forwarded ports:", IDC_PFWDSTATIC,
-                     "&Remove", IDC_PFWDREMOVE);
-           fwdsetter(&cp, IDC_PFWDLIST,
-                     "Add new forwarded port:", IDC_PFWDSTATIC2,
-                     "&Source port", IDC_SPORTSTATIC, IDC_SPORTEDIT,
-                     "Dest&ination", IDC_DPORTSTATIC, IDC_DPORTEDIT,
-                     "A&dd", IDC_PFWDADD,
-                     "&Local", IDC_PFWDLOCAL,
-                     "Re&mote", IDC_PFWDREMOTE);
-           endbox(&cp);
-
-       }
+    for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
+       struct controlset *s = ctrlbox->ctrlsets[index];
+       winctrl_layout(&dp, wc, &cp, s, &base_id);
     }
 }
 
     }
 }
 
-/* 
- * Helper function to load the session selected in SESSLIST
- * if any, as this is done in more than one place in
- * GenericMainDlgProc(). 0 => failure.
- */
-static int load_selected_session(HWND hwnd)
-{
-    int n = SendDlgItemMessage(hwnd, IDC_SESSLIST,
-                              LB_GETCURSEL, 0, 0);
-    int isdef;
-    if (n == LB_ERR) {
-       MessageBeep(0);
-       return 0;
-    }
-    isdef = !strcmp(sesslist.sessions[n], "Default Settings");
-    load_settings(sesslist.sessions[n], !isdef, &cfg);
-    init_dlg_ctrls(hwnd, TRUE);
-    if (!isdef)
-       SetDlgItemText(hwnd, IDC_SESSEDIT, sesslist.sessions[n]);
-    else
-       SetDlgItemText(hwnd, IDC_SESSEDIT, "");
-    /* Restore the selection, which will have been clobbered by
-     * SESSEDIT handling. */
-    SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_SETCURSEL, n, 0);
-    return 1;
-}
-
 /*
  * This function is the configuration box.
  */
 /*
  * This function is the configuration box.
  */
-static int GenericMainDlgProc(HWND hwnd, UINT msg,
-                             WPARAM wParam, LPARAM lParam, int dlgtype)
+static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
+                                      WPARAM wParam, LPARAM lParam)
 {
     HWND hw, treeview;
     struct treeview_faff tvfaff;
 {
     HWND hw, treeview;
     struct treeview_faff tvfaff;
-    HTREEITEM hsession;
-    OPENFILENAME of;
-    char filename[sizeof(cfg.keyfile.path)];
-    CHOOSEFONT cf;
-    LOGFONT lf;
-    char fontstatic[256];
-    char portname[32];
-    struct servent *service;
-    int i;
-    static UINT draglistmsg = WM_NULL;
+    int ret;
 
     switch (msg) {
       case WM_INITDIALOG:
 
     switch (msg) {
       case WM_INITDIALOG:
-       readytogo = 0;
+       dp.hwnd = hwnd;
+       create_controls(hwnd, "");     /* Open and Cancel buttons etc */
        SetWindowLong(hwnd, GWL_USERDATA, 0);
         if (help_path)
             SetWindowLong(hwnd, GWL_EXSTYLE,
        SetWindowLong(hwnd, GWL_USERDATA, 0);
         if (help_path)
             SetWindowLong(hwnd, GWL_EXSTYLE,
@@ -2213,52 +379,73 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
        /*
         * Set up the tree view contents.
         */
        /*
         * Set up the tree view contents.
         */
-       hsession = treeview_insert(&tvfaff, 0, "Session");
-       treeview_insert(&tvfaff, 1, "Logging");
-       treeview_insert(&tvfaff, 0, "Terminal");
-       treeview_insert(&tvfaff, 1, "Keyboard");
-       treeview_insert(&tvfaff, 1, "Bell");
-       treeview_insert(&tvfaff, 1, "Features");
-       treeview_insert(&tvfaff, 0, "Window");
-       treeview_insert(&tvfaff, 1, "Appearance");
-       treeview_insert(&tvfaff, 1, "Behaviour");
-       treeview_insert(&tvfaff, 1, "Translation");
-       treeview_insert(&tvfaff, 1, "Selection");
-       treeview_insert(&tvfaff, 1, "Colours");
-       treeview_insert(&tvfaff, 0, "Connection");
-       if (dlgtype == 0) {
-           treeview_insert(&tvfaff, 1, "Proxy");
-           treeview_insert(&tvfaff, 1, "Telnet");
-           treeview_insert(&tvfaff, 1, "Rlogin");
-           if (backends[3].backend != NULL) {
-               treeview_insert(&tvfaff, 1, "SSH");
-               /* XXX long name is ugly */
-               /* XXX make it closed by default? */
-               treeview_insert(&tvfaff, 2, "Auth");
-               treeview_insert(&tvfaff, 2, "Tunnels");
-               treeview_insert(&tvfaff, 2, "Bugs");
+       {
+           HTREEITEM hfirst = NULL;
+           int i;
+           char *path = NULL;
+
+           for (i = 0; i < ctrlbox->nctrlsets; i++) {
+               struct controlset *s = ctrlbox->ctrlsets[i];
+               HTREEITEM item;
+               int j;
+               char *c;
+
+               if (!s->pathname[0])
+                   continue;
+               j = path ? ctrl_path_compare(s->pathname, path) : 0;
+               if (j == INT_MAX)
+                   continue;          /* same path, nothing to add to tree */
+
+               /*
+                * We expect never to find an implicit path
+                * component. For example, we expect never to see
+                * A/B/C followed by A/D/E, because that would
+                * _implicitly_ create A/D. All our path prefixes
+                * are expected to contain actual controls and be
+                * selectable in the treeview; so we would expect
+                * to see A/D _explicitly_ before encountering
+                * A/D/E.
+                */
+               assert(j == ctrl_path_elements(s->pathname) - 1);
+
+               c = strrchr(s->pathname, '/');
+               if (!c)
+                       c = s->pathname;
+               else
+                       c++;
+
+               item = treeview_insert(&tvfaff, j, c, s->pathname);
+               if (!hfirst)
+                   hfirst = item;
+
+               path = s->pathname;
            }
            }
-       }
 
 
-       /*
-        * Put the treeview selection on to the Session panel. This
-        * should also cause creation of the relevant controls.
-        */
-       TreeView_SelectItem(treeview, hsession);
+           /*
+            * Put the treeview selection on to the Session panel.
+            * This should also cause creation of the relevant
+            * controls.
+            */
+           TreeView_SelectItem(treeview, hfirst);
+       }
 
        /*
         * Set focus into the first available control.
         */
        {
 
        /*
         * Set focus into the first available control.
         */
        {
-           HWND ctl;
-           ctl = GetDlgItem(hwnd, IDC_HOST);
-           if (!ctl)
-               ctl = GetDlgItem(hwnd, IDC_CLOSEEXIT);
-           SetFocus(ctl);
+           int i;
+           struct winctrl *c;
+
+           for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
+                i++) {
+               if (c->ctrl) {
+                   dlg_set_focus(c->ctrl, &dp);
+                   break;
+               }
+           }
        }
 
        SetWindowLong(hwnd, GWL_USERDATA, 1);
        }
 
        SetWindowLong(hwnd, GWL_USERDATA, 1);
-       sesslist_has_focus = 0;
        return 0;
       case WM_LBUTTONUP:
        /*
        return 0;
       case WM_LBUTTONUP:
        /*
@@ -2266,8 +453,8 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
         * previous double click on the session list.
         */
        ReleaseCapture();
         * previous double click on the session list.
         */
        ReleaseCapture();
-       if (readytogo)
-           SendMessage(hwnd, WM_COMMAND, IDOK, 0);
+       if (dp.ended)
+           EndDialog(hwnd, dp.endresult ? 1 : 0);
        break;
       case WM_NOTIFY:
        if (LOWORD(wParam) == IDCX_TREEVIEW &&
        break;
       case WM_NOTIFY:
        if (LOWORD(wParam) == IDCX_TREEVIEW &&
@@ -2275,7 +462,6 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
            HTREEITEM i =
                TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
            TVITEM item;
            HTREEITEM i =
                TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
            TVITEM item;
-           int j;
            char buffer[64];
  
            SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
            char buffer[64];
  
            SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
@@ -2283,55 +469,29 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
            item.hItem = i;
            item.pszText = buffer;
            item.cchTextMax = sizeof(buffer);
            item.hItem = i;
            item.pszText = buffer;
            item.cchTextMax = sizeof(buffer);
-           item.mask = TVIF_TEXT;
+           item.mask = TVIF_TEXT | TVIF_PARAM;
            TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
            TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
-           for (j = controlstartvalue; j < controlendvalue; j++) {
-               HWND item = GetDlgItem(hwnd, j);
-               if (item)
-                   DestroyWindow(item);
+           {
+               /* Destroy all controls in the currently visible panel. */
+               int k;
+               HWND item;
+               struct winctrl *c;
+
+               while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
+                   for (k = 0; k < c->num_ids; k++) {
+                       item = GetDlgItem(hwnd, c->base_id + k);
+                       if (item)
+                           DestroyWindow(item);
+                   }
+                   winctrl_rem_shortcuts(&dp, c);
+                   winctrl_remove(&ctrls_panel, c);
+                   sfree(c->data);
+                   sfree(c);
+               }
            }
            }
-           if (!strcmp(buffer, "Session"))
-               create_controls(hwnd, dlgtype, sessionpanelstart);
-           if (!strcmp(buffer, "Logging"))
-               create_controls(hwnd, dlgtype, loggingpanelstart);
-           if (!strcmp(buffer, "Keyboard"))
-               create_controls(hwnd, dlgtype, keyboardpanelstart);
-           if (!strcmp(buffer, "Terminal"))
-               create_controls(hwnd, dlgtype, terminalpanelstart);
-           if (!strcmp(buffer, "Bell"))
-               create_controls(hwnd, dlgtype, bellpanelstart);
-           if (!strcmp(buffer, "Features"))
-               create_controls(hwnd, dlgtype, featurespanelstart);
-           if (!strcmp(buffer, "Window"))
-               create_controls(hwnd, dlgtype, windowpanelstart);
-           if (!strcmp(buffer, "Appearance"))
-               create_controls(hwnd, dlgtype, appearancepanelstart);
-           if (!strcmp(buffer, "Behaviour"))
-               create_controls(hwnd, dlgtype, behaviourpanelstart);
-           if (!strcmp(buffer, "Tunnels"))
-               create_controls(hwnd, dlgtype, tunnelspanelstart);
-           if (!strcmp(buffer, "Connection"))
-               create_controls(hwnd, dlgtype, connectionpanelstart);
-           if (!strcmp(buffer, "Proxy"))
-               create_controls(hwnd, dlgtype, proxypanelstart);
-           if (!strcmp(buffer, "Telnet"))
-               create_controls(hwnd, dlgtype, telnetpanelstart);
-           if (!strcmp(buffer, "Rlogin"))
-               create_controls(hwnd, dlgtype, rloginpanelstart);
-           if (!strcmp(buffer, "SSH"))
-               create_controls(hwnd, dlgtype, sshpanelstart);
-           if (!strcmp(buffer, "Auth"))
-               create_controls(hwnd, dlgtype, sshauthpanelstart);
-           if (!strcmp(buffer, "Bugs"))
-               create_controls(hwnd, dlgtype, sshbugspanelstart);
-           if (!strcmp(buffer, "Selection"))
-               create_controls(hwnd, dlgtype, selectionpanelstart);
-           if (!strcmp(buffer, "Colours"))
-               create_controls(hwnd, dlgtype, colourspanelstart);
-           if (!strcmp(buffer, "Translation"))
-               create_controls(hwnd, dlgtype, translationpanelstart);
-
-           init_dlg_ctrls(hwnd, FALSE);
+           create_controls(hwnd, (char *)item.lParam);
+
+           dlg_refresh(NULL, &dp);    /* set up control values */
  
            SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
            InvalidateRect (hwnd, NULL, TRUE);
  
            SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
            InvalidateRect (hwnd, NULL, TRUE);
@@ -2341,1377 +501,25 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
        }
        break;
       case WM_COMMAND:
        }
        break;
       case WM_COMMAND:
+      case WM_DRAWITEM:
+      default:                        /* also handle drag list msg here */
        /*
         * Only process WM_COMMAND once the dialog is fully formed.
         */
        /*
         * Only process WM_COMMAND once the dialog is fully formed.
         */
-       if (GetWindowLong(hwnd, GWL_USERDATA) == 1)
-           switch (LOWORD(wParam)) {
-             case IDOK:
-               /* Behaviour of the "Open" button is different if the
-                * session list has focus, *unless* the user just
-                * double-clicked... */
-               if (sesslist_has_focus && !readytogo) {
-                   if (!load_selected_session(hwnd)) {
-                       MessageBeep(0);
-                       return 0;
-                   }
-               }
-               /* If at this point we have a valid session, go! */
-               if (*cfg.host) {
-                    if (requested_help) {
-                        WinHelp(hwnd, help_path, HELP_QUIT, 0);
-                        requested_help = FALSE;
-                    }
-                   EndDialog(hwnd, 1);
-                } else
-                   MessageBeep(0);
-               return 0;
-             case IDC_HELPBTN:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                    if (help_path) {
-                        WinHelp(hwnd, help_path,
-                                help_has_contents ? HELP_FINDER : HELP_CONTENTS,
-                                0);
-                        requested_help = TRUE;
-                    }
-                }
-                break;
-             case IDCANCEL:
-                if (requested_help) {
-                    WinHelp(hwnd, help_path, HELP_QUIT, 0);
-                    requested_help = FALSE;
-                }
-               EndDialog(hwnd, 0);
-               return 0;
-             case IDC_PROTTELNET:
-             case IDC_PROTRLOGIN:
-             case IDC_PROTSSH:
-             case IDC_PROTRAW:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   int i = IsDlgButtonChecked(hwnd, IDC_PROTSSH);
-                   int j = IsDlgButtonChecked(hwnd, IDC_PROTTELNET);
-                   int k = IsDlgButtonChecked(hwnd, IDC_PROTRLOGIN);
-                   cfg.protocol =
-                       i ? PROT_SSH : j ? PROT_TELNET : k ? PROT_RLOGIN :
-                       PROT_RAW;
-                   /*
-                    * When switching using the arrow keys, we
-                    * appear to get two of these messages, both
-                    * mentioning the target button in
-                    * LOWORD(wParam), but one of them called while
-                    * the previous button is still checked. This
-                    * causes an unnecessary reset of the port
-                    * number field, which we fix by ensuring here
-                    * that the button selected is indeed the one
-                    * checked.
-                    */
-                   if (IsDlgButtonChecked(hwnd, LOWORD(wParam)) &&
-                       ((cfg.protocol == PROT_SSH && cfg.port != 22)
-                        || (cfg.protocol == PROT_TELNET && cfg.port != 23)
-                        || (cfg.protocol == PROT_RLOGIN
-                            && cfg.port != 513))) {
-                       cfg.port = i ? 22 : j ? 23 : 513;
-                       SetDlgItemInt(hwnd, IDC_PORT, cfg.port, FALSE);
-                   }
-               }
-               break;
-             case IDC_HOST:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_HOST, cfg.host,
-                                  sizeof(cfg.host) - 1);
-               break;
-             case IDC_PORT:
-               if (HIWORD(wParam) == EN_CHANGE) {
-                   GetDlgItemText(hwnd, IDC_PORT, portname, 31);
-                   if (isdigit(portname[0]))
-                       MyGetDlgItemInt(hwnd, IDC_PORT, &cfg.port);
-                   else {
-                       service = getservbyname(portname, NULL);
-                       if (service)
-                           cfg.port = ntohs(service->s_port);
-                       else
-                           cfg.port = 0;
-                   }
-               }
-               break;
-             case IDC_SESSEDIT:
-               if (HIWORD(wParam) == EN_CHANGE) {
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_SETCURSEL,
-                                      (WPARAM) - 1, 0);
-                   GetDlgItemText(hwnd, IDC_SESSEDIT,
-                                  savedsession, sizeof(savedsession) - 1);
-                   savedsession[sizeof(savedsession) - 1] = '\0';
-               }
-               break;
-             case IDC_SESSSAVE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   /*
-                    * Save a session
-                    */
-                   char str[2048];
-                   GetDlgItemText(hwnd, IDC_SESSEDIT, str,
-                                  sizeof(str) - 1);
-                   if (!*str) {
-                       int n = SendDlgItemMessage(hwnd, IDC_SESSLIST,
-                                                  LB_GETCURSEL, 0, 0);
-                       if (n == LB_ERR) {
-                           MessageBeep(0);
-                           break;
-                       }
-                       strcpy(str, sesslist.sessions[n]);
-                   }
-                   save_settings(str, !!strcmp(str, "Default Settings"),
-                                 &cfg);
-                   get_sesslist(&sesslist, FALSE);
-                   get_sesslist(&sesslist, TRUE);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, WM_SETREDRAW,
-                                      FALSE, 0);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_RESETCONTENT,
-                                      0, 0);
-                   for (i = 0; i < sesslist.nsessions; i++)
-                       SendDlgItemMessage(hwnd, IDC_SESSLIST,
-                                          LB_ADDSTRING, 0,
-                                          (LPARAM) (sesslist.sessions[i]));
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_SETCURSEL,
-                                      (WPARAM) - 1, 0);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, WM_SETREDRAW,
-                                      TRUE, 0);
-                   InvalidateRect(GetDlgItem(hwnd, IDC_SESSLIST), NULL,
-                                  TRUE);
-               }
-               break;
-             case IDC_SESSLIST:
-             case IDC_SESSLOAD:
-               if (LOWORD(wParam) == IDC_SESSLIST) {
-                   if (HIWORD(wParam) == LBN_SETFOCUS)
-                       sesslist_has_focus = 1;
-                   else if (HIWORD(wParam) == LBN_KILLFOCUS)
-                       sesslist_has_focus = 0;
-               }
-               if (LOWORD(wParam) == IDC_SESSLOAD &&
-                   HIWORD(wParam) != BN_CLICKED &&
-                   HIWORD(wParam) != BN_DOUBLECLICKED) break;
-               if (LOWORD(wParam) == IDC_SESSLIST &&
-                   HIWORD(wParam) != LBN_DBLCLK) break;
-               /* Load the session selected in SESSLIST. */
-               if (load_selected_session(hwnd) &&
-                   LOWORD(wParam) == IDC_SESSLIST) {
-                   /*
-                    * A double-click on a saved session should
-                    * actually start the session, not just load it.
-                    * Unless it's Default Settings or some other
-                    * host-less set of saved settings.
-                    */
-                   if (*cfg.host) {
-                       readytogo = TRUE;
-                       SetCapture(hwnd);
-                   }
-               }
-               break;
-             case IDC_SESSDEL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   int n = SendDlgItemMessage(hwnd, IDC_SESSLIST,
-                                              LB_GETCURSEL, 0, 0);
-                   if (n == LB_ERR || n == 0) {
-                       MessageBeep(0);
-                       break;
-                   }
-                   del_settings(sesslist.sessions[n]);
-                   get_sesslist(&sesslist, FALSE);
-                   get_sesslist(&sesslist, TRUE);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, WM_SETREDRAW,
-                                      FALSE, 0);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_RESETCONTENT,
-                                      0, 0);
-                   for (i = 0; i < sesslist.nsessions; i++)
-                       SendDlgItemMessage(hwnd, IDC_SESSLIST,
-                                          LB_ADDSTRING, 0,
-                                          (LPARAM) (sesslist.sessions[i]));
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, LB_SETCURSEL,
-                                      (WPARAM) - 1, 0);
-                   SendDlgItemMessage(hwnd, IDC_SESSLIST, WM_SETREDRAW,
-                                      TRUE, 0);
-                   InvalidateRect(GetDlgItem(hwnd, IDC_SESSLIST), NULL,
-                                  TRUE);
-               }
-             case IDC_PINGEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_PINGEDIT,
-                                   &cfg.ping_interval);
-               break;
-             case IDC_NODELAY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.tcp_nodelay =
-                       IsDlgButtonChecked(hwnd, IDC_NODELAY);
-               break;
-             case IDC_DEL008:
-             case IDC_DEL127:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.bksp_is_delete =
-                       IsDlgButtonChecked(hwnd, IDC_DEL127);
-               break;
-             case IDC_HOMETILDE:
-             case IDC_HOMERXVT:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.rxvt_homeend =
-                       IsDlgButtonChecked(hwnd, IDC_HOMERXVT);
-               break;
-             case IDC_FUNCTILDE:
-             case IDC_FUNCLINUX:
-             case IDC_FUNCXTERM:
-             case IDC_FUNCVT400:
-             case IDC_FUNCVT100P:
-             case IDC_FUNCSCO:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       switch (LOWORD(wParam)) {
-                     case IDC_FUNCTILDE:
-                       cfg.funky_type = 0;
-                       break;
-                     case IDC_FUNCLINUX:
-                       cfg.funky_type = 1;
-                       break;
-                     case IDC_FUNCXTERM:
-                       cfg.funky_type = 2;
-                       break;
-                     case IDC_FUNCVT400:
-                       cfg.funky_type = 3;
-                       break;
-                     case IDC_FUNCVT100P:
-                       cfg.funky_type = 4;
-                       break;
-                     case IDC_FUNCSCO:
-                       cfg.funky_type = 5;
-                       break;
-                   }
-               break;
-             case IDC_KPNORMAL:
-             case IDC_KPAPPLIC:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.app_keypad =
-                       IsDlgButtonChecked(hwnd, IDC_KPAPPLIC);
-                   cfg.nethack_keypad = FALSE;
-               }
-               break;
-             case IDC_KPNH:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.app_keypad = FALSE;
-                   cfg.nethack_keypad = TRUE;
-               }
-               break;
-             case IDC_CURNORMAL:
-             case IDC_CURAPPLIC:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.app_cursor =
-                       IsDlgButtonChecked(hwnd, IDC_CURAPPLIC);
-               break;
-             case IDC_NOAPPLICC:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_applic_c =
-                       IsDlgButtonChecked(hwnd, IDC_NOAPPLICC);
-               break;
-             case IDC_NOAPPLICK:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_applic_k =
-                       IsDlgButtonChecked(hwnd, IDC_NOAPPLICK);
-               break;
-             case IDC_NOMOUSEREP:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_mouse_rep =
-                       IsDlgButtonChecked(hwnd, IDC_NOMOUSEREP);
-               break;
-             case IDC_NORESIZE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_remote_resize =
-                       IsDlgButtonChecked(hwnd, IDC_NORESIZE);
-               break;
-             case IDC_NOALTSCREEN:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_alt_screen =
-                       IsDlgButtonChecked(hwnd, IDC_NOALTSCREEN);
-               break;
-             case IDC_NOWINTITLE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_remote_wintitle =
-                       IsDlgButtonChecked(hwnd, IDC_NOWINTITLE);
-               break;
-             case IDC_NODBACKSPACE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_dbackspace =
-                       IsDlgButtonChecked(hwnd, IDC_NODBACKSPACE);
-               break;
-             case IDC_NOCHARSET:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.no_remote_charset =
-                       IsDlgButtonChecked(hwnd, IDC_NOCHARSET);
-               break;
-             case IDC_ALTF4:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.alt_f4 = IsDlgButtonChecked(hwnd, IDC_ALTF4);
-               break;
-             case IDC_ALTSPACE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.alt_space =
-                       IsDlgButtonChecked(hwnd, IDC_ALTSPACE);
-               break;
-             case IDC_ALTONLY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.alt_only =
-                       IsDlgButtonChecked(hwnd, IDC_ALTONLY);
-               break;
-             case IDC_ECHOBACKEND:
-             case IDC_ECHOYES:
-             case IDC_ECHONO:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (LOWORD(wParam) == IDC_ECHOBACKEND)
-                       cfg.localecho = AUTO;
-                   if (LOWORD(wParam) == IDC_ECHOYES)
-                       cfg.localecho = FORCE_ON;
-                   if (LOWORD(wParam) == IDC_ECHONO)
-                       cfg.localecho = FORCE_OFF;
-               }
-               break;
-             case IDC_EDITBACKEND:
-             case IDC_EDITYES:
-             case IDC_EDITNO:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (LOWORD(wParam) == IDC_EDITBACKEND)
-                       cfg.localedit = AUTO;
-                   if (LOWORD(wParam) == IDC_EDITYES)
-                       cfg.localedit = FORCE_ON;
-                   if (LOWORD(wParam) == IDC_EDITNO)
-                       cfg.localedit = FORCE_OFF;
-               }
-               break;
-             case IDC_ANSWEREDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_ANSWEREDIT, cfg.answerback,
-                                  sizeof(cfg.answerback) - 1);
-               break;
-             case IDC_ALWAYSONTOP:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.alwaysontop =
-                       IsDlgButtonChecked(hwnd, IDC_ALWAYSONTOP);
-               break;
-             case IDC_FULLSCREENONALTENTER:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.fullscreenonaltenter =
-                       IsDlgButtonChecked(hwnd, IDC_FULLSCREENONALTENTER);
-               break;
-             case IDC_SCROLLKEY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.scroll_on_key =
-                       IsDlgButtonChecked(hwnd, IDC_SCROLLKEY);
-               break;
-             case IDC_SCROLLDISP:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.scroll_on_disp =
-                       IsDlgButtonChecked(hwnd, IDC_SCROLLDISP);
-               break;
-             case IDC_COMPOSEKEY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.compose_key =
-                       IsDlgButtonChecked(hwnd, IDC_COMPOSEKEY);
-               break;
-             case IDC_CTRLALTKEYS:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.ctrlaltkeys =
-                       IsDlgButtonChecked(hwnd, IDC_CTRLALTKEYS);
-               break;
-             case IDC_TELNETKEY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.telnet_keyboard =
-                       IsDlgButtonChecked(hwnd, IDC_TELNETKEY);
-               break;
-             case IDC_TELNETRET:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.telnet_newline =
-                       IsDlgButtonChecked(hwnd, IDC_TELNETRET);
-               break;
-             case IDC_WRAPMODE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.wrap_mode =
-                       IsDlgButtonChecked(hwnd, IDC_WRAPMODE);
-               break;
-             case IDC_DECOM:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.dec_om = IsDlgButtonChecked(hwnd, IDC_DECOM);
-               break;
-             case IDC_LFHASCR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.lfhascr =
-                       IsDlgButtonChecked(hwnd, IDC_LFHASCR);
-               break;
-             case IDC_ROWSEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_ROWSEDIT, &cfg.height);
-               break;
-             case IDC_COLSEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_COLSEDIT, &cfg.width);
-               break;
-             case IDC_SAVEEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_SAVEEDIT, &cfg.savelines);
-               break;
-             case IDC_CHOOSEFONT:
-               {
-                   HDC hdc = GetDC(0);
-                   lf.lfHeight = -MulDiv(cfg.font.height,
-                                         GetDeviceCaps(hdc, LOGPIXELSY),
-                                         72);
-                   ReleaseDC(0, hdc);
-               }
-               lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
-               lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
-               lf.lfWeight = (cfg.font.isbold ? FW_BOLD : 0);
-               lf.lfCharSet = cfg.font.charset;
-               lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
-               lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
-               lf.lfQuality = DEFAULT_QUALITY;
-               lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
-               strncpy(lf.lfFaceName, cfg.font.name,
-                       sizeof(lf.lfFaceName) - 1);
-               lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0';
-
-               cf.lStructSize = sizeof(cf);
-               cf.hwndOwner = hwnd;
-               cf.lpLogFont = &lf;
-               cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
-                   CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
-
-               if (ChooseFont(&cf)) {
-                   strncpy(cfg.font.name, lf.lfFaceName,
-                           sizeof(cfg.font.name) - 1);
-                   cfg.font.name[sizeof(cfg.font.name) - 1] = '\0';
-                   cfg.font.isbold = (lf.lfWeight == FW_BOLD);
-                   cfg.font.charset = lf.lfCharSet;
-                   cfg.font.height = cf.iPointSize / 10;
-                   fmtfont(fontstatic);
-                   SetDlgItemText(hwnd, IDC_FONTSTATIC, fontstatic);
-               }
-               break;
-             case IDC_BELL_DISABLED:
-             case IDC_BELL_DEFAULT:
-             case IDC_BELL_WAVEFILE:
-             case IDC_BELL_VISUAL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (LOWORD(wParam) == IDC_BELL_DISABLED)
-                       cfg.beep = BELL_DISABLED;
-                   if (LOWORD(wParam) == IDC_BELL_DEFAULT)
-                       cfg.beep = BELL_DEFAULT;
-                   if (LOWORD(wParam) == IDC_BELL_WAVEFILE)
-                       cfg.beep = BELL_WAVEFILE;
-                   if (LOWORD(wParam) == IDC_BELL_VISUAL)
-                       cfg.beep = BELL_VISUAL;
-               }
-               break;
-             case IDC_B_IND_DISABLED:
-             case IDC_B_IND_FLASH:
-             case IDC_B_IND_STEADY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (LOWORD(wParam) == IDC_B_IND_DISABLED)
-                       cfg.beep_ind = B_IND_DISABLED;
-                   if (LOWORD(wParam) == IDC_B_IND_FLASH)
-                       cfg.beep_ind = B_IND_FLASH;
-                   if (LOWORD(wParam) == IDC_B_IND_STEADY)
-                       cfg.beep_ind = B_IND_STEADY;
-               }
-               break;
-             case IDC_BELL_WAVEBROWSE:
-               memset(&of, 0, sizeof(of));
-#ifdef OPENFILENAME_SIZE_VERSION_400
-               of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
-#else
-               of.lStructSize = sizeof(of);
-#endif
-               of.hwndOwner = hwnd;
-               of.lpstrFilter = "Wave Files (*.wav)\0*.WAV\0"
-                   "All Files (*.*)\0*\0\0\0";
-               of.lpstrCustomFilter = NULL;
-               of.nFilterIndex = 1;
-               of.lpstrFile = filename;
-               strcpy(filename, cfg.bell_wavefile.path);
-               of.nMaxFile = sizeof(filename);
-               of.lpstrFileTitle = NULL;
-               of.lpstrInitialDir = NULL;
-               of.lpstrTitle = "Select Bell Sound File";
-               of.Flags = 0;
-               if (GetOpenFileName(&of)) {
-                   strcpy(cfg.bell_wavefile.path, filename);
-                   SetDlgItemText(hwnd, IDC_BELL_WAVEEDIT,
-                                  cfg.bell_wavefile.path);
-               }
-               break;
-             case IDC_BELL_WAVEEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_BELL_WAVEEDIT,
-                                  cfg.bell_wavefile.path,
-                                  sizeof(cfg.bell_wavefile.path) - 1);
-               break;
-             case IDC_BELLOVL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.bellovl =
-                       IsDlgButtonChecked(hwnd, IDC_BELLOVL);
-               break;
-             case IDC_BELLOVLN:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_BELLOVLN, &cfg.bellovl_n);
-               break;
-             case IDC_BELLOVLT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemFlt(hwnd, IDC_BELLOVLT, &cfg.bellovl_t,
-                                   1000);
-               break;
-             case IDC_BELLOVLS:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemFlt(hwnd, IDC_BELLOVLS, &cfg.bellovl_s,
-                                   1000);
-               break;
-             case IDC_BLINKTEXT:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.blinktext =
-                       IsDlgButtonChecked(hwnd, IDC_BLINKTEXT);
-               break;
-             case IDC_BCE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.bce = IsDlgButtonChecked(hwnd, IDC_BCE);
-               break;
-             case IDC_WINNAME:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.win_name_always =
-                       !IsDlgButtonChecked(hwnd, IDC_WINNAME);
-               break;
-             case IDC_HIDEMOUSE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.hide_mouseptr =
-                       IsDlgButtonChecked(hwnd, IDC_HIDEMOUSE);
-               break;
-             case IDC_SUNKENEDGE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.sunken_edge =
-                       IsDlgButtonChecked(hwnd, IDC_SUNKENEDGE);
-               break;
-             case IDC_WINBEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   MyGetDlgItemInt(hwnd, IDC_WINBEDIT,
-                                   &cfg.window_border);
-               if (cfg.window_border > 32)
-                   cfg.window_border = 32;
-               break;
-             case IDC_CURBLOCK:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.cursor_type = 0;
-               break;
-             case IDC_CURUNDER:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.cursor_type = 1;
-               break;
-             case IDC_CURVERT:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.cursor_type = 2;
-               break;
-             case IDC_BLINKCUR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.blink_cur =
-                       IsDlgButtonChecked(hwnd, IDC_BLINKCUR);
-               break;
-             case IDC_SCROLLBAR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.scrollbar =
-                       IsDlgButtonChecked(hwnd, IDC_SCROLLBAR);
-               break;
-             case IDC_SCROLLBARFULLSCREEN:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                   cfg.scrollbar_in_fullscreen =
-                   IsDlgButtonChecked(hwnd, IDC_SCROLLBARFULLSCREEN);
-               break;
-             case IDC_RESIZETERM:
-             case IDC_RESIZEFONT:
-             case IDC_RESIZENONE:
-             case IDC_RESIZEEITHER:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.resize_action =
-                       IsDlgButtonChecked(hwnd,
-                                          IDC_RESIZETERM) ? RESIZE_TERM :
-                       IsDlgButtonChecked(hwnd,
-                                          IDC_RESIZEFONT) ? RESIZE_FONT :
-                       IsDlgButtonChecked(hwnd,
-                                          IDC_RESIZEEITHER) ? RESIZE_EITHER :
-                       RESIZE_DISABLED;
-               }
-               break;
-             case IDC_WINEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_WINEDIT, cfg.wintitle,
-                                  sizeof(cfg.wintitle) - 1);
-               break;
-             case IDC_COEALWAYS:
-             case IDC_COENEVER:
-             case IDC_COENORMAL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.close_on_exit =
-                       IsDlgButtonChecked(hwnd,
-                                          IDC_COEALWAYS) ? FORCE_ON :
-                       IsDlgButtonChecked(hwnd,
-                                          IDC_COENEVER) ? FORCE_OFF :
-                       AUTO;
-               }
-               break;
-             case IDC_CLOSEWARN:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.warn_on_close =
-                       IsDlgButtonChecked(hwnd, IDC_CLOSEWARN);
-               break;
-             case IDC_TTEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_TTEDIT, cfg.termtype,
-                                  sizeof(cfg.termtype) - 1);
-               break;
-
-               /* proxy config */
-             case IDC_PROXYHOSTEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PROXYHOSTEDIT, cfg.proxy_host, 
-                                  sizeof(cfg.proxy_host) - 1);
-               break;
-             case IDC_PROXYPORTEDIT:
-               if (HIWORD(wParam) == EN_CHANGE) {
-                   GetDlgItemText(hwnd, IDC_PROXYPORTEDIT, portname, 31);
-                   if (isdigit(portname[0]))
-                       MyGetDlgItemInt(hwnd, IDC_PROXYPORTEDIT, &cfg.proxy_port);
-                   else {
-                       service = getservbyname(portname, NULL);
-                       if (service)
-                           cfg.proxy_port = ntohs(service->s_port);
-                       else
-                           cfg.proxy_port = 0;
-                   }
-               }
-               break;
-             case IDC_PROXYEXCLUDEEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PROXYEXCLUDEEDIT,
-                                  cfg.proxy_exclude_list,
-                                  sizeof(cfg.proxy_exclude_list) - 1);
-               break;
-             case IDC_PROXYUSEREDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PROXYUSEREDIT,
-                                  cfg.proxy_username, 
-                                  sizeof(cfg.proxy_username) - 1);
-               break;
-             case IDC_PROXYPASSEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PROXYPASSEDIT,
-                                  cfg.proxy_password, 
-                                  sizeof(cfg.proxy_password) - 1);
-               break;
-             case IDC_PROXYTELNETCMDEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PROXYTELNETCMDEDIT,
-                                  cfg.proxy_telnet_command,
-                                  sizeof(cfg.proxy_telnet_command) - 1);
-               break;
-             case IDC_PROXYSOCKSVER5:
-             case IDC_PROXYSOCKSVER4:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.proxy_socks_version =
-                       IsDlgButtonChecked(hwnd, IDC_PROXYSOCKSVER4) ? 4 : 5;
-               }
-               break;
-             case IDC_PROXYLOCALHOST:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                   cfg.even_proxy_localhost =
-                   IsDlgButtonChecked(hwnd, IDC_PROXYLOCALHOST);
-               break;
-             case IDC_PROXYDNSNO:
-             case IDC_PROXYDNSAUTO:
-             case IDC_PROXYDNSYES:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.proxy_dns =
-                       IsDlgButtonChecked(hwnd, IDC_PROXYDNSNO) ? FORCE_OFF :
-                       IsDlgButtonChecked(hwnd, IDC_PROXYDNSYES) ? FORCE_ON :
-                       AUTO;
-               }
-               break;
-             case IDC_PROXYTYPENONE:
-             case IDC_PROXYTYPEHTTP:
-             case IDC_PROXYTYPESOCKS:
-             case IDC_PROXYTYPETELNET:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.proxy_type =
-                       IsDlgButtonChecked(hwnd, IDC_PROXYTYPEHTTP) ? PROXY_HTTP :
-                       IsDlgButtonChecked(hwnd, IDC_PROXYTYPESOCKS) ? PROXY_SOCKS :
-                       IsDlgButtonChecked(hwnd, IDC_PROXYTYPETELNET) ? PROXY_TELNET :
-                       PROXY_NONE;
-               }
-               break;
-
-             case IDC_LGFEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_LGFEDIT, cfg.logfilename.path,
-                                  sizeof(cfg.logfilename.path) - 1);
-               break;
-             case IDC_LGFBUTTON:
-               memset(&of, 0, sizeof(of));
-#ifdef OPENFILENAME_SIZE_VERSION_400
-               of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
-#else
-               of.lStructSize = sizeof(of);
-#endif
-               of.hwndOwner = hwnd;
-               of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
-               of.lpstrCustomFilter = NULL;
-               of.nFilterIndex = 1;
-               of.lpstrFile = filename;
-               strcpy(filename, cfg.logfilename.path);
-               of.nMaxFile = sizeof(filename);
-               of.lpstrFileTitle = NULL;
-               of.lpstrInitialDir = NULL;
-               of.lpstrTitle = "Select session log file";
-               of.Flags = 0;
-               if (GetSaveFileName(&of)) {
-                   strcpy(cfg.logfilename.path, filename);
-                   SetDlgItemText(hwnd, IDC_LGFEDIT, cfg.logfilename.path);
-               }
-               break;
-             case IDC_LSTATOFF:
-             case IDC_LSTATASCII:
-             case IDC_LSTATRAW:
-             case IDC_LSTATPACKET:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATOFF))
-                       cfg.logtype = LGTYP_NONE;
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATASCII))
-                       cfg.logtype = LGTYP_ASCII;
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATRAW))
-                       cfg.logtype = LGTYP_DEBUG;
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATPACKET))
-                       cfg.logtype = LGTYP_PACKETS;
-               }
-               break;
-             case IDC_LSTATXASK:
-             case IDC_LSTATXAPN:
-             case IDC_LSTATXOVR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATXASK))
-                       cfg.logxfovr = LGXF_ASK;
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATXAPN))
-                       cfg.logxfovr = LGXF_APN;
-                   if (IsDlgButtonChecked(hwnd, IDC_LSTATXOVR))
-                       cfg.logxfovr = LGXF_OVR;
-               }
-               break;
-             case IDC_TSEDIT:
-             case IDC_R_TSEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, LOWORD(wParam), cfg.termspeed,
-                                  sizeof(cfg.termspeed) - 1);
-               break;
-             case IDC_LOGEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_LOGEDIT, cfg.username,
-                                  sizeof(cfg.username) - 1);
-               break;
-             case IDC_RLLUSEREDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_RLLUSEREDIT,
-                                  cfg.localusername,
-                                  sizeof(cfg.localusername) - 1);
-               break;
-             case IDC_EMBSD:
-             case IDC_EMRFC:
-               cfg.rfc_environ = IsDlgButtonChecked(hwnd, IDC_EMRFC);
-               break;
-             case IDC_TPASSIVE:
-             case IDC_TACTIVE:
-               cfg.passive_telnet =
-                   IsDlgButtonChecked(hwnd, IDC_TPASSIVE);
-               break;
-             case IDC_ENVADD:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   char str[sizeof(cfg.environmt)];
-                   char *p;
-                   GetDlgItemText(hwnd, IDC_VAREDIT, str,
-                                  sizeof(str) - 1);
-                   if (!*str) {
-                       MessageBeep(0);
-                       break;
-                   }
-                   p = str + strlen(str);
-                   *p++ = '\t';
-                   GetDlgItemText(hwnd, IDC_VALEDIT, p,
-                                  sizeof(str) - 1 - (p - str));
-                   if (!*p) {
-                       MessageBeep(0);
-                       break;
-                   }
-                   p = cfg.environmt;
-                   while (*p) {
-                       while (*p)
-                           p++;
-                       p++;
-                   }
-                   if ((p - cfg.environmt) + strlen(str) + 2 <
-                       sizeof(cfg.environmt)) {
-                       strcpy(p, str);
-                       p[strlen(str) + 1] = '\0';
-                       SendDlgItemMessage(hwnd, IDC_ENVLIST, LB_ADDSTRING,
-                                          0, (LPARAM) str);
-                       SetDlgItemText(hwnd, IDC_VAREDIT, "");
-                       SetDlgItemText(hwnd, IDC_VALEDIT, "");
-                   } else {
-                       MessageBox(hwnd, "Environment too big",
-                                  "PuTTY Error", MB_OK | MB_ICONERROR);
-                   }
-               }
-               break;
-             case IDC_ENVREMOVE:
-               if (HIWORD(wParam) != BN_CLICKED &&
-                   HIWORD(wParam) != BN_DOUBLECLICKED) break;
-               i =
-                   SendDlgItemMessage(hwnd, IDC_ENVLIST, LB_GETCURSEL, 0,
-                                      0);
-               if (i == LB_ERR)
-                   MessageBeep(0);
-               else {
-                   char *p, *q;
-
-                   SendDlgItemMessage(hwnd, IDC_ENVLIST, LB_DELETESTRING,
-                                      i, 0);
-                   p = cfg.environmt;
-                   while (i > 0) {
-                       if (!*p)
-                           goto disaster;
-                       while (*p)
-                           p++;
-                       p++;
-                       i--;
-                   }
-                   q = p;
-                   if (!*p)
-                       goto disaster;
-                   while (*p)
-                       p++;
-                   p++;
-                   while (*p) {
-                       while (*p)
-                           *q++ = *p++;
-                       *q++ = *p++;
-                   }
-                   *q = '\0';
-                 disaster:;
-               }
-               break;
-             case IDC_NOPTY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.nopty = IsDlgButtonChecked(hwnd, IDC_NOPTY);
-               break;
-             case IDC_COMPRESS:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.compression =
-                       IsDlgButtonChecked(hwnd, IDC_COMPRESS);
-               break;
-             case IDC_SSH2DES:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.ssh2_des_cbc =
-                       IsDlgButtonChecked(hwnd, IDC_SSH2DES);
-               break;
-             case IDC_AGENTFWD:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.agentfwd =
-                       IsDlgButtonChecked(hwnd, IDC_AGENTFWD);
-               break;
-             case IDC_CHANGEUSER:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.change_username =
-                       IsDlgButtonChecked(hwnd, IDC_CHANGEUSER);
-               break;
-             case IDC_CIPHERLIST:
-             case IDC_CIPHERUP:
-             case IDC_CIPHERDN:
-               handle_prefslist(&cipherlist,
-                                cfg.ssh_cipherlist, CIPHER_MAX,
-                                0, hwnd, wParam, lParam);
-               break;
-             case IDC_SSHPROT1ONLY:
-             case IDC_SSHPROT1:
-             case IDC_SSHPROT2:
-             case IDC_SSHPROT2ONLY:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (IsDlgButtonChecked(hwnd, IDC_SSHPROT1ONLY))
-                       cfg.sshprot = 0;
-                   if (IsDlgButtonChecked(hwnd, IDC_SSHPROT1))
-                       cfg.sshprot = 1;
-                   else if (IsDlgButtonChecked(hwnd, IDC_SSHPROT2))
-                       cfg.sshprot = 2;
-                   else if (IsDlgButtonChecked(hwnd, IDC_SSHPROT2ONLY))
-                       cfg.sshprot = 3;
-               }
-               break;
-             case IDC_AUTHTIS:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.try_tis_auth =
-                       IsDlgButtonChecked(hwnd, IDC_AUTHTIS);
-               break;
-             case IDC_AUTHKI:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.try_ki_auth =
-                       IsDlgButtonChecked(hwnd, IDC_AUTHKI);
-               break;
-             case IDC_PKEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_PKEDIT, cfg.keyfile.path,
-                                  sizeof(cfg.keyfile.path) - 1);
-               break;
-             case IDC_CMDEDIT:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_CMDEDIT, cfg.remote_cmd,
-                                  sizeof(cfg.remote_cmd) - 1);
-               break;
-             case IDC_PKBUTTON:
-               memset(&of, 0, sizeof(of));
-#ifdef OPENFILENAME_SIZE_VERSION_400
-               of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
-#else
-               of.lStructSize = sizeof(of);
-#endif
-               of.hwndOwner = hwnd;
-               of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
-                   "All Files (*.*)\0*\0\0\0";
-               of.lpstrCustomFilter = NULL;
-               of.nFilterIndex = 1;
-               of.lpstrFile = filename;
-               strcpy(filename, cfg.keyfile.path);
-               of.nMaxFile = sizeof(filename);
-               of.lpstrFileTitle = NULL;
-               of.lpstrInitialDir = NULL;
-               of.lpstrTitle = "Select Private Key File";
-               of.Flags = 0;
-               if (GetOpenFileName(&of)) {
-                   strcpy(cfg.keyfile.path, filename);
-                   SetDlgItemText(hwnd, IDC_PKEDIT, cfg.keyfile.path);
-               }
-               break;
-             case IDC_RAWCNP:
-               cfg.rawcnp = IsDlgButtonChecked(hwnd, IDC_RAWCNP);
-               break;
-             case IDC_RTFPASTE:
-               cfg.rtf_paste = IsDlgButtonChecked(hwnd, IDC_RTFPASTE);
-               break;
-             case IDC_MBWINDOWS:
-             case IDC_MBXTERM:
-               cfg.mouse_is_xterm = IsDlgButtonChecked(hwnd, IDC_MBXTERM);
-               break;
-             case IDC_SELTYPELEX:
-             case IDC_SELTYPERECT:
-               cfg.rect_select = IsDlgButtonChecked(hwnd, IDC_SELTYPERECT);
-               break;
-             case IDC_MOUSEOVERRIDE:
-               cfg.mouse_override = IsDlgButtonChecked(hwnd, IDC_MOUSEOVERRIDE);
-               break;
-             case IDC_CCSET:
-               {
-                   BOOL ok;
-                   int i;
-                   int n = GetDlgItemInt(hwnd, IDC_CCEDIT, &ok, FALSE);
-
-                   if (!ok)
-                       MessageBeep(0);
-                   else {
-                       for (i = 0; i < 128; i++)
-                           if (SendDlgItemMessage
-                               (hwnd, IDC_CCLIST, LB_GETSEL, i, 0)) {
-                               char str[100];
-                               cfg.wordness[i] = n;
-                               SendDlgItemMessage(hwnd, IDC_CCLIST,
-                                                  LB_DELETESTRING, i, 0);
-                               sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
-                                       (i >= 0x21 && i != 0x7F) ? i : ' ',
-                                       cfg.wordness[i]);
-                               SendDlgItemMessage(hwnd, IDC_CCLIST,
-                                                  LB_INSERTSTRING, i,
-                                                  (LPARAM) str);
-                           }
-                   }
-               }
-               break;
-             case IDC_BOLDCOLOUR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   int n, i;
-                   cfg.bold_colour =
-                       IsDlgButtonChecked(hwnd, IDC_BOLDCOLOUR);
-               }
-               break;
-             case IDC_PALETTE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                       cfg.try_palette =
-                       IsDlgButtonChecked(hwnd, IDC_PALETTE);
-               break;
-             case IDC_COLOURLIST:
-               if (HIWORD(wParam) == LBN_DBLCLK ||
-                   HIWORD(wParam) == LBN_SELCHANGE) {
-                   int i =
-                       SendDlgItemMessage(hwnd, IDC_COLOURLIST,
-                                          LB_GETCURSEL,
-                                          0, 0);
-                   if (!cfg.bold_colour)
-                       i = (i < 3 ? i * 2 : i == 3 ? 5 : i * 2 - 2);
-                   SetDlgItemInt(hwnd, IDC_RVALUE, cfg.colours[i][0],
-                                 FALSE);
-                   SetDlgItemInt(hwnd, IDC_GVALUE, cfg.colours[i][1],
-                                 FALSE);
-                   SetDlgItemInt(hwnd, IDC_BVALUE, cfg.colours[i][2],
-                                 FALSE);
-               }
-               break;
-             case IDC_CHANGE:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   static CHOOSECOLOR cc;
-                   static DWORD custom[16] = { 0 };    /* zero initialisers */
-                   int i =
-                       SendDlgItemMessage(hwnd, IDC_COLOURLIST,
-                                          LB_GETCURSEL,
-                                          0, 0);
-                   if (!cfg.bold_colour)
-                       i = (i < 3 ? i * 2 : i == 3 ? 5 : i * 2 - 2);
-                   cc.lStructSize = sizeof(cc);
-                   cc.hwndOwner = hwnd;
-                   cc.hInstance = (HWND) hinst;
-                   cc.lpCustColors = custom;
-                   cc.rgbResult =
-                       RGB(cfg.colours[i][0], cfg.colours[i][1],
-                           cfg.colours[i][2]);
-                   cc.Flags = CC_FULLOPEN | CC_RGBINIT;
-                   if (ChooseColor(&cc)) {
-                       cfg.colours[i][0] =
-                           (unsigned char) (cc.rgbResult & 0xFF);
-                       cfg.colours[i][1] =
-                           (unsigned char) (cc.rgbResult >> 8) & 0xFF;
-                       cfg.colours[i][2] =
-                           (unsigned char) (cc.rgbResult >> 16) & 0xFF;
-                       SetDlgItemInt(hwnd, IDC_RVALUE, cfg.colours[i][0],
-                                     FALSE);
-                       SetDlgItemInt(hwnd, IDC_GVALUE, cfg.colours[i][1],
-                                     FALSE);
-                       SetDlgItemInt(hwnd, IDC_BVALUE, cfg.colours[i][2],
-                                     FALSE);
-                   }
-               }
-               break;
-             case IDC_CODEPAGE:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_CODEPAGE,
-                                                  CB_GETCURSEL, 0, 0);
-                   SendDlgItemMessage(hwnd, IDC_CODEPAGE, CB_GETLBTEXT,
-                                      index, (LPARAM)cfg.line_codepage);
-               } else if (HIWORD(wParam) == CBN_EDITCHANGE) {
-                   GetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage,
-                                  sizeof(cfg.line_codepage) - 1);
-               } else if (HIWORD(wParam) == CBN_KILLFOCUS) {
-                   strcpy(cfg.line_codepage,
-                          cp_name(decode_codepage(cfg.line_codepage)));
-                   SetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage);
-               }
-               break;
-             case IDC_PRINTER:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_PRINTER,
-                                                  CB_GETCURSEL, 0, 0);
-                   SendDlgItemMessage(hwnd, IDC_PRINTER, CB_GETLBTEXT,
-                                      index, (LPARAM)cfg.printer);
-               } else if (HIWORD(wParam) == CBN_EDITCHANGE) {
-                   GetDlgItemText(hwnd, IDC_PRINTER, cfg.printer,
-                                  sizeof(cfg.printer) - 1);
-               }
-               if (!strcmp(cfg.printer, PRINTER_DISABLED_STRING))
-                   *cfg.printer = '\0';
-               break;
-             case IDC_CAPSLOCKCYR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.xlat_capslockcyr =
-                       IsDlgButtonChecked (hwnd, IDC_CAPSLOCKCYR);
-               }
-               break;
-             case IDC_VTXWINDOWS:
-             case IDC_VTOEMANSI:
-             case IDC_VTOEMONLY:
-             case IDC_VTPOORMAN:
-             case IDC_VTUNICODE:
-               cfg.vtmode =
-                   (IsDlgButtonChecked(hwnd, IDC_VTXWINDOWS) ? VT_XWINDOWS
-                    : IsDlgButtonChecked(hwnd,
-                                         IDC_VTOEMANSI) ? VT_OEMANSI :
-                    IsDlgButtonChecked(hwnd,
-                                       IDC_VTOEMONLY) ? VT_OEMONLY :
-                    IsDlgButtonChecked(hwnd,
-                                       IDC_VTUNICODE) ? VT_UNICODE :
-                    VT_POORMAN);
-               break;
-             case IDC_X11_FORWARD:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                   cfg.x11_forward =
-                   IsDlgButtonChecked(hwnd, IDC_X11_FORWARD);
-               break;
-             case IDC_LPORT_ALL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                   cfg.lport_acceptall =
-                   IsDlgButtonChecked(hwnd, IDC_LPORT_ALL);
-               break;
-             case IDC_RPORT_ALL:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED)
-                   cfg.rport_acceptall =
-                   IsDlgButtonChecked(hwnd, IDC_RPORT_ALL);
-               break;
-             case IDC_X11_DISPLAY:
-               if (HIWORD(wParam) == EN_CHANGE)
-                   GetDlgItemText(hwnd, IDC_X11_DISPLAY, cfg.x11_display,
-                                  sizeof(cfg.x11_display) - 1);
-               break;
-             case IDC_X11MIT:
-             case IDC_X11XDM:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   if (IsDlgButtonChecked(hwnd, IDC_X11MIT))
-                       cfg.x11_auth = X11_MIT;
-                   else if (IsDlgButtonChecked(hwnd, IDC_X11XDM))
-                       cfg.x11_auth = X11_XDM;
-               }
-               break;
-             case IDC_PFWDADD:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   char str[sizeof(cfg.portfwd)];
-                   char *p;
-                   if (IsDlgButtonChecked(hwnd, IDC_PFWDLOCAL))
-                       str[0] = 'L';
-                   else
-                       str[0] = 'R';
-                   GetDlgItemText(hwnd, IDC_SPORTEDIT, str+1,
-                                  sizeof(str) - 2);
-                   if (!str[1]) {
-                       MessageBox(hwnd,
-                                  "You need to specify a source port number",
-                                  "PuTTY Error", MB_OK | MB_ICONERROR);
-                       break;
-                   }
-                   p = str + strlen(str);
-                   *p++ = '\t';
-                   GetDlgItemText(hwnd, IDC_DPORTEDIT, p,
-                                  sizeof(str) - 1 - (p - str));
-                   if (!*p || !strchr(p, ':')) {
-                       MessageBox(hwnd,
-                                  "You need to specify a destination address\n"
-                                  "in the form \"host.name:port\"",
-                                  "PuTTY Error", MB_OK | MB_ICONERROR);
-                       break;
-                   }
-                   p = cfg.portfwd;
-                   while (*p) {
-                       while (*p)
-                           p++;
-                       p++;
-                   }
-                   if ((p - cfg.portfwd) + strlen(str) + 2 <
-                       sizeof(cfg.portfwd)) {
-                       strcpy(p, str);
-                       p[strlen(str) + 1] = '\0';
-                       SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_ADDSTRING,
-                                          0, (LPARAM) str);
-                       SetDlgItemText(hwnd, IDC_SPORTEDIT, "");
-                       SetDlgItemText(hwnd, IDC_DPORTEDIT, "");
-                   } else {
-                       MessageBox(hwnd, "Too many forwardings",
-                                  "PuTTY Error", MB_OK | MB_ICONERROR);
-                   }
-               }
-               break;
-             case IDC_PFWDREMOVE:
-               if (HIWORD(wParam) != BN_CLICKED &&
-                   HIWORD(wParam) != BN_DOUBLECLICKED) break;
-               i = SendDlgItemMessage(hwnd, IDC_PFWDLIST,
-                                      LB_GETCURSEL, 0, 0);
-               if (i == LB_ERR)
-                   MessageBeep(0);
-               else {
-                   char *p, *q;
-
-                   SendDlgItemMessage(hwnd, IDC_PFWDLIST, LB_DELETESTRING,
-                                      i, 0);
-                   p = cfg.portfwd;
-                   while (i > 0) {
-                       if (!*p)
-                           goto disaster2;
-                       while (*p)
-                           p++;
-                       p++;
-                       i--;
-                   }
-                   q = p;
-                   if (!*p)
-                       goto disaster2;
-                   while (*p)
-                       p++;
-                   p++;
-                   while (*p) {
-                       while (*p)
-                           *q++ = *p++;
-                       *q++ = *p++;
-                   }
-                   *q = '\0';
-                 disaster2:;
-               }
-               break;
-             case IDC_BUGD_IGNORE1:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_IGNORE1,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_ignore1 = (index == 0 ? AUTO :
-                                         index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_PLAINPW1:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_PLAINPW1,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_plainpw1 = (index == 0 ? AUTO :
-                                          index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_RSA1:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_RSA1,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_rsa1 = (index == 0 ? AUTO :
-                                      index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_HMAC2:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_HMAC2,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_hmac2 = (index == 0 ? AUTO :
-                                       index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_DERIVEKEY2:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_DERIVEKEY2,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_derivekey2 = (index == 0 ? AUTO :
-                                            index == 1 ? FORCE_OFF:FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_RSAPAD2:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_RSAPAD2,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_rsapad2 = (index == 0 ? AUTO :
-                                         index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_DHGEX2:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_DHGEX2,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_dhgex2 = (index == 0 ? AUTO :
-                                        index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-             case IDC_BUGD_PKSESSID2:
-               if (HIWORD(wParam) == CBN_SELCHANGE) {
-                   int index = SendDlgItemMessage(hwnd, IDC_BUGD_PKSESSID2,
-                                                  CB_GETCURSEL, 0, 0);
-                   cfg.sshbug_pksessid2 = (index == 0 ? AUTO :
-                                            index == 1 ? FORCE_OFF : FORCE_ON);
-               }
-               break;
-           }
-       return 0;
+       if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
+           ret = winctrl_handle_command(&dp, msg, wParam, lParam);
+           if (dp.ended && GetCapture() != hwnd)
+               EndDialog(hwnd, dp.endresult ? 1 : 0);
+       } else
+           ret = 0;
+       return ret;
       case WM_HELP:
         if (help_path) {
       case WM_HELP:
         if (help_path) {
-            int id = ((LPHELPINFO)lParam)->iCtrlId;
-            char *cmd = help_context_cmd(id);
-            if (cmd) {
-                WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
+           if (winctrl_context_help(&dp, hwnd,
+                                    ((LPHELPINFO)lParam)->iCtrlId))
                 requested_help = TRUE;
                 requested_help = TRUE;
-            } else {
+           else
                 MessageBeep(0);
                 MessageBeep(0);
-            }
         }
         break;
       case WM_CLOSE:
         }
         break;
       case WM_CLOSE:
@@ -3728,47 +536,26 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
            force_normal(hwnd);
        return 0;
 
            force_normal(hwnd);
        return 0;
 
-      default:
-       /*
-        * Handle application-defined messages eg. DragListBox
-        */
-       /* First find out what the number is (once). */
-       if (draglistmsg == WM_NULL)
-           draglistmsg = RegisterWindowMessage (DRAGLISTMSGSTRING);
-
-       if (msg == draglistmsg) {
-           /* Only process once dialog is fully formed. */
-           if (GetWindowLong(hwnd, GWL_USERDATA) == 1) switch (LOWORD(wParam)) {
-             case IDC_CIPHERLIST:
-               return handle_prefslist(&cipherlist,
-                                       cfg.ssh_cipherlist, CIPHER_MAX,
-                                       1, hwnd, wParam, lParam);
-           }
-       }
-       return 0;
-
     }
     return 0;
 }
 
     }
     return 0;
 }
 
-static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
-                               WPARAM wParam, LPARAM lParam)
+void modal_about_box(HWND hwnd)
 {
 {
-    if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) {
-    }
-    if (msg == WM_COMMAND && LOWORD(wParam) == IDCX_ABOUT) {
-       EnableWindow(hwnd, 0);
-       DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
-       EnableWindow(hwnd, 1);
-       SetActiveWindow(hwnd);
-    }
-    return GenericMainDlgProc(hwnd, msg, wParam, lParam, 0);
+    EnableWindow(hwnd, 0);
+    DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
+    EnableWindow(hwnd, 1);
+    SetActiveWindow(hwnd);
 }
 
 }
 
-static int CALLBACK ReconfDlgProc(HWND hwnd, UINT msg,
-                                 WPARAM wParam, LPARAM lParam)
+void show_help(HWND hwnd)
 {
 {
-    return GenericMainDlgProc(hwnd, msg, wParam, lParam, 1);
+    if (help_path) {
+       WinHelp(hwnd, help_path,
+               help_has_contents ? HELP_FINDER : HELP_CONTENTS,
+               0);
+       requested_help = TRUE;
+    }
 }
 
 void defuse_showwindow(void)
 }
 
 void defuse_showwindow(void)
@@ -3792,12 +579,31 @@ int do_config(void)
 {
     int ret;
 
 {
     int ret;
 
+    ctrlbox = ctrl_new_box();
+    setup_config_box(ctrlbox, &sesslist, FALSE, 0);
+    win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
+    winctrl_init(&ctrls_base);
+    winctrl_init(&ctrls_panel);
+    dp.controltrees[0] = &ctrls_base;
+    dp.controltrees[1] = &ctrls_panel;
+    dp.nctrltrees = 2;
+    dp.errtitle = "PuTTY Error";
+    dp.data = &cfg;
+    dp.ended = FALSE;
+    dp.lastfocused = NULL;
+    memset(dp.shortcuts, 0, sizeof(dp.shortcuts));
+    dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
+
     get_sesslist(&sesslist, TRUE);
     get_sesslist(&sesslist, TRUE);
-    savedsession[0] = '\0';
     ret =
     ret =
-       DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
+       DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
+                 GenericMainDlgProc);
     get_sesslist(&sesslist, FALSE);
 
     get_sesslist(&sesslist, FALSE);
 
+    ctrl_free_box(ctrlbox);
+    winctrl_cleanup(&ctrls_base);
+    winctrl_cleanup(&ctrls_panel);
+
     return ret;
 }
 
     return ret;
 }
 
@@ -3807,8 +613,30 @@ int do_reconfig(HWND hwnd)
     int ret;
 
     backup_cfg = cfg;                 /* structure copy */
     int ret;
 
     backup_cfg = cfg;                 /* structure copy */
+
+    ctrlbox = ctrl_new_box();
+    setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
+    win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
+    winctrl_init(&ctrls_base);
+    winctrl_init(&ctrls_panel);
+    dp.controltrees[0] = &ctrls_base;
+    dp.controltrees[1] = &ctrls_panel;
+    dp.nctrltrees = 2;
+    dp.errtitle = "PuTTY Error";
+    dp.data = &cfg;
+    dp.ended = FALSE;
+    dp.lastfocused = NULL;
+    memset(dp.shortcuts, 0, sizeof(dp.shortcuts));
+    dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
+
     ret =
     ret =
-       DialogBox(hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc);
+       DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
+                 GenericMainDlgProc);
+
+    ctrl_free_box(ctrlbox);
+    winctrl_cleanup(&ctrls_base);
+    winctrl_cleanup(&ctrls_panel);
+
     if (!ret)
        cfg = backup_cfg;              /* structure copy */
 
     if (!ret)
        cfg = backup_cfg;              /* structure copy */
 
diff --git a/winhelp.h b/winhelp.h
new file mode 100644 (file)
index 0000000..04e1214
--- /dev/null
+++ b/winhelp.h
@@ -0,0 +1,109 @@
+/*
+ * winhelp.h - define Windows Help context names for the controls
+ * in the PuTTY config box.
+ */
+
+#define HELPCTX(x) P(WINHELP_CTX_ ## x)
+
+#define WINHELP_CTX_no_help NULL
+
+#define WINHELP_CTX_session_hostname "session.hostname"
+#define WINHELP_CTX_session_saved "session.saved"
+#define WINHELP_CTX_session_coe "session.coe"
+#define WINHELP_CTX_logging_main "logging.main"
+#define WINHELP_CTX_logging_filename "logging.filename"
+#define WINHELP_CTX_logging_exists "logging.exists"
+#define WINHELP_CTX_keyboard_backspace "keyboard.backspace"
+#define WINHELP_CTX_keyboard_homeend "keyboard.homeend"
+#define WINHELP_CTX_keyboard_funkeys "keyboard.funkeys"
+#define WINHELP_CTX_keyboard_appkeypad "keyboard.appkeypad"
+#define WINHELP_CTX_keyboard_appcursor "keyboard.appcursor"
+#define WINHELP_CTX_keyboard_nethack "keyboard.nethack"
+#define WINHELP_CTX_keyboard_compose "keyboard.compose"
+#define WINHELP_CTX_keyboard_ctrlalt "keyboard.ctrlalt"
+#define WINHELP_CTX_features_application "features.application"
+#define WINHELP_CTX_features_mouse "features.mouse"
+#define WINHELP_CTX_features_resize "features.resize"
+#define WINHELP_CTX_features_altscreen "features.altscreen"
+#define WINHELP_CTX_features_retitle "features.retitle"
+#define WINHELP_CTX_features_dbackspace "features.dbackspace"
+#define WINHELP_CTX_features_charset "features.charset"
+#define WINHELP_CTX_terminal_autowrap "terminal.autowrap"
+#define WINHELP_CTX_terminal_decom "terminal.decom"
+#define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr"
+#define WINHELP_CTX_terminal_bce "terminal.bce"
+#define WINHELP_CTX_terminal_blink "terminal.blink"
+#define WINHELP_CTX_terminal_answerback "terminal.answerback"
+#define WINHELP_CTX_terminal_localecho "terminal.localecho"
+#define WINHELP_CTX_terminal_localedit "terminal.localedit"
+#define WINHELP_CTX_terminal_printing "terminal.printing"
+#define WINHELP_CTX_bell_style "bell.style"
+#define WINHELP_CTX_bell_taskbar "bell.taskbar"
+#define WINHELP_CTX_bell_overload "bell.overload"
+#define WINHELP_CTX_window_size "window.size"
+#define WINHELP_CTX_window_resize "window.resize"
+#define WINHELP_CTX_window_scrollback "window.scrollback"
+#define WINHELP_CTX_behaviour_closewarn "behaviour.closewarn"
+#define WINHELP_CTX_behaviour_altf4 "behaviour.altf4"
+#define WINHELP_CTX_behaviour_altspace "behaviour.altspace"
+#define WINHELP_CTX_behaviour_altonly "behaviour.altonly"
+#define WINHELP_CTX_behaviour_alwaysontop "behaviour.alwaysontop"
+#define WINHELP_CTX_behaviour_altenter "behaviour.altenter"
+#define WINHELP_CTX_appearance_cursor "appearance.cursor"
+#define WINHELP_CTX_appearance_font "appearance.font"
+#define WINHELP_CTX_appearance_title "appearance.title"
+#define WINHELP_CTX_appearance_hidemouse "appearance.hidemouse"
+#define WINHELP_CTX_appearance_border "appearance.border"
+#define WINHELP_CTX_connection_termtype "connection.termtype"
+#define WINHELP_CTX_connection_username "connection.username"
+#define WINHELP_CTX_connection_keepalive "connection.keepalive"
+#define WINHELP_CTX_connection_nodelay "connection.nodelay"
+#define WINHELP_CTX_proxy_type "proxy.type"
+#define WINHELP_CTX_proxy_main "proxy.main"
+#define WINHELP_CTX_proxy_exclude "proxy.exclude"
+#define WINHELP_CTX_proxy_dns "proxy.dns"
+#define WINHELP_CTX_proxy_auth "proxy.auth"
+#define WINHELP_CTX_proxy_command "proxy.command"
+#define WINHELP_CTX_proxy_socksver "proxy.socksver"
+#define WINHELP_CTX_telnet_termspeed "telnet.termspeed"
+#define WINHELP_CTX_telnet_environ "telnet.environ"
+#define WINHELP_CTX_telnet_oldenviron "telnet.oldenviron"
+#define WINHELP_CTX_telnet_passive "telnet.passive"
+#define WINHELP_CTX_telnet_specialkeys "telnet.specialkeys"
+#define WINHELP_CTX_telnet_newline "telnet.newline"
+#define WINHELP_CTX_rlogin_termspeed "rlogin.termspeed"
+#define WINHELP_CTX_rlogin_localuser "rlogin.localuser"
+#define WINHELP_CTX_ssh_nopty "ssh.nopty"
+#define WINHELP_CTX_ssh_ciphers "ssh.ciphers"
+#define WINHELP_CTX_ssh_protocol "ssh.protocol"
+#define WINHELP_CTX_ssh_command "ssh.command"
+#define WINHELP_CTX_ssh_compress "ssh.compress"
+#define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey"
+#define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd"
+#define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser"
+#define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis"
+#define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki"
+#define WINHELP_CTX_selection_buttons "selection.buttons"
+#define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag"
+#define WINHELP_CTX_selection_rect "selection.rect"
+#define WINHELP_CTX_selection_charclasses "selection.charclasses"
+#define WINHELP_CTX_selection_linedraw "selection.linedraw"
+#define WINHELP_CTX_selection_rtf "selection.rtf"
+#define WINHELP_CTX_colours_bold "colours.bold"
+#define WINHELP_CTX_colours_logpal "colours.logpal"
+#define WINHELP_CTX_colours_config "colours.config"
+#define WINHELP_CTX_translation_codepage "translation.codepage"
+#define WINHELP_CTX_translation_cyrillic "translation.cyrillic"
+#define WINHELP_CTX_translation_linedraw "translation.linedraw"
+#define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11"
+#define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth"
+#define WINHELP_CTX_ssh_tunnels_portfwd "ssh.tunnels.portfwd"
+#define WINHELP_CTX_ssh_tunnels_portfwd_localhost "ssh.tunnels.portfwd.localhost"
+#define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1"
+#define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1"
+#define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1"
+#define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2"
+#define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2"
+#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2"
+#define WINHELP_CTX_ssh_bugs_dhgex2 "ssh.bugs.dhgex2"
+#define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2"
index 1cbe5b782ffc3fb2804241c766c782ad26e40e41..21d7aa89ff411770c78e4f1e9fbc3b1f1d273784 100644 (file)
@@ -7,6 +7,10 @@
 
 #include <stdio.h>                    /* for FILENAME_MAX */
 
 
 #include <stdio.h>                    /* for FILENAME_MAX */
 
+#include "tree234.h"
+
+#include "winhelp.h"
+
 struct Filename {
     char path[FILENAME_MAX];
 };
 struct Filename {
     char path[FILENAME_MAX];
 };
@@ -111,6 +115,16 @@ GLOBAL void *logctx;
  */
 #define sk_getxdmdata(socket, ip, port) (0)
 
  */
 #define sk_getxdmdata(socket, ip, port) (0)
 
+/*
+ * File-selector filter strings used in the config box. On Windows,
+ * these strings are of exactly the type needed to go in
+ * `lpstrFilter' in an OPENFILENAME structure.
+ */
+#define FILTER_KEY_FILES ("PuTTY Private Key Files (*.ppk)\0*.ppk\0" \
+                             "All Files (*.*)\0*\0\0\0")
+#define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \
+                              "All Files (*.*)\0*\0\0\0")
+
 /*
  * Exports from winctrls.c.
  */
 /*
  * Exports from winctrls.c.
  */
@@ -141,6 +155,24 @@ struct prefslist {
     int dragging;
 };
 
     int dragging;
 };
 
+/*
+ * This structure is passed to event handler functions as the `dlg'
+ * parameter, and hence is passed back to winctrls access functions.
+ */
+struct dlgparam {
+    HWND hwnd;                        /* the hwnd of the dialog box */
+    struct winctrls *controltrees[8];  /* can have several of these */
+    int nctrltrees;
+    char *errtitle;                   /* title of error sub-messageboxes */
+    void *data;                               /* data to pass in refresh events */
+    union control *focused, *lastfocused; /* which ctrl has focus now/before */
+    int coloursel_wanted;             /* has an event handler asked for
+                                       * a colour selector? */
+    char shortcuts[128];              /* track which shortcuts in use */
+    struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */
+    int ended, endresult;             /* has the dialog been ended? */
+};
+
 /*
  * Exports from winctrls.c.
  */
 /*
  * Exports from winctrls.c.
  */
@@ -151,7 +183,7 @@ HWND doctl(struct ctlpos *cp, RECT r,
 void bartitle(struct ctlpos *cp, char *name, int id);
 void beginbox(struct ctlpos *cp, char *name, int idbox);
 void endbox(struct ctlpos *cp);
 void bartitle(struct ctlpos *cp, char *name, int id);
 void beginbox(struct ctlpos *cp, char *name, int idbox);
 void endbox(struct ctlpos *cp);
-void multiedit(struct ctlpos *cp, ...);
+void multiedit(struct ctlpos *cp, int password, ...);
 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...);
 void bareradioline(struct ctlpos *cp, int nacross, ...);
 void radiobig(struct ctlpos *cp, char *text, int id, ...);
 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...);
 void bareradioline(struct ctlpos *cp, int nacross, ...);
 void radiobig(struct ctlpos *cp, char *text, int id, ...);
@@ -183,8 +215,8 @@ void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
               char *btext, int bid, int eid, char *s2text, int s2id);
 void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
                char *btext, int bid, ...);
               char *btext, int bid, int eid, char *s2text, int s2id);
 void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
                char *btext, int bid, ...);
-void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
-              int sid, int listid, int upbid, int dnbid);
+void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,
+              char *stext, int sid, int listid, int upbid, int dnbid);
 int handle_prefslist(struct prefslist *hdl,
                     int *array, int maxmemb,
                     int is_dlmsg, HWND hwnd,
 int handle_prefslist(struct prefslist *hdl,
                     int *array, int maxmemb,
                     int is_dlmsg, HWND hwnd,
@@ -196,6 +228,56 @@ void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
               char *btext, int bid,
               char *r1text, int r1id, char *r2text, int r2id);
 
               char *btext, int bid,
               char *r1text, int r1id, char *r2text, int r2id);
 
+#define MAX_SHORTCUTS_PER_CTRL 16
+
+/*
+ * This structure is what's stored for each `union control' in the
+ * portable-dialog interface.
+ */
+struct winctrl {
+    union control *ctrl;
+    /*
+     * The control may have several components at the Windows
+     * level, with different dialog IDs. To avoid needing N
+     * separate platformsidectrl structures (which could be stored
+     * separately in a tree234 so that lookup by ID worked), we
+     * impose the constraint that those IDs must be in a contiguous
+     * block.
+     */
+    int base_id;
+    int num_ids;
+    /*
+     * Remember what keyboard shortcuts were used by this control,
+     * so that when we remove it again we can take them out of the
+     * list in the dlgparam.
+     */
+    char shortcuts[MAX_SHORTCUTS_PER_CTRL];
+    /*
+     * Some controls need a piece of allocated memory in which to
+     * store temporary data about the control.
+     */
+    void *data;
+};
+/*
+ * And this structure holds a set of the above, in two separate
+ * tree234s so that it can find an item by `union control' or by
+ * dialog ID.
+ */
+struct winctrls {
+    tree234 *byctrl, *byid;
+};
+void winctrl_init(struct winctrls *);
+void winctrl_cleanup(struct winctrls *);
+void winctrl_add(struct winctrls *, struct winctrl *);
+void winctrl_remove(struct winctrls *, struct winctrl *);
+struct winctrl *winctrl_findbyctrl(struct winctrls *, union control *);
+struct winctrl *winctrl_findbyid(struct winctrls *, int);
+struct winctrl *winctrl_findbyindex(struct winctrls *, int);
+void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
+                   struct ctlpos *cp, struct controlset *s, int *id);
+int winctrl_handle_command(struct dlgparam *dp, UINT msg,
+                          WPARAM wParam, LPARAM lParam);
+
 /*
  * Exports from windlg.c.
  */
 /*
  * Exports from windlg.c.
  */