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

Last change on this file since 61 was 61, 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: 54.0 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; // @@@
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,
403 PROCESSMODE ProcessMode,
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 // @@@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, // 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 *
676 */
677
678VOID ProcessRow(PROWDEF pRowDef,
679 PTABLEDEF pOwningTable,
680 PROCESSMODE ProcessMode,
681 PLONG plY,
682 PDLGPRIVATE pDlgData)
683{
684 ULONG ul;
685 LONG lX;
686 PLISTNODE pNode;
687
688 pRowDef->pOwningTable = pOwningTable;
689
690 if (ProcessMode == PROCESS_CALC_SIZES)
691 {
692 pRowDef->cpRow.cx = 0;
693 pRowDef->cpRow.cy = 0;
694 }
695 else if (ProcessMode == PROCESS_CALC_POSITIONS)
696 {
697 // set up x and y so that the columns can
698 // base on that
699 pRowDef->cpRow.x = pOwningTable->cpTable.x;
700 // decrease y by row height
701 *plY -= pRowDef->cpRow.cy;
702 // and use that for our bottom position
703 pRowDef->cpRow.y = *plY;
704
705 // set lX to left of row; used by column calls below
706 lX = pRowDef->cpRow.x;
707 }
708
709 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
710 {
711 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
712
713 ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData);
714
715 if (ProcessMode == PROCESS_CALC_SIZES)
716 {
717 // row width = sum of all columns
718 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
719
720 // row height = maximum height of a column
721 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
722 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
723 }
724 }
725}
726
727/*
728 *@@ ProcessTable:
729 *
730 * This routine is a bit sick because it can be
731 * called recursively if a nested table is found
732 * in a COLUMNDEF.
733 *
734 * With PROCESS_CALC_POSITIONS, pptl must specify
735 * the lower left corner of the table. For the
736 * root call, this will be {0, 0}; for nested calls,
737 * this must be the lower left corner of the column
738 * to which the nested table belongs.
739 *
740 */
741
742VOID ProcessTable(PTABLEDEF pTableDef,
743 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
744 PROCESSMODE ProcessMode,
745 PDLGPRIVATE pDlgData)
746{
747 ULONG ul;
748 LONG lY;
749 PLISTNODE pNode;
750
751 if (ProcessMode == PROCESS_CALC_SIZES)
752 {
753 pTableDef->cpTable.cx = 0;
754 pTableDef->cpTable.cy = 0;
755 }
756 else if (ProcessMode == PROCESS_CALC_POSITIONS)
757 {
758 pTableDef->cpTable.x = pcpTable->x;
759 pTableDef->cpTable.y = pcpTable->y;
760
761 // start the rows on top
762 lY = pcpTable->y + pTableDef->cpTable.cy;
763 }
764
765 FOR_ALL_NODES(&pTableDef->llRows, pNode)
766 {
767 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
768
769 ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData);
770
771 if (ProcessMode == PROCESS_CALC_SIZES)
772 {
773 // table width = maximum width of a row
774 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
775 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
776
777 // table height = sum of all rows
778 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
779 }
780 }
781}
782
783/*
784 *@@ ProcessAll:
785 *
786 * -- PROCESS_CALC_SIZES: calculates the sizes
787 * of all tables, rows, columns, and controls.
788 *
789 * -- PROCESS_CALC_POSITIONS: calculates the positions
790 * based on the sizes calculated before.
791 *
792 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
793 * positions and sizes calculated before.
794 *
795 */
796
797VOID ProcessAll(PDLGPRIVATE pDlgData,
798 PSIZEL pszlClient,
799 PROCESSMODE ProcessMode)
800{
801 ULONG ul;
802 PLISTNODE pNode;
803 CONTROLPOS cpTable;
804 ZERO(&cpTable);
805
806 switch (ProcessMode)
807 {
808 case PROCESS_CALC_SIZES:
809 pszlClient->cx = 0;
810 pszlClient->cy = 0;
811 break;
812
813 case PROCESS_CALC_POSITIONS:
814 // start with the table on top
815 cpTable.y = pszlClient->cy;
816 break;
817 }
818
819 FOR_ALL_NODES(&pDlgData->llTables, pNode)
820 {
821 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
822
823 if (ProcessMode == PROCESS_CALC_POSITIONS)
824 {
825 cpTable.x = 0;
826 cpTable.y -= pTableDefThis->cpTable.cy;
827 }
828
829 ProcessTable(pTableDefThis,
830 &cpTable, // start pos
831 ProcessMode,
832 pDlgData);
833
834 if (ProcessMode == PROCESS_CALC_SIZES)
835 {
836 pszlClient->cx += pTableDefThis->cpTable.cx;
837 pszlClient->cy += pTableDefThis->cpTable.cy;
838 }
839 }
840}
841
842/*
843 *@@ CreateColumn:
844 *
845 */
846
847APIRET CreateColumn(PROWDEF pCurrentRow,
848 BOOL fIsNestedTable,
849 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
850 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
851{
852 APIRET arc = NO_ERROR;
853
854 if (!pCurrentRow)
855 arc = DLGERR_CONTROL_BEFORE_ROW;
856 else
857 {
858 // append the control def
859 if (!pvDefinition)
860 arc = DLGERR_NULL_CTL_DEF;
861 else
862 {
863 // create column and store ctl def
864 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
865 if (!pColumnDef)
866 arc = ERROR_NOT_ENOUGH_MEMORY;
867 else
868 {
869 memset(pColumnDef, 0, sizeof(COLUMNDEF));
870 pColumnDef->pOwningRow = pCurrentRow;
871 pColumnDef->fIsNestedTable = fIsNestedTable;
872 pColumnDef->pvDefinition = pvDefinition;
873
874 *ppColumnDef = pColumnDef;
875 }
876 }
877 }
878
879 return (arc);
880}
881
882/* ******************************************************************
883 *
884 * Public APIs
885 *
886 ********************************************************************/
887
888/*
889 *@@ STACKITEM:
890 *
891 */
892
893typedef struct _STACKITEM
894{
895 PTABLEDEF pLastTable;
896 PROWDEF pLastRow;
897
898} STACKITEM, *PSTACKITEM;
899
900/*
901 *@@ dlghCreateDlg:
902 * replacement for WinCreateDlg for creating a dialog
903 * from a settings array in memory, which is formatted
904 * automatically.
905 *
906 * This does NOT use regular dialog templates from
907 * module resources. Instead, you pass in an array
908 * of _DLGHITEM structures, which define the controls
909 * and how they are to be formatted.
910 *
911 * A regular standard dialog would use something like
912 *
913 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
914 *
915 * for flCreateFlags. To make the dlg sizeable, specify
916 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
917 *
918 * <B>Usage:</B>
919 *
920 * Like WinLoadDlg, this creates a standard WC_FRAME and
921 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
922 * to the dialog with pCreateParams in mp2. You can use
923 * WinProcessDlg as usual. In your dlg proc, use WinDefDlgProc
924 * as usual.
925 *
926 * There is NO run-time overhead after creation; after this
927 * function returns, the dialog is a standard dialog as if
928 * loaded from WinLoadDlg.
929 *
930 * The array of _DLGHITEM structures defines how the
931 * dialog is set up. All this is ONLY used by this function
932 * and NOT needed after the dialog has been created.
933 *
934 * In _DLGHITEM, the "Type" field determines what this
935 * structure defines. A number of handy macros have been
936 * defined to make this easier and to provide type-checking
937 * at compile time.
938 *
939 * The macros are:
940 *
941 * -- START_TABLE starts a new table. The tables may nest,
942 * but must each be properly terminated with END_TABLE.
943 *
944 * -- START_ROW(fl) starts a new row in a table.
945 *
946 * fl specifies formatting flags for the row. This
947 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
948 * ROW_VALIGN_TOP and affects all items in the control.
949 *
950 * -- START_GROUP_TABLE(pDef) starts a group. This
951 * behaves exacly like START_TABLE, but in addition,
952 * it produces a control around the table. Useful
953 * for group boxes. pDef must point to a _CONTROLDEF,
954 * whose size parameter is ignored.
955 *
956 * This must also be terminated with END_TABLE.
957 *
958 * -- CONTROL_DEF(pDef) defines a control in a table row.
959 * pDef must point to a _CONTROLDEF structure.
960 *
961 * The main difference (and advantage) of this
962 * function is that there is NO information in
963 * _CONTROLDEF about a control's _position_.
964 * Instead, the structure only contains the _size_
965 * of the control. All positions are computed by
966 * this function, depending on the position of the
967 * control and its nesting within the various tables.
968 *
969 * If you specify SZL_AUTOSIZE, the size of the
970 * control is even computed automatically. Presently,
971 * this only works for statics with SS_TEXT and
972 * SS_BITMAP.
973 *
974 * Unless separated with TYPE_START_NEW_ROW items,
975 * subsequent control items will be considered
976 * to be in the same row (== positioned next to
977 * each other).
978 *
979 * There are a few rules, whose violation will produce
980 * an error:
981 *
982 * -- The entire array must be enclosed in a table
983 * (the "root" table).
984 *
985 * -- After START_TABLE or START_GROUP_TABLE, there must
986 * always be a START_ROW first.
987 *
988 * To create a dialog, set up arrays like the following:
989 *
990 + // control definitions referenced by DlgTemplate:
991 + CONTROLDEF
992 + (1) GroupDef = {
993 + WC_STATIC, "", ....,
994 + { 0, 0 }, // size, ignored for groups
995 + 5 // spacing
996 + },
997 + (2) CnrDef = {
998 + WC_CONTAINER, "", ....,
999 + { 50, 50 }, // size
1000 + 5 // spacing
1001 + },
1002 + (3) Static = {
1003 + WC_STATIC, "Static below cnr", ...,
1004 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
1005 + 5 // spacing
1006 + },
1007 + (4) OKButton = {
1008 + WC_STATIC, "~OK", ...,
1009 + { 100, 30 }, // size
1010 + 5 // spacing
1011 + },
1012 + (5) CancelButton = {
1013 + WC_STATIC, "~Cancel", ...,
1014 + { 100, 30 }, // size
1015 + 5 // spacing
1016 + };
1017 +
1018 + DLGHITEM DlgTemplate[] =
1019 + {
1020 + START_TABLE, // root table, must exist
1021 + START_ROW(0), // row 1 in the root table
1022 + // create group on top
1023 + (1) START_GROUP_TABLE(&Group),
1024 + START_ROW(0),
1025 + (2) CONTROL_DEF(&CnrDef),
1026 + START_ROW(0),
1027 + (3) CONTROL_DEF(&Static),
1028 + END_TABLE, // end of group
1029 + START_ROW(0), // row 2 in the root table
1030 + // two buttons next to each other
1031 + (4) CONTROL_DEF(&OKButton),
1032 + (5) CONTROL_DEF(&CancelButton),
1033 + END_TABLE
1034 + }
1035 *
1036 * This will produce a dlg like this:
1037 *
1038 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1039 + º º
1040 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1041 + º ³ ³ º
1042 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1043 + º ³ ³ ³ ³ º
1044 + º ³ ³ Cnr inside group (2) ³ ³ º
1045 + º ³ ³ ³ ³ º
1046 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1047 + º ³ Static below cnr (3) ³ º
1048 + º ³ ³ º
1049 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1050 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1051 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1052 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1053 + º º
1054 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1055 *
1056 * <B>Errors:</B>
1057 *
1058 * This does not return a HWND, but an APIRET. This will be
1059 * one of the following:
1060 *
1061 * -- NO_ERROR: only in that case, the phwndDlg ptr
1062 * receives the HWND of the new dialog, which can
1063 * then be given to WinProcessDlg. Don't forget
1064 * WinDestroyWindow.
1065 *
1066 * -- ERROR_NOT_ENOUGH_MEMORY
1067 *
1068 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1069 * outside a table definition.
1070 *
1071 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1072 * appeared right after a table definition. You must
1073 * specify a row first.
1074 *
1075 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1076 * but the CONTROLDEF ptr was NULL.
1077 *
1078 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1079 * WC_FRAME window. Maybe an invalid owner was specified.
1080 *
1081 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1082 * DLGHITEM.
1083 *
1084 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1085 * improper nesting of TYPE_START_NEW_TABLE and
1086 * TYPE_END_TABLE fields.
1087 *
1088 * <B>Example:</B>
1089 *
1090 * The typical calling sequence would be:
1091 *
1092 + HWND hwndDlg = NULLHANDLE;
1093 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1094 + hwndOwner,
1095 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1096 + fnwpMyDlgProc,
1097 + "My Dlg Title",
1098 + G_aMyDialogTemplate,
1099 + ARRAYITEMSIZE(G_aMyDialogTemplate),
1100 + NULL))
1101 + {
1102 + ULONG idReturn = WinProcessDlg(pDlgData->hwndDlg);
1103 + WinDestroyWindow(hwndDlg);
1104 + }
1105 *
1106 *
1107 */
1108
1109APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1110 HWND hwndOwner,
1111 ULONG flCreateFlags, // in: standard FCF_* frame flags
1112 PFNWP pfnwpDialogProc,
1113 const char *pcszDlgTitle,
1114 PDLGHITEM paDlgItems, // in: definition array
1115 ULONG cDlgItems, // in: array item count (NOT array size)
1116 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1117 const char *pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1118{
1119 APIRET arc = NO_ERROR;
1120
1121 #define SPACING 10
1122
1123 PTABLEDEF pCurrentTable = NULL;
1124 PROWDEF pCurrentRow = NULL;
1125 ULONG ul;
1126 LINKLIST llStack;
1127
1128 PDLGPRIVATE pDlgData = NEW(DLGPRIVATE);
1129
1130 if (!pDlgData)
1131 return (ERROR_NOT_ENOUGH_MEMORY);
1132
1133 ZERO(pDlgData);
1134 lstInit(&pDlgData->llTables, FALSE);
1135 lstInit(&pDlgData->llControls, FALSE);
1136
1137 pDlgData->pcszControlsFont = pcszControlsFont;
1138
1139 /*
1140 * 1) parse the table and create structures from it
1141 *
1142 */
1143
1144 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1145
1146 for (ul = 0;
1147 ul < cDlgItems;
1148 ul++)
1149 {
1150 PDLGHITEM pItemThis = &paDlgItems[ul];
1151
1152 switch (pItemThis->Type)
1153 {
1154 /*
1155 * TYPE_START_NEW_TABLE:
1156 *
1157 */
1158
1159 case TYPE_START_NEW_TABLE:
1160 {
1161 // root table or nested?
1162 BOOL fIsRoot = (pCurrentTable == NULL);
1163
1164 // push the current table on the stack
1165 PSTACKITEM pStackItem = NEW(STACKITEM);
1166 if (pStackItem)
1167 {
1168 pStackItem->pLastTable = pCurrentTable;
1169 pStackItem->pLastRow = pCurrentRow;
1170 lstPush(&llStack, pStackItem);
1171 }
1172
1173 // create new table
1174 pCurrentTable = NEW(TABLEDEF);
1175 if (!pCurrentTable)
1176 arc = ERROR_NOT_ENOUGH_MEMORY;
1177 else
1178 {
1179 ZERO(pCurrentTable);
1180
1181 lstInit(&pCurrentTable->llRows, FALSE);
1182
1183 if (pItemThis->ulData)
1184 // control specified: store it (this will become a PM group)
1185 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1186
1187 if (fIsRoot)
1188 // root table:
1189 // append to dialog data list
1190 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1191 else
1192 {
1193 // nested table:
1194 // create "table" column for this
1195 PCOLUMNDEF pColumnDef;
1196 arc = CreateColumn(pCurrentRow,
1197 TRUE, // nested table
1198 pCurrentTable,
1199 &pColumnDef);
1200 if (!arc)
1201 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1202 }
1203 }
1204
1205 pCurrentRow = NULL;
1206 break; }
1207
1208 /*
1209 * TYPE_START_NEW_ROW:
1210 *
1211 */
1212
1213 case TYPE_START_NEW_ROW:
1214 {
1215 if (!pCurrentTable)
1216 arc = DLGERR_ROW_BEFORE_TABLE;
1217 else
1218 {
1219 // create new row
1220 pCurrentRow = NEW(ROWDEF);
1221 if (!pCurrentRow)
1222 arc = ERROR_NOT_ENOUGH_MEMORY;
1223 else
1224 {
1225 ZERO(pCurrentRow);
1226
1227 pCurrentRow->pOwningTable = pCurrentTable;
1228 lstInit(&pCurrentRow->llColumns, FALSE);
1229
1230 pCurrentRow->flRowFormat = pItemThis->ulData;
1231
1232 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1233 }
1234 }
1235 break; }
1236
1237 /*
1238 * TYPE_CONTROL_DEF:
1239 *
1240 */
1241
1242 case TYPE_CONTROL_DEF:
1243 {
1244 PCOLUMNDEF pColumnDef;
1245 arc = CreateColumn(pCurrentRow,
1246 FALSE, // no nested table
1247 (PVOID)pItemThis->ulData,
1248 &pColumnDef);
1249 if (!arc)
1250 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1251 break; }
1252
1253 /*
1254 * TYPE_END_TABLE:
1255 *
1256 */
1257
1258 case TYPE_END_TABLE:
1259 {
1260 PLISTNODE pNode = lstPop(&llStack);
1261 if (!pNode)
1262 // nothing on the stack:
1263 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1264 else
1265 {
1266 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1267 pCurrentTable = pStackItem->pLastTable;
1268 pCurrentRow = pStackItem->pLastRow;
1269
1270 lstRemoveNode(&llStack, pNode);
1271 }
1272 break; }
1273
1274 default:
1275 arc = DLGERR_INVALID_CODE;
1276 }
1277
1278 if (arc)
1279 break;
1280 }
1281
1282 if (arc == NO_ERROR)
1283 if (lstCountItems(&llStack))
1284 arc = DLGERR_TABLE_NOT_CLOSED;
1285
1286 lstClear(&llStack);
1287
1288 if (arc == NO_ERROR)
1289 {
1290 /*
1291 * 2) create empty dialog frame
1292 *
1293 */
1294
1295 FRAMECDATA fcData = {0};
1296
1297 #define FCF_DIALOGBOX 0x40000000L
1298
1299 fcData.cb = sizeof(FRAMECDATA);
1300 fcData.flCreateFlags = flCreateFlags | FCF_DIALOGBOX;
1301
1302 pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1303 WC_FRAME,
1304 (PSZ)pcszDlgTitle,
1305 0, // style; invisible for now
1306 0, 0, 0, 0,
1307 hwndOwner,
1308 HWND_TOP,
1309 0, // ID
1310 &fcData,
1311 NULL); // presparams
1312
1313 if (!pDlgData->hwndDlg)
1314 arc = DLGERR_CANNOT_CREATE_FRAME;
1315 else
1316 {
1317 SIZEL szlClient = {0};
1318 RECTL rclClient;
1319 HWND hwndFocusItem = NULLHANDLE;
1320
1321 /*
1322 * 3) compute size of client after computing all control sizes
1323 *
1324 */
1325
1326 ProcessAll(pDlgData,
1327 &szlClient,
1328 PROCESS_CALC_SIZES);
1329
1330 // this might have created a HPS and a font in dlgdata
1331 // for auto-sizing controls
1332 if (pDlgData->hps)
1333 {
1334 if (pDlgData->lcidLast)
1335 {
1336 // delete old font
1337 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1338 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1339 pDlgData->lcidLast = 0;
1340 }
1341
1342 WinReleasePS(pDlgData->hps);
1343 pDlgData->hps = NULLHANDLE;
1344 }
1345
1346 WinSubclassWindow(pDlgData->hwndDlg, pfnwpDialogProc);
1347
1348 // calculate the frame size from the client size
1349 rclClient.xLeft = 10;
1350 rclClient.yBottom = 10;
1351 rclClient.xRight = szlClient.cx + 2 * SPACING;
1352 rclClient.yTop = szlClient.cy + 2 * SPACING;
1353 WinCalcFrameRect(pDlgData->hwndDlg,
1354 &rclClient,
1355 FALSE); // frame from client
1356
1357 WinSetWindowPos(pDlgData->hwndDlg,
1358 0,
1359 10,
1360 10,
1361 rclClient.xRight,
1362 rclClient.yTop,
1363 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1364
1365 /*
1366 * 4) compute positions of all controls
1367 *
1368 */
1369
1370 ProcessAll(pDlgData,
1371 &szlClient,
1372 PROCESS_CALC_POSITIONS);
1373
1374 /*
1375 * 5) create control windows, finally
1376 *
1377 */
1378
1379 pDlgData->ptlTotalOfs.x = SPACING;
1380 pDlgData->ptlTotalOfs.y = SPACING;
1381
1382 ProcessAll(pDlgData,
1383 &szlClient,
1384 PROCESS_CREATE_CONTROLS);
1385
1386 /*
1387 * 6) WM_INITDLG, set focus
1388 *
1389 */
1390
1391 hwndFocusItem = (pDlgData->hwndFirstFocus)
1392 ? pDlgData->hwndFirstFocus
1393 : pDlgData->hwndDlg;
1394 if (!WinSendMsg(pDlgData->hwndDlg,
1395 WM_INITDLG,
1396 (MPARAM)hwndFocusItem,
1397 (MPARAM)pCreateParams))
1398 {
1399 // if WM_INITDLG returns FALSE, this means
1400 // the dlg proc has not changed the focus;
1401 // we must then set the focus here
1402 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1403 }
1404 }
1405 }
1406
1407 if (pDlgData)
1408 {
1409 PLISTNODE pTableNode;
1410
1411 if (arc)
1412 {
1413 // error: clean up
1414 if (pDlgData->hwndDlg)
1415 WinDestroyWindow(pDlgData->hwndDlg);
1416 }
1417 else
1418 // no error: output dialog
1419 *phwndDlg = pDlgData->hwndDlg;
1420
1421 // in any case, clean up our mess:
1422
1423 // clean up the tables
1424 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1425 {
1426 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1427
1428 // for each table, clean up the rows
1429 PLISTNODE pRowNode;
1430 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1431 {
1432 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1433
1434 // for each row, clean up the columns
1435 PLISTNODE pColumnNode;
1436 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1437 {
1438 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1439 free(pColumn);
1440 }
1441 lstClear(&pRow->llColumns);
1442
1443 free(pRow);
1444 }
1445 lstClear(&pTable->llRows);
1446
1447 free(pTable);
1448 }
1449
1450 lstClear(&pDlgData->llTables);
1451 lstClear(&pDlgData->llControls);
1452
1453 free(pDlgData);
1454 }
1455
1456 return (arc);
1457}
1458
1459/*
1460 *@@ dlghSetPrevFocus:
1461 * "backward" function for rotating the focus
1462 * in a dialog when the "shift+tab" keys get
1463 * pressed.
1464 *
1465 * pllWindows must be a linked list with the
1466 * plain HWND window handles of the focussable
1467 * controls in the dialog.
1468 */
1469
1470VOID dlghSetPrevFocus(PVOID pvllWindows)
1471{
1472 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
1473
1474 // check current focus
1475 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
1476
1477 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
1478
1479 BOOL fRestart = FALSE;
1480
1481 while (pNode)
1482 {
1483 CHAR szClass[100];
1484
1485 // previos node
1486 pNode = pNode->pPrevious;
1487
1488 if ( (!pNode) // no next, or not found:
1489 && (!fRestart) // avoid infinite looping if bad list
1490 )
1491 {
1492 pNode = lstQueryLastNode(pllWindows);
1493 fRestart = TRUE;
1494 }
1495
1496 if (pNode)
1497 {
1498 // check if this is a focusable control
1499 if (WinQueryClassName((HWND)pNode->pItemData,
1500 sizeof(szClass),
1501 szClass))
1502 {
1503 if ( (strcmp(szClass, "#5")) // not static
1504 )
1505 break;
1506 // else: go for next then
1507 }
1508 }
1509 }
1510
1511 if (pNode)
1512 {
1513 WinSetFocus(HWND_DESKTOP,
1514 (HWND)pNode->pItemData);
1515 }
1516}
1517
1518/*
1519 *@@ dlghSetNextFocus:
1520 * "forward" function for rotating the focus
1521 * in a dialog when the "ab" key gets pressed.
1522 *
1523 * pllWindows must be a linked list with the
1524 * plain HWND window handles of the focussable
1525 * controls in the dialog.
1526 */
1527
1528VOID dlghSetNextFocus(PVOID pvllWindows)
1529{
1530 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
1531
1532 // check current focus
1533 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
1534
1535 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
1536
1537 BOOL fRestart = FALSE;
1538
1539 while (pNode)
1540 {
1541 CHAR szClass[100];
1542
1543 // next focus in node
1544 pNode = pNode->pNext;
1545
1546 if ( (!pNode) // no next, or not found:
1547 && (!fRestart) // avoid infinite looping if bad list
1548 )
1549 {
1550 pNode = lstQueryFirstNode(pllWindows);
1551 fRestart = TRUE;
1552 }
1553
1554 if (pNode)
1555 {
1556 // check if this is a focusable control
1557 if (WinQueryClassName((HWND)pNode->pItemData,
1558 sizeof(szClass),
1559 szClass))
1560 {
1561 if ( (strcmp(szClass, "#5")) // not static
1562 )
1563 break;
1564 // else: go for next then
1565 }
1566 }
1567 }
1568
1569 if (pNode)
1570 {
1571 WinSetFocus(HWND_DESKTOP,
1572 (HWND)pNode->pItemData);
1573 }
1574}
1575
1576/*
1577 *@@ MatchMnemonic:
1578 * returns TRUE if the specified control matches
1579 *
1580 *
1581 *@@added V0.9.9 (2001-03-17) [umoeller]
1582 */
1583
1584/*
1585 *@@ dlghProcessMnemonic:
1586 * finds the control which matches usch
1587 * and gives it the focus. If this is a
1588 * static control, the next control in the
1589 * list is given focus instead. (Standard
1590 * dialog behavior.)
1591 *
1592 * Pass in usch from WM_CHAR. It is assumed
1593 * that the caller has already tested for
1594 * the "alt" key to be depressed.
1595 *
1596 *@@added V0.9.9 (2001-03-17) [umoeller]
1597 */
1598
1599HWND dlghProcessMnemonic(PVOID pvllWindows,
1600 USHORT usch)
1601{
1602 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
1603
1604 HWND hwndFound = NULLHANDLE;
1605 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
1606 CHAR szClass[100];
1607
1608 while (pNode)
1609 {
1610 HWND hwnd = (HWND)pNode->pItemData;
1611
1612 if (WinSendMsg(hwnd,
1613 WM_MATCHMNEMONIC,
1614 (MPARAM)usch,
1615 0))
1616 {
1617 // according to the docs, only buttons and static
1618 // return TRUE to that msg;
1619 // if this is a static, give focus to the next
1620 // control
1621
1622 _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
1623
1624 // check if this is a focusable control
1625 if (WinQueryClassName(hwnd,
1626 sizeof(szClass),
1627 szClass))
1628 {
1629 if (!strcmp(szClass, "#3"))
1630 // it's a button: click it
1631 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
1632 else if (!strcmp(szClass, "#5"))
1633 {
1634 // it's a static: give focus to following control
1635 pNode = pNode->pNext;
1636 if (pNode)
1637 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
1638 }
1639 }
1640 else
1641 // any other control (are there any?): give them focus
1642 WinSetFocus(HWND_DESKTOP, hwnd);
1643
1644 // in any case, stop
1645 hwndFound = hwnd;
1646 break;
1647 }
1648
1649 pNode = pNode->pNext;
1650 }
1651
1652 return (hwndFound);
1653}
1654
1655/*
1656 *@@ dlghEnter:
1657 * presses the first button with BS_DEFAULT.
1658 */
1659
1660BOOL dlghEnter(PVOID pvllWindows)
1661{
1662 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
1663
1664 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
1665 CHAR szClass[100];
1666 while (pNode)
1667 {
1668 HWND hwnd = (HWND)pNode->pItemData;
1669 if (WinQueryClassName(hwnd,
1670 sizeof(szClass),
1671 szClass))
1672 {
1673 if (!strcmp(szClass, "#3")) // button
1674 {
1675 _Pmpf((__FUNCTION__ ": found button"));
1676 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
1677 == (BS_PUSHBUTTON | BS_DEFAULT)
1678 )
1679 {
1680 _Pmpf((" is default!"));
1681 WinPostMsg(hwnd,
1682 BM_CLICK,
1683 (MPARAM)TRUE, // upclick
1684 0);
1685 return (TRUE);
1686 }
1687 }
1688 }
1689
1690 pNode = pNode->pNext;
1691 }
1692
1693 return (FALSE);
1694}
1695
1696
Note: See TracBrowser for help on using the repository browser.