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

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