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

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

Misc changes.

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