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

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

Major work on textview control and dialogs.

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