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 | |
d74d141c |
151 | static void radioline_common(struct ctlpos *cp, int nacross, va_list ap) |
32874aea |
152 | { |
8c3cd914 |
153 | RECT r; |
8c3cd914 |
154 | int group; |
155 | int i; |
1cd48051 |
156 | char *btext; |
8c3cd914 |
157 | |
8c3cd914 |
158 | group = WS_GROUP; |
159 | i = 0; |
1cd48051 |
160 | btext = va_arg(ap, char *); |
8c3cd914 |
161 | while (1) { |
32874aea |
162 | char *nextbtext; |
163 | int bid; |
164 | if (!btext) |
165 | break; |
166 | if (i == nacross) { |
f37caa11 |
167 | cp->ypos += r.bottom + GAPBETWEEN; |
32874aea |
168 | i = 0; |
f37caa11 |
169 | } |
32874aea |
170 | bid = va_arg(ap, int); |
171 | nextbtext = va_arg(ap, char *); |
172 | r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross; |
173 | if (nextbtext) |
174 | r.right = |
175 | (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left; |
176 | else |
177 | r.right = cp->width - r.left; |
178 | r.top = cp->ypos; |
179 | r.bottom = RADIOHEIGHT; |
180 | doctl(cp, r, "BUTTON", |
181 | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
182 | group, 0, btext, bid); |
183 | group = 0; |
184 | i++; |
185 | btext = nextbtext; |
8c3cd914 |
186 | } |
8c3cd914 |
187 | cp->ypos += r.bottom + GAPBETWEEN; |
188 | } |
189 | |
190 | /* |
d74d141c |
191 | * A set of radio buttons on the same line, with a static above |
192 | * them. `nacross' dictates how many parts the line is divided into |
193 | * (you might want this not to equal the number of buttons if you |
194 | * needed to line up some 2s and some 3s to look good in the same |
195 | * panel). |
196 | * |
197 | * There's a bit of a hack in here to ensure that if nacross |
198 | * exceeds the actual number of buttons, the rightmost button |
199 | * really does get all the space right to the edge of the line, so |
200 | * you can do things like |
201 | * |
202 | * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle |
203 | */ |
204 | void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) |
205 | { |
206 | RECT r; |
207 | va_list ap; |
208 | |
209 | r.left = GAPBETWEEN; |
210 | r.top = cp->ypos; |
211 | r.right = cp->width; |
212 | r.bottom = STATICHEIGHT; |
213 | cp->ypos += r.bottom + GAPWITHIN; |
214 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
215 | va_start(ap, nacross); |
216 | radioline_common(cp, nacross, ap); |
217 | va_end(ap); |
218 | } |
219 | |
220 | /* |
221 | * A set of radio buttons on the same line, without a static above |
222 | * them. Otherwise just like radioline. |
223 | */ |
224 | void bareradioline(struct ctlpos *cp, int nacross, ...) |
225 | { |
226 | va_list ap; |
227 | |
228 | va_start(ap, nacross); |
229 | radioline_common(cp, nacross, ap); |
230 | va_end(ap); |
231 | } |
232 | |
233 | /* |
8c3cd914 |
234 | * A set of radio buttons on multiple lines, with a static above |
235 | * them. |
236 | */ |
32874aea |
237 | void radiobig(struct ctlpos *cp, char *text, int id, ...) |
238 | { |
8c3cd914 |
239 | RECT r; |
240 | va_list ap; |
241 | int group; |
242 | |
32874aea |
243 | r.left = GAPBETWEEN; |
244 | r.top = cp->ypos; |
245 | r.right = cp->width; |
246 | r.bottom = STATICHEIGHT; |
8c3cd914 |
247 | cp->ypos += r.bottom + GAPWITHIN; |
248 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
249 | va_start(ap, id); |
250 | group = WS_GROUP; |
251 | while (1) { |
32874aea |
252 | char *btext; |
253 | int bid; |
254 | btext = va_arg(ap, char *); |
255 | if (!btext) |
256 | break; |
257 | bid = va_arg(ap, int); |
258 | r.left = GAPBETWEEN; |
259 | r.top = cp->ypos; |
260 | r.right = cp->width; |
261 | r.bottom = STATICHEIGHT; |
262 | cp->ypos += r.bottom + GAPWITHIN; |
263 | doctl(cp, r, "BUTTON", |
264 | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
265 | group, 0, btext, bid); |
266 | group = 0; |
8c3cd914 |
267 | } |
268 | va_end(ap); |
269 | cp->ypos += GAPBETWEEN - GAPWITHIN; |
270 | } |
271 | |
272 | /* |
273 | * A single standalone checkbox. |
274 | */ |
32874aea |
275 | void checkbox(struct ctlpos *cp, char *text, int id) |
276 | { |
8c3cd914 |
277 | RECT r; |
278 | |
32874aea |
279 | r.left = GAPBETWEEN; |
280 | r.top = cp->ypos; |
281 | r.right = cp->width; |
282 | r.bottom = CHECKBOXHEIGHT; |
8c3cd914 |
283 | cp->ypos += r.bottom + GAPBETWEEN; |
284 | doctl(cp, r, "BUTTON", |
32874aea |
285 | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, |
286 | text, id); |
8c3cd914 |
287 | } |
288 | |
289 | /* |
6e522441 |
290 | * A single standalone static text control. |
291 | */ |
32874aea |
292 | void statictext(struct ctlpos *cp, char *text, int id) |
293 | { |
6e522441 |
294 | RECT r; |
295 | |
32874aea |
296 | r.left = GAPBETWEEN; |
297 | r.top = cp->ypos; |
298 | r.right = cp->width; |
299 | r.bottom = STATICHEIGHT; |
6e522441 |
300 | cp->ypos += r.bottom + GAPBETWEEN; |
301 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); |
302 | } |
303 | |
304 | /* |
8c3cd914 |
305 | * A button on the right hand side, with a static to its left. |
306 | */ |
307 | void staticbtn(struct ctlpos *cp, char *stext, int sid, |
32874aea |
308 | char *btext, int bid) |
309 | { |
8c3cd914 |
310 | const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? |
32874aea |
311 | PUSHBTNHEIGHT : STATICHEIGHT); |
8c3cd914 |
312 | RECT r; |
313 | int lwid, rwid, rpos; |
314 | |
315 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
316 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
317 | rwid = cp->width + GAPBETWEEN - rpos; |
318 | |
32874aea |
319 | r.left = GAPBETWEEN; |
320 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
321 | r.right = lwid; |
322 | r.bottom = STATICHEIGHT; |
8c3cd914 |
323 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
324 | |
32874aea |
325 | r.left = rpos; |
326 | r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; |
327 | r.right = rwid; |
328 | r.bottom = PUSHBTNHEIGHT; |
8c3cd914 |
329 | doctl(cp, r, "BUTTON", |
32874aea |
330 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
331 | 0, btext, bid); |
8c3cd914 |
332 | |
333 | cp->ypos += height + GAPBETWEEN; |
334 | } |
335 | |
336 | /* |
337 | * An edit control on the right hand side, with a static to its left. |
338 | */ |
6e522441 |
339 | static void staticedit_internal(struct ctlpos *cp, char *stext, |
32874aea |
340 | int sid, int eid, int percentedit, |
341 | int style) |
342 | { |
8c3cd914 |
343 | const int height = (EDITHEIGHT > STATICHEIGHT ? |
32874aea |
344 | EDITHEIGHT : STATICHEIGHT); |
8c3cd914 |
345 | RECT r; |
346 | int lwid, rwid, rpos; |
347 | |
32874aea |
348 | rpos = |
349 | GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100; |
350 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
351 | rwid = cp->width + GAPBETWEEN - rpos; |
352 | |
32874aea |
353 | r.left = GAPBETWEEN; |
354 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
355 | r.right = lwid; |
356 | r.bottom = STATICHEIGHT; |
8c3cd914 |
357 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
358 | |
32874aea |
359 | r.left = rpos; |
360 | r.top = cp->ypos + (height - EDITHEIGHT) / 2; |
361 | r.right = rwid; |
362 | r.bottom = EDITHEIGHT; |
8c3cd914 |
363 | doctl(cp, r, "EDIT", |
32874aea |
364 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style, |
365 | WS_EX_CLIENTEDGE, "", eid); |
8c3cd914 |
366 | |
367 | cp->ypos += height + GAPBETWEEN; |
368 | } |
369 | |
6e522441 |
370 | void staticedit(struct ctlpos *cp, char *stext, |
32874aea |
371 | int sid, int eid, int percentedit) |
372 | { |
6e522441 |
373 | staticedit_internal(cp, stext, sid, eid, percentedit, 0); |
374 | } |
375 | |
376 | void staticpassedit(struct ctlpos *cp, char *stext, |
32874aea |
377 | int sid, int eid, int percentedit) |
378 | { |
6e522441 |
379 | staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD); |
380 | } |
381 | |
382 | /* |
383 | * A big multiline edit control with a static labelling it. |
384 | */ |
385 | void bigeditctrl(struct ctlpos *cp, char *stext, |
32874aea |
386 | int sid, int eid, int lines) |
387 | { |
6e522441 |
388 | RECT r; |
389 | |
32874aea |
390 | r.left = GAPBETWEEN; |
391 | r.top = cp->ypos; |
392 | r.right = cp->width; |
393 | r.bottom = STATICHEIGHT; |
6e522441 |
394 | cp->ypos += r.bottom + GAPWITHIN; |
395 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
396 | |
32874aea |
397 | r.left = GAPBETWEEN; |
398 | r.top = cp->ypos; |
399 | r.right = cp->width; |
400 | r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT; |
6e522441 |
401 | cp->ypos += r.bottom + GAPBETWEEN; |
402 | doctl(cp, r, "EDIT", |
32874aea |
403 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE, |
404 | WS_EX_CLIENTEDGE, "", eid); |
6e522441 |
405 | } |
406 | |
8c3cd914 |
407 | /* |
408 | * A tab-control substitute when a real tab control is unavailable. |
409 | */ |
32874aea |
410 | void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) |
411 | { |
8c3cd914 |
412 | const int height = (COMBOHEIGHT > STATICHEIGHT ? |
32874aea |
413 | COMBOHEIGHT : STATICHEIGHT); |
8c3cd914 |
414 | RECT r; |
415 | int bigwid, lwid, rwid, rpos; |
416 | static const int BIGGAP = 15; |
417 | static const int MEDGAP = 3; |
418 | |
32874aea |
419 | bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP; |
8c3cd914 |
420 | cp->ypos += MEDGAP; |
421 | rpos = BIGGAP + (bigwid + BIGGAP) / 2; |
32874aea |
422 | lwid = rpos - 2 * BIGGAP; |
8c3cd914 |
423 | rwid = bigwid + BIGGAP - rpos; |
424 | |
32874aea |
425 | r.left = BIGGAP; |
426 | r.top = cp->ypos + (height - STATICHEIGHT) / 2; |
427 | r.right = lwid; |
428 | r.bottom = STATICHEIGHT; |
8c3cd914 |
429 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
430 | |
32874aea |
431 | r.left = rpos; |
432 | r.top = cp->ypos + (height - COMBOHEIGHT) / 2; |
433 | r.right = rwid; |
434 | r.bottom = COMBOHEIGHT * 10; |
8c3cd914 |
435 | doctl(cp, r, "COMBOBOX", |
32874aea |
436 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | |
437 | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); |
8c3cd914 |
438 | |
439 | cp->ypos += height + MEDGAP + GAPBETWEEN; |
440 | |
32874aea |
441 | r.left = GAPBETWEEN; |
442 | r.top = cp->ypos; |
443 | r.right = cp->width; |
444 | r.bottom = 2; |
8c3cd914 |
445 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, |
32874aea |
446 | 0, "", s2id); |
8c3cd914 |
447 | } |
448 | |
449 | /* |
450 | * A static line, followed by an edit control on the left hand side |
451 | * and a button on the right. |
452 | */ |
453 | void editbutton(struct ctlpos *cp, char *stext, int sid, |
32874aea |
454 | int eid, char *btext, int bid) |
455 | { |
8c3cd914 |
456 | const int height = (EDITHEIGHT > PUSHBTNHEIGHT ? |
32874aea |
457 | EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
458 | RECT r; |
459 | int lwid, rwid, rpos; |
460 | |
32874aea |
461 | r.left = GAPBETWEEN; |
462 | r.top = cp->ypos; |
463 | r.right = cp->width; |
464 | r.bottom = STATICHEIGHT; |
8c3cd914 |
465 | cp->ypos += r.bottom + GAPWITHIN; |
466 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
467 | |
468 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
469 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
470 | rwid = cp->width + GAPBETWEEN - rpos; |
471 | |
32874aea |
472 | r.left = GAPBETWEEN; |
473 | r.top = cp->ypos + (height - EDITHEIGHT) / 2; |
474 | r.right = lwid; |
475 | r.bottom = EDITHEIGHT; |
8c3cd914 |
476 | doctl(cp, r, "EDIT", |
32874aea |
477 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
478 | WS_EX_CLIENTEDGE, "", eid); |
8c3cd914 |
479 | |
32874aea |
480 | r.left = rpos; |
481 | r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; |
482 | r.right = rwid; |
483 | r.bottom = PUSHBTNHEIGHT; |
8c3cd914 |
484 | doctl(cp, r, "BUTTON", |
32874aea |
485 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
486 | 0, btext, bid); |
8c3cd914 |
487 | |
488 | cp->ypos += height + GAPBETWEEN; |
489 | } |
490 | |
491 | /* |
492 | * Special control which was hard to describe generically: the |
493 | * session-saver assembly. A static; below that an edit box; below |
494 | * that a list box. To the right of the list box, a column of |
495 | * buttons. |
496 | */ |
497 | void sesssaver(struct ctlpos *cp, char *text, |
32874aea |
498 | int staticid, int editid, int listid, ...) |
499 | { |
8c3cd914 |
500 | RECT r; |
501 | va_list ap; |
502 | int lwid, rwid, rpos; |
503 | int y; |
504 | const int LISTDEFHEIGHT = 66; |
505 | |
506 | rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; |
32874aea |
507 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
508 | rwid = cp->width + GAPBETWEEN - rpos; |
509 | |
510 | /* The static control. */ |
32874aea |
511 | r.left = GAPBETWEEN; |
512 | r.top = cp->ypos; |
513 | r.right = lwid; |
514 | r.bottom = STATICHEIGHT; |
8c3cd914 |
515 | cp->ypos += r.bottom + GAPWITHIN; |
516 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); |
517 | |
518 | /* The edit control. */ |
32874aea |
519 | r.left = GAPBETWEEN; |
520 | r.top = cp->ypos; |
521 | r.right = lwid; |
522 | r.bottom = EDITHEIGHT; |
8c3cd914 |
523 | cp->ypos += r.bottom + GAPWITHIN; |
524 | doctl(cp, r, "EDIT", |
32874aea |
525 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
526 | WS_EX_CLIENTEDGE, "", editid); |
8c3cd914 |
527 | |
528 | /* |
529 | * The buttons (we should hold off on the list box until we |
530 | * know how big the buttons are). |
531 | */ |
532 | va_start(ap, listid); |
533 | y = cp->ypos; |
534 | while (1) { |
32874aea |
535 | char *btext = va_arg(ap, char *); |
536 | int bid; |
537 | if (!btext) |
538 | break; |
539 | bid = va_arg(ap, int); |
540 | r.left = rpos; |
541 | r.top = y; |
542 | r.right = rwid; |
543 | r.bottom = PUSHBTNHEIGHT; |
544 | y += r.bottom + GAPWITHIN; |
545 | doctl(cp, r, "BUTTON", |
546 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
547 | 0, btext, bid); |
8c3cd914 |
548 | } |
549 | |
550 | /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */ |
551 | y -= cp->ypos; |
552 | y -= GAPWITHIN; |
32874aea |
553 | if (y < LISTDEFHEIGHT) |
554 | y = LISTDEFHEIGHT; |
555 | r.left = GAPBETWEEN; |
556 | r.top = cp->ypos; |
557 | r.right = lwid; |
558 | r.bottom = y; |
8c3cd914 |
559 | cp->ypos += y + GAPBETWEEN; |
560 | doctl(cp, r, "LISTBOX", |
32874aea |
561 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | |
562 | LBS_NOTIFY | LBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
563 | } |
564 | |
565 | /* |
566 | * Another special control: the environment-variable setter. A |
567 | * static line first; then a pair of edit boxes with associated |
568 | * statics, and two buttons; then a list box. |
569 | */ |
570 | void envsetter(struct ctlpos *cp, char *stext, int sid, |
32874aea |
571 | char *e1stext, int e1sid, int e1id, |
572 | char *e2stext, int e2sid, int e2id, |
573 | int listid, char *b1text, int b1id, char *b2text, int b2id) |
574 | { |
8c3cd914 |
575 | RECT r; |
32874aea |
576 | const int height = (STATICHEIGHT > EDITHEIGHT |
577 | && STATICHEIGHT > |
578 | PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT > |
579 | PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
580 | const static int percents[] = { 20, 35, 10, 25 }; |
581 | int i, j, xpos, percent; |
582 | const int LISTHEIGHT = 42; |
583 | |
584 | /* The static control. */ |
32874aea |
585 | r.left = GAPBETWEEN; |
586 | r.top = cp->ypos; |
587 | r.right = cp->width; |
588 | r.bottom = STATICHEIGHT; |
8c3cd914 |
589 | cp->ypos += r.bottom + GAPWITHIN; |
590 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
591 | |
592 | /* The statics+edits+buttons. */ |
593 | for (j = 0; j < 2; j++) { |
32874aea |
594 | percent = 10; |
595 | for (i = 0; i < 4; i++) { |
596 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
597 | r.left = xpos + GAPBETWEEN; |
598 | percent += percents[i]; |
599 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
600 | r.right = xpos - r.left; |
601 | r.top = cp->ypos; |
602 | r.bottom = (i == 0 ? STATICHEIGHT : |
603 | i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT); |
604 | r.top += (height - r.bottom) / 2; |
605 | if (i == 0) { |
606 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, |
607 | j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid); |
608 | } else if (i == 1) { |
609 | doctl(cp, r, "EDIT", |
610 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
611 | WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id); |
612 | } else if (i == 3) { |
613 | doctl(cp, r, "BUTTON", |
614 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
615 | 0, j == 0 ? b1text : b2text, j == 0 ? b1id : b2id); |
616 | } |
617 | } |
618 | cp->ypos += height + GAPWITHIN; |
8c3cd914 |
619 | } |
620 | |
621 | /* The list box. */ |
32874aea |
622 | r.left = GAPBETWEEN; |
623 | r.top = cp->ypos; |
624 | r.right = cp->width; |
625 | r.bottom = LISTHEIGHT; |
8c3cd914 |
626 | cp->ypos += r.bottom + GAPBETWEEN; |
627 | doctl(cp, r, "LISTBOX", |
32874aea |
628 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
629 | | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
630 | } |
631 | |
632 | /* |
633 | * Yet another special control: the character-class setter. A |
634 | * static, then a list, then a line containing a |
635 | * button-and-static-and-edit. |
636 | */ |
637 | void charclass(struct ctlpos *cp, char *stext, int sid, int listid, |
32874aea |
638 | char *btext, int bid, int eid, char *s2text, int s2id) |
639 | { |
8c3cd914 |
640 | RECT r; |
32874aea |
641 | const int height = (STATICHEIGHT > EDITHEIGHT |
642 | && STATICHEIGHT > |
643 | PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT > |
644 | PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); |
8c3cd914 |
645 | const static int percents[] = { 30, 40, 30 }; |
646 | int i, xpos, percent; |
647 | const int LISTHEIGHT = 66; |
648 | |
649 | /* The static control. */ |
32874aea |
650 | r.left = GAPBETWEEN; |
651 | r.top = cp->ypos; |
652 | r.right = cp->width; |
653 | r.bottom = STATICHEIGHT; |
8c3cd914 |
654 | cp->ypos += r.bottom + GAPWITHIN; |
655 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
656 | |
657 | /* The list box. */ |
32874aea |
658 | r.left = GAPBETWEEN; |
659 | r.top = cp->ypos; |
660 | r.right = cp->width; |
661 | r.bottom = LISTHEIGHT; |
8c3cd914 |
662 | cp->ypos += r.bottom + GAPWITHIN; |
663 | doctl(cp, r, "LISTBOX", |
32874aea |
664 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
665 | | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
666 | |
667 | /* The button+static+edit. */ |
668 | percent = xpos = 0; |
669 | for (i = 0; i < 3; i++) { |
32874aea |
670 | r.left = xpos + GAPBETWEEN; |
671 | percent += percents[i]; |
672 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
673 | r.right = xpos - r.left; |
674 | r.top = cp->ypos; |
675 | r.bottom = (i == 0 ? PUSHBTNHEIGHT : |
676 | i == 1 ? STATICHEIGHT : EDITHEIGHT); |
677 | r.top += (height - r.bottom) / 2; |
678 | if (i == 0) { |
679 | doctl(cp, r, "BUTTON", |
680 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
681 | 0, btext, bid); |
682 | } else if (i == 1) { |
683 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER, |
684 | 0, s2text, s2id); |
685 | } else if (i == 2) { |
686 | doctl(cp, r, "EDIT", |
687 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
688 | WS_EX_CLIENTEDGE, "", eid); |
689 | } |
8c3cd914 |
690 | } |
691 | cp->ypos += height + GAPBETWEEN; |
692 | } |
693 | |
694 | /* |
695 | * A special control (horrors!). The colour editor. A static line; |
696 | * then on the left, a list box, and on the right, a sequence of |
697 | * two-part statics followed by a button. |
698 | */ |
699 | void colouredit(struct ctlpos *cp, char *stext, int sid, int listid, |
32874aea |
700 | char *btext, int bid, ...) |
701 | { |
8c3cd914 |
702 | RECT r; |
703 | int y; |
704 | va_list ap; |
705 | int lwid, rwid, rpos; |
706 | const int LISTHEIGHT = 66; |
707 | |
708 | /* The static control. */ |
32874aea |
709 | r.left = GAPBETWEEN; |
710 | r.top = cp->ypos; |
711 | r.right = cp->width; |
712 | r.bottom = STATICHEIGHT; |
8c3cd914 |
713 | cp->ypos += r.bottom + GAPWITHIN; |
714 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
32874aea |
715 | |
8c3cd914 |
716 | rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3; |
32874aea |
717 | lwid = rpos - 2 * GAPBETWEEN; |
8c3cd914 |
718 | rwid = cp->width + GAPBETWEEN - rpos; |
719 | |
720 | /* The list box. */ |
32874aea |
721 | r.left = GAPBETWEEN; |
722 | r.top = cp->ypos; |
723 | r.right = lwid; |
724 | r.bottom = LISTHEIGHT; |
8c3cd914 |
725 | doctl(cp, r, "LISTBOX", |
32874aea |
726 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
727 | | LBS_USETABSTOPS | LBS_NOTIFY, WS_EX_CLIENTEDGE, "", listid); |
8c3cd914 |
728 | |
729 | /* The statics. */ |
730 | y = cp->ypos; |
731 | va_start(ap, bid); |
732 | while (1) { |
32874aea |
733 | char *ltext; |
734 | int lid, rid; |
735 | ltext = va_arg(ap, char *); |
736 | if (!ltext) |
737 | break; |
738 | lid = va_arg(ap, int); |
739 | rid = va_arg(ap, int); |
740 | r.top = y; |
741 | r.bottom = STATICHEIGHT; |
742 | y += r.bottom + GAPWITHIN; |
743 | r.left = rpos; |
744 | r.right = rwid / 2; |
745 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid); |
746 | r.left = rpos + r.right; |
747 | r.right = rwid - r.right; |
748 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "", |
749 | rid); |
8c3cd914 |
750 | } |
751 | va_end(ap); |
752 | |
753 | /* The button. */ |
32874aea |
754 | r.top = y + 2 * GAPWITHIN; |
755 | r.bottom = PUSHBTNHEIGHT; |
756 | r.left = rpos; |
757 | r.right = rwid; |
8c3cd914 |
758 | doctl(cp, r, "BUTTON", |
32874aea |
759 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
760 | 0, btext, bid); |
8c3cd914 |
761 | |
762 | cp->ypos += LISTHEIGHT + GAPBETWEEN; |
763 | } |
764 | |
6e522441 |
765 | /* |
766 | * A progress bar (from Common Controls). We like our progress bars |
767 | * to be smooth and unbroken, without those ugly divisions; some |
768 | * older compilers may not support that, but that's life. |
769 | */ |
32874aea |
770 | void progressbar(struct ctlpos *cp, int id) |
771 | { |
6e522441 |
772 | RECT r; |
773 | |
32874aea |
774 | r.left = GAPBETWEEN; |
775 | r.top = cp->ypos; |
776 | r.right = cp->width; |
777 | r.bottom = PROGBARHEIGHT; |
6e522441 |
778 | cp->ypos += r.bottom + GAPBETWEEN; |
779 | |
32874aea |
780 | doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE |
6e522441 |
781 | #ifdef PBS_SMOOTH |
32874aea |
782 | | PBS_SMOOTH |
6e522441 |
783 | #endif |
32874aea |
784 | , WS_EX_CLIENTEDGE, "", id); |
6e522441 |
785 | } |
d74d141c |
786 | |
787 | /* |
788 | * Another special control: the forwarding options setter. First a |
789 | * list box; next a static header line, introducing a pair of edit |
790 | * boxes with associated statics, another button, and a radio |
791 | * button pair. |
792 | */ |
793 | void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid, |
794 | char *e1stext, int e1sid, int e1id, |
795 | char *e2stext, int e2sid, int e2id, |
796 | char *btext, int bid) |
797 | { |
798 | RECT r; |
799 | const int height = (STATICHEIGHT > EDITHEIGHT |
800 | && STATICHEIGHT > |
801 | PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT > |
802 | PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); |
803 | const static int percents[] = { 25, 35, 15, 25 }; |
804 | int i, j, xpos, percent; |
805 | const int LISTHEIGHT = 42; |
806 | |
807 | /* The list box. */ |
808 | r.left = GAPBETWEEN; |
809 | r.top = cp->ypos; |
810 | r.right = cp->width; |
811 | r.bottom = LISTHEIGHT; |
812 | cp->ypos += r.bottom + GAPBETWEEN; |
813 | doctl(cp, r, "LISTBOX", |
814 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS |
815 | | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); |
816 | |
817 | /* The static control. */ |
818 | r.left = GAPBETWEEN; |
819 | r.top = cp->ypos; |
820 | r.right = cp->width; |
821 | r.bottom = STATICHEIGHT; |
822 | cp->ypos += r.bottom + GAPWITHIN; |
823 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); |
824 | |
825 | /* The statics+edits+buttons. */ |
826 | for (j = 0; j < 2; j++) { |
827 | percent = 0; |
828 | for (i = 0; i < (j ? 2 : 4); i++) { |
829 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
830 | r.left = xpos + GAPBETWEEN; |
831 | percent += percents[i]; |
832 | if (j==1 && i==1) percent = 100; |
833 | xpos = (cp->width + GAPBETWEEN) * percent / 100; |
834 | r.right = xpos - r.left; |
835 | r.top = cp->ypos; |
836 | r.bottom = (i == 0 ? STATICHEIGHT : |
837 | i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT); |
838 | r.top += (height - r.bottom) / 2; |
839 | if (i == 0) { |
840 | doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, |
841 | j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid); |
842 | } else if (i == 1) { |
843 | doctl(cp, r, "EDIT", |
844 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, |
845 | WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id); |
846 | } else if (i == 3) { |
847 | doctl(cp, r, "BUTTON", |
848 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, |
849 | 0, btext, bid); |
850 | } |
851 | } |
852 | cp->ypos += height + GAPWITHIN; |
853 | } |
854 | } |