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