]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windlg.c
faa71994fc3427ab9511262df1050d9e4cd8b35b
[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_COMMAND:
335         switch (LOWORD(wParam)) {
336           case IDOK:
337             logbox = NULL;
338             DestroyWindow (hwnd);
339             return 0;
340           case IDN_COPY:
341             if (HIWORD(wParam) == BN_CLICKED ||
342                 HIWORD(wParam) == BN_DOUBLECLICKED) {
343                 int selcount;
344                 int *selitems;
345                 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
346                                               LB_GETSELCOUNT, 0, 0);
347                 selitems = malloc(selcount * sizeof(int));
348                 if (selitems) {
349                     int count = SendDlgItemMessage(hwnd, IDN_LIST,
350                                                    LB_GETSELITEMS,
351                                                    selcount, (LPARAM)selitems);
352                     int i;
353                     int size;
354                     char *clipdata;
355                     static unsigned char sel_nl[] = SEL_NL;
356
357                     if (count == 0) {  /* can't copy zero stuff */
358                         MessageBeep(0);
359                         break;
360                     }
361
362                     size = 0;
363                     for (i = 0; i < count; i++)
364                         size += strlen(events[selitems[i]]) + sizeof(sel_nl);
365
366                     clipdata = malloc(size);
367                     if (clipdata) {
368                         char *p = clipdata;
369                         for (i = 0; i < count; i++) {
370                             char *q = events[selitems[i]];
371                             int qlen = strlen(q);
372                             memcpy(p, q, qlen);
373                             p += qlen;
374                             memcpy(p, sel_nl, sizeof(sel_nl));
375                             p += sizeof(sel_nl);
376                         }
377                         write_clip(clipdata, size, TRUE);
378                         free(clipdata);
379                     }
380                     free(selitems);
381
382                     for (i = 0; i < nevents; i++)
383                         SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
384                                            FALSE, i);
385                 }
386             }
387             return 0;
388         }
389         return 0;
390       case WM_CLOSE:
391         logbox = NULL;
392         DestroyWindow (hwnd);
393         return 0;
394     }
395     return 0;
396 }
397
398 static int CALLBACK LicenceProc (HWND hwnd, UINT msg,
399                                  WPARAM wParam, LPARAM lParam) {
400     switch (msg) {
401       case WM_INITDIALOG:
402         return 1;
403       case WM_COMMAND:
404         switch (LOWORD(wParam)) {
405           case IDOK:
406             EndDialog(hwnd, 1);
407             return 0;
408         }
409         return 0;
410       case WM_CLOSE:
411         EndDialog(hwnd, 1);
412         return 0;
413     }
414     return 0;
415 }
416
417 static int CALLBACK AboutProc (HWND hwnd, UINT msg,
418                                WPARAM wParam, LPARAM lParam) {
419     switch (msg) {
420       case WM_INITDIALOG:
421         SetDlgItemText (hwnd, IDA_VERSION, ver);
422         return 1;
423       case WM_COMMAND:
424         switch (LOWORD(wParam)) {
425           case IDOK:
426             abtbox = NULL;
427             DestroyWindow (hwnd);
428             return 0;
429           case IDA_LICENCE:
430             EnableWindow(hwnd, 0);
431             DialogBox (hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
432                        NULL, LicenceProc);
433             EnableWindow(hwnd, 1);
434             SetActiveWindow(hwnd);
435             return 0;
436         }
437         return 0;
438       case WM_CLOSE:
439         abtbox = NULL;
440         DestroyWindow (hwnd);
441         return 0;
442     }
443     return 0;
444 }
445
446 /* ----------------------------------------------------------------------
447  * Routines to self-manage the controls in a dialog box.
448  */
449
450 #define GAPBETWEEN 3
451 #define GAPWITHIN 1
452 #define DLGWIDTH 168
453 #define STATICHEIGHT 8
454 #define CHECKBOXHEIGHT 8
455 #define RADIOHEIGHT 8
456 #define EDITHEIGHT 12
457 #define COMBOHEIGHT 12
458 #define PUSHBTNHEIGHT 14
459
460 struct ctlpos {
461     HWND hwnd;
462     LONG units;
463     WPARAM font;
464     int ypos, width;
465 };
466
467 /* Used on self-constructed dialogs. */
468 static void ctlposinit(struct ctlpos *cp, HWND hwnd) {
469     RECT r;
470     cp->hwnd = hwnd;
471     cp->units = GetWindowLong(hwnd, GWL_USERDATA);
472     cp->font = GetWindowLong(hwnd, DWL_USER);
473     cp->ypos = GAPBETWEEN;
474     GetClientRect(hwnd, &r);
475     cp->width = (r.right * 4) / (cp->units & 0xFFFF) - 2*GAPBETWEEN;
476 }
477
478 /* Used on kosher dialogs. */
479 static void ctlposinit2(struct ctlpos *cp, HWND hwnd) {
480     RECT r;
481     cp->hwnd = hwnd;
482     r.left = r.top = 0;
483     r.right = 4;
484     r.bottom = 8;
485     MapDialogRect(hwnd, &r);
486     cp->units = (r.bottom << 16) | r.right;
487     cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0);
488     cp->ypos = GAPBETWEEN;
489     GetClientRect(hwnd, &r);
490     cp->width = (r.right * 4) / (cp->units & 0xFFFF) - 2*GAPBETWEEN;
491 }
492
493 static void doctl(struct ctlpos *cp, RECT r,
494                   char *wclass, int wstyle, int exstyle,
495                   char *wtext, int wid) {
496     HWND ctl;
497     /*
498      * Note nonstandard use of RECT. This is deliberate: by
499      * transforming the width and height directly we arrange to
500      * have all supposedly same-sized controls really same-sized.
501      */
502
503     /* MapDialogRect, or its near equivalent. */
504     r.left = (r.left * (cp->units & 0xFFFF)) / 4;
505     r.right = (r.right * (cp->units & 0xFFFF)) / 4;
506     r.top = (r.top * ((cp->units>>16) & 0xFFFF)) / 8;
507     r.bottom = (r.bottom * ((cp->units>>16) & 0xFFFF)) / 8;
508
509     ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
510                          r.left, r.top, r.right, r.bottom,
511                          cp->hwnd, (HMENU)wid, hinst, NULL);
512     SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
513 }
514
515 /*
516  * Some edit boxes. Each one has a static above it. The percentages
517  * of the horizontal space are provided.
518  */
519 static void multiedit(struct ctlpos *cp, ...) {
520     RECT r;
521     va_list ap;
522     int percent, xpos;
523
524     percent = xpos = 0;
525     va_start(ap, cp);
526     while (1) {
527         char *text;
528         int staticid, editid, pcwidth;
529         text = va_arg(ap, char *);
530         if (!text)
531             break;
532         staticid = va_arg(ap, int);
533         editid = va_arg(ap, int);
534         pcwidth = va_arg(ap, int);
535
536         r.left = xpos + GAPBETWEEN;
537         percent += pcwidth;
538         xpos = (cp->width + GAPBETWEEN) * percent / 100;
539         r.right = xpos - r.left;
540
541         r.top = cp->ypos; r.bottom = STATICHEIGHT;
542         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
543               text, staticid);
544         r.top = cp->ypos + 8 + GAPWITHIN; r.bottom = EDITHEIGHT;
545         doctl(cp, r, "EDIT",
546               WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
547               WS_EX_CLIENTEDGE,
548               "", editid);
549     }
550     va_end(ap);
551     cp->ypos += 8+GAPWITHIN+12+GAPBETWEEN;
552 }
553
554 /*
555  * A set of radio buttons on the same line, with a static above
556  * them. `nacross' dictates how many parts the line is divided into
557  * (you might want this not to equal the number of buttons if you
558  * needed to line up some 2s and some 3s to look good in the same
559  * panel).
560  */
561 static void radioline(struct ctlpos *cp,
562                       char *text, int id, int nacross, ...) {
563     RECT r;
564     va_list ap;
565     int group;
566     int i;
567
568     r.left = GAPBETWEEN; r.top = cp->ypos;
569     r.right = cp->width; r.bottom = STATICHEIGHT;
570     cp->ypos += r.bottom + GAPWITHIN;
571     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
572     va_start(ap, nacross);
573     group = WS_GROUP;
574     i = 0;
575     while (1) {
576         char *btext;
577         int bid;
578         btext = va_arg(ap, char *);
579         if (!btext)
580             break;
581         bid = va_arg(ap, int);
582         r.left = GAPBETWEEN + i * (cp->width+GAPBETWEEN)/nacross;
583         r.right = (i+1) * (cp->width+GAPBETWEEN)/nacross - r.left;
584         r.top = cp->ypos; r.bottom = RADIOHEIGHT;
585         doctl(cp, r, "BUTTON",
586               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group,
587               0,
588               btext, bid);
589         group = 0;
590         i++;
591     }
592     va_end(ap);
593     cp->ypos += r.bottom + GAPBETWEEN;
594 }
595
596 /*
597  * A set of radio buttons on multiple lines, with a static above
598  * them.
599  */
600 static void radiobig(struct ctlpos *cp, char *text, int id, ...) {
601     RECT r;
602     va_list ap;
603     int group;
604
605     r.left = GAPBETWEEN; r.top = cp->ypos;
606     r.right = cp->width; r.bottom = STATICHEIGHT;
607     cp->ypos += r.bottom + GAPWITHIN;
608     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
609     va_start(ap, id);
610     group = WS_GROUP;
611     while (1) {
612         char *btext;
613         int bid;
614         btext = va_arg(ap, char *);
615         if (!btext)
616             break;
617         bid = va_arg(ap, int);
618         r.left = GAPBETWEEN; r.top = cp->ypos;
619         r.right = cp->width; r.bottom = STATICHEIGHT;
620         cp->ypos += r.bottom + GAPWITHIN;
621         doctl(cp, r, "BUTTON",
622               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group,
623               0,
624               btext, bid);
625         group = 0;
626     }
627     va_end(ap);
628     cp->ypos += GAPBETWEEN - GAPWITHIN;
629 }
630
631 /*
632  * A single standalone checkbox.
633  */
634 static void checkbox(struct ctlpos *cp, char *text, int id) {
635     RECT r;
636
637     r.left = GAPBETWEEN; r.top = cp->ypos;
638     r.right = cp->width; r.bottom = CHECKBOXHEIGHT;
639     cp->ypos += r.bottom + GAPBETWEEN;
640     doctl(cp, r, "BUTTON",
641           BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
642           text, id);
643 }
644
645 /*
646  * A button on the right hand side, with a static to its left.
647  */
648 static void staticbtn(struct ctlpos *cp, char *stext, int sid,
649                       char *btext, int bid) {
650     const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
651                         PUSHBTNHEIGHT : STATICHEIGHT);
652     RECT r;
653     int lwid, rwid, rpos;
654
655     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
656     lwid = rpos - 2*GAPBETWEEN;
657     rwid = cp->width + GAPBETWEEN - rpos;
658
659     r.left = GAPBETWEEN; r.top = cp->ypos + (height-STATICHEIGHT)/2;
660     r.right = lwid; r.bottom = STATICHEIGHT;
661     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
662
663     r.left = rpos; r.top = cp->ypos + (height-PUSHBTNHEIGHT)/2;
664     r.right = rwid; r.bottom = PUSHBTNHEIGHT;
665     doctl(cp, r, "BUTTON",
666           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
667           0,
668           btext, bid);
669
670     cp->ypos += height + GAPBETWEEN;
671 }
672
673 /*
674  * An edit control on the right hand side, with a static to its left.
675  */
676 static void staticedit(struct ctlpos *cp, char *stext, int sid, int eid) {
677     const int height = (EDITHEIGHT > STATICHEIGHT ?
678                         EDITHEIGHT : STATICHEIGHT);
679     RECT r;
680     int lwid, rwid, rpos;
681
682     rpos = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
683     lwid = rpos - 2*GAPBETWEEN;
684     rwid = cp->width + GAPBETWEEN - rpos;
685
686     r.left = GAPBETWEEN; r.top = cp->ypos + (height-STATICHEIGHT)/2;
687     r.right = lwid; r.bottom = STATICHEIGHT;
688     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
689
690     r.left = rpos; r.top = cp->ypos + (height-EDITHEIGHT)/2;
691     r.right = rwid; r.bottom = EDITHEIGHT;
692     doctl(cp, r, "EDIT",
693           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
694           WS_EX_CLIENTEDGE,
695           "", eid);
696
697     cp->ypos += height + GAPBETWEEN;
698 }
699
700 /*
701  * A tab-control substitute when a real tab control is unavailable.
702  */
703 static void ersatztab(struct ctlpos *cp, char *stext, int sid,
704                       int lid, int s2id) {
705     const int height = (COMBOHEIGHT > STATICHEIGHT ?
706                         COMBOHEIGHT : STATICHEIGHT);
707     RECT r;
708     int bigwid, lwid, rwid, rpos;
709     static const int BIGGAP = 15;
710     static const int MEDGAP = 3;
711
712     bigwid = cp->width + 2*GAPBETWEEN - 2*BIGGAP;
713     cp->ypos += MEDGAP;
714     rpos = BIGGAP + (bigwid + BIGGAP) / 2;
715     lwid = rpos - 2*BIGGAP;
716     rwid = bigwid + BIGGAP - rpos;
717
718     r.left = BIGGAP; r.top = cp->ypos + (height-STATICHEIGHT)/2;
719     r.right = lwid; r.bottom = STATICHEIGHT;
720     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
721
722     r.left = rpos; r.top = cp->ypos + (height-COMBOHEIGHT)/2;
723     r.right = rwid; r.bottom = COMBOHEIGHT*10;
724     doctl(cp, r, "COMBOBOX",
725           WS_CHILD | WS_VISIBLE | WS_TABSTOP |
726           CBS_DROPDOWNLIST | CBS_HASSTRINGS,
727           WS_EX_CLIENTEDGE,
728           "", lid);
729
730     cp->ypos += height + MEDGAP + GAPBETWEEN;
731
732     r.left = GAPBETWEEN; r.top = cp->ypos;
733     r.right = cp->width; r.bottom = 2;
734     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
735           0, "", s2id);
736 }
737
738 /*
739  * A static line, followed by an edit control on the left hand side
740  * and a button on the right.
741  */
742 static void editbutton(struct ctlpos *cp, char *stext, int sid,
743                        int eid, char *btext, int bid) {
744     const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?
745                         EDITHEIGHT : PUSHBTNHEIGHT);
746     RECT r;
747     int lwid, rwid, rpos;
748
749     r.left = GAPBETWEEN; r.top = cp->ypos;
750     r.right = cp->width; r.bottom = STATICHEIGHT;
751     cp->ypos += r.bottom + GAPWITHIN;
752     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
753
754     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
755     lwid = rpos - 2*GAPBETWEEN;
756     rwid = cp->width + GAPBETWEEN - rpos;
757
758     r.left = GAPBETWEEN; r.top = cp->ypos + (height-EDITHEIGHT)/2;
759     r.right = lwid; r.bottom = EDITHEIGHT;
760     doctl(cp, r, "EDIT",
761           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
762           WS_EX_CLIENTEDGE,
763           "", eid);
764
765     r.left = rpos; r.top = cp->ypos + (height-PUSHBTNHEIGHT)/2;
766     r.right = rwid; r.bottom = PUSHBTNHEIGHT;
767     doctl(cp, r, "BUTTON",
768           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
769           0,
770           btext, bid);
771
772     cp->ypos += height + GAPBETWEEN;
773 }
774
775 /*
776  * Special control which was hard to describe generically: the
777  * session-saver assembly. A static; below that an edit box; below
778  * that a list box. To the right of the list box, a column of
779  * buttons.
780  */
781 static void sesssaver(struct ctlpos *cp, char *text,
782                       int staticid, int editid, int listid, ...) {
783     RECT r;
784     va_list ap;
785     int lwid, rwid, rpos;
786     int y;
787     const int LISTDEFHEIGHT = 66;
788
789     rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
790     lwid = rpos - 2*GAPBETWEEN;
791     rwid = cp->width + GAPBETWEEN - rpos;
792
793     /* The static control. */
794     r.left = GAPBETWEEN; r.top = cp->ypos;
795     r.right = lwid; r.bottom = STATICHEIGHT;
796     cp->ypos += r.bottom + GAPWITHIN;
797     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
798
799     /* The edit control. */
800     r.left = GAPBETWEEN; r.top = cp->ypos;
801     r.right = lwid; r.bottom = EDITHEIGHT;
802     cp->ypos += r.bottom + GAPWITHIN;
803     doctl(cp, r, "EDIT",
804           WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
805           WS_EX_CLIENTEDGE,
806           "", editid);
807
808     /*
809      * The buttons (we should hold off on the list box until we
810      * know how big the buttons are).
811      */
812     va_start(ap, listid);
813     y = cp->ypos;
814     while (1) {
815         char *btext = va_arg(ap, char *);
816         int bid;
817         if (!btext) break;
818         bid = va_arg(ap, int);
819         r.left = rpos; r.top = y;
820         r.right = rwid; r.bottom = PUSHBTNHEIGHT;
821         y += r.bottom + GAPWITHIN;
822         doctl(cp, r, "BUTTON",
823               WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
824               0,
825               btext, bid);
826     }
827
828     /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
829     y -= cp->ypos;
830     y -= GAPWITHIN;
831     if (y < LISTDEFHEIGHT) y = LISTDEFHEIGHT;
832     r.left = GAPBETWEEN; r.top = cp->ypos;
833     r.right = lwid; r.bottom = y;
834     cp->ypos += y + GAPBETWEEN;
835     doctl(cp, r, "LISTBOX",
836           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | 
837           LBS_STANDARD | LBS_HASSTRINGS,
838           WS_EX_CLIENTEDGE,
839           "", listid);
840 }
841
842 /*
843  * Another special control: the environment-variable setter. A
844  * static line first; then a pair of edit boxes with associated
845  * statics, and two buttons; then a list box.
846  */
847 static void envsetter(struct ctlpos *cp, char *stext, int sid,
848                       char *e1stext, int e1sid, int e1id,
849                       char *e2stext, int e2sid, int e2id,
850                       int listid,
851                       char *b1text, int b1id, char *b2text, int b2id) {
852     RECT r;
853     const int height = (STATICHEIGHT > EDITHEIGHT && STATICHEIGHT > PUSHBTNHEIGHT ?
854                         STATICHEIGHT :
855                         EDITHEIGHT > PUSHBTNHEIGHT ?
856                         EDITHEIGHT : PUSHBTNHEIGHT);
857     const static int percents[] = { 20, 35, 10, 25 };
858     int i, j, xpos, percent;
859     const int LISTHEIGHT = 42;
860
861     /* The static control. */
862     r.left = GAPBETWEEN; r.top = cp->ypos;
863     r.right = cp->width; r.bottom = STATICHEIGHT;
864     cp->ypos += r.bottom + GAPWITHIN;
865     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
866
867     /* The statics+edits+buttons. */
868     for (j = 0; j < 2; j++) {
869         percent = 10;
870         for (i = 0; i < 4; i++) {
871             xpos = (cp->width + GAPBETWEEN) * percent / 100;
872             r.left = xpos + GAPBETWEEN;
873             percent += percents[i];
874             xpos = (cp->width + GAPBETWEEN) * percent / 100;
875             r.right = xpos - r.left;
876             r.top = cp->ypos;
877             r.bottom = (i==0 ? STATICHEIGHT :
878                         i==1 ? EDITHEIGHT :
879                         PUSHBTNHEIGHT);
880             r.top += (height-r.bottom)/2;
881             if (i==0) {
882                 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
883                       j==0 ? e1stext : e2stext, j==0 ? e1sid : e2sid);
884             } else if (i==1) {
885                 doctl(cp, r, "EDIT",
886                       WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
887                       WS_EX_CLIENTEDGE,
888                       "", j==0 ? e1id : e2id);
889             } else if (i==3) {
890                 doctl(cp, r, "BUTTON",
891                       WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
892                       0,
893                       j==0 ? b1text : b2text, j==0 ? b1id : b2id);
894             }
895         }
896         cp->ypos += height + GAPWITHIN;
897     }
898
899     /* The list box. */
900     r.left = GAPBETWEEN; r.top = cp->ypos;
901     r.right = cp->width; r.bottom = LISTHEIGHT;
902     cp->ypos += r.bottom + GAPBETWEEN;
903     doctl(cp, r, "LISTBOX",
904           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
905           LBS_USETABSTOPS,
906           WS_EX_CLIENTEDGE,
907           "", listid);
908 }
909
910 /*
911  * Yet another special control: the character-class setter. A
912  * static, then a list, then a line containing a
913  * button-and-static-and-edit. 
914  */
915 static void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
916                       char *btext, int bid, int eid, char *s2text, int s2id) {
917     RECT r;
918     const int height = (STATICHEIGHT > EDITHEIGHT && STATICHEIGHT > PUSHBTNHEIGHT ?
919                         STATICHEIGHT :
920                         EDITHEIGHT > PUSHBTNHEIGHT ?
921                         EDITHEIGHT : PUSHBTNHEIGHT);
922     const static int percents[] = { 30, 40, 30 };
923     int i, xpos, percent;
924     const int LISTHEIGHT = 66;
925
926     /* The static control. */
927     r.left = GAPBETWEEN; r.top = cp->ypos;
928     r.right = cp->width; r.bottom = STATICHEIGHT;
929     cp->ypos += r.bottom + GAPWITHIN;
930     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
931
932     /* The list box. */
933     r.left = GAPBETWEEN; r.top = cp->ypos;
934     r.right = cp->width; r.bottom = LISTHEIGHT;
935     cp->ypos += r.bottom + GAPWITHIN;
936     doctl(cp, r, "LISTBOX",
937           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
938           LBS_USETABSTOPS,
939           WS_EX_CLIENTEDGE,
940           "", listid);
941
942     /* The button+static+edit. */
943     percent = xpos = 0;
944     for (i = 0; i < 3; i++) {
945         r.left = xpos + GAPBETWEEN;
946         percent += percents[i];
947         xpos = (cp->width + GAPBETWEEN) * percent / 100;
948         r.right = xpos - r.left;
949         r.top = cp->ypos;
950         r.bottom = (i==0 ? PUSHBTNHEIGHT :
951                     i==1 ? STATICHEIGHT :
952                     EDITHEIGHT);
953         r.top += (height-r.bottom)/2;
954         if (i==0) {
955             doctl(cp, r, "BUTTON",
956                   WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
957                   0, btext, bid);
958         } else if (i==1) {
959             doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER,
960                   0, s2text, s2id);
961         } else if (i==2) {
962             doctl(cp, r, "EDIT",
963                   WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
964                   WS_EX_CLIENTEDGE, "", eid);
965         }
966     }
967     cp->ypos += height + GAPBETWEEN;
968 }
969
970 /*
971  * A special control (horrors!). The colour editor. A static line;
972  * then on the left, a list box, and on the right, a sequence of
973  * two-part statics followed by a button.
974  */
975 static void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
976                        char *btext, int bid, ...) {
977     RECT r;
978     int y;
979     va_list ap;
980     int lwid, rwid, rpos;
981     const int LISTHEIGHT = 66;
982
983     /* The static control. */
984     r.left = GAPBETWEEN; r.top = cp->ypos;
985     r.right = cp->width; r.bottom = STATICHEIGHT;
986     cp->ypos += r.bottom + GAPWITHIN;
987     doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
988     
989     rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3;
990     lwid = rpos - 2*GAPBETWEEN;
991     rwid = cp->width + GAPBETWEEN - rpos;
992
993     /* The list box. */
994     r.left = GAPBETWEEN; r.top = cp->ypos;
995     r.right = lwid; r.bottom = LISTHEIGHT;
996     doctl(cp, r, "LISTBOX",
997           WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
998           LBS_USETABSTOPS,
999           WS_EX_CLIENTEDGE,
1000           "", listid);
1001
1002     /* The statics. */
1003     y = cp->ypos;
1004     va_start(ap, bid);
1005     while (1) {
1006         char *ltext;
1007         int lid, rid;
1008         ltext = va_arg(ap, char *);
1009         if (!ltext) break;
1010         lid = va_arg(ap, int);
1011         rid = va_arg(ap, int);
1012         r.top = y; r.bottom = STATICHEIGHT;
1013         y += r.bottom + GAPWITHIN;
1014         r.left = rpos; r.right = rwid/2;
1015         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid);
1016         r.left = rpos + r.right; r.right = rwid - r.right;
1017         doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "", rid);
1018     }
1019     va_end(ap);
1020
1021     /* The button. */
1022     r.top = y + 2*GAPWITHIN; r.bottom = PUSHBTNHEIGHT;
1023     r.left = rpos; r.right = rwid;
1024     doctl(cp, r, "BUTTON",
1025           WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1026           0, btext, bid);
1027
1028     cp->ypos += LISTHEIGHT + GAPBETWEEN;
1029 }
1030
1031 static int GeneralPanelProc (HWND hwnd, UINT msg,
1032                              WPARAM wParam, LPARAM lParam) {
1033     switch (msg) {
1034       case WM_SETFONT:
1035         {
1036             HFONT hfont = (HFONT)wParam;
1037             HFONT oldfont;
1038             HDC hdc;
1039             TEXTMETRIC tm;
1040             LONG units;
1041
1042             hdc = GetDC(hwnd);
1043             oldfont = SelectObject(hdc, hfont);
1044             GetTextMetrics(hdc, &tm);
1045             units = (tm.tmHeight << 16) | tm.tmAveCharWidth;
1046             SelectObject(hdc, oldfont);
1047             DeleteDC(hdc);
1048             SetWindowLong(hwnd, GWL_USERDATA, units);
1049             SetWindowLong(hwnd, DWL_USER, wParam);
1050         }
1051         return 0;
1052       case WM_INITDIALOG:
1053         SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1054         return 1;
1055       case WM_CLOSE:
1056         DestroyWindow (hwnd);
1057         return 1;
1058     }
1059     return 0;
1060 }
1061
1062 static char savedsession[2048];
1063
1064 static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
1065                                     WPARAM wParam, LPARAM lParam) {
1066     int i;
1067     struct ctlpos cp;
1068     enum { controlstartvalue = 1000,
1069         IDC_HOSTSTATIC,
1070         IDC_HOST,
1071         IDC_PORTSTATIC,
1072         IDC_PORT,
1073         IDC_PROTSTATIC,
1074         IDC_PROTRAW,
1075         IDC_PROTTELNET,
1076         IDC_PROTSSH,
1077         IDC_SESSSTATIC,
1078         IDC_SESSEDIT,
1079         IDC_SESSLIST,
1080         IDC_SESSLOAD,
1081         IDC_SESSSAVE,
1082         IDC_SESSDEL,
1083         IDC_CLOSEEXIT,
1084         IDC_CLOSEWARN
1085     };
1086
1087     switch (msg) {
1088       case WM_INITDIALOG:
1089         /* Accelerators used: [aco] dehlnprstwx */
1090         ctlposinit(&cp, hwnd);
1091         multiedit(&cp,
1092                   "Host &Name", IDC_HOSTSTATIC, IDC_HOST, 75,
1093                   "&Port", IDC_PORTSTATIC, IDC_PORT, 25, NULL);
1094         if (backends[2].backend == NULL) {
1095             /* this is PuTTYtel, so only two protocols available */
1096             radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3,
1097                       "&Raw", IDC_PROTRAW,
1098                       "&Telnet", IDC_PROTTELNET, NULL);
1099         } else {
1100             radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3,
1101                       "&Raw", IDC_PROTRAW,
1102                       "&Telnet", IDC_PROTTELNET,
1103 #ifdef FWHACK
1104                       "SS&H/hack",
1105 #else
1106                       "SS&H",
1107 #endif
1108                       IDC_PROTSSH, NULL);
1109         }
1110         sesssaver(&cp, "Stor&ed Sessions",
1111                   IDC_SESSSTATIC, IDC_SESSEDIT, IDC_SESSLIST,
1112                   "&Load", IDC_SESSLOAD,
1113                   "&Save", IDC_SESSSAVE,
1114                   "&Delete", IDC_SESSDEL, NULL);
1115         checkbox(&cp, "Close Window on E&xit", IDC_CLOSEEXIT);
1116         checkbox(&cp, "&Warn on Close", IDC_CLOSEWARN);
1117
1118         SetDlgItemText (hwnd, IDC_HOST, cfg.host);
1119         SetDlgItemText (hwnd, IDC_SESSEDIT, savedsession);
1120         SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1121         for (i = 0; i < nsessions; i++)
1122             SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1123                                 0, (LPARAM) (sessions[i]));
1124         CheckRadioButton (hwnd, IDC_PROTRAW, IDC_PROTSSH,
1125                           cfg.protocol==PROT_SSH ? IDC_PROTSSH : 
1126                           cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : IDC_PROTRAW );
1127         CheckDlgButton (hwnd, IDC_CLOSEEXIT, cfg.close_on_exit);
1128         CheckDlgButton (hwnd, IDC_CLOSEWARN, cfg.warn_on_close);
1129         break;
1130       case WM_LBUTTONUP:
1131         /*
1132          * Button release should trigger WM_OK if there was a
1133          * previous double click on the session list.
1134          */
1135         ReleaseCapture();
1136         if (readytogo)
1137             SendMessage (GetParent(hwnd), WM_COMMAND, IDOK, 0);
1138         break;
1139       case WM_COMMAND:
1140         switch (LOWORD(wParam)) {
1141           case IDC_PROTTELNET:
1142           case IDC_PROTSSH:
1143           case IDC_PROTRAW:
1144             if (HIWORD(wParam) == BN_CLICKED ||
1145                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1146                 int i = IsDlgButtonChecked (hwnd, IDC_PROTSSH);
1147                 int j = IsDlgButtonChecked (hwnd, IDC_PROTTELNET);
1148                 cfg.protocol = i ? PROT_SSH : j ? PROT_TELNET : PROT_RAW ;
1149                 if ((cfg.protocol == PROT_SSH && cfg.port == 23) ||
1150                     (cfg.protocol == PROT_TELNET && cfg.port == 22)) {
1151                     cfg.port = i ? 22 : 23;
1152                     SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1153                 }
1154             }
1155             break;
1156           case IDC_HOST:
1157             if (HIWORD(wParam) == EN_CHANGE)
1158                 GetDlgItemText (hwnd, IDC_HOST, cfg.host,
1159                                 sizeof(cfg.host)-1);
1160             break;
1161           case IDC_PORT:
1162             if (HIWORD(wParam) == EN_CHANGE)
1163                 MyGetDlgItemInt (hwnd, IDC_PORT, &cfg.port);
1164             break;
1165           case IDC_CLOSEEXIT:
1166             if (HIWORD(wParam) == BN_CLICKED ||
1167                 HIWORD(wParam) == BN_DOUBLECLICKED)
1168                 cfg.close_on_exit = IsDlgButtonChecked (hwnd, IDC_CLOSEEXIT);
1169             break;
1170           case IDC_CLOSEWARN:
1171             if (HIWORD(wParam) == BN_CLICKED ||
1172                 HIWORD(wParam) == BN_DOUBLECLICKED)
1173                 cfg.warn_on_close = IsDlgButtonChecked (hwnd, IDC_CLOSEWARN);
1174             break;
1175           case IDC_SESSEDIT:
1176             if (HIWORD(wParam) == EN_CHANGE) {
1177                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1178                                     (WPARAM) -1, 0);
1179                 GetDlgItemText (hwnd, IDC_SESSEDIT,
1180                                 savedsession, sizeof(savedsession)-1);
1181                 savedsession[sizeof(savedsession)-1] = '\0';
1182             }
1183             break;
1184           case IDC_SESSSAVE:
1185             if (HIWORD(wParam) == BN_CLICKED ||
1186                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1187                 /*
1188                  * Save a session
1189                  */
1190                 char str[2048];
1191                 GetDlgItemText (hwnd, IDC_SESSEDIT, str, sizeof(str)-1);
1192                 if (!*str) {
1193                     int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1194                                                 LB_GETCURSEL, 0, 0);
1195                     if (n == LB_ERR) {
1196                         MessageBeep(0);
1197                         break;
1198                     }
1199                     strcpy (str, sessions[n]);
1200                 }
1201                 save_settings (str, !!strcmp(str, "Default Settings"));
1202                 get_sesslist (FALSE);
1203                 get_sesslist (TRUE);
1204                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_RESETCONTENT,
1205                                     0, 0);
1206                 for (i = 0; i < nsessions; i++)
1207                     SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1208                                         0, (LPARAM) (sessions[i]));
1209                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1210                                     (WPARAM) -1, 0);
1211             }
1212             break;
1213           case IDC_SESSLIST:
1214           case IDC_SESSLOAD:
1215             if (LOWORD(wParam) == IDC_SESSLOAD &&
1216                 HIWORD(wParam) != BN_CLICKED &&
1217                 HIWORD(wParam) != BN_DOUBLECLICKED)
1218                 break;
1219             if (LOWORD(wParam) == IDC_SESSLIST &&
1220                 HIWORD(wParam) != LBN_DBLCLK)
1221                 break;
1222             {
1223                 int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1224                                             LB_GETCURSEL, 0, 0);
1225                 if (n == LB_ERR) {
1226                     MessageBeep(0);
1227                     break;
1228                 }
1229                 load_settings (sessions[n],
1230                                !!strcmp(sessions[n], "Default Settings"));
1231                 SetDlgItemText (hwnd, IDC_HOST, cfg.host);
1232                 SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1233                 CheckRadioButton (hwnd, IDC_PROTRAW, IDC_PROTSSH,
1234                                   (cfg.protocol==PROT_SSH ? IDC_PROTSSH :
1235                                   cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : IDC_PROTRAW));
1236                 CheckDlgButton (hwnd, IDC_CLOSEEXIT, cfg.close_on_exit);
1237                 CheckDlgButton (hwnd, IDC_CLOSEWARN, cfg.warn_on_close);
1238                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1239                                     (WPARAM) -1, 0);
1240             }
1241             if (LOWORD(wParam) == IDC_SESSLIST) {
1242                 /*
1243                  * A double-click on a saved session should
1244                  * actually start the session, not just load it.
1245                  * Unless it's Default Settings or some other
1246                  * host-less set of saved settings.
1247                  */
1248                 if (*cfg.host) {
1249                     readytogo = TRUE;
1250                     SetCapture(hwnd);
1251                 }
1252             }
1253             break;
1254           case IDC_SESSDEL:
1255             if (HIWORD(wParam) == BN_CLICKED ||
1256                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1257                 int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1258                                             LB_GETCURSEL, 0, 0);
1259                 if (n == LB_ERR || n == 0) {
1260                     MessageBeep(0);
1261                     break;
1262                 }
1263                 del_settings(sessions[n]);
1264                 get_sesslist (FALSE);
1265                 get_sesslist (TRUE);
1266                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_RESETCONTENT,
1267                                     0, 0);
1268                 for (i = 0; i < nsessions; i++)
1269                     SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1270                                         0, (LPARAM) (sessions[i]));
1271                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1272                                     (WPARAM) -1, 0);
1273             }
1274         }
1275     }
1276     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1277 }
1278
1279 static int CALLBACK KeyboardProc (HWND hwnd, UINT msg,
1280                                   WPARAM wParam, LPARAM lParam) {
1281     struct ctlpos cp;
1282     enum { controlstartvalue = 1000,
1283         IDC_DELSTATIC,
1284         IDC_DEL008,
1285         IDC_DEL127,
1286         IDC_HOMESTATIC,
1287         IDC_HOMETILDE,
1288         IDC_HOMERXVT,
1289         IDC_FUNCSTATIC,
1290         IDC_FUNCTILDE,
1291         IDC_FUNCLINUX,
1292         IDC_FUNCXTERM,
1293         IDC_KPSTATIC,
1294         IDC_KPNORMAL,
1295         IDC_KPAPPLIC,
1296         IDC_KPNH,
1297         IDC_CURSTATIC,
1298         IDC_CURNORMAL,
1299         IDC_CURAPPLIC,
1300         IDC_ALTF4,
1301         IDC_ALTSPACE,
1302         IDC_LDISCTERM,
1303         IDC_SCROLLKEY
1304     };
1305
1306     switch (msg) {
1307       case WM_INITDIALOG:
1308         /* Accelerators used: [aco] 4?ehiklmnprsuvxy */
1309         ctlposinit(&cp, hwnd);
1310         radioline(&cp, "Action of Backspace:", IDC_DELSTATIC, 2,
1311                   "Control-&H", IDC_DEL008,
1312                   "Control-&? (127)", IDC_DEL127, NULL);
1313         radioline(&cp, "Action of Home and End:", IDC_HOMESTATIC, 2,
1314                   "&Standard", IDC_HOMETILDE,
1315                   "&rxvt", IDC_HOMERXVT, NULL);
1316         radioline(&cp, "Function key and keypad layout:", IDC_FUNCSTATIC, 3,
1317                   "&VT400", IDC_FUNCTILDE,
1318                   "&Linux", IDC_FUNCLINUX,
1319                   "&Xterm R6", IDC_FUNCXTERM, NULL);
1320         radioline(&cp, "Initial state of cursor keys:", IDC_CURSTATIC, 2,
1321                   "&Normal", IDC_CURNORMAL,
1322                   "A&pplication", IDC_CURAPPLIC, NULL);
1323         radioline(&cp, "Initial state of numeric keypad:", IDC_KPSTATIC, 3,
1324                   "Nor&mal", IDC_KPNORMAL,
1325                   "Appl&ication", IDC_KPAPPLIC,
1326                   "N&etHack", IDC_KPNH, NULL);
1327         checkbox(&cp, "ALT-F&4 is special (closes window)", IDC_ALTF4);
1328         checkbox(&cp, "ALT-Space is special (S&ystem menu)", IDC_ALTSPACE);
1329         checkbox(&cp, "&Use local terminal line discipline", IDC_LDISCTERM);
1330         checkbox(&cp, "Reset scrollback on &keypress", IDC_SCROLLKEY);
1331
1332         CheckRadioButton (hwnd, IDC_DEL008, IDC_DEL127,
1333                           cfg.bksp_is_delete ? IDC_DEL127 : IDC_DEL008);
1334         CheckRadioButton (hwnd, IDC_HOMETILDE, IDC_HOMERXVT,
1335                           cfg.rxvt_homeend ? IDC_HOMERXVT : IDC_HOMETILDE);
1336         CheckRadioButton (hwnd, IDC_FUNCTILDE, IDC_FUNCXTERM,
1337                           cfg.funky_type ? 
1338                           (cfg.funky_type==2 ? IDC_FUNCXTERM 
1339                            : IDC_FUNCLINUX )
1340                           : IDC_FUNCTILDE);
1341         CheckRadioButton (hwnd, IDC_CURNORMAL, IDC_CURAPPLIC,
1342                           cfg.app_cursor ? IDC_CURAPPLIC : IDC_CURNORMAL);
1343         CheckRadioButton (hwnd, IDC_KPNORMAL, IDC_KPNH,
1344                           cfg.nethack_keypad ? IDC_KPNH :
1345                           cfg.app_keypad ? IDC_KPAPPLIC : IDC_KPNORMAL);
1346         CheckDlgButton (hwnd, IDC_ALTF4, cfg.alt_f4);
1347         CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space);
1348         CheckDlgButton (hwnd, IDC_LDISCTERM, cfg.ldisc_term);
1349         CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key);
1350         break;
1351       case WM_COMMAND:
1352         if (HIWORD(wParam) == BN_CLICKED ||
1353             HIWORD(wParam) == BN_DOUBLECLICKED)
1354             switch (LOWORD(wParam)) {
1355               case IDC_DEL008:
1356               case IDC_DEL127:
1357                 cfg.bksp_is_delete = IsDlgButtonChecked (hwnd, IDC_DEL127);
1358                 break;
1359               case IDC_HOMETILDE:
1360               case IDC_HOMERXVT:
1361                 cfg.rxvt_homeend = IsDlgButtonChecked (hwnd, IDC_HOMERXVT);
1362                 break;
1363               case IDC_FUNCXTERM:
1364                 cfg.funky_type = 2;
1365                 break;
1366               case IDC_FUNCTILDE:
1367               case IDC_FUNCLINUX:
1368                 cfg.funky_type = IsDlgButtonChecked (hwnd, IDC_FUNCLINUX);
1369                 break;
1370               case IDC_KPNORMAL:
1371               case IDC_KPAPPLIC:
1372                 cfg.app_keypad = IsDlgButtonChecked (hwnd, IDC_KPAPPLIC);
1373                 cfg.nethack_keypad = FALSE;
1374                 break;
1375               case IDC_KPNH:
1376                 cfg.app_keypad = FALSE;
1377                 cfg.nethack_keypad = TRUE;
1378                 break;
1379               case IDC_CURNORMAL:
1380               case IDC_CURAPPLIC:
1381                 cfg.app_cursor = IsDlgButtonChecked (hwnd, IDC_CURAPPLIC);
1382                 break;
1383               case IDC_ALTF4:
1384                 if (HIWORD(wParam) == BN_CLICKED ||
1385                     HIWORD(wParam) == BN_DOUBLECLICKED)
1386                     cfg.alt_f4 = IsDlgButtonChecked (hwnd, IDC_ALTF4);
1387                 break;
1388               case IDC_ALTSPACE:
1389                 if (HIWORD(wParam) == BN_CLICKED ||
1390                     HIWORD(wParam) == BN_DOUBLECLICKED)
1391                     cfg.alt_space = IsDlgButtonChecked (hwnd, IDC_ALTSPACE);
1392                 break;
1393               case IDC_LDISCTERM:
1394                 if (HIWORD(wParam) == BN_CLICKED ||
1395                     HIWORD(wParam) == BN_DOUBLECLICKED)
1396                     cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC_LDISCTERM);
1397                 break;
1398               case IDC_SCROLLKEY:
1399                 if (HIWORD(wParam) == BN_CLICKED ||
1400                     HIWORD(wParam) == BN_DOUBLECLICKED)
1401                     cfg.scroll_on_key = IsDlgButtonChecked (hwnd, IDC_SCROLLKEY);
1402                 break;
1403             }
1404     }
1405     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1406 }
1407
1408 static void fmtfont (char *buf) {
1409     sprintf (buf, "Font: %s, ", cfg.font);
1410     if (cfg.fontisbold)
1411         strcat(buf, "bold, ");
1412     if (cfg.fontheight == 0)
1413         strcat (buf, "default height");
1414     else
1415         sprintf (buf+strlen(buf), "%d-%s",
1416                  (cfg.fontheight < 0 ? -cfg.fontheight : cfg.fontheight),
1417                  (cfg.fontheight < 0 ? "pixel" : "point"));
1418 }
1419
1420 static int CALLBACK TerminalProc (HWND hwnd, UINT msg,
1421                                     WPARAM wParam, LPARAM lParam) {
1422     struct ctlpos cp;
1423     CHOOSEFONT cf;
1424     LOGFONT lf;
1425     char fontstatic[256];
1426     enum { controlstartvalue = 1000,
1427         IDC_WRAPMODE,
1428         IDC_DECOM,
1429         IDC_DIMSTATIC,
1430         IDC_ROWSSTATIC,
1431         IDC_ROWSEDIT,
1432         IDC_COLSSTATIC,
1433         IDC_COLSEDIT,
1434         IDC_SAVESTATIC,
1435         IDC_SAVEEDIT,
1436         IDC_FONTSTATIC,
1437         IDC_CHOOSEFONT,
1438         IDC_LFHASCR,
1439         IDC_BEEP,
1440         IDC_BCE,
1441         IDC_BLINKTEXT
1442     };
1443
1444     switch (msg) {
1445       case WM_INITDIALOG:
1446         /* Accelerators used: [aco] dghlmnprsw */
1447         ctlposinit(&cp, hwnd);
1448         multiedit(&cp,
1449                   "&Rows", IDC_ROWSSTATIC, IDC_ROWSEDIT, 33,
1450                   "Colu&mns", IDC_COLSSTATIC, IDC_COLSEDIT, 33,
1451                   "&Scrollback", IDC_SAVESTATIC, IDC_SAVEEDIT, 33,
1452                   NULL);
1453         staticbtn(&cp, "", IDC_FONTSTATIC, "C&hange...", IDC_CHOOSEFONT);
1454         checkbox(&cp, "Auto &wrap mode initially on", IDC_WRAPMODE);
1455         checkbox(&cp, "&DEC Origin Mode initially on", IDC_DECOM);
1456         checkbox(&cp, "Implicit CR in every &LF", IDC_LFHASCR);
1457         checkbox(&cp, "Bee&p enabled", IDC_BEEP);
1458         checkbox(&cp, "Use Back&ground colour erase", IDC_BCE);
1459         checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
1460
1461         CheckDlgButton (hwnd, IDC_WRAPMODE, cfg.wrap_mode);
1462         CheckDlgButton (hwnd, IDC_DECOM, cfg.dec_om);
1463         CheckDlgButton (hwnd, IDC_LFHASCR, cfg.lfhascr);
1464         SetDlgItemInt (hwnd, IDC_ROWSEDIT, cfg.height, FALSE);
1465         SetDlgItemInt (hwnd, IDC_COLSEDIT, cfg.width, FALSE);
1466         SetDlgItemInt (hwnd, IDC_SAVEEDIT, cfg.savelines, FALSE);
1467         fmtfont (fontstatic);
1468         SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
1469         CheckDlgButton (hwnd, IDC_BEEP, cfg.beep);
1470         CheckDlgButton (hwnd, IDC_BCE, cfg.bce);
1471         CheckDlgButton (hwnd, IDC_BLINKTEXT, cfg.blinktext);
1472         break;
1473       case WM_COMMAND:
1474         switch (LOWORD(wParam)) {
1475           case IDC_WRAPMODE:
1476             if (HIWORD(wParam) == BN_CLICKED ||
1477                 HIWORD(wParam) == BN_DOUBLECLICKED)
1478                 cfg.wrap_mode = IsDlgButtonChecked (hwnd, IDC_WRAPMODE);
1479             break;
1480           case IDC_DECOM:
1481             if (HIWORD(wParam) == BN_CLICKED ||
1482                 HIWORD(wParam) == BN_DOUBLECLICKED)
1483                 cfg.dec_om = IsDlgButtonChecked (hwnd, IDC_DECOM);
1484             break;
1485           case IDC_LFHASCR:
1486             if (HIWORD(wParam) == BN_CLICKED ||
1487                 HIWORD(wParam) == BN_DOUBLECLICKED)
1488                 cfg.lfhascr = IsDlgButtonChecked (hwnd, IDC_LFHASCR);
1489             break;
1490           case IDC_ROWSEDIT:
1491             if (HIWORD(wParam) == EN_CHANGE)
1492                 MyGetDlgItemInt (hwnd, IDC_ROWSEDIT, &cfg.height);
1493             break;
1494           case IDC_COLSEDIT:
1495             if (HIWORD(wParam) == EN_CHANGE)
1496                 MyGetDlgItemInt (hwnd, IDC_COLSEDIT, &cfg.width);
1497             break;
1498           case IDC_SAVEEDIT:
1499             if (HIWORD(wParam) == EN_CHANGE)
1500                 MyGetDlgItemInt (hwnd, IDC_SAVEEDIT, &cfg.savelines);
1501             break;
1502           case IDC_CHOOSEFONT:
1503             lf.lfHeight = cfg.fontheight;
1504             lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
1505             lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
1506             lf.lfWeight = (cfg.fontisbold ? FW_BOLD : 0);
1507             lf.lfCharSet = cfg.fontcharset;
1508             lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
1509             lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1510             lf.lfQuality = DEFAULT_QUALITY;
1511             lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1512             strncpy (lf.lfFaceName, cfg.font, sizeof(lf.lfFaceName)-1);
1513             lf.lfFaceName[sizeof(lf.lfFaceName)-1] = '\0';
1514
1515             cf.lStructSize = sizeof(cf);
1516             cf.hwndOwner = hwnd;
1517             cf.lpLogFont = &lf;
1518             cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
1519                 CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
1520
1521             if (ChooseFont (&cf)) {
1522                 strncpy (cfg.font, lf.lfFaceName, sizeof(cfg.font)-1);
1523                 cfg.font[sizeof(cfg.font)-1] = '\0';
1524                 cfg.fontisbold = (lf.lfWeight == FW_BOLD);
1525                 cfg.fontcharset = lf.lfCharSet;
1526                 cfg.fontheight = lf.lfHeight;
1527                 fmtfont (fontstatic);
1528                 SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
1529             }
1530             break;
1531            case IDC_BEEP:
1532              if (HIWORD(wParam) == BN_CLICKED ||
1533                  HIWORD(wParam) == BN_DOUBLECLICKED)
1534                  cfg.beep = IsDlgButtonChecked (hwnd, IDC_BEEP);
1535              break;
1536            case IDC_BLINKTEXT:
1537              if (HIWORD(wParam) == BN_CLICKED ||
1538                  HIWORD(wParam) == BN_DOUBLECLICKED)
1539                  cfg.blinktext = IsDlgButtonChecked (hwnd, IDC_BLINKTEXT);
1540              break;
1541            case IDC_BCE:
1542              if (HIWORD(wParam) == BN_CLICKED ||
1543                  HIWORD(wParam) == BN_DOUBLECLICKED)
1544                  cfg.bce = IsDlgButtonChecked (hwnd, IDC_BCE);
1545              break;
1546         }
1547         break;
1548     }
1549     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1550 }
1551
1552 static int CALLBACK WindowProc (HWND hwnd, UINT msg,
1553                                     WPARAM wParam, LPARAM lParam) {
1554     struct ctlpos cp;
1555     enum { controlstartvalue = 1000,
1556          IDC_WINNAME,
1557          IDC_BLINKCUR,
1558          IDC_SCROLLBAR,
1559          IDC_LOCKSIZE,
1560          IDC_WINTITLE,
1561          IDC_WINEDIT
1562     };
1563
1564     switch (msg) {
1565       case WM_INITDIALOG:
1566         /* Accelerators used: [aco] bikty */
1567         ctlposinit(&cp, hwnd);
1568         multiedit(&cp,
1569                   "Initial window &title:", IDC_WINTITLE, IDC_WINEDIT, 100,
1570                   NULL);
1571         checkbox(&cp, "Avoid ever using &icon title", IDC_WINNAME);
1572         checkbox(&cp, "&Blinking cursor", IDC_BLINKCUR);
1573         checkbox(&cp, "Displa&y scrollbar", IDC_SCROLLBAR);
1574         checkbox(&cp, "Loc&k Window size", IDC_LOCKSIZE);
1575
1576         SetDlgItemText (hwnd, IDC_WINEDIT, cfg.wintitle);
1577         CheckDlgButton (hwnd, IDC_WINNAME, cfg.win_name_always);
1578         CheckDlgButton (hwnd, IDC_BLINKCUR, cfg.blink_cur);
1579         CheckDlgButton (hwnd, IDC_SCROLLBAR, cfg.scrollbar);
1580         CheckDlgButton (hwnd, IDC_LOCKSIZE, cfg.locksize);
1581         break;
1582       case WM_COMMAND:
1583         switch (LOWORD(wParam)) {
1584           case IDC_WINNAME:
1585             if (HIWORD(wParam) == BN_CLICKED ||
1586                 HIWORD(wParam) == BN_DOUBLECLICKED)
1587                 cfg.win_name_always = IsDlgButtonChecked (hwnd, IDC_WINNAME);
1588             break;
1589           case IDC_BLINKCUR:
1590             if (HIWORD(wParam) == BN_CLICKED ||
1591                 HIWORD(wParam) == BN_DOUBLECLICKED)
1592                 cfg.blink_cur = IsDlgButtonChecked (hwnd, IDC_BLINKCUR);
1593             break;
1594           case IDC_SCROLLBAR:
1595             if (HIWORD(wParam) == BN_CLICKED ||
1596                 HIWORD(wParam) == BN_DOUBLECLICKED)
1597                 cfg.scrollbar = IsDlgButtonChecked (hwnd, IDC_SCROLLBAR);
1598             break;
1599           case IDC_LOCKSIZE:
1600              if (HIWORD(wParam) == BN_CLICKED ||
1601                  HIWORD(wParam) == BN_DOUBLECLICKED)
1602                 cfg.locksize = IsDlgButtonChecked (hwnd, IDC_LOCKSIZE);
1603             break;
1604           case IDC_WINEDIT:
1605             if (HIWORD(wParam) == EN_CHANGE)
1606                 GetDlgItemText (hwnd, IDC_WINEDIT, cfg.wintitle,
1607                                 sizeof(cfg.wintitle)-1);
1608             break;
1609         }
1610         break;
1611     }
1612     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1613 }
1614
1615 static int CALLBACK TelnetProc (HWND hwnd, UINT msg,
1616                                     WPARAM wParam, LPARAM lParam) {
1617     int i;
1618     struct ctlpos cp;
1619     enum { controlstartvalue = 1000,
1620          IDC_TTSTATIC,
1621          IDC_TTEDIT,
1622          IDC_TSSTATIC,
1623          IDC_TSEDIT,
1624          IDC_LOGSTATIC,
1625          IDC_LOGEDIT,
1626          IDC_ENVSTATIC,
1627          IDC_VARSTATIC,
1628          IDC_VAREDIT,
1629          IDC_VALSTATIC,
1630          IDC_VALEDIT,
1631          IDC_ENVLIST,
1632          IDC_ENVADD,
1633          IDC_ENVREMOVE,
1634          IDC_EMSTATIC,
1635          IDC_EMBSD,
1636          IDC_EMRFC
1637     };
1638
1639     switch (msg) {
1640       case WM_INITDIALOG:
1641         /* Accelerators used: [aco] bdflrstuv */
1642         ctlposinit(&cp, hwnd);
1643         staticedit(&cp, "Terminal-&type string", IDC_TTSTATIC, IDC_TTEDIT);
1644         staticedit(&cp, "Terminal-&speed string", IDC_TSSTATIC, IDC_TSEDIT);
1645         staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC, IDC_LOGEDIT);
1646         envsetter(&cp, "Environment variables:", IDC_ENVSTATIC,
1647                   "&Variable", IDC_VARSTATIC, IDC_VAREDIT,
1648                   "Va&lue", IDC_VALSTATIC, IDC_VALEDIT,
1649                   IDC_ENVLIST,
1650                   "A&dd", IDC_ENVADD, "&Remove", IDC_ENVREMOVE);
1651         radioline(&cp, "Handling of OLD_ENVIRON ambiguity:", IDC_EMSTATIC, 2,
1652                   "&BSD (commonplace)", IDC_EMBSD,
1653                   "R&FC 1408 (unusual)", IDC_EMRFC, NULL);
1654
1655         SetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype);
1656         SetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed);
1657         SetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username);
1658         {
1659           char *p = cfg.environmt;
1660             while (*p) {
1661                 SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_ADDSTRING, 0,
1662                                     (LPARAM) p);
1663                 p += strlen(p)+1;
1664             }
1665         }
1666         CheckRadioButton (hwnd, IDC_EMBSD, IDC_EMRFC,
1667                           cfg.rfc_environ ? IDC_EMRFC : IDC_EMBSD);
1668         break;
1669       case WM_COMMAND:
1670         switch (LOWORD(wParam)) {
1671           case IDC_TTEDIT:
1672             if (HIWORD(wParam) == EN_CHANGE)
1673             GetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype,
1674                             sizeof(cfg.termtype)-1);
1675             break;
1676           case IDC_TSEDIT:
1677             if (HIWORD(wParam) == EN_CHANGE)
1678                 GetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed,
1679                                 sizeof(cfg.termspeed)-1);
1680             break;
1681           case IDC_LOGEDIT:
1682             if (HIWORD(wParam) == EN_CHANGE)
1683                 GetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username,
1684                                 sizeof(cfg.username)-1);
1685             break;
1686           case IDC_EMBSD:
1687           case IDC_EMRFC:
1688             cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC_EMRFC);
1689             break;
1690           case IDC_ENVADD:
1691             if (HIWORD(wParam) == BN_CLICKED ||
1692                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1693               char str[sizeof(cfg.environmt)];
1694                 char *p;
1695                 GetDlgItemText (hwnd, IDC_VAREDIT, str, sizeof(str)-1);
1696                 if (!*str) {
1697                     MessageBeep(0);
1698                     break;
1699                 }
1700                 p = str + strlen(str);
1701                 *p++ = '\t';
1702                 GetDlgItemText (hwnd, IDC_VALEDIT, p, sizeof(str)-1-(p-str));
1703                 if (!*p) {
1704                     MessageBeep(0);
1705                     break;
1706                 }
1707               p = cfg.environmt;
1708                 while (*p) {
1709                     while (*p) p++;
1710                     p++;
1711                 }
1712               if ((p-cfg.environmt) + strlen(str) + 2 < sizeof(cfg.environmt)) {
1713                     strcpy (p, str);
1714                     p[strlen(str)+1] = '\0';
1715                     SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_ADDSTRING,
1716                                         0, (LPARAM)str);
1717                     SetDlgItemText (hwnd, IDC_VAREDIT, "");
1718                     SetDlgItemText (hwnd, IDC_VALEDIT, "");
1719                 } else {
1720                     MessageBox(hwnd, "Environment too big", "PuTTY Error",
1721                                MB_OK | MB_ICONERROR);
1722                 }
1723             }
1724             break;
1725           case IDC_ENVREMOVE:
1726             if (HIWORD(wParam) != BN_CLICKED &&
1727                 HIWORD(wParam) != BN_DOUBLECLICKED)
1728                 break;
1729             i = SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_GETCURSEL, 0, 0);
1730             if (i == LB_ERR)
1731                 MessageBeep (0);
1732             else {
1733                 char *p, *q;
1734
1735                 SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_DELETESTRING,
1736                                     i, 0);
1737               p = cfg.environmt;
1738                 while (i > 0) {
1739                     if (!*p)
1740                         goto disaster;
1741                     while (*p) p++;
1742                     p++;
1743                     i--;
1744                 }
1745                 q = p;
1746                 if (!*p)
1747                     goto disaster;
1748                 while (*p) p++;
1749                 p++;
1750                 while (*p) {
1751                     while (*p)
1752                         *q++ = *p++;
1753                     *q++ = *p++;
1754                 }
1755                 *q = '\0';
1756                 disaster:;
1757             }
1758             break;
1759         }
1760         break;
1761     }
1762     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1763 }
1764
1765 static int CALLBACK SshProc (HWND hwnd, UINT msg,
1766                              WPARAM wParam, LPARAM lParam) {
1767     struct ctlpos cp;
1768     OPENFILENAME of;
1769     char filename[sizeof(cfg.keyfile)];
1770     enum { controlstartvalue = 1000,
1771          IDC_TTSTATIC,
1772          IDC_TTEDIT,
1773          IDC_LOGSTATIC,
1774          IDC_LOGEDIT,
1775          IDC_NOPTY,
1776          IDC_CIPHERSTATIC,
1777          IDC_CIPHER3DES,
1778          IDC_CIPHERBLOWF,
1779          IDC_CIPHERDES,
1780          IDC_AUTHTIS,
1781          IDC_PKSTATIC,
1782          IDC_PKEDIT,
1783          IDC_PKBUTTON,
1784          IDC_SSHPROTSTATIC,
1785          IDC_SSHPROT1,
1786          IDC_SSHPROT2,
1787          IDC_AGENTFWD,
1788          IDC_CMDSTATIC,
1789          IDC_CMDEDIT
1790     };
1791
1792     switch (msg) {
1793       case WM_INITDIALOG:
1794         /* Accelerators used: [aco] 123abdkmprtuw */
1795         ctlposinit(&cp, hwnd);
1796         staticedit(&cp, "Terminal-&type string", IDC_TTSTATIC, IDC_TTEDIT);
1797         staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC, IDC_LOGEDIT);
1798         multiedit(&cp,
1799                   "&Remote command:", IDC_CMDSTATIC, IDC_CMDEDIT, 100,
1800                   NULL);
1801         checkbox(&cp, "Don't allocate a &pseudo-terminal", IDC_NOPTY);
1802         checkbox(&cp, "Atte&mpt TIS or CryptoCard authentication",
1803                  IDC_AUTHTIS);
1804         checkbox(&cp, "Allow &agent forwarding", IDC_AGENTFWD);
1805         editbutton(&cp, "Private &key file for authentication:",
1806                     IDC_PKSTATIC, IDC_PKEDIT, "Bro&wse...", IDC_PKBUTTON);
1807         radioline(&cp, "Preferred SSH protocol version:",
1808                   IDC_SSHPROTSTATIC, 2,
1809                   "&1", IDC_SSHPROT1, "&2", IDC_SSHPROT2, NULL);
1810         radioline(&cp, "Preferred encryption algorithm:", IDC_CIPHERSTATIC, 3,
1811                   "&3DES", IDC_CIPHER3DES,
1812                   "&Blowfish", IDC_CIPHERBLOWF,
1813                   "&DES", IDC_CIPHERDES, NULL);    
1814
1815         SetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype);
1816         SetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username);
1817         CheckDlgButton (hwnd, IDC_NOPTY, cfg.nopty);
1818         CheckDlgButton (hwnd, IDC_AGENTFWD, cfg.agentfwd);
1819         CheckRadioButton (hwnd, IDC_CIPHER3DES, IDC_CIPHERDES,
1820                           cfg.cipher == CIPHER_BLOWFISH ? IDC_CIPHERBLOWF :
1821                           cfg.cipher == CIPHER_DES ? IDC_CIPHERDES :
1822                           IDC_CIPHER3DES);
1823         CheckRadioButton (hwnd, IDC_SSHPROT1, IDC_SSHPROT2,
1824                           cfg.sshprot == 1 ? IDC_SSHPROT1 : IDC_SSHPROT2);
1825         CheckDlgButton (hwnd, IDC_AUTHTIS, cfg.try_tis_auth);
1826         SetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile);
1827         SetDlgItemText (hwnd, IDC_CMDEDIT, cfg.remote_cmd);
1828         break;
1829       case WM_COMMAND:
1830         switch (LOWORD(wParam)) {
1831           case IDC_TTEDIT:
1832             if (HIWORD(wParam) == EN_CHANGE)
1833             GetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype,
1834                             sizeof(cfg.termtype)-1);
1835             break;
1836           case IDC_LOGEDIT:
1837             if (HIWORD(wParam) == EN_CHANGE)
1838                 GetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username,
1839                                 sizeof(cfg.username)-1);
1840             break;
1841           case IDC_NOPTY:
1842             if (HIWORD(wParam) == BN_CLICKED ||
1843                 HIWORD(wParam) == BN_DOUBLECLICKED)
1844                 cfg.nopty = IsDlgButtonChecked (hwnd, IDC_NOPTY);
1845             break;
1846           case IDC_AGENTFWD:
1847             if (HIWORD(wParam) == BN_CLICKED ||
1848                 HIWORD(wParam) == BN_DOUBLECLICKED)
1849                 cfg.agentfwd = IsDlgButtonChecked (hwnd, IDC_AGENTFWD);
1850             break;
1851           case IDC_CIPHER3DES:
1852           case IDC_CIPHERBLOWF:
1853           case IDC_CIPHERDES:
1854             if (HIWORD(wParam) == BN_CLICKED ||
1855                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1856                 if (IsDlgButtonChecked (hwnd, IDC_CIPHER3DES))
1857                     cfg.cipher = CIPHER_3DES;
1858                 else if (IsDlgButtonChecked (hwnd, IDC_CIPHERBLOWF))
1859                     cfg.cipher = CIPHER_BLOWFISH;
1860                 else if (IsDlgButtonChecked (hwnd, IDC_CIPHERDES))
1861                     cfg.cipher = CIPHER_DES;
1862             }
1863             break;
1864           case IDC_SSHPROT1:
1865           case IDC_SSHPROT2:
1866             if (HIWORD(wParam) == BN_CLICKED ||
1867                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1868                 if (IsDlgButtonChecked (hwnd, IDC_SSHPROT1))
1869                     cfg.sshprot = 1;
1870                 else if (IsDlgButtonChecked (hwnd, IDC_SSHPROT2))
1871                     cfg.sshprot = 2;
1872             }
1873             break;
1874           case IDC_AUTHTIS:
1875             if (HIWORD(wParam) == BN_CLICKED ||
1876                 HIWORD(wParam) == BN_DOUBLECLICKED)
1877                 cfg.try_tis_auth = IsDlgButtonChecked (hwnd, IDC_AUTHTIS);
1878             break;
1879           case IDC_PKEDIT:
1880             if (HIWORD(wParam) == EN_CHANGE)
1881                 GetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile,
1882                                 sizeof(cfg.keyfile)-1);
1883             break;
1884           case IDC_CMDEDIT:
1885             if (HIWORD(wParam) == EN_CHANGE)
1886                 GetDlgItemText (hwnd, IDC_CMDEDIT, cfg.remote_cmd,
1887                                 sizeof(cfg.remote_cmd)-1);
1888             break;
1889           case IDC_PKBUTTON:
1890             /*
1891              * FIXME: this crashes. Find out why.
1892              */
1893             memset(&of, 0, sizeof(of));
1894 #ifdef OPENFILENAME_SIZE_VERSION_400
1895             of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1896 #else
1897             of.lStructSize = sizeof(of);
1898 #endif
1899             of.hwndOwner = hwnd;
1900             of.lpstrFilter = "All Files\0*\0\0\0";
1901             of.lpstrCustomFilter = NULL;
1902             of.nFilterIndex = 1;
1903             of.lpstrFile = filename; strcpy(filename, cfg.keyfile);
1904             of.nMaxFile = sizeof(filename);
1905             of.lpstrFileTitle = NULL;
1906             of.lpstrInitialDir = NULL;
1907             of.lpstrTitle = "Select Public Key File";
1908             of.Flags = 0;
1909             if (GetOpenFileName(&of)) {
1910                 strcpy(cfg.keyfile, filename);
1911                 SetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile);
1912             }
1913             break;
1914         }
1915         break;
1916     }
1917     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1918 }
1919
1920 static int CALLBACK SelectionProc (HWND hwnd, UINT msg,
1921                                     WPARAM wParam, LPARAM lParam) {
1922     struct ctlpos cp;
1923     int i;
1924     enum { controlstartvalue = 1000,
1925          IDC_MBSTATIC,
1926          IDC_MBWINDOWS,
1927          IDC_MBXTERM,
1928          IDC_CCSTATIC,
1929          IDC_CCLIST,
1930          IDC_CCSET,
1931          IDC_CCSTATIC2,
1932          IDC_CCEDIT
1933     };
1934
1935     switch (msg) {
1936       case WM_INITDIALOG:
1937         /* Accelerators used: [aco] stwx */
1938         ctlposinit(&cp, hwnd);
1939         radiobig(&cp, "Action of mouse buttons:", IDC_MBSTATIC,
1940                  "&Windows (Right pastes, Middle extends)", IDC_MBWINDOWS,
1941                  "&xterm (Right extends, Middle pastes)", IDC_MBXTERM,
1942                  NULL);
1943         charclass(&cp, "Character classes:", IDC_CCSTATIC, IDC_CCLIST,
1944                   "&Set", IDC_CCSET, IDC_CCEDIT,
1945                   "&to class", IDC_CCSTATIC2);
1946
1947         CheckRadioButton (hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
1948                           cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS);
1949         {
1950             static int tabs[4] = {25, 61, 96, 128};
1951             SendDlgItemMessage (hwnd, IDC_CCLIST, LB_SETTABSTOPS, 4,
1952                                 (LPARAM) tabs);
1953         }
1954         for (i=0; i<256; i++) {
1955             char str[100];
1956             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1957                     (i>=0x21 && i != 0x7F) ? i : ' ',
1958                     cfg.wordness[i]);
1959             SendDlgItemMessage (hwnd, IDC_CCLIST, LB_ADDSTRING, 0,
1960                                 (LPARAM) str);
1961         }
1962         break;
1963       case WM_COMMAND:
1964         switch (LOWORD(wParam)) {
1965           case IDC_MBWINDOWS:
1966           case IDC_MBXTERM:
1967             cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC_MBXTERM);
1968             break;
1969           case IDC_CCSET:
1970             {
1971                 BOOL ok;
1972                 int i;
1973                 int n = GetDlgItemInt (hwnd, IDC_CCEDIT, &ok, FALSE);
1974
1975                 if (!ok)
1976                     MessageBeep (0);
1977                 else {
1978                     for (i=0; i<256; i++)
1979                         if (SendDlgItemMessage (hwnd, IDC_CCLIST, LB_GETSEL,
1980                                                 i, 0)) {
1981                             char str[100];
1982                             cfg.wordness[i] = n;
1983                             SendDlgItemMessage (hwnd, IDC_CCLIST,
1984                                                 LB_DELETESTRING, i, 0);
1985                             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1986                                     (i>=0x21 && i != 0x7F) ? i : ' ',
1987                                     cfg.wordness[i]);
1988                             SendDlgItemMessage (hwnd, IDC_CCLIST,
1989                                                 LB_INSERTSTRING, i,
1990                                                 (LPARAM)str);
1991                         }
1992                 }
1993             }
1994             break;
1995         }
1996         break;
1997     }
1998     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1999 }
2000
2001 static int CALLBACK ColourProc (HWND hwnd, UINT msg,
2002                                     WPARAM wParam, LPARAM lParam) {
2003     static const char *const colours[] = {
2004         "Default Foreground", "Default Bold Foreground",
2005         "Default Background", "Default Bold Background",
2006         "Cursor Text", "Cursor Colour",
2007         "ANSI Black", "ANSI Black Bold",
2008         "ANSI Red", "ANSI Red Bold",
2009         "ANSI Green", "ANSI Green Bold",
2010         "ANSI Yellow", "ANSI Yellow Bold",
2011         "ANSI Blue", "ANSI Blue Bold",
2012         "ANSI Magenta", "ANSI Magenta Bold",
2013         "ANSI Cyan", "ANSI Cyan Bold",
2014         "ANSI White", "ANSI White Bold"
2015     };
2016     static const int permanent[] = {
2017         TRUE, FALSE, TRUE, FALSE, TRUE, TRUE,
2018         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE,
2019         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE
2020     };
2021     struct ctlpos cp;
2022     enum { controlstartvalue = 1000,
2023          IDC_BOLDCOLOUR,
2024          IDC_PALETTE,
2025          IDC_STATIC,
2026          IDC_LIST,
2027          IDC_RSTATIC,
2028          IDC_GSTATIC,
2029          IDC_BSTATIC,
2030          IDC_RVALUE,
2031          IDC_GVALUE,
2032          IDC_BVALUE,
2033          IDC_CHANGE
2034     };
2035
2036     switch (msg) {
2037       case WM_INITDIALOG:
2038         /* Accelerators used: [aco] bmlu */
2039         ctlposinit(&cp, hwnd);
2040         checkbox(&cp, "&Bolded text is a different colour", IDC_BOLDCOLOUR);
2041         checkbox(&cp, "Attempt to use &logical palettes", IDC_PALETTE);
2042         colouredit(&cp, "Select a colo&ur and click to modify it:",
2043                    IDC_STATIC, IDC_LIST,
2044                    "&Modify...", IDC_CHANGE,
2045                    "Red:", IDC_RSTATIC, IDC_RVALUE,
2046                    "Green:", IDC_GSTATIC, IDC_GVALUE,
2047                    "Blue:", IDC_BSTATIC, IDC_BVALUE, NULL);
2048
2049         CheckDlgButton (hwnd, IDC_BOLDCOLOUR, cfg.bold_colour);
2050         CheckDlgButton (hwnd, IDC_PALETTE, cfg.try_palette);
2051         {
2052             int i;
2053             for (i=0; i<22; i++)
2054                 if (cfg.bold_colour || permanent[i])
2055                     SendDlgItemMessage (hwnd, IDC_LIST, LB_ADDSTRING, 0,
2056                                         (LPARAM) colours[i]);
2057         }
2058         SendDlgItemMessage (hwnd, IDC_LIST, LB_SETCURSEL, 0, 0);
2059         SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[0][0], FALSE);
2060         SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[0][1], FALSE);
2061         SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[0][2], FALSE);
2062         break;
2063       case WM_COMMAND:
2064         switch (LOWORD(wParam)) {
2065           case IDC_BOLDCOLOUR:
2066             if (HIWORD(wParam) == BN_CLICKED ||
2067                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2068                 int n, i;
2069                 cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC_BOLDCOLOUR);
2070                 n = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCOUNT, 0, 0);
2071                 if (cfg.bold_colour && n!=22) {
2072                     for (i=0; i<22; i++)
2073                         if (!permanent[i])
2074                             SendDlgItemMessage (hwnd, IDC_LIST,
2075                                                 LB_INSERTSTRING, i,
2076                                                 (LPARAM) colours[i]);
2077                 } else if (!cfg.bold_colour && n!=12) {
2078                     for (i=22; i-- ;)
2079                         if (!permanent[i])
2080                             SendDlgItemMessage (hwnd, IDC_LIST,
2081                                                 LB_DELETESTRING, i, 0);
2082                 }
2083             }
2084             break;
2085           case IDC_PALETTE:
2086             if (HIWORD(wParam) == BN_CLICKED ||
2087                 HIWORD(wParam) == BN_DOUBLECLICKED)
2088                 cfg.try_palette = IsDlgButtonChecked (hwnd, IDC_PALETTE);
2089             break;
2090           case IDC_LIST:
2091             if (HIWORD(wParam) == LBN_DBLCLK ||
2092                 HIWORD(wParam) == LBN_SELCHANGE) {
2093                 int i = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCURSEL,
2094                                             0, 0);
2095                 if (!cfg.bold_colour)
2096                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
2097                 SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[i][0], FALSE);
2098                 SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[i][1], FALSE);
2099                 SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[i][2], FALSE);
2100             }
2101             break;
2102           case IDC_CHANGE:
2103             if (HIWORD(wParam) == BN_CLICKED ||
2104                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2105                 static CHOOSECOLOR cc;
2106                 static DWORD custom[16] = {0};   /* zero initialisers */
2107                 int i = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCURSEL,
2108                                             0, 0);
2109                 if (!cfg.bold_colour)
2110                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
2111                 cc.lStructSize = sizeof(cc);
2112                 cc.hwndOwner = hwnd;
2113                 cc.hInstance = (HWND)hinst;
2114                 cc.lpCustColors = custom;
2115                 cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1],
2116                                     cfg.colours[i][2]);
2117                 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
2118                 if (ChooseColor(&cc)) {
2119                     cfg.colours[i][0] =
2120                         (unsigned char) (cc.rgbResult & 0xFF);
2121                     cfg.colours[i][1] =
2122                         (unsigned char) (cc.rgbResult >> 8) & 0xFF;
2123                     cfg.colours[i][2] =
2124                         (unsigned char) (cc.rgbResult >> 16) & 0xFF;
2125                     SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[i][0],
2126                                    FALSE);
2127                     SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[i][1],
2128                                    FALSE);
2129                     SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[i][2],
2130                                    FALSE);
2131                 }
2132             }
2133             break;
2134         }
2135         break;
2136     }
2137     return GeneralPanelProc (hwnd, msg, wParam, lParam);
2138 }
2139
2140 static int CALLBACK TranslationProc (HWND hwnd, UINT msg,
2141                                   WPARAM wParam, LPARAM lParam) {
2142     struct ctlpos cp;
2143     enum { controlstartvalue = 1000,
2144          IDC_XLATSTATIC,
2145          IDC_NOXLAT,
2146          IDC_KOI8WIN1251,
2147          IDC_88592WIN1250,
2148          IDC_CAPSLOCKCYR,
2149          IDC_VTSTATIC,
2150          IDC_VTXWINDOWS,
2151          IDC_VTOEMANSI,
2152          IDC_VTOEMONLY,
2153          IDC_VTPOORMAN
2154     };
2155
2156     switch (msg) {
2157       case WM_INITDIALOG:
2158         /* Accelerators used: [aco] beiknpsx */
2159         ctlposinit(&cp, hwnd);
2160         radiobig(&cp,
2161                  "Handling of VT100 line drawing characters:", IDC_VTSTATIC,
2162                  "Font has &XWindows encoding", IDC_VTXWINDOWS,
2163                  "Use font in &both ANSI and OEM modes", IDC_VTOEMANSI,
2164                  "Use font in O&EM mode only", IDC_VTOEMONLY,
2165                  "&Poor man's line drawing (""+"", ""-"" and ""|"")",
2166                  IDC_VTPOORMAN, NULL);
2167         radiobig(&cp,
2168                  "Character set translation:", IDC_XLATSTATIC,
2169                  "&None", IDC_NOXLAT,
2170                  "&KOI8 / Win-1251", IDC_KOI8WIN1251,
2171                  "&ISO-8859-2 / Win-1250", IDC_88592WIN1250, NULL);
2172         checkbox(&cp, "CAP&S LOCK acts as cyrillic switch", IDC_CAPSLOCKCYR);
2173
2174         CheckRadioButton (hwnd, IDC_NOXLAT, IDC_88592WIN1250,
2175                           cfg.xlat_88592w1250 ? IDC_88592WIN1250 :
2176                           cfg.xlat_enablekoiwin ? IDC_KOI8WIN1251 :
2177                           IDC_NOXLAT);
2178         CheckDlgButton (hwnd, IDC_CAPSLOCKCYR, cfg.xlat_capslockcyr);
2179         CheckRadioButton (hwnd, IDC_VTXWINDOWS, IDC_VTPOORMAN,
2180                           cfg.vtmode == VT_XWINDOWS ? IDC_VTXWINDOWS :
2181                           cfg.vtmode == VT_OEMANSI ? IDC_VTOEMANSI :
2182                           cfg.vtmode == VT_OEMONLY ? IDC_VTOEMONLY :
2183                           IDC_VTPOORMAN);
2184       case WM_COMMAND:
2185         switch (LOWORD(wParam)) {
2186           case IDC_NOXLAT:
2187           case IDC_KOI8WIN1251:
2188           case IDC_88592WIN1250:
2189             cfg.xlat_enablekoiwin =
2190                 IsDlgButtonChecked (hwnd, IDC_KOI8WIN1251);
2191             cfg.xlat_88592w1250 =
2192                 IsDlgButtonChecked (hwnd, IDC_88592WIN1250);
2193             break;
2194           case IDC_CAPSLOCKCYR:
2195             if (HIWORD(wParam) == BN_CLICKED ||
2196                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2197                 cfg.xlat_capslockcyr =
2198                     IsDlgButtonChecked (hwnd, IDC_CAPSLOCKCYR);
2199             }
2200             break;
2201           case IDC_VTXWINDOWS:
2202           case IDC_VTOEMANSI:
2203           case IDC_VTOEMONLY:
2204           case IDC_VTPOORMAN:
2205             cfg.vtmode =
2206                 (IsDlgButtonChecked (hwnd, IDC_VTXWINDOWS) ? VT_XWINDOWS :
2207                  IsDlgButtonChecked (hwnd, IDC_VTOEMANSI) ? VT_OEMANSI :
2208                  IsDlgButtonChecked (hwnd, IDC_VTOEMONLY) ? VT_OEMONLY :
2209                  VT_POORMAN);
2210             break;
2211         }
2212     }
2213     return GeneralPanelProc (hwnd, msg, wParam, lParam);
2214 }
2215
2216 static DLGPROC panelproc[NPANELS] = {
2217     ConnectionProc, KeyboardProc, TerminalProc, WindowProc,
2218     TelnetProc, SshProc, SelectionProc, ColourProc, TranslationProc
2219 };
2220
2221 static char *names[NPANELS] = {
2222     "Connection", "Keyboard", "Terminal", "Window", "Telnet",
2223     "SSH", "Selection", "Colours", "Translation"
2224 };
2225
2226 static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8};
2227 static int reconfp[RECONF_NPANELS] = { 1, 2, 3, 6, 7, 8};
2228
2229 static HWND makesubdialog(HWND hwnd, int x, int y, int w, int h, int n) {
2230     RECT r;
2231     HWND ret;
2232     WPARAM font;
2233     r.left = x; r.top = y;
2234     r.right = r.left + w; r.bottom = r.top + h;
2235     MapDialogRect(hwnd, &r);
2236     ret = CreateWindowEx(WS_EX_CONTROLPARENT,
2237                            WC_DIALOG, "",   /* no title */
2238                            WS_CHILD | WS_VISIBLE | DS_SETFONT,
2239                            r.left, r.top,
2240                            r.right-r.left, r.bottom-r.top,
2241                            hwnd, (HMENU)IDC_SUBDLG,
2242                            hinst, NULL);
2243     SetWindowLong (ret, DWL_DLGPROC, (LONG)panelproc[n]);
2244     font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2245     SendMessage (ret, WM_SETFONT, font, MAKELPARAM(0, 0));
2246     SendMessage (ret, WM_INITDIALOG, 0, 0);
2247     return ret;
2248 }
2249
2250 static int GenericMainDlgProc (HWND hwnd, UINT msg,
2251                                WPARAM wParam, LPARAM lParam,
2252                                int npanels, int *panelnums, HWND *page) {
2253     HWND hw, tabctl;
2254
2255     switch (msg) {
2256       case WM_INITDIALOG:
2257         {                              /* centre the window */
2258             RECT rs, rd;
2259
2260             hw = GetDesktopWindow();
2261             if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
2262                 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
2263                             (rs.bottom + rs.top + rd.top - rd.bottom)/2,
2264                             rd.right-rd.left, rd.bottom-rd.top, TRUE);
2265         }
2266         {
2267             RECT r;
2268             r.left = 3; r.right = r.left + 174;
2269             r.top = 3; r.bottom = r.top + 193;
2270             MapDialogRect(hwnd, &r);
2271             tabctl = CreateWindowEx(0, WC_TABCONTROL, "",
2272                                     WS_CHILD | WS_VISIBLE |
2273                                     WS_TABSTOP | TCS_MULTILINE,
2274                                     r.left, r.top,
2275                                     r.right-r.left, r.bottom-r.top,
2276                                     hwnd, (HMENU)IDC_TAB, hinst, NULL);
2277
2278             if (!tabctl) {
2279                 struct ctlpos cp;
2280                 ctlposinit2(&cp, hwnd);
2281                 ersatztab(&cp, "Category:", IDC_TABSTATIC1, IDC_TABLIST,
2282                           IDC_TABSTATIC2);
2283             } else {
2284                 WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2285                 SendMessage(tabctl, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
2286             }
2287         }
2288         *page = NULL;
2289         if (tabctl) {                  /* initialise the tab control */
2290             TC_ITEMHEADER tab;
2291             int i;
2292
2293             for (i=0; i<npanels; i++) {
2294                 tab.mask = TCIF_TEXT;
2295                 tab.pszText = names[panelnums[i]];
2296                 TabCtrl_InsertItem (tabctl, i, &tab);
2297             }
2298         } else {
2299             int i;
2300
2301             for (i=0; i<npanels; i++) {
2302                 SendDlgItemMessage(hwnd, IDC_TABLIST, CB_ADDSTRING,
2303                                    0, (LPARAM)names[panelnums[i]]);
2304             }
2305             SendDlgItemMessage(hwnd, IDC_TABLIST, CB_SETCURSEL, 0, 0);
2306         }
2307         *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[0]);
2308         SetFocus (*page);
2309         return 0;
2310       case WM_NOTIFY:
2311         if (LOWORD(wParam) == IDC_TAB &&
2312             ((LPNMHDR)lParam)->code == TCN_SELCHANGE) {
2313             int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
2314             if (*page)
2315                 DestroyWindow (*page);
2316             *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2317             SetFocus (((LPNMHDR)lParam)->hwndFrom);   /* ensure focus stays */
2318             return 0;
2319         }
2320         break;
2321       case WM_COMMAND:
2322         switch (LOWORD(wParam)) {
2323           case IDC_TABLIST:
2324             if (HIWORD(wParam) == CBN_SELCHANGE) {
2325                 HWND tablist = GetDlgItem (hwnd, IDC_TABLIST);
2326                 int i = SendMessage (tablist, CB_GETCURSEL, 0, 0);
2327                 if (*page)
2328                     DestroyWindow (*page);
2329                 *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2330                 SetFocus(tablist);     /* ensure focus stays */
2331                 return 0;
2332             }
2333             break;
2334           case IDOK:
2335             if (*cfg.host)
2336                 EndDialog (hwnd, 1);
2337             else
2338                 MessageBeep (0);
2339             return 0;
2340           case IDCANCEL:
2341             EndDialog (hwnd, 0);
2342             return 0;
2343         }
2344         return 0;
2345       case WM_CLOSE:
2346         EndDialog (hwnd, 0);
2347         return 0;
2348
2349         /* Grrr Explorer will maximize Dialogs! */
2350       case WM_SIZE:
2351         if (wParam == SIZE_MAXIMIZED)
2352            force_normal(hwnd);
2353         return 0;
2354     }
2355     return 0;
2356 }
2357
2358 static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
2359                                  WPARAM wParam, LPARAM lParam) {
2360     static HWND page = NULL;
2361
2362     if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) {
2363     }
2364     if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) {
2365         EnableWindow(hwnd, 0);
2366         DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2367                   GetParent(hwnd), AboutProc);
2368         EnableWindow(hwnd, 1);
2369         SetActiveWindow(hwnd);
2370     }
2371     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2372                                MAIN_NPANELS, mainp, &page);
2373 }
2374
2375 static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg,
2376                                    WPARAM wParam, LPARAM lParam) {
2377     static HWND page;
2378     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2379                                RECONF_NPANELS, reconfp, &page);
2380 }
2381
2382 void get_sesslist(int allocate) {
2383     static char otherbuf[2048];
2384     static char *buffer;
2385     int buflen, bufsize, i;
2386     char *p, *ret;
2387     void *handle;
2388
2389     if (allocate) {
2390         
2391         if ((handle = enum_settings_start()) == NULL)
2392             return;
2393
2394         buflen = bufsize = 0;
2395         buffer = NULL;
2396         do {
2397             ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf));
2398             if (ret) {
2399                 int len = strlen(otherbuf)+1;
2400                 if (bufsize < buflen+len) {
2401                     bufsize = buflen + len + 2048;
2402                     buffer = srealloc(buffer, bufsize);
2403                 }
2404                 strcpy(buffer+buflen, otherbuf);
2405                 buflen += strlen(buffer+buflen)+1;
2406             }
2407         } while (ret);
2408         enum_settings_finish(handle);
2409         buffer = srealloc(buffer, buflen+1);
2410         buffer[buflen] = '\0';
2411
2412         p = buffer;
2413         nsessions = 1;                 /* "Default Settings" counts as one */
2414         while (*p) {
2415             if (strcmp(p, "Default Settings"))
2416                 nsessions++;
2417             while (*p) p++;
2418             p++;
2419         }
2420
2421         sessions = smalloc(nsessions * sizeof(char *));
2422         sessions[0] = "Default Settings";
2423         p = buffer;
2424         i = 1;
2425         while (*p) {
2426             if (strcmp(p, "Default Settings"))
2427                 sessions[i++] = p;
2428             while (*p) p++;
2429             p++;
2430         }
2431     } else {
2432         sfree (buffer);
2433         sfree (sessions);
2434     }
2435 }
2436
2437 int do_config (void) {
2438     int ret;
2439
2440     get_sesslist(TRUE);
2441     savedsession[0] = '\0';
2442     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
2443     get_sesslist(FALSE);
2444
2445     return ret;
2446 }
2447
2448 int do_reconfig (HWND hwnd) {
2449     Config backup_cfg;
2450     int ret;
2451
2452     backup_cfg = cfg;                  /* structure copy */
2453     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc);
2454     if (!ret)
2455         cfg = backup_cfg;              /* structure copy */
2456     else
2457         force_normal(hwnd);
2458
2459     return ret;
2460 }
2461
2462 void do_defaults (char *session) {
2463     if (session)
2464         load_settings (session, TRUE);
2465     else
2466         load_settings ("Default Settings", FALSE);
2467 }
2468
2469 void logevent (char *string) {
2470     if (nevents >= negsize) {
2471         negsize += 64;
2472         events = srealloc (events, negsize * sizeof(*events));
2473     }
2474     events[nevents] = smalloc(1+strlen(string));
2475     strcpy (events[nevents], string);
2476     nevents++;
2477     if (logbox) {
2478         int count;
2479         SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
2480                             0, (LPARAM)string);
2481         count = SendDlgItemMessage (logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
2482         SendDlgItemMessage (logbox, IDN_LIST, LB_SETTOPINDEX, count-1, 0);
2483     }
2484 }
2485
2486 void showeventlog (HWND hwnd) {
2487     if (!logbox) {
2488         logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX),
2489                                hwnd, LogProc);
2490         ShowWindow (logbox, SW_SHOWNORMAL);
2491     }
2492 }
2493
2494 void showabout (HWND hwnd) {
2495     if (!abtbox) {
2496         abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2497                                hwnd, AboutProc);
2498         ShowWindow (abtbox, SW_SHOWNORMAL);
2499     }
2500 }
2501
2502 void verify_ssh_host_key(char *host, int port, char *keytype,
2503                          char *keystr, char *fingerprint) {
2504     int ret;
2505
2506     static const char absentmsg[] =
2507         "The server's host key is not cached in the registry. You\n"
2508         "have no guarantee that the server is the computer you\n"
2509         "think it is.\n"
2510         "The server's key fingerprint is:\n"
2511         "%s\n"
2512         "If you trust this host, hit Yes to add the key to\n"
2513         "PuTTY's cache and carry on connecting.\n"
2514         "If you do not trust this host, hit No to abandon the\n"
2515         "connection.\n";
2516
2517     static const char wrongmsg[] =
2518         "WARNING - POTENTIAL SECURITY BREACH!\n"
2519         "\n"
2520         "The server's host key does not match the one PuTTY has\n"
2521         "cached in the registry. This means that either the\n"
2522         "server administrator has changed the host key, or you\n"
2523         "have actually connected to another computer pretending\n"
2524         "to be the server.\n"
2525         "The new key fingerprint is:\n"
2526         "%s\n"
2527         "If you were expecting this change and trust the new key,\n"
2528         "hit Yes to update PuTTY's cache and continue connecting.\n"
2529         "If you want to carry on connecting but without updating\n"
2530         "the cache, hit No.\n"
2531         "If you want to abandon the connection completely, hit\n"
2532         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n"
2533         "choice.\n";
2534
2535     static const char mbtitle[] = "PuTTY Security Alert";
2536
2537     
2538     char message[160+                  /* sensible fingerprint max size */
2539                  (sizeof(absentmsg) > sizeof(wrongmsg) ?
2540                   sizeof(absentmsg) : sizeof(wrongmsg))];
2541
2542     /*
2543      * Verify the key against the registry.
2544      */
2545     ret = verify_host_key(host, port, keytype, keystr);
2546
2547     if (ret == 0)                      /* success - key matched OK */
2548         return;
2549     if (ret == 2) {                    /* key was different */
2550         int mbret;
2551         sprintf(message, wrongmsg, fingerprint);
2552         mbret = MessageBox(NULL, message, mbtitle,
2553                            MB_ICONWARNING | MB_YESNOCANCEL);
2554         if (mbret == IDYES)
2555             store_host_key(host, port, keytype, keystr);
2556         if (mbret == IDCANCEL)
2557             exit(0);
2558     }
2559     if (ret == 1) {                    /* key was absent */
2560         int mbret;
2561         sprintf(message, absentmsg, fingerprint);
2562         mbret = MessageBox(NULL, message, mbtitle,
2563                            MB_ICONWARNING | MB_YESNO);
2564         if (mbret == IDNO)
2565             exit(0);
2566         store_host_key(host, port, keytype, keystr);
2567     }
2568 }