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

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

Buncha fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 97.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 * In addition, this has dlghMessageBox (a WinMessageBox
10 * replacement) and some helper functions for simulating
11 * dialog behavior in regular window procs (see
12 * dlghSetPrevFocus and others).
13 *
14 * Usage: All PM programs.
15 *
16 * Function prefixes (new with V0.81):
17 * -- dlg* Dialog functions
18 *
19 * Note: Version numbering in this file relates to XWorkplace version
20 * numbering.
21 *
22 *@@added V0.9.9 (2001-04-01) [umoeller]
23 *@@header "helpers\dialog.h"
24 */
25
26/*
27 * Copyright (C) 2001 Ulrich M”ller.
28 * This file is part of the "XWorkplace helpers" source package.
29 * This is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published
31 * by the Free Software Foundation, in version 2 as it comes in the
32 * "COPYING" file of the XWorkplace main distribution.
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 */
38
39#define OS2EMX_PLAIN_CHAR
40 // this is needed for "os2emx.h"; if this is defined,
41 // emx will define PSZ as _signed_ char, otherwise
42 // as unsigned char
43
44#define INCL_DOSERRORS
45
46#define INCL_WINWINDOWMGR
47#define INCL_WINFRAMEMGR
48#define INCL_WINDIALOGS
49#define INCL_WININPUT
50#define INCL_WINSTATICS
51#define INCL_WINBUTTONS
52#define INCL_WINENTRYFIELDS
53#define INCL_WINSYS
54
55#define INCL_GPIPRIMITIVES
56#define INCL_GPIBITMAPS
57#define INCL_GPILCIDS
58#include <os2.h>
59
60#include <stdlib.h>
61#include <string.h>
62#include <stdio.h>
63
64#include "setup.h" // code generation and debugging options
65
66#include "helpers\comctl.h"
67#include "helpers\dialog.h"
68#include "helpers\gpih.h"
69#include "helpers\linklist.h"
70#include "helpers\standards.h"
71#include "helpers\stringh.h"
72#include "helpers\winh.h"
73#include "helpers\xstring.h"
74
75/*
76 *@@category: Helpers\PM helpers\Dialog templates
77 */
78
79/* ******************************************************************
80 *
81 * Private declarations
82 *
83 ********************************************************************/
84
85/*
86 *@@ DLGPRIVATE:
87 * private data to the dlg manager, allocated
88 * by dlghCreateDlg.
89 *
90 * This only exists while the dialog is being
91 * created and is not stored with the new dialog.
92 */
93
94typedef struct _DLGPRIVATE
95{
96 // public data
97 HWND hwndDlg;
98
99 // definition data (private)
100 LINKLIST llTables;
101
102 HWND hwndFirstFocus,
103 hwndDefPushbutton; // V0.9.14 (2001-08-21) [umoeller]
104
105 POINTL ptlTotalOfs;
106
107 LINKLIST llControls; // linked list of all PCOLUMNDEF structs,
108 // in the order in which windows were
109 // created
110
111 const char *pcszControlsFont; // from dlghCreateDlg
112
113 // V0.9.14 (2001-08-01) [umoeller]
114 HPS hps;
115 const char *pcszFontLast;
116 LONG lcidLast;
117 FONTMETRICS fmLast;
118
119} DLGPRIVATE, *PDLGPRIVATE;
120
121typedef struct _COLUMNDEF *PCOLUMNDEF;
122typedef struct _ROWDEF *PROWDEF;
123typedef struct _TABLEDEF *PTABLEDEF;
124
125/*
126 *@@ CONTROLPOS:
127 * control position. We don't want to use SWP.
128 */
129
130typedef struct _CONTROLPOS
131{
132 LONG x,
133 y,
134 cx,
135 cy;
136} CONTROLPOS, *PCONTROLPOS;
137
138/*
139 *@@ COLUMNDEF:
140 * representation of a table column.
141 * This is stored in a linked list in ROWDEF.
142 *
143 * A table column represents either a PM control
144 * window or another table, which may therefore
145 * be nested.
146 */
147
148typedef struct _COLUMNDEF
149{
150 PROWDEF pOwningRow; // row whose linked list this column belongs to
151
152 BOOL fIsNestedTable; // if TRUE, pvDefinition points to a nested TABLEDEF;
153 // if FALSE, pvDefinition points to a CONTROLDEF as
154 // specified by the caller
155
156 PVOID pvDefinition; // either a PTABLEDEF or a PCONTROLDEF
157
158 CONTROLPOS cpControl, // real pos and size of control
159 cpColumn; // pos and size of column; can be wider, spacings applied
160
161 HWND hwndControl; // created control; NULLHANDLE for tables always
162
163} COLUMNDEF;
164
165/*
166 *@@ ROWDEF:
167 *
168 */
169
170typedef struct _ROWDEF
171{
172 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
173
174 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
175
176 ULONG flRowFormat; // one of:
177 // -- ROW_VALIGN_BOTTOM 0x0000
178 // -- ROW_VALIGN_CENTER 0x0001
179 // -- ROW_VALIGN_TOP 0x0002
180
181 CONTROLPOS cpRow;
182
183} ROWDEF;
184
185/*
186 *@@ TABLEDEF:
187 *
188 */
189
190typedef struct _TABLEDEF
191{
192 LINKLIST llRows; // contains ROWDEF structs, no auto-free
193
194 PCONTROLDEF pCtlDef; // if != NULL, we create a PM control around the table
195
196 CONTROLPOS cpTable;
197
198} TABLEDEF;
199
200/*
201 *@@ PROCESSMODE:
202 *
203 */
204
205typedef enum _PROCESSMODE
206{
207 PROCESS_CALC_SIZES, // step 1
208 PROCESS_CALC_POSITIONS, // step 3
209 PROCESS_CREATE_CONTROLS // step 4
210} PROCESSMODE;
211
212/* ******************************************************************
213 *
214 * Worker routines
215 *
216 ********************************************************************/
217
218#define PM_GROUP_SPACING_X 16
219#define PM_GROUP_SPACING_TOP 20
220
221APIRET ProcessTable(PTABLEDEF pTableDef,
222 const CONTROLPOS *pcpTable,
223 PROCESSMODE ProcessMode,
224 PDLGPRIVATE pDlgData);
225
226/*
227 *@@ SetDlgFont:
228 *
229 *@@added V0.9.16 (2001-10-11) [umoeller]
230 */
231
232VOID SetDlgFont(PCONTROLDEF pControlDef,
233 PDLGPRIVATE pDlgData)
234{
235 LONG lPointSize = 0;
236 PCSZ pcszFontThis = pControlDef->pcszFont;
237 // can be NULL,
238 // or CTL_COMMON_FONT
239
240 if (pcszFontThis == CTL_COMMON_FONT)
241 pcszFontThis = pDlgData->pcszControlsFont;
242
243 if (!pDlgData->hps)
244 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
245
246 // check if we can reuse font data from last time
247 // V0.9.14 (2001-08-01) [umoeller]
248 if (strhcmp(pcszFontThis, // can be NULL!
249 pDlgData->pcszFontLast))
250 {
251 // different font than last time:
252
253 // delete old font?
254 if (pDlgData->lcidLast)
255 {
256 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT); // LCID_DEFAULT == 0
257 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
258 }
259
260 if (pcszFontThis)
261 {
262 // create new font
263 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
264 FALSE,
265 pDlgData->hps,
266 pcszFontThis,
267 &pDlgData->fmLast,
268 &lPointSize);
269
270 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
271 if (pDlgData->fmLast.fsDefn & FM_DEFN_OUTLINE)
272 gpihSetPointSize(pDlgData->hps, lPointSize);
273 }
274 else
275 {
276 // use default font:
277 // @@todo handle presparams, maybe inherited?
278 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
279 GpiQueryFontMetrics(pDlgData->hps,
280 sizeof(pDlgData->fmLast),
281 &pDlgData->fmLast);
282 }
283
284 pDlgData->pcszFontLast = pcszFontThis; // can be NULL
285 }
286}
287
288/*
289 *@@ CalcAutoSizeText:
290 *
291 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
292 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed broken fonts
293 *@@changed V0.9.14 (2001-08-01) [umoeller]: now caching fonts, which is significantly faster
294 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
295 */
296
297APIRET CalcAutoSizeText(PCONTROLDEF pControlDef,
298 BOOL fMultiLine, // in: if TRUE, multiple lines
299 PSIZEL pszlAuto, // out: computed size
300 PDLGPRIVATE pDlgData)
301{
302 APIRET arc = NO_ERROR;
303
304 SetDlgFont(pControlDef, pDlgData);
305
306 pszlAuto->cy = pDlgData->fmLast.lMaxBaselineExt
307 + pDlgData->fmLast.lExternalLeading;
308
309 // ok, we FINALLY have a font now...
310 // get the control string and see how much space it needs
311 if ( (pControlDef->pcszText)
312 && (pControlDef->pcszText != (PCSZ)-1)
313 )
314 {
315 // do we have multiple lines?
316 if (fMultiLine)
317 {
318 RECTL rcl = {0, 0, 0, 0};
319 if (pControlDef->szlControlProposed.cx > 0)
320 rcl.xRight = pControlDef->szlControlProposed.cx; // V0.9.12 (2001-05-31) [umoeller]
321 else
322 rcl.xRight = winhQueryScreenCX() * 2 / 3;
323 if (pControlDef->szlControlProposed.cy > 0)
324 rcl.yTop = pControlDef->szlControlProposed.cy; // V0.9.12 (2001-05-31) [umoeller]
325 else
326 rcl.yTop = winhQueryScreenCY() * 2 / 3;
327
328 winhDrawFormattedText(pDlgData->hps,
329 &rcl,
330 pControlDef->pcszText,
331 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
332 pszlAuto->cx = rcl.xRight - rcl.xLeft;
333 pszlAuto->cy = rcl.yTop - rcl.yBottom;
334 }
335 else
336 {
337 POINTL aptl[TXTBOX_COUNT];
338 GpiQueryTextBox(pDlgData->hps,
339 strlen(pControlDef->pcszText),
340 (PCH)pControlDef->pcszText,
341 TXTBOX_COUNT,
342 aptl);
343 pszlAuto->cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
344 }
345 }
346 else
347 arc = DLGERR_INVALID_CONTROL_TITLE;
348
349 return (arc);
350}
351
352/*
353 *@@ CalcAutoSize:
354 *
355 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
356 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
357 */
358
359APIRET CalcAutoSize(PCONTROLDEF pControlDef,
360 PSIZEL pszlAuto, // out: computed size
361 PDLGPRIVATE pDlgData)
362{
363 APIRET arc = NO_ERROR;
364
365 // dumb defaults
366 pszlAuto->cx = 100;
367 pszlAuto->cy = 30;
368
369 switch ((ULONG)pControlDef->pcszClass)
370 {
371 case 0xffff0003L: // WC_BUTTON:
372 if (!(arc = CalcAutoSizeText(pControlDef,
373 FALSE, // no multiline
374 pszlAuto,
375 pDlgData)))
376 {
377 if (pControlDef->flStyle & ( BS_AUTOCHECKBOX
378 | BS_AUTORADIOBUTTON
379 | BS_AUTO3STATE
380 | BS_3STATE
381 | BS_CHECKBOX
382 | BS_RADIOBUTTON))
383 {
384 // give a little extra width for the box bitmap
385 pszlAuto->cx += 20; // @@todo
386 // and height
387 pszlAuto->cy += 2;
388 }
389 else if (pControlDef->flStyle & BS_BITMAP)
390 ;
391 else if (pControlDef->flStyle & (BS_ICON | BS_MINIICON))
392 ;
393 // we can't test for BS_PUSHBUTTON because that's 0x0000
394 else if (!(pControlDef->flStyle & BS_USERBUTTON))
395 {
396 pszlAuto->cx += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) + 15);
397 pszlAuto->cy += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) + 15);
398 }
399 }
400 break;
401
402 case 0xffff0005L: // WC_STATIC:
403 if ((pControlDef->flStyle & 0x0F) == SS_TEXT)
404 arc = CalcAutoSizeText(pControlDef,
405 ((pControlDef->flStyle & DT_WORDBREAK) != 0),
406 pszlAuto,
407 pDlgData);
408 else if ((pControlDef->flStyle & 0x0F) == SS_BITMAP)
409 {
410 HBITMAP hbm;
411 if (hbm = (HBITMAP)pControlDef->pcszText)
412 {
413 BITMAPINFOHEADER2 bmih2;
414 ZERO(&bmih2);
415 bmih2.cbFix = sizeof(bmih2);
416 if (GpiQueryBitmapInfoHeader(hbm,
417 &bmih2))
418 {
419 pszlAuto->cx = bmih2.cx;
420 pszlAuto->cy = bmih2.cy;
421 }
422 else
423 arc = DLGERR_INVALID_STATIC_BITMAP;
424 }
425 }
426 else if ((pControlDef->flStyle & 0x0F) == SS_ICON)
427 {
428 pszlAuto->cx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
429 pszlAuto->cy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
430 }
431 break;
432
433 default:
434 // any other control (just to be safe):
435 SetDlgFont(pControlDef, pDlgData);
436 pszlAuto->cx = 50;
437 pszlAuto->cy = pDlgData->fmLast.lMaxBaselineExt
438 + pDlgData->fmLast.lExternalLeading
439 + 5; // some space
440 }
441
442 return (arc);
443}
444
445/*
446 *@@ ColumnCalcSizes:
447 * implementation for PROCESS_CALC_SIZES in
448 * ProcessColumn.
449 *
450 *@@added V0.9.15 (2001-08-26) [umoeller]
451 *@@changed V0.9.16 (2001-10-15) [umoeller]: fixed ugly group table spacings
452 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
453 *@@changed V0.9.16 (2002-02-02) [umoeller]: added support for explicit group size
454 */
455
456APIRET ColumnCalcSizes(PCOLUMNDEF pColumnDef,
457 PDLGPRIVATE pDlgData)
458{
459 APIRET arc = NO_ERROR;
460
461 ULONG ulXSpacing = 0,
462 ulYSpacing = 0;
463 if (pColumnDef->fIsNestedTable)
464 {
465 // nested table: recurse!!
466 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
467 if (!(arc = ProcessTable(pTableDef,
468 NULL,
469 PROCESS_CALC_SIZES,
470 pDlgData)))
471 {
472 // store the size of the sub-table
473 pColumnDef->cpControl.cx = pTableDef->cpTable.cx;
474 pColumnDef->cpControl.cy = pTableDef->cpTable.cy;
475
476 // should we create a PM control around the table?
477 if (pTableDef->pCtlDef)
478 {
479 // yes:
480
481 // check if maybe an explicit size was specified
482 // for the group; if that is larger than what
483 // we've calculated above, use it instead
484 if (pTableDef->pCtlDef->szlControlProposed.cx > pColumnDef->cpControl.cx)
485 // should be -1 for auto-size
486 pColumnDef->cpControl.cx = pTableDef->pCtlDef->szlControlProposed.cx;
487
488 if (pTableDef->pCtlDef->szlControlProposed.cy > pColumnDef->cpControl.cy)
489 // should be -1 for auto-size
490 pColumnDef->cpControl.cy = pTableDef->pCtlDef->szlControlProposed.cy;
491
492 // in any case, make this wider
493 ulXSpacing = 2 * PM_GROUP_SPACING_X;
494 ulYSpacing = // 3 * PM_GROUP_SPACING_X;
495 (PM_GROUP_SPACING_X + PM_GROUP_SPACING_TOP);
496 }
497 }
498 }
499 else
500 {
501 // no nested table, but control:
502 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
503 PSIZEL pszl = &pControlDef->szlControlProposed;
504 SIZEL szlAuto;
505
506 if ( (pszl->cx < -1)
507 && (pszl->cx >= -100)
508 )
509 {
510 // other negative CX value:
511 // this is then a percentage of the row width... ignore for now
512 // V0.9.16 (2002-02-02) [umoeller]
513 szlAuto.cx = 0;
514 szlAuto.cy = 0;
515 }
516 else if ( (pszl->cx == -1)
517 || (pszl->cy == -1)
518 )
519 {
520 arc = CalcAutoSize(pControlDef,
521 &szlAuto,
522 pDlgData);
523 }
524
525 if (!arc)
526 {
527 if (pszl->cx < 0)
528 pColumnDef->cpControl.cx = szlAuto.cx;
529 else
530 pColumnDef->cpControl.cx = pszl->cx;
531
532 if (pszl->cy < 0)
533 pColumnDef->cpControl.cy = szlAuto.cy;
534 else
535 pColumnDef->cpControl.cy = pszl->cy;
536
537 // @@todo hack sizes
538
539 ulXSpacing
540 = ulYSpacing
541 = (2 * pControlDef->ulSpacing);
542 }
543 }
544
545 pColumnDef->cpColumn.cx = pColumnDef->cpControl.cx
546 + ulXSpacing;
547 pColumnDef->cpColumn.cy = pColumnDef->cpControl.cy
548 + ulYSpacing;
549
550 return (arc);
551}
552
553/*
554 *@@ ColumnCalcPositions:
555 * implementation for PROCESS_CALC_POSITIONS in
556 * ProcessColumn.
557 *
558 *@@added V0.9.15 (2001-08-26) [umoeller]
559 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
560 */
561
562APIRET ColumnCalcPositions(PCOLUMNDEF pColumnDef,
563 PROWDEF pOwningRow, // in: current row from ProcessRow
564 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
565 PDLGPRIVATE pDlgData)
566{
567 APIRET arc = NO_ERROR;
568
569 // calculate column position: this includes spacing
570 ULONG ulSpacing = 0;
571
572 // column position = *plX on ProcessRow stack
573 pColumnDef->cpColumn.x = *plX;
574 pColumnDef->cpColumn.y = pOwningRow->cpRow.y;
575
576 // check vertical alignment of row;
577 // we might need to increase column y
578 switch (pOwningRow->flRowFormat & ROW_VALIGN_MASK)
579 {
580 // case ROW_VALIGN_BOTTOM: // do nothing
581
582 case ROW_VALIGN_CENTER:
583 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
584 pColumnDef->cpColumn.y
585 += ( (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy)
586 / 2);
587 break;
588
589 case ROW_VALIGN_TOP:
590 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
591 pColumnDef->cpColumn.y
592 += (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy);
593 break;
594 }
595
596 if (pColumnDef->fIsNestedTable)
597 {
598 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
599 // should we create a PM control around the table?
600 if (pTableDef->pCtlDef)
601 // yes:
602 ulSpacing = PM_GROUP_SPACING_X / 2; // V0.9.16 (2001-10-15) [umoeller]
603 }
604 else
605 {
606 // no nested table, but control:
607 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
608 ulSpacing = pControlDef->ulSpacing;
609 }
610
611 // increase plX by column width
612 *plX += pColumnDef->cpColumn.cx;
613
614 // calculate CONTROL pos from COLUMN pos by applying spacing
615 pColumnDef->cpControl.x = pColumnDef->cpColumn.x
616 + ulSpacing;
617 pColumnDef->cpControl.y = pColumnDef->cpColumn.y
618 + ulSpacing;
619
620 if (pColumnDef->fIsNestedTable)
621 {
622 // nested table:
623 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
624
625 // recurse!! to create windows for the sub-table
626 arc = ProcessTable(pTableDef,
627 &pColumnDef->cpControl, // start pos for new table
628 PROCESS_CALC_POSITIONS,
629 pDlgData);
630 }
631
632 return (arc);
633}
634
635/*
636 *@@ ColumnCreateControls:
637 * implementation for PROCESS_CREATE_CONTROLS in
638 * ProcessColumn.
639 *
640 *@@added V0.9.15 (2001-08-26) [umoeller]
641 *@@changed V0.9.16 (2001-10-15) [umoeller]: fixed ugly group table spacings
642 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed entry field ES_MARGIN positioning
643 */
644
645APIRET ColumnCreateControls(PCOLUMNDEF pColumnDef,
646 PDLGPRIVATE pDlgData)
647{
648 APIRET arc = NO_ERROR;
649
650 PCONTROLPOS pcp = NULL;
651 PCONTROLDEF pControlDef = NULL;
652 const char *pcszTitle = NULL;
653 ULONG flStyle = 0;
654 LHANDLE lHandleSet = NULLHANDLE;
655 ULONG flOld = 0;
656
657 LONG x, cx, y, cy; // for combo box hacks
658
659 if (pColumnDef->fIsNestedTable)
660 {
661 // nested table:
662 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
663
664 // recurse!!
665 if (!(arc = ProcessTable(pTableDef,
666 NULL,
667 PROCESS_CREATE_CONTROLS,
668 pDlgData)))
669 {
670 // should we create a PM control around the table?
671 // (do this AFTER the other controls from recursing,
672 // otherwise the stupid container doesn't show up)
673 if (pTableDef->pCtlDef)
674 {
675 // yes:
676 pcp = &pColumnDef->cpColumn; // !! not control
677 pControlDef = pTableDef->pCtlDef;
678 pcszTitle = pControlDef->pcszText;
679 flStyle = pControlDef->flStyle;
680
681 x = pcp->x + pDlgData->ptlTotalOfs.x;
682 cx = pcp->cx - PM_GROUP_SPACING_X;
683 // note, just one spacing: for the _column_ size,
684 // we have specified 2 X spacings
685 y = pcp->y + pDlgData->ptlTotalOfs.y;
686 // cy = pcp->cy - PM_GROUP_SPACING_X;
687 cy = pcp->cy - /* PM_GROUP_SPACING_X - */ PM_GROUP_SPACING_TOP / 2;
688 }
689 }
690 }
691 else
692 {
693 // no nested table, but control:
694 pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
695 pcp = &pColumnDef->cpControl;
696 pcszTitle = pControlDef->pcszText;
697 flStyle = pControlDef->flStyle;
698
699 x = pcp->x + pDlgData->ptlTotalOfs.x;
700 cx = pcp->cx;
701 y = pcp->y + pDlgData->ptlTotalOfs.y;
702 cy = pcp->cy;
703
704 // now implement hacks for certain controls
705 switch ((ULONG)pControlDef->pcszClass)
706 {
707 case 0xffff0005L: // WC_STATIC:
708 // change the title if this is a static with SS_BITMAP;
709 // we have used a HBITMAP in there!
710 if ( ( ((flStyle & 0x0F) == SS_BITMAP)
711 || ((flStyle & 0x0F) == SS_ICON)
712 )
713 )
714 {
715 // change style flag to not use SS_BITMAP nor SS_ICON;
716 // control creation fails otherwise (stupid, stupid PM)
717 flOld = flStyle;
718 flStyle = ((flStyle & ~0x0F) | SS_FGNDFRAME);
719 pcszTitle = "";
720 lHandleSet = (LHANDLE)pControlDef->pcszText;
721 }
722 break;
723
724 case 0xffff0002L: // combobox
725 // hack the stupid drop-down combobox which doesn't
726 // expand otherwise (the size of the drop-down is
727 // the full size of the control... duh)
728 if (flStyle & (CBS_DROPDOWN | CBS_DROPDOWNLIST))
729 {
730 y -= 100;
731 cy += 100;
732 }
733 break;
734
735 case 0xffff0006L: // entry field
736 // the stupid entry field resizes itself if it has
737 // the ES_MARGIN style, so correlate that too... dammit
738 // V0.9.16 (2001-12-08) [umoeller]
739 if (flStyle & ES_MARGIN)
740 {
741 LONG cxMargin = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
742 LONG cyMargin = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
743
744 x += cxMargin;
745 y += cxMargin;
746 }
747 break;
748 } // end switch ((ULONG)pControlDef->pcszClass)
749 }
750
751 if (pcp && pControlDef)
752 {
753 // create something:
754 // PPRESPARAMS ppp = NULL;
755
756 const char *pcszFont = pControlDef->pcszFont;
757 // can be NULL, or CTL_COMMON_FONT
758 if (pcszFont == CTL_COMMON_FONT)
759 pcszFont = pDlgData->pcszControlsFont;
760
761 /* if (pcszFont)
762 winhStorePresParam(&ppp,
763 PP_FONTNAMESIZE,
764 strlen(pcszFont),
765 (PVOID)pcszFont); */
766
767 if (pColumnDef->hwndControl
768 = WinCreateWindow(pDlgData->hwndDlg, // parent
769 (PSZ)pControlDef->pcszClass,
770 (pcszTitle) // hacked
771 ? (PSZ)pcszTitle
772 : "",
773 flStyle, // hacked
774 x,
775 y,
776 cx,
777 cy,
778 pDlgData->hwndDlg, // owner
779 HWND_BOTTOM,
780 pControlDef->usID,
781 pControlDef->pvCtlData,
782 NULL))
783 {
784 if (lHandleSet)
785 {
786 // subclass the damn static
787 if ((flOld & 0x0F) == SS_ICON)
788 // this was a static:
789 ctlPrepareStaticIcon(pColumnDef->hwndControl,
790 1);
791 else
792 // this was a bitmap:
793 ctlPrepareStretchedBitmap(pColumnDef->hwndControl,
794 TRUE);
795
796 WinSendMsg(pColumnDef->hwndControl,
797 SM_SETHANDLE,
798 (MPARAM)lHandleSet,
799 0);
800 }
801 else
802 if (pcszFont)
803 // we must set the font explicitly here...
804 // doesn't always work with WinCreateWindow
805 // presparams parameter, for some reason
806 // V0.9.12 (2001-05-31) [umoeller]
807 winhSetWindowFont(pColumnDef->hwndControl,
808 pcszFont);
809
810 lstAppendItem(&pDlgData->llControls,
811 pColumnDef);
812
813 // if this is the first control with WS_TABSTOP,
814 // we give it the focus later
815 if ( (flStyle & WS_TABSTOP)
816 && (!pDlgData->hwndFirstFocus)
817 )
818 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
819
820 // if this is the first default push button,
821 // go store it too
822 // V0.9.14 (2001-08-21) [umoeller]
823 if ( (!pDlgData->hwndDefPushbutton)
824 && ((ULONG)pControlDef->pcszClass == 0xffff0003L)
825 && (pControlDef->flStyle & BS_DEFAULT)
826 )
827 pDlgData->hwndDefPushbutton = pColumnDef->hwndControl;
828 }
829 else
830 // V0.9.14 (2001-08-03) [umoeller]
831 arc = DLGERR_CANNOT_CREATE_CONTROL;
832 }
833
834 return (arc);
835}
836
837/*
838 *@@ ProcessColumn:
839 * processes a column, which per definition is either
840 * a control or a nested subtable.
841 *
842 * A column is part of a row, which in turn is part
843 * of a table. There can be several columns in a row,
844 * and several rows in a table.
845 *
846 * Since tables may be specified as columns, it is
847 * possible to produce complex dialog layouts by
848 * nesting tables.
849 *
850 * This does the following:
851 *
852 * -- PROCESS_CALC_SIZES: size is taken from control def,
853 * or for tables, this produces a recursive ProcessTable
854 * call.
855 * Preconditions: none.
856 *
857 * -- PROCESS_CALC_POSITIONS: position of each column
858 * is taken from *plX, which is increased by the
859 * column width by this call.
860 *
861 * Preconditions: Owning row must already have its
862 * y position properly set, or we can't compute
863 * ours. Besides, plX must point to the current X
864 * in the row and will be incremented by the columns
865 * size here.
866 *
867 * -- PROCESS_CREATE_CONTROLS: well, creates the controls.
868 *
869 * For tables, this recurses again. If the table has
870 * a string assigned, this also produces a group box
871 * after the recursion.
872 *
873 *@@changed V0.9.12 (2001-05-31) [umoeller]: added control data
874 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed font problems
875 */
876
877APIRET ProcessColumn(PCOLUMNDEF pColumnDef,
878 PROWDEF pOwningRow, // in: current row from ProcessRow
879 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
880 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
881 PDLGPRIVATE pDlgData)
882{
883 APIRET arc = NO_ERROR;
884
885 pColumnDef->pOwningRow = pOwningRow;
886
887 switch (ProcessMode)
888 {
889 /*
890 * PROCESS_CALC_SIZES:
891 * step 1.
892 */
893
894 case PROCESS_CALC_SIZES:
895 arc = ColumnCalcSizes(pColumnDef,
896 pDlgData);
897 break;
898
899 /*
900 * PROCESS_CALC_POSITIONS:
901 * step 2.
902 */
903
904 case PROCESS_CALC_POSITIONS:
905 arc = ColumnCalcPositions(pColumnDef,
906 pOwningRow,
907 plX,
908 pDlgData);
909 break;
910
911 /*
912 * PROCESS_CREATE_CONTROLS:
913 * step 3.
914 */
915
916 case PROCESS_CREATE_CONTROLS:
917 arc = ColumnCreateControls(pColumnDef,
918 pDlgData);
919 break;
920 }
921
922 return (arc);
923}
924
925/*
926 *@@ ProcessRow:
927 * level-3 procedure (called from ProcessTable),
928 * which in turn calls ProcessColumn for each column
929 * in the row.
930 *
931 * See ProcessAll for the meaning of ProcessMode.
932 */
933
934APIRET ProcessRow(PROWDEF pRowDef,
935 PTABLEDEF pOwningTable, // in: current table from ProcessTable
936 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
937 PLONG plY, // in/out: current y position (decremented)
938 PDLGPRIVATE pDlgData)
939{
940 APIRET arc = NO_ERROR;
941 LONG lX;
942 PLISTNODE pNode;
943
944 pRowDef->pOwningTable = pOwningTable;
945
946 if (ProcessMode == PROCESS_CALC_SIZES)
947 {
948 pRowDef->cpRow.cx = 0;
949 pRowDef->cpRow.cy = 0;
950 }
951 else if (ProcessMode == PROCESS_CALC_POSITIONS)
952 {
953 // set up x and y so that the columns can
954 // base on that
955 pRowDef->cpRow.x = pOwningTable->cpTable.x;
956 // decrease y by row height
957 *plY -= pRowDef->cpRow.cy;
958 // and use that for our bottom position
959 pRowDef->cpRow.y = *plY;
960
961 // set lX to left of row; used by column calls below
962 lX = pRowDef->cpRow.x;
963 }
964
965 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
966 {
967 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
968
969 if (!(arc = ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData)))
970 {
971 if (ProcessMode == PROCESS_CALC_SIZES)
972 {
973 // row width = sum of all columns
974 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
975
976 // row height = maximum height of a column
977 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
978 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
979 }
980 }
981 }
982
983 return (arc);
984}
985
986/*
987 *@@ ProcessTable:
988 * level-2 procedure (called from ProcessAll),
989 * which in turn calls ProcessRow for each row
990 * in the table (which in turn calls ProcessColumn
991 * for each column in the row).
992 *
993 * See ProcessAll for the meaning of ProcessMode.
994 *
995 * This routine is a bit sick because it can even be
996 * called recursively from ProcessColumn (!) if a
997 * nested table is found in a COLUMNDEF.
998 *
999 * With PROCESS_CALC_POSITIONS, pptl must specify
1000 * the lower left corner of the table. For the
1001 * root call, this will be {0, 0}; for nested calls,
1002 * this must be the lower left corner of the column
1003 * to which the nested table belongs.
1004 *
1005 */
1006
1007APIRET ProcessTable(PTABLEDEF pTableDef,
1008 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
1009 PROCESSMODE ProcessMode, // in: processing mode (see ProcessAll)
1010 PDLGPRIVATE pDlgData)
1011{
1012 APIRET arc = NO_ERROR;
1013 LONG lY;
1014 PLISTNODE pNode;
1015
1016 if (ProcessMode == PROCESS_CALC_SIZES)
1017 {
1018 pTableDef->cpTable.cx = 0;
1019 pTableDef->cpTable.cy = 0;
1020 }
1021 else if (ProcessMode == PROCESS_CALC_POSITIONS)
1022 {
1023 pTableDef->cpTable.x = pcpTable->x;
1024 pTableDef->cpTable.y = pcpTable->y;
1025
1026 // start the rows on top
1027 lY = pcpTable->y + pTableDef->cpTable.cy;
1028 }
1029
1030 FOR_ALL_NODES(&pTableDef->llRows, pNode)
1031 {
1032 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
1033
1034 if (!(arc = ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData)))
1035 {
1036 if (ProcessMode == PROCESS_CALC_SIZES)
1037 {
1038 // table width = maximum width of a row
1039 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
1040 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
1041
1042 // table height = sum of all rows
1043 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
1044 }
1045 }
1046 else
1047 break;
1048 }
1049
1050 return (arc);
1051}
1052
1053/*
1054 *@@ ProcessAll:
1055 * level-1 procedure, which in turn calls ProcessTable
1056 * for each root-level table found (which in turn
1057 * calls ProcessRow for each row in the table, which
1058 * in turn calls ProcessColumn for each column in
1059 * the row).
1060 *
1061 * The first trick to formatting is that ProcessAll will
1062 * get three times, thus going down the entire tree three
1063 * times, with ProcessMode being set to one of the
1064 * following for each call (in this order):
1065 *
1066 * -- PROCESS_CALC_SIZES: calculates the sizes
1067 * of all tables, rows, columns, and controls.
1068 *
1069 * After this first call, we know all the sizes
1070 * only and can then calculate the positions.
1071 *
1072 * -- PROCESS_CALC_POSITIONS: calculates the positions
1073 * based on the sizes calculated before.
1074 *
1075 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
1076 * positions and sizes calculated before.
1077 *
1078 * The second trick is the precondition that tables may
1079 * nest by allowing another table definition in a column.
1080 * This way we can recurse from ProcessColumn back into
1081 * ProcessTable and thus know the size and position of a
1082 * nested table column just as if it were a regular control.
1083 */
1084
1085APIRET ProcessAll(PDLGPRIVATE pDlgData,
1086 PSIZEL pszlClient,
1087 PROCESSMODE ProcessMode)
1088{
1089 APIRET arc = NO_ERROR;
1090 PLISTNODE pNode;
1091 CONTROLPOS cpTable;
1092 ZERO(&cpTable);
1093
1094 switch (ProcessMode)
1095 {
1096 case PROCESS_CALC_SIZES:
1097 pszlClient->cx = 0;
1098 pszlClient->cy = 0;
1099 break;
1100
1101 case PROCESS_CALC_POSITIONS:
1102 // start with the table on top
1103 cpTable.y = pszlClient->cy;
1104 break;
1105 }
1106
1107 FOR_ALL_NODES(&pDlgData->llTables, pNode)
1108 {
1109 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
1110
1111 if (ProcessMode == PROCESS_CALC_POSITIONS)
1112 {
1113 cpTable.x = 0;
1114 cpTable.y -= pTableDefThis->cpTable.cy;
1115 }
1116
1117 if (!(arc = ProcessTable(pTableDefThis,
1118 &cpTable, // start pos
1119 ProcessMode,
1120 pDlgData)))
1121 {
1122 if (ProcessMode == PROCESS_CALC_SIZES)
1123 {
1124 // all sizes have now been computed:
1125 pszlClient->cx += pTableDefThis->cpTable.cx;
1126 pszlClient->cy += pTableDefThis->cpTable.cy;
1127 }
1128 }
1129 }
1130
1131 return (arc);
1132}
1133
1134/*
1135 *@@ CreateColumn:
1136 *
1137 */
1138
1139APIRET CreateColumn(PROWDEF pCurrentRow,
1140 BOOL fIsNestedTable,
1141 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
1142 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
1143{
1144 APIRET arc = NO_ERROR;
1145
1146 if (!pCurrentRow)
1147 arc = DLGERR_CONTROL_BEFORE_ROW;
1148 else
1149 {
1150 // append the control def
1151 if (!pvDefinition)
1152 arc = DLGERR_NULL_CTL_DEF;
1153 else
1154 {
1155 // create column and store ctl def
1156 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
1157 if (!pColumnDef)
1158 arc = ERROR_NOT_ENOUGH_MEMORY;
1159 else
1160 {
1161 memset(pColumnDef, 0, sizeof(COLUMNDEF));
1162 pColumnDef->pOwningRow = pCurrentRow;
1163 pColumnDef->fIsNestedTable = fIsNestedTable;
1164 pColumnDef->pvDefinition = pvDefinition;
1165
1166 *ppColumnDef = pColumnDef;
1167 }
1168 }
1169 }
1170
1171 return (arc);
1172}
1173
1174/*
1175 *@@ FreeTable:
1176 * frees the specified table and recurses
1177 * into nested tables, if necessary.
1178 *
1179 * This was added with V0.9.14 to fix the
1180 * bad memory leaks with nested tables.
1181 *
1182 *@@added V0.9.14 (2001-08-01) [umoeller]
1183 */
1184
1185VOID FreeTable(PTABLEDEF pTable)
1186{
1187 // for each table, clean up the rows
1188 PLISTNODE pRowNode;
1189 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1190 {
1191 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1192
1193 // for each row, clean up the columns
1194 PLISTNODE pColumnNode;
1195 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1196 {
1197 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1198
1199 if (pColumn->fIsNestedTable)
1200 {
1201 // nested table: recurse!
1202 PTABLEDEF pNestedTable = (PTABLEDEF)pColumn->pvDefinition;
1203 FreeTable(pNestedTable);
1204 }
1205
1206 free(pColumn);
1207 }
1208 lstClear(&pRow->llColumns);
1209
1210 free(pRow);
1211 }
1212 lstClear(&pTable->llRows);
1213
1214 free(pTable);
1215}
1216
1217/* ******************************************************************
1218 *
1219 * Dialog formatter engine
1220 *
1221 ********************************************************************/
1222
1223/*
1224 *@@ STACKITEM:
1225 *
1226 */
1227
1228typedef struct _STACKITEM
1229{
1230 PTABLEDEF pLastTable;
1231 PROWDEF pLastRow;
1232
1233} STACKITEM, *PSTACKITEM;
1234
1235#define SPACING 10
1236
1237/*
1238 *@@ Dlg0_Init:
1239 *
1240 *@@added V0.9.15 (2001-08-26) [umoeller]
1241 */
1242
1243APIRET Dlg0_Init(PDLGPRIVATE *ppDlgData,
1244 PCSZ pcszControlsFont)
1245{
1246 PDLGPRIVATE pDlgData;
1247 if (!(pDlgData = NEW(DLGPRIVATE)))
1248 return (ERROR_NOT_ENOUGH_MEMORY);
1249 ZERO(pDlgData);
1250 lstInit(&pDlgData->llTables, FALSE);
1251 lstInit(&pDlgData->llControls, FALSE);
1252
1253 pDlgData->pcszControlsFont = pcszControlsFont;
1254
1255 *ppDlgData = pDlgData;
1256
1257 return NO_ERROR;
1258}
1259
1260/*
1261 *@@ Dlg1_ParseTables:
1262 *
1263 *@@added V0.9.15 (2001-08-26) [umoeller]
1264 */
1265
1266APIRET Dlg1_ParseTables(PDLGPRIVATE pDlgData,
1267 PCDLGHITEM paDlgItems, // in: definition array
1268 ULONG cDlgItems) // in: array item count (NOT array size)
1269{
1270 APIRET arc = NO_ERROR;
1271
1272 LINKLIST llStack;
1273 ULONG ul;
1274 PTABLEDEF pCurrentTable = NULL;
1275 PROWDEF pCurrentRow = NULL;
1276
1277 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1278
1279 for (ul = 0;
1280 ul < cDlgItems;
1281 ul++)
1282 {
1283 PCDLGHITEM pItemThis = &paDlgItems[ul];
1284
1285 switch (pItemThis->Type)
1286 {
1287 /*
1288 * TYPE_START_NEW_TABLE:
1289 *
1290 */
1291
1292 case TYPE_START_NEW_TABLE:
1293 {
1294 // root table or nested?
1295 BOOL fIsRoot = (pCurrentTable == NULL);
1296
1297 // push the current table on the stack
1298 PSTACKITEM pStackItem;
1299 if (!(pStackItem = NEW(STACKITEM)))
1300 {
1301 arc = ERROR_NOT_ENOUGH_MEMORY;
1302 break;
1303 }
1304 else
1305 {
1306 pStackItem->pLastTable = pCurrentTable;
1307 pStackItem->pLastRow = pCurrentRow;
1308 lstPush(&llStack, pStackItem);
1309 }
1310
1311 // create new table
1312 if (!(pCurrentTable = NEW(TABLEDEF)))
1313 arc = ERROR_NOT_ENOUGH_MEMORY;
1314 else
1315 {
1316 ZERO(pCurrentTable);
1317
1318 lstInit(&pCurrentTable->llRows, FALSE);
1319
1320 if (pItemThis->ulData)
1321 // control specified: store it (this will become a PM group)
1322 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1323
1324 if (fIsRoot)
1325 // root table:
1326 // append to dialog data list
1327 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1328 else
1329 {
1330 // nested table:
1331 // create "table" column for this
1332 PCOLUMNDEF pColumnDef;
1333 if (!(arc = CreateColumn(pCurrentRow,
1334 TRUE, // nested table
1335 pCurrentTable,
1336 &pColumnDef)))
1337 lstAppendItem(&pCurrentRow->llColumns,
1338 pColumnDef);
1339 }
1340 }
1341
1342 pCurrentRow = NULL;
1343 break; }
1344
1345 /*
1346 * TYPE_START_NEW_ROW:
1347 *
1348 */
1349
1350 case TYPE_START_NEW_ROW:
1351 {
1352 if (!pCurrentTable)
1353 arc = DLGERR_ROW_BEFORE_TABLE;
1354 else
1355 {
1356 // create new row
1357 if (!(pCurrentRow = NEW(ROWDEF)))
1358 arc = ERROR_NOT_ENOUGH_MEMORY;
1359 else
1360 {
1361 ZERO(pCurrentRow);
1362
1363 pCurrentRow->pOwningTable = pCurrentTable;
1364 lstInit(&pCurrentRow->llColumns, FALSE);
1365
1366 pCurrentRow->flRowFormat = pItemThis->ulData;
1367
1368 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1369 }
1370 }
1371 break; }
1372
1373 /*
1374 * TYPE_CONTROL_DEF:
1375 *
1376 */
1377
1378 case TYPE_CONTROL_DEF:
1379 {
1380 PCOLUMNDEF pColumnDef;
1381 if (!(arc = CreateColumn(pCurrentRow,
1382 FALSE, // no nested table
1383 (PVOID)pItemThis->ulData,
1384 &pColumnDef)))
1385 lstAppendItem(&pCurrentRow->llColumns,
1386 pColumnDef);
1387 break; }
1388
1389 /*
1390 * TYPE_END_TABLE:
1391 *
1392 */
1393
1394 case TYPE_END_TABLE:
1395 {
1396 PLISTNODE pNode = lstPop(&llStack);
1397 if (!pNode)
1398 // nothing on the stack:
1399 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1400 else
1401 {
1402 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1403 pCurrentTable = pStackItem->pLastTable;
1404 pCurrentRow = pStackItem->pLastRow;
1405
1406 lstRemoveNode(&llStack, pNode);
1407 }
1408 break; }
1409
1410 default:
1411 arc = DLGERR_INVALID_CODE;
1412 }
1413
1414 if (arc)
1415 break;
1416 }
1417
1418 if ((!arc) && (lstCountItems(&llStack)))
1419 arc = DLGERR_TABLE_NOT_CLOSED;
1420
1421 lstClear(&llStack);
1422
1423 return (arc);
1424}
1425
1426/*
1427 *@@ Dlg2_CalcSizes:
1428 *
1429 *@@added V0.9.15 (2001-08-26) [umoeller]
1430 */
1431
1432APIRET Dlg2_CalcSizes(PDLGPRIVATE pDlgData,
1433 PSIZEL pszlClient) // out: dialog's client size
1434{
1435 APIRET arc = ProcessAll(pDlgData,
1436 pszlClient,
1437 PROCESS_CALC_SIZES);
1438 // this goes into major recursions...
1439
1440 // free the cached font resources that
1441 // might have been created here
1442 if (pDlgData->hps)
1443 {
1444 if (pDlgData->lcidLast)
1445 {
1446 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1447 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1448 }
1449 WinReleasePS(pDlgData->hps);
1450 }
1451
1452 return (arc);
1453}
1454
1455/*
1456 *@@ Dlg3_PositionAndCreate:
1457 *
1458 *@@added V0.9.15 (2001-08-26) [umoeller]
1459 *@@changed V0.9.15 (2001-08-26) [umoeller]: BS_DEFAULT for other than first button was ignored, fixed
1460 */
1461
1462APIRET Dlg3_PositionAndCreate(PDLGPRIVATE pDlgData,
1463 PSIZEL pszlClient, // in: dialog's client size
1464 HWND *phwndFocusItem) // out: item to give focus to
1465{
1466 APIRET arc = NO_ERROR;
1467
1468 /*
1469 * 5) compute _positions_ of all controls
1470 *
1471 */
1472
1473 ProcessAll(pDlgData,
1474 pszlClient,
1475 PROCESS_CALC_POSITIONS);
1476
1477 /*
1478 * 6) create control windows, finally
1479 *
1480 */
1481
1482 pDlgData->ptlTotalOfs.x
1483 = pDlgData->ptlTotalOfs.y
1484 = SPACING;
1485
1486 ProcessAll(pDlgData,
1487 pszlClient,
1488 PROCESS_CREATE_CONTROLS);
1489
1490 if (pDlgData->hwndDefPushbutton)
1491 {
1492 // we had a default pushbutton:
1493 // go set it V0.9.14 (2001-08-21) [umoeller]
1494 WinSetWindowULong(pDlgData->hwndDlg,
1495 QWL_DEFBUTTON,
1496 pDlgData->hwndDefPushbutton);
1497 *phwndFocusItem = pDlgData->hwndDefPushbutton;
1498 // V0.9.15 (2001-08-26) [umoeller]
1499 }
1500 else
1501 *phwndFocusItem = (pDlgData->hwndFirstFocus)
1502 ? pDlgData->hwndFirstFocus
1503 : pDlgData->hwndDlg;
1504
1505 return (arc);
1506}
1507
1508/*
1509 *@@ Dlg9_Cleanup:
1510 *
1511 *@@added V0.9.15 (2001-08-26) [umoeller]
1512 */
1513
1514VOID Dlg9_Cleanup(PDLGPRIVATE *ppDlgData)
1515{
1516 PDLGPRIVATE pDlgData;
1517 if ( (ppDlgData)
1518 && (pDlgData = *ppDlgData)
1519 )
1520 {
1521 PLISTNODE pTableNode;
1522
1523 // in any case, clean up our mess:
1524
1525 // clean up the tables
1526 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1527 {
1528 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1529
1530 FreeTable(pTable);
1531 // this may recurse for nested tables
1532 }
1533
1534 lstClear(&pDlgData->llTables);
1535 lstClear(&pDlgData->llControls);
1536
1537 free(pDlgData);
1538
1539 *ppDlgData = NULL;
1540 }
1541}
1542
1543/* ******************************************************************
1544 *
1545 * Dialog formatter entry points
1546 *
1547 ********************************************************************/
1548
1549/*
1550 *@@ dlghCreateDlg:
1551 * replacement for WinCreateDlg/WinLoadDlg for creating a
1552 * dialog from a settings array in memory, which is
1553 * formatted automatically.
1554 *
1555 * This does NOT use regular dialog templates from
1556 * module resources. Instead, you pass in an array
1557 * of DLGHITEM structures, which define the controls
1558 * and how they are to be formatted.
1559 *
1560 * The main advantage compared to dialog resources is
1561 * that with this function, you will never have to
1562 * define control _positions_. Instead, you only specify
1563 * the control _sizes_, and all positions are computed
1564 * automatically here. Even better, for many controls,
1565 * auto-sizing is supported according to the control's
1566 * text (e.g. for statics and checkboxes).
1567 *
1568 * A regular standard dialog would use something like
1569 *
1570 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
1571 *
1572 * for flCreateFlags. To make the dlg sizeable, specify
1573 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
1574 *
1575 * <B>Usage:</B>
1576 *
1577 * Like WinLoadDlg, this creates a standard WC_FRAME and
1578 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
1579 * to the dialog with pCreateParams in mp2.
1580 *
1581 * If this func returns no error, you can then use
1582 * WinProcessDlg with the newly created dialog as usual. In
1583 * your dlg proc, use WinDefDlgProc as usual.
1584 *
1585 * There is NO run-time overhead for either code or memory
1586 * after dialog creation; after this function returns, the
1587 * dialog is a standard dialog as if loaded from WinLoadDlg.
1588 * The array of DLGHITEM structures defines how the
1589 * dialog is set up. All this is ONLY used by this function
1590 * and NOT needed after the dialog has been created.
1591 *
1592 * In DLGHITEM, the "Type" field determines what this
1593 * structure defines. A number of handy macros have been
1594 * defined to make this easier and to provide type-checking
1595 * at compile time. See dialog.h for more.
1596 *
1597 * Essentially, such a dialog item operates similarly to
1598 * HTML tables. There are rows and columns in the table,
1599 * and each control which is specified must be a column
1600 * in some table. Tables may also nest (see below).
1601 *
1602 * The macros are:
1603 *
1604 * -- START_TABLE starts a new table. The tables may nest,
1605 * but must each be properly terminated with END_TABLE.
1606 *
1607 * -- START_GROUP_TABLE(pDef) starts a group. This
1608 * behaves exacly like START_TABLE, but in addition,
1609 * it produces a static group control around the table.
1610 * Useful for group boxes. pDef must point to a
1611 * CONTROLDEF describing the control to be used for
1612 * the group (usually a WC_STATIC with SS_GROUP style),
1613 * whose size parameter is ignored.
1614 *
1615 * As with START_TABLE, START_GROUP_TABLE must be
1616 * terminated with END_TABLE.
1617 *
1618 * -- START_ROW(fl) starts a new row in a table (regular
1619 * or group). This must also be the first item after
1620 * the (group) table tag.
1621 *
1622 * fl specifies formatting flags for the row. This
1623 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
1624 * ROW_VALIGN_TOP and affects all items in the row.
1625 *
1626 * -- CONTROL_DEF(pDef) defines a control in a table row.
1627 * pDef must point to a CONTROLDEF structure.
1628 *
1629 * Again, there is is NO information in CONTROLDEF
1630 * about a control's _position_. Instead, the structure
1631 * only contains the _size_ of the control. All
1632 * positions are computed by this function, depending
1633 * on the sizes of the controls and their nesting within
1634 * the various tables.
1635 *
1636 * If you specify SZL_AUTOSIZE with either cx or cy
1637 * or both, the size of the control is even computed
1638 * automatically. Presently, this only works for statics
1639 * with SS_TEXT, SS_ICON, and SS_BITMAP, push buttons,
1640 * and radio and check boxes.
1641 *
1642 * Unless separated with START_ROW items, subsequent
1643 * control items will be considered to be in the same
1644 * row (== positioned next to each other).
1645 *
1646 * There are a few rules, whose violation will produce
1647 * an error:
1648 *
1649 * -- The entire array must be enclosed in a table
1650 * (the "root" table).
1651 *
1652 * -- After START_TABLE or START_GROUP_TABLE, there must
1653 * always be a START_ROW first.
1654 *
1655 * To create a dialog, set up arrays like the following:
1656 *
1657 + // control definitions referenced by DlgTemplate:
1658 + CONTROLDEF
1659 + (1) GroupDef = {
1660 + WC_STATIC, "", ....,
1661 + { 0, 0 }, // size, ignored for groups
1662 + 5 // spacing
1663 + },
1664 + (2) CnrDef = {
1665 + WC_CONTAINER, "", ....,
1666 + { 50, 50 }, // size
1667 + 5 // spacing
1668 + },
1669 + (3) Static = {
1670 + WC_STATIC, "Static below cnr", ...,
1671 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
1672 + 5 // spacing
1673 + },
1674 + (4) OKButton = {
1675 + WC_STATIC, "~OK", ...,
1676 + { 100, 30 }, // size
1677 + 5 // spacing
1678 + },
1679 + (5) CancelButton = {
1680 + WC_STATIC, "~Cancel", ...,
1681 + { 100, 30 }, // size
1682 + 5 // spacing
1683 + };
1684 +
1685 + DLGHITEM DlgTemplate[] =
1686 + {
1687 + START_TABLE, // root table, required
1688 + START_ROW(0), // row 1 in the root table, required
1689 + // create group on top
1690 + (1) START_GROUP_TABLE(&Group),
1691 + START_ROW(0),
1692 + (2) CONTROL_DEF(&CnrDef),
1693 + START_ROW(0),
1694 + (3) CONTROL_DEF(&Static),
1695 + END_TABLE, // end of group
1696 + START_ROW(0), // row 2 in the root table
1697 + // two buttons next to each other
1698 + (4) CONTROL_DEF(&OKButton),
1699 + (5) CONTROL_DEF(&CancelButton),
1700 + END_TABLE
1701 + }
1702 *
1703 * This will produce a dlg like this:
1704 *
1705 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1706 + º º
1707 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1708 + º ³ ³ º
1709 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1710 + º ³ ³ ³ ³ º
1711 + º ³ ³ Cnr inside group (2) ³ ³ º
1712 + º ³ ³ ³ ³ º
1713 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1714 + º ³ ³ º
1715 + º ³ Static below cnr (3) ³ º
1716 + º ³ ³ º
1717 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1718 + º º
1719 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1720 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1721 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1722 + º º
1723 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1724 *
1725 * <B>Errors:</B>
1726 *
1727 * This does not return a HWND, but an APIRET. This will be
1728 * one of the following:
1729 *
1730 * -- NO_ERROR: only in that case, the phwndDlg ptr
1731 * receives the HWND of the new dialog, which can
1732 * then be given to WinProcessDlg. Don't forget
1733 * WinDestroyWindow.
1734 *
1735 * -- ERROR_NOT_ENOUGH_MEMORY
1736 *
1737 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1738 * outside a table definition.
1739 *
1740 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1741 * appeared right after a table definition. You must
1742 * specify a row first.
1743 *
1744 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1745 * but the CONTROLDEF ptr was NULL.
1746 *
1747 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1748 * WC_FRAME window. Maybe an invalid owner was specified.
1749 *
1750 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1751 * DLGHITEM.
1752 *
1753 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1754 * improper nesting of TYPE_START_NEW_TABLE and
1755 * TYPE_END_TABLE fields.
1756 *
1757 * <B>Example:</B>
1758 *
1759 * The typical calling sequence would be:
1760 *
1761 + HWND hwndDlg = NULLHANDLE;
1762 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1763 + hwndOwner,
1764 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1765 + fnwpMyDlgProc,
1766 + "My Dlg Title",
1767 + DlgTemplate, // DLGHITEM array
1768 + ARRAYITEMCOUNT(DlgTemplate),
1769 + NULL, // mp2 for WM_INITDLG
1770 + "9.WarpSans")) // default font
1771 + {
1772 + ULONG idReturn = WinProcessDlg(hwndDlg);
1773 + WinDestroyWindow(hwndDlg);
1774 + }
1775 *
1776 *@@changed V0.9.14 (2001-07-07) [umoeller]: fixed disabled mouse with hwndOwner == HWND_DESKTOP
1777 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed major memory leaks with nested tables
1778 *@@changed V0.9.14 (2001-08-21) [umoeller]: fixed default push button problems
1779 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed bad owner if not direct desktop child
1780 */
1781
1782APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1783 HWND hwndOwner,
1784 ULONG flCreateFlags, // in: standard FCF_* frame flags
1785 PFNWP pfnwpDialogProc,
1786 PCSZ pcszDlgTitle,
1787 PCDLGHITEM paDlgItems, // in: definition array
1788 ULONG cDlgItems, // in: array item count (NOT array size)
1789 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1790 PCSZ pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1791{
1792 APIRET arc = NO_ERROR;
1793
1794 ULONG ul;
1795
1796 PDLGPRIVATE pDlgData = NULL;
1797
1798 HWND hwndDesktop = WinQueryDesktopWindow(NULLHANDLE, NULLHANDLE);
1799 // works with a null HAB
1800
1801 /*
1802 * 1) parse the table and create structures from it
1803 *
1804 */
1805
1806 if (!(arc = Dlg0_Init(&pDlgData,
1807 pcszControlsFont)))
1808 {
1809 if (!(arc = Dlg1_ParseTables(pDlgData,
1810 paDlgItems,
1811 cDlgItems)))
1812 {
1813 /*
1814 * 2) create empty dialog frame
1815 *
1816 */
1817
1818 FRAMECDATA fcData = {0};
1819 ULONG flStyle = 0;
1820 HWND hwndOwnersParent;
1821
1822 fcData.cb = sizeof(FRAMECDATA);
1823 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
1824
1825 if (flCreateFlags & FCF_SIZEBORDER)
1826 // dialog has size border:
1827 // add "clip siblings" style
1828 flStyle |= WS_CLIPSIBLINGS;
1829
1830 if (hwndOwner == HWND_DESKTOP)
1831 // there's some dumb XWorkplace code left
1832 // which uses this, and this disables the
1833 // mouse for some reason
1834 // V0.9.14 (2001-07-07) [umoeller]
1835 hwndOwner = NULLHANDLE;
1836
1837 // now, make sure the owner window is child of
1838 // HWND_DESKTOP... if it is not, we'll only disable
1839 // some dumb child window, which is not sufficient
1840 // V0.9.16 (2001-12-06) [umoeller]
1841 while ( (hwndOwner)
1842 && (hwndOwnersParent = WinQueryWindow(hwndOwner, QW_PARENT))
1843 && (hwndOwnersParent != hwndDesktop)
1844 )
1845 hwndOwner = hwndOwnersParent;
1846
1847 if (!(pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1848 WC_FRAME,
1849 (PSZ)pcszDlgTitle,
1850 flStyle, // style; invisible for now
1851 0, 0, 0, 0,
1852 hwndOwner,
1853 HWND_TOP,
1854 0, // ID
1855 &fcData,
1856 NULL))) // presparams
1857 arc = DLGERR_CANNOT_CREATE_FRAME;
1858 else
1859 {
1860 HWND hwndDlg = pDlgData->hwndDlg;
1861 HWND hwndFocusItem = NULLHANDLE;
1862 SIZEL szlClient = {0};
1863 RECTL rclClient;
1864
1865 /*
1866 * 3) compute size of all controls
1867 *
1868 */
1869
1870 if (!(arc = Dlg2_CalcSizes(pDlgData,
1871 &szlClient)))
1872 {
1873 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
1874
1875 /*
1876 * 4) compute size of dialog client from total
1877 * size of all controls
1878 */
1879
1880 // calculate the frame size from the client size
1881 rclClient.xLeft = 10;
1882 rclClient.yBottom = 10;
1883 rclClient.xRight = szlClient.cx + 2 * SPACING;
1884 rclClient.yTop = szlClient.cy + 2 * SPACING;
1885 WinCalcFrameRect(hwndDlg,
1886 &rclClient,
1887 FALSE); // frame from client
1888
1889 WinSetWindowPos(hwndDlg,
1890 0,
1891 10,
1892 10,
1893 rclClient.xRight,
1894 rclClient.yTop,
1895 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1896
1897 arc = Dlg3_PositionAndCreate(pDlgData,
1898 &szlClient,
1899 &hwndFocusItem);
1900
1901 /*
1902 * 7) WM_INITDLG, set focus
1903 *
1904 */
1905
1906 if (!WinSendMsg(pDlgData->hwndDlg,
1907 WM_INITDLG,
1908 (MPARAM)hwndFocusItem,
1909 (MPARAM)pCreateParams))
1910 {
1911 // if WM_INITDLG returns FALSE, this means
1912 // the dlg proc has not changed the focus;
1913 // we must then set the focus here
1914 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1915 }
1916 }
1917 }
1918 }
1919
1920 if (arc)
1921 {
1922 // error: clean up
1923 if (pDlgData->hwndDlg)
1924 {
1925 WinDestroyWindow(pDlgData->hwndDlg);
1926 pDlgData->hwndDlg = NULLHANDLE;
1927 }
1928 }
1929 else
1930 // no error: output dialog
1931 *phwndDlg = pDlgData->hwndDlg;
1932
1933 Dlg9_Cleanup(&pDlgData);
1934 }
1935
1936 if (arc)
1937 {
1938 CHAR szErr[300];
1939 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
1940 winhDebugBox(hwndOwner,
1941 "Error in Dialog Manager",
1942 szErr);
1943 }
1944
1945 return (arc);
1946}
1947
1948/*
1949 *@@ dlghFormatDlg:
1950 * similar to dlghCreateDlg in that this can
1951 * dynamically format dialog items.
1952 *
1953 * The differences however are the following:
1954 *
1955 * -- This assumes that hwndDlg already points
1956 * to a valid dialog frame and that this
1957 * dialog should be modified according to
1958 * flFlags.
1959 *
1960 * This is what's used in XWorkplace for notebook
1961 * settings pages since these always have to be
1962 * based on a resource dialog (which is loaded
1963 * empty).
1964 *
1965 * flFlags can be any combination of the following:
1966 *
1967 * -- DFFL_CREATECONTROLS: paDlgItems points to
1968 * an array of cDlgItems DLGHITEM structures
1969 * (see dlghCreateDlg) which is used for creating
1970 * subwindows in hwndDlg. By using this flag, the
1971 * function will essentially work like dlghCreateDlg,
1972 * except that the frame is already created.
1973 *
1974 * -- DFFL_RESIZEFRAME: hwndDlg should be resized so
1975 * that it will properly surround the controls.
1976 *
1977 * This can only be used in conjunction with
1978 * DFFL_RESIZEFRAME.
1979 *
1980 *@@added V0.9.16 (2001-09-29) [umoeller]
1981 */
1982
1983APIRET dlghFormatDlg(HWND hwndDlg, // in: dialog frame to work on
1984 PCDLGHITEM paDlgItems, // in: definition array
1985 ULONG cDlgItems, // in: array item count (NOT array size)
1986 PCSZ pcszControlsFont, // in: font for ctls with CTL_COMMON_FONT
1987 ULONG flFlags) // in: DFFL_* flags
1988{
1989 APIRET arc = NO_ERROR;
1990
1991 ULONG ul;
1992
1993 PDLGPRIVATE pDlgData = NULL;
1994
1995 /*
1996 * 1) parse the table and create structures from it
1997 *
1998 */
1999
2000 if (!(arc = Dlg0_Init(&pDlgData,
2001 pcszControlsFont)))
2002 {
2003 if (!(arc = Dlg1_ParseTables(pDlgData,
2004 paDlgItems,
2005 cDlgItems)))
2006 {
2007 /*
2008 * 2) create empty dialog frame
2009 *
2010 */
2011
2012 HWND hwndFocusItem = NULLHANDLE;
2013 SIZEL szlClient = {0};
2014 RECTL rclClient;
2015
2016 pDlgData->hwndDlg = hwndDlg;
2017
2018 /*
2019 * 3) compute size of all controls
2020 *
2021 */
2022
2023 Dlg2_CalcSizes(pDlgData,
2024 &szlClient);
2025
2026 // WinSubclassWindow(hwndDlg, pfnwpDialogProc);
2027
2028 /*
2029 * 4) compute size of dialog client from total
2030 * size of all controls
2031 */
2032
2033 if (flFlags & DFFL_RESIZEFRAME)
2034 {
2035 // calculate the frame size from the client size
2036 rclClient.xLeft = 10;
2037 rclClient.yBottom = 10;
2038 rclClient.xRight = szlClient.cx + 2 * SPACING;
2039 rclClient.yTop = szlClient.cy + 2 * SPACING;
2040 WinCalcFrameRect(hwndDlg,
2041 &rclClient,
2042 FALSE); // frame from client
2043
2044 WinSetWindowPos(hwndDlg,
2045 0,
2046 10,
2047 10,
2048 rclClient.xRight,
2049 rclClient.yTop,
2050 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
2051 }
2052
2053 if (flFlags & DFFL_CREATECONTROLS)
2054 {
2055 if (!(arc = Dlg3_PositionAndCreate(pDlgData,
2056 &szlClient,
2057 &hwndFocusItem)))
2058 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
2059 }
2060 }
2061
2062 Dlg9_Cleanup(&pDlgData);
2063 }
2064
2065 if (arc)
2066 {
2067 CHAR szErr[300];
2068 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
2069 winhDebugBox(NULLHANDLE,
2070 "Error in Dialog Manager",
2071 szErr);
2072 }
2073
2074 return (arc);
2075}
2076
2077/* ******************************************************************
2078 *
2079 * Dialog arrays
2080 *
2081 ********************************************************************/
2082
2083/*
2084 *@@ dlghCreateArray:
2085 * creates a "dialog array" for dynamically
2086 * building a dialog template in memory.
2087 *
2088 * A dialog array is simply an array of
2089 * DLGHITEM structures, as you would normally
2090 * define them statically in the source.
2091 * However, there are situations where you
2092 * might want to leave out certain controls
2093 * depending on certain conditions, which
2094 * can be difficult with static arrays.
2095 *
2096 * As a result, these "array" functions have
2097 * been added to allow for adding static
2098 * DLGHITEM subarrays to a dynamic array in
2099 * memory, which can then be passed to the
2100 * formatter.
2101 *
2102 * Usage:
2103 *
2104 * 1) Call this function with the maximum
2105 * amount of DLGHITEM's that will need
2106 * to be allocated in cMaxItems. Set this
2107 * to the total sum of all DLGHITEM's
2108 * in all the subarrays.
2109 *
2110 * 2) For each of the subarrays, call
2111 * dlghAppendToArray to have the subarray
2112 * appended to the dialog array.
2113 * After each call, DLGARRAY.cDlgItemsNow
2114 * will contain the actual total count of
2115 * DLGHITEM's that were added.
2116 *
2117 * 3) Call dlghCreateDialog with the dialog
2118 * array.
2119 *
2120 * 4) Call dlghFreeArray.
2121 *
2122 * Sort of like this (error checking omitted):
2123 *
2124 + DLGHITEM dlgSampleFront = ... // always included
2125 + DLGHITEM dlgSampleSometimes = ... // not always included
2126 + DLGHITEM dlgSampleTail = ... // always included
2127 +
2128 + PDLGARRAY pArraySample = NULL;
2129 + dlghCreateArray( ARRAYITEMCOUNT(dlgSampleFront)
2130 + + ARRAYITEMCOUNT(dlgSampleSometimes)
2131 + + ARRAYITEMCOUNT(dlgSampleTail),
2132 + &pArraySample);
2133 +
2134 + // always include front
2135 + dlghAppendToArray(pArraySample,
2136 + dlgSampleFront,
2137 + ARRAYITEMCOUNT(dlgSampleFront));
2138 + // include "sometimes" conditionally
2139 + if (...)
2140 + dlghAppendToArray(pArraySample,
2141 + dlgSampleSometimes,
2142 + ARRAYITEMCOUNT(dlgSampleSometimes));
2143 + // include tail always
2144 + dlghAppendToArray(pArraySample,
2145 + dlgSampleTail,
2146 + ARRAYITEMCOUNT(dlgSampleTail));
2147 +
2148 + // now create the dialog from the array
2149 + dlghCreateDialog(&hwndDlg,
2150 + hwndOwner,
2151 + FCF_ ...
2152 + fnwpMyDialogProc,
2153 + "Title",
2154 + pArray->paDialogItems, // dialog array!
2155 + pArray->cDlgItemsNow, // real count of items!
2156 + NULL,
2157 + NULL);
2158 +
2159 + dlghFreeArray(&pArraySample);
2160 *
2161 *@@added V0.9.16 (2001-10-15) [umoeller]
2162 */
2163
2164APIRET dlghCreateArray(ULONG cMaxItems,
2165 PDLGARRAY *ppArray) // out: DLGARRAY
2166{
2167 APIRET arc = NO_ERROR;
2168 PDLGARRAY pArray;
2169
2170 if (pArray = NEW(DLGARRAY))
2171 {
2172 ULONG cb;
2173
2174 ZERO(pArray);
2175 if ( (cb = cMaxItems * sizeof(DLGHITEM))
2176 && (pArray->paDlgItems = (DLGHITEM*)malloc(cb))
2177 )
2178 {
2179 memset(pArray->paDlgItems, 0, cb);
2180 pArray->cDlgItemsMax = cMaxItems;
2181 *ppArray = pArray;
2182 }
2183 else
2184 arc = ERROR_NOT_ENOUGH_MEMORY;
2185
2186 if (arc)
2187 dlghFreeArray(&pArray);
2188 }
2189 else
2190 arc = ERROR_NOT_ENOUGH_MEMORY;
2191
2192 return arc;
2193}
2194
2195/*
2196 *@@ dlghFreeArray:
2197 * frees a dialog array created by dlghCreateArray.
2198 *
2199 *@@added V0.9.16 (2001-10-15) [umoeller]
2200 */
2201
2202APIRET dlghFreeArray(PDLGARRAY *ppArray)
2203{
2204 PDLGARRAY pArray;
2205 if ( (ppArray)
2206 && (pArray = *ppArray)
2207 )
2208 {
2209 if (pArray->paDlgItems)
2210 free(pArray->paDlgItems);
2211 free(pArray);
2212 }
2213 else
2214 return ERROR_INVALID_PARAMETER;
2215
2216 return NO_ERROR;
2217}
2218
2219/*
2220 *@@ dlghAppendToArray:
2221 * appends a subarray of DLGHITEM's to the
2222 * given DLGARRAY. See dlghCreateArray for
2223 * usage.
2224 *
2225 * Returns:
2226 *
2227 * -- NO_ERROR
2228 *
2229 * -- ERROR_INVALID_PARAMETER
2230 *
2231 * -- DLGERR_ARRAY_TOO_SMALL: pArray does not
2232 * have enough memory to hold the new items.
2233 * The cMaxItems parameter given to dlghCreateArray
2234 * wasn't large enough.
2235 *
2236 *@@added V0.9.16 (2001-10-15) [umoeller]
2237 */
2238
2239APIRET dlghAppendToArray(PDLGARRAY pArray, // in: dialog array created by dlghCreateArray
2240 PCDLGHITEM paItems, // in: subarray to be appended
2241 ULONG cItems) // in: subarray item count (NOT array size)
2242{
2243 APIRET arc = NO_ERROR;
2244 if (pArray)
2245 {
2246 if ( (pArray->cDlgItemsMax >= cItems)
2247 && (pArray->cDlgItemsMax - pArray->cDlgItemsNow >= cItems)
2248 )
2249 {
2250 // enough space left in the array:
2251 memcpy(&pArray->paDlgItems[pArray->cDlgItemsNow],
2252 paItems, // source
2253 cItems * sizeof(DLGHITEM));
2254 pArray->cDlgItemsNow += cItems;
2255 }
2256 else
2257 arc = DLGERR_ARRAY_TOO_SMALL;
2258 }
2259 else
2260 arc = ERROR_INVALID_PARAMETER;
2261
2262 return (arc);
2263}
2264
2265/* ******************************************************************
2266 *
2267 * Standard dialogs
2268 *
2269 ********************************************************************/
2270
2271/*
2272 *@@ dlghCreateMessageBox:
2273 *
2274 *@@added V0.9.13 (2001-06-21) [umoeller]
2275 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
2276 */
2277
2278APIRET dlghCreateMessageBox(HWND *phwndDlg,
2279 HWND hwndOwner,
2280 HPOINTER hptrIcon,
2281 PCSZ pcszTitle,
2282 PCSZ pcszMessage,
2283 ULONG flFlags,
2284 PCSZ pcszFont,
2285 const MSGBOXSTRINGS *pStrings,
2286 PULONG pulAlarmFlag) // out: alarm sound to be played
2287{
2288 CONTROLDEF
2289 Icon = {
2290 WC_STATIC,
2291 NULL, // text, set below
2292 WS_VISIBLE | SS_ICON,
2293 0, // ID
2294 NULL, // no font
2295 0,
2296 { SZL_AUTOSIZE, SZL_AUTOSIZE },
2297 5
2298 },
2299 InfoText =
2300 {
2301 WC_STATIC,
2302 NULL, // text, set below
2303 WS_VISIBLE | SS_TEXT | DT_WORDBREAK | DT_LEFT | DT_TOP,
2304 10, // ID
2305 CTL_COMMON_FONT,
2306 0,
2307 { 400, SZL_AUTOSIZE },
2308 5
2309 },
2310 Buttons[] = {
2311 {
2312 WC_BUTTON,
2313 NULL, // text, set below
2314 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2315 1, // ID
2316 CTL_COMMON_FONT, // no font
2317 0,
2318 { 100, 30 },
2319 5
2320 },
2321 {
2322 WC_BUTTON,
2323 NULL, // text, set below
2324 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2325 2, // ID
2326 CTL_COMMON_FONT, // no font
2327 0,
2328 { 100, 30 },
2329 5
2330 },
2331 {
2332 WC_BUTTON,
2333 NULL, // text, set below
2334 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2335 3, // ID
2336 CTL_COMMON_FONT, // no font
2337 0,
2338 { 100, 30 },
2339 5
2340 }
2341 };
2342
2343 DLGHITEM MessageBox[] =
2344 {
2345 START_TABLE,
2346 START_ROW(ROW_VALIGN_CENTER),
2347 CONTROL_DEF(&Icon),
2348 START_TABLE,
2349 START_ROW(ROW_VALIGN_CENTER),
2350 CONTROL_DEF(&InfoText),
2351 START_ROW(ROW_VALIGN_CENTER),
2352 CONTROL_DEF(&Buttons[0]),
2353 CONTROL_DEF(&Buttons[1]),
2354 CONTROL_DEF(&Buttons[2]),
2355 END_TABLE,
2356 END_TABLE
2357 };
2358
2359 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2360
2361 const char *p0 = "Error",
2362 *p1 = NULL,
2363 *p2 = NULL;
2364
2365 Icon.pcszText = (PCSZ)hptrIcon;
2366 InfoText.pcszText = pcszMessage;
2367
2368 // now work on the three buttons of the dlg template:
2369 // give them proper titles or hide them
2370 if (flButtons == MB_OK)
2371 {
2372 p0 = pStrings->pcszOK;
2373 }
2374 else if (flButtons == MB_OKCANCEL)
2375 {
2376 p0 = pStrings->pcszOK;
2377 p1 = pStrings->pcszCancel;
2378 }
2379 else if (flButtons == MB_RETRYCANCEL)
2380 {
2381 p0 = pStrings->pcszRetry;
2382 p1 = pStrings->pcszCancel;
2383 }
2384 else if (flButtons == MB_ABORTRETRYIGNORE)
2385 {
2386 p0 = pStrings->pcszAbort;
2387 p1 = pStrings->pcszRetry;
2388 p2 = pStrings->pcszIgnore;
2389 }
2390 else if (flButtons == MB_YESNO)
2391 {
2392 p0 = pStrings->pcszYes;
2393 p1 = pStrings->pcszNo;
2394 }
2395 else if (flButtons == MB_YESNOCANCEL)
2396 {
2397 p0 = pStrings->pcszYes;
2398 p1 = pStrings->pcszNo;
2399 p2 = pStrings->pcszCancel;
2400 }
2401 else if (flButtons == MB_CANCEL)
2402 {
2403 p0 = pStrings->pcszCancel;
2404 }
2405 else if (flButtons == MB_ENTER)
2406 {
2407 p0 = pStrings->pcszEnter;
2408 }
2409 else if (flButtons == MB_ENTERCANCEL)
2410 {
2411 p0 = pStrings->pcszEnter;
2412 p1 = pStrings->pcszCancel;
2413 }
2414 else if (flButtons == MB_YES_YES2ALL_NO)
2415 {
2416 p0 = pStrings->pcszYes;
2417 p1 = pStrings->pcszYesToAll;
2418 p2 = pStrings->pcszNo;
2419 }
2420
2421 // now set strings and hide empty buttons
2422 Buttons[0].pcszText = p0;
2423
2424 if (p1)
2425 Buttons[1].pcszText = p1;
2426 else
2427 Buttons[1].flStyle &= ~WS_VISIBLE;
2428
2429 if (p2)
2430 Buttons[2].pcszText = p2;
2431 else
2432 Buttons[2].flStyle &= ~WS_VISIBLE;
2433
2434 // query default button IDs
2435 if (flFlags & MB_DEFBUTTON2)
2436 Buttons[1].flStyle |= BS_DEFAULT;
2437 else if (flFlags & MB_DEFBUTTON3)
2438 Buttons[2].flStyle |= BS_DEFAULT;
2439 else
2440 Buttons[0].flStyle |= BS_DEFAULT;
2441
2442 *pulAlarmFlag = WA_NOTE;
2443 if (flFlags & (MB_ICONHAND | MB_ERROR))
2444 *pulAlarmFlag = WA_ERROR;
2445 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
2446 *pulAlarmFlag = WA_WARNING;
2447
2448 return (dlghCreateDlg(phwndDlg,
2449 hwndOwner,
2450 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2451 WinDefDlgProc,
2452 pcszTitle,
2453 MessageBox,
2454 ARRAYITEMCOUNT(MessageBox),
2455 NULL,
2456 pcszFont));
2457}
2458
2459/*
2460 *@@ dlghProcessMessageBox:
2461 *
2462 *@@added V0.9.13 (2001-06-21) [umoeller]
2463 */
2464
2465ULONG dlghProcessMessageBox(HWND hwndDlg,
2466 ULONG ulAlarmFlag,
2467 ULONG flFlags)
2468{
2469 ULONG ulrcDlg;
2470 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
2471
2472 winhCenterWindow(hwndDlg);
2473
2474 if (flFlags & MB_SYSTEMMODAL)
2475 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
2476
2477 if (ulAlarmFlag)
2478 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
2479
2480 ulrcDlg = WinProcessDlg(hwndDlg);
2481
2482 WinDestroyWindow(hwndDlg);
2483
2484 if (flButtons == MB_OK)
2485 return MBID_OK;
2486 else if (flButtons == MB_OKCANCEL)
2487 switch (ulrcDlg)
2488 {
2489 case 1: return MBID_OK;
2490 default: return MBID_CANCEL;
2491 }
2492 else if (flButtons == MB_RETRYCANCEL)
2493 switch (ulrcDlg)
2494 {
2495 case 1: return MBID_RETRY;
2496 default: return MBID_CANCEL;
2497 }
2498 else if (flButtons == MB_ABORTRETRYIGNORE)
2499 switch (ulrcDlg)
2500 {
2501 case 2: return MBID_RETRY;
2502 case 3: return MBID_IGNORE;
2503 default: return MBID_ABORT;
2504 }
2505 else if (flButtons == MB_YESNO)
2506 switch (ulrcDlg)
2507 {
2508 case 1: return MBID_YES;
2509 default: return MBID_NO;
2510 }
2511 else if (flButtons == MB_YESNOCANCEL)
2512 switch (ulrcDlg)
2513 {
2514 case 1: return MBID_YES;
2515 case 2: return MBID_NO;
2516 default: return MBID_CANCEL;
2517 }
2518 else if (flButtons == MB_CANCEL)
2519 return MBID_CANCEL;
2520 else if (flButtons == MB_ENTER)
2521 return MBID_ENTER;
2522 else if (flButtons == MB_ENTERCANCEL)
2523 switch (ulrcDlg)
2524 {
2525 case 1: return MBID_ENTER;
2526 default: return MBID_CANCEL;
2527 }
2528 else if (flButtons == MB_YES_YES2ALL_NO)
2529 switch (ulrcDlg)
2530 {
2531 case 1: return MBID_YES;
2532 case 2: return MBID_YES2ALL;
2533 default: return MBID_NO;
2534 }
2535
2536 return (MBID_CANCEL);
2537}
2538
2539/*
2540 *@@ dlghMessageBox:
2541 * WinMessageBox replacement.
2542 *
2543 * This has all the flags of the standard call,
2544 * but looks much prettier. Besides, it allows
2545 * you to specify any icon to be displayed.
2546 *
2547 * Currently the following flStyle's are supported:
2548 *
2549 * -- MB_OK 0x0000
2550 * -- MB_OKCANCEL 0x0001
2551 * -- MB_RETRYCANCEL 0x0002
2552 * -- MB_ABORTRETRYIGNORE 0x0003
2553 * -- MB_YESNO 0x0004
2554 * -- MB_YESNOCANCEL 0x0005
2555 * -- MB_CANCEL 0x0006
2556 * -- MB_ENTER 0x0007 (not implemented yet)
2557 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
2558 *
2559 * -- MB_YES_YES2ALL_NO 0x0009
2560 * This is new: this has three buttons called "Yes"
2561 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
2562 *
2563 * -- MB_DEFBUTTON2 (for two-button styles)
2564 * -- MB_DEFBUTTON3 (for three-button styles)
2565 *
2566 * -- MB_ICONHAND
2567 * -- MB_ICONEXCLAMATION
2568 *
2569 * Returns MBID_* codes like WinMessageBox.
2570 *
2571 *@@added V0.9.13 (2001-06-21) [umoeller]
2572 */
2573
2574ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
2575 HPOINTER hptrIcon, // in: icon to display
2576 PCSZ pcszTitle, // in: title
2577 PCSZ pcszMessage, // in: message
2578 ULONG flFlags, // in: standard message box flags
2579 PCSZ pcszFont, // in: font (e.g. "9.WarpSans")
2580 const MSGBOXSTRINGS *pStrings) // in: strings array
2581{
2582 HWND hwndDlg;
2583 ULONG ulAlarmFlag;
2584 APIRET arc = dlghCreateMessageBox(&hwndDlg,
2585 hwndOwner,
2586 hptrIcon,
2587 pcszTitle,
2588 pcszMessage,
2589 flFlags,
2590 pcszFont,
2591 pStrings,
2592 &ulAlarmFlag);
2593
2594 if (!arc && hwndDlg)
2595 {
2596 // SHOW DIALOG
2597 return (dlghProcessMessageBox(hwndDlg,
2598 ulAlarmFlag,
2599 flFlags));
2600 }
2601 else
2602 {
2603 CHAR szMsg[100];
2604 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
2605 WinMessageBox(HWND_DESKTOP,
2606 NULLHANDLE,
2607 "Error",
2608 szMsg,
2609 0,
2610 MB_CANCEL | MB_MOVEABLE);
2611 }
2612
2613 return (DID_CANCEL);
2614}
2615
2616/*
2617 *@@ cmnTextEntryBox:
2618 * common dialog for entering a text string.
2619 * The dialog has a descriptive text on top
2620 * with an entry field below and "OK" and "Cancel"
2621 * buttons.
2622 *
2623 * The string from the user is returned in a
2624 * new buffer, which must be free'd by the caller.
2625 * Returns NULL if the user pressed "Cancel".
2626 *
2627 * fl can be any combination of the following
2628 * flags:
2629 *
2630 * -- TEBF_REMOVETILDE: tilde ("~") characters
2631 * are removed from pcszTitle before setting
2632 * the title. Useful for reusing menu item
2633 * texts.
2634 *
2635 * -- TEBF_REMOVEELLIPSE: ellipse ("...") strings
2636 * are removed from pcszTitle before setting
2637 * the title. Useful for reusing menu item
2638 * texts.
2639 *
2640 * -- TEBF_SELECTALL: the default text in the
2641 * entry field is initially highlighted.
2642 *
2643 *@@added V0.9.15 (2001-09-14) [umoeller]
2644 */
2645
2646PSZ dlghTextEntryBox(HWND hwndOwner,
2647 PCSZ pcszTitle, // in: dlg title
2648 PCSZ pcszDescription, // in: descriptive text above entry field
2649 PCSZ pcszDefault, // in: default text for entry field or NULL
2650 PCSZ pcszOK, // in: "OK" string
2651 PCSZ pcszCancel, // in: "Cancel" string
2652 ULONG ulMaxLen, // in: maximum length for entry
2653 ULONG fl, // in: TEBF_* flags
2654 PCSZ pcszFont) // in: font (e.g. "9.WarpSans")
2655{
2656 CONTROLDEF
2657 Static = {
2658 WC_STATIC,
2659 NULL,
2660 WS_VISIBLE | SS_TEXT | DT_LEFT | DT_WORDBREAK,
2661 -1,
2662 CTL_COMMON_FONT,
2663 0,
2664 { 300, SZL_AUTOSIZE }, // size
2665 5 // spacing
2666 },
2667 Entry = {
2668 WC_ENTRYFIELD,
2669 NULL,
2670 WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_MARGIN | ES_AUTOSCROLL,
2671 999,
2672 CTL_COMMON_FONT,
2673 0,
2674 { 300, 20 }, // size
2675 5 // spacing
2676 },
2677 OKButton = {
2678 WC_BUTTON,
2679 NULL,
2680 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_DEFAULT,
2681 DID_OK,
2682 CTL_COMMON_FONT,
2683 0,
2684 { 100, 30 }, // size
2685 5 // spacing
2686 },
2687 CancelButton = {
2688 WC_BUTTON,
2689 NULL,
2690 WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
2691 DID_CANCEL,
2692 CTL_COMMON_FONT,
2693 0,
2694 { 100, 30 }, // size
2695 5 // spacing
2696 };
2697 DLGHITEM DlgTemplate[] =
2698 {
2699 START_TABLE,
2700 START_ROW(0),
2701 CONTROL_DEF(&Static),
2702 START_ROW(0),
2703 CONTROL_DEF(&Entry),
2704 START_ROW(0),
2705 CONTROL_DEF(&OKButton),
2706 CONTROL_DEF(&CancelButton),
2707 END_TABLE
2708 };
2709
2710 HWND hwndDlg = NULLHANDLE;
2711 PSZ pszReturn = NULL;
2712 XSTRING strTitle;
2713
2714 xstrInitCopy(&strTitle, pcszTitle, 0);
2715
2716 if (fl & (TEBF_REMOVEELLIPSE | TEBF_REMOVETILDE))
2717 {
2718 ULONG ulOfs;
2719 if (fl & TEBF_REMOVEELLIPSE)
2720 {
2721 ulOfs = 0;
2722 while (xstrFindReplaceC(&strTitle,
2723 &ulOfs,
2724 "...",
2725 ""))
2726 ;
2727 }
2728
2729 if (fl & TEBF_REMOVETILDE)
2730 {
2731 ulOfs = 0;
2732 while (xstrFindReplaceC(&strTitle,
2733 &ulOfs,
2734 "~",
2735 ""))
2736 ;
2737 }
2738 }
2739
2740 Static.pcszText = pcszDescription;
2741
2742 OKButton.pcszText = pcszOK;
2743 CancelButton.pcszText = pcszCancel;
2744
2745 if (NO_ERROR == dlghCreateDlg(&hwndDlg,
2746 hwndOwner,
2747 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
2748 WinDefDlgProc,
2749 strTitle.psz,
2750 DlgTemplate, // DLGHITEM array
2751 ARRAYITEMCOUNT(DlgTemplate),
2752 NULL,
2753 pcszFont))
2754 {
2755 HWND hwndEF = WinWindowFromID(hwndDlg, 999);
2756 winhCenterWindow(hwndDlg);
2757 winhSetEntryFieldLimit(hwndEF, ulMaxLen);
2758 if (pcszDefault)
2759 {
2760 WinSetWindowText(hwndEF, (PSZ)pcszDefault);
2761 if (fl & TEBF_SELECTALL)
2762 winhEntryFieldSelectAll(hwndEF);
2763 }
2764 WinSetFocus(HWND_DESKTOP, hwndEF);
2765 if (DID_OK == WinProcessDlg(hwndDlg))
2766 pszReturn = winhQueryWindowText(hwndEF);
2767
2768 WinDestroyWindow(hwndDlg);
2769 }
2770
2771 xstrClear(&strTitle);
2772
2773 return (pszReturn);
2774}
2775
2776/* ******************************************************************
2777 *
2778 * Dialog input handlers
2779 *
2780 ********************************************************************/
2781
2782/*
2783 *@@ dlghSetPrevFocus:
2784 * "backward" function for rotating the focus
2785 * in a dialog when the "shift+tab" keys get
2786 * pressed.
2787 *
2788 * pllWindows must be a linked list with the
2789 * plain HWND window handles of the focussable
2790 * controls in the dialog.
2791 */
2792
2793VOID dlghSetPrevFocus(PVOID pvllWindows)
2794{
2795 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2796
2797 // check current focus
2798 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
2799
2800 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
2801
2802 BOOL fRestart = FALSE;
2803
2804 while (pNode)
2805 {
2806 CHAR szClass[100];
2807
2808 // previos node
2809 pNode = pNode->pPrevious;
2810
2811 if ( (!pNode) // no next, or not found:
2812 && (!fRestart) // avoid infinite looping if bad list
2813 )
2814 {
2815 pNode = lstQueryLastNode(pllWindows);
2816 fRestart = TRUE;
2817 }
2818
2819 if (pNode)
2820 {
2821 // check if this is a focusable control
2822 if (WinQueryClassName((HWND)pNode->pItemData,
2823 sizeof(szClass),
2824 szClass))
2825 {
2826 if ( (strcmp(szClass, "#5")) // not static
2827 )
2828 break;
2829 // else: go for next then
2830 }
2831 }
2832 }
2833
2834 if (pNode)
2835 {
2836 WinSetFocus(HWND_DESKTOP,
2837 (HWND)pNode->pItemData);
2838 }
2839}
2840
2841/*
2842 *@@ dlghSetNextFocus:
2843 * "forward" function for rotating the focus
2844 * in a dialog when the "ab" key gets pressed.
2845 *
2846 * pllWindows must be a linked list with the
2847 * plain HWND window handles of the focussable
2848 * controls in the dialog.
2849 */
2850
2851VOID dlghSetNextFocus(PVOID pvllWindows)
2852{
2853 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2854
2855 // check current focus
2856 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
2857
2858 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
2859
2860 BOOL fRestart = FALSE;
2861
2862 while (pNode)
2863 {
2864 CHAR szClass[100];
2865
2866 // next focus in node
2867 pNode = pNode->pNext;
2868
2869 if ( (!pNode) // no next, or not found:
2870 && (!fRestart) // avoid infinite looping if bad list
2871 )
2872 {
2873 pNode = lstQueryFirstNode(pllWindows);
2874 fRestart = TRUE;
2875 }
2876
2877 if (pNode)
2878 {
2879 // check if this is a focusable control
2880 if (WinQueryClassName((HWND)pNode->pItemData,
2881 sizeof(szClass),
2882 szClass))
2883 {
2884 if ( (strcmp(szClass, "#5")) // not static
2885 )
2886 break;
2887 // else: go for next then
2888 }
2889 }
2890 }
2891
2892 if (pNode)
2893 {
2894 WinSetFocus(HWND_DESKTOP,
2895 (HWND)pNode->pItemData);
2896 }
2897}
2898
2899/*
2900 *@@ dlghProcessMnemonic:
2901 * finds the control which matches usch
2902 * and gives it the focus. If this is a
2903 * static control, the next control in the
2904 * list is given focus instead. (Standard
2905 * dialog behavior.)
2906 *
2907 * Pass in usch from WM_CHAR. It is assumed
2908 * that the caller has already tested for
2909 * the "alt" key to be depressed.
2910 *
2911 *@@added V0.9.9 (2001-03-17) [umoeller]
2912 */
2913
2914HWND dlghProcessMnemonic(PVOID pvllWindows,
2915 USHORT usch)
2916{
2917 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2918
2919 HWND hwndFound = NULLHANDLE;
2920 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2921 CHAR szClass[100];
2922
2923 while (pNode)
2924 {
2925 HWND hwnd = (HWND)pNode->pItemData;
2926
2927 if (WinSendMsg(hwnd,
2928 WM_MATCHMNEMONIC,
2929 (MPARAM)usch,
2930 0))
2931 {
2932 // according to the docs, only buttons and static
2933 // return TRUE to that msg;
2934 // if this is a static, give focus to the next
2935 // control
2936
2937 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
2938
2939 // check if this is a focusable control
2940 if (WinQueryClassName(hwnd,
2941 sizeof(szClass),
2942 szClass))
2943 {
2944 if (!strcmp(szClass, "#3"))
2945 // it's a button: click it
2946 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
2947 else if (!strcmp(szClass, "#5"))
2948 {
2949 // it's a static: give focus to following control
2950 pNode = pNode->pNext;
2951 if (pNode)
2952 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
2953 }
2954 }
2955 else
2956 // any other control (are there any?): give them focus
2957 WinSetFocus(HWND_DESKTOP, hwnd);
2958
2959 // in any case, stop
2960 hwndFound = hwnd;
2961 break;
2962 }
2963
2964 pNode = pNode->pNext;
2965 }
2966
2967 return (hwndFound);
2968}
2969
2970/*
2971 *@@ dlghEnter:
2972 * presses the first button with BS_DEFAULT.
2973 */
2974
2975BOOL dlghEnter(PVOID pvllWindows)
2976{
2977 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
2978
2979 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
2980 CHAR szClass[100];
2981 while (pNode)
2982 {
2983 HWND hwnd = (HWND)pNode->pItemData;
2984 if (WinQueryClassName(hwnd,
2985 sizeof(szClass),
2986 szClass))
2987 {
2988 if (!strcmp(szClass, "#3")) // button
2989 {
2990 // _Pmpf((__FUNCTION__ ": found button"));
2991 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
2992 == (BS_PUSHBUTTON | BS_DEFAULT)
2993 )
2994 {
2995 // _Pmpf((" is default!"));
2996 WinPostMsg(hwnd,
2997 BM_CLICK,
2998 (MPARAM)TRUE, // upclick
2999 0);
3000 return (TRUE);
3001 }
3002 }
3003 }
3004
3005 pNode = pNode->pNext;
3006 }
3007
3008 return (FALSE);
3009}
3010
3011
Note: See TracBrowser for help on using the repository browser.