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'
33 #define STATICHEIGHT 8
34 #define TITLEHEIGHT 12
35 #define CHECKBOXHEIGHT 8
39 #define LISTINCREMENT 8
40 #define COMBOHEIGHT 12
41 #define PUSHBTNHEIGHT 14
42 #define PROGBARHEIGHT 14
44 void ctlposinit(struct ctlpos
*cp
, HWND hwnd
,
45 int leftborder
, int rightborder
, int topborder
)
49 cp
->font
= SendMessage(hwnd
, WM_GETFONT
, 0, 0);
51 GetClientRect(hwnd
, &r
);
55 MapDialogRect(hwnd
, &r2
);
56 cp
->dlu4inpix
= r2
.right
;
57 cp
->width
= (r
.right
* 4) / (r2
.right
) - 2 * GAPBETWEEN
;
58 cp
->xoff
= leftborder
;
59 cp
->width
-= leftborder
+ rightborder
;
62 HWND
doctl(struct ctlpos
*cp
, RECT r
,
63 char *wclass
, int wstyle
, int exstyle
, char *wtext
, int wid
)
67 * Note nonstandard use of RECT. This is deliberate: by
68 * transforming the width and height directly we arrange to
69 * have all supposedly same-sized controls really same-sized.
73 MapDialogRect(cp
->hwnd
, &r
);
76 * We can pass in cp->hwnd == NULL, to indicate a dry run
77 * without creating any actual controls.
80 ctl
= CreateWindowEx(exstyle
, wclass
, wtext
, wstyle
,
81 r
.left
, r
.top
, r
.right
, r
.bottom
,
82 cp
->hwnd
, (HMENU
) wid
, hinst
, NULL
);
83 SendMessage(ctl
, WM_SETFONT
, cp
->font
, MAKELPARAM(TRUE
, 0));
85 if (!strcmp(wclass
, "LISTBOX")) {
87 * Bizarre Windows bug: the list box calculates its
88 * number of lines based on the font it has at creation
89 * time, but sending it WM_SETFONT doesn't cause it to
90 * recalculate. So now, _after_ we've sent it
91 * WM_SETFONT, we explicitly resize it (to the same
92 * size it was already!) to force it to reconsider.
94 SetWindowPos(ctl
, NULL
, 0, 0, r
.right
, r
.bottom
,
95 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
96 SWP_NOMOVE
| SWP_NOZORDER
);
104 * A title bar across the top of a sub-dialog.
106 void bartitle(struct ctlpos
*cp
, char *name
, int id
)
113 r
.bottom
= STATICHEIGHT
;
114 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
115 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, name
, id
);
119 * Begin a grouping box, with or without a group title.
121 void beginbox(struct ctlpos
*cp
, char *name
, int idbox
)
123 cp
->boxystart
= cp
->ypos
;
125 cp
->boxystart
-= STATICHEIGHT
/ 2;
127 cp
->ypos
+= STATICHEIGHT
;
129 cp
->width
-= 2 * GAPXBOX
;
136 * End a grouping box.
138 void endbox(struct ctlpos
*cp
)
142 cp
->width
+= 2 * GAPXBOX
;
143 cp
->ypos
+= GAPYBOX
- GAPBETWEEN
;
146 r
.top
= cp
->boxystart
;
147 r
.bottom
= cp
->ypos
- cp
->boxystart
;
148 doctl(cp
, r
, "BUTTON", BS_GROUPBOX
| WS_CHILD
| WS_VISIBLE
, 0,
149 cp
->boxtext ? cp
->boxtext
: "", cp
->boxid
);
154 * Some edit boxes. Each one has a static above it. The percentages
155 * of the horizontal space are provided.
157 void multiedit(struct ctlpos
*cp
, int password
, ...)
164 va_start(ap
, password
);
167 int staticid
, editid
, pcwidth
;
168 text
= va_arg(ap
, char *);
171 staticid
= va_arg(ap
, int);
172 editid
= va_arg(ap
, int);
173 pcwidth
= va_arg(ap
, int);
175 r
.left
= xpos
+ GAPBETWEEN
;
177 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
178 r
.right
= xpos
- r
.left
;
181 r
.bottom
= STATICHEIGHT
;
182 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
183 r
.top
= cp
->ypos
+ 8 + GAPWITHIN
;
184 r
.bottom
= EDITHEIGHT
;
186 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
|
187 (password ? ES_PASSWORD
: 0),
188 WS_EX_CLIENTEDGE
, "", editid
);
191 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ EDITHEIGHT
+ GAPBETWEEN
;
195 * A static line, followed by a full-width combo box.
197 void combobox(struct ctlpos
*cp
, char *text
, int staticid
, int listid
)
205 r
.bottom
= STATICHEIGHT
;
206 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
207 r
.top
= cp
->ypos
+ 8 + GAPWITHIN
;
208 r
.bottom
= COMBOHEIGHT
* 10;
209 doctl(cp
, r
, "COMBOBOX",
210 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
211 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", listid
);
213 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
+ COMBOHEIGHT
+ GAPBETWEEN
;
216 struct radio
{ char *text
; int id
; };
218 static void radioline_common(struct ctlpos
*cp
, char *text
, int id
,
219 int nacross
, struct radio
*buttons
, int nbuttons
)
230 r
.bottom
= STATICHEIGHT
;
231 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
232 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
237 for (j
= 0; j
< nbuttons
; j
++) {
238 char *btext
= buttons
[j
].text
;
239 int bid
= buttons
[j
].id
;
242 cp
->ypos
+= r
.bottom
+ (nacross
> 1 ? GAPBETWEEN
: GAPWITHIN
);
245 r
.left
= GAPBETWEEN
+ i
* (cp
->width
+ GAPBETWEEN
) / nacross
;
248 (i
+ 1) * (cp
->width
+ GAPBETWEEN
) / nacross
- r
.left
;
250 r
.right
= cp
->width
- r
.left
;
252 r
.bottom
= RADIOHEIGHT
;
253 doctl(cp
, r
, "BUTTON",
254 BS_NOTIFY
| BS_AUTORADIOBUTTON
| WS_CHILD
|
255 WS_VISIBLE
| WS_TABSTOP
| group
, 0, btext
, bid
);
259 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
263 * A set of radio buttons on the same line, with a static above
264 * them. `nacross' dictates how many parts the line is divided into
265 * (you might want this not to equal the number of buttons if you
266 * needed to line up some 2s and some 3s to look good in the same
269 * There's a bit of a hack in here to ensure that if nacross
270 * exceeds the actual number of buttons, the rightmost button
271 * really does get all the space right to the edge of the line, so
272 * you can do things like
274 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
276 void radioline(struct ctlpos
*cp
, char *text
, int id
, int nacross
, ...)
279 struct radio
*buttons
;
282 va_start(ap
, nacross
);
285 char *btext
= va_arg(ap
, char *);
289 bid
= va_arg(ap
, int);
292 buttons
= snewn(nbuttons
, struct radio
);
293 va_start(ap
, nacross
);
294 for (i
= 0; i
< nbuttons
; i
++) {
295 buttons
[i
].text
= va_arg(ap
, char *);
296 buttons
[i
].id
= va_arg(ap
, int);
299 radioline_common(cp
, text
, id
, nacross
, buttons
, nbuttons
);
304 * A set of radio buttons on the same line, without a static above
305 * them. Otherwise just like radioline.
307 void bareradioline(struct ctlpos
*cp
, int nacross
, ...)
310 struct radio
*buttons
;
313 va_start(ap
, nacross
);
316 char *btext
= va_arg(ap
, char *);
320 bid
= va_arg(ap
, int);
323 buttons
= snewn(nbuttons
, struct radio
);
324 va_start(ap
, nacross
);
325 for (i
= 0; i
< nbuttons
; i
++) {
326 buttons
[i
].text
= va_arg(ap
, char *);
327 buttons
[i
].id
= va_arg(ap
, int);
330 radioline_common(cp
, NULL
, 0, nacross
, buttons
, nbuttons
);
335 * A set of radio buttons on multiple lines, with a static above
338 void radiobig(struct ctlpos
*cp
, char *text
, int id
, ...)
341 struct radio
*buttons
;
347 char *btext
= va_arg(ap
, char *);
351 bid
= va_arg(ap
, int);
354 buttons
= snewn(nbuttons
, struct radio
);
356 for (i
= 0; i
< nbuttons
; i
++) {
357 buttons
[i
].text
= va_arg(ap
, char *);
358 buttons
[i
].id
= va_arg(ap
, int);
361 radioline_common(cp
, text
, id
, 1, buttons
, nbuttons
);
366 * A single standalone checkbox.
368 void checkbox(struct ctlpos
*cp
, char *text
, int id
)
375 r
.bottom
= CHECKBOXHEIGHT
;
376 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
377 doctl(cp
, r
, "BUTTON",
378 BS_NOTIFY
| BS_AUTOCHECKBOX
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0,
383 * Wrap a piece of text for a static text control. Returns the
384 * wrapped text (a malloc'ed string containing \ns), and also
385 * returns the number of lines required.
387 char *staticwrap(struct ctlpos
*cp
, HWND hwnd
, char *text
, int *lines
)
389 HDC hdc
= GetDC(hwnd
);
390 int lpx
= GetDeviceCaps(hdc
, LOGPIXELSX
);
391 int width
, nlines
, j
;
396 HFONT oldfont
, newfont
;
398 ret
= snewn(1+strlen(text
), char);
401 pwidths
= snewn(1+strlen(text
), INT
);
404 * Work out the width the text will need to fit in, by doing
405 * the same adjustment that the `statictext' function itself
408 SetMapMode(hdc
, MM_TEXT
); /* ensure logical units == pixels */
409 r
.left
= r
.top
= r
.bottom
= 0;
411 MapDialogRect(hwnd
, &r
);
417 * We must select the correct font into the HDC before calling
418 * GetTextExtent*, or silly things will happen.
420 newfont
= (HFONT
)SendMessage(hwnd
, WM_GETFONT
, 0, 0);
421 oldfont
= SelectObject(hdc
, newfont
);
424 if (!GetTextExtentExPoint(hdc
, p
, strlen(p
), width
,
425 &nfit
, pwidths
, &size
) ||
426 (size_t)nfit
>= strlen(p
)) {
428 * Either GetTextExtentExPoint returned failure, or the
429 * whole of the rest of the text fits on this line.
430 * Either way, we stop wrapping, copy the remainder of
431 * the input string unchanged to the output, and leave.
438 * Now we search backwards along the string from `nfit',
439 * looking for a space at which to break the line. If we
440 * don't find one at all, that's fine - we'll just break
441 * the line at `nfit'.
443 for (j
= nfit
; j
> 0; j
--) {
444 if (isspace((unsigned char)p
[j
])) {
455 while (*p
&& isspace((unsigned char)*p
))
461 SelectObject(hdc
, oldfont
);
462 ReleaseDC(cp
->hwnd
, hdc
);
464 if (lines
) *lines
= nlines
;
470 * A single standalone static text control.
472 void statictext(struct ctlpos
*cp
, char *text
, int lines
, int id
)
479 r
.bottom
= STATICHEIGHT
* lines
;
480 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
481 doctl(cp
, r
, "STATIC",
482 WS_CHILD
| WS_VISIBLE
| SS_LEFTNOWORDWRAP
,
487 * An owner-drawn static text control for a panel title.
489 void paneltitle(struct ctlpos
*cp
, int id
)
496 r
.bottom
= TITLEHEIGHT
;
497 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
498 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_OWNERDRAW
,
503 * A button on the right hand side, with a static to its left.
505 void staticbtn(struct ctlpos
*cp
, char *stext
, int sid
,
506 char *btext
, int bid
)
508 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
509 PUSHBTNHEIGHT
: STATICHEIGHT
);
511 int lwid
, rwid
, rpos
;
513 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
514 lwid
= rpos
- 2 * GAPBETWEEN
;
515 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
518 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
520 r
.bottom
= STATICHEIGHT
;
521 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
524 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
526 r
.bottom
= PUSHBTNHEIGHT
;
527 doctl(cp
, r
, "BUTTON",
528 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
531 cp
->ypos
+= height
+ GAPBETWEEN
;
535 * A simple push button.
537 void button(struct ctlpos
*cp
, char *btext
, int bid
, int defbtn
)
544 r
.bottom
= PUSHBTNHEIGHT
;
546 /* Q67655: the _dialog box_ must know which button is default
547 * as well as the button itself knowing */
548 if (defbtn
&& cp
->hwnd
)
549 SendMessage(cp
->hwnd
, DM_SETDEFID
, bid
, 0);
551 doctl(cp
, r
, "BUTTON",
552 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
553 (defbtn ? BS_DEFPUSHBUTTON
: 0) | BS_PUSHBUTTON
,
556 cp
->ypos
+= PUSHBTNHEIGHT
+ GAPBETWEEN
;
560 * Like staticbtn, but two buttons.
562 void static2btn(struct ctlpos
*cp
, char *stext
, int sid
,
563 char *btext1
, int bid1
, char *btext2
, int bid2
)
565 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
566 PUSHBTNHEIGHT
: STATICHEIGHT
);
568 int lwid
, rwid1
, rwid2
, rpos1
, rpos2
;
570 rpos1
= GAPBETWEEN
+ (cp
->width
+ GAPBETWEEN
) / 2;
571 rpos2
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
572 lwid
= rpos1
- 2 * GAPBETWEEN
;
573 rwid1
= rpos2
- rpos1
- GAPBETWEEN
;
574 rwid2
= cp
->width
+ GAPBETWEEN
- rpos2
;
577 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
579 r
.bottom
= STATICHEIGHT
;
580 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
583 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
585 r
.bottom
= PUSHBTNHEIGHT
;
586 doctl(cp
, r
, "BUTTON",
587 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
591 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
593 r
.bottom
= PUSHBTNHEIGHT
;
594 doctl(cp
, r
, "BUTTON",
595 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
598 cp
->ypos
+= height
+ GAPBETWEEN
;
602 * An edit control on the right hand side, with a static to its left.
604 static void staticedit_internal(struct ctlpos
*cp
, char *stext
,
605 int sid
, int eid
, int percentedit
,
608 const int height
= (EDITHEIGHT
> STATICHEIGHT ?
609 EDITHEIGHT
: STATICHEIGHT
);
611 int lwid
, rwid
, rpos
;
614 GAPBETWEEN
+ (100 - percentedit
) * (cp
->width
+ GAPBETWEEN
) / 100;
615 lwid
= rpos
- 2 * GAPBETWEEN
;
616 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
619 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
621 r
.bottom
= STATICHEIGHT
;
622 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
625 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
627 r
.bottom
= EDITHEIGHT
;
629 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
| style
,
630 WS_EX_CLIENTEDGE
, "", eid
);
632 cp
->ypos
+= height
+ GAPBETWEEN
;
635 void staticedit(struct ctlpos
*cp
, char *stext
,
636 int sid
, int eid
, int percentedit
)
638 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, 0);
641 void staticpassedit(struct ctlpos
*cp
, char *stext
,
642 int sid
, int eid
, int percentedit
)
644 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, ES_PASSWORD
);
648 * A drop-down list box on the right hand side, with a static to
651 void staticddl(struct ctlpos
*cp
, char *stext
,
652 int sid
, int lid
, int percentlist
)
654 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
655 COMBOHEIGHT
: STATICHEIGHT
);
657 int lwid
, rwid
, rpos
;
660 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
661 lwid
= rpos
- 2 * GAPBETWEEN
;
662 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
665 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
667 r
.bottom
= STATICHEIGHT
;
668 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
671 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
673 r
.bottom
= COMBOHEIGHT
*4;
674 doctl(cp
, r
, "COMBOBOX",
675 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
676 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
678 cp
->ypos
+= height
+ GAPBETWEEN
;
682 * A combo box on the right hand side, with a static to its left.
684 void staticcombo(struct ctlpos
*cp
, char *stext
,
685 int sid
, int lid
, int percentlist
)
687 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
688 COMBOHEIGHT
: STATICHEIGHT
);
690 int lwid
, rwid
, rpos
;
693 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
694 lwid
= rpos
- 2 * GAPBETWEEN
;
695 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
698 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
700 r
.bottom
= STATICHEIGHT
;
701 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
704 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
706 r
.bottom
= COMBOHEIGHT
*10;
707 doctl(cp
, r
, "COMBOBOX",
708 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
709 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
711 cp
->ypos
+= height
+ GAPBETWEEN
;
715 * A static, with a full-width drop-down list box below it.
717 void staticddlbig(struct ctlpos
*cp
, char *stext
,
725 r
.bottom
= STATICHEIGHT
;
726 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
727 cp
->ypos
+= STATICHEIGHT
;
732 r
.bottom
= COMBOHEIGHT
*4;
733 doctl(cp
, r
, "COMBOBOX",
734 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
735 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
736 cp
->ypos
+= COMBOHEIGHT
+ GAPBETWEEN
;
740 * A big multiline edit control with a static labelling it.
742 void bigeditctrl(struct ctlpos
*cp
, char *stext
,
743 int sid
, int eid
, int lines
)
750 r
.bottom
= STATICHEIGHT
;
751 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
752 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
757 r
.bottom
= EDITHEIGHT
+ (lines
- 1) * STATICHEIGHT
;
758 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
760 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| ES_MULTILINE
,
761 WS_EX_CLIENTEDGE
, "", eid
);
765 * A list box with a static labelling it.
767 void listbox(struct ctlpos
*cp
, char *stext
,
768 int sid
, int lid
, int lines
, int multi
)
776 r
.bottom
= STATICHEIGHT
;
777 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
778 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
784 r
.bottom
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
785 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
786 doctl(cp
, r
, "LISTBOX",
787 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
788 LBS_NOTIFY
| LBS_HASSTRINGS
| LBS_USETABSTOPS
|
789 (multi ? LBS_MULTIPLESEL
: 0),
790 WS_EX_CLIENTEDGE
, "", lid
);
794 * A tab-control substitute when a real tab control is unavailable.
796 void ersatztab(struct ctlpos
*cp
, char *stext
, int sid
, int lid
, int s2id
)
798 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
799 COMBOHEIGHT
: STATICHEIGHT
);
801 int bigwid
, lwid
, rwid
, rpos
;
802 static const int BIGGAP
= 15;
803 static const int MEDGAP
= 3;
805 bigwid
= cp
->width
+ 2 * GAPBETWEEN
- 2 * BIGGAP
;
807 rpos
= BIGGAP
+ (bigwid
+ BIGGAP
) / 2;
808 lwid
= rpos
- 2 * BIGGAP
;
809 rwid
= bigwid
+ BIGGAP
- rpos
;
812 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
814 r
.bottom
= STATICHEIGHT
;
815 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
818 r
.top
= cp
->ypos
+ (height
- COMBOHEIGHT
) / 2;
820 r
.bottom
= COMBOHEIGHT
* 10;
821 doctl(cp
, r
, "COMBOBOX",
822 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
823 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
825 cp
->ypos
+= height
+ MEDGAP
+ GAPBETWEEN
;
831 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_ETCHEDHORZ
,
836 * A static line, followed by an edit control on the left hand side
837 * and a button on the right.
839 void editbutton(struct ctlpos
*cp
, char *stext
, int sid
,
840 int eid
, char *btext
, int bid
)
842 const int height
= (EDITHEIGHT
> PUSHBTNHEIGHT ?
843 EDITHEIGHT
: PUSHBTNHEIGHT
);
845 int lwid
, rwid
, rpos
;
850 r
.bottom
= STATICHEIGHT
;
851 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
852 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
854 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
855 lwid
= rpos
- 2 * GAPBETWEEN
;
856 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
859 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
861 r
.bottom
= EDITHEIGHT
;
863 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
864 WS_EX_CLIENTEDGE
, "", eid
);
867 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
869 r
.bottom
= PUSHBTNHEIGHT
;
870 doctl(cp
, r
, "BUTTON",
871 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
874 cp
->ypos
+= height
+ GAPBETWEEN
;
878 * A special control for manipulating an ordered preference list
879 * (eg. for cipher selection).
880 * XXX: this is a rough hack and could be improved.
882 void prefslist(struct prefslist
*hdl
, struct ctlpos
*cp
, int lines
,
883 char *stext
, int sid
, int listid
, int upbid
, int dnbid
)
885 const static int percents
[] = { 5, 75, 20 };
887 int xpos
, percent
= 0, i
;
888 int listheight
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
889 const int BTNSHEIGHT
= 2*PUSHBTNHEIGHT
+ GAPBETWEEN
;
890 int totalheight
, buttonpos
;
892 /* Squirrel away IDs. */
893 hdl
->listid
= listid
;
897 /* The static label. */
902 r
.bottom
= STATICHEIGHT
;
903 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
904 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
907 if (listheight
> BTNSHEIGHT
) {
908 totalheight
= listheight
;
909 buttonpos
= (listheight
- BTNSHEIGHT
) / 2;
911 totalheight
= BTNSHEIGHT
;
915 for (i
=0; i
<3; i
++) {
917 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
918 left
= xpos
+ GAPBETWEEN
;
919 percent
+= percents
[i
];
920 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
925 /* The drag list box. */
926 r
.left
= left
; r
.right
= wid
;
927 r
.top
= cp
->ypos
; r
.bottom
= listheight
;
930 ctl
= doctl(cp
, r
, "LISTBOX",
931 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
932 WS_VSCROLL
| LBS_HASSTRINGS
| LBS_USETABSTOPS
,
940 /* The "Up" and "Down" buttons. */
941 /* XXX worry about accelerators if we have more than one
942 * prefslist on a panel */
943 r
.left
= left
; r
.right
= wid
;
944 r
.top
= cp
->ypos
+ buttonpos
; r
.bottom
= PUSHBTNHEIGHT
;
945 doctl(cp
, r
, "BUTTON",
946 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
947 WS_TABSTOP
| BS_PUSHBUTTON
,
950 r
.left
= left
; r
.right
= wid
;
951 r
.top
= cp
->ypos
+ buttonpos
+ PUSHBTNHEIGHT
+ GAPBETWEEN
;
952 r
.bottom
= PUSHBTNHEIGHT
;
953 doctl(cp
, r
, "BUTTON",
954 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
955 WS_TABSTOP
| BS_PUSHBUTTON
,
963 cp
->ypos
+= totalheight
+ GAPBETWEEN
;
968 * Helper function for prefslist: move item in list box.
970 static void pl_moveitem(HWND hwnd
, int listid
, int src
, int dst
)
974 /* Get the item's data. */
975 tlen
= SendDlgItemMessage (hwnd
, listid
, LB_GETTEXTLEN
, src
, 0);
976 txt
= snewn(tlen
+1, char);
977 SendDlgItemMessage (hwnd
, listid
, LB_GETTEXT
, src
, (LPARAM
) txt
);
978 val
= SendDlgItemMessage (hwnd
, listid
, LB_GETITEMDATA
, src
, 0);
979 /* Deselect old location. */
980 SendDlgItemMessage (hwnd
, listid
, LB_SETSEL
, FALSE
, src
);
981 /* Delete it at the old location. */
982 SendDlgItemMessage (hwnd
, listid
, LB_DELETESTRING
, src
, 0);
983 /* Insert it at new location. */
984 SendDlgItemMessage (hwnd
, listid
, LB_INSERTSTRING
, dst
,
986 SendDlgItemMessage (hwnd
, listid
, LB_SETITEMDATA
, dst
,
989 SendDlgItemMessage (hwnd
, listid
, LB_SETCURSEL
, dst
, 0);
993 int pl_itemfrompt(HWND hwnd
, POINT cursor
, BOOL scroll
)
996 POINT uppoint
, downpoint
;
997 int updist
, downdist
, upitem
, downitem
, i
;
1000 * Ghastly hackery to try to figure out not which
1001 * _item_, but which _gap between items_, the user
1002 * is pointing at. We do this by first working out
1003 * which list item is under the cursor, and then
1004 * working out how far the cursor would have to
1005 * move up or down before the answer was different.
1006 * Then we put the insertion point _above_ the
1007 * current item if the upper edge is closer than
1008 * the lower edge, or _below_ it if vice versa.
1010 ret
= LBItemFromPt(hwnd
, cursor
, scroll
);
1013 ret
= LBItemFromPt(hwnd
, cursor
, FALSE
);
1014 updist
= downdist
= 0;
1015 for (i
= 1; i
< 4096 && (!updist
|| !downdist
); i
++) {
1016 uppoint
= downpoint
= cursor
;
1019 upitem
= LBItemFromPt(hwnd
, uppoint
, FALSE
);
1020 downitem
= LBItemFromPt(hwnd
, downpoint
, FALSE
);
1021 if (!updist
&& upitem
!= ret
)
1023 if (!downdist
&& downitem
!= ret
)
1026 if (downdist
< updist
)
1032 * Handler for prefslist above.
1034 * Return value has bit 0 set if the dialog box procedure needs to
1035 * return TRUE from handling this message; it has bit 1 set if a
1036 * change may have been made in the contents of the list.
1038 int handle_prefslist(struct prefslist
*hdl
,
1039 int *array
, int maxmemb
,
1040 int is_dlmsg
, HWND hwnd
,
1041 WPARAM wParam
, LPARAM lParam
)
1048 if ((int)wParam
== hdl
->listid
) {
1049 DRAGLISTINFO
*dlm
= (DRAGLISTINFO
*)lParam
;
1050 int dest
= 0; /* initialise to placate gcc */
1051 switch (dlm
->uNotification
) {
1054 SendDlgItemMessage(hwnd
, hdl
->listid
,
1055 LB_ADDSTRING
, 0, (LPARAM
) "");
1057 hdl
->srcitem
= LBItemFromPt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1059 /* XXX hack Q183115 */
1060 SetWindowLong(hwnd
, DWL_MSGRESULT
, TRUE
);
1063 DrawInsert(hwnd
, dlm
->hWnd
, -1); /* Clear arrow */
1064 SendDlgItemMessage(hwnd
, hdl
->listid
,
1065 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1070 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1071 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1072 DrawInsert (hwnd
, dlm
->hWnd
, dest
);
1074 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_MOVECURSOR
);
1076 SetWindowLong(hwnd
, DWL_MSGRESULT
, DL_STOPCURSOR
);
1079 if (hdl
->dragging
) {
1080 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1081 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1082 DrawInsert (hwnd
, dlm
->hWnd
, -1);
1084 SendDlgItemMessage(hwnd
, hdl
->listid
,
1085 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1086 if (hdl
->dragging
) {
1089 /* Correct for "missing" item. */
1090 if (dest
> hdl
->srcitem
) dest
--;
1091 pl_moveitem(hwnd
, hdl
->listid
, hdl
->srcitem
, dest
);
1101 if (((LOWORD(wParam
) == hdl
->upbid
) ||
1102 (LOWORD(wParam
) == hdl
->dnbid
)) &&
1103 ((HIWORD(wParam
) == BN_CLICKED
) ||
1104 (HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1105 /* Move an item up or down the list. */
1106 /* Get the current selection, if any. */
1107 int selection
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCURSEL
, 0, 0);
1108 if (selection
== LB_ERR
) {
1112 /* Get the total number of items. */
1113 nitems
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCOUNT
, 0, 0);
1114 /* Should we do anything? */
1115 if (LOWORD(wParam
) == hdl
->upbid
&& (selection
> 0))
1116 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
- 1);
1117 else if (LOWORD(wParam
) == hdl
->dnbid
&& (selection
< nitems
- 1))
1118 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
+ 1);
1127 /* Update array to match the list box. */
1128 for (i
=0; i
< maxmemb
; i
++)
1129 array
[i
] = SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETITEMDATA
,
1137 * A progress bar (from Common Controls). We like our progress bars
1138 * to be smooth and unbroken, without those ugly divisions; some
1139 * older compilers may not support that, but that's life.
1141 void progressbar(struct ctlpos
*cp
, int id
)
1145 r
.left
= GAPBETWEEN
;
1147 r
.right
= cp
->width
;
1148 r
.bottom
= PROGBARHEIGHT
;
1149 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
1151 doctl(cp
, r
, PROGRESS_CLASS
, WS_CHILD
| WS_VISIBLE
1155 , WS_EX_CLIENTEDGE
, "", id
);
1158 /* ----------------------------------------------------------------------
1159 * Platform-specific side of portable dialog-box mechanism.
1163 * This function takes a string, escapes all the ampersands, and
1164 * places a single (unescaped) ampersand in front of the first
1165 * occurrence of the given shortcut character (which may be
1168 * Return value is a malloc'ed copy of the processed version of the
1171 static char *shortcut_escape(char *text
, char shortcut
)
1177 return NULL
; /* sfree won't choke on this */
1179 ret
= snewn(2*strlen(text
)+1, char); /* size potentially doubles! */
1180 shortcut
= tolower((unsigned char)shortcut
);
1185 if (shortcut
!= NO_SHORTCUT
&&
1186 tolower((unsigned char)*p
) == shortcut
) {
1188 shortcut
= NO_SHORTCUT
; /* stop it happening twice */
1189 } else if (*p
== '&') {
1198 void winctrl_add_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1201 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1202 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1203 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1204 assert(!dp
->shortcuts
[s
]);
1205 dp
->shortcuts
[s
] = TRUE
;
1209 void winctrl_rem_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1212 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1213 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1214 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1215 assert(dp
->shortcuts
[s
]);
1216 dp
->shortcuts
[s
] = FALSE
;
1220 static int winctrl_cmp_byctrl(void *av
, void *bv
)
1222 struct winctrl
*a
= (struct winctrl
*)av
;
1223 struct winctrl
*b
= (struct winctrl
*)bv
;
1224 if (a
->ctrl
< b
->ctrl
)
1226 else if (a
->ctrl
> b
->ctrl
)
1231 static int winctrl_cmp_byid(void *av
, void *bv
)
1233 struct winctrl
*a
= (struct winctrl
*)av
;
1234 struct winctrl
*b
= (struct winctrl
*)bv
;
1235 if (a
->base_id
< b
->base_id
)
1237 else if (a
->base_id
> b
->base_id
)
1242 static int winctrl_cmp_byctrl_find(void *av
, void *bv
)
1244 union control
*a
= (union control
*)av
;
1245 struct winctrl
*b
= (struct winctrl
*)bv
;
1248 else if (a
> b
->ctrl
)
1253 static int winctrl_cmp_byid_find(void *av
, void *bv
)
1256 struct winctrl
*b
= (struct winctrl
*)bv
;
1257 if (*a
< b
->base_id
)
1259 else if (*a
>= b
->base_id
+ b
->num_ids
)
1265 void winctrl_init(struct winctrls
*wc
)
1267 wc
->byctrl
= newtree234(winctrl_cmp_byctrl
);
1268 wc
->byid
= newtree234(winctrl_cmp_byid
);
1270 void winctrl_cleanup(struct winctrls
*wc
)
1274 while ((c
= index234(wc
->byid
, 0)) != NULL
) {
1275 winctrl_remove(wc
, c
);
1280 freetree234(wc
->byctrl
);
1281 freetree234(wc
->byid
);
1282 wc
->byctrl
= wc
->byid
= NULL
;
1285 void winctrl_add(struct winctrls
*wc
, struct winctrl
*c
)
1287 struct winctrl
*ret
;
1289 ret
= add234(wc
->byctrl
, c
);
1292 ret
= add234(wc
->byid
, c
);
1296 void winctrl_remove(struct winctrls
*wc
, struct winctrl
*c
)
1298 struct winctrl
*ret
;
1299 ret
= del234(wc
->byctrl
, c
);
1300 ret
= del234(wc
->byid
, c
);
1304 struct winctrl
*winctrl_findbyctrl(struct winctrls
*wc
, union control
*ctrl
)
1306 return find234(wc
->byctrl
, ctrl
, winctrl_cmp_byctrl_find
);
1309 struct winctrl
*winctrl_findbyid(struct winctrls
*wc
, int id
)
1311 return find234(wc
->byid
, &id
, winctrl_cmp_byid_find
);
1314 struct winctrl
*winctrl_findbyindex(struct winctrls
*wc
, int index
)
1316 return index234(wc
->byid
, index
);
1319 void winctrl_layout(struct dlgparam
*dp
, struct winctrls
*wc
,
1320 struct ctlpos
*cp
, struct controlset
*s
, int *id
)
1322 struct ctlpos columns
[16];
1323 int ncols
, colstart
, colspan
;
1325 struct ctlpos tabdelays
[16];
1326 union control
*tabdelayed
[16];
1331 char shortcuts
[MAX_SHORTCUTS_PER_CTRL
];
1334 int i
, actual_base_id
, base_id
, num_ids
;
1339 /* Start a containing box, if we have a boxname. */
1340 if (s
->boxname
&& *s
->boxname
) {
1341 struct winctrl
*c
= snew(struct winctrl
);
1343 c
->base_id
= base_id
;
1346 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1348 beginbox(cp
, s
->boxtitle
, base_id
);
1352 /* Draw a title, if we have one. */
1353 if (!s
->boxname
&& s
->boxtitle
) {
1354 struct winctrl
*c
= snew(struct winctrl
);
1356 c
->base_id
= base_id
;
1358 c
->data
= dupstr(s
->boxtitle
);
1359 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1361 paneltitle(cp
, base_id
);
1365 /* Initially we have just one column. */
1367 columns
[0] = *cp
; /* structure copy */
1369 /* And initially, there are no pending tab-delayed controls. */
1372 /* Loop over each control in the controlset. */
1373 for (i
= 0; i
< s
->ncontrols
; i
++) {
1374 union control
*ctrl
= s
->ctrls
[i
];
1377 * Generic processing that pertains to all control types.
1378 * At the end of this if statement, we'll have produced
1379 * `ctrl' (a pointer to the control we have to create, or
1380 * think about creating, in this iteration of the loop),
1381 * `pos' (a suitable ctlpos with which to position it), and
1382 * `c' (a winctrl structure to receive details of the
1383 * dialog IDs). Or we'll have done a `continue', if it was
1384 * CTRL_COLUMNS and doesn't require any control creation at
1387 if (ctrl
->generic
.type
== CTRL_COLUMNS
) {
1388 assert((ctrl
->columns
.ncols
== 1) ^ (ncols
== 1));
1392 * We're splitting into multiple columns.
1394 int lpercent
, rpercent
, lx
, rx
, i
;
1396 ncols
= ctrl
->columns
.ncols
;
1397 assert(ncols
<= lenof(columns
));
1398 for (i
= 1; i
< ncols
; i
++)
1399 columns
[i
] = columns
[0]; /* structure copy */
1402 for (i
= 0; i
< ncols
; i
++) {
1403 rpercent
= lpercent
+ ctrl
->columns
.percentages
[i
];
1404 lx
= columns
[i
].xoff
+ lpercent
*
1405 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1406 rx
= columns
[i
].xoff
+ rpercent
*
1407 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1408 columns
[i
].xoff
= lx
;
1409 columns
[i
].width
= rx
- lx
- GAPBETWEEN
;
1410 lpercent
= rpercent
;
1414 * We're recombining the various columns into one.
1416 int maxy
= columns
[0].ypos
;
1418 for (i
= 1; i
< ncols
; i
++)
1419 if (maxy
< columns
[i
].ypos
)
1420 maxy
= columns
[i
].ypos
;
1422 columns
[0] = *cp
; /* structure copy */
1423 columns
[0].ypos
= maxy
;
1427 } else if (ctrl
->generic
.type
== CTRL_TABDELAY
) {
1430 assert(!ctrl
->generic
.tabdelay
);
1431 ctrl
= ctrl
->tabdelay
.ctrl
;
1433 for (i
= 0; i
< ntabdelays
; i
++)
1434 if (tabdelayed
[i
] == ctrl
)
1436 assert(i
< ntabdelays
); /* we have to have found it */
1438 pos
= tabdelays
[i
]; /* structure copy */
1440 colstart
= colspan
= -1; /* indicate this was tab-delayed */
1444 * If it wasn't one of those, it's a genuine control;
1445 * so we'll have to compute a position for it now, by
1446 * checking its column span.
1450 colstart
= COLUMN_START(ctrl
->generic
.column
);
1451 colspan
= COLUMN_SPAN(ctrl
->generic
.column
);
1453 pos
= columns
[colstart
]; /* structure copy */
1454 pos
.width
= columns
[colstart
+colspan
-1].width
+
1455 (columns
[colstart
+colspan
-1].xoff
- columns
[colstart
].xoff
);
1457 for (col
= colstart
; col
< colstart
+colspan
; col
++)
1458 if (pos
.ypos
< columns
[col
].ypos
)
1459 pos
.ypos
= columns
[col
].ypos
;
1462 * If this control is to be tabdelayed, add it to the
1463 * tabdelay list, and unset pos.hwnd to inhibit actual
1466 if (ctrl
->generic
.tabdelay
) {
1467 assert(ntabdelays
< lenof(tabdelays
));
1468 tabdelays
[ntabdelays
] = pos
; /* structure copy */
1469 tabdelayed
[ntabdelays
] = ctrl
;
1475 /* Most controls don't need anything in c->data. */
1478 /* And they all start off with no shortcuts registered. */
1479 memset(shortcuts
, NO_SHORTCUT
, lenof(shortcuts
));
1482 /* Almost all controls start at base_id. */
1483 actual_base_id
= base_id
;
1486 * Now we're ready to actually create the control, by
1487 * switching on its type.
1489 switch (ctrl
->generic
.type
) {
1492 char *wrapped
, *escaped
;
1495 wrapped
= staticwrap(&pos
, cp
->hwnd
,
1496 ctrl
->generic
.label
, &lines
);
1497 escaped
= shortcut_escape(wrapped
, NO_SHORTCUT
);
1498 statictext(&pos
, escaped
, lines
, base_id
);
1504 num_ids
= 2; /* static, edit */
1505 escaped
= shortcut_escape(ctrl
->editbox
.label
,
1506 ctrl
->editbox
.shortcut
);
1507 shortcuts
[nshortcuts
++] = ctrl
->editbox
.shortcut
;
1508 if (ctrl
->editbox
.percentwidth
== 100) {
1509 if (ctrl
->editbox
.has_list
)
1510 combobox(&pos
, escaped
,
1511 base_id
, base_id
+1);
1513 multiedit(&pos
, ctrl
->editbox
.password
, escaped
,
1514 base_id
, base_id
+1, 100, NULL
);
1516 if (ctrl
->editbox
.has_list
) {
1517 staticcombo(&pos
, escaped
, base_id
, base_id
+1,
1518 ctrl
->editbox
.percentwidth
);
1520 (ctrl
->editbox
.password ? staticpassedit
: staticedit
)
1521 (&pos
, escaped
, base_id
, base_id
+1,
1522 ctrl
->editbox
.percentwidth
);
1528 num_ids
= ctrl
->radio
.nbuttons
+ 1; /* label as well */
1530 struct radio
*buttons
;
1533 escaped
= shortcut_escape(ctrl
->radio
.label
,
1534 ctrl
->radio
.shortcut
);
1535 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcut
;
1537 buttons
= snewn(ctrl
->radio
.nbuttons
, struct radio
);
1539 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1541 shortcut_escape(ctrl
->radio
.buttons
[i
],
1542 (char)(ctrl
->radio
.shortcuts ?
1543 ctrl
->radio
.shortcuts
[i
] :
1545 buttons
[i
].id
= base_id
+ 1 + i
;
1546 if (ctrl
->radio
.shortcuts
) {
1547 assert(nshortcuts
< MAX_SHORTCUTS_PER_CTRL
);
1548 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcuts
[i
];
1552 radioline_common(&pos
, escaped
, base_id
,
1553 ctrl
->radio
.ncolumns
,
1554 buttons
, ctrl
->radio
.nbuttons
);
1556 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1557 sfree(buttons
[i
].text
);
1565 escaped
= shortcut_escape(ctrl
->checkbox
.label
,
1566 ctrl
->checkbox
.shortcut
);
1567 shortcuts
[nshortcuts
++] = ctrl
->checkbox
.shortcut
;
1568 checkbox(&pos
, escaped
, base_id
);
1572 escaped
= shortcut_escape(ctrl
->button
.label
,
1573 ctrl
->button
.shortcut
);
1574 shortcuts
[nshortcuts
++] = ctrl
->button
.shortcut
;
1575 if (ctrl
->button
.iscancel
)
1576 actual_base_id
= IDCANCEL
;
1578 button(&pos
, escaped
, actual_base_id
, ctrl
->button
.isdefault
);
1583 escaped
= shortcut_escape(ctrl
->listbox
.label
,
1584 ctrl
->listbox
.shortcut
);
1585 shortcuts
[nshortcuts
++] = ctrl
->listbox
.shortcut
;
1586 if (ctrl
->listbox
.draglist
) {
1587 data
= snew(struct prefslist
);
1589 prefslist(data
, &pos
, ctrl
->listbox
.height
, escaped
,
1590 base_id
, base_id
+1, base_id
+2, base_id
+3);
1591 shortcuts
[nshortcuts
++] = 'u'; /* Up */
1592 shortcuts
[nshortcuts
++] = 'd'; /* Down */
1593 } else if (ctrl
->listbox
.height
== 0) {
1594 /* Drop-down list. */
1595 if (ctrl
->listbox
.percentwidth
== 100) {
1596 staticddlbig(&pos
, escaped
,
1597 base_id
, base_id
+1);
1599 staticddl(&pos
, escaped
, base_id
,
1600 base_id
+1, ctrl
->listbox
.percentwidth
);
1603 /* Ordinary list. */
1604 listbox(&pos
, escaped
, base_id
, base_id
+1,
1605 ctrl
->listbox
.height
, ctrl
->listbox
.multisel
);
1607 if (ctrl
->listbox
.ncols
) {
1609 * This method of getting the box width is a bit of
1610 * a hack; we'd do better to try to retrieve the
1611 * actual width in dialog units from doctl() just
1612 * before MapDialogRect. But that's going to be no
1613 * fun, and this should be good enough accuracy.
1615 int width
= cp
->width
* ctrl
->listbox
.percentwidth
;
1619 tabarray
= snewn(ctrl
->listbox
.ncols
-1, int);
1621 for (i
= 0; i
< ctrl
->listbox
.ncols
-1; i
++) {
1622 percent
+= ctrl
->listbox
.percentages
[i
];
1623 tabarray
[i
] = width
* percent
/ 10000;
1625 SendDlgItemMessage(cp
->hwnd
, base_id
+1, LB_SETTABSTOPS
,
1626 ctrl
->listbox
.ncols
-1, (LPARAM
)tabarray
);
1631 case CTRL_FILESELECT
:
1633 escaped
= shortcut_escape(ctrl
->fileselect
.label
,
1634 ctrl
->fileselect
.shortcut
);
1635 shortcuts
[nshortcuts
++] = ctrl
->fileselect
.shortcut
;
1636 editbutton(&pos
, escaped
, base_id
, base_id
+1,
1637 "Bro&wse...", base_id
+2);
1638 shortcuts
[nshortcuts
++] = 'w';
1641 case CTRL_FONTSELECT
:
1643 escaped
= shortcut_escape(ctrl
->fontselect
.label
,
1644 ctrl
->fontselect
.shortcut
);
1645 shortcuts
[nshortcuts
++] = ctrl
->fontselect
.shortcut
;
1646 statictext(&pos
, escaped
, 1, base_id
);
1647 staticbtn(&pos
, "", base_id
+1, "Change...", base_id
+2);
1649 data
= snew(FontSpec
);
1652 assert(!"Can't happen");
1653 num_ids
= 0; /* placate gcc */
1658 * Create a `struct winctrl' for this control, and advance
1659 * the dialog ID counter, if it's actually been created
1660 * (and isn't tabdelayed).
1663 struct winctrl
*c
= snew(struct winctrl
);
1666 c
->base_id
= actual_base_id
;
1667 c
->num_ids
= num_ids
;
1669 memcpy(c
->shortcuts
, shortcuts
, sizeof(shortcuts
));
1671 winctrl_add_shortcuts(dp
, c
);
1672 if (actual_base_id
== base_id
)
1676 if (colstart
>= 0) {
1678 * Update the ypos in all columns crossed by this
1682 for (i
= colstart
; i
< colstart
+colspan
; i
++)
1683 columns
[i
].ypos
= pos
.ypos
;
1688 * We've now finished laying out the controls; so now update
1689 * the ctlpos and control ID that were passed in, terminate
1690 * any containing box, and return.
1692 for (i
= 0; i
< ncols
; i
++)
1693 if (cp
->ypos
< columns
[i
].ypos
)
1694 cp
->ypos
= columns
[i
].ypos
;
1697 if (s
->boxname
&& *s
->boxname
)
1701 static void winctrl_set_focus(union control
*ctrl
, struct dlgparam
*dp
,
1706 dp
->lastfocused
= dp
->focused
;
1708 } else if (!has_focus
&& dp
->focused
== ctrl
) {
1709 dp
->lastfocused
= dp
->focused
;
1714 union control
*dlg_last_focused(union control
*ctrl
, void *dlg
)
1716 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
1717 return dp
->focused
== ctrl ? dp
->lastfocused
: dp
->focused
;
1721 * The dialog-box procedure calls this function to handle Windows
1722 * messages on a control we manage.
1724 int winctrl_handle_command(struct dlgparam
*dp
, UINT msg
,
1725 WPARAM wParam
, LPARAM lParam
)
1728 union control
*ctrl
;
1730 static UINT draglistmsg
= WM_NULL
;
1733 * Filter out pointless window messages. Our interest is in
1734 * WM_COMMAND and the drag list message, and nothing else.
1736 if (draglistmsg
== WM_NULL
)
1737 draglistmsg
= RegisterWindowMessage (DRAGLISTMSGSTRING
);
1739 if (msg
!= draglistmsg
&& msg
!= WM_COMMAND
&& msg
!= WM_DRAWITEM
)
1743 * Look up the control ID in our data.
1746 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
1747 c
= winctrl_findbyid(dp
->controltrees
[i
], LOWORD(wParam
));
1752 return 0; /* we have nothing to do */
1754 if (msg
== WM_DRAWITEM
) {
1756 * Owner-draw request for a panel title.
1758 LPDRAWITEMSTRUCT di
= (LPDRAWITEMSTRUCT
) lParam
;
1760 RECT r
= di
->rcItem
;
1763 SetMapMode(hdc
, MM_TEXT
); /* ensure logical units == pixels */
1765 GetTextExtentPoint32(hdc
, (char *)c
->data
,
1766 strlen((char *)c
->data
), &s
);
1767 DrawEdge(hdc
, &r
, EDGE_ETCHED
, BF_ADJUST
| BF_RECT
);
1769 r
.left
+ (r
.right
-r
.left
-s
.cx
)/2,
1770 r
.top
+ (r
.bottom
-r
.top
-s
.cy
)/2,
1771 (char *)c
->data
, strlen((char *)c
->data
));
1777 id
= LOWORD(wParam
) - c
->base_id
;
1779 if (!ctrl
|| !ctrl
->generic
.handler
)
1780 return 0; /* nothing we can do here */
1783 * From here on we do not issue `return' statements until the
1784 * very end of the dialog box: any event handler is entitled to
1785 * ask for a colour selector, so we _must_ always allow control
1786 * to reach the end of this switch statement so that the
1787 * subsequent code can test dp->coloursel_wanted().
1790 dp
->coloursel_wanted
= FALSE
;
1793 * Now switch on the control type and the message.
1795 switch (ctrl
->generic
.type
) {
1797 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1798 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1799 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1800 if (msg
== WM_COMMAND
&& ctrl
->editbox
.has_list
&&
1801 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1802 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1804 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1805 HIWORD(wParam
) == EN_CHANGE
)
1806 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1807 if (msg
== WM_COMMAND
&&
1808 ctrl
->editbox
.has_list
) {
1809 if (HIWORD(wParam
) == CBN_SELCHANGE
) {
1813 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1814 CB_GETCURSEL
, 0, 0);
1815 len
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1816 CB_GETLBTEXTLEN
, index
, 0);
1817 text
= snewn(len
+1, char);
1818 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, CB_GETLBTEXT
,
1819 index
, (LPARAM
)text
);
1820 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
1822 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1823 } else if (HIWORD(wParam
) == CBN_EDITCHANGE
) {
1824 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1825 } else if (HIWORD(wParam
) == CBN_KILLFOCUS
) {
1826 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
1832 if (msg
== WM_COMMAND
&&
1833 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1834 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1836 * We sometimes get spurious BN_CLICKED messages for the
1837 * radio button that is just about to _lose_ selection, if
1838 * we're switching using the arrow keys. Therefore we
1839 * double-check that the button in wParam is actually
1840 * checked before generating an event.
1842 if (msg
== WM_COMMAND
&&
1843 (HIWORD(wParam
) == BN_CLICKED
||
1844 HIWORD(wParam
) == BN_DOUBLECLICKED
) &&
1845 IsDlgButtonChecked(dp
->hwnd
, LOWORD(wParam
))) {
1846 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1850 if (msg
== WM_COMMAND
&&
1851 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1852 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1853 if (msg
== WM_COMMAND
&&
1854 (HIWORD(wParam
) == BN_CLICKED
||
1855 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1856 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1860 if (msg
== WM_COMMAND
&&
1861 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1862 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1863 if (msg
== WM_COMMAND
&&
1864 (HIWORD(wParam
) == BN_CLICKED
||
1865 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1866 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1870 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
!= 0 &&
1871 (HIWORD(wParam
)==LBN_SETFOCUS
|| HIWORD(wParam
)==LBN_KILLFOCUS
))
1872 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == LBN_SETFOCUS
);
1873 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
== 0 &&
1874 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1875 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1876 if (msg
== WM_COMMAND
&& id
>= 2 &&
1877 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1878 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1879 if (ctrl
->listbox
.draglist
) {
1881 pret
= handle_prefslist(c
->data
, NULL
, 0, (msg
!= WM_COMMAND
),
1882 dp
->hwnd
, wParam
, lParam
);
1884 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1887 if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_DBLCLK
) {
1888 SetCapture(dp
->hwnd
);
1889 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1890 } else if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_SELCHANGE
) {
1891 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1895 case CTRL_FILESELECT
:
1896 if (msg
== WM_COMMAND
&& id
== 1 &&
1897 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1898 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1899 if (msg
== WM_COMMAND
&& id
== 2 &&
1900 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1901 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1902 if (msg
== WM_COMMAND
&& id
== 1 && HIWORD(wParam
) == EN_CHANGE
)
1903 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1905 (msg
== WM_COMMAND
&&
1906 (HIWORD(wParam
) == BN_CLICKED
||
1907 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1909 char filename
[FILENAME_MAX
];
1912 memset(&of
, 0, sizeof(of
));
1913 #ifdef OPENFILENAME_SIZE_VERSION_400
1914 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1916 of
.lStructSize
= sizeof(of
);
1918 of
.hwndOwner
= dp
->hwnd
;
1919 if (ctrl
->fileselect
.filter
)
1920 of
.lpstrFilter
= ctrl
->fileselect
.filter
;
1922 of
.lpstrFilter
= "All Files (*.*)\0*\0\0\0";
1923 of
.lpstrCustomFilter
= NULL
;
1924 of
.nFilterIndex
= 1;
1925 of
.lpstrFile
= filename
;
1926 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, filename
, lenof(filename
));
1927 filename
[lenof(filename
)-1] = '\0';
1928 of
.nMaxFile
= lenof(filename
);
1929 of
.lpstrFileTitle
= NULL
;
1930 of
.lpstrInitialDir
= NULL
;
1931 of
.lpstrTitle
= ctrl
->fileselect
.title
;
1933 if (ctrl
->fileselect
.for_writing
)
1934 ret
= GetSaveFileName(&of
);
1936 ret
= GetOpenFileName(&of
);
1938 SetDlgItemText(dp
->hwnd
, c
->base_id
+ 1, filename
);
1939 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1943 case CTRL_FONTSELECT
:
1944 if (msg
== WM_COMMAND
&& id
== 2 &&
1945 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1946 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1948 (msg
== WM_COMMAND
&&
1949 (HIWORD(wParam
) == BN_CLICKED
||
1950 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1954 FontSpec fs
= *(FontSpec
*)c
->data
;
1957 lf
.lfHeight
= -MulDiv(fs
.height
,
1958 GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1960 lf
.lfWidth
= lf
.lfEscapement
= lf
.lfOrientation
= 0;
1961 lf
.lfItalic
= lf
.lfUnderline
= lf
.lfStrikeOut
= 0;
1962 lf
.lfWeight
= (fs
.isbold ? FW_BOLD
: 0);
1963 lf
.lfCharSet
= fs
.charset
;
1964 lf
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1965 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1966 lf
.lfQuality
= DEFAULT_QUALITY
;
1967 lf
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1968 strncpy(lf
.lfFaceName
, fs
.name
,
1969 sizeof(lf
.lfFaceName
) - 1);
1970 lf
.lfFaceName
[sizeof(lf
.lfFaceName
) - 1] = '\0';
1972 cf
.lStructSize
= sizeof(cf
);
1973 cf
.hwndOwner
= dp
->hwnd
;
1975 cf
.Flags
= CF_FIXEDPITCHONLY
| CF_FORCEFONTEXIST
|
1976 CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
;
1978 if (ChooseFont(&cf
)) {
1979 strncpy(fs
.name
, lf
.lfFaceName
,
1980 sizeof(fs
.name
) - 1);
1981 fs
.name
[sizeof(fs
.name
) - 1] = '\0';
1982 fs
.isbold
= (lf
.lfWeight
== FW_BOLD
);
1983 fs
.charset
= lf
.lfCharSet
;
1984 fs
.height
= cf
.iPointSize
/ 10;
1985 dlg_fontsel_set(ctrl
, dp
, fs
);
1986 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1993 * If the above event handler has asked for a colour selector,
1994 * now is the time to generate one.
1996 if (dp
->coloursel_wanted
) {
1997 static CHOOSECOLOR cc
;
1998 static DWORD custom
[16] = { 0 }; /* zero initialisers */
1999 cc
.lStructSize
= sizeof(cc
);
2000 cc
.hwndOwner
= dp
->hwnd
;
2001 cc
.hInstance
= (HWND
) hinst
;
2002 cc
.lpCustColors
= custom
;
2003 cc
.rgbResult
= RGB(dp
->coloursel_result
.r
,
2004 dp
->coloursel_result
.g
,
2005 dp
->coloursel_result
.b
);
2006 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
;
2007 if (ChooseColor(&cc
)) {
2008 dp
->coloursel_result
.r
=
2009 (unsigned char) (cc
.rgbResult
& 0xFF);
2010 dp
->coloursel_result
.g
=
2011 (unsigned char) (cc
.rgbResult
>> 8) & 0xFF;
2012 dp
->coloursel_result
.b
=
2013 (unsigned char) (cc
.rgbResult
>> 16) & 0xFF;
2014 dp
->coloursel_result
.ok
= TRUE
;
2016 dp
->coloursel_result
.ok
= FALSE
;
2017 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
2024 * This function can be called to produce context help on a
2025 * control. Returns TRUE if it has actually launched WinHelp.
2027 int winctrl_context_help(struct dlgparam
*dp
, HWND hwnd
, int id
)
2034 * Look up the control ID in our data.
2037 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2038 c
= winctrl_findbyid(dp
->controltrees
[i
], id
);
2043 return 0; /* we have nothing to do */
2046 * This is the Windows front end, so we're allowed to assume
2047 * `helpctx.p' is a context string.
2049 if (!c
->ctrl
|| !c
->ctrl
->generic
.helpctx
.p
)
2050 return 0; /* no help available for this ctrl */
2052 cmd
= dupprintf("JI(`',`%s')", c
->ctrl
->generic
.helpctx
.p
);
2053 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
2059 * Now the various functions that the platform-independent
2060 * mechanism can call to access the dialog box entries.
2063 static struct winctrl
*dlg_findbyctrl(struct dlgparam
*dp
, union control
*ctrl
)
2067 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2068 struct winctrl
*c
= winctrl_findbyctrl(dp
->controltrees
[i
], ctrl
);
2075 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int whichbutton
)
2077 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2078 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2079 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2080 CheckRadioButton(dp
->hwnd
,
2082 c
->base_id
+ c
->ctrl
->radio
.nbuttons
,
2083 c
->base_id
+ 1 + whichbutton
);
2086 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
2088 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2089 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2091 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2092 for (i
= 0; i
< c
->ctrl
->radio
.nbuttons
; i
++)
2093 if (IsDlgButtonChecked(dp
->hwnd
, c
->base_id
+ 1 + i
))
2095 assert(!"No radio button was checked?!");
2099 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
2101 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2102 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2103 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2104 CheckDlgButton(dp
->hwnd
, c
->base_id
, (checked
!= 0));
2107 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
2109 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2110 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2111 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2112 return 0 != IsDlgButtonChecked(dp
->hwnd
, c
->base_id
);
2115 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
2117 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2118 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2119 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2120 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
2123 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
2125 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2126 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2127 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2128 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, buffer
, length
);
2129 buffer
[length
-1] = '\0';
2132 /* The `listbox' functions can also apply to combo boxes. */
2133 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
2135 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2136 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2139 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2140 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2141 c
->ctrl
->editbox
.has_list
)));
2142 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2143 LB_RESETCONTENT
: CB_RESETCONTENT
);
2144 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2147 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
2149 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2150 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2153 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2154 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2155 c
->ctrl
->editbox
.has_list
)));
2156 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2157 LB_DELETESTRING
: CB_DELETESTRING
);
2158 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2161 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
2163 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2164 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2167 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2168 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2169 c
->ctrl
->editbox
.has_list
)));
2170 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2171 LB_ADDSTRING
: CB_ADDSTRING
);
2172 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2176 * Each listbox entry may have a numeric id associated with it.
2177 * Note that some front ends only permit a string to be stored at
2178 * each position, which means that _if_ you put two identical
2179 * strings in any listbox then you MUST not assign them different
2180 * IDs and expect to get meaningful results back.
2182 void dlg_listbox_addwithid(union control
*ctrl
, void *dlg
,
2183 char const *text
, int id
)
2185 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2186 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2187 int msg
, msg2
, index
;
2189 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2190 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2191 c
->ctrl
->editbox
.has_list
)));
2192 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2193 LB_ADDSTRING
: CB_ADDSTRING
);
2194 msg2
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2195 LB_SETITEMDATA
: CB_SETITEMDATA
);
2196 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2197 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg2
, index
, (LPARAM
)id
);
2200 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
2202 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2203 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2205 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
);
2206 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETITEMDATA
: CB_GETITEMDATA
);
2208 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2211 /* dlg_listbox_index returns <0 if no single element is selected. */
2212 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
2214 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2215 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2217 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2218 !c
->ctrl
->listbox
.multisel
);
2219 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETCURSEL
: CB_GETCURSEL
);
2220 ret
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2227 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
2229 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2230 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2231 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2232 c
->ctrl
->listbox
.multisel
&&
2233 c
->ctrl
->listbox
.height
!= 0);
2235 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, LB_GETSEL
, index
, 0);
2238 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
2240 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2241 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2243 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2244 !c
->ctrl
->listbox
.multisel
);
2245 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_SETCURSEL
: CB_SETCURSEL
);
2246 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2249 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
2251 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2252 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2253 assert(c
&& c
->ctrl
->generic
.type
== CTRL_TEXT
);
2254 SetDlgItemText(dp
->hwnd
, c
->base_id
, text
);
2257 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
2259 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2260 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2261 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2262 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, fn
.path
);
2265 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
2267 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2268 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2269 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2270 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, fn
->path
, lenof(fn
->path
));
2271 fn
->path
[lenof(fn
->path
)-1] = '\0';
2274 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
2276 char *buf
, *boldstr
;
2277 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2278 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2279 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2281 *(FontSpec
*)c
->data
= fs
; /* structure copy */
2283 boldstr
= (fs
.isbold ?
"bold, " : "");
2285 buf
= dupprintf("Font: %s, %sdefault height", fs
.name
, boldstr
);
2287 buf
= dupprintf("Font: %s, %s%d-point", fs
.name
, boldstr
,
2288 (fs
.height
< 0 ?
-fs
.height
: fs
.height
));
2289 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, buf
);
2293 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
2295 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2296 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2297 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2298 *fs
= *(FontSpec
*)c
->data
; /* structure copy */
2302 * Bracketing a large set of updates in these two functions will
2303 * cause the front end (if possible) to delay updating the screen
2304 * until it's all complete, thus avoiding flicker.
2306 void dlg_update_start(union control
*ctrl
, void *dlg
)
2308 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2309 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2310 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2311 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, WM_SETREDRAW
, FALSE
, 0);
2315 void dlg_update_done(union control
*ctrl
, void *dlg
)
2317 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2318 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2319 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2320 HWND hw
= GetDlgItem(dp
->hwnd
, c
->base_id
+1);
2321 SendMessage(hw
, WM_SETREDRAW
, TRUE
, 0);
2322 InvalidateRect(hw
, NULL
, TRUE
);
2326 void dlg_set_focus(union control
*ctrl
, void *dlg
)
2328 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2329 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2332 switch (ctrl
->generic
.type
) {
2333 case CTRL_EDITBOX
: id
= c
->base_id
+ 1; break;
2335 for (id
= c
->base_id
+ ctrl
->radio
.nbuttons
; id
> 1; id
--)
2336 if (IsDlgButtonChecked(dp
->hwnd
, id
))
2339 * In the theoretically-unlikely case that no button was
2340 * selected, id should come out of this as 1, which is a
2341 * reasonable enough choice.
2344 case CTRL_CHECKBOX
: id
= c
->base_id
; break;
2345 case CTRL_BUTTON
: id
= c
->base_id
; break;
2346 case CTRL_LISTBOX
: id
= c
->base_id
+ 1; break;
2347 case CTRL_FILESELECT
: id
= c
->base_id
+ 1; break;
2348 case CTRL_FONTSELECT
: id
= c
->base_id
+ 2; break;
2349 default: id
= c
->base_id
; break;
2351 ctl
= GetDlgItem(dp
->hwnd
, id
);
2356 * During event processing, you might well want to give an error
2357 * indication to the user. dlg_beep() is a quick and easy generic
2358 * error; dlg_error() puts up a message-box or equivalent.
2360 void dlg_beep(void *dlg
)
2362 /* struct dlgparam *dp = (struct dlgparam *)dlg; */
2366 void dlg_error_msg(void *dlg
, char *msg
)
2368 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2369 MessageBox(dp
->hwnd
, msg
,
2370 dp
->errtitle ? dp
->errtitle
: NULL
,
2371 MB_OK
| MB_ICONERROR
);
2375 * This function signals to the front end that the dialog's
2376 * processing is completed, and passes an integer value (typically
2377 * a success status).
2379 void dlg_end(void *dlg
, int value
)
2381 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2383 dp
->endresult
= value
;
2386 void dlg_refresh(union control
*ctrl
, void *dlg
)
2388 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2394 * Send EVENT_REFRESH to absolutely everything.
2396 for (j
= 0; j
< dp
->nctrltrees
; j
++) {
2398 (c
= winctrl_findbyindex(dp
->controltrees
[j
], i
)) != NULL
;
2400 if (c
->ctrl
&& c
->ctrl
->generic
.handler
!= NULL
)
2401 c
->ctrl
->generic
.handler(c
->ctrl
, dp
,
2402 dp
->data
, EVENT_REFRESH
);
2407 * Send EVENT_REFRESH to a specific control.
2409 if (ctrl
->generic
.handler
!= NULL
)
2410 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
2414 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
2416 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2417 dp
->coloursel_wanted
= TRUE
;
2418 dp
->coloursel_result
.r
= r
;
2419 dp
->coloursel_result
.g
= g
;
2420 dp
->coloursel_result
.b
= b
;
2423 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
2424 int *r
, int *g
, int *b
)
2426 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2427 if (dp
->coloursel_result
.ok
) {
2428 *r
= dp
->coloursel_result
.r
;
2429 *g
= dp
->coloursel_result
.g
;
2430 *b
= dp
->coloursel_result
.b
;
2436 struct perctrl_privdata
{
2437 union control
*ctrl
;
2442 static int perctrl_privdata_cmp(void *av
, void *bv
)
2444 struct perctrl_privdata
*a
= (struct perctrl_privdata
*)av
;
2445 struct perctrl_privdata
*b
= (struct perctrl_privdata
*)bv
;
2446 if (a
->ctrl
< b
->ctrl
)
2448 else if (a
->ctrl
> b
->ctrl
)
2453 void dp_init(struct dlgparam
*dp
)
2458 dp
->focused
= dp
->lastfocused
= NULL
;
2459 memset(dp
->shortcuts
, 0, sizeof(dp
->shortcuts
));
2461 dp
->errtitle
= NULL
;
2462 dp
->privdata
= newtree234(perctrl_privdata_cmp
);
2465 void dp_add_tree(struct dlgparam
*dp
, struct winctrls
*wc
)
2467 assert(dp
->nctrltrees
< lenof(dp
->controltrees
));
2468 dp
->controltrees
[dp
->nctrltrees
++] = wc
;
2471 void dp_cleanup(struct dlgparam
*dp
)
2473 struct perctrl_privdata
*p
;
2476 while ( (p
= index234(dp
->privdata
, 0)) != NULL
) {
2477 del234(dp
->privdata
, p
);
2482 freetree234(dp
->privdata
);
2483 dp
->privdata
= NULL
;
2487 void *dlg_get_privdata(union control
*ctrl
, void *dlg
)
2489 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2490 struct perctrl_privdata tmp
, *p
;
2492 p
= find234(dp
->privdata
, &tmp
, NULL
);
2499 void dlg_set_privdata(union control
*ctrl
, void *dlg
, void *ptr
)
2501 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2502 struct perctrl_privdata tmp
, *p
;
2504 p
= find234(dp
->privdata
, &tmp
, NULL
);
2506 p
= snew(struct perctrl_privdata
);
2508 p
->needs_free
= FALSE
;
2509 add234(dp
->privdata
, p
);
2514 void *dlg_alloc_privdata(union control
*ctrl
, void *dlg
, size_t size
)
2516 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2517 struct perctrl_privdata tmp
, *p
;
2519 p
= find234(dp
->privdata
, &tmp
, NULL
);
2521 p
= snew(struct perctrl_privdata
);
2523 p
->needs_free
= FALSE
;
2524 add234(dp
->privdata
, p
);
2526 assert(!p
->needs_free
);
2527 p
->needs_free
= TRUE
;
2529 * This is an internal allocation routine, so it's allowed to
2530 * use smalloc directly.
2532 p
->data
= smalloc(size
);