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

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

Lots of changes from the last three weeks.

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