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

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

Misc helpers updates.

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