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

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

misc fixes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 108.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 PLINKLIST pllControls; // linked list of HWNDs in the order
112 // in which controls were created;
113 // ptr can be NULL
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
232static APIRET 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
243static VOID 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
309static APIRET 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
375static APIRET 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
478static APIRET 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
604static APIRET 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
691static APIRET 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 // append window that was created
913 // V0.9.18 (2002-03-03) [umoeller]
914 if (pDlgData->pllControls)
915 lstAppendItem(pDlgData->pllControls,
916 (PVOID)pColumnDef->hwndControl);
917
918 // if this is the first control with WS_TABSTOP,
919 // we give it the focus later
920 if ( (flStyle & WS_TABSTOP)
921 && (!pDlgData->hwndFirstFocus)
922 )
923 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
924
925 // if this is the first default push button,
926 // go store it too
927 // V0.9.14 (2001-08-21) [umoeller]
928 if ( (!pDlgData->hwndDefPushbutton)
929 && ((ULONG)pControlDef->pcszClass == 0xffff0003L)
930 && (pControlDef->flStyle & BS_DEFAULT)
931 )
932 pDlgData->hwndDefPushbutton = pColumnDef->hwndControl;
933 }
934 else
935 // V0.9.14 (2001-08-03) [umoeller]
936 arc = DLGERR_CANNOT_CREATE_CONTROL;
937 }
938
939 return (arc);
940}
941
942/*
943 *@@ ProcessColumn:
944 * processes a column, which per definition is either
945 * a control or a nested subtable.
946 *
947 * A column is part of a row, which in turn is part
948 * of a table. There can be several columns in a row,
949 * and several rows in a table.
950 *
951 * Since tables may be specified as columns, it is
952 * possible to produce complex dialog layouts by
953 * nesting tables.
954 *
955 * This does the following:
956 *
957 * -- PROCESS_1_CALC_SIZES: size is taken from control def,
958 * or for tables, this produces a recursive ProcessTable
959 * call.
960 * Preconditions: none.
961 *
962 * -- PROCESS_4_CALC_POSITIONS: position of each column
963 * is taken from *plX, which is increased by the
964 * column width by this call.
965 *
966 * Preconditions: Owning row must already have its
967 * y position properly set, or we can't compute
968 * ours. Besides, plX must point to the current X
969 * in the row and will be incremented by the columns
970 * size here.
971 *
972 * -- PROCESS_5_CREATE_CONTROLS: well, creates the controls.
973 *
974 * For tables, this recurses again. If the table has
975 * a string assigned, this also produces a group box
976 * after the recursion.
977 *
978 *@@changed V0.9.12 (2001-05-31) [umoeller]: added control data
979 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed font problems
980 */
981
982static APIRET ProcessColumn(PCOLUMNDEF pColumnDef,
983 PROWDEF pOwningRow, // in: current row from ProcessRow
984 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
985 PLONG plX, // in/out: PROCESS_4_CALC_POSITIONS only
986 PDLGPRIVATE pDlgData)
987{
988 APIRET arc = NO_ERROR;
989
990 pColumnDef->pOwningRow = pOwningRow;
991
992 switch (ProcessMode)
993 {
994 /*
995 * PROCESS_1_CALC_SIZES:
996 * step 1.
997 */
998
999 case PROCESS_1_CALC_SIZES:
1000 arc = ColumnCalcSizes(pColumnDef,
1001 ProcessMode,
1002 pDlgData);
1003 break;
1004
1005 /*
1006 * PROCESS_2_CALC_SIZES_FROM_TABLES:
1007 *
1008 */
1009
1010 case PROCESS_2_CALC_SIZES_FROM_TABLES:
1011 if (pColumnDef->fIsNestedTable)
1012 {
1013 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
1014 if (!(arc = ProcessTable(pTableDef,
1015 NULL,
1016 PROCESS_2_CALC_SIZES_FROM_TABLES,
1017 pDlgData)))
1018 ;
1019 }
1020 else
1021 {
1022 // no nested table, but control:
1023 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
1024
1025 if ( (pControlDef->szlControlProposed.cx < -1)
1026 && (pControlDef->szlControlProposed.cx >= -100)
1027 )
1028 {
1029 // other negative CX value:
1030 // this we ignored during PROCESS_1_CALC_SIZES
1031 // (see ColumnCalcSizes); now set it to the
1032 // table width!
1033 ULONG cxThis = pOwningRow->pOwningTable->cpTable.cx
1034 * -pControlDef->szlControlProposed.cx / 100;
1035
1036 // but the table already has spacing applied,
1037 // so reduce that
1038 pColumnDef->cpControl.cx = cxThis
1039 - (2 * pControlDef->ulSpacing);
1040
1041 pColumnDef->cpColumn.cx = cxThis;
1042
1043 // now we might have to re-compute auto-size
1044 if (pControlDef->szlControlProposed.cy == -1)
1045 {
1046 SIZEL szlAuto;
1047 if (!(arc = CalcAutoSize(pControlDef,
1048 // now that we now the width,
1049 // use that!
1050 pColumnDef->cpControl.cx,
1051 &szlAuto,
1052 pDlgData)))
1053 {
1054 LONG cyColumnOld = pColumnDef->cpColumn.cy;
1055 LONG lDelta;
1056 PROWDEF pRowThis;
1057
1058 pColumnDef->cpControl.cy = szlAuto.cy;
1059 pColumnDef->cpColumn.cy = szlAuto.cy
1060 + (2 * pControlDef->ulSpacing);
1061 }
1062 }
1063 }
1064
1065 if ( (pControlDef->szlControlProposed.cy < -1)
1066 && (pControlDef->szlControlProposed.cy >= -100)
1067 )
1068 {
1069 // same thing for CY, but this time we
1070 // take the row height
1071 ULONG cyThis = pOwningRow->cpRow.cy
1072 * -pControlDef->szlControlProposed.cy / 100;
1073
1074 // but the table already has spacing applied,
1075 // so reduce that
1076 pColumnDef->cpControl.cy = cyThis
1077 - (2 * pControlDef->ulSpacing);
1078
1079 pColumnDef->cpColumn.cy = cyThis;
1080 }
1081 }
1082 break;
1083
1084 /*
1085 * PROCESS_3_CALC_FINAL_TABLE_SIZES:
1086 *
1087 */
1088
1089 case PROCESS_3_CALC_FINAL_TABLE_SIZES:
1090 // re-run calc sizes since we now know all
1091 // the auto-size items
1092 arc = ColumnCalcSizes(pColumnDef,
1093 ProcessMode,
1094 pDlgData);
1095 break;
1096
1097 /*
1098 * PROCESS_4_CALC_POSITIONS:
1099 * step 4.
1100 */
1101
1102 case PROCESS_4_CALC_POSITIONS:
1103 arc = ColumnCalcPositions(pColumnDef,
1104 pOwningRow,
1105 plX,
1106 pDlgData);
1107 break;
1108
1109 /*
1110 * PROCESS_5_CREATE_CONTROLS:
1111 * step 5.
1112 */
1113
1114 case PROCESS_5_CREATE_CONTROLS:
1115 arc = ColumnCreateControls(pColumnDef,
1116 pDlgData);
1117 break;
1118 }
1119
1120 return (arc);
1121}
1122
1123/*
1124 *@@ ProcessRow:
1125 * level-3 procedure (called from ProcessTable),
1126 * which in turn calls ProcessColumn for each column
1127 * in the row.
1128 *
1129 * See ProcessAll for the meaning of ProcessMode.
1130 */
1131
1132static APIRET ProcessRow(PROWDEF pRowDef,
1133 PTABLEDEF pOwningTable, // in: current table from ProcessTable
1134 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
1135 PLONG plY, // in/out: current y position (decremented)
1136 PDLGPRIVATE pDlgData)
1137{
1138 APIRET arc = NO_ERROR;
1139 LONG lX;
1140 PLISTNODE pNode;
1141
1142 pRowDef->pOwningTable = pOwningTable;
1143
1144 if ( (ProcessMode == PROCESS_1_CALC_SIZES)
1145 || (ProcessMode == PROCESS_3_CALC_FINAL_TABLE_SIZES)
1146 )
1147 {
1148 pRowDef->cpRow.cx = 0;
1149 pRowDef->cpRow.cy = 0;
1150 }
1151 else if (ProcessMode == PROCESS_4_CALC_POSITIONS)
1152 {
1153 // set up x and y so that the columns can
1154 // base on that
1155 pRowDef->cpRow.x = pOwningTable->cpTable.x;
1156 // decrease y by row height
1157 *plY -= pRowDef->cpRow.cy;
1158 // and use that for our bottom position
1159 pRowDef->cpRow.y = *plY;
1160
1161 // set lX to left of row; used by column calls below
1162 lX = pRowDef->cpRow.x;
1163 }
1164
1165 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
1166 {
1167 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
1168
1169 if (!(arc = ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData)))
1170 {
1171 if ( (ProcessMode == PROCESS_1_CALC_SIZES)
1172 || (ProcessMode == PROCESS_3_CALC_FINAL_TABLE_SIZES)
1173 )
1174 {
1175 // row width = sum of all columns
1176 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
1177
1178 // row height = maximum height of a column
1179 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
1180 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
1181 }
1182 }
1183 }
1184
1185 return (arc);
1186}
1187
1188/*
1189 *@@ ProcessTable:
1190 * level-2 procedure (called from ProcessAll),
1191 * which in turn calls ProcessRow for each row
1192 * in the table (which in turn calls ProcessColumn
1193 * for each column in the row).
1194 *
1195 * See ProcessAll for the meaning of ProcessMode.
1196 *
1197 * This routine is a bit sick because it can even be
1198 * called recursively from ProcessColumn (!) if a
1199 * nested table is found in a COLUMNDEF.
1200 *
1201 * With PROCESS_4_CALC_POSITIONS, pptl must specify
1202 * the lower left corner of the table. For the
1203 * root call, this will be {0, 0}; for nested calls,
1204 * this must be the lower left corner of the column
1205 * to which the nested table belongs.
1206 *
1207 */
1208
1209static APIRET ProcessTable(PTABLEDEF pTableDef,
1210 const CONTROLPOS *pcpTable, // in: table position with PROCESS_4_CALC_POSITIONS
1211 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
1212 PDLGPRIVATE pDlgData)
1213{
1214 APIRET arc = NO_ERROR;
1215 LONG lY;
1216 PLISTNODE pNode;
1217
1218 switch (ProcessMode)
1219 {
1220 case PROCESS_1_CALC_SIZES:
1221 case PROCESS_3_CALC_FINAL_TABLE_SIZES:
1222 pTableDef->cpTable.cx = 0;
1223 pTableDef->cpTable.cy = 0;
1224 break;
1225
1226 case PROCESS_4_CALC_POSITIONS:
1227 pTableDef->cpTable.x = pcpTable->x;
1228 pTableDef->cpTable.y = pcpTable->y;
1229
1230 // start the rows on top
1231 lY = pcpTable->y + pTableDef->cpTable.cy;
1232 break;
1233 }
1234
1235 FOR_ALL_NODES(&pTableDef->llRows, pNode)
1236 {
1237 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
1238
1239 if (!(arc = ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData)))
1240 {
1241 if ( (ProcessMode == PROCESS_1_CALC_SIZES)
1242 || (ProcessMode == PROCESS_3_CALC_FINAL_TABLE_SIZES)
1243 )
1244 {
1245 // table width = maximum width of a row
1246 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
1247 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
1248
1249 // table height = sum of all rows
1250 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
1251 }
1252 }
1253 else
1254 break;
1255 }
1256
1257 return (arc);
1258}
1259
1260/*
1261 *@@ ProcessAll:
1262 * level-1 procedure, which in turn calls ProcessTable
1263 * for each root-level table found (which in turn
1264 * calls ProcessRow for each row in the table, which
1265 * in turn calls ProcessColumn for each column in
1266 * the row).
1267 *
1268 * The first trick to formatting is that ProcessAll will
1269 * get three times, thus going down the entire tree three
1270 * times, with ProcessMode being set to one of the
1271 * following for each call (in this order):
1272 *
1273 * -- PROCESS_1_CALC_SIZES: calculates the preliminary
1274 * sizes of all tables, rows, columns, and controls
1275 * except those controls that have specified that
1276 * their size should depend on others.
1277 *
1278 * -- PROCESS_2_CALC_SIZES_FROM_TABLES: calculates the
1279 * sizes of those controls that want to depend on
1280 * others.
1281 *
1282 * -- PROCESS_3_CALC_FINAL_TABLE_SIZES: since the table
1283 * and row sizes might have changed during
1284 * PROCESS_2_CALC_SIZES_FROM_TABLES, we need to re-run
1285 * to re-calculate the size of all rows and tables.
1286 * After this first call, we know _all_ the sizes
1287 * and can then calculate the positions.
1288 *
1289 * -- PROCESS_4_CALC_POSITIONS: calculates the positions
1290 * based on the sizes calculated before.
1291 *
1292 * -- PROCESS_5_CREATE_CONTROLS: creates the controls with the
1293 * positions and sizes calculated before.
1294 *
1295 * The second trick is the precondition that tables may
1296 * nest by allowing another table definition in a column.
1297 * This way we can recurse from ProcessColumn back into
1298 * ProcessTable and thus know the size and position of a
1299 * nested table column just as if it were a regular control.
1300 */
1301
1302static APIRET ProcessAll(PDLGPRIVATE pDlgData,
1303 PROCESSMODE ProcessMode)
1304{
1305 APIRET arc = NO_ERROR;
1306 PLISTNODE pNode;
1307 CONTROLPOS cpTable;
1308 ZERO(&cpTable);
1309
1310 switch (ProcessMode)
1311 {
1312 case PROCESS_1_CALC_SIZES:
1313 case PROCESS_3_CALC_FINAL_TABLE_SIZES:
1314 pDlgData->szlClient.cx = 0;
1315 pDlgData->szlClient.cy = 0;
1316 break;
1317
1318 case PROCESS_4_CALC_POSITIONS:
1319 // start with the table on top
1320 cpTable.y = pDlgData->szlClient.cy;
1321 break;
1322 }
1323
1324 FOR_ALL_NODES(&pDlgData->llTables, pNode)
1325 {
1326 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
1327
1328 if (ProcessMode == PROCESS_4_CALC_POSITIONS)
1329 {
1330 cpTable.x = 0;
1331 cpTable.y -= pTableDefThis->cpTable.cy;
1332 }
1333
1334 if (!(arc = ProcessTable(pTableDefThis,
1335 &cpTable, // start pos
1336 ProcessMode,
1337 pDlgData)))
1338 {
1339 if ( (ProcessMode == PROCESS_2_CALC_SIZES_FROM_TABLES)
1340 || (ProcessMode == PROCESS_3_CALC_FINAL_TABLE_SIZES)
1341 )
1342 {
1343 // all sizes have now been computed:
1344 pDlgData->szlClient.cx += pTableDefThis->cpTable.cx;
1345 pDlgData->szlClient.cy += pTableDefThis->cpTable.cy;
1346 }
1347 }
1348 }
1349
1350 return (arc);
1351}
1352
1353/*
1354 *@@ CreateColumn:
1355 *
1356 */
1357
1358static APIRET CreateColumn(PROWDEF pCurrentRow,
1359 BOOL fIsNestedTable,
1360 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
1361 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
1362{
1363 APIRET arc = NO_ERROR;
1364
1365 if (!pCurrentRow)
1366 arc = DLGERR_CONTROL_BEFORE_ROW;
1367 else
1368 {
1369 // append the control def
1370 if (!pvDefinition)
1371 arc = DLGERR_NULL_CTL_DEF;
1372 else
1373 {
1374 // create column and store ctl def
1375 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
1376 if (!pColumnDef)
1377 arc = ERROR_NOT_ENOUGH_MEMORY;
1378 else
1379 {
1380 memset(pColumnDef, 0, sizeof(COLUMNDEF));
1381 pColumnDef->pOwningRow = pCurrentRow;
1382 pColumnDef->fIsNestedTable = fIsNestedTable;
1383 pColumnDef->pvDefinition = pvDefinition;
1384
1385 *ppColumnDef = pColumnDef;
1386 }
1387 }
1388 }
1389
1390 return (arc);
1391}
1392
1393/*
1394 *@@ FreeTable:
1395 * frees the specified table and recurses
1396 * into nested tables, if necessary.
1397 *
1398 * This was added with V0.9.14 to fix the
1399 * bad memory leaks with nested tables.
1400 *
1401 *@@added V0.9.14 (2001-08-01) [umoeller]
1402 */
1403
1404static VOID FreeTable(PTABLEDEF pTable)
1405{
1406 // for each table, clean up the rows
1407 PLISTNODE pRowNode;
1408 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1409 {
1410 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1411
1412 // for each row, clean up the columns
1413 PLISTNODE pColumnNode;
1414 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1415 {
1416 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1417
1418 if (pColumn->fIsNestedTable)
1419 {
1420 // nested table: recurse!
1421 PTABLEDEF pNestedTable = (PTABLEDEF)pColumn->pvDefinition;
1422 FreeTable(pNestedTable);
1423 }
1424
1425 free(pColumn);
1426 }
1427 lstClear(&pRow->llColumns);
1428
1429 free(pRow);
1430 }
1431 lstClear(&pTable->llRows);
1432
1433 free(pTable);
1434}
1435
1436/* ******************************************************************
1437 *
1438 * Dialog formatter engine
1439 *
1440 ********************************************************************/
1441
1442/*
1443 *@@ STACKITEM:
1444 *
1445 */
1446
1447typedef struct _STACKITEM
1448{
1449 PTABLEDEF pLastTable;
1450 PROWDEF pLastRow;
1451
1452} STACKITEM, *PSTACKITEM;
1453
1454#define SPACING 10
1455
1456/*
1457 *@@ Dlg0_Init:
1458 *
1459 *@@added V0.9.15 (2001-08-26) [umoeller]
1460 *@@changed V0.9.18 (2002-03-03) [umoeller]: aded pllWindows
1461 */
1462
1463static APIRET Dlg0_Init(PDLGPRIVATE *ppDlgData,
1464 PCSZ pcszControlsFont,
1465 PLINKLIST pllControls)
1466{
1467 PDLGPRIVATE pDlgData;
1468 if (!(pDlgData = NEW(DLGPRIVATE)))
1469 return (ERROR_NOT_ENOUGH_MEMORY);
1470 ZERO(pDlgData);
1471 lstInit(&pDlgData->llTables, FALSE);
1472
1473 if (pllControls)
1474 pDlgData->pllControls = pllControls;
1475
1476 pDlgData->pcszControlsFont = pcszControlsFont;
1477
1478 *ppDlgData = pDlgData;
1479
1480 return NO_ERROR;
1481}
1482
1483/*
1484 *@@ Dlg1_ParseTables:
1485 *
1486 *@@added V0.9.15 (2001-08-26) [umoeller]
1487 */
1488
1489static APIRET Dlg1_ParseTables(PDLGPRIVATE pDlgData,
1490 PCDLGHITEM paDlgItems, // in: definition array
1491 ULONG cDlgItems) // in: array item count (NOT array size)
1492{
1493 APIRET arc = NO_ERROR;
1494
1495 LINKLIST llStack;
1496 ULONG ul;
1497 PTABLEDEF pCurrentTable = NULL;
1498 PROWDEF pCurrentRow = NULL;
1499
1500 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1501
1502 for (ul = 0;
1503 ul < cDlgItems;
1504 ul++)
1505 {
1506 PCDLGHITEM pItemThis = &paDlgItems[ul];
1507
1508 switch (pItemThis->Type)
1509 {
1510 /*
1511 * TYPE_START_NEW_TABLE:
1512 *
1513 */
1514
1515 case TYPE_START_NEW_TABLE:
1516 {
1517 // root table or nested?
1518 BOOL fIsRoot = (pCurrentTable == NULL);
1519
1520 // push the current table on the stack
1521 PSTACKITEM pStackItem;
1522 if (!(pStackItem = NEW(STACKITEM)))
1523 {
1524 arc = ERROR_NOT_ENOUGH_MEMORY;
1525 break;
1526 }
1527 else
1528 {
1529 pStackItem->pLastTable = pCurrentTable;
1530 pStackItem->pLastRow = pCurrentRow;
1531 lstPush(&llStack, pStackItem);
1532 }
1533
1534 // create new table
1535 if (!(pCurrentTable = NEW(TABLEDEF)))
1536 arc = ERROR_NOT_ENOUGH_MEMORY;
1537 else
1538 {
1539 ZERO(pCurrentTable);
1540
1541 lstInit(&pCurrentTable->llRows, FALSE);
1542
1543 if (pItemThis->ulData)
1544 // control specified: store it (this will become a PM group)
1545 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1546
1547 if (fIsRoot)
1548 // root table:
1549 // append to dialog data list
1550 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1551 else
1552 {
1553 // nested table:
1554 // create "table" column for this
1555 PCOLUMNDEF pColumnDef;
1556 if (!(arc = CreateColumn(pCurrentRow,
1557 TRUE, // nested table
1558 pCurrentTable,
1559 &pColumnDef)))
1560 {
1561 pCurrentTable->pOwningColumn = pColumnDef;
1562 lstAppendItem(&pCurrentRow->llColumns,
1563 pColumnDef);
1564 }
1565 }
1566 }
1567
1568 pCurrentRow = NULL;
1569 break; }
1570
1571 /*
1572 * TYPE_START_NEW_ROW:
1573 *
1574 */
1575
1576 case TYPE_START_NEW_ROW:
1577 {
1578 if (!pCurrentTable)
1579 arc = DLGERR_ROW_BEFORE_TABLE;
1580 else
1581 {
1582 // create new row
1583 if (!(pCurrentRow = NEW(ROWDEF)))
1584 arc = ERROR_NOT_ENOUGH_MEMORY;
1585 else
1586 {
1587 ZERO(pCurrentRow);
1588
1589 pCurrentRow->pOwningTable = pCurrentTable;
1590 lstInit(&pCurrentRow->llColumns, FALSE);
1591
1592 pCurrentRow->flRowFormat = pItemThis->ulData;
1593
1594 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1595 }
1596 }
1597 break; }
1598
1599 /*
1600 * TYPE_CONTROL_DEF:
1601 *
1602 */
1603
1604 case TYPE_CONTROL_DEF:
1605 {
1606 PCOLUMNDEF pColumnDef;
1607 if (!(arc = CreateColumn(pCurrentRow,
1608 FALSE, // no nested table
1609 (PVOID)pItemThis->ulData,
1610 &pColumnDef)))
1611 lstAppendItem(&pCurrentRow->llColumns,
1612 pColumnDef);
1613 break; }
1614
1615 /*
1616 * TYPE_END_TABLE:
1617 *
1618 */
1619
1620 case TYPE_END_TABLE:
1621 {
1622 PLISTNODE pNode = lstPop(&llStack);
1623 if (!pNode)
1624 // nothing on the stack:
1625 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1626 else
1627 {
1628 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1629 pCurrentTable = pStackItem->pLastTable;
1630 pCurrentRow = pStackItem->pLastRow;
1631
1632 lstRemoveNode(&llStack, pNode);
1633 }
1634 break; }
1635
1636 default:
1637 arc = DLGERR_INVALID_CODE;
1638 }
1639
1640 if (arc)
1641 break;
1642 }
1643
1644 if ((!arc) && (lstCountItems(&llStack)))
1645 arc = DLGERR_TABLE_NOT_CLOSED;
1646
1647 lstClear(&llStack);
1648
1649 return (arc);
1650}
1651
1652/*
1653 *@@ Dlg2_CalcSizes:
1654 *
1655 * After this, DLGPRIVATE.szlClient is valid.
1656 *
1657 *@@added V0.9.15 (2001-08-26) [umoeller]
1658 */
1659
1660static APIRET Dlg2_CalcSizes(PDLGPRIVATE pDlgData)
1661{
1662 APIRET arc;
1663
1664 if (!(arc = ProcessAll(pDlgData,
1665 PROCESS_1_CALC_SIZES)))
1666 // this goes into major recursions...
1667 // run again to compute sizes that depend on tables
1668 if (!(arc = ProcessAll(pDlgData,
1669 PROCESS_2_CALC_SIZES_FROM_TABLES)))
1670 arc = ProcessAll(pDlgData,
1671 PROCESS_3_CALC_FINAL_TABLE_SIZES);
1672
1673 // free the cached font resources that
1674 // might have been created here
1675 if (pDlgData->hps)
1676 {
1677 if (pDlgData->lcidLast)
1678 {
1679 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1680 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1681 }
1682 WinReleasePS(pDlgData->hps);
1683 }
1684
1685 return (arc);
1686}
1687
1688/*
1689 *@@ Dlg3_PositionAndCreate:
1690 *
1691 *@@added V0.9.15 (2001-08-26) [umoeller]
1692 *@@changed V0.9.15 (2001-08-26) [umoeller]: BS_DEFAULT for other than first button was ignored, fixed
1693 */
1694
1695static APIRET Dlg3_PositionAndCreate(PDLGPRIVATE pDlgData,
1696 HWND *phwndFocusItem) // out: item to give focus to
1697{
1698 APIRET arc = NO_ERROR;
1699
1700 /*
1701 * 5) compute _positions_ of all controls
1702 *
1703 */
1704
1705 ProcessAll(pDlgData,
1706 PROCESS_4_CALC_POSITIONS);
1707
1708 /*
1709 * 6) create control windows, finally
1710 *
1711 */
1712
1713 pDlgData->ptlTotalOfs.x
1714 = pDlgData->ptlTotalOfs.y
1715 = SPACING;
1716
1717 ProcessAll(pDlgData,
1718 PROCESS_5_CREATE_CONTROLS);
1719
1720 if (pDlgData->hwndDefPushbutton)
1721 {
1722 // we had a default pushbutton:
1723 // go set it V0.9.14 (2001-08-21) [umoeller]
1724 WinSetWindowULong(pDlgData->hwndDlg,
1725 QWL_DEFBUTTON,
1726 pDlgData->hwndDefPushbutton);
1727 *phwndFocusItem = pDlgData->hwndDefPushbutton;
1728 // V0.9.15 (2001-08-26) [umoeller]
1729 }
1730 else
1731 *phwndFocusItem = (pDlgData->hwndFirstFocus)
1732 ? pDlgData->hwndFirstFocus
1733 : pDlgData->hwndDlg;
1734
1735 return (arc);
1736}
1737
1738/*
1739 *@@ Dlg9_Cleanup:
1740 *
1741 *@@added V0.9.15 (2001-08-26) [umoeller]
1742 */
1743
1744static VOID Dlg9_Cleanup(PDLGPRIVATE *ppDlgData)
1745{
1746 PDLGPRIVATE pDlgData;
1747 if ( (ppDlgData)
1748 && (pDlgData = *ppDlgData)
1749 )
1750 {
1751 PLISTNODE pTableNode;
1752
1753 // in any case, clean up our mess:
1754
1755 // clean up the tables
1756 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1757 {
1758 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1759
1760 FreeTable(pTable);
1761 // this may recurse for nested tables
1762 }
1763
1764 lstClear(&pDlgData->llTables);
1765
1766 free(pDlgData);
1767
1768 *ppDlgData = NULL;
1769 }
1770}
1771
1772/* ******************************************************************
1773 *
1774 * Dialog formatter entry points
1775 *
1776 ********************************************************************/
1777
1778/*
1779 *@@ dlghCreateDlg:
1780 * replacement for WinCreateDlg/WinLoadDlg for creating a
1781 * dialog from a settings array in memory, which is
1782 * formatted automatically.
1783 *
1784 * This does NOT use regular dialog templates from
1785 * module resources. Instead, you pass in an array
1786 * of DLGHITEM structures, which define the controls
1787 * and how they are to be formatted.
1788 *
1789 * The main advantage compared to dialog resources is
1790 * that with this function, you will never have to
1791 * define control _positions_. Instead, you only specify
1792 * the control _sizes_, and all positions are computed
1793 * automatically here. Even better, for many controls,
1794 * auto-sizing is supported according to the control's
1795 * text (e.g. for statics and checkboxes). In a way,
1796 * this is a bit similar to HTML tables.
1797 *
1798 * A regular standard dialog would use something like
1799 *
1800 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
1801 *
1802 * for flCreateFlags. To make the dlg sizeable, specify
1803 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
1804 *
1805 * <B>Usage:</B>
1806 *
1807 * Like WinLoadDlg, this creates a standard WC_FRAME and
1808 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
1809 * to the dialog with pCreateParams in mp2.
1810 *
1811 * If this func returns no error, you can then use
1812 * WinProcessDlg with the newly created dialog as usual. In
1813 * your dlg proc, use WinDefDlgProc as usual.
1814 *
1815 * There is NO run-time overhead for either code or memory
1816 * after dialog creation; after this function returns, the
1817 * dialog is a standard dialog as if loaded from WinLoadDlg.
1818 * The array of DLGHITEM structures defines how the
1819 * dialog is set up. All this is ONLY used by this function
1820 * and NOT needed after the dialog has been created.
1821 *
1822 * In DLGHITEM, the "Type" field determines what this
1823 * structure defines. A number of handy macros have been
1824 * defined to make this easier and to provide type-checking
1825 * at compile time. See dialog.h for more.
1826 *
1827 * Essentially, such a dialog item operates similarly to
1828 * HTML tables. There are rows and columns in the table,
1829 * and each control which is specified must be a column
1830 * in some table. Tables may also nest (see below).
1831 *
1832 * The DLGHITEM macros are:
1833 *
1834 * -- START_TABLE starts a new table. The tables may nest,
1835 * but must each be properly terminated with END_TABLE.
1836 *
1837 * -- START_GROUP_TABLE(pDef) starts a group. This
1838 * behaves exacly like START_TABLE, but in addition,
1839 * it produces a static group control around the table.
1840 * Useful for group boxes. pDef must point to a
1841 * CONTROLDEF describing the control to be used for
1842 * the group (usually a WC_STATIC with SS_GROUP style),
1843 * whose size parameter is ignored.
1844 *
1845 * As with START_TABLE, START_GROUP_TABLE must be
1846 * terminated with END_TABLE.
1847 *
1848 * -- START_ROW(fl) starts a new row in a table (regular
1849 * or group). This must also be the first item after
1850 * the (group) table tag.
1851 *
1852 * fl specifies formatting flags for the row. This
1853 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
1854 * ROW_VALIGN_TOP and affects all items in the row.
1855 *
1856 * -- CONTROL_DEF(pDef) defines a control in a table row.
1857 * pDef must point to a CONTROLDEF structure.
1858 *
1859 * Again, there is is NO information in CONTROLDEF
1860 * about a control's _position_. Instead, the structure
1861 * only contains the _size_ of the control. All
1862 * positions are computed by this function, depending
1863 * on the sizes of the controls and their nesting within
1864 * the various tables.
1865 *
1866 * If you specify SZL_AUTOSIZE with either cx or cy
1867 * or both, the size of the control is even computed
1868 * automatically. Presently, this only works for statics
1869 * with SS_TEXT, SS_ICON, and SS_BITMAP, push buttons,
1870 * and radio and check boxes.
1871 *
1872 * Unless separated with START_ROW items, subsequent
1873 * control items will be considered to be in the same
1874 * row (== positioned next to each other).
1875 *
1876 * There are a few rules, whose violation will produce
1877 * an error:
1878 *
1879 * -- The entire array must be enclosed in a table
1880 * (the "root" table).
1881 *
1882 * -- After START_TABLE or START_GROUP_TABLE, there must
1883 * always be a START_ROW first.
1884 *
1885 * While it is possible to set up the CONTROLDEFs manually
1886 * as static structures, I recommend using the bunch of
1887 * other macros that were defined in dialog.h for this.
1888 * For example, you can use CONTROLDEF_PUSHBUTTON to create
1889 * a push button, and many more.
1890 *
1891 * To create a dialog, set up arrays like the following:
1892 *
1893 + // control definitions referenced by DlgTemplate:
1894 + CONTROLDEF
1895 + (1) GroupDef = CONTROLDEF_GROUP("Group",
1896 + -1, // ID
1897 + SZL_AUTOSIZE,
1898 + SZL_AUTOSIZE),
1899 + (2) CnrDef = CONTROLDEF_CONTAINER(-1, // ID,
1900 + 50,
1901 + 50),
1902 + (3) Static = CONTROLDEF_TEXT("Static below cnr",
1903 + -1, // ID
1904 + SZL_AUTOSIZE,
1905 + SZL_AUTOSIZE),
1906 + (4) OKButton = CONTROLDEF_DEFPUSHBUTTON("~OK",
1907 + DID_OK,
1908 + SZL_AUTOSIZE,
1909 + SZL_AUTOSIZE),
1910 + (5) CancelButton = CONTROLDEF_PUSHBUTTON("~Cancel",
1911 + DID_CANCEL,
1912 + SZL_AUTOSIZE,
1913 + SZL_AUTOSIZE);
1914 +
1915 + DLGHITEM DlgTemplate[] =
1916 + {
1917 + START_TABLE, // root table, required
1918 + START_ROW(0), // row 1 in the root table, required
1919 + // create group on top
1920 + (1) START_GROUP_TABLE(&Group),
1921 + START_ROW(0),
1922 + (2) CONTROL_DEF(&CnrDef),
1923 + START_ROW(0),
1924 + (3) CONTROL_DEF(&Static),
1925 + END_TABLE, // end of group
1926 + START_ROW(0), // row 2 in the root table
1927 + // two buttons next to each other
1928 + (4) CONTROL_DEF(&OKButton),
1929 + (5) CONTROL_DEF(&CancelButton),
1930 + END_TABLE
1931 + }
1932 *
1933 * This will produce a dlg like this:
1934 *
1935 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1936 + º º
1937 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1938 + º ³ ³ º
1939 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1940 + º ³ ³ ³ ³ º
1941 + º ³ ³ Cnr inside group (2) ³ ³ º
1942 + º ³ ³ ³ ³ º
1943 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1944 + º ³ ³ º
1945 + º ³ Static below cnr (3) ³ º
1946 + º ³ ³ º
1947 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1948 + º º
1949 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1950 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1951 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1952 + º º
1953 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1954 *
1955 * <B>Example:</B>
1956 *
1957 * The typical calling sequence would be:
1958 *
1959 + HWND hwndDlg = NULLHANDLE;
1960 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1961 + hwndOwner,
1962 + FCF_TITLEBAR | FCF_SYSMENU
1963 + | FCF_DLGBORDER | FCF_NOBYTEALIGN,
1964 + fnwpMyDlgProc,
1965 + "My Dlg Title",
1966 + DlgTemplate, // DLGHITEM array
1967 + ARRAYITEMCOUNT(DlgTemplate),
1968 + NULL, // mp2 for WM_INITDLG
1969 + "9.WarpSans")) // default font
1970 + {
1971 + ULONG idReturn = WinProcessDlg(hwndDlg);
1972 + WinDestroyWindow(hwndDlg);
1973 + }
1974 *
1975 * <B>Errors:</B>
1976 *
1977 * This does not return a HWND, but an APIRET. This will be
1978 * one of the following:
1979 *
1980 * -- NO_ERROR: only in that case, the phwndDlg ptr
1981 * receives the HWND of the new dialog, which can
1982 * then be given to WinProcessDlg. Don't forget
1983 * WinDestroyWindow.
1984 *
1985 * -- ERROR_NOT_ENOUGH_MEMORY
1986 *
1987 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1988 * outside a table definition.
1989 *
1990 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1991 * appeared right after a table definition. You must
1992 * specify a row first.
1993 *
1994 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1995 * but the CONTROLDEF ptr was NULL.
1996 *
1997 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1998 * WC_FRAME window. Maybe an invalid owner was specified.
1999 *
2000 * -- DLGERR_INVALID_CODE: invalid "Type" field in
2001 * DLGHITEM.
2002 *
2003 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
2004 * improper nesting of TYPE_START_NEW_TABLE and
2005 * TYPE_END_TABLE fields.
2006 *
2007 * -- DLGERR_CANNOT_CREATE_CONTROL: creation of some
2008 * sub-control failed. Maybe an invalid window class
2009 * was specified.
2010 *
2011 * -- DLGERR_INVALID_CONTROL_TITLE: bad window title in
2012 * control.
2013 *
2014 * -- DLGERR_INVALID_STATIC_BITMAP: static bitmap contains
2015 * an invalid bitmap handle.
2016 *
2017 *@@changed V0.9.14 (2001-07-07) [umoeller]: fixed disabled mouse with hwndOwner == HWND_DESKTOP
2018 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed major memory leaks with nested tables
2019 *@@changed V0.9.14 (2001-08-21) [umoeller]: fixed default push button problems
2020 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed bad owner if not direct desktop child
2021 */
2022
2023APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
2024 HWND hwndOwner,
2025 ULONG flCreateFlags, // in: standard FCF_* frame flags
2026 PFNWP pfnwpDialogProc,
2027 PCSZ pcszDlgTitle,
2028 PCDLGHITEM paDlgItems, // in: definition array
2029 ULONG cDlgItems, // in: array item count (NOT array size)
2030 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
2031 PCSZ pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
2032{
2033 APIRET arc = NO_ERROR;
2034
2035 ULONG ul;
2036
2037 PDLGPRIVATE pDlgData = NULL;
2038
2039 HWND hwndDesktop = WinQueryDesktopWindow(NULLHANDLE, NULLHANDLE);
2040 // works with a null HAB
2041
2042 /*
2043 * 1) parse the table and create structures from it
2044 *
2045 */
2046
2047 if (!(arc = Dlg0_Init(&pDlgData,
2048 pcszControlsFont,
2049 NULL)))
2050 {
2051 if (!(arc = Dlg1_ParseTables(pDlgData,
2052 paDlgItems,
2053 cDlgItems)))
2054 {
2055 /*
2056 * 2) create empty dialog frame
2057 *
2058 */
2059
2060 FRAMECDATA fcData = {0};
2061 ULONG flStyle = 0;
2062 HWND hwndOwnersParent;
2063
2064 fcData.cb = sizeof(FRAMECDATA);
2065 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
2066
2067 if (flCreateFlags & FCF_SIZEBORDER)
2068 // dialog has size border:
2069 // add "clip siblings" style
2070 flStyle |= WS_CLIPSIBLINGS;
2071
2072 if (hwndOwner == HWND_DESKTOP)
2073 // there's some dumb XWorkplace code left
2074 // which uses this, and this disables the
2075 // mouse for some reason
2076 // V0.9.14 (2001-07-07) [umoeller]
2077 hwndOwner = NULLHANDLE;
2078
2079 // now, make sure the owner window is child of
2080 // HWND_DESKTOP... if it is not, we'll only disable
2081 // some dumb child window, which is not sufficient
2082 // V0.9.16 (2001-12-06) [umoeller]
2083 while ( (hwndOwner)
2084 && (hwndOwnersParent = WinQueryWindow(hwndOwner, QW_PARENT))
2085 && (hwndOwnersParent != hwndDesktop)
2086 )
2087 hwndOwner = hwndOwnersParent;
2088
2089 if (!(pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
2090 WC_FRAME,
2091 (PSZ)pcszDlgTitle,
2092 flStyle, // style; invisible for now
2093 0, 0, 0, 0,
2094 hwndOwner,
2095 HWND_TOP,
2096 0, // ID
2097 &fcData,
2098 NULL))) // presparams
2099 arc = DLGERR_CANNOT_CREATE_FRAME;
2100 else
2101 {
2102 HWND hwndDlg = pDlgData->hwndDlg;
2103 HWND hwndFocusItem = NULLHANDLE;
2104 RECTL rclClient;
2105
2106 /*
2107 * 3) compute size of all controls
2108 *
2109 */
2110
2111 if (!(arc = Dlg2_CalcSizes(pDlgData)))
2112 {
2113 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
2114
2115 /*
2116 * 4) compute size of dialog client from total
2117 * size of all controls
2118 */
2119
2120 // calculate the frame size from the client size
2121 rclClient.xLeft = 10;
2122 rclClient.yBottom = 10;
2123 rclClient.xRight = pDlgData->szlClient.cx + 2 * SPACING;
2124 rclClient.yTop = pDlgData->szlClient.cy + 2 * SPACING;
2125 WinCalcFrameRect(hwndDlg,
2126 &rclClient,
2127 FALSE); // frame from client
2128
2129 WinSetWindowPos(hwndDlg,
2130 0,
2131 10,
2132 10,
2133 rclClient.xRight,
2134 rclClient.yTop,
2135 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
2136
2137 arc = Dlg3_PositionAndCreate(pDlgData,
2138 &hwndFocusItem);
2139
2140 /*
2141 * 7) WM_INITDLG, set focus
2142 *
2143 */
2144
2145 if (!WinSendMsg(pDlgData->hwndDlg,
2146 WM_INITDLG,
2147 (MPARAM)hwndFocusItem,
2148 (MPARAM)pCreateParams))
2149 {
2150 // if WM_INITDLG returns FALSE, this means
2151 // the dlg proc has not changed the focus;
2152 // we must then set the focus here
2153 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
2154 }
2155 }
2156 }
2157 }
2158
2159 if (arc)
2160 {
2161 // error: clean up
2162 if (pDlgData->hwndDlg)
2163 {
2164 WinDestroyWindow(pDlgData->hwndDlg);
2165 pDlgData->hwndDlg = NULLHANDLE;
2166 }
2167 }
2168 else
2169 // no error: output dialog
2170 *phwndDlg = pDlgData->hwndDlg;
2171
2172 Dlg9_Cleanup(&pDlgData);
2173 }
2174
2175 if (arc)
2176 {
2177 CHAR szErr[300];
2178 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
2179 winhDebugBox(hwndOwner,
2180 "Error in Dialog Manager",
2181 szErr);
2182 }
2183
2184 return (arc);
2185}
2186
2187/*
2188 *@@ dlghFormatDlg:
2189 * similar to dlghCreateDlg in that this can
2190 * dynamically format dialog items.
2191 *
2192 * The differences however are the following:
2193 *
2194 * -- This assumes that hwndDlg already points
2195 * to a valid dialog frame and that this
2196 * dialog should be modified according to
2197 * flFlags.
2198 *
2199 * This is what's used in XWorkplace for notebook
2200 * settings pages since these always have to be
2201 * based on a resource dialog (which is loaded
2202 * empty).
2203 *
2204 * flFlags can be any combination of the following:
2205 *
2206 * -- DFFL_CREATECONTROLS: paDlgItems points to
2207 * an array of cDlgItems DLGHITEM structures
2208 * (see dlghCreateDlg) which is used for creating
2209 * subwindows in hwndDlg. By using this flag, the
2210 * function will essentially work like dlghCreateDlg,
2211 * except that the frame is already created.
2212 *
2213 * If pszlClient is specified, it receives the required
2214 * size of the client to surround all controls properly.
2215 * You can then use dlghResizeFrame to resize the frame
2216 * with a bit of spacing, if desired.
2217 *
2218 *@@added V0.9.16 (2001-09-29) [umoeller]
2219 *@@changed V0.9.18 (2002-03-03) [umoeller]: added pszlClient, fixed output
2220 */
2221
2222APIRET dlghFormatDlg(HWND hwndDlg, // in: dialog frame to work on
2223 PCDLGHITEM paDlgItems, // in: definition array
2224 ULONG cDlgItems, // in: array item count (NOT array size)
2225 PCSZ pcszControlsFont, // in: font for ctls with CTL_COMMON_FONT
2226 ULONG flFlags, // in: DFFL_* flags
2227 PSIZEL pszlClient, // out: size of all controls (ptr can be NULL)
2228 PVOID *ppllControls) // out: new LINKLIST receiving HWNDs of created controls (ptr can be NULL)
2229{
2230 APIRET arc = NO_ERROR;
2231
2232 ULONG ul;
2233
2234 PDLGPRIVATE pDlgData = NULL;
2235 PLINKLIST pllControls = NULL;
2236
2237 /*
2238 * 1) parse the table and create structures from it
2239 *
2240 */
2241
2242 if (ppllControls)
2243 pllControls = *(PLINKLIST*)ppllControls = lstCreate(FALSE);
2244
2245 if (!(arc = Dlg0_Init(&pDlgData,
2246 pcszControlsFont,
2247 pllControls)))
2248 {
2249 if (!(arc = Dlg1_ParseTables(pDlgData,
2250 paDlgItems,
2251 cDlgItems)))
2252 {
2253 HWND hwndFocusItem;
2254
2255 /*
2256 * 2) create empty dialog frame
2257 *
2258 */
2259
2260 pDlgData->hwndDlg = hwndDlg;
2261
2262 /*
2263 * 3) compute size of all controls
2264 *
2265 */
2266
2267 Dlg2_CalcSizes(pDlgData);
2268
2269 if (pszlClient)
2270 {
2271 pszlClient->cx = pDlgData->szlClient.cx + 2 * SPACING;
2272 pszlClient->cy = pDlgData->szlClient.cy + 2 * SPACING;
2273 }
2274
2275 if (flFlags & DFFL_CREATECONTROLS)
2276 {
2277 if (!(arc = Dlg3_PositionAndCreate(pDlgData,
2278 &hwndFocusItem)))
2279 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
2280 }
2281 }
2282
2283 Dlg9_Cleanup(&pDlgData);
2284 }
2285
2286 if (arc)
2287 {
2288 CHAR szErr[300];
2289 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
2290 winhDebugBox(NULLHANDLE,
2291 "Error in Dialog Manager",
2292 szErr);
2293 }
2294
2295 return (arc);
2296}
2297
2298/*
2299 *@@ dlghResizeFrame:
2300 *
2301 *@@added V0.9.18 (2002-03-03) [umoeller]
2302 */
2303
2304VOID dlghResizeFrame(HWND hwndDlg,
2305 PSIZEL pszlClient)
2306{
2307 // calculate the frame size from the client size
2308 RECTL rclClient;
2309 rclClient.xLeft = 10;
2310 rclClient.yBottom = 10;
2311 rclClient.xRight = pszlClient->cx;
2312 rclClient.yTop = pszlClient->cy;
2313 WinCalcFrameRect(hwndDlg,
2314 &rclClient,
2315 FALSE); // frame from client
2316
2317 WinSetWindowPos(hwndDlg,
2318 0,
2319 10,
2320 10,
2321 rclClient.xRight,
2322 rclClient.yTop,
2323 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
2324}
2325
2326/* ******************************************************************
2327 *
2328 * Dialog arrays
2329 *
2330 ********************************************************************/
2331
2332/*
2333 *@@ dlghCreateArray:
2334 * creates a "dialog array" for dynamically
2335 * building a dialog template in memory.
2336 *
2337 * A dialog array is simply an array of
2338 * DLGHITEM structures, as you would normally
2339 * define them statically in the source.
2340 * However, there are situations where you
2341 * might want to leave out certain controls
2342 * depending on certain conditions, which
2343 * can be difficult with static arrays.
2344 *
2345 * As a result, these "array" functions have
2346 * been added to allow for adding static
2347 * DLGHITEM subarrays to a dynamic array in
2348 * memory, which can then be passed to the
2349 * formatter.
2350 *
2351 * Usage:
2352 *
2353 * 1) Call this function with the maximum
2354 * amount of DLGHITEM's that will need
2355 * to be allocated in cMaxItems. Set this
2356 * to the total sum of all DLGHITEM's
2357 * in all the subarrays.
2358 *
2359 * 2) For each of the subarrays, call
2360 * dlghAppendToArray to have the subarray
2361 * appended to the dialog array.
2362 * After each call, DLGARRAY.cDlgItemsNow
2363 * will contain the actual total count of
2364 * DLGHITEM's that were added.
2365 *
2366 * 3) Call dlghCreateDialog with the dialog
2367 * array.
2368 *
2369 * 4) Call dlghFreeArray.
2370 *
2371 * Sort of like this (error checking omitted):
2372 *
2373 + DLGHITEM dlgSampleFront = ... // always included
2374 + DLGHITEM dlgSampleSometimes = ... // not always included
2375 + DLGHITEM dlgSampleTail = ... // always included
2376 +
2377 + PDLGARRAY pArraySample = NULL;
2378 + dlghCreateArray( ARRAYITEMCOUNT(dlgSampleFront)
2379 + + ARRAYITEMCOUNT(dlgSampleSometimes)
2380 + + ARRAYITEMCOUNT(dlgSampleTail),
2381 + &pArraySample);
2382 +
2383 + // always include front
2384 + dlghAppendToArray(pArraySample,
2385 + dlgSampleFront,
2386 + ARRAYITEMCOUNT(dlgSampleFront));
2387 + // include "sometimes" conditionally
2388 + if (...)
2389 + dlghAppendToArray(pArraySample,
2390 + dlgSampleSometimes,
2391 + ARRAYITEMCOUNT(dlgSampleSometimes));
2392 + // include tail always
2393 + dlghAppendToArray(pArraySample,
2394 + dlgSampleTail,
2395 + ARRAYITEMCOUNT(dlgSampleTail));
2396 +
2397 + // now create the dialog from the array
2398 + dlghCreateDialog(&hwndDlg,
2399 + hwndOwner,
2400 + FCF_ ...
2401 + fnwpMyDialogProc,
2402 + "Title",
2403 + pArray->paDialogItems, // dialog array!
2404 + pArray->cDlgItemsNow, // real count of items!
2405 + NULL,
2406 + NULL);
2407 +
2408 + dlghFreeArray(&pArraySample);
2409 *
2410 *@@added V0.9.16 (2001-10-15) [umoeller]
2411 */
2412
2413APIRET dlghCreateArray(ULONG cMaxItems,
2414 PDLGARRAY *ppArray) // out: DLGARRAY
2415{
2416 APIRET arc = NO_ERROR;
2417 PDLGARRAY pArray;
2418
2419 if (pArray = NEW(DLGARRAY))
2420 {
2421 ULONG cb;
2422
2423 ZERO(pArray);
2424 if ( (cb = cMaxItems * sizeof(DLGHITEM))
2425 && (pArray->paDlgItems = (DLGHITEM*)malloc(cb))
2426 )
2427 {
2428 memset(pArray->paDlgItems, 0, cb);
2429 pArray->cDlgItemsMax = cMaxItems;
2430 *ppArray = pArray;
2431 }
2432 else
2433 arc = ERROR_NOT_ENOUGH_MEMORY;
2434
2435 if (arc)
2436 dlghFreeArray(&pArray);
2437 }
2438 else
2439 arc = ERROR_NOT_ENOUGH_MEMORY;
2440
2441 return arc;
2442}
2443
2444/*
2445 *@@ dlghFreeArray:
2446 * frees a dialog array created by dlghCreateArray.
2447 *
2448 *@@added V0.9.16 (2001-10-15) [umoeller]
2449 */
2450
2451APIRET dlghFreeArray(PDLGARRAY *ppArray)
2452{
2453 PDLGARRAY pArray;
2454 if ( (ppArray)
2455 && (pArray = *ppArray)
2456 )
2457 {
2458 if (pArray->paDlgItems)
2459 free(pArray->paDlgItems);
2460 free(pArray);
2461 }
2462 else
2463 return ERROR_INVALID_PARAMETER;
2464
2465 return NO_ERROR;
2466}
2467
2468/*
2469 *@@ dlghAppendToArray:
2470 * appends a subarray of DLGHITEM's to the
2471 * given DLGARRAY. See dlghCreateArray for
2472 * usage.
2473 *
2474 * Returns:
2475 *
2476 * -- NO_ERROR
2477 *
2478 * -- ERROR_INVALID_PARAMETER
2479 *
2480 * -- DLGERR_ARRAY_TOO_SMALL: pArray does not
2481 * have enough memory to hold the new items.
2482 * The cMaxItems parameter given to dlghCreateArray
2483 * wasn't large enough.
2484 *
2485 *@@added V0.9.16 (2001-10-15) [umoeller]
2486 */
2487
2488APIRET dlghAppendToArray(PDLGARRAY pArray, // in: dialog array created by dlghCreateArray
2489 PCDLGHITEM paItems, // in: subarray to be appended
2490 ULONG cItems) // in: subarray item count (NOT array size)
2491{
2492 APIRET arc = NO_ERROR;
2493 if (pArray)
2494 {
2495 if ( (pArray->cDlgItemsMax >= cItems)
2496 && (pArray->cDlgItemsMax - pArray->cDlgItemsNow >= cItems)
2497 )
2498 {
2499 // enough space left in the array:
2500 memcpy(&pArray->paDlgItems[pArray->cDlgItemsNow],
2501 paItems, // source
2502 cItems * sizeof(DLGHITEM));
2503 pArray->cDlgItemsNow += cItems;
2504 }
2505 else
2506 arc = DLGERR_ARRAY_TOO_SMALL;
2507 }
2508 else
2509 arc = ERROR_INVALID_PARAMETER;
2510
2511 return (arc);
2512}
2513
2514/* ******************************************************************
2515 *
2516 * Standard dialogs
2517 *
2518 ********************************************************************/
2519
2520/*
2521 *@@ dlghCreateMessageBox:
2522 *
2523 *@@added V0.9.13 (2001-06-21) [umoeller]
2524 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
2525 */
2526
2527APIRET dlghCreateMessageBox(HWND *phwndDlg,
2528 HWND hwndOwner,
2529 HPOINTER hptrIcon,
2530 PCSZ pcszTitle,
2531 PCSZ pcszMessage,
2532 ULONG flFlags,
2533 PCSZ pcszFont,
2534 const MSGBOXSTRINGS *pStrings,
2535 PULONG pulAlarmFlag) // out: alarm sound to be played
2536{
2537 CONTROLDEF
2538 Icon = {
2539 WC_STATIC,
2540 NULL, // text, set below
2541 WS_VISIBLE | SS_ICON,
2542 0, // ID
2543 NULL, // no font
2544 0,
2545 { SZL_AUTOSIZE, SZL_AUTOSIZE },
2546 5
2547 },
2548 InfoText =
2549 {
2550 WC_STATIC,
2551 NULL, // text, set below
2552 WS_VISIBLE | SS_TEXT | DT_WORDBREAK | DT_LEFT | DT_TOP,
2553 10, // ID
2554 CTL_COMMON_FONT,
2555 0,
2556 { 400, SZL_AUTOSIZE },
2557 5
2558 },
2559 Buttons[] = {
2560 {
2561 WC_BUTTON,
2562 NULL, // text, set below
2563 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2564 1, // ID
2565 CTL_COMMON_FONT, // no font
2566 0,
2567 { 100, 30 },
2568 5
2569 },
2570 {
2571 WC_BUTTON,
2572 NULL, // text, set below
2573 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2574 2, // ID
2575 CTL_COMMON_FONT, // no font
2576 0,
2577 { 100, 30 },
2578 5
2579 },
2580 {
2581 WC_BUTTON,
2582 NULL, // text, set below
2583 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2584 3, // ID
2585 CTL_COMMON_FONT, // no font
2586 0,
2587 { 100, 30 },
2588 5
2589 }
2590 };
2591
2592 DLGHITEM MessageBox[] =
2593 {
2594 START_TABLE,
2595 START_ROW(ROW_VALIGN_CENTER),
2596 CONTROL_DEF(&Icon),
2597 START_TABLE,
2598 START_ROW(ROW_VALIGN_CENTER),
2599 CONTROL_DEF(&InfoText),
2600 START_ROW(ROW_VALIGN_CENTER),
2601 CONTROL_DEF(&Buttons[0]),
2602 CONTROL_DEF(&Buttons[1]),
2603 CONTROL_DEF(&Buttons[2]),
2604 END_TABLE,
2605 END_TABLE
2606 };
2607
2608 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2609
2610 PCSZ p0 = "Error",
2611 p1 = NULL,
2612 p2 = NULL;
2613
2614 Icon.pcszText = (PCSZ)hptrIcon;
2615 InfoText.pcszText = pcszMessage;
2616
2617 // now work on the three buttons of the dlg template:
2618 // give them proper titles or hide them
2619 if (flButtons == MB_OK)
2620 {
2621 p0 = pStrings->pcszOK;
2622 }
2623 else if (flButtons == MB_OKCANCEL)
2624 {
2625 p0 = pStrings->pcszOK;
2626 p1 = pStrings->pcszCancel;
2627 }
2628 else if (flButtons == MB_RETRYCANCEL)
2629 {
2630 p0 = pStrings->pcszRetry;
2631 p1 = pStrings->pcszCancel;
2632 }
2633 else if (flButtons == MB_ABORTRETRYIGNORE)
2634 {
2635 p0 = pStrings->pcszAbort;
2636 p1 = pStrings->pcszRetry;
2637 p2 = pStrings->pcszIgnore;
2638 }
2639 else if (flButtons == MB_YESNO)
2640 {
2641 p0 = pStrings->pcszYes;
2642 p1 = pStrings->pcszNo;
2643 }
2644 else if (flButtons == MB_YESNOCANCEL)
2645 {
2646 p0 = pStrings->pcszYes;
2647 p1 = pStrings->pcszNo;
2648 p2 = pStrings->pcszCancel;
2649 }
2650 else if (flButtons == MB_CANCEL)
2651 {
2652 p0 = pStrings->pcszCancel;
2653 }
2654 else if (flButtons == MB_ENTER)
2655 {
2656 p0 = pStrings->pcszEnter;
2657 }
2658 else if (flButtons == MB_ENTERCANCEL)
2659 {
2660 p0 = pStrings->pcszEnter;
2661 p1 = pStrings->pcszCancel;
2662 }
2663 else if (flButtons == MB_YES_YES2ALL_NO)
2664 {
2665 p0 = pStrings->pcszYes;
2666 p1 = pStrings->pcszYesToAll;
2667 p2 = pStrings->pcszNo;
2668 }
2669
2670 // now set strings and hide empty buttons
2671 Buttons[0].pcszText = p0;
2672
2673 if (p1)
2674 Buttons[1].pcszText = p1;
2675 else
2676 Buttons[1].flStyle &= ~WS_VISIBLE;
2677
2678 if (p2)
2679 Buttons[2].pcszText = p2;
2680 else
2681 Buttons[2].flStyle &= ~WS_VISIBLE;
2682
2683 // query default button IDs
2684 if (flFlags & MB_DEFBUTTON2)
2685 Buttons[1].flStyle |= BS_DEFAULT;
2686 else if (flFlags & MB_DEFBUTTON3)
2687 Buttons[2].flStyle |= BS_DEFAULT;
2688 else
2689 Buttons[0].flStyle |= BS_DEFAULT;
2690
2691 *pulAlarmFlag = WA_NOTE;
2692 if (flFlags & (MB_ICONHAND | MB_ERROR))
2693 *pulAlarmFlag = WA_ERROR;
2694 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
2695 *pulAlarmFlag = WA_WARNING;
2696
2697 return (dlghCreateDlg(phwndDlg,
2698 hwndOwner,
2699 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2700 WinDefDlgProc,
2701 pcszTitle,
2702 MessageBox,
2703 ARRAYITEMCOUNT(MessageBox),
2704 NULL,
2705 pcszFont));
2706}
2707
2708/*
2709 *@@ dlghProcessMessageBox:
2710 *
2711 *@@added V0.9.13 (2001-06-21) [umoeller]
2712 */
2713
2714ULONG dlghProcessMessageBox(HWND hwndDlg,
2715 ULONG ulAlarmFlag,
2716 ULONG flFlags)
2717{
2718 ULONG ulrcDlg;
2719 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2720
2721 winhCenterWindow(hwndDlg);
2722
2723 if (flFlags & MB_SYSTEMMODAL)
2724 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
2725
2726 if (ulAlarmFlag)
2727 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
2728
2729 ulrcDlg = WinProcessDlg(hwndDlg);
2730
2731 WinDestroyWindow(hwndDlg);
2732
2733 if (flButtons == MB_OK)
2734 return MBID_OK;
2735 else if (flButtons == MB_OKCANCEL)
2736 switch (ulrcDlg)
2737 {
2738 case 1: return MBID_OK;
2739 default: return MBID_CANCEL;
2740 }
2741 else if (flButtons == MB_RETRYCANCEL)
2742 switch (ulrcDlg)
2743 {
2744 case 1: return MBID_RETRY;
2745 default: return MBID_CANCEL;
2746 }
2747 else if (flButtons == MB_ABORTRETRYIGNORE)
2748 switch (ulrcDlg)
2749 {
2750 case 2: return MBID_RETRY;
2751 case 3: return MBID_IGNORE;
2752 default: return MBID_ABORT;
2753 }
2754 else if (flButtons == MB_YESNO)
2755 switch (ulrcDlg)
2756 {
2757 case 1: return MBID_YES;
2758 default: return MBID_NO;
2759 }
2760 else if (flButtons == MB_YESNOCANCEL)
2761 switch (ulrcDlg)
2762 {
2763 case 1: return MBID_YES;
2764 case 2: return MBID_NO;
2765 default: return MBID_CANCEL;
2766 }
2767 else if (flButtons == MB_CANCEL)
2768 return MBID_CANCEL;
2769 else if (flButtons == MB_ENTER)
2770 return MBID_ENTER;
2771 else if (flButtons == MB_ENTERCANCEL)
2772 switch (ulrcDlg)
2773 {
2774 case 1: return MBID_ENTER;
2775 default: return MBID_CANCEL;
2776 }
2777 else if (flButtons == MB_YES_YES2ALL_NO)
2778 switch (ulrcDlg)
2779 {
2780 case 1: return MBID_YES;
2781 case 2: return MBID_YES2ALL;
2782 default: return MBID_NO;
2783 }
2784
2785 return (MBID_CANCEL);
2786}
2787
2788/*
2789 *@@ dlghMessageBox:
2790 * WinMessageBox replacement.
2791 *
2792 * This has all the flags of the standard call,
2793 * but looks much prettier. Besides, it allows
2794 * you to specify any icon to be displayed.
2795 *
2796 * Currently the following flStyle's are supported:
2797 *
2798 * -- MB_OK 0x0000
2799 * -- MB_OKCANCEL 0x0001
2800 * -- MB_RETRYCANCEL 0x0002
2801 * -- MB_ABORTRETRYIGNORE 0x0003
2802 * -- MB_YESNO 0x0004
2803 * -- MB_YESNOCANCEL 0x0005
2804 * -- MB_CANCEL 0x0006
2805 * -- MB_ENTER 0x0007 (not implemented yet)
2806 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
2807 *
2808 * -- MB_YES_YES2ALL_NO 0x0009
2809 * This is new: this has three buttons called "Yes"
2810 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
2811 *
2812 * -- MB_DEFBUTTON2 (for two-button styles)
2813 * -- MB_DEFBUTTON3 (for three-button styles)
2814 *
2815 * -- MB_ICONHAND
2816 * -- MB_ICONEXCLAMATION
2817 *
2818 * Returns MBID_* codes like WinMessageBox.
2819 *
2820 *@@added V0.9.13 (2001-06-21) [umoeller]
2821 */
2822
2823ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
2824 HPOINTER hptrIcon, // in: icon to display
2825 PCSZ pcszTitle, // in: title
2826 PCSZ pcszMessage, // in: message
2827 ULONG flFlags, // in: standard message box flags
2828 PCSZ pcszFont, // in: font (e.g. "9.WarpSans")
2829 const MSGBOXSTRINGS *pStrings) // in: strings array
2830{
2831 HWND hwndDlg;
2832 ULONG ulAlarmFlag;
2833 APIRET arc = dlghCreateMessageBox(&hwndDlg,
2834 hwndOwner,
2835 hptrIcon,
2836 pcszTitle,
2837 pcszMessage,
2838 flFlags,
2839 pcszFont,
2840 pStrings,
2841 &ulAlarmFlag);
2842
2843 if (!arc && hwndDlg)
2844 {
2845 // SHOW DIALOG
2846 return (dlghProcessMessageBox(hwndDlg,
2847 ulAlarmFlag,
2848 flFlags));
2849 }
2850 else
2851 {
2852 CHAR szMsg[100];
2853 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
2854 WinMessageBox(HWND_DESKTOP,
2855 NULLHANDLE,
2856 "Error",
2857 szMsg,
2858 0,
2859 MB_CANCEL | MB_MOVEABLE);
2860 }
2861
2862 return (DID_CANCEL);
2863}
2864
2865/*
2866 *@@ cmnTextEntryBox:
2867 * common dialog for entering a text string.
2868 * The dialog has a descriptive text on top
2869 * with an entry field below and "OK" and "Cancel"
2870 * buttons.
2871 *
2872 * The string from the user is returned in a
2873 * new buffer, which must be free'd by the caller.
2874 * Returns NULL if the user pressed "Cancel".
2875 *
2876 * fl can be any combination of the following
2877 * flags:
2878 *
2879 * -- TEBF_REMOVETILDE: tilde ("~") characters
2880 * are removed from pcszTitle before setting
2881 * the title. Useful for reusing menu item
2882 * texts.
2883 *
2884 * -- TEBF_REMOVEELLIPSE: ellipse ("...") strings
2885 * are removed from pcszTitle before setting
2886 * the title. Useful for reusing menu item
2887 * texts.
2888 *
2889 * -- TEBF_SELECTALL: the default text in the
2890 * entry field is initially highlighted.
2891 *
2892 *@@added V0.9.15 (2001-09-14) [umoeller]
2893 */
2894
2895PSZ dlghTextEntryBox(HWND hwndOwner,
2896 PCSZ pcszTitle, // in: dlg title
2897 PCSZ pcszDescription, // in: descriptive text above entry field
2898 PCSZ pcszDefault, // in: default text for entry field or NULL
2899 PCSZ pcszOK, // in: "OK" string
2900 PCSZ pcszCancel, // in: "Cancel" string
2901 ULONG ulMaxLen, // in: maximum length for entry
2902 ULONG fl, // in: TEBF_* flags
2903 PCSZ pcszFont) // in: font (e.g. "9.WarpSans")
2904{
2905 CONTROLDEF
2906 Static = {
2907 WC_STATIC,
2908 NULL,
2909 WS_VISIBLE | SS_TEXT | DT_LEFT | DT_WORDBREAK,
2910 -1,
2911 CTL_COMMON_FONT,
2912 0,
2913 { 300, SZL_AUTOSIZE }, // size
2914 5 // spacing
2915 },
2916 Entry = {
2917 WC_ENTRYFIELD,
2918 NULL,
2919 WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_MARGIN | ES_AUTOSCROLL,
2920 999,
2921 CTL_COMMON_FONT,
2922 0,
2923 { 300, SZL_AUTOSIZE }, // size
2924 5 // spacing
2925 },
2926 OKButton = {
2927 WC_BUTTON,
2928 NULL,
2929 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_DEFAULT,
2930 DID_OK,
2931 CTL_COMMON_FONT,
2932 0,
2933 { 100, 30 }, // size
2934 5 // spacing
2935 },
2936 CancelButton = {
2937 WC_BUTTON,
2938 NULL,
2939 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2940 DID_CANCEL,
2941 CTL_COMMON_FONT,
2942 0,
2943 { 100, 30 }, // size
2944 5 // spacing
2945 };
2946 DLGHITEM DlgTemplate[] =
2947 {
2948 START_TABLE,
2949 START_ROW(0),
2950 CONTROL_DEF(&Static),
2951 START_ROW(0),
2952 CONTROL_DEF(&Entry),
2953 START_ROW(0),
2954 CONTROL_DEF(&OKButton),
2955 CONTROL_DEF(&CancelButton),
2956 END_TABLE
2957 };
2958
2959 HWND hwndDlg = NULLHANDLE;
2960 PSZ pszReturn = NULL;
2961 XSTRING strTitle;
2962
2963 xstrInitCopy(&strTitle, pcszTitle, 0);
2964
2965 if (fl & (TEBF_REMOVEELLIPSE | TEBF_REMOVETILDE))
2966 {
2967 ULONG ulOfs;
2968 if (fl & TEBF_REMOVEELLIPSE)
2969 {
2970 ulOfs = 0;
2971 while (xstrFindReplaceC(&strTitle,
2972 &ulOfs,
2973 "...",
2974 ""))
2975 ;
2976 }
2977
2978 if (fl & TEBF_REMOVETILDE)
2979 {
2980 ulOfs = 0;
2981 while (xstrFindReplaceC(&strTitle,
2982 &ulOfs,
2983 "~",
2984 ""))
2985 ;
2986 }
2987 }
2988
2989 Static.pcszText = pcszDescription;
2990
2991 OKButton.pcszText = pcszOK;
2992 CancelButton.pcszText = pcszCancel;
2993
2994 if (NO_ERROR == dlghCreateDlg(&hwndDlg,
2995 hwndOwner,
2996 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2997 WinDefDlgProc,
2998 strTitle.psz,
2999 DlgTemplate, // DLGHITEM array
3000 ARRAYITEMCOUNT(DlgTemplate),
3001 NULL,
3002 pcszFont))
3003 {
3004 HWND hwndEF = WinWindowFromID(hwndDlg, 999);
3005 winhCenterWindow(hwndDlg);
3006 winhSetEntryFieldLimit(hwndEF, ulMaxLen);
3007 if (pcszDefault)
3008 {
3009 WinSetWindowText(hwndEF, (PSZ)pcszDefault);
3010 if (fl & TEBF_SELECTALL)
3011 winhEntryFieldSelectAll(hwndEF);
3012 }
3013 WinSetFocus(HWND_DESKTOP, hwndEF);
3014 if (DID_OK == WinProcessDlg(hwndDlg))
3015 pszReturn = winhQueryWindowText(hwndEF);
3016
3017 WinDestroyWindow(hwndDlg);
3018 }
3019
3020 xstrClear(&strTitle);
3021
3022 return (pszReturn);
3023}
3024
3025/* ******************************************************************
3026 *
3027 * Dialog input handlers
3028 *
3029 ********************************************************************/
3030
3031/*
3032 *@@ dlghSetPrevFocus:
3033 * "backward" function for rotating the focus
3034 * in a dialog when the "shift+tab" keys get
3035 * pressed.
3036 *
3037 * pllWindows must be a linked list with the
3038 * plain HWND window handles of the focussable
3039 * controls in the dialog.
3040 */
3041
3042VOID dlghSetPrevFocus(PVOID pvllWindows)
3043{
3044 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
3045
3046 // check current focus
3047 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
3048
3049 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
3050
3051 BOOL fRestart = FALSE;
3052
3053 while (pNode)
3054 {
3055 CHAR szClass[100];
3056
3057 // previos node
3058 pNode = pNode->pPrevious;
3059
3060 if ( (!pNode) // no next, or not found:
3061 && (!fRestart) // avoid infinite looping if bad list
3062 )
3063 {
3064 pNode = lstQueryLastNode(pllWindows);
3065 fRestart = TRUE;
3066 }
3067
3068 if (pNode)
3069 {
3070 // check if this is a focusable control
3071 if (WinQueryClassName((HWND)pNode->pItemData,
3072 sizeof(szClass),
3073 szClass))
3074 {
3075 if ( (strcmp(szClass, "#5")) // not static
3076 )
3077 break;
3078 // else: go for next then
3079 }
3080 }
3081 }
3082
3083 if (pNode)
3084 {
3085 WinSetFocus(HWND_DESKTOP,
3086 (HWND)pNode->pItemData);
3087 }
3088}
3089
3090/*
3091 *@@ dlghSetNextFocus:
3092 * "forward" function for rotating the focus
3093 * in a dialog when the "ab" key gets pressed.
3094 *
3095 * pllWindows must be a linked list with the
3096 * plain HWND window handles of the focussable
3097 * controls in the dialog.
3098 */
3099
3100VOID dlghSetNextFocus(PVOID pvllWindows)
3101{
3102 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
3103
3104 // check current focus
3105 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
3106
3107 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
3108
3109 BOOL fRestart = FALSE;
3110
3111 while (pNode)
3112 {
3113 CHAR szClass[100];
3114
3115 // next focus in node
3116 pNode = pNode->pNext;
3117
3118 if ( (!pNode) // no next, or not found:
3119 && (!fRestart) // avoid infinite looping if bad list
3120 )
3121 {
3122 pNode = lstQueryFirstNode(pllWindows);
3123 fRestart = TRUE;
3124 }
3125
3126 if (pNode)
3127 {
3128 // check if this is a focusable control
3129 if (WinQueryClassName((HWND)pNode->pItemData,
3130 sizeof(szClass),
3131 szClass))
3132 {
3133 if ( (strcmp(szClass, "#5")) // not static
3134 )
3135 break;
3136 // else: go for next then
3137 }
3138 }
3139 }
3140
3141 if (pNode)
3142 {
3143 WinSetFocus(HWND_DESKTOP,
3144 (HWND)pNode->pItemData);
3145 }
3146}
3147
3148/*
3149 *@@ dlghProcessMnemonic:
3150 * finds the control which matches usch
3151 * and gives it the focus. If this is a
3152 * static control, the next control in the
3153 * list is given focus instead. (Standard
3154 * dialog behavior.)
3155 *
3156 * Pass in usch from WM_CHAR. It is assumed
3157 * that the caller has already tested for
3158 * the "alt" key to be depressed.
3159 *
3160 *@@added V0.9.9 (2001-03-17) [umoeller]
3161 */
3162
3163HWND dlghProcessMnemonic(PVOID pvllWindows,
3164 USHORT usch)
3165{
3166 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
3167
3168 HWND hwndFound = NULLHANDLE;
3169 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
3170 CHAR szClass[100];
3171
3172 while (pNode)
3173 {
3174 HWND hwnd = (HWND)pNode->pItemData;
3175
3176 if (WinSendMsg(hwnd,
3177 WM_MATCHMNEMONIC,
3178 (MPARAM)usch,
3179 0))
3180 {
3181 // according to the docs, only buttons and static
3182 // return TRUE to that msg;
3183 // if this is a static, give focus to the next
3184 // control
3185
3186 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
3187
3188 // check if this is a focusable control
3189 if (WinQueryClassName(hwnd,
3190 sizeof(szClass),
3191 szClass))
3192 {
3193 if (!strcmp(szClass, "#3"))
3194 // it's a button: click it
3195 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
3196 else if (!strcmp(szClass, "#5"))
3197 {
3198 // it's a static: give focus to following control
3199 pNode = pNode->pNext;
3200 if (pNode)
3201 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
3202 }
3203 }
3204 else
3205 // any other control (are there any?): give them focus
3206 WinSetFocus(HWND_DESKTOP, hwnd);
3207
3208 // in any case, stop
3209 hwndFound = hwnd;
3210 break;
3211 }
3212
3213 pNode = pNode->pNext;
3214 }
3215
3216 return (hwndFound);
3217}
3218
3219/*
3220 *@@ dlghEnter:
3221 * presses the first button with BS_DEFAULT.
3222 */
3223
3224BOOL dlghEnter(PVOID pvllWindows)
3225{
3226 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
3227
3228 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
3229 CHAR szClass[100];
3230 while (pNode)
3231 {
3232 HWND hwnd = (HWND)pNode->pItemData;
3233 if (WinQueryClassName(hwnd,
3234 sizeof(szClass),
3235 szClass))
3236 {
3237 if (!strcmp(szClass, "#3")) // button
3238 {
3239 // _Pmpf((__FUNCTION__ ": found button"));
3240 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
3241 == (BS_PUSHBUTTON | BS_DEFAULT)
3242 )
3243 {
3244 // _Pmpf((" is default!"));
3245 WinPostMsg(hwnd,
3246 BM_CLICK,
3247 (MPARAM)TRUE, // upclick
3248 0);
3249 return (TRUE);
3250 }
3251 }
3252 }
3253
3254 pNode = pNode->pNext;
3255 }
3256
3257 return (FALSE);
3258}
3259
3260
Note: See TracBrowser for help on using the repository browser.