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