Fix various small compiler warnings, mostly unused local variables
[u/mdw/putty] / winctrls.c
1 /*
2 * winctrls.c: routines to self-manage the controls in a dialog
3 * box.
4 */
5
6 #include <windows.h>
7 #include <commctrl.h>
8
9 #include "winstuff.h"
10 #include "puttymem.h"
11
12 #include "putty.h"
13
14 #define GAPBETWEEN 3
15 #define GAPWITHIN 1
16 #define GAPXBOX 7
17 #define GAPYBOX 4
18 #define DLGWIDTH 168
19 #define STATICHEIGHT 8
20 #define CHECKBOXHEIGHT 8
21 #define RADIOHEIGHT 8
22 #define EDITHEIGHT 12
23 #define COMBOHEIGHT 12
24 #define PUSHBTNHEIGHT 14
25 #define PROGBARHEIGHT 14
26
27 void ctlposinit(struct ctlpos *cp, HWND hwnd,
28 int leftborder, int rightborder, int topborder)
29 {
30 RECT r, r2;
31 cp->hwnd = hwnd;
32 cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0);
33 cp->ypos = topborder;
34 GetClientRect(hwnd, &r);
35 r2.left = r2.top = 0;
36 r2.right = 4;
37 r2.bottom = 8;
38 MapDialogRect(hwnd, &r2);
39 cp->dlu4inpix = r2.right;
40 cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN;
41 cp->xoff = leftborder;
42 cp->width -= leftborder + rightborder;
43 }
44
45 HWND doctl(struct ctlpos *cp, RECT r,
46 char *wclass, int wstyle, int exstyle, char *wtext, int wid)
47 {
48 HWND ctl;
49 /*
50 * Note nonstandard use of RECT. This is deliberate: by
51 * transforming the width and height directly we arrange to
52 * have all supposedly same-sized controls really same-sized.
53 */
54
55 r.left += cp->xoff;
56 MapDialogRect(cp->hwnd, &r);
57
58 ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
59 r.left, r.top, r.right, r.bottom,
60 cp->hwnd, (HMENU) wid, hinst, NULL);
61 SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
62 return ctl;
63 }
64
65 /*
66 * A title bar across the top of a sub-dialog.
67 */
68 void bartitle(struct ctlpos *cp, char *name, int id)
69 {
70 RECT r;
71
72 r.left = GAPBETWEEN;
73 r.right = cp->width;
74 r.top = cp->ypos;
75 r.bottom = STATICHEIGHT;
76 cp->ypos += r.bottom + GAPBETWEEN;
77 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id);
78 }
79
80 /*
81 * Begin a grouping box, with or without a group title.
82 */
83 void beginbox(struct ctlpos *cp, char *name, int idbox)
84 {
85 cp->boxystart = cp->ypos;
86 if (!name)
87 cp->boxystart -= STATICHEIGHT / 2;
88 if (name)
89 cp->ypos += STATICHEIGHT;
90 cp->ypos += GAPYBOX;
91 cp->width -= 2 * GAPXBOX;
92 cp->xoff += GAPXBOX;
93 cp->boxid = idbox;
94 cp->boxtext = name;
95 }
96
97 /*
98 * End a grouping box.
99 */
100 void endbox(struct ctlpos *cp)
101 {
102 RECT r;
103 cp->xoff -= GAPXBOX;
104 cp->width += 2 * GAPXBOX;
105 cp->ypos += GAPYBOX - GAPBETWEEN;
106 r.left = GAPBETWEEN;
107 r.right = cp->width;
108 r.top = cp->boxystart;
109 r.bottom = cp->ypos - cp->boxystart;
110 doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0,
111 cp->boxtext ? cp->boxtext : "", cp->boxid);
112 cp->ypos += GAPYBOX;
113 }
114
115 /*
116 * Some edit boxes. Each one has a static above it. The percentages
117 * of the horizontal space are provided.
118 */
119 void multiedit(struct ctlpos *cp, ...)
120 {
121 RECT r;
122 va_list ap;
123 int percent, xpos;
124
125 percent = xpos = 0;
126 va_start(ap, cp);
127 while (1) {
128 char *text;
129 int staticid, editid, pcwidth;
130 text = va_arg(ap, char *);
131 if (!text)
132 break;
133 staticid = va_arg(ap, int);
134 editid = va_arg(ap, int);
135 pcwidth = va_arg(ap, int);
136
137 r.left = xpos + GAPBETWEEN;
138 percent += pcwidth;
139 xpos = (cp->width + GAPBETWEEN) * percent / 100;
140 r.right = xpos - r.left;
141
142 r.top = cp->ypos;
143 r.bottom = STATICHEIGHT;
144 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
145 r.top = cp->ypos + 8 + GAPWITHIN;
146 r.bottom = EDITHEIGHT;
147 doctl(cp, r, "EDIT",
148 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
149 WS_EX_CLIENTEDGE, "", editid);
150 }
151 va_end(ap);
152 cp->ypos += STATICHEIGHT + GAPWITHIN + EDITHEIGHT + GAPBETWEEN;
153 }
154
155 /*
156 * A static line, followed by a full-width drop-down list (ie a
157 * non-editing combo box).
158 */
159 void dropdownlist(struct ctlpos *cp, char *text, int staticid, int listid)
160 {
161 RECT r;
162
163 r.left = GAPBETWEEN;
164 r.right = cp->width;
165
166 r.top = cp->ypos;
167 r.bottom = STATICHEIGHT;
168 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
169 r.top = cp->ypos + 8 + GAPWITHIN;
170 r.bottom = COMBOHEIGHT * 10;
171 doctl(cp, r, "COMBOBOX",
172 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
173 CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);
174
175 cp->ypos += STATICHEIGHT + GAPWITHIN + COMBOHEIGHT + GAPBETWEEN;
176 }
177
178 static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
179 {
180 RECT r;
181 int group;
182 int i;
183 char *btext;
184
185 group = WS_GROUP;
186 i = 0;
187 btext = va_arg(ap, char *);
188 while (1) {
189 char *nextbtext;
190 int bid;
191 if (!btext)
192 break;
193 if (i == nacross) {
194 cp->ypos += r.bottom + GAPBETWEEN;
195 i = 0;
196 }
197 bid = va_arg(ap, int);
198 nextbtext = va_arg(ap, char *);
199 r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross;
200 if (nextbtext)
201 r.right =
202 (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left;
203 else
204 r.right = cp->width - r.left;
205 r.top = cp->ypos;
206 r.bottom = RADIOHEIGHT;
207 doctl(cp, r, "BUTTON",
208 BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
209 group, 0, btext, bid);
210 group = 0;
211 i++;
212 btext = nextbtext;
213 }
214 cp->ypos += r.bottom + GAPBETWEEN;
215 }
216
217 /*
218 * A set of radio buttons on the same line, with a static above
219 * them. `nacross' dictates how many parts the line is divided into
220 * (you might want this not to equal the number of buttons if you
221 * needed to line up some 2s and some 3s to look good in the same
222 * panel).
223 *
224 * There's a bit of a hack in here to ensure that if nacross
225 * exceeds the actual number of buttons, the rightmost button
226 * really does get all the space right to the edge of the line, so
227 * you can do things like
228 *
229 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
230 */
231 void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
232 {
233 RECT r;
234 va_list ap;
235
236 r.left = GAPBETWEEN;
237 r.top = cp->ypos;
238 r.right = cp->width;
239 r.bottom = STATICHEIGHT;
240 cp->ypos += r.bottom + GAPWITHIN;
241 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
242 va_start(ap, nacross);
243 radioline_common(cp, nacross, ap);
244 va_end(ap);
245 }
246
247 /*
248 * A set of radio buttons on the same line, without a static above
249 * them. Otherwise just like radioline.
250 */
251 void bareradioline(struct ctlpos *cp, int nacross, ...)
252 {
253 va_list ap;
254
255 va_start(ap, nacross);
256 radioline_common(cp, nacross, ap);
257 va_end(ap);
258 }
259
260 /*
261 * A set of radio buttons on multiple lines, with a static above
262 * them.
263 */
264 void radiobig(struct ctlpos *cp, char *text, int id, ...)
265 {
266 RECT r;
267 va_list ap;
268 int group;
269
270 r.left = GAPBETWEEN;
271 r.top = cp->ypos;
272 r.right = cp->width;
273 r.bottom = STATICHEIGHT;
274 cp->ypos += r.bottom + GAPWITHIN;
275 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
276 va_start(ap, id);
277 group = WS_GROUP;
278 while (1) {
279 char *btext;
280 int bid;
281 btext = va_arg(ap, char *);
282 if (!btext)
283 break;
284 bid = va_arg(ap, int);
285 r.left = GAPBETWEEN;
286 r.top = cp->ypos;
287 r.right = cp->width;
288 r.bottom = STATICHEIGHT;
289 cp->ypos += r.bottom + GAPWITHIN;
290 doctl(cp, r, "BUTTON",
291 BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
292 group, 0, btext, bid);
293 group = 0;
294 }
295 va_end(ap);
296 cp->ypos += GAPBETWEEN - GAPWITHIN;
297 }
298
299 /*
300 * A single standalone checkbox.
301 */
302 void checkbox(struct ctlpos *cp, char *text, int id)
303 {
304 RECT r;
305
306 r.left = GAPBETWEEN;
307 r.top = cp->ypos;
308 r.right = cp->width;
309 r.bottom = CHECKBOXHEIGHT;
310 cp->ypos += r.bottom + GAPBETWEEN;
311 doctl(cp, r, "BUTTON",
312 BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
313 text, id);
314 }
315
316 /*
317 * A single standalone static text control.
318 */
319 void statictext(struct ctlpos *cp, char *text, int id)
320 {
321 RECT r;
322
323 r.left = GAPBETWEEN;
324 r.top = cp->ypos;
325 r.right = cp->width;
326 r.bottom = STATICHEIGHT;
327 cp->ypos += r.bottom + GAPBETWEEN;
328 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
329 }
330
331 /*
332 * A button on the right hand side, with a static to its left.
333 */
334 void staticbtn(struct ctlpos *cp, char *stext, int sid,
335 char *btext, int bid)
336 {
337 const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
338 PUSHBTNHEIGHT : STATICHEIGHT);
339 RECT r;
340 int lwid, rwid, rpos;
341
342 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
343 lwid = rpos - 2 * GAPBETWEEN;
344 rwid = cp->width + GAPBETWEEN - rpos;
345
346 r.left = GAPBETWEEN;
347 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
348 r.right = lwid;
349 r.bottom = STATICHEIGHT;
350 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
351
352 r.left = rpos;
353 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
354 r.right = rwid;
355 r.bottom = PUSHBTNHEIGHT;
356 doctl(cp, r, "BUTTON",
357 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
358 0, btext, bid);
359
360 cp->ypos += height + GAPBETWEEN;
361 }
362
363 /*
364 * Like staticbtn, but two buttons.
365 */
366 void static2btn(struct ctlpos *cp, char *stext, int sid,
367 char *btext1, int bid1, char *btext2, int bid2)
368 {
369 const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
370 PUSHBTNHEIGHT : STATICHEIGHT);
371 RECT r;
372 int lwid, rwid1, rwid2, rpos1, rpos2;
373
374 rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
375 rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
376 lwid = rpos1 - 2 * GAPBETWEEN;
377 rwid1 = rpos2 - rpos1 - GAPBETWEEN;
378 rwid2 = cp->width + GAPBETWEEN - rpos2;
379
380 r.left = GAPBETWEEN;
381 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
382 r.right = lwid;
383 r.bottom = STATICHEIGHT;
384 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
385
386 r.left = rpos1;
387 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
388 r.right = rwid1;
389 r.bottom = PUSHBTNHEIGHT;
390 doctl(cp, r, "BUTTON",
391 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
392 0, btext1, bid1);
393
394 r.left = rpos2;
395 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
396 r.right = rwid2;
397 r.bottom = PUSHBTNHEIGHT;
398 doctl(cp, r, "BUTTON",
399 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
400 0, btext2, bid2);
401
402 cp->ypos += height + GAPBETWEEN;
403 }
404
405 /*
406 * An edit control on the right hand side, with a static to its left.
407 */
408 static void staticedit_internal(struct ctlpos *cp, char *stext,
409 int sid, int eid, int percentedit,
410 int style)
411 {
412 const int height = (EDITHEIGHT > STATICHEIGHT ?
413 EDITHEIGHT : STATICHEIGHT);
414 RECT r;
415 int lwid, rwid, rpos;
416
417 rpos =
418 GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100;
419 lwid = rpos - 2 * GAPBETWEEN;
420 rwid = cp->width + GAPBETWEEN - rpos;
421
422 r.left = GAPBETWEEN;
423 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
424 r.right = lwid;
425 r.bottom = STATICHEIGHT;
426 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
427
428 r.left = rpos;
429 r.top = cp->ypos + (height - EDITHEIGHT) / 2;
430 r.right = rwid;
431 r.bottom = EDITHEIGHT;
432 doctl(cp, r, "EDIT",
433 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style,
434 WS_EX_CLIENTEDGE, "", eid);
435
436 cp->ypos += height + GAPBETWEEN;
437 }
438
439 void staticedit(struct ctlpos *cp, char *stext,
440 int sid, int eid, int percentedit)
441 {
442 staticedit_internal(cp, stext, sid, eid, percentedit, 0);
443 }
444
445 void staticpassedit(struct ctlpos *cp, char *stext,
446 int sid, int eid, int percentedit)
447 {
448 staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD);
449 }
450
451 /*
452 * A big multiline edit control with a static labelling it.
453 */
454 void bigeditctrl(struct ctlpos *cp, char *stext,
455 int sid, int eid, int lines)
456 {
457 RECT r;
458
459 r.left = GAPBETWEEN;
460 r.top = cp->ypos;
461 r.right = cp->width;
462 r.bottom = STATICHEIGHT;
463 cp->ypos += r.bottom + GAPWITHIN;
464 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
465
466 r.left = GAPBETWEEN;
467 r.top = cp->ypos;
468 r.right = cp->width;
469 r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT;
470 cp->ypos += r.bottom + GAPBETWEEN;
471 doctl(cp, r, "EDIT",
472 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE,
473 WS_EX_CLIENTEDGE, "", eid);
474 }
475
476 /*
477 * A tab-control substitute when a real tab control is unavailable.
478 */
479 void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id)
480 {
481 const int height = (COMBOHEIGHT > STATICHEIGHT ?
482 COMBOHEIGHT : STATICHEIGHT);
483 RECT r;
484 int bigwid, lwid, rwid, rpos;
485 static const int BIGGAP = 15;
486 static const int MEDGAP = 3;
487
488 bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP;
489 cp->ypos += MEDGAP;
490 rpos = BIGGAP + (bigwid + BIGGAP) / 2;
491 lwid = rpos - 2 * BIGGAP;
492 rwid = bigwid + BIGGAP - rpos;
493
494 r.left = BIGGAP;
495 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
496 r.right = lwid;
497 r.bottom = STATICHEIGHT;
498 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
499
500 r.left = rpos;
501 r.top = cp->ypos + (height - COMBOHEIGHT) / 2;
502 r.right = rwid;
503 r.bottom = COMBOHEIGHT * 10;
504 doctl(cp, r, "COMBOBOX",
505 WS_CHILD | WS_VISIBLE | WS_TABSTOP |
506 CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);
507
508 cp->ypos += height + MEDGAP + GAPBETWEEN;
509
510 r.left = GAPBETWEEN;
511 r.top = cp->ypos;
512 r.right = cp->width;
513 r.bottom = 2;
514 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
515 0, "", s2id);
516 }
517
518 /*
519 * A static line, followed by an edit control on the left hand side
520 * and a button on the right.
521 */
522 void editbutton(struct ctlpos *cp, char *stext, int sid,
523 int eid, char *btext, int bid)
524 {
525 const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?
526 EDITHEIGHT : PUSHBTNHEIGHT);
527 RECT r;
528 int lwid, rwid, rpos;
529
530 r.left = GAPBETWEEN;
531 r.top = cp->ypos;
532 r.right = cp->width;
533 r.bottom = STATICHEIGHT;
534 cp->ypos += r.bottom + GAPWITHIN;
535 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
536
537 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
538 lwid = rpos - 2 * GAPBETWEEN;
539 rwid = cp->width + GAPBETWEEN - rpos;
540
541 r.left = GAPBETWEEN;
542 r.top = cp->ypos + (height - EDITHEIGHT) / 2;
543 r.right = lwid;
544 r.bottom = EDITHEIGHT;
545 doctl(cp, r, "EDIT",
546 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
547 WS_EX_CLIENTEDGE, "", eid);
548
549 r.left = rpos;
550 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
551 r.right = rwid;
552 r.bottom = PUSHBTNHEIGHT;
553 doctl(cp, r, "BUTTON",
554 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
555 0, btext, bid);
556
557 cp->ypos += height + GAPBETWEEN;
558 }
559
560 /*
561 * Special control which was hard to describe generically: the
562 * session-saver assembly. A static; below that an edit box; below
563 * that a list box. To the right of the list box, a column of
564 * buttons.
565 */
566 void sesssaver(struct ctlpos *cp, char *text,
567 int staticid, int editid, int listid, ...)
568 {
569 RECT r;
570 va_list ap;
571 int lwid, rwid, rpos;
572 int y;
573 const int LISTDEFHEIGHT = 66;
574
575 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
576 lwid = rpos - 2 * GAPBETWEEN;
577 rwid = cp->width + GAPBETWEEN - rpos;
578
579 /* The static control. */
580 r.left = GAPBETWEEN;
581 r.top = cp->ypos;
582 r.right = lwid;
583 r.bottom = STATICHEIGHT;
584 cp->ypos += r.bottom + GAPWITHIN;
585 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
586
587 /* The edit control. */
588 r.left = GAPBETWEEN;
589 r.top = cp->ypos;
590 r.right = lwid;
591 r.bottom = EDITHEIGHT;
592 cp->ypos += r.bottom + GAPWITHIN;
593 doctl(cp, r, "EDIT",
594 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
595 WS_EX_CLIENTEDGE, "", editid);
596
597 /*
598 * The buttons (we should hold off on the list box until we
599 * know how big the buttons are).
600 */
601 va_start(ap, listid);
602 y = cp->ypos;
603 while (1) {
604 char *btext = va_arg(ap, char *);
605 int bid;
606 if (!btext)
607 break;
608 bid = va_arg(ap, int);
609 r.left = rpos;
610 r.top = y;
611 r.right = rwid;
612 r.bottom = PUSHBTNHEIGHT;
613 y += r.bottom + GAPWITHIN;
614 doctl(cp, r, "BUTTON",
615 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
616 0, btext, bid);
617 }
618
619 /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
620 y -= cp->ypos;
621 y -= GAPWITHIN;
622 if (y < LISTDEFHEIGHT)
623 y = LISTDEFHEIGHT;
624 r.left = GAPBETWEEN;
625 r.top = cp->ypos;
626 r.right = lwid;
627 r.bottom = y;
628 cp->ypos += y + GAPBETWEEN;
629 doctl(cp, r, "LISTBOX",
630 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
631 LBS_NOTIFY | LBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);
632 }
633
634 /*
635 * Another special control: the environment-variable setter. A
636 * static line first; then a pair of edit boxes with associated
637 * statics, and two buttons; then a list box.
638 */
639 void envsetter(struct ctlpos *cp, char *stext, int sid,
640 char *e1stext, int e1sid, int e1id,
641 char *e2stext, int e2sid, int e2id,
642 int listid, char *b1text, int b1id, char *b2text, int b2id)
643 {
644 RECT r;
645 const int height = (STATICHEIGHT > EDITHEIGHT
646 && STATICHEIGHT >
647 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
648 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
649 const static int percents[] = { 20, 35, 10, 25 };
650 int i, j, xpos, percent;
651 const int LISTHEIGHT = 42;
652
653 /* The static control. */
654 r.left = GAPBETWEEN;
655 r.top = cp->ypos;
656 r.right = cp->width;
657 r.bottom = STATICHEIGHT;
658 cp->ypos += r.bottom + GAPWITHIN;
659 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
660
661 /* The statics+edits+buttons. */
662 for (j = 0; j < 2; j++) {
663 percent = 10;
664 for (i = 0; i < 4; i++) {
665 xpos = (cp->width + GAPBETWEEN) * percent / 100;
666 r.left = xpos + GAPBETWEEN;
667 percent += percents[i];
668 xpos = (cp->width + GAPBETWEEN) * percent / 100;
669 r.right = xpos - r.left;
670 r.top = cp->ypos;
671 r.bottom = (i == 0 ? STATICHEIGHT :
672 i == 1 ? EDITHEIGHT : 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, "", j == 0 ? e1id : e2id);
681 } else if (i == 3) {
682 doctl(cp, r, "BUTTON",
683 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
684 0, j == 0 ? b1text : b2text, j == 0 ? b1id : b2id);
685 }
686 }
687 cp->ypos += height + GAPWITHIN;
688 }
689
690 /* The list box. */
691 r.left = GAPBETWEEN;
692 r.top = cp->ypos;
693 r.right = cp->width;
694 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, WS_EX_CLIENTEDGE, "", listid);
699 }
700
701 /*
702 * Yet another special control: the character-class setter. A
703 * static, then a list, then a line containing a
704 * button-and-static-and-edit.
705 */
706 void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
707 char *btext, int bid, int eid, char *s2text, int s2id)
708 {
709 RECT r;
710 const int height = (STATICHEIGHT > EDITHEIGHT
711 && STATICHEIGHT >
712 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
713 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
714 const static int percents[] = { 30, 40, 30 };
715 int i, xpos, percent;
716 const int LISTHEIGHT = 66;
717
718 /* The static control. */
719 r.left = GAPBETWEEN;
720 r.top = cp->ypos;
721 r.right = cp->width;
722 r.bottom = STATICHEIGHT;
723 cp->ypos += r.bottom + GAPWITHIN;
724 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
725
726 /* The list box. */
727 r.left = GAPBETWEEN;
728 r.top = cp->ypos;
729 r.right = cp->width;
730 r.bottom = LISTHEIGHT;
731 cp->ypos += r.bottom + GAPWITHIN;
732 doctl(cp, r, "LISTBOX",
733 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
734 | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
735
736 /* The button+static+edit. */
737 percent = xpos = 0;
738 for (i = 0; i < 3; i++) {
739 r.left = xpos + GAPBETWEEN;
740 percent += percents[i];
741 xpos = (cp->width + GAPBETWEEN) * percent / 100;
742 r.right = xpos - r.left;
743 r.top = cp->ypos;
744 r.bottom = (i == 0 ? PUSHBTNHEIGHT :
745 i == 1 ? STATICHEIGHT : 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 void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
769 char *btext, int bid, ...)
770 {
771 RECT r;
772 int y;
773 va_list ap;
774 int lwid, rwid, rpos;
775 const int LISTHEIGHT = 66;
776
777 /* The static control. */
778 r.left = GAPBETWEEN;
779 r.top = cp->ypos;
780 r.right = cp->width;
781 r.bottom = STATICHEIGHT;
782 cp->ypos += r.bottom + GAPWITHIN;
783 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
784
785 rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3;
786 lwid = rpos - 2 * GAPBETWEEN;
787 rwid = cp->width + GAPBETWEEN - rpos;
788
789 /* The list box. */
790 r.left = GAPBETWEEN;
791 r.top = cp->ypos;
792 r.right = lwid;
793 r.bottom = LISTHEIGHT;
794 doctl(cp, r, "LISTBOX",
795 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
796 | LBS_USETABSTOPS | LBS_NOTIFY, WS_EX_CLIENTEDGE, "", listid);
797
798 /* The statics. */
799 y = cp->ypos;
800 va_start(ap, bid);
801 while (1) {
802 char *ltext;
803 int lid, rid;
804 ltext = va_arg(ap, char *);
805 if (!ltext)
806 break;
807 lid = va_arg(ap, int);
808 rid = va_arg(ap, int);
809 r.top = y;
810 r.bottom = STATICHEIGHT;
811 y += r.bottom + GAPWITHIN;
812 r.left = rpos;
813 r.right = rwid / 2;
814 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid);
815 r.left = rpos + r.right;
816 r.right = rwid - r.right;
817 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "",
818 rid);
819 }
820 va_end(ap);
821
822 /* The button. */
823 r.top = y + 2 * GAPWITHIN;
824 r.bottom = PUSHBTNHEIGHT;
825 r.left = rpos;
826 r.right = rwid;
827 doctl(cp, r, "BUTTON",
828 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
829 0, btext, bid);
830
831 cp->ypos += LISTHEIGHT + GAPBETWEEN;
832 }
833
834 /*
835 * A special control for manipulating an ordered preference list
836 * (eg. for cipher selection).
837 * XXX: this is a rough hack and could be improved.
838 */
839 void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
840 int sid, int listid, int upbid, int dnbid)
841 {
842 const static int percents[] = { 5, 75, 20 };
843 RECT r;
844 int xpos, percent = 0, i;
845 const int DEFLISTHEIGHT = 52; /* XXX configurable? */
846 const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;
847 int totalheight;
848
849 /* Squirrel away IDs. */
850 hdl->listid = listid;
851 hdl->upbid = upbid;
852 hdl->dnbid = dnbid;
853
854 /* The static label. */
855 r.left = GAPBETWEEN;
856 r.top = cp->ypos;
857 r.right = cp->width;
858 r.bottom = STATICHEIGHT;
859 cp->ypos += r.bottom + GAPWITHIN;
860 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
861
862 /* XXX it'd be nice to centre the buttons wrt the listbox
863 * but we'd have to find out how high the latter actually is. */
864 if (DEFLISTHEIGHT > BTNSHEIGHT) {
865 totalheight = DEFLISTHEIGHT;
866 } else {
867 totalheight = BTNSHEIGHT;
868 }
869
870 for (i=0; i<3; i++) {
871 int left, wid;
872 xpos = (cp->width + GAPBETWEEN) * percent / 100;
873 left = xpos + GAPBETWEEN;
874 percent += percents[i];
875 xpos = (cp->width + GAPBETWEEN) * percent / 100;
876 wid = xpos - left;
877
878 switch (i) {
879 case 1:
880 /* The drag list box. */
881 r.left = left; r.right = wid;
882 r.top = cp->ypos; r.bottom = totalheight;
883 {
884 HWND ctl;
885 ctl = doctl(cp, r, "LISTBOX",
886 WS_CHILD | WS_VISIBLE | WS_TABSTOP |
887 WS_VSCROLL | LBS_HASSTRINGS,
888 WS_EX_CLIENTEDGE,
889 "", listid);
890 MakeDragList(ctl);
891 }
892 break;
893
894 case 2:
895 /* The "Up" and "Down" buttons. */
896 /* XXX worry about accelerators if we have more than one
897 * prefslist on a panel */
898 r.left = left; r.right = wid;
899 r.top = cp->ypos; r.bottom = PUSHBTNHEIGHT;
900 doctl(cp, r, "BUTTON",
901 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
902 0, "&Up", upbid);
903
904 r.left = left; r.right = wid;
905 r.top = cp->ypos + PUSHBTNHEIGHT + GAPBETWEEN;
906 r.bottom = PUSHBTNHEIGHT;
907 doctl(cp, r, "BUTTON",
908 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
909 0, "&Down", dnbid);
910
911 break;
912
913 }
914 }
915
916 cp->ypos += totalheight + GAPBETWEEN;
917
918 }
919
920 /*
921 * Helper function for prefslist: move item in list box.
922 */
923 static void pl_moveitem(HWND hwnd, int listid, int src, int dst)
924 {
925 int tlen, val;
926 char *txt;
927 /* Get the item's data. */
928 tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0);
929 txt = smalloc(tlen+1);
930 SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt);
931 val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0);
932 /* Deselect old location. */
933 SendDlgItemMessage (hwnd, listid, LB_SETSEL, FALSE, src);
934 /* Delete it at the old location. */
935 SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0);
936 /* Insert it at new location. */
937 SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst,
938 (LPARAM) txt);
939 SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst,
940 (LPARAM) val);
941 /* Set selection. */
942 SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0);
943 sfree (txt);
944 }
945
946 int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
947 {
948 int ret;
949 POINT uppoint, downpoint;
950 int updist, downdist, upitem, downitem, i;
951
952 /*
953 * Ghastly hackery to try to figure out not which
954 * _item_, but which _gap between items_, the user
955 * is pointing at. We do this by first working out
956 * which list item is under the cursor, and then
957 * working out how far the cursor would have to
958 * move up or down before the answer was different.
959 * Then we put the insertion point _above_ the
960 * current item if the upper edge is closer than
961 * the lower edge, or _below_ it if vice versa.
962 */
963 ret = LBItemFromPt(hwnd, cursor, scroll);
964 if (ret == -1)
965 return ret;
966 ret = LBItemFromPt(hwnd, cursor, FALSE);
967 updist = downdist = 0;
968 for (i = 1; i < 4096 && (!updist || !downdist); i++) {
969 uppoint = downpoint = cursor;
970 uppoint.y -= i;
971 downpoint.y += i;
972 upitem = LBItemFromPt(hwnd, uppoint, FALSE);
973 downitem = LBItemFromPt(hwnd, downpoint, FALSE);
974 if (!updist && upitem != ret)
975 updist = i;
976 if (!downdist && downitem != ret)
977 downdist = i;
978 }
979 if (downdist < updist)
980 ret++;
981 return ret;
982 }
983
984 /*
985 * Handler for prefslist above.
986 */
987 int handle_prefslist(struct prefslist *hdl,
988 int *array, int maxmemb,
989 int is_dlmsg, HWND hwnd,
990 WPARAM wParam, LPARAM lParam)
991 {
992 int i;
993 int ret;
994
995 if (is_dlmsg) {
996
997 if ((int)wParam == hdl->listid) {
998 DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam;
999 int dest;
1000 switch (dlm->uNotification) {
1001 case DL_BEGINDRAG:
1002 hdl->dummyitem =
1003 SendDlgItemMessage(hwnd, hdl->listid,
1004 LB_ADDSTRING, 0, (LPARAM) "");
1005
1006 hdl->srcitem = LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);
1007 hdl->dragging = 0;
1008 /* XXX hack Q183115 */
1009 SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
1010 ret = 1; break;
1011 case DL_CANCELDRAG:
1012 DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */
1013 SendDlgItemMessage(hwnd, hdl->listid,
1014 LB_DELETESTRING, hdl->dummyitem, 0);
1015 hdl->dragging = 0;
1016 ret = 1; break;
1017 case DL_DRAGGING:
1018 hdl->dragging = 1;
1019 dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
1020 if (dest > hdl->dummyitem) dest = hdl->dummyitem;
1021 DrawInsert (hwnd, dlm->hWnd, dest);
1022 if (dest >= 0)
1023 SetWindowLong(hwnd, DWL_MSGRESULT, DL_MOVECURSOR);
1024 else
1025 SetWindowLong(hwnd, DWL_MSGRESULT, DL_STOPCURSOR);
1026 ret = 1; break;
1027 case DL_DROPPED:
1028 ret = 1;
1029 if (!hdl->dragging) break;
1030 dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
1031 if (dest > hdl->dummyitem) dest = hdl->dummyitem;
1032 DrawInsert (hwnd, dlm->hWnd, -1);
1033 SendDlgItemMessage(hwnd, hdl->listid,
1034 LB_DELETESTRING, hdl->dummyitem, 0);
1035 hdl->dragging = 0;
1036 if (dest >= 0) {
1037 /* Correct for "missing" item. This means you can't drag
1038 * an item to the end, but that seems to be the way this
1039 * control is used. */
1040 if (dest > hdl->srcitem) dest--;
1041 pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);
1042 }
1043 ret = 1; break;
1044 }
1045 }
1046
1047 } else {
1048
1049 ret = 0;
1050 if (((LOWORD(wParam) == hdl->upbid) ||
1051 (LOWORD(wParam) == hdl->dnbid)) &&
1052 ((HIWORD(wParam) == BN_CLICKED) ||
1053 (HIWORD(wParam) == BN_DOUBLECLICKED))) {
1054 /* Move an item up or down the list. */
1055 /* Get the current selection, if any. */
1056 int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0);
1057 if (selection == LB_ERR) {
1058 MessageBeep(0);
1059 } else {
1060 int nitems;
1061 /* Get the total number of items. */
1062 nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0);
1063 /* Should we do anything? */
1064 if (LOWORD(wParam) == hdl->upbid && (selection > 0))
1065 pl_moveitem(hwnd, hdl->listid, selection, selection - 1);
1066 else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))
1067 pl_moveitem(hwnd, hdl->listid, selection, selection + 1);
1068 }
1069
1070 }
1071
1072 }
1073
1074 /* Update array to match the list box. */
1075 for (i=0; i < maxmemb; i++)
1076 array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,
1077 i, 0);
1078
1079 return ret;
1080
1081 }
1082
1083 /*
1084 * A progress bar (from Common Controls). We like our progress bars
1085 * to be smooth and unbroken, without those ugly divisions; some
1086 * older compilers may not support that, but that's life.
1087 */
1088 void progressbar(struct ctlpos *cp, int id)
1089 {
1090 RECT r;
1091
1092 r.left = GAPBETWEEN;
1093 r.top = cp->ypos;
1094 r.right = cp->width;
1095 r.bottom = PROGBARHEIGHT;
1096 cp->ypos += r.bottom + GAPBETWEEN;
1097
1098 doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE
1099 #ifdef PBS_SMOOTH
1100 | PBS_SMOOTH
1101 #endif
1102 , WS_EX_CLIENTEDGE, "", id);
1103 }
1104
1105 /*
1106 * Another special control: the forwarding options setter. First a
1107 * list box; next a static header line, introducing a pair of edit
1108 * boxes with associated statics, another button, and a radio
1109 * button pair.
1110 */
1111 void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
1112 char *e1stext, int e1sid, int e1id,
1113 char *e2stext, int e2sid, int e2id,
1114 char *btext, int bid)
1115 {
1116 RECT r;
1117 const int height = (STATICHEIGHT > EDITHEIGHT
1118 && STATICHEIGHT >
1119 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
1120 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
1121 const static int percents[] = { 25, 35, 15, 25 };
1122 int i, j, xpos, percent;
1123 const int LISTHEIGHT = 42;
1124
1125 /* The list box. */
1126 r.left = GAPBETWEEN;
1127 r.top = cp->ypos;
1128 r.right = cp->width;
1129 r.bottom = LISTHEIGHT;
1130 cp->ypos += r.bottom + GAPBETWEEN;
1131 doctl(cp, r, "LISTBOX",
1132 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
1133 | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
1134
1135 /* The static control. */
1136 r.left = GAPBETWEEN;
1137 r.top = cp->ypos;
1138 r.right = cp->width;
1139 r.bottom = STATICHEIGHT;
1140 cp->ypos += r.bottom + GAPWITHIN;
1141 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
1142
1143 /* The statics+edits+buttons. */
1144 for (j = 0; j < 2; j++) {
1145 percent = 0;
1146 for (i = 0; i < (j ? 2 : 4); i++) {
1147 xpos = (cp->width + GAPBETWEEN) * percent / 100;
1148 r.left = xpos + GAPBETWEEN;
1149 percent += percents[i];
1150 if (j==1 && i==1) percent = 100;
1151 xpos = (cp->width + GAPBETWEEN) * percent / 100;
1152 r.right = xpos - r.left;
1153 r.top = cp->ypos;
1154 r.bottom = (i == 0 ? STATICHEIGHT :
1155 i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
1156 r.top += (height - r.bottom) / 2;
1157 if (i == 0) {
1158 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
1159 j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
1160 } else if (i == 1) {
1161 doctl(cp, r, "EDIT",
1162 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
1163 WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
1164 } else if (i == 3) {
1165 doctl(cp, r, "BUTTON",
1166 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1167 0, btext, bid);
1168 }
1169 }
1170 cp->ypos += height + GAPWITHIN;
1171 }
1172 }