]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windlg.c
840ca4656a2bb10180c3247d7cb53d83c96de971
[PuTTY.git] / windlg.c
1 #include <windows.h>
2 #include <commctrl.h>
3 #include <commdlg.h>
4 #ifndef AUTO_WINSOCK
5 #ifdef WINSOCK_TWO
6 #include <winsock2.h>
7 #else
8 #include <winsock.h>
9 #endif
10 #endif
11 #include <stdio.h>
12 #include <stdlib.h>
13
14 #include "ssh.h"
15 #include "putty.h"
16 #include "win_res.h"
17 #include "storage.h"
18
19 #define NPANELS 9
20 #define MAIN_NPANELS 9
21 #define RECONF_NPANELS 6
22
23 static char **events = NULL;
24 static int nevents = 0, negsize = 0;
25
26 static HWND logbox = NULL, abtbox = NULL;
27
28 static void gpps(void *handle, char *name, char *def, char *val, int len) {
29     if (!read_setting_s(handle, name, val, len)) {
30         strncpy(val, def, len);
31         val[len-1] = '\0';
32     }
33 }
34
35 static void gppi(void *handle, char *name, int def, int *i) {
36     *i = read_setting_i(handle, name, def);
37 }
38
39 static HINSTANCE hinst;
40
41 static int readytogo;
42
43 static void save_settings (char *section, int do_host) {
44     int i;
45     char *p;
46     void *sesskey;
47
48     sesskey = open_settings_w(section);
49     if (!sesskey)
50         return;
51
52     write_setting_i (sesskey, "Present", 1);
53     if (do_host) {
54         write_setting_s (sesskey, "HostName", cfg.host);
55         write_setting_i (sesskey, "PortNumber", cfg.port);
56         p = "raw";
57         for (i = 0; backends[i].name != NULL; i++)
58             if (backends[i].protocol == cfg.protocol) {
59                 p = backends[i].name;
60                 break;
61             }
62         write_setting_s (sesskey, "Protocol", p);
63     }
64     write_setting_i (sesskey, "CloseOnExit", !!cfg.close_on_exit);
65     write_setting_i (sesskey, "WarnOnClose", !!cfg.warn_on_close);
66     write_setting_s (sesskey, "TerminalType", cfg.termtype);
67     write_setting_s (sesskey, "TerminalSpeed", cfg.termspeed);
68     {
69       char buf[2*sizeof(cfg.environmt)], *p, *q;
70         p = buf;
71       q = cfg.environmt;
72         while (*q) {
73             while (*q) {
74                 int c = *q++;
75                 if (c == '=' || c == ',' || c == '\\')
76                     *p++ = '\\';
77                 if (c == '\t')
78                     c = '=';
79                 *p++ = c;
80             }
81             *p++ = ',';
82             q++;
83         }
84         *p = '\0';
85         write_setting_s (sesskey, "Environment", buf);
86     }
87     write_setting_s (sesskey, "UserName", cfg.username);
88     write_setting_i (sesskey, "NoPTY", cfg.nopty);
89     write_setting_i (sesskey, "AgentFwd", cfg.agentfwd);
90     write_setting_s (sesskey, "RemoteCmd", cfg.remote_cmd);
91     write_setting_s (sesskey, "Cipher", cfg.cipher == CIPHER_BLOWFISH ? "blowfish" :
92                              cfg.cipher == CIPHER_DES ? "des" : "3des");
93     write_setting_i (sesskey, "AuthTIS", cfg.try_tis_auth);
94     write_setting_i (sesskey, "SshProt", cfg.sshprot);
95     write_setting_s (sesskey, "PublicKeyFile", cfg.keyfile);
96     write_setting_s (sesskey, "RemoteCommand", cfg.remote_cmd);
97     write_setting_i (sesskey, "RFCEnviron", cfg.rfc_environ);
98     write_setting_i (sesskey, "BackspaceIsDelete", cfg.bksp_is_delete);
99     write_setting_i (sesskey, "RXVTHomeEnd", cfg.rxvt_homeend);
100     write_setting_i (sesskey, "LinuxFunctionKeys", cfg.funky_type);
101     write_setting_i (sesskey, "ApplicationCursorKeys", cfg.app_cursor);
102     write_setting_i (sesskey, "ApplicationKeypad", cfg.app_keypad);
103     write_setting_i (sesskey, "NetHackKeypad", cfg.nethack_keypad);
104     write_setting_i (sesskey, "AltF4", cfg.alt_f4);
105     write_setting_i (sesskey, "AltSpace", cfg.alt_space);
106     write_setting_i (sesskey, "LdiscTerm", cfg.ldisc_term);
107     write_setting_i (sesskey, "BlinkCur", cfg.blink_cur);
108     write_setting_i (sesskey, "Beep", cfg.beep);
109     write_setting_i (sesskey, "ScrollbackLines", cfg.savelines);
110     write_setting_i (sesskey, "DECOriginMode", cfg.dec_om);
111     write_setting_i (sesskey, "AutoWrapMode", cfg.wrap_mode);
112     write_setting_i (sesskey, "LFImpliesCR", cfg.lfhascr);
113     write_setting_i (sesskey, "WinNameAlways", cfg.win_name_always);
114     write_setting_s (sesskey, "WinTitle", cfg.wintitle);
115     write_setting_i (sesskey, "TermWidth", cfg.width);
116     write_setting_i (sesskey, "TermHeight", cfg.height);
117     write_setting_s (sesskey, "Font", cfg.font);
118     write_setting_i (sesskey, "FontIsBold", cfg.fontisbold);
119     write_setting_i (sesskey, "FontCharSet", cfg.fontcharset);
120     write_setting_i (sesskey, "FontHeight", cfg.fontheight);
121     write_setting_i (sesskey, "FontVTMode", cfg.vtmode);
122     write_setting_i (sesskey, "TryPalette", cfg.try_palette);
123     write_setting_i (sesskey, "BoldAsColour", cfg.bold_colour);
124     for (i=0; i<22; i++) {
125         char buf[20], buf2[30];
126         sprintf(buf, "Colour%d", i);
127         sprintf(buf2, "%d,%d,%d", cfg.colours[i][0],
128                 cfg.colours[i][1], cfg.colours[i][2]);
129         write_setting_s (sesskey, buf, buf2);
130     }
131     write_setting_i (sesskey, "MouseIsXterm", cfg.mouse_is_xterm);
132     for (i=0; i<256; i+=32) {
133         char buf[20], buf2[256];
134         int j;
135         sprintf(buf, "Wordness%d", i);
136         *buf2 = '\0';
137         for (j=i; j<i+32; j++) {
138             sprintf(buf2+strlen(buf2), "%s%d",
139                     (*buf2 ? "," : ""), cfg.wordness[j]);
140         }
141         write_setting_s (sesskey, buf, buf2);
142     }
143     write_setting_i (sesskey, "KoiWinXlat", cfg.xlat_enablekoiwin);
144     write_setting_i (sesskey, "88592Xlat", cfg.xlat_88592w1250);
145     write_setting_i (sesskey, "CapsLockCyr", cfg.xlat_capslockcyr);
146     write_setting_i (sesskey, "ScrollBar", cfg.scrollbar);
147     write_setting_i (sesskey, "ScrollOnKey", cfg.scroll_on_key);
148     write_setting_i (sesskey, "LockSize", cfg.locksize);
149     write_setting_i (sesskey, "BCE", cfg.bce);
150     write_setting_i (sesskey, "BlinkText", cfg.blinktext);
151
152     close_settings_w(sesskey);
153 }
154
155 static void load_settings (char *section, int do_host) {
156     int i;
157     char prot[10];
158     void *sesskey;
159
160     sesskey = open_settings_r(section);
161
162     gpps (sesskey, "HostName", "", cfg.host, sizeof(cfg.host));
163     gppi (sesskey, "PortNumber", default_port, &cfg.port);
164
165     gpps (sesskey, "Protocol", "default", prot, 10);
166     cfg.protocol = default_protocol;
167     for (i = 0; backends[i].name != NULL; i++)
168         if (!strcmp(prot, backends[i].name)) {
169             cfg.protocol = backends[i].protocol;
170             break;
171         }
172
173     gppi (sesskey, "CloseOnExit", 1, &cfg.close_on_exit);
174     gppi (sesskey, "WarnOnClose", 1, &cfg.warn_on_close);
175     gpps (sesskey, "TerminalType", "xterm", cfg.termtype,
176           sizeof(cfg.termtype));
177     gpps (sesskey, "TerminalSpeed", "38400,38400", cfg.termspeed,
178           sizeof(cfg.termspeed));
179     {
180       char buf[2*sizeof(cfg.environmt)], *p, *q;
181         gpps (sesskey, "Environment", "", buf, sizeof(buf));
182         p = buf;
183         q = cfg.environmt;
184         while (*p) {
185             while (*p && *p != ',') {
186                 int c = *p++;
187                 if (c == '=')
188                     c = '\t';
189                 if (c == '\\')
190                     c = *p++;
191                 *q++ = c;
192             }
193             if (*p == ',') p++;
194             *q++ = '\0';
195         }
196         *q = '\0';
197     }
198     gpps (sesskey, "UserName", "", cfg.username, sizeof(cfg.username));
199     gppi (sesskey, "NoPTY", 0, &cfg.nopty);
200     gppi (sesskey, "AgentFwd", 0, &cfg.agentfwd);
201     gpps (sesskey, "RemoteCmd", "", cfg.remote_cmd, sizeof(cfg.remote_cmd));
202     {
203         char cipher[10];
204         gpps (sesskey, "Cipher", "3des", cipher, 10);
205         if (!strcmp(cipher, "blowfish"))
206             cfg.cipher = CIPHER_BLOWFISH;
207         else if (!strcmp(cipher, "des"))
208             cfg.cipher = CIPHER_DES;
209         else
210             cfg.cipher = CIPHER_3DES;
211     }
212     gppi (sesskey, "SshProt", 1, &cfg.sshprot);
213     gppi (sesskey, "AuthTIS", 0, &cfg.try_tis_auth);
214     gpps (sesskey, "PublicKeyFile", "", cfg.keyfile, sizeof(cfg.keyfile));
215     gpps (sesskey, "RemoteCommand", "", cfg.remote_cmd,
216           sizeof(cfg.remote_cmd));
217     gppi (sesskey, "RFCEnviron", 0, &cfg.rfc_environ);
218     gppi (sesskey, "BackspaceIsDelete", 1, &cfg.bksp_is_delete);
219     gppi (sesskey, "RXVTHomeEnd", 0, &cfg.rxvt_homeend);
220     gppi (sesskey, "LinuxFunctionKeys", 0, &cfg.funky_type);
221     gppi (sesskey, "ApplicationCursorKeys", 0, &cfg.app_cursor);
222     gppi (sesskey, "ApplicationKeypad", 0, &cfg.app_keypad);
223     gppi (sesskey, "NetHackKeypad", 0, &cfg.nethack_keypad);
224     gppi (sesskey, "AltF4", 1, &cfg.alt_f4);
225     gppi (sesskey, "AltSpace", 0, &cfg.alt_space);
226     gppi (sesskey, "LdiscTerm", 0, &cfg.ldisc_term);
227     gppi (sesskey, "BlinkCur", 0, &cfg.blink_cur);
228     gppi (sesskey, "Beep", 1, &cfg.beep);
229     gppi (sesskey, "ScrollbackLines", 200, &cfg.savelines);
230     gppi (sesskey, "DECOriginMode", 0, &cfg.dec_om);
231     gppi (sesskey, "AutoWrapMode", 1, &cfg.wrap_mode);
232     gppi (sesskey, "LFImpliesCR", 0, &cfg.lfhascr);
233     gppi (sesskey, "WinNameAlways", 0, &cfg.win_name_always);
234     gpps (sesskey, "WinTitle", "", cfg.wintitle, sizeof(cfg.wintitle));
235     gppi (sesskey, "TermWidth", 80, &cfg.width);
236     gppi (sesskey, "TermHeight", 24, &cfg.height);
237     gpps (sesskey, "Font", "Courier", cfg.font, sizeof(cfg.font));
238     gppi (sesskey, "FontIsBold", 0, &cfg.fontisbold);
239     gppi (sesskey, "FontCharSet", ANSI_CHARSET, &cfg.fontcharset);
240     gppi (sesskey, "FontHeight", 10, &cfg.fontheight);
241     gppi (sesskey, "FontVTMode", VT_OEMANSI, (int *)&cfg.vtmode);
242     gppi (sesskey, "TryPalette", 0, &cfg.try_palette);
243     gppi (sesskey, "BoldAsColour", 1, &cfg.bold_colour);
244     for (i=0; i<22; i++) {
245         static char *defaults[] = {
246             "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",
247             "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85",
248             "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187",
249             "85,85,255", "187,0,187", "255,85,255", "0,187,187",
250             "85,255,255", "187,187,187", "255,255,255"
251         };
252         char buf[20], buf2[30];
253         int c0, c1, c2;
254         sprintf(buf, "Colour%d", i);
255         gpps (sesskey, buf, defaults[i], buf2, sizeof(buf2));
256         if(sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) {
257             cfg.colours[i][0] = c0;
258             cfg.colours[i][1] = c1;
259             cfg.colours[i][2] = c2;
260         }
261     }
262     gppi (sesskey, "MouseIsXterm", 0, &cfg.mouse_is_xterm);
263     for (i=0; i<256; i+=32) {
264         static char *defaults[] = {
265             "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
266             "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1",
267             "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2",
268             "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1",
269             "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
270             "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
271             "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",
272             "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"
273         };
274         char buf[20], buf2[256], *p;
275         int j;
276         sprintf(buf, "Wordness%d", i);
277         gpps (sesskey, buf, defaults[i/32], buf2, sizeof(buf2));
278         p = buf2;
279         for (j=i; j<i+32; j++) {
280             char *q = p;
281             while (*p && *p != ',') p++;
282             if (*p == ',') *p++ = '\0';
283             cfg.wordness[j] = atoi(q);
284         }
285     }
286     gppi (sesskey, "KoiWinXlat", 0, &cfg.xlat_enablekoiwin);
287     gppi (sesskey, "88592Xlat", 0, &cfg.xlat_88592w1250);
288     gppi (sesskey, "CapsLockCyr", 0, &cfg.xlat_capslockcyr);
289     gppi (sesskey, "ScrollBar", 1, &cfg.scrollbar);
290     gppi (sesskey, "ScrollOnKey", 0, &cfg.scroll_on_key);
291     gppi (sesskey, "LockSize", 0, &cfg.locksize);
292     gppi (sesskey, "BCE", 0, &cfg.bce);
293     gppi (sesskey, "BlinkText", 0, &cfg.blinktext);
294
295     close_settings_r(sesskey);
296 }
297
298 static void force_normal(HWND hwnd)
299 {
300 static int recurse = 0;
301
302     WINDOWPLACEMENT wp;
303
304     if(recurse) return;
305     recurse = 1;
306
307     wp.length = sizeof(wp);
308     if (GetWindowPlacement(hwnd, &wp))
309     {
310         wp.showCmd = SW_SHOWNORMAL;
311         SetWindowPlacement(hwnd, &wp);
312     }
313     recurse = 0;
314 }
315
316 static void MyGetDlgItemInt (HWND hwnd, int id, int *result) {
317     BOOL ok;
318     int n;
319     n = GetDlgItemInt (hwnd, id, &ok, FALSE);
320     if (ok)
321         *result = n;
322 }
323
324 static int CALLBACK LogProc (HWND hwnd, UINT msg,
325                              WPARAM wParam, LPARAM lParam) {
326     int i;
327
328     switch (msg) {
329       case WM_INITDIALOG:
330         for (i=0; i<nevents; i++)
331             SendDlgItemMessage (hwnd, IDN_LIST, LB_ADDSTRING,
332                                 0, (LPARAM)events[i]);
333         return 1;
334 /*      case WM_CTLCOLORDLG: */
335 /*      return (int) GetStockObject (LTGRAY_BRUSH); */
336       case WM_COMMAND:
337         switch (LOWORD(wParam)) {
338           case IDOK:
339             logbox = NULL;
340             DestroyWindow (hwnd);
341             return 0;
342           case IDN_COPY:
343             if (HIWORD(wParam) == BN_CLICKED ||
344                 HIWORD(wParam) == BN_DOUBLECLICKED) {
345                 int selcount;
346                 int *selitems;
347                 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
348                                               LB_GETSELCOUNT, 0, 0);
349                 selitems = malloc(selcount * sizeof(int));
350                 if (selitems) {
351                     int count = SendDlgItemMessage(hwnd, IDN_LIST,
352                                                    LB_GETSELITEMS,
353                                                    selcount, (LPARAM)selitems);
354                     int i;
355                     int size;
356                     char *clipdata;
357                     static unsigned char sel_nl[] = SEL_NL;
358
359                     size = 0;
360                     for (i = 0; i < count; i++)
361                         size += strlen(events[selitems[i]]) + sizeof(sel_nl);
362
363                     clipdata = malloc(size);
364                     if (clipdata) {
365                         char *p = clipdata;
366                         for (i = 0; i < count; i++) {
367                             char *q = events[selitems[i]];
368                             int qlen = strlen(q);
369                             memcpy(p, q, qlen);
370                             p += qlen;
371                             memcpy(p, sel_nl, sizeof(sel_nl));
372                             p += sizeof(sel_nl);
373                         }
374                         write_clip(clipdata, size);
375                         term_deselect();
376                         free(clipdata);
377                     }
378                     free(selitems);
379                 }
380             }
381             return 0;
382         }
383         return 0;
384       case WM_CLOSE:
385         logbox = NULL;
386         DestroyWindow (hwnd);
387         return 0;
388     }
389     return 0;
390 }
391
392 static int CALLBACK LicenceProc (HWND hwnd, UINT msg,
393                                  WPARAM wParam, LPARAM lParam) {
394     switch (msg) {
395       case WM_INITDIALOG:
396         return 1;
397       case WM_COMMAND:
398         switch (LOWORD(wParam)) {
399           case IDOK:
400             EndDialog(hwnd, 1);
401             return 0;
402         }
403         return 0;
404       case WM_CLOSE:
405         EndDialog(hwnd, 1);
406         return 0;
407     }
408     return 0;
409 }
410
411 static int CALLBACK AboutProc (HWND hwnd, UINT msg,
412                                WPARAM wParam, LPARAM lParam) {
413     switch (msg) {
414       case WM_INITDIALOG:
415         SetDlgItemText (hwnd, IDA_VERSION, ver);
416         return 1;
417 /*      case WM_CTLCOLORDLG: */
418 /*      return (int) GetStockObject (LTGRAY_BRUSH); */
419 /*      case WM_CTLCOLORSTATIC: */
420 /*      SetBkColor ((HDC)wParam, RGB(192,192,192)); */
421 /*      return (int) GetStockObject (LTGRAY_BRUSH); */
422       case WM_COMMAND:
423         switch (LOWORD(wParam)) {
424           case IDOK:
425             abtbox = NULL;
426             DestroyWindow (hwnd);
427             return 0;
428           case IDA_LICENCE:
429             EnableWindow(hwnd, 0);
430             DialogBox (hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
431                        NULL, LicenceProc);
432             EnableWindow(hwnd, 1);
433             SetActiveWindow(hwnd);
434             return 0;
435         }
436         return 0;
437       case WM_CLOSE:
438         abtbox = NULL;
439         DestroyWindow (hwnd);
440         return 0;
441     }
442     return 0;
443 }
444
445 /* ----------------------------------------------------------------------
446  * Routines to self-manage the controls in a dialog box.
447  */
448
449 #define GAPBETWEEN 3
450 #define GAPWITHIN 1
451 #define DLGWIDTH 168
452 #define STATICHEIGHT 8
453 #define CHECKBOXHEIGHT 8
454 #define RADIOHEIGHT 8
455 #define EDITHEIGHT 12
456 #define COMBOHEIGHT 12
457 #define PUSHBTNHEIGHT 14
458
459 struct ctlpos {
460     HWND hwnd;
461     LONG units;
462     WPARAM font;
463     int ypos, width;
464 };
465
466 /* Used on self-constructed dialogs. */
467 void ctlposinit(struct ctlpos *cp, HWND hwnd) {
468     RECT r;
469     cp->hwnd = hwnd;
470     cp->units = GetWindowLong(hwnd, GWL_USERDATA);
471     cp->font = GetWindowLong(hwnd, DWL_USER);
472     cp->ypos = GAPBETWEEN;
473     GetClientRect(hwnd, &r);
474     cp->width = (r.right * 4) / (cp->units & 0xFFFF) - 2*GAPBETWEEN;
475 }
476
477 /* Used on kosher dialogs. */
478 void ctlposinit2(struct ctlpos *cp, HWND hwnd) {
479     RECT r;
480     cp->hwnd = hwnd;
481     r.left = r.top = 0;
482     r.right = 4;
483     r.bottom = 8;
484     MapDialogRect(hwnd, &r);
485     cp->units = (r.bottom << 16) | r.right;
486     cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0);
487     cp->ypos = GAPBETWEEN;
488     GetClientRect(hwnd, &r);
489     cp->width = (r.right * 4) / (cp->units & 0xFFFF) - 2*GAPBETWEEN;
490 }
491
492 void doctl(struct ctlpos *cp, RECT r, char *wclass, int wstyle, int exstyle,
493            char *wtext, int wid) {
494     HWND ctl;
495     /*
496      * Note nonstandard use of RECT. This is deliberate: by
497      * transforming the width and height directly we arrange to
498      * have all supposedly same-sized controls really same-sized.
499      */
500
501     /* MapDialogRect, or its near equivalent. */
502     r.left = (r.left * (cp->units & 0xFFFF)) / 4;
503     r.right = (r.right * (cp->units & 0xFFFF)) / 4;
504     r.top = (r.top * ((cp->units>>16) & 0xFFFF)) / 8;
505     r.bottom = (r.bottom * ((cp->units>>16) & 0xFFFF)) / 8;
506
507     ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
508                          r.left, r.top, r.right, r.bottom,
509                          cp->hwnd, (HMENU)wid, hinst, NULL);
510     SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
511 }
512
513 /*
514  * Some edit boxes. Each one has a static above it. The percentages
515  * of the horizontal space are provided.
516  */
517 void multiedit(struct ctlpos *cp, ...) {
518     RECT r;
519     va_list ap;
520     int percent, xpos;
521
522     percent = xpos = 0;
523     va_start(ap, cp);
524     while (1) {
525         char *text;
526         int staticid, editid, pcwidth;
527         text = va_arg(ap, char *);
528         if (!text)
529             break;
530         staticid = va_arg(ap, int);
531         editid = va_arg(ap, int);
532         pcwidth = va_arg(ap, int);
533
534         r.left = xpos + GAPBETWEEN;
535         percent += pcwidth;
536         xpos = (cp->width + GAPBETWEEN) * percent / 100;
537         r.right = xpos - r.left;
538
539         r.top = cp->ypos; r.bottom = STATICHEIGHT;
540         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
541               text, staticid);
542         r.top = cp->ypos + 8 + GAPWITHIN; r.bottom = EDITHEIGHT;
543         doctl(cp, r, "EDIT",
544               WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
545               WS_EX_CLIENTEDGE,
546               "", editid);
547     }
548     va_end(ap);
549     cp->ypos += 8+GAPWITHIN+12+GAPBETWEEN;
550 }
551
552 /*
553  * A set of radio buttons on the same line, with a static above
554  * them. `nacross' dictates how many parts the line is divided into
555  * (you might want this not to equal the number of buttons if you
556  * needed to line up some 2s and some 3s to look good in the same
557  * panel).
558  */
559 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) {
560     RECT r;
561     va_list ap;
562     int group;
563     int i;
564
565     r.left = GAPBETWEEN; r.top = cp->ypos;
566     r.right = cp->width; r.bottom = STATICHEIGHT;
567     cp->ypos += r.bottom + GAPWITHIN;
568     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
569     va_start(ap, nacross);
570     group = WS_GROUP;
571     i = 0;
572     while (1) {
573         char *btext;
574         int bid;
575         btext = va_arg(ap, char *);
576         if (!btext)
577             break;
578         bid = va_arg(ap, int);
579         r.left = GAPBETWEEN + i * (cp->width+GAPBETWEEN)/nacross;
580         r.right = (i+1) * (cp->width+GAPBETWEEN)/nacross - r.left;
581         r.top = cp->ypos; r.bottom = RADIOHEIGHT;
582         doctl(cp, r, "BUTTON",
583               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group,
584               0,
585               btext, bid);
586         group = 0;
587         i++;
588     }
589     va_end(ap);
590     cp->ypos += r.bottom + GAPBETWEEN;
591 }
592
593 /*
594  * A set of radio buttons on multiple lines, with a static above
595  * them.
596  */
597 void radiobig(struct ctlpos *cp, char *text, int id, ...) {
598     RECT r;
599     va_list ap;
600     int group;
601
602     r.left = GAPBETWEEN; r.top = cp->ypos;
603     r.right = cp->width; r.bottom = STATICHEIGHT;
604     cp->ypos += r.bottom + GAPWITHIN;
605     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
606     va_start(ap, id);
607     group = WS_GROUP;
608     while (1) {
609         char *btext;
610         int bid;
611         btext = va_arg(ap, char *);
612         if (!btext)
613             break;
614         bid = va_arg(ap, int);
615         r.left = GAPBETWEEN; r.top = cp->ypos;
616         r.right = cp->width; r.bottom = STATICHEIGHT;
617         cp->ypos += r.bottom + GAPWITHIN;
618         doctl(cp, r, "BUTTON",
619               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group,
620               0,
621               btext, bid);
622         group = 0;
623     }
624     va_end(ap);
625     cp->ypos += GAPBETWEEN - GAPWITHIN;
626 }
627
628 /*
629  * A single standalone checkbox.
630  */
631 void checkbox(struct ctlpos *cp, char *text, int id) {
632     RECT r;
633
634     r.left = GAPBETWEEN; r.top = cp->ypos;
635     r.right = cp->width; r.bottom = CHECKBOXHEIGHT;
636     cp->ypos += r.bottom + GAPBETWEEN;
637     doctl(cp, r, "BUTTON",
638           BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
639           text, id);
640 }
641
642 /*
643  * A button on the right hand side, with a static to its left.
644  */
645 void staticbtn(struct ctlpos *cp, char *stext, int sid, char *btext, int bid) {
646     const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
647                         PUSHBTNHEIGHT : STATICHEIGHT);
648     RECT r;
649     int lwid, rwid, rpos;
650
651     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
652     lwid = rpos - 2*GAPBETWEEN;
653     rwid = cp->width + GAPBETWEEN - rpos;
654
655     r.left = GAPBETWEEN; r.top = cp->ypos + (height-STATICHEIGHT)/2;
656     r.right = lwid; r.bottom = STATICHEIGHT;
657     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
658
659     r.left = rpos; r.top = cp->ypos + (height-PUSHBTNHEIGHT)/2;
660     r.right = rwid; r.bottom = PUSHBTNHEIGHT;
661     doctl(cp, r, "BUTTON",
662           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
663           0,
664           btext, bid);
665
666     cp->ypos += height + GAPBETWEEN;
667 }
668
669 /*
670  * An edit control on the right hand side, with a static to its left.
671  */
672 void staticedit(struct ctlpos *cp, char *stext, int sid, int eid) {
673     const int height = (EDITHEIGHT > STATICHEIGHT ?
674                         EDITHEIGHT : STATICHEIGHT);
675     RECT r;
676     int lwid, rwid, rpos;
677
678     rpos = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
679     lwid = rpos - 2*GAPBETWEEN;
680     rwid = cp->width + GAPBETWEEN - rpos;
681
682     r.left = GAPBETWEEN; r.top = cp->ypos + (height-STATICHEIGHT)/2;
683     r.right = lwid; r.bottom = STATICHEIGHT;
684     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
685
686     r.left = rpos; r.top = cp->ypos + (height-EDITHEIGHT)/2;
687     r.right = rwid; r.bottom = EDITHEIGHT;
688     doctl(cp, r, "EDIT",
689           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
690           WS_EX_CLIENTEDGE,
691           "", eid);
692
693     cp->ypos += height + GAPBETWEEN;
694 }
695
696 /*
697  * A tab-control substitute when a real tab control is unavailable.
698  */
699 void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) {
700     const int height = (COMBOHEIGHT > STATICHEIGHT ?
701                         COMBOHEIGHT : STATICHEIGHT);
702     RECT r;
703     int bigwid, lwid, rwid, rpos;
704     static const int BIGGAP = 15;
705     static const int MEDGAP = 3;
706
707     bigwid = cp->width + 2*GAPBETWEEN - 2*BIGGAP;
708     cp->ypos += MEDGAP;
709     rpos = BIGGAP + (bigwid + BIGGAP) / 2;
710     lwid = rpos - 2*BIGGAP;
711     rwid = bigwid + BIGGAP - rpos;
712
713     r.left = BIGGAP; r.top = cp->ypos + (height-STATICHEIGHT)/2;
714     r.right = lwid; r.bottom = STATICHEIGHT;
715     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
716
717     r.left = rpos; r.top = cp->ypos + (height-COMBOHEIGHT)/2;
718     r.right = rwid; r.bottom = COMBOHEIGHT*10;
719     doctl(cp, r, "COMBOBOX",
720           WS_CHILD | WS_VISIBLE | WS_TABSTOP |
721           CBS_DROPDOWNLIST | CBS_HASSTRINGS,
722           WS_EX_CLIENTEDGE,
723           "", lid);
724
725     cp->ypos += height + MEDGAP + GAPBETWEEN;
726
727     r.left = GAPBETWEEN; r.top = cp->ypos;
728     r.right = cp->width; r.bottom = 2;
729     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
730           0, "", s2id);
731 }
732
733 /*
734  * A static line, followed by an edit control on the left hand side
735  * and a button on the right.
736  */
737 void editbutton(struct ctlpos *cp, char *stext, int sid,
738                 int eid, char *btext, int bid) {
739     const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?
740                         EDITHEIGHT : PUSHBTNHEIGHT);
741     RECT r;
742     int lwid, rwid, rpos;
743
744     r.left = GAPBETWEEN; r.top = cp->ypos;
745     r.right = cp->width; r.bottom = STATICHEIGHT;
746     cp->ypos += r.bottom + GAPWITHIN;
747     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
748
749     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
750     lwid = rpos - 2*GAPBETWEEN;
751     rwid = cp->width + GAPBETWEEN - rpos;
752
753     r.left = GAPBETWEEN; r.top = cp->ypos + (height-EDITHEIGHT)/2;
754     r.right = lwid; r.bottom = EDITHEIGHT;
755     doctl(cp, r, "EDIT",
756           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
757           WS_EX_CLIENTEDGE,
758           "", eid);
759
760     r.left = rpos; r.top = cp->ypos + (height-PUSHBTNHEIGHT)/2;
761     r.right = rwid; r.bottom = PUSHBTNHEIGHT;
762     doctl(cp, r, "BUTTON",
763           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
764           0,
765           btext, bid);
766
767     cp->ypos += height + GAPBETWEEN;
768 }
769
770 /*
771  * Special control which was hard to describe generically: the
772  * session-saver assembly. A static; below that an edit box; below
773  * that a list box. To the right of the list box, a column of
774  * buttons.
775  */
776 void sesssaver(struct ctlpos *cp, char *text,
777                int staticid, int editid, int listid, ...) {
778     RECT r;
779     va_list ap;
780     int lwid, rwid, rpos;
781     int y;
782     const int LISTDEFHEIGHT = 66;
783
784     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
785     lwid = rpos - 2*GAPBETWEEN;
786     rwid = cp->width + GAPBETWEEN - rpos;
787
788     /* The static control. */
789     r.left = GAPBETWEEN; r.top = cp->ypos;
790     r.right = lwid; r.bottom = STATICHEIGHT;
791     cp->ypos += r.bottom + GAPWITHIN;
792     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
793
794     /* The edit control. */
795     r.left = GAPBETWEEN; r.top = cp->ypos;
796     r.right = lwid; r.bottom = EDITHEIGHT;
797     cp->ypos += r.bottom + GAPWITHIN;
798     doctl(cp, r, "EDIT",
799           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
800           WS_EX_CLIENTEDGE,
801           "", staticid);
802
803     /*
804      * The buttons (we should hold off on the list box until we
805      * know how big the buttons are).
806      */
807     va_start(ap, listid);
808     y = cp->ypos;
809     while (1) {
810         char *btext = va_arg(ap, char *);
811         int bid;
812         if (!btext) break;
813         bid = va_arg(ap, int);
814         r.left = rpos; r.top = y;
815         r.right = rwid; r.bottom = PUSHBTNHEIGHT;
816         y += r.bottom + GAPWITHIN;
817         doctl(cp, r, "BUTTON",
818               WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
819               0,
820               btext, bid);
821     }
822
823     /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
824     y -= cp->ypos;
825     y -= GAPWITHIN;
826     if (y < LISTDEFHEIGHT) y = LISTDEFHEIGHT;
827     r.left = GAPBETWEEN; r.top = cp->ypos;
828     r.right = lwid; r.bottom = y;
829     cp->ypos += y + GAPBETWEEN;
830     doctl(cp, r, "LISTBOX",
831           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS,
832           WS_EX_CLIENTEDGE,
833           "", listid);
834 }
835
836 /*
837  * Another special control: the environment-variable setter. A
838  * static line first; then a pair of edit boxes with associated
839  * statics, and two buttons; then a list box.
840  */
841 void envsetter(struct ctlpos *cp, char *stext, int sid,
842                char *e1stext, int e1sid, int e1id,
843                char *e2stext, int e2sid, int e2id,
844                int listid, char *b1text, int b1id, char *b2text, int b2id) {
845     RECT r;
846     const int height = (STATICHEIGHT > EDITHEIGHT && STATICHEIGHT > PUSHBTNHEIGHT ?
847                         STATICHEIGHT :
848                         EDITHEIGHT > PUSHBTNHEIGHT ?
849                         EDITHEIGHT : PUSHBTNHEIGHT);
850     const static int percents[] = { 20, 35, 10, 25 };
851     int i, j, xpos, percent;
852     const int LISTHEIGHT = 42;
853
854     /* The static control. */
855     r.left = GAPBETWEEN; r.top = cp->ypos;
856     r.right = cp->width; r.bottom = STATICHEIGHT;
857     cp->ypos += r.bottom + GAPWITHIN;
858     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
859
860     /* The statics+edits+buttons. */
861     for (j = 0; j < 2; j++) {
862         percent = 10;
863         for (i = 0; i < 4; i++) {
864             xpos = (cp->width + GAPBETWEEN) * percent / 100;
865             r.left = xpos + GAPBETWEEN;
866             percent += percents[i];
867             xpos = (cp->width + GAPBETWEEN) * percent / 100;
868             r.right = xpos - r.left;
869             r.top = cp->ypos;
870             r.bottom = (i==0 ? STATICHEIGHT :
871                         i==1 ? EDITHEIGHT :
872                         PUSHBTNHEIGHT);
873             r.top += (height-r.bottom)/2;
874             if (i==0) {
875                 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
876                       j==0 ? e1stext : e2stext, j==0 ? e1sid : e2sid);
877             } else if (i==1) {
878                 doctl(cp, r, "EDIT",
879                       WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
880                       WS_EX_CLIENTEDGE,
881                       "", j==0 ? e1id : e2id);
882             } else if (i==3) {
883                 doctl(cp, r, "BUTTON",
884                       WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
885                       0,
886                       j==0 ? b1text : b2text, j==0 ? b1id : b2id);
887             }
888         }
889         cp->ypos += height + GAPWITHIN;
890     }
891
892     /* The list box. */
893     r.left = GAPBETWEEN; r.top = cp->ypos;
894     r.right = cp->width; r.bottom = LISTHEIGHT;
895     cp->ypos += r.bottom + GAPBETWEEN;
896     doctl(cp, r, "LISTBOX",
897           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
898           LBS_USETABSTOPS,
899           WS_EX_CLIENTEDGE,
900           "", listid);
901 }
902
903 /*
904  * Yet another special control: the character-class setter. A
905  * static, then a list, then a line containing a
906  * button-and-static-and-edit. 
907  */
908 void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
909                char *btext, int bid, int eid, char *s2text, int s2id) {
910     RECT r;
911     const int height = (STATICHEIGHT > EDITHEIGHT && STATICHEIGHT > PUSHBTNHEIGHT ?
912                         STATICHEIGHT :
913                         EDITHEIGHT > PUSHBTNHEIGHT ?
914                         EDITHEIGHT : PUSHBTNHEIGHT);
915     const static int percents[] = { 30, 40, 30 };
916     int i, xpos, percent;
917     const int LISTHEIGHT = 66;
918
919     /* The static control. */
920     r.left = GAPBETWEEN; r.top = cp->ypos;
921     r.right = cp->width; r.bottom = STATICHEIGHT;
922     cp->ypos += r.bottom + GAPWITHIN;
923     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
924
925     /* The list box. */
926     r.left = GAPBETWEEN; r.top = cp->ypos;
927     r.right = cp->width; r.bottom = LISTHEIGHT;
928     cp->ypos += r.bottom + GAPWITHIN;
929     doctl(cp, r, "LISTBOX",
930           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
931           LBS_USETABSTOPS,
932           WS_EX_CLIENTEDGE,
933           "", listid);
934
935     /* The button+static+edit. */
936     percent = xpos = 0;
937     for (i = 0; i < 3; i++) {
938         r.left = xpos + GAPBETWEEN;
939         percent += percents[i];
940         xpos = (cp->width + GAPBETWEEN) * percent / 100;
941         r.right = xpos - r.left;
942         r.top = cp->ypos;
943         r.bottom = (i==0 ? PUSHBTNHEIGHT :
944                     i==1 ? STATICHEIGHT :
945                     EDITHEIGHT);
946         r.top += (height-r.bottom)/2;
947         if (i==0) {
948             doctl(cp, r, "BUTTON",
949                   WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
950                   0, btext, bid);
951         } else if (i==1) {
952             doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER,
953                   0, s2text, s2id);
954         } else if (i==2) {
955             doctl(cp, r, "EDIT",
956                   WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
957                   WS_EX_CLIENTEDGE, "", eid);
958         }
959     }
960     cp->ypos += height + GAPBETWEEN;
961 }
962
963 /*
964  * A special control (horrors!). The colour editor. A static line;
965  * then on the left, a list box, and on the right, a sequence of
966  * two-part statics followed by a button.
967  */
968 void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
969                 char *btext, int bid, ...) {
970     RECT r;
971     int y;
972     va_list ap;
973     int lwid, rwid, rpos;
974     const int LISTHEIGHT = 66;
975
976     /* The static control. */
977     r.left = GAPBETWEEN; r.top = cp->ypos;
978     r.right = cp->width; r.bottom = STATICHEIGHT;
979     cp->ypos += r.bottom + GAPWITHIN;
980     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
981     
982     rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3;
983     lwid = rpos - 2*GAPBETWEEN;
984     rwid = cp->width + GAPBETWEEN - rpos;
985
986     /* The list box. */
987     r.left = GAPBETWEEN; r.top = cp->ypos;
988     r.right = lwid; r.bottom = LISTHEIGHT;
989     doctl(cp, r, "LISTBOX",
990           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
991           LBS_USETABSTOPS,
992           WS_EX_CLIENTEDGE,
993           "", listid);
994
995     /* The statics. */
996     y = cp->ypos;
997     va_start(ap, bid);
998     while (1) {
999         char *ltext;
1000         int lid, rid;
1001         ltext = va_arg(ap, char *);
1002         if (!ltext) break;
1003         lid = va_arg(ap, int);
1004         rid = va_arg(ap, int);
1005         r.top = y; r.bottom = STATICHEIGHT;
1006         y += r.bottom + GAPWITHIN;
1007         r.left = rpos; r.right = rwid/2;
1008         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid);
1009         r.left = rpos + r.right; r.right = rwid - r.right;
1010         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "", rid);
1011     }
1012     va_end(ap);
1013
1014     /* The button. */
1015     r.top = y + 2*GAPWITHIN; r.bottom = PUSHBTNHEIGHT;
1016     r.left = rpos; r.right = rwid;
1017     doctl(cp, r, "BUTTON",
1018           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1019           0, btext, bid);
1020
1021     cp->ypos += LISTHEIGHT + GAPBETWEEN;
1022 }
1023
1024 static int GeneralPanelProc (HWND hwnd, UINT msg,
1025                              WPARAM wParam, LPARAM lParam) {
1026     switch (msg) {
1027       case WM_SETFONT:
1028         {
1029             HFONT hfont = (HFONT)wParam;
1030             HFONT oldfont;
1031             HDC hdc;
1032             TEXTMETRIC tm;
1033             LONG units;
1034
1035             hdc = GetDC(hwnd);
1036             oldfont = SelectObject(hdc, hfont);
1037             GetTextMetrics(hdc, &tm);
1038             units = (tm.tmHeight << 16) | tm.tmAveCharWidth;
1039             SelectObject(hdc, oldfont);
1040             DeleteDC(hdc);
1041             SetWindowLong(hwnd, GWL_USERDATA, units);
1042             SetWindowLong(hwnd, DWL_USER, wParam);
1043         }
1044         return 0;
1045       case WM_INITDIALOG:
1046         SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1047         return 1;
1048       case WM_CLOSE:
1049         DestroyWindow (hwnd);
1050         return 1;
1051     }
1052     return 0;
1053 }
1054
1055 static char savedsession[2048];
1056
1057 static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
1058                                     WPARAM wParam, LPARAM lParam) {
1059     int i;
1060     struct ctlpos cp;
1061
1062     switch (msg) {
1063       case WM_INITDIALOG:
1064         /* Accelerators used: [aco] dehlnprstwx */
1065         ctlposinit(&cp, hwnd);
1066         multiedit(&cp,
1067                   "Host &Name", IDC0_HOSTSTATIC, IDC0_HOST, 75,
1068                   "&Port", IDC0_PORTSTATIC, IDC0_PORT, 25, NULL);
1069         radioline(&cp, "Protocol:", IDC0_PROTSTATIC, 3,
1070                   "&Raw", IDC0_PROTRAW,
1071                   "&Telnet", IDC0_PROTTELNET,
1072 #ifdef FWHACK
1073                   "SS&H/hack",
1074 #else
1075                   "SS&H",
1076 #endif
1077                   IDC0_PROTSSH, NULL);
1078         sesssaver(&cp, "Stor&ed Sessions",
1079                   IDC0_SESSSTATIC, IDC0_SESSEDIT, IDC0_SESSLIST,
1080                   "&Load", IDC0_SESSLOAD,
1081                   "&Save", IDC0_SESSSAVE,
1082                   "&Delete", IDC0_SESSDEL, NULL);
1083         checkbox(&cp, "Close Window on E&xit", IDC0_CLOSEEXIT);
1084         checkbox(&cp, "&Warn on Close", IDC0_CLOSEWARN);
1085
1086         SetDlgItemText (hwnd, IDC0_HOST, cfg.host);
1087         SetDlgItemText (hwnd, IDC0_SESSEDIT, savedsession);
1088         SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
1089         for (i = 0; i < nsessions; i++)
1090             SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
1091                                 0, (LPARAM) (sessions[i]));
1092         CheckRadioButton (hwnd, IDC0_PROTRAW, IDC0_PROTSSH,
1093                           cfg.protocol==PROT_SSH ? IDC0_PROTSSH : 
1094                           cfg.protocol==PROT_TELNET ? IDC0_PROTTELNET : IDC0_PROTRAW );
1095         CheckDlgButton (hwnd, IDC0_CLOSEEXIT, cfg.close_on_exit);
1096         CheckDlgButton (hwnd, IDC0_CLOSEWARN, cfg.warn_on_close);
1097         break;
1098       case WM_LBUTTONUP:
1099         /*
1100          * Button release should trigger WM_OK if there was a
1101          * previous double click on the session list.
1102          */
1103         ReleaseCapture();
1104         if (readytogo)
1105             SendMessage (GetParent(hwnd), WM_COMMAND, IDOK, 0);
1106         break;
1107       case WM_COMMAND:
1108         switch (LOWORD(wParam)) {
1109           case IDC0_PROTTELNET:
1110           case IDC0_PROTSSH:
1111           case IDC0_PROTRAW:
1112             if (HIWORD(wParam) == BN_CLICKED ||
1113                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1114                 int i = IsDlgButtonChecked (hwnd, IDC0_PROTSSH);
1115                 int j = IsDlgButtonChecked (hwnd, IDC0_PROTTELNET);
1116                 cfg.protocol = i ? PROT_SSH : j ? PROT_TELNET : PROT_RAW ;
1117                 if ((cfg.protocol == PROT_SSH && cfg.port == 23) ||
1118                     (cfg.protocol == PROT_TELNET && cfg.port == 22)) {
1119                     cfg.port = i ? 22 : 23;
1120                     SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
1121                 }
1122             }
1123             break;
1124           case IDC0_HOST:
1125             if (HIWORD(wParam) == EN_CHANGE)
1126                 GetDlgItemText (hwnd, IDC0_HOST, cfg.host,
1127                                 sizeof(cfg.host)-1);
1128             break;
1129           case IDC0_PORT:
1130             if (HIWORD(wParam) == EN_CHANGE)
1131                 MyGetDlgItemInt (hwnd, IDC0_PORT, &cfg.port);
1132             break;
1133           case IDC0_CLOSEEXIT:
1134             if (HIWORD(wParam) == BN_CLICKED ||
1135                 HIWORD(wParam) == BN_DOUBLECLICKED)
1136                 cfg.close_on_exit = IsDlgButtonChecked (hwnd, IDC0_CLOSEEXIT);
1137             break;
1138           case IDC0_CLOSEWARN:
1139             if (HIWORD(wParam) == BN_CLICKED ||
1140                 HIWORD(wParam) == BN_DOUBLECLICKED)
1141                 cfg.warn_on_close = IsDlgButtonChecked (hwnd, IDC0_CLOSEWARN);
1142             break;
1143           case IDC0_SESSEDIT:
1144             if (HIWORD(wParam) == EN_CHANGE) {
1145                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
1146                                     (WPARAM) -1, 0);
1147                 GetDlgItemText (hwnd, IDC0_SESSEDIT,
1148                                 savedsession, sizeof(savedsession)-1);
1149                 savedsession[sizeof(savedsession)-1] = '\0';
1150             }
1151             break;
1152           case IDC0_SESSSAVE:
1153             if (HIWORD(wParam) == BN_CLICKED ||
1154                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1155                 /*
1156                  * Save a session
1157                  */
1158                 char str[2048];
1159                 GetDlgItemText (hwnd, IDC0_SESSEDIT, str, sizeof(str)-1);
1160                 if (!*str) {
1161                     int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
1162                                                 LB_GETCURSEL, 0, 0);
1163                     if (n == LB_ERR) {
1164                         MessageBeep(0);
1165                         break;
1166                     }
1167                     strcpy (str, sessions[n]);
1168                 }
1169                 save_settings (str, !!strcmp(str, "Default Settings"));
1170                 get_sesslist (FALSE);
1171                 get_sesslist (TRUE);
1172                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_RESETCONTENT,
1173                                     0, 0);
1174                 for (i = 0; i < nsessions; i++)
1175                     SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
1176                                         0, (LPARAM) (sessions[i]));
1177                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
1178                                     (WPARAM) -1, 0);
1179             }
1180             break;
1181           case IDC0_SESSLIST:
1182           case IDC0_SESSLOAD:
1183             if (LOWORD(wParam) == IDC0_SESSLOAD &&
1184                 HIWORD(wParam) != BN_CLICKED &&
1185                 HIWORD(wParam) != BN_DOUBLECLICKED)
1186                 break;
1187             if (LOWORD(wParam) == IDC0_SESSLIST &&
1188                 HIWORD(wParam) != LBN_DBLCLK)
1189                 break;
1190             {
1191                 int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
1192                                             LB_GETCURSEL, 0, 0);
1193                 if (n == LB_ERR) {
1194                     MessageBeep(0);
1195                     break;
1196                 }
1197                 load_settings (sessions[n],
1198                                !!strcmp(sessions[n], "Default Settings"));
1199                 SetDlgItemText (hwnd, IDC0_HOST, cfg.host);
1200                 SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
1201                 CheckRadioButton (hwnd, IDC0_PROTRAW, IDC0_PROTSSH,
1202                                   (cfg.protocol==PROT_SSH ? IDC0_PROTSSH :
1203                                   cfg.protocol==PROT_TELNET ? IDC0_PROTTELNET : IDC0_PROTRAW));
1204                 CheckDlgButton (hwnd, IDC0_CLOSEEXIT, cfg.close_on_exit);
1205                 CheckDlgButton (hwnd, IDC0_CLOSEWARN, cfg.warn_on_close);
1206                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
1207                                     (WPARAM) -1, 0);
1208             }
1209             if (LOWORD(wParam) == IDC0_SESSLIST) {
1210                 /*
1211                  * A double-click on a saved session should
1212                  * actually start the session, not just load it.
1213                  * Unless it's Default Settings or some other
1214                  * host-less set of saved settings.
1215                  */
1216                 if (*cfg.host) {
1217                     readytogo = TRUE;
1218                     SetCapture(hwnd);
1219                 }
1220             }
1221             break;
1222           case IDC0_SESSDEL:
1223             if (HIWORD(wParam) == BN_CLICKED ||
1224                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1225                 int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
1226                                             LB_GETCURSEL, 0, 0);
1227                 if (n == LB_ERR || n == 0) {
1228                     MessageBeep(0);
1229                     break;
1230                 }
1231                 del_settings(sessions[n]);
1232                 get_sesslist (FALSE);
1233                 get_sesslist (TRUE);
1234                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_RESETCONTENT,
1235                                     0, 0);
1236                 for (i = 0; i < nsessions; i++)
1237                     SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
1238                                         0, (LPARAM) (sessions[i]));
1239                 SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
1240                                     (WPARAM) -1, 0);
1241             }
1242         }
1243     }
1244     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1245 }
1246
1247 static int CALLBACK KeyboardProc (HWND hwnd, UINT msg,
1248                                   WPARAM wParam, LPARAM lParam) {
1249     struct ctlpos cp;
1250     switch (msg) {
1251       case WM_INITDIALOG:
1252         /* Accelerators used: [aco] 4?ehiklmnprsuvxy */
1253         ctlposinit(&cp, hwnd);
1254         radioline(&cp, "Action of Backspace:", IDC1_DELSTATIC, 2,
1255                   "Control-&H", IDC1_DEL008,
1256                   "Control-&? (127)", IDC1_DEL127, NULL);
1257         radioline(&cp, "Action of Home and End:", IDC1_HOMESTATIC, 2,
1258                   "&Standard", IDC1_HOMETILDE,
1259                   "&rxvt", IDC1_HOMERXVT, NULL);
1260         radioline(&cp, "Function key and keypad layout:", IDC1_FUNCSTATIC, 3,
1261                   "&VT400", IDC1_FUNCTILDE,
1262                   "&Linux", IDC1_FUNCLINUX,
1263                   "&Xterm R6", IDC1_FUNCXTERM, NULL);
1264         radioline(&cp, "Initial state of cursor keys:", IDC1_CURSTATIC, 2,
1265                   "&Normal", IDC1_CURNORMAL,
1266                   "A&pplication", IDC1_CURAPPLIC, NULL);
1267         radioline(&cp, "Initial state of numeric keypad:", IDC1_KPSTATIC, 3,
1268                   "Nor&mal", IDC1_KPNORMAL,
1269                   "Appl&ication", IDC1_KPAPPLIC,
1270                   "N&etHack", IDC1_KPNH, NULL);
1271         checkbox(&cp, "ALT-F&4 is special (closes window)", IDC1_ALTF4);
1272         checkbox(&cp, "ALT-Space is special (S&ystem menu)", IDC1_ALTSPACE);
1273         checkbox(&cp, "&Use local terminal line discipline", IDC1_LDISCTERM);
1274         checkbox(&cp, "Reset scrollback on &keypress", IDC1_SCROLLKEY);
1275
1276         CheckRadioButton (hwnd, IDC1_DEL008, IDC1_DEL127,
1277                           cfg.bksp_is_delete ? IDC1_DEL127 : IDC1_DEL008);
1278         CheckRadioButton (hwnd, IDC1_HOMETILDE, IDC1_HOMERXVT,
1279                           cfg.rxvt_homeend ? IDC1_HOMERXVT : IDC1_HOMETILDE);
1280         CheckRadioButton (hwnd, IDC1_FUNCTILDE, IDC1_FUNCXTERM,
1281                           cfg.funky_type ? 
1282                           (cfg.funky_type==2 ? IDC1_FUNCXTERM 
1283                            : IDC1_FUNCLINUX )
1284                           : IDC1_FUNCTILDE);
1285         CheckRadioButton (hwnd, IDC1_CURNORMAL, IDC1_CURAPPLIC,
1286                           cfg.app_cursor ? IDC1_CURAPPLIC : IDC1_CURNORMAL);
1287         CheckRadioButton (hwnd, IDC1_KPNORMAL, IDC1_KPNH,
1288                           cfg.nethack_keypad ? IDC1_KPNH :
1289                           cfg.app_keypad ? IDC1_KPAPPLIC : IDC1_KPNORMAL);
1290         CheckDlgButton (hwnd, IDC1_ALTF4, cfg.alt_f4);
1291         CheckDlgButton (hwnd, IDC1_ALTSPACE, cfg.alt_space);
1292         CheckDlgButton (hwnd, IDC1_LDISCTERM, cfg.ldisc_term);
1293         CheckDlgButton (hwnd, IDC1_SCROLLKEY, cfg.scroll_on_key);
1294         break;
1295       case WM_COMMAND:
1296         if (HIWORD(wParam) == BN_CLICKED ||
1297             HIWORD(wParam) == BN_DOUBLECLICKED)
1298             switch (LOWORD(wParam)) {
1299               case IDC1_DEL008:
1300               case IDC1_DEL127:
1301                 cfg.bksp_is_delete = IsDlgButtonChecked (hwnd, IDC1_DEL127);
1302                 break;
1303               case IDC1_HOMETILDE:
1304               case IDC1_HOMERXVT:
1305                 cfg.rxvt_homeend = IsDlgButtonChecked (hwnd, IDC1_HOMERXVT);
1306                 break;
1307               case IDC1_FUNCXTERM:
1308                 cfg.funky_type = 2;
1309                 break;
1310               case IDC1_FUNCTILDE:
1311               case IDC1_FUNCLINUX:
1312                 cfg.funky_type = IsDlgButtonChecked (hwnd, IDC1_FUNCLINUX);
1313                 break;
1314               case IDC1_KPNORMAL:
1315               case IDC1_KPAPPLIC:
1316                 cfg.app_keypad = IsDlgButtonChecked (hwnd, IDC1_KPAPPLIC);
1317                 cfg.nethack_keypad = FALSE;
1318                 break;
1319               case IDC1_KPNH:
1320                 cfg.app_keypad = FALSE;
1321                 cfg.nethack_keypad = TRUE;
1322                 break;
1323               case IDC1_CURNORMAL:
1324               case IDC1_CURAPPLIC:
1325                 cfg.app_cursor = IsDlgButtonChecked (hwnd, IDC1_CURAPPLIC);
1326                 break;
1327               case IDC1_ALTF4:
1328                 if (HIWORD(wParam) == BN_CLICKED ||
1329                     HIWORD(wParam) == BN_DOUBLECLICKED)
1330                     cfg.alt_f4 = IsDlgButtonChecked (hwnd, IDC1_ALTF4);
1331                 break;
1332               case IDC1_ALTSPACE:
1333                 if (HIWORD(wParam) == BN_CLICKED ||
1334                     HIWORD(wParam) == BN_DOUBLECLICKED)
1335                     cfg.alt_space = IsDlgButtonChecked (hwnd, IDC1_ALTSPACE);
1336                 break;
1337               case IDC1_LDISCTERM:
1338                 if (HIWORD(wParam) == BN_CLICKED ||
1339                     HIWORD(wParam) == BN_DOUBLECLICKED)
1340                     cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC1_LDISCTERM);
1341                 break;
1342               case IDC1_SCROLLKEY:
1343                 if (HIWORD(wParam) == BN_CLICKED ||
1344                     HIWORD(wParam) == BN_DOUBLECLICKED)
1345                     cfg.scroll_on_key = IsDlgButtonChecked (hwnd, IDC1_SCROLLKEY);
1346                 break;
1347             }
1348     }
1349     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1350 }
1351
1352 static void fmtfont (char *buf) {
1353     sprintf (buf, "Font: %s, ", cfg.font);
1354     if (cfg.fontisbold)
1355         strcat(buf, "bold, ");
1356     if (cfg.fontheight == 0)
1357         strcat (buf, "default height");
1358     else
1359         sprintf (buf+strlen(buf), "%d-%s",
1360                  (cfg.fontheight < 0 ? -cfg.fontheight : cfg.fontheight),
1361                  (cfg.fontheight < 0 ? "pixel" : "point"));
1362 }
1363
1364 static int CALLBACK TerminalProc (HWND hwnd, UINT msg,
1365                                     WPARAM wParam, LPARAM lParam) {
1366     struct ctlpos cp;
1367     CHOOSEFONT cf;
1368     LOGFONT lf;
1369     char fontstatic[256];
1370
1371     switch (msg) {
1372       case WM_INITDIALOG:
1373         /* Accelerators used: [aco] dghlmnprsw */
1374         ctlposinit(&cp, hwnd);
1375         multiedit(&cp,
1376                   "&Rows", IDC2_ROWSSTATIC, IDC2_ROWSEDIT, 33,
1377                   "Colu&mns", IDC2_COLSSTATIC, IDC2_COLSEDIT, 33,
1378                   "&Scrollback", IDC2_SAVESTATIC, IDC2_SAVEEDIT, 33,
1379                   NULL);
1380         staticbtn(&cp, "", IDC2_FONTSTATIC, "C&hange...", IDC2_CHOOSEFONT);
1381         checkbox(&cp, "Auto &wrap mode initially on", IDC2_WRAPMODE);
1382         checkbox(&cp, "&DEC Origin Mode initially on", IDC2_DECOM);
1383         checkbox(&cp, "Implicit CR in every &LF", IDC2_LFHASCR);
1384         checkbox(&cp, "Bee&p enabled", IDC1_BEEP);
1385         checkbox(&cp, "Use Back&ground colour erase", IDC2_BCE);
1386         checkbox(&cp, "Enable bli&nking text", IDC2_BLINKTEXT);
1387
1388         CheckDlgButton (hwnd, IDC2_WRAPMODE, cfg.wrap_mode);
1389         CheckDlgButton (hwnd, IDC2_DECOM, cfg.dec_om);
1390         CheckDlgButton (hwnd, IDC2_LFHASCR, cfg.lfhascr);
1391         SetDlgItemInt (hwnd, IDC2_ROWSEDIT, cfg.height, FALSE);
1392         SetDlgItemInt (hwnd, IDC2_COLSEDIT, cfg.width, FALSE);
1393         SetDlgItemInt (hwnd, IDC2_SAVEEDIT, cfg.savelines, FALSE);
1394         fmtfont (fontstatic);
1395         SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic);
1396         CheckDlgButton (hwnd, IDC1_BEEP, cfg.beep);
1397         CheckDlgButton (hwnd, IDC2_BCE, cfg.bce);
1398         CheckDlgButton (hwnd, IDC2_BLINKTEXT, cfg.blinktext);
1399         break;
1400       case WM_COMMAND:
1401         switch (LOWORD(wParam)) {
1402           case IDC2_WRAPMODE:
1403             if (HIWORD(wParam) == BN_CLICKED ||
1404                 HIWORD(wParam) == BN_DOUBLECLICKED)
1405                 cfg.wrap_mode = IsDlgButtonChecked (hwnd, IDC2_WRAPMODE);
1406             break;
1407           case IDC2_DECOM:
1408             if (HIWORD(wParam) == BN_CLICKED ||
1409                 HIWORD(wParam) == BN_DOUBLECLICKED)
1410                 cfg.dec_om = IsDlgButtonChecked (hwnd, IDC2_DECOM);
1411             break;
1412           case IDC2_LFHASCR:
1413             if (HIWORD(wParam) == BN_CLICKED ||
1414                 HIWORD(wParam) == BN_DOUBLECLICKED)
1415                 cfg.lfhascr = IsDlgButtonChecked (hwnd, IDC2_LFHASCR);
1416             break;
1417           case IDC2_ROWSEDIT:
1418             if (HIWORD(wParam) == EN_CHANGE)
1419                 MyGetDlgItemInt (hwnd, IDC2_ROWSEDIT, &cfg.height);
1420             break;
1421           case IDC2_COLSEDIT:
1422             if (HIWORD(wParam) == EN_CHANGE)
1423                 MyGetDlgItemInt (hwnd, IDC2_COLSEDIT, &cfg.width);
1424             break;
1425           case IDC2_SAVEEDIT:
1426             if (HIWORD(wParam) == EN_CHANGE)
1427                 MyGetDlgItemInt (hwnd, IDC2_SAVEEDIT, &cfg.savelines);
1428             break;
1429           case IDC2_CHOOSEFONT:
1430             lf.lfHeight = cfg.fontheight;
1431             lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
1432             lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
1433             lf.lfWeight = (cfg.fontisbold ? FW_BOLD : 0);
1434             lf.lfCharSet = cfg.fontcharset;
1435             lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
1436             lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1437             lf.lfQuality = DEFAULT_QUALITY;
1438             lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1439             strncpy (lf.lfFaceName, cfg.font, sizeof(lf.lfFaceName)-1);
1440             lf.lfFaceName[sizeof(lf.lfFaceName)-1] = '\0';
1441
1442             cf.lStructSize = sizeof(cf);
1443             cf.hwndOwner = hwnd;
1444             cf.lpLogFont = &lf;
1445             cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
1446                 CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
1447
1448             if (ChooseFont (&cf)) {
1449                 strncpy (cfg.font, lf.lfFaceName, sizeof(cfg.font)-1);
1450                 cfg.font[sizeof(cfg.font)-1] = '\0';
1451                 cfg.fontisbold = (lf.lfWeight == FW_BOLD);
1452                 cfg.fontcharset = lf.lfCharSet;
1453                 cfg.fontheight = lf.lfHeight;
1454                 fmtfont (fontstatic);
1455                 SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic);
1456             }
1457             break;
1458            case IDC1_BEEP:
1459              if (HIWORD(wParam) == BN_CLICKED ||
1460                  HIWORD(wParam) == BN_DOUBLECLICKED)
1461                  cfg.beep = IsDlgButtonChecked (hwnd, IDC1_BEEP);
1462              break;
1463            case IDC2_BLINKTEXT:
1464              if (HIWORD(wParam) == BN_CLICKED ||
1465                  HIWORD(wParam) == BN_DOUBLECLICKED)
1466                  cfg.blinktext = IsDlgButtonChecked (hwnd, IDC2_BLINKTEXT);
1467              break;
1468            case IDC2_BCE:
1469              if (HIWORD(wParam) == BN_CLICKED ||
1470                  HIWORD(wParam) == BN_DOUBLECLICKED)
1471                  cfg.bce = IsDlgButtonChecked (hwnd, IDC2_BCE);
1472              break;
1473         }
1474         break;
1475     }
1476     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1477 }
1478
1479 static int CALLBACK WindowProc (HWND hwnd, UINT msg,
1480                                     WPARAM wParam, LPARAM lParam) {
1481     struct ctlpos cp;
1482     switch (msg) {
1483       case WM_INITDIALOG:
1484         /* Accelerators used: [aco] bikty */
1485         ctlposinit(&cp, hwnd);
1486         multiedit(&cp,
1487                   "Initial window &title:", IDCW_WINTITLE, IDCW_WINEDIT, 100,
1488                   NULL);
1489         checkbox(&cp, "Avoid ever using &icon title", IDCW_WINNAME);
1490         checkbox(&cp, "&Blinking cursor", IDCW_BLINKCUR);
1491         checkbox(&cp, "Displa&y scrollbar", IDCW_SCROLLBAR);
1492         checkbox(&cp, "Loc&k Window size", IDCW_LOCKSIZE);
1493
1494         SetDlgItemText (hwnd, IDCW_WINEDIT, cfg.wintitle);
1495         CheckDlgButton (hwnd, IDCW_WINNAME, cfg.win_name_always);
1496         CheckDlgButton (hwnd, IDCW_BLINKCUR, cfg.blink_cur);
1497         CheckDlgButton (hwnd, IDCW_SCROLLBAR, cfg.scrollbar);
1498         CheckDlgButton (hwnd, IDCW_LOCKSIZE, cfg.locksize);
1499         break;
1500       case WM_COMMAND:
1501         switch (LOWORD(wParam)) {
1502           case IDCW_WINNAME:
1503             if (HIWORD(wParam) == BN_CLICKED ||
1504                 HIWORD(wParam) == BN_DOUBLECLICKED)
1505                 cfg.win_name_always = IsDlgButtonChecked (hwnd, IDCW_WINNAME);
1506             break;
1507           case IDCW_BLINKCUR:
1508             if (HIWORD(wParam) == BN_CLICKED ||
1509                 HIWORD(wParam) == BN_DOUBLECLICKED)
1510                 cfg.blink_cur = IsDlgButtonChecked (hwnd, IDCW_BLINKCUR);
1511             break;
1512           case IDCW_SCROLLBAR:
1513             if (HIWORD(wParam) == BN_CLICKED ||
1514                 HIWORD(wParam) == BN_DOUBLECLICKED)
1515                 cfg.scrollbar = IsDlgButtonChecked (hwnd, IDCW_SCROLLBAR);
1516             break;
1517           case IDCW_LOCKSIZE:
1518              if (HIWORD(wParam) == BN_CLICKED ||
1519                  HIWORD(wParam) == BN_DOUBLECLICKED)
1520                 cfg.locksize = IsDlgButtonChecked (hwnd, IDCW_LOCKSIZE);
1521             break;
1522           case IDCW_WINEDIT:
1523             if (HIWORD(wParam) == EN_CHANGE)
1524                 GetDlgItemText (hwnd, IDCW_WINEDIT, cfg.wintitle,
1525                                 sizeof(cfg.wintitle)-1);
1526             break;
1527         }
1528         break;
1529     }
1530     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1531 }
1532
1533 static int CALLBACK TelnetProc (HWND hwnd, UINT msg,
1534                                     WPARAM wParam, LPARAM lParam) {
1535     int i;
1536     struct ctlpos cp;
1537
1538     switch (msg) {
1539       case WM_INITDIALOG:
1540         /* Accelerators used: [aco] bdflrstuv */
1541         ctlposinit(&cp, hwnd);
1542         staticedit(&cp, "Terminal-&type string", IDC3_TTSTATIC, IDC3_TTEDIT);
1543         staticedit(&cp, "Terminal-&speed string", IDC3_TSSTATIC, IDC3_TSEDIT);
1544         staticedit(&cp, "Auto-login &username", IDC3_LOGSTATIC, IDC3_LOGEDIT);
1545         envsetter(&cp, "Environment variables:", IDC3_ENVSTATIC,
1546                   "&Variable", IDC3_VARSTATIC, IDC3_VAREDIT,
1547                   "Va&lue", IDC3_VALSTATIC, IDC3_VALEDIT,
1548                   IDC3_ENVLIST,
1549                   "A&dd", IDC3_ENVADD, "&Remove", IDC3_ENVREMOVE);
1550         radioline(&cp, "Handling of OLD_ENVIRON ambiguity:", IDC3_EMSTATIC, 2,
1551                   "&BSD (commonplace)", IDC3_EMBSD,
1552                   "R&FC 1408 (unusual)", IDC3_EMRFC, NULL);
1553
1554         SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype);
1555         SetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed);
1556         SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username);
1557         {
1558           char *p = cfg.environmt;
1559             while (*p) {
1560                 SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING, 0,
1561                                     (LPARAM) p);
1562                 p += strlen(p)+1;
1563             }
1564         }
1565         CheckRadioButton (hwnd, IDC3_EMBSD, IDC3_EMRFC,
1566                           cfg.rfc_environ ? IDC3_EMRFC : IDC3_EMBSD);
1567         break;
1568       case WM_COMMAND:
1569         switch (LOWORD(wParam)) {
1570           case IDC3_TTEDIT:
1571             if (HIWORD(wParam) == EN_CHANGE)
1572             GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype,
1573                             sizeof(cfg.termtype)-1);
1574             break;
1575           case IDC3_TSEDIT:
1576             if (HIWORD(wParam) == EN_CHANGE)
1577                 GetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed,
1578                                 sizeof(cfg.termspeed)-1);
1579             break;
1580           case IDC3_LOGEDIT:
1581             if (HIWORD(wParam) == EN_CHANGE)
1582                 GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username,
1583                                 sizeof(cfg.username)-1);
1584             break;
1585           case IDC3_EMBSD:
1586           case IDC3_EMRFC:
1587             cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC3_EMRFC);
1588             break;
1589           case IDC3_ENVADD:
1590             if (HIWORD(wParam) == BN_CLICKED ||
1591                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1592               char str[sizeof(cfg.environmt)];
1593                 char *p;
1594                 GetDlgItemText (hwnd, IDC3_VAREDIT, str, sizeof(str)-1);
1595                 if (!*str) {
1596                     MessageBeep(0);
1597                     break;
1598                 }
1599                 p = str + strlen(str);
1600                 *p++ = '\t';
1601                 GetDlgItemText (hwnd, IDC3_VALEDIT, p, sizeof(str)-1-(p-str));
1602                 if (!*p) {
1603                     MessageBeep(0);
1604                     break;
1605                 }
1606               p = cfg.environmt;
1607                 while (*p) {
1608                     while (*p) p++;
1609                     p++;
1610                 }
1611               if ((p-cfg.environmt) + strlen(str) + 2 < sizeof(cfg.environmt)) {
1612                     strcpy (p, str);
1613                     p[strlen(str)+1] = '\0';
1614                     SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING,
1615                                         0, (LPARAM)str);
1616                     SetDlgItemText (hwnd, IDC3_VAREDIT, "");
1617                     SetDlgItemText (hwnd, IDC3_VALEDIT, "");
1618                 } else {
1619                     MessageBox(hwnd, "Environment too big", "PuTTY Error",
1620                                MB_OK | MB_ICONERROR);
1621                 }
1622             }
1623             break;
1624           case IDC3_ENVREMOVE:
1625             if (HIWORD(wParam) != BN_CLICKED &&
1626                 HIWORD(wParam) != BN_DOUBLECLICKED)
1627                 break;
1628             i = SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_GETCURSEL, 0, 0);
1629             if (i == LB_ERR)
1630                 MessageBeep (0);
1631             else {
1632                 char *p, *q;
1633
1634                 SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_DELETESTRING,
1635                                     i, 0);
1636               p = cfg.environmt;
1637                 while (i > 0) {
1638                     if (!*p)
1639                         goto disaster;
1640                     while (*p) p++;
1641                     p++;
1642                     i--;
1643                 }
1644                 q = p;
1645                 if (!*p)
1646                     goto disaster;
1647                 while (*p) p++;
1648                 p++;
1649                 while (*p) {
1650                     while (*p)
1651                         *q++ = *p++;
1652                     *q++ = *p++;
1653                 }
1654                 *q = '\0';
1655                 disaster:;
1656             }
1657             break;
1658         }
1659         break;
1660     }
1661     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1662 }
1663
1664 static int CALLBACK SshProc (HWND hwnd, UINT msg,
1665                              WPARAM wParam, LPARAM lParam) {
1666     struct ctlpos cp;
1667     OPENFILENAME of;
1668     char filename[sizeof(cfg.keyfile)];
1669
1670     switch (msg) {
1671       case WM_INITDIALOG:
1672         /* Accelerators used: [aco] 123abdkmprtuw */
1673         ctlposinit(&cp, hwnd);
1674         staticedit(&cp, "Terminal-&type string", IDC3_TTSTATIC, IDC3_TTEDIT);
1675         staticedit(&cp, "Auto-login &username", IDC3_LOGSTATIC, IDC3_LOGEDIT);
1676         multiedit(&cp,
1677                   "&Remote command:", IDC3_CMDSTATIC, IDC3_CMDEDIT, 100,
1678                   NULL);
1679         checkbox(&cp, "Don't allocate a &pseudo-terminal", IDC3_NOPTY);
1680         checkbox(&cp, "Atte&mpt TIS or CryptoCard authentication",
1681                  IDC3_AUTHTIS);
1682         checkbox(&cp, "Allow &agent forwarding", IDC3_AGENTFWD);
1683         editbutton(&cp, "Private &key file for authentication:",
1684                     IDC3_PKSTATIC, IDC3_PKEDIT, "Bro&wse...", IDC3_PKBUTTON);
1685         radioline(&cp, "Preferred SSH protocol version:",
1686                   IDC3_SSHPROTSTATIC, 2,
1687                   "&1", IDC3_SSHPROT1, "&2", IDC3_SSHPROT2, NULL);
1688         radioline(&cp, "Preferred encryption algorithm:", IDC3_CIPHERSTATIC, 3,
1689                   "&3DES", IDC3_CIPHER3DES,
1690                   "&Blowfish", IDC3_CIPHERBLOWF,
1691                   "&DES", IDC3_CIPHERDES, NULL);    
1692
1693         SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype);
1694         SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username);
1695         CheckDlgButton (hwnd, IDC3_NOPTY, cfg.nopty);
1696         CheckDlgButton (hwnd, IDC3_AGENTFWD, cfg.agentfwd);
1697         CheckRadioButton (hwnd, IDC3_CIPHER3DES, IDC3_CIPHERDES,
1698                           cfg.cipher == CIPHER_BLOWFISH ? IDC3_CIPHERBLOWF :
1699                           cfg.cipher == CIPHER_DES ? IDC3_CIPHERDES :
1700                           IDC3_CIPHER3DES);
1701         CheckRadioButton (hwnd, IDC3_SSHPROT1, IDC3_SSHPROT2,
1702                           cfg.sshprot == 1 ? IDC3_SSHPROT1 : IDC3_SSHPROT2);
1703         CheckDlgButton (hwnd, IDC3_AUTHTIS, cfg.try_tis_auth);
1704         SetDlgItemText (hwnd, IDC3_PKEDIT, cfg.keyfile);
1705         SetDlgItemText (hwnd, IDC3_CMDEDIT, cfg.remote_cmd);
1706         break;
1707       case WM_COMMAND:
1708         switch (LOWORD(wParam)) {
1709           case IDC3_TTEDIT:
1710             if (HIWORD(wParam) == EN_CHANGE)
1711             GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype,
1712                             sizeof(cfg.termtype)-1);
1713             break;
1714           case IDC3_LOGEDIT:
1715             if (HIWORD(wParam) == EN_CHANGE)
1716                 GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username,
1717                                 sizeof(cfg.username)-1);
1718             break;
1719           case IDC3_NOPTY:
1720             if (HIWORD(wParam) == BN_CLICKED ||
1721                 HIWORD(wParam) == BN_DOUBLECLICKED)
1722                 cfg.nopty = IsDlgButtonChecked (hwnd, IDC3_NOPTY);
1723             break;
1724           case IDC3_AGENTFWD:
1725             if (HIWORD(wParam) == BN_CLICKED ||
1726                 HIWORD(wParam) == BN_DOUBLECLICKED)
1727                 cfg.agentfwd = IsDlgButtonChecked (hwnd, IDC3_AGENTFWD);
1728             break;
1729           case IDC3_CIPHER3DES:
1730           case IDC3_CIPHERBLOWF:
1731           case IDC3_CIPHERDES:
1732             if (HIWORD(wParam) == BN_CLICKED ||
1733                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1734                 if (IsDlgButtonChecked (hwnd, IDC3_CIPHER3DES))
1735                     cfg.cipher = CIPHER_3DES;
1736                 else if (IsDlgButtonChecked (hwnd, IDC3_CIPHERBLOWF))
1737                     cfg.cipher = CIPHER_BLOWFISH;
1738                 else if (IsDlgButtonChecked (hwnd, IDC3_CIPHERDES))
1739                     cfg.cipher = CIPHER_DES;
1740             }
1741             break;
1742           case IDC3_SSHPROT1:
1743           case IDC3_SSHPROT2:
1744             if (HIWORD(wParam) == BN_CLICKED ||
1745                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1746                 if (IsDlgButtonChecked (hwnd, IDC3_SSHPROT1))
1747                     cfg.sshprot = 1;
1748                 else if (IsDlgButtonChecked (hwnd, IDC3_SSHPROT2))
1749                     cfg.sshprot = 2;
1750             }
1751             break;
1752           case IDC3_AUTHTIS:
1753             if (HIWORD(wParam) == BN_CLICKED ||
1754                 HIWORD(wParam) == BN_DOUBLECLICKED)
1755                 cfg.try_tis_auth = IsDlgButtonChecked (hwnd, IDC3_AUTHTIS);
1756             break;
1757           case IDC3_PKEDIT:
1758             if (HIWORD(wParam) == EN_CHANGE)
1759                 GetDlgItemText (hwnd, IDC3_PKEDIT, cfg.keyfile,
1760                                 sizeof(cfg.keyfile)-1);
1761             break;
1762           case IDC3_CMDEDIT:
1763             if (HIWORD(wParam) == EN_CHANGE)
1764                 GetDlgItemText (hwnd, IDC3_CMDEDIT, cfg.remote_cmd,
1765                                 sizeof(cfg.remote_cmd)-1);
1766             break;
1767           case IDC3_PKBUTTON:
1768             /*
1769              * FIXME: this crashes. Find out why.
1770              */
1771             memset(&of, 0, sizeof(of));
1772 #ifdef OPENFILENAME_SIZE_VERSION_400
1773             of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1774 #else
1775             of.lStructSize = sizeof(of);
1776 #endif
1777             of.hwndOwner = hwnd;
1778             of.lpstrFilter = "All Files\0*\0\0\0";
1779             of.lpstrCustomFilter = NULL;
1780             of.nFilterIndex = 1;
1781             of.lpstrFile = filename; strcpy(filename, cfg.keyfile);
1782             of.nMaxFile = sizeof(filename);
1783             of.lpstrFileTitle = NULL;
1784             of.lpstrInitialDir = NULL;
1785             of.lpstrTitle = "Select Public Key File";
1786             of.Flags = 0;
1787             if (GetOpenFileName(&of)) {
1788                 strcpy(cfg.keyfile, filename);
1789                 SetDlgItemText (hwnd, IDC3_PKEDIT, cfg.keyfile);
1790             }
1791             break;
1792         }
1793         break;
1794     }
1795     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1796 }
1797
1798 static int CALLBACK SelectionProc (HWND hwnd, UINT msg,
1799                                     WPARAM wParam, LPARAM lParam) {
1800     struct ctlpos cp;
1801     int i;
1802
1803     switch (msg) {
1804       case WM_INITDIALOG:
1805         /* Accelerators used: [aco] stwx */
1806         ctlposinit(&cp, hwnd);
1807         radiobig(&cp, "Action of mouse buttons:", IDC4_MBSTATIC,
1808                  "&Windows (Right pastes, Middle extends)", IDC4_MBWINDOWS,
1809                  "&xterm (Right extends, Middle pastes)", IDC4_MBXTERM,
1810                  NULL);
1811         charclass(&cp, "Character classes:", IDC4_CCSTATIC, IDC4_CCLIST,
1812                   "&Set", IDC4_CCSET, IDC4_CCEDIT,
1813                   "&to class", IDC4_CCSTATIC2);
1814
1815         CheckRadioButton (hwnd, IDC4_MBWINDOWS, IDC4_MBXTERM,
1816                           cfg.mouse_is_xterm ? IDC4_MBXTERM : IDC4_MBWINDOWS);
1817         {
1818             static int tabs[4] = {25, 61, 96, 128};
1819             SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_SETTABSTOPS, 4,
1820                                 (LPARAM) tabs);
1821         }
1822         for (i=0; i<256; i++) {
1823             char str[100];
1824             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1825                     (i>=0x21 && i != 0x7F) ? i : ' ',
1826                     cfg.wordness[i]);
1827             SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_ADDSTRING, 0,
1828                                 (LPARAM) str);
1829         }
1830         break;
1831       case WM_COMMAND:
1832         switch (LOWORD(wParam)) {
1833           case IDC4_MBWINDOWS:
1834           case IDC4_MBXTERM:
1835             cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC4_MBXTERM);
1836             break;
1837           case IDC4_CCSET:
1838             {
1839                 BOOL ok;
1840                 int i;
1841                 int n = GetDlgItemInt (hwnd, IDC4_CCEDIT, &ok, FALSE);
1842
1843                 if (!ok)
1844                     MessageBeep (0);
1845                 else {
1846                     for (i=0; i<256; i++)
1847                         if (SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_GETSEL,
1848                                                 i, 0)) {
1849                             char str[100];
1850                             cfg.wordness[i] = n;
1851                             SendDlgItemMessage (hwnd, IDC4_CCLIST,
1852                                                 LB_DELETESTRING, i, 0);
1853                             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1854                                     (i>=0x21 && i != 0x7F) ? i : ' ',
1855                                     cfg.wordness[i]);
1856                             SendDlgItemMessage (hwnd, IDC4_CCLIST,
1857                                                 LB_INSERTSTRING, i,
1858                                                 (LPARAM)str);
1859                         }
1860                 }
1861             }
1862             break;
1863         }
1864         break;
1865     }
1866     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1867 }
1868
1869 static int CALLBACK ColourProc (HWND hwnd, UINT msg,
1870                                     WPARAM wParam, LPARAM lParam) {
1871     static const char *const colours[] = {
1872         "Default Foreground", "Default Bold Foreground",
1873         "Default Background", "Default Bold Background",
1874         "Cursor Text", "Cursor Colour",
1875         "ANSI Black", "ANSI Black Bold",
1876         "ANSI Red", "ANSI Red Bold",
1877         "ANSI Green", "ANSI Green Bold",
1878         "ANSI Yellow", "ANSI Yellow Bold",
1879         "ANSI Blue", "ANSI Blue Bold",
1880         "ANSI Magenta", "ANSI Magenta Bold",
1881         "ANSI Cyan", "ANSI Cyan Bold",
1882         "ANSI White", "ANSI White Bold"
1883     };
1884     static const int permanent[] = {
1885         TRUE, FALSE, TRUE, FALSE, TRUE, TRUE,
1886         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE,
1887         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE
1888     };
1889     struct ctlpos cp;
1890
1891     switch (msg) {
1892       case WM_INITDIALOG:
1893         /* Accelerators used: [aco] bmlu */
1894         ctlposinit(&cp, hwnd);
1895         checkbox(&cp, "&Bolded text is a different colour", IDC5_BOLDCOLOUR);
1896         checkbox(&cp, "Attempt to use &logical palettes", IDC5_PALETTE);
1897         colouredit(&cp, "Select a colo&ur and click to modify it:",
1898                    IDC5_STATIC, IDC5_LIST,
1899                    "&Modify...", IDC5_CHANGE,
1900                    "Red:", IDC5_RSTATIC, IDC5_RVALUE,
1901                    "Green:", IDC5_GSTATIC, IDC5_GVALUE,
1902                    "Blue:", IDC5_BSTATIC, IDC5_BVALUE, NULL);
1903
1904         CheckDlgButton (hwnd, IDC5_BOLDCOLOUR, cfg.bold_colour);
1905         CheckDlgButton (hwnd, IDC5_PALETTE, cfg.try_palette);
1906         {
1907             int i;
1908             for (i=0; i<22; i++)
1909                 if (cfg.bold_colour || permanent[i])
1910                     SendDlgItemMessage (hwnd, IDC5_LIST, LB_ADDSTRING, 0,
1911                                         (LPARAM) colours[i]);
1912         }
1913         SendDlgItemMessage (hwnd, IDC5_LIST, LB_SETCURSEL, 0, 0);
1914         SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[0][0], FALSE);
1915         SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[0][1], FALSE);
1916         SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[0][2], FALSE);
1917         break;
1918       case WM_COMMAND:
1919         switch (LOWORD(wParam)) {
1920           case IDC5_BOLDCOLOUR:
1921             if (HIWORD(wParam) == BN_CLICKED ||
1922                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1923                 int n, i;
1924                 cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC5_BOLDCOLOUR);
1925                 n = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCOUNT, 0, 0);
1926                 if (cfg.bold_colour && n!=22) {
1927                     for (i=0; i<22; i++)
1928                         if (!permanent[i])
1929                             SendDlgItemMessage (hwnd, IDC5_LIST,
1930                                                 LB_INSERTSTRING, i,
1931                                                 (LPARAM) colours[i]);
1932                 } else if (!cfg.bold_colour && n!=12) {
1933                     for (i=22; i-- ;)
1934                         if (!permanent[i])
1935                             SendDlgItemMessage (hwnd, IDC5_LIST,
1936                                                 LB_DELETESTRING, i, 0);
1937                 }
1938             }
1939             break;
1940           case IDC5_PALETTE:
1941             if (HIWORD(wParam) == BN_CLICKED ||
1942                 HIWORD(wParam) == BN_DOUBLECLICKED)
1943                 cfg.try_palette = IsDlgButtonChecked (hwnd, IDC5_PALETTE);
1944             break;
1945           case IDC5_LIST:
1946             if (HIWORD(wParam) == LBN_DBLCLK ||
1947                 HIWORD(wParam) == LBN_SELCHANGE) {
1948                 int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL,
1949                                             0, 0);
1950                 if (!cfg.bold_colour)
1951                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
1952                 SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], FALSE);
1953                 SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], FALSE);
1954                 SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], FALSE);
1955             }
1956             break;
1957           case IDC5_CHANGE:
1958             if (HIWORD(wParam) == BN_CLICKED ||
1959                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1960                 static CHOOSECOLOR cc;
1961                 static DWORD custom[16] = {0};   /* zero initialisers */
1962                 int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL,
1963                                             0, 0);
1964                 if (!cfg.bold_colour)
1965                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
1966                 cc.lStructSize = sizeof(cc);
1967                 cc.hwndOwner = hwnd;
1968                 cc.hInstance = (HWND)hinst;
1969                 cc.lpCustColors = custom;
1970                 cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1],
1971                                     cfg.colours[i][2]);
1972                 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
1973                 if (ChooseColor(&cc)) {
1974                     cfg.colours[i][0] =
1975                         (unsigned char) (cc.rgbResult & 0xFF);
1976                     cfg.colours[i][1] =
1977                         (unsigned char) (cc.rgbResult >> 8) & 0xFF;
1978                     cfg.colours[i][2] =
1979                         (unsigned char) (cc.rgbResult >> 16) & 0xFF;
1980                     SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0],
1981                                    FALSE);
1982                     SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1],
1983                                    FALSE);
1984                     SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2],
1985                                    FALSE);
1986                 }
1987             }
1988             break;
1989         }
1990         break;
1991     }
1992     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1993 }
1994
1995 static int CALLBACK TranslationProc (HWND hwnd, UINT msg,
1996                                   WPARAM wParam, LPARAM lParam) {
1997     struct ctlpos cp;
1998
1999     switch (msg) {
2000       case WM_INITDIALOG:
2001         /* Accelerators used: [aco] beiknpsx */
2002         ctlposinit(&cp, hwnd);
2003         radiobig(&cp,
2004                  "Handling of VT100 line drawing characters:", IDC2_VTSTATIC,
2005                  "Font has &XWindows encoding", IDC2_VTXWINDOWS,
2006                  "Use font in &both ANSI and OEM modes", IDC2_VTOEMANSI,
2007                  "Use font in O&EM mode only", IDC2_VTOEMONLY,
2008                  "&Poor man's line drawing (""+"", ""-"" and ""|"")",
2009                  IDC2_VTPOORMAN, NULL);
2010         radiobig(&cp,
2011                  "Character set translation:", IDC6_XLATSTATIC,
2012                  "&None", IDC6_NOXLAT,
2013                  "&KOI8 / Win-1251", IDC6_KOI8WIN1251,
2014                  "&ISO-8859-2 / Win-1250", IDC6_88592WIN1250, NULL);
2015         checkbox(&cp, "CAP&S LOCK acts as cyrillic switch", IDC6_CAPSLOCKCYR);
2016
2017         CheckRadioButton (hwnd, IDC6_NOXLAT, IDC6_88592WIN1250,
2018                           cfg.xlat_88592w1250 ? IDC6_88592WIN1250 :
2019                           cfg.xlat_enablekoiwin ? IDC6_KOI8WIN1251 :
2020                           IDC6_NOXLAT);
2021         CheckDlgButton (hwnd, IDC6_CAPSLOCKCYR, cfg.xlat_capslockcyr);
2022         CheckRadioButton (hwnd, IDC2_VTXWINDOWS, IDC2_VTPOORMAN,
2023                           cfg.vtmode == VT_XWINDOWS ? IDC2_VTXWINDOWS :
2024                           cfg.vtmode == VT_OEMANSI ? IDC2_VTOEMANSI :
2025                           cfg.vtmode == VT_OEMONLY ? IDC2_VTOEMONLY :
2026                           IDC2_VTPOORMAN);
2027       case WM_COMMAND:
2028         switch (LOWORD(wParam)) {
2029           case IDC6_NOXLAT:
2030           case IDC6_KOI8WIN1251:
2031           case IDC6_88592WIN1250:
2032             cfg.xlat_enablekoiwin =
2033                 IsDlgButtonChecked (hwnd, IDC6_KOI8WIN1251);
2034             cfg.xlat_88592w1250 =
2035                 IsDlgButtonChecked (hwnd, IDC6_88592WIN1250);
2036             break;
2037           case IDC6_CAPSLOCKCYR:
2038             if (HIWORD(wParam) == BN_CLICKED ||
2039                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2040                 cfg.xlat_capslockcyr =
2041                     IsDlgButtonChecked (hwnd, IDC6_CAPSLOCKCYR);
2042             }
2043             break;
2044           case IDC2_VTXWINDOWS:
2045           case IDC2_VTOEMANSI:
2046           case IDC2_VTOEMONLY:
2047           case IDC2_VTPOORMAN:
2048             cfg.vtmode =
2049                 (IsDlgButtonChecked (hwnd, IDC2_VTXWINDOWS) ? VT_XWINDOWS :
2050                  IsDlgButtonChecked (hwnd, IDC2_VTOEMANSI) ? VT_OEMANSI :
2051                  IsDlgButtonChecked (hwnd, IDC2_VTOEMONLY) ? VT_OEMONLY :
2052                  VT_POORMAN);
2053             break;
2054         }
2055     }
2056     return GeneralPanelProc (hwnd, msg, wParam, lParam);
2057 }
2058
2059 static DLGPROC panelproc[NPANELS] = {
2060     ConnectionProc, KeyboardProc, TerminalProc, WindowProc,
2061     TelnetProc, SshProc, SelectionProc, ColourProc, TranslationProc
2062 };
2063 static char *panelids[NPANELS] = {
2064     MAKEINTRESOURCE(IDD_PANEL0),
2065     MAKEINTRESOURCE(IDD_PANEL1),
2066     MAKEINTRESOURCE(IDD_PANEL2),
2067     MAKEINTRESOURCE(IDD_PANELW),
2068     MAKEINTRESOURCE(IDD_PANEL3),
2069     MAKEINTRESOURCE(IDD_PANEL35),
2070     MAKEINTRESOURCE(IDD_PANEL4),
2071     MAKEINTRESOURCE(IDD_PANEL5),
2072     MAKEINTRESOURCE(IDD_PANEL6)
2073 };
2074
2075 static char *names[NPANELS] = {
2076     "Connection", "Keyboard", "Terminal", "Window", "Telnet",
2077     "SSH", "Selection", "Colours", "Translation"
2078 };
2079
2080 static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8};
2081 static int reconfp[RECONF_NPANELS] = { 1, 2, 3, 6, 7, 8};
2082
2083 static HWND makesubdialog(HWND hwnd, int x, int y, int w, int h, int n) {
2084     RECT r;
2085     HWND ret;
2086     WPARAM font;
2087     r.left = x; r.top = y;
2088     r.right = r.left + w; r.bottom = r.top + h;
2089     MapDialogRect(hwnd, &r);
2090     ret = CreateWindowEx(WS_EX_CONTROLPARENT,
2091                            WC_DIALOG, "",   /* no title */
2092                            WS_CHILD | WS_VISIBLE | DS_SETFONT,
2093                            r.left, r.top,
2094                            r.right-r.left, r.bottom-r.top,
2095                            hwnd, (HMENU)(panelids[n]),
2096                            hinst, NULL);
2097     SetWindowLong (ret, DWL_DLGPROC, (LONG)panelproc[n]);
2098     font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2099     SendMessage (ret, WM_SETFONT, font, MAKELPARAM(0, 0));
2100     SendMessage (ret, WM_INITDIALOG, 0, 0);
2101     return ret;
2102 }
2103
2104 static int GenericMainDlgProc (HWND hwnd, UINT msg,
2105                                WPARAM wParam, LPARAM lParam,
2106                                int npanels, int *panelnums, HWND *page) {
2107     HWND hw, tabctl;
2108
2109     switch (msg) {
2110       case WM_INITDIALOG:
2111         {                              /* centre the window */
2112             RECT rs, rd;
2113
2114             hw = GetDesktopWindow();
2115             if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
2116                 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
2117                             (rs.bottom + rs.top + rd.top - rd.bottom)/2,
2118                             rd.right-rd.left, rd.bottom-rd.top, TRUE);
2119         }
2120         {
2121             RECT r;
2122             r.left = 3; r.right = r.left + 174;
2123             r.top = 3; r.bottom = r.top + 193;
2124             MapDialogRect(hwnd, &r);
2125             tabctl = CreateWindowEx(0, WC_TABCONTROL, "",
2126                                     WS_CHILD | WS_VISIBLE |
2127                                     WS_TABSTOP | TCS_MULTILINE,
2128                                     r.left, r.top,
2129                                     r.right-r.left, r.bottom-r.top,
2130                                     hwnd, (HMENU)IDC_TAB, hinst, NULL);
2131
2132             if (!tabctl) {
2133                 struct ctlpos cp;
2134                 ctlposinit2(&cp, hwnd);
2135                 ersatztab(&cp, "Category:", IDC_TABSTATIC1, IDC_TABLIST,
2136                           IDC_TABSTATIC2);
2137             } else {
2138                 WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2139                 SendMessage(tabctl, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
2140             }
2141         }
2142         *page = NULL;
2143         if (tabctl) {                  /* initialise the tab control */
2144             TC_ITEMHEADER tab;
2145             int i;
2146
2147             for (i=0; i<npanels; i++) {
2148                 tab.mask = TCIF_TEXT;
2149                 tab.pszText = names[panelnums[i]];
2150                 TabCtrl_InsertItem (tabctl, i, &tab);
2151             }
2152         } else {
2153             int i;
2154
2155             for (i=0; i<npanels; i++) {
2156                 SendDlgItemMessage(hwnd, IDC_TABLIST, CB_ADDSTRING,
2157                                    0, (LPARAM)names[panelnums[i]]);
2158             }
2159             SendDlgItemMessage(hwnd, IDC_TABLIST, CB_SETCURSEL, 0, 0);
2160         }
2161         *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[0]);
2162         SetFocus (*page);
2163         return 0;
2164       case WM_NOTIFY:
2165         if (LOWORD(wParam) == IDC_TAB &&
2166             ((LPNMHDR)lParam)->code == TCN_SELCHANGE) {
2167             int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
2168             if (*page)
2169                 DestroyWindow (*page);
2170             *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2171             SetFocus (((LPNMHDR)lParam)->hwndFrom);   /* ensure focus stays */
2172             return 0;
2173         }
2174         break;
2175       case WM_COMMAND:
2176         switch (LOWORD(wParam)) {
2177           case IDC_TABLIST:
2178             if (HIWORD(wParam) == CBN_SELCHANGE) {
2179                 HWND tablist = GetDlgItem (hwnd, IDC_TABLIST);
2180                 int i = SendMessage (tablist, CB_GETCURSEL, 0, 0);
2181                 if (*page)
2182                     DestroyWindow (*page);
2183                 *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2184                 SetFocus(tablist);     /* ensure focus stays */
2185                 return 0;
2186             }
2187             break;
2188           case IDOK:
2189             if (*cfg.host)
2190                 EndDialog (hwnd, 1);
2191             else
2192                 MessageBeep (0);
2193             return 0;
2194           case IDCANCEL:
2195             EndDialog (hwnd, 0);
2196             return 0;
2197         }
2198         return 0;
2199       case WM_CLOSE:
2200         EndDialog (hwnd, 0);
2201         return 0;
2202
2203         /* Grrr Explorer will maximize Dialogs! */
2204       case WM_SIZE:
2205         if (wParam == SIZE_MAXIMIZED)
2206            force_normal(hwnd);
2207         return 0;
2208     }
2209     return 0;
2210 }
2211
2212 static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
2213                                  WPARAM wParam, LPARAM lParam) {
2214 #if 0
2215     HWND hw;
2216     int i;
2217 #endif
2218     static HWND page = NULL;
2219
2220     if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) {
2221 #if 0
2222         /*
2223          * If the Connection panel is active and the Session List
2224          * box is selected, we treat a press of Open to have an
2225          * implicit press of Load preceding it.
2226          */
2227         hw = GetDlgItem (hwnd, IDC_TAB);
2228         i = TabCtrl_GetCurSel(hw);
2229         if (panelproc[mainp[i]] == ConnectionProc &&
2230             page && implicit_load_ok) {
2231             SendMessage (page, WM_COMMAND,
2232                          MAKELONG(IDC0_SESSLOAD, BN_CLICKED), 0);
2233         }
2234 #endif
2235     }
2236     if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) {
2237         EnableWindow(hwnd, 0);
2238         DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2239                   GetParent(hwnd), AboutProc);
2240         EnableWindow(hwnd, 1);
2241         SetActiveWindow(hwnd);
2242     }
2243     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2244                                MAIN_NPANELS, mainp, &page);
2245 }
2246
2247 static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg,
2248                                    WPARAM wParam, LPARAM lParam) {
2249     static HWND page;
2250     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2251                                RECONF_NPANELS, reconfp, &page);
2252 }
2253
2254 void get_sesslist(int allocate) {
2255     static char otherbuf[2048];
2256     static char *buffer;
2257     int buflen, bufsize, i;
2258     char *p, *ret;
2259     void *handle;
2260
2261     if (allocate) {
2262         
2263         if ((handle = enum_settings_start()) == NULL)
2264             return;
2265
2266         buflen = bufsize = 0;
2267         buffer = NULL;
2268         do {
2269             ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf));
2270             if (ret) {
2271                 int len = strlen(otherbuf)+1;
2272                 if (bufsize < buflen+len) {
2273                     bufsize = buflen + len + 2048;
2274                     buffer = srealloc(buffer, bufsize);
2275                 }
2276                 strcpy(buffer+buflen, otherbuf);
2277                 buflen += strlen(buffer+buflen)+1;
2278             }
2279         } while (ret);
2280         enum_settings_finish(handle);
2281         buffer = srealloc(buffer, buflen+1);
2282         buffer[buflen] = '\0';
2283
2284         p = buffer;
2285         nsessions = 1;                 /* "Default Settings" counts as one */
2286         while (*p) {
2287             if (strcmp(p, "Default Settings"))
2288                 nsessions++;
2289             while (*p) p++;
2290             p++;
2291         }
2292
2293         sessions = smalloc(nsessions * sizeof(char *));
2294         sessions[0] = "Default Settings";
2295         p = buffer;
2296         i = 1;
2297         while (*p) {
2298             if (strcmp(p, "Default Settings"))
2299                 sessions[i++] = p;
2300             while (*p) p++;
2301             p++;
2302         }
2303     } else {
2304         sfree (buffer);
2305         sfree (sessions);
2306     }
2307 }
2308
2309 int do_config (void) {
2310     int ret;
2311
2312     get_sesslist(TRUE);
2313     savedsession[0] = '\0';
2314     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
2315     get_sesslist(FALSE);
2316
2317     return ret;
2318 }
2319
2320 int do_reconfig (HWND hwnd) {
2321     Config backup_cfg;
2322     int ret;
2323
2324     backup_cfg = cfg;                  /* structure copy */
2325     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc);
2326     if (!ret)
2327         cfg = backup_cfg;              /* structure copy */
2328     else
2329         force_normal(hwnd);
2330
2331     return ret;
2332 }
2333
2334 void do_defaults (char *session) {
2335     if (session)
2336         load_settings (session, TRUE);
2337     else
2338         load_settings ("Default Settings", FALSE);
2339 }
2340
2341 void logevent (char *string) {
2342     if (nevents >= negsize) {
2343         negsize += 64;
2344         events = srealloc (events, negsize * sizeof(*events));
2345     }
2346     events[nevents] = smalloc(1+strlen(string));
2347     strcpy (events[nevents], string);
2348     nevents++;
2349     if (logbox) {
2350         int count;
2351         SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
2352                             0, (LPARAM)string);
2353         count = SendDlgItemMessage (logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
2354         SendDlgItemMessage (logbox, IDN_LIST, LB_SETTOPINDEX, count-1, 0);
2355     }
2356 }
2357
2358 void showeventlog (HWND hwnd) {
2359     if (!logbox) {
2360         logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX),
2361                                hwnd, LogProc);
2362         ShowWindow (logbox, SW_SHOWNORMAL);
2363     }
2364 }
2365
2366 void showabout (HWND hwnd) {
2367     if (!abtbox) {
2368         abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2369                                hwnd, AboutProc);
2370         ShowWindow (abtbox, SW_SHOWNORMAL);
2371     }
2372 }
2373
2374 void verify_ssh_host_key(char *host, int port, char *keytype,
2375                          char *keystr, char *fingerprint) {
2376     int ret;
2377
2378     static const char absentmsg[] =
2379         "The server's host key is not cached in the registry. You\n"
2380         "have no guarantee that the server is the computer you\n"
2381         "think it is.\n"
2382         "The server's key fingerprint is:\n"
2383         "%s\n"
2384         "If you trust this host, hit Yes to add the key to\n"
2385         "PuTTY's cache and carry on connecting.\n"
2386         "If you do not trust this host, hit No to abandon the\n"
2387         "connection.\n";
2388
2389     static const char wrongmsg[] =
2390         "WARNING - POTENTIAL SECURITY BREACH!\n"
2391         "\n"
2392         "The server's host key does not match the one PuTTY has\n"
2393         "cached in the registry. This means that either the\n"
2394         "server administrator has changed the host key, or you\n"
2395         "have actually connected to another computer pretending\n"
2396         "to be the server.\n"
2397         "The new key fingerprint is:\n"
2398         "%s\n"
2399         "If you were expecting this change and trust the new key,\n"
2400         "hit Yes to update PuTTY's cache and continue connecting.\n"
2401         "If you want to carry on connecting but without updating\n"
2402         "the cache, hit No.\n"
2403         "If you want to abandon the connection completely, hit\n"
2404         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n"
2405         "choice.\n";
2406
2407     static const char mbtitle[] = "PuTTY Security Alert";
2408
2409     
2410     char message[160+                  /* sensible fingerprint max size */
2411                  (sizeof(absentmsg) > sizeof(wrongmsg) ?
2412                   sizeof(absentmsg) : sizeof(wrongmsg))];
2413
2414     /*
2415      * Verify the key against the registry.
2416      */
2417     ret = verify_host_key(host, port, keytype, keystr);
2418
2419     if (ret == 0)                      /* success - key matched OK */
2420         return;
2421     if (ret == 2) {                    /* key was different */
2422         int mbret;
2423         sprintf(message, wrongmsg, fingerprint);
2424         mbret = MessageBox(NULL, message, mbtitle,
2425                            MB_ICONWARNING | MB_YESNOCANCEL);
2426         if (mbret == IDYES)
2427             store_host_key(host, port, keytype, keystr);
2428         if (mbret == IDCANCEL)
2429             exit(0);
2430     }
2431     if (ret == 1) {                    /* key was absent */
2432         int mbret;
2433         sprintf(message, absentmsg, fingerprint);
2434         mbret = MessageBox(NULL, message, mbtitle,
2435                            MB_ICONWARNING | MB_YESNO);
2436         if (mbret == IDNO)
2437             exit(0);
2438         store_host_key(host, port, keytype, keystr);
2439     }
2440 }