2 * winctrls.c: routines to self-manage the controls in a dialog
7 * Possible TODO in new cross-platform config box stuff:
9 * - When lining up two controls alongside each other, I wonder if
10 * we could conveniently arrange to centre them vertically?
11 * Particularly ugly in the current setup is the `Add new
12 * forwarded port:' static next to the rather taller `Remove'
32 #define STATICHEIGHT 8
33 #define TITLEHEIGHT 12
34 #define CHECKBOXHEIGHT 8
38 #define LISTINCREMENT 8
39 #define COMBOHEIGHT 12
40 #define PUSHBTNHEIGHT 14
41 #define PROGBARHEIGHT 14
43 void ctlposinit(struct ctlpos
*cp
, HWND hwnd
,
44 int leftborder
, int rightborder
, int topborder
)
48 cp
->font
= SendMessage(hwnd
, WM_GETFONT
, 0, 0);
50 GetClientRect(hwnd
, &r
);
54 MapDialogRect(hwnd
, &r2
);
55 cp
->dlu4inpix
= r2
.right
;
56 cp
->width
= (r
.right
* 4) / (r2
.right
) - 2 * GAPBETWEEN
;
57 cp
->xoff
= leftborder
;
58 cp
->width
-= leftborder
+ rightborder
;
61 HWND
doctl(struct ctlpos
*cp
, RECT r
,
62 char *wclass
, int wstyle
, int exstyle
, char *wtext
, int wid
)
66 * Note nonstandard use of RECT. This is deliberate: by
67 * transforming the width and height directly we arrange to
68 * have all supposedly same-sized controls really same-sized.
72 MapDialogRect(cp
->hwnd
, &r
);
75 * We can pass in cp->hwnd == NULL, to indicate a dry run
76 * without creating any actual controls.
79 ctl
= CreateWindowEx(exstyle
, wclass
, wtext
, wstyle
,
80 r
.left
, r
.top
, r
.right
, r
.bottom
,
81 cp
->hwnd
, (HMENU
) wid
, hinst
, NULL
);
82 SendMessage(ctl
, WM_SETFONT
, cp
->font
, MAKELPARAM(TRUE
, 0));
84 if (!strcmp(wclass
, "LISTBOX")) {
86 * Bizarre Windows bug: the list box calculates its
87 * number of lines based on the font it has at creation
88 * time, but sending it WM_SETFONT doesn't cause it to
89 * recalculate. So now, _after_ we've sent it
90 * WM_SETFONT, we explicitly resize it (to the same
91 * size it was already!) to force it to reconsider.
93 SetWindowPos(ctl
, NULL
, 0, 0, r
.right
, r
.bottom
,
94 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
95 SWP_NOMOVE
| SWP_NOZORDER
);
102 * A title bar across the top of a sub-dialog.
104 void bartitle(struct ctlpos
*cp
, char *name
, int id
)
111 r
.bottom
= STATICHEIGHT
;
112 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
113 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, name
, id
);
117 * Begin a grouping box, with or without a group title.
119 void beginbox(struct ctlpos
*cp
, char *name
, int idbox
)
121 cp
->boxystart
= cp
->ypos
;
123 cp
->boxystart
-= STATICHEIGHT
/ 2;
125 cp
->ypos
+= STATICHEIGHT
;
127 cp
->width
-= 2 * GAPXBOX
;
134 * End a grouping box.
136 void endbox(struct ctlpos
*cp
)
140 cp
->width
+= 2 * GAPXBOX
;
141 cp
->ypos
+= GAPYBOX
- GAPBETWEEN
;
144 r
.top
= cp
->boxystart
;
145 r
.bottom
= cp
->ypos
- cp
->boxystart
;
146 doctl(cp
, r
, "BUTTON", BS_GROUPBOX
| WS_CHILD
| WS_VISIBLE
, 0,
147 cp
->boxtext ? cp
->boxtext
: "", cp
->boxid
);
152 * Some edit boxes. Each one has a static above it. The percentages
153 * of the horizontal space are provided.
155 void multiedit(struct ctlpos
*cp
, int password
, ...)
162 va_start(ap
, password
);
165 int staticid
, editid
, pcwidth
;
166 text
= va_arg(ap
, char *);
169 staticid
= va_arg(ap
, int);
170 editid
= va_arg(ap
, int);
171 pcwidth
= va_arg(ap
, int);
173 r
.left
= xpos
+ GAPBETWEEN
;
175 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
176 r
.right
= xpos
- r
.left
;
179 r
.bottom
= STATICHEIGHT
;
180 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
181 r
.top
= cp
->ypos
+ 8 + GAPWITHIN
;
182 r
.bottom
= EDITHEIGHT
;
184 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
|
185 (password ? ES_PASSWORD
: 0),
186 WS_EX_CLIENTEDGE
, "", editid
);
189 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ EDITHEIGHT
+ GAPBETWEEN
;
193 * A static line, followed by a full-width combo box.
195 void combobox(struct ctlpos
*cp
, char *text
, int staticid
, int listid
)
203 r
.bottom
= STATICHEIGHT
;
204 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
205 r
.top
= cp
->ypos
+ 8 + GAPWITHIN
;
206 r
.bottom
= COMBOHEIGHT
* 10;
207 doctl(cp
, r
, "COMBOBOX",
208 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
209 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", listid
);
211 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ COMBOHEIGHT
+ GAPBETWEEN
;
214 struct radio
{ char *text
; int id
; };
216 static void radioline_common(struct ctlpos
*cp
, char *text
, int id
,
217 int nacross
, struct radio
*buttons
, int nbuttons
)
228 r
.bottom
= STATICHEIGHT
;
229 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
230 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
235 for (j
= 0; j
< nbuttons
; j
++) {
236 char *btext
= buttons
[j
].text
;
237 int bid
= buttons
[j
].id
;
240 cp
->ypos
+= r
.bottom
+ (nacross
> 1 ? GAPBETWEEN
: GAPWITHIN
);
243 r
.left
= GAPBETWEEN
+ i
* (cp
->width
+ GAPBETWEEN
) / nacross
;
246 (i
+ 1) * (cp
->width
+ GAPBETWEEN
) / nacross
- r
.left
;
248 r
.right
= cp
->width
- r
.left
;
250 r
.bottom
= RADIOHEIGHT
;
251 doctl(cp
, r
, "BUTTON",
252 BS_NOTIFY
| BS_AUTORADIOBUTTON
| WS_CHILD
|
253 WS_VISIBLE
| WS_TABSTOP
| group
, 0, btext
, bid
);
257 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
261 * A set of radio buttons on the same line, with a static above
262 * them. `nacross' dictates how many parts the line is divided into
263 * (you might want this not to equal the number of buttons if you
264 * needed to line up some 2s and some 3s to look good in the same
267 * There's a bit of a hack in here to ensure that if nacross
268 * exceeds the actual number of buttons, the rightmost button
269 * really does get all the space right to the edge of the line, so
270 * you can do things like
272 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
274 void radioline(struct ctlpos
*cp
, char *text
, int id
, int nacross
, ...)
277 struct radio
*buttons
;
280 va_start(ap
, nacross
);
283 char *btext
= va_arg(ap
, char *);
287 bid
= va_arg(ap
, int);
290 buttons
= smalloc(nbuttons
* sizeof(struct radio
));
291 va_start(ap
, nacross
);
292 for (i
= 0; i
< nbuttons
; i
++) {
293 buttons
[i
].text
= va_arg(ap
, char *);
294 buttons
[i
].id
= va_arg(ap
, int);
297 radioline_common(cp
, text
, id
, nacross
, buttons
, nbuttons
);
302 * A set of radio buttons on the same line, without a static above
303 * them. Otherwise just like radioline.
305 void bareradioline(struct ctlpos
*cp
, int nacross
, ...)
308 struct radio
*buttons
;
311 va_start(ap
, nacross
);
314 char *btext
= va_arg(ap
, char *);
318 bid
= va_arg(ap
, int);
321 buttons
= smalloc(nbuttons
* sizeof(struct radio
));
322 va_start(ap
, nacross
);
323 for (i
= 0; i
< nbuttons
; i
++) {
324 buttons
[i
].text
= va_arg(ap
, char *);
325 buttons
[i
].id
= va_arg(ap
, int);
328 radioline_common(cp
, NULL
, 0, nacross
, buttons
, nbuttons
);
333 * A set of radio buttons on multiple lines, with a static above
336 void radiobig(struct ctlpos
*cp
, char *text
, int id
, ...)
339 struct radio
*buttons
;
345 char *btext
= va_arg(ap
, char *);
349 bid
= va_arg(ap
, int);
352 buttons
= smalloc(nbuttons
* sizeof(struct radio
));
354 for (i
= 0; i
< nbuttons
; i
++) {
355 buttons
[i
].text
= va_arg(ap
, char *);
356 buttons
[i
].id
= va_arg(ap
, int);
359 radioline_common(cp
, text
, id
, 1, buttons
, nbuttons
);
364 * A single standalone checkbox.
366 void checkbox(struct ctlpos
*cp
, char *text
, int id
)
373 r
.bottom
= CHECKBOXHEIGHT
;
374 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
375 doctl(cp
, r
, "BUTTON",
376 BS_NOTIFY
| BS_AUTOCHECKBOX
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0,
381 * Wrap a piece of text for a static text control. Returns the
382 * wrapped text (a malloc'ed string containing \ns), and also
383 * returns the number of lines required.
385 char *staticwrap(struct ctlpos
*cp
, HWND hwnd
, char *text
, int *lines
)
387 HFONT font
= (HFONT
) cp
->font
;
388 HDC hdc
= GetDC(hwnd
);
389 int lpx
= GetDeviceCaps(hdc
, LOGPIXELSX
);
390 int width
, nlines
, j
;
396 ret
= smalloc(1+strlen(text
));
399 pwidths
= smalloc(sizeof(INT
)*(1+strlen(text
)));
402 * Work out the width the text will need to fit in, by doing
403 * the same adjustment that the `statictext' function itself
406 * We must first convert from dialog-box units into pixels, and
407 * then from pixels into the `logical units' that Windows uses
408 * within GDI. You can't make this stuff up.
410 r
.left
= r
.top
= r
.bottom
= 0;
412 MapDialogRect(hwnd
, &r
);
413 width
= MulDiv(r
.right
, lpx
, 72);
418 if (!GetTextExtentExPoint(hdc
, p
, strlen(p
), width
,
419 &nfit
, pwidths
, &size
) ||
420 (size_t)nfit
>= strlen(p
)) {
422 * Either GetTextExtentExPoint returned failure, or the
423 * whole of the rest of the text fits on this line.
424 * Either way, we stop wrapping, copy the remainder of
425 * the input string unchanged to the output, and leave.
432 * Now we search backwards along the string from `nfit',
433 * looking for a space at which to break the line. If we
434 * don't find one at all, that's fine - we'll just break
435 * the line at `nfit'.
437 for (j
= nfit
; j
> 0; j
--) {
438 if (isspace((unsigned char)p
[j
])) {
449 while (*p
&& isspace((unsigned char)*p
))
455 ReleaseDC(cp
->hwnd
, hdc
);
457 if (lines
) *lines
= nlines
;
463 * A single standalone static text control.
465 void statictext(struct ctlpos
*cp
, char *text
, int lines
, int id
)
472 r
.bottom
= STATICHEIGHT
* lines
;
473 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
474 doctl(cp
, r
, "STATIC",
475 WS_CHILD
| WS_VISIBLE
| SS_LEFTNOWORDWRAP
,
480 * An owner-drawn static text control for a panel title.
482 void paneltitle(struct ctlpos
*cp
, int id
)
489 r
.bottom
= TITLEHEIGHT
;
490 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
491 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_OWNERDRAW
,
496 * A button on the right hand side, with a static to its left.
498 void staticbtn(struct ctlpos
*cp
, char *stext
, int sid
,
499 char *btext
, int bid
)
501 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
502 PUSHBTNHEIGHT
: STATICHEIGHT
);
504 int lwid
, rwid
, rpos
;
506 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
507 lwid
= rpos
- 2 * GAPBETWEEN
;
508 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
511 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
513 r
.bottom
= STATICHEIGHT
;
514 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
517 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
519 r
.bottom
= PUSHBTNHEIGHT
;
520 doctl(cp
, r
, "BUTTON",
521 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
524 cp
->ypos
+= height
+ GAPBETWEEN
;
528 * A simple push button.
530 void button(struct ctlpos
*cp
, char *btext
, int bid
, int defbtn
)
537 r
.bottom
= PUSHBTNHEIGHT
;
539 /* Q67655: the _dialog box_ must know which button is default
540 * as well as the button itself knowing */
541 if (defbtn
&& cp
->hwnd
)
542 SendMessage(cp
->hwnd
, DM_SETDEFID
, bid
, 0);
544 doctl(cp
, r
, "BUTTON",
545 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
546 (defbtn ? BS_DEFPUSHBUTTON
: 0) | BS_PUSHBUTTON
,
549 cp
->ypos
+= PUSHBTNHEIGHT
+ GAPBETWEEN
;
553 * Like staticbtn, but two buttons.
555 void static2btn(struct ctlpos
*cp
, char *stext
, int sid
,
556 char *btext1
, int bid1
, char *btext2
, int bid2
)
558 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
559 PUSHBTNHEIGHT
: STATICHEIGHT
);
561 int lwid
, rwid1
, rwid2
, rpos1
, rpos2
;
563 rpos1
= GAPBETWEEN
+ (cp
->width
+ GAPBETWEEN
) / 2;
564 rpos2
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
565 lwid
= rpos1
- 2 * GAPBETWEEN
;
566 rwid1
= rpos2
- rpos1
- GAPBETWEEN
;
567 rwid2
= cp
->width
+ GAPBETWEEN
- rpos2
;
570 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
572 r
.bottom
= STATICHEIGHT
;
573 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
576 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
578 r
.bottom
= PUSHBTNHEIGHT
;
579 doctl(cp
, r
, "BUTTON",
580 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
584 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
586 r
.bottom
= PUSHBTNHEIGHT
;
587 doctl(cp
, r
, "BUTTON",
588 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
591 cp
->ypos
+= height
+ GAPBETWEEN
;
595 * An edit control on the right hand side, with a static to its left.
597 static void staticedit_internal(struct ctlpos
*cp
, char *stext
,
598 int sid
, int eid
, int percentedit
,
601 const int height
= (EDITHEIGHT
> STATICHEIGHT ?
602 EDITHEIGHT
: STATICHEIGHT
);
604 int lwid
, rwid
, rpos
;
607 GAPBETWEEN
+ (100 - percentedit
) * (cp
->width
+ GAPBETWEEN
) / 100;
608 lwid
= rpos
- 2 * GAPBETWEEN
;
609 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
612 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
614 r
.bottom
= STATICHEIGHT
;
615 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
618 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
620 r
.bottom
= EDITHEIGHT
;
622 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
| style
,
623 WS_EX_CLIENTEDGE
, "", eid
);
625 cp
->ypos
+= height
+ GAPBETWEEN
;
628 void staticedit(struct ctlpos
*cp
, char *stext
,
629 int sid
, int eid
, int percentedit
)
631 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, 0);
634 void staticpassedit(struct ctlpos
*cp
, char *stext
,
635 int sid
, int eid
, int percentedit
)
637 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, ES_PASSWORD
);
641 * A drop-down list box on the right hand side, with a static to
644 void staticddl(struct ctlpos
*cp
, char *stext
,
645 int sid
, int lid
, int percentlist
)
647 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
648 COMBOHEIGHT
: STATICHEIGHT
);
650 int lwid
, rwid
, rpos
;
653 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
654 lwid
= rpos
- 2 * GAPBETWEEN
;
655 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
658 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
660 r
.bottom
= STATICHEIGHT
;
661 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
664 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
666 r
.bottom
= COMBOHEIGHT
*4;
667 doctl(cp
, r
, "COMBOBOX",
668 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
669 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
671 cp
->ypos
+= height
+ GAPBETWEEN
;
675 * A combo box on the right hand side, with a static to its left.
677 void staticcombo(struct ctlpos
*cp
, char *stext
,
678 int sid
, int lid
, int percentlist
)
680 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
681 COMBOHEIGHT
: STATICHEIGHT
);
683 int lwid
, rwid
, rpos
;
686 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
687 lwid
= rpos
- 2 * GAPBETWEEN
;
688 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
691 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
693 r
.bottom
= STATICHEIGHT
;
694 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
697 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
699 r
.bottom
= COMBOHEIGHT
*10;
700 doctl(cp
, r
, "COMBOBOX",
701 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
702 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
704 cp
->ypos
+= height
+ GAPBETWEEN
;
708 * A static, with a full-width drop-down list box below it.
710 void staticddlbig(struct ctlpos
*cp
, char *stext
,
718 r
.bottom
= STATICHEIGHT
;
719 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
720 cp
->ypos
+= STATICHEIGHT
;
725 r
.bottom
= COMBOHEIGHT
*4;
726 doctl(cp
, r
, "COMBOBOX",
727 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
728 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
729 cp
->ypos
+= COMBOHEIGHT
+ GAPBETWEEN
;
733 * A big multiline edit control with a static labelling it.
735 void bigeditctrl(struct ctlpos
*cp
, char *stext
,
736 int sid
, int eid
, int lines
)
743 r
.bottom
= STATICHEIGHT
;
744 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
745 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
750 r
.bottom
= EDITHEIGHT
+ (lines
- 1) * STATICHEIGHT
;
751 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
753 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| ES_MULTILINE
,
754 WS_EX_CLIENTEDGE
, "", eid
);
758 * A list box with a static labelling it.
760 void listbox(struct ctlpos
*cp
, char *stext
,
761 int sid
, int lid
, int lines
, int multi
)
769 r
.bottom
= STATICHEIGHT
;
770 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
771 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
777 r
.bottom
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
778 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
779 doctl(cp
, r
, "LISTBOX",
780 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
781 LBS_NOTIFY
| LBS_HASSTRINGS
| LBS_USETABSTOPS
|
782 (multi ? LBS_MULTIPLESEL
: 0),
783 WS_EX_CLIENTEDGE
, "", lid
);
787 * A tab-control substitute when a real tab control is unavailable.
789 void ersatztab(struct ctlpos
*cp
, char *stext
, int sid
, int lid
, int s2id
)
791 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
792 COMBOHEIGHT
: STATICHEIGHT
);
794 int bigwid
, lwid
, rwid
, rpos
;
795 static const int BIGGAP
= 15;
796 static const int MEDGAP
= 3;
798 bigwid
= cp
->width
+ 2 * GAPBETWEEN
- 2 * BIGGAP
;
800 rpos
= BIGGAP
+ (bigwid
+ BIGGAP
) / 2;
801 lwid
= rpos
- 2 * BIGGAP
;
802 rwid
= bigwid
+ BIGGAP
- rpos
;
805 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
807 r
.bottom
= STATICHEIGHT
;
808 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
811 r
.top
= cp
->ypos
+ (height
- COMBOHEIGHT
) / 2;
813 r
.bottom
= COMBOHEIGHT
* 10;
814 doctl(cp
, r
, "COMBOBOX",
815 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
816 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
818 cp
->ypos
+= height
+ MEDGAP
+ GAPBETWEEN
;
824 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_ETCHEDHORZ
,
829 * A static line, followed by an edit control on the left hand side
830 * and a button on the right.
832 void editbutton(struct ctlpos
*cp
, char *stext
, int sid
,
833 int eid
, char *btext
, int bid
)
835 const int height
= (EDITHEIGHT
> PUSHBTNHEIGHT ?
836 EDITHEIGHT
: PUSHBTNHEIGHT
);
838 int lwid
, rwid
, rpos
;
843 r
.bottom
= STATICHEIGHT
;
844 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
845 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
847 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
848 lwid
= rpos
- 2 * GAPBETWEEN
;
849 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
852 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
854 r
.bottom
= EDITHEIGHT
;
856 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
857 WS_EX_CLIENTEDGE
, "", eid
);
860 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
862 r
.bottom
= PUSHBTNHEIGHT
;
863 doctl(cp
, r
, "BUTTON",
864 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
867 cp
->ypos
+= height
+ GAPBETWEEN
;
871 * A special control for manipulating an ordered preference list
872 * (eg. for cipher selection).
873 * XXX: this is a rough hack and could be improved.
875 void prefslist(struct prefslist
*hdl
, struct ctlpos
*cp
, int lines
,
876 char *stext
, int sid
, int listid
, int upbid
, int dnbid
)
878 const static int percents
[] = { 5, 75, 20 };
880 int xpos
, percent
= 0, i
;
881 int listheight
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
882 const int BTNSHEIGHT
= 2*PUSHBTNHEIGHT
+ GAPBETWEEN
;
883 int totalheight
, buttonpos
;
885 /* Squirrel away IDs. */
886 hdl
->listid
= listid
;
890 /* The static label. */
895 r
.bottom
= STATICHEIGHT
;
896 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
897 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
900 if (listheight
> BTNSHEIGHT
) {
901 totalheight
= listheight
;
902 buttonpos
= (listheight
- BTNSHEIGHT
) / 2;
904 totalheight
= BTNSHEIGHT
;
908 for (i
=0; i
<3; i
++) {
910 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
911 left
= xpos
+ GAPBETWEEN
;
912 percent
+= percents
[i
];
913 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
918 /* The drag list box. */
919 r
.left
= left
; r
.right
= wid
;
920 r
.top
= cp
->ypos
; r
.bottom
= listheight
;
923 ctl
= doctl(cp
, r
, "LISTBOX",
924 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
925 WS_VSCROLL
| LBS_HASSTRINGS
| LBS_USETABSTOPS
,
933 /* The "Up" and "Down" buttons. */
934 /* XXX worry about accelerators if we have more than one
935 * prefslist on a panel */
936 r
.left
= left
; r
.right
= wid
;
937 r
.top
= cp
->ypos
+ buttonpos
; r
.bottom
= PUSHBTNHEIGHT
;
938 doctl(cp
, r
, "BUTTON",
939 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
940 WS_TABSTOP
| BS_PUSHBUTTON
,
943 r
.left
= left
; r
.right
= wid
;
944 r
.top
= cp
->ypos
+ buttonpos
+ PUSHBTNHEIGHT
+ GAPBETWEEN
;
945 r
.bottom
= PUSHBTNHEIGHT
;
946 doctl(cp
, r
, "BUTTON",
947 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
948 WS_TABSTOP
| BS_PUSHBUTTON
,
956 cp
->ypos
+= totalheight
+ GAPBETWEEN
;
961 * Helper function for prefslist: move item in list box.
963 static void pl_moveitem(HWND hwnd
, int listid
, int src
, int dst
)
967 /* Get the item's data. */
968 tlen
= SendDlgItemMessage (hwnd
, listid
, LB_GETTEXTLEN
, src
, 0);
969 txt
= smalloc(tlen
+1);
970 SendDlgItemMessage (hwnd
, listid
, LB_GETTEXT
, src
, (LPARAM
) txt
);
971 val
= SendDlgItemMessage (hwnd
, listid
, LB_GETITEMDATA
, src
, 0);
972 /* Deselect old location. */
973 SendDlgItemMessage (hwnd
, listid
, LB_SETSEL
, FALSE
, src
);
974 /* Delete it at the old location. */
975 SendDlgItemMessage (hwnd
, listid
, LB_DELETESTRING
, src
, 0);
976 /* Insert it at new location. */
977 SendDlgItemMessage (hwnd
, listid
, LB_INSERTSTRING
, dst
,
979 SendDlgItemMessage (hwnd
, listid
, LB_SETITEMDATA
, dst
,
982 SendDlgItemMessage (hwnd
, listid
, LB_SETCURSEL
, dst
, 0);
986 int pl_itemfrompt(HWND hwnd
, POINT cursor
, BOOL scroll
)
989 POINT uppoint
, downpoint
;
990 int updist
, downdist
, upitem
, downitem
, i
;
993 * Ghastly hackery to try to figure out not which
994 * _item_, but which _gap between items_, the user
995 * is pointing at. We do this by first working out
996 * which list item is under the cursor, and then
997 * working out how far the cursor would have to
998 * move up or down before the answer was different.
999 * Then we put the insertion point _above_ the
1000 * current item if the upper edge is closer than
1001 * the lower edge, or _below_ it if vice versa.
1003 ret
= LBItemFromPt(hwnd
, cursor
, scroll
);
1006 ret
= LBItemFromPt(hwnd
, cursor
, FALSE
);
1007 updist
= downdist
= 0;
1008 for (i
= 1; i
< 4096 && (!updist
|| !downdist
); i
++) {
1009 uppoint
= downpoint
= cursor
;
1012 upitem
= LBItemFromPt(hwnd
, uppoint
, FALSE
);
1013 downitem
= LBItemFromPt(hwnd
, downpoint
, FALSE
);
1014 if (!updist
&& upitem
!= ret
)
1016 if (!downdist
&& downitem
!= ret
)
1019 if (downdist
< updist
)
1025 * Handler for prefslist above.
1027 * Return value has bit 0 set if the dialog box procedure needs to
1028 * return TRUE from handling this message; it has bit 1 set if a
1029 * change may have been made in the contents of the list.
1031 int handle_prefslist(struct prefslist
*hdl
,
1032 int *array
, int maxmemb
,
1033 int is_dlmsg
, HWND hwnd
,
1034 WPARAM wParam
, LPARAM lParam
)
1041 if ((int)wParam
== hdl
->listid
) {
1042 DRAGLISTINFO
*dlm
= (DRAGLISTINFO
*)lParam
;
1044 switch (dlm
->uNotification
) {
1047 SendDlgItemMessage(hwnd
, hdl
->listid
,
1048 LB_ADDSTRING
, 0, (LPARAM
) "");
1050 hdl
->srcitem
= LBItemFromPt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1052 /* XXX hack Q183115 */
1053 SetWindowLong(hwnd
, DWL_MSGRESULT
, TRUE
);
1056 DrawInsert(hwnd
, dlm
->hWnd
, -1); /* Clear arrow */
1057 SendDlgItemMessage(hwnd
, hdl
->listid
,
1058 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1063 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1064 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1065 DrawInsert (hwnd
, dlm
->hWnd
, dest
);
1067 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_MOVECURSOR
);
1069 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_STOPCURSOR
);
1072 if (hdl
->dragging
) {
1073 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1074 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1075 DrawInsert (hwnd
, dlm
->hWnd
, -1);
1077 SendDlgItemMessage(hwnd
, hdl
->listid
,
1078 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1079 if (hdl
->dragging
) {
1082 /* Correct for "missing" item. */
1083 if (dest
> hdl
->srcitem
) dest
--;
1084 pl_moveitem(hwnd
, hdl
->listid
, hdl
->srcitem
, dest
);
1094 if (((LOWORD(wParam
) == hdl
->upbid
) ||
1095 (LOWORD(wParam
) == hdl
->dnbid
)) &&
1096 ((HIWORD(wParam
) == BN_CLICKED
) ||
1097 (HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1098 /* Move an item up or down the list. */
1099 /* Get the current selection, if any. */
1100 int selection
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCURSEL
, 0, 0);
1101 if (selection
== LB_ERR
) {
1105 /* Get the total number of items. */
1106 nitems
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCOUNT
, 0, 0);
1107 /* Should we do anything? */
1108 if (LOWORD(wParam
) == hdl
->upbid
&& (selection
> 0))
1109 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
- 1);
1110 else if (LOWORD(wParam
) == hdl
->dnbid
&& (selection
< nitems
- 1))
1111 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
+ 1);
1120 /* Update array to match the list box. */
1121 for (i
=0; i
< maxmemb
; i
++)
1122 array
[i
] = SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETITEMDATA
,
1130 * A progress bar (from Common Controls). We like our progress bars
1131 * to be smooth and unbroken, without those ugly divisions; some
1132 * older compilers may not support that, but that's life.
1134 void progressbar(struct ctlpos
*cp
, int id
)
1138 r
.left
= GAPBETWEEN
;
1140 r
.right
= cp
->width
;
1141 r
.bottom
= PROGBARHEIGHT
;
1142 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
1144 doctl(cp
, r
, PROGRESS_CLASS
, WS_CHILD
| WS_VISIBLE
1148 , WS_EX_CLIENTEDGE
, "", id
);
1151 /* ----------------------------------------------------------------------
1152 * Platform-specific side of portable dialog-box mechanism.
1156 * This function takes a string, escapes all the ampersands, and
1157 * places a single (unescaped) ampersand in front of the first
1158 * occurrence of the given shortcut character (which may be
1161 * Return value is a malloc'ed copy of the processed version of the
1164 static char *shortcut_escape(char *text
, char shortcut
)
1170 return NULL
; /* sfree won't choke on this */
1172 ret
= smalloc(2*strlen(text
)+1); /* size potentially doubles! */
1173 shortcut
= tolower((unsigned char)shortcut
);
1178 if (shortcut
!= NO_SHORTCUT
&&
1179 tolower((unsigned char)*p
) == shortcut
) {
1181 shortcut
= NO_SHORTCUT
; /* stop it happening twice */
1182 } else if (*p
== '&') {
1191 void winctrl_add_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1194 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1195 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1196 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1197 assert(!dp
->shortcuts
[s
]);
1198 dp
->shortcuts
[s
] = TRUE
;
1202 void winctrl_rem_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1205 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1206 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1207 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1208 assert(dp
->shortcuts
[s
]);
1209 dp
->shortcuts
[s
] = FALSE
;
1213 static int winctrl_cmp_byctrl(void *av
, void *bv
)
1215 struct winctrl
*a
= (struct winctrl
*)av
;
1216 struct winctrl
*b
= (struct winctrl
*)bv
;
1217 if (a
->ctrl
< b
->ctrl
)
1219 else if (a
->ctrl
> b
->ctrl
)
1224 static int winctrl_cmp_byid(void *av
, void *bv
)
1226 struct winctrl
*a
= (struct winctrl
*)av
;
1227 struct winctrl
*b
= (struct winctrl
*)bv
;
1228 if (a
->base_id
< b
->base_id
)
1230 else if (a
->base_id
> b
->base_id
)
1235 static int winctrl_cmp_byctrl_find(void *av
, void *bv
)
1237 union control
*a
= (union control
*)av
;
1238 struct winctrl
*b
= (struct winctrl
*)bv
;
1241 else if (a
> b
->ctrl
)
1246 static int winctrl_cmp_byid_find(void *av
, void *bv
)
1249 struct winctrl
*b
= (struct winctrl
*)bv
;
1250 if (*a
< b
->base_id
)
1252 else if (*a
>= b
->base_id
+ b
->num_ids
)
1258 void winctrl_init(struct winctrls
*wc
)
1260 wc
->byctrl
= newtree234(winctrl_cmp_byctrl
);
1261 wc
->byid
= newtree234(winctrl_cmp_byid
);
1263 void winctrl_cleanup(struct winctrls
*wc
)
1267 while ((c
= index234(wc
->byid
, 0)) != NULL
) {
1268 winctrl_remove(wc
, c
);
1273 freetree234(wc
->byctrl
);
1274 freetree234(wc
->byid
);
1275 wc
->byctrl
= wc
->byid
= NULL
;
1278 void winctrl_add(struct winctrls
*wc
, struct winctrl
*c
)
1280 struct winctrl
*ret
;
1282 ret
= add234(wc
->byctrl
, c
);
1285 ret
= add234(wc
->byid
, c
);
1289 void winctrl_remove(struct winctrls
*wc
, struct winctrl
*c
)
1291 struct winctrl
*ret
;
1292 ret
= del234(wc
->byctrl
, c
);
1293 ret
= del234(wc
->byid
, c
);
1297 struct winctrl
*winctrl_findbyctrl(struct winctrls
*wc
, union control
*ctrl
)
1299 return find234(wc
->byctrl
, ctrl
, winctrl_cmp_byctrl_find
);
1302 struct winctrl
*winctrl_findbyid(struct winctrls
*wc
, int id
)
1304 return find234(wc
->byid
, &id
, winctrl_cmp_byid_find
);
1307 struct winctrl
*winctrl_findbyindex(struct winctrls
*wc
, int index
)
1309 return index234(wc
->byid
, index
);
1312 void winctrl_layout(struct dlgparam
*dp
, struct winctrls
*wc
,
1313 struct ctlpos
*cp
, struct controlset
*s
, int *id
)
1315 struct ctlpos columns
[16];
1316 int ncols
, colstart
, colspan
;
1318 struct ctlpos tabdelays
[16];
1319 union control
*tabdelayed
[16];
1324 char shortcuts
[MAX_SHORTCUTS_PER_CTRL
], nshortcuts
;
1326 int i
, base_id
, num_ids
, orig_tabdelay
;
1331 /* Start a containing box, if we have a boxname. */
1332 if (s
->boxname
&& *s
->boxname
) {
1333 struct winctrl
*c
= smalloc(sizeof(struct winctrl
));
1335 c
->base_id
= base_id
;
1338 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1340 beginbox(cp
, s
->boxtitle
, base_id
);
1344 /* Draw a title, if we have one. */
1345 if (!s
->boxname
&& s
->boxtitle
) {
1346 struct winctrl
*c
= smalloc(sizeof(struct winctrl
));
1348 c
->base_id
= base_id
;
1350 c
->data
= dupstr(s
->boxtitle
);
1351 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1353 paneltitle(cp
, base_id
);
1357 /* Initially we have just one column. */
1359 columns
[0] = *cp
; /* structure copy */
1361 /* And initially, there are no pending tab-delayed controls. */
1364 /* Loop over each control in the controlset. */
1365 for (i
= 0; i
< s
->ncontrols
; i
++) {
1366 union control
*ctrl
= s
->ctrls
[i
];
1368 orig_tabdelay
= FALSE
;
1371 * Generic processing that pertains to all control types.
1372 * At the end of this if statement, we'll have produced
1373 * `ctrl' (a pointer to the control we have to create, or
1374 * think about creating, in this iteration of the loop),
1375 * `pos' (a suitable ctlpos with which to position it), and
1376 * `c' (a winctrl structure to receive details of the
1377 * dialog IDs). Or we'll have done a `continue', if it was
1378 * CTRL_COLUMNS and doesn't require any control creation at
1381 if (ctrl
->generic
.type
== CTRL_COLUMNS
) {
1382 assert((ctrl
->columns
.ncols
== 1) ^ (ncols
== 1));
1386 * We're splitting into multiple columns.
1388 int lpercent
, rpercent
, lx
, rx
, i
;
1390 ncols
= ctrl
->columns
.ncols
;
1391 assert(ncols
<= lenof(columns
));
1392 for (i
= 1; i
< ncols
; i
++)
1393 columns
[i
] = columns
[0]; /* structure copy */
1396 for (i
= 0; i
< ncols
; i
++) {
1397 rpercent
= lpercent
+ ctrl
->columns
.percentages
[i
];
1398 lx
= columns
[i
].xoff
+ lpercent
*
1399 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1400 rx
= columns
[i
].xoff
+ rpercent
*
1401 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1402 columns
[i
].xoff
= lx
;
1403 columns
[i
].width
= rx
- lx
- GAPBETWEEN
;
1404 lpercent
= rpercent
;
1408 * We're recombining the various columns into one.
1410 int maxy
= columns
[0].ypos
;
1412 for (i
= 1; i
< ncols
; i
++)
1413 if (maxy
< columns
[i
].ypos
)
1414 maxy
= columns
[i
].ypos
;
1416 columns
[0] = *cp
; /* structure copy */
1417 columns
[0].ypos
= maxy
;
1421 } else if (ctrl
->generic
.type
== CTRL_TABDELAY
) {
1424 assert(!ctrl
->generic
.tabdelay
);
1425 ctrl
= ctrl
->tabdelay
.ctrl
;
1426 orig_tabdelay
= TRUE
;
1428 for (i
= 0; i
< ntabdelays
; i
++)
1429 if (tabdelayed
[i
] == ctrl
)
1431 assert(i
< ntabdelays
); /* we have to have found it */
1433 pos
= tabdelays
[i
]; /* structure copy */
1437 * If it wasn't one of those, it's a genuine control;
1438 * so we'll have to compute a position for it now, by
1439 * checking its column span.
1443 colstart
= COLUMN_START(ctrl
->generic
.column
);
1444 colspan
= COLUMN_SPAN(ctrl
->generic
.column
);
1446 pos
= columns
[colstart
]; /* structure copy */
1447 pos
.width
= columns
[colstart
+colspan
-1].width
+
1448 (columns
[colstart
+colspan
-1].xoff
- columns
[colstart
].xoff
);
1450 for (col
= colstart
; col
< colstart
+colspan
; col
++)
1451 if (pos
.ypos
< columns
[col
].ypos
)
1452 pos
.ypos
= columns
[col
].ypos
;
1455 * If this control is to be tabdelayed, add it to the
1456 * tabdelay list, and unset pos.hwnd to inhibit actual
1459 if (ctrl
->generic
.tabdelay
) {
1460 assert(ntabdelays
< lenof(tabdelays
));
1461 tabdelays
[ntabdelays
] = pos
; /* structure copy */
1462 tabdelayed
[ntabdelays
] = ctrl
;
1468 /* Most controls don't need anything in c->data. */
1471 /* And they all start off with no shortcuts registered. */
1472 memset(shortcuts
, NO_SHORTCUT
, lenof(shortcuts
));
1476 * Now we're ready to actually create the control, by
1477 * switching on its type.
1479 switch (ctrl
->generic
.type
) {
1482 char *wrapped
, *escaped
;
1485 wrapped
= staticwrap(&pos
, cp
->hwnd
,
1486 ctrl
->generic
.label
, &lines
);
1487 escaped
= shortcut_escape(wrapped
, NO_SHORTCUT
);
1488 statictext(&pos
, escaped
, lines
, base_id
);
1494 num_ids
= 2; /* static, edit */
1495 escaped
= shortcut_escape(ctrl
->editbox
.label
,
1496 ctrl
->editbox
.shortcut
);
1497 shortcuts
[nshortcuts
++] = ctrl
->editbox
.shortcut
;
1498 if (ctrl
->editbox
.percentwidth
== 100) {
1499 if (ctrl
->editbox
.has_list
)
1500 combobox(&pos
, escaped
,
1501 base_id
, base_id
+1);
1503 multiedit(&pos
, ctrl
->editbox
.password
, escaped
,
1504 base_id
, base_id
+1, 100, NULL
);
1506 if (ctrl
->editbox
.has_list
) {
1507 staticcombo(&pos
, escaped
, base_id
, base_id
+1,
1508 ctrl
->editbox
.percentwidth
);
1510 (ctrl
->editbox
.password ? staticpassedit
: staticedit
)
1511 (&pos
, escaped
, base_id
, base_id
+1,
1512 ctrl
->editbox
.percentwidth
);
1518 num_ids
= ctrl
->radio
.nbuttons
+ 1; /* label as well */
1520 struct radio
*buttons
;
1523 escaped
= shortcut_escape(ctrl
->radio
.label
,
1524 ctrl
->radio
.shortcut
);
1525 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcut
;
1527 buttons
= smalloc(ctrl
->radio
.nbuttons
* sizeof(struct radio
));
1529 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1531 shortcut_escape(ctrl
->radio
.buttons
[i
],
1532 (char)(ctrl
->radio
.shortcuts ?
1533 ctrl
->radio
.shortcuts
[i
] :
1535 buttons
[i
].id
= base_id
+ 1 + i
;
1536 if (ctrl
->radio
.shortcuts
) {
1537 assert(nshortcuts
< MAX_SHORTCUTS_PER_CTRL
);
1538 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcuts
[i
];
1542 radioline_common(&pos
, escaped
, base_id
,
1543 ctrl
->radio
.ncolumns
,
1544 buttons
, ctrl
->radio
.nbuttons
);
1546 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1547 sfree(buttons
[i
].text
);
1555 escaped
= shortcut_escape(ctrl
->checkbox
.label
,
1556 ctrl
->checkbox
.shortcut
);
1557 shortcuts
[nshortcuts
++] = ctrl
->checkbox
.shortcut
;
1558 checkbox(&pos
, escaped
, base_id
);
1562 escaped
= shortcut_escape(ctrl
->button
.label
,
1563 ctrl
->button
.shortcut
);
1564 shortcuts
[nshortcuts
++] = ctrl
->button
.shortcut
;
1566 button(&pos
, escaped
, base_id
, ctrl
->button
.isdefault
);
1571 escaped
= shortcut_escape(ctrl
->listbox
.label
,
1572 ctrl
->listbox
.shortcut
);
1573 shortcuts
[nshortcuts
++] = ctrl
->listbox
.shortcut
;
1574 if (ctrl
->listbox
.draglist
) {
1575 data
= smalloc(sizeof(struct prefslist
));
1577 prefslist(data
, &pos
, ctrl
->listbox
.height
, escaped
,
1578 base_id
, base_id
+1, base_id
+2, base_id
+3);
1579 shortcuts
[nshortcuts
++] = 'u'; /* Up */
1580 shortcuts
[nshortcuts
++] = 'd'; /* Down */
1581 } else if (ctrl
->listbox
.height
== 0) {
1582 /* Drop-down list. */
1583 if (ctrl
->listbox
.percentwidth
== 100) {
1584 staticddlbig(&pos
, escaped
,
1585 base_id
, base_id
+1);
1587 staticddl(&pos
, escaped
, base_id
,
1588 base_id
+1, ctrl
->listbox
.percentwidth
);
1591 /* Ordinary list. */
1592 listbox(&pos
, escaped
, base_id
, base_id
+1,
1593 ctrl
->listbox
.height
, ctrl
->listbox
.multisel
);
1595 if (ctrl
->listbox
.ncols
) {
1597 * This method of getting the box width is a bit of
1598 * a hack; we'd do better to try to retrieve the
1599 * actual width in dialog units from doctl() just
1600 * before MapDialogRect. But that's going to be no
1601 * fun, and this should be good enough accuracy.
1603 int width
= cp
->width
* ctrl
->listbox
.percentwidth
;
1607 tabarray
= smalloc((ctrl
->listbox
.ncols
-1) * sizeof(int));
1609 for (i
= 0; i
< ctrl
->listbox
.ncols
-1; i
++) {
1610 percent
+= ctrl
->listbox
.percentages
[i
];
1611 tabarray
[i
] = width
* percent
/ 10000;
1613 SendDlgItemMessage(cp
->hwnd
, base_id
+1, LB_SETTABSTOPS
,
1614 ctrl
->listbox
.ncols
-1, (LPARAM
)tabarray
);
1619 case CTRL_FILESELECT
:
1621 escaped
= shortcut_escape(ctrl
->fileselect
.label
,
1622 ctrl
->fileselect
.shortcut
);
1623 shortcuts
[nshortcuts
++] = ctrl
->fileselect
.shortcut
;
1624 editbutton(&pos
, escaped
, base_id
, base_id
+1,
1625 "Bro&wse...", base_id
+2);
1626 shortcuts
[nshortcuts
++] = 'w';
1629 case CTRL_FONTSELECT
:
1631 escaped
= shortcut_escape(ctrl
->fontselect
.label
,
1632 ctrl
->fontselect
.shortcut
);
1633 shortcuts
[nshortcuts
++] = ctrl
->fontselect
.shortcut
;
1634 statictext(&pos
, escaped
, 1, base_id
);
1635 staticbtn(&pos
, "", base_id
+1, "Change...", base_id
+2);
1637 data
= smalloc(sizeof(FontSpec
));
1640 assert(!"Can't happen");
1645 * Create a `struct winctrl' for this control, and advance
1646 * the dialog ID counter, if it's actually been created
1647 * (and isn't tabdelayed).
1650 struct winctrl
*c
= smalloc(sizeof(struct winctrl
));
1653 c
->base_id
= base_id
;
1654 c
->num_ids
= num_ids
;
1656 memcpy(c
->shortcuts
, shortcuts
, sizeof(shortcuts
));
1658 winctrl_add_shortcuts(dp
, c
);
1662 if (!orig_tabdelay
) {
1664 * Update the ypos in all columns crossed by this
1668 for (i
= colstart
; i
< colstart
+colspan
; i
++)
1669 columns
[i
].ypos
= pos
.ypos
;
1674 * We've now finished laying out the controls; so now update
1675 * the ctlpos and control ID that were passed in, terminate
1676 * any containing box, and return.
1678 for (i
= 0; i
< ncols
; i
++)
1679 if (cp
->ypos
< columns
[i
].ypos
)
1680 cp
->ypos
= columns
[i
].ypos
;
1683 if (s
->boxname
&& *s
->boxname
)
1687 static void winctrl_set_focus(union control
*ctrl
, struct dlgparam
*dp
,
1692 dp
->lastfocused
= dp
->focused
;
1694 } else if (!has_focus
&& dp
->focused
== ctrl
) {
1695 dp
->lastfocused
= dp
->focused
;
1700 union control
*dlg_last_focused(void *dlg
)
1702 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
1703 return dp
->lastfocused
;
1707 * The dialog-box procedure calls this function to handle Windows
1708 * messages on a control we manage.
1710 int winctrl_handle_command(struct dlgparam
*dp
, UINT msg
,
1711 WPARAM wParam
, LPARAM lParam
)
1714 union control
*ctrl
;
1716 static UINT draglistmsg
= WM_NULL
;
1719 * Filter out pointless window messages. Our interest is in
1720 * WM_COMMAND and the drag list message, and nothing else.
1722 if (draglistmsg
== WM_NULL
)
1723 draglistmsg
= RegisterWindowMessage (DRAGLISTMSGSTRING
);
1725 if (msg
!= draglistmsg
&& msg
!= WM_COMMAND
&& msg
!= WM_DRAWITEM
)
1729 * Look up the control ID in our data.
1731 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
1732 c
= winctrl_findbyid(dp
->controltrees
[i
], LOWORD(wParam
));
1737 return 0; /* we have nothing to do */
1739 if (msg
== WM_DRAWITEM
) {
1741 * Owner-draw request for a panel title.
1743 LPDRAWITEMSTRUCT di
= (LPDRAWITEMSTRUCT
) lParam
;
1745 RECT r
= di
->rcItem
;
1748 GetTextExtentPoint32(hdc
, (char *)c
->data
,
1749 strlen((char *)c
->data
), &s
);
1750 DrawEdge(hdc
, &r
, EDGE_ETCHED
, BF_ADJUST
| BF_RECT
);
1752 r
.left
+ (r
.right
-r
.left
-s
.cx
)/2,
1753 r
.top
+ (r
.bottom
-r
.top
-s
.cy
)/2,
1754 (char *)c
->data
, strlen((char *)c
->data
));
1760 id
= LOWORD(wParam
) - c
->base_id
;
1762 if (!ctrl
|| !ctrl
->generic
.handler
)
1763 return 0; /* nothing we can do here */
1766 * From here on we do not issue `return' statements until the
1767 * very end of the dialog box: any event handler is entitled to
1768 * ask for a colour selector, so we _must_ always allow control
1769 * to reach the end of this switch statement so that the
1770 * subsequent code can test dp->coloursel_wanted().
1773 dp
->coloursel_wanted
= FALSE
;
1776 * Now switch on the control type and the message.
1778 switch (ctrl
->generic
.type
) {
1780 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1781 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1782 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1783 if (msg
== WM_COMMAND
&& ctrl
->editbox
.has_list
&&
1784 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1785 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1787 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1788 HIWORD(wParam
) == EN_CHANGE
)
1789 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1790 if (msg
== WM_COMMAND
&&
1791 ctrl
->editbox
.has_list
) {
1792 if (HIWORD(wParam
) == CBN_SELCHANGE
) {
1796 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1797 CB_GETCURSEL
, 0, 0);
1798 len
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1799 CB_GETLBTEXTLEN
, index
, 0);
1800 text
= smalloc(len
+1);
1801 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, CB_GETLBTEXT
,
1802 index
, (LPARAM
)text
);
1803 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
1805 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1806 } else if (HIWORD(wParam
) == CBN_EDITCHANGE
) {
1807 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1808 } else if (HIWORD(wParam
) == CBN_KILLFOCUS
) {
1809 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
1815 if (msg
== WM_COMMAND
&&
1816 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1817 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1819 * We sometimes get spurious BN_CLICKED messages for the
1820 * radio button that is just about to _lose_ selection, if
1821 * we're switching using the arrow keys. Therefore we
1822 * double-check that the button in wParam is actually
1823 * checked before generating an event.
1825 if (msg
== WM_COMMAND
&&
1826 HIWORD(wParam
) == BN_CLICKED
||
1827 HIWORD(wParam
) == BN_DOUBLECLICKED
&&
1828 IsDlgButtonChecked(dp
->hwnd
, LOWORD(wParam
))) {
1829 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1833 if (msg
== WM_COMMAND
&&
1834 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1835 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1836 if (msg
== WM_COMMAND
&&
1837 (HIWORD(wParam
) == BN_CLICKED
||
1838 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1839 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1843 if (msg
== WM_COMMAND
&&
1844 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1845 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1846 if (msg
== WM_COMMAND
&&
1847 (HIWORD(wParam
) == BN_CLICKED
||
1848 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1849 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1853 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
!= 0 &&
1854 (HIWORD(wParam
)==LBN_SETFOCUS
|| HIWORD(wParam
)==LBN_KILLFOCUS
))
1855 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == LBN_SETFOCUS
);
1856 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
== 0 &&
1857 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1858 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1859 if (msg
== WM_COMMAND
&& id
>= 2 &&
1860 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1861 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1862 if (ctrl
->listbox
.draglist
) {
1864 pret
= handle_prefslist(c
->data
, NULL
, 0, (msg
!= WM_COMMAND
),
1865 dp
->hwnd
, wParam
, lParam
);
1867 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1870 if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_DBLCLK
) {
1871 SetCapture(dp
->hwnd
);
1872 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1873 } else if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_SELCHANGE
) {
1874 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1878 case CTRL_FILESELECT
:
1879 if (msg
== WM_COMMAND
&& id
== 1 &&
1880 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1881 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1882 if (msg
== WM_COMMAND
&& id
== 2 &&
1883 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1884 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1886 (msg
== WM_COMMAND
&&
1887 (HIWORD(wParam
) == BN_CLICKED
||
1888 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1890 char filename
[FILENAME_MAX
];
1893 memset(&of
, 0, sizeof(of
));
1894 #ifdef OPENFILENAME_SIZE_VERSION_400
1895 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1897 of
.lStructSize
= sizeof(of
);
1899 of
.hwndOwner
= dp
->hwnd
;
1900 if (ctrl
->fileselect
.filter
)
1901 of
.lpstrFilter
= ctrl
->fileselect
.filter
;
1903 of
.lpstrFilter
= "All Files (*.*)\0*\0\0\0";
1904 of
.lpstrCustomFilter
= NULL
;
1905 of
.nFilterIndex
= 1;
1906 of
.lpstrFile
= filename
;
1907 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, filename
, lenof(filename
));
1908 filename
[lenof(filename
)-1] = '\0';
1909 of
.nMaxFile
= lenof(filename
);
1910 of
.lpstrFileTitle
= NULL
;
1911 of
.lpstrInitialDir
= NULL
;
1912 of
.lpstrTitle
= ctrl
->fileselect
.title
;
1914 if (ctrl
->fileselect
.for_writing
)
1915 ret
= GetSaveFileName(&of
);
1917 ret
= GetOpenFileName(&of
);
1919 SetDlgItemText(dp
->hwnd
, c
->base_id
+ 1, filename
);
1920 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1924 case CTRL_FONTSELECT
:
1925 if (msg
== WM_COMMAND
&& id
== 2 &&
1926 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1927 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1929 (msg
== WM_COMMAND
&&
1930 (HIWORD(wParam
) == BN_CLICKED
||
1931 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1935 FontSpec fs
= *(FontSpec
*)c
->data
;
1938 lf
.lfHeight
= -MulDiv(fs
.height
,
1939 GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1941 lf
.lfWidth
= lf
.lfEscapement
= lf
.lfOrientation
= 0;
1942 lf
.lfItalic
= lf
.lfUnderline
= lf
.lfStrikeOut
= 0;
1943 lf
.lfWeight
= (fs
.isbold ? FW_BOLD
: 0);
1944 lf
.lfCharSet
= fs
.charset
;
1945 lf
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1946 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1947 lf
.lfQuality
= DEFAULT_QUALITY
;
1948 lf
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1949 strncpy(lf
.lfFaceName
, fs
.name
,
1950 sizeof(lf
.lfFaceName
) - 1);
1951 lf
.lfFaceName
[sizeof(lf
.lfFaceName
) - 1] = '\0';
1953 cf
.lStructSize
= sizeof(cf
);
1954 cf
.hwndOwner
= dp
->hwnd
;
1956 cf
.Flags
= CF_FIXEDPITCHONLY
| CF_FORCEFONTEXIST
|
1957 CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
;
1959 if (ChooseFont(&cf
)) {
1960 strncpy(fs
.name
, lf
.lfFaceName
,
1961 sizeof(fs
.name
) - 1);
1962 fs
.name
[sizeof(fs
.name
) - 1] = '\0';
1963 fs
.isbold
= (lf
.lfWeight
== FW_BOLD
);
1964 fs
.charset
= lf
.lfCharSet
;
1965 fs
.height
= cf
.iPointSize
/ 10;
1966 dlg_fontsel_set(ctrl
, dp
, fs
);
1967 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1974 * If the above event handler has asked for a colour selector,
1975 * now is the time to generate one.
1977 if (dp
->coloursel_wanted
) {
1978 static CHOOSECOLOR cc
;
1979 static DWORD custom
[16] = { 0 }; /* zero initialisers */
1980 cc
.lStructSize
= sizeof(cc
);
1981 cc
.hwndOwner
= dp
->hwnd
;
1982 cc
.hInstance
= (HWND
) hinst
;
1983 cc
.lpCustColors
= custom
;
1984 cc
.rgbResult
= RGB(dp
->coloursel_result
.r
,
1985 dp
->coloursel_result
.g
,
1986 dp
->coloursel_result
.b
);
1987 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
;
1988 if (ChooseColor(&cc
)) {
1989 dp
->coloursel_result
.r
=
1990 (unsigned char) (cc
.rgbResult
& 0xFF);
1991 dp
->coloursel_result
.g
=
1992 (unsigned char) (cc
.rgbResult
>> 8) & 0xFF;
1993 dp
->coloursel_result
.b
=
1994 (unsigned char) (cc
.rgbResult
>> 16) & 0xFF;
1995 dp
->coloursel_result
.ok
= TRUE
;
1997 dp
->coloursel_result
.ok
= FALSE
;
1998 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
2005 * This function can be called to produce context help on a
2006 * control. Returns TRUE if it has actually launched WinHelp.
2008 int winctrl_context_help(struct dlgparam
*dp
, HWND hwnd
, int id
)
2015 * Look up the control ID in our data.
2017 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2018 c
= winctrl_findbyid(dp
->controltrees
[i
], id
);
2023 return 0; /* we have nothing to do */
2026 * This is the Windows front end, so we're allowed to assume
2027 * `helpctx.p' is a context string.
2029 if (!c
->ctrl
|| !c
->ctrl
->generic
.helpctx
.p
)
2030 return 0; /* no help available for this ctrl */
2032 cmd
= dupprintf("JI(`',`%s')", c
->ctrl
->generic
.helpctx
.p
);
2033 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
2039 * Now the various functions that the platform-independent
2040 * mechanism can call to access the dialog box entries.
2043 static struct winctrl
*dlg_findbyctrl(struct dlgparam
*dp
, union control
*ctrl
)
2047 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2048 struct winctrl
*c
= winctrl_findbyctrl(dp
->controltrees
[i
], ctrl
);
2055 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int whichbutton
)
2057 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2058 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2059 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2060 CheckRadioButton(dp
->hwnd
,
2062 c
->base_id
+ c
->ctrl
->radio
.nbuttons
,
2063 c
->base_id
+ 1 + whichbutton
);
2066 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
2068 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2069 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2071 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2072 for (i
= 0; i
< c
->ctrl
->radio
.nbuttons
; i
++)
2073 if (IsDlgButtonChecked(dp
->hwnd
, c
->base_id
+ 1 + i
))
2075 assert(!"No radio button was checked?!");
2079 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
2081 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2082 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2083 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2084 CheckDlgButton(dp
->hwnd
, c
->base_id
, (checked
!= 0));
2087 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
2089 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2090 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2091 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2092 return 0 != IsDlgButtonChecked(dp
->hwnd
, c
->base_id
);
2095 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
2097 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2098 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2099 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2100 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
2103 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
2105 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2106 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2107 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2108 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, buffer
, length
);
2109 buffer
[length
-1] = '\0';
2112 /* The `listbox' functions can also apply to combo boxes. */
2113 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
2115 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2116 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2119 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2120 c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2121 c
->ctrl
->editbox
.has_list
));
2122 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2123 LB_RESETCONTENT
: CB_RESETCONTENT
);
2124 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2127 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
2129 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2130 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2133 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2134 c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2135 c
->ctrl
->editbox
.has_list
));
2136 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2137 LB_DELETESTRING
: CB_DELETESTRING
);
2138 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2141 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
2143 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2144 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2147 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2148 c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2149 c
->ctrl
->editbox
.has_list
));
2150 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2151 LB_ADDSTRING
: CB_ADDSTRING
);
2152 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2156 * Each listbox entry may have a numeric id associated with it.
2157 * Note that some front ends only permit a string to be stored at
2158 * each position, which means that _if_ you put two identical
2159 * strings in any listbox then you MUST not assign them different
2160 * IDs and expect to get meaningful results back.
2162 void dlg_listbox_addwithindex(union control
*ctrl
, void *dlg
,
2163 char const *text
, int id
)
2165 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2166 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2167 int msg
, msg2
, index
;
2169 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2170 c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2171 c
->ctrl
->editbox
.has_list
));
2172 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2173 LB_ADDSTRING
: CB_ADDSTRING
);
2174 msg2
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2175 LB_SETITEMDATA
: CB_SETITEMDATA
);
2176 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2177 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg2
, index
, (LPARAM
)id
);
2180 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
2182 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2183 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2185 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
);
2186 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETITEMDATA
: CB_GETITEMDATA
);
2188 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2191 /* dlg_listbox_index returns <0 if no single element is selected. */
2192 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
2194 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2195 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2197 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2198 !c
->ctrl
->listbox
.multisel
);
2199 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETCURSEL
: CB_GETCURSEL
);
2200 ret
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2207 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
2209 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2210 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2211 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2212 c
->ctrl
->listbox
.multisel
&&
2213 c
->ctrl
->listbox
.height
!= 0);
2215 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, LB_GETSEL
, index
, 0);
2218 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
2220 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2221 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2223 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2224 !c
->ctrl
->listbox
.multisel
);
2225 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_SETCURSEL
: CB_SETCURSEL
);
2226 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2229 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
2231 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2232 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2233 assert(c
&& c
->ctrl
->generic
.type
== CTRL_TEXT
);
2234 SetDlgItemText(dp
->hwnd
, c
->base_id
, text
);
2237 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
2239 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2240 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2241 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2242 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, fn
.path
);
2245 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
2247 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2248 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2249 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2250 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, fn
->path
, lenof(fn
->path
));
2251 fn
->path
[lenof(fn
->path
)-1] = '\0';
2254 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
2256 char *buf
, *boldstr
;
2257 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2258 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2259 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2261 *(FontSpec
*)c
->data
= fs
; /* structure copy */
2263 boldstr
= (fs
.isbold ?
"bold, " : "");
2265 buf
= dupprintf("Font: %s, %sdefault height", fs
.name
, boldstr
);
2267 buf
= dupprintf("Font: %s, %s%d-point", fs
.name
, boldstr
,
2268 (fs
.height
< 0 ?
-fs
.height
: fs
.height
));
2269 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, buf
);
2273 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
2275 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2276 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2277 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2278 *fs
= *(FontSpec
*)c
->data
; /* structure copy */
2282 * Bracketing a large set of updates in these two functions will
2283 * cause the front end (if possible) to delay updating the screen
2284 * until it's all complete, thus avoiding flicker.
2286 void dlg_update_start(union control
*ctrl
, void *dlg
)
2288 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2289 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2290 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2291 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, WM_SETREDRAW
, FALSE
, 0);
2295 void dlg_update_done(union control
*ctrl
, void *dlg
)
2297 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2298 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2299 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2300 HWND hw
= GetDlgItem(dp
->hwnd
, c
->base_id
+1);
2301 SendMessage(hw
, WM_SETREDRAW
, TRUE
, 0);
2302 InvalidateRect(hw
, NULL
, TRUE
);
2306 void dlg_set_focus(union control
*ctrl
, void *dlg
)
2308 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2309 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2312 switch (ctrl
->generic
.type
) {
2313 case CTRL_EDITBOX
: id
= c
->base_id
+ 1; break;
2315 for (id
= c
->base_id
+ ctrl
->radio
.nbuttons
; id
> 1; id
--)
2316 if (IsDlgButtonChecked(dp
->hwnd
, id
))
2319 * In the theoretically-unlikely case that no button was
2320 * selected, id should come out of this as 1, which is a
2321 * reasonable enough choice.
2324 case CTRL_CHECKBOX
: id
= c
->base_id
; break;
2325 case CTRL_BUTTON
: id
= c
->base_id
; break;
2326 case CTRL_LISTBOX
: id
= c
->base_id
+ 1; break;
2327 case CTRL_FILESELECT
: id
= c
->base_id
+ 1; break;
2328 case CTRL_FONTSELECT
: id
= c
->base_id
+ 2; break;
2329 default: id
= c
->base_id
; break;
2331 ctl
= GetDlgItem(dp
->hwnd
, id
);
2336 * During event processing, you might well want to give an error
2337 * indication to the user. dlg_beep() is a quick and easy generic
2338 * error; dlg_error() puts up a message-box or equivalent.
2340 void dlg_beep(void *dlg
)
2342 /* struct dlgparam *dp = (struct dlgparam *)dlg; */
2346 void dlg_error_msg(void *dlg
, char *msg
)
2348 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2349 MessageBox(dp
->hwnd
, msg
,
2350 dp
->errtitle ? dp
->errtitle
: NULL
,
2351 MB_OK
| MB_ICONERROR
);
2355 * This function signals to the front end that the dialog's
2356 * processing is completed, and passes an integer value (typically
2357 * a success status).
2359 void dlg_end(void *dlg
, int value
)
2361 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2363 dp
->endresult
= value
;
2366 void dlg_refresh(union control
*ctrl
, void *dlg
)
2368 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2374 * Send EVENT_REFRESH to absolutely everything.
2376 for (j
= 0; j
< dp
->nctrltrees
; j
++) {
2378 (c
= winctrl_findbyindex(dp
->controltrees
[j
], i
)) != NULL
;
2380 if (c
->ctrl
&& c
->ctrl
->generic
.handler
!= NULL
)
2381 c
->ctrl
->generic
.handler(c
->ctrl
, dp
,
2382 dp
->data
, EVENT_REFRESH
);
2387 * Send EVENT_REFRESH to a specific control.
2389 if (ctrl
->generic
.handler
!= NULL
)
2390 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
2394 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
2396 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2397 dp
->coloursel_wanted
= TRUE
;
2398 dp
->coloursel_result
.r
= r
;
2399 dp
->coloursel_result
.g
= g
;
2400 dp
->coloursel_result
.b
= b
;
2403 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
2404 int *r
, int *g
, int *b
)
2406 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2407 if (dp
->coloursel_result
.ok
) {
2408 *r
= dp
->coloursel_result
.r
;
2409 *g
= dp
->coloursel_result
.g
;
2410 *b
= dp
->coloursel_result
.b
;