From fe8abbf463f798f37ee4f43b3b85583a80fbddf4 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 5 Mar 2003 22:07:40 +0000 Subject: [PATCH] The long-awaited config box revamp! I've taken the whole config box to pieces, and put it back together in a new table-driven form. config.c sets up a data structure describing most of the config box; wincfg.c adds in the Windows-specific options (so that config.c can also form the basis for Mac and Unix config boxes). Then winctrls.c contains a shiny new layout engine which consumes that data structure, and windlg.c passes all WM_COMMAND and similar messages to a driver alongside that layout engine. In the process I've sorted out nicer-looking panel titles and finally fixed the list-boxes-are- never-the-right-size bug (turned out to be Windows's fault, of course). I _believe_ it should do everything the old config box did, including context help. Now everyone has to test it thoroughly... git-svn-id: svn://svn.tartarus.org/sgt/putty@2908 cda61777-01e9-0310-a592-d414129be87e --- Recipe | 4 +- config.c | 1526 ++++++++++++++++++++++++ dialog.c | 587 ++++++++++ dialog.h | 665 +++++++++++ doc/config.but | 70 +- win_res.rc | 14 - wincfg.c | 275 +++++ winctrls.c | 2086 ++++++++++++++++++++++++++------- windlg.c | 3580 ++++---------------------------------------------------- winhelp.h | 109 ++ winstuff.h | 88 +- 11 files changed, 5130 insertions(+), 3874 deletions(-) create mode 100644 config.c create mode 100644 dialog.c create mode 100644 dialog.h create mode 100644 wincfg.c create mode 100644 winhelp.h diff --git a/Recipe b/Recipe index 07bd56e0..08399e4d 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 00000000..ebcd6077 --- /dev/null +++ b/config.c @@ -0,0 +1,1526 @@ +/* + * config.c - the platform-independent parts of the PuTTY + * configuration box. + */ + +#include +#include + +#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 00000000..d9d6aafd --- /dev/null +++ b/dialog.c @@ -0,0 +1,587 @@ +/* + * dialog.c - a reasonably platform-independent mechanism for + * describing dialog boxes. + */ + +#include +#include +#include + +#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 00000000..6b9f5e38 --- /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 f96d2576..4bf4c251 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 28e36c7d..4c424711 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 00000000..5b5bf9a4 --- /dev/null +++ b/wincfg.c @@ -0,0 +1,275 @@ +/* + * wincfg.c - the Windows-specific parts of the PuTTY configuration + * box. + */ + +#include + +#include +#include + +#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 53228236..4d055f46 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 #include +#include #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,11 +373,93 @@ 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. */ void statictext(struct ctlpos *cp, char *text, int lines, int id) @@ -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,13 +518,38 @@ 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. */ void static2btn(struct ctlpos *cp, char *stext, int sid, @@ -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; @@ -482,6 +672,64 @@ void staticddl(struct ctlpos *cp, char *stext, } /* + * 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. */ void bigeditctrl(struct ctlpos *cp, char *stext, @@ -507,6 +755,35 @@ void bigeditctrl(struct ctlpos *cp, char *stext, } /* + * 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. */ void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) @@ -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 9299a006..ca1890c5 100644 --- a/windlg.c +++ b/windlg.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -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< 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 00000000..04e1214b --- /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 1cbe5b78..21d7aa89 100644 --- a/winstuff.h +++ b/winstuff.h @@ -7,6 +7,10 @@ #include /* for FILENAME_MAX */ +#include "tree234.h" + +#include "winhelp.h" + struct Filename { char path[FILENAME_MAX]; }; @@ -112,6 +116,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. */ @@ -142,6 +156,24 @@ struct prefslist { }; /* + * 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. */ void ctlposinit(struct ctlpos *cp, HWND hwnd, @@ -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. */ -- 2.11.0