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