Better yet, look for sftp-server on the user's PATH as well, which
[u/mdw/putty] / winctrls.c
CommitLineData
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"
ca20bfcf 10#include "puttymem.h"
11
12#include "putty.h"
8c3cd914 13
14#define GAPBETWEEN 3
15#define GAPWITHIN 1
16#define GAPXBOX 7
17#define GAPYBOX 4
18#define DLGWIDTH 168
19#define STATICHEIGHT 8
20#define CHECKBOXHEIGHT 8
21#define RADIOHEIGHT 8
22#define EDITHEIGHT 12
23#define COMBOHEIGHT 12
24#define PUSHBTNHEIGHT 14
6e522441 25#define PROGBARHEIGHT 14
8c3cd914 26
27void ctlposinit(struct ctlpos *cp, HWND hwnd,
32874aea 28 int leftborder, int rightborder, int topborder)
29{
8c3cd914 30 RECT r, r2;
31 cp->hwnd = hwnd;
32 cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0);
33 cp->ypos = topborder;
34 GetClientRect(hwnd, &r);
35 r2.left = r2.top = 0;
36 r2.right = 4;
37 r2.bottom = 8;
38 MapDialogRect(hwnd, &r2);
39 cp->dlu4inpix = r2.right;
32874aea 40 cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN;
8c3cd914 41 cp->xoff = leftborder;
42 cp->width -= leftborder + rightborder;
43}
44
ca20bfcf 45HWND doctl(struct ctlpos *cp, RECT r,
32874aea 46 char *wclass, int wstyle, int exstyle, char *wtext, int wid)
47{
8c3cd914 48 HWND ctl;
49 /*
50 * Note nonstandard use of RECT. This is deliberate: by
51 * transforming the width and height directly we arrange to
52 * have all supposedly same-sized controls really same-sized.
53 */
54
55 r.left += cp->xoff;
56 MapDialogRect(cp->hwnd, &r);
57
58 ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,
32874aea 59 r.left, r.top, r.right, r.bottom,
60 cp->hwnd, (HMENU) wid, hinst, NULL);
8c3cd914 61 SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
ca20bfcf 62 return ctl;
8c3cd914 63}
64
65/*
66 * A title bar across the top of a sub-dialog.
67 */
32874aea 68void bartitle(struct ctlpos *cp, char *name, int id)
69{
8c3cd914 70 RECT r;
71
32874aea 72 r.left = GAPBETWEEN;
73 r.right = cp->width;
74 r.top = cp->ypos;
75 r.bottom = STATICHEIGHT;
8c3cd914 76 cp->ypos += r.bottom + GAPBETWEEN;
77 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id);
78}
79
80/*
81 * Begin a grouping box, with or without a group title.
82 */
32874aea 83void beginbox(struct ctlpos *cp, char *name, int idbox)
84{
8c3cd914 85 cp->boxystart = cp->ypos;
3ac9cd9f 86 if (!name)
32874aea 87 cp->boxystart -= STATICHEIGHT / 2;
8c3cd914 88 if (name)
32874aea 89 cp->ypos += STATICHEIGHT;
8c3cd914 90 cp->ypos += GAPYBOX;
32874aea 91 cp->width -= 2 * GAPXBOX;
8c3cd914 92 cp->xoff += GAPXBOX;
93 cp->boxid = idbox;
8c3cd914 94 cp->boxtext = name;
95}
96
97/*
98 * End a grouping box.
99 */
32874aea 100void endbox(struct ctlpos *cp)
101{
8c3cd914 102 RECT r;
103 cp->xoff -= GAPXBOX;
32874aea 104 cp->width += 2 * GAPXBOX;
8c3cd914 105 cp->ypos += GAPYBOX - GAPBETWEEN;
32874aea 106 r.left = GAPBETWEEN;
107 r.right = cp->width;
108 r.top = cp->boxystart;
109 r.bottom = cp->ypos - cp->boxystart;
3ac9cd9f 110 doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0,
32874aea 111 cp->boxtext ? cp->boxtext : "", cp->boxid);
8c3cd914 112 cp->ypos += GAPYBOX;
113}
114
115/*
116 * Some edit boxes. Each one has a static above it. The percentages
117 * of the horizontal space are provided.
118 */
32874aea 119void multiedit(struct ctlpos *cp, ...)
120{
8c3cd914 121 RECT r;
122 va_list ap;
123 int percent, xpos;
124
125 percent = xpos = 0;
126 va_start(ap, cp);
127 while (1) {
32874aea 128 char *text;
129 int staticid, editid, pcwidth;
130 text = va_arg(ap, char *);
131 if (!text)
132 break;
133 staticid = va_arg(ap, int);
134 editid = va_arg(ap, int);
135 pcwidth = va_arg(ap, int);
136
137 r.left = xpos + GAPBETWEEN;
138 percent += pcwidth;
139 xpos = (cp->width + GAPBETWEEN) * percent / 100;
140 r.right = xpos - r.left;
141
142 r.top = cp->ypos;
143 r.bottom = STATICHEIGHT;
144 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
145 r.top = cp->ypos + 8 + GAPWITHIN;
146 r.bottom = EDITHEIGHT;
147 doctl(cp, r, "EDIT",
148 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
149 WS_EX_CLIENTEDGE, "", editid);
8c3cd914 150 }
151 va_end(ap);
875b193f 152 cp->ypos += STATICHEIGHT + GAPWITHIN + EDITHEIGHT + GAPBETWEEN;
153}
154
155/*
156 * A static line, followed by a full-width drop-down list (ie a
157 * non-editing combo box).
158 */
159void dropdownlist(struct ctlpos *cp, char *text, int staticid, int listid)
160{
161 RECT r;
875b193f 162
163 r.left = GAPBETWEEN;
164 r.right = cp->width;
165
166 r.top = cp->ypos;
167 r.bottom = STATICHEIGHT;
168 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
169 r.top = cp->ypos + 8 + GAPWITHIN;
170 r.bottom = COMBOHEIGHT * 10;
171 doctl(cp, r, "COMBOBOX",
172 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
173 CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);
174
175 cp->ypos += STATICHEIGHT + GAPWITHIN + COMBOHEIGHT + GAPBETWEEN;
8c3cd914 176}
177
d74d141c 178static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
32874aea 179{
8c3cd914 180 RECT r;
8c3cd914 181 int group;
182 int i;
1cd48051 183 char *btext;
8c3cd914 184
8c3cd914 185 group = WS_GROUP;
186 i = 0;
1cd48051 187 btext = va_arg(ap, char *);
8c3cd914 188 while (1) {
32874aea 189 char *nextbtext;
190 int bid;
191 if (!btext)
192 break;
193 if (i == nacross) {
f37caa11 194 cp->ypos += r.bottom + GAPBETWEEN;
32874aea 195 i = 0;
f37caa11 196 }
32874aea 197 bid = va_arg(ap, int);
198 nextbtext = va_arg(ap, char *);
199 r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross;
200 if (nextbtext)
201 r.right =
202 (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left;
203 else
204 r.right = cp->width - r.left;
205 r.top = cp->ypos;
206 r.bottom = RADIOHEIGHT;
207 doctl(cp, r, "BUTTON",
208 BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
209 group, 0, btext, bid);
210 group = 0;
211 i++;
212 btext = nextbtext;
8c3cd914 213 }
8c3cd914 214 cp->ypos += r.bottom + GAPBETWEEN;
215}
216
217/*
d74d141c 218 * A set of radio buttons on the same line, with a static above
219 * them. `nacross' dictates how many parts the line is divided into
220 * (you might want this not to equal the number of buttons if you
221 * needed to line up some 2s and some 3s to look good in the same
222 * panel).
223 *
224 * There's a bit of a hack in here to ensure that if nacross
225 * exceeds the actual number of buttons, the rightmost button
226 * really does get all the space right to the edge of the line, so
227 * you can do things like
228 *
229 * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
230 */
231void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
232{
233 RECT r;
234 va_list ap;
235
236 r.left = GAPBETWEEN;
237 r.top = cp->ypos;
238 r.right = cp->width;
239 r.bottom = STATICHEIGHT;
240 cp->ypos += r.bottom + GAPWITHIN;
241 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
242 va_start(ap, nacross);
243 radioline_common(cp, nacross, ap);
244 va_end(ap);
245}
246
247/*
248 * A set of radio buttons on the same line, without a static above
249 * them. Otherwise just like radioline.
250 */
251void bareradioline(struct ctlpos *cp, int nacross, ...)
252{
253 va_list ap;
254
255 va_start(ap, nacross);
256 radioline_common(cp, nacross, ap);
257 va_end(ap);
258}
259
260/*
8c3cd914 261 * A set of radio buttons on multiple lines, with a static above
262 * them.
263 */
32874aea 264void radiobig(struct ctlpos *cp, char *text, int id, ...)
265{
8c3cd914 266 RECT r;
267 va_list ap;
268 int group;
269
32874aea 270 r.left = GAPBETWEEN;
271 r.top = cp->ypos;
272 r.right = cp->width;
273 r.bottom = STATICHEIGHT;
8c3cd914 274 cp->ypos += r.bottom + GAPWITHIN;
275 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
276 va_start(ap, id);
277 group = WS_GROUP;
278 while (1) {
32874aea 279 char *btext;
280 int bid;
281 btext = va_arg(ap, char *);
282 if (!btext)
283 break;
284 bid = va_arg(ap, int);
285 r.left = GAPBETWEEN;
286 r.top = cp->ypos;
287 r.right = cp->width;
288 r.bottom = STATICHEIGHT;
289 cp->ypos += r.bottom + GAPWITHIN;
290 doctl(cp, r, "BUTTON",
291 BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP |
292 group, 0, btext, bid);
293 group = 0;
8c3cd914 294 }
295 va_end(ap);
296 cp->ypos += GAPBETWEEN - GAPWITHIN;
297}
298
299/*
300 * A single standalone checkbox.
301 */
32874aea 302void checkbox(struct ctlpos *cp, char *text, int id)
303{
8c3cd914 304 RECT r;
305
32874aea 306 r.left = GAPBETWEEN;
307 r.top = cp->ypos;
308 r.right = cp->width;
309 r.bottom = CHECKBOXHEIGHT;
8c3cd914 310 cp->ypos += r.bottom + GAPBETWEEN;
311 doctl(cp, r, "BUTTON",
32874aea 312 BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,
313 text, id);
8c3cd914 314}
315
316/*
6e522441 317 * A single standalone static text control.
318 */
32874aea 319void statictext(struct ctlpos *cp, char *text, int id)
320{
6e522441 321 RECT r;
322
32874aea 323 r.left = GAPBETWEEN;
324 r.top = cp->ypos;
325 r.right = cp->width;
326 r.bottom = STATICHEIGHT;
6e522441 327 cp->ypos += r.bottom + GAPBETWEEN;
328 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
329}
330
331/*
8c3cd914 332 * A button on the right hand side, with a static to its left.
333 */
334void staticbtn(struct ctlpos *cp, char *stext, int sid,
32874aea 335 char *btext, int bid)
336{
8c3cd914 337 const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
32874aea 338 PUSHBTNHEIGHT : STATICHEIGHT);
8c3cd914 339 RECT r;
340 int lwid, rwid, rpos;
341
342 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
32874aea 343 lwid = rpos - 2 * GAPBETWEEN;
8c3cd914 344 rwid = cp->width + GAPBETWEEN - rpos;
345
32874aea 346 r.left = GAPBETWEEN;
347 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
348 r.right = lwid;
349 r.bottom = STATICHEIGHT;
8c3cd914 350 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
351
32874aea 352 r.left = rpos;
353 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
354 r.right = rwid;
355 r.bottom = PUSHBTNHEIGHT;
8c3cd914 356 doctl(cp, r, "BUTTON",
32874aea 357 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
358 0, btext, bid);
8c3cd914 359
360 cp->ypos += height + GAPBETWEEN;
361}
362
363/*
af282e3b 364 * Like staticbtn, but two buttons.
365 */
366void static2btn(struct ctlpos *cp, char *stext, int sid,
367 char *btext1, int bid1, char *btext2, int bid2)
368{
369 const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
370 PUSHBTNHEIGHT : STATICHEIGHT);
371 RECT r;
372 int lwid, rwid1, rwid2, rpos1, rpos2;
373
374 rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
375 rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
376 lwid = rpos1 - 2 * GAPBETWEEN;
377 rwid1 = rpos2 - rpos1 - GAPBETWEEN;
378 rwid2 = cp->width + GAPBETWEEN - rpos2;
379
380 r.left = GAPBETWEEN;
381 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
382 r.right = lwid;
383 r.bottom = STATICHEIGHT;
384 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
385
386 r.left = rpos1;
387 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
388 r.right = rwid1;
389 r.bottom = PUSHBTNHEIGHT;
390 doctl(cp, r, "BUTTON",
391 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
392 0, btext1, bid1);
393
394 r.left = rpos2;
395 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
396 r.right = rwid2;
397 r.bottom = PUSHBTNHEIGHT;
398 doctl(cp, r, "BUTTON",
399 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
400 0, btext2, bid2);
401
402 cp->ypos += height + GAPBETWEEN;
403}
404
405/*
8c3cd914 406 * An edit control on the right hand side, with a static to its left.
407 */
6e522441 408static void staticedit_internal(struct ctlpos *cp, char *stext,
32874aea 409 int sid, int eid, int percentedit,
410 int style)
411{
8c3cd914 412 const int height = (EDITHEIGHT > STATICHEIGHT ?
32874aea 413 EDITHEIGHT : STATICHEIGHT);
8c3cd914 414 RECT r;
415 int lwid, rwid, rpos;
416
32874aea 417 rpos =
418 GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100;
419 lwid = rpos - 2 * GAPBETWEEN;
8c3cd914 420 rwid = cp->width + GAPBETWEEN - rpos;
421
32874aea 422 r.left = GAPBETWEEN;
423 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
424 r.right = lwid;
425 r.bottom = STATICHEIGHT;
8c3cd914 426 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
427
32874aea 428 r.left = rpos;
429 r.top = cp->ypos + (height - EDITHEIGHT) / 2;
430 r.right = rwid;
431 r.bottom = EDITHEIGHT;
8c3cd914 432 doctl(cp, r, "EDIT",
32874aea 433 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style,
434 WS_EX_CLIENTEDGE, "", eid);
8c3cd914 435
436 cp->ypos += height + GAPBETWEEN;
437}
438
6e522441 439void staticedit(struct ctlpos *cp, char *stext,
32874aea 440 int sid, int eid, int percentedit)
441{
6e522441 442 staticedit_internal(cp, stext, sid, eid, percentedit, 0);
443}
444
445void staticpassedit(struct ctlpos *cp, char *stext,
32874aea 446 int sid, int eid, int percentedit)
447{
6e522441 448 staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD);
449}
450
451/*
452 * A big multiline edit control with a static labelling it.
453 */
454void bigeditctrl(struct ctlpos *cp, char *stext,
32874aea 455 int sid, int eid, int lines)
456{
6e522441 457 RECT r;
458
32874aea 459 r.left = GAPBETWEEN;
460 r.top = cp->ypos;
461 r.right = cp->width;
462 r.bottom = STATICHEIGHT;
6e522441 463 cp->ypos += r.bottom + GAPWITHIN;
464 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
465
32874aea 466 r.left = GAPBETWEEN;
467 r.top = cp->ypos;
468 r.right = cp->width;
469 r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT;
6e522441 470 cp->ypos += r.bottom + GAPBETWEEN;
471 doctl(cp, r, "EDIT",
32874aea 472 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE,
473 WS_EX_CLIENTEDGE, "", eid);
6e522441 474}
475
8c3cd914 476/*
477 * A tab-control substitute when a real tab control is unavailable.
478 */
32874aea 479void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id)
480{
8c3cd914 481 const int height = (COMBOHEIGHT > STATICHEIGHT ?
32874aea 482 COMBOHEIGHT : STATICHEIGHT);
8c3cd914 483 RECT r;
484 int bigwid, lwid, rwid, rpos;
485 static const int BIGGAP = 15;
486 static const int MEDGAP = 3;
487
32874aea 488 bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP;
8c3cd914 489 cp->ypos += MEDGAP;
490 rpos = BIGGAP + (bigwid + BIGGAP) / 2;
32874aea 491 lwid = rpos - 2 * BIGGAP;
8c3cd914 492 rwid = bigwid + BIGGAP - rpos;
493
32874aea 494 r.left = BIGGAP;
495 r.top = cp->ypos + (height - STATICHEIGHT) / 2;
496 r.right = lwid;
497 r.bottom = STATICHEIGHT;
8c3cd914 498 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
499
32874aea 500 r.left = rpos;
501 r.top = cp->ypos + (height - COMBOHEIGHT) / 2;
502 r.right = rwid;
503 r.bottom = COMBOHEIGHT * 10;
8c3cd914 504 doctl(cp, r, "COMBOBOX",
32874aea 505 WS_CHILD | WS_VISIBLE | WS_TABSTOP |
506 CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);
8c3cd914 507
508 cp->ypos += height + MEDGAP + GAPBETWEEN;
509
32874aea 510 r.left = GAPBETWEEN;
511 r.top = cp->ypos;
512 r.right = cp->width;
513 r.bottom = 2;
8c3cd914 514 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,
32874aea 515 0, "", s2id);
8c3cd914 516}
517
518/*
519 * A static line, followed by an edit control on the left hand side
520 * and a button on the right.
521 */
522void editbutton(struct ctlpos *cp, char *stext, int sid,
32874aea 523 int eid, char *btext, int bid)
524{
8c3cd914 525 const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?
32874aea 526 EDITHEIGHT : PUSHBTNHEIGHT);
8c3cd914 527 RECT r;
528 int lwid, rwid, rpos;
529
32874aea 530 r.left = GAPBETWEEN;
531 r.top = cp->ypos;
532 r.right = cp->width;
533 r.bottom = STATICHEIGHT;
8c3cd914 534 cp->ypos += r.bottom + GAPWITHIN;
535 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
536
537 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
32874aea 538 lwid = rpos - 2 * GAPBETWEEN;
8c3cd914 539 rwid = cp->width + GAPBETWEEN - rpos;
540
32874aea 541 r.left = GAPBETWEEN;
542 r.top = cp->ypos + (height - EDITHEIGHT) / 2;
543 r.right = lwid;
544 r.bottom = EDITHEIGHT;
8c3cd914 545 doctl(cp, r, "EDIT",
32874aea 546 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
547 WS_EX_CLIENTEDGE, "", eid);
8c3cd914 548
32874aea 549 r.left = rpos;
550 r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
551 r.right = rwid;
552 r.bottom = PUSHBTNHEIGHT;
8c3cd914 553 doctl(cp, r, "BUTTON",
32874aea 554 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
555 0, btext, bid);
8c3cd914 556
557 cp->ypos += height + GAPBETWEEN;
558}
559
560/*
561 * Special control which was hard to describe generically: the
562 * session-saver assembly. A static; below that an edit box; below
563 * that a list box. To the right of the list box, a column of
564 * buttons.
565 */
566void sesssaver(struct ctlpos *cp, char *text,
32874aea 567 int staticid, int editid, int listid, ...)
568{
8c3cd914 569 RECT r;
570 va_list ap;
571 int lwid, rwid, rpos;
572 int y;
573 const int LISTDEFHEIGHT = 66;
574
575 rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
32874aea 576 lwid = rpos - 2 * GAPBETWEEN;
8c3cd914 577 rwid = cp->width + GAPBETWEEN - rpos;
578
579 /* The static control. */
32874aea 580 r.left = GAPBETWEEN;
581 r.top = cp->ypos;
582 r.right = lwid;
583 r.bottom = STATICHEIGHT;
8c3cd914 584 cp->ypos += r.bottom + GAPWITHIN;
585 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);
586
587 /* The edit control. */
32874aea 588 r.left = GAPBETWEEN;
589 r.top = cp->ypos;
590 r.right = lwid;
591 r.bottom = EDITHEIGHT;
8c3cd914 592 cp->ypos += r.bottom + GAPWITHIN;
593 doctl(cp, r, "EDIT",
32874aea 594 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
595 WS_EX_CLIENTEDGE, "", editid);
8c3cd914 596
597 /*
598 * The buttons (we should hold off on the list box until we
599 * know how big the buttons are).
600 */
601 va_start(ap, listid);
602 y = cp->ypos;
603 while (1) {
32874aea 604 char *btext = va_arg(ap, char *);
605 int bid;
606 if (!btext)
607 break;
608 bid = va_arg(ap, int);
609 r.left = rpos;
610 r.top = y;
611 r.right = rwid;
612 r.bottom = PUSHBTNHEIGHT;
613 y += r.bottom + GAPWITHIN;
614 doctl(cp, r, "BUTTON",
615 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
616 0, btext, bid);
8c3cd914 617 }
618
619 /* Compute list box height. LISTDEFHEIGHT, or height of buttons. */
620 y -= cp->ypos;
621 y -= GAPWITHIN;
32874aea 622 if (y < LISTDEFHEIGHT)
623 y = LISTDEFHEIGHT;
624 r.left = GAPBETWEEN;
625 r.top = cp->ypos;
626 r.right = lwid;
627 r.bottom = y;
8c3cd914 628 cp->ypos += y + GAPBETWEEN;
629 doctl(cp, r, "LISTBOX",
32874aea 630 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
631 LBS_NOTIFY | LBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);
8c3cd914 632}
633
634/*
635 * Another special control: the environment-variable setter. A
636 * static line first; then a pair of edit boxes with associated
637 * statics, and two buttons; then a list box.
638 */
639void envsetter(struct ctlpos *cp, char *stext, int sid,
32874aea 640 char *e1stext, int e1sid, int e1id,
641 char *e2stext, int e2sid, int e2id,
642 int listid, char *b1text, int b1id, char *b2text, int b2id)
643{
8c3cd914 644 RECT r;
32874aea 645 const int height = (STATICHEIGHT > EDITHEIGHT
646 && STATICHEIGHT >
647 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
648 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
8c3cd914 649 const static int percents[] = { 20, 35, 10, 25 };
650 int i, j, xpos, percent;
651 const int LISTHEIGHT = 42;
652
653 /* The static control. */
32874aea 654 r.left = GAPBETWEEN;
655 r.top = cp->ypos;
656 r.right = cp->width;
657 r.bottom = STATICHEIGHT;
8c3cd914 658 cp->ypos += r.bottom + GAPWITHIN;
659 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
660
661 /* The statics+edits+buttons. */
662 for (j = 0; j < 2; j++) {
32874aea 663 percent = 10;
664 for (i = 0; i < 4; i++) {
665 xpos = (cp->width + GAPBETWEEN) * percent / 100;
666 r.left = xpos + GAPBETWEEN;
667 percent += percents[i];
668 xpos = (cp->width + GAPBETWEEN) * percent / 100;
669 r.right = xpos - r.left;
670 r.top = cp->ypos;
671 r.bottom = (i == 0 ? STATICHEIGHT :
672 i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
673 r.top += (height - r.bottom) / 2;
674 if (i == 0) {
675 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
676 j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
677 } else if (i == 1) {
678 doctl(cp, r, "EDIT",
679 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
680 WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
681 } else if (i == 3) {
682 doctl(cp, r, "BUTTON",
683 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
684 0, j == 0 ? b1text : b2text, j == 0 ? b1id : b2id);
685 }
686 }
687 cp->ypos += height + GAPWITHIN;
8c3cd914 688 }
689
690 /* The list box. */
32874aea 691 r.left = GAPBETWEEN;
692 r.top = cp->ypos;
693 r.right = cp->width;
694 r.bottom = LISTHEIGHT;
8c3cd914 695 cp->ypos += r.bottom + GAPBETWEEN;
696 doctl(cp, r, "LISTBOX",
32874aea 697 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
698 | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
8c3cd914 699}
700
701/*
702 * Yet another special control: the character-class setter. A
703 * static, then a list, then a line containing a
704 * button-and-static-and-edit.
705 */
706void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
32874aea 707 char *btext, int bid, int eid, char *s2text, int s2id)
708{
8c3cd914 709 RECT r;
32874aea 710 const int height = (STATICHEIGHT > EDITHEIGHT
711 && STATICHEIGHT >
712 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
713 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
8c3cd914 714 const static int percents[] = { 30, 40, 30 };
715 int i, xpos, percent;
716 const int LISTHEIGHT = 66;
717
718 /* The static control. */
32874aea 719 r.left = GAPBETWEEN;
720 r.top = cp->ypos;
721 r.right = cp->width;
722 r.bottom = STATICHEIGHT;
8c3cd914 723 cp->ypos += r.bottom + GAPWITHIN;
724 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
725
726 /* The list box. */
32874aea 727 r.left = GAPBETWEEN;
728 r.top = cp->ypos;
729 r.right = cp->width;
730 r.bottom = LISTHEIGHT;
8c3cd914 731 cp->ypos += r.bottom + GAPWITHIN;
732 doctl(cp, r, "LISTBOX",
32874aea 733 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
734 | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
8c3cd914 735
736 /* The button+static+edit. */
737 percent = xpos = 0;
738 for (i = 0; i < 3; i++) {
32874aea 739 r.left = xpos + GAPBETWEEN;
740 percent += percents[i];
741 xpos = (cp->width + GAPBETWEEN) * percent / 100;
742 r.right = xpos - r.left;
743 r.top = cp->ypos;
744 r.bottom = (i == 0 ? PUSHBTNHEIGHT :
745 i == 1 ? STATICHEIGHT : EDITHEIGHT);
746 r.top += (height - r.bottom) / 2;
747 if (i == 0) {
748 doctl(cp, r, "BUTTON",
749 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
750 0, btext, bid);
751 } else if (i == 1) {
752 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_CENTER,
753 0, s2text, s2id);
754 } else if (i == 2) {
755 doctl(cp, r, "EDIT",
756 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
757 WS_EX_CLIENTEDGE, "", eid);
758 }
8c3cd914 759 }
760 cp->ypos += height + GAPBETWEEN;
761}
762
763/*
764 * A special control (horrors!). The colour editor. A static line;
765 * then on the left, a list box, and on the right, a sequence of
766 * two-part statics followed by a button.
767 */
768void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
32874aea 769 char *btext, int bid, ...)
770{
8c3cd914 771 RECT r;
772 int y;
773 va_list ap;
774 int lwid, rwid, rpos;
775 const int LISTHEIGHT = 66;
776
777 /* The static control. */
32874aea 778 r.left = GAPBETWEEN;
779 r.top = cp->ypos;
780 r.right = cp->width;
781 r.bottom = STATICHEIGHT;
8c3cd914 782 cp->ypos += r.bottom + GAPWITHIN;
783 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
32874aea 784
8c3cd914 785 rpos = GAPBETWEEN + 2 * (cp->width + GAPBETWEEN) / 3;
32874aea 786 lwid = rpos - 2 * GAPBETWEEN;
8c3cd914 787 rwid = cp->width + GAPBETWEEN - rpos;
788
789 /* The list box. */
32874aea 790 r.left = GAPBETWEEN;
791 r.top = cp->ypos;
792 r.right = lwid;
793 r.bottom = LISTHEIGHT;
8c3cd914 794 doctl(cp, r, "LISTBOX",
32874aea 795 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
796 | LBS_USETABSTOPS | LBS_NOTIFY, WS_EX_CLIENTEDGE, "", listid);
8c3cd914 797
798 /* The statics. */
799 y = cp->ypos;
800 va_start(ap, bid);
801 while (1) {
32874aea 802 char *ltext;
803 int lid, rid;
804 ltext = va_arg(ap, char *);
805 if (!ltext)
806 break;
807 lid = va_arg(ap, int);
808 rid = va_arg(ap, int);
809 r.top = y;
810 r.bottom = STATICHEIGHT;
811 y += r.bottom + GAPWITHIN;
812 r.left = rpos;
813 r.right = rwid / 2;
814 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, ltext, lid);
815 r.left = rpos + r.right;
816 r.right = rwid - r.right;
817 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0, "",
818 rid);
8c3cd914 819 }
820 va_end(ap);
821
822 /* The button. */
32874aea 823 r.top = y + 2 * GAPWITHIN;
824 r.bottom = PUSHBTNHEIGHT;
825 r.left = rpos;
826 r.right = rwid;
8c3cd914 827 doctl(cp, r, "BUTTON",
32874aea 828 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
829 0, btext, bid);
8c3cd914 830
831 cp->ypos += LISTHEIGHT + GAPBETWEEN;
832}
833
6e522441 834/*
ca20bfcf 835 * A special control for manipulating an ordered preference list
836 * (eg. for cipher selection).
837 * XXX: this is a rough hack and could be improved.
838 */
839void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
840 int sid, int listid, int upbid, int dnbid)
841{
842 const static int percents[] = { 5, 75, 20 };
843 RECT r;
844 int xpos, percent = 0, i;
845 const int DEFLISTHEIGHT = 52; /* XXX configurable? */
846 const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;
847 int totalheight;
848
849 /* Squirrel away IDs. */
850 hdl->listid = listid;
851 hdl->upbid = upbid;
852 hdl->dnbid = dnbid;
853
854 /* The static label. */
855 r.left = GAPBETWEEN;
856 r.top = cp->ypos;
857 r.right = cp->width;
858 r.bottom = STATICHEIGHT;
859 cp->ypos += r.bottom + GAPWITHIN;
860 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
861
862 /* XXX it'd be nice to centre the buttons wrt the listbox
863 * but we'd have to find out how high the latter actually is. */
864 if (DEFLISTHEIGHT > BTNSHEIGHT) {
865 totalheight = DEFLISTHEIGHT;
866 } else {
867 totalheight = BTNSHEIGHT;
868 }
869
870 for (i=0; i<3; i++) {
871 int left, wid;
872 xpos = (cp->width + GAPBETWEEN) * percent / 100;
873 left = xpos + GAPBETWEEN;
874 percent += percents[i];
875 xpos = (cp->width + GAPBETWEEN) * percent / 100;
876 wid = xpos - left;
877
878 switch (i) {
879 case 1:
880 /* The drag list box. */
881 r.left = left; r.right = wid;
882 r.top = cp->ypos; r.bottom = totalheight;
883 {
884 HWND ctl;
885 ctl = doctl(cp, r, "LISTBOX",
886 WS_CHILD | WS_VISIBLE | WS_TABSTOP |
887 WS_VSCROLL | LBS_HASSTRINGS,
888 WS_EX_CLIENTEDGE,
889 "", listid);
890 MakeDragList(ctl);
891 }
892 break;
893
894 case 2:
895 /* The "Up" and "Down" buttons. */
896 /* XXX worry about accelerators if we have more than one
897 * prefslist on a panel */
898 r.left = left; r.right = wid;
899 r.top = cp->ypos; r.bottom = PUSHBTNHEIGHT;
900 doctl(cp, r, "BUTTON",
901 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
902 0, "&Up", upbid);
903
904 r.left = left; r.right = wid;
905 r.top = cp->ypos + PUSHBTNHEIGHT + GAPBETWEEN;
906 r.bottom = PUSHBTNHEIGHT;
907 doctl(cp, r, "BUTTON",
908 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
909 0, "&Down", dnbid);
910
911 break;
912
913 }
914 }
915
916 cp->ypos += totalheight + GAPBETWEEN;
917
918}
919
920/*
921 * Helper function for prefslist: move item in list box.
922 */
923static void pl_moveitem(HWND hwnd, int listid, int src, int dst)
924{
925 int tlen, val;
926 char *txt;
927 /* Get the item's data. */
928 tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0);
929 txt = smalloc(tlen+1);
930 SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt);
931 val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0);
932 /* Deselect old location. */
933 SendDlgItemMessage (hwnd, listid, LB_SETSEL, FALSE, src);
934 /* Delete it at the old location. */
935 SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0);
936 /* Insert it at new location. */
937 SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst,
938 (LPARAM) txt);
939 SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst,
940 (LPARAM) val);
941 /* Set selection. */
942 SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0);
943 sfree (txt);
944}
945
946int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
947{
948 int ret;
949 POINT uppoint, downpoint;
950 int updist, downdist, upitem, downitem, i;
951
952 /*
953 * Ghastly hackery to try to figure out not which
954 * _item_, but which _gap between items_, the user
955 * is pointing at. We do this by first working out
956 * which list item is under the cursor, and then
957 * working out how far the cursor would have to
958 * move up or down before the answer was different.
959 * Then we put the insertion point _above_ the
960 * current item if the upper edge is closer than
961 * the lower edge, or _below_ it if vice versa.
962 */
963 ret = LBItemFromPt(hwnd, cursor, scroll);
ca20bfcf 964 if (ret == -1)
965 return ret;
966 ret = LBItemFromPt(hwnd, cursor, FALSE);
ca20bfcf 967 updist = downdist = 0;
968 for (i = 1; i < 4096 && (!updist || !downdist); i++) {
969 uppoint = downpoint = cursor;
970 uppoint.y -= i;
971 downpoint.y += i;
972 upitem = LBItemFromPt(hwnd, uppoint, FALSE);
973 downitem = LBItemFromPt(hwnd, downpoint, FALSE);
974 if (!updist && upitem != ret)
975 updist = i;
976 if (!downdist && downitem != ret)
977 downdist = i;
978 }
979 if (downdist < updist)
980 ret++;
981 return ret;
982}
983
984/*
985 * Handler for prefslist above.
986 */
987int handle_prefslist(struct prefslist *hdl,
988 int *array, int maxmemb,
989 int is_dlmsg, HWND hwnd,
990 WPARAM wParam, LPARAM lParam)
991{
992 int i;
993 int ret;
994
995 if (is_dlmsg) {
996
cdcbdf3b 997 if ((int)wParam == hdl->listid) {
ca20bfcf 998 DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam;
999 int dest;
1000 switch (dlm->uNotification) {
1001 case DL_BEGINDRAG:
1002 hdl->dummyitem =
1003 SendDlgItemMessage(hwnd, hdl->listid,
1004 LB_ADDSTRING, 0, (LPARAM) "");
1005
1006 hdl->srcitem = LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);
1007 hdl->dragging = 0;
1008 /* XXX hack Q183115 */
1009 SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
1010 ret = 1; break;
1011 case DL_CANCELDRAG:
1012 DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */
1013 SendDlgItemMessage(hwnd, hdl->listid,
1014 LB_DELETESTRING, hdl->dummyitem, 0);
1015 hdl->dragging = 0;
1016 ret = 1; break;
1017 case DL_DRAGGING:
1018 hdl->dragging = 1;
1019 dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
1020 if (dest > hdl->dummyitem) dest = hdl->dummyitem;
1021 DrawInsert (hwnd, dlm->hWnd, dest);
1022 if (dest >= 0)
1023 SetWindowLong(hwnd, DWL_MSGRESULT, DL_MOVECURSOR);
1024 else
1025 SetWindowLong(hwnd, DWL_MSGRESULT, DL_STOPCURSOR);
1026 ret = 1; break;
1027 case DL_DROPPED:
1028 ret = 1;
1029 if (!hdl->dragging) break;
1030 dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
1031 if (dest > hdl->dummyitem) dest = hdl->dummyitem;
1032 DrawInsert (hwnd, dlm->hWnd, -1);
1033 SendDlgItemMessage(hwnd, hdl->listid,
1034 LB_DELETESTRING, hdl->dummyitem, 0);
1035 hdl->dragging = 0;
1036 if (dest >= 0) {
1037 /* Correct for "missing" item. This means you can't drag
1038 * an item to the end, but that seems to be the way this
1039 * control is used. */
1040 if (dest > hdl->srcitem) dest--;
1041 pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);
1042 }
1043 ret = 1; break;
1044 }
1045 }
1046
1047 } else {
1048
1049 ret = 0;
1050 if (((LOWORD(wParam) == hdl->upbid) ||
1051 (LOWORD(wParam) == hdl->dnbid)) &&
1052 ((HIWORD(wParam) == BN_CLICKED) ||
1053 (HIWORD(wParam) == BN_DOUBLECLICKED))) {
1054 /* Move an item up or down the list. */
1055 /* Get the current selection, if any. */
1056 int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0);
1057 if (selection == LB_ERR) {
1058 MessageBeep(0);
1059 } else {
1060 int nitems;
1061 /* Get the total number of items. */
1062 nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0);
1063 /* Should we do anything? */
1064 if (LOWORD(wParam) == hdl->upbid && (selection > 0))
1065 pl_moveitem(hwnd, hdl->listid, selection, selection - 1);
1066 else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))
1067 pl_moveitem(hwnd, hdl->listid, selection, selection + 1);
1068 }
1069
1070 }
1071
1072 }
1073
1074 /* Update array to match the list box. */
1075 for (i=0; i < maxmemb; i++)
1076 array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,
1077 i, 0);
1078
1079 return ret;
1080
1081}
1082
1083/*
6e522441 1084 * A progress bar (from Common Controls). We like our progress bars
1085 * to be smooth and unbroken, without those ugly divisions; some
1086 * older compilers may not support that, but that's life.
1087 */
32874aea 1088void progressbar(struct ctlpos *cp, int id)
1089{
6e522441 1090 RECT r;
1091
32874aea 1092 r.left = GAPBETWEEN;
1093 r.top = cp->ypos;
1094 r.right = cp->width;
1095 r.bottom = PROGBARHEIGHT;
6e522441 1096 cp->ypos += r.bottom + GAPBETWEEN;
1097
32874aea 1098 doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE
6e522441 1099#ifdef PBS_SMOOTH
32874aea 1100 | PBS_SMOOTH
6e522441 1101#endif
32874aea 1102 , WS_EX_CLIENTEDGE, "", id);
6e522441 1103}
d74d141c 1104
1105/*
1106 * Another special control: the forwarding options setter. First a
1107 * list box; next a static header line, introducing a pair of edit
1108 * boxes with associated statics, another button, and a radio
1109 * button pair.
1110 */
1111void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
1112 char *e1stext, int e1sid, int e1id,
1113 char *e2stext, int e2sid, int e2id,
1114 char *btext, int bid)
1115{
1116 RECT r;
1117 const int height = (STATICHEIGHT > EDITHEIGHT
1118 && STATICHEIGHT >
1119 PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
1120 PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
1121 const static int percents[] = { 25, 35, 15, 25 };
1122 int i, j, xpos, percent;
1123 const int LISTHEIGHT = 42;
1124
1125 /* The list box. */
1126 r.left = GAPBETWEEN;
1127 r.top = cp->ypos;
1128 r.right = cp->width;
1129 r.bottom = LISTHEIGHT;
1130 cp->ypos += r.bottom + GAPBETWEEN;
1131 doctl(cp, r, "LISTBOX",
1132 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
1133 | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
1134
1135 /* The static control. */
1136 r.left = GAPBETWEEN;
1137 r.top = cp->ypos;
1138 r.right = cp->width;
1139 r.bottom = STATICHEIGHT;
1140 cp->ypos += r.bottom + GAPWITHIN;
1141 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
1142
1143 /* The statics+edits+buttons. */
1144 for (j = 0; j < 2; j++) {
1145 percent = 0;
1146 for (i = 0; i < (j ? 2 : 4); i++) {
1147 xpos = (cp->width + GAPBETWEEN) * percent / 100;
1148 r.left = xpos + GAPBETWEEN;
1149 percent += percents[i];
1150 if (j==1 && i==1) percent = 100;
1151 xpos = (cp->width + GAPBETWEEN) * percent / 100;
1152 r.right = xpos - r.left;
1153 r.top = cp->ypos;
1154 r.bottom = (i == 0 ? STATICHEIGHT :
1155 i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
1156 r.top += (height - r.bottom) / 2;
1157 if (i == 0) {
1158 doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
1159 j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
1160 } else if (i == 1) {
1161 doctl(cp, r, "EDIT",
1162 WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
1163 WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
1164 } else if (i == 3) {
1165 doctl(cp, r, "BUTTON",
1166 WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1167 0, btext, bid);
1168 }
1169 }
1170 cp->ypos += height + GAPWITHIN;
1171 }
1172}