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

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

Buncha fixes, and fixes for fixes.

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