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

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

Tons of changes from the last weeks.

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