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