]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windlg.c
f660fb00d7ab27b428de01ebb1fcb434d2d8123f
[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         radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3,
1095                   "&Raw", IDC_PROTRAW,
1096                   "&Telnet", IDC_PROTTELNET,
1097 #ifdef FWHACK
1098                   "SS&H/hack",
1099 #else
1100                   "SS&H",
1101 #endif
1102                   IDC_PROTSSH, NULL);
1103         sesssaver(&cp, "Stor&ed Sessions",
1104                   IDC_SESSSTATIC, IDC_SESSEDIT, IDC_SESSLIST,
1105                   "&Load", IDC_SESSLOAD,
1106                   "&Save", IDC_SESSSAVE,
1107                   "&Delete", IDC_SESSDEL, NULL);
1108         checkbox(&cp, "Close Window on E&xit", IDC_CLOSEEXIT);
1109         checkbox(&cp, "&Warn on Close", IDC_CLOSEWARN);
1110
1111         SetDlgItemText (hwnd, IDC_HOST, cfg.host);
1112         SetDlgItemText (hwnd, IDC_SESSEDIT, savedsession);
1113         SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1114         for (i = 0; i < nsessions; i++)
1115             SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1116                                 0, (LPARAM) (sessions[i]));
1117         CheckRadioButton (hwnd, IDC_PROTRAW, IDC_PROTSSH,
1118                           cfg.protocol==PROT_SSH ? IDC_PROTSSH : 
1119                           cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : IDC_PROTRAW );
1120         CheckDlgButton (hwnd, IDC_CLOSEEXIT, cfg.close_on_exit);
1121         CheckDlgButton (hwnd, IDC_CLOSEWARN, cfg.warn_on_close);
1122         break;
1123       case WM_LBUTTONUP:
1124         /*
1125          * Button release should trigger WM_OK if there was a
1126          * previous double click on the session list.
1127          */
1128         ReleaseCapture();
1129         if (readytogo)
1130             SendMessage (GetParent(hwnd), WM_COMMAND, IDOK, 0);
1131         break;
1132       case WM_COMMAND:
1133         switch (LOWORD(wParam)) {
1134           case IDC_PROTTELNET:
1135           case IDC_PROTSSH:
1136           case IDC_PROTRAW:
1137             if (HIWORD(wParam) == BN_CLICKED ||
1138                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1139                 int i = IsDlgButtonChecked (hwnd, IDC_PROTSSH);
1140                 int j = IsDlgButtonChecked (hwnd, IDC_PROTTELNET);
1141                 cfg.protocol = i ? PROT_SSH : j ? PROT_TELNET : PROT_RAW ;
1142                 if ((cfg.protocol == PROT_SSH && cfg.port == 23) ||
1143                     (cfg.protocol == PROT_TELNET && cfg.port == 22)) {
1144                     cfg.port = i ? 22 : 23;
1145                     SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1146                 }
1147             }
1148             break;
1149           case IDC_HOST:
1150             if (HIWORD(wParam) == EN_CHANGE)
1151                 GetDlgItemText (hwnd, IDC_HOST, cfg.host,
1152                                 sizeof(cfg.host)-1);
1153             break;
1154           case IDC_PORT:
1155             if (HIWORD(wParam) == EN_CHANGE)
1156                 MyGetDlgItemInt (hwnd, IDC_PORT, &cfg.port);
1157             break;
1158           case IDC_CLOSEEXIT:
1159             if (HIWORD(wParam) == BN_CLICKED ||
1160                 HIWORD(wParam) == BN_DOUBLECLICKED)
1161                 cfg.close_on_exit = IsDlgButtonChecked (hwnd, IDC_CLOSEEXIT);
1162             break;
1163           case IDC_CLOSEWARN:
1164             if (HIWORD(wParam) == BN_CLICKED ||
1165                 HIWORD(wParam) == BN_DOUBLECLICKED)
1166                 cfg.warn_on_close = IsDlgButtonChecked (hwnd, IDC_CLOSEWARN);
1167             break;
1168           case IDC_SESSEDIT:
1169             if (HIWORD(wParam) == EN_CHANGE) {
1170                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1171                                     (WPARAM) -1, 0);
1172                 GetDlgItemText (hwnd, IDC_SESSEDIT,
1173                                 savedsession, sizeof(savedsession)-1);
1174                 savedsession[sizeof(savedsession)-1] = '\0';
1175             }
1176             break;
1177           case IDC_SESSSAVE:
1178             if (HIWORD(wParam) == BN_CLICKED ||
1179                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1180                 /*
1181                  * Save a session
1182                  */
1183                 char str[2048];
1184                 GetDlgItemText (hwnd, IDC_SESSEDIT, str, sizeof(str)-1);
1185                 if (!*str) {
1186                     int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1187                                                 LB_GETCURSEL, 0, 0);
1188                     if (n == LB_ERR) {
1189                         MessageBeep(0);
1190                         break;
1191                     }
1192                     strcpy (str, sessions[n]);
1193                 }
1194                 save_settings (str, !!strcmp(str, "Default Settings"));
1195                 get_sesslist (FALSE);
1196                 get_sesslist (TRUE);
1197                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_RESETCONTENT,
1198                                     0, 0);
1199                 for (i = 0; i < nsessions; i++)
1200                     SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1201                                         0, (LPARAM) (sessions[i]));
1202                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1203                                     (WPARAM) -1, 0);
1204             }
1205             break;
1206           case IDC_SESSLIST:
1207           case IDC_SESSLOAD:
1208             if (LOWORD(wParam) == IDC_SESSLOAD &&
1209                 HIWORD(wParam) != BN_CLICKED &&
1210                 HIWORD(wParam) != BN_DOUBLECLICKED)
1211                 break;
1212             if (LOWORD(wParam) == IDC_SESSLIST &&
1213                 HIWORD(wParam) != LBN_DBLCLK)
1214                 break;
1215             {
1216                 int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1217                                             LB_GETCURSEL, 0, 0);
1218                 if (n == LB_ERR) {
1219                     MessageBeep(0);
1220                     break;
1221                 }
1222                 load_settings (sessions[n],
1223                                !!strcmp(sessions[n], "Default Settings"));
1224                 SetDlgItemText (hwnd, IDC_HOST, cfg.host);
1225                 SetDlgItemInt (hwnd, IDC_PORT, cfg.port, FALSE);
1226                 CheckRadioButton (hwnd, IDC_PROTRAW, IDC_PROTSSH,
1227                                   (cfg.protocol==PROT_SSH ? IDC_PROTSSH :
1228                                   cfg.protocol==PROT_TELNET ? IDC_PROTTELNET : IDC_PROTRAW));
1229                 CheckDlgButton (hwnd, IDC_CLOSEEXIT, cfg.close_on_exit);
1230                 CheckDlgButton (hwnd, IDC_CLOSEWARN, cfg.warn_on_close);
1231                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1232                                     (WPARAM) -1, 0);
1233             }
1234             if (LOWORD(wParam) == IDC_SESSLIST) {
1235                 /*
1236                  * A double-click on a saved session should
1237                  * actually start the session, not just load it.
1238                  * Unless it's Default Settings or some other
1239                  * host-less set of saved settings.
1240                  */
1241                 if (*cfg.host) {
1242                     readytogo = TRUE;
1243                     SetCapture(hwnd);
1244                 }
1245             }
1246             break;
1247           case IDC_SESSDEL:
1248             if (HIWORD(wParam) == BN_CLICKED ||
1249                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1250                 int n = SendDlgItemMessage (hwnd, IDC_SESSLIST,
1251                                             LB_GETCURSEL, 0, 0);
1252                 if (n == LB_ERR || n == 0) {
1253                     MessageBeep(0);
1254                     break;
1255                 }
1256                 del_settings(sessions[n]);
1257                 get_sesslist (FALSE);
1258                 get_sesslist (TRUE);
1259                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_RESETCONTENT,
1260                                     0, 0);
1261                 for (i = 0; i < nsessions; i++)
1262                     SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_ADDSTRING,
1263                                         0, (LPARAM) (sessions[i]));
1264                 SendDlgItemMessage (hwnd, IDC_SESSLIST, LB_SETCURSEL,
1265                                     (WPARAM) -1, 0);
1266             }
1267         }
1268     }
1269     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1270 }
1271
1272 static int CALLBACK KeyboardProc (HWND hwnd, UINT msg,
1273                                   WPARAM wParam, LPARAM lParam) {
1274     struct ctlpos cp;
1275     enum { controlstartvalue = 1000,
1276         IDC_DELSTATIC,
1277         IDC_DEL008,
1278         IDC_DEL127,
1279         IDC_HOMESTATIC,
1280         IDC_HOMETILDE,
1281         IDC_HOMERXVT,
1282         IDC_FUNCSTATIC,
1283         IDC_FUNCTILDE,
1284         IDC_FUNCLINUX,
1285         IDC_FUNCXTERM,
1286         IDC_KPSTATIC,
1287         IDC_KPNORMAL,
1288         IDC_KPAPPLIC,
1289         IDC_KPNH,
1290         IDC_CURSTATIC,
1291         IDC_CURNORMAL,
1292         IDC_CURAPPLIC,
1293         IDC_ALTF4,
1294         IDC_ALTSPACE,
1295         IDC_LDISCTERM,
1296         IDC_SCROLLKEY
1297     };
1298
1299     switch (msg) {
1300       case WM_INITDIALOG:
1301         /* Accelerators used: [aco] 4?ehiklmnprsuvxy */
1302         ctlposinit(&cp, hwnd);
1303         radioline(&cp, "Action of Backspace:", IDC_DELSTATIC, 2,
1304                   "Control-&H", IDC_DEL008,
1305                   "Control-&? (127)", IDC_DEL127, NULL);
1306         radioline(&cp, "Action of Home and End:", IDC_HOMESTATIC, 2,
1307                   "&Standard", IDC_HOMETILDE,
1308                   "&rxvt", IDC_HOMERXVT, NULL);
1309         radioline(&cp, "Function key and keypad layout:", IDC_FUNCSTATIC, 3,
1310                   "&VT400", IDC_FUNCTILDE,
1311                   "&Linux", IDC_FUNCLINUX,
1312                   "&Xterm R6", IDC_FUNCXTERM, NULL);
1313         radioline(&cp, "Initial state of cursor keys:", IDC_CURSTATIC, 2,
1314                   "&Normal", IDC_CURNORMAL,
1315                   "A&pplication", IDC_CURAPPLIC, NULL);
1316         radioline(&cp, "Initial state of numeric keypad:", IDC_KPSTATIC, 3,
1317                   "Nor&mal", IDC_KPNORMAL,
1318                   "Appl&ication", IDC_KPAPPLIC,
1319                   "N&etHack", IDC_KPNH, NULL);
1320         checkbox(&cp, "ALT-F&4 is special (closes window)", IDC_ALTF4);
1321         checkbox(&cp, "ALT-Space is special (S&ystem menu)", IDC_ALTSPACE);
1322         checkbox(&cp, "&Use local terminal line discipline", IDC_LDISCTERM);
1323         checkbox(&cp, "Reset scrollback on &keypress", IDC_SCROLLKEY);
1324
1325         CheckRadioButton (hwnd, IDC_DEL008, IDC_DEL127,
1326                           cfg.bksp_is_delete ? IDC_DEL127 : IDC_DEL008);
1327         CheckRadioButton (hwnd, IDC_HOMETILDE, IDC_HOMERXVT,
1328                           cfg.rxvt_homeend ? IDC_HOMERXVT : IDC_HOMETILDE);
1329         CheckRadioButton (hwnd, IDC_FUNCTILDE, IDC_FUNCXTERM,
1330                           cfg.funky_type ? 
1331                           (cfg.funky_type==2 ? IDC_FUNCXTERM 
1332                            : IDC_FUNCLINUX )
1333                           : IDC_FUNCTILDE);
1334         CheckRadioButton (hwnd, IDC_CURNORMAL, IDC_CURAPPLIC,
1335                           cfg.app_cursor ? IDC_CURAPPLIC : IDC_CURNORMAL);
1336         CheckRadioButton (hwnd, IDC_KPNORMAL, IDC_KPNH,
1337                           cfg.nethack_keypad ? IDC_KPNH :
1338                           cfg.app_keypad ? IDC_KPAPPLIC : IDC_KPNORMAL);
1339         CheckDlgButton (hwnd, IDC_ALTF4, cfg.alt_f4);
1340         CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space);
1341         CheckDlgButton (hwnd, IDC_LDISCTERM, cfg.ldisc_term);
1342         CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key);
1343         break;
1344       case WM_COMMAND:
1345         if (HIWORD(wParam) == BN_CLICKED ||
1346             HIWORD(wParam) == BN_DOUBLECLICKED)
1347             switch (LOWORD(wParam)) {
1348               case IDC_DEL008:
1349               case IDC_DEL127:
1350                 cfg.bksp_is_delete = IsDlgButtonChecked (hwnd, IDC_DEL127);
1351                 break;
1352               case IDC_HOMETILDE:
1353               case IDC_HOMERXVT:
1354                 cfg.rxvt_homeend = IsDlgButtonChecked (hwnd, IDC_HOMERXVT);
1355                 break;
1356               case IDC_FUNCXTERM:
1357                 cfg.funky_type = 2;
1358                 break;
1359               case IDC_FUNCTILDE:
1360               case IDC_FUNCLINUX:
1361                 cfg.funky_type = IsDlgButtonChecked (hwnd, IDC_FUNCLINUX);
1362                 break;
1363               case IDC_KPNORMAL:
1364               case IDC_KPAPPLIC:
1365                 cfg.app_keypad = IsDlgButtonChecked (hwnd, IDC_KPAPPLIC);
1366                 cfg.nethack_keypad = FALSE;
1367                 break;
1368               case IDC_KPNH:
1369                 cfg.app_keypad = FALSE;
1370                 cfg.nethack_keypad = TRUE;
1371                 break;
1372               case IDC_CURNORMAL:
1373               case IDC_CURAPPLIC:
1374                 cfg.app_cursor = IsDlgButtonChecked (hwnd, IDC_CURAPPLIC);
1375                 break;
1376               case IDC_ALTF4:
1377                 if (HIWORD(wParam) == BN_CLICKED ||
1378                     HIWORD(wParam) == BN_DOUBLECLICKED)
1379                     cfg.alt_f4 = IsDlgButtonChecked (hwnd, IDC_ALTF4);
1380                 break;
1381               case IDC_ALTSPACE:
1382                 if (HIWORD(wParam) == BN_CLICKED ||
1383                     HIWORD(wParam) == BN_DOUBLECLICKED)
1384                     cfg.alt_space = IsDlgButtonChecked (hwnd, IDC_ALTSPACE);
1385                 break;
1386               case IDC_LDISCTERM:
1387                 if (HIWORD(wParam) == BN_CLICKED ||
1388                     HIWORD(wParam) == BN_DOUBLECLICKED)
1389                     cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC_LDISCTERM);
1390                 break;
1391               case IDC_SCROLLKEY:
1392                 if (HIWORD(wParam) == BN_CLICKED ||
1393                     HIWORD(wParam) == BN_DOUBLECLICKED)
1394                     cfg.scroll_on_key = IsDlgButtonChecked (hwnd, IDC_SCROLLKEY);
1395                 break;
1396             }
1397     }
1398     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1399 }
1400
1401 static void fmtfont (char *buf) {
1402     sprintf (buf, "Font: %s, ", cfg.font);
1403     if (cfg.fontisbold)
1404         strcat(buf, "bold, ");
1405     if (cfg.fontheight == 0)
1406         strcat (buf, "default height");
1407     else
1408         sprintf (buf+strlen(buf), "%d-%s",
1409                  (cfg.fontheight < 0 ? -cfg.fontheight : cfg.fontheight),
1410                  (cfg.fontheight < 0 ? "pixel" : "point"));
1411 }
1412
1413 static int CALLBACK TerminalProc (HWND hwnd, UINT msg,
1414                                     WPARAM wParam, LPARAM lParam) {
1415     struct ctlpos cp;
1416     CHOOSEFONT cf;
1417     LOGFONT lf;
1418     char fontstatic[256];
1419     enum { controlstartvalue = 1000,
1420         IDC_WRAPMODE,
1421         IDC_DECOM,
1422         IDC_DIMSTATIC,
1423         IDC_ROWSSTATIC,
1424         IDC_ROWSEDIT,
1425         IDC_COLSSTATIC,
1426         IDC_COLSEDIT,
1427         IDC_SAVESTATIC,
1428         IDC_SAVEEDIT,
1429         IDC_FONTSTATIC,
1430         IDC_CHOOSEFONT,
1431         IDC_LFHASCR,
1432         IDC_BEEP,
1433         IDC_BCE,
1434         IDC_BLINKTEXT
1435     };
1436
1437     switch (msg) {
1438       case WM_INITDIALOG:
1439         /* Accelerators used: [aco] dghlmnprsw */
1440         ctlposinit(&cp, hwnd);
1441         multiedit(&cp,
1442                   "&Rows", IDC_ROWSSTATIC, IDC_ROWSEDIT, 33,
1443                   "Colu&mns", IDC_COLSSTATIC, IDC_COLSEDIT, 33,
1444                   "&Scrollback", IDC_SAVESTATIC, IDC_SAVEEDIT, 33,
1445                   NULL);
1446         staticbtn(&cp, "", IDC_FONTSTATIC, "C&hange...", IDC_CHOOSEFONT);
1447         checkbox(&cp, "Auto &wrap mode initially on", IDC_WRAPMODE);
1448         checkbox(&cp, "&DEC Origin Mode initially on", IDC_DECOM);
1449         checkbox(&cp, "Implicit CR in every &LF", IDC_LFHASCR);
1450         checkbox(&cp, "Bee&p enabled", IDC_BEEP);
1451         checkbox(&cp, "Use Back&ground colour erase", IDC_BCE);
1452         checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
1453
1454         CheckDlgButton (hwnd, IDC_WRAPMODE, cfg.wrap_mode);
1455         CheckDlgButton (hwnd, IDC_DECOM, cfg.dec_om);
1456         CheckDlgButton (hwnd, IDC_LFHASCR, cfg.lfhascr);
1457         SetDlgItemInt (hwnd, IDC_ROWSEDIT, cfg.height, FALSE);
1458         SetDlgItemInt (hwnd, IDC_COLSEDIT, cfg.width, FALSE);
1459         SetDlgItemInt (hwnd, IDC_SAVEEDIT, cfg.savelines, FALSE);
1460         fmtfont (fontstatic);
1461         SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
1462         CheckDlgButton (hwnd, IDC_BEEP, cfg.beep);
1463         CheckDlgButton (hwnd, IDC_BCE, cfg.bce);
1464         CheckDlgButton (hwnd, IDC_BLINKTEXT, cfg.blinktext);
1465         break;
1466       case WM_COMMAND:
1467         switch (LOWORD(wParam)) {
1468           case IDC_WRAPMODE:
1469             if (HIWORD(wParam) == BN_CLICKED ||
1470                 HIWORD(wParam) == BN_DOUBLECLICKED)
1471                 cfg.wrap_mode = IsDlgButtonChecked (hwnd, IDC_WRAPMODE);
1472             break;
1473           case IDC_DECOM:
1474             if (HIWORD(wParam) == BN_CLICKED ||
1475                 HIWORD(wParam) == BN_DOUBLECLICKED)
1476                 cfg.dec_om = IsDlgButtonChecked (hwnd, IDC_DECOM);
1477             break;
1478           case IDC_LFHASCR:
1479             if (HIWORD(wParam) == BN_CLICKED ||
1480                 HIWORD(wParam) == BN_DOUBLECLICKED)
1481                 cfg.lfhascr = IsDlgButtonChecked (hwnd, IDC_LFHASCR);
1482             break;
1483           case IDC_ROWSEDIT:
1484             if (HIWORD(wParam) == EN_CHANGE)
1485                 MyGetDlgItemInt (hwnd, IDC_ROWSEDIT, &cfg.height);
1486             break;
1487           case IDC_COLSEDIT:
1488             if (HIWORD(wParam) == EN_CHANGE)
1489                 MyGetDlgItemInt (hwnd, IDC_COLSEDIT, &cfg.width);
1490             break;
1491           case IDC_SAVEEDIT:
1492             if (HIWORD(wParam) == EN_CHANGE)
1493                 MyGetDlgItemInt (hwnd, IDC_SAVEEDIT, &cfg.savelines);
1494             break;
1495           case IDC_CHOOSEFONT:
1496             lf.lfHeight = cfg.fontheight;
1497             lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
1498             lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
1499             lf.lfWeight = (cfg.fontisbold ? FW_BOLD : 0);
1500             lf.lfCharSet = cfg.fontcharset;
1501             lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
1502             lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1503             lf.lfQuality = DEFAULT_QUALITY;
1504             lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1505             strncpy (lf.lfFaceName, cfg.font, sizeof(lf.lfFaceName)-1);
1506             lf.lfFaceName[sizeof(lf.lfFaceName)-1] = '\0';
1507
1508             cf.lStructSize = sizeof(cf);
1509             cf.hwndOwner = hwnd;
1510             cf.lpLogFont = &lf;
1511             cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
1512                 CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
1513
1514             if (ChooseFont (&cf)) {
1515                 strncpy (cfg.font, lf.lfFaceName, sizeof(cfg.font)-1);
1516                 cfg.font[sizeof(cfg.font)-1] = '\0';
1517                 cfg.fontisbold = (lf.lfWeight == FW_BOLD);
1518                 cfg.fontcharset = lf.lfCharSet;
1519                 cfg.fontheight = lf.lfHeight;
1520                 fmtfont (fontstatic);
1521                 SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
1522             }
1523             break;
1524            case IDC_BEEP:
1525              if (HIWORD(wParam) == BN_CLICKED ||
1526                  HIWORD(wParam) == BN_DOUBLECLICKED)
1527                  cfg.beep = IsDlgButtonChecked (hwnd, IDC_BEEP);
1528              break;
1529            case IDC_BLINKTEXT:
1530              if (HIWORD(wParam) == BN_CLICKED ||
1531                  HIWORD(wParam) == BN_DOUBLECLICKED)
1532                  cfg.blinktext = IsDlgButtonChecked (hwnd, IDC_BLINKTEXT);
1533              break;
1534            case IDC_BCE:
1535              if (HIWORD(wParam) == BN_CLICKED ||
1536                  HIWORD(wParam) == BN_DOUBLECLICKED)
1537                  cfg.bce = IsDlgButtonChecked (hwnd, IDC_BCE);
1538              break;
1539         }
1540         break;
1541     }
1542     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1543 }
1544
1545 static int CALLBACK WindowProc (HWND hwnd, UINT msg,
1546                                     WPARAM wParam, LPARAM lParam) {
1547     struct ctlpos cp;
1548     enum { controlstartvalue = 1000,
1549          IDC_WINNAME,
1550          IDC_BLINKCUR,
1551          IDC_SCROLLBAR,
1552          IDC_LOCKSIZE,
1553          IDC_WINTITLE,
1554          IDC_WINEDIT
1555     };
1556
1557     switch (msg) {
1558       case WM_INITDIALOG:
1559         /* Accelerators used: [aco] bikty */
1560         ctlposinit(&cp, hwnd);
1561         multiedit(&cp,
1562                   "Initial window &title:", IDC_WINTITLE, IDC_WINEDIT, 100,
1563                   NULL);
1564         checkbox(&cp, "Avoid ever using &icon title", IDC_WINNAME);
1565         checkbox(&cp, "&Blinking cursor", IDC_BLINKCUR);
1566         checkbox(&cp, "Displa&y scrollbar", IDC_SCROLLBAR);
1567         checkbox(&cp, "Loc&k Window size", IDC_LOCKSIZE);
1568
1569         SetDlgItemText (hwnd, IDC_WINEDIT, cfg.wintitle);
1570         CheckDlgButton (hwnd, IDC_WINNAME, cfg.win_name_always);
1571         CheckDlgButton (hwnd, IDC_BLINKCUR, cfg.blink_cur);
1572         CheckDlgButton (hwnd, IDC_SCROLLBAR, cfg.scrollbar);
1573         CheckDlgButton (hwnd, IDC_LOCKSIZE, cfg.locksize);
1574         break;
1575       case WM_COMMAND:
1576         switch (LOWORD(wParam)) {
1577           case IDC_WINNAME:
1578             if (HIWORD(wParam) == BN_CLICKED ||
1579                 HIWORD(wParam) == BN_DOUBLECLICKED)
1580                 cfg.win_name_always = IsDlgButtonChecked (hwnd, IDC_WINNAME);
1581             break;
1582           case IDC_BLINKCUR:
1583             if (HIWORD(wParam) == BN_CLICKED ||
1584                 HIWORD(wParam) == BN_DOUBLECLICKED)
1585                 cfg.blink_cur = IsDlgButtonChecked (hwnd, IDC_BLINKCUR);
1586             break;
1587           case IDC_SCROLLBAR:
1588             if (HIWORD(wParam) == BN_CLICKED ||
1589                 HIWORD(wParam) == BN_DOUBLECLICKED)
1590                 cfg.scrollbar = IsDlgButtonChecked (hwnd, IDC_SCROLLBAR);
1591             break;
1592           case IDC_LOCKSIZE:
1593              if (HIWORD(wParam) == BN_CLICKED ||
1594                  HIWORD(wParam) == BN_DOUBLECLICKED)
1595                 cfg.locksize = IsDlgButtonChecked (hwnd, IDC_LOCKSIZE);
1596             break;
1597           case IDC_WINEDIT:
1598             if (HIWORD(wParam) == EN_CHANGE)
1599                 GetDlgItemText (hwnd, IDC_WINEDIT, cfg.wintitle,
1600                                 sizeof(cfg.wintitle)-1);
1601             break;
1602         }
1603         break;
1604     }
1605     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1606 }
1607
1608 static int CALLBACK TelnetProc (HWND hwnd, UINT msg,
1609                                     WPARAM wParam, LPARAM lParam) {
1610     int i;
1611     struct ctlpos cp;
1612     enum { controlstartvalue = 1000,
1613          IDC_TTSTATIC,
1614          IDC_TTEDIT,
1615          IDC_TSSTATIC,
1616          IDC_TSEDIT,
1617          IDC_LOGSTATIC,
1618          IDC_LOGEDIT,
1619          IDC_ENVSTATIC,
1620          IDC_VARSTATIC,
1621          IDC_VAREDIT,
1622          IDC_VALSTATIC,
1623          IDC_VALEDIT,
1624          IDC_ENVLIST,
1625          IDC_ENVADD,
1626          IDC_ENVREMOVE,
1627          IDC_EMSTATIC,
1628          IDC_EMBSD,
1629          IDC_EMRFC
1630     };
1631
1632     switch (msg) {
1633       case WM_INITDIALOG:
1634         /* Accelerators used: [aco] bdflrstuv */
1635         ctlposinit(&cp, hwnd);
1636         staticedit(&cp, "Terminal-&type string", IDC_TTSTATIC, IDC_TTEDIT);
1637         staticedit(&cp, "Terminal-&speed string", IDC_TSSTATIC, IDC_TSEDIT);
1638         staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC, IDC_LOGEDIT);
1639         envsetter(&cp, "Environment variables:", IDC_ENVSTATIC,
1640                   "&Variable", IDC_VARSTATIC, IDC_VAREDIT,
1641                   "Va&lue", IDC_VALSTATIC, IDC_VALEDIT,
1642                   IDC_ENVLIST,
1643                   "A&dd", IDC_ENVADD, "&Remove", IDC_ENVREMOVE);
1644         radioline(&cp, "Handling of OLD_ENVIRON ambiguity:", IDC_EMSTATIC, 2,
1645                   "&BSD (commonplace)", IDC_EMBSD,
1646                   "R&FC 1408 (unusual)", IDC_EMRFC, NULL);
1647
1648         SetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype);
1649         SetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed);
1650         SetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username);
1651         {
1652           char *p = cfg.environmt;
1653             while (*p) {
1654                 SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_ADDSTRING, 0,
1655                                     (LPARAM) p);
1656                 p += strlen(p)+1;
1657             }
1658         }
1659         CheckRadioButton (hwnd, IDC_EMBSD, IDC_EMRFC,
1660                           cfg.rfc_environ ? IDC_EMRFC : IDC_EMBSD);
1661         break;
1662       case WM_COMMAND:
1663         switch (LOWORD(wParam)) {
1664           case IDC_TTEDIT:
1665             if (HIWORD(wParam) == EN_CHANGE)
1666             GetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype,
1667                             sizeof(cfg.termtype)-1);
1668             break;
1669           case IDC_TSEDIT:
1670             if (HIWORD(wParam) == EN_CHANGE)
1671                 GetDlgItemText (hwnd, IDC_TSEDIT, cfg.termspeed,
1672                                 sizeof(cfg.termspeed)-1);
1673             break;
1674           case IDC_LOGEDIT:
1675             if (HIWORD(wParam) == EN_CHANGE)
1676                 GetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username,
1677                                 sizeof(cfg.username)-1);
1678             break;
1679           case IDC_EMBSD:
1680           case IDC_EMRFC:
1681             cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC_EMRFC);
1682             break;
1683           case IDC_ENVADD:
1684             if (HIWORD(wParam) == BN_CLICKED ||
1685                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1686               char str[sizeof(cfg.environmt)];
1687                 char *p;
1688                 GetDlgItemText (hwnd, IDC_VAREDIT, str, sizeof(str)-1);
1689                 if (!*str) {
1690                     MessageBeep(0);
1691                     break;
1692                 }
1693                 p = str + strlen(str);
1694                 *p++ = '\t';
1695                 GetDlgItemText (hwnd, IDC_VALEDIT, p, sizeof(str)-1-(p-str));
1696                 if (!*p) {
1697                     MessageBeep(0);
1698                     break;
1699                 }
1700               p = cfg.environmt;
1701                 while (*p) {
1702                     while (*p) p++;
1703                     p++;
1704                 }
1705               if ((p-cfg.environmt) + strlen(str) + 2 < sizeof(cfg.environmt)) {
1706                     strcpy (p, str);
1707                     p[strlen(str)+1] = '\0';
1708                     SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_ADDSTRING,
1709                                         0, (LPARAM)str);
1710                     SetDlgItemText (hwnd, IDC_VAREDIT, "");
1711                     SetDlgItemText (hwnd, IDC_VALEDIT, "");
1712                 } else {
1713                     MessageBox(hwnd, "Environment too big", "PuTTY Error",
1714                                MB_OK | MB_ICONERROR);
1715                 }
1716             }
1717             break;
1718           case IDC_ENVREMOVE:
1719             if (HIWORD(wParam) != BN_CLICKED &&
1720                 HIWORD(wParam) != BN_DOUBLECLICKED)
1721                 break;
1722             i = SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_GETCURSEL, 0, 0);
1723             if (i == LB_ERR)
1724                 MessageBeep (0);
1725             else {
1726                 char *p, *q;
1727
1728                 SendDlgItemMessage (hwnd, IDC_ENVLIST, LB_DELETESTRING,
1729                                     i, 0);
1730               p = cfg.environmt;
1731                 while (i > 0) {
1732                     if (!*p)
1733                         goto disaster;
1734                     while (*p) p++;
1735                     p++;
1736                     i--;
1737                 }
1738                 q = p;
1739                 if (!*p)
1740                     goto disaster;
1741                 while (*p) p++;
1742                 p++;
1743                 while (*p) {
1744                     while (*p)
1745                         *q++ = *p++;
1746                     *q++ = *p++;
1747                 }
1748                 *q = '\0';
1749                 disaster:;
1750             }
1751             break;
1752         }
1753         break;
1754     }
1755     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1756 }
1757
1758 static int CALLBACK SshProc (HWND hwnd, UINT msg,
1759                              WPARAM wParam, LPARAM lParam) {
1760     struct ctlpos cp;
1761     OPENFILENAME of;
1762     char filename[sizeof(cfg.keyfile)];
1763     enum { controlstartvalue = 1000,
1764          IDC_TTSTATIC,
1765          IDC_TTEDIT,
1766          IDC_LOGSTATIC,
1767          IDC_LOGEDIT,
1768          IDC_NOPTY,
1769          IDC_CIPHERSTATIC,
1770          IDC_CIPHER3DES,
1771          IDC_CIPHERBLOWF,
1772          IDC_CIPHERDES,
1773          IDC_AUTHTIS,
1774          IDC_PKSTATIC,
1775          IDC_PKEDIT,
1776          IDC_PKBUTTON,
1777          IDC_SSHPROTSTATIC,
1778          IDC_SSHPROT1,
1779          IDC_SSHPROT2,
1780          IDC_AGENTFWD,
1781          IDC_CMDSTATIC,
1782          IDC_CMDEDIT
1783     };
1784
1785     switch (msg) {
1786       case WM_INITDIALOG:
1787         /* Accelerators used: [aco] 123abdkmprtuw */
1788         ctlposinit(&cp, hwnd);
1789         staticedit(&cp, "Terminal-&type string", IDC_TTSTATIC, IDC_TTEDIT);
1790         staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC, IDC_LOGEDIT);
1791         multiedit(&cp,
1792                   "&Remote command:", IDC_CMDSTATIC, IDC_CMDEDIT, 100,
1793                   NULL);
1794         checkbox(&cp, "Don't allocate a &pseudo-terminal", IDC_NOPTY);
1795         checkbox(&cp, "Atte&mpt TIS or CryptoCard authentication",
1796                  IDC_AUTHTIS);
1797         checkbox(&cp, "Allow &agent forwarding", IDC_AGENTFWD);
1798         editbutton(&cp, "Private &key file for authentication:",
1799                     IDC_PKSTATIC, IDC_PKEDIT, "Bro&wse...", IDC_PKBUTTON);
1800         radioline(&cp, "Preferred SSH protocol version:",
1801                   IDC_SSHPROTSTATIC, 2,
1802                   "&1", IDC_SSHPROT1, "&2", IDC_SSHPROT2, NULL);
1803         radioline(&cp, "Preferred encryption algorithm:", IDC_CIPHERSTATIC, 3,
1804                   "&3DES", IDC_CIPHER3DES,
1805                   "&Blowfish", IDC_CIPHERBLOWF,
1806                   "&DES", IDC_CIPHERDES, NULL);    
1807
1808         SetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype);
1809         SetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username);
1810         CheckDlgButton (hwnd, IDC_NOPTY, cfg.nopty);
1811         CheckDlgButton (hwnd, IDC_AGENTFWD, cfg.agentfwd);
1812         CheckRadioButton (hwnd, IDC_CIPHER3DES, IDC_CIPHERDES,
1813                           cfg.cipher == CIPHER_BLOWFISH ? IDC_CIPHERBLOWF :
1814                           cfg.cipher == CIPHER_DES ? IDC_CIPHERDES :
1815                           IDC_CIPHER3DES);
1816         CheckRadioButton (hwnd, IDC_SSHPROT1, IDC_SSHPROT2,
1817                           cfg.sshprot == 1 ? IDC_SSHPROT1 : IDC_SSHPROT2);
1818         CheckDlgButton (hwnd, IDC_AUTHTIS, cfg.try_tis_auth);
1819         SetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile);
1820         SetDlgItemText (hwnd, IDC_CMDEDIT, cfg.remote_cmd);
1821         break;
1822       case WM_COMMAND:
1823         switch (LOWORD(wParam)) {
1824           case IDC_TTEDIT:
1825             if (HIWORD(wParam) == EN_CHANGE)
1826             GetDlgItemText (hwnd, IDC_TTEDIT, cfg.termtype,
1827                             sizeof(cfg.termtype)-1);
1828             break;
1829           case IDC_LOGEDIT:
1830             if (HIWORD(wParam) == EN_CHANGE)
1831                 GetDlgItemText (hwnd, IDC_LOGEDIT, cfg.username,
1832                                 sizeof(cfg.username)-1);
1833             break;
1834           case IDC_NOPTY:
1835             if (HIWORD(wParam) == BN_CLICKED ||
1836                 HIWORD(wParam) == BN_DOUBLECLICKED)
1837                 cfg.nopty = IsDlgButtonChecked (hwnd, IDC_NOPTY);
1838             break;
1839           case IDC_AGENTFWD:
1840             if (HIWORD(wParam) == BN_CLICKED ||
1841                 HIWORD(wParam) == BN_DOUBLECLICKED)
1842                 cfg.agentfwd = IsDlgButtonChecked (hwnd, IDC_AGENTFWD);
1843             break;
1844           case IDC_CIPHER3DES:
1845           case IDC_CIPHERBLOWF:
1846           case IDC_CIPHERDES:
1847             if (HIWORD(wParam) == BN_CLICKED ||
1848                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1849                 if (IsDlgButtonChecked (hwnd, IDC_CIPHER3DES))
1850                     cfg.cipher = CIPHER_3DES;
1851                 else if (IsDlgButtonChecked (hwnd, IDC_CIPHERBLOWF))
1852                     cfg.cipher = CIPHER_BLOWFISH;
1853                 else if (IsDlgButtonChecked (hwnd, IDC_CIPHERDES))
1854                     cfg.cipher = CIPHER_DES;
1855             }
1856             break;
1857           case IDC_SSHPROT1:
1858           case IDC_SSHPROT2:
1859             if (HIWORD(wParam) == BN_CLICKED ||
1860                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1861                 if (IsDlgButtonChecked (hwnd, IDC_SSHPROT1))
1862                     cfg.sshprot = 1;
1863                 else if (IsDlgButtonChecked (hwnd, IDC_SSHPROT2))
1864                     cfg.sshprot = 2;
1865             }
1866             break;
1867           case IDC_AUTHTIS:
1868             if (HIWORD(wParam) == BN_CLICKED ||
1869                 HIWORD(wParam) == BN_DOUBLECLICKED)
1870                 cfg.try_tis_auth = IsDlgButtonChecked (hwnd, IDC_AUTHTIS);
1871             break;
1872           case IDC_PKEDIT:
1873             if (HIWORD(wParam) == EN_CHANGE)
1874                 GetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile,
1875                                 sizeof(cfg.keyfile)-1);
1876             break;
1877           case IDC_CMDEDIT:
1878             if (HIWORD(wParam) == EN_CHANGE)
1879                 GetDlgItemText (hwnd, IDC_CMDEDIT, cfg.remote_cmd,
1880                                 sizeof(cfg.remote_cmd)-1);
1881             break;
1882           case IDC_PKBUTTON:
1883             /*
1884              * FIXME: this crashes. Find out why.
1885              */
1886             memset(&of, 0, sizeof(of));
1887 #ifdef OPENFILENAME_SIZE_VERSION_400
1888             of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1889 #else
1890             of.lStructSize = sizeof(of);
1891 #endif
1892             of.hwndOwner = hwnd;
1893             of.lpstrFilter = "All Files\0*\0\0\0";
1894             of.lpstrCustomFilter = NULL;
1895             of.nFilterIndex = 1;
1896             of.lpstrFile = filename; strcpy(filename, cfg.keyfile);
1897             of.nMaxFile = sizeof(filename);
1898             of.lpstrFileTitle = NULL;
1899             of.lpstrInitialDir = NULL;
1900             of.lpstrTitle = "Select Public Key File";
1901             of.Flags = 0;
1902             if (GetOpenFileName(&of)) {
1903                 strcpy(cfg.keyfile, filename);
1904                 SetDlgItemText (hwnd, IDC_PKEDIT, cfg.keyfile);
1905             }
1906             break;
1907         }
1908         break;
1909     }
1910     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1911 }
1912
1913 static int CALLBACK SelectionProc (HWND hwnd, UINT msg,
1914                                     WPARAM wParam, LPARAM lParam) {
1915     struct ctlpos cp;
1916     int i;
1917     enum { controlstartvalue = 1000,
1918          IDC_MBSTATIC,
1919          IDC_MBWINDOWS,
1920          IDC_MBXTERM,
1921          IDC_CCSTATIC,
1922          IDC_CCLIST,
1923          IDC_CCSET,
1924          IDC_CCSTATIC2,
1925          IDC_CCEDIT
1926     };
1927
1928     switch (msg) {
1929       case WM_INITDIALOG:
1930         /* Accelerators used: [aco] stwx */
1931         ctlposinit(&cp, hwnd);
1932         radiobig(&cp, "Action of mouse buttons:", IDC_MBSTATIC,
1933                  "&Windows (Right pastes, Middle extends)", IDC_MBWINDOWS,
1934                  "&xterm (Right extends, Middle pastes)", IDC_MBXTERM,
1935                  NULL);
1936         charclass(&cp, "Character classes:", IDC_CCSTATIC, IDC_CCLIST,
1937                   "&Set", IDC_CCSET, IDC_CCEDIT,
1938                   "&to class", IDC_CCSTATIC2);
1939
1940         CheckRadioButton (hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
1941                           cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS);
1942         {
1943             static int tabs[4] = {25, 61, 96, 128};
1944             SendDlgItemMessage (hwnd, IDC_CCLIST, LB_SETTABSTOPS, 4,
1945                                 (LPARAM) tabs);
1946         }
1947         for (i=0; i<256; i++) {
1948             char str[100];
1949             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1950                     (i>=0x21 && i != 0x7F) ? i : ' ',
1951                     cfg.wordness[i]);
1952             SendDlgItemMessage (hwnd, IDC_CCLIST, LB_ADDSTRING, 0,
1953                                 (LPARAM) str);
1954         }
1955         break;
1956       case WM_COMMAND:
1957         switch (LOWORD(wParam)) {
1958           case IDC_MBWINDOWS:
1959           case IDC_MBXTERM:
1960             cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC_MBXTERM);
1961             break;
1962           case IDC_CCSET:
1963             {
1964                 BOOL ok;
1965                 int i;
1966                 int n = GetDlgItemInt (hwnd, IDC_CCEDIT, &ok, FALSE);
1967
1968                 if (!ok)
1969                     MessageBeep (0);
1970                 else {
1971                     for (i=0; i<256; i++)
1972                         if (SendDlgItemMessage (hwnd, IDC_CCLIST, LB_GETSEL,
1973                                                 i, 0)) {
1974                             char str[100];
1975                             cfg.wordness[i] = n;
1976                             SendDlgItemMessage (hwnd, IDC_CCLIST,
1977                                                 LB_DELETESTRING, i, 0);
1978                             sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
1979                                     (i>=0x21 && i != 0x7F) ? i : ' ',
1980                                     cfg.wordness[i]);
1981                             SendDlgItemMessage (hwnd, IDC_CCLIST,
1982                                                 LB_INSERTSTRING, i,
1983                                                 (LPARAM)str);
1984                         }
1985                 }
1986             }
1987             break;
1988         }
1989         break;
1990     }
1991     return GeneralPanelProc (hwnd, msg, wParam, lParam);
1992 }
1993
1994 static int CALLBACK ColourProc (HWND hwnd, UINT msg,
1995                                     WPARAM wParam, LPARAM lParam) {
1996     static const char *const colours[] = {
1997         "Default Foreground", "Default Bold Foreground",
1998         "Default Background", "Default Bold Background",
1999         "Cursor Text", "Cursor Colour",
2000         "ANSI Black", "ANSI Black Bold",
2001         "ANSI Red", "ANSI Red Bold",
2002         "ANSI Green", "ANSI Green Bold",
2003         "ANSI Yellow", "ANSI Yellow Bold",
2004         "ANSI Blue", "ANSI Blue Bold",
2005         "ANSI Magenta", "ANSI Magenta Bold",
2006         "ANSI Cyan", "ANSI Cyan Bold",
2007         "ANSI White", "ANSI White Bold"
2008     };
2009     static const int permanent[] = {
2010         TRUE, FALSE, TRUE, FALSE, TRUE, TRUE,
2011         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE,
2012         TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE
2013     };
2014     struct ctlpos cp;
2015     enum { controlstartvalue = 1000,
2016          IDC_BOLDCOLOUR,
2017          IDC_PALETTE,
2018          IDC_STATIC,
2019          IDC_LIST,
2020          IDC_RSTATIC,
2021          IDC_GSTATIC,
2022          IDC_BSTATIC,
2023          IDC_RVALUE,
2024          IDC_GVALUE,
2025          IDC_BVALUE,
2026          IDC_CHANGE
2027     };
2028
2029     switch (msg) {
2030       case WM_INITDIALOG:
2031         /* Accelerators used: [aco] bmlu */
2032         ctlposinit(&cp, hwnd);
2033         checkbox(&cp, "&Bolded text is a different colour", IDC_BOLDCOLOUR);
2034         checkbox(&cp, "Attempt to use &logical palettes", IDC_PALETTE);
2035         colouredit(&cp, "Select a colo&ur and click to modify it:",
2036                    IDC_STATIC, IDC_LIST,
2037                    "&Modify...", IDC_CHANGE,
2038                    "Red:", IDC_RSTATIC, IDC_RVALUE,
2039                    "Green:", IDC_GSTATIC, IDC_GVALUE,
2040                    "Blue:", IDC_BSTATIC, IDC_BVALUE, NULL);
2041
2042         CheckDlgButton (hwnd, IDC_BOLDCOLOUR, cfg.bold_colour);
2043         CheckDlgButton (hwnd, IDC_PALETTE, cfg.try_palette);
2044         {
2045             int i;
2046             for (i=0; i<22; i++)
2047                 if (cfg.bold_colour || permanent[i])
2048                     SendDlgItemMessage (hwnd, IDC_LIST, LB_ADDSTRING, 0,
2049                                         (LPARAM) colours[i]);
2050         }
2051         SendDlgItemMessage (hwnd, IDC_LIST, LB_SETCURSEL, 0, 0);
2052         SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[0][0], FALSE);
2053         SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[0][1], FALSE);
2054         SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[0][2], FALSE);
2055         break;
2056       case WM_COMMAND:
2057         switch (LOWORD(wParam)) {
2058           case IDC_BOLDCOLOUR:
2059             if (HIWORD(wParam) == BN_CLICKED ||
2060                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2061                 int n, i;
2062                 cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC_BOLDCOLOUR);
2063                 n = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCOUNT, 0, 0);
2064                 if (cfg.bold_colour && n!=22) {
2065                     for (i=0; i<22; i++)
2066                         if (!permanent[i])
2067                             SendDlgItemMessage (hwnd, IDC_LIST,
2068                                                 LB_INSERTSTRING, i,
2069                                                 (LPARAM) colours[i]);
2070                 } else if (!cfg.bold_colour && n!=12) {
2071                     for (i=22; i-- ;)
2072                         if (!permanent[i])
2073                             SendDlgItemMessage (hwnd, IDC_LIST,
2074                                                 LB_DELETESTRING, i, 0);
2075                 }
2076             }
2077             break;
2078           case IDC_PALETTE:
2079             if (HIWORD(wParam) == BN_CLICKED ||
2080                 HIWORD(wParam) == BN_DOUBLECLICKED)
2081                 cfg.try_palette = IsDlgButtonChecked (hwnd, IDC_PALETTE);
2082             break;
2083           case IDC_LIST:
2084             if (HIWORD(wParam) == LBN_DBLCLK ||
2085                 HIWORD(wParam) == LBN_SELCHANGE) {
2086                 int i = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCURSEL,
2087                                             0, 0);
2088                 if (!cfg.bold_colour)
2089                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
2090                 SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[i][0], FALSE);
2091                 SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[i][1], FALSE);
2092                 SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[i][2], FALSE);
2093             }
2094             break;
2095           case IDC_CHANGE:
2096             if (HIWORD(wParam) == BN_CLICKED ||
2097                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2098                 static CHOOSECOLOR cc;
2099                 static DWORD custom[16] = {0};   /* zero initialisers */
2100                 int i = SendDlgItemMessage (hwnd, IDC_LIST, LB_GETCURSEL,
2101                                             0, 0);
2102                 if (!cfg.bold_colour)
2103                     i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
2104                 cc.lStructSize = sizeof(cc);
2105                 cc.hwndOwner = hwnd;
2106                 cc.hInstance = (HWND)hinst;
2107                 cc.lpCustColors = custom;
2108                 cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1],
2109                                     cfg.colours[i][2]);
2110                 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
2111                 if (ChooseColor(&cc)) {
2112                     cfg.colours[i][0] =
2113                         (unsigned char) (cc.rgbResult & 0xFF);
2114                     cfg.colours[i][1] =
2115                         (unsigned char) (cc.rgbResult >> 8) & 0xFF;
2116                     cfg.colours[i][2] =
2117                         (unsigned char) (cc.rgbResult >> 16) & 0xFF;
2118                     SetDlgItemInt (hwnd, IDC_RVALUE, cfg.colours[i][0],
2119                                    FALSE);
2120                     SetDlgItemInt (hwnd, IDC_GVALUE, cfg.colours[i][1],
2121                                    FALSE);
2122                     SetDlgItemInt (hwnd, IDC_BVALUE, cfg.colours[i][2],
2123                                    FALSE);
2124                 }
2125             }
2126             break;
2127         }
2128         break;
2129     }
2130     return GeneralPanelProc (hwnd, msg, wParam, lParam);
2131 }
2132
2133 static int CALLBACK TranslationProc (HWND hwnd, UINT msg,
2134                                   WPARAM wParam, LPARAM lParam) {
2135     struct ctlpos cp;
2136     enum { controlstartvalue = 1000,
2137          IDC_XLATSTATIC,
2138          IDC_NOXLAT,
2139          IDC_KOI8WIN1251,
2140          IDC_88592WIN1250,
2141          IDC_CAPSLOCKCYR,
2142          IDC_VTSTATIC,
2143          IDC_VTXWINDOWS,
2144          IDC_VTOEMANSI,
2145          IDC_VTOEMONLY,
2146          IDC_VTPOORMAN
2147     };
2148
2149     switch (msg) {
2150       case WM_INITDIALOG:
2151         /* Accelerators used: [aco] beiknpsx */
2152         ctlposinit(&cp, hwnd);
2153         radiobig(&cp,
2154                  "Handling of VT100 line drawing characters:", IDC_VTSTATIC,
2155                  "Font has &XWindows encoding", IDC_VTXWINDOWS,
2156                  "Use font in &both ANSI and OEM modes", IDC_VTOEMANSI,
2157                  "Use font in O&EM mode only", IDC_VTOEMONLY,
2158                  "&Poor man's line drawing (""+"", ""-"" and ""|"")",
2159                  IDC_VTPOORMAN, NULL);
2160         radiobig(&cp,
2161                  "Character set translation:", IDC_XLATSTATIC,
2162                  "&None", IDC_NOXLAT,
2163                  "&KOI8 / Win-1251", IDC_KOI8WIN1251,
2164                  "&ISO-8859-2 / Win-1250", IDC_88592WIN1250, NULL);
2165         checkbox(&cp, "CAP&S LOCK acts as cyrillic switch", IDC_CAPSLOCKCYR);
2166
2167         CheckRadioButton (hwnd, IDC_NOXLAT, IDC_88592WIN1250,
2168                           cfg.xlat_88592w1250 ? IDC_88592WIN1250 :
2169                           cfg.xlat_enablekoiwin ? IDC_KOI8WIN1251 :
2170                           IDC_NOXLAT);
2171         CheckDlgButton (hwnd, IDC_CAPSLOCKCYR, cfg.xlat_capslockcyr);
2172         CheckRadioButton (hwnd, IDC_VTXWINDOWS, IDC_VTPOORMAN,
2173                           cfg.vtmode == VT_XWINDOWS ? IDC_VTXWINDOWS :
2174                           cfg.vtmode == VT_OEMANSI ? IDC_VTOEMANSI :
2175                           cfg.vtmode == VT_OEMONLY ? IDC_VTOEMONLY :
2176                           IDC_VTPOORMAN);
2177       case WM_COMMAND:
2178         switch (LOWORD(wParam)) {
2179           case IDC_NOXLAT:
2180           case IDC_KOI8WIN1251:
2181           case IDC_88592WIN1250:
2182             cfg.xlat_enablekoiwin =
2183                 IsDlgButtonChecked (hwnd, IDC_KOI8WIN1251);
2184             cfg.xlat_88592w1250 =
2185                 IsDlgButtonChecked (hwnd, IDC_88592WIN1250);
2186             break;
2187           case IDC_CAPSLOCKCYR:
2188             if (HIWORD(wParam) == BN_CLICKED ||
2189                 HIWORD(wParam) == BN_DOUBLECLICKED) {
2190                 cfg.xlat_capslockcyr =
2191                     IsDlgButtonChecked (hwnd, IDC_CAPSLOCKCYR);
2192             }
2193             break;
2194           case IDC_VTXWINDOWS:
2195           case IDC_VTOEMANSI:
2196           case IDC_VTOEMONLY:
2197           case IDC_VTPOORMAN:
2198             cfg.vtmode =
2199                 (IsDlgButtonChecked (hwnd, IDC_VTXWINDOWS) ? VT_XWINDOWS :
2200                  IsDlgButtonChecked (hwnd, IDC_VTOEMANSI) ? VT_OEMANSI :
2201                  IsDlgButtonChecked (hwnd, IDC_VTOEMONLY) ? VT_OEMONLY :
2202                  VT_POORMAN);
2203             break;
2204         }
2205     }
2206     return GeneralPanelProc (hwnd, msg, wParam, lParam);
2207 }
2208
2209 static DLGPROC panelproc[NPANELS] = {
2210     ConnectionProc, KeyboardProc, TerminalProc, WindowProc,
2211     TelnetProc, SshProc, SelectionProc, ColourProc, TranslationProc
2212 };
2213
2214 static char *names[NPANELS] = {
2215     "Connection", "Keyboard", "Terminal", "Window", "Telnet",
2216     "SSH", "Selection", "Colours", "Translation"
2217 };
2218
2219 static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8};
2220 static int reconfp[RECONF_NPANELS] = { 1, 2, 3, 6, 7, 8};
2221
2222 static HWND makesubdialog(HWND hwnd, int x, int y, int w, int h, int n) {
2223     RECT r;
2224     HWND ret;
2225     WPARAM font;
2226     r.left = x; r.top = y;
2227     r.right = r.left + w; r.bottom = r.top + h;
2228     MapDialogRect(hwnd, &r);
2229     ret = CreateWindowEx(WS_EX_CONTROLPARENT,
2230                            WC_DIALOG, "",   /* no title */
2231                            WS_CHILD | WS_VISIBLE | DS_SETFONT,
2232                            r.left, r.top,
2233                            r.right-r.left, r.bottom-r.top,
2234                            hwnd, (HMENU)IDC_SUBDLG,
2235                            hinst, NULL);
2236     SetWindowLong (ret, DWL_DLGPROC, (LONG)panelproc[n]);
2237     font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2238     SendMessage (ret, WM_SETFONT, font, MAKELPARAM(0, 0));
2239     SendMessage (ret, WM_INITDIALOG, 0, 0);
2240     return ret;
2241 }
2242
2243 static int GenericMainDlgProc (HWND hwnd, UINT msg,
2244                                WPARAM wParam, LPARAM lParam,
2245                                int npanels, int *panelnums, HWND *page) {
2246     HWND hw, tabctl;
2247
2248     switch (msg) {
2249       case WM_INITDIALOG:
2250         {                              /* centre the window */
2251             RECT rs, rd;
2252
2253             hw = GetDesktopWindow();
2254             if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
2255                 MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
2256                             (rs.bottom + rs.top + rd.top - rd.bottom)/2,
2257                             rd.right-rd.left, rd.bottom-rd.top, TRUE);
2258         }
2259         {
2260             RECT r;
2261             r.left = 3; r.right = r.left + 174;
2262             r.top = 3; r.bottom = r.top + 193;
2263             MapDialogRect(hwnd, &r);
2264             tabctl = CreateWindowEx(0, WC_TABCONTROL, "",
2265                                     WS_CHILD | WS_VISIBLE |
2266                                     WS_TABSTOP | TCS_MULTILINE,
2267                                     r.left, r.top,
2268                                     r.right-r.left, r.bottom-r.top,
2269                                     hwnd, (HMENU)IDC_TAB, hinst, NULL);
2270
2271             if (!tabctl) {
2272                 struct ctlpos cp;
2273                 ctlposinit2(&cp, hwnd);
2274                 ersatztab(&cp, "Category:", IDC_TABSTATIC1, IDC_TABLIST,
2275                           IDC_TABSTATIC2);
2276             } else {
2277                 WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
2278                 SendMessage(tabctl, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
2279             }
2280         }
2281         *page = NULL;
2282         if (tabctl) {                  /* initialise the tab control */
2283             TC_ITEMHEADER tab;
2284             int i;
2285
2286             for (i=0; i<npanels; i++) {
2287                 tab.mask = TCIF_TEXT;
2288                 tab.pszText = names[panelnums[i]];
2289                 TabCtrl_InsertItem (tabctl, i, &tab);
2290             }
2291         } else {
2292             int i;
2293
2294             for (i=0; i<npanels; i++) {
2295                 SendDlgItemMessage(hwnd, IDC_TABLIST, CB_ADDSTRING,
2296                                    0, (LPARAM)names[panelnums[i]]);
2297             }
2298             SendDlgItemMessage(hwnd, IDC_TABLIST, CB_SETCURSEL, 0, 0);
2299         }
2300         *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[0]);
2301         SetFocus (*page);
2302         return 0;
2303       case WM_NOTIFY:
2304         if (LOWORD(wParam) == IDC_TAB &&
2305             ((LPNMHDR)lParam)->code == TCN_SELCHANGE) {
2306             int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
2307             if (*page)
2308                 DestroyWindow (*page);
2309             *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2310             SetFocus (((LPNMHDR)lParam)->hwndFrom);   /* ensure focus stays */
2311             return 0;
2312         }
2313         break;
2314       case WM_COMMAND:
2315         switch (LOWORD(wParam)) {
2316           case IDC_TABLIST:
2317             if (HIWORD(wParam) == CBN_SELCHANGE) {
2318                 HWND tablist = GetDlgItem (hwnd, IDC_TABLIST);
2319                 int i = SendMessage (tablist, CB_GETCURSEL, 0, 0);
2320                 if (*page)
2321                     DestroyWindow (*page);
2322                 *page = makesubdialog(hwnd, 6, 30, 168, 163, panelnums[i]);
2323                 SetFocus(tablist);     /* ensure focus stays */
2324                 return 0;
2325             }
2326             break;
2327           case IDOK:
2328             if (*cfg.host)
2329                 EndDialog (hwnd, 1);
2330             else
2331                 MessageBeep (0);
2332             return 0;
2333           case IDCANCEL:
2334             EndDialog (hwnd, 0);
2335             return 0;
2336         }
2337         return 0;
2338       case WM_CLOSE:
2339         EndDialog (hwnd, 0);
2340         return 0;
2341
2342         /* Grrr Explorer will maximize Dialogs! */
2343       case WM_SIZE:
2344         if (wParam == SIZE_MAXIMIZED)
2345            force_normal(hwnd);
2346         return 0;
2347     }
2348     return 0;
2349 }
2350
2351 static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
2352                                  WPARAM wParam, LPARAM lParam) {
2353     static HWND page = NULL;
2354
2355     if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) {
2356     }
2357     if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) {
2358         EnableWindow(hwnd, 0);
2359         DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2360                   GetParent(hwnd), AboutProc);
2361         EnableWindow(hwnd, 1);
2362         SetActiveWindow(hwnd);
2363     }
2364     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2365                                MAIN_NPANELS, mainp, &page);
2366 }
2367
2368 static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg,
2369                                    WPARAM wParam, LPARAM lParam) {
2370     static HWND page;
2371     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
2372                                RECONF_NPANELS, reconfp, &page);
2373 }
2374
2375 void get_sesslist(int allocate) {
2376     static char otherbuf[2048];
2377     static char *buffer;
2378     int buflen, bufsize, i;
2379     char *p, *ret;
2380     void *handle;
2381
2382     if (allocate) {
2383         
2384         if ((handle = enum_settings_start()) == NULL)
2385             return;
2386
2387         buflen = bufsize = 0;
2388         buffer = NULL;
2389         do {
2390             ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf));
2391             if (ret) {
2392                 int len = strlen(otherbuf)+1;
2393                 if (bufsize < buflen+len) {
2394                     bufsize = buflen + len + 2048;
2395                     buffer = srealloc(buffer, bufsize);
2396                 }
2397                 strcpy(buffer+buflen, otherbuf);
2398                 buflen += strlen(buffer+buflen)+1;
2399             }
2400         } while (ret);
2401         enum_settings_finish(handle);
2402         buffer = srealloc(buffer, buflen+1);
2403         buffer[buflen] = '\0';
2404
2405         p = buffer;
2406         nsessions = 1;                 /* "Default Settings" counts as one */
2407         while (*p) {
2408             if (strcmp(p, "Default Settings"))
2409                 nsessions++;
2410             while (*p) p++;
2411             p++;
2412         }
2413
2414         sessions = smalloc(nsessions * sizeof(char *));
2415         sessions[0] = "Default Settings";
2416         p = buffer;
2417         i = 1;
2418         while (*p) {
2419             if (strcmp(p, "Default Settings"))
2420                 sessions[i++] = p;
2421             while (*p) p++;
2422             p++;
2423         }
2424     } else {
2425         sfree (buffer);
2426         sfree (sessions);
2427     }
2428 }
2429
2430 int do_config (void) {
2431     int ret;
2432
2433     get_sesslist(TRUE);
2434     savedsession[0] = '\0';
2435     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
2436     get_sesslist(FALSE);
2437
2438     return ret;
2439 }
2440
2441 int do_reconfig (HWND hwnd) {
2442     Config backup_cfg;
2443     int ret;
2444
2445     backup_cfg = cfg;                  /* structure copy */
2446     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc);
2447     if (!ret)
2448         cfg = backup_cfg;              /* structure copy */
2449     else
2450         force_normal(hwnd);
2451
2452     return ret;
2453 }
2454
2455 void do_defaults (char *session) {
2456     if (session)
2457         load_settings (session, TRUE);
2458     else
2459         load_settings ("Default Settings", FALSE);
2460 }
2461
2462 void logevent (char *string) {
2463     if (nevents >= negsize) {
2464         negsize += 64;
2465         events = srealloc (events, negsize * sizeof(*events));
2466     }
2467     events[nevents] = smalloc(1+strlen(string));
2468     strcpy (events[nevents], string);
2469     nevents++;
2470     if (logbox) {
2471         int count;
2472         SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
2473                             0, (LPARAM)string);
2474         count = SendDlgItemMessage (logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
2475         SendDlgItemMessage (logbox, IDN_LIST, LB_SETTOPINDEX, count-1, 0);
2476     }
2477 }
2478
2479 void showeventlog (HWND hwnd) {
2480     if (!logbox) {
2481         logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX),
2482                                hwnd, LogProc);
2483         ShowWindow (logbox, SW_SHOWNORMAL);
2484     }
2485 }
2486
2487 void showabout (HWND hwnd) {
2488     if (!abtbox) {
2489         abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
2490                                hwnd, AboutProc);
2491         ShowWindow (abtbox, SW_SHOWNORMAL);
2492     }
2493 }
2494
2495 void verify_ssh_host_key(char *host, int port, char *keytype,
2496                          char *keystr, char *fingerprint) {
2497     int ret;
2498
2499     static const char absentmsg[] =
2500         "The server's host key is not cached in the registry. You\n"
2501         "have no guarantee that the server is the computer you\n"
2502         "think it is.\n"
2503         "The server's key fingerprint is:\n"
2504         "%s\n"
2505         "If you trust this host, hit Yes to add the key to\n"
2506         "PuTTY's cache and carry on connecting.\n"
2507         "If you do not trust this host, hit No to abandon the\n"
2508         "connection.\n";
2509
2510     static const char wrongmsg[] =
2511         "WARNING - POTENTIAL SECURITY BREACH!\n"
2512         "\n"
2513         "The server's host key does not match the one PuTTY has\n"
2514         "cached in the registry. This means that either the\n"
2515         "server administrator has changed the host key, or you\n"
2516         "have actually connected to another computer pretending\n"
2517         "to be the server.\n"
2518         "The new key fingerprint is:\n"
2519         "%s\n"
2520         "If you were expecting this change and trust the new key,\n"
2521         "hit Yes to update PuTTY's cache and continue connecting.\n"
2522         "If you want to carry on connecting but without updating\n"
2523         "the cache, hit No.\n"
2524         "If you want to abandon the connection completely, hit\n"
2525         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n"
2526         "choice.\n";
2527
2528     static const char mbtitle[] = "PuTTY Security Alert";
2529
2530     
2531     char message[160+                  /* sensible fingerprint max size */
2532                  (sizeof(absentmsg) > sizeof(wrongmsg) ?
2533                   sizeof(absentmsg) : sizeof(wrongmsg))];
2534
2535     /*
2536      * Verify the key against the registry.
2537      */
2538     ret = verify_host_key(host, port, keytype, keystr);
2539
2540     if (ret == 0)                      /* success - key matched OK */
2541         return;
2542     if (ret == 2) {                    /* key was different */
2543         int mbret;
2544         sprintf(message, wrongmsg, fingerprint);
2545         mbret = MessageBox(NULL, message, mbtitle,
2546                            MB_ICONWARNING | MB_YESNOCANCEL);
2547         if (mbret == IDYES)
2548             store_host_key(host, port, keytype, keystr);
2549         if (mbret == IDCANCEL)
2550             exit(0);
2551     }
2552     if (ret == 1) {                    /* key was absent */
2553         int mbret;
2554         sprintf(message, absentmsg, fingerprint);
2555         mbret = MessageBox(NULL, message, mbtitle,
2556                            MB_ICONWARNING | MB_YESNO);
2557         if (mbret == IDNO)
2558             exit(0);
2559         store_host_key(host, port, keytype, keystr);
2560     }
2561 }