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

Last change on this file since 199 was 199, checked in by umoeller, 23 years ago

Menu fixes.

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