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

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

Lots of updates from the last week for conditional compiles and other stuff.

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