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

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

misc changes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 47.6 KB
Line 
1
2/*
3 *@@sourcefile dialog.c:
4 * contains PM helper functions to create and
5 * auto-format dialogs from control arrays in memory.
6 *
7 * See dlghCreateDlg for details.
8 *
9 * Usage: All PM programs.
10 *
11 * Function prefixes (new with V0.81):
12 * -- dlg* Dialog functions
13 *
14 * Note: Version numbering in this file relates to XWorkplace version
15 * numbering.
16 *
17 *@@added V0.9.9 (2001-04-01) [umoeller]
18 *@@header "helpers\dialog.h"
19 */
20
21/*
22 * Copyright (C) 2001 Ulrich M”ller.
23 * This file is part of the "XWorkplace helpers" source package.
24 * This is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published
26 * by the Free Software Foundation, in version 2 as it comes in the
27 * "COPYING" file of the XWorkplace main distribution.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 */
33
34#define OS2EMX_PLAIN_CHAR
35 // this is needed for "os2emx.h"; if this is defined,
36 // emx will define PSZ as _signed_ char, otherwise
37 // as unsigned char
38
39#define INCL_DOSERRORS
40
41#define INCL_WINWINDOWMGR
42#define INCL_WINFRAMEMGR
43#define INCL_WINDIALOGS
44#define INCL_WINBUTTONS
45#define INCL_WINSTATICS
46#define INCL_WINSYS
47
48#define INCL_GPIPRIMITIVES
49#define INCL_GPIBITMAPS
50#define INCL_GPILCIDS
51#include <os2.h>
52
53#include <stdlib.h>
54#include <string.h>
55#include <stdio.h>
56
57#include "setup.h" // code generation and debugging options
58
59#include "helpers\comctl.h"
60#include "helpers\dialog.h"
61#include "helpers\gpih.h"
62#include "helpers\linklist.h"
63#include "helpers\standards.h"
64#include "helpers\stringh.h"
65#include "helpers\winh.h"
66
67/*
68 *@@category: Helpers\PM helpers\Dialog templates
69 */
70
71/* ******************************************************************
72 *
73 * Private declarations
74 *
75 ********************************************************************/
76
77/*
78 *@@ DLGPRIVATE:
79 * private data to the dlg manager, allocated
80 * by dlghCreateDlg. This is what is really
81 * used, even though the prototype only
82 * declares DIALOGDATA.
83 */
84
85typedef struct _DLGPRIVATE
86{
87 // public data
88 HWND hwndDlg;
89
90 // definition data (private)
91 LINKLIST llTables;
92
93 HWND hwndFirstFocus;
94
95 POINTL ptlTotalOfs;
96
97 LINKLIST llControls; // linked list of all PCOLUMNDEF structs,
98 // in the order in which windows were
99 // created
100
101 const char *pcszControlsFont; // from dlghCreateDlg
102
103 HPS hps;
104 const char *pcszFontLast;
105 LONG lcidLast;
106} DLGPRIVATE, *PDLGPRIVATE;
107
108typedef struct _COLUMNDEF *PCOLUMNDEF;
109typedef struct _ROWDEF *PROWDEF;
110typedef struct _TABLEDEF *PTABLEDEF;
111
112/*
113 *@@ CONTROLPOS:
114 * control position. We don't want to use SWP.
115 */
116
117typedef struct _CONTROLPOS
118{
119 LONG x,
120 y,
121 cx,
122 cy;
123} CONTROLPOS, *PCONTROLPOS;
124
125/*
126 *@@ COLUMNDEF:
127 * representation of a table column.
128 * This is stored in a linked list in ROWDEF.
129 *
130 * A table column represents either a PM control
131 * window or another table, which may therefore
132 * be nested.
133 */
134
135typedef struct _COLUMNDEF
136{
137 PROWDEF pOwningRow; // row whose linked list this column belongs to
138
139 BOOL fIsNestedTable; // if TRUE, pvDefinition points to a nested TABLEDEF;
140 // if FALSE, pvDefinition points to a CONTROLDEF as
141 // specified by the user
142
143 PVOID pvDefinition; // either a PTABLEDEF or a PCONTROLDEF
144
145 CONTROLPOS cpControl, // real pos and size of control
146 cpColumn; // pos and size of column; can be wider, spacings applied
147
148 HWND hwndControl; // created control; NULLHANDLE for tables always
149
150} COLUMNDEF;
151
152/*
153 *@@ ROWDEF:
154 *
155 */
156
157typedef struct _ROWDEF
158{
159 PTABLEDEF pOwningTable; // table whose linked list this row belongs to
160
161 LINKLIST llColumns; // contains COLUMNDEF structs, no auto-free
162
163 ULONG flRowFormat; // one of:
164 // -- ROW_VALIGN_BOTTOM 0x0000
165 // -- ROW_VALIGN_CENTER 0x0001
166 // -- ROW_VALIGN_TOP 0x0002
167
168 CONTROLPOS cpRow;
169
170} ROWDEF;
171
172/*
173 *@@ TABLEDEF:
174 *
175 */
176
177typedef struct _TABLEDEF
178{
179 LINKLIST llRows; // contains ROWDEF structs, no auto-free
180
181 PCONTROLDEF pCtlDef; // if != NULL, we create a PM control around the table
182
183 CONTROLPOS cpTable;
184
185} TABLEDEF;
186
187/*
188 *@@ PROCESSMODE:
189 *
190 */
191
192typedef enum _PROCESSMODE
193{
194 PROCESS_CALC_SIZES, // step 1
195 PROCESS_CALC_POSITIONS, // step 3
196 PROCESS_CREATE_CONTROLS // step 4
197} PROCESSMODE;
198
199/* ******************************************************************
200 *
201 * Worker routines
202 *
203 ********************************************************************/
204
205#define PM_GROUP_SPACING_X 20
206#define PM_GROUP_SPACING_TOP 30
207
208VOID ProcessTable(PTABLEDEF pTableDef,
209 const CONTROLPOS *pcpTable,
210 PROCESSMODE ProcessMode,
211 PDLGPRIVATE pDlgData);
212
213/*
214 *@@ CalcAutoSizeText:
215 *
216 */
217
218VOID CalcAutoSizeText(PCONTROLDEF pControlDef,
219 BOOL fMultiLine, // in: if TRUE, multiple lines
220 PSIZEL pszlAuto, // out: computed size
221 PDLGPRIVATE pDlgData)
222{
223 BOOL fFind = TRUE;
224 RECTL rclText;
225
226 const char *pcszFontThis = pControlDef->pcszFont;
227 // can be NULL,
228 // or CTL_COMMON_FONT
229
230 if (pcszFontThis == CTL_COMMON_FONT)
231 pcszFontThis = pDlgData->pcszControlsFont;
232
233 if (!pDlgData->hps)
234 {
235 // first call: get a PS
236 pDlgData->hps = WinGetPS(pDlgData->hwndDlg);
237 fFind = TRUE;
238 }
239 else
240 if (strhcmp(pcszFontThis, pDlgData->pcszFontLast))
241 fFind = TRUE;
242
243 if (fFind)
244 {
245 FONTMETRICS fm;
246 LONG lcid;
247 LONG lPointSize = 0;
248 if (pDlgData->lcidLast)
249 {
250 // delete old font
251 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
252 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
253 }
254
255 // create new font
256 pDlgData->lcidLast = gpihFindPresFont(NULLHANDLE, // no window yet
257 FALSE,
258 pDlgData->hps,
259 pcszFontThis,
260 &fm,
261 &lPointSize);
262 GpiSetCharSet(pDlgData->hps, pDlgData->lcidLast);
263 if (fm.fsDefn & FM_DEFN_OUTLINE)
264 gpihSetPointSize(pDlgData->hps, lPointSize);
265
266 pszlAuto->cy = fm.lMaxBaselineExt + fm.lExternalLeading;
267 }
268
269 // ok, we FINALLY have a font now...
270 // get the control string and see how much space it needs
271 if (pControlDef->pcszText)
272 {
273 // do we have multiple lines?
274 if (fMultiLine)
275 {
276 RECTL rcl = {0, 0, 1000, 1000};
277 winhDrawFormattedText(pDlgData->hps,
278 &rcl,
279 pControlDef->pcszText,
280 DT_LEFT | DT_TOP | DT_WORDBREAK | DT_QUERYEXTENT);
281 pszlAuto->cx = rcl.xRight - rcl.xLeft;
282 pszlAuto->cy = rcl.yTop - rcl.yBottom;
283 }
284 else
285 {
286 POINTL aptl[TXTBOX_COUNT];
287 GpiQueryTextBox(pDlgData->hps,
288 strlen(pControlDef->pcszText),
289 (PCH)pControlDef->pcszText,
290 TXTBOX_COUNT,
291 aptl);
292 pszlAuto->cx = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
293 }
294 }
295}
296
297/*
298 *@@ CalcAutoSize:
299 *
300 */
301
302VOID CalcAutoSize(PCONTROLDEF pControlDef,
303 PSIZEL pszlAuto, // out: computed size
304 PDLGPRIVATE pDlgData)
305{
306 // dumb defaults
307 pszlAuto->cx = 100;
308 pszlAuto->cy = 30;
309
310 switch ((ULONG)pControlDef->pcszClass)
311 {
312 case 0xffff0003L: // WC_BUTTON:
313 CalcAutoSizeText(pControlDef,
314 FALSE, // no multiline
315 pszlAuto,
316 pDlgData);
317 if (pControlDef->flStyle & ( BS_AUTOCHECKBOX
318 | BS_AUTORADIOBUTTON
319 | BS_AUTO3STATE
320 | BS_3STATE
321 | BS_CHECKBOX
322 | BS_RADIOBUTTON))
323 pszlAuto->cx += 20; // @@@
324 else if (pControlDef->flStyle & BS_BITMAP)
325 ;
326 else if (pControlDef->flStyle & (BS_ICON | BS_MINIICON))
327 ;
328 // we can't test for BS_PUSHBUTTON because that's 0x0000
329 else if (!(pControlDef->flStyle & BS_USERBUTTON))
330 {
331 pszlAuto->cx += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) + 15);
332 pszlAuto->cy += (2 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) + 15);
333 }
334 break;
335
336 case 0xffff0005L: // WC_STATIC:
337 if (pControlDef->flStyle & SS_TEXT)
338 CalcAutoSizeText(pControlDef,
339 ((pControlDef->flStyle & DT_WORDBREAK) != 0),
340 pszlAuto,
341 pDlgData);
342 else if (pControlDef->flStyle & SS_BITMAP)
343 {
344 HBITMAP hbm = (HBITMAP)pControlDef->pcszText;
345 if (hbm)
346 {
347 BITMAPINFOHEADER2 bmih2;
348 ZERO(&bmih2);
349 bmih2.cbFix = sizeof(bmih2);
350 if (GpiQueryBitmapInfoHeader(hbm,
351 &bmih2))
352 {
353 pszlAuto->cx = bmih2.cx;
354 pszlAuto->cy = bmih2.cy;
355 }
356 }
357 }
358 break;
359 }
360}
361
362/*
363 *@@ ProcessColumn:
364 * processes a column, which per definition is either
365 * a control or a nested subtable.
366 *
367 * A column is part of a row, which in turn is part
368 * of a table. There can be several columns in a row,
369 * and several rows in a table.
370 *
371 * Since tables may be specified as columns, it is
372 * possible to produce complex dialog layouts by
373 * nesting tables.
374 *
375 * This does the following:
376 *
377 * -- PROCESS_CALC_SIZES: size is taken from control def,
378 * or for tables, this produces a recursive ProcessTable
379 * call.
380 * Preconditions: none.
381 *
382 * -- PROCESS_CALC_POSITIONS: position of each column
383 * is taken from *plX, which is increased by the
384 * column width by this call.
385 *
386 * Preconditions: Owning row must already have its
387 * y position properly set, or we can't compute
388 * ours. Besides, plX must point to the current X
389 * in the row and will be incremented by the columns
390 * size here.
391 *
392 * -- PROCESS_CREATE_CONTROLS: well, creates the controls.
393 *
394 * For tables, this recurses again. If the table has
395 * a string assigned, this also produces a group box
396 * after the recursion.
397 *
398 */
399
400VOID ProcessColumn(PCOLUMNDEF pColumnDef,
401 PROWDEF pOwningRow,
402 PROCESSMODE ProcessMode,
403 PLONG plX, // in/out: PROCESS_CALC_POSITIONS only
404 PDLGPRIVATE pDlgData)
405{
406 pColumnDef->pOwningRow = pOwningRow;
407
408 switch (ProcessMode)
409 {
410 /*
411 * PROCESS_CALC_SIZES:
412 * step 1.
413 */
414
415 case PROCESS_CALC_SIZES:
416 {
417 ULONG ulXSpacing = 0,
418 ulYSpacing = 0;
419 if (pColumnDef->fIsNestedTable)
420 {
421 // nested table: recurse!!
422 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
423 ProcessTable(pTableDef,
424 NULL,
425 ProcessMode,
426 pDlgData);
427
428 // store the size of the sub-table
429 pColumnDef->cpControl.cx = pTableDef->cpTable.cx;
430 pColumnDef->cpControl.cy = pTableDef->cpTable.cy;
431
432 // should we create a PM control around the table?
433 if (pTableDef->pCtlDef)
434 {
435 // yes: make this wider
436 ulXSpacing = (2 * PM_GROUP_SPACING_X);
437 ulYSpacing = (PM_GROUP_SPACING_X + PM_GROUP_SPACING_TOP);
438 }
439 }
440 else
441 {
442 // no nested table, but control:
443 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
444 PSIZEL pszl = &pControlDef->szlControlProposed;
445 SIZEL szlAuto;
446
447 if ( (pszl->cx == -1)
448 || (pszl->cy == -1)
449 )
450 {
451 CalcAutoSize(pControlDef,
452 &szlAuto,
453 pDlgData);
454 }
455
456 if (pszl->cx == -1)
457 pColumnDef->cpControl.cx = szlAuto.cx;
458 else
459 pColumnDef->cpControl.cx = pszl->cx;
460
461 if (pszl->cy == -1)
462 pColumnDef->cpControl.cy = szlAuto.cy;
463 else
464 pColumnDef->cpControl.cy = pszl->cy;
465
466 // @@@hack sizes
467
468 ulXSpacing = ulYSpacing = (2 * pControlDef->ulSpacing);
469 }
470
471 pColumnDef->cpColumn.cx = pColumnDef->cpControl.cx
472 + ulXSpacing;
473 pColumnDef->cpColumn.cy = pColumnDef->cpControl.cy
474 + ulYSpacing;
475 break; }
476
477 /*
478 * PROCESS_CALC_POSITIONS:
479 * step 2.
480 */
481
482 case PROCESS_CALC_POSITIONS:
483 {
484 // calculate column position: this includes spacing
485 ULONG ulSpacing = 0;
486
487 // column position = *plX on ProcessRow stack
488 pColumnDef->cpColumn.x = *plX;
489 pColumnDef->cpColumn.y = pOwningRow->cpRow.y;
490
491 // check vertical alignment of row;
492 // we might need to increase column y
493 switch (pOwningRow->flRowFormat & ROW_VALIGN_MASK)
494 {
495 // case ROW_VALIGN_BOTTOM: // do nothing
496
497 case ROW_VALIGN_CENTER:
498 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
499 pColumnDef->cpColumn.y
500 += ( (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy)
501 / 2);
502 break;
503
504 case ROW_VALIGN_TOP:
505 if (pColumnDef->cpColumn.cy < pOwningRow->cpRow.cy)
506 pColumnDef->cpColumn.y
507 += (pOwningRow->cpRow.cy - pColumnDef->cpColumn.cy);
508 break;
509 }
510
511 if (pColumnDef->fIsNestedTable)
512 {
513 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
514 // should we create a PM control around the table?
515 if (pTableDef->pCtlDef)
516 // yes:
517 ulSpacing = PM_GROUP_SPACING_X;
518 }
519 else
520 {
521 // no nested table, but control:
522 PCONTROLDEF pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
523 ulSpacing = pControlDef->ulSpacing;
524 }
525
526 // increase plX by column width
527 *plX += pColumnDef->cpColumn.cx;
528
529 // calculate CONTROL pos from COLUMN pos by applying spacing
530 pColumnDef->cpControl.x = pColumnDef->cpColumn.x
531 + ulSpacing;
532 pColumnDef->cpControl.y = pColumnDef->cpColumn.y
533 + ulSpacing;
534
535 if (pColumnDef->fIsNestedTable)
536 {
537 // nested table:
538 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
539
540 // recurse!! to create windows for the sub-table
541 ProcessTable(pTableDef,
542 &pColumnDef->cpControl, // start pos for new table
543 ProcessMode,
544 pDlgData);
545 }
546 break; }
547
548 /*
549 * PROCESS_CREATE_CONTROLS:
550 * step 3.
551 */
552
553 case PROCESS_CREATE_CONTROLS:
554 {
555 PCONTROLPOS pcp = NULL;
556 PCONTROLDEF pControlDef = NULL;
557 const char *pcszTitle = NULL;
558 ULONG flStyle = 0;
559 LHANDLE lHandleSet = NULLHANDLE;
560
561 if (pColumnDef->fIsNestedTable)
562 {
563 // nested table:
564 PTABLEDEF pTableDef = (PTABLEDEF)pColumnDef->pvDefinition;
565
566 // recurse!!
567 ProcessTable(pTableDef,
568 NULL,
569 ProcessMode,
570 pDlgData);
571
572 // should we create a PM control around the table?
573 // (do this AFTER the other controls from recursing,
574 // otherwise the stupid container doesn't show up)
575 if (pTableDef->pCtlDef)
576 {
577 // yes:
578 pcp = &pColumnDef->cpColumn; // !! not control
579 pControlDef = pTableDef->pCtlDef;
580 pcszTitle = pControlDef->pcszText;
581 flStyle = pControlDef->flStyle;
582 }
583 }
584 else
585 {
586 // no nested table, but control:
587 pControlDef = (PCONTROLDEF)pColumnDef->pvDefinition;
588 pcp = &pColumnDef->cpControl;
589 pcszTitle = pControlDef->pcszText;
590 flStyle = pControlDef->flStyle;
591
592 // change the title if this is a static with SS_BITMAP;
593 // we have used a HBITMAP in there!
594 if ( ((ULONG)pControlDef->pcszClass == 0xffff0005L) // WC_STATIC:
595 && (flStyle & SS_BITMAP)
596 )
597 {
598 // change style flag to not use SS_BITMAP;
599 // control creation fails otherwise (stupid, stupid PM)
600 flStyle = ((flStyle & ~SS_BITMAP) | SS_FGNDFRAME);
601 pcszTitle = "";
602 lHandleSet = (LHANDLE)pControlDef->pcszText;
603 }
604 }
605
606 if (pcp && pControlDef)
607 {
608 // create something:
609 PPRESPARAMS ppp = NULL;
610
611 const char *pcszFont = pControlDef->pcszFont;
612 // can be NULL, or CTL_COMMON_FONT
613 if (pcszFont == CTL_COMMON_FONT)
614 pcszFont = pDlgData->pcszControlsFont;
615
616 if (pcszFont)
617 winhStorePresParam(&ppp,
618 PP_FONTNAMESIZE,
619 strlen(pcszFont),
620 (PVOID)pcszFont);
621
622 pColumnDef->hwndControl
623 = WinCreateWindow(pDlgData->hwndDlg, // parent
624 (PSZ)pControlDef->pcszClass,
625 (pcszTitle) // hacked
626 ? (PSZ)pcszTitle
627 : "",
628 flStyle, // hacked
629 pcp->x + pDlgData->ptlTotalOfs.x,
630 pcp->y + pDlgData->ptlTotalOfs.y,
631 pcp->cx,
632 pcp->cy,
633 pDlgData->hwndDlg, // owner
634 HWND_BOTTOM,
635 pControlDef->usID,
636 NULL, // control data @@@
637 ppp);
638
639 if (pColumnDef->hwndControl && lHandleSet)
640 {
641 // subclass the damn static
642 ctlPrepareStretchedBitmap(pColumnDef->hwndControl,
643 TRUE);
644
645 WinSendMsg(pColumnDef->hwndControl,
646 SM_SETHANDLE,
647 (MPARAM)lHandleSet,
648 0);
649 }
650
651 if (ppp)
652 free(ppp);
653
654 if (pColumnDef->hwndControl)
655 {
656 lstAppendItem(&pDlgData->llControls,
657 pColumnDef);
658
659 // if this is the first control with WS_TABSTOP,
660 // we give it the focus later
661 if ( (flStyle & WS_TABSTOP)
662 && (!pDlgData->hwndFirstFocus)
663 )
664 pDlgData->hwndFirstFocus = pColumnDef->hwndControl;
665 }
666 }
667 break; }
668 }
669
670}
671
672/*
673 *@@ ProcessRow:
674 *
675 */
676
677VOID ProcessRow(PROWDEF pRowDef,
678 PTABLEDEF pOwningTable,
679 PROCESSMODE ProcessMode,
680 PLONG plY,
681 PDLGPRIVATE pDlgData)
682{
683 ULONG ul;
684 LONG lX;
685 PLISTNODE pNode;
686
687 pRowDef->pOwningTable = pOwningTable;
688
689 if (ProcessMode == PROCESS_CALC_SIZES)
690 {
691 pRowDef->cpRow.cx = 0;
692 pRowDef->cpRow.cy = 0;
693 }
694 else if (ProcessMode == PROCESS_CALC_POSITIONS)
695 {
696 // set up x and y so that the columns can
697 // base on that
698 pRowDef->cpRow.x = pOwningTable->cpTable.x;
699 // decrease y by row height
700 *plY -= pRowDef->cpRow.cy;
701 // and use that for our bottom position
702 pRowDef->cpRow.y = *plY;
703
704 // set lX to left of row; used by column calls below
705 lX = pRowDef->cpRow.x;
706 }
707
708 FOR_ALL_NODES(&pRowDef->llColumns, pNode)
709 {
710 PCOLUMNDEF pColumnDefThis = (PCOLUMNDEF)pNode->pItemData;
711
712 ProcessColumn(pColumnDefThis, pRowDef, ProcessMode, &lX, pDlgData);
713
714 if (ProcessMode == PROCESS_CALC_SIZES)
715 {
716 // row width = sum of all columns
717 pRowDef->cpRow.cx += pColumnDefThis->cpColumn.cx;
718
719 // row height = maximum height of a column
720 if (pRowDef->cpRow.cy < pColumnDefThis->cpColumn.cy)
721 pRowDef->cpRow.cy = pColumnDefThis->cpColumn.cy;
722 }
723 }
724}
725
726/*
727 *@@ ProcessTable:
728 *
729 * This routine is a bit sick because it can be
730 * called recursively if a nested table is found
731 * in a COLUMNDEF.
732 *
733 * With PROCESS_CALC_POSITIONS, pptl must specify
734 * the lower left corner of the table. For the
735 * root call, this will be {0, 0}; for nested calls,
736 * this must be the lower left corner of the column
737 * to which the nested table belongs.
738 *
739 */
740
741VOID ProcessTable(PTABLEDEF pTableDef,
742 const CONTROLPOS *pcpTable, // in: table position with PROCESS_CALC_POSITIONS
743 PROCESSMODE ProcessMode,
744 PDLGPRIVATE pDlgData)
745{
746 ULONG ul;
747 LONG lY;
748 PLISTNODE pNode;
749
750 if (ProcessMode == PROCESS_CALC_SIZES)
751 {
752 pTableDef->cpTable.cx = 0;
753 pTableDef->cpTable.cy = 0;
754 }
755 else if (ProcessMode == PROCESS_CALC_POSITIONS)
756 {
757 pTableDef->cpTable.x = pcpTable->x;
758 pTableDef->cpTable.y = pcpTable->y;
759
760 // start the rows on top
761 lY = pcpTable->y + pTableDef->cpTable.cy;
762 }
763
764 FOR_ALL_NODES(&pTableDef->llRows, pNode)
765 {
766 PROWDEF pRowDefThis = (PROWDEF)pNode->pItemData;
767
768 ProcessRow(pRowDefThis, pTableDef, ProcessMode, &lY, pDlgData);
769
770 if (ProcessMode == PROCESS_CALC_SIZES)
771 {
772 // table width = maximum width of a row
773 if (pTableDef->cpTable.cx < pRowDefThis->cpRow.cx)
774 pTableDef->cpTable.cx = pRowDefThis->cpRow.cx;
775
776 // table height = sum of all rows
777 pTableDef->cpTable.cy += pRowDefThis->cpRow.cy;
778 }
779 }
780}
781
782/*
783 *@@ ProcessAll:
784 *
785 * -- PROCESS_CALC_SIZES: calculates the sizes
786 * of all tables, rows, columns, and controls.
787 *
788 * -- PROCESS_CALC_POSITIONS: calculates the positions
789 * based on the sizes calculated before.
790 *
791 * -- PROCESS_CREATE_CONTROLS: creates the controls with the
792 * positions and sizes calculated before.
793 *
794 */
795
796VOID ProcessAll(PDLGPRIVATE pDlgData,
797 PSIZEL pszlClient,
798 PROCESSMODE ProcessMode)
799{
800 ULONG ul;
801 PLISTNODE pNode;
802 CONTROLPOS cpTable;
803 ZERO(&cpTable);
804
805 switch (ProcessMode)
806 {
807 case PROCESS_CALC_SIZES:
808 pszlClient->cx = 0;
809 pszlClient->cy = 0;
810 break;
811
812 case PROCESS_CALC_POSITIONS:
813 // start with the table on top
814 cpTable.y = pszlClient->cy;
815 break;
816 }
817
818 FOR_ALL_NODES(&pDlgData->llTables, pNode)
819 {
820 PTABLEDEF pTableDefThis = (PTABLEDEF)pNode->pItemData;
821
822 if (ProcessMode == PROCESS_CALC_POSITIONS)
823 {
824 cpTable.x = 0;
825 cpTable.y -= pTableDefThis->cpTable.cy;
826 }
827
828 ProcessTable(pTableDefThis,
829 &cpTable, // start pos
830 ProcessMode,
831 pDlgData);
832
833 if (ProcessMode == PROCESS_CALC_SIZES)
834 {
835 pszlClient->cx += pTableDefThis->cpTable.cx;
836 pszlClient->cy += pTableDefThis->cpTable.cy;
837 }
838 }
839}
840
841/*
842 *@@ CreateColumn:
843 *
844 */
845
846APIRET CreateColumn(PROWDEF pCurrentRow,
847 BOOL fIsNestedTable,
848 PVOID pvDefinition, // in: either PTABLEDEF or PCONTROLDEF
849 PCOLUMNDEF *ppColumnDef) // out: new COLUMNDEF
850{
851 APIRET arc = NO_ERROR;
852
853 if (!pCurrentRow)
854 arc = DLGERR_CONTROL_BEFORE_ROW;
855 else
856 {
857 // append the control def
858 if (!pvDefinition)
859 arc = DLGERR_NULL_CTL_DEF;
860 else
861 {
862 // create column and store ctl def
863 PCOLUMNDEF pColumnDef = NEW(COLUMNDEF);
864 if (!pColumnDef)
865 arc = ERROR_NOT_ENOUGH_MEMORY;
866 else
867 {
868 memset(pColumnDef, 0, sizeof(COLUMNDEF));
869 pColumnDef->pOwningRow = pCurrentRow;
870 pColumnDef->fIsNestedTable = fIsNestedTable;
871 pColumnDef->pvDefinition = pvDefinition;
872
873 *ppColumnDef = pColumnDef;
874 }
875 }
876 }
877
878 return (arc);
879}
880
881/* ******************************************************************
882 *
883 * Public APIs
884 *
885 ********************************************************************/
886
887/*
888 *@@ STACKITEM:
889 *
890 */
891
892typedef struct _STACKITEM
893{
894 PTABLEDEF pLastTable;
895 PROWDEF pLastRow;
896
897} STACKITEM, *PSTACKITEM;
898
899/*
900 *@@ dlghCreateDlg:
901 * replacement for WinCreateDlg for creating a dialog
902 * from a settings array in memory, which is formatted
903 * automatically.
904 *
905 * This does NOT use regular dialog templates from
906 * module resources. Instead, you pass in an array
907 * of _DLGHITEM structures, which define the controls
908 * and how they are to be formatted.
909 *
910 * A regular standard dialog would use something like
911 *
912 + FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN
913 *
914 * for flCreateFlags. To make the dlg sizeable, specify
915 * FCF_SIZEBORDER instead of FCF_DLGBORDER.
916 *
917 * <B>Usage:</B>
918 *
919 * Like WinLoadDlg, this creates a standard WC_FRAME and
920 * subclasses it with fnwpMyDlgProc. It then sends WM_INITDLG
921 * to the dialog with pCreateParams in mp2. You can use
922 * WinProcessDlg as usual. In your dlg proc, use WinDefDlgProc
923 * as usual.
924 *
925 * There is NO run-time overhead after creation; after this
926 * function returns, the dialog is a standard dialog as if
927 * loaded from WinLoadDlg.
928 *
929 * The array of _DLGHITEM structures defines how the
930 * dialog is set up. All this is ONLY used by this function
931 * and NOT needed after the dialog has been created.
932 *
933 * In _DLGHITEM, the "Type" field determines what this
934 * structure defines. A number of handy macros have been
935 * defined to make this easier and to provide type-checking
936 * at compile time.
937 *
938 * The macros are:
939 *
940 * -- START_TABLE starts a new table. The tables may nest,
941 * but must each be properly terminated with END_TABLE.
942 *
943 * -- START_ROW(fl) starts a new row in a table.
944 *
945 * fl specifies formatting flags for the row. This
946 * can be one of ROW_VALIGN_BOTTOM, ROW_VALIGN_CENTER,
947 * ROW_VALIGN_TOP and affects all items in the control.
948 *
949 * -- START_GROUP_TABLE(pDef) starts a group. This
950 * behaves exacly like START_TABLE, but in addition,
951 * it produces a control around the table. Useful
952 * for group boxes. pDef must point to a _CONTROLDEF,
953 * whose size parameter is ignored.
954 *
955 * This must also be terminated with END_TABLE.
956 *
957 * -- CONTROL_DEF(pDef) defines a control in a table row.
958 * pDef must point to a _CONTROLDEF structure.
959 *
960 * The main difference (and advantage) of this
961 * function is that there is NO information in
962 * _CONTROLDEF about a control's _position_.
963 * Instead, the structure only contains the _size_
964 * of the control. All positions are computed by
965 * this function, depending on the position of the
966 * control and its nesting within the various tables.
967 *
968 * If you specify SZL_AUTOSIZE, the size of the
969 * control is even computed automatically. Presently,
970 * this only works for statics with SS_TEXT and
971 * SS_BITMAP.
972 *
973 * Unless separated with TYPE_START_NEW_ROW items,
974 * subsequent control items will be considered
975 * to be in the same row (== positioned next to
976 * each other).
977 *
978 * There are a few rules, whose violation will produce
979 * an error:
980 *
981 * -- The entire array must be enclosed in a table
982 * (the "root" table).
983 *
984 * -- After START_TABLE or START_GROUP_TABLE, there must
985 * always be a START_ROW first.
986 *
987 * To create a dialog, set up arrays like the following:
988 *
989 + // control definitions referenced by DlgTemplate:
990 + CONTROLDEF
991 + (1) GroupDef = {
992 + WC_STATIC, "", ....,
993 + { 0, 0 }, // size, ignored for groups
994 + 5 // spacing
995 + },
996 + (2) CnrDef = {
997 + WC_CONTAINER, "", ....,
998 + { 50, 50 }, // size
999 + 5 // spacing
1000 + },
1001 + (3) Static = {
1002 + WC_STATIC, "Static below cnr", ...,
1003 + { SZL_AUTOSIZE, SZL_AUTOSIZE }, // size
1004 + 5 // spacing
1005 + },
1006 + (4) OKButton = {
1007 + WC_STATIC, "~OK", ...,
1008 + { 100, 30 }, // size
1009 + 5 // spacing
1010 + },
1011 + (5) CancelButton = {
1012 + WC_STATIC, "~Cancel", ...,
1013 + { 100, 30 }, // size
1014 + 5 // spacing
1015 + };
1016 +
1017 + DLGHITEM DlgTemplate[] =
1018 + {
1019 + START_TABLE, // root table, must exist
1020 + START_ROW(0), // row 1 in the root table
1021 + // create group on top
1022 + (1) START_GROUP_TABLE(&Group),
1023 + START_ROW(0),
1024 + (2) CONTROL_DEF(&CnrDef),
1025 + START_ROW(0),
1026 + (3) CONTROL_DEF(&Static),
1027 + END_TABLE, // end of group
1028 + START_ROW(0), // row 2 in the root table
1029 + // two buttons next to each other
1030 + (4) CONTROL_DEF(&OKButton),
1031 + (5) CONTROL_DEF(&CancelButton),
1032 + END_TABLE
1033 + }
1034 *
1035 * This will produce a dlg like this:
1036 *
1037 + ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
1038 + º º
1039 + º ÚÄ Group (1) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1040 + º ³ ³ º
1041 + º ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ º
1042 + º ³ ³ ³ ³ º
1043 + º ³ ³ Cnr inside group (2) ³ ³ º
1044 + º ³ ³ ³ ³ º
1045 + º ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ º
1046 + º ³ Static below cnr (3) ³ º
1047 + º ³ ³ º
1048 + º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1049 + º ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º
1050 + º ³ OK (4) ³ ³ Cancel (5) ³ º
1051 + º ÀÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º
1052 + º º
1053 + ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍŒ
1054 *
1055 * <B>Errors:</B>
1056 *
1057 * This does not return a HWND, but an APIRET. This will be
1058 * one of the following:
1059 *
1060 * -- NO_ERROR: only in that case, the phwndDlg ptr
1061 * receives the HWND of the new dialog, which can
1062 * then be given to WinProcessDlg. Don't forget
1063 * WinDestroyWindow.
1064 *
1065 * -- ERROR_NOT_ENOUGH_MEMORY
1066 *
1067 * -- DLGERR_ROW_BEFORE_TABLE: a row definition appeared
1068 * outside a table definition.
1069 *
1070 * -- DLGERR_CONTROL_BEFORE_ROW: a control definition
1071 * appeared right after a table definition. You must
1072 * specify a row first.
1073 *
1074 * -- DLGERR_NULL_CTL_DEF: TYPE_END_TABLE was specified,
1075 * but the CONTROLDEF ptr was NULL.
1076 *
1077 * -- DLGERR_CANNOT_CREATE_FRAME: unable to create the
1078 * WC_FRAME window. Maybe an invalid owner was specified.
1079 *
1080 * -- DLGERR_INVALID_CODE: invalid "Type" field in
1081 * DLGHITEM.
1082 *
1083 * -- DLGERR_TABLE_NOT_CLOSED, DLGERR_TOO_MANY_TABLES_CLOSED:
1084 * improper nesting of TYPE_START_NEW_TABLE and
1085 * TYPE_END_TABLE fields.
1086 *
1087 * <B>Example:</B>
1088 *
1089 * The typical calling sequence would be:
1090 *
1091 + HWND hwndDlg = NULLHANDLE;
1092 + if (NO_ERROR == dlghCreateDlg(&hwndDlg,
1093 + hwndOwner,
1094 + FCF_DLGBORDER | FCF_NOBYTEALIGN,
1095 + fnwpMyDlgProc,
1096 + "My Dlg Title",
1097 + G_aMyDialogTemplate,
1098 + ARRAYITEMSIZE(G_aMyDialogTemplate),
1099 + NULL))
1100 + {
1101 + ULONG idReturn = WinProcessDlg(pDlgData->hwndDlg);
1102 + WinDestroyWindow(hwndDlg);
1103 + }
1104 *
1105 *
1106 */
1107
1108APIRET dlghCreateDlg(HWND *phwndDlg, // out: new dialog
1109 HWND hwndOwner,
1110 ULONG flCreateFlags, // in: standard FCF_* frame flags
1111 PFNWP pfnwpDialogProc,
1112 const char *pcszDlgTitle,
1113 PDLGHITEM paDlgItems, // in: definition array
1114 ULONG cDlgItems, // in: array item count (NOT array size)
1115 PVOID pCreateParams, // in: for mp2 of WM_INITDLG
1116 const char *pcszControlsFont) // in: font for ctls with CTL_COMMON_FONT
1117{
1118 APIRET arc = NO_ERROR;
1119
1120 #define SPACING 10
1121
1122 PTABLEDEF pCurrentTable = NULL;
1123 PROWDEF pCurrentRow = NULL;
1124 ULONG ul;
1125 LINKLIST llStack;
1126
1127 PDLGPRIVATE pDlgData = NEW(DLGPRIVATE);
1128
1129 if (!pDlgData)
1130 return (ERROR_NOT_ENOUGH_MEMORY);
1131
1132 ZERO(pDlgData);
1133 lstInit(&pDlgData->llTables, FALSE);
1134 lstInit(&pDlgData->llControls, FALSE);
1135
1136 pDlgData->pcszControlsFont = pcszControlsFont;
1137
1138 /*
1139 * 1) parse the table and create structures from it
1140 *
1141 */
1142
1143 lstInit(&llStack, TRUE); // this is our stack for nested table definitions
1144
1145 for (ul = 0;
1146 ul < cDlgItems;
1147 ul++)
1148 {
1149 PDLGHITEM pItemThis = &paDlgItems[ul];
1150
1151 switch (pItemThis->Type)
1152 {
1153 /*
1154 * TYPE_START_NEW_TABLE:
1155 *
1156 */
1157
1158 case TYPE_START_NEW_TABLE:
1159 {
1160 // root table or nested?
1161 BOOL fIsRoot = (pCurrentTable == NULL);
1162
1163 // push the current table on the stack
1164 PSTACKITEM pStackItem = NEW(STACKITEM);
1165 if (pStackItem)
1166 {
1167 pStackItem->pLastTable = pCurrentTable;
1168 pStackItem->pLastRow = pCurrentRow;
1169 lstPush(&llStack, pStackItem);
1170 }
1171
1172 // create new table
1173 pCurrentTable = NEW(TABLEDEF);
1174 if (!pCurrentTable)
1175 arc = ERROR_NOT_ENOUGH_MEMORY;
1176 else
1177 {
1178 ZERO(pCurrentTable);
1179
1180 lstInit(&pCurrentTable->llRows, FALSE);
1181
1182 if (pItemThis->ulData)
1183 // control specified: store it (this will become a PM group)
1184 pCurrentTable->pCtlDef = (PCONTROLDEF)pItemThis->ulData;
1185
1186 if (fIsRoot)
1187 // root table:
1188 // append to dialog data list
1189 lstAppendItem(&pDlgData->llTables, pCurrentTable);
1190 else
1191 {
1192 // nested table:
1193 // create "table" column for this
1194 PCOLUMNDEF pColumnDef;
1195 arc = CreateColumn(pCurrentRow,
1196 TRUE, // nested table
1197 pCurrentTable,
1198 &pColumnDef);
1199 if (!arc)
1200 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1201 }
1202 }
1203
1204 pCurrentRow = NULL;
1205 break; }
1206
1207 /*
1208 * TYPE_START_NEW_ROW:
1209 *
1210 */
1211
1212 case TYPE_START_NEW_ROW:
1213 {
1214 if (!pCurrentTable)
1215 arc = DLGERR_ROW_BEFORE_TABLE;
1216 else
1217 {
1218 // create new row
1219 pCurrentRow = NEW(ROWDEF);
1220 if (!pCurrentRow)
1221 arc = ERROR_NOT_ENOUGH_MEMORY;
1222 else
1223 {
1224 ZERO(pCurrentRow);
1225
1226 pCurrentRow->pOwningTable = pCurrentTable;
1227 lstInit(&pCurrentRow->llColumns, FALSE);
1228
1229 pCurrentRow->flRowFormat = pItemThis->ulData;
1230
1231 lstAppendItem(&pCurrentTable->llRows, pCurrentRow);
1232 }
1233 }
1234 break; }
1235
1236 /*
1237 * TYPE_CONTROL_DEF:
1238 *
1239 */
1240
1241 case TYPE_CONTROL_DEF:
1242 {
1243 PCOLUMNDEF pColumnDef;
1244 arc = CreateColumn(pCurrentRow,
1245 FALSE, // no nested table
1246 (PVOID)pItemThis->ulData,
1247 &pColumnDef);
1248 if (!arc)
1249 lstAppendItem(&pCurrentRow->llColumns, pColumnDef);
1250 break; }
1251
1252 /*
1253 * TYPE_END_TABLE:
1254 *
1255 */
1256
1257 case TYPE_END_TABLE:
1258 {
1259 PLISTNODE pNode = lstPop(&llStack);
1260 if (!pNode)
1261 // nothing on the stack:
1262 arc = DLGERR_TOO_MANY_TABLES_CLOSED;
1263 else
1264 {
1265 PSTACKITEM pStackItem = (PSTACKITEM)pNode->pItemData;
1266 pCurrentTable = pStackItem->pLastTable;
1267 pCurrentRow = pStackItem->pLastRow;
1268
1269 lstRemoveNode(&llStack, pNode);
1270 }
1271 break; }
1272
1273 default:
1274 arc = DLGERR_INVALID_CODE;
1275 }
1276
1277 if (arc)
1278 break;
1279 }
1280
1281 if (arc == NO_ERROR)
1282 if (lstCountItems(&llStack))
1283 arc = DLGERR_TABLE_NOT_CLOSED;
1284
1285 lstClear(&llStack);
1286
1287 if (arc == NO_ERROR)
1288 {
1289 /*
1290 * 2) create empty dialog frame
1291 *
1292 */
1293
1294 FRAMECDATA fcData = {0};
1295
1296 #define FCF_DIALOGBOX 0x40000000L
1297
1298 fcData.cb = sizeof(FRAMECDATA);
1299 fcData.flCreateFlags = flCreateFlags | FCF_DIALOGBOX;
1300
1301 pDlgData->hwndDlg = WinCreateWindow(HWND_DESKTOP,
1302 WC_FRAME,
1303 (PSZ)pcszDlgTitle,
1304 0, // style; invisible for now
1305 0, 0, 0, 0,
1306 hwndOwner,
1307 HWND_TOP,
1308 0, // ID
1309 &fcData,
1310 NULL); // presparams
1311
1312 if (!pDlgData->hwndDlg)
1313 arc = DLGERR_CANNOT_CREATE_FRAME;
1314 else
1315 {
1316 SIZEL szlClient = {0};
1317 RECTL rclClient;
1318 HWND hwndFocusItem = NULLHANDLE;
1319
1320 /*
1321 * 3) compute size of client after computing all control sizes
1322 *
1323 */
1324
1325 ProcessAll(pDlgData,
1326 &szlClient,
1327 PROCESS_CALC_SIZES);
1328
1329 // this might have created a HPS and a font in dlgdata
1330 // for auto-sizing controls
1331 if (pDlgData->hps)
1332 {
1333 if (pDlgData->lcidLast)
1334 {
1335 // delete old font
1336 GpiSetCharSet(pDlgData->hps, LCID_DEFAULT);
1337 GpiDeleteSetId(pDlgData->hps, pDlgData->lcidLast);
1338 pDlgData->lcidLast = 0;
1339 }
1340
1341 WinReleasePS(pDlgData->hps);
1342 pDlgData->hps = NULLHANDLE;
1343 }
1344
1345 WinSubclassWindow(pDlgData->hwndDlg, pfnwpDialogProc);
1346
1347 // calculate the frame size from the client size
1348 rclClient.xLeft = 10;
1349 rclClient.yBottom = 10;
1350 rclClient.xRight = szlClient.cx + 2 * SPACING;
1351 rclClient.yTop = szlClient.cy + 2 * SPACING;
1352 WinCalcFrameRect(pDlgData->hwndDlg,
1353 &rclClient,
1354 FALSE); // frame from client
1355
1356 WinSetWindowPos(pDlgData->hwndDlg,
1357 0,
1358 10,
1359 10,
1360 rclClient.xRight,
1361 rclClient.yTop,
1362 SWP_MOVE | SWP_SIZE | SWP_NOADJUST);
1363
1364 /*
1365 * 4) compute positions of all controls
1366 *
1367 */
1368
1369 ProcessAll(pDlgData,
1370 &szlClient,
1371 PROCESS_CALC_POSITIONS);
1372
1373 /*
1374 * 5) create control windows, finally
1375 *
1376 */
1377
1378 pDlgData->ptlTotalOfs.x = SPACING;
1379 pDlgData->ptlTotalOfs.y = SPACING;
1380
1381 ProcessAll(pDlgData,
1382 &szlClient,
1383 PROCESS_CREATE_CONTROLS);
1384
1385 /*
1386 * 6) WM_INITDLG, set focus
1387 *
1388 */
1389
1390 hwndFocusItem = (pDlgData->hwndFirstFocus)
1391 ? pDlgData->hwndFirstFocus
1392 : pDlgData->hwndDlg;
1393 if (!WinSendMsg(pDlgData->hwndDlg,
1394 WM_INITDLG,
1395 (MPARAM)hwndFocusItem,
1396 (MPARAM)pCreateParams))
1397 {
1398 // if WM_INITDLG returns FALSE, this means
1399 // the dlg proc has not changed the focus;
1400 // we must then set the focus here
1401 WinSetFocus(HWND_DESKTOP, hwndFocusItem);
1402 }
1403 }
1404 }
1405
1406 if (pDlgData)
1407 {
1408 PLISTNODE pTableNode;
1409
1410 if (arc)
1411 {
1412 // error: clean up
1413 if (pDlgData->hwndDlg)
1414 WinDestroyWindow(pDlgData->hwndDlg);
1415 }
1416 else
1417 // no error: output dialog
1418 *phwndDlg = pDlgData->hwndDlg;
1419
1420 // in any case, clean up our mess:
1421
1422 // clean up the tables
1423 FOR_ALL_NODES(&pDlgData->llTables, pTableNode)
1424 {
1425 PTABLEDEF pTable = (PTABLEDEF)pTableNode->pItemData;
1426
1427 // for each table, clean up the rows
1428 PLISTNODE pRowNode;
1429 FOR_ALL_NODES(&pTable->llRows, pRowNode)
1430 {
1431 PROWDEF pRow = (PROWDEF)pRowNode->pItemData;
1432
1433 // for each row, clean up the columns
1434 PLISTNODE pColumnNode;
1435 FOR_ALL_NODES(&pRow->llColumns, pColumnNode)
1436 {
1437 PCOLUMNDEF pColumn = (PCOLUMNDEF)pColumnNode->pItemData;
1438 free(pColumn);
1439 }
1440 lstClear(&pRow->llColumns);
1441
1442 free(pRow);
1443 }
1444 lstClear(&pTable->llRows);
1445
1446 free(pTable);
1447 }
1448
1449 lstClear(&pDlgData->llTables);
1450 lstClear(&pDlgData->llControls);
1451
1452 free(pDlgData);
1453 }
1454
1455 return (arc);
1456}
1457
1458
Note: See TracBrowser for help on using the repository browser.