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

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

Misc updates.

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