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

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

Lafaix and Ratcliffe updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 72.0 KB
Line 
1
2/*
3 *@@sourcefile dialog.c:
4 * contains PM helper functions to create and
5 * auto-format dialogs from control arrays in memory.
6 *
7 * See dlghCreateDlg for details.
8 *
9 * In addition, this has dlghMessageBox (a WinMessageBox
10 * replacement) and some helper functions for simulating
11 * dialog behavior in regular window procs (see
12 * dlghSetPrevFocus and others).
13 *
14 * 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_WINBUTTONS
51#define INCL_WINSTATICS
52#define INCL_WINSYS
53
54#define INCL_GPIPRIMITIVES
55#define INCL_GPIBITMAPS
56#define INCL_GPILCIDS
57#include <os2.h>
58
59#include <stdlib.h>
60#include <string.h>
61#include <stdio.h>
62
63#include "setup.h" // code generation and debugging options
64
65#include "helpers\comctl.h"
66#include "helpers\dialog.h"
67#include "helpers\gpih.h"
68#include "helpers\linklist.h"
69#include "helpers\standards.h"
70#include "helpers\stringh.h"
71#include "helpers\winh.h"
72
73/*
74 *@@category: Helpers\PM helpers\Dialog templates
75 */
76
77/* ******************************************************************
78 *
79 * Private declarations
80 *
81 ********************************************************************/
82
83/*
84 *@@ DLGPRIVATE:
85 * private data to the dlg manager, allocated
86 * by dlghCreateDlg. This is what is really
87 * used, even though the prototype only
88 * declares DIALOGDATA.
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
104 POINTL ptlTotalOfs;
105
106 LINKLIST llControls; // linked list of all PCOLUMNDEF structs,
107 // in the order in which windows were
108 // created
109
110 const char *pcszControlsFont; // from dlghCreateDlg
111
112 // V0.9.14 (2001-08-01) [umoeller]
113 HPS hps;
114 const char *pcszFontLast;
115 LONG lcidLast;
116 FONTMETRICS fmLast;
117
118} DLGPRIVATE, *PDLGPRIVATE;
119
120typedef struct _COLUMNDEF *PCOLUMNDEF;
121typedef struct _ROWDEF *PROWDEF;
122typedef struct _TABLEDEF *PTABLEDEF;
123
124/*
125 *@@ CONTROLPOS:
126 * control position. We don't want to use SWP.
127 */
128
129typedef struct _CONTROLPOS
130{
131 LONG x,
132 y,
133 cx,
134 cy;
135} CONTROLPOS, *PCONTROLPOS;
136
137/*
138 *@@ COLUMNDEF:
139 * representation of a table column.
140 * This is stored in a linked list in ROWDEF.
141 *
142 * A table column represents either a PM control
143 * window or another table, which may therefore
144 * be nested.
145 */
146
147typedef struct _COLUMNDEF
148{
149 PROWDEF pOwningRow; // row whose linked list this column belongs to
150
151 BOOL fIsNestedTable; // if TRUE, pvDefinition points to a nested TABLEDEF;
152 // if FALSE, pvDefinition points to a CONTROLDEF as
153 // specified by the caller
154
155 PVOID pvDefinition; // either a PTABLEDEF or a PCONTROLDEF
156
157 CONTROLPOS cpControl, // real pos and size of control
158 cpColumn; // pos and size of column; can be wider, spacings applied
159
160 HWND hwndControl; // created control; NULLHANDLE for tables always
161
162} COLUMNDEF;
163
164/*
165 *@@ ROWDEF:
166 *
167 */
168
169typedef struct _ROWDEF
170{
171 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
172
173 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
174
175 ULONG flRowFormat; // one of:
176 // -- ROW_VALIGN_BOTTOM 0x0000
177 // -- ROW_VALIGN_CENTER 0x0001
178 // -- ROW_VALIGN_TOP 0x0002
179
180 CONTROLPOS cpRow;
181
182} ROWDEF;
183
184/*
185 *@@ TABLEDEF:
186 *
187 */
188
189typedef struct _TABLEDEF
190{
191 LINKLIST llRows; // contains ROWDEF structs, no auto-free
192
193 PCONTROLDEF pCtlDef; // if != NULL, we create a PM control around the table
194
195 CONTROLPOS cpTable;
196
197} TABLEDEF;
198
199/*
200 *@@ PROCESSMODE:
201 *
202 */
203
204typedef enum _PROCESSMODE
205{
206 PROCESS_CALC_SIZES, // step 1
207 PROCESS_CALC_POSITIONS, // step 3
208 PROCESS_CREATE_CONTROLS // step 4
209} PROCESSMODE;
210
211/* ******************************************************************
212 *
213 * Worker routines
214 *
215 ********************************************************************/
216
217#define PM_GROUP_SPACING_X 10
218#define PM_GROUP_SPACING_TOP 20
219
220APIRET ProcessTable(PTABLEDEF pTableDef,
221 const CONTROLPOS *pcpTable,
222 PROCESSMODE ProcessMode,
223 PDLGPRIVATE pDlgData);
224
225/*
226 *@@ CalcAutoSizeText:
227 *
228 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
229 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed broken fonts
230 *@@changed V0.9.14 (2001-08-01) [umoeller]: now caching fonts, which is significantly faster
231 */
232
233VOID CalcAutoSizeText(PCONTROLDEF pControlDef,
234 BOOL fMultiLine, // in: if TRUE, multiple lines
235 PSIZEL pszlAuto, // out: computed size
236 PDLGPRIVATE pDlgData)
237{
238 const char *pcszFontThis = pControlDef->pcszFont;
239 // can be NULL,
240 // or CTL_COMMON_FONT
241
242 if (pcszFontThis == CTL_COMMON_FONT)
243 pcszFontThis = pDlgData->pcszControlsFont;
244
245 if (!pDlgData->hps)
246 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
247
248 if (pcszFontThis)
249 {
250 LONG lPointSize = 0;
251
252 // check if we can reuse font data from last time
253 // V0.9.14 (2001-08-01) [umoeller]
254 if (strhcmp(pcszFontThis,
255 pDlgData->pcszFontLast))
256 {
257 // different font than last time:
258
259 // delete old font?
260 if (pDlgData->lcidLast)
261 {
262 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
263 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
264 }
265
266 // create new font
267 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
268 FALSE,
269 pDlgData->hps,
270 pcszFontThis,
271 &pDlgData->fmLast,
272 &lPointSize);
273
274 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
275 if (pDlgData->fmLast.fsDefn & FM_DEFN_OUTLINE)
276 gpihSetPointSize(pDlgData->hps, lPointSize);
277
278 pDlgData->pcszFontLast = pcszFontThis;
279 }
280
281 pszlAuto->cy = pDlgData->fmLast.lMaxBaselineExt
282 + pDlgData->fmLast.lExternalLeading;
283 }
284
285 // ok, we FINALLY have a font now...
286 // get the control string and see how much space it needs
287 if (pControlDef->pcszText)
288 {
289 // do we have multiple lines?
290 if (fMultiLine)
291 {
292 RECTL rcl = {0, 0, 0, 0};
293 if (pControlDef->szlControlProposed.cx != -1)
294 rcl.xRight = pControlDef->szlControlProposed.cx; // V0.9.12 (2001-05-31) [umoeller]
295 else
296 rcl.xRight = winhQueryScreenCX() * 2 / 3;
297 if (pControlDef->szlControlProposed.cy != -1)
298 rcl.yTop = pControlDef->szlControlProposed.cy; // V0.9.12 (2001-05-31) [umoeller]
299 else
300 rcl.yTop = winhQueryScreenCY() * 2 / 3;
301
302 winhDrawFormattedText(pDlgData->hps,
303 &rcl,
304 pControlDef->pcszText,
305 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
306 pszlAuto->cx = rcl.xRight - rcl.xLeft;
307 pszlAuto->cy = rcl.yTop - rcl.yBottom;
308 }
309 else
310 {
311 POINTL aptl[TXTBOX_COUNT];
312 GpiQueryTextBox(pDlgData->hps,
313 strlen(pControlDef->pcszText),
314 (PCH)pControlDef->pcszText,
315 TXTBOX_COUNT,
316 aptl);
317 pszlAuto->cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
318 }
319 }
320}
321
322/*
323 *@@ CalcAutoSize:
324 *
325 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
326 */
327
328VOID CalcAutoSize(PCONTROLDEF pControlDef,
329 PSIZEL pszlAuto, // out: computed size
330 PDLGPRIVATE pDlgData)
331{
332 // dumb defaults
333 pszlAuto->cx = 100;
334 pszlAuto->cy = 30;
335
336 switch ((ULONG)pControlDef->pcszClass)
337 {
338 case 0xffff0003L: // WC_BUTTON:
339 CalcAutoSizeText(pControlDef,
340 FALSE, // no multiline
341 pszlAuto,
342 pDlgData);
343 if (pControlDef->flStyle & ( BS_AUTOCHECKBOX
344 | BS_AUTORADIOBUTTON
345 | BS_AUTO3STATE
346 | BS_3STATE
347 | BS_CHECKBOX
348 | BS_RADIOBUTTON))
349 {
350 // give a little extra width for the box bitmap
351 pszlAuto->cx += 20; // @@todo
352 // and height
353 pszlAuto->cy += 2;
354 }
355 else if (pControlDef->flStyle & BS_BITMAP)
356 ;
357 else if (pControlDef->flStyle & (BS_ICON | BS_MINIICON))
358 ;
359 // we can't test for BS_PUSHBUTTON because that's 0x0000
360 else if (!(pControlDef->flStyle & BS_USERBUTTON))
361 {
362 pszlAuto->cx += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) + 15);
363 pszlAuto->cy += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) + 15);
364 }
365 break;
366
367 case 0xffff0005L: // WC_STATIC:
368 if ((pControlDef->flStyle & 0x0F) == SS_TEXT)
369 CalcAutoSizeText(pControlDef,
370 ((pControlDef->flStyle & DT_WORDBREAK) != 0),
371 pszlAuto,
372 pDlgData);
373 else if ((pControlDef->flStyle & 0x0F) == SS_BITMAP)
374 {
375 HBITMAP hbm = (HBITMAP)pControlDef->pcszText;
376 if (hbm)
377 {
378 BITMAPINFOHEADER2 bmih2;
379 ZERO(&bmih2);
380 bmih2.cbFix = sizeof(bmih2);
381 if (GpiQueryBitmapInfoHeader(hbm,
382 &bmih2))
383 {
384 pszlAuto->cx = bmih2.cx;
385 pszlAuto->cy = bmih2.cy;
386 }
387 }
388 }
389 else if ((pControlDef->flStyle & 0x0F) == SS_ICON)
390 {
391 pszlAuto->cx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
392 pszlAuto->cy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
393 }
394 break;
395 }
396}
397
398/*
399 *@@ ProcessColumn:
400 * processes a column, which per definition is either
401 * a control or a nested subtable.
402 *
403 * A column is part of a row, which in turn is part
404 * of a table. There can be several columns in a row,
405 * and several rows in a table.
406 *
407 * Since tables may be specified as columns, it is
408 * possible to produce complex dialog layouts by
409 * nesting tables.
410 *
411 * This does the following:
412 *
413 * -- PROCESS_CALC_SIZES: size is taken from control def,
414 * or for tables, this produces a recursive ProcessTable
415 * call.
416 * Preconditions: none.
417 *
418 * -- PROCESS_CALC_POSITIONS: position of each column
419 * is taken from *plX, which is increased by the
420 * column width by this call.
421 *
422 * Preconditions: Owning row must already have its
423 * y position properly set, or we can't compute
424 * ours. Besides, plX must point to the current X
425 * in the row and will be incremented by the columns
426 * size here.
427 *
428 * -- PROCESS_CREATE_CONTROLS: well, creates the controls.
429 *
430 * For tables, this recurses again. If the table has
431 * a string assigned, this also produces a group box
432 * after the recursion.
433 *
434 *@@changed V0.9.12 (2001-05-31) [umoeller]: added control data
435 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed font problems
436 */
437
438APIRET ProcessColumn(PCOLUMNDEF pColumnDef,
439 PROWDEF pOwningRow, // in: current row from ProcessRow
440 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
441 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
442 PDLGPRIVATE pDlgData)
443{
444 APIRET arc = NO_ERROR;
445
446 pColumnDef->pOwningRow = pOwningRow;
447
448 switch (ProcessMode)
449 {
450 /*
451 * PROCESS_CALC_SIZES:
452 * step 1.
453 */
454
455 case PROCESS_CALC_SIZES:
456 {
457 ULONG ulXSpacing = 0,
458 ulYSpacing = 0;
459 if (pColumnDef->fIsNestedTable)
460 {
461 // nested table: recurse!!
462 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
463 ProcessTable(pTableDef,
464 NULL,
465 ProcessMode,
466 pDlgData);
467
468 // store the size of the sub-table
469 pColumnDef->cpControl.cx = pTableDef->cpTable.cx;
470 pColumnDef->cpControl.cy = pTableDef->cpTable.cy;
471
472 // should we create a PM control around the table?
473 if (pTableDef->pCtlDef)
474 {
475 // yes: make this wider
476 ulXSpacing = (2 * PM_GROUP_SPACING_X);
477 ulYSpacing = (PM_GROUP_SPACING_X + PM_GROUP_SPACING_TOP);
478 }
479 }
480 else
481 {
482 // no nested table, but control:
483 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
484 PSIZEL pszl = &pControlDef->szlControlProposed;
485 SIZEL szlAuto;
486
487 if ( (pszl->cx == -1)
488 || (pszl->cy == -1)
489 )
490 {
491 CalcAutoSize(pControlDef,
492 &szlAuto,
493 pDlgData);
494 }
495
496 if (pszl->cx == -1)
497 pColumnDef->cpControl.cx = szlAuto.cx;
498 else
499 pColumnDef->cpControl.cx = pszl->cx;
500
501 if (pszl->cy == -1)
502 pColumnDef->cpControl.cy = szlAuto.cy;
503 else
504 pColumnDef->cpControl.cy = pszl->cy;
505
506 // @@todo hack sizes
507
508 ulXSpacing = ulYSpacing = (2 * pControlDef->ulSpacing);
509 }
510
511 pColumnDef->cpColumn.cx = pColumnDef->cpControl.cx
512 + ulXSpacing;
513 pColumnDef->cpColumn.cy = pColumnDef->cpControl.cy
514 + ulYSpacing;
515 break; }
516
517 /*
518 * PROCESS_CALC_POSITIONS:
519 * step 2.
520 */
521
522 case PROCESS_CALC_POSITIONS:
523 {
524 // calculate column position: this includes spacing
525 ULONG ulSpacing = 0;
526
527 // column position = *plX on ProcessRow stack
528 pColumnDef->cpColumn.x = *plX;
529 pColumnDef->cpColumn.y = pOwningRow->cpRow.y;
530
531 // check vertical alignment of row;
532 // we might need to increase column y
533 switch (pOwningRow->flRowFormat & ROW_VALIGN_MASK)
534 {
535 // case ROW_VALIGN_BOTTOM: // do nothing
536
537 case ROW_VALIGN_CENTER:
538 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
539 pColumnDef->cpColumn.y
540 += ( (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy)
541 / 2);
542 break;
543
544 case ROW_VALIGN_TOP:
545 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
546 pColumnDef->cpColumn.y
547 += (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy);
548 break;
549 }
550
551 if (pColumnDef->fIsNestedTable)
552 {
553 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
554 // should we create a PM control around the table?
555 if (pTableDef->pCtlDef)
556 // yes:
557 ulSpacing = PM_GROUP_SPACING_X;
558 }
559 else
560 {
561 // no nested table, but control:
562 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
563 ulSpacing = pControlDef->ulSpacing;
564 }
565
566 // increase plX by column width
567 *plX += pColumnDef->cpColumn.cx;
568
569 // calculate CONTROL pos from COLUMN pos by applying spacing
570 pColumnDef->cpControl.x = pColumnDef->cpColumn.x
571 + ulSpacing;
572 pColumnDef->cpControl.y = pColumnDef->cpColumn.y
573 + ulSpacing;
574
575 if (pColumnDef->fIsNestedTable)
576 {
577 // nested table:
578 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
579
580 // recurse!! to create windows for the sub-table
581 ProcessTable(pTableDef,
582 &pColumnDef->cpControl, // start pos for new table
583 ProcessMode,
584 pDlgData);
585 }
586 break; }
587
588 /*
589 * PROCESS_CREATE_CONTROLS:
590 * step 3.
591 */
592
593 case PROCESS_CREATE_CONTROLS:
594 {
595 PCONTROLPOS pcp = NULL;
596 PCONTROLDEF pControlDef = NULL;
597 const char *pcszTitle = NULL;
598 ULONG flStyle = 0;
599 LHANDLE lHandleSet = NULLHANDLE;
600 ULONG flOld = 0;
601
602 if (pColumnDef->fIsNestedTable)
603 {
604 // nested table:
605 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
606
607 // recurse!!
608 if (!(arc = ProcessTable(pTableDef,
609 NULL,
610 ProcessMode,
611 pDlgData)))
612 {
613 // should we create a PM control around the table?
614 // (do this AFTER the other controls from recursing,
615 // otherwise the stupid container doesn't show up)
616 if (pTableDef->pCtlDef)
617 {
618 // yes:
619 pcp = &pColumnDef->cpColumn; // !! not control
620 pControlDef = pTableDef->pCtlDef;
621 pcszTitle = pControlDef->pcszText;
622 flStyle = pControlDef->flStyle;
623 }
624 }
625 else
626 break;
627 }
628 else
629 {
630 // no nested table, but control:
631 pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
632 pcp = &pColumnDef->cpControl;
633 pcszTitle = pControlDef->pcszText;
634 flStyle = pControlDef->flStyle;
635
636 // change the title if this is a static with SS_BITMAP;
637 // we have used a HBITMAP in there!
638 if ( ((ULONG)pControlDef->pcszClass == 0xffff0005L) // WC_STATIC:
639 && ( ((flStyle & 0x0F) == SS_BITMAP)
640 || ((flStyle & 0x0F) == SS_ICON)
641 )
642 )
643 {
644 // change style flag to not use SS_BITMAP nor SS_ICON;
645 // control creation fails otherwise (stupid, stupid PM)
646 flOld = flStyle;
647 flStyle = ((flStyle & ~0x0F) | SS_FGNDFRAME);
648 pcszTitle = "";
649 lHandleSet = (LHANDLE)pControlDef->pcszText;
650 }
651 }
652
653 if (pcp && pControlDef)
654 {
655 // create something:
656 // PPRESPARAMS ppp = NULL;
657
658 const char *pcszFont = pControlDef->pcszFont;
659 // can be NULL, or CTL_COMMON_FONT
660 if (pcszFont == CTL_COMMON_FONT)
661 pcszFont = pDlgData->pcszControlsFont;
662
663 /* if (pcszFont)
664 winhStorePresParam(&ppp,
665 PP_FONTNAMESIZE,
666 strlen(pcszFont),
667 (PVOID)pcszFont); */
668
669 if (pColumnDef->hwndControl
670 = WinCreateWindow(pDlgData->hwndDlg, // parent
671 (PSZ)pControlDef->pcszClass,
672 (pcszTitle) // hacked
673 ? (PSZ)pcszTitle
674 : "",
675 flStyle, // hacked
676 pcp->x + pDlgData->ptlTotalOfs.x,
677 pcp->y + pDlgData->ptlTotalOfs.y,
678 pcp->cx,
679 pcp->cy,
680 pDlgData->hwndDlg, // owner
681 HWND_BOTTOM,
682 pControlDef->usID,
683 pControlDef->pvCtlData,
684 NULL))
685 {
686 if (lHandleSet)
687 {
688 // subclass the damn static
689 if ((flOld & 0x0F) == SS_ICON)
690 // this was a static:
691 ctlPrepareStaticIcon(pColumnDef->hwndControl,
692 1);
693 else
694 // this was a bitmap:
695 ctlPrepareStretchedBitmap(pColumnDef->hwndControl,
696 TRUE);
697
698 WinSendMsg(pColumnDef->hwndControl,
699 SM_SETHANDLE,
700 (MPARAM)lHandleSet,
701 0);
702 }
703 else
704 if (pcszFont)
705 // we must set the font explicitly here...
706 // doesn't always work with WinCreateWindow
707 // presparams parameter, for some reason
708 // V0.9.12 (2001-05-31) [umoeller]
709 winhSetWindowFont(pColumnDef->hwndControl,
710 pcszFont);
711
712 lstAppendItem(&pDlgData->llControls,
713 pColumnDef);
714
715 // if this is the first control with WS_TABSTOP,
716 // we give it the focus later
717 if ( (flStyle & WS_TABSTOP)
718 && (!pDlgData->hwndFirstFocus)
719 )
720 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
721 }
722 else
723 // V0.9.14 (2001-08-03) [umoeller]
724 arc = DLGERR_CANNOT_CREATE_CONTROL;
725 }
726 break; }
727 }
728
729 return (arc);
730}
731
732/*
733 *@@ ProcessRow:
734 * level-3 procedure (called from ProcessTable),
735 * which in turn calls ProcessColumn for each column
736 * in the row.
737 *
738 * See ProcessAll for the meaning of ProcessMode.
739 */
740
741APIRET ProcessRow(PROWDEF pRowDef,
742 PTABLEDEF pOwningTable, // in: current table from ProcessTable
743 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
744 PLONG plY, // in/out: current y position (decremented)
745 PDLGPRIVATE pDlgData)
746{
747 APIRET arc = NO_ERROR;
748 LONG lX;
749 PLISTNODE pNode;
750
751 pRowDef->pOwningTable = pOwningTable;
752
753 if (ProcessMode == PROCESS_CALC_SIZES)
754 {
755 pRowDef->cpRow.cx = 0;
756 pRowDef->cpRow.cy = 0;
757 }
758 else if (ProcessMode == PROCESS_CALC_POSITIONS)
759 {
760 // set up x and y so that the columns can
761 // base on that
762 pRowDef->cpRow.x = pOwningTable->cpTable.x;
763 // decrease y by row height
764 *plY -= pRowDef->cpRow.cy;
765 // and use that for our bottom position
766 pRowDef->cpRow.y = *plY;
767
768 // set lX to left of row; used by column calls below
769 lX = pRowDef->cpRow.x;
770 }
771
772 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
773 {
774 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
775
776 if (!(arc = ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData)))
777 {
778 if (ProcessMode == PROCESS_CALC_SIZES)
779 {
780 // row width = sum of all columns
781 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
782
783 // row height = maximum height of a column
784 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
785 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
786 }
787 }
788 }
789
790 return (arc);
791}
792
793/*
794 *@@ ProcessTable:
795 * level-2 procedure (called from ProcessAll),
796 * which in turn calls ProcessRow for each row
797 * in the table (which in turn calls ProcessColumn
798 * for each column in the row).
799 *
800 * See ProcessAll for the meaning of ProcessMode.
801 *
802 * This routine is a bit sick because it can even be
803 * called recursively from ProcessColumn (!) if a
804 * nested table is found in a COLUMNDEF.
805 *
806 * With PROCESS_CALC_POSITIONS, pptl must specify
807 * the lower left corner of the table. For the
808 * root call, this will be {0, 0}; for nested calls,
809 * this must be the lower left corner of the column
810 * to which the nested table belongs.
811 *
812 */
813
814APIRET ProcessTable(PTABLEDEF pTableDef,
815 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
816 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
817 PDLGPRIVATE pDlgData)
818{
819 APIRET arc = NO_ERROR;
820 LONG lY;
821 PLISTNODE pNode;
822
823 if (ProcessMode == PROCESS_CALC_SIZES)
824 {
825 pTableDef->cpTable.cx = 0;
826 pTableDef->cpTable.cy = 0;
827 }
828 else if (ProcessMode == PROCESS_CALC_POSITIONS)
829 {
830 pTableDef->cpTable.x = pcpTable->x;
831 pTableDef->cpTable.y = pcpTable->y;
832
833 // start the rows on top
834 lY = pcpTable->y + pTableDef->cpTable.cy;
835 }
836
837 FOR_ALL_NODES(&pTableDef->llRows, pNode)
838 {
839 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
840
841 if (!(arc = ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData)))
842 {
843 if (ProcessMode == PROCESS_CALC_SIZES)
844 {
845 // table width = maximum width of a row
846 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
847 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
848
849 // table height = sum of all rows
850 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
851 }
852 }
853 else
854 break;
855 }
856
857 return (arc);
858}
859
860/*
861 *@@ ProcessAll:
862 * level-1 procedure, which in turn calls ProcessTable
863 * for each root-level table found (which in turn
864 * calls ProcessRow for each row in the table, which
865 * in turn calls ProcessColumn for each column in
866 * the row).
867 *
868 * The first trick to formatting is that ProcessAll will
869 * get three times, thus going down the entire tree three
870 * times, with ProcessMode being set to one of the
871 * following for each call (in this order):
872 *
873 * -- PROCESS_CALC_SIZES: calculates the sizes
874 * of all tables, rows, columns, and controls.
875 *
876 * After this first call, we know all the sizes
877 * only and then then calculate the positions.
878 *
879 * -- PROCESS_CALC_POSITIONS: calculates the positions
880 * based on the sizes calculated before.
881 *
882 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
883 * positions and sizes calculated before.
884 *
885 * The second trick is the precondition that tables may
886 * nest by allowing a table definition instead of a
887 * control definition in a column. This way we can
888 * recurse from columns back into tables and thus
889 * know the size and position of a nested table column
890 * just as if it were a regular control.
891 */
892
893APIRET ProcessAll(PDLGPRIVATE pDlgData,
894 PSIZEL pszlClient,
895 PROCESSMODE ProcessMode)
896{
897 APIRET arc = NO_ERROR;
898 PLISTNODE pNode;
899 CONTROLPOS cpTable;
900 ZERO(&cpTable);
901
902 switch (ProcessMode)
903 {
904 case PROCESS_CALC_SIZES:
905 pszlClient->cx = 0;
906 pszlClient->cy = 0;
907 break;
908
909 case PROCESS_CALC_POSITIONS:
910 // start with the table on top
911 cpTable.y = pszlClient->cy;
912 break;
913 }
914
915 FOR_ALL_NODES(&pDlgData->llTables, pNode)
916 {
917 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
918
919 if (ProcessMode == PROCESS_CALC_POSITIONS)
920 {
921 cpTable.x = 0;
922 cpTable.y -= pTableDefThis->cpTable.cy;
923 }
924
925 if (!(arc = ProcessTable(pTableDefThis,
926 &cpTable, // start pos
927 ProcessMode,
928 pDlgData)))
929 {
930 if (ProcessMode == PROCESS_CALC_SIZES)
931 {
932 pszlClient->cx += pTableDefThis->cpTable.cx;
933 pszlClient->cy += pTableDefThis->cpTable.cy;
934 }
935 }
936 }
937
938 return (arc);
939}
940
941/*
942 *@@ CreateColumn:
943 *
944 */
945
946APIRET CreateColumn(PROWDEF pCurrentRow,
947 BOOL fIsNestedTable,
948 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
949 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
950{
951 APIRET arc = NO_ERROR;
952
953 if (!pCurrentRow)
954 arc = DLGERR_CONTROL_BEFORE_ROW;
955 else
956 {
957 // append the control def
958 if (!pvDefinition)
959 arc = DLGERR_NULL_CTL_DEF;
960 else
961 {
962 // create column and store ctl def
963 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
964 if (!pColumnDef)
965 arc = ERROR_NOT_ENOUGH_MEMORY;
966 else
967 {
968 memset(pColumnDef, 0, sizeof(COLUMNDEF));
969 pColumnDef->pOwningRow = pCurrentRow;
970 pColumnDef->fIsNestedTable = fIsNestedTable;
971 pColumnDef->pvDefinition = pvDefinition;
972
973 *ppColumnDef = pColumnDef;
974 }
975 }
976 }
977
978 return (arc);
979}
980
981/*
982 *@@ FreeTable:
983 * frees the specified table and recurses
984 * into nested tables, if necessary.
985 *
986 * This was added with V0.9.14 to fix the
987 * bad memory leaks with nested tables.
988 *
989 *@@added V0.9.14 (2001-08-01) [umoeller]
990 */
991
992VOID FreeTable(PTABLEDEF pTable)
993{
994 // for each table, clean up the rows
995 PLISTNODE pRowNode;
996 FOR_ALL_NODES(&pTable->llRows, pRowNode)
997 {
998 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
999
1000 // for each row, clean up the columns
1001 PLISTNODE pColumnNode;
1002 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1003 {
1004 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1005
1006 if (pColumn->fIsNestedTable)
1007 {
1008 // nested table: recurse!
1009 PTABLEDEF pNestedTable = (PTABLEDEF)pColumn->pvDefinition;
1010 FreeTable(pNestedTable);
1011 }
1012
1013 free(pColumn);
1014 }
1015 lstClear(&pRow->llColumns);
1016
1017 free(pRow);
1018 }
1019 lstClear(&pTable->llRows);
1020
1021 free(pTable);
1022}
1023
1024/* ******************************************************************
1025 *
1026 * Public APIs
1027 *
1028 ********************************************************************/
1029
1030/*
1031 *@@ STACKITEM:
1032 *
1033 */
1034
1035typedef struct _STACKITEM
1036{
1037 PTABLEDEF pLastTable;
1038 PROWDEF pLastRow;
1039
1040} STACKITEM, *PSTACKITEM;
1041
1042/*
1043 *@@ dlghCreateDlg:
1044 * replacement for WinCreateDlg/WinLoadDlg for creating a
1045 * dialog from a settings array in memory, which is
1046 * formatted automatically.
1047 *
1048 * This does NOT use regular dialog templates from
1049 * module resources. Instead, you pass in an array
1050 * of _DLGHITEM structures, which define the controls
1051 * and how they are to be formatted.
1052 *
1053 * A regular standard dialog would use something like
1054 *
1055 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
1056 *
1057 * for flCreateFlags. To make the dlg sizeable, specify
1058 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
1059 *
1060 * <B>Usage:</B>
1061 *
1062 * Like WinLoadDlg, this creates a standard WC_FRAME and
1063 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
1064 * to the dialog with pCreateParams in mp2.
1065 *
1066 * If this func returns no error, you can then use
1067 * WinProcessDlg with the newly created dialog as usual. In
1068 * your dlg proc, use WinDefDlgProc as usual.
1069 *
1070 * There is NO run-time overhead after creation; after this
1071 * function returns, the dialog is a standard dialog as if
1072 * loaded from WinLoadDlg.
1073 *
1074 * The array of _DLGHITEM structures defines how the
1075 * dialog is set up. All this is ONLY used by this function
1076 * and NOT needed after the dialog has been created.
1077 *
1078 * In _DLGHITEM, the "Type" field determines what this
1079 * structure defines. A number of handy macros have been
1080 * defined to make this easier and to provide type-checking
1081 * at compile time.
1082 *
1083 * Essentially, such a dialog item operates similarly to
1084 * HTML tables. There are rows and columns in the table,
1085 * and each control which is specified must be a column
1086 * in some table. Tables may also nest (see below).
1087 *
1088 * The macros are:
1089 *
1090 * -- START_TABLE starts a new table. The tables may nest,
1091 * but must each be properly terminated with END_TABLE.
1092 *
1093 * -- START_GROUP_TABLE(pDef) starts a group. This
1094 * behaves exacly like START_TABLE, but in addition,
1095 * it produces a static group control around the table.
1096 * Useful for group boxes. pDef must point to a
1097 * _CONTROLDEF describing the control to be used for
1098 * the group (usually a WC_STATIC with SS_GROUP style),
1099 * whose size parameter is ignored.
1100 *
1101 * As with START_TABLE, START_GROUP_TABLE must be
1102 * terminated with END_TABLE.
1103 *
1104 * -- START_ROW(fl) starts a new row in a table (regular
1105 * or group). This must also be the first item after
1106 * the (group) table tag.
1107 *
1108 * fl specifies formatting flags for the row. This
1109 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
1110 * ROW_VALIGN_TOP and affects all items in the control.
1111 *
1112 * -- CONTROL_DEF(pDef) defines a control in a table row.
1113 * pDef must point to a _CONTROLDEF structure.
1114 *
1115 * The main difference (and advantage) of this
1116 * function is that there is NO information in
1117 * _CONTROLDEF about a control's _position_.
1118 * Instead, the structure only contains the _size_
1119 * of the control. All positions are computed by
1120 * this function, depending on the sizes of the
1121 * controls and their nesting within the various tables.
1122 *
1123 * If you specify SZL_AUTOSIZE, the size of the
1124 * control is even computed automatically. Presently,
1125 * this only works for statics with SS_TEXT, SS_ICON,
1126 * and SS_BITMAP.
1127 *
1128 * Unless separated with START_ROW items, subsequent
1129 * control items will be considered to be in the same
1130 * row (== positioned next to each other).
1131 *
1132 * There are a few rules, whose violation will produce
1133 * an error:
1134 *
1135 * -- The entire array must be enclosed in a table
1136 * (the "root" table).
1137 *
1138 * -- After START_TABLE or START_GROUP_TABLE, there must
1139 * always be a START_ROW first.
1140 *
1141 * To create a dialog, set up arrays like the following:
1142 *
1143 + // control definitions referenced by DlgTemplate:
1144 + CONTROLDEF
1145 + (1) GroupDef = {
1146 + WC_STATIC, "", ....,
1147 + { 0, 0 }, // size, ignored for groups
1148 + 5 // spacing
1149 + },
1150 + (2) CnrDef = {
1151 + WC_CONTAINER, "", ....,
1152 + { 50, 50 }, // size
1153 + 5 // spacing
1154 + },
1155 + (3) Static = {
1156 + WC_STATIC, "Static below cnr", ...,
1157 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
1158 + 5 // spacing
1159 + },
1160 + (4) OKButton = {
1161 + WC_STATIC, "~OK", ...,
1162 + { 100, 30 }, // size
1163 + 5 // spacing
1164 + },
1165 + (5) CancelButton = {
1166 + WC_STATIC, "~Cancel", ...,
1167 + { 100, 30 }, // size
1168 + 5 // spacing
1169 + };
1170 +
1171 + DLGHITEM DlgTemplate[] =
1172 + {
1173 + START_TABLE, // root table, required
1174 + START_ROW(0), // row 1 in the root table, required
1175 + // create group on top
1176 + (1) START_GROUP_TABLE(&Group),
1177 + START_ROW(0),
1178 + (2) CONTROL_DEF(&CnrDef),
1179 + START_ROW(0),
1180 + (3) CONTROL_DEF(&Static),
1181 + END_TABLE, // end of group
1182 + START_ROW(0), // row 2 in the root table
1183 + // two buttons next to each other
1184 + (4) CONTROL_DEF(&OKButton),
1185 + (5) CONTROL_DEF(&CancelButton),
1186 + END_TABLE
1187 + }
1188 *
1189 * This will produce a dlg like this:
1190 *
1191 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1192 + º º
1193 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1194 + º ³ ³ º
1195 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1196 + º ³ ³ ³ ³ º
1197 + º ³ ³ Cnr inside group (2) ³ ³ º
1198 + º ³ ³ ³ ³ º
1199 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1200 + º ³ Static below cnr (3) ³ º
1201 + º ³ ³ º
1202 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1203 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1204 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1205 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1206 + º º
1207 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1208 *
1209 * <B>Errors:</B>
1210 *
1211 * This does not return a HWND, but an APIRET. This will be
1212 * one of the following:
1213 *
1214 * -- NO_ERROR: only in that case, the phwndDlg ptr
1215 * receives the HWND of the new dialog, which can
1216 * then be given to WinProcessDlg. Don't forget
1217 * WinDestroyWindow.
1218 *
1219 * -- ERROR_NOT_ENOUGH_MEMORY
1220 *
1221 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1222 * outside a table definition.
1223 *
1224 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1225 * appeared right after a table definition. You must
1226 * specify a row first.
1227 *
1228 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1229 * but the CONTROLDEF ptr was NULL.
1230 *
1231 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1232 * WC_FRAME window. Maybe an invalid owner was specified.
1233 *
1234 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1235 * DLGHITEM.
1236 *
1237 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1238 * improper nesting of TYPE_START_NEW_TABLE and
1239 * TYPE_END_TABLE fields.
1240 *
1241 * <B>Example:</B>
1242 *
1243 * The typical calling sequence would be:
1244 *
1245 + HWND hwndDlg = NULLHANDLE;
1246 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1247 + hwndOwner,
1248 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1249 + fnwpMyDlgProc,
1250 + "My Dlg Title",
1251 + DlgTemplate, // DLGHITEM array
1252 + ARRAYITEMCOUNT(DlgTemplate),
1253 + NULL))
1254 + {
1255 + ULONG idReturn = WinProcessDlg(hwndDlg);
1256 + WinDestroyWindow(hwndDlg);
1257 + }
1258 *
1259 *@@changed V0.9.14 (2001-07-07) [umoeller]: fixed disabled mouse with hwndOwner == HWND_DESKTOP
1260 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed major memory leaks with nested tables
1261 */
1262
1263APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1264 HWND hwndOwner,
1265 ULONG flCreateFlags, // in: standard FCF_* frame flags
1266 PFNWP pfnwpDialogProc,
1267 const char *pcszDlgTitle,
1268 PDLGHITEM paDlgItems, // in: definition array
1269 ULONG cDlgItems, // in: array item count (NOT array size)
1270 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1271 const char *pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1272{
1273 APIRET arc = NO_ERROR;
1274
1275 #define SPACING 10
1276
1277 PTABLEDEF pCurrentTable = NULL;
1278 PROWDEF pCurrentRow = NULL;
1279 ULONG ul;
1280 LINKLIST llStack;
1281
1282 PDLGPRIVATE pDlgData = NEW(DLGPRIVATE);
1283
1284 if (!pDlgData)
1285 return (ERROR_NOT_ENOUGH_MEMORY);
1286
1287 ZERO(pDlgData);
1288 lstInit(&pDlgData->llTables, FALSE);
1289 lstInit(&pDlgData->llControls, FALSE);
1290
1291 pDlgData->pcszControlsFont = pcszControlsFont;
1292
1293 /*
1294 * 1) parse the table and create structures from it
1295 *
1296 */
1297
1298 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1299
1300 for (ul = 0;
1301 ul < cDlgItems;
1302 ul++)
1303 {
1304 PDLGHITEM pItemThis = &paDlgItems[ul];
1305
1306 switch (pItemThis->Type)
1307 {
1308 /*
1309 * TYPE_START_NEW_TABLE:
1310 *
1311 */
1312
1313 case TYPE_START_NEW_TABLE:
1314 {
1315 // root table or nested?
1316 BOOL fIsRoot = (pCurrentTable == NULL);
1317
1318 // push the current table on the stack
1319 PSTACKITEM pStackItem = NEW(STACKITEM);
1320 if (pStackItem)
1321 {
1322 pStackItem->pLastTable = pCurrentTable;
1323 pStackItem->pLastRow = pCurrentRow;
1324 lstPush(&llStack, pStackItem);
1325 }
1326
1327 // create new table
1328 pCurrentTable = NEW(TABLEDEF);
1329 if (!pCurrentTable)
1330 arc = ERROR_NOT_ENOUGH_MEMORY;
1331 else
1332 {
1333 ZERO(pCurrentTable);
1334
1335 lstInit(&pCurrentTable->llRows, FALSE);
1336
1337 if (pItemThis->ulData)
1338 // control specified: store it (this will become a PM group)
1339 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1340
1341 if (fIsRoot)
1342 // root table:
1343 // append to dialog data list
1344 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1345 else
1346 {
1347 // nested table:
1348 // create "table" column for this
1349 PCOLUMNDEF pColumnDef;
1350 if (!(arc = CreateColumn(pCurrentRow,
1351 TRUE, // nested table
1352 pCurrentTable,
1353 &pColumnDef)))
1354 lstAppendItem(&pCurrentRow->llColumns,
1355 pColumnDef);
1356 }
1357 }
1358
1359 pCurrentRow = NULL;
1360 break; }
1361
1362 /*
1363 * TYPE_START_NEW_ROW:
1364 *
1365 */
1366
1367 case TYPE_START_NEW_ROW:
1368 {
1369 if (!pCurrentTable)
1370 arc = DLGERR_ROW_BEFORE_TABLE;
1371 else
1372 {
1373 // create new row
1374 pCurrentRow = NEW(ROWDEF);
1375 if (!pCurrentRow)
1376 arc = ERROR_NOT_ENOUGH_MEMORY;
1377 else
1378 {
1379 ZERO(pCurrentRow);
1380
1381 pCurrentRow->pOwningTable = pCurrentTable;
1382 lstInit(&pCurrentRow->llColumns, FALSE);
1383
1384 pCurrentRow->flRowFormat = pItemThis->ulData;
1385
1386 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1387 }
1388 }
1389 break; }
1390
1391 /*
1392 * TYPE_CONTROL_DEF:
1393 *
1394 */
1395
1396 case TYPE_CONTROL_DEF:
1397 {
1398 PCOLUMNDEF pColumnDef;
1399 if (!(arc = CreateColumn(pCurrentRow,
1400 FALSE, // no nested table
1401 (PVOID)pItemThis->ulData,
1402 &pColumnDef)))
1403 lstAppendItem(&pCurrentRow->llColumns,
1404 pColumnDef);
1405 break; }
1406
1407 /*
1408 * TYPE_END_TABLE:
1409 *
1410 */
1411
1412 case TYPE_END_TABLE:
1413 {
1414 PLISTNODE pNode = lstPop(&llStack);
1415 if (!pNode)
1416 // nothing on the stack:
1417 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1418 else
1419 {
1420 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1421 pCurrentTable = pStackItem->pLastTable;
1422 pCurrentRow = pStackItem->pLastRow;
1423
1424 lstRemoveNode(&llStack, pNode);
1425 }
1426 break; }
1427
1428 default:
1429 arc = DLGERR_INVALID_CODE;
1430 }
1431
1432 if (arc)
1433 break;
1434 }
1435
1436 if (arc == NO_ERROR)
1437 if (lstCountItems(&llStack))
1438 arc = DLGERR_TABLE_NOT_CLOSED;
1439
1440 lstClear(&llStack);
1441
1442 if (arc == NO_ERROR)
1443 {
1444 /*
1445 * 2) create empty dialog frame
1446 *
1447 */
1448
1449 FRAMECDATA fcData = {0};
1450 ULONG flStyle = 0;
1451
1452 fcData.cb = sizeof(FRAMECDATA);
1453 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
1454
1455 if (flCreateFlags & FCF_SIZEBORDER)
1456 // dialog has size border:
1457 // add "clip siblings" style
1458 flStyle |= WS_CLIPSIBLINGS;
1459
1460 if (hwndOwner == HWND_DESKTOP)
1461 // there's some dumb XWorkplace code left
1462 // which uses this, and this disables the
1463 // mouse for some reason
1464 // V0.9.14 (2001-07-07) [umoeller]
1465 hwndOwner = NULLHANDLE;
1466
1467 pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1468 WC_FRAME,
1469 (PSZ)pcszDlgTitle,
1470 flStyle, // style; invisible for now
1471 0, 0, 0, 0,
1472 hwndOwner,
1473 HWND_TOP,
1474 0, // ID
1475 &fcData,
1476 NULL); // presparams
1477
1478 if (!pDlgData->hwndDlg)
1479 arc = DLGERR_CANNOT_CREATE_FRAME;
1480 else
1481 {
1482 HWND hwndDlg = pDlgData->hwndDlg;
1483 SIZEL szlClient = {0};
1484 RECTL rclClient;
1485 HWND hwndFocusItem = NULLHANDLE;
1486
1487 /*
1488 * 3) compute size of all controls
1489 *
1490 */
1491
1492 ProcessAll(pDlgData,
1493 &szlClient,
1494 PROCESS_CALC_SIZES);
1495
1496 if (pDlgData->lcidLast)
1497 {
1498 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1499 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1500 }
1501 if (pDlgData->hps)
1502 WinReleasePS(pDlgData->hps);
1503
1504 /*
1505 * 4) compute size of dialog client from total
1506 * size of all controls
1507 */
1508
1509 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
1510
1511 // calculate the frame size from the client size
1512 rclClient.xLeft = 10;
1513 rclClient.yBottom = 10;
1514 rclClient.xRight = szlClient.cx + 2 * SPACING;
1515 rclClient.yTop = szlClient.cy + 2 * SPACING;
1516 WinCalcFrameRect(hwndDlg,
1517 &rclClient,
1518 FALSE); // frame from client
1519
1520 WinSetWindowPos(hwndDlg,
1521 0,
1522 10,
1523 10,
1524 rclClient.xRight,
1525 rclClient.yTop,
1526 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1527
1528 /*
1529 * 5) compute _positions_ of all controls
1530 *
1531 */
1532
1533 ProcessAll(pDlgData,
1534 &szlClient,
1535 PROCESS_CALC_POSITIONS);
1536
1537 /*
1538 * 6) create control windows, finally
1539 *
1540 */
1541
1542 pDlgData->ptlTotalOfs.x = SPACING;
1543 pDlgData->ptlTotalOfs.y = SPACING;
1544
1545 ProcessAll(pDlgData,
1546 &szlClient,
1547 PROCESS_CREATE_CONTROLS);
1548
1549 /*
1550 * 7) WM_INITDLG, set focus
1551 *
1552 */
1553
1554 hwndFocusItem = (pDlgData->hwndFirstFocus)
1555 ? pDlgData->hwndFirstFocus
1556 : hwndDlg;
1557 if (!WinSendMsg(hwndDlg,
1558 WM_INITDLG,
1559 (MPARAM)hwndFocusItem,
1560 (MPARAM)pCreateParams))
1561 {
1562 // if WM_INITDLG returns FALSE, this means
1563 // the dlg proc has not changed the focus;
1564 // we must then set the focus here
1565 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1566 }
1567 }
1568 }
1569
1570 if (pDlgData)
1571 {
1572 PLISTNODE pTableNode;
1573
1574 if (arc)
1575 {
1576 // error: clean up
1577 if (pDlgData->hwndDlg)
1578 WinDestroyWindow(pDlgData->hwndDlg);
1579 }
1580 else
1581 // no error: output dialog
1582 *phwndDlg = pDlgData->hwndDlg;
1583
1584 // in any case, clean up our mess:
1585
1586 // clean up the tables
1587 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1588 {
1589 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1590
1591 FreeTable(pTable);
1592 // this may recurse for nested tables
1593 }
1594
1595 lstClear(&pDlgData->llTables);
1596 lstClear(&pDlgData->llControls);
1597
1598 free(pDlgData);
1599 }
1600
1601 if (arc)
1602 {
1603 CHAR szErr[300];
1604 sprintf(szErr, "Error %d occured in dlghCreateDlg.", arc);
1605 winhDebugBox(hwndOwner,
1606 "Error in Dialog Manager",
1607 szErr);
1608 }
1609
1610 return (arc);
1611}
1612
1613/*
1614 *@@ dlghCreateMessageBox:
1615 *
1616 *@@added V0.9.13 (2001-06-21) [umoeller]
1617 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
1618 */
1619
1620APIRET dlghCreateMessageBox(HWND *phwndDlg,
1621 HWND hwndOwner,
1622 HPOINTER hptrIcon,
1623 const char *pcszTitle,
1624 const char *pcszMessage,
1625 ULONG flFlags,
1626 const char *pcszFont,
1627 const MSGBOXSTRINGS *pStrings,
1628 PULONG pulAlarmFlag) // out: alarm sound to be played
1629{
1630 CONTROLDEF
1631 Icon = {
1632 WC_STATIC,
1633 NULL, // text, set below
1634 WS_VISIBLE | SS_ICON,
1635 0, // ID
1636 NULL, // no font
1637 0,
1638 { SZL_AUTOSIZE, SZL_AUTOSIZE },
1639 5
1640 },
1641 InfoText =
1642 {
1643 WC_STATIC,
1644 NULL, // text, set below
1645 WS_VISIBLE | SS_TEXT | DT_WORDBREAK | DT_LEFT | DT_TOP,
1646 10, // ID
1647 CTL_COMMON_FONT,
1648 0,
1649 { 400, SZL_AUTOSIZE },
1650 5
1651 },
1652 Buttons[] = {
1653 {
1654 WC_BUTTON,
1655 NULL, // text, set below
1656 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1657 1, // ID
1658 CTL_COMMON_FONT, // no font
1659 0,
1660 { 100, 30 },
1661 5
1662 },
1663 {
1664 WC_BUTTON,
1665 NULL, // text, set below
1666 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1667 2, // ID
1668 CTL_COMMON_FONT, // no font
1669 0,
1670 { 100, 30 },
1671 5
1672 },
1673 {
1674 WC_BUTTON,
1675 NULL, // text, set below
1676 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
1677 3, // ID
1678 CTL_COMMON_FONT, // no font
1679 0,
1680 { 100, 30 },
1681 5
1682 }
1683 };
1684
1685 DLGHITEM MessageBox[] =
1686 {
1687 START_TABLE,
1688 START_ROW(ROW_VALIGN_CENTER),
1689 CONTROL_DEF(&Icon),
1690 START_TABLE,
1691 START_ROW(ROW_VALIGN_CENTER),
1692 CONTROL_DEF(&InfoText),
1693 START_ROW(ROW_VALIGN_CENTER),
1694 CONTROL_DEF(&Buttons[0]),
1695 CONTROL_DEF(&Buttons[1]),
1696 CONTROL_DEF(&Buttons[2]),
1697 END_TABLE,
1698 END_TABLE
1699 };
1700
1701 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
1702
1703 const char *p0 = "Error",
1704 *p1 = NULL,
1705 *p2 = NULL;
1706
1707 Icon.pcszText = (const char *)hptrIcon;
1708 InfoText.pcszText = pcszMessage;
1709
1710 // now work on the three buttons of the dlg template:
1711 // give them proper titles or hide them
1712 if (flButtons == MB_OK)
1713 {
1714 p0 = pStrings->pcszOK;
1715 }
1716 else if (flButtons == MB_OKCANCEL)
1717 {
1718 p0 = pStrings->pcszOK;
1719 p1 = pStrings->pcszCancel;
1720 }
1721 else if (flButtons == MB_RETRYCANCEL)
1722 {
1723 p0 = pStrings->pcszRetry;
1724 p1 = pStrings->pcszCancel;
1725 }
1726 else if (flButtons == MB_ABORTRETRYIGNORE)
1727 {
1728 p0 = pStrings->pcszAbort;
1729 p1 = pStrings->pcszRetry;
1730 p2 = pStrings->pcszIgnore;
1731 }
1732 else if (flButtons == MB_YESNO)
1733 {
1734 p0 = pStrings->pcszYes;
1735 p1 = pStrings->pcszNo;
1736 }
1737 else if (flButtons == MB_YESNOCANCEL)
1738 {
1739 p0 = pStrings->pcszYes;
1740 p1 = pStrings->pcszNo;
1741 p2 = pStrings->pcszCancel;
1742 }
1743 else if (flButtons == MB_CANCEL)
1744 {
1745 p0 = pStrings->pcszCancel;
1746 }
1747 else if (flButtons == MB_ENTER)
1748 {
1749 p0 = pStrings->pcszEnter;
1750 }
1751 else if (flButtons == MB_ENTERCANCEL)
1752 {
1753 p0 = pStrings->pcszEnter;
1754 p1 = pStrings->pcszCancel;
1755 }
1756 else if (flButtons == MB_YES_YES2ALL_NO)
1757 {
1758 p0 = pStrings->pcszYes;
1759 p1 = pStrings->pcszYesToAll;
1760 p2 = pStrings->pcszNo;
1761 }
1762
1763 // now set strings and hide empty buttons
1764 Buttons[0].pcszText = p0;
1765
1766 if (p1)
1767 Buttons[1].pcszText = p1;
1768 else
1769 Buttons[1].flStyle &= ~WS_VISIBLE;
1770
1771 if (p2)
1772 Buttons[2].pcszText = p2;
1773 else
1774 Buttons[2].flStyle &= ~WS_VISIBLE;
1775
1776 // query default button IDs
1777 if (flFlags & MB_DEFBUTTON2)
1778 Buttons[1].flStyle |= BS_DEFAULT;
1779 else if (flFlags & MB_DEFBUTTON3)
1780 Buttons[2].flStyle |= BS_DEFAULT;
1781 else
1782 Buttons[0].flStyle |= BS_DEFAULT;
1783
1784 *pulAlarmFlag = WA_NOTE;
1785 if (flFlags & (MB_ICONHAND | MB_ERROR))
1786 *pulAlarmFlag = WA_ERROR;
1787 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
1788 *pulAlarmFlag = WA_WARNING;
1789
1790 return (dlghCreateDlg(phwndDlg,
1791 hwndOwner,
1792 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
1793 WinDefDlgProc,
1794 pcszTitle,
1795 MessageBox,
1796 ARRAYITEMCOUNT(MessageBox),
1797 NULL,
1798 pcszFont));
1799}
1800
1801/*
1802 *@@ dlghProcessMessageBox:
1803 *
1804 *@@added V0.9.13 (2001-06-21) [umoeller]
1805 */
1806
1807ULONG dlghProcessMessageBox(HWND hwndDlg,
1808 ULONG ulAlarmFlag,
1809 ULONG flFlags)
1810{
1811 ULONG ulrcDlg;
1812 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
1813
1814 winhCenterWindow(hwndDlg);
1815
1816 if (flFlags & MB_SYSTEMMODAL)
1817 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
1818
1819 if (ulAlarmFlag)
1820 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
1821
1822 ulrcDlg = WinProcessDlg(hwndDlg);
1823
1824 WinDestroyWindow(hwndDlg);
1825
1826 if (flButtons == MB_OK)
1827 return MBID_OK;
1828 else if (flButtons == MB_OKCANCEL)
1829 switch (ulrcDlg)
1830 {
1831 case 1: return MBID_OK;
1832 default: return MBID_CANCEL;
1833 }
1834 else if (flButtons == MB_RETRYCANCEL)
1835 switch (ulrcDlg)
1836 {
1837 case 1: return MBID_RETRY;
1838 default: return MBID_CANCEL;
1839 }
1840 else if (flButtons == MB_ABORTRETRYIGNORE)
1841 switch (ulrcDlg)
1842 {
1843 case 2: return MBID_RETRY;
1844 case 3: return MBID_IGNORE;
1845 default: return MBID_ABORT;
1846 }
1847 else if (flButtons == MB_YESNO)
1848 switch (ulrcDlg)
1849 {
1850 case 1: return MBID_YES;
1851 default: return MBID_NO;
1852 }
1853 else if (flButtons == MB_YESNOCANCEL)
1854 switch (ulrcDlg)
1855 {
1856 case 1: return MBID_YES;
1857 case 2: return MBID_NO;
1858 default: return MBID_CANCEL;
1859 }
1860 else if (flButtons == MB_CANCEL)
1861 return MBID_CANCEL;
1862 else if (flButtons == MB_ENTER)
1863 return MBID_ENTER;
1864 else if (flButtons == MB_ENTERCANCEL)
1865 switch (ulrcDlg)
1866 {
1867 case 1: return MBID_ENTER;
1868 default: return MBID_CANCEL;
1869 }
1870 else if (flButtons == MB_YES_YES2ALL_NO)
1871 switch (ulrcDlg)
1872 {
1873 case 1: return MBID_YES;
1874 case 2: return MBID_YES2ALL;
1875 default: return MBID_NO;
1876 }
1877
1878 return (MBID_CANCEL);
1879}
1880
1881/*
1882 *@@ dlghMessageBox:
1883 * WinMessageBox replacement.
1884 *
1885 * This has all the flags of the standard call,
1886 * but looks much prettier. Besides, it allows
1887 * you to specify any icon to be displayed.
1888 *
1889 * Currently the following flStyle's are supported:
1890 *
1891 * -- MB_OK 0x0000
1892 * -- MB_OKCANCEL 0x0001
1893 * -- MB_RETRYCANCEL 0x0002
1894 * -- MB_ABORTRETRYIGNORE 0x0003
1895 * -- MB_YESNO 0x0004
1896 * -- MB_YESNOCANCEL 0x0005
1897 * -- MB_CANCEL 0x0006
1898 * -- MB_ENTER 0x0007 (not implemented yet)
1899 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
1900 *
1901 * -- MB_YES_YES2ALL_NO 0x0009
1902 * This is new: this has three buttons called "Yes"
1903 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
1904 *
1905 * -- MB_DEFBUTTON2 (for two-button styles)
1906 * -- MB_DEFBUTTON3 (for three-button styles)
1907 *
1908 * -- MB_ICONHAND
1909 * -- MB_ICONEXCLAMATION
1910 *
1911 * Returns MBID_* codes like WinMessageBox.
1912 *
1913 *@@added V0.9.13 (2001-06-21) [umoeller]
1914 */
1915
1916ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
1917 HPOINTER hptrIcon, // in: icon to display
1918 const char *pcszTitle, // in: title
1919 const char *pcszMessage, // in: message
1920 ULONG flFlags, // in: standard message box flags
1921 const char *pcszFont, // in: font (e.g. "9.WarpSans")
1922 const MSGBOXSTRINGS *pStrings) // in: strings array
1923{
1924 HWND hwndDlg;
1925 ULONG ulAlarmFlag;
1926 APIRET arc = dlghCreateMessageBox(&hwndDlg,
1927 hwndOwner,
1928 hptrIcon,
1929 pcszTitle,
1930 pcszMessage,
1931 flFlags,
1932 pcszFont,
1933 pStrings,
1934 &ulAlarmFlag);
1935
1936 if (!arc && hwndDlg)
1937 {
1938 // SHOW DIALOG
1939 return (dlghProcessMessageBox(hwndDlg,
1940 ulAlarmFlag,
1941 flFlags));
1942 }
1943 else
1944 {
1945 CHAR szMsg[100];
1946 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
1947 WinMessageBox(HWND_DESKTOP,
1948 NULLHANDLE,
1949 "Error",
1950 szMsg,
1951 0,
1952 MB_CANCEL | MB_MOVEABLE);
1953 }
1954
1955 return (DID_CANCEL);
1956}
1957
1958/*
1959 *@@ dlghSetPrevFocus:
1960 * "backward" function for rotating the focus
1961 * in a dialog when the "shift+tab" keys get
1962 * pressed.
1963 *
1964 * pllWindows must be a linked list with the
1965 * plain HWND window handles of the focussable
1966 * controls in the dialog.
1967 */
1968
1969VOID dlghSetPrevFocus(PVOID pvllWindows)
1970{
1971 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
1972
1973 // check current focus
1974 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
1975
1976 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
1977
1978 BOOL fRestart = FALSE;
1979
1980 while (pNode)
1981 {
1982 CHAR szClass[100];
1983
1984 // previos node
1985 pNode = pNode->pPrevious;
1986
1987 if ( (!pNode) // no next, or not found:
1988 && (!fRestart) // avoid infinite looping if bad list
1989 )
1990 {
1991 pNode = lstQueryLastNode(pllWindows);
1992 fRestart = TRUE;
1993 }
1994
1995 if (pNode)
1996 {
1997 // check if this is a focusable control
1998 if (WinQueryClassName((HWND)pNode->pItemData,
1999 sizeof(szClass),
2000 szClass))
2001 {
2002 if ( (strcmp(szClass, "#5")) // not static
2003 )
2004 break;
2005 // else: go for next then
2006 }
2007 }
2008 }
2009
2010 if (pNode)
2011 {
2012 WinSetFocus(HWND_DESKTOP,
2013 (HWND)pNode->pItemData);
2014 }
2015}
2016
2017/*
2018 *@@ dlghSetNextFocus:
2019 * "forward" function for rotating the focus
2020 * in a dialog when the "ab" key gets pressed.
2021 *
2022 * pllWindows must be a linked list with the
2023 * plain HWND window handles of the focussable
2024 * controls in the dialog.
2025 */
2026
2027VOID dlghSetNextFocus(PVOID pvllWindows)
2028{
2029 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2030
2031 // check current focus
2032 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
2033
2034 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
2035
2036 BOOL fRestart = FALSE;
2037
2038 while (pNode)
2039 {
2040 CHAR szClass[100];
2041
2042 // next focus in node
2043 pNode = pNode->pNext;
2044
2045 if ( (!pNode) // no next, or not found:
2046 && (!fRestart) // avoid infinite looping if bad list
2047 )
2048 {
2049 pNode = lstQueryFirstNode(pllWindows);
2050 fRestart = TRUE;
2051 }
2052
2053 if (pNode)
2054 {
2055 // check if this is a focusable control
2056 if (WinQueryClassName((HWND)pNode->pItemData,
2057 sizeof(szClass),
2058 szClass))
2059 {
2060 if ( (strcmp(szClass, "#5")) // not static
2061 )
2062 break;
2063 // else: go for next then
2064 }
2065 }
2066 }
2067
2068 if (pNode)
2069 {
2070 WinSetFocus(HWND_DESKTOP,
2071 (HWND)pNode->pItemData);
2072 }
2073}
2074
2075/*
2076 *@@ MatchMnemonic:
2077 * returns TRUE if the specified control matches
2078 *
2079 *
2080 *@@added V0.9.9 (2001-03-17) [umoeller]
2081 */
2082
2083/*
2084 *@@ dlghProcessMnemonic:
2085 * finds the control which matches usch
2086 * and gives it the focus. If this is a
2087 * static control, the next control in the
2088 * list is given focus instead. (Standard
2089 * dialog behavior.)
2090 *
2091 * Pass in usch from WM_CHAR. It is assumed
2092 * that the caller has already tested for
2093 * the "alt" key to be depressed.
2094 *
2095 *@@added V0.9.9 (2001-03-17) [umoeller]
2096 */
2097
2098HWND dlghProcessMnemonic(PVOID pvllWindows,
2099 USHORT usch)
2100{
2101 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2102
2103 HWND hwndFound = NULLHANDLE;
2104 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2105 CHAR szClass[100];
2106
2107 while (pNode)
2108 {
2109 HWND hwnd = (HWND)pNode->pItemData;
2110
2111 if (WinSendMsg(hwnd,
2112 WM_MATCHMNEMONIC,
2113 (MPARAM)usch,
2114 0))
2115 {
2116 // according to the docs, only buttons and static
2117 // return TRUE to that msg;
2118 // if this is a static, give focus to the next
2119 // control
2120
2121 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
2122
2123 // check if this is a focusable control
2124 if (WinQueryClassName(hwnd,
2125 sizeof(szClass),
2126 szClass))
2127 {
2128 if (!strcmp(szClass, "#3"))
2129 // it's a button: click it
2130 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
2131 else if (!strcmp(szClass, "#5"))
2132 {
2133 // it's a static: give focus to following control
2134 pNode = pNode->pNext;
2135 if (pNode)
2136 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
2137 }
2138 }
2139 else
2140 // any other control (are there any?): give them focus
2141 WinSetFocus(HWND_DESKTOP, hwnd);
2142
2143 // in any case, stop
2144 hwndFound = hwnd;
2145 break;
2146 }
2147
2148 pNode = pNode->pNext;
2149 }
2150
2151 return (hwndFound);
2152}
2153
2154/*
2155 *@@ dlghEnter:
2156 * presses the first button with BS_DEFAULT.
2157 */
2158
2159BOOL dlghEnter(PVOID pvllWindows)
2160{
2161 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2162
2163 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2164 CHAR szClass[100];
2165 while (pNode)
2166 {
2167 HWND hwnd = (HWND)pNode->pItemData;
2168 if (WinQueryClassName(hwnd,
2169 sizeof(szClass),
2170 szClass))
2171 {
2172 if (!strcmp(szClass, "#3")) // button
2173 {
2174 // _Pmpf((__FUNCTION__ ": found button"));
2175 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
2176 == (BS_PUSHBUTTON | BS_DEFAULT)
2177 )
2178 {
2179 // _Pmpf((" is default!"));
2180 WinPostMsg(hwnd,
2181 BM_CLICK,
2182 (MPARAM)TRUE, // upclick
2183 0);
2184 return (TRUE);
2185 }
2186 }
2187 }
2188
2189 pNode = pNode->pNext;
2190 }
2191
2192 return (FALSE);
2193}
2194
2195
Note: See TracBrowser for help on using the repository browser.