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

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

Sources as of V0.9.20.

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