home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / dialog.c < prev    next >
C/C++ Source or Header  |  2002-08-11  |  132KB  |  3,723 lines

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