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

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

Dialog mgr.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 46.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 * Usage: All PM programs.
10 *
11 * Function prefixes (new with V0.81):
12 * -- dlg* Dialog functions
13 *
14 * Note: Version numbering in this file relates to XWorkplace version
15 * numbering.
16 *
17 *@@header "helpers\dialog.h"
18 */
19
20/*
21 * Copyright (C) 2001 Ulrich M”ller.
22 * This file is part of the "XWorkplace helpers" source package.
23 * This is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published
25 * by the Free Software Foundation, in version 2 as it comes in the
26 * "COPYING" file of the XWorkplace main distribution.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 */
32
33#define OS2EMX_PLAIN_CHAR
34 // this is needed for "os2emx.h"; if this is defined,
35 // emx will define PSZ as _signed_ char, otherwise
36 // as unsigned char
37
38#define INCL_DOSERRORS
39
40#define INCL_WINWINDOWMGR
41#define INCL_WINFRAMEMGR
42#define INCL_WINDIALOGS
43#define INCL_WINBUTTONS
44#define INCL_WINSTATICS
45#define INCL_WINSYS
46
47#define INCL_GPIPRIMITIVES
48#define INCL_GPIBITMAPS
49#define INCL_GPILCIDS
50#include <os2.h>
51
52#include <stdlib.h>
53#include <string.h>
54#include <stdio.h>
55
56#include "setup.h" // code generation and debugging options
57
58#include "helpers\comctl.h"
59#include "helpers\dialog.h"
60#include "helpers\gpih.h"
61#include "helpers\linklist.h"
62#include "helpers\standards.h"
63#include "helpers\stringh.h"
64#include "helpers\winh.h"
65
66/*
67 *@@category: Helpers\PM helpers\Dialog templates
68 */
69
70/* ******************************************************************
71 *
72 * Private declarations
73 *
74 ********************************************************************/
75
76/*
77 *@@ DLGPRIVATE:
78 * private data to the dlg manager, allocated
79 * by dlghCreateDlg. This is what is really
80 * used, even though the prototype only
81 * declares DIALOGDATA.
82 *
83 *@@added V0.9.9 (2001-04-01) [umoeller]
84 */
85
86typedef struct _DLGPRIVATE
87{
88 // public data
89 HWND hwndDlg;
90
91 // definition data (private)
92 LINKLIST llTables;
93
94 HWND hwndFirstFocus;
95
96 POINTL ptlTotalOfs;
97
98 LINKLIST llControls; // linked list of all PCOLUMNDEF structs,
99 // in the order in which windows were
100 // created
101
102 const char *pcszControlsFont; // from dlghCreateDlg
103
104 HPS hps;
105 const char *pcszFontLast;
106 LONG lcidLast;
107} DLGPRIVATE, *PDLGPRIVATE;
108
109typedef struct _COLUMNDEF *PCOLUMNDEF;
110typedef struct _ROWDEF *PROWDEF;
111typedef struct _TABLEDEF *PTABLEDEF;
112
113/*
114 *@@ CONTROLPOS:
115 * control position. We don't want to use SWP.
116 *
117 *@@added V0.9.9 (2001-04-01) [umoeller]
118 */
119
120typedef struct _CONTROLPOS
121{
122 LONG x,
123 y,
124 cx,
125 cy;
126} CONTROLPOS, *PCONTROLPOS;
127
128/*
129 *@@ COLUMNDEF:
130 * representation of a table column.
131 * This is stored in a linked list in ROWDEF.
132 *
133 * A table column represents either a PM control
134 * window or another table, which may therefore
135 * be nested.
136 *
137 *@@added V0.9.9 (2001-04-01) [umoeller]
138 */
139
140typedef struct _COLUMNDEF
141{
142 PROWDEF pOwningRow; // row whose linked list this column belongs to
143
144 BOOL fIsNestedTable; // if TRUE, pvDefinition points to a nested TABLEDEF;
145 // if FALSE, pvDefinition points to a CONTROLDEF as
146 // specified by the user
147
148 PVOID pvDefinition; // either a PTABLEDEF or a PCONTROLDEF
149
150 CONTROLPOS cpControl, // real pos and size of control
151 cpColumn; // pos and size of column; can be wider, spacings applied
152
153 HWND hwndControl; // created control; NULLHANDLE for tables always
154
155} COLUMNDEF;
156
157/*
158 *@@ ROWDEF:
159 *
160 *@@added V0.9.9 (2001-04-01) [umoeller]
161 */
162
163typedef struct _ROWDEF
164{
165 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
166
167 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
168
169 CONTROLPOS cpRow;
170
171} ROWDEF;
172
173/*
174 *@@ TABLEDEF:
175 *
176 *@@added V0.9.9 (2001-04-01) [umoeller]
177 */
178
179typedef struct _TABLEDEF
180{
181 LINKLIST llRows; // contains ROWDEF structs, no auto-free
182
183 PCONTROLDEF pCtlDef; // if != NULL, we create a PM control around the table
184
185 CONTROLPOS cpTable;
186
187} TABLEDEF;
188
189/*
190 *@@ PROCESSMODE:
191 *
192 *@@added V0.9.9 (2001-04-01) [umoeller]
193 */
194
195typedef enum _PROCESSMODE
196{
197 PROCESS_CALC_SIZES, // step 1
198 PROCESS_CALC_POSITIONS, // step 2
199 PROCESS_CREATE_CONTROLS // step 3
200} PROCESSMODE;
201
202/* ******************************************************************
203 *
204 * Worker routines
205 *
206 ********************************************************************/
207
208#define PM_GROUP_SPACING_X 20
209#define PM_GROUP_SPACING_TOP 30
210
211VOID ProcessTable(PTABLEDEF pTableDef,
212 const CONTROLPOS *pcpTable,
213 PROCESSMODE ProcessMode,
214 PDLGPRIVATE pDlgData);
215
216/*
217 *@@ CalcAutoSizeText:
218 *
219 *@@added V0.9.9 (2001-04-01) [umoeller]
220 */
221
222VOID CalcAutoSizeText(PCONTROLDEF pControlDef,
223 PSIZEL pszlAuto, // out: computed size
224 PDLGPRIVATE pDlgData)
225{
226 BOOL fFind = TRUE;
227 RECTL rclText;
228
229 const char *pcszFontThis = pControlDef->pcszFont;
230 // can be NULL,
231 // or CTL_COMMON_FONT
232
233 if (pcszFontThis == CTL_COMMON_FONT)
234 pcszFontThis = pDlgData->pcszControlsFont;
235
236 if (!pDlgData->hps)
237 {
238 // first call: get a PS
239 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
240 fFind = TRUE;
241 }
242 else
243 if (strhcmp(pcszFontThis, pDlgData->pcszFontLast))
244 fFind = TRUE;
245
246 if (fFind)
247 {
248 FONTMETRICS fm;
249 LONG lcid;
250 LONG lPointSize = 0;
251 if (pDlgData->lcidLast)
252 {
253 // delete old font
254 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
255 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
256 }
257
258 // create new font
259 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
260 FALSE,
261 pDlgData->hps,
262 pcszFontThis,
263 &fm,
264 &lPointSize);
265 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
266 if (fm.fsDefn & FM_DEFN_OUTLINE)
267 gpihSetPointSize(pDlgData->hps, lPointSize);
268
269 pszlAuto->cy = fm.lMaxBaselineExt + fm.lExternalLeading;
270 }
271
272 // ok, we FINALLY have a font now...
273 // get the control string and see how much space it needs
274 if (pControlDef->pcszText)
275 {
276 POINTL aptl[TXTBOX_COUNT];
277 GpiQueryTextBox(pDlgData->hps,
278 strlen(pControlDef->pcszText),
279 (PCH)pControlDef->pcszText,
280 TXTBOX_COUNT,
281 aptl);
282 pszlAuto->cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
283 }
284}
285
286/*
287 *@@ CalcAutoSize:
288 *
289 *@@added V0.9.9 (2001-04-01) [umoeller]
290 */
291
292VOID CalcAutoSize(PCONTROLDEF pControlDef,
293 PSIZEL pszlAuto, // out: computed size
294 PDLGPRIVATE pDlgData)
295{
296 // dumb defaults
297 pszlAuto->cx = 100;
298 pszlAuto->cy = 30;
299
300 switch ((ULONG)pControlDef->pcszClass)
301 {
302 case 0xffff0003L: // WC_BUTTON:
303 CalcAutoSizeText(pControlDef,
304 pszlAuto,
305 pDlgData);
306 if (pControlDef->flStyle & ( BS_AUTOCHECKBOX
307 | BS_AUTORADIOBUTTON
308 | BS_AUTO3STATE
309 | BS_3STATE
310 | BS_CHECKBOX
311 | BS_RADIOBUTTON))
312 pszlAuto->cx += 20; // @@@
313 else if (pControlDef->flStyle & BS_BITMAP)
314 ;
315 else if (pControlDef->flStyle & (BS_ICON | BS_MINIICON))
316 ;
317 // we can't test for BS_PUSHBUTTON because that's 0x0000
318 else if (!(pControlDef->flStyle & BS_USERBUTTON))
319 {
320 pszlAuto->cx += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) + 15);
321 pszlAuto->cy += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) + 15);
322 }
323 break;
324
325 case 0xffff0005L: // WC_STATIC:
326 if (pControlDef->flStyle & SS_TEXT)
327 CalcAutoSizeText(pControlDef,
328 pszlAuto,
329 pDlgData);
330 else if (pControlDef->flStyle & SS_BITMAP)
331 {
332 HBITMAP hbm = (HBITMAP)pControlDef->pcszText;
333 if (hbm)
334 {
335 BITMAPINFOHEADER2 bmih2;
336 ZERO(&bmih2);
337 bmih2.cbFix = sizeof(bmih2);
338 if (GpiQueryBitmapInfoHeader(hbm,
339 &bmih2))
340 {
341 pszlAuto->cx = bmih2.cx;
342 pszlAuto->cy = bmih2.cy;
343 }
344 }
345 }
346 break;
347 }
348}
349
350/*
351 *@@ ProcessColumn:
352 * processes a column, which per definition is either
353 * a control or a nested subtable.
354 *
355 * A column is part of a row, which in turn is part
356 * of a table. There can be several columns in a row,
357 * and several rows in a table.
358 *
359 * Since tables may be specified as columns, it is
360 * possible to produce complex dialog layouts by
361 * nesting tables.
362 *
363 * This does the following:
364 *
365 * -- PROCESS_CALC_SIZES: size is taken from control def,
366 * or for tables, this produces a recursive ProcessTable
367 * call.
368 * Preconditions: none.
369 *
370 * -- PROCESS_CALC_POSITIONS: position of each column
371 * is taken from *plX, which is increased by the
372 * column width by this call.
373 *
374 * Preconditions: Owning row must already have its
375 * y position properly set, or we can't compute
376 * ours. Besides, plX must point to the current X
377 * in the row and will be incremented by the columns
378 * size here.
379 *
380 * -- PROCESS_CREATE_CONTROLS: well, creates the controls.
381 *
382 * For tables, this recurses again. If the table has
383 * a string assigned, this also produces a group box
384 * after the recursion.
385 *
386 *@@added V0.9.9 (2001-04-01) [umoeller]
387 */
388
389VOID ProcessColumn(PCOLUMNDEF pColumnDef,
390 PROWDEF pOwningRow,
391 PROCESSMODE ProcessMode,
392 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
393 PDLGPRIVATE pDlgData)
394{
395 pColumnDef->pOwningRow = pOwningRow;
396
397 // for PROCESS_CALC_SIZES: have control return its size
398 // plus spacings into szlControl
399 // for PROCESS_CALC_POSITIONS: have control compute its
400 // position from the column position (there may be
401 // spacings)
402 // for PROCESS_CREATE_CONTROLS: well, create the control
403 switch (ProcessMode)
404 {
405 case PROCESS_CALC_SIZES:
406 {
407 ULONG ulXSpacing = 0,
408 ulYSpacing = 0;
409 if (pColumnDef->fIsNestedTable)
410 {
411 // nested table: recurse!!
412 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
413 ProcessTable(pTableDef,
414 NULL,
415 ProcessMode,
416 pDlgData);
417
418 // store the size of the sub-table
419 pColumnDef->cpControl.cx = pTableDef->cpTable.cx;
420 pColumnDef->cpControl.cy = pTableDef->cpTable.cy;
421
422 // should we create a PM control around the table?
423 if (pTableDef->pCtlDef)
424 {
425 // yes: make this wider
426 ulXSpacing = (2 * PM_GROUP_SPACING_X);
427 ulYSpacing = (PM_GROUP_SPACING_X + PM_GROUP_SPACING_TOP);
428 }
429 }
430 else
431 {
432 // no nested table, but control:
433 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
434 PSIZEL pszl = &pControlDef->szlControlProposed;
435 SIZEL szlAuto;
436
437 if ( (pszl->cx == -1)
438 || (pszl->cy == -1)
439 )
440 {
441 CalcAutoSize(pControlDef,
442 &szlAuto,
443 pDlgData);
444 }
445
446 if (pszl->cx == -1)
447 pColumnDef->cpControl.cx = szlAuto.cx;
448 else
449 pColumnDef->cpControl.cx = pszl->cx;
450
451 if (pszl->cy == -1)
452 pColumnDef->cpControl.cy = szlAuto.cy;
453 else
454 pColumnDef->cpControl.cy = pszl->cy;
455
456 // @@@hack sizes
457
458 ulXSpacing = ulYSpacing = (2 * pControlDef->ulSpacing);
459 }
460
461 pColumnDef->cpColumn.cx = pColumnDef->cpControl.cx
462 + ulXSpacing;
463 pColumnDef->cpColumn.cy = pColumnDef->cpControl.cy
464 + ulYSpacing;
465 break; }
466
467 case PROCESS_CALC_POSITIONS:
468 {
469 // calculate column position: this includes spacing
470 ULONG ulSpacing = 0;
471
472 // column position = *plX
473 pColumnDef->cpColumn.x = *plX;
474 pColumnDef->cpColumn.y = pOwningRow->cpRow.y;
475
476 if (pColumnDef->fIsNestedTable)
477 {
478 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
479 // should we create a PM control around the table?
480 if (pTableDef->pCtlDef)
481 // yes:
482 ulSpacing = PM_GROUP_SPACING_X;
483 }
484 else
485 {
486 // no nested table, but control:
487 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
488 ulSpacing = pControlDef->ulSpacing;
489 }
490
491 // increase plX by column width
492 *plX += pColumnDef->cpColumn.cx;
493
494 // calculate control pos by applying spacing
495 pColumnDef->cpControl.x = pColumnDef->cpColumn.x
496 + ulSpacing;
497 pColumnDef->cpControl.y = pColumnDef->cpColumn.y
498 + ulSpacing;
499
500 if (pColumnDef->fIsNestedTable)
501 {
502 // nested table:
503 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
504
505 // recurse!! to create windows for the sub-table
506 ProcessTable(pTableDef,
507 &pColumnDef->cpControl, // start pos for new table
508 ProcessMode,
509 pDlgData);
510 }
511 break; }
512
513 case PROCESS_CREATE_CONTROLS:
514 {
515 PCONTROLPOS pcp = NULL;
516 PCONTROLDEF pControlDef = NULL;
517 const char *pcszTitle = NULL;
518 ULONG flStyle = 0;
519 LHANDLE lHandleSet = NULLHANDLE;
520
521 if (pColumnDef->fIsNestedTable)
522 {
523 // nested table:
524 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
525
526 // recurse!!
527 ProcessTable(pTableDef,
528 NULL,
529 ProcessMode,
530 pDlgData);
531
532 // should we create a PM control around the table?
533 // (do this AFTER the other controls from recursing,
534 // otherwise the stupid container doesn't show up)
535 if (pTableDef->pCtlDef)
536 {
537 // yes:
538 pcp = &pColumnDef->cpColumn; // !! not control
539 pControlDef = pTableDef->pCtlDef;
540 pcszTitle = pControlDef->pcszText;
541 flStyle = pControlDef->flStyle;
542 }
543 }
544 else
545 {
546 // no nested table, but control:
547 pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
548 pcp = &pColumnDef->cpControl;
549 pcszTitle = pControlDef->pcszText;
550 flStyle = pControlDef->flStyle;
551
552 // change the title if this is a static with SS_BITMAP;
553 // we have used a HBITMAP in there!
554 if ( ((ULONG)pControlDef->pcszClass == 0xffff0005L) // WC_STATIC:
555 && (flStyle & SS_BITMAP)
556 )
557 {
558 // change style flag to not use SS_BITMAP;
559 // control creation fails otherwise (stupid, stupid PM)
560 flStyle = ((flStyle & ~SS_BITMAP) | SS_FGNDFRAME);
561 pcszTitle = "";
562 lHandleSet = (LHANDLE)pControlDef->pcszText;
563 }
564 }
565
566 if (pcp && pControlDef)
567 {
568 // create something:
569 PPRESPARAMS ppp = NULL;
570
571 const char *pcszFont = pControlDef->pcszFont;
572 // can be NULL, or CTL_COMMON_FONT
573 if (pcszFont == CTL_COMMON_FONT)
574 pcszFont = pDlgData->pcszControlsFont;
575
576 if (pcszFont)
577 winhStorePresParam(&ppp,
578 PP_FONTNAMESIZE,
579 strlen(pcszFont),
580 (PVOID)pcszFont);
581
582 pColumnDef->hwndControl
583 = WinCreateWindow(pDlgData->hwndDlg, // parent
584 (PSZ)pControlDef->pcszClass,
585 (pcszTitle) // hacked
586 ? (PSZ)pcszTitle
587 : "",
588 flStyle, // hacked
589 pcp->x + pDlgData->ptlTotalOfs.x,
590 pcp->y + pDlgData->ptlTotalOfs.y,
591 pcp->cx,
592 pcp->cy,
593 pDlgData->hwndDlg, // owner
594 HWND_BOTTOM,
595 pControlDef->usID,
596 NULL, // control data @@@
597 ppp);
598
599 if (pColumnDef->hwndControl && lHandleSet)
600 {
601 // subclass the damn static
602 ctlPrepareStretchedBitmap(pColumnDef->hwndControl,
603 TRUE);
604
605 WinSendMsg(pColumnDef->hwndControl,
606 SM_SETHANDLE,
607 (MPARAM)lHandleSet,
608 0);
609 }
610
611 if (ppp)
612 free(ppp);
613
614 if (pColumnDef->hwndControl)
615 {
616 lstAppendItem(&pDlgData->llControls,
617 pColumnDef);
618
619 // if this is the first control with WS_TABSTOP,
620 // we give it the focus later
621 if ( (flStyle & WS_TABSTOP)
622 && (!pDlgData->hwndFirstFocus)
623 )
624 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
625 }
626 }
627 break; }
628 }
629
630}
631
632/*
633 *@@ ProcessRow:
634 *
635 *@@added V0.9.9 (2001-04-01) [umoeller]
636 */
637
638VOID ProcessRow(PROWDEF pRowDef,
639 PTABLEDEF pOwningTable,
640 PROCESSMODE ProcessMode,
641 PLONG plY,
642 PDLGPRIVATE pDlgData)
643{
644 ULONG ul;
645 LONG lX;
646 PLISTNODE pNode;
647
648 pRowDef->pOwningTable = pOwningTable;
649
650 if (ProcessMode == PROCESS_CALC_SIZES)
651 {
652 pRowDef->cpRow.cx = 0;
653 pRowDef->cpRow.cy = 0;
654 }
655 else if (ProcessMode == PROCESS_CALC_POSITIONS)
656 {
657 // set up x and y so that the columns can
658 // base on that
659 pRowDef->cpRow.x = pOwningTable->cpTable.x;
660 // decrease y by row height
661 *plY -= pRowDef->cpRow.cy;
662 // and use that for our bottom position
663 pRowDef->cpRow.y = *plY;
664
665 // set lX to left of row; used by column calls below
666 lX = pRowDef->cpRow.x;
667 }
668
669 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
670 {
671 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
672
673 ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData);
674
675 if (ProcessMode == PROCESS_CALC_SIZES)
676 {
677 // row width = sum of all columns
678 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
679
680 // row height = maximum height of a column
681 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
682 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
683 }
684 }
685}
686
687/*
688 *@@ ProcessTable:
689 *
690 * This routine is a bit sick because it can be
691 * called recursively if a nested table is found
692 * in a COLUMNDEF.
693 *
694 * With PROCESS_CALC_POSITIONS, pptl must specify
695 * the lower left corner of the table. For the
696 * root call, this will be {0, 0}; for nested calls,
697 * this must be the lower left corner of the column
698 * to which the nested table belongs.
699 *
700 *@@added V0.9.9 (2001-04-01) [umoeller]
701 */
702
703VOID ProcessTable(PTABLEDEF pTableDef,
704 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
705 PROCESSMODE ProcessMode,
706 PDLGPRIVATE pDlgData)
707{
708 ULONG ul;
709 LONG lY;
710 PLISTNODE pNode;
711
712 if (ProcessMode == PROCESS_CALC_SIZES)
713 {
714 pTableDef->cpTable.cx = 0;
715 pTableDef->cpTable.cy = 0;
716 }
717 else if (ProcessMode == PROCESS_CALC_POSITIONS)
718 {
719 pTableDef->cpTable.x = pcpTable->x;
720 pTableDef->cpTable.y = pcpTable->y;
721
722 // start the rows on top
723 lY = pcpTable->y + pTableDef->cpTable.cy;
724 }
725
726 FOR_ALL_NODES(&pTableDef->llRows, pNode)
727 {
728 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
729
730 ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData);
731
732 if (ProcessMode == PROCESS_CALC_SIZES)
733 {
734 // table width = maximum width of a row
735 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
736 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
737
738 // table height = sum of all rows
739 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
740 }
741 }
742}
743
744/*
745 *@@ ProcessAll:
746 *
747 * -- PROCESS_CALC_SIZES: calculates the sizes
748 * of all tables, rows, columns, and controls.
749 *
750 * -- PROCESS_CALC_POSITIONS: calculates the positions
751 * based on the sizes calculated before.
752 *
753 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
754 * positions and sizes calculated before.
755 *
756 *@@added V0.9.9 (2001-04-01) [umoeller]
757 */
758
759VOID ProcessAll(PDLGPRIVATE pDlgData,
760 PSIZEL pszlClient,
761 PROCESSMODE ProcessMode)
762{
763 ULONG ul;
764 PLISTNODE pNode;
765 CONTROLPOS cpTable;
766 ZERO(&cpTable);
767
768 switch (ProcessMode)
769 {
770 case PROCESS_CALC_SIZES:
771 pszlClient->cx = 0;
772 pszlClient->cy = 0;
773 break;
774
775 case PROCESS_CALC_POSITIONS:
776 // start with the table on top
777 cpTable.y = pszlClient->cy;
778 break;
779 }
780
781 FOR_ALL_NODES(&pDlgData->llTables, pNode)
782 {
783 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
784
785 if (ProcessMode == PROCESS_CALC_POSITIONS)
786 {
787 cpTable.x = 0;
788 cpTable.y -= pTableDefThis->cpTable.cy;
789 }
790
791 ProcessTable(pTableDefThis,
792 &cpTable, // start pos
793 ProcessMode,
794 pDlgData);
795
796 if (ProcessMode == PROCESS_CALC_SIZES)
797 {
798 pszlClient->cx += pTableDefThis->cpTable.cx;
799 pszlClient->cy += pTableDefThis->cpTable.cy;
800 }
801 }
802}
803
804/*
805 *@@ CreateColumn:
806 *
807 *@@added V0.9.9 (2001-04-01) [umoeller]
808 */
809
810APIRET CreateColumn(PROWDEF pCurrentRow,
811 BOOL fIsNestedTable,
812 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
813 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
814{
815 APIRET arc = NO_ERROR;
816
817 if (!pCurrentRow)
818 arc = DLGERR_CONTROL_BEFORE_ROW;
819 else
820 {
821 // append the control def
822 if (!pvDefinition)
823 arc = DLGERR_NULL_CTL_DEF;
824 else
825 {
826 // create column and store ctl def
827 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
828 if (!pColumnDef)
829 arc = ERROR_NOT_ENOUGH_MEMORY;
830 else
831 {
832 memset(pColumnDef, 0, sizeof(COLUMNDEF));
833 pColumnDef->pOwningRow = pCurrentRow;
834 pColumnDef->fIsNestedTable = fIsNestedTable;
835 pColumnDef->pvDefinition = pvDefinition;
836
837 *ppColumnDef = pColumnDef;
838 }
839 }
840 }
841
842 return (arc);
843}
844
845/* ******************************************************************
846 *
847 * Public APIs
848 *
849 ********************************************************************/
850
851/*
852 *@@ STACKITEM:
853 *
854 *@@added V0.9.9 (2001-04-01) [umoeller]
855 */
856
857typedef struct _STACKITEM
858{
859 PTABLEDEF pLastTable;
860 PROWDEF pLastRow;
861
862} STACKITEM, *PSTACKITEM;
863
864/*
865 *@@ dlghCreateDlg:
866 * replacement for WinCreateDlg for creating a dialog
867 * from a settings array in memory, which is formatted
868 * automatically.
869 *
870 * This does NOT use regular dialog templates from
871 * module resources. Instead, you pass in an array
872 * of _DLGHITEM structures, which define the controls
873 * and how they are to be formatted.
874 *
875 * A regular standard dialog would use something like
876 *
877 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
878 *
879 * for flCreateFlags. To make the dlg sizeable, specify
880 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
881 *
882 * <B>Usage:</B>
883 *
884 * Like WinLoadDlg, this creates a standard WC_FRAME and
885 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
886 * to the dialog with pCreateParams in mp2. You can use
887 * WinProcessDlg as usual. In your dlg proc, use WinDefDlgProc
888 * as usual.
889 *
890 * There is NO run-time overhead after creation; after this
891 * function returns, the dialog is a standard dialog as if
892 * loaded from WinLoadDlg.
893 *
894 * The array of _DLGHITEM structures defines how the
895 * dialog is set up. All this is ONLY used by this function
896 * and NOT needed after the dialog has been created.
897 *
898 * In _DLGHITEM, the "Type" field determines what this
899 * structure defines. A number of handy macros have been
900 * defined to make this easier and to provide type-checking
901 * at compile time.
902 *
903 * The macros are:
904 *
905 * -- START_TABLE starts a new table. The tables may nest,
906 * but must each be properly terminated with END_TABLE.
907 *
908 * -- START_ROW(fl) starts a new row in a table.
909 *
910 * fl specifies formatting flags for the row. This
911 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
912 * ROW_VALIGN_TOP and affects all items in the control.
913 *
914 * -- START_GROUP_TABLE(pDef) starts a group. This
915 * behaves exacly like START_TABLE, but in addition,
916 * it produces a control around the table. Useful
917 * for group boxes. pDef must point to a _CONTROLDEF,
918 * whose size parameter is ignored.
919 *
920 * This must also be terminated with END_TABLE.
921 *
922 * -- CONTROL_DEF(pDef) defines a control in a table row.
923 * pDef must point to a _CONTROLDEF structure.
924 *
925 * The main difference (and advantage) of this
926 * function is that there is NO information in
927 * _CONTROLDEF about a control's _position_.
928 * Instead, the structure only contains the _size_
929 * of the control. All positions are computed by
930 * this function, depending on the position of the
931 * control and its nesting within the various tables.
932 *
933 * If you specify SZL_AUTOSIZE, the size of the
934 * control is even computed automatically. Presently,
935 * this only works for statics with SS_TEXT and
936 * SS_BITMAP.
937 *
938 * Unless separated with TYPE_START_NEW_ROW items,
939 * subsequent control items will be considered
940 * to be in the same row (== positioned next to
941 * each other).
942 *
943 * There are a few rules, whose violation will produce
944 * an error:
945 *
946 * -- The entire array must be enclosed in a table
947 * (the "root" table).
948 *
949 * -- After START_TABLE or START_GROUP_TABLE, there must
950 * always be a START_ROW first.
951 *
952 * To create a dialog, set up arrays like the following:
953 *
954 + // control definitions referenced by DlgTemplate:
955 + CONTROLDEF
956 + (1) GroupDef = {
957 + WC_STATIC, "", ....,
958 + { 0, 0 }, // size, ignored for groups
959 + 5 // spacing
960 + },
961 + (2) CnrDef = {
962 + WC_CONTAINER, "", ....,
963 + { 50, 50 }, // size
964 + 5 // spacing
965 + },
966 + (3) Static = {
967 + WC_STATIC, "Static below cnr", ...,
968 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
969 + 5 // spacing
970 + },
971 + (4) OKButton = {
972 + WC_STATIC, "~OK", ...,
973 + { 100, 30 }, // size
974 + 5 // spacing
975 + },
976 + (5) CancelButton = {
977 + WC_STATIC, "~Cancel", ...,
978 + { 100, 30 }, // size
979 + 5 // spacing
980 + };
981 +
982 + DLGHITEM DlgTemplate[] =
983 + {
984 + START_TABLE, // root table, must exist
985 + START_ROW(0), // row 1 in the root table
986 + // create group on top
987 + (1) START_GROUP_TABLE(&Group),
988 + START_ROW(0),
989 + (2) CONTROL_DEF(&CnrDef),
990 + START_ROW(0),
991 + (3) CONTROL_DEF(&Static),
992 + END_TABLE, // end of group
993 + START_ROW(0), // row 2 in the root table
994 + // two buttons next to each other
995 + (4) CONTROL_DEF(&OKButton),
996 + (5) CONTROL_DEF(&CancelButton),
997 + END_TABLE
998 + }
999 *
1000 * This will produce a dlg like this:
1001 *
1002 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1003 + º º
1004 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1005 + º ³ ³ º
1006 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1007 + º ³ ³ ³ ³ º
1008 + º ³ ³ Cnr inside group (2) ³ ³ º
1009 + º ³ ³ ³ ³ º
1010 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1011 + º ³ Static below cnr (3) ³ º
1012 + º ³ ³ º
1013 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1014 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1015 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1016 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1017 + º º
1018 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1019 *
1020 * <B>Errors:</B>
1021 *
1022 * This does not return a HWND, but an APIRET. This will be
1023 * one of the following:
1024 *
1025 * -- NO_ERROR: only in that case, the phwndDlg ptr
1026 * receives the HWND of the new dialog, which can
1027 * then be given to WinProcessDlg. Don't forget
1028 * WinDestroyWindow.
1029 *
1030 * -- ERROR_NOT_ENOUGH_MEMORY
1031 *
1032 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1033 * outside a table definition.
1034 *
1035 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1036 * appeared right after a table definition. You must
1037 * specify a row first.
1038 *
1039 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1040 * but the CONTROLDEF ptr was NULL.
1041 *
1042 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1043 * WC_FRAME window. Maybe an invalid owner was specified.
1044 *
1045 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1046 * DLGHITEM.
1047 *
1048 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1049 * improper nesting of TYPE_START_NEW_TABLE and
1050 * TYPE_END_TABLE fields.
1051 *
1052 * <B>Example:</B>
1053 *
1054 * The typical calling sequence would be:
1055 *
1056 + HWND hwndDlg = NULLHANDLE;
1057 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1058 + hwndOwner,
1059 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1060 + fnwpMyDlgProc,
1061 + "My Dlg Title",
1062 + G_aMyDialogTemplate,
1063 + ARRAYITEMSIZE(G_aMyDialogTemplate),
1064 + NULL))
1065 + {
1066 + ULONG idReturn = WinProcessDlg(pDlgData->hwndDlg);
1067 + WinDestroyWindow(hwndDlg);
1068 + }
1069 *
1070 *
1071 *@@added V0.9.9 (2001-03-30) [umoeller]
1072 */
1073
1074APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1075 HWND hwndOwner,
1076 ULONG flCreateFlags, // in: standard FCF_* frame flags
1077 PFNWP pfnwpDialogProc,
1078 const char *pcszDlgTitle,
1079 PDLGHITEM paDlgItems, // in: definition array
1080 ULONG cDlgItems, // in: array item count (NOT array size)
1081 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1082 const char *pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1083{
1084 APIRET arc = NO_ERROR;
1085
1086 #define SPACING 10
1087
1088 PTABLEDEF pCurrentTable = NULL;
1089 PROWDEF pCurrentRow = NULL;
1090 ULONG ul;
1091 LINKLIST llStack;
1092
1093 PDLGPRIVATE pDlgData = NEW(DLGPRIVATE);
1094
1095 if (!pDlgData)
1096 return (ERROR_NOT_ENOUGH_MEMORY);
1097
1098 ZERO(pDlgData);
1099 lstInit(&pDlgData->llTables, FALSE);
1100 lstInit(&pDlgData->llControls, FALSE);
1101
1102 pDlgData->pcszControlsFont = pcszControlsFont;
1103
1104 /*
1105 * 1) parse the table and create structures from it
1106 *
1107 */
1108
1109 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1110
1111 for (ul = 0;
1112 ul < cDlgItems;
1113 ul++)
1114 {
1115 PDLGHITEM pItemThis = &paDlgItems[ul];
1116
1117 switch (pItemThis->Type)
1118 {
1119 /*
1120 * TYPE_START_NEW_TABLE:
1121 *
1122 */
1123
1124 case TYPE_START_NEW_TABLE:
1125 {
1126 // root table or nested?
1127 BOOL fIsRoot = (pCurrentTable == NULL);
1128
1129 // push the current table on the stack
1130 PSTACKITEM pStackItem = NEW(STACKITEM);
1131 if (pStackItem)
1132 {
1133 pStackItem->pLastTable = pCurrentTable;
1134 pStackItem->pLastRow = pCurrentRow;
1135 lstPush(&llStack, pStackItem);
1136 }
1137
1138 // create new table
1139 pCurrentTable = NEW(TABLEDEF);
1140 if (!pCurrentTable)
1141 arc = ERROR_NOT_ENOUGH_MEMORY;
1142 else
1143 {
1144 ZERO(pCurrentTable);
1145
1146 lstInit(&pCurrentTable->llRows, FALSE);
1147
1148 if (pItemThis->ulData)
1149 // control specified: store it (this will become a PM group)
1150 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1151
1152 if (fIsRoot)
1153 // root table:
1154 // append to dialog data list
1155 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1156 else
1157 {
1158 // nested table:
1159 // create "table" column for this
1160 PCOLUMNDEF pColumnDef;
1161 arc = CreateColumn(pCurrentRow,
1162 TRUE, // nested table
1163 pCurrentTable,
1164 &pColumnDef);
1165 if (!arc)
1166 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1167 }
1168 }
1169
1170 pCurrentRow = NULL;
1171 break; }
1172
1173 /*
1174 * TYPE_START_NEW_ROW:
1175 *
1176 */
1177
1178 case TYPE_START_NEW_ROW:
1179 {
1180 if (!pCurrentTable)
1181 arc = DLGERR_ROW_BEFORE_TABLE;
1182 else
1183 {
1184 // create new row
1185 pCurrentRow = NEW(ROWDEF);
1186 if (!pCurrentRow)
1187 arc = ERROR_NOT_ENOUGH_MEMORY;
1188 else
1189 {
1190 ZERO(pCurrentRow);
1191
1192 pCurrentRow->pOwningTable = pCurrentTable;
1193 lstInit(&pCurrentRow->llColumns, FALSE);
1194
1195 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1196 }
1197 }
1198 break; }
1199
1200 /*
1201 * TYPE_CONTROL_DEF:
1202 *
1203 */
1204
1205 case TYPE_CONTROL_DEF:
1206 {
1207 PCOLUMNDEF pColumnDef;
1208 arc = CreateColumn(pCurrentRow,
1209 FALSE, // no nested table
1210 (PVOID)pItemThis->ulData,
1211 &pColumnDef);
1212 if (!arc)
1213 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1214 break; }
1215
1216 /*
1217 * TYPE_END_TABLE:
1218 *
1219 */
1220
1221 case TYPE_END_TABLE:
1222 {
1223 PLISTNODE pNode = lstPop(&llStack);
1224 if (!pNode)
1225 // nothing on the stack:
1226 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1227 else
1228 {
1229 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1230 pCurrentTable = pStackItem->pLastTable;
1231 pCurrentRow = pStackItem->pLastRow;
1232
1233 lstRemoveNode(&llStack, pNode);
1234 }
1235 break; }
1236
1237 default:
1238 arc = DLGERR_INVALID_CODE;
1239 }
1240
1241 if (arc)
1242 break;
1243 }
1244
1245 if (arc == NO_ERROR)
1246 if (lstCountItems(&llStack))
1247 arc = DLGERR_TABLE_NOT_CLOSED;
1248
1249 lstClear(&llStack);
1250
1251 if (arc == NO_ERROR)
1252 {
1253 /*
1254 * 2) create empty dialog frame
1255 *
1256 */
1257
1258 FRAMECDATA fcData = {0};
1259
1260 #define FCF_DIALOGBOX 0x40000000L
1261
1262 fcData.cb = sizeof(FRAMECDATA);
1263 fcData.flCreateFlags = flCreateFlags | FCF_DIALOGBOX;
1264
1265 pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1266 WC_FRAME,
1267 (PSZ)pcszDlgTitle,
1268 0, // style; invisible for now
1269 0, 0, 0, 0,
1270 hwndOwner,
1271 HWND_TOP,
1272 0, // ID
1273 &fcData,
1274 NULL); // presparams
1275
1276 if (!pDlgData->hwndDlg)
1277 arc = DLGERR_CANNOT_CREATE_FRAME;
1278 else
1279 {
1280 SIZEL szlClient = {0};
1281 RECTL rclClient;
1282 HWND hwndFocusItem = NULLHANDLE;
1283
1284 /*
1285 * 3) compute size of client after computing all control sizes
1286 *
1287 */
1288
1289 ProcessAll(pDlgData,
1290 &szlClient,
1291 PROCESS_CALC_SIZES);
1292
1293 // this might have created a HPS and a font in dlgdata
1294 // for auto-sizing controls
1295 if (pDlgData->hps)
1296 {
1297 if (pDlgData->lcidLast)
1298 {
1299 // delete old font
1300 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1301 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1302 pDlgData->lcidLast = 0;
1303 }
1304
1305 WinReleasePS(pDlgData->hps);
1306 pDlgData->hps = NULLHANDLE;
1307 }
1308
1309 WinSubclassWindow(pDlgData->hwndDlg, pfnwpDialogProc);
1310
1311 // calculate the frame size from the client size
1312 rclClient.xLeft = 10;
1313 rclClient.yBottom = 10;
1314 rclClient.xRight = szlClient.cx + 2 * SPACING;
1315 rclClient.yTop = szlClient.cy + 2 * SPACING;
1316 WinCalcFrameRect(pDlgData->hwndDlg,
1317 &rclClient,
1318 FALSE); // frame from client
1319
1320 WinSetWindowPos(pDlgData->hwndDlg,
1321 0,
1322 10,
1323 10,
1324 rclClient.xRight,
1325 rclClient.yTop,
1326 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1327
1328 /*
1329 * 4) compute positions of all controls
1330 *
1331 */
1332
1333 ProcessAll(pDlgData,
1334 &szlClient,
1335 PROCESS_CALC_POSITIONS);
1336
1337 /*
1338 * 5) create control windows, finally
1339 *
1340 */
1341
1342 pDlgData->ptlTotalOfs.x = SPACING;
1343 pDlgData->ptlTotalOfs.y = SPACING;
1344
1345 ProcessAll(pDlgData,
1346 &szlClient,
1347 PROCESS_CREATE_CONTROLS);
1348
1349 /*
1350 * 6) WM_INITDLG, set focus
1351 *
1352 */
1353
1354 hwndFocusItem = (pDlgData->hwndFirstFocus)
1355 ? pDlgData->hwndFirstFocus
1356 : pDlgData->hwndDlg;
1357 if (!WinSendMsg(pDlgData->hwndDlg,
1358 WM_INITDLG,
1359 (MPARAM)hwndFocusItem,
1360 (MPARAM)pCreateParams))
1361 {
1362 // if WM_INITDLG returns FALSE, this means
1363 // the dlg proc has not changed the focus;
1364 // we must then set the focus here
1365 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1366 }
1367 }
1368 }
1369
1370 if (pDlgData)
1371 {
1372 PLISTNODE pTableNode;
1373
1374 if (arc)
1375 {
1376 // error: clean up
1377 if (pDlgData->hwndDlg)
1378 WinDestroyWindow(pDlgData->hwndDlg);
1379 }
1380 else
1381 // no error: output dialog
1382 *phwndDlg = pDlgData->hwndDlg;
1383
1384 // in any case, clean up our mess:
1385
1386 // clean up the tables
1387 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1388 {
1389 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1390
1391 // for each table, clean up the rows
1392 PLISTNODE pRowNode;
1393 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1394 {
1395 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1396
1397 // for each row, clean up the columns
1398 PLISTNODE pColumnNode;
1399 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1400 {
1401 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1402 free(pColumn);
1403 }
1404 lstClear(&pRow->llColumns);
1405
1406 free(pRow);
1407 }
1408 lstClear(&pTable->llRows);
1409
1410 free(pTable);
1411 }
1412
1413 lstClear(&pDlgData->llTables);
1414 lstClear(&pDlgData->llControls);
1415
1416 free(pDlgData);
1417 }
1418
1419 return (arc);
1420}
1421
1422
Note: See TracBrowser for help on using the repository browser.