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