8c3cd914 |
1 | /* |
2 | * winctrls.c: routines to self-manage the controls in a dialog |
3 | * box. |
4 | */ |
5 | |
6 | #include <windows.h> |
6e522441 |
7 | #include <commctrl.h> |
8c3cd914 |
8 | |
9 | #include "winstuff.h" |
10 | |
11 | #define GAPBETWEEN 3 |
12 | #define GAPWITHIN 1 |
13 | #define GAPXBOX 7 |
14 | #define GAPYBOX 4 |
15 | #define DLGWIDTH 168 |
16 | #define STATICHEIGHT 8 |
17 | #define CHECKBOXHEIGHT 8 |
18 | #define RADIOHEIGHT 8 |
19 | #define EDITHEIGHT 12 |
20 | #define COMBOHEIGHT 12 |
21 | #define PUSHBTNHEIGHT 14 |
6e522441 |
22 | #define PROGBARHEIGHT 14 |
8c3cd914 |
23 | |
24 | void ctlposinit(struct ctlpos *cp, HWND hwnd, |
32874aea |
25 | int leftborder, int rightborder, int topborder) |
26 | { |
8c3cd914 |
27 | RECT r, r2; |
28 | cp->hwnd = hwnd; |
29 | cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0); |
30 | cp->ypos = topborder; |
31 | GetClientRect(hwnd, &r); |
32 | r2.left = r2.top = 0; |
33 | r2.right = 4; |
34 | r2.bottom = 8; |
35 | MapDialogRect(hwnd, &r2); |
36 | cp->dlu4inpix = r2.right; |
32874aea |
37 | cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN; |
8c3cd914 |
38 | cp->xoff = leftborder; |
39 | cp->width -= leftborder + rightborder; |
40 | } |
41 | |
42 | void doctl(struct ctlpos *cp, RECT r, |
32874aea |
43 | char *wclass, int wstyle, int exstyle, char *wtext, int wid) |
44 | { |
8c3cd914 |
45 | HWND ctl; |
46 | /* |
47 | * Note nonstandard use of RECT. This is deliberate: by |
48 | * transforming the width and height directly we arrange to |
49 | * have all supposedly same-sized controls really same-sized. |
50 | */ |
51 | |
52 | r.left += cp->xoff; |
53 | MapDialogRect(cp->hwnd, &r); |
54 | |
55 | ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle, |
32874aea |
56 | r.left, r.top, r.right, r.bottom, |
57 | cp->hwnd, (HMENU) wid, hinst, NULL); |
8c3cd914 |
58 | SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0)); |
59 | } |
60 | |
61 | /* |
62 | * A title bar across the top of a sub-dialog. |
63 | */ |
32874aea |
64 | void bartitle(struct ctlpos *cp, char *name, int id) |
65 | { |
8c3cd914 |
66 | RECT r; |
67 | |
32874aea |
68 | r.left = GAPBETWEEN; |
69 | r.right = cp->width; |
70 | r.top = cp->ypos; |
71 | r.bottom = STATICHEIGHT; |
8c3cd914 |
72 | cp->ypos += r.bottom + GAPBETWEEN; |
73 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id); |
74 | } |
75 | |
76 | /* |
77 | * Begin a grouping box, with or without a group title. |
78 | */ |
32874aea |
79 | void beginbox(struct ctlpos *cp, char *name, int idbox) |
80 | { |
8c3cd914 |
81 | cp->boxystart = cp->ypos; |
3ac9cd9f |
82 | if (!name) |
32874aea |
83 | cp->boxystart -= STATICHEIGHT / 2; |
8c3cd914 |
84 | if (name) |
32874aea |
85 | cp->ypos += STATICHEIGHT; |
8c3cd914 |
86 | cp->ypos += GAPYBOX; |
32874aea |
87 | cp->width -= 2 * GAPXBOX; |
8c3cd914 |
88 | cp->xoff += GAPXBOX; |
89 | cp->boxid = idbox; |
8c3cd914 |
90 | cp->boxtext = name; |
91 | } |
92 | |
93 | /* |
94 | * End a grouping box. |
95 | */ |
32874aea |
96 | void endbox(struct ctlpos *cp) |
97 | { |
8c3cd914 |
98 | RECT r; |
99 | cp->xoff -= GAPXBOX; |
32874aea |
100 | cp->width += 2 * GAPXBOX; |
8c3cd914 |
101 | cp->ypos += GAPYBOX - GAPBETWEEN; |
32874aea |
102 | r.left = GAPBETWEEN; |
103 | r.right = cp->width; |
104 | r.top = cp->boxystart; |
105 | r.bottom = cp->ypos - cp->boxystart; |
3ac9cd9f |
106 | doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0, |
32874aea |
107 | cp->boxtext ? cp->boxtext : "", cp->boxid); |
8c3cd914 |
108 | cp->ypos += GAPYBOX; |
109 | } |
110 | |
111 | /* |
112 | * Some edit boxes. Each one has a static above it. The percentages |
113 | * of the horizontal space are provided. |
114 | */ |
32874aea |
115 | void multiedit(struct ctlpos *cp, ...) |
116 | { |
8c3cd914 |
117 | RECT r; |
118 | va_list ap; |
119 | int percent, xpos; |
120 | |
121 | percent = xpos = 0; |
122 | va_start(ap, cp); |
123 | while (1) { |
32874aea |
124 | char *text; |
125 | int staticid, editid, pcwidth; |
126 | text = va_arg(ap, char *); |
127 | if (!text) |
128 | break; |
129 | staticid = va_arg(ap, int); |
130 | editid = va_arg(ap, int); |
131 | pcwidth = va_arg(ap, int); |
132 | |
133 | r.left = xpos + GAPBETWEEN; |
134 | percent += pcwidth; |
135 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
136 | r.right = xpos - r.left; |
137 | |
138 | r.top = cp->ypos; |
139 | r.bottom = STATICHEIGHT; |
140 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); |
141 | r.top = cp->ypos + 8 + GAPWITHIN; |
142 | r.bottom = EDITHEIGHT; |
143 | doctl(cp, r, "EDIT", |
144 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
145 | WS_EX_CLIENTEDGE, "", editid); |
8c3cd914 |
146 | } |
147 | va_end(ap); |
32874aea |
148 | cp->ypos += 8 + GAPWITHIN + 12 + GAPBETWEEN; |
8c3cd914 |
149 | } |
150 | |
151 | /* |
152 | * A set of radio buttons on the same line, with a static above |
153 | * them. `nacross' dictates how many parts the line is divided into |
154 | * (you might want this not to equal the number of buttons if you |
155 | * needed to line up some 2s and some 3s to look good in the same |
156 | * panel). |
1cd48051 |
157 | * |
158 | * There's a bit of a hack in here to ensure that if nacross |
159 | * exceeds the actual number of buttons, the rightmost button |
160 | * really does get all the space right to the edge of the line, so |
161 | * you can do things like |
162 | * |
163 | * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle |
8c3cd914 |
164 | */ |
32874aea |
165 | void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) |
166 | { |
8c3cd914 |
167 | RECT r; |
168 | va_list ap; |
169 | int group; |
170 | int i; |
1cd48051 |
171 | char *btext; |
8c3cd914 |
172 | |
32874aea |
173 | r.left = GAPBETWEEN; |
174 | r.top = cp->ypos; |
175 | r.right = cp->width; |
176 | r.bottom = STATICHEIGHT; |
8c3cd914 |
177 | cp->ypos += r.bottom + GAPWITHIN; |
178 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
179 | va_start(ap, nacross); |
180 | group = WS_GROUP; |
181 | i = 0; |
1cd48051 |
182 | btext = va_arg(ap, char *); |
8c3cd914 |
183 | while (1) { |
32874aea |
184 | char *nextbtext; |
185 | int bid; |
186 | if (!btext) |
187 | break; |
188 | if (i == nacross) { |
f37caa11 |
189 | cp->ypos += r.bottom + GAPBETWEEN; |
32874aea |
190 | i = 0; |
f37caa11 |
191 | } |
32874aea |
192 | bid = va_arg(ap, int); |
193 | nextbtext = va_arg(ap, char *); |
194 | r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross; |
195 | if (nextbtext) |
196 | r.right = |
197 | (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left; |
198 | else |
199 | r.right = cp->width - r.left; |
200 | r.top = cp->ypos; |
201 | r.bottom = RADIOHEIGHT; |
202 | doctl(cp, r, "BUTTON", |
203 | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
204 | group, 0, btext, bid); |
205 | group = 0; |
206 | i++; |
207 | btext = nextbtext; |
8c3cd914 |
208 | } |
209 | va_end(ap); |
210 | cp->ypos += r.bottom + GAPBETWEEN; |
211 | } |
212 | |
213 | /* |
214 | * A set of radio buttons on multiple lines, with a static above |
215 | * them. |
216 | */ |
32874aea |
217 | void radiobig(struct ctlpos *cp, char *text, int id, ...) |
218 | { |
8c3cd914 |
219 | RECT r; |
220 | va_list ap; |
221 | int group; |
222 | |
32874aea |
223 | r.left = GAPBETWEEN; |
224 | r.top = cp->ypos; |
225 | r.right = cp->width; |
226 | r.bottom = STATICHEIGHT; |
8c3cd914 |
227 | cp->ypos += r.bottom + GAPWITHIN; |
228 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
229 | va_start(ap, id); |
230 | group = WS_GROUP; |
231 | while (1) { |
32874aea |
232 | char *btext; |
233 | int bid; |
234 | btext = va_arg(ap, char *); |
235 | if (!btext) |
236 | break; |
237 | bid = va_arg(ap, int); |
238 | r.left = GAPBETWEEN; |
239 | r.top = cp->ypos; |
240 | r.right = cp->width; |
241 | r.bottom = STATICHEIGHT; |
242 | cp->ypos += r.bottom + GAPWITHIN; |
243 | doctl(cp, r, "BUTTON", |
244 | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
245 | group, 0, btext, bid); |
246 | group = 0; |
8c3cd914 |
247 | } |
248 | va_end(ap); |
249 | cp->ypos += GAPBETWEEN - GAPWITHIN; |
250 | } |
251 | |
252 | /* |
253 | * A single standalone checkbox. |
254 | */ |
32874aea |
255 | void checkbox(struct ctlpos *cp, char *text, int id) |
256 | { |
8c3cd914 |
257 | RECT r; |
258 | |
32874aea |
259 | r.left = GAPBETWEEN; |
260 | r.top = cp->ypos; |
261 | r.right = cp->width; |
262 | r.bottom = CHECKBOXHEIGHT; |
8c3cd914 |
263 | cp->ypos += r.bottom + GAPBETWEEN; |
264 | doctl(cp, r, "BUTTON", |
32874aea |
265 | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, |
266 | text, id); |
8c3cd914 |
267 | } |
268 | |
269 | /* |
6e522441 |
270 | * A single standalone static text control. |
271 | */ |
32874aea |
272 | void statictext(struct ctlpos *cp, char *text, int id) |
273 | { |
6e522441 |
274 | RECT r; |
275 | |
32874aea |
276 | r.left = GAPBETWEEN; |
277 | r.top = cp->ypos; |
278 | r.right = cp->width; |
279 | r.bottom = STATICHEIGHT; |
6e522441 |
280 | cp->ypos += r.bottom + GAPBETWEEN; |
281 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
282 | } |
283 | |
284 | /* |
8c3cd914 |
285 | * A button on the right hand side, with a static to its left. |
286 | */ |
287 | void staticbtn(struct ctlpos *cp, char *stext, int sid, |
32874aea |
288 | char *btext, int bid) |
289 | { |
8c3cd914 |
290 | const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? |
32874aea |
291 | PUSHBTNHEIGHT : STATICHEIGHT); |
8c3cd914 |
292 | RECT r; |
293 | int lwid, rwid, rpos; |
294 | |
295 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
296 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
297 | rwid = cp->width + GAPBETWEEN - rpos; |
298 | |
32874aea |
299 | r.left = GAPBETWEEN; |
300 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
301 | r.right = lwid; |
302 | r.bottom = STATICHEIGHT; |
8c3cd914 |
303 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
304 | |
32874aea |
305 | r.left = rpos; |
306 | r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; |
307 | r.right = rwid; |
308 | r.bottom = PUSHBTNHEIGHT; |
8c3cd914 |
309 | doctl(cp, r, "BUTTON", |
32874aea |
310 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
311 | 0, btext, bid); |
8c3cd914 |
312 | |
313 | cp->ypos += height + GAPBETWEEN; |
314 | } |
315 | |
316 | /* |
317 | * An edit control on the right hand side, with a static to its left. |
318 | */ |
6e522441 |
319 | static void staticedit_internal(struct ctlpos *cp, char *stext, |
32874aea |
320 | int sid, int eid, int percentedit, |
321 | int style) |
322 | { |
8c3cd914 |
323 | const int height = (EDITHEIGHT > STATICHEIGHT ? |
32874aea |
324 | EDITHEIGHT : STATICHEIGHT); |
8c3cd914 |
325 | RECT r; |
326 | int lwid, rwid, rpos; |
327 | |
32874aea |
328 | rpos = |
329 | GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100; |
330 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
331 | rwid = cp->width + GAPBETWEEN - rpos; |
332 | |
32874aea |
333 | r.left = GAPBETWEEN; |
334 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
335 | r.right = lwid; |
336 | r.bottom = STATICHEIGHT; |
8c3cd914 |
337 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
338 | |
32874aea |
339 | r.left = rpos; |
340 | r.top = cp->ypos + (height - EDITHEIGHT) / 2; |
341 | r.right = rwid; |
342 | r.bottom = EDITHEIGHT; |
8c3cd914 |
343 | doctl(cp, r, "EDIT", |
32874aea |
344 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style, |
345 | WS_EX_CLIENTEDGE, "", eid); |
8c3cd914 |
346 | |
347 | cp->ypos += height + GAPBETWEEN; |
348 | } |
349 | |
6e522441 |
350 | void staticedit(struct ctlpos *cp, char *stext, |
32874aea |
351 | int sid, int eid, int percentedit) |
352 | { |
6e522441 |
353 | staticedit_internal(cp, stext, sid, eid, percentedit, 0); |
354 | } |
355 | |
356 | void staticpassedit(struct ctlpos *cp, char *stext, |
32874aea |
357 | int sid, int eid, int percentedit) |
358 | { |
6e522441 |
359 | staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD); |
360 | } |
361 | |
362 | /* |
363 | * A big multiline edit control with a static labelling it. |
364 | */ |
365 | void bigeditctrl(struct ctlpos *cp, char *stext, |
32874aea |
366 | int sid, int eid, int lines) |
367 | { |
6e522441 |
368 | RECT r; |
369 | |
32874aea |
370 | r.left = GAPBETWEEN; |
371 | r.top = cp->ypos; |
372 | r.right = cp->width; |
373 | r.bottom = STATICHEIGHT; |
6e522441 |
374 | cp->ypos += r.bottom + GAPWITHIN; |
375 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
376 | |
32874aea |
377 | r.left = GAPBETWEEN; |
378 | r.top = cp->ypos; |
379 | r.right = cp->width; |
380 | r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT; |
6e522441 |
381 | cp->ypos += r.bottom + GAPBETWEEN; |
382 | doctl(cp, r, "EDIT", |
32874aea |
383 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE, |
384 | WS_EX_CLIENTEDGE, "", eid); |
6e522441 |
385 | } |
386 | |
8c3cd914 |
387 | /* |
388 | * A tab-control substitute when a real tab control is unavailable. |
389 | */ |
32874aea |
390 | void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) |
391 | { |
8c3cd914 |
392 | const int height = (COMBOHEIGHT > STATICHEIGHT ? |
32874aea |
393 | COMBOHEIGHT : STATICHEIGHT); |
8c3cd914 |
394 | RECT r; |
395 | int bigwid, lwid, rwid, rpos; |
396 | static const int BIGGAP = 15; |
397 | static const int MEDGAP = 3; |
398 | |
32874aea |
399 | bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP; |
8c3cd914 |
400 | cp->ypos += MEDGAP; |
401 | rpos = BIGGAP + (bigwid + BIGGAP) / 2; |
32874aea |
402 | lwid = rpos - 2 * BIGGAP; |
8c3cd914 |
403 | rwid = bigwid + BIGGAP - rpos; |
404 | |
32874aea |
405 | r.left = BIGGAP; |
406 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
407 | r.right = lwid; |
408 | r.bottom = STATICHEIGHT; |
8c3cd914 |
409 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
410 | |
32874aea |
411 | r.left = rpos; |
412 | r.top = cp->ypos + (height - COMBOHEIGHT) / 2; |
413 | r.right = rwid; |
414 | r.bottom = COMBOHEIGHT * 10; |
8c3cd914 |
415 | doctl(cp, r, "COMBOBOX", |
32874aea |
416 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
417 | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); |
8c3cd914 |
418 | |
419 | cp->ypos += height + MEDGAP + GAPBETWEEN; |
420 | |
32874aea |
421 | r.left = GAPBETWEEN; |
422 | r.top = cp->ypos; |
423 | r.right = cp->width; |
424 | r.bottom = 2; |
8c3cd914 |
425 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, |
32874aea |
426 | 0, "", s2id); |
8c3cd914 |
427 | } |
428 | |
429 | /* |
430 | * A static line, followed by an edit control on the left hand side |
431 | * and a button on the right. |
432 | */ |
433 | void editbutton(struct ctlpos *cp, char *stext, int sid, |
32874aea |
434 | int eid, char *btext, int bid) |
435 | { |
8c3cd914 |
436 | const int height = (EDITHEIGHT > PUSHBTNHEIGHT ? |
32874aea |
437 | EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
438 | RECT r; |
439 | int lwid, rwid, rpos; |
440 | |
32874aea |
441 | r.left = GAPBETWEEN; |
442 | r.top = cp->ypos; |
443 | r.right = cp->width; |
444 | r.bottom = STATICHEIGHT; |
8c3cd914 |
445 | cp->ypos += r.bottom + GAPWITHIN; |
446 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
447 | |
448 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
449 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
450 | rwid = cp->width + GAPBETWEEN - rpos; |
451 | |
32874aea |
452 | r.left = GAPBETWEEN; |
453 | r.top = cp->ypos + (height - EDITHEIGHT) / 2; |
454 | r.right = lwid; |
455 | r.bottom = EDITHEIGHT; |
8c3cd914 |
456 | doctl(cp, r, "EDIT", |
32874aea |
457 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
458 | WS_EX_CLIENTEDGE, "", eid); |
8c3cd914 |
459 | |
32874aea |
460 | r.left = rpos; |
461 | r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; |
462 | r.right = rwid; |
463 | r.bottom = PUSHBTNHEIGHT; |
8c3cd914 |
464 | doctl(cp, r, "BUTTON", |
32874aea |
465 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
466 | 0, btext, bid); |
8c3cd914 |
467 | |
468 | cp->ypos += height + GAPBETWEEN; |
469 | } |
470 | |
471 | /* |
472 | * Special control which was hard to describe generically: the |
473 | * session-saver assembly. A static; below that an edit box; below |
474 | * that a list box. To the right of the list box, a column of |
475 | * buttons. |
476 | */ |
477 | void sesssaver(struct ctlpos *cp, char *text, |
32874aea |
478 | int staticid, int editid, int listid, ...) |
479 | { |
8c3cd914 |
480 | RECT r; |
481 | va_list ap; |
482 | int lwid, rwid, rpos; |
483 | int y; |
484 | const int LISTDEFHEIGHT = 66; |
485 | |
486 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
487 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
488 | rwid = cp->width + GAPBETWEEN - rpos; |
489 | |
490 | /* The static control. */ |
32874aea |
491 | r.left = GAPBETWEEN; |
492 | r.top = cp->ypos; |
493 | r.right = lwid; |
494 | r.bottom = STATICHEIGHT; |
8c3cd914 |
495 | cp->ypos += r.bottom + GAPWITHIN; |
496 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); |
497 | |
498 | /* The edit control. */ |
32874aea |
499 | r.left = GAPBETWEEN; |
500 | r.top = cp->ypos; |
501 | r.right = lwid; |
502 | r.bottom = EDITHEIGHT; |
8c3cd914 |
503 | cp->ypos += r.bottom + GAPWITHIN; |
504 | doctl(cp, r, "EDIT", |
32874aea |
505 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
506 | WS_EX_CLIENTEDGE, "", editid); |
8c3cd914 |
507 | |
508 | /* |
509 | * The buttons (we should hold off on the list box until we |
510 | * know how big the buttons are). |
511 | */ |
512 | va_start(ap, listid); |
513 | y = cp->ypos; |
514 | while (1) { |
32874aea |
515 | char *btext = va_arg(ap, char *); |
516 | int bid; |
517 | if (!btext) |
518 | break; |
519 | bid = va_arg(ap, int); |
520 | r.left = rpos; |
521 | r.top = y; |
522 | r.right = rwid; |
523 | r.bottom = PUSHBTNHEIGHT; |
524 | y += r.bottom + GAPWITHIN; |
525 | doctl(cp, r, "BUTTON", |
526 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
527 | 0, btext, bid); |
8c3cd914 |
528 | } |
529 | |
530 | /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */ |
531 | y -= cp->ypos; |
532 | y -= GAPWITHIN; |
32874aea |
533 | if (y < LISTDEFHEIGHT) |
534 | y = LISTDEFHEIGHT; |
535 | r.left = GAPBETWEEN; |
536 | r.top = cp->ypos; |
537 | r.right = lwid; |
538 | r.bottom = y; |
8c3cd914 |
539 | cp->ypos += y + GAPBETWEEN; |
540 | doctl(cp, r, "LISTBOX", |
32874aea |
541 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | |
542 | LBS_NOTIFY | LBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
543 | } |
544 | |
545 | /* |
546 | * Another special control: the environment-variable setter. A |
547 | * static line first; then a pair of edit boxes with associated |
548 | * statics, and two buttons; then a list box. |
549 | */ |
550 | void envsetter(struct ctlpos *cp, char *stext, int sid, |
32874aea |
551 | char *e1stext, int e1sid, int e1id, |
552 | char *e2stext, int e2sid, int e2id, |
553 | int listid, char *b1text, int b1id, char *b2text, int b2id) |
554 | { |
8c3cd914 |
555 | RECT r; |
32874aea |
556 | const int height = (STATICHEIGHT > EDITHEIGHT |
557 | && STATICHEIGHT > |
558 | PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT > |
559 | PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
560 | const static int percents[] = { 20, 35, 10, 25 }; |
561 | int i, j, xpos, percent; |
562 | const int LISTHEIGHT = 42; |
563 | |
564 | /* The static control. */ |
32874aea |
565 | r.left = GAPBETWEEN; |
566 | r.top = cp->ypos; |
567 | r.right = cp->width; |
568 | r.bottom = STATICHEIGHT; |
8c3cd914 |
569 | cp->ypos += r.bottom + GAPWITHIN; |
570 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
571 | |
572 | /* The statics+edits+buttons. */ |
573 | for (j = 0; j < 2; j++) { |
32874aea |
574 | percent = 10; |
575 | for (i = 0; i < 4; i++) { |
576 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
577 | r.left = xpos + GAPBETWEEN; |
578 | percent += percents[i]; |
579 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
580 | r.right = xpos - r.left; |
581 | r.top = cp->ypos; |
582 | r.bottom = (i == 0 ? STATICHEIGHT : |
583 | i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT); |
584 | r.top += (height - r.bottom) / 2; |
585 | if (i == 0) { |
586 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, |
587 | j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid); |
588 | } else if (i == 1) { |
589 | doctl(cp, r, "EDIT", |
590 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
591 | WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id); |
592 | } else if (i == 3) { |
593 | doctl(cp, r, "BUTTON", |
594 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
595 | 0, j == 0 ? b1text : b2text, j == 0 ? b1id : b2id); |
596 | } |
597 | } |
598 | cp->ypos += height + GAPWITHIN; |
8c3cd914 |
599 | } |
600 | |
601 | /* The list box. */ |
32874aea |
602 | r.left = GAPBETWEEN; |
603 | r.top = cp->ypos; |
604 | r.right = cp->width; |
605 | r.bottom = LISTHEIGHT; |
8c3cd914 |
606 | cp->ypos += r.bottom + GAPBETWEEN; |
607 | doctl(cp, r, "LISTBOX", |
32874aea |
608 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
609 | | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
610 | } |
611 | |
612 | /* |
613 | * Yet another special control: the character-class setter. A |
614 | * static, then a list, then a line containing a |
615 | * button-and-static-and-edit. |
616 | */ |
617 | void charclass(struct ctlpos *cp, char *stext, int sid, int listid, |
32874aea |
618 | char *btext, int bid, int eid, char *s2text, int s2id) |
619 | { |
8c3cd914 |
620 | RECT r; |
32874aea |
621 | const int height = (STATICHEIGHT > EDITHEIGHT |
622 | && STATICHEIGHT > |
623 | PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT > |
624 | PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
625 | const static int percents[] = { 30, 40, 30 }; |
626 | int i, xpos, percent; |
627 | const int LISTHEIGHT = 66; |
628 | |
629 | /* The static control. */ |
32874aea |
630 | r.left = GAPBETWEEN; |
631 | r.top = cp->ypos; |
632 | r.right = cp->width; |
633 | r.bottom = STATICHEIGHT; |
8c3cd914 |
634 | cp->ypos += r.bottom + GAPWITHIN; |
635 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
636 | |
637 | /* The list box. */ |
32874aea |
638 | r.left = GAPBETWEEN; |
639 | r.top = cp->ypos; |
640 | r.right = cp->width; |
641 | r.bottom = LISTHEIGHT; |
8c3cd914 |
642 | cp->ypos += r.bottom + GAPWITHIN; |
643 | doctl(cp, r, "LISTBOX", |
32874aea |
644 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
645 | | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
646 | |
647 | /* The button+static+edit. */ |
648 | percent = xpos = 0; |
649 | for (i = 0; i < 3; i++) { |
32874aea |
650 | r.left = xpos + GAPBETWEEN; |
651 | percent += percents[i]; |
652 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
653 | r.right = xpos - r.left; |
654 | r.top = cp->ypos; |
655 | r.bottom = (i == 0 ? PUSHBTNHEIGHT : |
656 | i == 1 ? STATICHEIGHT : EDITHEIGHT); |
657 | r.top += (height - r.bottom) / 2; |
658 | if (i == 0) { |
659 | doctl(cp, r, "BUTTON", |
660 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
661 | 0, btext, bid); |
662 | } else if (i == 1) { |
663 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER, |
664 | 0, s2text, s2id); |
665 | } else if (i == 2) { |
666 | doctl(cp, r, "EDIT", |
667 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
668 | WS_EX_CLIENTEDGE, "", eid); |
669 | } |
8c3cd914 |
670 | } |
671 | cp->ypos += height + GAPBETWEEN; |
672 | } |
673 | |
674 | /* |
675 | * A special control (horrors!). The colour editor. A static line; |
676 | * then on the left, a list box, and on the right, a sequence of |
677 | * two-part statics followed by a button. |
678 | */ |
679 | void colouredit(struct ctlpos *cp, char *stext, int sid, int listid, |
32874aea |
680 | char *btext, int bid, ...) |
681 | { |
8c3cd914 |
682 | RECT r; |
683 | int y; |
684 | va_list ap; |
685 | int lwid, rwid, rpos; |
686 | const int LISTHEIGHT = 66; |
687 | |
688 | /* The static control. */ |
32874aea |
689 | r.left = GAPBETWEEN; |
690 | r.top = cp->ypos; |
691 | r.right = cp->width; |
692 | r.bottom = STATICHEIGHT; |
8c3cd914 |
693 | cp->ypos += r.bottom + GAPWITHIN; |
694 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
32874aea |
695 | |
8c3cd914 |
696 | rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3; |
32874aea |
697 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
698 | rwid = cp->width + GAPBETWEEN - rpos; |
699 | |
700 | /* The list box. */ |
32874aea |
701 | r.left = GAPBETWEEN; |
702 | r.top = cp->ypos; |
703 | r.right = lwid; |
704 | r.bottom = LISTHEIGHT; |
8c3cd914 |
705 | doctl(cp, r, "LISTBOX", |
32874aea |
706 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
707 | | LBS_USETABSTOPS | LBS_NOTIFY, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
708 | |
709 | /* The statics. */ |
710 | y = cp->ypos; |
711 | va_start(ap, bid); |
712 | while (1) { |
32874aea |
713 | char *ltext; |
714 | int lid, rid; |
715 | ltext = va_arg(ap, char *); |
716 | if (!ltext) |
717 | break; |
718 | lid = va_arg(ap, int); |
719 | rid = va_arg(ap, int); |
720 | r.top = y; |
721 | r.bottom = STATICHEIGHT; |
722 | y += r.bottom + GAPWITHIN; |
723 | r.left = rpos; |
724 | r.right = rwid / 2; |
725 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid); |
726 | r.left = rpos + r.right; |
727 | r.right = rwid - r.right; |
728 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "", |
729 | rid); |
8c3cd914 |
730 | } |
731 | va_end(ap); |
732 | |
733 | /* The button. */ |
32874aea |
734 | r.top = y + 2 * GAPWITHIN; |
735 | r.bottom = PUSHBTNHEIGHT; |
736 | r.left = rpos; |
737 | r.right = rwid; |
8c3cd914 |
738 | doctl(cp, r, "BUTTON", |
32874aea |
739 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
740 | 0, btext, bid); |
8c3cd914 |
741 | |
742 | cp->ypos += LISTHEIGHT + GAPBETWEEN; |
743 | } |
744 | |
6e522441 |
745 | /* |
746 | * A progress bar (from Common Controls). We like our progress bars |
747 | * to be smooth and unbroken, without those ugly divisions; some |
748 | * older compilers may not support that, but that's life. |
749 | */ |
32874aea |
750 | void progressbar(struct ctlpos *cp, int id) |
751 | { |
6e522441 |
752 | RECT r; |
753 | |
32874aea |
754 | r.left = GAPBETWEEN; |
755 | r.top = cp->ypos; |
756 | r.right = cp->width; |
757 | r.bottom = PROGBARHEIGHT; |
6e522441 |
758 | cp->ypos += r.bottom + GAPBETWEEN; |
759 | |
32874aea |
760 | doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE |
6e522441 |
761 | #ifdef PBS_SMOOTH |
32874aea |
762 | | PBS_SMOOTH |
6e522441 |
763 | #endif |
32874aea |
764 | , WS_EX_CLIENTEDGE, "", id); |
6e522441 |
765 | } |