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

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

Patches from Martin and Paul, plus regexp support.

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