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

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

Misc updates

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