2 * winctrls.c: routines to self-manage the controls in a dialog
19 #define STATICHEIGHT 8
20 #define CHECKBOXHEIGHT 8
23 #define COMBOHEIGHT 12
24 #define PUSHBTNHEIGHT 14
25 #define PROGBARHEIGHT 14
27 void ctlposinit(struct ctlpos
*cp
, HWND hwnd
,
28 int leftborder
, int rightborder
, int topborder
)
32 cp
->font
= SendMessage(hwnd
, WM_GETFONT
, 0, 0);
34 GetClientRect(hwnd
, &r
);
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
;
45 HWND
doctl(struct ctlpos
*cp
, RECT r
,
46 char *wclass
, int wstyle
, int exstyle
, char *wtext
, int wid
)
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.
56 MapDialogRect(cp
->hwnd
, &r
);
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));
66 * A title bar across the top of a sub-dialog.
68 void bartitle(struct ctlpos
*cp
, char *name
, int id
)
75 r
.bottom
= STATICHEIGHT
;
76 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
77 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, name
, id
);
81 * Begin a grouping box, with or without a group title.
83 void beginbox(struct ctlpos
*cp
, char *name
, int idbox
)
85 cp
->boxystart
= cp
->ypos
;
87 cp
->boxystart
-= STATICHEIGHT
/ 2;
89 cp
->ypos
+= STATICHEIGHT
;
91 cp
->width
-= 2 * GAPXBOX
;
100 void endbox(struct ctlpos
*cp
)
104 cp
->width
+= 2 * GAPXBOX
;
105 cp
->ypos
+= GAPYBOX
- GAPBETWEEN
;
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
);
116 * Some edit boxes. Each one has a static above it. The percentages
117 * of the horizontal space are provided.
119 void multiedit(struct ctlpos
*cp
, ...)
129 int staticid
, editid
, pcwidth
;
130 text
= va_arg(ap
, char *);
133 staticid
= va_arg(ap
, int);
134 editid
= va_arg(ap
, int);
135 pcwidth
= va_arg(ap
, int);
137 r
.left
= xpos
+ GAPBETWEEN
;
139 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
140 r
.right
= xpos
- r
.left
;
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
;
148 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
149 WS_EX_CLIENTEDGE
, "", editid
);
152 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ EDITHEIGHT
+ GAPBETWEEN
;
156 * A static line, followed by a full-width combo box.
158 void combobox(struct ctlpos
*cp
, char *text
, int staticid
, int listid
)
166 r
.bottom
= STATICHEIGHT
;
167 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
168 r
.top
= cp
->ypos
+ 8 + GAPWITHIN
;
169 r
.bottom
= COMBOHEIGHT
* 10;
170 doctl(cp
, r
, "COMBOBOX",
171 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
172 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", listid
);
174 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ COMBOHEIGHT
+ GAPBETWEEN
;
177 static void radioline_common(struct ctlpos
*cp
, int nacross
, va_list ap
)
186 btext
= va_arg(ap
, char *);
193 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
196 bid
= va_arg(ap
, int);
197 nextbtext
= va_arg(ap
, char *);
198 r
.left
= GAPBETWEEN
+ i
* (cp
->width
+ GAPBETWEEN
) / nacross
;
201 (i
+ 1) * (cp
->width
+ GAPBETWEEN
) / nacross
- r
.left
;
203 r
.right
= cp
->width
- r
.left
;
205 r
.bottom
= RADIOHEIGHT
;
206 doctl(cp
, r
, "BUTTON",
207 BS_AUTORADIOBUTTON
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
208 group
, 0, btext
, bid
);
213 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
217 * A set of radio buttons on the same line, with a static above
218 * them. `nacross' dictates how many parts the line is divided into
219 * (you might want this not to equal the number of buttons if you
220 * needed to line up some 2s and some 3s to look good in the same
223 * There's a bit of a hack in here to ensure that if nacross
224 * exceeds the actual number of buttons, the rightmost button
225 * really does get all the space right to the edge of the line, so
226 * you can do things like
228 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
230 void radioline(struct ctlpos
*cp
, char *text
, int id
, int nacross
, ...)
238 r
.bottom
= STATICHEIGHT
;
239 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
240 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
241 va_start(ap
, nacross
);
242 radioline_common(cp
, nacross
, ap
);
247 * A set of radio buttons on the same line, without a static above
248 * them. Otherwise just like radioline.
250 void bareradioline(struct ctlpos
*cp
, int nacross
, ...)
254 va_start(ap
, nacross
);
255 radioline_common(cp
, nacross
, ap
);
260 * A set of radio buttons on multiple lines, with a static above
263 void radiobig(struct ctlpos
*cp
, char *text
, int id
, ...)
272 r
.bottom
= STATICHEIGHT
;
273 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
274 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
280 btext
= va_arg(ap
, char *);
283 bid
= va_arg(ap
, int);
287 r
.bottom
= STATICHEIGHT
;
288 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
289 doctl(cp
, r
, "BUTTON",
290 BS_AUTORADIOBUTTON
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
291 group
, 0, btext
, bid
);
295 cp
->ypos
+= GAPBETWEEN
- GAPWITHIN
;
299 * A single standalone checkbox.
301 void checkbox(struct ctlpos
*cp
, char *text
, int id
)
308 r
.bottom
= CHECKBOXHEIGHT
;
309 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
310 doctl(cp
, r
, "BUTTON",
311 BS_AUTOCHECKBOX
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0,
316 * A single standalone static text control.
318 void statictext(struct ctlpos
*cp
, char *text
, int lines
, int id
)
325 r
.bottom
= STATICHEIGHT
* lines
;
326 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
327 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
331 * A button on the right hand side, with a static to its left.
333 void staticbtn(struct ctlpos
*cp
, char *stext
, int sid
,
334 char *btext
, int bid
)
336 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
337 PUSHBTNHEIGHT
: STATICHEIGHT
);
339 int lwid
, rwid
, rpos
;
341 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
342 lwid
= rpos
- 2 * GAPBETWEEN
;
343 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
346 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
348 r
.bottom
= STATICHEIGHT
;
349 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
352 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
354 r
.bottom
= PUSHBTNHEIGHT
;
355 doctl(cp
, r
, "BUTTON",
356 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
359 cp
->ypos
+= height
+ GAPBETWEEN
;
363 * Like staticbtn, but two buttons.
365 void static2btn(struct ctlpos
*cp
, char *stext
, int sid
,
366 char *btext1
, int bid1
, char *btext2
, int bid2
)
368 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
369 PUSHBTNHEIGHT
: STATICHEIGHT
);
371 int lwid
, rwid1
, rwid2
, rpos1
, rpos2
;
373 rpos1
= GAPBETWEEN
+ (cp
->width
+ GAPBETWEEN
) / 2;
374 rpos2
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
375 lwid
= rpos1
- 2 * GAPBETWEEN
;
376 rwid1
= rpos2
- rpos1
- GAPBETWEEN
;
377 rwid2
= cp
->width
+ GAPBETWEEN
- rpos2
;
380 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
382 r
.bottom
= STATICHEIGHT
;
383 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
386 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
388 r
.bottom
= PUSHBTNHEIGHT
;
389 doctl(cp
, r
, "BUTTON",
390 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
394 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
396 r
.bottom
= PUSHBTNHEIGHT
;
397 doctl(cp
, r
, "BUTTON",
398 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
401 cp
->ypos
+= height
+ GAPBETWEEN
;
405 * An edit control on the right hand side, with a static to its left.
407 static void staticedit_internal(struct ctlpos
*cp
, char *stext
,
408 int sid
, int eid
, int percentedit
,
411 const int height
= (EDITHEIGHT
> STATICHEIGHT ?
412 EDITHEIGHT
: STATICHEIGHT
);
414 int lwid
, rwid
, rpos
;
417 GAPBETWEEN
+ (100 - percentedit
) * (cp
->width
+ GAPBETWEEN
) / 100;
418 lwid
= rpos
- 2 * GAPBETWEEN
;
419 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
422 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
424 r
.bottom
= STATICHEIGHT
;
425 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
428 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
430 r
.bottom
= EDITHEIGHT
;
432 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
| style
,
433 WS_EX_CLIENTEDGE
, "", eid
);
435 cp
->ypos
+= height
+ GAPBETWEEN
;
438 void staticedit(struct ctlpos
*cp
, char *stext
,
439 int sid
, int eid
, int percentedit
)
441 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, 0);
444 void staticpassedit(struct ctlpos
*cp
, char *stext
,
445 int sid
, int eid
, int percentedit
)
447 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, ES_PASSWORD
);
451 * A big multiline edit control with a static labelling it.
453 void bigeditctrl(struct ctlpos
*cp
, char *stext
,
454 int sid
, int eid
, int lines
)
461 r
.bottom
= STATICHEIGHT
;
462 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
463 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
468 r
.bottom
= EDITHEIGHT
+ (lines
- 1) * STATICHEIGHT
;
469 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
471 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| ES_MULTILINE
,
472 WS_EX_CLIENTEDGE
, "", eid
);
476 * A tab-control substitute when a real tab control is unavailable.
478 void ersatztab(struct ctlpos
*cp
, char *stext
, int sid
, int lid
, int s2id
)
480 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
481 COMBOHEIGHT
: STATICHEIGHT
);
483 int bigwid
, lwid
, rwid
, rpos
;
484 static const int BIGGAP
= 15;
485 static const int MEDGAP
= 3;
487 bigwid
= cp
->width
+ 2 * GAPBETWEEN
- 2 * BIGGAP
;
489 rpos
= BIGGAP
+ (bigwid
+ BIGGAP
) / 2;
490 lwid
= rpos
- 2 * BIGGAP
;
491 rwid
= bigwid
+ BIGGAP
- rpos
;
494 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
496 r
.bottom
= STATICHEIGHT
;
497 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
500 r
.top
= cp
->ypos
+ (height
- COMBOHEIGHT
) / 2;
502 r
.bottom
= COMBOHEIGHT
* 10;
503 doctl(cp
, r
, "COMBOBOX",
504 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
505 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
507 cp
->ypos
+= height
+ MEDGAP
+ GAPBETWEEN
;
513 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_ETCHEDHORZ
,
518 * A static line, followed by an edit control on the left hand side
519 * and a button on the right.
521 void editbutton(struct ctlpos
*cp
, char *stext
, int sid
,
522 int eid
, char *btext
, int bid
)
524 const int height
= (EDITHEIGHT
> PUSHBTNHEIGHT ?
525 EDITHEIGHT
: PUSHBTNHEIGHT
);
527 int lwid
, rwid
, rpos
;
532 r
.bottom
= STATICHEIGHT
;
533 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
534 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
536 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
537 lwid
= rpos
- 2 * GAPBETWEEN
;
538 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
541 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
543 r
.bottom
= EDITHEIGHT
;
545 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
546 WS_EX_CLIENTEDGE
, "", eid
);
549 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
551 r
.bottom
= PUSHBTNHEIGHT
;
552 doctl(cp
, r
, "BUTTON",
553 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
556 cp
->ypos
+= height
+ GAPBETWEEN
;
560 * Special control which was hard to describe generically: the
561 * session-saver assembly. A static; below that an edit box; below
562 * that a list box. To the right of the list box, a column of
565 void sesssaver(struct ctlpos
*cp
, char *text
,
566 int staticid
, int editid
, int listid
, ...)
570 int lwid
, rwid
, rpos
;
572 const int LISTDEFHEIGHT
= 66;
574 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
575 lwid
= rpos
- 2 * GAPBETWEEN
;
576 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
578 /* The static control. */
582 r
.bottom
= STATICHEIGHT
;
583 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
584 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
586 /* The edit control. */
590 r
.bottom
= EDITHEIGHT
;
591 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
593 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
594 WS_EX_CLIENTEDGE
, "", editid
);
597 * The buttons (we should hold off on the list box until we
598 * know how big the buttons are).
600 va_start(ap
, listid
);
603 char *btext
= va_arg(ap
, char *);
607 bid
= va_arg(ap
, int);
611 r
.bottom
= PUSHBTNHEIGHT
;
612 y
+= r
.bottom
+ GAPWITHIN
;
613 doctl(cp
, r
, "BUTTON",
614 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
618 /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
621 if (y
< LISTDEFHEIGHT
)
627 cp
->ypos
+= y
+ GAPBETWEEN
;
628 doctl(cp
, r
, "LISTBOX",
629 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
630 LBS_NOTIFY
| LBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", listid
);
634 * Another special control: the environment-variable setter. A
635 * static line first; then a pair of edit boxes with associated
636 * statics, and two buttons; then a list box.
638 void envsetter(struct ctlpos
*cp
, char *stext
, int sid
,
639 char *e1stext
, int e1sid
, int e1id
,
640 char *e2stext
, int e2sid
, int e2id
,
641 int listid
, char *b1text
, int b1id
, char *b2text
, int b2id
)
644 const int height
= (STATICHEIGHT
> EDITHEIGHT
646 PUSHBTNHEIGHT ? STATICHEIGHT
: EDITHEIGHT
>
647 PUSHBTNHEIGHT ? EDITHEIGHT
: PUSHBTNHEIGHT
);
648 const static int percents
[] = { 20, 35, 10, 25 };
649 int i
, j
, xpos
, percent
;
650 const int LISTHEIGHT
= 42;
652 /* The static control. */
656 r
.bottom
= STATICHEIGHT
;
657 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
658 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
660 /* The statics+edits+buttons. */
661 for (j
= 0; j
< 2; j
++) {
663 for (i
= 0; i
< 4; i
++) {
664 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
665 r
.left
= xpos
+ GAPBETWEEN
;
666 percent
+= percents
[i
];
667 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
668 r
.right
= xpos
- r
.left
;
670 r
.bottom
= (i
== 0 ? STATICHEIGHT
:
671 i
== 1 ? EDITHEIGHT
: PUSHBTNHEIGHT
);
672 r
.top
+= (height
- r
.bottom
) / 2;
674 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0,
675 j
== 0 ? e1stext
: e2stext
, j
== 0 ? e1sid
: e2sid
);
678 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
679 WS_EX_CLIENTEDGE
, "", j
== 0 ? e1id
: e2id
);
681 doctl(cp
, r
, "BUTTON",
682 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
683 0, j
== 0 ? b1text
: b2text
, j
== 0 ? b1id
: b2id
);
686 cp
->ypos
+= height
+ GAPWITHIN
;
693 r
.bottom
= LISTHEIGHT
;
694 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
695 doctl(cp
, r
, "LISTBOX",
696 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| LBS_HASSTRINGS
697 | LBS_USETABSTOPS
, WS_EX_CLIENTEDGE
, "", listid
);
701 * Yet another special control: the character-class setter. A
702 * static, then a list, then a line containing a
703 * button-and-static-and-edit.
705 void charclass(struct ctlpos
*cp
, char *stext
, int sid
, int listid
,
706 char *btext
, int bid
, int eid
, char *s2text
, int s2id
)
709 const int height
= (STATICHEIGHT
> EDITHEIGHT
711 PUSHBTNHEIGHT ? STATICHEIGHT
: EDITHEIGHT
>
712 PUSHBTNHEIGHT ? EDITHEIGHT
: PUSHBTNHEIGHT
);
713 const static int percents
[] = { 30, 40, 30 };
714 int i
, xpos
, percent
;
715 const int LISTHEIGHT
= 52;
717 /* The static control. */
721 r
.bottom
= STATICHEIGHT
;
722 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
723 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
729 r
.bottom
= LISTHEIGHT
;
730 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
731 doctl(cp
, r
, "LISTBOX",
732 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| LBS_HASSTRINGS
733 | LBS_USETABSTOPS
, WS_EX_CLIENTEDGE
, "", listid
);
735 /* The button+static+edit. */
737 for (i
= 0; i
< 3; i
++) {
738 r
.left
= xpos
+ GAPBETWEEN
;
739 percent
+= percents
[i
];
740 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
741 r
.right
= xpos
- r
.left
;
743 r
.bottom
= (i
== 0 ? PUSHBTNHEIGHT
:
744 i
== 1 ? STATICHEIGHT
: EDITHEIGHT
);
745 r
.top
+= (height
- r
.bottom
) / 2;
747 doctl(cp
, r
, "BUTTON",
748 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
751 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_CENTER
,
755 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
756 WS_EX_CLIENTEDGE
, "", eid
);
759 cp
->ypos
+= height
+ GAPBETWEEN
;
763 * A special control (horrors!). The colour editor. A static line;
764 * then on the left, a list box, and on the right, a sequence of
765 * two-part statics followed by a button.
767 void colouredit(struct ctlpos
*cp
, char *stext
, int sid
, int listid
,
768 char *btext
, int bid
, ...)
773 int lwid
, rwid
, rpos
;
774 const int LISTHEIGHT
= 66;
776 /* The static control. */
780 r
.bottom
= STATICHEIGHT
;
781 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
782 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
784 rpos
= GAPBETWEEN
+ 2 * (cp
->width
+ GAPBETWEEN
) / 3;
785 lwid
= rpos
- 2 * GAPBETWEEN
;
786 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
792 r
.bottom
= LISTHEIGHT
;
793 doctl(cp
, r
, "LISTBOX",
794 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| LBS_HASSTRINGS
795 | LBS_USETABSTOPS
| LBS_NOTIFY
, WS_EX_CLIENTEDGE
, "", listid
);
803 ltext
= va_arg(ap
, char *);
806 lid
= va_arg(ap
, int);
807 rid
= va_arg(ap
, int);
809 r
.bottom
= STATICHEIGHT
;
810 y
+= r
.bottom
+ GAPWITHIN
;
813 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, ltext
, lid
);
814 r
.left
= rpos
+ r
.right
;
815 r
.right
= rwid
- r
.right
;
816 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_RIGHT
, 0, "",
822 r
.top
= y
+ 2 * GAPWITHIN
;
823 r
.bottom
= PUSHBTNHEIGHT
;
826 doctl(cp
, r
, "BUTTON",
827 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
830 cp
->ypos
+= LISTHEIGHT
+ GAPBETWEEN
;
834 * A special control for manipulating an ordered preference list
835 * (eg. for cipher selection).
836 * XXX: this is a rough hack and could be improved.
838 void prefslist(struct prefslist
*hdl
, struct ctlpos
*cp
, char *stext
,
839 int sid
, int listid
, int upbid
, int dnbid
)
841 const static int percents
[] = { 5, 75, 20 };
843 int xpos
, percent
= 0, i
;
844 const int DEFLISTHEIGHT
= 52; /* XXX configurable? */
845 const int BTNSHEIGHT
= 2*PUSHBTNHEIGHT
+ GAPBETWEEN
;
848 /* Squirrel away IDs. */
849 hdl
->listid
= listid
;
853 /* The static label. */
857 r
.bottom
= STATICHEIGHT
;
858 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
859 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
861 /* XXX it'd be nice to centre the buttons wrt the listbox
862 * but we'd have to find out how high the latter actually is. */
863 if (DEFLISTHEIGHT
> BTNSHEIGHT
) {
864 totalheight
= DEFLISTHEIGHT
;
866 totalheight
= BTNSHEIGHT
;
869 for (i
=0; i
<3; i
++) {
871 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
872 left
= xpos
+ GAPBETWEEN
;
873 percent
+= percents
[i
];
874 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
879 /* The drag list box. */
880 r
.left
= left
; r
.right
= wid
;
881 r
.top
= cp
->ypos
; r
.bottom
= totalheight
;
884 ctl
= doctl(cp
, r
, "LISTBOX",
885 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
886 WS_VSCROLL
| LBS_HASSTRINGS
,
894 /* The "Up" and "Down" buttons. */
895 /* XXX worry about accelerators if we have more than one
896 * prefslist on a panel */
897 r
.left
= left
; r
.right
= wid
;
898 r
.top
= cp
->ypos
; r
.bottom
= PUSHBTNHEIGHT
;
899 doctl(cp
, r
, "BUTTON",
900 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
903 r
.left
= left
; r
.right
= wid
;
904 r
.top
= cp
->ypos
+ PUSHBTNHEIGHT
+ GAPBETWEEN
;
905 r
.bottom
= PUSHBTNHEIGHT
;
906 doctl(cp
, r
, "BUTTON",
907 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
915 cp
->ypos
+= totalheight
+ GAPBETWEEN
;
920 * Helper function for prefslist: move item in list box.
922 static void pl_moveitem(HWND hwnd
, int listid
, int src
, int dst
)
926 /* Get the item's data. */
927 tlen
= SendDlgItemMessage (hwnd
, listid
, LB_GETTEXTLEN
, src
, 0);
928 txt
= smalloc(tlen
+1);
929 SendDlgItemMessage (hwnd
, listid
, LB_GETTEXT
, src
, (LPARAM
) txt
);
930 val
= SendDlgItemMessage (hwnd
, listid
, LB_GETITEMDATA
, src
, 0);
931 /* Deselect old location. */
932 SendDlgItemMessage (hwnd
, listid
, LB_SETSEL
, FALSE
, src
);
933 /* Delete it at the old location. */
934 SendDlgItemMessage (hwnd
, listid
, LB_DELETESTRING
, src
, 0);
935 /* Insert it at new location. */
936 SendDlgItemMessage (hwnd
, listid
, LB_INSERTSTRING
, dst
,
938 SendDlgItemMessage (hwnd
, listid
, LB_SETITEMDATA
, dst
,
941 SendDlgItemMessage (hwnd
, listid
, LB_SETCURSEL
, dst
, 0);
945 int pl_itemfrompt(HWND hwnd
, POINT cursor
, BOOL scroll
)
948 POINT uppoint
, downpoint
;
949 int updist
, downdist
, upitem
, downitem
, i
;
952 * Ghastly hackery to try to figure out not which
953 * _item_, but which _gap between items_, the user
954 * is pointing at. We do this by first working out
955 * which list item is under the cursor, and then
956 * working out how far the cursor would have to
957 * move up or down before the answer was different.
958 * Then we put the insertion point _above_ the
959 * current item if the upper edge is closer than
960 * the lower edge, or _below_ it if vice versa.
962 ret
= LBItemFromPt(hwnd
, cursor
, scroll
);
965 ret
= LBItemFromPt(hwnd
, cursor
, FALSE
);
966 updist
= downdist
= 0;
967 for (i
= 1; i
< 4096 && (!updist
|| !downdist
); i
++) {
968 uppoint
= downpoint
= cursor
;
971 upitem
= LBItemFromPt(hwnd
, uppoint
, FALSE
);
972 downitem
= LBItemFromPt(hwnd
, downpoint
, FALSE
);
973 if (!updist
&& upitem
!= ret
)
975 if (!downdist
&& downitem
!= ret
)
978 if (downdist
< updist
)
984 * Handler for prefslist above.
986 int handle_prefslist(struct prefslist
*hdl
,
987 int *array
, int maxmemb
,
988 int is_dlmsg
, HWND hwnd
,
989 WPARAM wParam
, LPARAM lParam
)
996 if ((int)wParam
== hdl
->listid
) {
997 DRAGLISTINFO
*dlm
= (DRAGLISTINFO
*)lParam
;
999 switch (dlm
->uNotification
) {
1002 SendDlgItemMessage(hwnd
, hdl
->listid
,
1003 LB_ADDSTRING
, 0, (LPARAM
) "");
1005 hdl
->srcitem
= LBItemFromPt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1007 /* XXX hack Q183115 */
1008 SetWindowLong(hwnd
, DWL_MSGRESULT
, TRUE
);
1011 DrawInsert(hwnd
, dlm
->hWnd
, -1); /* Clear arrow */
1012 SendDlgItemMessage(hwnd
, hdl
->listid
,
1013 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1018 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1019 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1020 DrawInsert (hwnd
, dlm
->hWnd
, dest
);
1022 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_MOVECURSOR
);
1024 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_STOPCURSOR
);
1027 if (hdl
->dragging
) {
1028 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1029 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1030 DrawInsert (hwnd
, dlm
->hWnd
, -1);
1032 SendDlgItemMessage(hwnd
, hdl
->listid
,
1033 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1034 if (hdl
->dragging
) {
1037 /* Correct for "missing" item. */
1038 if (dest
> hdl
->srcitem
) dest
--;
1039 pl_moveitem(hwnd
, hdl
->listid
, hdl
->srcitem
, dest
);
1049 if (((LOWORD(wParam
) == hdl
->upbid
) ||
1050 (LOWORD(wParam
) == hdl
->dnbid
)) &&
1051 ((HIWORD(wParam
) == BN_CLICKED
) ||
1052 (HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1053 /* Move an item up or down the list. */
1054 /* Get the current selection, if any. */
1055 int selection
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCURSEL
, 0, 0);
1056 if (selection
== LB_ERR
) {
1060 /* Get the total number of items. */
1061 nitems
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCOUNT
, 0, 0);
1062 /* Should we do anything? */
1063 if (LOWORD(wParam
) == hdl
->upbid
&& (selection
> 0))
1064 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
- 1);
1065 else if (LOWORD(wParam
) == hdl
->dnbid
&& (selection
< nitems
- 1))
1066 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
+ 1);
1073 /* Update array to match the list box. */
1074 for (i
=0; i
< maxmemb
; i
++)
1075 array
[i
] = SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETITEMDATA
,
1083 * A progress bar (from Common Controls). We like our progress bars
1084 * to be smooth and unbroken, without those ugly divisions; some
1085 * older compilers may not support that, but that's life.
1087 void progressbar(struct ctlpos
*cp
, int id
)
1091 r
.left
= GAPBETWEEN
;
1093 r
.right
= cp
->width
;
1094 r
.bottom
= PROGBARHEIGHT
;
1095 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
1097 doctl(cp
, r
, PROGRESS_CLASS
, WS_CHILD
| WS_VISIBLE
1101 , WS_EX_CLIENTEDGE
, "", id
);
1105 * Another special control: the forwarding options setter. First a
1106 * list box; next a static header line, introducing a pair of edit
1107 * boxes with associated statics, another button, and a radio
1110 void fwdsetter(struct ctlpos
*cp
, int listid
, char *stext
, int sid
,
1111 char *e1stext
, int e1sid
, int e1id
,
1112 char *e2stext
, int e2sid
, int e2id
,
1113 char *btext
, int bid
)
1116 const int height
= (STATICHEIGHT
> EDITHEIGHT
1118 PUSHBTNHEIGHT ? STATICHEIGHT
: EDITHEIGHT
>
1119 PUSHBTNHEIGHT ? EDITHEIGHT
: PUSHBTNHEIGHT
);
1120 const static int percents
[] = { 25, 35, 15, 25 };
1121 int i
, j
, xpos
, percent
;
1122 const int LISTHEIGHT
= 42;
1125 r
.left
= GAPBETWEEN
;
1127 r
.right
= cp
->width
;
1128 r
.bottom
= LISTHEIGHT
;
1129 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
1130 doctl(cp
, r
, "LISTBOX",
1131 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| LBS_HASSTRINGS
1132 | LBS_USETABSTOPS
, WS_EX_CLIENTEDGE
, "", listid
);
1134 /* The static control. */
1135 r
.left
= GAPBETWEEN
;
1137 r
.right
= cp
->width
;
1138 r
.bottom
= STATICHEIGHT
;
1139 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
1140 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
1142 /* The statics+edits+buttons. */
1143 for (j
= 0; j
< 2; j
++) {
1145 for (i
= 0; i
< (j ?
2 : 4); i
++) {
1146 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
1147 r
.left
= xpos
+ GAPBETWEEN
;
1148 percent
+= percents
[i
];
1149 if (j
==1 && i
==1) percent
= 100;
1150 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
1151 r
.right
= xpos
- r
.left
;
1153 r
.bottom
= (i
== 0 ? STATICHEIGHT
:
1154 i
== 1 ? EDITHEIGHT
: PUSHBTNHEIGHT
);
1155 r
.top
+= (height
- r
.bottom
) / 2;
1157 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0,
1158 j
== 0 ? e1stext
: e2stext
, j
== 0 ? e1sid
: e2sid
);
1159 } else if (i
== 1) {
1160 doctl(cp
, r
, "EDIT",
1161 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
1162 WS_EX_CLIENTEDGE
, "", j
== 0 ? e1id
: e2id
);
1163 } else if (i
== 3) {
1164 doctl(cp
, r
, "BUTTON",
1165 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
1169 cp
->ypos
+= height
+ GAPWITHIN
;