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

Last change on this file since 209 was 209, checked in by umoeller, 23 years ago

Dialog formatter rewrite.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 158.2 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 * See @dlg_algorithm for the gory details of the new
10 * algorithm used since V0.9.21. Even though much
11 * of this file was rewritten, the new dialog is
12 * backwards-compatible with all the hacks that existing
13 * dialogs might use for aligning things properly.
14 *
15 * In addition, this has dlghMessageBox (a WinMessageBox
16 * replacement) and some helper functions for simulating
17 * dialog behavior in regular window procs (see
18 * dlghSetPrevFocus and others).
19 *
20 * If you are out to find all workarounds to get certain
21 * buggy PM controls aligned right, this file is definitely
22 * the place.
23 *
24 * Usage: All PM programs.
25 *
26 * Function prefixes (new with V0.81):
27 * -- dlg* Dialog functions
28 *
29 * Note: Version numbering in this file relates to XWorkplace version
30 * numbering.
31 *
32 *@@added V0.9.9 (2001-04-01) [umoeller]
33 *@@header "helpers\dialog.h"
34 */
35
36/*
37 * Copyright (C) 2001-2002 Ulrich M”ller.
38 * This file is part of the "XWorkplace helpers" source package.
39 * This is free software; you can redistribute it and/or modify
40 * it under the terms of the GNU General Public License as published
41 * by the Free Software Foundation, in version 2 as it comes in the
42 * "COPYING" file of the XWorkplace main distribution.
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 */
48
49// #define DEBUG_DIALOG_WINDOWS 1
50 // blue frame is table
51 // green frame is column
52 // red frame is control
53
54#define OS2EMX_PLAIN_CHAR
55 // this is needed for "os2emx.h"; if this is defined,
56 // emx will define PSZ as _signed_ char, otherwise
57 // as unsigned char
58
59#define INCL_DOSPROCESS
60#define INCL_DOSEXCEPTIONS
61#define INCL_DOSERRORS
62
63#define INCL_WINWINDOWMGR
64#define INCL_WINFRAMEMGR
65#define INCL_WINPOINTERS
66#define INCL_WININPUT
67#define INCL_WINDIALOGS
68#define INCL_WINSTATICS
69#define INCL_WINBUTTONS
70#define INCL_WINENTRYFIELDS
71#define INCL_WINSTDCNR
72#define INCL_WINSYS
73
74#define INCL_GPIPRIMITIVES
75#define INCL_GPIBITMAPS
76#define INCL_GPILCIDS
77#include <os2.h>
78
79#include <stdlib.h>
80#include <string.h>
81#include <stdio.h>
82#include <setjmp.h>
83
84#include "setup.h" // code generation and debugging options
85
86#include "helpers\comctl.h"
87#include "helpers\dialog.h"
88#include "helpers\except.h"
89#include "helpers\gpih.h"
90#include "helpers\linklist.h"
91#include "helpers\standards.h"
92#include "helpers\stringh.h"
93#include "helpers\textview.h"
94#include "helpers\winh.h"
95#include "helpers\xstring.h"
96
97#pragma hdrstop
98
99/*
100 *@@category: Helpers\PM helpers\Dialog templates
101 */
102
103/* ******************************************************************
104 *
105 * Glossary entries
106 *
107 ********************************************************************/
108
109/*
110 *@@gloss: dlg_using_macros Using the Dialog Formatter Macros
111 *
112 * The DLGHITEM macros are:
113 *
114 * -- START_TABLE starts a new table. The tables may nest,
115 * but must each be properly terminated with END_TABLE.
116 *
117 * Note that as opposed to HTML tables, the columns
118 * in the rows of the table are NOT aligned under each
119 * other. If that is what you want, use START_TABLE_ALIGN
120 * instead.
121 *
122 * -- START_GROUP_TABLE(pDef) starts a group. This
123 * behaves exacly like START_TABLE, but in addition,
124 * it produces a static group control around the table.
125 * Useful for group boxes. pDef must point to a
126 * CONTROLDEF describing the control to be used for
127 * the group (usually a WC_STATIC with SS_GROUP style),
128 * whose size parameter is ignored.
129 *
130 * As with START_TABLE, START_GROUP_TABLE must be
131 * terminated with END_TABLE.
132 *
133 * As with START_TABLE, columns in the rows of the table
134 * are NOT aligned under each other. If that is what you
135 * want, use START_GROUP_TABLE_ALIGN instead.
136 *
137 * -- START_ROW(fl) starts a new row in a table (regular
138 * or group). This must also be the first item after
139 * the (group) table tag.
140 *
141 * fl specifies formatting flags for the row. This
142 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
143 * ROW_VALIGN_TOP and affects all items in the row.
144 *
145 * -- CONTROL_DEF(pDef) defines a control in a table row.
146 * pDef must point to a CONTROLDEF structure.
147 *
148 * Again, there is is NO information in CONTROLDEF
149 * about a control's _position_. Instead, the structure
150 * only contains the _size_ of the control. All
151 * positions are computed by this function, depending
152 * on the sizes of the controls and their nesting within
153 * the various tables.
154 *
155 * If you specify SZL_AUTOSIZE with either cx or cy
156 * or both, the size of the control is even computed
157 * automatically. Presently, this only works for statics
158 * with SS_TEXT, SS_ICON, and SS_BITMAP, push buttons,
159 * and radio and check boxes.
160 *
161 * Unless separated with START_ROW items, subsequent
162 * control items will be considered to be in the same
163 * row (== positioned next to each other).
164 *
165 * Columns will only be aligned horizontally if the
166 * container table was specified with START_TABLE_ALIGN
167 * or START_GROUP_TABLE_ALIGN.
168 *
169 * There are a few rules, whose violation will produce
170 * an error:
171 *
172 * -- The entire array must be enclosed in a table
173 * (the "root" table).
174 *
175 * -- After START_TABLE or START_GROUP_TABLE, there must
176 * always be a START_ROW first.
177 *
178 * While it is possible to set up the CONTROLDEFs manually
179 * as static structures, I recommend using the bunch of
180 * other macros that were defined in dialog.h for this.
181 * For example, you can use CONTROLDEF_PUSHBUTTON to create
182 * a push button, and many more.
183 *
184 * To create a dialog, set up arrays like the following:
185 *
186 + // control definitions referenced by DlgTemplate:
187 + CONTROLDEF
188 + (1) GroupDef = CONTROLDEF_GROUP("Group",
189 + -1, // ID
190 + SZL_AUTOSIZE,
191 + SZL_AUTOSIZE),
192 + (2) CnrDef = CONTROLDEF_CONTAINER(-1, // ID,
193 + 50,
194 + 50),
195 + (3) Static = CONTROLDEF_TEXT("Static below cnr",
196 + -1, // ID
197 + SZL_AUTOSIZE,
198 + SZL_AUTOSIZE),
199 + (4) OKButton = CONTROLDEF_DEFPUSHBUTTON("~OK",
200 + DID_OK,
201 + SZL_AUTOSIZE,
202 + SZL_AUTOSIZE),
203 + (5) CancelButton = CONTROLDEF_PUSHBUTTON("~Cancel",
204 + DID_CANCEL,
205 + SZL_AUTOSIZE,
206 + SZL_AUTOSIZE);
207 +
208 + DLGHITEM DlgTemplate[] =
209 + {
210 + START_TABLE, // root table, required
211 + START_ROW(0), // row 1 in the root table, required
212 + // create group on top
213 + (1) START_GROUP_TABLE(&Group),
214 + START_ROW(0),
215 + (2) CONTROL_DEF(&CnrDef),
216 + START_ROW(0),
217 + (3) CONTROL_DEF(&Static),
218 + END_TABLE, // end of group
219 + START_ROW(0), // row 2 in the root table
220 + // two buttons next to each other
221 + (4) CONTROL_DEF(&OKButton),
222 + (5) CONTROL_DEF(&CancelButton),
223 + END_TABLE
224 + }
225 *
226 * This will produce a dlg like this:
227 *
228 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
229 + º º
230 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
231 + º ³ ³ º
232 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
233 + º ³ ³ ³ ³ º
234 + º ³ ³ Cnr inside group (2) ³ ³ º
235 + º ³ ³ ³ ³ º
236 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
237 + º ³ ³ º
238 + º ³ Static below cnr (3) ³ º
239 + º ³ ³ º
240 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
241 + º º
242 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
243 + º ³ OK (4) ³ ³ Cancel (5) ³ º
244 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
245 + º º
246 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
247 */
248
249/*
250 *@@gloss: dlg_boxmodel Dialog formatter box model
251 *
252 * We now use the CSS box model for the dialog
253 * formatter (V0.9.21).
254 *
255 + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
256 + ³ margin ³
257 + ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³
258 + ³ ³# border ###########³ ³
259 + ³ ³##ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿##³ ³
260 + ³ ³##³ padding ³##³ ³
261 + ³ ³##³ ÚÄÄÄÄÄÄÄÄÄ¿ ³##³ ³
262 + ³ ³##³ ³ content ³ ³##³ ³
263 + ³ ³##³ ÀÄÄÄÄÄÄÄÄÄÙ ³##³ ³
264 + ³ ³##³ ³##³ ³
265 + ³ ³##ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ##³ ³
266 + ³ ³####################³ ³
267 + ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³
268 + ³ ³
269 + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
270 *
271 * See "http://www.w3.org/TR/REC-CSS1#formatting-model".
272 *
273 * Remark on the box model for subtables:
274 *
275 * We do not want to apply spacing twice for nested tables. This
276 * is especially important for group boxes (i.e. tables which
277 * have a PM group static control created around them). As a
278 * result, the definition is that ONLY COLUMNS HAVE SPACING,
279 * EVER. Neither tables nor rows do.
280 */
281
282/*
283 *@@gloss: dlg_algorithm Dialog formatter algorithm
284 *
285 * See @dlg_boxmodel for information about how the
286 * rectangles are defined.
287 *
288 * The dialog formatter allows you to specify PM dialog layouts
289 * as nested tables, without having to specify a single control
290 * position. In fact, it is impossible to give the dialog
291 * formatter fixed positions. Instead, you give the formatter
292 * the control _sizes_, and everything is layed out automatically.
293 * You may even have the formatter compute the sizes automatically
294 * based on the control classes and values; it is possible to
295 * create dialogs without specifying a single size also.
296 *
297 * There are several tricks to how this works.
298 *
299 * 1) The dialog is supposed to consist of one outer table,
300 * which in turn has rows. The rows in turn have columns.
301 * We process the outer table by iterating over its rows
302 * (see ProcessTable). For each row, we iterate over the
303 * columns (see ProcessRow). Most of the processing then
304 * actually occurs in ProcessColumn.
305 *
306 * 2) Each column can contain a PM control, as specified in a
307 * CONTROLDEF structure on input. Alternatively, a column
308 * can be a nested subtable (with more rows and columns).
309 * If so, ProcessColumn recurses back into ProcessTable
310 * (which again goes into ProcessRow and ProcessColumn).
311 * There is no limit to how deep tables may nest, except
312 * the stack size of the current thread. ;-)
313 *
314 * 3) This whole recursive iteration is performed several times.
315 * In fact, we run through the entire data up to seven times
316 * presently. Only in the last run, we actually create the
317 * controls. Details follow.
318 *
319 * 4) We have sophisticated means for computing the size of
320 * certain controls automatically. See CalcAutoSize.
321 *
322 * The algorithm used here has been greatly reworked with V0.9.21.
323 * Essentially, the new trick is that we first compute all widths
324 * (which requires several iterations since widths may now inherit
325 * from each other), then all heights (which, for many controls,
326 * depend on the widths), then all positions. The previous approach
327 * to calculate widths and heights at the same time has turned out
328 * to not work very well.
329 *
330 * With each of the seven recursive iterations, ProcessTable,
331 * ProcessRow, and ProcessColumn receive a ProcessMode variable,
332 * which is just an integer that is raised with each run. These
333 * are the PROCESS_* constants all over the code.
334 *
335 * In the seven iterations, the following happens:
336 *
337 * 1) PROCESS_1_CALC_MIN_WIDTHS: calculates cxMinContent of all
338 * columns, rows, and tables from the inside to the outside
339 * recursively. cxMinContent specifies the minimum width of
340 * the column, row, and table, respectively. This value is
341 * required for all subsequent computations.
342 *
343 * This works as follows:
344 *
345 * -- From the root table, process all rows. [1]
346 *
347 * -- From each row, process all columns.
348 *
349 * -- For each column, calculate the minimum size of
350 * the content.
351 *
352 * (*) If the column is a control leaf node (i.e.
353 * does not hold a subtable), get the minimum
354 * size of the control.
355 *
356 * (*) If the CONTROLDEF specified an explicit
357 * width, copy it to cxMinContent.
358 *
359 * (*) If the CONTROLDEF specified SZL_AUTOSIZE
360 * for cx, evaluate the control type (icon,
361 * bitmap, text etc.) and set the minimum size
362 * to the result. See CalcAutoSize.
363 *
364 * (*) The control may specify its desired width
365 * as a percentage of the parent table's
366 * width. In that case, set the cxMinContent
367 * to 0 for now because it is to be ignored
368 * in the computation of the parent row and
369 * table widths.
370 *
371 * (*) If the column is a subtable, recurse with
372 * the subtable ([1] above) by calling ProcessTable
373 * again. Then set the control's cxMinContent to
374 * the table's cxMinBox, which was set by the
375 * subtable's ProcessTable (see below).
376 *
377 * After this, we have set the column's cxMinContent
378 * to either a minimum size (explicit or automatic )
379 * or 0.
380 *
381 * -- Set the row's cxMinBox to the sum of the minimum
382 * widths of the contained columns, with padding,
383 * border, and margin applied to each column.
384 *
385 * -- Set the table's cxMinBox to the cxMinBox of the table's
386 * widest row.
387 *
388 * 2) PROCESS_2_CALC_RELATIVE_WIDTHS: calculates the widths of
389 * those columns that have specified that their size should
390 * be a percentage of the parent table's width, and calculate
391 * the content rectangles of all columns and the box rectangles
392 * of the parent rows and tables. Again, do this recursively
393 * from the inside columns to the outside tables.
394 *
395 * First, we inherit table sizes from parent tables in
396 * ProcessTable. If a column has a subtable that has the
397 * TABLE_INHERIT_SIZE table flag set, compute a new cxMinBox
398 * for that table. _After_ that, recurse into the table to allow
399 * subtables of that subtable to inherit that size as well.
400 *
401 * In the columns, this then sets cpContent.cx in the columns,
402 * with the following processing in the control leaf columns:
403 *
404 * (*) If the control has specified an explicit or automatic
405 * width, we have set cxMinContent in PROCESS_1_CALC_MIN_WIDTHS;
406 * copy that width to cpContent.cx.
407 *
408 * (*) However, if the control had specified its desired
409 * width as a percentage of the parent table's width,
410 * calculate the cpContent.cx for the control from the
411 * table's cxMinBox (from PROCESS_1_CALC_MIN_WIDTHS).
412 *
413 * In the rows and tables, cpBox.cx and cpTable.cx is set as
414 * above from the column cpContent.cx values, with spacing applied.
415 *
416 * 3) PROCESS_3_ALIGN_COLUMNS: if the table has the TABLE_ALIGN_COLUMNS
417 * flag set, for each column in the table, find the widest
418 * instance in all rows, and set the cpContent.cx of all
419 * columns in that row to that width.
420 *
421 * 4) PROCESS_4_REMAINDER_WIDTHS: for all controls that have
422 * SZL_REMAINDER set for their widths, fill those so that
423 * they can use up the remaining width in the row.
424 *
425 * 5) PROCESS_5_CALC_HEIGHTS: Now that we know all widths,
426 * calculate the heights. We set cpContent.cy of all columns,
427 * cpRowBox.cy of all rows, and cpTableBox.cy of the tables,
428 * with the following processing in the control leaf columns:
429 *
430 * (*) If the control has specified an explicit or automatic
431 * height, use or compute that. In the case of autosize
432 * height, we now know the final width of the control,
433 * which is important for computing the height of multi-line
434 * static controls (which depend on the width, obviously).
435 * The new approach finally works for the case where a
436 * multi-line static control specifies its width as a
437 * percentage of the parent table, but still wants an
438 * autosize height.
439 *
440 * 6) PROCESS_6_CALC_POSITIONS: Now that we know all heights and
441 * widths, for each column, calculate cpContent.x and y.
442 *
443 * 7) PROCESS_7_CREATE_CONTROLS: finally creates the controls
444 * at the cpContent.x and y position, with the cpContent.cx
445 * and cy size.
446 */
447
448/* ******************************************************************
449 *
450 * Private declarations
451 *
452 ********************************************************************/
453
454/*
455 *@@ DLGPRIVATE:
456 * private data to the dlg manager, allocated
457 * by dlghCreateDlg.
458 *
459 * This only exists while the dialog is being
460 * created and is not stored with the new dialog.
461 */
462
463typedef struct _DLGPRIVATE
464{
465 HWND hwndDlg; // dialog frame
466
467 // definition data
468 LINKLIST llTables; // linked list of TABLEDEF structs
469
470 HWND hwndFirstFocus,
471 hwndDefPushbutton; // V0.9.14 (2001-08-21) [umoeller]
472
473 POINTL ptlTotalOfs;
474
475 PLINKLIST pllControls; // linked list of HWNDs in the order
476 // in which controls were created;
477 // ptr can be NULL
478
479 PCSZ pcszControlsFont; // from dlghCreateDlg
480
481 // size of the client to be created
482 SIZEL szlClient;
483
484 // various cached data V0.9.14 (2001-08-01) [umoeller]
485 HPS hps;
486 PCSZ pcszFontLast;
487 LONG lcidLast;
488 FONTMETRICS fmLast;
489
490 LONG cxBorder,
491 cyBorder; // cached now V0.9.19 (2002-04-17) [umoeller]
492
493 double dFactorX, // correlation factors for dialog units
494 dFactorY; // V0.9.19 (2002-04-24) [umoeller]
495
496 ULONG flNeedsProcessing; // flags to avoid unnecessary recursions:
497 #define FL_REMAINDER_WIDTHS 0x0001
498 // dialog needs PROCESS_4_REMAINDER_WIDTHS processing
499
500} DLGPRIVATE, *PDLGPRIVATE;
501
502// macros for the dlg units conversion;
503#define FACTOR_X (pDlgData->dFactorX)
504#ifdef USE_SQUARE_CORRELATION
505#define FACTOR_Y (pDlgData->dFactorX)
506#else
507#define FACTOR_Y (pDlgData->dFactorY)
508#endif
509
510typedef struct _COLUMNDEF *PCOLUMNDEF;
511typedef struct _ROWDEF *PROWDEF;
512typedef struct _TABLEDEF *PTABLEDEF;
513
514/*
515 *@@ CONTROLPOS:
516 * control position. We don't want to use SWP.
517 * We could have used RECTL, but the xRight
518 * etc. fields are always confusing with what
519 * we're doing... so we just define this.
520 */
521
522typedef struct _CONTROLPOS
523{
524 LONG x,
525 y,
526 cx,
527 cy;
528} CONTROLPOS, *PCONTROLPOS;
529
530/*
531 *@@ enProcessMode:
532 * enumeration that specifies the current processing
533 * mode while recursing through the tables, rows,
534 * and columns.
535 *
536 * For each processing mode, ProcessAll gets called
537 * while the dialog is being set up, which in turn
538 * calls ProcessTable for all tables, which in turn
539 * calls ProcessRow for all rows in that table,
540 * which in turn calls ProcessColumn for all rows
541 * in that column. ProcessColumn will recurse back
542 * into ProcessTable if a column represents a
543 * subtable.
544 *
545 * See @dlg_algorithm for what processing takes
546 * place with each of the process modes.
547 */
548
549typedef enum _enProcessMode
550{
551 PROCESS_1_CALC_MIN_WIDTHS,
552 PROCESS_2_CALC_RELATIVE_WIDTHS,
553 PROCESS_3_ALIGN_COLUMNS,
554 PROCESS_4_REMAINDER_WIDTHS,
555 PROCESS_5_CALC_HEIGHTS,
556 PROCESS_6_CALC_POSITIONS,
557 PROCESS_7_CREATE_CONTROLS
558} enProcessMode;
559
560/*
561 *@@ COLUMNDEF:
562 * representation of a column in a table row.
563 *
564 * Each COLUMNDEF is stored in a linked list
565 * in ROWDEF.llColumns.
566 *
567 * Such a column represents either a PM control
568 * window or another table, which may therefore
569 * be nested.
570 *
571 * See @dlg_algorithm for the new algorithm
572 * used since V0.9.21.
573 *
574 * With V0.9.21, we now follow the CSS box model
575 * to render these things better without all
576 * the hacks I am no longer able to understand
577 * myself. See @dlg_box_model for details.
578 *
579 *@@changed V0.9.21 (2002-08-18) [umoeller]: completely replaced
580 */
581
582typedef struct _COLUMNDEF
583{
584 // 1) input table set up by the DLGHITEM parser (CreateColumn):
585
586 // a) column position in recursion hierarchy
587
588 PROWDEF pOwningRow; // row whose linked list this column belongs to
589
590 PTABLEDEF pNestedTable; // if != NULL, this column represents a nested table,
591 // and this points to the contained table definition
592 // with which we must recurse back into ProcessTable().
593 // In that case, if (pcszClass != NULL) also, it
594 // must represent the PM static group control to
595 // create around the table.
596 // If (pNestedTable == NULL), this column is a control
597 // leaf node, and pcszClass and the following represent
598 // the control to create for the column.
599
600 ULONG ulColumnIndex; // zero-based column index in parent row
601 // V0.9.21 (2002-08-18) [umoeller]
602
603 // b) information for control window to be created:
604
605 PCSZ pcszClass; // class of control to create; if NULL, create no
606 // control; NULL is valid only for subtable columns
607 PCSZ pcszText; // window text; if NULL, it is replaced with "" in
608 // ColumnCreateControl
609 ULONG flStyle; // window style (should have at least WS_VISIBLE)
610 USHORT usID; // window ID or -1 for "don't care"
611 PCSZ pcszFont; // font to use for this window; this has CTL_COMMON_FONT
612 // resolved to the dialog default font already
613 SIZEL szlProposed; // proposed size for the control; copied from the
614 // CONTROLDEF with the dialog units already converted
615 // to pixels. Can be
616 // -- SZL_AUTOSIZE (-1) if the control wants to be autosized,
617 // -- SZL_REMAINDER (0) if the control wants the remaining
618 // space in its column,
619 // -- a negative number from [-100, -1] to specify the width
620 // as a percentage of the parent table _box_.
621 PVOID pvCtlData; // control-specific data for WinCreateWindow
622
623 // c) spacing:
624
625 RECTL rclPadding, // spacing, in pixels, to apply to the four borders of
626 rclMargin; // the content to get the box; calculated from CONTROLDEF
627 // V0.9.21 (2002-08-16) [umoeller]
628
629 LONG cxSpacingTotal; // sum of rclPadding and rclMargin xLeft and
630 // xRight, respectively
631
632 // 2) data from CalcAutoSize:
633
634 SIZEL szlAuto;
635
636 // 3) result data after recursion:
637
638 LONG cxMinContent; // minimum width of the content box;
639 // computed in PROCESS_1_CALC_MIN_WIDTHS
640
641 CONTROLPOS cpContent; // cx computed in PROCESS_2_CALC_RELATIVE_WIDTHS,
642 // cy computed in PROCESS_5_CALC_HEIGHTS,
643 // x and y computed in PROCESS_6_CALC_POSITIONS;
644 // this is where the control will be created
645
646 CONTROLPOS cpBox; // cx and cy computed in PROCESS_5_CALC_HEIGHTS,
647 // x and y computed in PROCESS_6_CALC_POSITIONS
648
649 HWND hwndControl; // created control after PROCESS_7_CREATE_CONTROLS;
650 // for tables, this is only set if we have created
651 // a PM group around them. Note: For text box
652 // controls, we create the control earlier,
653 // in CalcAutoSizeTextView during PROCESS_5_CALC_HEIGHTS,
654 // because only then we can ask the control for
655 // its required height.
656
657} COLUMNDEF;
658
659/*
660 *@@ ROWDEF:
661 * representation of a row in a table.
662 *
663 * Each ROWDEF is stored in a linked list in
664 * TABLEDEF.llRows and in turn contains a
665 * linked list of the contained COLUMNDEF structs
666 * in its llColumns member.
667 */
668
669typedef struct _ROWDEF
670{
671 // input table set up by the DLGHITEM parser:
672
673 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
674
675 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
676
677 ULONG flRowFormat; // one of:
678 // -- ROW_VALIGN_BOTTOM 0x0000
679 // -- ROW_VALIGN_CENTER 0x0001
680 // -- ROW_VALIGN_TOP 0x0002
681
682 ULONG cColumns; // no. of columns in this row
683 // V0.9.21 (2002-08-18) [umoeller]
684
685 // result data after recursion:
686
687 LONG cxMinBox; // set by PROCESS_1_CALC_MIN_WIDTHS to the sum of
688 // the minimum widths of the contained columns,
689 // with padding, border, and margin applied to each
690 // column
691
692 CONTROLPOS cpRowBox; // cx computed in PROCESS_2_CALC_RELATIVE_WIDTHS,
693 // cy computed in PROCESS_5_CALC_HEIGHTS,
694 // x and y computed in PROCESS_6_CALC_POSITIONS
695
696 ULONG cWantRemainders; // count of controls in the row that have
697 // SZL_REMAINDER set; counted in ProcessColumn
698 // with PROCESS_2_CALC_RELATIVE_WIDTHS
699 // so that we can quickly process
700 // PROCESS_4_REMAINDER_WIDTHS
701
702 LONG cxNonRemainder; // sum of the widths of those controls that do
703 // not have SZL_REMAINDER set; counted in
704 // ProcessColumn with PROCESS_3_ALIGN_COLUMNS
705 // so that we can quickly process
706 // PROCESS_4_REMAINDER_WIDTHS
707
708} ROWDEF;
709
710/*
711 *@@ TABLEDEF:
712 * representation of a table. If pOwningColumn
713 * is NULL, this is the dialog's root table.
714 * Otherwise this is a nested subtable in the
715 * specified column.
716 *
717 * Each TABLEDEF has its contained rows
718 * in the llRows member.
719 */
720
721typedef struct _TABLEDEF
722{
723 // input table set up by the DLGHITEM parser:
724
725 PCOLUMNDEF pOwningColumn; // != NULL if this is a nested table
726
727 LINKLIST llRows; // contains ROWDEF structs, no auto-free
728
729 ULONG flTable; // TABLE_* flags, copied from DLGHITEM
730
731 // result data after recursion:
732
733 LONG cxMinBox; // set by PROCESS_1_CALC_MIN_WIDTHS to the cxMinBox of
734 // the widest row
735
736 PROWDEF pWidestRow; // used during PROCESS_2_CALC_RELATIVE_WIDTHS to
737 // calculate percentage widths
738
739 CONTROLPOS cpTableBox; // cx computed in PROCESS_2_CALC_RELATIVE_WIDTHS,
740 // cy computed in PROCESS_5_CALC_HEIGHTS,
741 // x and y computed in PROCESS_6_CALC_POSITIONS
742
743} TABLEDEF;
744
745/* ******************************************************************
746 *
747 * Debug frames
748 *
749 ********************************************************************/
750
751#ifdef DEBUG_DIALOG_WINDOWS
752
753static PFNWP G_pfnwpStatic = NULL;
754
755/*
756 *@@ fnwpDebugFrame:
757 * window proc for the subclassed static that is used in
758 * debug mode to mark rectangles in the dialog. This
759 * was added to support painting dotted rectangles. The
760 * line type is expected to sit in QWL_USER. We use
761 * PP_FOREGROUNDCOLOR as the paint color for the rectangle.
762 *
763 * This is only compiled if DEBUG_DIALOG_WINDOWS is
764 * set.
765 *
766 *@@added V0.9.21 (2002-08-16) [umoeller]
767 */
768
769static MRESULT EXPENTRY fnwpDebugFrame(HWND hwndBox, ULONG msg, MPARAM mp1, MPARAM mp2)
770{
771 MRESULT mrc = 0;
772
773 switch (msg)
774 {
775 case WM_PAINT:
776 {
777 LONG lcol = winhQueryPresColor2(hwndBox,
778 PP_FOREGROUNDCOLOR,
779 PP_FOREGROUNDCOLORINDEX,
780 FALSE,
781 -1);
782 LONG lLineType = WinQueryWindowULong(hwndBox, QWL_USER);
783
784 HPS hps;
785 if (hps = WinBeginPaint(hwndBox, NULLHANDLE, NULL))
786 {
787 RECTL rcl;
788 POINTL ptl;
789 WinQueryWindowRect(hwndBox, &rcl);
790
791 gpihSwitchToRGB(hps);
792
793 GpiSetColor(hps, lcol);
794 GpiSetLineType(hps, lLineType);
795 ptl.x = 0;
796 ptl.y = 0;
797 GpiMove(hps, &ptl);
798 ptl.x = rcl.xRight - 1;
799 ptl.y = rcl.yTop - 1;
800 GpiBox(hps,
801 DRO_OUTLINE,
802 &ptl,
803 0,
804 0);
805 WinEndPaint(hps);
806 }
807 }
808 break;
809
810 default:
811 mrc = G_pfnwpStatic(hwndBox, msg, mp1, mp2);
812 }
813
814 return mrc;
815}
816
817/*
818 *@@ CreateDebugFrame:
819 * in debug mode, creates a subclassed static that
820 * shows the rectangles that were produced in the
821 * dialog. See fnwpDebugFrame.
822 *
823 * This is only compiled if DEBUG_DIALOG_WINDOWS is
824 * set.
825 *
826 *@@added V0.9.21 (2002-08-16) [umoeller]
827 */
828
829HWND CreateDebugFrame(HWND hwndParent,
830 LONG x,
831 LONG y,
832 LONG cx,
833 LONG cy,
834 LONG lcol,
835 LONG lLineType)
836{
837 HWND hwndDebug;
838 if (hwndDebug = WinCreateWindow(hwndParent,
839 WC_STATIC,
840 "",
841 WS_VISIBLE,
842 x,
843 y,
844 cx,
845 cy,
846 hwndParent, // owner
847 HWND_BOTTOM,
848 -1,
849 NULL,
850 NULL))
851 {
852 G_pfnwpStatic = WinSubclassWindow(hwndDebug, fnwpDebugFrame);
853 winhSetPresColor(hwndDebug, PP_FOREGROUNDCOLOR, lcol);
854 WinSetWindowULong(hwndDebug, QWL_USER, lLineType);
855 }
856
857 return hwndDebug;
858}
859
860#endif // DEBUG_DIALOG_WINDOWS
861
862/* ******************************************************************
863 *
864 * Column processing
865 *
866 ********************************************************************/
867
868// forward declaration so we can recurse into tables from columns
869
870static APIRET ProcessTable(PTABLEDEF pTableDef,
871 const CONTROLPOS *pcpTableBox,
872 enProcessMode ProcessMode,
873 PDLGPRIVATE pDlgData);
874
875/*
876 *@@ SetDlgFont:
877 * refreshes the cached font data in DLGPRIVATE
878 * for the given font. Used all the time with
879 * CalcAutoSize.
880 *
881 * In DLGPRIVATE, this modifies hps, lcidLast,
882 * and pcszFontLast. After this, the FONTMETRICS
883 * in fmLast are set.
884 *
885 *@@added V0.9.16 (2001-10-11) [umoeller]
886 */
887
888static VOID SetDlgFont(PCSZ pcszFontThis,
889 PDLGPRIVATE pDlgData)
890{
891 LONG lPointSize = 0;
892
893 if (!pDlgData->hps)
894 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
895
896 // check if we can reuse font data from last time
897 // V0.9.14 (2001-08-01) [umoeller]
898 if (strhcmp(pcszFontThis, // can be NULL!
899 pDlgData->pcszFontLast))
900 {
901 // different font than last time:
902
903 // delete old font?
904 if (pDlgData->lcidLast)
905 {
906 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT); // LCID_DEFAULT == 0
907 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
908 }
909
910 if (pcszFontThis)
911 {
912 // create new font
913 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
914 FALSE,
915 pDlgData->hps,
916 pcszFontThis,
917 &pDlgData->fmLast,
918 &lPointSize);
919
920 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
921 if (pDlgData->fmLast.fsDefn & FM_DEFN_OUTLINE)
922 gpihSetPointSize(pDlgData->hps, lPointSize);
923 }
924 else
925 {
926 // use default font:
927 // @@todo handle presparams, maybe inherited?
928 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
929 GpiQueryFontMetrics(pDlgData->hps,
930 sizeof(pDlgData->fmLast),
931 &pDlgData->fmLast);
932 }
933
934 pDlgData->pcszFontLast = pcszFontThis; // can be NULL
935 }
936}
937
938/*
939 *@@ CalcAutoSizeTextSingle:
940 * implementation for CalcAutoSize for single-line
941 * static text controls.
942 *
943 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
944 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed broken fonts
945 *@@changed V0.9.14 (2001-08-01) [umoeller]: now caching fonts, which is significantly faster
946 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
947 *@@changed V0.9.16 (2002-02-02) [umoeller]: added ulWidth
948 *@@changed V0.9.21 (2002-08-18) [umoeller]: renamed; moved multi-line processing to CalcAutoSizeTextMulti
949 */
950
951static APIRET CalcAutoSizeTextSingle(PCOLUMNDEF pColumn,
952 PDLGPRIVATE pDlgData)
953{
954 SetDlgFont(pColumn->pcszFont, pDlgData);
955
956 pColumn->szlAuto.cy = pDlgData->fmLast.lMaxBaselineExt
957 + pDlgData->fmLast.lExternalLeading;
958
959 // get the control string and see how much space it needs
960 if (pColumn->pcszText)
961 {
962 POINTL aptl[TXTBOX_COUNT];
963 if (!GpiQueryTextBox(pDlgData->hps,
964 strlen(pColumn->pcszText),
965 (PCH)pColumn->pcszText,
966 TXTBOX_COUNT,
967 aptl))
968 return DLGERR_GPIQUERYTEXTBOX;
969
970 pColumn->szlAuto.cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
971
972 return NO_ERROR;
973 }
974
975 return DLGERR_INVALID_CONTROL_TITLE;
976}
977
978/*
979 *@@ CalcAutoSizeTextMulti:
980 * implementation for CalcAutoSize for multi-line
981 * static text controls.
982 *
983 *@@added V0.9.21 (2002-08-18) [umoeller]
984 */
985
986static APIRET CalcAutoSizeTextMulti(PCOLUMNDEF pColumn,
987 ULONG ulWidth, // in: proposed width of control (req.)
988 PDLGPRIVATE pDlgData)
989{
990 SetDlgFont(pColumn->pcszFont, pDlgData);
991
992 // get the control string and see how much space it needs
993 if (pColumn->pcszText)
994 {
995 RECTL rcl = {0, 0, 0, 0};
996 rcl.xRight = ulWidth;
997 if (pColumn->szlProposed.cy > 0)
998 rcl.yTop = pColumn->szlProposed.cy;
999 else
1000 rcl.yTop = winhQueryScreenCY() * 2 / 3;
1001
1002 winhDrawFormattedText(pDlgData->hps,
1003 &rcl,
1004 pColumn->pcszText,
1005 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
1006 pColumn->szlAuto.cx = rcl.xRight - rcl.xLeft;
1007 pColumn->szlAuto.cy = rcl.yTop - rcl.yBottom;
1008
1009 return NO_ERROR;
1010 }
1011
1012 return DLGERR_INVALID_CONTROL_TITLE;
1013}
1014
1015/*
1016 *@@ CalcAutoSizeTextView:
1017 * implementation for CalcAutoSize for the XTextView
1018 * control.
1019 *
1020 * This is slightly sick. We create the control already
1021 * here in order to be able to have it format the text
1022 * and then send TXM_QUERYTEXTEXTENT to it, which was
1023 * added to the control with V0.9.20 for this very
1024 * purpose. The control that we create here will then be
1025 * reused by ColumnCreateControl and not be recreated,
1026 * which would be way too expensive.
1027 *
1028 *@@added V0.9.20 (2002-08-10) [umoeller]
1029 *@@changed V0.9.21 (2002-08-18) [umoeller]: removed temp windows list
1030 */
1031
1032static APIRET CalcAutoSizeTextView(PCOLUMNDEF pColumn,
1033 ULONG ulWidth, // in: proposed width of control (req.)
1034 PDLGPRIVATE pDlgData)
1035{
1036 APIRET arc = NO_ERROR;
1037
1038 PCSZ pcszTitle;
1039
1040 if ( (pColumn->hwndControl)
1041 || (pColumn->hwndControl = WinCreateWindow(pDlgData->hwndDlg, // parent
1042 (PSZ)pColumn->pcszClass,
1043 NULL,
1044 pColumn->flStyle,
1045 0,
1046 0,
1047 ulWidth,
1048 2000, // cy, for now
1049 pDlgData->hwndDlg, // owner
1050 HWND_BOTTOM,
1051 pColumn->usID,
1052 pColumn->pvCtlData,
1053 NULL))
1054 )
1055 {
1056 HWND hwnd = pColumn->hwndControl;
1057 SIZEL szlTemp;
1058
1059 if (pColumn->pcszFont)
1060 winhSetWindowFont(hwnd,
1061 pColumn->pcszFont);
1062
1063 WinSetWindowText(hwnd,
1064 (pcszTitle = pColumn->pcszText)
1065 ? (PSZ)pcszTitle
1066 : "");
1067
1068 WinSendMsg(hwnd,
1069 TXM_QUERYTEXTEXTENT,
1070 (MPARAM)&szlTemp,
1071 0);
1072
1073 pColumn->szlAuto.cy = szlTemp.cy;
1074 }
1075 else
1076 arc = DLGERR_CANNOT_CREATE_CONTROL;
1077
1078 return arc;
1079}
1080
1081/*
1082 *@@ CalcAutoSize:
1083 * helper func that gets called from ColumnCalcSizes for
1084 * every control that has set SZL_AUTOSIZE for its size.
1085 *
1086 * We try to be smart and set a correct size for the
1087 * control, depending on its class and data.
1088 *
1089 * This sets pColumn->szlAuto and possibly pre-creates
1090 * a window for the control and stores the window
1091 * in pColumn->hwndControl.
1092 *
1093 * Presently this works for
1094 *
1095 * -- static text, single and multiline
1096 *
1097 * -- static icons and bitmaps
1098 *
1099 * -- pushbuttons, radio buttons, and checkboxes
1100 *
1101 * -- the XTextView control (yes! V0.9.20).
1102 *
1103 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed various things with statics
1104 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
1105 *@@changed V0.9.20 (2002-08-10) [umoeller]: added support for textview
1106 */
1107
1108static APIRET CalcAutoSize(PCOLUMNDEF pColumn,
1109 ULONG ulWidth, // in: proposed width of control
1110 PDLGPRIVATE pDlgData)
1111{
1112 APIRET arc = NO_ERROR;
1113
1114 switch ((ULONG)pColumn->pcszClass)
1115 {
1116 case 0xffff0003L: // WC_BUTTON:
1117 if (!(arc = CalcAutoSizeTextSingle(pColumn,
1118 pDlgData)))
1119 {
1120 if (pColumn->flStyle & ( BS_AUTOCHECKBOX
1121 | BS_AUTORADIOBUTTON
1122 | BS_AUTO3STATE
1123 | BS_3STATE
1124 | BS_CHECKBOX
1125 | BS_RADIOBUTTON))
1126 {
1127 // give a little extra width for the box bitmap
1128 // V0.9.19 (2002-04-24) [umoeller]
1129 pColumn->szlAuto.cx += ctlQueryCheckboxSize() + 4;
1130 // and height
1131 pColumn->szlAuto.cy += 2;
1132 }
1133 else if (pColumn->flStyle & BS_BITMAP)
1134 ; // @@todo
1135 else if (pColumn->flStyle & (BS_ICON | BS_MINIICON))
1136 ; // @@todo
1137 // we can't test for BS_PUSHBUTTON because that's 0x0000
1138 else if (!(pColumn->flStyle & BS_USERBUTTON))
1139 {
1140 pColumn->szlAuto.cx += (2 * pDlgData->cxBorder + 15);
1141 pColumn->szlAuto.cy += (2 * pDlgData->cyBorder + 15);
1142 }
1143 }
1144 break;
1145
1146 case 0xffff0005L: // WC_STATIC:
1147 switch (pColumn->flStyle & 0x0F)
1148 {
1149 case SS_TEXT:
1150 if (pColumn->flStyle & DT_WORDBREAK)
1151 // multi-line:
1152 arc = CalcAutoSizeTextMulti(pColumn,
1153 ulWidth,
1154 pDlgData);
1155 else
1156 // single-line:
1157 arc = CalcAutoSizeTextSingle(pColumn,
1158 pDlgData);
1159 break;
1160
1161 case SS_BITMAP:
1162 {
1163 HBITMAP hbm;
1164 if (hbm = (HBITMAP)pColumn->pcszText)
1165 {
1166 BITMAPINFOHEADER2 bmih2;
1167 ZERO(&bmih2);
1168 bmih2.cbFix = sizeof(bmih2);
1169 if (GpiQueryBitmapInfoHeader(hbm,
1170 &bmih2))
1171 {
1172 pColumn->szlAuto.cx = bmih2.cx;
1173 pColumn->szlAuto.cy = bmih2.cy;
1174 }
1175 else
1176 arc = DLGERR_GPIQUERYBITMAPINFOHEADER;
1177 }
1178 else
1179 arc = DLGERR_INVALID_STATIC_BITMAP;
1180 }
1181 break;
1182
1183 case SS_ICON:
1184 pColumn->szlAuto.cx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
1185 pColumn->szlAuto.cy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
1186 break;
1187 }
1188 break;
1189
1190 default:
1191 // added support for textview V0.9.20 (2002-08-10) [umoeller]
1192 if ( (((ULONG)pColumn->pcszClass & 0xFFFF0000) != 0xFFFF0000)
1193 // don't run strcmp on the PM pseudo-strings
1194 && (!strcmp(pColumn->pcszClass, WC_XTEXTVIEW))
1195 )
1196 {
1197 arc = CalcAutoSizeTextView(pColumn,
1198 ulWidth,
1199 pDlgData);
1200 }
1201 else
1202 {
1203 // any other control (just to be safe):
1204 SetDlgFont(pColumn->pcszFont, pDlgData);
1205 pColumn->szlAuto.cx = 50;
1206 pColumn->szlAuto.cy = pDlgData->fmLast.lMaxBaselineExt
1207 + pDlgData->fmLast.lExternalLeading
1208 + 7; // some space
1209 }
1210 }
1211
1212 return arc;
1213}
1214
1215/*
1216 *@@ ColumnCalcPositions:
1217 * implementation for PROCESS_6_CALC_POSITIONS in
1218 * ProcessColumn.
1219 *
1220 *@@added V0.9.15 (2001-08-26) [umoeller]
1221 *@@changed V0.9.16 (2001-10-15) [umoeller]: added APIRET
1222 *@@changed V0.9.19 (2002-04-24) [umoeller]: fixed PM groups alignment
1223 *@@changed V0.9.21 (2002-08-16) [umoeller]: adjusted for new algorithm
1224 */
1225
1226static APIRET ColumnCalcPositions(PCOLUMNDEF pColumn,
1227 PROWDEF pOwningRow, // in: current row from ProcessRow
1228 PLONG plX, // in/out: PROCESS_6_CALC_POSITIONS only
1229 PDLGPRIVATE pDlgData)
1230{
1231 APIRET arc = NO_ERROR;
1232
1233 // column box = *plX on ProcessRow stack
1234 pColumn->cpBox.x = *plX;
1235 pColumn->cpBox.y = pOwningRow->cpRowBox.y;
1236
1237 // check vertical alignment of row;
1238 // we might need to increase column y
1239 switch (pOwningRow->flRowFormat & ROW_VALIGN_MASK)
1240 {
1241 // case ROW_VALIGN_BOTTOM: // do nothing
1242
1243 case ROW_VALIGN_CENTER:
1244 if (pColumn->cpBox.cy < pOwningRow->cpRowBox.cy)
1245 pColumn->cpBox.y += ( (pOwningRow->cpRowBox.cy - pColumn->cpBox.cy)
1246 / 2);
1247 break;
1248
1249 case ROW_VALIGN_TOP:
1250 if (pColumn->cpBox.cy < pOwningRow->cpRowBox.cy)
1251 pColumn->cpBox.y += (pOwningRow->cpRowBox.cy - pColumn->cpBox.cy);
1252 break;
1253 }
1254
1255 // increase plX by box width
1256 *plX += pColumn->cpBox.cx;
1257
1258 // calculate CONTROL pos from COLUMN pos by applying spacing
1259 pColumn->cpContent.x = pColumn->cpBox.x
1260 + pColumn->rclPadding.xLeft
1261 + pColumn->rclMargin.xLeft;
1262 pColumn->cpContent.y = pColumn->cpBox.y
1263 + pColumn->rclPadding.yBottom
1264 + pColumn->rclMargin.yBottom;
1265
1266 if (pColumn->pNestedTable)
1267 {
1268 // nested table:
1269 // recurse!! to create windows for the sub-table
1270 arc = ProcessTable(pColumn->pNestedTable,
1271 &pColumn->cpContent, // start pos for new table
1272 PROCESS_6_CALC_POSITIONS,
1273 pDlgData);
1274 }
1275
1276 return arc;
1277}
1278
1279/*
1280 *@@ ColumnCreateControl:
1281 * implementation for PROCESS_7_CREATE_CONTROLS in
1282 * ProcessColumn.
1283 *
1284 *@@added V0.9.15 (2001-08-26) [umoeller]
1285 *@@changed V0.9.16 (2001-10-15) [umoeller]: fixed ugly group table spacings
1286 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed entry field ES_MARGIN positioning
1287 *@@changed V0.9.19 (2002-04-17) [umoeller]: fixes for the STUPID drop-down comboboxes
1288 *@@changed V0.9.19 (2002-04-24) [umoeller]: fixed PM groups alignment
1289 *@@changed V0.9.21 (2002-08-16) [umoeller]: adjusted for new algorithm
1290 *@@changed V0.9.21 (2002-08-18) [umoeller]: setting entry field length to CCHMAXPATH per default now
1291 */
1292
1293static APIRET ColumnCreateControl(PCOLUMNDEF pColumn,
1294 PDLGPRIVATE pDlgData)
1295{
1296 APIRET arc = NO_ERROR;
1297
1298 PCSZ pcszClass = NULL;
1299 PCSZ pcszText = NULL;
1300 ULONG flStyle = 0;
1301 LHANDLE lHandleSet = NULLHANDLE;
1302 ULONG flOld = 0;
1303
1304 if (pColumn->pNestedTable)
1305 {
1306 // nested table:
1307 // recurse!!
1308 if (!(arc = ProcessTable(pColumn->pNestedTable,
1309 NULL,
1310 PROCESS_7_CREATE_CONTROLS,
1311 pDlgData)))
1312 {
1313 // should we create a PM control around the table?
1314 // (do this AFTER the other controls from recursing,
1315 // otherwise the stupid container doesn't show up)
1316 if (pcszClass = pColumn->pcszClass)
1317 {
1318 // yes:
1319 pcszText = pColumn->pcszText;
1320 flStyle = pColumn->flStyle;
1321
1322 // V0.9.21 (2002-08-16) [umoeller]
1323 // Removed all the terrible hacks for the PM group
1324 // control. We now use rclPadding and rclMargin
1325 // in the CONTROLDEF for determining the group
1326 // box position and size. Those were set initially
1327 // by the DLGHITEM parser. See CreateColumn().
1328 // If the group box had an explicit size set,
1329 // that is used by ProcessTable for the table
1330 // box also.
1331 }
1332
1333#ifdef DEBUG_DIALOG_WINDOWS
1334 // debug: create a frame with the exact size
1335 // of the table in blue
1336 CreateDebugFrame(pDlgData->hwndDlg, // parent
1337 pTableDef->cpTableBox.x + pDlgData->ptlTotalOfs.x,
1338 pTableDef->cpTableBox.y + pDlgData->ptlTotalOfs.y,
1339 pTableDef->cpTableBox.cx,
1340 pTableDef->cpTableBox.cy,
1341 RGBCOL_BLUE,
1342 LINETYPE_DOT);
1343#endif
1344 }
1345 }
1346 else
1347 {
1348 // no nested table, but control:
1349 pcszClass = pColumn->pcszClass;
1350 pcszText = pColumn->pcszText;
1351 flStyle = pColumn->flStyle;
1352 }
1353
1354 if ( (!arc) // check error code V0.9.21 (2002-08-16) [umoeller]
1355 && (pcszClass)
1356 )
1357 {
1358 // create something:
1359 LONG x, cx, y, cy; // for control hacks
1360 HWND hwndFound = NULLHANDLE;
1361 LONG cxUse;
1362 PCSZ pcszTextEF = NULL; // for entry field
1363
1364 // V0.9.21 (2002-08-16) [umoeller]
1365 // determine the position where to create the
1366 // control; this is the content box, to which
1367 // we add the absolute position on the dlgdata
1368 // stack, and padding. Padding is presently
1369 // only set for the group box around tables.
1370
1371 // To create the control, we use the explicit
1372 // or automatic size of the control instead
1373 // of the column width. Otherwise all buttons
1374 // are expanded to the column width, for example.
1375 if ( (!(cxUse = pColumn->cxMinContent))
1376 || (pColumn->pNestedTable)
1377 )
1378 cxUse = pColumn->cpContent.cx;
1379
1380 x = pColumn->cpContent.x
1381 - pColumn->rclPadding.xLeft
1382 + pDlgData->ptlTotalOfs.x;
1383 cx = cxUse
1384 + pColumn->rclPadding.xLeft
1385 + pColumn->rclPadding.xRight;
1386 y = pColumn->cpContent.y
1387 - pColumn->rclPadding.yBottom
1388 + pDlgData->ptlTotalOfs.y;
1389 cy = pColumn->cpContent.cy
1390 + pColumn->rclPadding.yBottom
1391 + pColumn->rclPadding.yTop;
1392
1393 // now implement hacks for certain controls
1394 switch ((ULONG)pcszClass)
1395 {
1396 case 0xffff0005L: // WC_STATIC:
1397 // change the title if this is a static with SS_BITMAP;
1398 // we have used a HBITMAP in there!
1399 if ( ( ((flStyle & 0x0F) == SS_BITMAP)
1400 || ((flStyle & 0x0F) == SS_ICON)
1401 )
1402 )
1403 {
1404 // change style flag to not use SS_BITMAP nor SS_ICON;
1405 // control creation fails otherwise (stupid, stupid PM)
1406 flOld = flStyle;
1407 flStyle = ((flStyle & ~0x0F) | SS_FGNDFRAME);
1408 pcszText = "";
1409 lHandleSet = (LHANDLE)pColumn->pcszText;
1410 }
1411 break;
1412
1413 case 0xffff0002L: // combobox
1414 if (flStyle & (CBS_DROPDOWN | CBS_DROPDOWNLIST))
1415 {
1416 // while calculating the column height,
1417 // we have set pColumn->cpContent.cy
1418 // to the height of a single line to get
1419 // the position calculations right...
1420 LONG cyDelta = pColumn->szlProposed.cy - pColumn->cpContent.cy;
1421 y -= cyDelta;
1422 cy += cyDelta;
1423 }
1424 break;
1425
1426 case 0xffff0006L: // entry field
1427 case 0xffff000AL: // MLE:
1428 // the stupid entry field resizes itself if it has
1429 // the ES_MARGIN style, so correlate that too... dammit
1430 // V0.9.16 (2001-12-08) [umoeller]
1431 if (flStyle & ES_MARGIN)
1432 {
1433 LONG cxMargin = 3 * pDlgData->cxBorder;
1434 LONG cyMargin = 3 * pDlgData->cyBorder;
1435
1436 x += cxMargin;
1437 y += cyMargin;
1438 cx -= 2 * cxMargin;
1439 cy -= 2 * cyMargin;
1440 }
1441
1442 if ((ULONG)pcszClass == 0xffff0006L)
1443 {
1444 // entry field:
1445 // defer setting the text because we should
1446 // first set the entry field limit
1447 // V0.9.21 (2002-08-18) [umoeller]
1448 pcszTextEF = pcszText;
1449 pcszText = ""; // for now
1450 }
1451 break;
1452 } // end switch ((ULONG)pControlDef->pcszClass)
1453
1454 if (pColumn->hwndControl)
1455 {
1456 // control was already created by CalcAutoSize:
1457 // resize it to what we really need
1458 WinSetWindowPos(pColumn->hwndControl,
1459 HWND_BOTTOM,
1460 x,
1461 y,
1462 cx,
1463 cy,
1464 SWP_SIZE | SWP_MOVE | SWP_ZORDER);
1465 }
1466 else
1467 {
1468 TRY_QUIET(excpt1)
1469 {
1470 pColumn->hwndControl = WinCreateWindow(pDlgData->hwndDlg, // parent
1471 (PSZ)pcszClass, // window class, hacked
1472 (pcszText) // control text, hacked
1473 ? (PSZ)pcszText
1474 : "",
1475 flStyle, // window style, hacked
1476 x,
1477 y,
1478 cx,
1479 cy,
1480 pDlgData->hwndDlg, // owner == parent
1481 HWND_BOTTOM,
1482 pColumn->usID,
1483 pColumn->pvCtlData,
1484 NULL);
1485 }
1486 CATCH(excpt1)
1487 {
1488 CHAR szClass2[100] = "Invalid mem",
1489 szText2[100] = "Invalid mem";
1490 ULONG Size, Attr;
1491 if (!(arc = DosQueryMem((PVOID)pcszClass, &Size, &Attr)))
1492 strhncpy0(szClass2, pcszClass, sizeof(szClass2));
1493 if (!(arc = DosQueryMem((PVOID)pcszClass, &Size, &Attr)))
1494 strhncpy0(szText2, pcszText, sizeof(szText2));
1495
1496 _Pmpf(("Crash creating control of class 0x%lX (%s), title 0x%lX (%s)",
1497 szClass2,
1498 szText2));
1499 arc = ERROR_PROTECTION_VIOLATION;
1500 } END_CATCH();
1501 }
1502
1503 if (pColumn->hwndControl)
1504 {
1505 // control created, or reused above:
1506
1507 if (lHandleSet)
1508 {
1509 // subclass the damn static
1510 if ((flOld & 0x0F) == SS_ICON)
1511 // this was a static:
1512 ctlPrepareStaticIcon(pColumn->hwndControl,
1513 1);
1514 else
1515 // this was a bitmap:
1516 ctlPrepareStretchedBitmap(pColumn->hwndControl,
1517 TRUE);
1518
1519 WinSendMsg(pColumn->hwndControl,
1520 SM_SETHANDLE,
1521 (MPARAM)lHandleSet,
1522 0);
1523 }
1524 else
1525 {
1526 if (pcszTextEF)
1527 {
1528 // entry field:
1529 // set text limit to CCHMAXPATH per default,
1530 // and set the real text now
1531 // V0.9.21 (2002-08-18) [umoeller]
1532 winhSetEntryFieldLimit(pColumn->hwndControl, CCHMAXPATH);
1533 WinSetWindowText(pColumn->hwndControl, (PSZ)pcszTextEF);
1534 }
1535
1536 if (pColumn->pcszFont)
1537 // we must set the font explicitly here...
1538 // doesn't always work with WinCreateWindow
1539 // presparams parameter, for some reason
1540 // V0.9.12 (2001-05-31) [umoeller]
1541 winhSetWindowFont(pColumn->hwndControl,
1542 pColumn->pcszFont);
1543 }
1544
1545#ifdef DEBUG_DIALOG_WINDOWS
1546 // if (!pColumn->fIsNestedTable)
1547 {
1548 // debug: create a frame with the exact size
1549 // of the box (not the content), so this
1550 // includes spacing
1551 CreateDebugFrame(pDlgData->hwndDlg, // parent
1552 pColumn->cpBox.x + pDlgData->ptlTotalOfs.x,
1553 pColumn->cpBox.y + pDlgData->ptlTotalOfs.y,
1554 pColumn->cpBox.cx,
1555 pColumn->cpBox.cy,
1556 RGBCOL_DARKGREEN,
1557 LINETYPE_SOLID);
1558
1559 // and another one for the content (control size)
1560 CreateDebugFrame(pDlgData->hwndDlg, // parent
1561 pColumn->cpContent.x + pDlgData->ptlTotalOfs.x,
1562 pColumn->cpContent.y + pDlgData->ptlTotalOfs.y,
1563 pColumn->cpContent.cx,
1564 pColumn->cpContent.cy,
1565 RGBCOL_RED,
1566 LINETYPE_ALTERNATE);
1567 }
1568#endif
1569
1570 // append window that was created
1571 // V0.9.18 (2002-03-03) [umoeller]
1572 if (pDlgData->pllControls)
1573 lstAppendItem(pDlgData->pllControls,
1574 (PVOID)pColumn->hwndControl);
1575
1576 // if this is the first control with WS_TABSTOP,
1577 // we'll give it the focus later
1578 if ( (flStyle & WS_TABSTOP)
1579 && (!pDlgData->hwndFirstFocus)
1580 )
1581 pDlgData->hwndFirstFocus = pColumn->hwndControl;
1582
1583 // if this is the first default push button,
1584 // go store it too
1585 // V0.9.14 (2001-08-21) [umoeller]
1586 if ( (!pDlgData->hwndDefPushbutton)
1587 && ((ULONG)pColumn->pcszClass == 0xffff0003L)
1588 && (pColumn->flStyle & BS_DEFAULT)
1589 )
1590 pDlgData->hwndDefPushbutton = pColumn->hwndControl;
1591 }
1592 else
1593 // V0.9.14 (2001-08-03) [umoeller]
1594 arc = DLGERR_CANNOT_CREATE_CONTROL;
1595 }
1596
1597 return arc;
1598}
1599
1600/* ******************************************************************
1601 *
1602 * Recursive workers for columns, rows, tables
1603 *
1604 ********************************************************************/
1605
1606/*
1607 *@@ ProcessColumn:
1608 * processes a column, which per definition is either
1609 * a control or a nested subtable.
1610 *
1611 * A column is part of a row, which in turn is part
1612 * of a table. There can be several columns in a row,
1613 * and several rows in a table.
1614 *
1615 * Since tables may be specified as columns, it is
1616 * possible to produce complex dialog layouts by
1617 * nesting tables.
1618 *
1619 * Preconditions:
1620 *
1621 * -- PROCESS_6_CALC_POSITIONS: position of each column
1622 * is taken from *plX, which is increased by the
1623 * column width by this call.
1624 *
1625 * Owning row must already have its y position properly
1626 * set, or we can't compute ours. Besides, plX must
1627 * point to the current X in the row and will be
1628 * incremented by the column's size here.
1629 *
1630 *@@changed V0.9.12 (2001-05-31) [umoeller]: added control data
1631 *@@changed V0.9.12 (2001-05-31) [umoeller]: fixed font problems
1632 *@@changed V0.9.20 (2002-08-08) [umoeller]: added support for aligning columns horizontally
1633 *@@changed V0.9.21 (2002-08-16) [umoeller]: calc size rewritten for new algorithm
1634 */
1635
1636static APIRET ProcessColumn(PCOLUMNDEF pColumn,
1637 PROWDEF pOwningRow, // in: current row from ProcessRow
1638 enProcessMode ProcessMode, // in: processing mode (see ProcessAll)
1639 PLONG plX, // in/out: PROCESS_6_CALC_POSITIONS only
1640 PDLGPRIVATE pDlgData)
1641{
1642 APIRET arc = NO_ERROR;
1643
1644 // pColumn->pOwningRow = pOwningRow;
1645
1646 switch (ProcessMode)
1647 {
1648 /*
1649 * PROCESS_1_CALC_MIN_WIDTHS:
1650 *
1651 */
1652
1653 case PROCESS_1_CALC_MIN_WIDTHS:
1654 // rewritten V0.9.21 (2002-08-16) [umoeller]
1655
1656 if (!pColumn->pNestedTable)
1657 {
1658 // control leaf node, not subtable:
1659
1660 // check if the control title is valid... we
1661 // crash later otherwise
1662 if ((ULONG)pColumn->pcszText > 0xFFFF0000)
1663 arc = DLGERR_INVALID_CONTROL_TITLE;
1664 else
1665 {
1666 if (pColumn->szlProposed.cx > 0)
1667 {
1668 // explicit size:
1669 pColumn->cxMinContent = pColumn->szlProposed.cx;
1670 }
1671 else if (pColumn->szlProposed.cx == SZL_AUTOSIZE) // -1
1672 {
1673 // autosize width:
1674 // calc then
1675 arc = CalcAutoSize(pColumn,
1676 1000, // width, whatever...
1677 // that's what we want to know!
1678 pDlgData);
1679
1680 pColumn->cxMinContent = pColumn->szlAuto.cx;
1681 }
1682 // else: -100 < width < -1: percentage of parent table,
1683 // leave cxMinContent == 0 for now
1684
1685 #ifdef DEBUG_DIALOG_WINDOWS
1686 if (pControlDef->pcszText)
1687 {
1688 CHAR szTemp[40];
1689 strhncpy0(szTemp, pControlDef->pcszText, sizeof(szTemp));
1690 strcpy(szTemp + 35, "...");
1691 _PmpfF(("PROCESS_1_CALC_MIN_WIDTHS [%s] cxMinContent %d",
1692 szTemp,
1693 pColumn->cxMinContent));
1694 }
1695 #endif
1696 }
1697 }
1698 else
1699 {
1700 // column represents subtable:
1701 // recurse!
1702 if (!(arc = ProcessTable(pColumn->pNestedTable,
1703 NULL,
1704 ProcessMode,
1705 pDlgData)))
1706 {
1707 // ProcessTable has set pTableDef->cxMinBox,
1708 // that is the size of the entire table box...
1709 // use that for our cxMinContent
1710 pColumn->cxMinContent = pColumn->pNestedTable->cxMinBox;
1711 }
1712 }
1713
1714 break; // PROCESS_1_CALC_MIN_WIDTHS
1715
1716 /*
1717 * PROCESS_2_CALC_RELATIVE_WIDTHS:
1718 *
1719 */
1720
1721 case PROCESS_2_CALC_RELATIVE_WIDTHS:
1722 // rewritten V0.9.21 (2002-08-16) [umoeller]
1723
1724 if (!pColumn->pNestedTable)
1725 {
1726 // control leaf node, not subtable:
1727 if ( (pColumn->szlProposed.cx == SZL_AUTOSIZE) // -1
1728 || (pColumn->szlProposed.cx > 0) // not SZL_REMAINDER
1729 )
1730 {
1731 // explicit size or autosize:
1732 // that was computed with PROCESS_1_CALC_MIN_WIDTHS,
1733 // so copy from cxMinContent
1734 pColumn->cpContent.cx = pColumn->cxMinContent;
1735 }
1736 else if (pColumn->szlProposed.cx < 0)
1737 {
1738 // control wants percentage of parent table width:
1739 PTABLEDEF pOwningTable = pOwningRow->pOwningTable;
1740 PROWDEF pWidestRow;
1741
1742 #ifdef DEBUG_DIALOG_WINDOWS
1743 {
1744 CHAR szTemp[40];
1745 strhncpy0(szTemp,
1746 (pControlDef->pcszText) ? pControlDef->pcszText : "NULL",
1747 sizeof(szTemp));
1748 strcpy(szTemp + 35, "...");
1749 _PmpfF(("PROCESS_2_CALC_RELATIVE_WIDTHS [%s]"));
1750 }
1751 #endif
1752
1753 if (pWidestRow = pOwningTable->pWidestRow)
1754 {
1755 PLISTNODE pNode;
1756 PCOLUMNDEF pSubColumn,
1757 pTablesColumn;
1758 LONG cxWidestRowsMinBox = pOwningTable->cxMinBox; // pWidestRow->cxMinBox;
1759
1760 _Pmpf((" pWidestRow->cxMinBox %d", pWidestRow->cxMinBox));
1761 _Pmpf((" pOwningTable->cxMinBox %d", pOwningTable->cxMinBox));
1762
1763 // now, since we're supposed to set our own CONTENT
1764 // rectangle, subtract our own padding and margin
1765 cxWidestRowsMinBox -= pColumn->cxSpacingTotal;
1766
1767 pColumn->cpContent.cx = (double)cxWidestRowsMinBox
1768 * -pColumn->szlProposed.cx
1769 / 100;
1770 }
1771
1772 #ifdef DEBUG_DIALOG_WINDOWS
1773 {
1774 _Pmpf((" cpContent.cx %d",
1775 pColumn->cpContent.cx));
1776 _Pmpf((" pWidestRow was 0x%lX", pWidestRow));
1777 }
1778 #endif
1779 } // end if (pControlDef->szlDlgUnits.cx < 0)
1780 else if (pColumn->szlProposed.cx == SZL_REMAINDER) // 0
1781 {
1782 // SZL_REMAINDER is calculated in PROCESS_4_REMAINDER_WIDTHS,
1783 // just count the remainders in the ROWDEF for now so
1784 // we can process them quickly later
1785 ++(pOwningRow->cWantRemainders);
1786
1787 // set the flag so that Dlg2_CalcSizes knows we'll
1788 // need PROCESS_4_REMAINDER_WIDTHS
1789 pDlgData->flNeedsProcessing |= FL_REMAINDER_WIDTHS;
1790 }
1791 }
1792 else
1793 {
1794 // column represents subtable:
1795 // recurse!
1796 if (!(arc = ProcessTable(pColumn->pNestedTable,
1797 NULL,
1798 ProcessMode,
1799 pDlgData)))
1800 {
1801 // ProcessTable has set pTableDef->cpTableBox.cx,
1802 // that is the size of the entire table box...
1803 // use that for our cpContent.cx
1804 pColumn->cpContent.cx = pColumn->pNestedTable->cpTableBox.cx;
1805 }
1806 }
1807 break; // PROCESS_2_CALC_RELATIVE_WIDTHS
1808
1809 /*
1810 * PROCESS_3_ALIGN_COLUMNS:
1811 *
1812 */
1813
1814 case PROCESS_3_ALIGN_COLUMNS:
1815 // rewritten V0.9.21 (2002-08-16) [umoeller]
1816
1817 if (!pColumn->pNestedTable)
1818 {
1819 // control leaf node, not subtable:
1820
1821 LONG cxWidest = 0;
1822 PTABLEDEF pOwningTable;
1823
1824 // if our owning table has the TABLE_ALIGN_COLUMNS
1825 // flag set, set this columns width to the width
1826 // of the widest column in all rows of this table,
1827 // but align this column ONLY if it does not want
1828 // SZL_REMAINDER
1829
1830 if ( (pColumn->szlProposed.cx != SZL_REMAINDER) // 0
1831 && (pOwningTable = pOwningRow->pOwningTable)
1832 && (pOwningTable->flTable & TABLE_ALIGN_COLUMNS)
1833 )
1834 {
1835 // find the widest corresponding column in the
1836 // surrounding table; pColumn->ulColumnIndex
1837 // has our index, so we need to run through
1838 // the rows and compare the widths of all
1839 // columns with that index
1840 PLISTNODE pRowNode;
1841
1842 FOR_ALL_NODES(&pOwningTable->llRows, pRowNode)
1843 {
1844 PROWDEF pRowThis = (PROWDEF)pRowNode->pItemData;
1845 PCOLUMNDEF pCorrespondingColumn;
1846 if ( (pCorrespondingColumn = (PCOLUMNDEF)lstItemFromIndex(&pRowThis->llColumns,
1847 pColumn->ulColumnIndex))
1848 && (pCorrespondingColumn->cxMinContent > cxWidest)
1849 )
1850 {
1851 #ifdef DEBUG_DIALOG_WINDOWS
1852 {
1853 const CONTROLDEF *pControlDef = (const CONTROLDEF *)pCorrespondingColumn->pvDefinition;
1854 if (pControlDef->pcszText)
1855 {
1856 CHAR szTemp[40];
1857 strhncpy0(szTemp, pControlDef->pcszText, sizeof(szTemp));
1858 strcpy(szTemp + 35, "...");
1859 _PmpfF((" widest column is [%s] cxMinContent %d",
1860 szTemp,
1861 pCorrespondingColumn->cxMinContent));
1862 }
1863 }
1864 #endif
1865
1866 cxWidest = pCorrespondingColumn->cxMinContent;
1867 }
1868 }
1869
1870 pColumn->cpContent.cx = cxWidest;
1871
1872 #ifdef DEBUG_DIALOG_WINDOWS
1873 {
1874 if (pControlDef->pcszText)
1875 {
1876 CHAR szTemp[40];
1877 strhncpy0(szTemp, pControlDef->pcszText, sizeof(szTemp));
1878 strcpy(szTemp + 35, "...");
1879 _PmpfF(("PROCESS_3_ALIGN_COLUMNS [%s] cpContent.cx %d",
1880 szTemp,
1881 pColumn->cpContent.cx));
1882 }
1883 }
1884 #endif
1885 }
1886 }
1887 else
1888 {
1889 // column represents subtable:
1890 // recurse!
1891 arc = ProcessTable(pColumn->pNestedTable,
1892 NULL,
1893 ProcessMode,
1894 pDlgData);
1895
1896 // the width of a table can NOT change from this
1897 // processing, so no need to adjust
1898 // (individual rows might have changed though)
1899 // @@todo wrong, if the first column is made
1900 // wider, the second column might now move over
1901 // the table width, fix that
1902 }
1903
1904 // in any case (control or subtable), count the
1905 // widths of all columns that do not have SZL_REMAINDER
1906 // set for PROCESS_4_REMAINDER_WIDTHS in the next loop
1907
1908 // NOTE: we add _all_ columns, including those that have
1909 // SZL_REMAINDER set (for which cpContent.cx is null
1910 // presently), because the "non remainder" space needs
1911 // to include the spacing of _all_ columns (see the
1912 // PROCESS_4_REMAINDER_WIDTHS processing below)
1913 pOwningRow->cxNonRemainder += pColumn->cpContent.cx
1914 + pColumn->cxSpacingTotal;
1915
1916 break; // PROCESS_3_ALIGN_COLUMNS
1917
1918 /*
1919 * PROCESS_4_REMAINDER_WIDTHS:
1920 *
1921 */
1922
1923 case PROCESS_4_REMAINDER_WIDTHS:
1924 if (!pColumn->pNestedTable)
1925 {
1926 // control leaf node, not subtable:
1927 PTABLEDEF pOwningTable;
1928
1929 if ( (pColumn->szlProposed.cx == SZL_REMAINDER) // 0
1930 && (pOwningTable = pOwningRow->pOwningTable)
1931 )
1932 {
1933 LONG cxRemaining = pOwningTable->cpTableBox.cx - pOwningRow->cxNonRemainder;
1934 // NOTE: pOwningRow->cxNonRemainder has
1935 // -- the sum of the column content rectangle widths that did not
1936 // have SZL_REMAINDER set
1937 // -- the sum of _all_ column spacings, even if the column had
1938 // SZL_REMAINDER set,
1939 // so this really has the leftover space now.
1940 // Divide that by the no. of columns in the row that have
1941 // SZL_REMAINDER set so that we evenly distribute the remaining
1942 // space among those columns.
1943 pColumn->cpContent.cx = ( cxRemaining
1944 / pOwningRow->cWantRemainders
1945 // this cannot be 0 since we have
1946 // that flag and were thus counted
1947 );
1948
1949 #ifdef DEBUG_DIALOG_WINDOWS
1950 {
1951 CHAR szTemp[40] = "NULL";
1952
1953 if (pControlDef->pcszText)
1954 {
1955 strhncpy0(szTemp, pControlDef->pcszText, sizeof(szTemp));
1956 strcpy(szTemp + 35, "...");
1957 }
1958
1959 _PmpfF(("PROCESS_4_REMAINDER_WIDTHS [%s] pOwningTable->cpTableBox.cx %d",
1960 szTemp,
1961 pOwningTable->cpTableBox.cx));
1962 _PmpfF((" pOwningRow->cxNonRemainder %d",
1963 pOwningRow->cxNonRemainder));
1964 _PmpfF((" pOwningRow->cWantRemainders %d",
1965 pOwningRow->cWantRemainders));
1966 _PmpfF((" cpContent.cx %d",
1967 pColumn->cpContent.cx));
1968 }
1969 #endif
1970
1971 }
1972 }
1973 else
1974 {
1975 // column represents subtable:
1976 // recurse!
1977 arc = ProcessTable(pColumn->pNestedTable,
1978 NULL,
1979 ProcessMode,
1980 pDlgData);
1981 }
1982 break;
1983
1984 /*
1985 * PROCESS_5_CALC_HEIGHTS:
1986 *
1987 */
1988
1989 case PROCESS_5_CALC_HEIGHTS:
1990 if (!pColumn->pNestedTable)
1991 {
1992 // control leaf node, not subtable:
1993 if (pColumn->szlProposed.cy > 0)
1994 {
1995 // explicit size:
1996 pColumn->cpContent.cy = pColumn->szlProposed.cy;
1997
1998 if ((ULONG)pColumn->pcszClass == 0xffff0002L)
1999 {
2000 // hack the stupid drop-down combobox where the
2001 // size of the drop-down is the full size of the
2002 // control: when creating the control, we _do_
2003 // specify the full size, but for the column,
2004 // we must rather use a single line with
2005 // the current font
2006 // V0.9.19 (2002-04-17) [umoeller]
2007 if (pColumn->flStyle & (CBS_DROPDOWN | CBS_DROPDOWNLIST))
2008 {
2009 SetDlgFont(pColumn->pcszFont, pDlgData);
2010
2011 pColumn->cpContent.cy
2012 = pDlgData->fmLast.lMaxBaselineExt
2013 + pDlgData->fmLast.lExternalLeading
2014 + 2 * 3 * pDlgData->cyBorder;
2015 }
2016 }
2017 }
2018 else if (pColumn->szlProposed.cy == SZL_AUTOSIZE) // -1
2019 {
2020 // autosize height:
2021 // we MIGHT have run autosize already above
2022 // (e.g. for the typical bitmap or icon statics),
2023 // so check if szlAuto.cy is already set
2024 if (!(pColumn->cpContent.cy = pColumn->szlAuto.cy))
2025 {
2026 // not yet determined:
2027 // calc then
2028 arc = CalcAutoSize(pColumn,
2029 // width: now we know
2030 pColumn->cpContent.cx,
2031 pDlgData);
2032 pColumn->cpContent.cy = pColumn->szlAuto.cy;
2033 }
2034 }
2035 // else: -100 < width < -1: @@todo
2036 // add another stage for using the percentage of
2037 // the table height
2038 }
2039 else
2040 {
2041 // column represents subtable:
2042 // recurse!
2043 if (!(arc = ProcessTable(pColumn->pNestedTable,
2044 NULL,
2045 ProcessMode,
2046 pDlgData)))
2047 {
2048 // ProcessTable has set pTableDef->cpTableBox.cy,
2049 // that is the size of the entire table box...
2050 // use that for our cpContent.cy
2051 pColumn->cpContent.cy = pColumn->pNestedTable->cpTableBox.cy;
2052 }
2053 }
2054
2055 // we finally know the DEFINITE width and height
2056 // of the content, so we can set the box width
2057 // and height here now (ProcessRow relies on this
2058 // to determine the row height)
2059 pColumn->cpBox.cx = pColumn->cpContent.cx
2060 + pColumn->cxSpacingTotal;
2061 pColumn->cpBox.cy = pColumn->cpContent.cy
2062 + pColumn->rclPadding.yBottom
2063 + pColumn->rclMargin.yBottom
2064 + pColumn->rclPadding.yTop
2065 + pColumn->rclMargin.yTop;
2066 break;
2067
2068 /*
2069 * PROCESS_6_CALC_POSITIONS:
2070 *
2071 */
2072
2073 case PROCESS_6_CALC_POSITIONS:
2074 arc = ColumnCalcPositions(pColumn,
2075 pOwningRow,
2076 plX,
2077 pDlgData);
2078 break;
2079
2080 /*
2081 * PROCESS_7_CREATE_CONTROLS:
2082 *
2083 */
2084
2085 case PROCESS_7_CREATE_CONTROLS:
2086 arc = ColumnCreateControl(pColumn,
2087 pDlgData);
2088 break;
2089 }
2090
2091 return arc;
2092}
2093
2094/*
2095 *@@ ProcessRow:
2096 * level-3 procedure (called from ProcessTable),
2097 * which in turn calls ProcessColumn for each column
2098 * in the row.
2099 *
2100 *@@changed V0.9.21 (2002-08-16) [umoeller]: adjusted for new algorithm
2101 */
2102
2103static APIRET ProcessRow(PROWDEF pRowDef,
2104 PTABLEDEF pOwningTable, // in: current table from ProcessTable
2105 enProcessMode ProcessMode, // in: processing mode (see ProcessAll)
2106 PLONG plY, // in/out: current y position (decremented)
2107 PDLGPRIVATE pDlgData)
2108{
2109 APIRET arc = NO_ERROR;
2110 LONG lXRowBox;
2111 PLISTNODE pNode;
2112
2113 pRowDef->pOwningTable = pOwningTable;
2114
2115 switch (ProcessMode)
2116 {
2117 case PROCESS_3_ALIGN_COLUMNS:
2118 // we've computed pRowDef->cpRowBox.cx in the
2119 // previous run (PROCESS_2_CALC_RELATIVE_WIDTHS),
2120 // but since we keep adding to it, we must
2121 // reset it now... the row will probably become
2122 // larger!
2123 pRowDef->cpRowBox.cx = 0;
2124 break;
2125
2126 case PROCESS_6_CALC_POSITIONS:
2127 // set up x and y so that the columns can
2128 // base on that
2129 pRowDef->cpRowBox.x = pOwningTable->cpTableBox.x;
2130 // decrease y by row height
2131 *plY -= pRowDef->cpRowBox.cy;
2132 // and use that for our bottom position
2133 pRowDef->cpRowBox.y = *plY;
2134
2135 // set lX to left of row; used by column calls below
2136 lXRowBox = pRowDef->cpRowBox.x;
2137 break;
2138 }
2139
2140 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
2141 {
2142 PCOLUMNDEF pColumnThis = (PCOLUMNDEF)pNode->pItemData;
2143
2144 if (!(arc = ProcessColumn(pColumnThis,
2145 pRowDef,
2146 ProcessMode,
2147 &lXRowBox,
2148 pDlgData)))
2149 {
2150 switch (ProcessMode)
2151 {
2152 case PROCESS_1_CALC_MIN_WIDTHS:
2153 // row width = sum of all columns
2154 pRowDef->cxMinBox += pColumnThis->cxMinContent
2155 + pColumnThis->cxSpacingTotal;
2156 break;
2157
2158 case PROCESS_2_CALC_RELATIVE_WIDTHS:
2159 case PROCESS_3_ALIGN_COLUMNS: // reapply, was reset above
2160 // row width = sum of all columns
2161 pRowDef->cpRowBox.cx += pColumnThis->cpContent.cx
2162 + pColumnThis->cxSpacingTotal;
2163 break;
2164
2165 case PROCESS_5_CALC_HEIGHTS:
2166 // row height = maximum height of a column
2167 if (pRowDef->cpRowBox.cy < pColumnThis->cpBox.cy)
2168 pRowDef->cpRowBox.cy = pColumnThis->cpBox.cy;
2169 }
2170 }
2171 // we should stop on errors V0.9.20 (2002-08-10) [umoeller]
2172 else
2173 break;
2174 }
2175
2176 return arc;
2177}
2178
2179/*
2180 *@@ ProcessTable:
2181 * level-2 procedure (called from ProcessAll),
2182 * which in turn calls ProcessRow for each row
2183 * in the table (which in turn calls ProcessColumn
2184 * for each column in the row).
2185 *
2186 * See ProcessAll for the meaning of ProcessMode.
2187 *
2188 * This routine is a bit sick because it can even be
2189 * called recursively from ProcessColumn (!) if a
2190 * nested table is found in a COLUMNDEF.
2191 *
2192 * With PROCESS_6_CALC_POSITIONS, pptl must specify
2193 * the lower left corner of the table. For the
2194 * root call, this will be {0, 0}; for nested calls,
2195 * this must be the lower left corner of the column
2196 * to which the nested table belongs.
2197 *
2198 *@@changed V0.9.21 (2002-08-16) [umoeller]: adjusted for new algorithm
2199 */
2200
2201static APIRET ProcessTable(PTABLEDEF pTableDef,
2202 const CONTROLPOS *pcpTableBox, // in: table position with PROCESS_6_CALC_POSITIONS
2203 enProcessMode ProcessMode, // in: processing mode (see ProcessAll)
2204 PDLGPRIVATE pDlgData)
2205{
2206 APIRET arc = NO_ERROR;
2207 LONG lY;
2208 PLISTNODE pNode;
2209
2210 switch (ProcessMode)
2211 {
2212 case PROCESS_2_CALC_RELATIVE_WIDTHS:
2213 // before calculating relative widths for
2214 // the table, inherit size from parent table
2215 // if the TABLE_INHERIT_SIZE table flag is
2216 // set; this was originally designed
2217 // as a separate step (PROCESS_2_INHERIT_TABLE_SIZES),
2218 // but we can save ourselves a loop here
2219 // V0.9.21 (2002-08-16) [umoeller]
2220 if (pTableDef->flTable & TABLE_INHERIT_SIZE)
2221 {
2222 PCOLUMNDEF pOwningColumn;
2223 PROWDEF pOwningRow;
2224 PTABLEDEF pOwningTable;
2225 if ( (!(pOwningColumn = pTableDef->pOwningColumn))
2226 || (!(pOwningRow = pOwningColumn->pOwningRow))
2227 || (!(pOwningTable = pOwningRow->pOwningTable))
2228 )
2229 arc = DLGERR_ROOT_TABLE_INHERIT_SIZE;
2230 else
2231 {
2232 PLISTNODE pRowNode;
2233 LONG cxMax = 0;
2234 FOR_ALL_NODES(&pOwningTable->llRows, pRowNode)
2235 {
2236 PROWDEF pRowThis = (PROWDEF)pRowNode->pItemData;
2237 if (pRowThis->cxMinBox > cxMax)
2238 cxMax = pRowThis->cxMinBox;
2239 }
2240
2241 _Pmpf((" cxMax found is %d", cxMax));
2242
2243 pTableDef->cxMinBox = cxMax
2244 - pOwningColumn->cxSpacingTotal;
2245 }
2246 }
2247 break;
2248
2249 case PROCESS_6_CALC_POSITIONS:
2250 pTableDef->cpTableBox.x = pcpTableBox->x;
2251 pTableDef->cpTableBox.y = pcpTableBox->y;
2252
2253 // start the rows on top
2254 lY = pcpTableBox->y + pTableDef->cpTableBox.cy;
2255 break;
2256 }
2257
2258 if (!arc)
2259 {
2260 FOR_ALL_NODES(&pTableDef->llRows, pNode)
2261 {
2262 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
2263
2264 if (!(arc = ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData)))
2265 {
2266 switch (ProcessMode)
2267 {
2268 case PROCESS_1_CALC_MIN_WIDTHS:
2269 // table width = maximum width of a row
2270 if (pTableDef->cxMinBox < pRowDefThis->cxMinBox)
2271 {
2272 pTableDef->cxMinBox = pRowDefThis->cxMinBox;
2273
2274 // remember the widest row so that
2275 // PROCESS_2_CALC_RELATIVE_WIDTHS can calc relative
2276 // widths from it
2277
2278 pTableDef->pWidestRow = pRowDefThis;
2279 }
2280 break;
2281
2282 case PROCESS_2_CALC_RELATIVE_WIDTHS:
2283 case PROCESS_3_ALIGN_COLUMNS:
2284 {
2285 PCOLUMNDEF pOwningColumn;
2286
2287 // table width = maximum width of a row
2288 if (pTableDef->cpTableBox.cx < pRowDefThis->cpRowBox.cx)
2289 pTableDef->cpTableBox.cx = pRowDefThis->cpRowBox.cx;
2290
2291 // preserve the inherited width if it was larger
2292 if (pTableDef->cxMinBox > pTableDef->cpTableBox.cx)
2293 pTableDef->cpTableBox.cx = pTableDef->cxMinBox;
2294
2295 // if the table has a group box with an explicit
2296 // size, use it if it's larger
2297 if ( (pOwningColumn = pTableDef->pOwningColumn)
2298 && (pOwningColumn->pcszClass)
2299 && (pOwningColumn->szlProposed.cx > 0)
2300 && (pOwningColumn->szlProposed.cx > pTableDef->cpTableBox.cx)
2301 )
2302 pTableDef->cpTableBox.cx = pOwningColumn->szlProposed.cx;
2303 }
2304 break;
2305
2306 case PROCESS_5_CALC_HEIGHTS:
2307 // table height = sum of all rows
2308 pTableDef->cpTableBox.cy += pRowDefThis->cpRowBox.cy;
2309 break;
2310 }
2311 }
2312 else
2313 break;
2314 }
2315 }
2316
2317 return arc;
2318}
2319
2320/*
2321 *@@ ProcessAll:
2322 * level-1 procedure, which in turn calls ProcessTable
2323 * for each root-level table found (which in turn
2324 * calls ProcessRow for each row in the table, which
2325 * in turn calls ProcessColumn for each column in
2326 * the row).
2327 */
2328
2329static APIRET ProcessAll(PDLGPRIVATE pDlgData,
2330 enProcessMode ProcessMode)
2331{
2332 APIRET arc = NO_ERROR;
2333 PLISTNODE pNode;
2334 CONTROLPOS cpTableBox;
2335 ZERO(&cpTableBox);
2336
2337 switch (ProcessMode)
2338 {
2339 case PROCESS_1_CALC_MIN_WIDTHS:
2340 pDlgData->szlClient.cx = 0;
2341 pDlgData->szlClient.cy = 0;
2342 break;
2343
2344 case PROCESS_6_CALC_POSITIONS:
2345 // start with the table on top
2346 cpTableBox.y = pDlgData->szlClient.cy;
2347 break;
2348 }
2349
2350 FOR_ALL_NODES(&pDlgData->llTables, pNode)
2351 {
2352 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
2353
2354 if (ProcessMode == PROCESS_6_CALC_POSITIONS)
2355 {
2356 cpTableBox.x = 0;
2357 cpTableBox.y -= pTableDefThis->cpTableBox.cy;
2358 }
2359
2360 if (!(arc = ProcessTable(pTableDefThis,
2361 &cpTableBox, // start pos
2362 ProcessMode,
2363 pDlgData)))
2364 {
2365 switch (ProcessMode)
2366 {
2367 case PROCESS_5_CALC_HEIGHTS:
2368 {
2369 // all sizes have now been computed:
2370 pDlgData->szlClient.cx += pTableDefThis->cpTableBox.cx;
2371 pDlgData->szlClient.cy += pTableDefThis->cpTableBox.cy;
2372 }
2373 }
2374 }
2375 // we should stop on errors V0.9.20 (2002-08-10) [umoeller]
2376 else
2377 break;
2378 }
2379
2380 return arc;
2381}
2382
2383/* ******************************************************************
2384 *
2385 * DLGHITEM parser
2386 *
2387 ********************************************************************/
2388
2389/*
2390 *@@ CreateColumn:
2391 *
2392 *@@changed V0.9.21 (2002-08-18) [umoeller]: mostly rewritten for new algorithm
2393 */
2394
2395static APIRET CreateColumn(PDLGPRIVATE pDlgData,
2396 PROWDEF pCurrentRow,
2397 PTABLEDEF pNestedTable,
2398 const CONTROLDEF *pControlDef,
2399 PCOLUMNDEF *ppColumn) // out: new COLUMNDEF
2400{
2401 APIRET arc = NO_ERROR;
2402
2403 if (!pCurrentRow)
2404 arc = DLGERR_CONTROL_BEFORE_ROW;
2405 else
2406 {
2407 // create column and store ctl def
2408 PCOLUMNDEF pColumn;
2409 if (!(pColumn = NEW(COLUMNDEF)))
2410 arc = ERROR_NOT_ENOUGH_MEMORY;
2411 else
2412 {
2413 ZERO(pColumn);
2414
2415 pColumn->pOwningRow = pCurrentRow;
2416
2417 if (pColumn->pNestedTable = pNestedTable)
2418 {
2419 // should we create a PM control around the table?
2420 if (pControlDef)
2421 {
2422 // yes:
2423 pColumn->rclPadding.xLeft
2424 = pColumn->rclPadding.xRight
2425 = GROUP_INNER_SPACING_X * FACTOR_X;
2426
2427 pColumn->rclPadding.yBottom = GROUP_INNER_SPACING_BOTTOM * FACTOR_Y;
2428
2429 pColumn->rclPadding.yTop = GROUP_INNER_SPACING_TOP * FACTOR_Y;
2430
2431 pColumn->rclMargin.xLeft = GROUP_OUTER_SPACING_X * FACTOR_X;
2432 pColumn->rclMargin.xRight = GROUP_OUTER_SPACING_X * FACTOR_X;
2433 pColumn->rclMargin.yBottom = GROUP_OUTER_SPACING_BOTTOM * FACTOR_Y;
2434 pColumn->rclMargin.yTop = GROUP_OUTER_SPACING_TOP * FACTOR_Y;
2435 }
2436 }
2437 else
2438 if (!pControlDef)
2439 arc = DLGERR_NULL_CONTROLDEF;
2440
2441 if (pControlDef)
2442 {
2443 // copy control fields
2444 pColumn->pcszClass = pControlDef->pcszClass;
2445 pColumn->pcszText = pControlDef->pcszText;
2446 pColumn->flStyle = pControlDef->flStyle;
2447 pColumn->usID = pControlDef->usID;
2448
2449 // resolve font
2450 if (pControlDef->pcszFont == CTL_COMMON_FONT)
2451 pColumn->pcszFont = pDlgData->pcszControlsFont;
2452 else
2453 pColumn->pcszFont = pControlDef->pcszFont;
2454 // can be NULL
2455
2456 // copy and convert proposed size
2457 if (pControlDef->szlDlgUnits.cx > 0)
2458 // not SZL_AUTOSIZE, not SZL_REMAINDER, not percentage:
2459 // convert from dlgunits to pixels
2460 pColumn->szlProposed.cx = pControlDef->szlDlgUnits.cx * FACTOR_X;
2461 else
2462 pColumn->szlProposed.cx = pControlDef->szlDlgUnits.cx;
2463
2464 if (pControlDef->szlDlgUnits.cy > 0)
2465 // not SZL_AUTOSIZE, not SZL_REMAINDER, not percentage:
2466 // convert from dlgunits to pixels
2467 pColumn->szlProposed.cy = pControlDef->szlDlgUnits.cy * FACTOR_Y;
2468 else
2469 pColumn->szlProposed.cy = pControlDef->szlDlgUnits.cy;
2470
2471 pColumn->pvCtlData = pControlDef->pvCtlData;
2472
2473 // note: we increase the margin here... it was null
2474 // unless set above because we had a PM group control
2475 // around a table
2476 pColumn->rclMargin.xLeft += pControlDef->duSpacing * FACTOR_X;
2477 pColumn->rclMargin.xRight += pControlDef->duSpacing * FACTOR_X;
2478
2479 pColumn->rclMargin.yBottom += pControlDef->duSpacing * FACTOR_Y;
2480 pColumn->rclMargin.yTop += pControlDef->duSpacing * FACTOR_Y;
2481 }
2482
2483 // calculate the column's total spacing width
2484 // for speed
2485 pColumn->cxSpacingTotal = pColumn->rclPadding.xLeft
2486 + pColumn->rclMargin.xLeft
2487 + pColumn->rclPadding.xRight
2488 + pColumn->rclMargin.xRight;
2489
2490 // set the column index V0.9.21 (2002-08-18) [umoeller]
2491 pColumn->ulColumnIndex = (pCurrentRow->cColumns)++;
2492
2493 *ppColumn = pColumn;
2494 }
2495 }
2496
2497 return arc;
2498}
2499
2500/*
2501 *@@ FreeTable:
2502 * frees the specified table and recurses
2503 * into nested tables, if necessary.
2504 *
2505 * This was added with V0.9.14 to fix the
2506 * bad memory leaks with nested tables.
2507 *
2508 *@@added V0.9.14 (2001-08-01) [umoeller]
2509 */
2510
2511static VOID FreeTable(PTABLEDEF pTable)
2512{
2513 // for each table, clean up the rows
2514 PLISTNODE pRowNode;
2515 FOR_ALL_NODES(&pTable->llRows, pRowNode)
2516 {
2517 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
2518
2519 // for each row, clean up the columns
2520 PLISTNODE pColumnNode;
2521 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
2522 {
2523 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
2524
2525 if (pColumn->pNestedTable)
2526 // nested table: recurse!
2527 FreeTable(pColumn->pNestedTable);
2528
2529 free(pColumn);
2530 }
2531 lstClear(&pRow->llColumns);
2532
2533 free(pRow);
2534 }
2535 lstClear(&pTable->llRows);
2536
2537 free(pTable);
2538}
2539
2540/*
2541 *@@ STACKITEM:
2542 *
2543 */
2544
2545typedef struct _STACKITEM
2546{
2547 PTABLEDEF pLastTable;
2548 PROWDEF pLastRow;
2549
2550} STACKITEM, *PSTACKITEM;
2551
2552/*
2553 *@@ Dlg0_Init:
2554 *
2555 *@@added V0.9.15 (2001-08-26) [umoeller]
2556 *@@changed V0.9.18 (2002-03-03) [umoeller]: added pllWindows
2557 *@@changed V0.9.19 (2002-04-24) [umoeller]: added resolution correlation
2558 */
2559
2560static APIRET Dlg0_Init(PDLGPRIVATE *ppDlgData,
2561 PCSZ pcszControlsFont,
2562 PLINKLIST pllControls)
2563{
2564 PDLGPRIVATE pDlgData;
2565 POINTL ptl = {100, 100};
2566
2567 if (!(pDlgData = NEW(DLGPRIVATE)))
2568 return (ERROR_NOT_ENOUGH_MEMORY);
2569
2570 ZERO(pDlgData);
2571 lstInit(&pDlgData->llTables, FALSE);
2572
2573 if (pllControls)
2574 pDlgData->pllControls = pllControls;
2575
2576 pDlgData->pcszControlsFont = pcszControlsFont;
2577
2578 // cache these now too V0.9.19 (2002-04-17) [umoeller]
2579 pDlgData->cxBorder = WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
2580 pDlgData->cyBorder = WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
2581
2582 // check how many pixels we get out of the
2583 // dlgunits (100/100) for mapping all sizes
2584 // V0.9.19 (2002-04-24) [umoeller]
2585 if (WinMapDlgPoints(NULLHANDLE,
2586 &ptl,
2587 1,
2588 TRUE))
2589 {
2590 // this worked:
2591 // for 1024x768, I get 200/250 out of the above,
2592 // so calculate a factor from that; we multiply
2593 // szlDlgUnits with this factor when calculating
2594 // the sizes
2595 pDlgData->dFactorX = (double)ptl.x / (double)100; // 2 on 1024x768
2596 pDlgData->dFactorY = (double)ptl.y / (double)100; // 2.5 on 1024x768
2597 }
2598 else
2599 {
2600 // didn't work:
2601 pDlgData->dFactorX = 2;
2602 pDlgData->dFactorY = 2.5;
2603 }
2604
2605 *ppDlgData = pDlgData;
2606
2607 return NO_ERROR;
2608}
2609
2610/*
2611 *@@ Dlg1_ParseTables:
2612 *
2613 *@@added V0.9.15 (2001-08-26) [umoeller]
2614 */
2615
2616static APIRET Dlg1_ParseTables(PDLGPRIVATE pDlgData,
2617 PCDLGHITEM paDlgItems, // in: definition array
2618 ULONG cDlgItems) // in: array item count (NOT array size)
2619{
2620 APIRET arc = NO_ERROR;
2621
2622 LINKLIST llStack;
2623 ULONG ul;
2624 PTABLEDEF pCurrentTable = NULL;
2625 PROWDEF pCurrentRow = NULL;
2626
2627 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
2628
2629 for (ul = 0;
2630 ul < cDlgItems;
2631 ul++)
2632 {
2633 PCDLGHITEM pItemThis = &paDlgItems[ul];
2634
2635 switch (pItemThis->Type)
2636 {
2637 /*
2638 * TYPE_START_NEW_TABLE:
2639 *
2640 */
2641
2642 case TYPE_START_NEW_TABLE:
2643 {
2644 // root table or nested?
2645 BOOL fIsRoot = (pCurrentTable == NULL);
2646
2647 // push the current table on the stack
2648 PSTACKITEM pStackItem;
2649 if (!(pStackItem = NEW(STACKITEM)))
2650 {
2651 arc = ERROR_NOT_ENOUGH_MEMORY;
2652 break;
2653 }
2654 else
2655 {
2656 pStackItem->pLastTable = pCurrentTable;
2657 pStackItem->pLastRow = pCurrentRow;
2658 lstPush(&llStack, pStackItem);
2659 }
2660
2661 // create new table
2662 if (!(pCurrentTable = NEW(TABLEDEF)))
2663 arc = ERROR_NOT_ENOUGH_MEMORY;
2664 else
2665 {
2666 const CONTROLDEF *pControlDef;
2667 ZERO(pCurrentTable);
2668
2669 lstInit(&pCurrentTable->llRows, FALSE);
2670
2671 // if control specified: store it (this will become a PM group)
2672 // pCurrentTable->pCtlDef = (const CONTROLDEF *)pItemThis->ul1; // pItemThis->pCtlDef;
2673 pControlDef = (const CONTROLDEF *)pItemThis->ul1;
2674 // can be NULL for plain table
2675
2676 pCurrentTable->flTable = pItemThis->ul2; // V0.9.20 (2002-08-08) [umoeller]
2677
2678 if (fIsRoot)
2679 // root table:
2680 // append to dialog data list
2681 lstAppendItem(&pDlgData->llTables, pCurrentTable);
2682 else
2683 {
2684 // nested table:
2685 // create "table" column for this in the current row
2686 PCOLUMNDEF pColumn;
2687 if (!(arc = CreateColumn(pDlgData,
2688 pCurrentRow,
2689 pCurrentTable, // nested table
2690 pControlDef,
2691 &pColumn)))
2692 {
2693 pCurrentTable->pOwningColumn = pColumn;
2694 lstAppendItem(&pCurrentRow->llColumns,
2695 pColumn);
2696 }
2697 }
2698 }
2699
2700 // reset current row so we can detect
2701 // wrong DLGHITEM ordering
2702 pCurrentRow = NULL;
2703 }
2704 break;
2705
2706 /*
2707 * TYPE_START_NEW_ROW:
2708 *
2709 */
2710
2711 case TYPE_START_NEW_ROW:
2712 if (!pCurrentTable)
2713 arc = DLGERR_ROW_BEFORE_TABLE;
2714 else
2715 {
2716 // create new row
2717 if (!(pCurrentRow = NEW(ROWDEF)))
2718 arc = ERROR_NOT_ENOUGH_MEMORY;
2719 else
2720 {
2721 ZERO(pCurrentRow);
2722
2723 pCurrentRow->pOwningTable = pCurrentTable;
2724 lstInit(&pCurrentRow->llColumns, FALSE);
2725
2726 pCurrentRow->flRowFormat = pItemThis->ul2;
2727
2728 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
2729 }
2730 }
2731 break;
2732
2733 /*
2734 * TYPE_CONTROL_DEF:
2735 *
2736 */
2737
2738 case TYPE_CONTROL_DEF:
2739 {
2740 PCOLUMNDEF pColumn;
2741 if (!(arc = CreateColumn(pDlgData,
2742 pCurrentRow,
2743 FALSE, // no nested table
2744 (PVOID)pItemThis->ul1, // pCtlDef, V0.9.21 (2002-08-18) [umoeller]
2745 &pColumn)))
2746 lstAppendItem(&pCurrentRow->llColumns,
2747 pColumn);
2748 }
2749 break;
2750
2751 /*
2752 * TYPE_END_TABLE:
2753 *
2754 */
2755
2756 case TYPE_END_TABLE:
2757 {
2758 PLISTNODE pNode;
2759 if (!(pNode = lstPop(&llStack)))
2760 // nothing on the stack:
2761 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
2762 else
2763 {
2764 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
2765 pCurrentTable = pStackItem->pLastTable;
2766 pCurrentRow = pStackItem->pLastRow;
2767
2768 lstRemoveNode(&llStack, pNode);
2769 }
2770 }
2771 break;
2772
2773 default:
2774 arc = DLGERR_INVALID_CODE;
2775 }
2776
2777 if (arc)
2778 break;
2779 }
2780
2781 // all tables should be closed now
2782 if ( (!arc)
2783 && (lstCountItems(&llStack))
2784 )
2785 arc = DLGERR_TABLE_NOT_CLOSED;
2786
2787 lstClear(&llStack);
2788
2789 return arc;
2790}
2791
2792/*
2793 *@@ Dlg2_CalcSizes:
2794 * calls ProcessAll with the first four process modes
2795 * for calculating the final sizes (but not yet the
2796 * positions) of the controls.
2797 *
2798 * See @dlg_algorithm for details.
2799 *
2800 * After this, DLGPRIVATE.szlClient is valid.
2801 *
2802 *@@added V0.9.15 (2001-08-26) [umoeller]
2803 *@@changed V0.9.21 (2002-08-16) [umoeller]: adjusted for new algorithm
2804 */
2805
2806static APIRET Dlg2_CalcSizes(PDLGPRIVATE pDlgData)
2807{
2808 APIRET arc;
2809
2810 // call process mode with the first for "calc size"
2811 // process modes
2812 // changed V0.9.21 (2002-08-16) [umoeller]
2813 if (!(arc = ProcessAll(pDlgData,
2814 PROCESS_1_CALC_MIN_WIDTHS)))
2815 if (!(arc = ProcessAll(pDlgData,
2816 PROCESS_2_CALC_RELATIVE_WIDTHS)))
2817 if (!(arc = ProcessAll(pDlgData,
2818 PROCESS_3_ALIGN_COLUMNS)))
2819 // skip PROCESS_4_REMAINDER_WIDTHS if the dlg doesn't need it
2820 if ( (!(pDlgData->flNeedsProcessing & FL_REMAINDER_WIDTHS))
2821 || (!(arc = ProcessAll(pDlgData,
2822 PROCESS_4_REMAINDER_WIDTHS)))
2823 )
2824 arc = ProcessAll(pDlgData,
2825 PROCESS_5_CALC_HEIGHTS);
2826
2827 // free the cached font resources that
2828 // might have been created here
2829 if (pDlgData->hps)
2830 {
2831 if (pDlgData->lcidLast)
2832 {
2833 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
2834 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
2835 }
2836
2837 WinReleasePS(pDlgData->hps);
2838 }
2839
2840 return arc;
2841}
2842
2843/*
2844 *@@ Dlg3_PositionAndCreate:
2845 *
2846 *@@added V0.9.15 (2001-08-26) [umoeller]
2847 *@@changed V0.9.15 (2001-08-26) [umoeller]: BS_DEFAULT for other than first button was ignored, fixed
2848 *@@changed V0.9.20 (2002-08-10) [umoeller]: return code checking was missing, fixed
2849 */
2850
2851static APIRET Dlg3_PositionAndCreate(PDLGPRIVATE pDlgData,
2852 HWND *phwndFocusItem) // out: item to give focus to
2853{
2854 APIRET arc = NO_ERROR;
2855
2856 /*
2857 * 5) compute _positions_ of all controls
2858 *
2859 */
2860
2861 // this was missing a return code, fixed V0.9.20 (2002-08-10) [umoeller]
2862 if (!(arc = ProcessAll(pDlgData,
2863 PROCESS_6_CALC_POSITIONS)))
2864 {
2865 /*
2866 * 6) create control windows, finally
2867 *
2868 */
2869
2870 pDlgData->ptlTotalOfs.x = DLG_OUTER_SPACING_X * FACTOR_X;
2871 pDlgData->ptlTotalOfs.y = DLG_OUTER_SPACING_Y * FACTOR_Y;
2872
2873 // this was missing a return code, fixed V0.9.20 (2002-08-10) [umoeller]
2874 if (!(arc = ProcessAll(pDlgData,
2875 PROCESS_7_CREATE_CONTROLS)))
2876 {
2877 if (pDlgData->hwndDefPushbutton)
2878 {
2879 // we had a default pushbutton:
2880 // go set it V0.9.14 (2001-08-21) [umoeller]
2881 WinSetWindowULong(pDlgData->hwndDlg,
2882 QWL_DEFBUTTON,
2883 pDlgData->hwndDefPushbutton);
2884 *phwndFocusItem = pDlgData->hwndDefPushbutton;
2885 // V0.9.15 (2001-08-26) [umoeller]
2886 }
2887 else
2888 *phwndFocusItem = (pDlgData->hwndFirstFocus)
2889 ? pDlgData->hwndFirstFocus
2890 : pDlgData->hwndDlg;
2891 }
2892 }
2893
2894 return arc;
2895}
2896
2897/*
2898 *@@ Dlg9_Cleanup:
2899 *
2900 *@@added V0.9.15 (2001-08-26) [umoeller]
2901 */
2902
2903static VOID Dlg9_Cleanup(PDLGPRIVATE *ppDlgData)
2904{
2905 PDLGPRIVATE pDlgData;
2906 if ( (ppDlgData)
2907 && (pDlgData = *ppDlgData)
2908 )
2909 {
2910 PLISTNODE pTableNode;
2911
2912 // clean up the tables
2913 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
2914 {
2915 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
2916
2917 FreeTable(pTable);
2918 // this may recurse for nested tables
2919 }
2920
2921 lstClear(&pDlgData->llTables);
2922
2923 free(pDlgData);
2924 *ppDlgData = NULL;
2925 }
2926}
2927
2928/* ******************************************************************
2929 *
2930 * Dialog formatter entry points
2931 *
2932 ********************************************************************/
2933
2934/*
2935 *@@ dlghCreateDlg:
2936 * replacement for WinCreateDlg/WinLoadDlg for creating a
2937 * dialog from a settings array in memory, which is
2938 * formatted automatically.
2939 *
2940 * This does NOT use regular dialog templates from
2941 * module resources. Instead, you pass in an array
2942 * of DLGHITEM structures, which define the controls
2943 * and how they are to be formatted.
2944 *
2945 * The main advantage compared to dialog resources is
2946 * that with this function, you will never have to
2947 * define control _positions_. Instead, you only specify
2948 * the control _sizes_, and all positions are computed
2949 * automatically here. Even better, for many controls,
2950 * auto-sizing is supported according to the control's
2951 * text (e.g. for statics and checkboxes). This is
2952 * quite similar to HTML tables.
2953 *
2954 * A regular standard dialog would use something like
2955 *
2956 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN | FCF_CLOSEBUTTON
2957 *
2958 * for flCreateFlags. To make the dlg sizeable, specify
2959 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
2960 *
2961 * dialog.h defines FCF_FIXED_DLG and FCF_SIZEABLE_DLG
2962 * to make this more handy.
2963 *
2964 * <B>Usage:</B>
2965 *
2966 * Like WinLoadDlg, this creates a standard WC_FRAME and
2967 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
2968 * to the dialog with pCreateParams in mp2.
2969 *
2970 * If this func returns no error, you can then use
2971 * WinProcessDlg with the newly created dialog as usual. In
2972 * your dlg proc, use WinDefDlgProc as usual.
2973 *
2974 * There is NO run-time overhead for either code or memory
2975 * after dialog creation; after this function returns, the
2976 * dialog is a standard dialog as if loaded from WinLoadDlg.
2977 * The array of DLGHITEM structures defines how the
2978 * dialog is set up. All this is ONLY used by this function
2979 * and NOT needed after the dialog has been created.
2980 *
2981 * In DLGHITEM, the "Type" field determines what this
2982 * structure defines. A number of handy macros have been
2983 * defined to make this easier and to provide type-checking
2984 * at compile time. See dialog.h for the complete (and
2985 * ever-expanding) list of definitions.
2986 *
2987 * See @dlg_using_macros for how to use these macros.
2988 *
2989 * Essentially, such a dialog item operates similarly to
2990 * HTML tables. There are rows and columns in the table,
2991 * and each control which is specified must be a column
2992 * in some table. Tables may also nest (see below).
2993 *
2994 * See @dlg_algorithm for the gory details of the new
2995 * algorithm used since V0.9.21.
2996 *
2997 * See @dlg_boxmodel for information about how the
2998 * rectangles are defined.
2999 *
3000 * <B>Example:</B>
3001 *
3002 * The typical calling sequence would be:
3003 *
3004 + HWND hwndDlg = NULLHANDLE;
3005 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
3006 + hwndOwner,
3007 + FCF_TITLEBAR | FCF_SYSMENU
3008 + | FCF_DLGBORDER | FCF_NOBYTEALIGN,
3009 + fnwpMyDlgProc,
3010 + "My Dlg Title",
3011 + dlgTemplate, // DLGHITEM array
3012 + ARRAYITEMCOUNT(dlgTemplate),
3013 + NULL, // mp2 for WM_INITDLG
3014 + "9.WarpSans")) // default font
3015 + {
3016 + ULONG idReturn = WinProcessDlg(hwndDlg);
3017 + WinDestroyWindow(hwndDlg);
3018 + }
3019 *
3020 * <B>Errors:</B>
3021 *
3022 * This does not return a HWND, but an APIRET. This will be
3023 * one of the following:
3024 *
3025 * -- NO_ERROR: only in that case, the phwndDlg ptr
3026 * receives the HWND of the new dialog, which can
3027 * then be given to WinProcessDlg. Don't forget
3028 * WinDestroyWindow.
3029 *
3030 * -- ERROR_NOT_ENOUGH_MEMORY
3031 *
3032 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
3033 * outside a table definition.
3034 *
3035 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
3036 * appeared right after a table definition. You must
3037 * specify a row first.
3038 *
3039 * -- DLGERR_NULL_CTL_DEF: A required CONTROLDEF ptr
3040 * was NULL.
3041 *
3042 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
3043 * WC_FRAME window. Typically an invalid owner was
3044 * specified.
3045 *
3046 * -- DLGERR_INVALID_CODE: invalid "Type" field in
3047 * DLGHITEM.
3048 *
3049 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
3050 * improper nesting of TYPE_START_NEW_TABLE and
3051 * TYPE_END_TABLE fields.
3052 *
3053 * -- DLGERR_CANNOT_CREATE_CONTROL: creation of some
3054 * sub-control failed. Maybe an invalid window class
3055 * was specified.
3056 *
3057 * -- DLGERR_INVALID_CONTROL_TITLE: bad window title in
3058 * control.
3059 *
3060 * -- DLGERR_INVALID_STATIC_BITMAP: static bitmap contains
3061 * an invalid bitmap handle.
3062 *
3063 *@@changed V0.9.14 (2001-07-07) [umoeller]: fixed disabled mouse with hwndOwner == HWND_DESKTOP
3064 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed major memory leaks with nested tables
3065 *@@changed V0.9.14 (2001-08-21) [umoeller]: fixed default push button problems
3066 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed bad owner if not direct desktop child
3067 *@@changed V0.9.19 (2002-04-24) [umoeller]: added excpt handling
3068 */
3069
3070APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
3071 HWND hwndOwner, // in: owner for dialog
3072 ULONG flCreateFlags, // in: standard FCF_* frame flags
3073 PFNWP pfnwpDialogProc, // in: dialog winproc or WinDefDlgProc
3074 PCSZ pcszDlgTitle, // in: title to set for dlg frame's titlebar
3075 PCDLGHITEM paDlgItems, // in: definition array with tables, rows, and columns
3076 ULONG cDlgItems, // in: ARRAYITEMCOUNT(paDlgItems)
3077 PVOID pCreateParams, // in: create param for mp2 of WM_INITDLG
3078 PCSZ pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
3079{
3080 APIRET arc = NO_ERROR;
3081
3082 TRY_LOUD(excpt1)
3083 {
3084 ULONG ul;
3085
3086 PDLGPRIVATE pDlgData = NULL;
3087
3088 HWND hwndDesktop = WinQueryDesktopWindow(NULLHANDLE, NULLHANDLE);
3089 // works with a null HAB
3090
3091 /*
3092 * 1) parse the table and create structures from it
3093 *
3094 */
3095
3096 if (!(arc = Dlg0_Init(&pDlgData,
3097 pcszControlsFont,
3098 NULL)))
3099 {
3100 if (!(arc = Dlg1_ParseTables(pDlgData,
3101 paDlgItems,
3102 cDlgItems)))
3103 {
3104 /*
3105 * 2) create empty dialog frame
3106 *
3107 */
3108
3109 FRAMECDATA fcData = {0};
3110 ULONG flStyle = 0;
3111 HWND hwndOwnersParent;
3112
3113 fcData.cb = sizeof(FRAMECDATA);
3114 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
3115
3116 if (flCreateFlags & FCF_SIZEBORDER)
3117 // dialog has size border:
3118 // add "clip siblings" style
3119 flStyle |= WS_CLIPSIBLINGS;
3120
3121 if (hwndOwner == HWND_DESKTOP)
3122 // there's some dumb XWorkplace code left
3123 // which uses this, and this disables the
3124 // mouse for some reason
3125 // V0.9.14 (2001-07-07) [umoeller]
3126 hwndOwner = NULLHANDLE;
3127
3128 // now, make sure the owner window is child of
3129 // HWND_DESKTOP... if it is not, we'll only disable
3130 // some dumb child window, which is not sufficient
3131 // V0.9.16 (2001-12-06) [umoeller]
3132 while ( (hwndOwner)
3133 && (hwndOwnersParent = WinQueryWindow(hwndOwner, QW_PARENT))
3134 && (hwndOwnersParent != hwndDesktop)
3135 )
3136 hwndOwner = hwndOwnersParent;
3137
3138 if (!(pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
3139 WC_FRAME,
3140 (PSZ)pcszDlgTitle,
3141 flStyle, // style; invisible for now
3142 0, 0, 0, 0,
3143 hwndOwner,
3144 HWND_TOP,
3145 0, // ID
3146 &fcData,
3147 NULL))) // presparams
3148 arc = DLGERR_CANNOT_CREATE_FRAME;
3149 else
3150 {
3151 HWND hwndDlg = pDlgData->hwndDlg;
3152 HWND hwndFocusItem = NULLHANDLE;
3153 RECTL rclClient;
3154
3155 /*
3156 * 3) compute size of all controls
3157 *
3158 */
3159
3160 if (!(arc = Dlg2_CalcSizes(pDlgData)))
3161 {
3162 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
3163
3164 /*
3165 * 4) compute size of dialog client from total
3166 * size of all controls
3167 */
3168
3169 // calculate the frame size from the client size
3170 rclClient.xLeft = DLG_OUTER_SPACING_X * FACTOR_X; // 10;
3171 rclClient.yBottom = DLG_OUTER_SPACING_Y * FACTOR_Y; // 10;
3172 rclClient.xRight = pDlgData->szlClient.cx
3173 + 2 * (DLG_OUTER_SPACING_X * FACTOR_X)
3174 - 1;
3175 rclClient.yTop = pDlgData->szlClient.cy
3176 + 2 * (DLG_OUTER_SPACING_Y * FACTOR_Y)
3177 - 1;
3178 WinCalcFrameRect(hwndDlg,
3179 &rclClient,
3180 FALSE); // frame from client
3181
3182 WinSetWindowPos(hwndDlg,
3183 0,
3184 10,
3185 10,
3186 rclClient.xRight,
3187 rclClient.yTop,
3188 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
3189
3190 arc = Dlg3_PositionAndCreate(pDlgData,
3191 &hwndFocusItem);
3192
3193 /*
3194 * 7) WM_INITDLG, set focus
3195 *
3196 */
3197
3198 if (!WinSendMsg(pDlgData->hwndDlg,
3199 WM_INITDLG,
3200 (MPARAM)hwndFocusItem,
3201 (MPARAM)pCreateParams))
3202 {
3203 // if WM_INITDLG returns FALSE, this means
3204 // the dlg proc has not changed the focus;
3205 // we must then set the focus here
3206 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
3207 }
3208 }
3209 }
3210 }
3211
3212 if (arc)
3213 {
3214 // error: clean up
3215 if (pDlgData->hwndDlg)
3216 {
3217 WinDestroyWindow(pDlgData->hwndDlg);
3218 pDlgData->hwndDlg = NULLHANDLE;
3219 }
3220 }
3221 else
3222 // no error: output dialog
3223 *phwndDlg = pDlgData->hwndDlg;
3224
3225 Dlg9_Cleanup(&pDlgData);
3226 }
3227 }
3228 CATCH(excpt1)
3229 {
3230 arc = ERROR_PROTECTION_VIOLATION;
3231 } END_CATCH();
3232
3233 if (arc)
3234 {
3235 CHAR szErr[300];
3236 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
3237 winhDebugBox(hwndOwner,
3238 "Error in Dialog Manager",
3239 szErr);
3240 }
3241
3242 return arc;
3243}
3244
3245/*
3246 *@@ dlghFormatDlg:
3247 * similar to dlghCreateDlg in that this can
3248 * dynamically format dialog items.
3249 *
3250 * The differences however are the following:
3251 *
3252 * -- This assumes that hwndDlg already points
3253 * to a valid dialog frame and that this
3254 * dialog should be modified according to
3255 * flFlags.
3256 *
3257 * This is what's used in XWorkplace for notebook
3258 * settings pages since these always have to be
3259 * based on a resource dialog (which is loaded
3260 * empty).
3261 *
3262 * flFlags can be any combination of the following:
3263 *
3264 * -- DFFL_CREATECONTROLS: paDlgItems points to
3265 * an array of cDlgItems DLGHITEM structures
3266 * (see dlghCreateDlg) which is used for creating
3267 * subwindows in hwndDlg. By using this flag, the
3268 * function will essentially work like dlghCreateDlg,
3269 * except that the frame is already created.
3270 *
3271 * If pszlClient is specified, it receives the required
3272 * size of the client to surround all controls properly.
3273 * You can then use dlghResizeFrame to resize the frame
3274 * with a bit of spacing, if desired.
3275 *
3276 *@@added V0.9.16 (2001-09-29) [umoeller]
3277 *@@changed V0.9.18 (2002-03-03) [umoeller]: added pszlClient, fixed output
3278 *@@changed V0.9.19 (2002-04-24) [umoeller]: added excpt handling
3279 */
3280
3281APIRET dlghFormatDlg(HWND hwndDlg, // in: dialog frame to work on
3282 PCDLGHITEM paDlgItems, // in: definition array
3283 ULONG cDlgItems, // in: array item count (NOT array size)
3284 PCSZ pcszControlsFont, // in: font for ctls with CTL_COMMON_FONT
3285 ULONG flFlags, // in: DFFL_* flags
3286 PSIZEL pszlClient, // out: size of all controls (ptr can be NULL)
3287 PVOID *ppllControls) // out: new LINKLIST receiving HWNDs of created controls (ptr can be NULL)
3288{
3289 APIRET arc = NO_ERROR;
3290
3291 TRY_LOUD(excpt1)
3292 {
3293 ULONG ul;
3294
3295 PDLGPRIVATE pDlgData = NULL;
3296 PLINKLIST pllControls = NULL;
3297
3298 /*
3299 * 1) parse the table and create structures from it
3300 *
3301 */
3302
3303 if (ppllControls)
3304 pllControls = *(PLINKLIST*)ppllControls = lstCreate(FALSE);
3305
3306 if (!(arc = Dlg0_Init(&pDlgData,
3307 pcszControlsFont,
3308 pllControls)))
3309 {
3310 if (!(arc = Dlg1_ParseTables(pDlgData,
3311 paDlgItems,
3312 cDlgItems)))
3313 {
3314 HWND hwndFocusItem;
3315
3316 /*
3317 * 2) create empty dialog frame
3318 *
3319 */
3320
3321 pDlgData->hwndDlg = hwndDlg;
3322
3323 /*
3324 * 3) compute size of all controls
3325 *
3326 */
3327
3328 Dlg2_CalcSizes(pDlgData);
3329
3330 if (pszlClient)
3331 {
3332 pszlClient->cx = pDlgData->szlClient.cx
3333 + 2 * (DLG_OUTER_SPACING_X * FACTOR_X);
3334 pszlClient->cy = pDlgData->szlClient.cy
3335 + 2 * (DLG_OUTER_SPACING_Y * FACTOR_Y);
3336 }
3337
3338 if (flFlags & DFFL_CREATECONTROLS)
3339 {
3340 if (!(arc = Dlg3_PositionAndCreate(pDlgData,
3341 &hwndFocusItem)))
3342 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
3343 }
3344 }
3345
3346 Dlg9_Cleanup(&pDlgData);
3347 }
3348 }
3349 CATCH(excpt1)
3350 {
3351 arc = ERROR_PROTECTION_VIOLATION;
3352 } END_CATCH();
3353
3354 if (arc)
3355 {
3356 CHAR szErr[300];
3357 sprintf(szErr, "Error %d occured in " __FUNCTION__ ".", arc);
3358 winhDebugBox(NULLHANDLE,
3359 "Error in Dialog Manager",
3360 szErr);
3361 }
3362
3363 return arc;
3364}
3365
3366/*
3367 *@@ dlghResizeFrame:
3368 *
3369 *@@added V0.9.18 (2002-03-03) [umoeller]
3370 */
3371
3372VOID dlghResizeFrame(HWND hwndDlg,
3373 PSIZEL pszlClient)
3374{
3375 // calculate the frame size from the client size
3376 RECTL rclClient;
3377 rclClient.xLeft = 10;
3378 rclClient.yBottom = 10;
3379 rclClient.xRight = pszlClient->cx;
3380 rclClient.yTop = pszlClient->cy;
3381 WinCalcFrameRect(hwndDlg,
3382 &rclClient,
3383 FALSE); // frame from client
3384
3385 WinSetWindowPos(hwndDlg,
3386 0,
3387 10,
3388 10,
3389 rclClient.xRight,
3390 rclClient.yTop,
3391 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
3392}
3393
3394/* ******************************************************************
3395 *
3396 * Dialog arrays
3397 *
3398 ********************************************************************/
3399
3400/*
3401 *@@ dlghCreateArray:
3402 * creates a "dialog array" for dynamically
3403 * building a dialog template in memory.
3404 *
3405 * A dialog array is simply an array of
3406 * DLGHITEM structures, as you would normally
3407 * define them statically in the source.
3408 * However, there are situations where you
3409 * might want to leave out certain controls
3410 * depending on certain conditions, which
3411 * can be difficult with static arrays.
3412 *
3413 * As a result, these "array" functions have
3414 * been added to allow for adding static
3415 * DLGHITEM subarrays to a dynamic array in
3416 * memory, which can then be passed to the
3417 * formatter.
3418 *
3419 * Usage:
3420 *
3421 * 1) Call this function with the maximum
3422 * amount of DLGHITEM's that will need
3423 * to be allocated in cMaxItems. Set this
3424 * to the total sum of all DLGHITEM's
3425 * in all the subarrays.
3426 *
3427 * 2) For each of the subarrays, call
3428 * dlghAppendToArray to have the subarray
3429 * appended to the dialog array.
3430 * After each call, DLGARRAY.cDlgItemsNow
3431 * will contain the actual total count of
3432 * DLGHITEM's that were added.
3433 *
3434 * 3) Call dlghCreateDialog with the dialog
3435 * array.
3436 *
3437 * 4) Call dlghFreeArray.
3438 *
3439 * Sort of like this (error checking omitted):
3440 *
3441 + DLGHITEM dlgSampleFront = ... // always included
3442 + DLGHITEM dlgSampleSometimes = ... // not always included
3443 + DLGHITEM dlgSampleTail = ... // always included
3444 +
3445 + PDLGARRAY pArraySample = NULL;
3446 + // create array with sufficient size
3447 + dlghCreateArray( ARRAYITEMCOUNT(dlgSampleFront)
3448 + + ARRAYITEMCOUNT(dlgSampleSometimes)
3449 + + ARRAYITEMCOUNT(dlgSampleTail),
3450 + &pArraySample);
3451 +
3452 + // always include front
3453 + dlghAppendToArray(pArraySample,
3454 + dlgSampleFront,
3455 + ARRAYITEMCOUNT(dlgSampleFront));
3456 + // include "sometimes" conditionally
3457 + if (...)
3458 + dlghAppendToArray(pArraySample,
3459 + dlgSampleSometimes,
3460 + ARRAYITEMCOUNT(dlgSampleSometimes));
3461 + // include tail always
3462 + dlghAppendToArray(pArraySample,
3463 + dlgSampleTail,
3464 + ARRAYITEMCOUNT(dlgSampleTail));
3465 +
3466 + // now create the dialog from the array
3467 + dlghCreateDialog(&hwndDlg,
3468 + hwndOwner,
3469 + FCF_ ...
3470 + fnwpMyDialogProc,
3471 + "Title",
3472 + pArray->paDlgItems, // dialog array!
3473 + pArray->cDlgItemsNow, // real count of items!
3474 + NULL,
3475 + NULL);
3476 +
3477 + dlghFreeArray(&pArraySample);
3478 *
3479 *@@added V0.9.16 (2001-10-15) [umoeller]
3480 */
3481
3482APIRET dlghCreateArray(ULONG cMaxItems,
3483 PDLGARRAY *ppArray) // out: DLGARRAY
3484{
3485 APIRET arc = NO_ERROR;
3486 PDLGARRAY pArray;
3487
3488 if (pArray = NEW(DLGARRAY))
3489 {
3490 ULONG cb;
3491
3492 ZERO(pArray);
3493 if ( (cb = cMaxItems * sizeof(DLGHITEM))
3494 && (pArray->paDlgItems = (DLGHITEM*)malloc(cb))
3495 )
3496 {
3497 memset(pArray->paDlgItems, 0, cb);
3498 pArray->cDlgItemsMax = cMaxItems;
3499 *ppArray = pArray;
3500 }
3501 else
3502 arc = ERROR_NOT_ENOUGH_MEMORY;
3503
3504 if (arc)
3505 dlghFreeArray(&pArray);
3506 }
3507 else
3508 arc = ERROR_NOT_ENOUGH_MEMORY;
3509
3510 return arc;
3511}
3512
3513/*
3514 *@@ dlghFreeArray:
3515 * frees a dialog array created by dlghCreateArray.
3516 *
3517 *@@added V0.9.16 (2001-10-15) [umoeller]
3518 */
3519
3520APIRET dlghFreeArray(PDLGARRAY *ppArray)
3521{
3522 PDLGARRAY pArray;
3523 if ( (ppArray)
3524 && (pArray = *ppArray)
3525 )
3526 {
3527 if (pArray->paDlgItems)
3528 free(pArray->paDlgItems);
3529 free(pArray);
3530 }
3531 else
3532 return ERROR_INVALID_PARAMETER;
3533
3534 return NO_ERROR;
3535}
3536
3537/*
3538 *@@ dlghAppendToArray:
3539 * appends a subarray of DLGHITEM's to the
3540 * given DLGARRAY. See dlghCreateArray for
3541 * usage.
3542 *
3543 * Returns:
3544 *
3545 * -- NO_ERROR
3546 *
3547 * -- ERROR_INVALID_PARAMETER
3548 *
3549 * -- DLGERR_ARRAY_TOO_SMALL: pArray does not
3550 * have enough memory to hold the new items.
3551 * The cMaxItems parameter given to dlghCreateArray
3552 * wasn't large enough.
3553 *
3554 *@@added V0.9.16 (2001-10-15) [umoeller]
3555 */
3556
3557APIRET dlghAppendToArray(PDLGARRAY pArray, // in: dialog array created by dlghCreateArray
3558 PCDLGHITEM paItems, // in: subarray to be appended
3559 ULONG cItems) // in: subarray item count (NOT array size)
3560{
3561 APIRET arc = NO_ERROR;
3562 if (pArray)
3563 {
3564 if ( (pArray->cDlgItemsMax >= cItems)
3565 && (pArray->cDlgItemsMax - pArray->cDlgItemsNow >= cItems)
3566 )
3567 {
3568 // enough space left in the array:
3569 memcpy(&pArray->paDlgItems[pArray->cDlgItemsNow],
3570 paItems, // source
3571 cItems * sizeof(DLGHITEM));
3572 pArray->cDlgItemsNow += cItems;
3573 }
3574 else
3575 arc = DLGERR_ARRAY_TOO_SMALL;
3576 }
3577 else
3578 arc = ERROR_INVALID_PARAMETER;
3579
3580 return arc;
3581}
3582
3583/* ******************************************************************
3584 *
3585 * Standard dialogs
3586 *
3587 ********************************************************************/
3588
3589/*
3590 *@@ fnwpMessageBox:
3591 *
3592 *@@added V0.9.19 (2002-04-24) [umoeller]
3593 */
3594
3595MRESULT EXPENTRY fnwpMessageBox(HWND hwndBox, ULONG msg, MPARAM mp1, MPARAM mp2)
3596{
3597 switch (msg)
3598 {
3599 case WM_HELP:
3600 {
3601 PFNHELP pfnHelp;
3602 if (pfnHelp = (PFNHELP)WinQueryWindowPtr(hwndBox, QWL_USER))
3603 pfnHelp(hwndBox);
3604
3605 return 0;
3606 }
3607 }
3608
3609 return WinDefDlgProc(hwndBox, msg, mp1, mp2);
3610}
3611
3612/*
3613 *@@ dlghCreateMessageBox:
3614 *
3615 *@@added V0.9.13 (2001-06-21) [umoeller]
3616 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
3617 *@@changed V0.9.19 (2002-04-24) [umoeller]: added pfnHelp
3618 *@@changed V0.9.20 (2002-07-12) [umoeller]: made icon spacing wider
3619 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed missing close button
3620 *@@changed V0.9.21 (2002-08-16) [umoeller]: now using table alignment
3621 */
3622
3623APIRET dlghCreateMessageBox(HWND *phwndDlg,
3624 HWND hwndOwner,
3625 HPOINTER hptrIcon,
3626 PCSZ pcszTitle,
3627 PCSZ pcszMessage,
3628 PFNHELP pfnHelp, // in: help callback or NULL
3629 ULONG flFlags,
3630 PCSZ pcszFont,
3631 const MSGBOXSTRINGS *pStrings,
3632 PULONG pulAlarmFlag) // out: alarm sound to be played
3633{
3634 APIRET arc;
3635
3636 CONTROLDEF
3637 Icon = CONTROLDEF_ICON_WIDER(NULLHANDLE, 0),
3638 // made icon spacing wider V0.9.20 (2002-07-12) [umoeller]
3639 Spacing = CONTROLDEF_TEXT(NULL, 0, 1, 1),
3640 InfoText = CONTROLDEF_TEXT_WORDBREAK(NULL, 10, MSGBOX_TEXTWIDTH),
3641 Buttons[] =
3642 {
3643 CONTROLDEF_PUSHBUTTON(NULL, 1, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3644 CONTROLDEF_PUSHBUTTON(NULL, 2, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3645 CONTROLDEF_PUSHBUTTON(NULL, 3, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3646 CONTROLDEF_HELPPUSHBUTTON(NULL, 4, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT)
3647 };
3648
3649 DLGHITEM MessageBoxFront[] =
3650 {
3651 START_TABLE_ALIGN,
3652 START_ROW(ROW_VALIGN_CENTER),
3653 CONTROL_DEF(&Icon),
3654 START_TABLE,
3655 START_ROW(ROW_VALIGN_CENTER),
3656 CONTROL_DEF(&Spacing),
3657 START_ROW(ROW_VALIGN_CENTER),
3658 CONTROL_DEF(&InfoText),
3659 START_ROW(ROW_VALIGN_CENTER),
3660 CONTROL_DEF(&Spacing),
3661 END_TABLE,
3662 START_ROW(ROW_VALIGN_CENTER),
3663 CONTROL_DEF(&Spacing),
3664 START_TABLE,
3665 START_ROW(ROW_VALIGN_CENTER),
3666 CONTROL_DEF(&Buttons[0]),
3667 CONTROL_DEF(&Buttons[1]),
3668 CONTROL_DEF(&Buttons[2]),
3669 },
3670 MessageBoxHelp[] =
3671 {
3672 CONTROL_DEF(&Buttons[3]),
3673 },
3674 MessageBoxTail[] =
3675 {
3676 END_TABLE,
3677 END_TABLE
3678 };
3679
3680 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
3681 PDLGARRAY pArrayBox;
3682
3683 PCSZ p0 = "Error",
3684 p1 = NULL,
3685 p2 = NULL;
3686
3687 Icon.pcszText = (PCSZ)hptrIcon;
3688 InfoText.pcszText = pcszMessage;
3689
3690 // now work on the three buttons of the dlg template:
3691 // give them proper titles or hide them
3692 if (flButtons == MB_OK)
3693 {
3694 p0 = pStrings->pcszOK;
3695 }
3696 else if (flButtons == MB_OKCANCEL)
3697 {
3698 p0 = pStrings->pcszOK;
3699 p1 = pStrings->pcszCancel;
3700 }
3701 else if (flButtons == MB_RETRYCANCEL)
3702 {
3703 p0 = pStrings->pcszRetry;
3704 p1 = pStrings->pcszCancel;
3705 }
3706 else if (flButtons == MB_ABORTRETRYIGNORE)
3707 {
3708 p0 = pStrings->pcszAbort;
3709 p1 = pStrings->pcszRetry;
3710 p2 = pStrings->pcszIgnore;
3711 }
3712 else if (flButtons == MB_YESNO)
3713 {
3714 p0 = pStrings->pcszYes;
3715 p1 = pStrings->pcszNo;
3716 }
3717 else if (flButtons == MB_YESNOCANCEL)
3718 {
3719 p0 = pStrings->pcszYes;
3720 p1 = pStrings->pcszNo;
3721 p2 = pStrings->pcszCancel;
3722 }
3723 else if (flButtons == MB_CANCEL)
3724 {
3725 p0 = pStrings->pcszCancel;
3726 }
3727 else if (flButtons == MB_ENTER)
3728 {
3729 p0 = pStrings->pcszEnter;
3730 }
3731 else if (flButtons == MB_ENTERCANCEL)
3732 {
3733 p0 = pStrings->pcszEnter;
3734 p1 = pStrings->pcszCancel;
3735 }
3736 else if (flButtons == MB_YES_YES2ALL_NO)
3737 {
3738 p0 = pStrings->pcszYes;
3739 p1 = pStrings->pcszYesToAll;
3740 p2 = pStrings->pcszNo;
3741 }
3742
3743 // now set strings and hide empty buttons
3744 Buttons[0].pcszText = p0;
3745
3746 if (p1)
3747 Buttons[1].pcszText = p1;
3748 else
3749 Buttons[1].flStyle &= ~WS_VISIBLE;
3750
3751 if (p2)
3752 Buttons[2].pcszText = p2;
3753 else
3754 Buttons[2].flStyle &= ~WS_VISIBLE;
3755
3756 // query default button IDs
3757 if (flFlags & MB_DEFBUTTON2)
3758 Buttons[1].flStyle |= BS_DEFAULT;
3759 else if (flFlags & MB_DEFBUTTON3)
3760 Buttons[2].flStyle |= BS_DEFAULT;
3761 else
3762 Buttons[0].flStyle |= BS_DEFAULT;
3763
3764 *pulAlarmFlag = WA_NOTE;
3765 if (flFlags & (MB_ICONHAND | MB_ERROR))
3766 *pulAlarmFlag = WA_ERROR;
3767 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
3768 *pulAlarmFlag = WA_WARNING;
3769
3770 if (pfnHelp)
3771 Buttons[3].pcszText = pStrings->pcszHelp;
3772
3773 if (!(arc = dlghCreateArray( ARRAYITEMCOUNT(MessageBoxFront)
3774 + ARRAYITEMCOUNT(MessageBoxHelp)
3775 + ARRAYITEMCOUNT(MessageBoxTail),
3776 &pArrayBox)))
3777 {
3778 if ( (!(arc = dlghAppendToArray(pArrayBox,
3779 MessageBoxFront,
3780 ARRAYITEMCOUNT(MessageBoxFront))))
3781 && ( (!pfnHelp)
3782 || (!(arc = dlghAppendToArray(pArrayBox,
3783 MessageBoxHelp,
3784 ARRAYITEMCOUNT(MessageBoxHelp))))
3785 )
3786 && (!(arc = dlghAppendToArray(pArrayBox,
3787 MessageBoxTail,
3788 ARRAYITEMCOUNT(MessageBoxTail))))
3789 )
3790 {
3791 if (!(arc = dlghCreateDlg(phwndDlg,
3792 hwndOwner,
3793 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
3794 | FCF_CLOSEBUTTON, // was missing V0.9.20 (2002-08-10) [umoeller]
3795 fnwpMessageBox,
3796 pcszTitle,
3797 pArrayBox->paDlgItems,
3798 pArrayBox->cDlgItemsNow,
3799 NULL,
3800 pcszFont)))
3801 // added help callback V0.9.19 (2002-04-24) [umoeller]
3802 WinSetWindowPtr(*phwndDlg, QWL_USER, (PVOID)pfnHelp);
3803 }
3804
3805 dlghFreeArray(&pArrayBox);
3806 }
3807
3808 return arc;
3809}
3810
3811/*
3812 *@@ dlghProcessMessageBox:
3813 *
3814 *@@added V0.9.13 (2001-06-21) [umoeller]
3815 */
3816
3817ULONG dlghProcessMessageBox(HWND hwndDlg,
3818 ULONG ulAlarmFlag,
3819 ULONG flFlags)
3820{
3821 ULONG ulrcDlg;
3822 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
3823
3824 winhCenterWindow(hwndDlg);
3825
3826 if (flFlags & MB_SYSTEMMODAL)
3827 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
3828
3829 if (ulAlarmFlag)
3830 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
3831
3832 ulrcDlg = WinProcessDlg(hwndDlg);
3833
3834 WinDestroyWindow(hwndDlg);
3835
3836 if (flButtons == MB_OK)
3837 return MBID_OK;
3838 else if (flButtons == MB_OKCANCEL)
3839 switch (ulrcDlg)
3840 {
3841 case 1: return MBID_OK;
3842 default: return MBID_CANCEL;
3843 }
3844 else if (flButtons == MB_RETRYCANCEL)
3845 switch (ulrcDlg)
3846 {
3847 case 1: return MBID_RETRY;
3848 default: return MBID_CANCEL;
3849 }
3850 else if (flButtons == MB_ABORTRETRYIGNORE)
3851 switch (ulrcDlg)
3852 {
3853 case 2: return MBID_RETRY;
3854 case 3: return MBID_IGNORE;
3855 default: return MBID_ABORT;
3856 }
3857 else if (flButtons == MB_YESNO)
3858 switch (ulrcDlg)
3859 {
3860 case 1: return MBID_YES;
3861 default: return MBID_NO;
3862 }
3863 else if (flButtons == MB_YESNOCANCEL)
3864 switch (ulrcDlg)
3865 {
3866 case 1: return MBID_YES;
3867 case 2: return MBID_NO;
3868 default: return MBID_CANCEL;
3869 }
3870 else if (flButtons == MB_CANCEL)
3871 return MBID_CANCEL;
3872 else if (flButtons == MB_ENTER)
3873 return MBID_ENTER;
3874 else if (flButtons == MB_ENTERCANCEL)
3875 switch (ulrcDlg)
3876 {
3877 case 1: return MBID_ENTER;
3878 default: return MBID_CANCEL;
3879 }
3880 else if (flButtons == MB_YES_YES2ALL_NO)
3881 switch (ulrcDlg)
3882 {
3883 case 1: return MBID_YES;
3884 case 2: return MBID_YES2ALL;
3885 default: return MBID_NO;
3886 }
3887
3888 return (MBID_CANCEL);
3889}
3890
3891/*
3892 *@@ dlghMessageBox:
3893 * WinMessageBox replacement, which uses dlghCreateDlg
3894 * internally.
3895 *
3896 * This has all the flags of the standard call,
3897 * but looks much prettier. Besides, it allows
3898 * you to specify any icon to be displayed.
3899 *
3900 * Currently the following flStyle's are supported:
3901 *
3902 * -- MB_OK 0x0000
3903 * -- MB_OKCANCEL 0x0001
3904 * -- MB_RETRYCANCEL 0x0002
3905 * -- MB_ABORTRETRYIGNORE 0x0003
3906 * -- MB_YESNO 0x0004
3907 * -- MB_YESNOCANCEL 0x0005
3908 * -- MB_CANCEL 0x0006
3909 * -- MB_ENTER 0x0007 (not implemented yet)
3910 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
3911 *
3912 * -- MB_YES_YES2ALL_NO 0x0009
3913 * This is new: this has three buttons called "Yes"
3914 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
3915 *
3916 * -- MB_DEFBUTTON2 (for two-button styles)
3917 * -- MB_DEFBUTTON3 (for three-button styles)
3918 *
3919 * -- MB_ICONHAND
3920 * -- MB_ICONEXCLAMATION
3921 *
3922 * If (pfnHelp != NULL), a "Help" button is also added and
3923 * pfnHelp gets called when the user presses it or the F1
3924 * key.
3925 *
3926 * Returns MBID_* codes like WinMessageBox.
3927 *
3928 *@@added V0.9.13 (2001-06-21) [umoeller]
3929 *@@changed V0.9.19 (2002-04-24) [umoeller]: added pfnHelp
3930 */
3931
3932ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
3933 HPOINTER hptrIcon, // in: icon to display
3934 PCSZ pcszTitle, // in: title
3935 PCSZ pcszMessage, // in: message
3936 PFNHELP pfnHelp, // in: help callback or NULL
3937 ULONG flFlags, // in: standard message box flags
3938 PCSZ pcszFont, // in: font (e.g. "9.WarpSans")
3939 const MSGBOXSTRINGS *pStrings) // in: strings array
3940{
3941 HWND hwndDlg;
3942 ULONG ulAlarmFlag;
3943 APIRET arc;
3944 CHAR szMsg[100];
3945
3946 if ( (!(arc = dlghCreateMessageBox(&hwndDlg,
3947 hwndOwner,
3948 hptrIcon,
3949 pcszTitle,
3950 pcszMessage,
3951 pfnHelp,
3952 flFlags,
3953 pcszFont,
3954 pStrings,
3955 &ulAlarmFlag)))
3956 && (hwndDlg)
3957 )
3958 {
3959 // SHOW DIALOG
3960 return (dlghProcessMessageBox(hwndDlg,
3961 ulAlarmFlag,
3962 flFlags));
3963 }
3964
3965 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
3966 WinMessageBox(HWND_DESKTOP,
3967 NULLHANDLE,
3968 "Error",
3969 szMsg,
3970 0,
3971 MB_CANCEL | MB_MOVEABLE);
3972
3973 return (DID_CANCEL);
3974}
3975
3976/*
3977 *@@ cmnTextEntryBox:
3978 * common dialog for entering a text string.
3979 * The dialog has a descriptive text on top
3980 * with an entry field below and "OK" and "Cancel"
3981 * buttons.
3982 *
3983 * The string from the user is returned in a
3984 * new buffer, which must be free'd by the caller.
3985 * Returns NULL if the user pressed "Cancel".
3986 *
3987 * fl can be any combination of the following
3988 * flags:
3989 *
3990 * -- TEBF_REMOVETILDE: tilde ("~") characters
3991 * are removed from pcszTitle before setting
3992 * the title. Useful for reusing menu item
3993 * texts.
3994 *
3995 * -- TEBF_REMOVEELLIPSE: ellipse ("...") strings
3996 * are removed from pcszTitle before setting
3997 * the title. Useful for reusing menu item
3998 * texts.
3999 *
4000 * -- TEBF_SELECTALL: the default text in the
4001 * entry field is initially highlighted.
4002 *
4003 *@@added V0.9.15 (2001-09-14) [umoeller]
4004 */
4005
4006PSZ dlghTextEntryBox(HWND hwndOwner,
4007 PCSZ pcszTitle, // in: dlg title
4008 PCSZ pcszDescription, // in: descriptive text above entry field
4009 PCSZ pcszDefault, // in: default text for entry field or NULL
4010 PCSZ pcszOK, // in: "OK" string
4011 PCSZ pcszCancel, // in: "Cancel" string
4012 ULONG ulMaxLen, // in: maximum length for entry
4013 ULONG fl, // in: TEBF_* flags
4014 PCSZ pcszFont) // in: font (e.g. "9.WarpSans")
4015{
4016 CONTROLDEF
4017 Static = CONTROLDEF_TEXT_WORDBREAK(
4018 NULL,
4019 -1,
4020 150),
4021 Entry = CONTROLDEF_ENTRYFIELD(
4022 NULL,
4023 999,
4024 150,
4025 SZL_AUTOSIZE),
4026 OKButton = CONTROLDEF_DEFPUSHBUTTON(
4027 NULL,
4028 DID_OK,
4029 STD_BUTTON_WIDTH,
4030 STD_BUTTON_HEIGHT),
4031 CancelButton = CONTROLDEF_PUSHBUTTON(
4032 NULL,
4033 DID_CANCEL,
4034 STD_BUTTON_WIDTH,
4035 STD_BUTTON_HEIGHT);
4036
4037 DLGHITEM DlgTemplate[] =
4038 {
4039 START_TABLE,
4040 START_ROW(0),
4041 CONTROL_DEF(&Static),
4042 START_ROW(0),
4043 CONTROL_DEF(&Entry),
4044 START_ROW(0),
4045 CONTROL_DEF(&OKButton),
4046 CONTROL_DEF(&CancelButton),
4047 END_TABLE
4048 };
4049
4050 HWND hwndDlg = NULLHANDLE;
4051 PSZ pszReturn = NULL;
4052 XSTRING strTitle;
4053
4054 xstrInitCopy(&strTitle, pcszTitle, 0);
4055
4056 if (fl & (TEBF_REMOVEELLIPSE | TEBF_REMOVETILDE))
4057 {
4058 ULONG ulOfs;
4059 if (fl & TEBF_REMOVEELLIPSE)
4060 {
4061 ulOfs = 0;
4062 while (xstrFindReplaceC(&strTitle,
4063 &ulOfs,
4064 "...",
4065 ""))
4066 ;
4067 }
4068
4069 if (fl & TEBF_REMOVETILDE)
4070 {
4071 ulOfs = 0;
4072 while (xstrFindReplaceC(&strTitle,
4073 &ulOfs,
4074 "~",
4075 ""))
4076 ;
4077 }
4078 }
4079
4080 Static.pcszText = pcszDescription;
4081
4082 OKButton.pcszText = pcszOK;
4083 CancelButton.pcszText = pcszCancel;
4084
4085 if (NO_ERROR == dlghCreateDlg(&hwndDlg,
4086 hwndOwner,
4087 FCF_FIXED_DLG,
4088 WinDefDlgProc,
4089 strTitle.psz,
4090 DlgTemplate, // DLGHITEM array
4091 ARRAYITEMCOUNT(DlgTemplate),
4092 NULL,
4093 pcszFont))
4094 {
4095 HWND hwndEF = WinWindowFromID(hwndDlg, 999);
4096 winhCenterWindow(hwndDlg);
4097 winhSetEntryFieldLimit(hwndEF, ulMaxLen);
4098 if (pcszDefault)
4099 {
4100 WinSetWindowText(hwndEF, (PSZ)pcszDefault);
4101 if (fl & TEBF_SELECTALL)
4102 winhEntryFieldSelectAll(hwndEF);
4103 }
4104 WinSetFocus(HWND_DESKTOP, hwndEF);
4105 if (DID_OK == WinProcessDlg(hwndDlg))
4106 pszReturn = winhQueryWindowText(hwndEF);
4107
4108 WinDestroyWindow(hwndDlg);
4109 }
4110
4111 xstrClear(&strTitle);
4112
4113 return (pszReturn);
4114}
4115
4116/* ******************************************************************
4117 *
4118 * Dialog input handlers
4119 *
4120 ********************************************************************/
4121
4122/*
4123 *@@ dlghSetPrevFocus:
4124 * "backward" function for rotating the focus
4125 * in a dialog when the "shift+tab" keys get
4126 * pressed.
4127 *
4128 * pllWindows must be a linked list with the
4129 * plain HWND window handles of the focussable
4130 * controls in the dialog.
4131 */
4132
4133VOID dlghSetPrevFocus(PVOID pvllWindows)
4134{
4135 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4136
4137 // check current focus
4138 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
4139
4140 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
4141
4142 BOOL fRestart = FALSE;
4143
4144 while (pNode)
4145 {
4146 CHAR szClass[100];
4147
4148 // previos node
4149 pNode = pNode->pPrevious;
4150
4151 if ( (!pNode) // no next, or not found:
4152 && (!fRestart) // avoid infinite looping if bad list
4153 )
4154 {
4155 pNode = lstQueryLastNode(pllWindows);
4156 fRestart = TRUE;
4157 }
4158
4159 if (pNode)
4160 {
4161 // check if this is a focusable control
4162 if (WinQueryClassName((HWND)pNode->pItemData,
4163 sizeof(szClass),
4164 szClass))
4165 {
4166 if ( (strcmp(szClass, "#5")) // not static
4167 )
4168 break;
4169 // else: go for next then
4170 }
4171 }
4172 }
4173
4174 if (pNode)
4175 {
4176 WinSetFocus(HWND_DESKTOP,
4177 (HWND)pNode->pItemData);
4178 }
4179}
4180
4181/*
4182 *@@ dlghSetNextFocus:
4183 * "forward" function for rotating the focus
4184 * in a dialog when the "ab" key gets pressed.
4185 *
4186 * pllWindows must be a linked list with the
4187 * plain HWND window handles of the focussable
4188 * controls in the dialog.
4189 */
4190
4191VOID dlghSetNextFocus(PVOID pvllWindows)
4192{
4193 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4194
4195 // check current focus
4196 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
4197
4198 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
4199
4200 BOOL fRestart = FALSE;
4201
4202 while (pNode)
4203 {
4204 CHAR szClass[100];
4205
4206 // next focus in node
4207 pNode = pNode->pNext;
4208
4209 if ( (!pNode) // no next, or not found:
4210 && (!fRestart) // avoid infinite looping if bad list
4211 )
4212 {
4213 pNode = lstQueryFirstNode(pllWindows);
4214 fRestart = TRUE;
4215 }
4216
4217 if (pNode)
4218 {
4219 // check if this is a focusable control
4220 if (WinQueryClassName((HWND)pNode->pItemData,
4221 sizeof(szClass),
4222 szClass))
4223 {
4224 if ( (strcmp(szClass, "#5")) // not static
4225 )
4226 break;
4227 // else: go for next then
4228 }
4229 }
4230 }
4231
4232 if (pNode)
4233 {
4234 WinSetFocus(HWND_DESKTOP,
4235 (HWND)pNode->pItemData);
4236 }
4237}
4238
4239/*
4240 *@@ dlghProcessMnemonic:
4241 * finds the control which matches usch
4242 * and gives it the focus. If this is a
4243 * static control, the next control in the
4244 * list is given focus instead. (Standard
4245 * dialog behavior.)
4246 *
4247 * Pass in usch from WM_CHAR. It is assumed
4248 * that the caller has already tested for
4249 * the "alt" key to be depressed.
4250 *
4251 *@@added V0.9.9 (2001-03-17) [umoeller]
4252 */
4253
4254HWND dlghProcessMnemonic(PVOID pvllWindows,
4255 USHORT usch)
4256{
4257 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4258
4259 HWND hwndFound = NULLHANDLE;
4260 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
4261 CHAR szClass[100];
4262
4263 while (pNode)
4264 {
4265 HWND hwnd = (HWND)pNode->pItemData;
4266
4267 if (WinSendMsg(hwnd,
4268 WM_MATCHMNEMONIC,
4269 (MPARAM)usch,
4270 0))
4271 {
4272 // according to the docs, only buttons and static
4273 // return TRUE to that msg;
4274 // if this is a static, give focus to the next
4275 // control
4276
4277 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
4278
4279 // check if this is a focusable control
4280 if (WinQueryClassName(hwnd,
4281 sizeof(szClass),
4282 szClass))
4283 {
4284 if (!strcmp(szClass, "#3"))
4285 // it's a button: click it
4286 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
4287 else if (!strcmp(szClass, "#5"))
4288 {
4289 // it's a static: give focus to following control
4290 pNode = pNode->pNext;
4291 if (pNode)
4292 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
4293 }
4294 }
4295 else
4296 // any other control (are there any?): give them focus
4297 WinSetFocus(HWND_DESKTOP, hwnd);
4298
4299 // in any case, stop
4300 hwndFound = hwnd;
4301 break;
4302 }
4303
4304 pNode = pNode->pNext;
4305 }
4306
4307 return (hwndFound);
4308}
4309
4310/*
4311 *@@ dlghEnter:
4312 * presses the first button with BS_DEFAULT.
4313 */
4314
4315BOOL dlghEnter(PVOID pvllWindows)
4316{
4317 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4318
4319 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
4320 CHAR szClass[100];
4321 while (pNode)
4322 {
4323 HWND hwnd = (HWND)pNode->pItemData;
4324 if (WinQueryClassName(hwnd,
4325 sizeof(szClass),
4326 szClass))
4327 {
4328 if (!strcmp(szClass, "#3")) // button
4329 {
4330 // _Pmpf((__FUNCTION__ ": found button"));
4331 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
4332 == (BS_PUSHBUTTON | BS_DEFAULT)
4333 )
4334 {
4335 // _Pmpf((" is default!"));
4336 WinPostMsg(hwnd,
4337 BM_CLICK,
4338 (MPARAM)TRUE, // upclick
4339 0);
4340 return (TRUE);
4341 }
4342 }
4343 }
4344
4345 pNode = pNode->pNext;
4346 }
4347
4348 return (FALSE);
4349}
4350
4351
Note: See TracBrowser for help on using the repository browser.