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

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

Misc changes.

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