source: trunk/src/helpers/dialog.c@ 122

Last change on this file since 122 was 122, checked in by umoeller, 24 years ago

Misc changes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 95.8 KB
Line 
1
2/*
3 *@@sourcefile dialog.c:
4 * contains PM helper functions to create and
5 * auto-format dialogs from control arrays in memory.
6 *
7 * See dlghCreateDlg for details.
8 *
9 * In addition, this has dlghMessageBox (a WinMessageBox
10 * replacement) and some helper functions for simulating
11 * dialog behavior in regular window procs (see
12 * dlghSetPrevFocus and others).
13 *
14 * Usage: All PM programs.
15 *
16 * Function prefixes (new with V0.81):
17 * -- dlg* Dialog functions
18 *
19 * Note: Version numbering in this file relates to XWorkplace version
20 * numbering.
21 *
22 *@@added V0.9.9 (2001-04-01) [umoeller]
23 *@@header "helpers\dialog.h"
24 */
25
26/*
27 * Copyright (C) 2001 Ulrich M”ller.
28 * This file is part of the "XWorkplace helpers" source package.
29 * This is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published
31 * by the Free Software Foundation, in version 2 as it comes in the
32 * "COPYING" file of the XWorkplace main distribution.
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 */
38
39#define OS2EMX_PLAIN_CHAR
40 // this is needed for "os2emx.h"; if this is defined,
41 // emx will define PSZ as _signed_ char, otherwise
42 // as unsigned char
43
44#define INCL_DOSERRORS
45
46#define INCL_WINWINDOWMGR
47#define INCL_WINFRAMEMGR
48#define INCL_WINDIALOGS
49#define INCL_WININPUT
50#define INCL_WINSTATICS
51#define INCL_WINBUTTONS
52#define INCL_WINENTRYFIELDS
53#define INCL_WINSYS
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPIBITMAPS
57#define INCL_GPILCIDS
58#include <os2.h>
59
60#include <stdlib.h>
61#include <string.h>
62#include <stdio.h>
63
64#include "setup.h" // code generation and debugging options
65
66#include "helpers\comctl.h"
67#include "helpers\dialog.h"
68#include "helpers\gpih.h"
69#include "helpers\linklist.h"
70#include "helpers\standards.h"
71#include "helpers\stringh.h"
72#include "helpers\winh.h"
73#include "helpers\xstring.h"
74
75/*
76 *@@category: Helpers\PM helpers\Dialog templates
77 */
78
79/* ******************************************************************
80 *
81 * Private declarations
82 *
83 ********************************************************************/
84
85/*
86 *@@ DLGPRIVATE:
87 * private data to the dlg manager, allocated
88 * by dlghCreateDlg.
89 *
90 * This only exists while the dialog is being
91 * created and is not stored with the new dialog.
92 */
93
94typedef struct _DLGPRIVATE
95{
96 // public data
97 HWND hwndDlg;
98
99 // definition data (private)
100 LINKLIST llTables;
101
102 HWND hwndFirstFocus,
103 hwndDefPushbutton; // V0.9.14 (2001-08-21) [umoeller]
104
105 POINTL ptlTotalOfs;
106
107 LINKLIST llControls; // linked list of all PCOLUMNDEF structs,
108 // in the order in which windows were
109 // created
110
111 const char *pcszControlsFont; // from dlghCreateDlg
112
113 // V0.9.14 (2001-08-01) [umoeller]
114 HPS hps;
115 const char *pcszFontLast;
116 LONG lcidLast;
117 FONTMETRICS fmLast;
118
119} DLGPRIVATE, *PDLGPRIVATE;
120
121typedef struct _COLUMNDEF *PCOLUMNDEF;
122typedef struct _ROWDEF *PROWDEF;
123typedef struct _TABLEDEF *PTABLEDEF;
124
125/*
126 *@@ CONTROLPOS:
127 * control position. We don't want to use SWP.
128 */
129
130typedef struct _CONTROLPOS
131{
132 LONG x,
133 y,
134 cx,
135 cy;
136} CONTROLPOS, *PCONTROLPOS;
137
138/*
139 *@@ COLUMNDEF:
140 * representation of a table column.
141 * This is stored in a linked list in ROWDEF.
142 *
143 * A table column represents either a PM control
144 * window or another table, which may therefore
145 * be nested.
146 */
147
148typedef struct _COLUMNDEF
149{
150 PROWDEF pOwningRow; // row whose linked list this column belongs to
151
152 BOOL fIsNestedTable; // if TRUE, pvDefinition points to a nested TABLEDEF;
153 // if FALSE, pvDefinition points to a CONTROLDEF as
154 // specified by the caller
155
156 PVOID pvDefinition; // either a PTABLEDEF or a PCONTROLDEF
157
158 CONTROLPOS cpControl, // real pos and size of control
159 cpColumn; // pos and size of column; can be wider, spacings applied
160
161 HWND hwndControl; // created control; NULLHANDLE for tables always
162
163} COLUMNDEF;
164
165/*
166 *@@ ROWDEF:
167 *
168 */
169
170typedef struct _ROWDEF
171{
172 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
173
174 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
175
176 ULONG flRowFormat; // one of:
177 // -- ROW_VALIGN_BOTTOM 0x0000
178 // -- ROW_VALIGN_CENTER 0x0001
179 // -- ROW_VALIGN_TOP 0x0002
180
181 CONTROLPOS cpRow;
182
183} ROWDEF;
184
185/*
186 *@@ TABLEDEF:
187 *
188 */
189
190typedef struct _TABLEDEF
191{
192 LINKLIST llRows; // contains ROWDEF structs, no auto-free
193
194 PCONTROLDEF pCtlDef; // if != NULL, we create a PM control around the table
195
196 CONTROLPOS cpTable;
197
198} TABLEDEF;
199
200/*
201 *@@ PROCESSMODE:
202 *
203 */
204
205typedef enum _PROCESSMODE
206{
207 PROCESS_CALC_SIZES, // step 1
208 PROCESS_CALC_POSITIONS, // step 3
209 PROCESS_CREATE_CONTROLS // step 4
210} PROCESSMODE;
211
212/* ******************************************************************
213 *
214 * Worker routines
215 *
216 ********************************************************************/
217
218#define PM_GROUP_SPACING_X 16
219#define PM_GROUP_SPACING_TOP 20
220
221APIRET ProcessTable(PTABLEDEF pTableDef,
222 const CONTROLPOS *pcpTable,
223 PROCESSMODE ProcessMode,
224 PDLGPRIVATE pDlgData);
225
226/*
227 *@@ SetDlgFont:
228 *
229 *@@added V0.9.16 (2001-10-11) [umoeller]
230 */
231
232VOID SetDlgFont(PCONTROLDEF pControlDef,
233 PDLGPRIVATE pDlgData)
234{
235 LONG lPointSize = 0;
236 const char *pcszFontThis = pControlDef->pcszFont;
237 // can be NULL,
238 // or CTL_COMMON_FONT
239
240 if (pcszFontThis == CTL_COMMON_FONT)
241 pcszFontThis = pDlgData->pcszControlsFont;
242
243 if (!pDlgData->hps)
244 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
245
246 // check if we can reuse font data from last time
247 // V0.9.14 (2001-08-01) [umoeller]
248 if (strhcmp(pcszFontThis, // can be NULL!
249 pDlgData->pcszFontLast))
250 {
251 // different font than last time:
252
253 // delete old font?
254 if (pDlgData->lcidLast)
255 {
256 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT); // LCID_DEFAULT == 0
257 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
258 }
259
260 if (pcszFontThis)
261 {
262 // create new font
263 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
264 FALSE,
265 pDlgData->hps,
266 pcszFontThis,
267 &pDlgData->fmLast,
268 &lPointSize);
269
270 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
271 if (pDlgData->fmLast.fsDefn & FM_DEFN_OUTLINE)
272 gpihSetPointSize(pDlgData->hps, lPointSize);
273 }
274 else
275 {
276 // use default font:
277 // @@todo handle presparams, maybe inherited?
278 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
279 GpiQueryFontMetrics(pDlgData->hps,
280 sizeof(pDlgData->fmLast),
281 &pDlgData->fmLast);
282 }
283
284 pDlgData->pcszFontLast = pcszFontThis; // can be NULL
285 }
286}
287
288/*
289 *@@ CalcAutoSizeText:
290 *
291 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
292 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed broken fonts
293 *@@changed V0.9.14 (2001-08-01) [umoeller]: now caching fonts, which is significantly faster
294 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
295 */
296
297APIRET CalcAutoSizeText(PCONTROLDEF pControlDef,
298 BOOL fMultiLine, // in: if TRUE, multiple lines
299 PSIZEL pszlAuto, // out: computed size
300 PDLGPRIVATE pDlgData)
301{
302 APIRET arc = NO_ERROR;
303
304 SetDlgFont(pControlDef, pDlgData);
305
306 pszlAuto->cy = pDlgData->fmLast.lMaxBaselineExt
307 + pDlgData->fmLast.lExternalLeading;
308
309 // ok, we FINALLY have a font now...
310 // get the control string and see how much space it needs
311 if ( (pControlDef->pcszText)
312 && (pControlDef->pcszText != (PCSZ)-1)
313 )
314 {
315 // do we have multiple lines?
316 if (fMultiLine)
317 {
318 RECTL rcl = {0, 0, 0, 0};
319 if (pControlDef->szlControlProposed.cx != -1)
320 rcl.xRight = pControlDef->szlControlProposed.cx; // V0.9.12 (2001-05-31) [umoeller]
321 else
322 rcl.xRight = winhQueryScreenCX() * 2 / 3;
323 if (pControlDef->szlControlProposed.cy != -1)
324 rcl.yTop = pControlDef->szlControlProposed.cy; // V0.9.12 (2001-05-31) [umoeller]
325 else
326 rcl.yTop = winhQueryScreenCY() * 2 / 3;
327
328 winhDrawFormattedText(pDlgData->hps,
329 &rcl,
330 pControlDef->pcszText,
331 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
332 pszlAuto->cx = rcl.xRight - rcl.xLeft;
333 pszlAuto->cy = rcl.yTop - rcl.yBottom;
334 }
335 else
336 {
337 POINTL aptl[TXTBOX_COUNT];
338 GpiQueryTextBox(pDlgData->hps,
339 strlen(pControlDef->pcszText),
340 (PCH)pControlDef->pcszText,
341 TXTBOX_COUNT,
342 aptl);
343 pszlAuto->cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
344 }
345 }
346 else
347 arc = DLGERR_INVALID_CONTROL_TITLE;
348
349 return (arc);
350}
351
352/*
353 *@@ CalcAutoSize:
354 *
355 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
356 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
357 */
358
359APIRET CalcAutoSize(PCONTROLDEF pControlDef,
360 PSIZEL pszlAuto, // out: computed size
361 PDLGPRIVATE pDlgData)
362{
363 APIRET arc = NO_ERROR;
364
365 // dumb defaults
366 pszlAuto->cx = 100;
367 pszlAuto->cy = 30;
368
369 switch ((ULONG)pControlDef->pcszClass)
370 {
371 case 0xffff0003L: // WC_BUTTON:
372 if (!(arc = CalcAutoSizeText(pControlDef,
373 FALSE, // no multiline
374 pszlAuto,
375 pDlgData)))
376 {
377 if (pControlDef->flStyle & ( BS_AUTOCHECKBOX
378 | BS_AUTORADIOBUTTON
379 | BS_AUTO3STATE
380 | BS_3STATE
381 | BS_CHECKBOX
382 | BS_RADIOBUTTON))
383 {
384 // give a little extra width for the box bitmap
385 pszlAuto->cx += 20; // @@todo
386 // and height
387 pszlAuto->cy += 2;
388 }
389 else if (pControlDef->flStyle & BS_BITMAP)
390 ;
391 else if (pControlDef->flStyle & (BS_ICON | BS_MINIICON))
392 ;
393 // we can't test for BS_PUSHBUTTON because that's 0x0000
394 else if (!(pControlDef->flStyle & BS_USERBUTTON))
395 {
396 pszlAuto->cx += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) + 15);
397 pszlAuto->cy += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) + 15);
398 }
399 }
400 break;
401
402 case 0xffff0005L: // WC_STATIC:
403 if ((pControlDef->flStyle & 0x0F) == SS_TEXT)
404 arc = CalcAutoSizeText(pControlDef,
405 ((pControlDef->flStyle & DT_WORDBREAK) != 0),
406 pszlAuto,
407 pDlgData);
408 else if ((pControlDef->flStyle & 0x0F) == SS_BITMAP)
409 {
410 HBITMAP hbm;
411 if (hbm = (HBITMAP)pControlDef->pcszText)
412 {
413 BITMAPINFOHEADER2 bmih2;
414 ZERO(&bmih2);
415 bmih2.cbFix = sizeof(bmih2);
416 if (GpiQueryBitmapInfoHeader(hbm,
417 &bmih2))
418 {
419 pszlAuto->cx = bmih2.cx;
420 pszlAuto->cy = bmih2.cy;
421 }
422 else
423 arc = DLGERR_INVALID_STATIC_BITMAP;
424 }
425 }
426 else if ((pControlDef->flStyle & 0x0F) == SS_ICON)
427 {
428 pszlAuto->cx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
429 pszlAuto->cy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
430 }
431 break;
432
433 default:
434 // any other control (just to be safe):
435 SetDlgFont(pControlDef, pDlgData);
436 pszlAuto->cx = 50;
437 pszlAuto->cy = pDlgData->fmLast.lMaxBaselineExt
438 + pDlgData->fmLast.lExternalLeading
439 + 5; // some space
440 }
441
442 return (arc);
443}
444
445/*
446 *@@ ColumnCalcSizes:
447 * implementation for PROCESS_CALC_SIZES in
448 * ProcessColumn.
449 *
450 *@@added V0.9.15 (2001-08-26) [umoeller]
451 *@@changed V0.9.16 (2001-10-15) [umoeller]: fixed ugly group table spacings
452 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
453 */
454
455APIRET ColumnCalcSizes(PCOLUMNDEF pColumnDef,
456 PDLGPRIVATE pDlgData)
457{
458 APIRET arc = NO_ERROR;
459
460 ULONG ulXSpacing = 0,
461 ulYSpacing = 0;
462 if (pColumnDef->fIsNestedTable)
463 {
464 // nested table: recurse!!
465 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
466 if (!(arc = ProcessTable(pTableDef,
467 NULL,
468 PROCESS_CALC_SIZES,
469 pDlgData)))
470 {
471 // store the size of the sub-table
472 pColumnDef->cpControl.cx = pTableDef->cpTable.cx;
473 pColumnDef->cpControl.cy = pTableDef->cpTable.cy;
474
475 // should we create a PM control around the table?
476 if (pTableDef->pCtlDef)
477 {
478 // yes: make this wider
479 ulXSpacing = 2 * PM_GROUP_SPACING_X;
480 ulYSpacing = // 3 * PM_GROUP_SPACING_X;
481 (PM_GROUP_SPACING_X + PM_GROUP_SPACING_TOP);
482 }
483 }
484 }
485 else
486 {
487 // no nested table, but control:
488 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
489 PSIZEL pszl = &pControlDef->szlControlProposed;
490 SIZEL szlAuto;
491
492 if ( (pszl->cx == -1)
493 || (pszl->cy == -1)
494 )
495 {
496 arc = CalcAutoSize(pControlDef,
497 &szlAuto,
498 pDlgData);
499 }
500
501 if (!arc)
502 {
503 if (pszl->cx == -1)
504 pColumnDef->cpControl.cx = szlAuto.cx;
505 else
506 pColumnDef->cpControl.cx = pszl->cx;
507
508 if (pszl->cy == -1)
509 pColumnDef->cpControl.cy = szlAuto.cy;
510 else
511 pColumnDef->cpControl.cy = pszl->cy;
512
513 // @@todo hack sizes
514
515 ulXSpacing
516 = ulYSpacing
517 = (2 * pControlDef->ulSpacing);
518 }
519 }
520
521 pColumnDef->cpColumn.cx = pColumnDef->cpControl.cx
522 + ulXSpacing;
523 pColumnDef->cpColumn.cy = pColumnDef->cpControl.cy
524 + ulYSpacing;
525
526 return (arc);
527}
528
529/*
530 *@@ ColumnCalcPositions:
531 * implementation for PROCESS_CALC_POSITIONS in
532 * ProcessColumn.
533 *
534 *@@added V0.9.15 (2001-08-26) [umoeller]
535 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
536 */
537
538APIRET ColumnCalcPositions(PCOLUMNDEF pColumnDef,
539 PROWDEF pOwningRow, // in: current row from ProcessRow
540 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
541 PDLGPRIVATE pDlgData)
542{
543 APIRET arc = NO_ERROR;
544
545 // calculate column position: this includes spacing
546 ULONG ulSpacing = 0;
547
548 // column position = *plX on ProcessRow stack
549 pColumnDef->cpColumn.x = *plX;
550 pColumnDef->cpColumn.y = pOwningRow->cpRow.y;
551
552 // check vertical alignment of row;
553 // we might need to increase column y
554 switch (pOwningRow->flRowFormat & ROW_VALIGN_MASK)
555 {
556 // case ROW_VALIGN_BOTTOM: // do nothing
557
558 case ROW_VALIGN_CENTER:
559 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
560 pColumnDef->cpColumn.y
561 += ( (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy)
562 / 2);
563 break;
564
565 case ROW_VALIGN_TOP:
566 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
567 pColumnDef->cpColumn.y
568 += (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy);
569 break;
570 }
571
572 if (pColumnDef->fIsNestedTable)
573 {
574 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
575 // should we create a PM control around the table?
576 if (pTableDef->pCtlDef)
577 // yes:
578 ulSpacing = PM_GROUP_SPACING_X / 2; // V0.9.16 (2001-10-15) [umoeller]
579 }
580 else
581 {
582 // no nested table, but control:
583 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
584 ulSpacing = pControlDef->ulSpacing;
585 }
586
587 // increase plX by column width
588 *plX += pColumnDef->cpColumn.cx;
589
590 // calculate CONTROL pos from COLUMN pos by applying spacing
591 pColumnDef->cpControl.x = pColumnDef->cpColumn.x
592 + ulSpacing;
593 pColumnDef->cpControl.y = pColumnDef->cpColumn.y
594 + ulSpacing;
595
596 if (pColumnDef->fIsNestedTable)
597 {
598 // nested table:
599 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
600
601 // recurse!! to create windows for the sub-table
602 arc = ProcessTable(pTableDef,
603 &pColumnDef->cpControl, // start pos for new table
604 PROCESS_CALC_POSITIONS,
605 pDlgData);
606 }
607
608 return (arc);
609}
610
611/*
612 *@@ ColumnCreateControls:
613 * implementation for PROCESS_CREATE_CONTROLS in
614 * ProcessColumn.
615 *
616 *@@added V0.9.15 (2001-08-26) [umoeller]
617 *@@changed V0.9.16 (2001-10-15) [umoeller]: fixed ugly group table spacings
618 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed entry field ES_MARGIN positioning
619 */
620
621APIRET ColumnCreateControls(PCOLUMNDEF pColumnDef,
622 PDLGPRIVATE pDlgData)
623{
624 APIRET arc = NO_ERROR;
625
626 PCONTROLPOS pcp = NULL;
627 PCONTROLDEF pControlDef = NULL;
628 const char *pcszTitle = NULL;
629 ULONG flStyle = 0;
630 LHANDLE lHandleSet = NULLHANDLE;
631 ULONG flOld = 0;
632
633 LONG x, cx, y, cy; // for combo box hacks
634
635 if (pColumnDef->fIsNestedTable)
636 {
637 // nested table:
638 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
639
640 // recurse!!
641 if (!(arc = ProcessTable(pTableDef,
642 NULL,
643 PROCESS_CREATE_CONTROLS,
644 pDlgData)))
645 {
646 // should we create a PM control around the table?
647 // (do this AFTER the other controls from recursing,
648 // otherwise the stupid container doesn't show up)
649 if (pTableDef->pCtlDef)
650 {
651 // yes:
652 pcp = &pColumnDef->cpColumn; // !! not control
653 pControlDef = pTableDef->pCtlDef;
654 pcszTitle = pControlDef->pcszText;
655 flStyle = pControlDef->flStyle;
656
657 x = pcp->x + pDlgData->ptlTotalOfs.x;
658 cx = pcp->cx - PM_GROUP_SPACING_X;
659 // note, just one spacing: for the _column_ size,
660 // we have specified 2 X spacings
661 y = pcp->y + pDlgData->ptlTotalOfs.y;
662 // cy = pcp->cy - PM_GROUP_SPACING_X;
663 cy = pcp->cy - /* PM_GROUP_SPACING_X - */ PM_GROUP_SPACING_TOP / 2;
664 }
665 }
666 }
667 else
668 {
669 // no nested table, but control:
670 pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
671 pcp = &pColumnDef->cpControl;
672 pcszTitle = pControlDef->pcszText;
673 flStyle = pControlDef->flStyle;
674
675 x = pcp->x + pDlgData->ptlTotalOfs.x;
676 cx = pcp->cx;
677 y = pcp->y + pDlgData->ptlTotalOfs.y;
678 cy = pcp->cy;
679
680 // change the title if this is a static with SS_BITMAP;
681 // we have used a HBITMAP in there!
682 if ( ((ULONG)pControlDef->pcszClass == 0xffff0005L) // WC_STATIC:
683 && ( ((flStyle & 0x0F) == SS_BITMAP)
684 || ((flStyle & 0x0F) == SS_ICON)
685 )
686 )
687 {
688 // change style flag to not use SS_BITMAP nor SS_ICON;
689 // control creation fails otherwise (stupid, stupid PM)
690 flOld = flStyle;
691 flStyle = ((flStyle & ~0x0F) | SS_FGNDFRAME);
692 pcszTitle = "";
693 lHandleSet = (LHANDLE)pControlDef->pcszText;
694 }
695 // hack the stupid drop-down combobox which doesn't
696 // expand otherwise (the size of the drop-down is
697 // the full size of the control... duh)
698 else if ( ((ULONG)pControlDef->pcszClass == 0xffff0002L)
699 && (flStyle & (CBS_DROPDOWN | CBS_DROPDOWNLIST))
700 )
701 {
702 y -= 100;
703 cy += 100;
704 }
705 // the stupid entry field resizes itself if it has
706 // the ES_MARGIN style, so correlate that too... dammit
707 // V0.9.16 (2001-12-08) [umoeller]
708 else if ( ((ULONG)pControlDef->pcszClass == 0xffff0006L)
709 && (flStyle & ES_MARGIN)
710 )
711 {
712 LONG cxMargin = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
713 LONG cyMargin = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
714
715 x += cxMargin;
716 y += cxMargin;
717 }
718 }
719
720 if (pcp && pControlDef)
721 {
722 // create something:
723 // PPRESPARAMS ppp = NULL;
724
725 const char *pcszFont = pControlDef->pcszFont;
726 // can be NULL, or CTL_COMMON_FONT
727 if (pcszFont == CTL_COMMON_FONT)
728 pcszFont = pDlgData->pcszControlsFont;
729
730 /* if (pcszFont)
731 winhStorePresParam(&ppp,
732 PP_FONTNAMESIZE,
733 strlen(pcszFont),
734 (PVOID)pcszFont); */
735
736 if (pColumnDef->hwndControl
737 = WinCreateWindow(pDlgData->hwndDlg, // parent
738 (PSZ)pControlDef->pcszClass,
739 (pcszTitle) // hacked
740 ? (PSZ)pcszTitle
741 : "",
742 flStyle, // hacked
743 x,
744 y,
745 cx,
746 cy,
747 pDlgData->hwndDlg, // owner
748 HWND_BOTTOM,
749 pControlDef->usID,
750 pControlDef->pvCtlData,
751 NULL))
752 {
753 if (lHandleSet)
754 {
755 // subclass the damn static
756 if ((flOld & 0x0F) == SS_ICON)
757 // this was a static:
758 ctlPrepareStaticIcon(pColumnDef->hwndControl,
759 1);
760 else
761 // this was a bitmap:
762 ctlPrepareStretchedBitmap(pColumnDef->hwndControl,
763 TRUE);
764
765 WinSendMsg(pColumnDef->hwndControl,
766 SM_SETHANDLE,
767 (MPARAM)lHandleSet,
768 0);
769 }
770 else
771 if (pcszFont)
772 // we must set the font explicitly here...
773 // doesn't always work with WinCreateWindow
774 // presparams parameter, for some reason
775 // V0.9.12 (2001-05-31) [umoeller]
776 winhSetWindowFont(pColumnDef->hwndControl,
777 pcszFont);
778
779 lstAppendItem(&pDlgData->llControls,
780 pColumnDef);
781
782 // if this is the first control with WS_TABSTOP,
783 // we give it the focus later
784 if ( (flStyle & WS_TABSTOP)
785 && (!pDlgData->hwndFirstFocus)
786 )
787 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
788
789 // if this is the first default push button,
790 // go store it too
791 // V0.9.14 (2001-08-21) [umoeller]
792 if ( (!pDlgData->hwndDefPushbutton)
793 && ((ULONG)pControlDef->pcszClass == 0xffff0003L)
794 && (pControlDef->flStyle & BS_DEFAULT)
795 )
796 pDlgData->hwndDefPushbutton = pColumnDef->hwndControl;
797 }
798 else
799 // V0.9.14 (2001-08-03) [umoeller]
800 arc = DLGERR_CANNOT_CREATE_CONTROL;
801 }
802
803 return (arc);
804}
805
806/*
807 *@@ ProcessColumn:
808 * processes a column, which per definition is either
809 * a control or a nested subtable.
810 *
811 * A column is part of a row, which in turn is part
812 * of a table. There can be several columns in a row,
813 * and several rows in a table.
814 *
815 * Since tables may be specified as columns, it is
816 * possible to produce complex dialog layouts by
817 * nesting tables.
818 *
819 * This does the following:
820 *
821 * -- PROCESS_CALC_SIZES: size is taken from control def,
822 * or for tables, this produces a recursive ProcessTable
823 * call.
824 * Preconditions: none.
825 *
826 * -- PROCESS_CALC_POSITIONS: position of each column
827 * is taken from *plX, which is increased by the
828 * column width by this call.
829 *
830 * Preconditions: Owning row must already have its
831 * y position properly set, or we can't compute
832 * ours. Besides, plX must point to the current X
833 * in the row and will be incremented by the columns
834 * size here.
835 *
836 * -- PROCESS_CREATE_CONTROLS: well, creates the controls.
837 *
838 * For tables, this recurses again. If the table has
839 * a string assigned, this also produces a group box
840 * after the recursion.
841 *
842 *@@changed V0.9.12 (2001-05-31) [umoeller]: added control data
843 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed font problems
844 */
845
846APIRET ProcessColumn(PCOLUMNDEF pColumnDef,
847 PROWDEF pOwningRow, // in: current row from ProcessRow
848 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
849 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
850 PDLGPRIVATE pDlgData)
851{
852 APIRET arc = NO_ERROR;
853
854 pColumnDef->pOwningRow = pOwningRow;
855
856 switch (ProcessMode)
857 {
858 /*
859 * PROCESS_CALC_SIZES:
860 * step 1.
861 */
862
863 case PROCESS_CALC_SIZES:
864 arc = ColumnCalcSizes(pColumnDef,
865 pDlgData);
866 break;
867
868 /*
869 * PROCESS_CALC_POSITIONS:
870 * step 2.
871 */
872
873 case PROCESS_CALC_POSITIONS:
874 arc = ColumnCalcPositions(pColumnDef,
875 pOwningRow,
876 plX,
877 pDlgData);
878 break;
879
880 /*
881 * PROCESS_CREATE_CONTROLS:
882 * step 3.
883 */
884
885 case PROCESS_CREATE_CONTROLS:
886 arc = ColumnCreateControls(pColumnDef,
887 pDlgData);
888 break;
889 }
890
891 return (arc);
892}
893
894/*
895 *@@ ProcessRow:
896 * level-3 procedure (called from ProcessTable),
897 * which in turn calls ProcessColumn for each column
898 * in the row.
899 *
900 * See ProcessAll for the meaning of ProcessMode.
901 */
902
903APIRET ProcessRow(PROWDEF pRowDef,
904 PTABLEDEF pOwningTable, // in: current table from ProcessTable
905 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
906 PLONG plY, // in/out: current y position (decremented)
907 PDLGPRIVATE pDlgData)
908{
909 APIRET arc = NO_ERROR;
910 LONG lX;
911 PLISTNODE pNode;
912
913 pRowDef->pOwningTable = pOwningTable;
914
915 if (ProcessMode == PROCESS_CALC_SIZES)
916 {
917 pRowDef->cpRow.cx = 0;
918 pRowDef->cpRow.cy = 0;
919 }
920 else if (ProcessMode == PROCESS_CALC_POSITIONS)
921 {
922 // set up x and y so that the columns can
923 // base on that
924 pRowDef->cpRow.x = pOwningTable->cpTable.x;
925 // decrease y by row height
926 *plY -= pRowDef->cpRow.cy;
927 // and use that for our bottom position
928 pRowDef->cpRow.y = *plY;
929
930 // set lX to left of row; used by column calls below
931 lX = pRowDef->cpRow.x;
932 }
933
934 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
935 {
936 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
937
938 if (!(arc = ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData)))
939 {
940 if (ProcessMode == PROCESS_CALC_SIZES)
941 {
942 // row width = sum of all columns
943 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
944
945 // row height = maximum height of a column
946 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
947 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
948 }
949 }
950 }
951
952 return (arc);
953}
954
955/*
956 *@@ ProcessTable:
957 * level-2 procedure (called from ProcessAll),
958 * which in turn calls ProcessRow for each row
959 * in the table (which in turn calls ProcessColumn
960 * for each column in the row).
961 *
962 * See ProcessAll for the meaning of ProcessMode.
963 *
964 * This routine is a bit sick because it can even be
965 * called recursively from ProcessColumn (!) if a
966 * nested table is found in a COLUMNDEF.
967 *
968 * With PROCESS_CALC_POSITIONS, pptl must specify
969 * the lower left corner of the table. For the
970 * root call, this will be {0, 0}; for nested calls,
971 * this must be the lower left corner of the column
972 * to which the nested table belongs.
973 *
974 */
975
976APIRET ProcessTable(PTABLEDEF pTableDef,
977 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
978 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
979 PDLGPRIVATE pDlgData)
980{
981 APIRET arc = NO_ERROR;
982 LONG lY;
983 PLISTNODE pNode;
984
985 if (ProcessMode == PROCESS_CALC_SIZES)
986 {
987 pTableDef->cpTable.cx = 0;
988 pTableDef->cpTable.cy = 0;
989 }
990 else if (ProcessMode == PROCESS_CALC_POSITIONS)
991 {
992 pTableDef->cpTable.x = pcpTable->x;
993 pTableDef->cpTable.y = pcpTable->y;
994
995 // start the rows on top
996 lY = pcpTable->y + pTableDef->cpTable.cy;
997 }
998
999 FOR_ALL_NODES(&pTableDef->llRows, pNode)
1000 {
1001 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
1002
1003 if (!(arc = ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData)))
1004 {
1005 if (ProcessMode == PROCESS_CALC_SIZES)
1006 {
1007 // table width = maximum width of a row
1008 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
1009 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
1010
1011 // table height = sum of all rows
1012 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
1013 }
1014 }
1015 else
1016 break;
1017 }
1018
1019 return (arc);
1020}
1021
1022/*
1023 *@@ ProcessAll:
1024 * level-1 procedure, which in turn calls ProcessTable
1025 * for each root-level table found (which in turn
1026 * calls ProcessRow for each row in the table, which
1027 * in turn calls ProcessColumn for each column in
1028 * the row).
1029 *
1030 * The first trick to formatting is that ProcessAll will
1031 * get three times, thus going down the entire tree three
1032 * times, with ProcessMode being set to one of the
1033 * following for each call (in this order):
1034 *
1035 * -- PROCESS_CALC_SIZES: calculates the sizes
1036 * of all tables, rows, columns, and controls.
1037 *
1038 * After this first call, we know all the sizes
1039 * only and can then calculate the positions.
1040 *
1041 * -- PROCESS_CALC_POSITIONS: calculates the positions
1042 * based on the sizes calculated before.
1043 *
1044 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
1045 * positions and sizes calculated before.
1046 *
1047 * The second trick is the precondition that tables may
1048 * nest by allowing another table definition in a column.
1049 * This way we can recurse from ProcessColumn back into
1050 * ProcessTable and thus know the size and position of a
1051 * nested table column just as if it were a regular control.
1052 */
1053
1054APIRET ProcessAll(PDLGPRIVATE pDlgData,
1055 PSIZEL pszlClient,
1056 PROCESSMODE ProcessMode)
1057{
1058 APIRET arc = NO_ERROR;
1059 PLISTNODE pNode;
1060 CONTROLPOS cpTable;
1061 ZERO(&cpTable);
1062
1063 switch (ProcessMode)
1064 {
1065 case PROCESS_CALC_SIZES:
1066 pszlClient->cx = 0;
1067 pszlClient->cy = 0;
1068 break;
1069
1070 case PROCESS_CALC_POSITIONS:
1071 // start with the table on top
1072 cpTable.y = pszlClient->cy;
1073 break;
1074 }
1075
1076 FOR_ALL_NODES(&pDlgData->llTables, pNode)
1077 {
1078 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
1079
1080 if (ProcessMode == PROCESS_CALC_POSITIONS)
1081 {
1082 cpTable.x = 0;
1083 cpTable.y -= pTableDefThis->cpTable.cy;
1084 }
1085
1086 if (!(arc = ProcessTable(pTableDefThis,
1087 &cpTable, // start pos
1088 ProcessMode,
1089 pDlgData)))
1090 {
1091 if (ProcessMode == PROCESS_CALC_SIZES)
1092 {
1093 pszlClient->cx += pTableDefThis->cpTable.cx;
1094 pszlClient->cy += pTableDefThis->cpTable.cy;
1095 }
1096 }
1097 }
1098
1099 return (arc);
1100}
1101
1102/*
1103 *@@ CreateColumn:
1104 *
1105 */
1106
1107APIRET CreateColumn(PROWDEF pCurrentRow,
1108 BOOL fIsNestedTable,
1109 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
1110 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
1111{
1112 APIRET arc = NO_ERROR;
1113
1114 if (!pCurrentRow)
1115 arc = DLGERR_CONTROL_BEFORE_ROW;
1116 else
1117 {
1118 // append the control def
1119 if (!pvDefinition)
1120 arc = DLGERR_NULL_CTL_DEF;
1121 else
1122 {
1123 // create column and store ctl def
1124 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
1125 if (!pColumnDef)
1126 arc = ERROR_NOT_ENOUGH_MEMORY;
1127 else
1128 {
1129 memset(pColumnDef, 0, sizeof(COLUMNDEF));
1130 pColumnDef->pOwningRow = pCurrentRow;
1131 pColumnDef->fIsNestedTable = fIsNestedTable;
1132 pColumnDef->pvDefinition = pvDefinition;
1133
1134 *ppColumnDef = pColumnDef;
1135 }
1136 }
1137 }
1138
1139 return (arc);
1140}
1141
1142/*
1143 *@@ FreeTable:
1144 * frees the specified table and recurses
1145 * into nested tables, if necessary.
1146 *
1147 * This was added with V0.9.14 to fix the
1148 * bad memory leaks with nested tables.
1149 *
1150 *@@added V0.9.14 (2001-08-01) [umoeller]
1151 */
1152
1153VOID FreeTable(PTABLEDEF pTable)
1154{
1155 // for each table, clean up the rows
1156 PLISTNODE pRowNode;
1157 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1158 {
1159 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1160
1161 // for each row, clean up the columns
1162 PLISTNODE pColumnNode;
1163 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1164 {
1165 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1166
1167 if (pColumn->fIsNestedTable)
1168 {
1169 // nested table: recurse!
1170 PTABLEDEF pNestedTable = (PTABLEDEF)pColumn->pvDefinition;
1171 FreeTable(pNestedTable);
1172 }
1173
1174 free(pColumn);
1175 }
1176 lstClear(&pRow->llColumns);
1177
1178 free(pRow);
1179 }
1180 lstClear(&pTable->llRows);
1181
1182 free(pTable);
1183}
1184
1185/* ******************************************************************
1186 *
1187 * Dialog formatter engine
1188 *
1189 ********************************************************************/
1190
1191/*
1192 *@@ STACKITEM:
1193 *
1194 */
1195
1196typedef struct _STACKITEM
1197{
1198 PTABLEDEF pLastTable;
1199 PROWDEF pLastRow;
1200
1201} STACKITEM, *PSTACKITEM;
1202
1203#define SPACING 10
1204
1205/*
1206 *@@ Dlg0_Init:
1207 *
1208 *@@added V0.9.15 (2001-08-26) [umoeller]
1209 */
1210
1211APIRET Dlg0_Init(PDLGPRIVATE *ppDlgData,
1212 PCSZ pcszControlsFont)
1213{
1214 PDLGPRIVATE pDlgData;
1215 if (!(pDlgData = NEW(DLGPRIVATE)))
1216 return (ERROR_NOT_ENOUGH_MEMORY);
1217 ZERO(pDlgData);
1218 lstInit(&pDlgData->llTables, FALSE);
1219 lstInit(&pDlgData->llControls, FALSE);
1220
1221 pDlgData->pcszControlsFont = pcszControlsFont;
1222
1223 *ppDlgData = pDlgData;
1224
1225 return NO_ERROR;
1226}
1227
1228/*
1229 *@@ Dlg1_ParseTables:
1230 *
1231 *@@added V0.9.15 (2001-08-26) [umoeller]
1232 */
1233
1234APIRET Dlg1_ParseTables(PDLGPRIVATE pDlgData,
1235 PDLGHITEM paDlgItems, // in: definition array
1236 ULONG cDlgItems) // in: array item count (NOT array size)
1237{
1238 APIRET arc = NO_ERROR;
1239
1240 LINKLIST llStack;
1241 ULONG ul;
1242 PTABLEDEF pCurrentTable = NULL;
1243 PROWDEF pCurrentRow = NULL;
1244
1245 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1246
1247 for (ul = 0;
1248 ul < cDlgItems;
1249 ul++)
1250 {
1251 PDLGHITEM pItemThis = &paDlgItems[ul];
1252
1253 switch (pItemThis->Type)
1254 {
1255 /*
1256 * TYPE_START_NEW_TABLE:
1257 *
1258 */
1259
1260 case TYPE_START_NEW_TABLE:
1261 {
1262 // root table or nested?
1263 BOOL fIsRoot = (pCurrentTable == NULL);
1264
1265 // push the current table on the stack
1266 PSTACKITEM pStackItem;
1267 if (!(pStackItem = NEW(STACKITEM)))
1268 {
1269 arc = ERROR_NOT_ENOUGH_MEMORY;
1270 break;
1271 }
1272 else
1273 {
1274 pStackItem->pLastTable = pCurrentTable;
1275 pStackItem->pLastRow = pCurrentRow;
1276 lstPush(&llStack, pStackItem);
1277 }
1278
1279 // create new table
1280 if (!(pCurrentTable = NEW(TABLEDEF)))
1281 arc = ERROR_NOT_ENOUGH_MEMORY;
1282 else
1283 {
1284 ZERO(pCurrentTable);
1285
1286 lstInit(&pCurrentTable->llRows, FALSE);
1287
1288 if (pItemThis->ulData)
1289 // control specified: store it (this will become a PM group)
1290 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1291
1292 if (fIsRoot)
1293 // root table:
1294 // append to dialog data list
1295 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1296 else
1297 {
1298 // nested table:
1299 // create "table" column for this
1300 PCOLUMNDEF pColumnDef;
1301 if (!(arc = CreateColumn(pCurrentRow,
1302 TRUE, // nested table
1303 pCurrentTable,
1304 &pColumnDef)))
1305 lstAppendItem(&pCurrentRow->llColumns,
1306 pColumnDef);
1307 }
1308 }
1309
1310 pCurrentRow = NULL;
1311 break; }
1312
1313 /*
1314 * TYPE_START_NEW_ROW:
1315 *
1316 */
1317
1318 case TYPE_START_NEW_ROW:
1319 {
1320 if (!pCurrentTable)
1321 arc = DLGERR_ROW_BEFORE_TABLE;
1322 else
1323 {
1324 // create new row
1325 if (!(pCurrentRow = NEW(ROWDEF)))
1326 arc = ERROR_NOT_ENOUGH_MEMORY;
1327 else
1328 {
1329 ZERO(pCurrentRow);
1330
1331 pCurrentRow->pOwningTable = pCurrentTable;
1332 lstInit(&pCurrentRow->llColumns, FALSE);
1333
1334 pCurrentRow->flRowFormat = pItemThis->ulData;
1335
1336 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1337 }
1338 }
1339 break; }
1340
1341 /*
1342 * TYPE_CONTROL_DEF:
1343 *
1344 */
1345
1346 case TYPE_CONTROL_DEF:
1347 {
1348 PCOLUMNDEF pColumnDef;
1349 if (!(arc = CreateColumn(pCurrentRow,
1350 FALSE, // no nested table
1351 (PVOID)pItemThis->ulData,
1352 &pColumnDef)))
1353 lstAppendItem(&pCurrentRow->llColumns,
1354 pColumnDef);
1355 break; }
1356
1357 /*
1358 * TYPE_END_TABLE:
1359 *
1360 */
1361
1362 case TYPE_END_TABLE:
1363 {
1364 PLISTNODE pNode = lstPop(&llStack);
1365 if (!pNode)
1366 // nothing on the stack:
1367 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1368 else
1369 {
1370 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1371 pCurrentTable = pStackItem->pLastTable;
1372 pCurrentRow = pStackItem->pLastRow;
1373
1374 lstRemoveNode(&llStack, pNode);
1375 }
1376 break; }
1377
1378 default:
1379 arc = DLGERR_INVALID_CODE;
1380 }
1381
1382 if (arc)
1383 break;
1384 }
1385
1386 if ((!arc) && (lstCountItems(&llStack)))
1387 arc = DLGERR_TABLE_NOT_CLOSED;
1388
1389 lstClear(&llStack);
1390
1391 return (arc);
1392}
1393
1394/*
1395 *@@ Dlg2_CalcSizes:
1396 *
1397 *@@added V0.9.15 (2001-08-26) [umoeller]
1398 */
1399
1400APIRET Dlg2_CalcSizes(PDLGPRIVATE pDlgData,
1401 PSIZEL pszlClient) // out: dialog's client size
1402{
1403 APIRET arc = ProcessAll(pDlgData,
1404 pszlClient,
1405 PROCESS_CALC_SIZES);
1406 // this goes into major recursions...
1407
1408 // free the cached font resources that
1409 // might have been created here
1410 if (pDlgData->hps)
1411 {
1412 if (pDlgData->lcidLast)
1413 {
1414 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1415 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1416 }
1417 WinReleasePS(pDlgData->hps);
1418 }
1419
1420 return (arc);
1421}
1422
1423/*
1424 *@@ Dlg3_PositionAndCreate:
1425 *
1426 *@@added V0.9.15 (2001-08-26) [umoeller]
1427 *@@changed V0.9.15 (2001-08-26) [umoeller]: BS_DEFAULT for other than first button was ignored, fixed
1428 */
1429
1430APIRET Dlg3_PositionAndCreate(PDLGPRIVATE pDlgData,
1431 PSIZEL pszlClient, // in: dialog's client size
1432 HWND *phwndFocusItem) // out: item to give focus to
1433{
1434 APIRET arc = NO_ERROR;
1435
1436 /*
1437 * 5) compute _positions_ of all controls
1438 *
1439 */
1440
1441 ProcessAll(pDlgData,
1442 pszlClient,
1443 PROCESS_CALC_POSITIONS);
1444
1445 /*
1446 * 6) create control windows, finally
1447 *
1448 */
1449
1450 pDlgData->ptlTotalOfs.x
1451 = pDlgData->ptlTotalOfs.y
1452 = SPACING;
1453
1454 ProcessAll(pDlgData,
1455 pszlClient,
1456 PROCESS_CREATE_CONTROLS);
1457
1458 if (pDlgData->hwndDefPushbutton)
1459 {
1460 // we had a default pushbutton:
1461 // go set it V0.9.14 (2001-08-21) [umoeller]
1462 WinSetWindowULong(pDlgData->hwndDlg,
1463 QWL_DEFBUTTON,
1464 pDlgData->hwndDefPushbutton);
1465 *phwndFocusItem = pDlgData->hwndDefPushbutton;
1466 // V0.9.15 (2001-08-26) [umoeller]
1467 }
1468 else
1469 *phwndFocusItem = (pDlgData->hwndFirstFocus)
1470 ? pDlgData->hwndFirstFocus
1471 : pDlgData->hwndDlg;
1472
1473 return (arc);
1474}
1475
1476/*
1477 *@@ Dlg9_Cleanup:
1478 *
1479 *@@added V0.9.15 (2001-08-26) [umoeller]
1480 */
1481
1482VOID Dlg9_Cleanup(PDLGPRIVATE *ppDlgData)
1483{
1484 PDLGPRIVATE pDlgData;
1485 if ( (ppDlgData)
1486 && (pDlgData = *ppDlgData)
1487 )
1488 {
1489 PLISTNODE pTableNode;
1490
1491 // in any case, clean up our mess:
1492
1493 // clean up the tables
1494 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1495 {
1496 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1497
1498 FreeTable(pTable);
1499 // this may recurse for nested tables
1500 }
1501
1502 lstClear(&pDlgData->llTables);
1503 lstClear(&pDlgData->llControls);
1504
1505 free(pDlgData);
1506
1507 *ppDlgData = NULL;
1508 }
1509}
1510
1511/* ******************************************************************
1512 *
1513 * Dialog formatter entry points
1514 *
1515 ********************************************************************/
1516
1517/*
1518 *@@ dlghCreateDlg:
1519 * replacement for WinCreateDlg/WinLoadDlg for creating a
1520 * dialog from a settings array in memory, which is
1521 * formatted automatically.
1522 *
1523 * This does NOT use regular dialog templates from
1524 * module resources. Instead, you pass in an array
1525 * of DLGHITEM structures, which define the controls
1526 * and how they are to be formatted.
1527 *
1528 * The main advantage compared to dialog resources is
1529 * that with this function, you will never have to
1530 * define control _positions_. Instead, you only specify
1531 * the control _sizes_, and all positions are computed
1532 * automatically here. Even better, for many controls,
1533 * auto-sizing is supported according to the control's
1534 * text (e.g. for statics and checkboxes).
1535 *
1536 * A regular standard dialog would use something like
1537 *
1538 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
1539 *
1540 * for flCreateFlags. To make the dlg sizeable, specify
1541 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
1542 *
1543 * <B>Usage:</B>
1544 *
1545 * Like WinLoadDlg, this creates a standard WC_FRAME and
1546 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
1547 * to the dialog with pCreateParams in mp2.
1548 *
1549 * If this func returns no error, you can then use
1550 * WinProcessDlg with the newly created dialog as usual. In
1551 * your dlg proc, use WinDefDlgProc as usual.
1552 *
1553 * There is NO run-time overhead for either code or memory
1554 * after dialog creation; after this function returns, the
1555 * dialog is a standard dialog as if loaded from WinLoadDlg.
1556 * The array of DLGHITEM structures defines how the
1557 * dialog is set up. All this is ONLY used by this function
1558 * and NOT needed after the dialog has been created.
1559 *
1560 * In DLGHITEM, the "Type" field determines what this
1561 * structure defines. A number of handy macros have been
1562 * defined to make this easier and to provide type-checking
1563 * at compile time. See dialog.h for more.
1564 *
1565 * Essentially, such a dialog item operates similarly to
1566 * HTML tables. There are rows and columns in the table,
1567 * and each control which is specified must be a column
1568 * in some table. Tables may also nest (see below).
1569 *
1570 * The macros are:
1571 *
1572 * -- START_TABLE starts a new table. The tables may nest,
1573 * but must each be properly terminated with END_TABLE.
1574 *
1575 * -- START_GROUP_TABLE(pDef) starts a group. This
1576 * behaves exacly like START_TABLE, but in addition,
1577 * it produces a static group control around the table.
1578 * Useful for group boxes. pDef must point to a
1579 * _CONTROLDEF describing the control to be used for
1580 * the group (usually a WC_STATIC with SS_GROUP style),
1581 * whose size parameter is ignored.
1582 *
1583 * As with START_TABLE, START_GROUP_TABLE must be
1584 * terminated with END_TABLE.
1585 *
1586 * -- START_ROW(fl) starts a new row in a table (regular
1587 * or group). This must also be the first item after
1588 * the (group) table tag.
1589 *
1590 * fl specifies formatting flags for the row. This
1591 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
1592 * ROW_VALIGN_TOP and affects all items in the row.
1593 *
1594 * -- CONTROL_DEF(pDef) defines a control in a table row.
1595 * pDef must point to a CONTROLDEF structure.
1596 *
1597 * Again, there is is NO information in
1598 * CONTROLDEF about a control's _position_.
1599 * Instead, the structure only contains the _size_
1600 * of the control. All positions are computed by
1601 * this function, depending on the sizes of the
1602 * controls and their nesting within the various tables.
1603 *
1604 * If you specify SZL_AUTOSIZE, the size of the
1605 * control is even computed automatically. Presently,
1606 * this only works for statics with SS_TEXT, SS_ICON,
1607 * and SS_BITMAP.
1608 *
1609 * Unless separated with START_ROW items, subsequent
1610 * control items will be considered to be in the same
1611 * row (== positioned next to each other).
1612 *
1613 * There are a few rules, whose violation will produce
1614 * an error:
1615 *
1616 * -- The entire array must be enclosed in a table
1617 * (the "root" table).
1618 *
1619 * -- After START_TABLE or START_GROUP_TABLE, there must
1620 * always be a START_ROW first.
1621 *
1622 * To create a dialog, set up arrays like the following:
1623 *
1624 + // control definitions referenced by DlgTemplate:
1625 + CONTROLDEF
1626 + (1) GroupDef = {
1627 + WC_STATIC, "", ....,
1628 + { 0, 0 }, // size, ignored for groups
1629 + 5 // spacing
1630 + },
1631 + (2) CnrDef = {
1632 + WC_CONTAINER, "", ....,
1633 + { 50, 50 }, // size
1634 + 5 // spacing
1635 + },
1636 + (3) Static = {
1637 + WC_STATIC, "Static below cnr", ...,
1638 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
1639 + 5 // spacing
1640 + },
1641 + (4) OKButton = {
1642 + WC_STATIC, "~OK", ...,
1643 + { 100, 30 }, // size
1644 + 5 // spacing
1645 + },
1646 + (5) CancelButton = {
1647 + WC_STATIC, "~Cancel", ...,
1648 + { 100, 30 }, // size
1649 + 5 // spacing
1650 + };
1651 +
1652 + DLGHITEM DlgTemplate[] =
1653 + {
1654 + START_TABLE, // root table, required
1655 + START_ROW(0), // row 1 in the root table, required
1656 + // create group on top
1657 + (1) START_GROUP_TABLE(&Group),
1658 + START_ROW(0),
1659 + (2) CONTROL_DEF(&CnrDef),
1660 + START_ROW(0),
1661 + (3) CONTROL_DEF(&Static),
1662 + END_TABLE, // end of group
1663 + START_ROW(0), // row 2 in the root table
1664 + // two buttons next to each other
1665 + (4) CONTROL_DEF(&OKButton),
1666 + (5) CONTROL_DEF(&CancelButton),
1667 + END_TABLE
1668 + }
1669 *
1670 * This will produce a dlg like this:
1671 *
1672 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1673 + º º
1674 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1675 + º ³ ³ º
1676 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1677 + º ³ ³ ³ ³ º
1678 + º ³ ³ Cnr inside group (2) ³ ³ º
1679 + º ³ ³ ³ ³ º
1680 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1681 + º ³ Static below cnr (3) ³ º
1682 + º ³ ³ º
1683 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1684 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1685 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1686 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1687 + º º
1688 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1689 *
1690 * <B>Errors:</B>
1691 *
1692 * This does not return a HWND, but an APIRET. This will be
1693 * one of the following:
1694 *
1695 * -- NO_ERROR: only in that case, the phwndDlg ptr
1696 * receives the HWND of the new dialog, which can
1697 * then be given to WinProcessDlg. Don't forget
1698 * WinDestroyWindow.
1699 *
1700 * -- ERROR_NOT_ENOUGH_MEMORY
1701 *
1702 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1703 * outside a table definition.
1704 *
1705 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1706 * appeared right after a table definition. You must
1707 * specify a row first.
1708 *
1709 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1710 * but the CONTROLDEF ptr was NULL.
1711 *
1712 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1713 * WC_FRAME window. Maybe an invalid owner was specified.
1714 *
1715 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1716 * DLGHITEM.
1717 *
1718 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1719 * improper nesting of TYPE_START_NEW_TABLE and
1720 * TYPE_END_TABLE fields.
1721 *
1722 * <B>Example:</B>
1723 *
1724 * The typical calling sequence would be:
1725 *
1726 + HWND hwndDlg = NULLHANDLE;
1727 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1728 + hwndOwner,
1729 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1730 + fnwpMyDlgProc,
1731 + "My Dlg Title",
1732 + DlgTemplate, // DLGHITEM array
1733 + ARRAYITEMCOUNT(DlgTemplate),
1734 + NULL, // mp2 for WM_INITDLG
1735 + "9.WarpSans")) // default font
1736 + {
1737 + ULONG idReturn = WinProcessDlg(hwndDlg);
1738 + WinDestroyWindow(hwndDlg);
1739 + }
1740 *
1741 *@@changed V0.9.14 (2001-07-07) [umoeller]: fixed disabled mouse with hwndOwner == HWND_DESKTOP
1742 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed major memory leaks with nested tables
1743 *@@changed V0.9.14 (2001-08-21) [umoeller]: fixed default push button problems
1744 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed bad owner if not direct desktop child
1745 */
1746
1747APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1748 HWND hwndOwner,
1749 ULONG flCreateFlags, // in: standard FCF_* frame flags
1750 PFNWP pfnwpDialogProc,
1751 const char *pcszDlgTitle,
1752 PDLGHITEM paDlgItems, // in: definition array
1753 ULONG cDlgItems, // in: array item count (NOT array size)
1754 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1755 const char *pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1756{
1757 APIRET arc = NO_ERROR;
1758
1759 ULONG ul;
1760
1761 PDLGPRIVATE pDlgData = NULL;
1762
1763 HWND hwndDesktop = WinQueryDesktopWindow(NULLHANDLE, NULLHANDLE);
1764 // works with a null HAB
1765
1766 /*
1767 * 1) parse the table and create structures from it
1768 *
1769 */
1770
1771 if (!(arc = Dlg0_Init(&pDlgData,
1772 pcszControlsFont)))
1773 {
1774 if (!(arc = Dlg1_ParseTables(pDlgData,
1775 paDlgItems,
1776 cDlgItems)))
1777 {
1778 /*
1779 * 2) create empty dialog frame
1780 *
1781 */
1782
1783 FRAMECDATA fcData = {0};
1784 ULONG flStyle = 0;
1785 HWND hwndOwnersParent;
1786
1787 fcData.cb = sizeof(FRAMECDATA);
1788 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
1789
1790 if (flCreateFlags & FCF_SIZEBORDER)
1791 // dialog has size border:
1792 // add "clip siblings" style
1793 flStyle |= WS_CLIPSIBLINGS;
1794
1795 if (hwndOwner == HWND_DESKTOP)
1796 // there's some dumb XWorkplace code left
1797 // which uses this, and this disables the
1798 // mouse for some reason
1799 // V0.9.14 (2001-07-07) [umoeller]
1800 hwndOwner = NULLHANDLE;
1801
1802 // now, make sure the owner window is child of
1803 // HWND_DESKTOP... if it is not, we'll only disable
1804 // some dumb child window, which is not sufficient
1805 // V0.9.16 (2001-12-06) [umoeller]
1806 while ( (hwndOwner)
1807 && (hwndOwnersParent = WinQueryWindow(hwndOwner, QW_PARENT))
1808 && (hwndOwnersParent != hwndDesktop)
1809 )
1810 hwndOwner = hwndOwnersParent;
1811
1812 if (!(pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1813 WC_FRAME,
1814 (PSZ)pcszDlgTitle,
1815 flStyle, // style; invisible for now
1816 0, 0, 0, 0,
1817 hwndOwner,
1818 HWND_TOP,
1819 0, // ID
1820 &fcData,
1821 NULL))) // presparams
1822 arc = DLGERR_CANNOT_CREATE_FRAME;
1823 else
1824 {
1825 HWND hwndDlg = pDlgData->hwndDlg;
1826 HWND hwndFocusItem = NULLHANDLE;
1827 SIZEL szlClient = {0};
1828 RECTL rclClient;
1829
1830 /*
1831 * 3) compute size of all controls
1832 *
1833 */
1834
1835 Dlg2_CalcSizes(pDlgData,
1836 &szlClient);
1837
1838 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
1839
1840 /*
1841 * 4) compute size of dialog client from total
1842 * size of all controls
1843 */
1844
1845 // calculate the frame size from the client size
1846 rclClient.xLeft = 10;
1847 rclClient.yBottom = 10;
1848 rclClient.xRight = szlClient.cx + 2 * SPACING;
1849 rclClient.yTop = szlClient.cy + 2 * SPACING;
1850 WinCalcFrameRect(hwndDlg,
1851 &rclClient,
1852 FALSE); // frame from client
1853
1854 WinSetWindowPos(hwndDlg,
1855 0,
1856 10,
1857 10,
1858 rclClient.xRight,
1859 rclClient.yTop,
1860 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1861
1862 arc = Dlg3_PositionAndCreate(pDlgData,
1863 &szlClient,
1864 &hwndFocusItem);
1865
1866 /*
1867 * 7) WM_INITDLG, set focus
1868 *
1869 */
1870
1871 if (!WinSendMsg(pDlgData->hwndDlg,
1872 WM_INITDLG,
1873 (MPARAM)hwndFocusItem,
1874 (MPARAM)pCreateParams))
1875 {
1876 // if WM_INITDLG returns FALSE, this means
1877 // the dlg proc has not changed the focus;
1878 // we must then set the focus here
1879 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1880 }
1881 }
1882 }
1883
1884 if (arc)
1885 {
1886 // error: clean up
1887 if (pDlgData->hwndDlg)
1888 {
1889 WinDestroyWindow(pDlgData->hwndDlg);
1890 pDlgData->hwndDlg = NULLHANDLE;
1891 }
1892 }
1893 else
1894 // no error: output dialog
1895 *phwndDlg = pDlgData->hwndDlg;
1896
1897 Dlg9_Cleanup(&pDlgData);
1898 }
1899
1900 if (arc)
1901 {
1902 CHAR szErr[300];
1903 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
1904 winhDebugBox(hwndOwner,
1905 "Error in Dialog Manager",
1906 szErr);
1907 }
1908
1909 return (arc);
1910}
1911
1912/*
1913 *@@ dlghFormatDlg:
1914 * similar to dlghCreateDlg in that this can
1915 * dynamically format dialog items.
1916 *
1917 * The differences however are the following:
1918 *
1919 * -- This assumes that hwndDlg already points
1920 * to a valid dialog frame and that this
1921 * dialog should be modified according to
1922 * flFlags.
1923 *
1924 * This is what's used in XWorkplace for notebook
1925 * settings pages since these always have to be
1926 * based on a resource dialog (which is loaded
1927 * empty).
1928 *
1929 * flFlags can be any combination of the following:
1930 *
1931 * -- DFFL_CREATECONTROLS: paDlgItems points to
1932 * an array of cDlgItems DLGHITEM structures
1933 * (see dlghCreateDlg) which is used for creating
1934 * subwindows in hwndDlg. By using this flag, the
1935 * function will essentially work like dlghCreateDlg,
1936 * except that the frame is already created.
1937 *
1938 * -- DFFL_RESIZEFRAME: hwndDlg should be resized so
1939 * that it will properly surround the controls.
1940 *
1941 * This can only be used in conjunction with
1942 * DFFL_RESIZEFRAME.
1943 *
1944 *@@added V0.9.16 (2001-09-29) [umoeller]
1945 */
1946
1947APIRET dlghFormatDlg(HWND hwndDlg, // in: dialog frame to work on
1948 PDLGHITEM paDlgItems, // in: definition array
1949 ULONG cDlgItems, // in: array item count (NOT array size)
1950 const char *pcszControlsFont, // in: font for ctls with CTL_COMMON_FONT
1951 ULONG flFlags) // in: DFFL_* flags
1952{
1953 APIRET arc = NO_ERROR;
1954
1955 ULONG ul;
1956
1957 PDLGPRIVATE pDlgData = NULL;
1958
1959 /*
1960 * 1) parse the table and create structures from it
1961 *
1962 */
1963
1964 if (!(arc = Dlg0_Init(&pDlgData,
1965 pcszControlsFont)))
1966 {
1967 if (!(arc = Dlg1_ParseTables(pDlgData,
1968 paDlgItems,
1969 cDlgItems)))
1970 {
1971 /*
1972 * 2) create empty dialog frame
1973 *
1974 */
1975
1976 HWND hwndFocusItem = NULLHANDLE;
1977 SIZEL szlClient = {0};
1978 RECTL rclClient;
1979
1980 pDlgData->hwndDlg = hwndDlg;
1981
1982 /*
1983 * 3) compute size of all controls
1984 *
1985 */
1986
1987 Dlg2_CalcSizes(pDlgData,
1988 &szlClient);
1989
1990 // WinSubclassWindow(hwndDlg, pfnwpDialogProc);
1991
1992 /*
1993 * 4) compute size of dialog client from total
1994 * size of all controls
1995 */
1996
1997 if (flFlags & DFFL_RESIZEFRAME)
1998 {
1999 // calculate the frame size from the client size
2000 rclClient.xLeft = 10;
2001 rclClient.yBottom = 10;
2002 rclClient.xRight = szlClient.cx + 2 * SPACING;
2003 rclClient.yTop = szlClient.cy + 2 * SPACING;
2004 WinCalcFrameRect(hwndDlg,
2005 &rclClient,
2006 FALSE); // frame from client
2007
2008 WinSetWindowPos(hwndDlg,
2009 0,
2010 10,
2011 10,
2012 rclClient.xRight,
2013 rclClient.yTop,
2014 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
2015 }
2016
2017 if (flFlags & DFFL_CREATECONTROLS)
2018 {
2019 if (!(arc = Dlg3_PositionAndCreate(pDlgData,
2020 &szlClient,
2021 &hwndFocusItem)))
2022 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
2023 }
2024 }
2025
2026 Dlg9_Cleanup(&pDlgData);
2027 }
2028
2029 if (arc)
2030 {
2031 CHAR szErr[300];
2032 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
2033 winhDebugBox(NULLHANDLE,
2034 "Error in Dialog Manager",
2035 szErr);
2036 }
2037
2038 return (arc);
2039}
2040
2041/* ******************************************************************
2042 *
2043 * Dialog arrays
2044 *
2045 ********************************************************************/
2046
2047/*
2048 *@@ dlghCreateArray:
2049 * creates a "dialog array" for dynamically
2050 * building a dialog template in memory.
2051 *
2052 * A dialog array is simply an array of
2053 * DLGHITEM structures, as you would normally
2054 * define them statically in the source.
2055 * However, there are situations where you
2056 * might want to leave out certain controls
2057 * depending on certain conditions, which
2058 * can be difficult with static arrays.
2059 *
2060 * As a result, these "array" functions have
2061 * been added to allow for adding static
2062 * DLGHITEM subarrays to a dynamic array in
2063 * memory, which can then be passed to the
2064 * formatter.
2065 *
2066 * Usage:
2067 *
2068 * 1) Call this function with the maximum
2069 * amount of DLGHITEM's that will need
2070 * to be allocated in cMaxItems. Set this
2071 * to the total sum of all DLGHITEM's
2072 * in all the subarrays.
2073 *
2074 * 2) For each of the subarrays, call
2075 * dlghAppendToArray to have the subarray
2076 * appended to the dialog array.
2077 * After each call, DLGARRAY.cDlgItemsNow
2078 * will contain the actual total count of
2079 * DLGHITEM's that were added.
2080 *
2081 * 3) Call dlghCreateDialog with the dialog
2082 * array.
2083 *
2084 * 4) Call dlghFreeArray.
2085 *
2086 * Sort of like this (error checking omitted):
2087 *
2088 + DLGHITEM dlgSampleFront = ... // always included
2089 + DLGHITEM dlgSampleSometimes = ... // not always included
2090 + DLGHITEM dlgSampleTail = ... // always included
2091 +
2092 + PDLGARRAY pArraySample = NULL;
2093 + dlghCreateArray( ARRAYITEMCOUNT(dlgSampleFront)
2094 + + ARRAYITEMCOUNT(dlgSampleSometimes)
2095 + + ARRAYITEMCOUNT(dlgSampleTail),
2096 + &pArraySample);
2097 +
2098 + // always include front
2099 + dlghAppendToArray(pArraySample,
2100 + dlgSampleFront,
2101 + ARRAYITEMCOUNT(dlgSampleFront));
2102 + // include "sometimes" conditionally
2103 + if (...)
2104 + dlghAppendToArray(pArraySample,
2105 + dlgSampleSometimes,
2106 + ARRAYITEMCOUNT(dlgSampleSometimes));
2107 + // include tail always
2108 + dlghAppendToArray(pArraySample,
2109 + dlgSampleTail,
2110 + ARRAYITEMCOUNT(dlgSampleTail));
2111 +
2112 + // now create the dialog from the array
2113 + dlghCreateDialog(&hwndDlg,
2114 + hwndOwner,
2115 + FCF_ ...
2116 + fnwpMyDialogProc,
2117 + "Title",
2118 + pArray->paDialogItems, // dialog array!
2119 + pArray->cDlgItemsNow, // real count of items!
2120 + NULL,
2121 + NULL);
2122 +
2123 + dlghFreeArray(&pArraySample);
2124 *
2125 *@@added V0.9.16 (2001-10-15) [umoeller]
2126 */
2127
2128APIRET dlghCreateArray(ULONG cMaxItems,
2129 PDLGARRAY *ppArray) // out: DLGARRAY
2130{
2131 APIRET arc = NO_ERROR;
2132 PDLGARRAY pArray;
2133
2134 if (pArray = NEW(DLGARRAY))
2135 {
2136 ULONG cb;
2137
2138 ZERO(pArray);
2139 if ( (cb = cMaxItems * sizeof(DLGHITEM))
2140 && (pArray->paDlgItems = (DLGHITEM*)malloc(cb))
2141 )
2142 {
2143 memset(pArray->paDlgItems, 0, cb);
2144 pArray->cDlgItemsMax = cMaxItems;
2145 *ppArray = pArray;
2146 }
2147 else
2148 arc = ERROR_NOT_ENOUGH_MEMORY;
2149
2150 if (arc)
2151 dlghFreeArray(&pArray);
2152 }
2153 else
2154 arc = ERROR_NOT_ENOUGH_MEMORY;
2155
2156 return arc;
2157}
2158
2159/*
2160 *@@ dlghFreeArray:
2161 * frees a dialog array created by dlghCreateArray.
2162 *
2163 *@@added V0.9.16 (2001-10-15) [umoeller]
2164 */
2165
2166APIRET dlghFreeArray(PDLGARRAY *ppArray)
2167{
2168 PDLGARRAY pArray;
2169 if ( (ppArray)
2170 && (pArray = *ppArray)
2171 )
2172 {
2173 if (pArray->paDlgItems)
2174 free(pArray->paDlgItems);
2175 free(pArray);
2176 }
2177 else
2178 return ERROR_INVALID_PARAMETER;
2179
2180 return NO_ERROR;
2181}
2182
2183/*
2184 *@@ dlghAppendToArray:
2185 * appends a subarray of DLGHITEM's to the
2186 * given DLGARRAY. See dlghCreateArray for
2187 * usage.
2188 *
2189 * Returns:
2190 *
2191 * -- NO_ERROR
2192 *
2193 * -- ERROR_INVALID_PARAMETER
2194 *
2195 * -- DLGERR_ARRAY_TOO_SMALL: pArray does not
2196 * have enough memory to hold the new items.
2197 * The cMaxItems parameter given to dlghCreateArray
2198 * wasn't large enough.
2199 *
2200 *@@added V0.9.16 (2001-10-15) [umoeller]
2201 */
2202
2203APIRET dlghAppendToArray(PDLGARRAY pArray, // in: dialog array created by dlghCreateArray
2204 DLGHITEM *paItems, // in: subarray to be appended
2205 ULONG cItems) // in: subarray item count (NOT array size)
2206{
2207 APIRET arc = NO_ERROR;
2208 if (pArray)
2209 {
2210 if ( (pArray->cDlgItemsMax >= cItems)
2211 && (pArray->cDlgItemsMax - pArray->cDlgItemsNow >= cItems)
2212 )
2213 {
2214 // enough space left in the array:
2215 memcpy(&pArray->paDlgItems[pArray->cDlgItemsNow],
2216 paItems, // source
2217 cItems * sizeof(DLGHITEM));
2218 pArray->cDlgItemsNow += cItems;
2219 }
2220 else
2221 arc = DLGERR_ARRAY_TOO_SMALL;
2222 }
2223 else
2224 arc = ERROR_INVALID_PARAMETER;
2225
2226 return (arc);
2227}
2228
2229/* ******************************************************************
2230 *
2231 * Standard dialogs
2232 *
2233 ********************************************************************/
2234
2235/*
2236 *@@ dlghCreateMessageBox:
2237 *
2238 *@@added V0.9.13 (2001-06-21) [umoeller]
2239 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
2240 */
2241
2242APIRET dlghCreateMessageBox(HWND *phwndDlg,
2243 HWND hwndOwner,
2244 HPOINTER hptrIcon,
2245 const char *pcszTitle,
2246 const char *pcszMessage,
2247 ULONG flFlags,
2248 const char *pcszFont,
2249 const MSGBOXSTRINGS *pStrings,
2250 PULONG pulAlarmFlag) // out: alarm sound to be played
2251{
2252 CONTROLDEF
2253 Icon = {
2254 WC_STATIC,
2255 NULL, // text, set below
2256 WS_VISIBLE | SS_ICON,
2257 0, // ID
2258 NULL, // no font
2259 0,
2260 { SZL_AUTOSIZE, SZL_AUTOSIZE },
2261 5
2262 },
2263 InfoText =
2264 {
2265 WC_STATIC,
2266 NULL, // text, set below
2267 WS_VISIBLE | SS_TEXT | DT_WORDBREAK | DT_LEFT | DT_TOP,
2268 10, // ID
2269 CTL_COMMON_FONT,
2270 0,
2271 { 400, SZL_AUTOSIZE },
2272 5
2273 },
2274 Buttons[] = {
2275 {
2276 WC_BUTTON,
2277 NULL, // text, set below
2278 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2279 1, // ID
2280 CTL_COMMON_FONT, // no font
2281 0,
2282 { 100, 30 },
2283 5
2284 },
2285 {
2286 WC_BUTTON,
2287 NULL, // text, set below
2288 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2289 2, // ID
2290 CTL_COMMON_FONT, // no font
2291 0,
2292 { 100, 30 },
2293 5
2294 },
2295 {
2296 WC_BUTTON,
2297 NULL, // text, set below
2298 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2299 3, // ID
2300 CTL_COMMON_FONT, // no font
2301 0,
2302 { 100, 30 },
2303 5
2304 }
2305 };
2306
2307 DLGHITEM MessageBox[] =
2308 {
2309 START_TABLE,
2310 START_ROW(ROW_VALIGN_CENTER),
2311 CONTROL_DEF(&Icon),
2312 START_TABLE,
2313 START_ROW(ROW_VALIGN_CENTER),
2314 CONTROL_DEF(&InfoText),
2315 START_ROW(ROW_VALIGN_CENTER),
2316 CONTROL_DEF(&Buttons[0]),
2317 CONTROL_DEF(&Buttons[1]),
2318 CONTROL_DEF(&Buttons[2]),
2319 END_TABLE,
2320 END_TABLE
2321 };
2322
2323 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2324
2325 const char *p0 = "Error",
2326 *p1 = NULL,
2327 *p2 = NULL;
2328
2329 Icon.pcszText = (const char *)hptrIcon;
2330 InfoText.pcszText = pcszMessage;
2331
2332 // now work on the three buttons of the dlg template:
2333 // give them proper titles or hide them
2334 if (flButtons == MB_OK)
2335 {
2336 p0 = pStrings->pcszOK;
2337 }
2338 else if (flButtons == MB_OKCANCEL)
2339 {
2340 p0 = pStrings->pcszOK;
2341 p1 = pStrings->pcszCancel;
2342 }
2343 else if (flButtons == MB_RETRYCANCEL)
2344 {
2345 p0 = pStrings->pcszRetry;
2346 p1 = pStrings->pcszCancel;
2347 }
2348 else if (flButtons == MB_ABORTRETRYIGNORE)
2349 {
2350 p0 = pStrings->pcszAbort;
2351 p1 = pStrings->pcszRetry;
2352 p2 = pStrings->pcszIgnore;
2353 }
2354 else if (flButtons == MB_YESNO)
2355 {
2356 p0 = pStrings->pcszYes;
2357 p1 = pStrings->pcszNo;
2358 }
2359 else if (flButtons == MB_YESNOCANCEL)
2360 {
2361 p0 = pStrings->pcszYes;
2362 p1 = pStrings->pcszNo;
2363 p2 = pStrings->pcszCancel;
2364 }
2365 else if (flButtons == MB_CANCEL)
2366 {
2367 p0 = pStrings->pcszCancel;
2368 }
2369 else if (flButtons == MB_ENTER)
2370 {
2371 p0 = pStrings->pcszEnter;
2372 }
2373 else if (flButtons == MB_ENTERCANCEL)
2374 {
2375 p0 = pStrings->pcszEnter;
2376 p1 = pStrings->pcszCancel;
2377 }
2378 else if (flButtons == MB_YES_YES2ALL_NO)
2379 {
2380 p0 = pStrings->pcszYes;
2381 p1 = pStrings->pcszYesToAll;
2382 p2 = pStrings->pcszNo;
2383 }
2384
2385 // now set strings and hide empty buttons
2386 Buttons[0].pcszText = p0;
2387
2388 if (p1)
2389 Buttons[1].pcszText = p1;
2390 else
2391 Buttons[1].flStyle &= ~WS_VISIBLE;
2392
2393 if (p2)
2394 Buttons[2].pcszText = p2;
2395 else
2396 Buttons[2].flStyle &= ~WS_VISIBLE;
2397
2398 // query default button IDs
2399 if (flFlags & MB_DEFBUTTON2)
2400 Buttons[1].flStyle |= BS_DEFAULT;
2401 else if (flFlags & MB_DEFBUTTON3)
2402 Buttons[2].flStyle |= BS_DEFAULT;
2403 else
2404 Buttons[0].flStyle |= BS_DEFAULT;
2405
2406 *pulAlarmFlag = WA_NOTE;
2407 if (flFlags & (MB_ICONHAND | MB_ERROR))
2408 *pulAlarmFlag = WA_ERROR;
2409 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
2410 *pulAlarmFlag = WA_WARNING;
2411
2412 return (dlghCreateDlg(phwndDlg,
2413 hwndOwner,
2414 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2415 WinDefDlgProc,
2416 pcszTitle,
2417 MessageBox,
2418 ARRAYITEMCOUNT(MessageBox),
2419 NULL,
2420 pcszFont));
2421}
2422
2423/*
2424 *@@ dlghProcessMessageBox:
2425 *
2426 *@@added V0.9.13 (2001-06-21) [umoeller]
2427 */
2428
2429ULONG dlghProcessMessageBox(HWND hwndDlg,
2430 ULONG ulAlarmFlag,
2431 ULONG flFlags)
2432{
2433 ULONG ulrcDlg;
2434 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2435
2436 winhCenterWindow(hwndDlg);
2437
2438 if (flFlags & MB_SYSTEMMODAL)
2439 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
2440
2441 if (ulAlarmFlag)
2442 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
2443
2444 ulrcDlg = WinProcessDlg(hwndDlg);
2445
2446 WinDestroyWindow(hwndDlg);
2447
2448 if (flButtons == MB_OK)
2449 return MBID_OK;
2450 else if (flButtons == MB_OKCANCEL)
2451 switch (ulrcDlg)
2452 {
2453 case 1: return MBID_OK;
2454 default: return MBID_CANCEL;
2455 }
2456 else if (flButtons == MB_RETRYCANCEL)
2457 switch (ulrcDlg)
2458 {
2459 case 1: return MBID_RETRY;
2460 default: return MBID_CANCEL;
2461 }
2462 else if (flButtons == MB_ABORTRETRYIGNORE)
2463 switch (ulrcDlg)
2464 {
2465 case 2: return MBID_RETRY;
2466 case 3: return MBID_IGNORE;
2467 default: return MBID_ABORT;
2468 }
2469 else if (flButtons == MB_YESNO)
2470 switch (ulrcDlg)
2471 {
2472 case 1: return MBID_YES;
2473 default: return MBID_NO;
2474 }
2475 else if (flButtons == MB_YESNOCANCEL)
2476 switch (ulrcDlg)
2477 {
2478 case 1: return MBID_YES;
2479 case 2: return MBID_NO;
2480 default: return MBID_CANCEL;
2481 }
2482 else if (flButtons == MB_CANCEL)
2483 return MBID_CANCEL;
2484 else if (flButtons == MB_ENTER)
2485 return MBID_ENTER;
2486 else if (flButtons == MB_ENTERCANCEL)
2487 switch (ulrcDlg)
2488 {
2489 case 1: return MBID_ENTER;
2490 default: return MBID_CANCEL;
2491 }
2492 else if (flButtons == MB_YES_YES2ALL_NO)
2493 switch (ulrcDlg)
2494 {
2495 case 1: return MBID_YES;
2496 case 2: return MBID_YES2ALL;
2497 default: return MBID_NO;
2498 }
2499
2500 return (MBID_CANCEL);
2501}
2502
2503/*
2504 *@@ dlghMessageBox:
2505 * WinMessageBox replacement.
2506 *
2507 * This has all the flags of the standard call,
2508 * but looks much prettier. Besides, it allows
2509 * you to specify any icon to be displayed.
2510 *
2511 * Currently the following flStyle's are supported:
2512 *
2513 * -- MB_OK 0x0000
2514 * -- MB_OKCANCEL 0x0001
2515 * -- MB_RETRYCANCEL 0x0002
2516 * -- MB_ABORTRETRYIGNORE 0x0003
2517 * -- MB_YESNO 0x0004
2518 * -- MB_YESNOCANCEL 0x0005
2519 * -- MB_CANCEL 0x0006
2520 * -- MB_ENTER 0x0007 (not implemented yet)
2521 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
2522 *
2523 * -- MB_YES_YES2ALL_NO 0x0009
2524 * This is new: this has three buttons called "Yes"
2525 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
2526 *
2527 * -- MB_DEFBUTTON2 (for two-button styles)
2528 * -- MB_DEFBUTTON3 (for three-button styles)
2529 *
2530 * -- MB_ICONHAND
2531 * -- MB_ICONEXCLAMATION
2532 *
2533 * Returns MBID_* codes like WinMessageBox.
2534 *
2535 *@@added V0.9.13 (2001-06-21) [umoeller]
2536 */
2537
2538ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
2539 HPOINTER hptrIcon, // in: icon to display
2540 const char *pcszTitle, // in: title
2541 const char *pcszMessage, // in: message
2542 ULONG flFlags, // in: standard message box flags
2543 const char *pcszFont, // in: font (e.g. "9.WarpSans")
2544 const MSGBOXSTRINGS *pStrings) // in: strings array
2545{
2546 HWND hwndDlg;
2547 ULONG ulAlarmFlag;
2548 APIRET arc = dlghCreateMessageBox(&hwndDlg,
2549 hwndOwner,
2550 hptrIcon,
2551 pcszTitle,
2552 pcszMessage,
2553 flFlags,
2554 pcszFont,
2555 pStrings,
2556 &ulAlarmFlag);
2557
2558 if (!arc && hwndDlg)
2559 {
2560 // SHOW DIALOG
2561 return (dlghProcessMessageBox(hwndDlg,
2562 ulAlarmFlag,
2563 flFlags));
2564 }
2565 else
2566 {
2567 CHAR szMsg[100];
2568 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
2569 WinMessageBox(HWND_DESKTOP,
2570 NULLHANDLE,
2571 "Error",
2572 szMsg,
2573 0,
2574 MB_CANCEL | MB_MOVEABLE);
2575 }
2576
2577 return (DID_CANCEL);
2578}
2579
2580/*
2581 *@@ cmnTextEntryBox:
2582 * common dialog for entering a text string.
2583 * The dialog has a descriptive text on top
2584 * with an entry field below and "OK" and "Cancel"
2585 * buttons.
2586 *
2587 * The string from the user is returned in a
2588 * new buffer, which must be free'd by the caller.
2589 * Returns NULL if the user pressed "Cancel".
2590 *
2591 * fl can be any combination of the following
2592 * flags:
2593 *
2594 * -- TEBF_REMOVETILDE: tilde ("~") characters
2595 * are removed from pcszTitle before setting
2596 * the title. Useful for reusing menu item
2597 * texts.
2598 *
2599 * -- TEBF_REMOVEELLIPSE: ellipse ("...") strings
2600 * are removed from pcszTitle before setting
2601 * the title. Useful for reusing menu item
2602 * texts.
2603 *
2604 * -- TEBF_SELECTALL: the default text in the
2605 * entry field is initially highlighted.
2606 *
2607 *@@added V0.9.15 (2001-09-14) [umoeller]
2608 */
2609
2610PSZ dlghTextEntryBox(HWND hwndOwner,
2611 const char *pcszTitle, // in: dlg title
2612 const char *pcszDescription, // in: descriptive text above entry field
2613 const char *pcszDefault, // in: default text for entry field or NULL
2614 const char *pcszOK, // in: "OK" string
2615 const char *pcszCancel, // in: "Cancel" string
2616 ULONG ulMaxLen, // in: maximum length for entry
2617 ULONG fl, // in: TEBF_* flags
2618 const char *pcszFont) // in: font (e.g. "9.WarpSans")
2619{
2620 CONTROLDEF
2621 Static = {
2622 WC_STATIC,
2623 NULL,
2624 WS_VISIBLE | SS_TEXT | DT_LEFT | DT_WORDBREAK,
2625 -1,
2626 CTL_COMMON_FONT,
2627 0,
2628 { 300, SZL_AUTOSIZE }, // size
2629 5 // spacing
2630 },
2631 Entry = {
2632 WC_ENTRYFIELD,
2633 NULL,
2634 WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_MARGIN | ES_AUTOSCROLL,
2635 999,
2636 CTL_COMMON_FONT,
2637 0,
2638 { 300, 20 }, // size
2639 5 // spacing
2640 },
2641 OKButton = {
2642 WC_BUTTON,
2643 NULL,
2644 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_DEFAULT,
2645 DID_OK,
2646 CTL_COMMON_FONT,
2647 0,
2648 { 100, 30 }, // size
2649 5 // spacing
2650 },
2651 CancelButton = {
2652 WC_BUTTON,
2653 NULL,
2654 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2655 DID_CANCEL,
2656 CTL_COMMON_FONT,
2657 0,
2658 { 100, 30 }, // size
2659 5 // spacing
2660 };
2661 DLGHITEM DlgTemplate[] =
2662 {
2663 START_TABLE,
2664 START_ROW(0),
2665 CONTROL_DEF(&Static),
2666 START_ROW(0),
2667 CONTROL_DEF(&Entry),
2668 START_ROW(0),
2669 CONTROL_DEF(&OKButton),
2670 CONTROL_DEF(&CancelButton),
2671 END_TABLE
2672 };
2673
2674 HWND hwndDlg = NULLHANDLE;
2675 PSZ pszReturn = NULL;
2676 XSTRING strTitle;
2677
2678 xstrInitCopy(&strTitle, pcszTitle, 0);
2679
2680 if (fl & (TEBF_REMOVEELLIPSE | TEBF_REMOVETILDE))
2681 {
2682 ULONG ulOfs;
2683 if (fl & TEBF_REMOVEELLIPSE)
2684 {
2685 ulOfs = 0;
2686 while (xstrFindReplaceC(&strTitle,
2687 &ulOfs,
2688 "...",
2689 ""))
2690 ;
2691 }
2692
2693 if (fl & TEBF_REMOVETILDE)
2694 {
2695 ulOfs = 0;
2696 while (xstrFindReplaceC(&strTitle,
2697 &ulOfs,
2698 "~",
2699 ""))
2700 ;
2701 }
2702 }
2703
2704 Static.pcszText = pcszDescription;
2705
2706 OKButton.pcszText = pcszOK;
2707 CancelButton.pcszText = pcszCancel;
2708
2709 if (NO_ERROR == dlghCreateDlg(&hwndDlg,
2710 hwndOwner,
2711 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2712 WinDefDlgProc,
2713 strTitle.psz,
2714 DlgTemplate, // DLGHITEM array
2715 ARRAYITEMCOUNT(DlgTemplate),
2716 NULL,
2717 pcszFont))
2718 {
2719 HWND hwndEF = WinWindowFromID(hwndDlg, 999);
2720 winhCenterWindow(hwndDlg);
2721 winhSetEntryFieldLimit(hwndEF, ulMaxLen);
2722 if (pcszDefault)
2723 {
2724 WinSetWindowText(hwndEF, (PSZ)pcszDefault);
2725 if (fl & TEBF_SELECTALL)
2726 winhEntryFieldSelectAll(hwndEF);
2727 }
2728 WinSetFocus(HWND_DESKTOP, hwndEF);
2729 if (DID_OK == WinProcessDlg(hwndDlg))
2730 pszReturn = winhQueryWindowText(hwndEF);
2731
2732 WinDestroyWindow(hwndDlg);
2733 }
2734
2735 xstrClear(&strTitle);
2736
2737 return (pszReturn);
2738}
2739
2740/* ******************************************************************
2741 *
2742 * Dialog input handlers
2743 *
2744 ********************************************************************/
2745
2746/*
2747 *@@ dlghSetPrevFocus:
2748 * "backward" function for rotating the focus
2749 * in a dialog when the "shift+tab" keys get
2750 * pressed.
2751 *
2752 * pllWindows must be a linked list with the
2753 * plain HWND window handles of the focussable
2754 * controls in the dialog.
2755 */
2756
2757VOID dlghSetPrevFocus(PVOID pvllWindows)
2758{
2759 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2760
2761 // check current focus
2762 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
2763
2764 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
2765
2766 BOOL fRestart = FALSE;
2767
2768 while (pNode)
2769 {
2770 CHAR szClass[100];
2771
2772 // previos node
2773 pNode = pNode->pPrevious;
2774
2775 if ( (!pNode) // no next, or not found:
2776 && (!fRestart) // avoid infinite looping if bad list
2777 )
2778 {
2779 pNode = lstQueryLastNode(pllWindows);
2780 fRestart = TRUE;
2781 }
2782
2783 if (pNode)
2784 {
2785 // check if this is a focusable control
2786 if (WinQueryClassName((HWND)pNode->pItemData,
2787 sizeof(szClass),
2788 szClass))
2789 {
2790 if ( (strcmp(szClass, "#5")) // not static
2791 )
2792 break;
2793 // else: go for next then
2794 }
2795 }
2796 }
2797
2798 if (pNode)
2799 {
2800 WinSetFocus(HWND_DESKTOP,
2801 (HWND)pNode->pItemData);
2802 }
2803}
2804
2805/*
2806 *@@ dlghSetNextFocus:
2807 * "forward" function for rotating the focus
2808 * in a dialog when the "ab" key gets pressed.
2809 *
2810 * pllWindows must be a linked list with the
2811 * plain HWND window handles of the focussable
2812 * controls in the dialog.
2813 */
2814
2815VOID dlghSetNextFocus(PVOID pvllWindows)
2816{
2817 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2818
2819 // check current focus
2820 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
2821
2822 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
2823
2824 BOOL fRestart = FALSE;
2825
2826 while (pNode)
2827 {
2828 CHAR szClass[100];
2829
2830 // next focus in node
2831 pNode = pNode->pNext;
2832
2833 if ( (!pNode) // no next, or not found:
2834 && (!fRestart) // avoid infinite looping if bad list
2835 )
2836 {
2837 pNode = lstQueryFirstNode(pllWindows);
2838 fRestart = TRUE;
2839 }
2840
2841 if (pNode)
2842 {
2843 // check if this is a focusable control
2844 if (WinQueryClassName((HWND)pNode->pItemData,
2845 sizeof(szClass),
2846 szClass))
2847 {
2848 if ( (strcmp(szClass, "#5")) // not static
2849 )
2850 break;
2851 // else: go for next then
2852 }
2853 }
2854 }
2855
2856 if (pNode)
2857 {
2858 WinSetFocus(HWND_DESKTOP,
2859 (HWND)pNode->pItemData);
2860 }
2861}
2862
2863/*
2864 *@@ dlghProcessMnemonic:
2865 * finds the control which matches usch
2866 * and gives it the focus. If this is a
2867 * static control, the next control in the
2868 * list is given focus instead. (Standard
2869 * dialog behavior.)
2870 *
2871 * Pass in usch from WM_CHAR. It is assumed
2872 * that the caller has already tested for
2873 * the "alt" key to be depressed.
2874 *
2875 *@@added V0.9.9 (2001-03-17) [umoeller]
2876 */
2877
2878HWND dlghProcessMnemonic(PVOID pvllWindows,
2879 USHORT usch)
2880{
2881 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2882
2883 HWND hwndFound = NULLHANDLE;
2884 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2885 CHAR szClass[100];
2886
2887 while (pNode)
2888 {
2889 HWND hwnd = (HWND)pNode->pItemData;
2890
2891 if (WinSendMsg(hwnd,
2892 WM_MATCHMNEMONIC,
2893 (MPARAM)usch,
2894 0))
2895 {
2896 // according to the docs, only buttons and static
2897 // return TRUE to that msg;
2898 // if this is a static, give focus to the next
2899 // control
2900
2901 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
2902
2903 // check if this is a focusable control
2904 if (WinQueryClassName(hwnd,
2905 sizeof(szClass),
2906 szClass))
2907 {
2908 if (!strcmp(szClass, "#3"))
2909 // it's a button: click it
2910 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
2911 else if (!strcmp(szClass, "#5"))
2912 {
2913 // it's a static: give focus to following control
2914 pNode = pNode->pNext;
2915 if (pNode)
2916 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
2917 }
2918 }
2919 else
2920 // any other control (are there any?): give them focus
2921 WinSetFocus(HWND_DESKTOP, hwnd);
2922
2923 // in any case, stop
2924 hwndFound = hwnd;
2925 break;
2926 }
2927
2928 pNode = pNode->pNext;
2929 }
2930
2931 return (hwndFound);
2932}
2933
2934/*
2935 *@@ dlghEnter:
2936 * presses the first button with BS_DEFAULT.
2937 */
2938
2939BOOL dlghEnter(PVOID pvllWindows)
2940{
2941 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2942
2943 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2944 CHAR szClass[100];
2945 while (pNode)
2946 {
2947 HWND hwnd = (HWND)pNode->pItemData;
2948 if (WinQueryClassName(hwnd,
2949 sizeof(szClass),
2950 szClass))
2951 {
2952 if (!strcmp(szClass, "#3")) // button
2953 {
2954 // _Pmpf((__FUNCTION__ ": found button"));
2955 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
2956 == (BS_PUSHBUTTON | BS_DEFAULT)
2957 )
2958 {
2959 // _Pmpf((" is default!"));
2960 WinPostMsg(hwnd,
2961 BM_CLICK,
2962 (MPARAM)TRUE, // upclick
2963 0);
2964 return (TRUE);
2965 }
2966 }
2967 }
2968
2969 pNode = pNode->pNext;
2970 }
2971
2972 return (FALSE);
2973}
2974
2975
Note: See TracBrowser for help on using the repository browser.