diff --git a/Recipe b/Recipe
index 07bd56e025e3368567ce7279184cc7504ba0bc00..08399e4d1992e17c519d53f401a482c0144914d5 100644
--- 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
-         + logging printing winutils
+         + logging printing winutils dialog config wincfg tree234
 
 # 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
-         + 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
diff --git a/config.c b/config.c
new file mode 100644
index 0000000000000000000000000000000000000000..ebcd6077ea964b816ac5acfa14a9fa4812614c17
--- /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
index 0000000000000000000000000000000000000000..d9d6aafd5a74034e8bfe0c3276e8fd78eef70e73
--- /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
index 0000000000000000000000000000000000000000..6b9f5e38bd5390719c821d502667757ce60f0c92
--- /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);
diff --git a/doc/config.but b/doc/config.but
index f96d25765bd6fb4da99499b203c22ae17d90ed61..4bf4c251a9ce7491d8b2a448f657667c8dbd9ae0 100644
--- a/doc/config.but
+++ b/doc/config.but
@@ -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
 
@@ -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.
 
-\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
@@ -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}.
 
-\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}
@@ -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.)
 
-\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}
@@ -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.
 
+\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}
diff --git a/win_res.rc b/win_res.rc
index 28e36c7daa843c24fe052773d63f3c2c5cdce691..4c424711ad8fc070a13839085e3d7693712477c5 100644
--- a/win_res.rc
+++ b/win_res.rc
@@ -48,20 +48,6 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 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 */
diff --git a/wincfg.c b/wincfg.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b5bf9a4985b649c76de16e52cdb0f0c1eab6b4c
--- /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)));
+}
diff --git a/winctrls.c b/winctrls.c
index 53228236e58dcfa622074b7db51afc13dffec509..4d055f4618de7d46bccfb63f8da6ff490e13d2b6 100644
--- a/winctrls.c
+++ b/winctrls.c
@@ -3,10 +3,23 @@
  * 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 <assert.h>
 
 #include "winstuff.h"
+#include "misc.h"
+#include "dialog.h"
 #include "puttymem.h"
 
 #include "putty.h"
@@ -17,9 +30,12 @@
 #define GAPYBOX 4
 #define DLGWIDTH 168
 #define STATICHEIGHT 8
+#define TITLEHEIGHT 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
@@ -55,10 +71,30 @@ HWND doctl(struct ctlpos *cp, RECT 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;
 }
 
@@ -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.
  */
-void multiedit(struct ctlpos *cp, ...)
+void multiedit(struct ctlpos *cp, int password, ...)
 {
     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;
@@ -145,7 +181,8 @@ void multiedit(struct ctlpos *cp, ...)
 	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);
@@ -174,29 +211,37 @@ void combobox(struct ctlpos *cp, char *text, int staticid, int listid)
     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;
-    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;
-    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) {
-	    cp->ypos += r.bottom + GAPBETWEEN;
+	    cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN);
 	    i = 0;
 	}
-	bid = va_arg(ap, int);
-	nextbtext = va_arg(ap, char *);
 	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
@@ -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",
-	      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++;
-	btext = nextbtext;
     }
     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, ...)
 {
-    RECT r;
     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);
-    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);
+    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;
+    struct radio *buttons;
+    int i, nbuttons;
 
     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);
+    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, ...)
 {
-    RECT r;
     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);
-    group = WS_GROUP;
+    nbuttons = 0;
     while (1) {
-	char *btext;
+	char *btext = va_arg(ap, char *);
 	int bid;
-	btext = va_arg(ap, char *);
 	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);
-    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",
-	  BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
+	  BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
 	  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.
  */
@@ -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;
-    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",
-	  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;
 }
 
+/*
+ * 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.
  */
@@ -387,7 +577,7 @@ void static2btn(struct ctlpos *cp, char *stext, int sid,
     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;
@@ -395,7 +585,7 @@ void static2btn(struct ctlpos *cp, char *stext, int sid,
     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;
@@ -481,6 +671,64 @@ void staticddl(struct ctlpos *cp, char *stext,
     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.
  */
@@ -506,6 +754,35 @@ void bigeditctrl(struct ctlpos *cp, char *stext,
 	  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.
  */
@@ -584,320 +861,48 @@ void editbutton(struct ctlpos *cp, char *stext, int sid,
     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;
 }
 
 /*
- * 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;
-    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++) {
@@ -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;
-            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 |
-			    WS_VSCROLL | LBS_HASSTRINGS,
+			    WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS,
                             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;
-            r.top = cp->ypos; r.bottom = PUSHBTNHEIGHT;
+            r.top = cp->ypos + buttonpos; 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, "&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",
-                  WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+                  BS_NOTIFY | WS_CHILD | WS_VISIBLE |
+		  WS_TABSTOP | BS_PUSHBUTTON,
                   0, "&Down", dnbid);
 
             break;
@@ -1016,6 +1023,10 @@ int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
 
 /*
  * 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,
@@ -1023,7 +1034,7 @@ int handle_prefslist(struct prefslist *hdl,
 		     WPARAM wParam, LPARAM lParam)
 {
     int i;
-    int ret;
+    int ret = 0;
 
     if (is_dlmsg) {
 
@@ -1040,13 +1051,13 @@ int handle_prefslist(struct prefslist *hdl,
 		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;
-                ret = 1; break;
+                ret |= 1; break;
               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);
-                ret = 1; break;
+                ret |= 1; break;
               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);
 		    }
+		    ret |= 2;
 		}
-                ret = 1; break;
+                ret |= 1; break;
             }
         }
 
     } else {
 
-        ret = 0;
         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);
+		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;
-
 }
 
 /*
@@ -1135,82 +1148,1267 @@ void progressbar(struct ctlpos *cp, int 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;
 }
diff --git a/windlg.c b/windlg.c
index 9299a0060258c0e30fe9976f9d765ae41e37fe83..ca1890c5162113532aeea2fa773a787cabb44883 100644
--- a/windlg.c
+++ b/windlg.c
@@ -3,6 +3,8 @@
 #include <commdlg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
 #include <ctype.h>
 #include <time.h>
 
@@ -11,6 +13,7 @@
 #include "winstuff.h"
 #include "win_res.h"
 #include "storage.h"
+#include "dialog.h"
 
 #ifdef MSVC4
 #define TVINSERTSTRUCT  TV_INSERTSTRUCT
@@ -18,15 +21,27 @@
 #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 int readytogo;
-static int sesslist_has_focus;
 static int requested_help;
 
-static struct prefslist cipherlist;
-
 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;
 }
 
-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)
 {
@@ -231,1222 +221,21 @@ static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
     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,
-				 int level, char *text)
+				 int level, char *text, char *path)
 {
     TVINSERTSTRUCT ins;
     int i;
@@ -1458,8 +247,10 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
 #else
 #define INSITEM item
 #endif
-    ins.INSITEM.mask = TVIF_TEXT;
+    ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
     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],
@@ -1473,675 +264,50 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
 /*
  * 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);
-	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.
  */
-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;
-    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:
-	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,
@@ -2213,52 +379,73 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
 	/*
 	 * 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.
 	 */
 	{
-	    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);
-	sesslist_has_focus = 0;
 	return 0;
       case WM_LBUTTONUP:
 	/*
@@ -2266,8 +453,8 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
 	 * 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 &&
@@ -2275,7 +462,6 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
 	    HTREEITEM i =
 		TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
 	    TVITEM item;
-	    int j;
 	    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.mask = TVIF_TEXT;
+	    item.mask = TVIF_TEXT | TVIF_PARAM;
 	    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);
@@ -2341,1377 +501,25 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
 	}
 	break;
       case WM_COMMAND:
+      case WM_DRAWITEM:
+      default:			       /* also handle drag list msg here */
 	/*
 	 * 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) {
-            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;
-            } else {
+	    else
                 MessageBeep(0);
-            }
         }
         break;
       case WM_CLOSE:
@@ -3728,47 +536,26 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
 	    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;
 }
 
-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)
@@ -3792,12 +579,31 @@ int do_config(void)
 {
     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);
-    savedsession[0] = '\0';
     ret =
-	DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
+	DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
+		  GenericMainDlgProc);
     get_sesslist(&sesslist, FALSE);
 
+    ctrl_free_box(ctrlbox);
+    winctrl_cleanup(&ctrls_base);
+    winctrl_cleanup(&ctrls_panel);
+
     return ret;
 }
 
@@ -3807,8 +613,30 @@ int do_reconfig(HWND hwnd)
     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 =
-	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 */
 
diff --git a/winhelp.h b/winhelp.h
new file mode 100644
index 0000000000000000000000000000000000000000..04e1214b8b6af859a58ab2011fc15cd0901d71f5
--- /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"
diff --git a/winstuff.h b/winstuff.h
index 1cbe5b782ffc3fb2804241c766c782ad26e40e41..21d7aa89ff411770c78e4f1e9fbc3b1f1d273784 100644
--- a/winstuff.h
+++ b/winstuff.h
@@ -7,6 +7,10 @@
 
 #include <stdio.h>		       /* for FILENAME_MAX */
 
+#include "tree234.h"
+
+#include "winhelp.h"
+
 struct Filename {
     char path[FILENAME_MAX];
 };
@@ -111,6 +115,16 @@ GLOBAL void *logctx;
  */
 #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.
  */
@@ -141,6 +155,24 @@ struct prefslist {
     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.
  */
@@ -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 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, ...);
@@ -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, ...);
-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,
@@ -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);
 
+#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.
  */