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

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

Misc changes

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