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

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