source: branches/branch-1-0/src/helpers/dialog.c@ 316

Last change on this file since 316 was 316, checked in by pr, 19 years ago

Message Boxes now added to Tasklist

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 158.8 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 V1.0.0. 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-2006 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 (V1.0.0).
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 (strings); it is possible
298 * to 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 * for the stack size of the current thread, of course.
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 V1.0.0.
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 V1.0.0.
576 *
577 * With V1.0.0, 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 V1.0.0 (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 // V1.0.0 (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 // V1.0.0 (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 // V1.0.0 (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_pfnwpDebugStaticOrig = 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 V1.0.0 (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_pfnwpDebugStaticOrig(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 V1.0.0 (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_pfnwpDebugStaticOrig = 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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (2002-08-16) [umoeller]: adjusted for new algorithm
1293 *@@changed V1.0.0 (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 volatile PCSZ pcszClass = NULL; // XWP V1.0.4 (2005-10-09) [pr]
1302 volatile PCSZ pcszText = NULL;
1303 volatile ULONG flStyle = 0;
1304 volatile LHANDLE lHandleSet = NULLHANDLE;
1305 volatile 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 // V1.0.0 (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 V1.0.0 (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 // V1.0.0 (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 // V1.0.0 (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 // V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 // V1.0.0 (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 V1.0.0 (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 V1.0.0 (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, V1.0.0 (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 V1.0.0 (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 V1.0.0 (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 V1.0.0.
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 +@@changed V1.0.0 (2002-08-21) [umoeller]: now setting wait pointer
3072 */
3073
3074APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
3075 HWND hwndOwner, // in: owner for dialog
3076 ULONG flCreateFlags, // in: standard FCF_* frame flags
3077 PFNWP pfnwpDialogProc, // in: dialog winproc or WinDefDlgProc
3078 PCSZ pcszDlgTitle, // in: title to set for dlg frame's titlebar
3079 PCDLGHITEM paDlgItems, // in: definition array with tables, rows, and columns
3080 ULONG cDlgItems, // in: ARRAYITEMCOUNT(paDlgItems)
3081 PVOID pCreateParams, // in: create param for mp2 of WM_INITDLG
3082 PCSZ pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
3083{
3084 APIRET arc = NO_ERROR;
3085
3086 volatile HPOINTER hptrOld = winhSetWaitPointer(); // XWP V1.0.4 (2005-10-09) [pr]
3087
3088 TRY_LOUD(excpt1)
3089 {
3090 ULONG ul;
3091
3092 PDLGPRIVATE pDlgData = NULL;
3093
3094 HWND hwndDesktop = WinQueryDesktopWindow(NULLHANDLE, NULLHANDLE);
3095 // works with a null HAB
3096
3097 /*
3098 * 1) parse the table and create structures from it
3099 *
3100 */
3101
3102 if (!(arc = Dlg0_Init(&pDlgData,
3103 pcszControlsFont,
3104 NULL)))
3105 {
3106 if (!(arc = Dlg1_ParseTables(pDlgData,
3107 paDlgItems,
3108 cDlgItems)))
3109 {
3110 /*
3111 * 2) create empty dialog frame
3112 *
3113 */
3114
3115 FRAMECDATA fcData = {0};
3116 ULONG flStyle = 0;
3117 HWND hwndOwnersParent;
3118
3119 fcData.cb = sizeof(FRAMECDATA);
3120 fcData.flCreateFlags = flCreateFlags | 0x40000000L;
3121
3122 if (flCreateFlags & FCF_SIZEBORDER)
3123 // dialog has size border:
3124 // add "clip siblings" style
3125 flStyle |= WS_CLIPSIBLINGS;
3126
3127 if (hwndOwner == HWND_DESKTOP)
3128 // there's some dumb XWorkplace code left
3129 // which uses this, and this disables the
3130 // mouse for some reason
3131 // V0.9.14 (2001-07-07) [umoeller]
3132 hwndOwner = NULLHANDLE;
3133
3134 // now, make sure the owner window is child of
3135 // HWND_DESKTOP... if it is not, we'll only disable
3136 // some dumb child window, which is not sufficient
3137 // V0.9.16 (2001-12-06) [umoeller]
3138 while ( (hwndOwner)
3139 && (hwndOwnersParent = WinQueryWindow(hwndOwner, QW_PARENT))
3140 && (hwndOwnersParent != hwndDesktop)
3141 )
3142 hwndOwner = hwndOwnersParent;
3143
3144 if (!(pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
3145 WC_FRAME,
3146 (PSZ)pcszDlgTitle,
3147 flStyle, // style; invisible for now
3148 0, 0, 0, 0,
3149 hwndOwner,
3150 HWND_TOP,
3151 0, // ID
3152 &fcData,
3153 NULL))) // presparams
3154 arc = DLGERR_CANNOT_CREATE_FRAME;
3155 else
3156 {
3157 HWND hwndDlg = pDlgData->hwndDlg;
3158 HWND hwndFocusItem = NULLHANDLE;
3159 RECTL rclClient;
3160
3161 /*
3162 * 3) compute size of all controls
3163 *
3164 */
3165
3166 if (!(arc = Dlg2_CalcSizes(pDlgData)))
3167 {
3168 WinSubclassWindow(hwndDlg, pfnwpDialogProc);
3169
3170 /*
3171 * 4) compute size of dialog client from total
3172 * size of all controls
3173 */
3174
3175 // calculate the frame size from the client size
3176 rclClient.xLeft = DLG_OUTER_SPACING_X * FACTOR_X; // 10;
3177 rclClient.yBottom = DLG_OUTER_SPACING_Y * FACTOR_Y; // 10;
3178 rclClient.xRight = pDlgData->szlClient.cx
3179 + 2 * (DLG_OUTER_SPACING_X * FACTOR_X)
3180 - 1;
3181 rclClient.yTop = pDlgData->szlClient.cy
3182 + 2 * (DLG_OUTER_SPACING_Y * FACTOR_Y)
3183 - 1;
3184 WinCalcFrameRect(hwndDlg,
3185 &rclClient,
3186 FALSE); // frame from client
3187
3188 WinSetWindowPos(hwndDlg,
3189 0,
3190 10,
3191 10,
3192 rclClient.xRight,
3193 rclClient.yTop,
3194 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
3195
3196 arc = Dlg3_PositionAndCreate(pDlgData,
3197 &hwndFocusItem);
3198
3199 /*
3200 * 7) WM_INITDLG, set focus
3201 *
3202 */
3203
3204 if (!WinSendMsg(pDlgData->hwndDlg,
3205 WM_INITDLG,
3206 (MPARAM)hwndFocusItem,
3207 (MPARAM)pCreateParams))
3208 {
3209 // if WM_INITDLG returns FALSE, this means
3210 // the dlg proc has not changed the focus;
3211 // we must then set the focus here
3212 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
3213 }
3214 }
3215 }
3216 }
3217
3218 if (arc)
3219 {
3220 // error: clean up
3221 if (pDlgData->hwndDlg)
3222 {
3223 WinDestroyWindow(pDlgData->hwndDlg);
3224 pDlgData->hwndDlg = NULLHANDLE;
3225 }
3226 }
3227 else
3228 // no error: output dialog
3229 *phwndDlg = pDlgData->hwndDlg;
3230
3231 Dlg9_Cleanup(&pDlgData);
3232 }
3233 }
3234 CATCH(excpt1)
3235 {
3236 arc = ERROR_PROTECTION_VIOLATION;
3237 } END_CATCH();
3238
3239 if (arc)
3240 {
3241 CHAR szErr[300];
3242 sprintf(szErr, "Error %d occurred in " __FUNCTION__ ".", arc);
3243 winhDebugBox(hwndOwner,
3244 "Error in Dialog Manager",
3245 szErr);
3246 }
3247
3248 WinSetPointer(HWND_DESKTOP, hptrOld); // V1.0.0 (2002-08-21) [umoeller]
3249
3250 return arc;
3251}
3252
3253/*
3254 *@@ dlghFormatDlg:
3255 * similar to dlghCreateDlg in that this can
3256 * dynamically format dialog items.
3257 *
3258 * The differences however are the following:
3259 *
3260 * -- This assumes that hwndDlg already points
3261 * to a valid dialog frame and that this
3262 * dialog should be modified according to
3263 * flFlags.
3264 *
3265 * This is what's used in XWorkplace for notebook
3266 * settings pages since these always have to be
3267 * based on a resource dialog (which is loaded
3268 * empty).
3269 *
3270 * flFlags can be any combination of the following:
3271 *
3272 * -- DFFL_CREATECONTROLS: paDlgItems points to
3273 * an array of cDlgItems DLGHITEM structures
3274 * (see dlghCreateDlg) which is used for creating
3275 * subwindows in hwndDlg. By using this flag, the
3276 * function will essentially work like dlghCreateDlg,
3277 * except that the frame is already created.
3278 *
3279 * If pszlClient is specified, it receives the required
3280 * size of the client to surround all controls properly.
3281 * You can then use dlghResizeFrame to resize the frame
3282 * with a bit of spacing, if desired.
3283 *
3284 *@@added V0.9.16 (2001-09-29) [umoeller]
3285 *@@changed V0.9.18 (2002-03-03) [umoeller]: added pszlClient, fixed output
3286 *@@changed V0.9.19 (2002-04-24) [umoeller]: added excpt handling
3287 */
3288
3289APIRET dlghFormatDlg(HWND hwndDlg, // in: dialog frame to work on
3290 PCDLGHITEM paDlgItems, // in: definition array
3291 ULONG cDlgItems, // in: array item count (NOT array size)
3292 PCSZ pcszControlsFont, // in: font for ctls with CTL_COMMON_FONT
3293 ULONG flFlags, // in: DFFL_* flags
3294 PSIZEL pszlClient, // out: size of all controls (ptr can be NULL)
3295 PVOID *ppllControls) // out: new LINKLIST receiving HWNDs of created controls (ptr can be NULL)
3296{
3297 APIRET arc = NO_ERROR;
3298
3299 TRY_LOUD(excpt1)
3300 {
3301 ULONG ul;
3302
3303 PDLGPRIVATE pDlgData = NULL;
3304 PLINKLIST pllControls = NULL;
3305
3306 /*
3307 * 1) parse the table and create structures from it
3308 *
3309 */
3310
3311 if (ppllControls)
3312 pllControls = *(PLINKLIST*)ppllControls = lstCreate(FALSE);
3313
3314 if (!(arc = Dlg0_Init(&pDlgData,
3315 pcszControlsFont,
3316 pllControls)))
3317 {
3318 if (!(arc = Dlg1_ParseTables(pDlgData,
3319 paDlgItems,
3320 cDlgItems)))
3321 {
3322 HWND hwndFocusItem;
3323
3324 /*
3325 * 2) create empty dialog frame
3326 *
3327 */
3328
3329 pDlgData->hwndDlg = hwndDlg;
3330
3331 /*
3332 * 3) compute size of all controls
3333 *
3334 */
3335
3336 Dlg2_CalcSizes(pDlgData);
3337
3338 if (pszlClient)
3339 {
3340 pszlClient->cx = pDlgData->szlClient.cx
3341 + 2 * (DLG_OUTER_SPACING_X * FACTOR_X);
3342 pszlClient->cy = pDlgData->szlClient.cy
3343 + 2 * (DLG_OUTER_SPACING_Y * FACTOR_Y);
3344 }
3345
3346 if (flFlags & DFFL_CREATECONTROLS)
3347 {
3348 if (!(arc = Dlg3_PositionAndCreate(pDlgData,
3349 &hwndFocusItem)))
3350 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
3351 }
3352 }
3353
3354 Dlg9_Cleanup(&pDlgData);
3355 }
3356 }
3357 CATCH(excpt1)
3358 {
3359 arc = ERROR_PROTECTION_VIOLATION;
3360 } END_CATCH();
3361
3362 if (arc)
3363 {
3364 CHAR szErr[300];
3365 sprintf(szErr, "Error %d occurred in " __FUNCTION__ ".", arc);
3366 winhDebugBox(NULLHANDLE,
3367 "Error in Dialog Manager",
3368 szErr);
3369 }
3370
3371 return arc;
3372}
3373
3374/*
3375 *@@ dlghResizeFrame:
3376 *
3377 *@@added V0.9.18 (2002-03-03) [umoeller]
3378 */
3379
3380VOID dlghResizeFrame(HWND hwndDlg,
3381 PSIZEL pszlClient)
3382{
3383 // calculate the frame size from the client size
3384 RECTL rclClient;
3385 rclClient.xLeft = 10;
3386 rclClient.yBottom = 10;
3387 rclClient.xRight = pszlClient->cx;
3388 rclClient.yTop = pszlClient->cy;
3389 WinCalcFrameRect(hwndDlg,
3390 &rclClient,
3391 FALSE); // frame from client
3392
3393 WinSetWindowPos(hwndDlg,
3394 0,
3395 10,
3396 10,
3397 rclClient.xRight,
3398 rclClient.yTop,
3399 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
3400}
3401
3402/* ******************************************************************
3403 *
3404 * Dialog arrays
3405 *
3406 ********************************************************************/
3407
3408/*
3409 *@@ dlghCreateArray:
3410 * creates a "dialog array" for dynamically
3411 * building a dialog template in memory.
3412 *
3413 * A dialog array is simply an array of
3414 * DLGHITEM structures, as you would normally
3415 * define them statically in the source.
3416 * However, there are situations where you
3417 * might want to leave out certain controls
3418 * depending on certain conditions, which
3419 * can be difficult with static arrays.
3420 *
3421 * As a result, these "array" functions have
3422 * been added to allow for adding static
3423 * DLGHITEM subarrays to a dynamic array in
3424 * memory, which can then be passed to the
3425 * formatter.
3426 *
3427 * Usage:
3428 *
3429 * 1) Call this function with the maximum
3430 * amount of DLGHITEM's that will need
3431 * to be allocated in cMaxItems. Set this
3432 * to the total sum of all DLGHITEM's
3433 * in all the subarrays.
3434 *
3435 * 2) For each of the subarrays, call
3436 * dlghAppendToArray to have the subarray
3437 * appended to the dialog array.
3438 * After each call, DLGARRAY.cDlgItemsNow
3439 * will contain the actual total count of
3440 * DLGHITEM's that were added.
3441 *
3442 * 3) Call dlghCreateDialog with the dialog
3443 * array.
3444 *
3445 * 4) Call dlghFreeArray.
3446 *
3447 * Sort of like this (error checking omitted):
3448 *
3449 + DLGHITEM dlgSampleFront = ... // always included
3450 + DLGHITEM dlgSampleSometimes = ... // not always included
3451 + DLGHITEM dlgSampleTail = ... // always included
3452 +
3453 + PDLGARRAY pArraySample = NULL;
3454 + // create array with sufficient size
3455 + dlghCreateArray( ARRAYITEMCOUNT(dlgSampleFront)
3456 + + ARRAYITEMCOUNT(dlgSampleSometimes)
3457 + + ARRAYITEMCOUNT(dlgSampleTail),
3458 + &pArraySample);
3459 +
3460 + // always include front
3461 + dlghAppendToArray(pArraySample,
3462 + dlgSampleFront,
3463 + ARRAYITEMCOUNT(dlgSampleFront));
3464 + // include "sometimes" conditionally
3465 + if (...)
3466 + dlghAppendToArray(pArraySample,
3467 + dlgSampleSometimes,
3468 + ARRAYITEMCOUNT(dlgSampleSometimes));
3469 + // include tail always
3470 + dlghAppendToArray(pArraySample,
3471 + dlgSampleTail,
3472 + ARRAYITEMCOUNT(dlgSampleTail));
3473 +
3474 + // now create the dialog from the array
3475 + dlghCreateDialog(&hwndDlg,
3476 + hwndOwner,
3477 + FCF_ ...
3478 + fnwpMyDialogProc,
3479 + "Title",
3480 + pArray->paDlgItems, // dialog array!
3481 + pArray->cDlgItemsNow, // real count of items!
3482 + NULL,
3483 + NULL);
3484 +
3485 + dlghFreeArray(&pArraySample);
3486 *
3487 *@@added V0.9.16 (2001-10-15) [umoeller]
3488 */
3489
3490APIRET dlghCreateArray(ULONG cMaxItems,
3491 PDLGARRAY *ppArray) // out: DLGARRAY
3492{
3493 APIRET arc = NO_ERROR;
3494 PDLGARRAY pArray;
3495
3496 if (pArray = NEW(DLGARRAY))
3497 {
3498 ULONG cb;
3499
3500 ZERO(pArray);
3501 if ( (cb = cMaxItems * sizeof(DLGHITEM))
3502 && (pArray->paDlgItems = (DLGHITEM*)malloc(cb))
3503 )
3504 {
3505 memset(pArray->paDlgItems, 0, cb);
3506 pArray->cDlgItemsMax = cMaxItems;
3507 *ppArray = pArray;
3508 }
3509 else
3510 arc = ERROR_NOT_ENOUGH_MEMORY;
3511
3512 if (arc)
3513 dlghFreeArray(&pArray);
3514 }
3515 else
3516 arc = ERROR_NOT_ENOUGH_MEMORY;
3517
3518 return arc;
3519}
3520
3521/*
3522 *@@ dlghFreeArray:
3523 * frees a dialog array created by dlghCreateArray.
3524 *
3525 *@@added V0.9.16 (2001-10-15) [umoeller]
3526 */
3527
3528APIRET dlghFreeArray(PDLGARRAY *ppArray)
3529{
3530 PDLGARRAY pArray;
3531 if ( (ppArray)
3532 && (pArray = *ppArray)
3533 )
3534 {
3535 if (pArray->paDlgItems)
3536 free(pArray->paDlgItems);
3537 free(pArray);
3538 }
3539 else
3540 return ERROR_INVALID_PARAMETER;
3541
3542 return NO_ERROR;
3543}
3544
3545/*
3546 *@@ dlghAppendToArray:
3547 * appends a subarray of DLGHITEM's to the
3548 * given DLGARRAY. See dlghCreateArray for
3549 * usage.
3550 *
3551 * Returns:
3552 *
3553 * -- NO_ERROR
3554 *
3555 * -- ERROR_INVALID_PARAMETER
3556 *
3557 * -- DLGERR_ARRAY_TOO_SMALL: pArray does not
3558 * have enough memory to hold the new items.
3559 * The cMaxItems parameter given to dlghCreateArray
3560 * wasn't large enough.
3561 *
3562 *@@added V0.9.16 (2001-10-15) [umoeller]
3563 */
3564
3565APIRET dlghAppendToArray(PDLGARRAY pArray, // in: dialog array created by dlghCreateArray
3566 PCDLGHITEM paItems, // in: subarray to be appended
3567 ULONG cItems) // in: subarray item count (NOT array size)
3568{
3569 APIRET arc = NO_ERROR;
3570 if (pArray)
3571 {
3572 if ( (pArray->cDlgItemsMax >= cItems)
3573 && (pArray->cDlgItemsMax - pArray->cDlgItemsNow >= cItems)
3574 )
3575 {
3576 // enough space left in the array:
3577 memcpy(&pArray->paDlgItems[pArray->cDlgItemsNow],
3578 paItems, // source
3579 cItems * sizeof(DLGHITEM));
3580 pArray->cDlgItemsNow += cItems;
3581 }
3582 else
3583 arc = DLGERR_ARRAY_TOO_SMALL;
3584 }
3585 else
3586 arc = ERROR_INVALID_PARAMETER;
3587
3588 return arc;
3589}
3590
3591/* ******************************************************************
3592 *
3593 * Standard dialogs
3594 *
3595 ********************************************************************/
3596
3597/*
3598 *@@ fnwpMessageBox:
3599 *
3600 *@@added V0.9.19 (2002-04-24) [umoeller]
3601 */
3602
3603MRESULT EXPENTRY fnwpMessageBox(HWND hwndBox, ULONG msg, MPARAM mp1, MPARAM mp2)
3604{
3605 switch (msg)
3606 {
3607 case WM_HELP:
3608 {
3609 PFNHELP pfnHelp;
3610 if (pfnHelp = (PFNHELP)WinQueryWindowPtr(hwndBox, QWL_USER))
3611 pfnHelp(hwndBox);
3612
3613 return 0;
3614 }
3615 }
3616
3617 return WinDefDlgProc(hwndBox, msg, mp1, mp2);
3618}
3619
3620/*
3621 *@@ dlghCreateMessageBox:
3622 *
3623 *@@added V0.9.13 (2001-06-21) [umoeller]
3624 *@@changed V0.9.14 (2001-07-26) [umoeller]: fixed missing focus on buttons
3625 *@@changed V0.9.19 (2002-04-24) [umoeller]: added pfnHelp
3626 *@@changed V0.9.20 (2002-07-12) [umoeller]: made icon spacing wider
3627 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed missing close button
3628 *@@changed V1.0.0 (2002-08-16) [umoeller]: now using table alignment
3629 *@@changed WarpIN V1.0.11 (2006-05-22) [pr]: add to Tasklist
3630 */
3631
3632APIRET dlghCreateMessageBox(HWND *phwndDlg,
3633 HWND hwndOwner,
3634 HPOINTER hptrIcon,
3635 PCSZ pcszTitle,
3636 PCSZ pcszMessage,
3637 PFNHELP pfnHelp, // in: help callback or NULL
3638 ULONG flFlags,
3639 PCSZ pcszFont,
3640 const MSGBOXSTRINGS *pStrings,
3641 PULONG pulAlarmFlag) // out: alarm sound to be played
3642{
3643 APIRET arc;
3644
3645 CONTROLDEF
3646 Icon = CONTROLDEF_ICON_WIDER(NULLHANDLE, 0),
3647 // made icon spacing wider V0.9.20 (2002-07-12) [umoeller]
3648 Spacing = CONTROLDEF_TEXT(NULL, 0, 1, 1),
3649 InfoText = CONTROLDEF_TEXT_WORDBREAK(NULL, 10, MSGBOX_TEXTWIDTH),
3650 Buttons[] =
3651 {
3652 CONTROLDEF_PUSHBUTTON(NULL, 1, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3653 CONTROLDEF_PUSHBUTTON(NULL, 2, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3654 CONTROLDEF_PUSHBUTTON(NULL, 3, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT),
3655 CONTROLDEF_HELPPUSHBUTTON(NULL, 4, STD_BUTTON_WIDTH, STD_BUTTON_HEIGHT)
3656 };
3657
3658 DLGHITEM MessageBoxFront[] =
3659 {
3660 START_TABLE_ALIGN,
3661 START_ROW(ROW_VALIGN_CENTER),
3662 CONTROL_DEF(&Icon),
3663 START_TABLE,
3664 START_ROW(ROW_VALIGN_CENTER),
3665 CONTROL_DEF(&Spacing),
3666 START_ROW(ROW_VALIGN_CENTER),
3667 CONTROL_DEF(&InfoText),
3668 START_ROW(ROW_VALIGN_CENTER),
3669 CONTROL_DEF(&Spacing),
3670 END_TABLE,
3671 START_ROW(ROW_VALIGN_CENTER),
3672 CONTROL_DEF(&Spacing),
3673 START_TABLE,
3674 START_ROW(ROW_VALIGN_CENTER),
3675 CONTROL_DEF(&Buttons[0]),
3676 CONTROL_DEF(&Buttons[1]),
3677 CONTROL_DEF(&Buttons[2]),
3678 },
3679 MessageBoxHelp[] =
3680 {
3681 CONTROL_DEF(&Buttons[3]),
3682 },
3683 MessageBoxTail[] =
3684 {
3685 END_TABLE,
3686 END_TABLE
3687 };
3688
3689 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
3690 PDLGARRAY pArrayBox;
3691
3692 PCSZ p0 = "Error",
3693 p1 = NULL,
3694 p2 = NULL;
3695
3696 Icon.pcszText = (PCSZ)hptrIcon;
3697 InfoText.pcszText = pcszMessage;
3698
3699 // now work on the three buttons of the dlg template:
3700 // give them proper titles or hide them
3701 if (flButtons == MB_OK)
3702 {
3703 p0 = pStrings->pcszOK;
3704 }
3705 else if (flButtons == MB_OKCANCEL)
3706 {
3707 p0 = pStrings->pcszOK;
3708 p1 = pStrings->pcszCancel;
3709 }
3710 else if (flButtons == MB_RETRYCANCEL)
3711 {
3712 p0 = pStrings->pcszRetry;
3713 p1 = pStrings->pcszCancel;
3714 }
3715 else if (flButtons == MB_ABORTRETRYIGNORE)
3716 {
3717 p0 = pStrings->pcszAbort;
3718 p1 = pStrings->pcszRetry;
3719 p2 = pStrings->pcszIgnore;
3720 }
3721 else if (flButtons == MB_YESNO)
3722 {
3723 p0 = pStrings->pcszYes;
3724 p1 = pStrings->pcszNo;
3725 }
3726 else if (flButtons == MB_YESNOCANCEL)
3727 {
3728 p0 = pStrings->pcszYes;
3729 p1 = pStrings->pcszNo;
3730 p2 = pStrings->pcszCancel;
3731 }
3732 else if (flButtons == MB_CANCEL)
3733 {
3734 p0 = pStrings->pcszCancel;
3735 }
3736 else if (flButtons == MB_ENTER)
3737 {
3738 p0 = pStrings->pcszEnter;
3739 }
3740 else if (flButtons == MB_ENTERCANCEL)
3741 {
3742 p0 = pStrings->pcszEnter;
3743 p1 = pStrings->pcszCancel;
3744 }
3745 else if (flButtons == MB_YES_YES2ALL_NO)
3746 {
3747 p0 = pStrings->pcszYes;
3748 p1 = pStrings->pcszYesToAll;
3749 p2 = pStrings->pcszNo;
3750 }
3751
3752 // now set strings and hide empty buttons
3753 Buttons[0].pcszText = p0;
3754
3755 if (p1)
3756 Buttons[1].pcszText = p1;
3757 else
3758 Buttons[1].flStyle &= ~WS_VISIBLE;
3759
3760 if (p2)
3761 Buttons[2].pcszText = p2;
3762 else
3763 Buttons[2].flStyle &= ~WS_VISIBLE;
3764
3765 // query default button IDs
3766 if (flFlags & MB_DEFBUTTON2)
3767 Buttons[1].flStyle |= BS_DEFAULT;
3768 else if (flFlags & MB_DEFBUTTON3)
3769 Buttons[2].flStyle |= BS_DEFAULT;
3770 else
3771 Buttons[0].flStyle |= BS_DEFAULT;
3772
3773 *pulAlarmFlag = WA_NOTE;
3774 if (flFlags & (MB_ICONHAND | MB_ERROR))
3775 *pulAlarmFlag = WA_ERROR;
3776 else if (flFlags & (MB_ICONEXCLAMATION | MB_WARNING))
3777 *pulAlarmFlag = WA_WARNING;
3778
3779 if (pfnHelp)
3780 Buttons[3].pcszText = pStrings->pcszHelp;
3781
3782 if (!(arc = dlghCreateArray( ARRAYITEMCOUNT(MessageBoxFront)
3783 + ARRAYITEMCOUNT(MessageBoxHelp)
3784 + ARRAYITEMCOUNT(MessageBoxTail),
3785 &pArrayBox)))
3786 {
3787 if ( (!(arc = dlghAppendToArray(pArrayBox,
3788 MessageBoxFront,
3789 ARRAYITEMCOUNT(MessageBoxFront))))
3790 && ( (!pfnHelp)
3791 || (!(arc = dlghAppendToArray(pArrayBox,
3792 MessageBoxHelp,
3793 ARRAYITEMCOUNT(MessageBoxHelp))))
3794 )
3795 && (!(arc = dlghAppendToArray(pArrayBox,
3796 MessageBoxTail,
3797 ARRAYITEMCOUNT(MessageBoxTail))))
3798 )
3799 {
3800 if (!(arc = dlghCreateDlg(phwndDlg,
3801 hwndOwner,
3802 FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
3803 | FCF_CLOSEBUTTON, // was missing V0.9.20 (2002-08-10) [umoeller]
3804 fnwpMessageBox,
3805 pcszTitle,
3806 pArrayBox->paDlgItems,
3807 pArrayBox->cDlgItemsNow,
3808 NULL,
3809 pcszFont)))
3810 {
3811 // WarpIN V1.0.11 (2006-05-22) [pr]
3812 winhAddToTasklist(*phwndDlg, NULLHANDLE);
3813 // added help callback V0.9.19 (2002-04-24) [umoeller]
3814 WinSetWindowPtr(*phwndDlg, QWL_USER, (PVOID)pfnHelp);
3815 }
3816 }
3817
3818 dlghFreeArray(&pArrayBox);
3819 }
3820
3821 return arc;
3822}
3823
3824/*
3825 *@@ dlghProcessMessageBox:
3826 *
3827 *@@added V0.9.13 (2001-06-21) [umoeller]
3828 */
3829
3830ULONG dlghProcessMessageBox(HWND hwndDlg,
3831 ULONG ulAlarmFlag,
3832 ULONG flFlags)
3833{
3834 ULONG ulrcDlg;
3835 ULONG flButtons = flFlags & 0xF; // low nibble contains MB_YESNO etc.
3836
3837 winhCenterWindow(hwndDlg);
3838
3839 if (flFlags & MB_SYSTEMMODAL)
3840 WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
3841
3842 if (ulAlarmFlag)
3843 WinAlarm(HWND_DESKTOP, ulAlarmFlag);
3844
3845 ulrcDlg = WinProcessDlg(hwndDlg);
3846
3847 WinDestroyWindow(hwndDlg);
3848
3849 if (flButtons == MB_OK)
3850 return MBID_OK;
3851 else if (flButtons == MB_OKCANCEL)
3852 switch (ulrcDlg)
3853 {
3854 case 1: return MBID_OK;
3855 default: return MBID_CANCEL;
3856 }
3857 else if (flButtons == MB_RETRYCANCEL)
3858 switch (ulrcDlg)
3859 {
3860 case 1: return MBID_RETRY;
3861 default: return MBID_CANCEL;
3862 }
3863 else if (flButtons == MB_ABORTRETRYIGNORE)
3864 switch (ulrcDlg)
3865 {
3866 case 2: return MBID_RETRY;
3867 case 3: return MBID_IGNORE;
3868 default: return MBID_ABORT;
3869 }
3870 else if (flButtons == MB_YESNO)
3871 switch (ulrcDlg)
3872 {
3873 case 1: return MBID_YES;
3874 default: return MBID_NO;
3875 }
3876 else if (flButtons == MB_YESNOCANCEL)
3877 switch (ulrcDlg)
3878 {
3879 case 1: return MBID_YES;
3880 case 2: return MBID_NO;
3881 default: return MBID_CANCEL;
3882 }
3883 else if (flButtons == MB_CANCEL)
3884 return MBID_CANCEL;
3885 else if (flButtons == MB_ENTER)
3886 return MBID_ENTER;
3887 else if (flButtons == MB_ENTERCANCEL)
3888 switch (ulrcDlg)
3889 {
3890 case 1: return MBID_ENTER;
3891 default: return MBID_CANCEL;
3892 }
3893 else if (flButtons == MB_YES_YES2ALL_NO)
3894 switch (ulrcDlg)
3895 {
3896 case 1: return MBID_YES;
3897 case 2: return MBID_YES2ALL;
3898 default: return MBID_NO;
3899 }
3900
3901 return (MBID_CANCEL);
3902}
3903
3904/*
3905 *@@ dlghMessageBox:
3906 * WinMessageBox replacement, which uses dlghCreateDlg
3907 * internally.
3908 *
3909 * This has all the flags of the standard call,
3910 * but looks much prettier. Besides, it allows
3911 * you to specify any icon to be displayed.
3912 *
3913 * Currently the following flStyle's are supported:
3914 *
3915 * -- MB_OK 0x0000
3916 * -- MB_OKCANCEL 0x0001
3917 * -- MB_RETRYCANCEL 0x0002
3918 * -- MB_ABORTRETRYIGNORE 0x0003
3919 * -- MB_YESNO 0x0004
3920 * -- MB_YESNOCANCEL 0x0005
3921 * -- MB_CANCEL 0x0006
3922 * -- MB_ENTER 0x0007 (not implemented yet)
3923 * -- MB_ENTERCANCEL 0x0008 (not implemented yet)
3924 *
3925 * -- MB_YES_YES2ALL_NO 0x0009
3926 * This is new: this has three buttons called "Yes"
3927 * (MBID_YES), "Yes to all" (MBID_YES2ALL), "No" (MBID_NO).
3928 *
3929 * -- MB_DEFBUTTON2 (for two-button styles)
3930 * -- MB_DEFBUTTON3 (for three-button styles)
3931 *
3932 * -- MB_ICONHAND
3933 * -- MB_ICONEXCLAMATION
3934 *
3935 * If (pfnHelp != NULL), a "Help" button is also added and
3936 * pfnHelp gets called when the user presses it or the F1
3937 * key.
3938 *
3939 * Returns MBID_* codes like WinMessageBox.
3940 *
3941 *@@added V0.9.13 (2001-06-21) [umoeller]
3942 *@@changed V0.9.19 (2002-04-24) [umoeller]: added pfnHelp
3943 */
3944
3945ULONG dlghMessageBox(HWND hwndOwner, // in: owner for msg box
3946 HPOINTER hptrIcon, // in: icon to display
3947 PCSZ pcszTitle, // in: title
3948 PCSZ pcszMessage, // in: message
3949 PFNHELP pfnHelp, // in: help callback or NULL
3950 ULONG flFlags, // in: standard message box flags
3951 PCSZ pcszFont, // in: font (e.g. "9.WarpSans")
3952 const MSGBOXSTRINGS *pStrings) // in: strings array
3953{
3954 HWND hwndDlg;
3955 ULONG ulAlarmFlag;
3956 APIRET arc;
3957 CHAR szMsg[100];
3958
3959 if ( (!(arc = dlghCreateMessageBox(&hwndDlg,
3960 hwndOwner,
3961 hptrIcon,
3962 pcszTitle,
3963 pcszMessage,
3964 pfnHelp,
3965 flFlags,
3966 pcszFont,
3967 pStrings,
3968 &ulAlarmFlag)))
3969 && (hwndDlg)
3970 )
3971 {
3972 // SHOW DIALOG
3973 return (dlghProcessMessageBox(hwndDlg,
3974 ulAlarmFlag,
3975 flFlags));
3976 }
3977
3978 sprintf(szMsg, "dlghCreateMessageBox reported error %u.", arc);
3979 WinMessageBox(HWND_DESKTOP,
3980 NULLHANDLE,
3981 "Error",
3982 szMsg,
3983 0,
3984 MB_CANCEL | MB_MOVEABLE);
3985
3986 return (DID_CANCEL);
3987}
3988
3989/*
3990 *@@ cmnTextEntryBox:
3991 * common dialog for entering a text string.
3992 * The dialog has a descriptive text on top
3993 * with an entry field below and "OK" and "Cancel"
3994 * buttons.
3995 *
3996 * The string from the user is returned in a
3997 * new buffer, which must be free'd by the caller.
3998 * Returns NULL if the user pressed "Cancel".
3999 *
4000 * fl can be any combination of the following
4001 * flags:
4002 *
4003 * -- TEBF_REMOVETILDE: tilde ("~") characters
4004 * are removed from pcszTitle before setting
4005 * the title. Useful for reusing menu item
4006 * texts.
4007 *
4008 * -- TEBF_REMOVEELLIPSE: ellipse ("...") strings
4009 * are removed from pcszTitle before setting
4010 * the title. Useful for reusing menu item
4011 * texts.
4012 *
4013 * -- TEBF_SELECTALL: the default text in the
4014 * entry field is initially highlighted.
4015 *
4016 *@@added V0.9.15 (2001-09-14) [umoeller]
4017 */
4018
4019PSZ dlghTextEntryBox(HWND hwndOwner,
4020 PCSZ pcszTitle, // in: dlg title
4021 PCSZ pcszDescription, // in: descriptive text above entry field
4022 PCSZ pcszDefault, // in: default text for entry field or NULL
4023 PCSZ pcszOK, // in: "OK" string
4024 PCSZ pcszCancel, // in: "Cancel" string
4025 ULONG ulMaxLen, // in: maximum length for entry
4026 ULONG fl, // in: TEBF_* flags
4027 PCSZ pcszFont) // in: font (e.g. "9.WarpSans")
4028{
4029 CONTROLDEF
4030 Static = CONTROLDEF_TEXT_WORDBREAK(
4031 NULL,
4032 -1,
4033 150),
4034 Entry = CONTROLDEF_ENTRYFIELD(
4035 NULL,
4036 999,
4037 150,
4038 SZL_AUTOSIZE),
4039 OKButton = CONTROLDEF_DEFPUSHBUTTON(
4040 NULL,
4041 DID_OK,
4042 STD_BUTTON_WIDTH,
4043 STD_BUTTON_HEIGHT),
4044 CancelButton = CONTROLDEF_PUSHBUTTON(
4045 NULL,
4046 DID_CANCEL,
4047 STD_BUTTON_WIDTH,
4048 STD_BUTTON_HEIGHT);
4049
4050 DLGHITEM DlgTemplate[] =
4051 {
4052 START_TABLE,
4053 START_ROW(0),
4054 CONTROL_DEF(&Static),
4055 START_ROW(0),
4056 CONTROL_DEF(&Entry),
4057 START_ROW(0),
4058 CONTROL_DEF(&OKButton),
4059 CONTROL_DEF(&CancelButton),
4060 END_TABLE
4061 };
4062
4063 HWND hwndDlg = NULLHANDLE;
4064 PSZ pszReturn = NULL;
4065 XSTRING strTitle;
4066
4067 xstrInitCopy(&strTitle, pcszTitle, 0);
4068
4069 if (fl & (TEBF_REMOVEELLIPSE | TEBF_REMOVETILDE))
4070 {
4071 ULONG ulOfs;
4072 if (fl & TEBF_REMOVEELLIPSE)
4073 {
4074 ulOfs = 0;
4075 while (xstrFindReplaceC(&strTitle,
4076 &ulOfs,
4077 "...",
4078 ""))
4079 ;
4080 }
4081
4082 if (fl & TEBF_REMOVETILDE)
4083 {
4084 ulOfs = 0;
4085 while (xstrFindReplaceC(&strTitle,
4086 &ulOfs,
4087 "~",
4088 ""))
4089 ;
4090 }
4091 }
4092
4093 Static.pcszText = pcszDescription;
4094
4095 OKButton.pcszText = pcszOK;
4096 CancelButton.pcszText = pcszCancel;
4097
4098 if (NO_ERROR == dlghCreateDlg(&hwndDlg,
4099 hwndOwner,
4100 FCF_FIXED_DLG,
4101 WinDefDlgProc,
4102 strTitle.psz,
4103 DlgTemplate, // DLGHITEM array
4104 ARRAYITEMCOUNT(DlgTemplate),
4105 NULL,
4106 pcszFont))
4107 {
4108 HWND hwndEF = WinWindowFromID(hwndDlg, 999);
4109 winhCenterWindow(hwndDlg);
4110 winhSetEntryFieldLimit(hwndEF, ulMaxLen);
4111 if (pcszDefault)
4112 {
4113 WinSetWindowText(hwndEF, (PSZ)pcszDefault);
4114 if (fl & TEBF_SELECTALL)
4115 winhEntryFieldSelectAll(hwndEF);
4116 }
4117 WinSetFocus(HWND_DESKTOP, hwndEF);
4118 if (DID_OK == WinProcessDlg(hwndDlg))
4119 pszReturn = winhQueryWindowText(hwndEF);
4120
4121 WinDestroyWindow(hwndDlg);
4122 }
4123
4124 xstrClear(&strTitle);
4125
4126 return (pszReturn);
4127}
4128
4129/* ******************************************************************
4130 *
4131 * Dialog input handlers
4132 *
4133 ********************************************************************/
4134
4135/*
4136 *@@ dlghSetPrevFocus:
4137 * "backward" function for rotating the focus
4138 * in a dialog when the "shift+tab" keys get
4139 * pressed.
4140 *
4141 * pllWindows must be a linked list with the
4142 * plain HWND window handles of the focussable
4143 * controls in the dialog.
4144 */
4145
4146VOID dlghSetPrevFocus(PVOID pvllWindows)
4147{
4148 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4149
4150 // check current focus
4151 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
4152
4153 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
4154
4155 BOOL fRestart = FALSE;
4156
4157 while (pNode)
4158 {
4159 CHAR szClass[100];
4160
4161 // previos node
4162 pNode = pNode->pPrevious;
4163
4164 if ( (!pNode) // no next, or not found:
4165 && (!fRestart) // avoid infinite looping if bad list
4166 )
4167 {
4168 pNode = lstQueryLastNode(pllWindows);
4169 fRestart = TRUE;
4170 }
4171
4172 if (pNode)
4173 {
4174 // check if this is a focusable control
4175 if (WinQueryClassName((HWND)pNode->pItemData,
4176 sizeof(szClass),
4177 szClass))
4178 {
4179 if ( (strcmp(szClass, "#5")) // not static
4180 )
4181 break;
4182 // else: go for next then
4183 }
4184 }
4185 }
4186
4187 if (pNode)
4188 {
4189 WinSetFocus(HWND_DESKTOP,
4190 (HWND)pNode->pItemData);
4191 }
4192}
4193
4194/*
4195 *@@ dlghSetNextFocus:
4196 * "forward" function for rotating the focus
4197 * in a dialog when the "ab" key gets pressed.
4198 *
4199 * pllWindows must be a linked list with the
4200 * plain HWND window handles of the focussable
4201 * controls in the dialog.
4202 */
4203
4204VOID dlghSetNextFocus(PVOID pvllWindows)
4205{
4206 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4207
4208 // check current focus
4209 HWND hwndFocus = WinQueryFocus(HWND_DESKTOP);
4210
4211 PLISTNODE pNode = lstNodeFromItem(pllWindows, (PVOID)hwndFocus);
4212
4213 BOOL fRestart = FALSE;
4214
4215 while (pNode)
4216 {
4217 CHAR szClass[100];
4218
4219 // next focus in node
4220 pNode = pNode->pNext;
4221
4222 if ( (!pNode) // no next, or not found:
4223 && (!fRestart) // avoid infinite looping if bad list
4224 )
4225 {
4226 pNode = lstQueryFirstNode(pllWindows);
4227 fRestart = TRUE;
4228 }
4229
4230 if (pNode)
4231 {
4232 // check if this is a focusable control
4233 if (WinQueryClassName((HWND)pNode->pItemData,
4234 sizeof(szClass),
4235 szClass))
4236 {
4237 if ( (strcmp(szClass, "#5")) // not static
4238 )
4239 break;
4240 // else: go for next then
4241 }
4242 }
4243 }
4244
4245 if (pNode)
4246 {
4247 WinSetFocus(HWND_DESKTOP,
4248 (HWND)pNode->pItemData);
4249 }
4250}
4251
4252/*
4253 *@@ dlghProcessMnemonic:
4254 * finds the control which matches usch
4255 * and gives it the focus. If this is a
4256 * static control, the next control in the
4257 * list is given focus instead. (Standard
4258 * dialog behavior.)
4259 *
4260 * Pass in usch from WM_CHAR. It is assumed
4261 * that the caller has already tested for
4262 * the "alt" key to be depressed.
4263 *
4264 *@@added V0.9.9 (2001-03-17) [umoeller]
4265 */
4266
4267HWND dlghProcessMnemonic(PVOID pvllWindows,
4268 USHORT usch)
4269{
4270 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4271
4272 HWND hwndFound = NULLHANDLE;
4273 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
4274 CHAR szClass[100];
4275
4276 while (pNode)
4277 {
4278 HWND hwnd = (HWND)pNode->pItemData;
4279
4280 if (WinSendMsg(hwnd,
4281 WM_MATCHMNEMONIC,
4282 (MPARAM)usch,
4283 0))
4284 {
4285 // according to the docs, only buttons and static
4286 // return TRUE to that msg;
4287 // if this is a static, give focus to the next
4288 // control
4289
4290 // _Pmpf((__FUNCTION__ ": hwnd 0x%lX", hwnd));
4291
4292 // check if this is a focusable control
4293 if (WinQueryClassName(hwnd,
4294 sizeof(szClass),
4295 szClass))
4296 {
4297 if (!strcmp(szClass, "#3"))
4298 // it's a button: click it
4299 WinSendMsg(hwnd, BM_CLICK, (MPARAM)TRUE, 0);
4300 else if (!strcmp(szClass, "#5"))
4301 {
4302 // it's a static: give focus to following control
4303 pNode = pNode->pNext;
4304 if (pNode)
4305 WinSetFocus(HWND_DESKTOP, (HWND)pNode->pItemData);
4306 }
4307 }
4308 else
4309 // any other control (are there any?): give them focus
4310 WinSetFocus(HWND_DESKTOP, hwnd);
4311
4312 // in any case, stop
4313 hwndFound = hwnd;
4314 break;
4315 }
4316
4317 pNode = pNode->pNext;
4318 }
4319
4320 return (hwndFound);
4321}
4322
4323/*
4324 *@@ dlghEnter:
4325 * presses the first button with BS_DEFAULT.
4326 */
4327
4328BOOL dlghEnter(PVOID pvllWindows)
4329{
4330 PLINKLIST pllWindows = (PLINKLIST)pvllWindows;
4331
4332 PLISTNODE pNode = lstQueryFirstNode(pllWindows);
4333 CHAR szClass[100];
4334 while (pNode)
4335 {
4336 HWND hwnd = (HWND)pNode->pItemData;
4337 if (WinQueryClassName(hwnd,
4338 sizeof(szClass),
4339 szClass))
4340 {
4341 if (!strcmp(szClass, "#3")) // button
4342 {
4343 // _Pmpf((__FUNCTION__ ": found button"));
4344 if ( (WinQueryWindowULong(hwnd, QWL_STYLE) & (BS_PUSHBUTTON | BS_DEFAULT))
4345 == (BS_PUSHBUTTON | BS_DEFAULT)
4346 )
4347 {
4348 // _Pmpf((" is default!"));
4349 WinPostMsg(hwnd,
4350 BM_CLICK,
4351 (MPARAM)TRUE, // upclick
4352 0);
4353 return (TRUE);
4354 }
4355 }
4356 }
4357
4358 pNode = pNode->pNext;
4359 }
4360
4361 return (FALSE);
4362}
4363
4364
Note: See TracBrowser for help on using the repository browser.