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

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