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

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

Misc updates.

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