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