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

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

Fixed build breaks.

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