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'
30 #define STATICHEIGHT 8
31 #define TITLEHEIGHT 12
32 #define CHECKBOXHEIGHT 8
36 #define LISTINCREMENT 8
37 #define COMBOHEIGHT 12
38 #define PUSHBTNHEIGHT 14
39 #define PROGBARHEIGHT 14
41 void ctlposinit(struct ctlpos
*cp
, HWND hwnd
,
42 int leftborder
, int rightborder
, int topborder
)
46 cp
->font
= SendMessage(hwnd
, WM_GETFONT
, 0, 0);
48 GetClientRect(hwnd
, &r
);
52 MapDialogRect(hwnd
, &r2
);
53 cp
->dlu4inpix
= r2
.right
;
54 cp
->width
= (r
.right
* 4) / (r2
.right
) - 2 * GAPBETWEEN
;
55 cp
->xoff
= leftborder
;
56 cp
->width
-= leftborder
+ rightborder
;
59 HWND
doctl(struct ctlpos
*cp
, RECT r
,
60 char *wclass
, int wstyle
, int exstyle
, char *wtext
, int wid
)
64 * Note nonstandard use of RECT. This is deliberate: by
65 * transforming the width and height directly we arrange to
66 * have all supposedly same-sized controls really same-sized.
70 MapDialogRect(cp
->hwnd
, &r
);
73 * We can pass in cp->hwnd == NULL, to indicate a dry run
74 * without creating any actual controls.
77 ctl
= CreateWindowEx(exstyle
, wclass
, wtext
, wstyle
,
78 r
.left
, r
.top
, r
.right
, r
.bottom
,
79 cp
->hwnd
, (HMENU
) wid
, hinst
, NULL
);
80 SendMessage(ctl
, WM_SETFONT
, cp
->font
, MAKELPARAM(TRUE
, 0));
82 if (!strcmp(wclass
, "LISTBOX")) {
84 * Bizarre Windows bug: the list box calculates its
85 * number of lines based on the font it has at creation
86 * time, but sending it WM_SETFONT doesn't cause it to
87 * recalculate. So now, _after_ we've sent it
88 * WM_SETFONT, we explicitly resize it (to the same
89 * size it was already!) to force it to reconsider.
91 SetWindowPos(ctl
, NULL
, 0, 0, r
.right
, r
.bottom
,
92 SWP_NOACTIVATE
| SWP_NOCOPYBITS
|
93 SWP_NOMOVE
| SWP_NOZORDER
);
101 * A title bar across the top of a sub-dialog.
103 void bartitle(struct ctlpos
*cp
, char *name
, int id
)
110 r
.bottom
= STATICHEIGHT
;
111 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
112 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, name
, id
);
116 * Begin a grouping box, with or without a group title.
118 void beginbox(struct ctlpos
*cp
, char *name
, int idbox
)
120 cp
->boxystart
= cp
->ypos
;
122 cp
->boxystart
-= STATICHEIGHT
/ 2;
124 cp
->ypos
+= STATICHEIGHT
;
126 cp
->width
-= 2 * GAPXBOX
;
133 * End a grouping box.
135 void endbox(struct ctlpos
*cp
)
139 cp
->width
+= 2 * GAPXBOX
;
140 cp
->ypos
+= GAPYBOX
- GAPBETWEEN
;
143 r
.top
= cp
->boxystart
;
144 r
.bottom
= cp
->ypos
- cp
->boxystart
;
145 doctl(cp
, r
, "BUTTON", BS_GROUPBOX
| WS_CHILD
| WS_VISIBLE
, 0,
146 cp
->boxtext ? cp
->boxtext
: "", cp
->boxid
);
151 * A static line, followed by a full-width edit box.
153 void editboxfw(struct ctlpos
*cp
, int password
, char *text
,
154 int staticid
, int editid
)
163 r
.bottom
= STATICHEIGHT
;
164 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
165 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
;
168 r
.bottom
= EDITHEIGHT
;
170 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
|
171 (password ? ES_PASSWORD
: 0),
172 WS_EX_CLIENTEDGE
, "", editid
);
173 cp
->ypos
+= EDITHEIGHT
+ GAPBETWEEN
;
177 * A static line, followed by a full-width combo box.
179 void combobox(struct ctlpos
*cp
, char *text
, int staticid
, int listid
)
188 r
.bottom
= STATICHEIGHT
;
189 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, staticid
);
190 cp
->ypos
+= STATICHEIGHT
+ GAPWITHIN
;
193 r
.bottom
= COMBOHEIGHT
* 10;
194 doctl(cp
, r
, "COMBOBOX",
195 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
196 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", listid
);
197 cp
->ypos
+= COMBOHEIGHT
+ GAPBETWEEN
;
200 struct radio
{ char *text
; int id
; };
202 static void radioline_common(struct ctlpos
*cp
, char *text
, int id
,
203 int nacross
, struct radio
*buttons
, int nbuttons
)
214 r
.bottom
= STATICHEIGHT
;
215 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
216 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, text
, id
);
221 for (j
= 0; j
< nbuttons
; j
++) {
222 char *btext
= buttons
[j
].text
;
223 int bid
= buttons
[j
].id
;
226 cp
->ypos
+= r
.bottom
+ (nacross
> 1 ? GAPBETWEEN
: GAPWITHIN
);
229 r
.left
= GAPBETWEEN
+ i
* (cp
->width
+ GAPBETWEEN
) / nacross
;
232 (i
+ 1) * (cp
->width
+ GAPBETWEEN
) / nacross
- r
.left
;
234 r
.right
= cp
->width
- r
.left
;
236 r
.bottom
= RADIOHEIGHT
;
237 doctl(cp
, r
, "BUTTON",
238 BS_NOTIFY
| BS_AUTORADIOBUTTON
| WS_CHILD
|
239 WS_VISIBLE
| WS_TABSTOP
| group
, 0, btext
, bid
);
243 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
247 * A set of radio buttons on the same line, with a static above
248 * them. `nacross' dictates how many parts the line is divided into
249 * (you might want this not to equal the number of buttons if you
250 * needed to line up some 2s and some 3s to look good in the same
253 * There's a bit of a hack in here to ensure that if nacross
254 * exceeds the actual number of buttons, the rightmost button
255 * really does get all the space right to the edge of the line, so
256 * you can do things like
258 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
260 void radioline(struct ctlpos
*cp
, char *text
, int id
, int nacross
, ...)
263 struct radio
*buttons
;
266 va_start(ap
, nacross
);
269 char *btext
= va_arg(ap
, char *);
273 bid
= va_arg(ap
, int);
277 buttons
= snewn(nbuttons
, struct radio
);
278 va_start(ap
, nacross
);
279 for (i
= 0; i
< nbuttons
; i
++) {
280 buttons
[i
].text
= va_arg(ap
, char *);
281 buttons
[i
].id
= va_arg(ap
, int);
284 radioline_common(cp
, text
, id
, nacross
, buttons
, nbuttons
);
289 * A set of radio buttons on the same line, without a static above
290 * them. Otherwise just like radioline.
292 void bareradioline(struct ctlpos
*cp
, int nacross
, ...)
295 struct radio
*buttons
;
298 va_start(ap
, nacross
);
301 char *btext
= va_arg(ap
, char *);
305 bid
= va_arg(ap
, int);
308 buttons
= snewn(nbuttons
, struct radio
);
309 va_start(ap
, nacross
);
310 for (i
= 0; i
< nbuttons
; i
++) {
311 buttons
[i
].text
= va_arg(ap
, char *);
312 buttons
[i
].id
= va_arg(ap
, int);
315 radioline_common(cp
, NULL
, 0, nacross
, buttons
, nbuttons
);
320 * A set of radio buttons on multiple lines, with a static above
323 void radiobig(struct ctlpos
*cp
, char *text
, int id
, ...)
326 struct radio
*buttons
;
332 char *btext
= va_arg(ap
, char *);
336 bid
= va_arg(ap
, int);
339 buttons
= snewn(nbuttons
, struct radio
);
341 for (i
= 0; i
< nbuttons
; i
++) {
342 buttons
[i
].text
= va_arg(ap
, char *);
343 buttons
[i
].id
= va_arg(ap
, int);
346 radioline_common(cp
, text
, id
, 1, buttons
, nbuttons
);
351 * A single standalone checkbox.
353 void checkbox(struct ctlpos
*cp
, char *text
, int id
)
360 r
.bottom
= CHECKBOXHEIGHT
;
361 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
362 doctl(cp
, r
, "BUTTON",
363 BS_NOTIFY
| BS_AUTOCHECKBOX
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0,
368 * Wrap a piece of text for a static text control. Returns the
369 * wrapped text (a malloc'ed string containing \ns), and also
370 * returns the number of lines required.
372 char *staticwrap(struct ctlpos
*cp
, HWND hwnd
, char *text
, int *lines
)
374 HDC hdc
= GetDC(hwnd
);
375 int lpx
= GetDeviceCaps(hdc
, LOGPIXELSX
);
376 int width
, nlines
, j
;
381 HFONT oldfont
, newfont
;
383 ret
= snewn(1+strlen(text
), char);
386 pwidths
= snewn(1+strlen(text
), INT
);
389 * Work out the width the text will need to fit in, by doing
390 * the same adjustment that the `statictext' function itself
393 SetMapMode(hdc
, MM_TEXT
); /* ensure logical units == pixels */
394 r
.left
= r
.top
= r
.bottom
= 0;
396 MapDialogRect(hwnd
, &r
);
402 * We must select the correct font into the HDC before calling
403 * GetTextExtent*, or silly things will happen.
405 newfont
= (HFONT
)SendMessage(hwnd
, WM_GETFONT
, 0, 0);
406 oldfont
= SelectObject(hdc
, newfont
);
409 if (!GetTextExtentExPoint(hdc
, p
, strlen(p
), width
,
410 &nfit
, pwidths
, &size
) ||
411 (size_t)nfit
>= strlen(p
)) {
413 * Either GetTextExtentExPoint returned failure, or the
414 * whole of the rest of the text fits on this line.
415 * Either way, we stop wrapping, copy the remainder of
416 * the input string unchanged to the output, and leave.
423 * Now we search backwards along the string from `nfit',
424 * looking for a space at which to break the line. If we
425 * don't find one at all, that's fine - we'll just break
426 * the line at `nfit'.
428 for (j
= nfit
; j
> 0; j
--) {
429 if (isspace((unsigned char)p
[j
])) {
440 while (*p
&& isspace((unsigned char)*p
))
446 SelectObject(hdc
, oldfont
);
447 ReleaseDC(cp
->hwnd
, hdc
);
449 if (lines
) *lines
= nlines
;
457 * A single standalone static text control.
459 void statictext(struct ctlpos
*cp
, char *text
, int lines
, int id
)
466 r
.bottom
= STATICHEIGHT
* lines
;
467 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
468 doctl(cp
, r
, "STATIC",
469 WS_CHILD
| WS_VISIBLE
| SS_LEFTNOWORDWRAP
,
474 * An owner-drawn static text control for a panel title.
476 void paneltitle(struct ctlpos
*cp
, int id
)
483 r
.bottom
= TITLEHEIGHT
;
484 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
485 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_OWNERDRAW
,
490 * A button on the right hand side, with a static to its left.
492 void staticbtn(struct ctlpos
*cp
, char *stext
, int sid
,
493 char *btext
, int bid
)
495 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
496 PUSHBTNHEIGHT
: STATICHEIGHT
);
498 int lwid
, rwid
, rpos
;
500 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
501 lwid
= rpos
- 2 * GAPBETWEEN
;
502 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
505 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
507 r
.bottom
= STATICHEIGHT
;
508 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
511 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
513 r
.bottom
= PUSHBTNHEIGHT
;
514 doctl(cp
, r
, "BUTTON",
515 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
518 cp
->ypos
+= height
+ GAPBETWEEN
;
522 * A simple push button.
524 void button(struct ctlpos
*cp
, char *btext
, int bid
, int defbtn
)
531 r
.bottom
= PUSHBTNHEIGHT
;
533 /* Q67655: the _dialog box_ must know which button is default
534 * as well as the button itself knowing */
535 if (defbtn
&& cp
->hwnd
)
536 SendMessage(cp
->hwnd
, DM_SETDEFID
, bid
, 0);
538 doctl(cp
, r
, "BUTTON",
539 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
540 (defbtn ? BS_DEFPUSHBUTTON
: 0) | BS_PUSHBUTTON
,
543 cp
->ypos
+= PUSHBTNHEIGHT
+ GAPBETWEEN
;
547 * Like staticbtn, but two buttons.
549 void static2btn(struct ctlpos
*cp
, char *stext
, int sid
,
550 char *btext1
, int bid1
, char *btext2
, int bid2
)
552 const int height
= (PUSHBTNHEIGHT
> STATICHEIGHT ?
553 PUSHBTNHEIGHT
: STATICHEIGHT
);
555 int lwid
, rwid1
, rwid2
, rpos1
, rpos2
;
557 rpos1
= GAPBETWEEN
+ (cp
->width
+ GAPBETWEEN
) / 2;
558 rpos2
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
559 lwid
= rpos1
- 2 * GAPBETWEEN
;
560 rwid1
= rpos2
- rpos1
- GAPBETWEEN
;
561 rwid2
= cp
->width
+ GAPBETWEEN
- rpos2
;
564 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
566 r
.bottom
= STATICHEIGHT
;
567 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
570 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
572 r
.bottom
= PUSHBTNHEIGHT
;
573 doctl(cp
, r
, "BUTTON",
574 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
578 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
580 r
.bottom
= PUSHBTNHEIGHT
;
581 doctl(cp
, r
, "BUTTON",
582 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
585 cp
->ypos
+= height
+ GAPBETWEEN
;
589 * An edit control on the right hand side, with a static to its left.
591 static void staticedit_internal(struct ctlpos
*cp
, char *stext
,
592 int sid
, int eid
, int percentedit
,
595 const int height
= (EDITHEIGHT
> STATICHEIGHT ?
596 EDITHEIGHT
: STATICHEIGHT
);
598 int lwid
, rwid
, rpos
;
601 GAPBETWEEN
+ (100 - percentedit
) * (cp
->width
+ GAPBETWEEN
) / 100;
602 lwid
= rpos
- 2 * GAPBETWEEN
;
603 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
606 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
608 r
.bottom
= STATICHEIGHT
;
609 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
612 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
614 r
.bottom
= EDITHEIGHT
;
616 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
| style
,
617 WS_EX_CLIENTEDGE
, "", eid
);
619 cp
->ypos
+= height
+ GAPBETWEEN
;
622 void staticedit(struct ctlpos
*cp
, char *stext
,
623 int sid
, int eid
, int percentedit
)
625 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, 0);
628 void staticpassedit(struct ctlpos
*cp
, char *stext
,
629 int sid
, int eid
, int percentedit
)
631 staticedit_internal(cp
, stext
, sid
, eid
, percentedit
, ES_PASSWORD
);
635 * A drop-down list box on the right hand side, with a static to
638 void staticddl(struct ctlpos
*cp
, char *stext
,
639 int sid
, int lid
, int percentlist
)
641 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
642 COMBOHEIGHT
: STATICHEIGHT
);
644 int lwid
, rwid
, rpos
;
647 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
648 lwid
= rpos
- 2 * GAPBETWEEN
;
649 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
652 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
654 r
.bottom
= STATICHEIGHT
;
655 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
658 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
660 r
.bottom
= COMBOHEIGHT
*4;
661 doctl(cp
, r
, "COMBOBOX",
662 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
663 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
665 cp
->ypos
+= height
+ GAPBETWEEN
;
669 * A combo box on the right hand side, with a static to its left.
671 void staticcombo(struct ctlpos
*cp
, char *stext
,
672 int sid
, int lid
, int percentlist
)
674 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
675 COMBOHEIGHT
: STATICHEIGHT
);
677 int lwid
, rwid
, rpos
;
680 GAPBETWEEN
+ (100 - percentlist
) * (cp
->width
+ GAPBETWEEN
) / 100;
681 lwid
= rpos
- 2 * GAPBETWEEN
;
682 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
685 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
687 r
.bottom
= STATICHEIGHT
;
688 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
691 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
693 r
.bottom
= COMBOHEIGHT
*10;
694 doctl(cp
, r
, "COMBOBOX",
695 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
696 CBS_DROPDOWN
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
698 cp
->ypos
+= height
+ GAPBETWEEN
;
702 * A static, with a full-width drop-down list box below it.
704 void staticddlbig(struct ctlpos
*cp
, char *stext
,
713 r
.bottom
= STATICHEIGHT
;
714 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
715 cp
->ypos
+= STATICHEIGHT
;
721 r
.bottom
= COMBOHEIGHT
*4;
722 doctl(cp
, r
, "COMBOBOX",
723 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
724 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
725 cp
->ypos
+= COMBOHEIGHT
+ GAPBETWEEN
;
729 * A big multiline edit control with a static labelling it.
731 void bigeditctrl(struct ctlpos
*cp
, char *stext
,
732 int sid
, int eid
, int lines
)
740 r
.bottom
= STATICHEIGHT
;
741 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
742 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
748 r
.bottom
= EDITHEIGHT
+ (lines
- 1) * STATICHEIGHT
;
749 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
751 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
| ES_MULTILINE
,
752 WS_EX_CLIENTEDGE
, "", eid
);
756 * A list box with a static labelling it.
758 void listbox(struct ctlpos
*cp
, char *stext
,
759 int sid
, int lid
, int lines
, int multi
)
767 r
.bottom
= STATICHEIGHT
;
768 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
769 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
775 r
.bottom
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
776 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
777 doctl(cp
, r
, "LISTBOX",
778 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| WS_VSCROLL
|
779 LBS_NOTIFY
| LBS_HASSTRINGS
| LBS_USETABSTOPS
|
780 (multi ? LBS_MULTIPLESEL
: 0),
781 WS_EX_CLIENTEDGE
, "", lid
);
785 * A tab-control substitute when a real tab control is unavailable.
787 void ersatztab(struct ctlpos
*cp
, char *stext
, int sid
, int lid
, int s2id
)
789 const int height
= (COMBOHEIGHT
> STATICHEIGHT ?
790 COMBOHEIGHT
: STATICHEIGHT
);
792 int bigwid
, lwid
, rwid
, rpos
;
793 static const int BIGGAP
= 15;
794 static const int MEDGAP
= 3;
796 bigwid
= cp
->width
+ 2 * GAPBETWEEN
- 2 * BIGGAP
;
798 rpos
= BIGGAP
+ (bigwid
+ BIGGAP
) / 2;
799 lwid
= rpos
- 2 * BIGGAP
;
800 rwid
= bigwid
+ BIGGAP
- rpos
;
803 r
.top
= cp
->ypos
+ (height
- STATICHEIGHT
) / 2;
805 r
.bottom
= STATICHEIGHT
;
806 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
809 r
.top
= cp
->ypos
+ (height
- COMBOHEIGHT
) / 2;
811 r
.bottom
= COMBOHEIGHT
* 10;
812 doctl(cp
, r
, "COMBOBOX",
813 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
814 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
, WS_EX_CLIENTEDGE
, "", lid
);
816 cp
->ypos
+= height
+ MEDGAP
+ GAPBETWEEN
;
822 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
| SS_ETCHEDHORZ
,
827 * A static line, followed by an edit control on the left hand side
828 * and a button on the right.
830 void editbutton(struct ctlpos
*cp
, char *stext
, int sid
,
831 int eid
, char *btext
, int bid
)
833 const int height
= (EDITHEIGHT
> PUSHBTNHEIGHT ?
834 EDITHEIGHT
: PUSHBTNHEIGHT
);
836 int lwid
, rwid
, rpos
;
841 r
.bottom
= STATICHEIGHT
;
842 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
843 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
845 rpos
= GAPBETWEEN
+ 3 * (cp
->width
+ GAPBETWEEN
) / 4;
846 lwid
= rpos
- 2 * GAPBETWEEN
;
847 rwid
= cp
->width
+ GAPBETWEEN
- rpos
;
850 r
.top
= cp
->ypos
+ (height
- EDITHEIGHT
) / 2;
852 r
.bottom
= EDITHEIGHT
;
854 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| ES_AUTOHSCROLL
,
855 WS_EX_CLIENTEDGE
, "", eid
);
858 r
.top
= cp
->ypos
+ (height
- PUSHBTNHEIGHT
) / 2;
860 r
.bottom
= PUSHBTNHEIGHT
;
861 doctl(cp
, r
, "BUTTON",
862 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
,
865 cp
->ypos
+= height
+ GAPBETWEEN
;
869 * A special control for manipulating an ordered preference list
870 * (eg. for cipher selection).
871 * XXX: this is a rough hack and could be improved.
873 void prefslist(struct prefslist
*hdl
, struct ctlpos
*cp
, int lines
,
874 char *stext
, int sid
, int listid
, int upbid
, int dnbid
)
876 const static int percents
[] = { 5, 75, 20 };
878 int xpos
, percent
= 0, i
;
879 int listheight
= LISTHEIGHT
+ (lines
- 1) * LISTINCREMENT
;
880 const int BTNSHEIGHT
= 2*PUSHBTNHEIGHT
+ GAPBETWEEN
;
881 int totalheight
, buttonpos
;
883 /* Squirrel away IDs. */
884 hdl
->listid
= listid
;
888 /* The static label. */
893 r
.bottom
= STATICHEIGHT
;
894 cp
->ypos
+= r
.bottom
+ GAPWITHIN
;
895 doctl(cp
, r
, "STATIC", WS_CHILD
| WS_VISIBLE
, 0, stext
, sid
);
898 if (listheight
> BTNSHEIGHT
) {
899 totalheight
= listheight
;
900 buttonpos
= (listheight
- BTNSHEIGHT
) / 2;
902 totalheight
= BTNSHEIGHT
;
906 for (i
=0; i
<3; i
++) {
908 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
909 left
= xpos
+ GAPBETWEEN
;
910 percent
+= percents
[i
];
911 xpos
= (cp
->width
+ GAPBETWEEN
) * percent
/ 100;
916 /* The drag list box. */
917 r
.left
= left
; r
.right
= wid
;
918 r
.top
= cp
->ypos
; r
.bottom
= listheight
;
921 ctl
= doctl(cp
, r
, "LISTBOX",
922 WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
|
923 WS_VSCROLL
| LBS_HASSTRINGS
| LBS_USETABSTOPS
,
931 /* The "Up" and "Down" buttons. */
932 /* XXX worry about accelerators if we have more than one
933 * prefslist on a panel */
934 r
.left
= left
; r
.right
= wid
;
935 r
.top
= cp
->ypos
+ buttonpos
; r
.bottom
= PUSHBTNHEIGHT
;
936 doctl(cp
, r
, "BUTTON",
937 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
938 WS_TABSTOP
| BS_PUSHBUTTON
,
941 r
.left
= left
; r
.right
= wid
;
942 r
.top
= cp
->ypos
+ buttonpos
+ PUSHBTNHEIGHT
+ GAPBETWEEN
;
943 r
.bottom
= PUSHBTNHEIGHT
;
944 doctl(cp
, r
, "BUTTON",
945 BS_NOTIFY
| WS_CHILD
| WS_VISIBLE
|
946 WS_TABSTOP
| BS_PUSHBUTTON
,
954 cp
->ypos
+= totalheight
+ GAPBETWEEN
;
959 * Helper function for prefslist: move item in list box.
961 static void pl_moveitem(HWND hwnd
, int listid
, int src
, int dst
)
965 /* Get the item's data. */
966 tlen
= SendDlgItemMessage (hwnd
, listid
, LB_GETTEXTLEN
, src
, 0);
967 txt
= snewn(tlen
+1, char);
968 SendDlgItemMessage (hwnd
, listid
, LB_GETTEXT
, src
, (LPARAM
) txt
);
969 val
= SendDlgItemMessage (hwnd
, listid
, LB_GETITEMDATA
, src
, 0);
970 /* Deselect old location. */
971 SendDlgItemMessage (hwnd
, listid
, LB_SETSEL
, FALSE
, src
);
972 /* Delete it at the old location. */
973 SendDlgItemMessage (hwnd
, listid
, LB_DELETESTRING
, src
, 0);
974 /* Insert it at new location. */
975 SendDlgItemMessage (hwnd
, listid
, LB_INSERTSTRING
, dst
,
977 SendDlgItemMessage (hwnd
, listid
, LB_SETITEMDATA
, dst
,
980 SendDlgItemMessage (hwnd
, listid
, LB_SETCURSEL
, dst
, 0);
984 int pl_itemfrompt(HWND hwnd
, POINT cursor
, BOOL scroll
)
987 POINT uppoint
, downpoint
;
988 int updist
, downdist
, upitem
, downitem
, i
;
991 * Ghastly hackery to try to figure out not which
992 * _item_, but which _gap between items_, the user
993 * is pointing at. We do this by first working out
994 * which list item is under the cursor, and then
995 * working out how far the cursor would have to
996 * move up or down before the answer was different.
997 * Then we put the insertion point _above_ the
998 * current item if the upper edge is closer than
999 * the lower edge, or _below_ it if vice versa.
1001 ret
= LBItemFromPt(hwnd
, cursor
, scroll
);
1004 ret
= LBItemFromPt(hwnd
, cursor
, FALSE
);
1005 updist
= downdist
= 0;
1006 for (i
= 1; i
< 4096 && (!updist
|| !downdist
); i
++) {
1007 uppoint
= downpoint
= cursor
;
1010 upitem
= LBItemFromPt(hwnd
, uppoint
, FALSE
);
1011 downitem
= LBItemFromPt(hwnd
, downpoint
, FALSE
);
1012 if (!updist
&& upitem
!= ret
)
1014 if (!downdist
&& downitem
!= ret
)
1017 if (downdist
< updist
)
1023 * Handler for prefslist above.
1025 * Return value has bit 0 set if the dialog box procedure needs to
1026 * return TRUE from handling this message; it has bit 1 set if a
1027 * change may have been made in the contents of the list.
1029 int handle_prefslist(struct prefslist
*hdl
,
1030 int *array
, int maxmemb
,
1031 int is_dlmsg
, HWND hwnd
,
1032 WPARAM wParam
, LPARAM lParam
)
1039 if ((int)wParam
== hdl
->listid
) {
1040 DRAGLISTINFO
*dlm
= (DRAGLISTINFO
*)lParam
;
1041 int dest
= 0; /* initialise to placate gcc */
1042 switch (dlm
->uNotification
) {
1044 /* Add a dummy item to make pl_itemfrompt() work
1046 * FIXME: this causes scrollbar glitches if the count of
1047 * listbox contains >= its height. */
1049 SendDlgItemMessage(hwnd
, hdl
->listid
,
1050 LB_ADDSTRING
, 0, (LPARAM
) "");
1052 hdl
->srcitem
= LBItemFromPt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1054 /* XXX hack Q183115 */
1055 SetWindowLongPtr(hwnd
, DWLP_MSGRESULT
, TRUE
);
1058 DrawInsert(hwnd
, dlm
->hWnd
, -1); /* Clear arrow */
1059 SendDlgItemMessage(hwnd
, hdl
->listid
,
1060 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1065 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1066 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1067 DrawInsert (hwnd
, dlm
->hWnd
, dest
);
1069 SetWindowLongPtr(hwnd
, DWLP_MSGRESULT
, DL_MOVECURSOR
);
1071 SetWindowLongPtr(hwnd
, DWLP_MSGRESULT
, DL_STOPCURSOR
);
1074 if (hdl
->dragging
) {
1075 dest
= pl_itemfrompt(dlm
->hWnd
, dlm
->ptCursor
, TRUE
);
1076 if (dest
> hdl
->dummyitem
) dest
= hdl
->dummyitem
;
1077 DrawInsert (hwnd
, dlm
->hWnd
, -1);
1079 SendDlgItemMessage(hwnd
, hdl
->listid
,
1080 LB_DELETESTRING
, hdl
->dummyitem
, 0);
1081 if (hdl
->dragging
) {
1084 /* Correct for "missing" item. */
1085 if (dest
> hdl
->srcitem
) dest
--;
1086 pl_moveitem(hwnd
, hdl
->listid
, hdl
->srcitem
, dest
);
1096 if (((LOWORD(wParam
) == hdl
->upbid
) ||
1097 (LOWORD(wParam
) == hdl
->dnbid
)) &&
1098 ((HIWORD(wParam
) == BN_CLICKED
) ||
1099 (HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1100 /* Move an item up or down the list. */
1101 /* Get the current selection, if any. */
1102 int selection
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCURSEL
, 0, 0);
1103 if (selection
== LB_ERR
) {
1107 /* Get the total number of items. */
1108 nitems
= SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETCOUNT
, 0, 0);
1109 /* Should we do anything? */
1110 if (LOWORD(wParam
) == hdl
->upbid
&& (selection
> 0))
1111 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
- 1);
1112 else if (LOWORD(wParam
) == hdl
->dnbid
&& (selection
< nitems
- 1))
1113 pl_moveitem(hwnd
, hdl
->listid
, selection
, selection
+ 1);
1122 /* Update array to match the list box. */
1123 for (i
=0; i
< maxmemb
; i
++)
1124 array
[i
] = SendDlgItemMessage (hwnd
, hdl
->listid
, LB_GETITEMDATA
,
1132 * A progress bar (from Common Controls). We like our progress bars
1133 * to be smooth and unbroken, without those ugly divisions; some
1134 * older compilers may not support that, but that's life.
1136 void progressbar(struct ctlpos
*cp
, int id
)
1140 r
.left
= GAPBETWEEN
;
1142 r
.right
= cp
->width
;
1143 r
.bottom
= PROGBARHEIGHT
;
1144 cp
->ypos
+= r
.bottom
+ GAPBETWEEN
;
1146 doctl(cp
, r
, PROGRESS_CLASS
, WS_CHILD
| WS_VISIBLE
1150 , WS_EX_CLIENTEDGE
, "", id
);
1153 /* ----------------------------------------------------------------------
1154 * Platform-specific side of portable dialog-box mechanism.
1158 * This function takes a string, escapes all the ampersands, and
1159 * places a single (unescaped) ampersand in front of the first
1160 * occurrence of the given shortcut character (which may be
1163 * Return value is a malloc'ed copy of the processed version of the
1166 static char *shortcut_escape(const char *text
, char shortcut
)
1173 return NULL
; /* sfree won't choke on this */
1175 ret
= snewn(2*strlen(text
)+1, char); /* size potentially doubles! */
1176 shortcut
= tolower((unsigned char)shortcut
);
1181 if (shortcut
!= NO_SHORTCUT
&&
1182 tolower((unsigned char)*p
) == shortcut
) {
1184 shortcut
= NO_SHORTCUT
; /* stop it happening twice */
1185 } else if (*p
== '&') {
1194 void winctrl_add_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1197 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1198 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1199 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1200 assert(!dp
->shortcuts
[s
]);
1201 dp
->shortcuts
[s
] = TRUE
;
1205 void winctrl_rem_shortcuts(struct dlgparam
*dp
, struct winctrl
*c
)
1208 for (i
= 0; i
< lenof(c
->shortcuts
); i
++)
1209 if (c
->shortcuts
[i
] != NO_SHORTCUT
) {
1210 unsigned char s
= tolower((unsigned char)c
->shortcuts
[i
]);
1211 assert(dp
->shortcuts
[s
]);
1212 dp
->shortcuts
[s
] = FALSE
;
1216 static int winctrl_cmp_byctrl(void *av
, void *bv
)
1218 struct winctrl
*a
= (struct winctrl
*)av
;
1219 struct winctrl
*b
= (struct winctrl
*)bv
;
1220 if (a
->ctrl
< b
->ctrl
)
1222 else if (a
->ctrl
> b
->ctrl
)
1227 static int winctrl_cmp_byid(void *av
, void *bv
)
1229 struct winctrl
*a
= (struct winctrl
*)av
;
1230 struct winctrl
*b
= (struct winctrl
*)bv
;
1231 if (a
->base_id
< b
->base_id
)
1233 else if (a
->base_id
> b
->base_id
)
1238 static int winctrl_cmp_byctrl_find(void *av
, void *bv
)
1240 union control
*a
= (union control
*)av
;
1241 struct winctrl
*b
= (struct winctrl
*)bv
;
1244 else if (a
> b
->ctrl
)
1249 static int winctrl_cmp_byid_find(void *av
, void *bv
)
1252 struct winctrl
*b
= (struct winctrl
*)bv
;
1253 if (*a
< b
->base_id
)
1255 else if (*a
>= b
->base_id
+ b
->num_ids
)
1261 void winctrl_init(struct winctrls
*wc
)
1263 wc
->byctrl
= newtree234(winctrl_cmp_byctrl
);
1264 wc
->byid
= newtree234(winctrl_cmp_byid
);
1266 void winctrl_cleanup(struct winctrls
*wc
)
1270 while ((c
= index234(wc
->byid
, 0)) != NULL
) {
1271 winctrl_remove(wc
, c
);
1276 freetree234(wc
->byctrl
);
1277 freetree234(wc
->byid
);
1278 wc
->byctrl
= wc
->byid
= NULL
;
1281 void winctrl_add(struct winctrls
*wc
, struct winctrl
*c
)
1283 struct winctrl
*ret
;
1285 ret
= add234(wc
->byctrl
, c
);
1288 ret
= add234(wc
->byid
, c
);
1292 void winctrl_remove(struct winctrls
*wc
, struct winctrl
*c
)
1294 struct winctrl
*ret
;
1295 ret
= del234(wc
->byctrl
, c
);
1296 ret
= del234(wc
->byid
, c
);
1300 struct winctrl
*winctrl_findbyctrl(struct winctrls
*wc
, union control
*ctrl
)
1302 return find234(wc
->byctrl
, ctrl
, winctrl_cmp_byctrl_find
);
1305 struct winctrl
*winctrl_findbyid(struct winctrls
*wc
, int id
)
1307 return find234(wc
->byid
, &id
, winctrl_cmp_byid_find
);
1310 struct winctrl
*winctrl_findbyindex(struct winctrls
*wc
, int index
)
1312 return index234(wc
->byid
, index
);
1315 void winctrl_layout(struct dlgparam
*dp
, struct winctrls
*wc
,
1316 struct ctlpos
*cp
, struct controlset
*s
, int *id
)
1318 struct ctlpos columns
[16];
1319 int ncols
, colstart
, colspan
;
1321 struct ctlpos tabdelays
[16];
1322 union control
*tabdelayed
[16];
1327 char shortcuts
[MAX_SHORTCUTS_PER_CTRL
];
1330 int i
, actual_base_id
, base_id
, num_ids
;
1335 /* Start a containing box, if we have a boxname. */
1336 if (s
->boxname
&& *s
->boxname
) {
1337 struct winctrl
*c
= snew(struct winctrl
);
1339 c
->base_id
= base_id
;
1342 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1344 beginbox(cp
, s
->boxtitle
, base_id
);
1348 /* Draw a title, if we have one. */
1349 if (!s
->boxname
&& s
->boxtitle
) {
1350 struct winctrl
*c
= snew(struct winctrl
);
1352 c
->base_id
= base_id
;
1354 c
->data
= dupstr(s
->boxtitle
);
1355 memset(c
->shortcuts
, NO_SHORTCUT
, lenof(c
->shortcuts
));
1357 paneltitle(cp
, base_id
);
1361 /* Initially we have just one column. */
1363 columns
[0] = *cp
; /* structure copy */
1365 /* And initially, there are no pending tab-delayed controls. */
1368 /* Loop over each control in the controlset. */
1369 for (i
= 0; i
< s
->ncontrols
; i
++) {
1370 union control
*ctrl
= s
->ctrls
[i
];
1373 * Generic processing that pertains to all control types.
1374 * At the end of this if statement, we'll have produced
1375 * `ctrl' (a pointer to the control we have to create, or
1376 * think about creating, in this iteration of the loop),
1377 * `pos' (a suitable ctlpos with which to position it), and
1378 * `c' (a winctrl structure to receive details of the
1379 * dialog IDs). Or we'll have done a `continue', if it was
1380 * CTRL_COLUMNS and doesn't require any control creation at
1383 if (ctrl
->generic
.type
== CTRL_COLUMNS
) {
1384 assert((ctrl
->columns
.ncols
== 1) ^ (ncols
== 1));
1388 * We're splitting into multiple columns.
1390 int lpercent
, rpercent
, lx
, rx
, i
;
1392 ncols
= ctrl
->columns
.ncols
;
1393 assert(ncols
<= lenof(columns
));
1394 for (i
= 1; i
< ncols
; i
++)
1395 columns
[i
] = columns
[0]; /* structure copy */
1398 for (i
= 0; i
< ncols
; i
++) {
1399 rpercent
= lpercent
+ ctrl
->columns
.percentages
[i
];
1400 lx
= columns
[i
].xoff
+ lpercent
*
1401 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1402 rx
= columns
[i
].xoff
+ rpercent
*
1403 (columns
[i
].width
+ GAPBETWEEN
) / 100;
1404 columns
[i
].xoff
= lx
;
1405 columns
[i
].width
= rx
- lx
- GAPBETWEEN
;
1406 lpercent
= rpercent
;
1410 * We're recombining the various columns into one.
1412 int maxy
= columns
[0].ypos
;
1414 for (i
= 1; i
< ncols
; i
++)
1415 if (maxy
< columns
[i
].ypos
)
1416 maxy
= columns
[i
].ypos
;
1418 columns
[0] = *cp
; /* structure copy */
1419 columns
[0].ypos
= maxy
;
1423 } else if (ctrl
->generic
.type
== CTRL_TABDELAY
) {
1426 assert(!ctrl
->generic
.tabdelay
);
1427 ctrl
= ctrl
->tabdelay
.ctrl
;
1429 for (i
= 0; i
< ntabdelays
; i
++)
1430 if (tabdelayed
[i
] == ctrl
)
1432 assert(i
< ntabdelays
); /* we have to have found it */
1434 pos
= tabdelays
[i
]; /* structure copy */
1436 colstart
= colspan
= -1; /* indicate this was tab-delayed */
1440 * If it wasn't one of those, it's a genuine control;
1441 * so we'll have to compute a position for it now, by
1442 * checking its column span.
1446 colstart
= COLUMN_START(ctrl
->generic
.column
);
1447 colspan
= COLUMN_SPAN(ctrl
->generic
.column
);
1449 pos
= columns
[colstart
]; /* structure copy */
1450 pos
.width
= columns
[colstart
+colspan
-1].width
+
1451 (columns
[colstart
+colspan
-1].xoff
- columns
[colstart
].xoff
);
1453 for (col
= colstart
; col
< colstart
+colspan
; col
++)
1454 if (pos
.ypos
< columns
[col
].ypos
)
1455 pos
.ypos
= columns
[col
].ypos
;
1458 * If this control is to be tabdelayed, add it to the
1459 * tabdelay list, and unset pos.hwnd to inhibit actual
1462 if (ctrl
->generic
.tabdelay
) {
1463 assert(ntabdelays
< lenof(tabdelays
));
1464 tabdelays
[ntabdelays
] = pos
; /* structure copy */
1465 tabdelayed
[ntabdelays
] = ctrl
;
1471 /* Most controls don't need anything in c->data. */
1474 /* And they all start off with no shortcuts registered. */
1475 memset(shortcuts
, NO_SHORTCUT
, lenof(shortcuts
));
1478 /* Almost all controls start at base_id. */
1479 actual_base_id
= base_id
;
1482 * Now we're ready to actually create the control, by
1483 * switching on its type.
1485 switch (ctrl
->generic
.type
) {
1488 char *wrapped
, *escaped
;
1491 wrapped
= staticwrap(&pos
, cp
->hwnd
,
1492 ctrl
->generic
.label
, &lines
);
1493 escaped
= shortcut_escape(wrapped
, NO_SHORTCUT
);
1494 statictext(&pos
, escaped
, lines
, base_id
);
1500 num_ids
= 2; /* static, edit */
1501 escaped
= shortcut_escape(ctrl
->editbox
.label
,
1502 ctrl
->editbox
.shortcut
);
1503 shortcuts
[nshortcuts
++] = ctrl
->editbox
.shortcut
;
1504 if (ctrl
->editbox
.percentwidth
== 100) {
1505 if (ctrl
->editbox
.has_list
)
1506 combobox(&pos
, escaped
,
1507 base_id
, base_id
+1);
1509 editboxfw(&pos
, ctrl
->editbox
.password
, escaped
,
1510 base_id
, base_id
+1);
1512 if (ctrl
->editbox
.has_list
) {
1513 staticcombo(&pos
, escaped
, base_id
, base_id
+1,
1514 ctrl
->editbox
.percentwidth
);
1516 (ctrl
->editbox
.password ? staticpassedit
: staticedit
)
1517 (&pos
, escaped
, base_id
, base_id
+1,
1518 ctrl
->editbox
.percentwidth
);
1524 num_ids
= ctrl
->radio
.nbuttons
+ 1; /* label as well */
1526 struct radio
*buttons
;
1529 escaped
= shortcut_escape(ctrl
->radio
.label
,
1530 ctrl
->radio
.shortcut
);
1531 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcut
;
1533 buttons
= snewn(ctrl
->radio
.nbuttons
, struct radio
);
1535 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1537 shortcut_escape(ctrl
->radio
.buttons
[i
],
1538 (char)(ctrl
->radio
.shortcuts ?
1539 ctrl
->radio
.shortcuts
[i
] :
1541 buttons
[i
].id
= base_id
+ 1 + i
;
1542 if (ctrl
->radio
.shortcuts
) {
1543 assert(nshortcuts
< MAX_SHORTCUTS_PER_CTRL
);
1544 shortcuts
[nshortcuts
++] = ctrl
->radio
.shortcuts
[i
];
1548 radioline_common(&pos
, escaped
, base_id
,
1549 ctrl
->radio
.ncolumns
,
1550 buttons
, ctrl
->radio
.nbuttons
);
1552 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1553 sfree(buttons
[i
].text
);
1561 escaped
= shortcut_escape(ctrl
->checkbox
.label
,
1562 ctrl
->checkbox
.shortcut
);
1563 shortcuts
[nshortcuts
++] = ctrl
->checkbox
.shortcut
;
1564 checkbox(&pos
, escaped
, base_id
);
1568 escaped
= shortcut_escape(ctrl
->button
.label
,
1569 ctrl
->button
.shortcut
);
1570 shortcuts
[nshortcuts
++] = ctrl
->button
.shortcut
;
1571 if (ctrl
->button
.iscancel
)
1572 actual_base_id
= IDCANCEL
;
1574 button(&pos
, escaped
, actual_base_id
, ctrl
->button
.isdefault
);
1579 escaped
= shortcut_escape(ctrl
->listbox
.label
,
1580 ctrl
->listbox
.shortcut
);
1581 shortcuts
[nshortcuts
++] = ctrl
->listbox
.shortcut
;
1582 if (ctrl
->listbox
.draglist
) {
1583 data
= snew(struct prefslist
);
1585 prefslist(data
, &pos
, ctrl
->listbox
.height
, escaped
,
1586 base_id
, base_id
+1, base_id
+2, base_id
+3);
1587 shortcuts
[nshortcuts
++] = 'u'; /* Up */
1588 shortcuts
[nshortcuts
++] = 'd'; /* Down */
1589 } else if (ctrl
->listbox
.height
== 0) {
1590 /* Drop-down list. */
1591 if (ctrl
->listbox
.percentwidth
== 100) {
1592 staticddlbig(&pos
, escaped
,
1593 base_id
, base_id
+1);
1595 staticddl(&pos
, escaped
, base_id
,
1596 base_id
+1, ctrl
->listbox
.percentwidth
);
1599 /* Ordinary list. */
1600 listbox(&pos
, escaped
, base_id
, base_id
+1,
1601 ctrl
->listbox
.height
, ctrl
->listbox
.multisel
);
1603 if (ctrl
->listbox
.ncols
) {
1605 * This method of getting the box width is a bit of
1606 * a hack; we'd do better to try to retrieve the
1607 * actual width in dialog units from doctl() just
1608 * before MapDialogRect. But that's going to be no
1609 * fun, and this should be good enough accuracy.
1611 int width
= cp
->width
* ctrl
->listbox
.percentwidth
;
1615 tabarray
= snewn(ctrl
->listbox
.ncols
-1, int);
1617 for (i
= 0; i
< ctrl
->listbox
.ncols
-1; i
++) {
1618 percent
+= ctrl
->listbox
.percentages
[i
];
1619 tabarray
[i
] = width
* percent
/ 10000;
1621 SendDlgItemMessage(cp
->hwnd
, base_id
+1, LB_SETTABSTOPS
,
1622 ctrl
->listbox
.ncols
-1, (LPARAM
)tabarray
);
1627 case CTRL_FILESELECT
:
1629 escaped
= shortcut_escape(ctrl
->fileselect
.label
,
1630 ctrl
->fileselect
.shortcut
);
1631 shortcuts
[nshortcuts
++] = ctrl
->fileselect
.shortcut
;
1632 editbutton(&pos
, escaped
, base_id
, base_id
+1,
1633 "Bro&wse...", base_id
+2);
1634 shortcuts
[nshortcuts
++] = 'w';
1637 case CTRL_FONTSELECT
:
1639 escaped
= shortcut_escape(ctrl
->fontselect
.label
,
1640 ctrl
->fontselect
.shortcut
);
1641 shortcuts
[nshortcuts
++] = ctrl
->fontselect
.shortcut
;
1642 statictext(&pos
, escaped
, 1, base_id
);
1643 staticbtn(&pos
, "", base_id
+1, "Change...", base_id
+2);
1644 data
= fontspec_new("", 0, 0, 0);
1648 assert(!"Can't happen");
1649 num_ids
= 0; /* placate gcc */
1654 * Create a `struct winctrl' for this control, and advance
1655 * the dialog ID counter, if it's actually been created
1656 * (and isn't tabdelayed).
1659 struct winctrl
*c
= snew(struct winctrl
);
1662 c
->base_id
= actual_base_id
;
1663 c
->num_ids
= num_ids
;
1665 memcpy(c
->shortcuts
, shortcuts
, sizeof(shortcuts
));
1667 winctrl_add_shortcuts(dp
, c
);
1668 if (actual_base_id
== base_id
)
1674 if (colstart
>= 0) {
1676 * Update the ypos in all columns crossed by this
1680 for (i
= colstart
; i
< colstart
+colspan
; i
++)
1681 columns
[i
].ypos
= pos
.ypos
;
1686 * We've now finished laying out the controls; so now update
1687 * the ctlpos and control ID that were passed in, terminate
1688 * any containing box, and return.
1690 for (i
= 0; i
< ncols
; i
++)
1691 if (cp
->ypos
< columns
[i
].ypos
)
1692 cp
->ypos
= columns
[i
].ypos
;
1695 if (s
->boxname
&& *s
->boxname
)
1699 static void winctrl_set_focus(union control
*ctrl
, struct dlgparam
*dp
,
1704 dp
->lastfocused
= dp
->focused
;
1706 } else if (!has_focus
&& dp
->focused
== ctrl
) {
1707 dp
->lastfocused
= dp
->focused
;
1712 union control
*dlg_last_focused(union control
*ctrl
, void *dlg
)
1714 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
1715 return dp
->focused
== ctrl ? dp
->lastfocused
: dp
->focused
;
1719 * The dialog-box procedure calls this function to handle Windows
1720 * messages on a control we manage.
1722 int winctrl_handle_command(struct dlgparam
*dp
, UINT msg
,
1723 WPARAM wParam
, LPARAM lParam
)
1726 union control
*ctrl
;
1728 static UINT draglistmsg
= WM_NULL
;
1731 * Filter out pointless window messages. Our interest is in
1732 * WM_COMMAND and the drag list message, and nothing else.
1734 if (draglistmsg
== WM_NULL
)
1735 draglistmsg
= RegisterWindowMessage (DRAGLISTMSGSTRING
);
1737 if (msg
!= draglistmsg
&& msg
!= WM_COMMAND
&& msg
!= WM_DRAWITEM
)
1741 * Look up the control ID in our data.
1744 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
1745 c
= winctrl_findbyid(dp
->controltrees
[i
], LOWORD(wParam
));
1750 return 0; /* we have nothing to do */
1752 if (msg
== WM_DRAWITEM
) {
1754 * Owner-draw request for a panel title.
1756 LPDRAWITEMSTRUCT di
= (LPDRAWITEMSTRUCT
) lParam
;
1758 RECT r
= di
->rcItem
;
1761 SetMapMode(hdc
, MM_TEXT
); /* ensure logical units == pixels */
1763 GetTextExtentPoint32(hdc
, (char *)c
->data
,
1764 strlen((char *)c
->data
), &s
);
1765 DrawEdge(hdc
, &r
, EDGE_ETCHED
, BF_ADJUST
| BF_RECT
);
1767 r
.left
+ (r
.right
-r
.left
-s
.cx
)/2,
1768 r
.top
+ (r
.bottom
-r
.top
-s
.cy
)/2,
1769 (char *)c
->data
, strlen((char *)c
->data
));
1775 id
= LOWORD(wParam
) - c
->base_id
;
1777 if (!ctrl
|| !ctrl
->generic
.handler
)
1778 return 0; /* nothing we can do here */
1781 * From here on we do not issue `return' statements until the
1782 * very end of the dialog box: any event handler is entitled to
1783 * ask for a colour selector, so we _must_ always allow control
1784 * to reach the end of this switch statement so that the
1785 * subsequent code can test dp->coloursel_wanted().
1788 dp
->coloursel_wanted
= FALSE
;
1791 * Now switch on the control type and the message.
1793 switch (ctrl
->generic
.type
) {
1795 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1796 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1797 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1798 if (msg
== WM_COMMAND
&& ctrl
->editbox
.has_list
&&
1799 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1800 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1802 if (msg
== WM_COMMAND
&& !ctrl
->editbox
.has_list
&&
1803 HIWORD(wParam
) == EN_CHANGE
)
1804 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1805 if (msg
== WM_COMMAND
&&
1806 ctrl
->editbox
.has_list
) {
1807 if (HIWORD(wParam
) == CBN_SELCHANGE
) {
1811 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1812 CB_GETCURSEL
, 0, 0);
1813 len
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1,
1814 CB_GETLBTEXTLEN
, index
, 0);
1815 text
= snewn(len
+1, char);
1816 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, CB_GETLBTEXT
,
1817 index
, (LPARAM
)text
);
1818 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
1820 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1821 } else if (HIWORD(wParam
) == CBN_EDITCHANGE
) {
1822 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1823 } else if (HIWORD(wParam
) == CBN_KILLFOCUS
) {
1824 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
1830 if (msg
== WM_COMMAND
&&
1831 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1832 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1834 * We sometimes get spurious BN_CLICKED messages for the
1835 * radio button that is just about to _lose_ selection, if
1836 * we're switching using the arrow keys. Therefore we
1837 * double-check that the button in wParam is actually
1838 * checked before generating an event.
1840 if (msg
== WM_COMMAND
&&
1841 (HIWORD(wParam
) == BN_CLICKED
||
1842 HIWORD(wParam
) == BN_DOUBLECLICKED
) &&
1843 IsDlgButtonChecked(dp
->hwnd
, LOWORD(wParam
))) {
1844 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1848 if (msg
== WM_COMMAND
&&
1849 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1850 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1851 if (msg
== WM_COMMAND
&&
1852 (HIWORD(wParam
) == BN_CLICKED
||
1853 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1854 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1858 if (msg
== WM_COMMAND
&&
1859 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1860 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1861 if (msg
== WM_COMMAND
&&
1862 (HIWORD(wParam
) == BN_CLICKED
||
1863 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
1864 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1868 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
!= 0 &&
1869 (HIWORD(wParam
)==LBN_SETFOCUS
|| HIWORD(wParam
)==LBN_KILLFOCUS
))
1870 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == LBN_SETFOCUS
);
1871 if (msg
== WM_COMMAND
&& ctrl
->listbox
.height
== 0 &&
1872 (HIWORD(wParam
)==CBN_SETFOCUS
|| HIWORD(wParam
)==CBN_KILLFOCUS
))
1873 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == CBN_SETFOCUS
);
1874 if (msg
== WM_COMMAND
&& id
>= 2 &&
1875 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1876 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1877 if (ctrl
->listbox
.draglist
) {
1879 pret
= handle_prefslist(c
->data
, NULL
, 0, (msg
!= WM_COMMAND
),
1880 dp
->hwnd
, wParam
, lParam
);
1882 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1885 if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_DBLCLK
) {
1886 SetCapture(dp
->hwnd
);
1887 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1888 } else if (msg
== WM_COMMAND
&& HIWORD(wParam
) == LBN_SELCHANGE
) {
1889 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1893 case CTRL_FILESELECT
:
1894 if (msg
== WM_COMMAND
&& id
== 1 &&
1895 (HIWORD(wParam
) == EN_SETFOCUS
|| HIWORD(wParam
) == EN_KILLFOCUS
))
1896 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == EN_SETFOCUS
);
1897 if (msg
== WM_COMMAND
&& id
== 2 &&
1898 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1899 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1900 if (msg
== WM_COMMAND
&& id
== 1 && HIWORD(wParam
) == EN_CHANGE
)
1901 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1903 (msg
== WM_COMMAND
&&
1904 (HIWORD(wParam
) == BN_CLICKED
||
1905 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1907 char filename
[FILENAME_MAX
];
1909 memset(&of
, 0, sizeof(of
));
1910 of
.hwndOwner
= dp
->hwnd
;
1911 if (ctrl
->fileselect
.filter
)
1912 of
.lpstrFilter
= ctrl
->fileselect
.filter
;
1914 of
.lpstrFilter
= "All Files (*.*)\0*\0\0\0";
1915 of
.lpstrCustomFilter
= NULL
;
1916 of
.nFilterIndex
= 1;
1917 of
.lpstrFile
= filename
;
1918 GetDlgItemText(dp
->hwnd
, c
->base_id
+1, filename
, lenof(filename
));
1919 filename
[lenof(filename
)-1] = '\0';
1920 of
.nMaxFile
= lenof(filename
);
1921 of
.lpstrFileTitle
= NULL
;
1922 of
.lpstrTitle
= ctrl
->fileselect
.title
;
1924 if (request_file(NULL
, &of
, FALSE
, ctrl
->fileselect
.for_writing
)) {
1925 SetDlgItemText(dp
->hwnd
, c
->base_id
+ 1, filename
);
1926 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1930 case CTRL_FONTSELECT
:
1931 if (msg
== WM_COMMAND
&& id
== 2 &&
1932 (HIWORD(wParam
) == BN_SETFOCUS
|| HIWORD(wParam
) == BN_KILLFOCUS
))
1933 winctrl_set_focus(ctrl
, dp
, HIWORD(wParam
) == BN_SETFOCUS
);
1935 (msg
== WM_COMMAND
&&
1936 (HIWORD(wParam
) == BN_CLICKED
||
1937 HIWORD(wParam
) == BN_DOUBLECLICKED
))) {
1941 FontSpec
*fs
= (FontSpec
*)c
->data
;
1944 lf
.lfHeight
= -MulDiv(fs
->height
,
1945 GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1947 lf
.lfWidth
= lf
.lfEscapement
= lf
.lfOrientation
= 0;
1948 lf
.lfItalic
= lf
.lfUnderline
= lf
.lfStrikeOut
= 0;
1949 lf
.lfWeight
= (fs
->isbold ? FW_BOLD
: 0);
1950 lf
.lfCharSet
= fs
->charset
;
1951 lf
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
1952 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1953 lf
.lfQuality
= DEFAULT_QUALITY
;
1954 lf
.lfPitchAndFamily
= FIXED_PITCH
| FF_DONTCARE
;
1955 strncpy(lf
.lfFaceName
, fs
->name
,
1956 sizeof(lf
.lfFaceName
) - 1);
1957 lf
.lfFaceName
[sizeof(lf
.lfFaceName
) - 1] = '\0';
1959 cf
.lStructSize
= sizeof(cf
);
1960 cf
.hwndOwner
= dp
->hwnd
;
1962 cf
.Flags
= (dp
->fixed_pitch_fonts ? CF_FIXEDPITCHONLY
: 0) |
1963 CF_FORCEFONTEXIST
| CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
;
1965 if (ChooseFont(&cf
)) {
1966 fs
= fontspec_new(lf
.lfFaceName
, (lf
.lfWeight
== FW_BOLD
),
1967 cf
.iPointSize
/ 10, lf
.lfCharSet
);
1968 dlg_fontsel_set(ctrl
, dp
, fs
);
1971 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1978 * If the above event handler has asked for a colour selector,
1979 * now is the time to generate one.
1981 if (dp
->coloursel_wanted
) {
1982 static CHOOSECOLOR cc
;
1983 static DWORD custom
[16] = { 0 }; /* zero initialisers */
1984 cc
.lStructSize
= sizeof(cc
);
1985 cc
.hwndOwner
= dp
->hwnd
;
1986 cc
.hInstance
= (HWND
) hinst
;
1987 cc
.lpCustColors
= custom
;
1988 cc
.rgbResult
= RGB(dp
->coloursel_result
.r
,
1989 dp
->coloursel_result
.g
,
1990 dp
->coloursel_result
.b
);
1991 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
;
1992 if (ChooseColor(&cc
)) {
1993 dp
->coloursel_result
.r
=
1994 (unsigned char) (cc
.rgbResult
& 0xFF);
1995 dp
->coloursel_result
.g
=
1996 (unsigned char) (cc
.rgbResult
>> 8) & 0xFF;
1997 dp
->coloursel_result
.b
=
1998 (unsigned char) (cc
.rgbResult
>> 16) & 0xFF;
1999 dp
->coloursel_result
.ok
= TRUE
;
2001 dp
->coloursel_result
.ok
= FALSE
;
2002 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
2009 * This function can be called to produce context help on a
2010 * control. Returns TRUE if it has actually launched some help.
2012 int winctrl_context_help(struct dlgparam
*dp
, HWND hwnd
, int id
)
2018 * Look up the control ID in our data.
2021 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2022 c
= winctrl_findbyid(dp
->controltrees
[i
], id
);
2027 return 0; /* we have nothing to do */
2030 * This is the Windows front end, so we're allowed to assume
2031 * `helpctx.p' is a context string.
2033 if (!c
->ctrl
|| !c
->ctrl
->generic
.helpctx
.p
)
2034 return 0; /* no help available for this ctrl */
2036 launch_help(hwnd
, c
->ctrl
->generic
.helpctx
.p
);
2041 * Now the various functions that the platform-independent
2042 * mechanism can call to access the dialog box entries.
2045 static struct winctrl
*dlg_findbyctrl(struct dlgparam
*dp
, union control
*ctrl
)
2049 for (i
= 0; i
< dp
->nctrltrees
; i
++) {
2050 struct winctrl
*c
= winctrl_findbyctrl(dp
->controltrees
[i
], ctrl
);
2057 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int whichbutton
)
2059 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2060 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2061 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2062 CheckRadioButton(dp
->hwnd
,
2064 c
->base_id
+ c
->ctrl
->radio
.nbuttons
,
2065 c
->base_id
+ 1 + whichbutton
);
2068 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
2070 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2071 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2073 assert(c
&& c
->ctrl
->generic
.type
== CTRL_RADIO
);
2074 for (i
= 0; i
< c
->ctrl
->radio
.nbuttons
; i
++)
2075 if (IsDlgButtonChecked(dp
->hwnd
, c
->base_id
+ 1 + i
))
2077 assert(!"No radio button was checked?!");
2081 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
2083 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2084 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2085 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2086 CheckDlgButton(dp
->hwnd
, c
->base_id
, (checked
!= 0));
2089 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
2091 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2092 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2093 assert(c
&& c
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
2094 return 0 != IsDlgButtonChecked(dp
->hwnd
, c
->base_id
);
2097 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
2099 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2100 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2101 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2102 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, text
);
2105 char *dlg_editbox_get(union control
*ctrl
, void *dlg
)
2107 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2108 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2109 assert(c
&& c
->ctrl
->generic
.type
== CTRL_EDITBOX
);
2110 return GetDlgItemText_alloc(dp
->hwnd
, c
->base_id
+1);
2113 /* The `listbox' functions can also apply to combo boxes. */
2114 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
2116 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2117 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2120 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2121 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2122 c
->ctrl
->editbox
.has_list
)));
2123 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2124 LB_RESETCONTENT
: CB_RESETCONTENT
);
2125 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2128 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
2130 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2131 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2134 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2135 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2136 c
->ctrl
->editbox
.has_list
)));
2137 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2138 LB_DELETESTRING
: CB_DELETESTRING
);
2139 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2142 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
2144 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2145 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2148 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2149 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2150 c
->ctrl
->editbox
.has_list
)));
2151 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2152 LB_ADDSTRING
: CB_ADDSTRING
);
2153 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2157 * Each listbox entry may have a numeric id associated with it.
2158 * Note that some front ends only permit a string to be stored at
2159 * each position, which means that _if_ you put two identical
2160 * strings in any listbox then you MUST not assign them different
2161 * IDs and expect to get meaningful results back.
2163 void dlg_listbox_addwithid(union control
*ctrl
, void *dlg
,
2164 char const *text
, int id
)
2166 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2167 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2168 int msg
, msg2
, index
;
2170 (c
->ctrl
->generic
.type
== CTRL_LISTBOX
||
2171 (c
->ctrl
->generic
.type
== CTRL_EDITBOX
&&
2172 c
->ctrl
->editbox
.has_list
)));
2173 msg
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2174 LB_ADDSTRING
: CB_ADDSTRING
);
2175 msg2
= (c
->ctrl
->generic
.type
==CTRL_LISTBOX
&& c
->ctrl
->listbox
.height
!=0 ?
2176 LB_SETITEMDATA
: CB_SETITEMDATA
);
2177 index
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, (LPARAM
)text
);
2178 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg2
, index
, (LPARAM
)id
);
2181 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
2183 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2184 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2186 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
);
2187 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETITEMDATA
: CB_GETITEMDATA
);
2189 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2192 /* dlg_listbox_index returns <0 if no single element is selected. */
2193 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
2195 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2196 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2198 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
);
2199 if (c
->ctrl
->listbox
.multisel
) {
2200 assert(c
->ctrl
->listbox
.height
!= 0); /* not combo box */
2201 ret
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, LB_GETSELCOUNT
, 0, 0);
2202 if (ret
== LB_ERR
|| ret
> 1)
2205 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_GETCURSEL
: CB_GETCURSEL
);
2206 ret
= SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, 0, 0);
2213 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
2215 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2216 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2217 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2218 c
->ctrl
->listbox
.multisel
&&
2219 c
->ctrl
->listbox
.height
!= 0);
2221 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, LB_GETSEL
, index
, 0);
2224 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
2226 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2227 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2229 assert(c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
&&
2230 !c
->ctrl
->listbox
.multisel
);
2231 msg
= (c
->ctrl
->listbox
.height
!= 0 ? LB_SETCURSEL
: CB_SETCURSEL
);
2232 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, msg
, index
, 0);
2235 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
2237 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2238 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2239 assert(c
&& c
->ctrl
->generic
.type
== CTRL_TEXT
);
2240 SetDlgItemText(dp
->hwnd
, c
->base_id
, text
);
2243 void dlg_label_change(union control
*ctrl
, void *dlg
, char const *text
)
2245 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2246 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2247 char *escaped
= NULL
;
2251 switch (c
->ctrl
->generic
.type
) {
2253 escaped
= shortcut_escape(text
, c
->ctrl
->editbox
.shortcut
);
2257 escaped
= shortcut_escape(text
, c
->ctrl
->radio
.shortcut
);
2261 escaped
= shortcut_escape(text
, ctrl
->checkbox
.shortcut
);
2265 escaped
= shortcut_escape(text
, ctrl
->button
.shortcut
);
2269 escaped
= shortcut_escape(text
, ctrl
->listbox
.shortcut
);
2272 case CTRL_FILESELECT
:
2273 escaped
= shortcut_escape(text
, ctrl
->fileselect
.shortcut
);
2276 case CTRL_FONTSELECT
:
2277 escaped
= shortcut_escape(text
, ctrl
->fontselect
.shortcut
);
2281 assert(!"Can't happen");
2285 SetDlgItemText(dp
->hwnd
, id
, escaped
);
2290 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename
*fn
)
2292 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2293 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2294 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2295 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, fn
->path
);
2298 Filename
*dlg_filesel_get(union control
*ctrl
, void *dlg
)
2300 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2301 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2304 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FILESELECT
);
2305 tmp
= GetDlgItemText_alloc(dp
->hwnd
, c
->base_id
+1);
2306 ret
= filename_from_str(tmp
);
2311 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
2313 char *buf
, *boldstr
;
2314 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2315 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2316 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2318 fontspec_free((FontSpec
*)c
->data
);
2319 c
->data
= fontspec_copy(fs
);
2321 boldstr
= (fs
->isbold ?
"bold, " : "");
2322 if (fs
->height
== 0)
2323 buf
= dupprintf("Font: %s, %sdefault height", fs
->name
, boldstr
);
2325 buf
= dupprintf("Font: %s, %s%d-%s", fs
->name
, boldstr
,
2326 (fs
->height
< 0 ?
-fs
->height
: fs
->height
),
2327 (fs
->height
< 0 ?
"pixel" : "point"));
2328 SetDlgItemText(dp
->hwnd
, c
->base_id
+1, buf
);
2331 dlg_auto_set_fixed_pitch_flag(dp
);
2334 FontSpec
*dlg_fontsel_get(union control
*ctrl
, void *dlg
)
2336 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2337 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2338 assert(c
&& c
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
2339 return fontspec_copy((FontSpec
*)c
->data
);
2343 * Bracketing a large set of updates in these two functions will
2344 * cause the front end (if possible) to delay updating the screen
2345 * until it's all complete, thus avoiding flicker.
2347 void dlg_update_start(union control
*ctrl
, void *dlg
)
2349 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2350 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2351 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2352 SendDlgItemMessage(dp
->hwnd
, c
->base_id
+1, WM_SETREDRAW
, FALSE
, 0);
2356 void dlg_update_done(union control
*ctrl
, void *dlg
)
2358 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2359 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2360 if (c
&& c
->ctrl
->generic
.type
== CTRL_LISTBOX
) {
2361 HWND hw
= GetDlgItem(dp
->hwnd
, c
->base_id
+1);
2362 SendMessage(hw
, WM_SETREDRAW
, TRUE
, 0);
2363 InvalidateRect(hw
, NULL
, TRUE
);
2367 void dlg_set_focus(union control
*ctrl
, void *dlg
)
2369 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2370 struct winctrl
*c
= dlg_findbyctrl(dp
, ctrl
);
2375 switch (ctrl
->generic
.type
) {
2376 case CTRL_EDITBOX
: id
= c
->base_id
+ 1; break;
2378 for (id
= c
->base_id
+ ctrl
->radio
.nbuttons
; id
> 1; id
--)
2379 if (IsDlgButtonChecked(dp
->hwnd
, id
))
2382 * In the theoretically-unlikely case that no button was
2383 * selected, id should come out of this as 1, which is a
2384 * reasonable enough choice.
2387 case CTRL_CHECKBOX
: id
= c
->base_id
; break;
2388 case CTRL_BUTTON
: id
= c
->base_id
; break;
2389 case CTRL_LISTBOX
: id
= c
->base_id
+ 1; break;
2390 case CTRL_FILESELECT
: id
= c
->base_id
+ 1; break;
2391 case CTRL_FONTSELECT
: id
= c
->base_id
+ 2; break;
2392 default: id
= c
->base_id
; break;
2394 ctl
= GetDlgItem(dp
->hwnd
, id
);
2399 * During event processing, you might well want to give an error
2400 * indication to the user. dlg_beep() is a quick and easy generic
2401 * error; dlg_error() puts up a message-box or equivalent.
2403 void dlg_beep(void *dlg
)
2405 /* struct dlgparam *dp = (struct dlgparam *)dlg; */
2409 void dlg_error_msg(void *dlg
, char *msg
)
2411 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2412 MessageBox(dp
->hwnd
, msg
,
2413 dp
->errtitle ? dp
->errtitle
: NULL
,
2414 MB_OK
| MB_ICONERROR
);
2418 * This function signals to the front end that the dialog's
2419 * processing is completed, and passes an integer value (typically
2420 * a success status).
2422 void dlg_end(void *dlg
, int value
)
2424 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2426 dp
->endresult
= value
;
2429 void dlg_refresh(union control
*ctrl
, void *dlg
)
2431 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2437 * Send EVENT_REFRESH to absolutely everything.
2439 for (j
= 0; j
< dp
->nctrltrees
; j
++) {
2441 (c
= winctrl_findbyindex(dp
->controltrees
[j
], i
)) != NULL
;
2443 if (c
->ctrl
&& c
->ctrl
->generic
.handler
!= NULL
)
2444 c
->ctrl
->generic
.handler(c
->ctrl
, dp
,
2445 dp
->data
, EVENT_REFRESH
);
2450 * Send EVENT_REFRESH to a specific control.
2452 if (ctrl
->generic
.handler
!= NULL
)
2453 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
2457 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
2459 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2460 dp
->coloursel_wanted
= TRUE
;
2461 dp
->coloursel_result
.r
= r
;
2462 dp
->coloursel_result
.g
= g
;
2463 dp
->coloursel_result
.b
= b
;
2466 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
2467 int *r
, int *g
, int *b
)
2469 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2470 if (dp
->coloursel_result
.ok
) {
2471 *r
= dp
->coloursel_result
.r
;
2472 *g
= dp
->coloursel_result
.g
;
2473 *b
= dp
->coloursel_result
.b
;
2479 void dlg_auto_set_fixed_pitch_flag(void *dlg
)
2481 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2482 Conf
*conf
= (Conf
*)dp
->data
;
2491 * Attempt to load the current font, and see if it's
2492 * variable-pitch. If so, start off the fixed-pitch flag for the
2493 * dialog box as false.
2495 * We assume here that any client of the dlg_* mechanism which is
2496 * using font selectors at all is also using a normal 'Conf *'
2500 quality
= conf_get_int(conf
, CONF_font_quality
);
2501 fs
= conf_get_fontspec(conf
, CONF_font
);
2503 hfont
= CreateFont(0, 0, 0, 0, FW_DONTCARE
, FALSE
, FALSE
, FALSE
,
2504 DEFAULT_CHARSET
, OUT_DEFAULT_PRECIS
,
2505 CLIP_DEFAULT_PRECIS
, FONT_QUALITY(quality
),
2506 FIXED_PITCH
| FF_DONTCARE
, fs
->name
);
2508 if (hdc
&& SelectObject(hdc
, hfont
) && GetTextMetrics(hdc
, &tm
)) {
2509 /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */
2510 is_var
= (tm
.tmPitchAndFamily
& TMPF_FIXED_PITCH
);
2512 is_var
= FALSE
; /* assume it's basically normal */
2515 ReleaseDC(NULL
, hdc
);
2517 DeleteObject(hfont
);
2520 dp
->fixed_pitch_fonts
= FALSE
;
2523 int dlg_get_fixed_pitch_flag(void *dlg
)
2525 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2526 return dp
->fixed_pitch_fonts
;
2529 void dlg_set_fixed_pitch_flag(void *dlg
, int flag
)
2531 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2532 dp
->fixed_pitch_fonts
= flag
;
2535 struct perctrl_privdata
{
2536 union control
*ctrl
;
2541 static int perctrl_privdata_cmp(void *av
, void *bv
)
2543 struct perctrl_privdata
*a
= (struct perctrl_privdata
*)av
;
2544 struct perctrl_privdata
*b
= (struct perctrl_privdata
*)bv
;
2545 if (a
->ctrl
< b
->ctrl
)
2547 else if (a
->ctrl
> b
->ctrl
)
2552 void dp_init(struct dlgparam
*dp
)
2557 dp
->focused
= dp
->lastfocused
= NULL
;
2558 memset(dp
->shortcuts
, 0, sizeof(dp
->shortcuts
));
2560 dp
->wintitle
= dp
->errtitle
= NULL
;
2561 dp
->privdata
= newtree234(perctrl_privdata_cmp
);
2562 dp
->fixed_pitch_fonts
= TRUE
;
2565 void dp_add_tree(struct dlgparam
*dp
, struct winctrls
*wc
)
2567 assert(dp
->nctrltrees
< lenof(dp
->controltrees
));
2568 dp
->controltrees
[dp
->nctrltrees
++] = wc
;
2571 void dp_cleanup(struct dlgparam
*dp
)
2573 struct perctrl_privdata
*p
;
2576 while ( (p
= index234(dp
->privdata
, 0)) != NULL
) {
2577 del234(dp
->privdata
, p
);
2582 freetree234(dp
->privdata
);
2583 dp
->privdata
= NULL
;
2585 sfree(dp
->wintitle
);
2586 sfree(dp
->errtitle
);
2589 void *dlg_get_privdata(union control
*ctrl
, void *dlg
)
2591 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2592 struct perctrl_privdata tmp
, *p
;
2594 p
= find234(dp
->privdata
, &tmp
, NULL
);
2601 void dlg_set_privdata(union control
*ctrl
, void *dlg
, void *ptr
)
2603 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2604 struct perctrl_privdata tmp
, *p
;
2606 p
= find234(dp
->privdata
, &tmp
, NULL
);
2608 p
= snew(struct perctrl_privdata
);
2610 p
->needs_free
= FALSE
;
2611 add234(dp
->privdata
, p
);
2616 void *dlg_alloc_privdata(union control
*ctrl
, void *dlg
, size_t size
)
2618 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
2619 struct perctrl_privdata tmp
, *p
;
2621 p
= find234(dp
->privdata
, &tmp
, NULL
);
2623 p
= snew(struct perctrl_privdata
);
2625 p
->needs_free
= FALSE
;
2626 add234(dp
->privdata
, p
);
2628 assert(!p
->needs_free
);
2629 p
->needs_free
= TRUE
;
2631 * This is an internal allocation routine, so it's allowed to
2632 * use smalloc directly.
2634 p
->data
= smalloc(size
);