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