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

  1.  
  2. /*
  3.  *@@sourcefile winh.c:
  4.  *      contains Presentation Manager helper functions that are
  5.  *      independent of a single application, i.e. these can be
  6.  *      used w/out the rest of the XWorkplace source in any PM
  7.  *      program.
  8.  *
  9.  *      Usage: All PM programs.
  10.  *
  11.  *      Function prefixes (new with V0.81):
  12.  *      --  winh*   Win (Presentation Manager) helper functions
  13.  *
  14.  *      Note: Version numbering in this file relates to XWorkplace version
  15.  *            numbering.
  16.  *
  17.  *@@header "helpers\winh.h"
  18.  */
  19.  
  20. /*
  21.  *      Copyright (C) 1997-2002 Ulrich Möller.
  22.  *      This file is part of the "XWorkplace helpers" source package.
  23.  *      This is free software; you can redistribute it and/or modify
  24.  *      it under the terms of the GNU General Public License as published
  25.  *      by the Free Software Foundation, in version 2 as it comes in the
  26.  *      "COPYING" file of the XWorkplace main distribution.
  27.  *      This program is distributed in the hope that it will be useful,
  28.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  29.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30.  *      GNU General Public License for more details.
  31.  */
  32.  
  33. #define OS2EMX_PLAIN_CHAR
  34.     // this is needed for "os2emx.h"; if this is defined,
  35.     // emx will define PSZ as _signed_ char, otherwise
  36.     // as unsigned char
  37.  
  38. #define INCL_DOSPROCESS
  39. #define INCL_DOSMODULEMGR
  40. #define INCL_DOSSEMAPHORES
  41. #define INCL_DOSDEVICES
  42. #define INCL_DOSDEVIOCTL
  43. #define INCL_DOSSESMGR
  44. #define INCL_DOSERRORS
  45.  
  46. #define INCL_WINWINDOWMGR
  47. #define INCL_WINMESSAGEMGR
  48. #define INCL_WINFRAMEMGR
  49. #define INCL_WININPUT
  50. #define INCL_WINDIALOGS
  51. #define INCL_WINPOINTERS
  52. #define INCL_WINRECTANGLES
  53. #define INCL_WINSHELLDATA
  54. #define INCL_WINTIMER
  55. #define INCL_WINSYS
  56. #define INCL_WINHELP
  57. #define INCL_WINPROGRAMLIST
  58. #define INCL_WINSWITCHLIST
  59. #define INCL_WINBUTTONS
  60. #define INCL_WINSTATICS
  61. #define INCL_WINMENUS
  62. #define INCL_WINENTRYFIELDS
  63. #define INCL_WINSCROLLBARS
  64. #define INCL_WINLISTBOXES
  65. #define INCL_WINSTDSPIN
  66. #define INCL_WINSTDSLIDER
  67. #define INCL_WINCIRCULARSLIDER
  68. #define INCL_WINSTDFILE
  69.  
  70. #define INCL_SPL
  71. #define INCL_SPLDOSPRINT
  72. #define INCL_SPLERRORS
  73.  
  74. #define INCL_GPIBITMAPS
  75. #define INCL_GPIPRIMITIVES
  76. #include <os2.h>
  77.  
  78. #include <stdlib.h>
  79. #include <string.h>
  80. #include <stdio.h>
  81. #include <stdarg.h>
  82.  
  83. #include "setup.h"                      // code generation and debugging options
  84.  
  85. #include "helpers\dosh.h"
  86. #include "helpers\winh.h"
  87. #include "helpers\prfh.h"
  88. #include "helpers\gpih.h"
  89. #include "helpers\standards.h"
  90. #include "helpers\stringh.h"
  91. #include "helpers\undoc.h"
  92. #include "helpers\xstring.h"            // extended string helpers
  93.  
  94. /*
  95.  *@@category: Helpers\PM helpers\Wrappers
  96.  */
  97.  
  98. /* ******************************************************************
  99.  *
  100.  *   Wrappers
  101.  *
  102.  ********************************************************************/
  103.  
  104. #ifdef WINH_STANDARDWRAPPERS
  105.  
  106.     /*
  107.      *@@ winhSendMsg:
  108.      *      wrapper for WinSendMsg.
  109.      *
  110.      *      If WINH_STANDARDWRAPPERS is #defined before
  111.      *      including win.h, all WinSendMsg calls are
  112.      *      redefined to use this wrapper instead. This
  113.      *      reduces the amount of external fixups required
  114.      *      for loading the module.
  115.      *
  116.      *@@added V0.9.12 (2001-05-18) [umoeller]
  117.      */
  118.  
  119.     MRESULT winhSendMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  120.     {
  121.         // put the call in brackets so the macro won't apply here
  122.         return ((WinSendMsg)(hwnd, msg, mp1, mp2));
  123.     }
  124.  
  125.     /*
  126.      *@@ winhSendDlgItemMsg:
  127.      *      wrapper for WinSendDlgItemMsg.
  128.      *
  129.      *      If WINH_STANDARDWRAPPERS is #defined before
  130.      *      including win.h, all WinSendMsg calls are
  131.      *      redefined to use this wrapper instead. This
  132.      *      reduces the amount of external fixups required
  133.      *      for loading the module.
  134.      *
  135.      *@@added V0.9.13 (2001-06-27) [umoeller]
  136.      */
  137.  
  138.     MRESULT winhSendDlgItemMsg(HWND hwnd, ULONG id, ULONG msg, MPARAM mp1, MPARAM mp2)
  139.     {
  140.         return ((WinSendDlgItemMsg)(hwnd, id, msg, mp1, mp2));
  141.     }
  142.  
  143.     /*
  144.      *@@ winhPostMsg:
  145.      *      wrapper for WinPostMsg.
  146.      *
  147.      *      If WINH_STANDARDWRAPPERS is #defined before
  148.      *      including win.h, all WinSendMsg calls are
  149.      *      redefined to use this wrapper instead. This
  150.      *      reduces the amount of external fixups required
  151.      *      for loading the module.
  152.      *
  153.      *@@added V0.9.12 (2001-05-18) [umoeller]
  154.      */
  155.  
  156.     BOOL winhPostMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  157.     {
  158.         // put the call in brackets so the macro won't apply here
  159.         return ((WinPostMsg)(hwnd, msg, mp1, mp2));
  160.     }
  161.  
  162.     /*
  163.      *@@ winhWindowFromID:
  164.      *
  165.      *@@added V0.9.12 (2001-05-18) [umoeller]
  166.      */
  167.  
  168.     HWND winhWindowFromID(HWND hwnd, ULONG id)
  169.     {
  170.         // put the call in brackets so the macro won't apply here
  171.         return ((WinWindowFromID)(hwnd, id));
  172.     }
  173.  
  174.     /*
  175.      *@@ winhQueryWindow:
  176.      *
  177.      *@@added V0.9.12 (2001-05-18) [umoeller]
  178.      */
  179.  
  180.     HWND winhQueryWindow(HWND hwnd, LONG lCode)
  181.     {
  182.         // put the call in brackets so the macro won't apply here
  183.         return ((WinQueryWindow)(hwnd, lCode));
  184.     }
  185.  
  186.     /*
  187.      *@@ winhQueryWindowPtr:
  188.      *
  189.      *@@added V0.9.13 (2001-06-21) [umoeller]
  190.      */
  191.  
  192.     PVOID winhQueryWindowPtr(HWND hwnd, LONG index)
  193.     {
  194.         // put the call in brackets so the macro won't apply here
  195.         return ((WinQueryWindowPtr)(hwnd, index));
  196.     }
  197.  
  198.     /*
  199.      *@@ winhSetWindowText2:
  200.      *
  201.      *@@added V0.9.13 (2001-06-21) [umoeller]
  202.      */
  203.  
  204.     BOOL winhSetWindowText2(HWND hwnd, const char *pcsz)
  205.     {
  206.         // put the call in brackets so the macro won't apply here
  207.         return (WinSetWindowText)(hwnd, (PSZ)pcsz);
  208.     }
  209.  
  210.     /*
  211.      *@@ winhSetDlgItemText:
  212.      *
  213.      *@@added V0.9.13 (2001-06-21) [umoeller]
  214.      */
  215.  
  216.     BOOL winhSetDlgItemText(HWND hwnd, ULONG id, const char *pcsz)
  217.     {
  218.         // put the call in brackets so the macro won't apply here
  219.         return (WinSetDlgItemText)(hwnd, id, (PSZ)pcsz);
  220.     }
  221.  
  222.     /*
  223.      *@@ winhRequestMutexSem:
  224.      *
  225.      *@@added V0.9.16 (2002-01-26) [umoeller]
  226.      */
  227.  
  228.     APIRET winhRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
  229.     {
  230.         // put the call in brackets so the macro won't apply here
  231.         return (WinRequestMutexSem)(hmtx, ulTimeout);
  232.     }
  233.  
  234. #endif // WINH_STANDARDWRAPPERS
  235.  
  236. /*
  237.  *@@category: Helpers\PM helpers\Rectangle helpers
  238.  */
  239.  
  240. /* ******************************************************************
  241.  *
  242.  *   Rectangle helpers
  243.  *
  244.  ********************************************************************/
  245.  
  246. /*
  247.  *@@ winhOffsetRect:
  248.  *      like WinOffsetRect, but doesn't require
  249.  *      an anchor block to be passed in. Why
  250.  *      the original would need an anchor block
  251.  *      for this awfully complicated task is
  252.  *      a mystery to me anyway.
  253.  *
  254.  *@@added V0.9.9 (2001-03-13) [umoeller]
  255.  */
  256.  
  257. VOID winhOffsetRect(PRECTL prcl,
  258.                     LONG lx,
  259.                     LONG ly)
  260. {
  261.     prcl->xLeft += lx;
  262.     prcl->xRight += lx;
  263.     prcl->yBottom += ly;
  264.     prcl->yTop += ly;
  265. }
  266.  
  267. /*
  268.  *@@category: Helpers\PM helpers\Generics
  269.  */
  270.  
  271. /* ******************************************************************
  272.  *
  273.  *   Generics
  274.  *
  275.  ********************************************************************/
  276.  
  277. /*
  278.  *@@ winhQueryWindowStyle:
  279.  *
  280.  *@@added V0.9.13 (2001-07-02) [umoeller]
  281.  */
  282.  
  283. ULONG winhQueryWindowStyle(HWND hwnd)
  284. {
  285.     return (WinQueryWindowULong(hwnd, QWL_STYLE));
  286. }
  287.  
  288. /*
  289.  *@@ winhEnableDlgItem:
  290.  *
  291.  *@@added V0.9.12 (2001-05-18) [umoeller]
  292.  */
  293.  
  294. BOOL winhEnableDlgItem(HWND hwndDlg,
  295.                        SHORT id,
  296.                        BOOL fEnable)
  297. {
  298.     return (WinEnableWindow(WinWindowFromID(hwndDlg, id), fEnable));
  299. }
  300.  
  301. /*
  302.  *@@ winhIsDlgItemEnabled:
  303.  *
  304.  *@@added V0.9.12 (2001-05-18) [umoeller]
  305.  */
  306.  
  307. BOOL winhIsDlgItemEnabled(HWND hwndDlg,
  308.                           SHORT id)
  309. {
  310.     return (WinIsWindowEnabled(WinWindowFromID(hwndDlg, id)));
  311. }
  312.  
  313.  
  314. /*
  315.  *@@category: Helpers\PM helpers\Menu helpers
  316.  */
  317.  
  318. /* ******************************************************************
  319.  *
  320.  *   Menu helpers
  321.  *
  322.  ********************************************************************/
  323.  
  324. /*
  325.  *@@ winhQueryMenuItem:
  326.  *      wrapper around MM_QUERYITEM.
  327.  *
  328.  *@@added V0.9.12 (2001-05-18) [umoeller]
  329.  */
  330.  
  331. BOOL winhQueryMenuItem(HWND hwndMenu,
  332.                        USHORT usItemID,
  333.                        BOOL fSearchSubmenus,
  334.                        PMENUITEM pmi)           // out: MENUITEM data
  335. {
  336.     return ((BOOL)WinSendMsg(hwndMenu,
  337.                              MM_QUERYITEM,
  338.                              MPFROM2SHORT(usItemID, fSearchSubmenus),
  339.                              (MPARAM)pmi));
  340. }
  341.  
  342. /*
  343.  *@@ winhQuerySubmenu:
  344.  *      tests whether sID specifies a submenu in
  345.  *      hMenu and returns the submenu window handle
  346.  *      if so.
  347.  *
  348.  *@@added V0.9.20 (2002-08-10) [umoeller]
  349.  */
  350.  
  351. HWND winhQuerySubmenu(HWND hMenu,
  352.                       SHORT sID)
  353. {
  354.     MENUITEM mi = {0};
  355.     if (    (WinSendMsg(hMenu,
  356.                         MM_QUERYITEM,
  357.                         MPFROM2SHORT(sID,
  358.                                      FALSE),
  359.                         (MPARAM)&mi))
  360.          && (mi.afStyle & MIS_SUBMENU)
  361.        )
  362.         return mi.hwndSubMenu;
  363.  
  364.     return NULLHANDLE;
  365. }
  366.  
  367. /*
  368.  *@@ winhInsertMenuItem:
  369.  *      this inserts one one menu item into a given menu.
  370.  *
  371.  *      Returns the return value of the MM_INSERTITEM msg:
  372.  *      --  MIT_MEMERROR:    space allocation for menu item failed
  373.  *      --  MIT_ERROR:       other error
  374.  *      --  other:           zero-based index of new item in menu.
  375.  */
  376.  
  377. SHORT winhInsertMenuItem(HWND hwndMenu,     // in:  menu to insert item into
  378.                          SHORT iPosition,   // in:  zero-based index of where to
  379.                                             //      insert or MIT_END
  380.                          SHORT sItemId,     // in:  ID of new menu item
  381.                          const char *pcszItemTitle,  // in:  title of new menu item
  382.                          SHORT afStyle,
  383.                             // in:  MIS_* style flags.
  384.                             // Valid menu item styles are:
  385.                             // --  MIS_SUBMENU
  386.                             // --  MIS_SEPARATOR
  387.                             // --  MIS_BITMAP: the display object is a bit map.
  388.                             // --  MIS_TEXT: the display object is a text string.
  389.                             // --  MIS_BUTTONSEPARATOR:
  390.                             //          The item is a menu button. Any menu can have zero,
  391.                             //          one, or two items of this type.  These are the last
  392.                             //          items in a menu and are automatically displayed after
  393.                             //          a separator bar. The user cannot move the cursor to
  394.                             //          these items, but can select them with the pointing
  395.                             //          device or with the appropriate key.
  396.                             // --  MIS_BREAK: the item begins a new row or column.
  397.                             // --  MIS_BREAKSEPARATOR:
  398.                             //          Same as MIS_BREAK, except that it draws a separator
  399.                             //          between rows or columns of a pull-down menu.
  400.                             //          This style can only be used within a submenu.
  401.                             // --  MIS_SYSCOMMAND:
  402.                             //          menu posts a WM_SYSCOMMAND message rather than a
  403.                             //          WM_COMMAND message.
  404.                             // --  MIS_OWNERDRAW:
  405.                             //          WM_DRAWITEM and WM_MEASUREITEM notification messages
  406.                             //          are sent to the owner to draw the item or determine its size.
  407.                             // --  MIS_HELP:
  408.                             //          menu posts a WM_HELP message rather than a
  409.                             //          WM_COMMAND message.
  410.                             // --  MIS_STATIC
  411.                             //          This type of item exists for information purposes only.
  412.                             //          It cannot be selected with the pointing device or
  413.                             //          keyboard.
  414.                          SHORT afAttr)
  415.                             // in:  MIA_* attribute flags
  416.                             // Valid menu item attributes (afAttr) are:
  417.                             // --  MIA_HILITED: if and only if, the item is selected.
  418.                             // --  MIA_CHECKED: a check mark appears next to the item (submenu only).
  419.                             // --  MIA_DISABLED: item is disabled and cannot be selected.
  420.                             //         The item is drawn in a disabled state (gray).
  421.                             // --  MIA_FRAMED: a frame is drawn around the item (top-level menu only).
  422.                             // --  MIA_NODISMISS:
  423.                             //          if the item is selected, the submenu remains down. A menu
  424.                             //          with this attribute is not hidden until the  application
  425.                             //          or user explicitly does so, for example by selecting either
  426.                             //          another menu on the action bar or by pressing the escape key.
  427. {
  428.     MENUITEM mi;
  429.     SHORT    src = MIT_ERROR;
  430.  
  431.     mi.iPosition = iPosition;
  432.     mi.afStyle = afStyle;
  433.     mi.afAttribute = afAttr;
  434.     mi.id = sItemId;
  435.     mi.hwndSubMenu = 0;
  436.     mi.hItem = 0;
  437.     src = SHORT1FROMMR(WinSendMsg(hwndMenu,
  438.                                   MM_INSERTITEM,
  439.                                   (MPARAM)&mi,
  440.                                   (MPARAM)pcszItemTitle));
  441.     return (src);
  442. }
  443.  
  444. /*
  445.  *@@ winhInsertSubmenu:
  446.  *      this inserts a submenu into a given menu and, if
  447.  *      sItemId != 0, inserts one item into this new submenu also.
  448.  *
  449.  *      See winhInsertMenuItem for valid menu item styles and
  450.  *      attributes.
  451.  *
  452.  *      Returns the HWND of the new submenu.
  453.  */
  454.  
  455. HWND winhInsertSubmenu(HWND hwndMenu,       // in: menu to add submenu to
  456.                        ULONG iPosition,     // in: index where to add submenu or MIT_END
  457.                        SHORT sMenuId,       // in: menu ID of new submenu
  458.                        const char *pcszSubmenuTitle, // in: title of new submenu
  459.                        USHORT afMenuStyle,  // in: MIS* style flags for submenu;
  460.                                             // MIS_SUBMENU will always be added
  461.                        SHORT sItemId,       // in: ID of first item to add to submenu;
  462.                                             // if 0, no first item is inserted
  463.                        const char *pcszItemTitle,    // in: title of this item
  464.                                             // (if sItemID != 0)
  465.                        USHORT afItemStyle,  // in: style flags for this item, e.g. MIS_TEXT
  466.                                             // (this is ignored if sItemID == 0)
  467.                        USHORT afAttribute)  // in: attributes for this item, e.g. MIA_DISABLED
  468.                                             // (this is ignored if sItemID == 0)
  469. {
  470.     MENUITEM mi;
  471.     SHORT    src = MIT_ERROR;
  472.     HWND     hwndNewMenu;
  473.  
  474.     // create new, empty menu
  475.     hwndNewMenu = WinCreateMenu(hwndMenu,
  476.                                 NULL); // no menu template
  477.     if (hwndNewMenu)
  478.     {
  479.         // add "submenu item" to this empty menu;
  480.         // for some reason, PM always needs submenus
  481.         // to be a menu item
  482.         mi.iPosition = iPosition;
  483.         mi.afStyle = afMenuStyle | MIS_SUBMENU;
  484.         mi.afAttribute = 0;
  485.         mi.id = sMenuId;
  486.         mi.hwndSubMenu = hwndNewMenu;
  487.         mi.hItem = 0;
  488.         src = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_INSERTITEM, (MPARAM)&mi, (MPARAM)pcszSubmenuTitle));
  489.         if (    (src != MIT_MEMERROR)
  490.             &&  (src != MIT_ERROR)
  491.            )
  492.         {
  493.             // set the new menu's ID to the same as the
  494.             // submenu item
  495.             WinSetWindowUShort(hwndNewMenu, QWS_ID, sMenuId);
  496.  
  497.             if (sItemId)
  498.             {
  499.                 // item id given: insert first menu item also
  500.                 mi.iPosition = 0;
  501.                 mi.afStyle = afItemStyle;
  502.                 mi.afAttribute = afAttribute;
  503.                 mi.id = sItemId;
  504.                 mi.hwndSubMenu = 0;
  505.                 mi.hItem = 0;
  506.                 WinSendMsg(hwndNewMenu,
  507.                            MM_INSERTITEM,
  508.                            (MPARAM)&mi,
  509.                            (MPARAM)pcszItemTitle);
  510.             }
  511.         }
  512.     }
  513.     return (hwndNewMenu);
  514. }
  515.  
  516. /*
  517.  *@@ winhSetMenuCondCascade:
  518.  *      sets the "conditional cascade" style
  519.  *      on the specified submenu.
  520.  *
  521.  *      This style must always be enabled manually
  522.  *      because the resource compiler won't handle it.
  523.  *
  524.  *      Note: Pass in the _submenu_ window handle,
  525.  *      not the one of the parent. With lDefaultItem,
  526.  *      specify the item ID in the submenu which is
  527.  *      to be checked as the default item.
  528.  *
  529.  *@@added V0.9.12 (2001-05-22) [umoeller]
  530.  *@@changed V0.9.20 (2002-08-10) [umoeller]: now supporting calling this more than once
  531.  */
  532.  
  533. BOOL winhSetMenuCondCascade(HWND hwndMenu,          // in: submenu handle
  534.                             LONG lDefaultItem)      // in: item ID of new default item
  535. {
  536.     BOOL    brc;
  537.     ULONG   ulStyle = WinQueryWindowULong(hwndMenu, QWL_STYLE);
  538.     LONG    lOldDefault = -1;
  539.  
  540.     if (ulStyle & MS_CONDITIONALCASCADE)
  541.     {
  542.         // menu is already conditional cascade:
  543.         lOldDefault = (LONG)WinSendMsg(hwndMenu,
  544.                                        MM_QUERYDEFAULTITEMID,
  545.                                        0,
  546.                                        0);
  547.     }
  548.     else
  549.     {
  550.         ulStyle |= MS_CONDITIONALCASCADE;
  551.         WinSetWindowULong(hwndMenu, QWL_STYLE, ulStyle);
  552.     }
  553.  
  554.     // make the first item in the subfolder
  555.     // the default of cascading submenu
  556.     brc = (BOOL)WinSendMsg(hwndMenu,
  557.                            MM_SETDEFAULTITEMID,
  558.                            (MPARAM)lDefaultItem,
  559.                            0);
  560.  
  561.     if (    (lOldDefault != -1)
  562.          && (lOldDefault != lDefaultItem)
  563.        )
  564.     {
  565.         // unset the "checked" attribute of the old one
  566.         // or we'll have two in the menu
  567.         WinSendMsg(hwndMenu,
  568.                    MM_SETITEMATTR,
  569.                    MPFROM2SHORT(lOldDefault,
  570.                                 FALSE),
  571.                    MPFROM2SHORT(MIA_CHECKED, 0));
  572.     }
  573.  
  574.     return brc;
  575. }
  576.  
  577. /*
  578.  *@@ winhInsertMenuSeparator:
  579.  *      this inserts a separator into a given menu at
  580.  *      the given position (which may be MIT_END);
  581.  *      returns the position at which the item was
  582.  *      inserted.
  583.  */
  584.  
  585. SHORT winhInsertMenuSeparator(HWND hMenu,       // in: menu to add separator to
  586.                               SHORT iPosition,  // in: index where to add separator or MIT_END
  587.                               SHORT sId)        // in: separator menu ID (doesn't really matter)
  588. {
  589.     MENUITEM mi;
  590.     mi.iPosition = iPosition;
  591.     mi.afStyle = MIS_SEPARATOR;             // append separator
  592.     mi.afAttribute = 0;
  593.     mi.id = sId;
  594.     mi.hwndSubMenu = 0;
  595.     mi.hItem = 0;
  596.  
  597.     return (SHORT1FROMMR(WinSendMsg(hMenu,
  598.                                     MM_INSERTITEM,
  599.                                     (MPARAM)&mi,
  600.                                     (MPARAM)"")));
  601. }
  602.  
  603. /*
  604.  *@@ winhCopyMenuItem2:
  605.  *      copies a menu item from hmenuSource to hmenuTarget.
  606.  *
  607.  *      This creates a full duplicate. If usID specifies
  608.  *      a submenu, the entire submenu is copied as well
  609.  *      (this will then recurse).
  610.  *
  611.  *      fl can be any combination of:
  612.  *
  613.  *      --  COPYFL_STRIPTABS: strip off \t and everything
  614.  *          that follows, if present.
  615.  *
  616.  *      NOTE: Copying submenus will work only if each item
  617.  *      in the submenu has a unique menu ID. This is due
  618.  *      to the dumb implementation of menus in PM where
  619.  *      it is impossible to query menu items without
  620.  *      knowing their ID.
  621.  *
  622.  *@@added V0.9.9 (2001-03-09) [umoeller]
  623.  *@@changed V0.9.20 (2002-08-10) [umoeller]: renamed, added fl
  624.  */
  625.  
  626. BOOL winhCopyMenuItem2(HWND hmenuTarget,
  627.                        HWND hmenuSource,
  628.                        USHORT usID,
  629.                        SHORT sTargetPosition,    // in: position to insert at or MIT_END
  630.                        ULONG fl)                 // in: COPYFL_* flags
  631. {
  632.     BOOL brc = FALSE;
  633.     MENUITEM mi = {0};
  634.     if (WinSendMsg(hmenuSource,
  635.                    MM_QUERYITEM,
  636.                    MPFROM2SHORT(usID,
  637.                                 FALSE),
  638.                    (MPARAM)&mi))
  639.     {
  640.         // found in source:
  641.         // is it a separator?
  642.         if (mi.afStyle & MIS_SEPARATOR)
  643.             winhInsertMenuSeparator(hmenuTarget,
  644.                                     sTargetPosition,
  645.                                     usID);
  646.         else
  647.         {
  648.             // no separator:
  649.             // get item text
  650.             PSZ pszSource;
  651.             if (pszSource = winhQueryMenuItemText(hmenuSource,
  652.                                                   usID))
  653.             {
  654.                 PSZ p;
  655.                 // remove the hotkey description
  656.                 // V0.9.20 (2002-08-10) [umoeller]
  657.                 if (    (fl & COPYFL_STRIPTABS)
  658.                      && (p = strchr(pszSource, '\t'))
  659.                    )
  660.                     *p = '\0';
  661.  
  662.                 if (    (mi.afStyle & MIS_SUBMENU)
  663.                      && (mi.hwndSubMenu)
  664.                    )
  665.                 {
  666.                     // this is the top of a submenu:
  667.                     HWND hwndSubMenu;
  668.                     if (hwndSubMenu = winhInsertSubmenu(hmenuTarget,
  669.                                                         sTargetPosition,
  670.                                                         mi.id,
  671.                                                         pszSource,
  672.                                                         mi.afStyle,
  673.                                                         0,
  674.                                                         NULL,
  675.                                                         0,
  676.                                                         0))
  677.                     {
  678.                         // now copy all the items in the submenu
  679.                         SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
  680.                                                                    MM_QUERYITEMCOUNT,
  681.                                                                    0,
  682.                                                                    0));
  683.                         // loop through all entries in the original submenu
  684.                         ULONG i;
  685.                         for (i = 0;
  686.                              i < cMenuItems;
  687.                              i++)
  688.                         {
  689.                             SHORT id = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
  690.                                                          MM_ITEMIDFROMPOSITION,
  691.                                                          MPFROMSHORT(i),
  692.                                                          0));
  693.                             // recurse
  694.                             winhCopyMenuItem2(hwndSubMenu,
  695.                                              mi.hwndSubMenu,
  696.                                              id,
  697.                                              MIT_END,
  698.                                              fl);
  699.                         }
  700.  
  701.                         // now check... was the original submenu
  702.                         // "conditional cascade"?
  703.                         if (WinQueryWindowULong(mi.hwndSubMenu,
  704.                                                 QWL_STYLE)
  705.                                 & MS_CONDITIONALCASCADE)
  706.                             // yes:
  707.                         {
  708.                             // get the original default item
  709.                             SHORT sDefID = SHORT1FROMMR(WinSendMsg(mi.hwndSubMenu,
  710.                                                                    MM_QUERYDEFAULTITEMID,
  711.                                                                    0,
  712.                                                                    0));
  713.                             // set "conditional cascade style" on target too
  714.                             winhSetMenuCondCascade(hwndSubMenu, sDefID);
  715.                         }
  716.                     } // end if (hwndSubmenu)
  717.                 } // end if (    (mi.afStyle & MIS_SUBMENU)
  718.                 else
  719.                 {
  720.                     // no submenu:
  721.                     // just copy that item
  722.                     SHORT s;
  723.                     mi.iPosition = sTargetPosition;
  724.                     s = SHORT1FROMMR(WinSendMsg(hmenuTarget,
  725.                                                 MM_INSERTITEM,
  726.                                                 MPFROMP(&mi),
  727.                                                 MPFROMP(pszSource)));
  728.                     if (    (s != MIT_MEMERROR)
  729.                          && (s != MIT_ERROR)
  730.                        )
  731.                         brc = TRUE;
  732.                 }
  733.  
  734.                 free(pszSource);
  735.  
  736.             } // end if (pszSource)
  737.         } // end else if (mi.afStyle & MIS_SEPARATOR)
  738.     } // end if (WinSendMsg(hmenuSource, MM_QUERYITEM,...
  739.  
  740.     return brc;
  741. }
  742.  
  743. /*
  744.  *@@ winhCopyMenuItem:
  745.  *      wrapper for winhCopyMenuItem2 because it was
  746.  *      exported.
  747.  *
  748.  *@@added V0.9.20 (2002-08-10) [umoeller]
  749.  */
  750.  
  751. BOOL winhCopyMenuItem(HWND hmenuTarget,
  752.                       HWND hmenuSource,
  753.                       USHORT usID,
  754.                       SHORT sTargetPosition)    // in: position to insert at or MIT_END
  755. {
  756.     return winhCopyMenuItem2(hmenuTarget, hmenuSource, usID, sTargetPosition, 0);
  757. }
  758.  
  759. /*
  760.  *@@ winhMergeIntoSubMenu:
  761.  *      creates a new submenu in hmenuTarget with the
  762.  *      specified title at the specified position
  763.  *      and copies the entire contents of hmenuSource
  764.  *      into that.
  765.  *
  766.  *      Returns the window handle of the new submenu
  767.  *      or NULLHANDLE on errors.
  768.  *
  769.  *      NOTE: Copying submenus will work only if each item
  770.  *      in the submenu has a unique menu ID. This is due
  771.  *      to the dumb implementation of menus in PM where
  772.  *      it is impossible to query menu items without
  773.  *      knowing their ID.
  774.  *
  775.  *@@added V0.9.9 (2001-03-09) [umoeller]
  776.  */
  777.  
  778. HWND winhMergeIntoSubMenu(HWND hmenuTarget,         // in: menu where to create submenu
  779.                           SHORT sTargetPosition,    // in: position to insert at or MIT_END
  780.                           const char *pcszTitle,    // in: title of new submenu or NULL
  781.                           SHORT sID,                // in: ID of new submenu
  782.                           HWND hmenuSource)         // in: menu to merge
  783. {
  784.     HWND    hwndNewSubmenu;
  785.     if (hwndNewSubmenu = WinCreateMenu(hmenuTarget, NULL))
  786.     {
  787.         MENUITEM    mi = {0};
  788.         SHORT       src = 0;
  789.         // SHORT s = 0;
  790.         mi.iPosition = MIT_END;
  791.         mi.afStyle = MIS_TEXT | MIS_SUBMENU;
  792.         mi.id = 2000;
  793.         mi.hwndSubMenu = hwndNewSubmenu;
  794.  
  795.         WinSetWindowUShort(hwndNewSubmenu, QWS_ID, sID);
  796.  
  797.         // insert new submenu into hmenuTarget
  798.         src = SHORT1FROMMR(WinSendMsg(hmenuTarget,
  799.                                       MM_INSERTITEM,
  800.                                       (MPARAM)&mi,
  801.                                       (MPARAM)pcszTitle));
  802.         if (    (src != MIT_MEMERROR)
  803.             &&  (src != MIT_ERROR)
  804.            )
  805.         {
  806.             int i;
  807.             SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(hmenuSource,
  808.                                                        MM_QUERYITEMCOUNT,
  809.                                                        0, 0));
  810.  
  811.             // loop through all entries in the original menu
  812.             for (i = 0; i < cMenuItems; i++)
  813.             {
  814.                 SHORT id = SHORT1FROMMR(WinSendMsg(hmenuSource,
  815.                                                    MM_ITEMIDFROMPOSITION,
  816.                                                    MPFROMSHORT(i),
  817.                                                    0));
  818.                 winhCopyMenuItem(hwndNewSubmenu,
  819.                                  hmenuSource,
  820.                                  id,
  821.                                  MIT_END);
  822.             }
  823.         }
  824.         else
  825.         {
  826.             // error:
  827.             WinDestroyWindow(hwndNewSubmenu);
  828.             hwndNewSubmenu = NULLHANDLE;
  829.         }
  830.     }
  831.  
  832.     return (hwndNewSubmenu);
  833. }
  834.  
  835. /*
  836.  *@@ winhMergeIntoSubMenu:
  837.  *      copies all items from hmenuSource into hmenuTarget,
  838.  *      starting at the given position.
  839.  *
  840.  *      Returns the no. of items that were copied.
  841.  *
  842.  *      NOTE: Copying submenus will work only if each item
  843.  *      in the submenu has a unique menu ID. This is due
  844.  *      to the dumb implementation of menus in PM where
  845.  *      it is impossible to query menu items without
  846.  *      knowing their ID.
  847.  *
  848.  *@@added V0.9.20 (2002-08-10) [umoeller]
  849.  */
  850.  
  851. ULONG winhMergeMenus(HWND hmenuTarget,         // in: menu to copy items to
  852.                      SHORT sTargetPosition,    // in: position to insert at or MIT_END
  853.                      HWND hmenuSource,         // in: menu to merge
  854.                      ULONG fl)                 // in: COPYFL_* flags for winhCopyMenuItem2
  855. {
  856.     SHORT   sTarget = MIT_END;
  857.  
  858.     int i;
  859.     SHORT cMenuItems = SHORT1FROMMR(WinSendMsg(hmenuSource,
  860.                                                MM_QUERYITEMCOUNT,
  861.                                                0, 0));
  862.  
  863.     // loop through all entries in the original menu
  864.     for (i = 0; i < cMenuItems; i++)
  865.     {
  866.         SHORT id = SHORT1FROMMR(WinSendMsg(hmenuSource,
  867.                                            MM_ITEMIDFROMPOSITION,
  868.                                            MPFROMSHORT(i),
  869.                                            0));
  870.         winhCopyMenuItem2(hmenuTarget,
  871.                           hmenuSource,
  872.                           id,
  873.                           MIT_END,
  874.                           fl);
  875.     }
  876.  
  877.     return i;
  878. }
  879.  
  880. /*
  881.  *@@ winhQueryMenuItemText:
  882.  *      this returns a menu item text as a PSZ
  883.  *      to a newly allocated buffer or NULL if
  884.  *      not found.
  885.  *
  886.  *      Returns NULL on error. Use free()
  887.  *      to free the return value.
  888.  *
  889.  *      This uses MM_QUERYITEMTEXT internally.
  890.  *      PMREF doesn't say anything about this,
  891.  *      but from my testing this always recurses
  892.  *      into submenus.
  893.  *
  894.  *      Use the WinSetMenuItemText macro to
  895.  *      set the menu item text.
  896.  */
  897.  
  898. PSZ winhQueryMenuItemText(HWND hwndMenu,
  899.                           USHORT usItemID)  // in: menu item ID (not index)
  900. {
  901.     PSZ     prc = NULL;
  902.  
  903.     SHORT   sLength;
  904.     if (sLength = SHORT1FROMMR(WinSendMsg(hwndMenu,
  905.                                           MM_QUERYITEMTEXTLENGTH,
  906.                                           (MPARAM)(ULONG)usItemID,
  907.                                           (MPARAM)NULL)))
  908.     {
  909.         prc = (PSZ)malloc(sLength + 1);
  910.         WinSendMsg(hwndMenu,
  911.                    MM_QUERYITEMTEXT,
  912.                    MPFROM2SHORT(usItemID, sLength + 1),
  913.                    (MPARAM)prc);
  914.     }
  915.  
  916.     return (prc);
  917. }
  918.  
  919. /*
  920.  *@@ winhAppend2MenuItemText:
  921.  *
  922.  *@@added V0.9.2 (2000-03-08) [umoeller]
  923.  */
  924.  
  925. BOOL winhAppend2MenuItemText(HWND hwndMenu,
  926.                              USHORT usItemID,  // in: menu item ID (not index)
  927.                              const char *pcszAppend, // in: text to append
  928.                              BOOL fTab)    // in: if TRUE, add \t before pcszAppend
  929. {
  930.     BOOL brc = FALSE;
  931.     CHAR szItemText[400];
  932.     if (WinSendMsg(hwndMenu,
  933.                    MM_QUERYITEMTEXT,
  934.                    MPFROM2SHORT(usItemID,
  935.                                 sizeof(szItemText)),
  936.                    (MPARAM)szItemText))
  937.     {
  938.         // text copied:
  939.         if (fTab)
  940.         {
  941.             if (strchr(szItemText, '\t'))
  942.                 // we already have a tab:
  943.                 strcat(szItemText, " ");
  944.             else
  945.                 strcat(szItemText, "\t");
  946.         }
  947.         strcat(szItemText, pcszAppend);
  948.  
  949.         brc = (BOOL)WinSendMsg(hwndMenu,
  950.                                MM_SETITEMTEXT,
  951.                                MPFROMSHORT(usItemID),
  952.                                (MPARAM)szItemText);
  953.     }
  954.  
  955.     return brc;
  956. }
  957.  
  958. /*
  959.  *@@ winhMenuRemoveEllipse:
  960.  *      removes a "..." substring from a menu item
  961.  *      title, if found. This is useful if confirmations
  962.  *      have been turned off for a certain menu item, which
  963.  *      should be reflected in the menu.
  964.  */
  965.  
  966. VOID winhMenuRemoveEllipse(HWND hwndMenu,
  967.                            USHORT usItemId)    // in:  item to remove "..." from
  968. {
  969.     CHAR szBuf[255];
  970.     CHAR *p;
  971.     WinSendMsg(hwndMenu,
  972.                MM_QUERYITEMTEXT,
  973.                MPFROM2SHORT(usItemId, sizeof(szBuf)-1),
  974.                (MPARAM)&szBuf);
  975.     if ((p = strstr(szBuf, "...")))
  976.         strcpy(p, p+3);
  977.     WinSendMsg(hwndMenu,
  978.                MM_SETITEMTEXT,
  979.                MPFROMSHORT(usItemId),
  980.                (MPARAM)&szBuf);
  981. }
  982.  
  983. /*
  984.  *@@ winhQueryItemUnderMouse:
  985.  *      this queries the menu item which corresponds
  986.  *      to the given mouse coordinates.
  987.  *      Returns the ID of the menu item and stores its
  988.  *      rectangle in *prtlItem; returns (-1) upon errors.
  989.  */
  990.  
  991. SHORT winhQueryItemUnderMouse(HWND hwndMenu,      // in: menu handle
  992.                               POINTL *pptlMouse,  // in: mouse coordinates
  993.                               RECTL *prtlItem)    // out: rectangle of menu item
  994. {
  995.     SHORT   s, sItemId, sItemCount;
  996.     HAB     habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
  997.  
  998.     sItemCount = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_QUERYITEMCOUNT, MPNULL, MPNULL));
  999.  
  1000.     for (s = 0;
  1001.          s <= sItemCount;
  1002.          s++)
  1003.     {
  1004.         sItemId = SHORT1FROMMR(WinSendMsg(hwndMenu,
  1005.                                           MM_ITEMIDFROMPOSITION,
  1006.                                           (MPARAM)(ULONG)s, MPNULL));
  1007.         WinSendMsg(hwndMenu,
  1008.                    MM_QUERYITEMRECT,
  1009.                    MPFROM2SHORT(sItemId, FALSE),
  1010.                    (MPARAM)prtlItem);
  1011.         if (WinPtInRect(habDesktop, prtlItem, pptlMouse))
  1012.             return (sItemId);
  1013.     }
  1014.     /* sItemId = (SHORT)WinSendMsg(hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)(sItemCount-1), MPNULL);
  1015.     return (sItemId); */
  1016.     return (-1); // error: no valid menu item
  1017. }
  1018.  
  1019. /*
  1020.  *@@category: Helpers\PM helpers\Slider helpers
  1021.  */
  1022.  
  1023. /* ******************************************************************
  1024.  *
  1025.  *   Slider helpers
  1026.  *
  1027.  ********************************************************************/
  1028.  
  1029. /*
  1030.  *@@ winhReplaceWithLinearSlider:
  1031.  *      this destroys the control with the ID ulID in hwndDlg
  1032.  *      and creates a linear slider at the same position with the
  1033.  *      same ID (effectively replacing it).
  1034.  *
  1035.  *      This is needed because the IBM dialog editor (DLGEDIT.EXE)
  1036.  *      keeps crashing when creating sliders. So the way to do
  1037.  *      this easily is to create some other control with DLGEDIT
  1038.  *      where the slider should be later and call this function
  1039.  *      on that control when the dialog is initialized.
  1040.  *
  1041.  *      You need to specify _one_ of the following with ulSliderStyle:
  1042.  *      -- SLS_HORIZONTAL: horizontal slider (default)
  1043.  *      -- SLS_VERTICAL: vertical slider
  1044.  *
  1045.  *      plus _one_ additional common slider style for positioning:
  1046.  *      -- for horizontal sliders: SLS_BOTTOM, SLS_CENTER, or SLS_TOP
  1047.  *      -- for vertical sliders: SLS_LEFT, SLS_CENTER, or SLS_RIGHT
  1048.  *
  1049.  *      Additional common slider styles are:
  1050.  *      -- SLS_PRIMARYSCALE1: determines the location of the scale
  1051.  *                  on the slider shaft by using increment
  1052.  *                  and spacing specified for scale 1 as
  1053.  *                  the incremental value for positioning
  1054.  *                  the slider arm. Scale 1 is displayed
  1055.  *                  above the slider shaft of a horizontal
  1056.  *                  slider and to the right of the slider
  1057.  *                  shaft of a vertical slider. This is
  1058.  *                  the default for a slider.
  1059.  *      -- SLS_PRIMARYSCALE2: not supported by this function
  1060.  *      -- SLS_READONLY: creates a read-only slider, which
  1061.  *                  presents information to the user but
  1062.  *                  allows no interaction with the user.
  1063.  *      -- SLS_RIBBONSTRIP: fills, as the slider arm moves, the
  1064.  *                  slider shaft between the home position
  1065.  *                  and the slider arm with a color value
  1066.  *                  different from slider shaft color,
  1067.  *                  similar to mercury in a thermometer.
  1068.  *      -- SLS_OWNERDRAW: notifies the application whenever the
  1069.  *                  slider shaft, the ribbon strip, the
  1070.  *                  slider arm, and the slider background
  1071.  *                  are to be drawn.
  1072.  *      -- SLS_SNAPTOINCREMENT: causes the slider arm, when positioned
  1073.  *                  between two values, to be positioned
  1074.  *                  to the nearest value and redrawn at
  1075.  *                  that position.
  1076.  *
  1077.  *      Additionally, for horizontal sliders:
  1078.  *      -- SLS_BUTTONSLEFT: specifies that the optional slider
  1079.  *                  buttons are to be used and places them
  1080.  *                  to the left of the slider shaft. The
  1081.  *                  buttons move the slider arm by one
  1082.  *                  position, left or right, in the
  1083.  *                  direction selected.
  1084.  *      -- SLS_BUTTONSRIGHT: specifies that the optional slider
  1085.  *                  buttons are to be used and places them
  1086.  *                  to the right of the slider shaft. The
  1087.  *                  buttons move the slider arm by one
  1088.  *                  position, left or right, in the
  1089.  *                  direction selected.
  1090.  *      -- SLS_HOMELEFT: specifies the slider arm's home
  1091.  *                  position. The left edge is used as the
  1092.  *                  base value for incrementing (default).
  1093.  *      -- SLS_HOMERIGHT: specifies the slider arm's home
  1094.  *                  position. The right edge is used as
  1095.  *                  the base value for incrementing.
  1096.  *
  1097.  *      Instead, for vertical sliders:
  1098.  *      -- SLS_BUTTONSBOTTOM: specifies that the optional slider
  1099.  *                  buttons are to be used and places them
  1100.  *                  at the bottom of the slider shaft. The
  1101.  *                  buttons move the slider arm by one
  1102.  *                  position, up or down, in the direction
  1103.  *                  selected.
  1104.  *      -- SLS_BUTTONSTOP: specifies that the optional slider
  1105.  *                  buttons are to be used and places them
  1106.  *                  at the top of the slider shaft. The
  1107.  *                  buttons move the slider arm by one
  1108.  *                  position, up or down, in the direction
  1109.  *                  selected.
  1110.  *      -- SLS_HOMEBOTTOM: specifies the slider arm's home
  1111.  *                  position. The bottom of the slider is
  1112.  *                  used as the base value for
  1113.  *                  incrementing.
  1114.  *      -- SLS_HOMETOP: specifies the slider arm's home
  1115.  *                  position. The top of the slider is
  1116.  *                  used as the base value for
  1117.  *                  incrementing.
  1118.  *
  1119.  *      Notes: This function automatically adds WS_PARENTCLIP,
  1120.  *      WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
  1121.  *      For the WS_TABSTOP style, hwndInsertAfter is important.
  1122.  *      If you specify HWND_TOP, your window will be the first
  1123.  *      in the tab stop list.
  1124.  *
  1125.  *      It also shows the slider after having done all the
  1126.  *      processing in here by calling WinShowWindow.
  1127.  *
  1128.  *      Also, we only provide support for scale 1 here, so
  1129.  *      do not specify SLS_PRIMARYSCALE2 with ulSliderStyle,
  1130.  *      and we have the slider calculate all the spacings.
  1131.  *
  1132.  *      This returns the HWND of the slider or NULLHANDLE upon
  1133.  *      errors.
  1134.  *
  1135.  *@@added V0.9.0 [umoeller]
  1136.  */
  1137.  
  1138. HWND winhReplaceWithLinearSlider(HWND hwndParent,   // in: parent of old control and slider
  1139.                                  HWND hwndOwner,          // in: owner of old control and slider
  1140.                                  HWND hwndInsertAfter,    // in: the control after which the slider should
  1141.                                                           // come up, or HWND_TOP, or HWND_BOTTOM
  1142.                                  ULONG ulID,              // in: ID of old control and slider
  1143.                                  ULONG ulSliderStyle,     // in: SLS_* styles
  1144.                                  ULONG ulTickCount)       // in: number of ticks (scale 1)
  1145. {
  1146.     HWND    hwndSlider = NULLHANDLE;
  1147.     HWND    hwndKill = WinWindowFromID(hwndParent, ulID);
  1148.     if (hwndKill)
  1149.     {
  1150.         SWP swpControl;
  1151.         if (WinQueryWindowPos(hwndKill, &swpControl))
  1152.         {
  1153.             SLDCDATA slcd;
  1154.  
  1155.             // destroy the old control
  1156.             WinDestroyWindow(hwndKill);
  1157.  
  1158.             // initialize slider control data
  1159.             slcd.cbSize = sizeof(SLDCDATA);
  1160.             slcd.usScale1Increments = ulTickCount;
  1161.             slcd.usScale1Spacing = 0;           // have slider calculate it
  1162.             slcd.usScale2Increments = 0;
  1163.             slcd.usScale2Spacing = 0;
  1164.  
  1165.             // create a slider with the same ID at the same
  1166.             // position
  1167.             hwndSlider = WinCreateWindow(hwndParent,
  1168.                                          WC_SLIDER,
  1169.                                          NULL,           // no window text
  1170.                                          ulSliderStyle
  1171.                                             | WS_PARENTCLIP
  1172.                                             | WS_SYNCPAINT
  1173.                                             | WS_TABSTOP,
  1174.                                          swpControl.x,
  1175.                                          swpControl.y,
  1176.                                          swpControl.cx,
  1177.                                          swpControl.cy,
  1178.                                          hwndOwner,
  1179.                                          hwndInsertAfter,
  1180.                                          ulID,           // same ID as destroyed control
  1181.                                          &slcd,          // slider control data
  1182.                                          NULL);          // presparams
  1183.  
  1184.             WinSendMsg(hwndSlider,
  1185.                        SLM_SETTICKSIZE,
  1186.                        MPFROM2SHORT(SMA_SETALLTICKS,
  1187.                                     6),     // 15 pixels high
  1188.                        NULL);
  1189.  
  1190.             WinShowWindow(hwndSlider, TRUE);
  1191.         }
  1192.     }
  1193.  
  1194.     return (hwndSlider);
  1195. }
  1196.  
  1197. /*
  1198.  *@@ winhSetSliderTicks:
  1199.  *      this adds ticks to the given linear slider,
  1200.  *      which are ulPixels pixels high. A useful
  1201.  *      value for this is 4.
  1202.  *
  1203.  *      This queries the slider for the primary
  1204.  *      scale values. Only the primary scale is
  1205.  *      supported.
  1206.  *
  1207.  *      This function goes sets the ticks twice,
  1208.  *      once with mpEveryOther1 and ulPixels1,
  1209.  *      and then a second time with mpEveryOther2
  1210.  *      and ulPixels2. This allows you to quickly
  1211.  *      give, say, every tenth item a taller tick.
  1212.  *
  1213.  *      For every set, if mpEveryOther is 0, this sets
  1214.  *      all ticks on the primary slider scale.
  1215.  *
  1216.  *      If mpEveryOther is != 0, SHORT1FROMMP
  1217.  *      specifies the first tick to set, and
  1218.  *      SHORT2FROMMP specifies every other tick
  1219.  *      to set from there. For example:
  1220.  *
  1221.  +          MPFROM2SHORT(9, 10)
  1222.  *
  1223.  *      would set tick 9, 19, 29, and so forth.
  1224.  *
  1225.  *      If both mpEveryOther and ulPixels are -1,
  1226.  *      that set is skipped.
  1227.  *
  1228.  *      Example: Considering a slider with a
  1229.  *      primary scale from 0 to 30, using
  1230.  *
  1231.  +          winhSetSliderTicks(hwndSlider,
  1232.  +                             0,                       // every tick
  1233.  +                             3,                       //      to three pixels
  1234.  +                             MPFROM2SHORT(9, 10)      // then every tenth
  1235.  +                             6);                      //      to six pixels.
  1236.  *
  1237.  *      Returns FALSE upon errors.
  1238.  *
  1239.  *@@added V0.9.1 (99-12-04) [umoeller]
  1240.  *@@changed V0.9.7 (2001-01-18) [umoeller]: added second set
  1241.  */
  1242.  
  1243. BOOL winhSetSliderTicks(HWND hwndSlider,            // in: linear slider
  1244.                         MPARAM mpEveryOther1,       // in: set 1
  1245.                         ULONG ulPixels1,
  1246.                         MPARAM mpEveryOther2,       // in: set 2
  1247.                         ULONG ulPixels2)
  1248. {
  1249.     BOOL brc = FALSE;
  1250.  
  1251.     ULONG ulSet;
  1252.     MPARAM mpEveryOther = mpEveryOther1;
  1253.     ULONG ulPixels = ulPixels1;
  1254.  
  1255.     // do this twice
  1256.     for (ulSet = 0;
  1257.          ulSet < 2;
  1258.          ulSet++)
  1259.     {
  1260.         if (mpEveryOther == 0)
  1261.         {
  1262.             // set all ticks:
  1263.             brc = (BOOL)WinSendMsg(hwndSlider,
  1264.                                    SLM_SETTICKSIZE,
  1265.                                    MPFROM2SHORT(SMA_SETALLTICKS,
  1266.                                                 ulPixels),
  1267.                                    NULL);
  1268.         }
  1269.         else if ( (mpEveryOther != (MPARAM)-1) && (ulPixels != -1) )
  1270.         {
  1271.             SLDCDATA  slcd;
  1272.             WNDPARAMS   wp;
  1273.             memset(&wp, 0, sizeof(WNDPARAMS));
  1274.             wp.fsStatus = WPM_CTLDATA;
  1275.             wp.cbCtlData = sizeof(slcd);
  1276.             wp.pCtlData = &slcd;
  1277.             // get primary scale data from the slider
  1278.             if (WinSendMsg(hwndSlider,
  1279.                            WM_QUERYWINDOWPARAMS,
  1280.                            (MPARAM)&wp,
  1281.                            0))
  1282.             {
  1283.                 USHORT usStart = SHORT1FROMMP(mpEveryOther),
  1284.                        usEveryOther = SHORT2FROMMP(mpEveryOther);
  1285.  
  1286.                 USHORT usScale1Max = slcd.usScale1Increments,
  1287.                        us;
  1288.  
  1289.                 brc = TRUE;
  1290.  
  1291.                 for (us = usStart; us < usScale1Max; us += usEveryOther)
  1292.                 {
  1293.                     if (!(BOOL)WinSendMsg(hwndSlider,
  1294.                                           SLM_SETTICKSIZE,
  1295.                                           MPFROM2SHORT(us,
  1296.                                                        ulPixels),
  1297.                                           NULL))
  1298.                     {
  1299.                         brc = FALSE;
  1300.                         break;
  1301.                     }
  1302.                 }
  1303.             }
  1304.         }
  1305.  
  1306.         // for the second loop, use second value set
  1307.         mpEveryOther = mpEveryOther2;
  1308.         ulPixels = ulPixels2;
  1309.                 // we only loop twice
  1310.     } // end for (ulSet = 0; ulSet < 2;
  1311.  
  1312.     return brc;
  1313. }
  1314.  
  1315. /*
  1316.  *@@ winhReplaceWithCircularSlider:
  1317.  *      this destroys the control with the ID ulID in hwndDlg
  1318.  *      and creates a linear slider at the same position with the
  1319.  *      same ID (effectively replacing it).
  1320.  *
  1321.  *      This is needed because the IBM dialog editor (DLGEDIT.EXE)
  1322.  *      cannot create circular sliders. So the way to do this
  1323.  *      easily is to create some other control with DLGEDIT
  1324.  *      where the slider should be later and call this function
  1325.  *      on that control when the dialog is initialized.
  1326.  *
  1327.  *      You need to specify the following with ulSliderStyle:
  1328.  *      --  CSS_CIRCULARVALUE: draws a circular thumb, rather than a line,
  1329.  *                  for the value indicator.
  1330.  *      --  CSS_MIDPOINT: makes the mid-point tick mark larger.
  1331.  *      --  CSS_NOBUTTON: does not display value buttons. Per default, the
  1332.  *                  slider displays "-" and "+" buttons to the bottom left
  1333.  *                  and bottom right of the knob. (BTW, these bitmaps can be
  1334.  *                  changed using CSM_SETBITMAPDATA.)
  1335.  *      --  CSS_NONUMBER: does not display the value on the dial.
  1336.  *      --  CSS_NOTEXT: does not display title text under the dial.
  1337.  *                  Otherwise, the text in the pszTitle parameter
  1338.  *                  will be used.
  1339.  *      --  CSS_NOTICKS (only listed in pmstddlg.h, not in PMREF):
  1340.  *                  obviously, this prevents tick marks from being drawn.
  1341.  *      --  CSS_POINTSELECT: permits the values on the circular slider
  1342.  *                  to change immediately when dragged.
  1343.  *                  Direct manipulation is performed by using a mouse to
  1344.  *                  click on and drag the circular slider. There are two
  1345.  *                  modes of direct manipulation for the circular slider:
  1346.  *                  <BR><B>1)</B> The default direct manipulation mode is to scroll to
  1347.  *                  the value indicated by the position of the mouse.
  1348.  *                  This could be important if you used a circular slider
  1349.  *                  for a volume control, for example. Increasing the volume
  1350.  *                  from 0% to 100% too quickly could result in damage to
  1351.  *                  both the user's ears and the equipment.
  1352.  *                  <BR><B>2)</B>The other mode of direct manipulation permits
  1353.  *                  the value on the circular slider to change immediately when dragged.
  1354.  *                  This mode is enabled using the CSS_POINTSELECT style bit. When this
  1355.  *                  style is used, the value of the dial can be changed by tracking
  1356.  *                  the value with the mouse, which changes values quickly.
  1357.  *      --  CSS_PROPORTIONALTICKS: allow the length of the tick marks to be calculated
  1358.  *                  as a percentage of the radius (for small sliders).
  1359.  *      --  CSS_360: permits the scroll range to extend 360 degrees.
  1360.  *                  CSS_360 forces the CSS_NONUMBER style on. This is necessary
  1361.  *                  to keep the value indicator from corrupting the number value.
  1362.  *
  1363.  *      FYI: The most commonly known circular slider in OS/2, the one in the
  1364.  *      default "Sound" object, has a style of 0x9002018a, meaning
  1365.  *      CSS_NOTEXT | CSS_POINTSELECT | CSS_NOTICKS.
  1366.  *
  1367.  *      Notes: This function automatically adds WS_PARENTCLIP,
  1368.  *      WS_TABSTOP, and WS_SYNCPAINT to the specified styles.
  1369.  *      For the WS_TABSTOP style, hwndInsertAfter is important.
  1370.  *      If you specify HWND_TOP, your window will be the first
  1371.  *      in the tab stop list.
  1372.  *
  1373.  *      It also shows the slider after having done all the
  1374.  *      processing in here by calling WinShowWindow.
  1375.  *
  1376.  *      This returns the HWND of the slider or NULLHANDLE upon
  1377.  *      errors.
  1378.  *
  1379.  *@@added V0.9.0 [umoeller]
  1380.  */
  1381.  
  1382. HWND winhReplaceWithCircularSlider(HWND hwndParent,   // in: parent of old control and slider
  1383.                                    HWND hwndOwner,          // in: owner of old control and slider
  1384.                                    HWND hwndInsertAfter,    // in: the control after which the slider should
  1385.                                                             // come up, or HWND_TOP, or HWND_BOTTOM
  1386.                                    ULONG ulID,              // in: ID of old control and slider
  1387.                                    ULONG ulSliderStyle,     // in: SLS_* styles
  1388.                                    SHORT sMin,              // in: minimum value (e.g. 0)
  1389.                                    SHORT sMax,              // in: maximum value (e.g. 100)
  1390.                                    USHORT usIncrement,      // in: minimum increment (e.g. 1)
  1391.                                    USHORT usTicksEvery)     // in: ticks ever x values (e.g. 20)
  1392. {
  1393.     HWND    hwndSlider = NULLHANDLE;
  1394.     HWND    hwndKill = WinWindowFromID(hwndParent, ulID);
  1395.     if (hwndKill)
  1396.     {
  1397.         SWP swpControl;
  1398.         if (WinQueryWindowPos(hwndKill, &swpControl))
  1399.         {
  1400.             // destroy the old control
  1401.             WinDestroyWindow(hwndKill);
  1402.  
  1403.             // WinRegisterCircularSlider();
  1404.  
  1405.             // create a slider with the same ID at the same
  1406.             // position
  1407.             hwndSlider = WinCreateWindow(hwndParent,
  1408.                                          WC_CIRCULARSLIDER,
  1409.                                          "dummy",        // no window text
  1410.                                          ulSliderStyle
  1411.                                             // | WS_PARENTCLIP
  1412.                                             // | WS_SYNCPAINT
  1413.                                             | WS_TABSTOP,
  1414.                                          swpControl.x,
  1415.                                          swpControl.y,
  1416.                                          swpControl.cx,
  1417.                                          swpControl.cy,
  1418.                                          hwndOwner,
  1419.                                          hwndInsertAfter,
  1420.                                          ulID,           // same ID as destroyed control
  1421.                                          NULL,           // control data
  1422.                                          NULL);          // presparams
  1423.  
  1424.             if (hwndSlider)
  1425.             {
  1426.                 // set slider range
  1427.                 WinSendMsg(hwndSlider,
  1428.                            CSM_SETRANGE,
  1429.                            (MPARAM)(ULONG)sMin,
  1430.                            (MPARAM)(ULONG)sMax);
  1431.  
  1432.                 // set slider increments
  1433.                 WinSendMsg(hwndSlider,
  1434.                            CSM_SETINCREMENT,
  1435.                            (MPARAM)(ULONG)usIncrement,
  1436.                            (MPARAM)(ULONG)usTicksEvery);
  1437.  
  1438.                 // set slider value
  1439.                 WinSendMsg(hwndSlider,
  1440.                            CSM_SETVALUE,
  1441.                            (MPARAM)0,
  1442.                            (MPARAM)0);
  1443.  
  1444.                 // for some reason, the slider always has
  1445.                 // WS_CLIPSIBLINGS set, even though we don't
  1446.                 // set this; we must unset this now, or
  1447.                 // the slider won't draw itself (%&$&%"$&%!!!)
  1448.                 WinSetWindowBits(hwndSlider,
  1449.                                  QWL_STYLE,
  1450.                                  0,         // unset bit
  1451.                                  WS_CLIPSIBLINGS);
  1452.  
  1453.                 WinShowWindow(hwndSlider, TRUE);
  1454.             }
  1455.         }
  1456.     }
  1457.  
  1458.     return (hwndSlider);
  1459. }
  1460.  
  1461. /*
  1462.  *@@category: Helpers\PM helpers\Spin button helpers
  1463.  */
  1464.  
  1465. /* ******************************************************************
  1466.  *
  1467.  *   Spin button helpers
  1468.  *
  1469.  ********************************************************************/
  1470.  
  1471. /*
  1472.  *@@ winhSetDlgItemSpinData:
  1473.  *      sets a spin button's limits and data within a dialog window.
  1474.  *      This only works for decimal spin buttons.
  1475.  */
  1476.  
  1477. VOID winhSetDlgItemSpinData(HWND hwndDlg,       // in: dlg window
  1478.                             ULONG idSpinButton,  // in: item ID of spin button
  1479.                             ULONG min,           // in: minimum allowed value
  1480.                             ULONG max,           // in: maximum allowed value
  1481.                             ULONG current)       // in: new current value
  1482. {
  1483.     HWND hwndSpinButton = WinWindowFromID(hwndDlg, idSpinButton);
  1484.     if (hwndSpinButton)
  1485.     {
  1486.         WinSendMsg(hwndSpinButton,
  1487.                    SPBM_SETLIMITS,          // Set limits message
  1488.                    (MPARAM)max,             // Spin Button maximum setting
  1489.                    (MPARAM)min);             // Spin Button minimum setting
  1490.  
  1491.         WinSendMsg(hwndSpinButton,
  1492.                    SPBM_SETCURRENTVALUE,    // Set current value message
  1493.                    (MPARAM)current,
  1494.                    (MPARAM)NULL);
  1495.     }
  1496. }
  1497.  
  1498. /*
  1499.  *@@ winhAdjustDlgItemSpinData:
  1500.  *      this can be called on a spin button control to
  1501.  *      have its current data snap to a grid. This only
  1502.  *      works for LONG integer values.
  1503.  *
  1504.  *      For example, if you specify 100 for the grid and call
  1505.  *      this func after you have received SPBN_UP/DOWNARROW,
  1506.  *      the spin button's value will always in/decrease
  1507.  *      so that the spin button's value is a multiple of 100.
  1508.  *
  1509.  *      By contrast, if (lGrid < 0), this will not really
  1510.  *      snap the value to a multiple of -lGrid, but instead
  1511.  *      in/decrease the value by -lGrid. The value will not
  1512.  *      necessarily be a multiple of the grid. (0.9.14)
  1513.  *
  1514.  *      This returns the "snapped" value to which the spin
  1515.  *      button was set.
  1516.  *
  1517.  *      If you specify lGrid == 0, this returns the spin
  1518.  *      button's value only without snapping (V0.9.0).
  1519.  *
  1520.  *@@changed V0.9.0 [umoeller]: added check for lGrid == 0 (caused division by zero previously)
  1521.  *@@changed V0.9.14 (2001-08-03) [umoeller]: added fixes for age-old problems with wrap around
  1522.  *@@changed V0.9.14 (2001-08-03) [umoeller]: added lGrid < 0 mode
  1523.  */
  1524.  
  1525. LONG winhAdjustDlgItemSpinData(HWND hwndDlg,     // in: dlg window
  1526.                                USHORT usItemID,  // in: item ID of spin button
  1527.                                LONG lGrid,       // in: grid
  1528.                                USHORT usNotifyCode) // in: SPBN_UP* or *DOWNARROW of WM_CONTROL message
  1529. {
  1530.     HWND hwndSpin = WinWindowFromID(hwndDlg, usItemID);
  1531.     LONG lBottom, lTop, lValue;
  1532.  
  1533.     // get value, which has already increased /
  1534.     // decreased by 1
  1535.     WinSendMsg(hwndSpin,
  1536.                SPBM_QUERYVALUE,
  1537.                (MPARAM)&lValue,
  1538.                MPFROM2SHORT(0, SPBQ_ALWAYSUPDATE));
  1539.  
  1540.     if ((lGrid)
  1541.         && (    (usNotifyCode == SPBN_UPARROW)
  1542.              || (usNotifyCode == SPBN_DOWNARROW)
  1543.            )
  1544.        )
  1545.     {
  1546.         // only if the up/down buttons were pressed,
  1547.         // snap to the nearest grid; if the user
  1548.         // manually enters something (SPBN_CHANGE),
  1549.         // we'll accept that value
  1550.         LONG lChanged = (usNotifyCode == SPBN_UPARROW)
  1551.                             // if the spin button went up, subtract 1
  1552.                             ? -1
  1553.                             : +1;
  1554.         LONG lPrev  = lValue + lChanged;
  1555.  
  1556.         // if grid is negative, it is assumed to
  1557.         // not be a "real" grid but jump in those
  1558.         // steps only
  1559.         if (lGrid < 0)
  1560.         {
  1561.             // add /subtract grid
  1562.             if (usNotifyCode == SPBN_UPARROW)
  1563.                 lValue = lPrev - lGrid;
  1564.             else
  1565.                 lValue = lPrev + lGrid;
  1566.  
  1567.             // lValue = (lValue / lGrid) * lGrid;
  1568.         }
  1569.         else
  1570.         {
  1571.             // add /subtract grid
  1572.             if (usNotifyCode == SPBN_UPARROW)
  1573.                 lValue = lPrev + lGrid;
  1574.             else
  1575.                 lValue = lPrev - lGrid;
  1576.  
  1577.             lValue = (lValue / lGrid) * lGrid;
  1578.         }
  1579.  
  1580.         // balance with spin button limits
  1581.         WinSendMsg(hwndSpin,
  1582.                    SPBM_QUERYLIMITS,
  1583.                    (MPARAM)&lTop,
  1584.                    (MPARAM)&lBottom);
  1585.         if (lValue < lBottom)
  1586.             lValue = lTop;
  1587.         else if (lValue > lTop)
  1588.             lValue = lBottom;
  1589.  
  1590.         WinSendMsg(hwndSpin,
  1591.                    SPBM_SETCURRENTVALUE,
  1592.                    (MPARAM)(lValue),
  1593.                    MPNULL);
  1594.     }
  1595.     return (lValue);
  1596. }
  1597.  
  1598. /*
  1599.  *@@category: Helpers\PM helpers\List box helpers
  1600.  */
  1601.  
  1602. /* ******************************************************************
  1603.  *
  1604.  *   List box helpers
  1605.  *
  1606.  ********************************************************************/
  1607.  
  1608. /*
  1609.  *@@ winhQueryLboxItemText:
  1610.  *      returns the text of the specified
  1611.  *      list box item in a newly allocated
  1612.  *      buffer.
  1613.  *
  1614.  *      Returns NULL on error. Use fre()
  1615.  *      to free the return value.
  1616.  *
  1617.  *@@added V0.9.1 (99-12-14) [umoeller]
  1618.  */
  1619.  
  1620. PSZ winhQueryLboxItemText(HWND hwndListbox,
  1621.                           SHORT sIndex)
  1622. {
  1623.     PSZ   pszReturn = 0;
  1624.     SHORT cbText = SHORT1FROMMR(WinSendMsg(hwndListbox,
  1625.                                            LM_QUERYITEMTEXTLENGTH,
  1626.                                            (MPARAM)(ULONG)sIndex,
  1627.                                            0));
  1628.     if ((cbText) && (cbText != LIT_ERROR))
  1629.     {
  1630.         pszReturn = (PSZ)malloc(cbText + 1);        // add zero terminator
  1631.         WinSendMsg(hwndListbox,
  1632.                    LM_QUERYITEMTEXT,
  1633.                    MPFROM2SHORT(sIndex,
  1634.                                 cbText + 1),
  1635.                    (MPARAM)pszReturn);
  1636.     }
  1637.  
  1638.     return (pszReturn);
  1639. }
  1640.  
  1641. /*
  1642.  *@@ winhMoveLboxItem:
  1643.  *      this moves one list box item from one
  1644.  *      list box to another, including the
  1645.  *      item text and the item "handle"
  1646.  *      (see LM_QUERYITEMHANDLE).
  1647.  *
  1648.  *      sTargetIndex can either be a regular
  1649.  *      item index or one of the following
  1650.  *      (as in LM_INSERTITEM):
  1651.  *      -- LIT_END
  1652.  *      -- LIT_SORTASCENDING
  1653.  *      -- LIT_SORTDESCENDING
  1654.  *
  1655.  *      If (fSelectTarget == TRUE), the new
  1656.  *      item is also selected in the target
  1657.  *      list box.
  1658.  *
  1659.  *      Returns FALSE if moving failed. In
  1660.  *      that case, the list boxes are unchanged.
  1661.  *
  1662.  *@@added V0.9.1 (99-12-14) [umoeller]
  1663.  */
  1664.  
  1665. BOOL winhMoveLboxItem(HWND hwndSource,
  1666.                       SHORT sSourceIndex,
  1667.                       HWND hwndTarget,
  1668.                       SHORT sTargetIndex,
  1669.                       BOOL fSelectTarget)
  1670. {
  1671.     BOOL brc = FALSE;
  1672.  
  1673.     PSZ pszItemText = winhQueryLboxItemText(hwndSource, sSourceIndex);
  1674.     if (pszItemText)
  1675.     {
  1676.         ULONG   ulItemHandle = winhQueryLboxItemHandle(hwndSource,
  1677.                                                        sSourceIndex);
  1678.                     // probably 0, if not used
  1679.         LONG lTargetIndex = WinInsertLboxItem(hwndTarget,
  1680.                                               sTargetIndex,
  1681.                                               pszItemText);
  1682.         if (    (lTargetIndex != LIT_ERROR)
  1683.              && (lTargetIndex != LIT_MEMERROR)
  1684.            )
  1685.         {
  1686.             // successfully inserted:
  1687.             winhSetLboxItemHandle(hwndTarget, lTargetIndex, ulItemHandle);
  1688.             if (fSelectTarget)
  1689.                 winhSetLboxSelectedItem(hwndTarget, lTargetIndex, TRUE);
  1690.  
  1691.             // remove source
  1692.             WinDeleteLboxItem(hwndSource,
  1693.                               sSourceIndex);
  1694.  
  1695.             brc = TRUE;
  1696.         }
  1697.  
  1698.         free(pszItemText);
  1699.     }
  1700.  
  1701.     return brc;
  1702. }
  1703.  
  1704. /*
  1705.  *@@ winhLboxSelectAll:
  1706.  *      this selects or deselects all items in the
  1707.  *      given list box, depending on fSelect.
  1708.  *
  1709.  *      Returns the number of items in the list box.
  1710.  */
  1711.  
  1712. ULONG winhLboxSelectAll(HWND hwndListBox,   // in: list box
  1713.                         BOOL fSelect)       // in: TRUE = select, FALSE = deselect
  1714. {
  1715.     LONG lItemCount = WinQueryLboxCount(hwndListBox);
  1716.     ULONG ul;
  1717.  
  1718.     for (ul = 0; ul < lItemCount; ul++)
  1719.     {
  1720.         WinSendMsg(hwndListBox,
  1721.                    LM_SELECTITEM,
  1722.                    (MPARAM)ul,      // index
  1723.                    (MPARAM)fSelect);
  1724.     }
  1725.  
  1726.     return (lItemCount);
  1727. }
  1728.  
  1729. /*
  1730.  *@@ winhLboxFindItemFromHandle:
  1731.  *      finds the list box item with the specified
  1732.  *      handle.
  1733.  *
  1734.  *      Of course this only makes sense if each item
  1735.  *      has a unique handle indeed.
  1736.  *
  1737.  *      Returns the index of the item found or -1.
  1738.  *
  1739.  *@@added V0.9.12 (2001-05-18) [umoeller]
  1740.  */
  1741.  
  1742. ULONG winhLboxFindItemFromHandle(HWND hwndListBox,
  1743.                                  ULONG ulHandle)
  1744. {
  1745.     LONG cItems;
  1746.     if (cItems = WinQueryLboxCount(hwndListBox))
  1747.     {
  1748.         ULONG ul;
  1749.         for (ul = 0;
  1750.              ul < cItems;
  1751.              ul++)
  1752.         {
  1753.             if (ulHandle == winhQueryLboxItemHandle(hwndListBox,
  1754.                                                     ul))
  1755.                 return (ul);
  1756.         }
  1757.     }
  1758.  
  1759.     return (-1);
  1760. }
  1761.  
  1762. /*
  1763.  *@@category: Helpers\PM helpers\Scroll bar helpers
  1764.  */
  1765.  
  1766. /* ******************************************************************
  1767.  *
  1768.  *   Scroll bar helpers
  1769.  *
  1770.  ********************************************************************/
  1771.  
  1772. /*
  1773.  *@@ winhUpdateScrollBar:
  1774.  *      updates the given scroll bar according to the given
  1775.  *      values. This updates the scroll bar's thumb size,
  1776.  *      extension, and position, all in one shot.
  1777.  *
  1778.  *      This function usually gets called when the window is
  1779.  *      created and later when the window is resized.
  1780.  *
  1781.  *      This simplifies the typical functionality of a scroll
  1782.  *      bar in a client window which is to be scrolled. I am
  1783.  *      wondering why IBM never included such a function, since
  1784.  *      it is so damn basic and still writing it cost me a whole
  1785.  *      day.
  1786.  *
  1787.  *      Terminology:
  1788.  *
  1789.  *      -- "window": the actual window with scroll bars which displays
  1790.  *         a subrectangle of the available data. With a typical PM
  1791.  *         application, this will be your client window.
  1792.  *
  1793.  *         The width or height of this must be passed in ulWinPels.
  1794.  *
  1795.  *      -- "viewport": the entire data to be displayed, of which the
  1796.  *         "window" can only display a subrectangle, if the viewport
  1797.  *         is larger than the window.
  1798.  *
  1799.  *         The width or height of this must be passed in ulViewportPels.
  1800.  *         This can be smaller than ulWinPels (if the window is larger
  1801.  *         than the data) or the same or larger than ulWinPels
  1802.  *         (if the window is too small to show all the data).
  1803.  *
  1804.  *      -- "window offset": the offset of the current window within
  1805.  *         the viewport.
  1806.  *
  1807.  *         For horizontal scroll bars, this is the X coordinate,
  1808.  *         counting from the left of the window (0 means leftmost).
  1809.  *
  1810.  *         For vertical scroll bars, this is counted from the _top_
  1811.  *         of the viewport (0 means topmost, as opposed to OS/2
  1812.  *         window coordinates!). This is because for vertical scroll
  1813.  *         bars controls, higher values move the thumb _down_. Yes
  1814.  *         indeed, this conflicts with PM's coordinate system.
  1815.  *
  1816.  *         The window offset is therefore always positive.
  1817.  *
  1818.  *      The scroll bar gets disabled if the entire viewport is visible,
  1819.  *      that is, if ulViewportPels <= ulWinPels. In that case
  1820.  *      FALSE is returned. If (fAutoHide == TRUE), the scroll
  1821.  *      bar is not only disabled, but also hidden from the display.
  1822.  *      In that case, you will need to reformat your output because
  1823.  *      your viewport becomes larger without the scroll bar.
  1824.  *
  1825.  *      This function will set the range of the scroll bar to 0 up
  1826.  *      to a value depending on the viewport size. For vertical scroll
  1827.  *      bars, 0 means topmost (which is kinda sick with the OS/2
  1828.  *      coordinate system), for horizontal scroll bars, 0 means leftmost.
  1829.  *
  1830.  *      The maximum value of the scroll bar will be
  1831.  *
  1832.  +          (ulViewportPels - ulWinPels) / usScrollUnitPels
  1833.  *
  1834.  *      The thumb size of the scroll bar will also be adjusted
  1835.  *      based on the viewport and window size, as it should be.
  1836.  *
  1837.  *@@added V0.9.1 (2000-02-14) [umoeller]
  1838.  *@@changed V0.9.3 (2000-04-30) [umoeller]: fixed pels/unit confusion
  1839.  *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
  1840.  */
  1841.  
  1842. BOOL winhUpdateScrollBar(HWND hwndScrollBar,    // in: scroll bar (vertical or horizontal)
  1843.                          ULONG ulWinPels,       // in: vertical or horizontal dimension of
  1844.                                                 // visible window part (in pixels),
  1845.                                                 // excluding the scroll bar!
  1846.                          ULONG ulViewportPels,  // in: dimension of total data part, of
  1847.                                                 // which ulWinPels is a sub-dimension
  1848.                                                 // (in pixels);
  1849.                                                 // if <= ulWinPels, the scrollbar will be
  1850.                                                 // disabled
  1851.                          ULONG ulCurPelsOfs,    // in: current offset of visible part
  1852.                                                 // (in pixels)
  1853.                          BOOL fAutoHide)        // in: hide scroll bar if disabled
  1854. {
  1855.     BOOL brc = FALSE;
  1856.  
  1857.     // _Pmpf(("Entering winhUpdateScrollBar"));
  1858.  
  1859.     // for large viewports, adjust scroll bar units
  1860.     USHORT  usScrollUnitPels = 1;
  1861.     if (ulViewportPels > 10000)
  1862.         usScrollUnitPels = 100;
  1863.  
  1864.     if (ulViewportPels > ulWinPels)
  1865.     {
  1866.         // scrollbar needed:
  1867.         USHORT  usThumbDivisorUnits = usScrollUnitPels;
  1868.         USHORT  lMaxAllowedUnitOfs;
  1869.         // _Pmpf(("winhUpdateScrollBar: ulViewportPels > ulWinPels, enabling scroller"));
  1870.         // divisor for thumb size (below)
  1871.         if (ulViewportPels > 10000)
  1872.             // for very large viewports, we need to
  1873.             // raise the divisor, because we only
  1874.             // have a USHORT
  1875.             usThumbDivisorUnits = usScrollUnitPels * 100;
  1876.  
  1877.         // viewport is larger than window:
  1878.         WinEnableWindow(hwndScrollBar, TRUE);
  1879.         if (fAutoHide)
  1880.             WinShowWindow(hwndScrollBar, TRUE);
  1881.  
  1882.         // calculate limit
  1883.         lMaxAllowedUnitOfs = ((ulViewportPels - ulWinPels + usScrollUnitPels)
  1884.                                // scroll unit is 10
  1885.                                / usScrollUnitPels);
  1886.  
  1887.         // _Pmpf(("    usCurUnitOfs: %d", ulCurUnitOfs));
  1888.         // _Pmpf(("    usMaxUnits: %d", lMaxAllowedUnitOfs));
  1889.  
  1890.         // set thumb position and limit
  1891.         WinSendMsg(hwndScrollBar,
  1892.                    SBM_SETSCROLLBAR,
  1893.                    (MPARAM)(ulCurPelsOfs), //  / usThumbDivisorUnits),   // position: 0 means top
  1894.                    MPFROM2SHORT(0,  // minimum
  1895.                                 lMaxAllowedUnitOfs));    // maximum
  1896.  
  1897.         // set thumb size based on ulWinPels and
  1898.         // ulViewportPels
  1899.         WinSendMsg(hwndScrollBar,
  1900.                    SBM_SETTHUMBSIZE,
  1901.                    MPFROM2SHORT(    ulWinPels / usThumbDivisorUnits,       // visible
  1902.                                     ulViewportPels / usThumbDivisorUnits), // total
  1903.                    0);
  1904.         brc = TRUE;
  1905.     }
  1906.     else
  1907.     {
  1908.         // _Pmpf(("winhUpdateScrollBar: ulViewportPels <= ulWinPels"));
  1909.         // entire viewport is visible:
  1910.         WinEnableWindow(hwndScrollBar, FALSE);
  1911.         if (fAutoHide)
  1912.             WinShowWindow(hwndScrollBar, FALSE);
  1913.     }
  1914.  
  1915.     // _Pmpf(("End of winhUpdateScrollBar"));
  1916.  
  1917.     return brc;
  1918. }
  1919.  
  1920. /*
  1921.  *@@ winhHandleScrollMsg:
  1922.  *      this helper handles a WM_VSCROLL or WM_HSCROLL
  1923.  *      message posted to a client window when the user
  1924.  *      has worked on a client scroll bar. Calling this
  1925.  *      function is ALL you need to do to handle those
  1926.  *      two messages.
  1927.  *
  1928.  *      This is most useful in conjunction with winhUpdateScrollBar.
  1929.  *      See that function for the terminology also.
  1930.  *
  1931.  *      This function calculates the new scrollbar position
  1932.  *      (from the mp2 value, which can be line up/down,
  1933.  *      page up/down, or slider track) and calls WinScrollWindow
  1934.  *      accordingly. The window part which became invalid
  1935.  *      because of the scrolling is automatically invalidated
  1936.  *      (using WinInvalidateRect), so expect a WM_PAINT after
  1937.  *      calling this function.
  1938.  *
  1939.  *      This function assumes that the scrollbar operates
  1940.  *      on values starting from zero. The maximum value
  1941.  *      of the scroll bar is:
  1942.  *
  1943.  +          ulViewportPels - (prcl2Scroll->yTop - prcl2Scroll->yBottom)
  1944.  *
  1945.  *      This function also automatically changes the scroll bar
  1946.  *      units, should you have a viewport size which doesn't fit
  1947.  *      into the SHORT's that the scroll bar uses internally. As
  1948.  *      a result, this function handles a the complete range of
  1949.  *      a ULONG for the viewport.
  1950.  *
  1951.  *      Replace "bottom" and "top" with "right" and "left" for
  1952.  *      horizontal scrollbars in the above formula.
  1953.  *
  1954.  *@@added V0.9.1 (2000-02-13) [umoeller]
  1955.  *@@changed V0.9.3 (2000-04-30) [umoeller]: changed prototype, fixed pels/unit confusion
  1956.  *@@changed V0.9.3 (2000-05-08) [umoeller]: now handling scroll units automatically
  1957.  *@@changed V0.9.7 (2001-01-17) [umoeller]: changed PLONG to PULONG
  1958.  */
  1959.  
  1960. BOOL winhHandleScrollMsg(HWND hwnd2Scroll,          // in: client window to scroll
  1961.                          HWND hwndScrollBar,        // in: vertical or horizontal scroll bar window
  1962.                          PULONG pulCurPelsOfs,      // in/out: current viewport offset;
  1963.                                                     // this is updated with the proper scroll units
  1964.                          PRECTL prcl2Scroll,        // in: hwnd2Scroll rectangle to scroll
  1965.                                                     // (in window coordinates);
  1966.                                                     // this is passed to WinScrollWindow,
  1967.                                                     // which considers this inclusive!
  1968.                          LONG ulViewportPels,       // in: total viewport dimension,
  1969.                                                     // into which *pulCurPelsOfs is an offset
  1970.                          USHORT usLineStepPels,     // in: pixels to scroll line-wise
  1971.                                                     // (scroll bar buttons pressed)
  1972.                          ULONG msg,                 // in: either WM_VSCROLL or WM_HSCROLL
  1973.                          MPARAM mp2)                // in: complete mp2 of WM_VSCROLL/WM_HSCROLL;
  1974.                                                     // this has two SHORT's (usPos and usCmd),
  1975.                                                     // see PMREF for details
  1976. {
  1977.     ULONG   ulOldPelsOfs = *pulCurPelsOfs;
  1978.     USHORT  usPosUnits = SHORT1FROMMP(mp2), // in scroll units
  1979.             usCmd = SHORT2FROMMP(mp2);
  1980.     LONG    lMaxAllowedUnitOfs;
  1981.     ULONG   ulWinPels;
  1982.  
  1983.     // for large viewports, adjust scroll bar units
  1984.     USHORT  usScrollUnitPels = 1;
  1985.     if (ulViewportPels > 10000)
  1986.         usScrollUnitPels = 100;
  1987.  
  1988.     // calculate window size (vertical or horizontal)
  1989.     if (msg == WM_VSCROLL)
  1990.         ulWinPels = (prcl2Scroll->yTop - prcl2Scroll->yBottom);
  1991.     else
  1992.         ulWinPels = (prcl2Scroll->xRight - prcl2Scroll->xLeft);
  1993.  
  1994.     lMaxAllowedUnitOfs = ((LONG)ulViewportPels - ulWinPels) / usScrollUnitPels;
  1995.  
  1996.     // _Pmpf(("Entering winhHandleScrollMsg"));
  1997.  
  1998.     switch (usCmd)
  1999.     {
  2000.         case SB_LINEUP:
  2001.             if (*pulCurPelsOfs > usLineStepPels)
  2002.                 *pulCurPelsOfs -= usLineStepPels;  //  * usScrollUnitPels);
  2003.             else
  2004.                 *pulCurPelsOfs = 0;
  2005.         break;
  2006.  
  2007.         case SB_LINEDOWN:
  2008.             *pulCurPelsOfs += usLineStepPels;  //  * usScrollUnitPels);
  2009.         break;
  2010.  
  2011.         case SB_PAGEUP:
  2012.             if (*pulCurPelsOfs > ulWinPels)
  2013.                 *pulCurPelsOfs -= ulWinPels; // convert to units
  2014.             else
  2015.                 *pulCurPelsOfs = 0;
  2016.         break;
  2017.  
  2018.         case SB_PAGEDOWN:
  2019.             *pulCurPelsOfs += ulWinPels; // convert to units
  2020.         break;
  2021.  
  2022.         case SB_SLIDERTRACK:
  2023.             *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
  2024.             // _Pmpf(("    SB_SLIDERTRACK: usUnits = %d", usPosUnits));
  2025.         break;
  2026.  
  2027.         case SB_SLIDERPOSITION:
  2028.             *pulCurPelsOfs = (usPosUnits * usScrollUnitPels);
  2029.         break;
  2030.     }
  2031.  
  2032.     // are we close to the lower limit?
  2033.     /* if (*plCurUnitOfs < usLineStepUnits) // usScrollUnit)
  2034.         *plCurUnitOfs = 0;
  2035.     // are we close to the upper limit?
  2036.     else if (*plCurUnitOfs + usLineStepUnits > lMaxUnitOfs)
  2037.     {
  2038.         _Pmpf(("        !!! limiting: %d to %d", *plCurUnitOfs, lMaxUnitOfs));
  2039.         *plCurUnitOfs = lMaxUnitOfs;
  2040.     } */
  2041.  
  2042.     /* if (*plCurPelsOfs < 0)
  2043.         *plCurPelsOfs = 0; */       // checked above
  2044.     if (*pulCurPelsOfs > (lMaxAllowedUnitOfs * usScrollUnitPels))
  2045.     {
  2046.         *pulCurPelsOfs = (lMaxAllowedUnitOfs * usScrollUnitPels);
  2047.     }
  2048.     if (    (*pulCurPelsOfs != ulOldPelsOfs)
  2049.          || (*pulCurPelsOfs == 0)
  2050.          || (*pulCurPelsOfs == (lMaxAllowedUnitOfs * usScrollUnitPels))
  2051.        )
  2052.     {
  2053.         RECTL   rcl2Scroll,
  2054.                 rcl2Update;
  2055.  
  2056.         // changed:
  2057.         WinSendMsg(hwndScrollBar,
  2058.                    SBM_SETPOS,
  2059.                    (MPARAM)(*pulCurPelsOfs / usScrollUnitPels), //  / usScrollUnit),
  2060.                    0);
  2061.         // scroll window rectangle:
  2062.         rcl2Scroll.xLeft =  prcl2Scroll->xLeft;
  2063.         rcl2Scroll.xRight =  prcl2Scroll->xRight;
  2064.         rcl2Scroll.yBottom =  prcl2Scroll->yBottom;
  2065.         rcl2Scroll.yTop =  prcl2Scroll->yTop;
  2066.  
  2067.         if (msg == WM_VSCROLL)
  2068.             WinScrollWindow(hwnd2Scroll,
  2069.                             0,
  2070.                             (*pulCurPelsOfs - ulOldPelsOfs)  // scroll units changed
  2071.                             ,    // * usScrollUnitPels,     // convert to pels
  2072.                             &rcl2Scroll,  // rcl to scroll
  2073.                             prcl2Scroll, // clipping rect
  2074.                             NULLHANDLE, // no region
  2075.                             &rcl2Update,
  2076.                             0);
  2077.         else
  2078.             WinScrollWindow(hwnd2Scroll,
  2079.                             -(LONG)(*pulCurPelsOfs - ulOldPelsOfs) // scroll units changed
  2080.                             ,    // * usScrollUnitPels,
  2081.                             0,
  2082.                             &rcl2Scroll,  // rcl to scroll
  2083.                             prcl2Scroll, // clipping rect
  2084.                             NULLHANDLE, // no region
  2085.                             &rcl2Update,
  2086.                             0);
  2087.  
  2088.         // WinScrollWindow has stored the invalid window
  2089.         // rectangle which needs to be repainted in rcl2Update:
  2090.         WinInvalidateRect(hwnd2Scroll, &rcl2Update, FALSE);
  2091.     }
  2092.  
  2093.     // _Pmpf(("End of winhHandleScrollMsg"));
  2094.  
  2095.     return (TRUE);
  2096. }
  2097.  
  2098. /*
  2099.  *@@ winhProcessScrollChars:
  2100.  *      helper for processing WM_CHAR messages for
  2101.  *      client windows with scroll bars.
  2102.  *
  2103.  *      If your window has scroll bars, you normally
  2104.  *      need to process a number of keystrokes to be
  2105.  *      able to scroll the window contents. This is
  2106.  *      tiresome to code, so here is a helper.
  2107.  *
  2108.  *      When receiving WM_CHAR, call this function.
  2109.  *      If this returns TRUE, the keystroke has been
  2110.  *      a scroll keystroke, and the window has been
  2111.  *      updated (by sending WM_VSCROLL or WM_HSCROLL
  2112.  *      to hwndClient). Otherwise, you should process
  2113.  *      the keystroke as usual because it's not a
  2114.  *      scroll keystroke.
  2115.  *
  2116.  *      The following keystrokes are processed here:
  2117.  *
  2118.  *      -- "cursor up, down, right, left": scroll one
  2119.  *         line in the proper direction.
  2120.  *      -- "page up, down": scroll one page up or down.
  2121.  *      -- "Home": scroll leftmost.
  2122.  *      -- "Ctrl+ Home": scroll topmost.
  2123.  *      -- "End": scroll rightmost.
  2124.  *      -- "Ctrl+ End": scroll bottommost.
  2125.  *      -- "Ctrl + page up, down": scroll one screen left or right.
  2126.  *
  2127.  *      This is CUA behavior.
  2128.  *
  2129.  *      Returns TRUE if the message has been
  2130.  *      processed.
  2131.  *
  2132.  *@@added V0.9.3 (2000-04-29) [umoeller]
  2133.  *@@changed V0.9.9 (2001-02-01) [lafaix]: Ctrl+PgUp/Dn now do one screen left/right
  2134.  */
  2135.  
  2136. BOOL winhProcessScrollChars(HWND hwndClient,    // in: client window
  2137.                             HWND hwndVScroll,   // in: vertical scroll bar
  2138.                             HWND hwndHScroll,   // in: horizontal scroll bar
  2139.                             MPARAM mp1,         // in: WM_CHAR mp1
  2140.                             MPARAM mp2,         // in: WM_CHAR mp2
  2141.                             ULONG ulVertMax,    // in: maximum viewport cy
  2142.                             ULONG ulHorzMax)    // in: maximum viewport cx
  2143. {
  2144.     BOOL    fProcessed = FALSE;
  2145.     USHORT usFlags    = SHORT1FROMMP(mp1);
  2146.     // USHORT usch       = SHORT1FROMMP(mp2);
  2147.     USHORT usvk       = SHORT2FROMMP(mp2);
  2148.  
  2149.     // _Pmpf(("Entering winhProcessScrollChars"));
  2150.  
  2151.     if (usFlags & KC_VIRTUALKEY)
  2152.     {
  2153.         ULONG   ulMsg = 0;
  2154.         SHORT   sPos = 0;
  2155.         SHORT   usCmd = 0;
  2156.         fProcessed = TRUE;
  2157.  
  2158.         switch (usvk)
  2159.         {
  2160.             case VK_UP:
  2161.                 ulMsg = WM_VSCROLL;
  2162.                 usCmd = SB_LINEUP;
  2163.             break;
  2164.  
  2165.             case VK_DOWN:
  2166.                 ulMsg = WM_VSCROLL;
  2167.                 usCmd = SB_LINEDOWN;
  2168.             break;
  2169.  
  2170.             case VK_RIGHT:
  2171.                 ulMsg = WM_HSCROLL;
  2172.                 usCmd = SB_LINERIGHT;
  2173.             break;
  2174.  
  2175.             case VK_LEFT:
  2176.                 ulMsg = WM_HSCROLL;
  2177.                 usCmd = SB_LINELEFT;
  2178.             break;
  2179.  
  2180.             case VK_PAGEUP:
  2181.                 if (usFlags & KC_CTRL)
  2182.                     ulMsg = WM_HSCROLL;
  2183.                  else
  2184.                     ulMsg = WM_VSCROLL;
  2185.                 usCmd = SB_PAGEUP;
  2186.             break;
  2187.  
  2188.             case VK_PAGEDOWN:
  2189.                 if (usFlags & KC_CTRL)
  2190.                     ulMsg = WM_HSCROLL;
  2191.                 else
  2192.                     ulMsg = WM_VSCROLL;
  2193.                 usCmd = SB_PAGEDOWN;
  2194.             break;
  2195.  
  2196.             case VK_HOME:
  2197.                 if (usFlags & KC_CTRL)
  2198.                     // vertical:
  2199.                     ulMsg = WM_VSCROLL;
  2200.                 else
  2201.                     ulMsg = WM_HSCROLL;
  2202.  
  2203.                 sPos = 0;
  2204.                 usCmd = SB_SLIDERPOSITION;
  2205.             break;
  2206.  
  2207.             case VK_END:
  2208.                 if (usFlags & KC_CTRL)
  2209.                 {
  2210.                     // vertical:
  2211.                     ulMsg = WM_VSCROLL;
  2212.                     sPos = ulVertMax;
  2213.                 }
  2214.                 else
  2215.                 {
  2216.                     ulMsg = WM_HSCROLL;
  2217.                     sPos = ulHorzMax;
  2218.                 }
  2219.  
  2220.                 usCmd = SB_SLIDERPOSITION;
  2221.             break;
  2222.  
  2223.             default:
  2224.                 // other:
  2225.                 fProcessed = FALSE;
  2226.         }
  2227.  
  2228.         if (    ((usFlags & KC_KEYUP) == 0)
  2229.              && (ulMsg)
  2230.            )
  2231.         {
  2232.             HWND   hwndScrollBar = ((ulMsg == WM_VSCROLL)
  2233.                                         ? hwndVScroll
  2234.                                         : hwndHScroll);
  2235.             if (WinIsWindowEnabled(hwndScrollBar))
  2236.             {
  2237.                 USHORT usID = WinQueryWindowUShort(hwndScrollBar,
  2238.                                                    QWS_ID);
  2239.                 WinSendMsg(hwndClient,
  2240.                            ulMsg,
  2241.                            MPFROMSHORT(usID),
  2242.                            MPFROM2SHORT(sPos,
  2243.                                         usCmd));
  2244.             }
  2245.         }
  2246.     }
  2247.  
  2248.     // _Pmpf(("End of  winhProcessScrollChars"));
  2249.  
  2250.     return (fProcessed);
  2251. }
  2252.  
  2253. /*
  2254.  *@@category: Helpers\PM helpers\Window positioning
  2255.  */
  2256.  
  2257. /* ******************************************************************
  2258.  *
  2259.  *   Window positioning helpers
  2260.  *
  2261.  ********************************************************************/
  2262.  
  2263. /*
  2264.  *@@ winhSaveWindowPos:
  2265.  *      saves the position of a certain window. As opposed
  2266.  *      to the barely documented WinStoreWindowPos API, this
  2267.  *      one only saves one regular SWP structure for the given
  2268.  *      window, as returned by WinQueryWindowPos for hwnd.
  2269.  *
  2270.  *      If the window is currently maximized or minimized,
  2271.  *      we won't store the current window size and position
  2272.  *      (which wouldn't make much sense), but retrieve the
  2273.  *      "restored" window position from the window words
  2274.  *      instead.
  2275.  *
  2276.  *      The window should still be visible on the screen
  2277.  *      when calling this function. Do not call it in WM_DESTROY,
  2278.  *      because then the SWP data is no longer valid.
  2279.  *
  2280.  *      This returns TRUE if saving was successful.
  2281.  *
  2282.  *@@changed V0.9.1 (99-12-19) [umoeller]: added minimize/maximize support
  2283.  */
  2284.  
  2285. BOOL winhSaveWindowPos(HWND hwnd,   // in: window to save
  2286.                        HINI hIni,   // in: INI file (or HINI_USER/SYSTEM)
  2287.                        const char *pcszApp,  // in: INI application name
  2288.                        const char *pcszKey)  // in: INI key name
  2289. {
  2290.     BOOL brc = FALSE;
  2291.     SWP swp;
  2292.     if (WinQueryWindowPos(hwnd, &swp))
  2293.     {
  2294.         if (swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE))
  2295.         {
  2296.             // window currently maximized or minimized:
  2297.             // retrieve "restore" position from window words
  2298.             swp.x = WinQueryWindowUShort(hwnd, QWS_XRESTORE);
  2299.             swp.y = WinQueryWindowUShort(hwnd, QWS_YRESTORE);
  2300.             swp.cx = WinQueryWindowUShort(hwnd, QWS_CXRESTORE);
  2301.             swp.cy = WinQueryWindowUShort(hwnd, QWS_CYRESTORE);
  2302.         }
  2303.  
  2304.         brc = PrfWriteProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, sizeof(swp));
  2305.     }
  2306.     return brc;
  2307. }
  2308.  
  2309. /*
  2310.  *@@ winhRestoreWindowPos:
  2311.  *      this will retrieve a window position which was
  2312.  *      previously stored using winhSaveWindowPos.
  2313.  *
  2314.  *      The window should not be visible to avoid flickering.
  2315.  *      "fl" must contain the SWP_flags as in WinSetWindowPos.
  2316.  *
  2317.  *      Note that only the following may be used:
  2318.  *      --  SWP_MOVE        reposition the window
  2319.  *      --  SWP_SIZE        also resize the window to
  2320.  *                          the stored position; this might
  2321.  *                          lead to problems with different
  2322.  *                          video resolutions, so be careful.
  2323.  *      --  SWP_SHOW        make window visible too
  2324.  *      --  SWP_NOREDRAW    changes are not redrawn
  2325.  *      --  SWP_NOADJUST    do not send a WM_ADJUSTWINDOWPOS message
  2326.  *                          before moving or sizing
  2327.  *      --  SWP_ACTIVATE    activate window (make topmost)
  2328.  *      --  SWP_DEACTIVATE  deactivate window (make bottommost)
  2329.  *
  2330.  *      Do not specify any other SWP_* flags.
  2331.  *
  2332.  *      If SWP_SIZE is not set, the window will be moved only.
  2333.  *
  2334.  *      This returns TRUE if INI data was found.
  2335.  *
  2336.  *      This function automatically checks for whether the
  2337.  *      window would be positioned outside the visible screen
  2338.  *      area and will adjust coordinates accordingly. This can
  2339.  *      happen when changing video resolutions.
  2340.  *
  2341.  *@@changed V0.9.7 (2000-12-20) [umoeller]: fixed invalid params if INI key not found
  2342.  */
  2343.  
  2344. BOOL winhRestoreWindowPos(HWND hwnd,   // in: window to restore
  2345.                           HINI hIni,   // in: INI file (or HINI_USER/SYSTEM)
  2346.                           const char *pcszApp,  // in: INI application name
  2347.                           const char *pcszKey,  // in: INI key name
  2348.                           ULONG fl)    // in: "fl" parameter for WinSetWindowPos
  2349. {
  2350.     BOOL    brc = FALSE;
  2351.     SWP     swp;
  2352.     ULONG   cbswp = sizeof(swp);
  2353.     ULONG   fl2 = (fl & ~SWP_ZORDER);
  2354.  
  2355.     if (PrfQueryProfileData(hIni, (PSZ)pcszApp, (PSZ)pcszKey, &swp, &cbswp))
  2356.     {
  2357.         ULONG ulScreenCX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  2358.         ULONG ulScreenCY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  2359.  
  2360.         brc = TRUE;
  2361.  
  2362.         if ((fl & SWP_SIZE) == 0)
  2363.         {
  2364.             // if no resize, we need to get the current position
  2365.             SWP swpNow;
  2366.             brc = WinQueryWindowPos(hwnd, &swpNow);
  2367.             swp.cx = swpNow.cx;
  2368.             swp.cy = swpNow.cy;
  2369.         }
  2370.  
  2371.         if (brc)
  2372.         {
  2373.             // check for full visibility
  2374.             if ( (swp.x + swp.cx) > ulScreenCX)
  2375.                 swp.x = ulScreenCX - swp.cx;
  2376.             if ( (swp.y + swp.cy) > ulScreenCY)
  2377.                 swp.y = ulScreenCY - swp.cy;
  2378.         }
  2379.  
  2380.         brc = TRUE;
  2381.  
  2382.     }
  2383.     else
  2384.     {
  2385.         // window pos not found in INI: unset SWP_MOVE etc.
  2386.         WinQueryWindowPos(hwnd, &swp);
  2387.         fl2 &= ~(SWP_MOVE | SWP_SIZE);
  2388.     }
  2389.  
  2390.     WinSetWindowPos(hwnd,
  2391.                     NULLHANDLE,       // insert-behind window
  2392.                     swp.x,
  2393.                     swp.y,
  2394.                     swp.cx,
  2395.                     swp.cy,
  2396.                     fl2);        // SWP_* flags
  2397.  
  2398.     return brc;
  2399. }
  2400.  
  2401. /*
  2402.  *@@ winhAdjustControls:
  2403.  *      helper function for dynamically adjusting a window's
  2404.  *      controls when the window is resized.
  2405.  *
  2406.  *      This is most useful with dialogs loaded from resources
  2407.  *      which should be sizeable. Normally, when the dialog
  2408.  *      frame is resized, the controls stick to their positions,
  2409.  *      and for dialogs with many controls, programming the
  2410.  *      changes can be tiresome.
  2411.  *
  2412.  *      Enter this function. ;-) Basically, this takes a
  2413.  *      static array of MPARAM's as input (plus one dynamic
  2414.  *      storage area for the window positions).
  2415.  *
  2416.  *      This function must get called in three contexts:
  2417.  *      during WM_INITDLG, during WM_WINDOWPOSCHANGED, and
  2418.  *      during WM_DESTROY, with varying parameters.
  2419.  *
  2420.  *      In detail, there are four things you need to do to make
  2421.  *      this work:
  2422.  *
  2423.  *      1)  Set up a static array (as a global variable) of
  2424.  *          MPARAM's, one for each control in your array.
  2425.  *          Each MPARAM will have the control's ID and the
  2426.  *          XAC_* flags (winh.h) how the control shall be moved.
  2427.  *          Use MPFROM2SHORT to easily create this. Example:
  2428.  *
  2429.  +          MPARAM ampControlFlags[] =
  2430.  +              {  MPFROM2SHORT(ID_CONTROL_1, XAC_MOVEX),
  2431.  +                 MPFROM2SHORT(ID_CONTROL_2, XAC_SIZEY),
  2432.  +                  ...
  2433.  +              }
  2434.  *
  2435.  *          This can safely be declared as a global variable
  2436.  *          because this data will only be read and never
  2437.  *          changed by this function.
  2438.  *
  2439.  *      2)  In WM_INITDLG of your dialog function, set up
  2440.  *          an XADJUSTCTRLS structure, preferrably in your
  2441.  *          window words (QWL_USER).
  2442.  *
  2443.  *          ZERO THAT STRUCTURE (memset(&xac, 0, sizeof(XADJUSTCTRLS),
  2444.  *          or this func will not work (because it will intialize
  2445.  *          things on the first WM_WINDOWPOSCHANGED).
  2446.  *
  2447.  *      3)  Intercept WM_WINDOWPOSCHANGED:
  2448.  *
  2449.  +          case WM_WINDOWPOSCHANGED:
  2450.  +          {
  2451.  +              // this msg is passed two SWP structs:
  2452.  +              // one for the old, one for the new data
  2453.  +              // (from PM docs)
  2454.  +              PSWP pswpNew = PVOIDFROMMP(mp1);
  2455.  +              PSWP pswpOld = pswpNew + 1;
  2456.  +
  2457.  +              // resizing?
  2458.  +              if (pswpNew->fl & SWP_SIZE)
  2459.  +              {
  2460.  +                  PXADJUSTCTRLS pxac = ... // get it from your window words
  2461.  +
  2462.  +                  winhAdjustControls(hwndDlg,             // dialog
  2463.  +                                     ampControlFlags,     // MPARAMs array
  2464.  +                                     sizeof(ampControlFlags) / sizeof(MPARAM),
  2465.  +                                                          // items count
  2466.  +                                     pswpNew,             // mp1
  2467.  +                                     pxac);               // storage area
  2468.  +              }
  2469.  +              mrc = WinDefDlgProc(hwnd, msg, mp1, mp2); ...
  2470.  *
  2471.  *      4)  In WM_DESTROY, call this function again with pmpFlags,
  2472.  *          pswpNew, and pswpNew set to NULL. This will clean up the
  2473.  *          data which has been allocated internally (pointed to from
  2474.  *          the XADJUSTCTRLS structure).
  2475.  *          Don't forget to free your storage for XADJUSTCTLRS
  2476.  *          _itself_, that's the job of the caller.
  2477.  *
  2478.  *      This might sound complicated, but it's a lot easier than
  2479.  *      having to write dozens of WinSetWindowPos calls oneself.
  2480.  *
  2481.  *@@added V0.9.0 [umoeller]
  2482.  *@@changed V0.9.19 (2002-04-13) [umoeller]: added correlation for entry field repositioning, this was always off
  2483.  */
  2484.  
  2485. BOOL winhAdjustControls(HWND hwndDlg,           // in: dialog (req.)
  2486.                         const MPARAM *pmpFlags, // in: init flags or NULL for cleanup
  2487.                         ULONG ulCount,          // in: item count (req.)
  2488.                         PSWP pswpNew,           // in: pswpNew from WM_WINDOWPOSCHANGED or NULL for cleanup
  2489.                         PXADJUSTCTRLS pxac)     // in: adjust-controls storage area (req.)
  2490. {
  2491.     BOOL    brc = FALSE;
  2492.     ULONG   ul = 0;
  2493.  
  2494.     /* if (!WinIsWindowVisible(hwndDlg))
  2495.         return (FALSE); */
  2496.  
  2497.     if ((pmpFlags) && (pxac))
  2498.     {
  2499.         PSWP    pswpThis;
  2500.         const MPARAM  *pmpThis;
  2501.         LONG    ldcx, ldcy;
  2502.         ULONG   cWindows = 0;
  2503.  
  2504.         // V0.9.19 (2002-04-13) [umoeller]
  2505.         LONG cxMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
  2506.         LONG cyMarginEF = 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
  2507.  
  2508.         // setup mode:
  2509.         if (pxac->fInitialized == FALSE)
  2510.         {
  2511.             // first call: get all the SWP's
  2512.             WinQueryWindowPos(hwndDlg, &pxac->swpMain);
  2513.             // _Pmpf(("winhAdjustControls: queried main cx = %d, cy = %d",
  2514.                //      pxac->swpMain.cx, pxac->swpMain.cy));
  2515.  
  2516.             pxac->paswp = (PSWP)malloc(sizeof(SWP) * ulCount);
  2517.  
  2518.             pswpThis = pxac->paswp;
  2519.             pmpThis = pmpFlags;
  2520.  
  2521.             for (ul = 0;
  2522.                  ul < ulCount;
  2523.                  ul++)
  2524.             {
  2525.                 HWND hwndThis;
  2526.                 CHAR szClass[10];
  2527.                 if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
  2528.                 {
  2529.                     WinQueryWindowPos(hwndThis, pswpThis);
  2530.  
  2531.                     // correlate the stupid repositioning of entry fields
  2532.                     // V0.9.19 (2002-04-13) [umoeller]
  2533.                     if (    (WinQueryClassName(hwndThis, sizeof(szClass), szClass)
  2534.                          && (!strcmp(szClass, "#6"))
  2535.                          && (WinQueryWindowULong(hwndThis, QWL_STYLE) & ES_MARGIN))
  2536.                        )
  2537.                     {
  2538.                         pswpThis->x += cxMarginEF;
  2539.                         pswpThis->y += cyMarginEF;
  2540.                         pswpThis->cx -= 2 * cxMarginEF;
  2541.                         pswpThis->cy -= 2 * cyMarginEF;
  2542.                     }
  2543.  
  2544.                     cWindows++;
  2545.                 }
  2546.  
  2547.                 pswpThis++;
  2548.                 pmpThis++;
  2549.             }
  2550.  
  2551.             pxac->fInitialized = TRUE;
  2552.             // _Pmpf(("winhAdjustControls: queried %d controls", cWindows));
  2553.         }
  2554.  
  2555.         if (pswpNew)
  2556.         {
  2557.             // compute width and height delta
  2558.             ldcx = (pswpNew->cx - pxac->swpMain.cx);
  2559.             ldcy = (pswpNew->cy - pxac->swpMain.cy);
  2560.  
  2561.             // _Pmpf(("winhAdjustControls: new cx = %d, cy = %d",
  2562.               //       pswpNew->cx, pswpNew->cy));
  2563.  
  2564.             // now adjust the controls
  2565.             cWindows = 0;
  2566.             pswpThis = pxac->paswp;
  2567.             pmpThis = pmpFlags;
  2568.             for (ul = 0;
  2569.                  ul < ulCount;
  2570.                  ul++)
  2571.             {
  2572.                 HWND hwndThis;
  2573.                 if (hwndThis = WinWindowFromID(hwndDlg, SHORT1FROMMP(*pmpThis)))
  2574.                 {
  2575.                     LONG    x = pswpThis->x,
  2576.                             y = pswpThis->y,
  2577.                             cx = pswpThis->cx,
  2578.                             cy = pswpThis->cy;
  2579.  
  2580.                     ULONG   ulSwpFlags = 0;
  2581.                     // get flags for this control
  2582.                     USHORT  usFlags = SHORT2FROMMP(*pmpThis);
  2583.  
  2584.                     if (usFlags & XAC_MOVEX)
  2585.                     {
  2586.                         x += ldcx;
  2587.                         ulSwpFlags |= SWP_MOVE;
  2588.                     }
  2589.                     if (usFlags & XAC_MOVEY)
  2590.                     {
  2591.                         y += ldcy;
  2592.                         ulSwpFlags |= SWP_MOVE;
  2593.                     }
  2594.                     if (usFlags & XAC_SIZEX)
  2595.                     {
  2596.                         cx += ldcx;
  2597.                         ulSwpFlags |= SWP_SIZE;
  2598.                     }
  2599.                     if (usFlags & XAC_SIZEY)
  2600.                     {
  2601.                         cy += ldcy;
  2602.                         ulSwpFlags |= SWP_SIZE;
  2603.                     }
  2604.  
  2605.                     if (ulSwpFlags)
  2606.                     {
  2607.                         WinSetWindowPos(hwndThis,
  2608.                                         NULLHANDLE, // hwndInsertBehind
  2609.                                         x, y, cx, cy,
  2610.                                         ulSwpFlags);
  2611.                         cWindows++;
  2612.                         brc = TRUE;
  2613.                     }
  2614.                 }
  2615.  
  2616.                 pswpThis++;
  2617.                 pmpThis++;
  2618.             }
  2619.  
  2620.             // _Pmpf(("winhAdjustControls: set %d windows", cWindows));
  2621.         }
  2622.     }
  2623.     else
  2624.     {
  2625.         // pxac == NULL:
  2626.         // cleanup mode
  2627.         if (pxac->paswp)
  2628.             free(pxac->paswp);
  2629.     }
  2630.  
  2631.     return brc;
  2632. }
  2633.  
  2634. /*
  2635.  *@@ winhCenterWindow:
  2636.  *      centers a window within its parent window. If that's
  2637.  *      the PM desktop, it will be centered according to the
  2638.  *      whole screen.
  2639.  *      For dialog boxes, use WinCenteredDlgBox as a one-shot
  2640.  *      function.
  2641.  *
  2642.  *      Note: When calling this function, the window should
  2643.  *      not be visible to avoid flickering.
  2644.  *      This func does not show the window either, so call
  2645.  *      WinShowWindow afterwards.
  2646.  */
  2647.  
  2648. void winhCenterWindow(HWND hwnd)
  2649. {
  2650.    RECTL rclParent;
  2651.    RECTL rclWindow;
  2652.  
  2653.    WinQueryWindowRect(hwnd, &rclWindow);
  2654.    WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
  2655.  
  2656.    rclWindow.xLeft   = (rclParent.xRight - rclWindow.xRight) / 2;
  2657.    rclWindow.yBottom = (rclParent.yTop   - rclWindow.yTop  ) / 2;
  2658.  
  2659.    WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
  2660.                     0, 0, SWP_MOVE);
  2661. }
  2662.  
  2663. /*
  2664.  *@@ winhCenteredDlgBox:
  2665.  *      just like WinDlgBox, but the dlg box is centered on the screen;
  2666.  *      you should mark the dlg template as not visible in the dlg
  2667.  *      editor, or display will flicker.
  2668.  *      As opposed to winhCenterWindow, this _does_ show the window.
  2669.  */
  2670.  
  2671. ULONG winhCenteredDlgBox(HWND hwndParent,
  2672.                          HWND hwndOwner,
  2673.                          PFNWP pfnDlgProc,
  2674.                          HMODULE hmod,
  2675.                          ULONG idDlg,
  2676.                          PVOID pCreateParams)
  2677. {
  2678.     ULONG   ulReply;
  2679.     HWND    hwndDlg = WinLoadDlg(hwndParent,
  2680.                                  hwndOwner,
  2681.                                  pfnDlgProc,
  2682.                                  hmod,
  2683.                                  idDlg,
  2684.                                  pCreateParams);
  2685.     winhCenterWindow(hwndDlg);
  2686.     ulReply = WinProcessDlg(hwndDlg);
  2687.     WinDestroyWindow(hwndDlg);
  2688.     return (ulReply);
  2689. }
  2690.  
  2691. /*
  2692.  *@@ winhPlaceBesides:
  2693.  *      attempts to place hwnd somewhere besides
  2694.  *      hwndRelative.
  2695.  *
  2696.  *      fl is presently ignored, but should be
  2697.  *      PLF_SMART for future extensions.
  2698.  *
  2699.  *      Works only if hwnd is a desktop child.
  2700.  *
  2701.  *@@added V0.9.19 (2002-04-17) [umoeller]
  2702.  */
  2703.  
  2704. BOOL winhPlaceBesides(HWND hwnd,
  2705.                       HWND hwndRelative,
  2706.                       ULONG fl)
  2707. {
  2708.     BOOL brc = FALSE;
  2709.  
  2710.     SWP     swpRel,
  2711.             swpThis;
  2712.     LONG    xNew, yNew;
  2713.  
  2714.     if (    (WinQueryWindowPos(hwndRelative, &swpRel))
  2715.          && (WinQueryWindowPos(hwnd, &swpThis))
  2716.        )
  2717.     {
  2718.         HWND    hwndRelParent,
  2719.                 hwndThisParent;
  2720.         POINTL  ptlRel = {swpRel.x, swpRel.y};
  2721.         if (    (hwndRelParent = WinQueryWindow(hwndRelative, QW_PARENT))
  2722.              && (hwndThisParent = WinQueryWindow(hwnd, QW_PARENT))
  2723.              && (hwndRelParent != hwndThisParent)
  2724.            )
  2725.         {
  2726.             WinMapWindowPoints(hwndRelParent,
  2727.                                hwndThisParent,
  2728.                                &ptlRel,
  2729.                                1);
  2730.         }
  2731.  
  2732.         // place right first
  2733.         xNew = ptlRel.x + swpRel.cx;
  2734.         // center vertically
  2735.         yNew = ptlRel.y  + ((swpRel.cy - swpThis.cy) / 2);
  2736.  
  2737.         if (xNew + swpThis.cy > WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN))
  2738.         {
  2739.             // place left then
  2740.             xNew = ptlRel.x - swpThis.cx;
  2741.  
  2742.             if (xNew < 0)
  2743.             {
  2744.                 // center then
  2745.                 winhCenterWindow(hwnd);
  2746.                 brc = TRUE;
  2747.             }
  2748.         }
  2749.  
  2750.         if (!brc)
  2751.             brc = WinSetWindowPos(hwnd,
  2752.                                   0,
  2753.                                   xNew,
  2754.                                   yNew,
  2755.                                   0,
  2756.                                   0,
  2757.                                   SWP_MOVE);
  2758.     }
  2759.  
  2760.     return brc;
  2761. }
  2762.  
  2763. /*
  2764.  *@@ winhFindWindowBelow:
  2765.  *      finds the window with the same parent
  2766.  *      which sits right below hwndFind in the
  2767.  *      window Z-order.
  2768.  *
  2769.  *@@added V0.9.7 (2000-12-04) [umoeller]
  2770.  */
  2771.  
  2772. HWND winhFindWindowBelow(HWND hwndFind)
  2773. {
  2774.     HWND hwnd = NULLHANDLE,
  2775.          hwndParent = WinQueryWindow(hwndFind, QW_PARENT);
  2776.  
  2777.     if (hwndParent)
  2778.     {
  2779.         HENUM   henum = WinBeginEnumWindows(hwndParent);
  2780.         HWND    hwndThis;
  2781.         while (hwndThis = WinGetNextWindow(henum))
  2782.         {
  2783.             SWP swp;
  2784.             WinQueryWindowPos(hwndThis, &swp);
  2785.             if (swp.hwndInsertBehind == hwndFind)
  2786.             {
  2787.                 hwnd = hwndThis;
  2788.                 break;
  2789.             }
  2790.         }
  2791.         WinEndEnumWindows(henum);
  2792.     }
  2793.  
  2794.     return (hwnd);
  2795. }
  2796.  
  2797. /*
  2798.  *@@category: Helpers\PM helpers\Presentation parameters
  2799.  */
  2800.  
  2801. /* ******************************************************************
  2802.  *
  2803.  *   Presparams helpers
  2804.  *
  2805.  ********************************************************************/
  2806.  
  2807. /*
  2808.  *@@ winhQueryWindowFont:
  2809.  *      returns the window font presentation parameter
  2810.  *      in a newly allocated buffer.
  2811.  *
  2812.  *      Returns NULL on error. Use free()
  2813.  *      to free the return value.
  2814.  *
  2815.  *@@added V0.9.1 (2000-02-14) [umoeller]
  2816.  */
  2817.  
  2818. PSZ winhQueryWindowFont(HWND hwnd)
  2819. {
  2820.     CHAR  szNewFont[100] = "";
  2821.     WinQueryPresParam(hwnd,
  2822.                       PP_FONTNAMESIZE,
  2823.                       0,
  2824.                       NULL,
  2825.                       (ULONG)sizeof(szNewFont),
  2826.                       (PVOID)&szNewFont,
  2827.                       QPF_NOINHERIT);
  2828.     if (szNewFont[0] != 0)
  2829.         return (strdup(szNewFont));
  2830.  
  2831.     return NULL;
  2832. }
  2833.  
  2834. /*
  2835.  *@@ winhSetWindowFont:
  2836.  *      this sets a window's font by invoking
  2837.  *      WinSetPresParam on it.
  2838.  *
  2839.  *      If (pszFont == NULL), a default font will be set
  2840.  *      (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
  2841.  *
  2842.  *      winh.h also defines the winhSetDlgItemFont macro.
  2843.  *
  2844.  *      Returns TRUE if successful or FALSE otherwise.
  2845.  *
  2846.  *@@added V0.9.0 [umoeller]
  2847.  */
  2848.  
  2849. BOOL winhSetWindowFont(HWND hwnd,
  2850.                        const char *pcszFont)
  2851. {
  2852.     CHAR    szFont[256];
  2853.  
  2854.     if (pcszFont == NULL)
  2855.     {
  2856.         if (doshIsWarp4())
  2857.             strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
  2858.         else
  2859.             strhncpy0(szFont, "8.Helv", sizeof(szFont));
  2860.     }
  2861.     else
  2862.         strhncpy0(szFont, pcszFont, sizeof(szFont));
  2863.  
  2864.     return (WinSetPresParam(hwnd,
  2865.                             PP_FONTNAMESIZE,
  2866.                             strlen(szFont)+1,
  2867.                             szFont));
  2868. }
  2869.  
  2870. /*
  2871.  *@@ winhSetControlsFont:
  2872.  *      this sets the font for all the controls of hwndDlg
  2873.  *      which have a control ID in the range of usIDMin to
  2874.  *      usIDMax. "Unused" IDs (i.e. -1) will also be set.
  2875.  *
  2876.  *      If (pszFont == NULL), a default font will be set
  2877.  *      (on Warp 4, "9.WarpSans", on Warp 3, "8.Helv").
  2878.  *
  2879.  *      Returns the no. of controls set.
  2880.  *
  2881.  *@@added V0.9.0 [umoeller]
  2882.  */
  2883.  
  2884. ULONG winhSetControlsFont(HWND hwndDlg,      // in: dlg to set
  2885.                           SHORT usIDMin,     // in: minimum control ID to be set (inclusive)
  2886.                           SHORT usIDMax,     // in: maximum control ID to be set (inclusive)
  2887.                           const char *pcszFont)  // in: font to use (e.g. "9.WarpSans") or NULL
  2888. {
  2889.     ULONG   ulrc = 0;
  2890.     HENUM   henum;
  2891.     HWND    hwndItem;
  2892.     CHAR    szFont[256];
  2893.     ULONG   cbFont;
  2894.  
  2895.     if (pcszFont == NULL)
  2896.     {
  2897.         if (doshIsWarp4())
  2898.             strhncpy0(szFont, "9.WarpSans", sizeof(szFont));
  2899.         else
  2900.             strhncpy0(szFont, "8.Helv", sizeof(szFont));
  2901.     }
  2902.     else
  2903.         strhncpy0(szFont, pcszFont, sizeof(szFont));
  2904.     cbFont = strlen(szFont)+1;
  2905.  
  2906.     // set font for all the dialog controls
  2907.     henum = WinBeginEnumWindows(hwndDlg);
  2908.     while ((hwndItem = WinGetNextWindow(henum)))
  2909.     {
  2910.         SHORT sID = WinQueryWindowUShort(hwndItem, QWS_ID);
  2911.         if (    (sID == -1)
  2912.              || ((sID >= usIDMin) && (sID <= usIDMax))
  2913.            )
  2914.             if (WinSetPresParam(hwndItem,
  2915.                                 PP_FONTNAMESIZE,
  2916.                                 cbFont,
  2917.                                 szFont))
  2918.                 // successful:
  2919.                 ulrc++;
  2920.     }
  2921.     WinEndEnumWindows(henum);
  2922.     return (ulrc);
  2923. }
  2924.  
  2925. /*
  2926.  *@@ winhStorePresParam:
  2927.  *      this appends a new presentation parameter to an
  2928.  *      array of presentation parameters which can be
  2929.  *      passed to WinCreateWindow. This is preferred
  2930.  *      over setting the presparams using WinSetPresParams,
  2931.  *      because that call will cause a lot of messages.
  2932.  *
  2933.  *      On the first call, pppp _must_ be NULL. This
  2934.  *      will allocate memory for storing the given
  2935.  *      data as necessary and modify *pppp to point
  2936.  *      to the new array.
  2937.  *
  2938.  *      On subsequent calls with the same pppp, memory
  2939.  *      will be reallocated, the old data will be copied,
  2940.  *      and the new given data will be appended.
  2941.  *
  2942.  *      Use free() on your PPRESPARAMS pointer (whose
  2943.  *      address was passed) after WinCreateWindow.
  2944.  *
  2945.  *      See winhQueryPresColor for typical presparams
  2946.  *      used in OS/2.
  2947.  *
  2948.  *      Example:
  2949.  *
  2950.  +          PPRESPARAMS ppp = NULL;
  2951.  +          CHAR szFont[] = "9.WarpSans";
  2952.  +          LONG lColor = CLR_WHITE;
  2953.  +          winhStorePresParam(&ppp, PP_FONTNAMESIZE, sizeof(szFont), szFont);
  2954.  +          winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
  2955.  +          WinCreateWindow(...., ppp);
  2956.  +          free(ppp);
  2957.  *
  2958.  *@@added V0.9.0 [umoeller]
  2959.  */
  2960.  
  2961. BOOL winhStorePresParam(PPRESPARAMS *pppp,      // in: data pointer (modified)
  2962.                         ULONG ulAttrType,       // in: PP_* index
  2963.                         ULONG cbData,           // in: sizeof(*pData), e.g. sizeof(LONG)
  2964.                         PVOID pData)            // in: presparam data (e.g. a PLONG to a color)
  2965. {
  2966.     BOOL        brc = FALSE;
  2967.     if (pppp)
  2968.     {
  2969.         ULONG       cbOld = 0,
  2970.                     cbNew;
  2971.         PBYTE       pbTemp = 0;
  2972.         PPRESPARAMS pppTemp = 0;
  2973.         PPARAM      pppCopyTo = 0;
  2974.  
  2975.         if (*pppp != NULL)
  2976.             // subsequent calls:
  2977.             cbOld = (**pppp).cb;
  2978.  
  2979.         cbNew = sizeof(ULONG)       // PRESPARAMS.cb
  2980.                 + cbOld             // old count, which does not include PRESPARAMS.cb
  2981.                 + sizeof(ULONG)     // PRESPARAMS.aparam[0].id
  2982.                 + sizeof(ULONG)     // PRESPARAMS.aparam[0].cb
  2983.                 + cbData;           // PRESPARAMS.aparam[0].ab[]
  2984.  
  2985.         pbTemp = (PBYTE)malloc(cbNew);
  2986.         if (pbTemp)
  2987.         {
  2988.             pppTemp = (PPRESPARAMS)pbTemp;
  2989.  
  2990.             if (*pppp != NULL)
  2991.             {
  2992.                 // copy old data
  2993.                 memcpy(pbTemp, *pppp, cbOld + sizeof(ULONG)); // including PRESPARAMS.cb
  2994.                 pppCopyTo = (PPARAM)(pbTemp             // new buffer
  2995.                                      + sizeof(ULONG)    // skipping PRESPARAMS.cb
  2996.                                      + cbOld);          // old PARAM array
  2997.             }
  2998.             else
  2999.                 // first call:
  3000.                 pppCopyTo = pppTemp->aparam;
  3001.  
  3002.             pppTemp->cb = cbNew - sizeof(ULONG);     // excluding PRESPARAMS.cb
  3003.             pppCopyTo->id = ulAttrType;
  3004.             pppCopyTo->cb = cbData;       // byte count of PARAM.ab[]
  3005.             memcpy(pppCopyTo->ab, pData, cbData);
  3006.  
  3007.             free(*pppp);
  3008.             *pppp = pppTemp;
  3009.  
  3010.             brc = TRUE;
  3011.         }
  3012.     }
  3013.     return brc;
  3014. }
  3015.  
  3016. /*
  3017.  *@@ winhQueryPresColor2:
  3018.  *      returns the specified color. This is queried in the
  3019.  *      following order:
  3020.  *
  3021.  *      1)  hwnd's pres params are searched for ulPP
  3022.  *          (which should be a PP_* index);
  3023.  *      2)  if (fInherit == TRUE), the parent windows
  3024.  *          are searched also;
  3025.  *      3)  if this fails or (fInherit == FALSE), WinQuerySysColor
  3026.  *          is called to get lSysColor (which should be a SYSCLR_*
  3027.  *          index), if lSysColor != -1;
  3028.  *      4)  if (lSysColor == -1), -1 is returned.
  3029.  *
  3030.  *      The return value is always an RGB LONG, _not_ a color index.
  3031.  *      This is even true for the returned system colors, which are
  3032.  *      converted to RGB.
  3033.  *
  3034.  *      If you do any painting with this value, you should switch
  3035.  *      the HPS you're using to RGB mode (use gpihSwitchToRGB for that).
  3036.  *
  3037.  *      Some useful ulPP / lSysColor pairs
  3038.  *      (default values as in PMREF):
  3039.  *
  3040.  +          --  PP_FOREGROUNDCOLOR          SYSCLR_WINDOWTEXT (for most controls also)
  3041.  +                                          SYSCLR_WINDOWSTATICTEXT (for static controls)
  3042.  +                 Foreground color (default: black)
  3043.  +          --  PP_BACKGROUNDCOLOR          SYSCLR_BACKGROUND
  3044.  +                                          SYSCLR_DIALOGBACKGROUND
  3045.  +                                          SYSCLR_FIELDBACKGROUND (for disabled scrollbars)
  3046.  +                                          SYSCLR_WINDOW (application surface -- empty clients)
  3047.  +                 Background color (default: light gray)
  3048.  +          --  PP_ACTIVETEXTFGNDCOLOR
  3049.  +          --  PP_HILITEFOREGROUNDCOLOR    SYSCLR_HILITEFOREGROUND
  3050.  +                 Highlighted foreground color, for example for selected menu
  3051.  +                 (def.: white)
  3052.  +          --  PP_ACTIVETEXTBGNDCOLOR
  3053.  +          --  PP_HILITEBACKGROUNDCOLOR    SYSCLR_HILITEBACKGROUND
  3054.  +                 Highlighted background color (def.: dark gray)
  3055.  +          --  PP_INACTIVETEXTFGNDCOLOR
  3056.  +          --  PP_DISABLEDFOREGROUNDCOLOR  SYSCLR_MENUDISABLEDTEXT
  3057.  +                 Disabled foreground color (dark gray)
  3058.  +          --  PP_INACTIVETEXTBGNDCOLOR
  3059.  +          --  PP_DISABLEDBACKGROUNDCOLOR
  3060.  +                 Disabled background color
  3061.  +          --  PP_BORDERCOLOR              SYSCLR_WINDOWFRAME
  3062.  +                                          SYSCLR_INACTIVEBORDER
  3063.  +                 Border color (around pushbuttons, in addition to
  3064.  +                 the 3D colors)
  3065.  +          --  PP_ACTIVECOLOR              SYSCLR_ACTIVETITLE
  3066.  +                 Active color
  3067.  +          --  PP_INACTIVECOLOR            SYSCLR_INACTIVETITLE
  3068.  +                 Inactive color
  3069.  *
  3070.  *      For menus:
  3071.  +          --  PP_MENUBACKGROUNDCOLOR      SYSCLR_MENU
  3072.  +          --  PP_MENUFOREGROUNDCOLOR      SYSCLR_MENUTEXT
  3073.  +          --  PP_MENUHILITEBGNDCOLOR      SYSCLR_MENUHILITEBGND
  3074.  +          --  PP_MENUHILITEFGNDCOLOR      SYSCLR_MENUHILITE
  3075.  +          --  ??                          SYSCLR_MENUDISABLEDTEXT
  3076.  +
  3077.  *      For containers (according to the API ref. at EDM/2):
  3078.  +          --  PP_FOREGROUNDCOLOR          SYSCLR_WINDOWTEXT
  3079.  +          --  PP_BACKGROUNDCOLOR          SYSCLR_WINDOW
  3080.  +          --  PP_HILITEFOREGROUNDCOLOR    SYSCLR_HILITEFOREGROUND
  3081.  +          --  PP_HILITEBACKGROUNDCOLOR    SYSCLR_HILITEBACKGROUND
  3082.  +          --  PP_BORDERCOLOR
  3083.  +                  (used for separator lines, eg. in Details view)
  3084.  +          --  PP_ICONTEXTBACKGROUNDCOLOR
  3085.  +                  (column titles in Details view?!?)
  3086.  +
  3087.  *      For listboxes / entryfields / MLE's:
  3088.  +          --  PP_BACKGROUNDCOLOR          SYSCLR_ENTRYFIELD
  3089.  *
  3090.  *  PMREF has more of these.
  3091.  *
  3092.  *@@changed V0.9.0 [umoeller]: removed INI key query, using SYSCLR_* instead; function prototype changed
  3093.  *@@changed V0.9.0 [umoeller]: added fInherit parameter
  3094.  *@@changed V0.9.7 (2000-12-02) [umoeller]: added lSysColor == -1 support
  3095.  *@@changed V0.9.20 (2002-08-04) [umoeller]: added ulPPIndex, renamed func
  3096.  */
  3097.  
  3098. LONG winhQueryPresColor2(HWND hwnd,          // in: window to query
  3099.                          ULONG ulppRGB,      // in: PP_* index for RGB color
  3100.                          ULONG ulppIndex,    // in: PP_* index for color _index_ (can be null)
  3101.                          BOOL fInherit,      // in: search parent windows too?
  3102.                          LONG lSysColor)     // in: SYSCLR_* index or -1
  3103. {
  3104.     ULONG   ul,
  3105.             attrFound;
  3106.     LONG    lColorFound;
  3107.  
  3108.     if (ulppRGB != (ULONG)-1)
  3109.     {
  3110.         ULONG fl = 0;
  3111.         if (!fInherit)
  3112.             fl = QPF_NOINHERIT;
  3113.         if (ulppIndex)
  3114.             fl |= QPF_ID2COLORINDEX;            // convert indexed color 2 to RGB V0.9.20 (2002-08-04) [umoeller]
  3115.  
  3116.         if ((ul = WinQueryPresParam(hwnd,
  3117.                                     ulppRGB,
  3118.                                     ulppIndex,
  3119.                                     &attrFound,
  3120.                                     sizeof(lColorFound),
  3121.                                     &lColorFound,
  3122.                                     fl)))
  3123.             return lColorFound;
  3124.     }
  3125.  
  3126.     // not found: get system color
  3127.     if (lSysColor != -1)
  3128.         return WinQuerySysColor(HWND_DESKTOP, lSysColor, 0);
  3129.  
  3130.     return -1;
  3131. }
  3132.  
  3133. /*
  3134.  *@@ winhQueryPresColor:
  3135.  *      compatibility function because this one was
  3136.  *      exported.
  3137.  *
  3138.  *@@added V0.9.20 (2002-08-04) [umoeller]
  3139.  */
  3140.  
  3141. LONG XWPENTRY winhQueryPresColor(HWND hwnd,
  3142.                                  ULONG ulPP,
  3143.                                  BOOL fInherit,
  3144.                                  LONG lSysColor)
  3145. {
  3146.     return winhQueryPresColor2(hwnd,
  3147.                                ulPP,
  3148.                                0,
  3149.                                fInherit,
  3150.                                lSysColor);
  3151. }
  3152.  
  3153. /*
  3154.  *@@ winhSetPresColor:
  3155.  *      sets a color presparam. ulIndex specifies
  3156.  *      the presparam to be set and would normally
  3157.  *      be either PP_BACKGROUNDCOLOR or PP_FOREGROUNDCOLOR.
  3158.  *
  3159.  *@@added V0.9.16 (2001-10-15) [umoeller]
  3160.  */
  3161.  
  3162. BOOL winhSetPresColor(HWND hwnd,
  3163.                       ULONG ulIndex,
  3164.                       LONG lColor)
  3165. {
  3166.     return (WinSetPresParam(hwnd,
  3167.                             ulIndex,
  3168.                             sizeof(LONG),
  3169.                             &lColor));
  3170. }
  3171.  
  3172. /*
  3173.  *@@category: Helpers\PM helpers\Help (IPF)
  3174.  */
  3175.  
  3176. /* ******************************************************************
  3177.  *
  3178.  *   Help instance helpers
  3179.  *
  3180.  ********************************************************************/
  3181.  
  3182. /*
  3183.  *@@ winhCreateHelp:
  3184.  *      creates a help instance and connects it with the
  3185.  *      given frame window.
  3186.  *
  3187.  *      If (pszFileName == NULL), we'll retrieve the
  3188.  *      executable's fully qualified file name and
  3189.  *      replace the extension with .HLP simply. This
  3190.  *      avoids the typical "Help not found" errors if
  3191.  *      the program isn't started in its own directory.
  3192.  *
  3193.  *      If you have created a help table in memory, specify it
  3194.  *      with pHelpTable. To load a help table from the resources,
  3195.  *      specify hmod (or NULLHANDLE) and set pHelpTable to the
  3196.  *      following:
  3197.  +
  3198.  +          (PHELPTABLE)MAKELONG(usTableID, 0xffff)
  3199.  *
  3200.  *      Returns the help window handle or NULLHANDLE on errors.
  3201.  *
  3202.  *      Based on an EDM/2 code snippet.
  3203.  *
  3204.  *@@added V0.9.4 (2000-07-03) [umoeller]
  3205.  */
  3206.  
  3207. HWND winhCreateHelp(HWND hwndFrame,      // in: app's frame window handle; can be NULLHANDLE
  3208.                     const char *pcszFileName,    // in: help file name or NULL
  3209.                     HMODULE hmod,           // in: module with help table or NULLHANDLE (current)
  3210.                     PHELPTABLE pHelpTable,  // in: help table or resource ID
  3211.                     const char *pcszWindowTitle) // in: help window title or NULL
  3212. {
  3213.     HELPINIT hi;
  3214.     PSZ      pszExt;
  3215.     CHAR     szName[CCHMAXPATH];
  3216.     HWND     hwndHelp;
  3217.  
  3218.     if (pcszFileName == NULL)
  3219.     {
  3220.         PPIB     ppib;
  3221.         PTIB     ptib;
  3222.         DosGetInfoBlocks(&ptib, &ppib);
  3223.         DosQueryModuleName(ppib->pib_hmte, sizeof(szName), szName);
  3224.  
  3225.         pszExt = strrchr(szName, '.');
  3226.         if (pszExt)
  3227.             strcpy(pszExt, ".hlp");
  3228.         else
  3229.             strcat(szName, ".hlp");
  3230.  
  3231.         pcszFileName = szName;
  3232.     }
  3233.  
  3234.     hi.cb                       = sizeof(HELPINIT);
  3235.     hi.ulReturnCode             = 0;
  3236.     hi.pszTutorialName          = NULL;
  3237.     hi.phtHelpTable             = pHelpTable;
  3238.     hi.hmodHelpTableModule      = hmod;
  3239.     hi.hmodAccelActionBarModule = NULLHANDLE;
  3240.     hi.idAccelTable             = 0;
  3241.     hi.idActionBar              = 0;
  3242.     hi.pszHelpWindowTitle       = (PSZ)pcszWindowTitle;
  3243.     hi.fShowPanelId             = CMIC_HIDE_PANEL_ID;
  3244.     hi.pszHelpLibraryName       = (PSZ)pcszFileName;
  3245.  
  3246.     hwndHelp = WinCreateHelpInstance(WinQueryAnchorBlock(hwndFrame),
  3247.                                      &hi);
  3248.     if ((hwndFrame) && (hwndHelp))
  3249.     {
  3250.         WinAssociateHelpInstance(hwndHelp, hwndFrame);
  3251.     }
  3252.  
  3253.     return (hwndHelp);
  3254. }
  3255.  
  3256. /*
  3257.  *@@ winhDisplayHelpPanel:
  3258.  *      displays the specified help panel ID.
  3259.  *
  3260.  *      If (ulHelpPanel == 0), this displays the
  3261.  *      standard OS/2 "Using help" panel.
  3262.  *
  3263.  *      Returns zero on success or one of the
  3264.  *      help manager error codes on failure.
  3265.  *      See HM_ERROR for those.
  3266.  *
  3267.  *@@added V0.9.7 (2001-01-21) [umoeller]
  3268.  */
  3269.  
  3270. ULONG winhDisplayHelpPanel(HWND hwndHelpInstance,   // in: from winhCreateHelp
  3271.                            ULONG ulHelpPanel)       // in: help panel ID
  3272. {
  3273.     return (ULONG)(WinSendMsg(hwndHelpInstance,
  3274.                               HM_DISPLAY_HELP,
  3275.                               (MPARAM)ulHelpPanel,
  3276.                               (MPARAM)(    (ulHelpPanel != 0)
  3277.                                            ? HM_RESOURCEID
  3278.                                            : 0)));
  3279. }
  3280.  
  3281. /*
  3282.  *@@ winhDestroyHelp:
  3283.  *      destroys the help instance created by winhCreateHelp.
  3284.  *
  3285.  *      Based on an EDM/2 code snippet.
  3286.  *
  3287.  *@@added V0.9.4 (2000-07-03) [umoeller]
  3288.  */
  3289.  
  3290. void winhDestroyHelp(HWND hwndHelp,
  3291.                      HWND hwndFrame)    // can be NULLHANDLE if not used with winhCreateHelp
  3292. {
  3293.     if (hwndHelp)
  3294.     {
  3295.         if (hwndFrame)
  3296.             WinAssociateHelpInstance(NULLHANDLE, hwndFrame);
  3297.         WinDestroyHelpInstance(hwndHelp);
  3298.     }
  3299. }
  3300.  
  3301. /*
  3302.  *@@category: Helpers\PM helpers\Application control
  3303.  */
  3304.  
  3305. /* ******************************************************************
  3306.  *
  3307.  *   Application control
  3308.  *
  3309.  ********************************************************************/
  3310.  
  3311. /*
  3312.  *@@ winhAnotherInstance:
  3313.  *      this tests whether another instance of the same
  3314.  *      application is already running.
  3315.  *
  3316.  *      To identify instances of the same application, the
  3317.  *      application must call this function during startup
  3318.  *      with the unique name of an OS/2 semaphore. As with
  3319.  *      all OS/2 semaphores, the semaphore name must begin
  3320.  *      with "\\SEM32\\". The semaphore isn't really used
  3321.  *      except for testing for its existence, since that
  3322.  *      name is unique among all processes.
  3323.  *
  3324.  *      If another instance is found, TRUE is returned. If
  3325.  *      (fSwitch == TRUE), that instance is switched to,
  3326.  *      using the tasklist.
  3327.  *
  3328.  *      If no other instance is found, FALSE is returned only.
  3329.  *
  3330.  *      Based on an EDM/2 code snippet.
  3331.  *
  3332.  *@@added V0.9.0 (99-10-22) [umoeller]
  3333.  */
  3334.  
  3335. BOOL winhAnotherInstance(const char *pcszSemName,    // in: semaphore ID
  3336.                          BOOL fSwitch)      // in: if TRUE, switch to first instance if running
  3337. {
  3338.     HMTX hmtx;
  3339.  
  3340.     if (DosCreateMutexSem((PSZ)pcszSemName,
  3341.                           &hmtx,
  3342.                           DC_SEM_SHARED,
  3343.                           TRUE)
  3344.               == NO_ERROR)
  3345.         // semapore created: this doesn't happen if the semaphore
  3346.         // exists already, so no other instance is running
  3347.         return (FALSE);
  3348.  
  3349.     // else: instance running
  3350.     hmtx = NULLHANDLE;
  3351.  
  3352.     // switch to other instance?
  3353.     if (fSwitch)
  3354.     {
  3355.         // yes: query mutex creator
  3356.         if (DosOpenMutexSem((PSZ)pcszSemName,
  3357.                             &hmtx)
  3358.                     == NO_ERROR)
  3359.         {
  3360.             PID     pid = 0;
  3361.             TID     tid = 0;        // unused
  3362.             ULONG   ulCount;        // unused
  3363.  
  3364.             if (DosQueryMutexSem(hmtx, &pid, &tid, &ulCount) == NO_ERROR)
  3365.             {
  3366.                 HSWITCH hswitch = WinQuerySwitchHandle(NULLHANDLE, pid);
  3367.                 if (hswitch != NULLHANDLE)
  3368.                     WinSwitchToProgram(hswitch);
  3369.             }
  3370.  
  3371.             DosCloseMutexSem(hmtx);
  3372.         }
  3373.     }
  3374.  
  3375.     return (TRUE);      // another instance exists
  3376. }
  3377.  
  3378. /*
  3379.  *@@ winhAddToTasklist:
  3380.  *      this adds the specified window to the tasklist
  3381.  *      with hIcon as its program icon (which is also
  3382.  *      set for the main window). This is useful for
  3383.  *      the old "dialog as main window" trick.
  3384.  *
  3385.  *      Returns the HSWITCH of the added entry.
  3386.  */
  3387.  
  3388. HSWITCH winhAddToTasklist(HWND hwnd,       // in: window to add
  3389.                           HPOINTER hIcon)  // in: icon for main window
  3390. {
  3391.     SWCNTRL     swctl;
  3392.     HSWITCH hswitch = 0;
  3393.     swctl.hwnd = hwnd;                     // window handle
  3394.     swctl.hwndIcon = hIcon;                // icon handle
  3395.     swctl.hprog = NULLHANDLE;              // program handle (use default)
  3396.     WinQueryWindowProcess(hwnd, &(swctl.idProcess), NULL);
  3397.                                            // process identifier
  3398.     swctl.idSession = 0;                   // session identifier ?
  3399.     swctl.uchVisibility = SWL_VISIBLE;     // visibility
  3400.     swctl.fbJump = SWL_JUMPABLE;           // jump indicator
  3401.     // get window title from window titlebar
  3402.     if (hwnd)
  3403.         WinQueryWindowText(hwnd, sizeof(swctl.szSwtitle), swctl.szSwtitle);
  3404.     swctl.bProgType = PROG_DEFAULT;        // program type
  3405.     hswitch = WinAddSwitchEntry(&swctl);
  3406.  
  3407.     // give the main window the icon
  3408.     if ((hwnd) && (hIcon))
  3409.         WinSendMsg(hwnd,
  3410.                    WM_SETICON,
  3411.                    (MPARAM)hIcon,
  3412.                    NULL);
  3413.  
  3414.     return (hswitch);
  3415. }
  3416.  
  3417. /*
  3418.  *@@category: Helpers\PM helpers\Miscellaneous
  3419.  */
  3420.  
  3421. /* ******************************************************************
  3422.  *
  3423.  *   Miscellaneous
  3424.  *
  3425.  ********************************************************************/
  3426.  
  3427. /*
  3428.  *@@ winhMyAnchorBlock:
  3429.  *      returns the proper anchor block (HAB)
  3430.  *      for the calling thread.
  3431.  *
  3432.  *      Many Win* functions require an HAB to be
  3433.  *      passed in. While many of them will work
  3434.  *      when passing in NULLHANDLE, some (such as
  3435.  *      WinGetMsg) won't. If you don't know the
  3436.  *      anchor block of the calling thread, use
  3437.  *      this function.
  3438.  *
  3439.  *      This creates a temporary object window to
  3440.  *      find out the anchor block. This is quite
  3441.  *      expensive so only use this if there's no
  3442.  *      other way to find out.
  3443.  *
  3444.  *@@added V0.9.11 (2001-04-20) [umoeller]
  3445.  */
  3446.  
  3447. HAB winhMyAnchorBlock(VOID)
  3448. {
  3449.     HAB hab = NULLHANDLE;
  3450.     HWND hwnd;
  3451.     if (hwnd = winhCreateObjectWindow(WC_BUTTON, NULL))
  3452.     {
  3453.         hab = WinQueryAnchorBlock(hwnd);
  3454.         WinDestroyWindow(hwnd);
  3455.     }
  3456.  
  3457.     return (hab);
  3458. }
  3459.  
  3460. /*
  3461.  *@@ winhFree:
  3462.  *      frees a block of memory allocated by the
  3463.  *      winh* functions.
  3464.  *
  3465.  *      Since the winh* functions use malloc(),
  3466.  *      you can also use free() directly on such
  3467.  *      blocks. However, you must use winhFree
  3468.  *      if the winh* functions are in a module
  3469.  *      with a different C runtime.
  3470.  *
  3471.  *@@added V0.9.7 (2000-12-06) [umoeller]
  3472.  */
  3473.  
  3474. VOID winhFree(PVOID p)
  3475. {
  3476.     if (p)
  3477.         free(p);
  3478. }
  3479.  
  3480. /*
  3481.  *@@ winhSleep:
  3482.  *      sleeps at least the specified amount of time,
  3483.  *      without blocking the message queue.
  3484.  *
  3485.  *      NOTE: This function is a bit expensive because
  3486.  *      it creates a temporary object window. If you
  3487.  *      need to sleep several times, you should rather
  3488.  *      use a private timer.
  3489.  *
  3490.  *@@added V0.9.4 (2000-07-11) [umoeller]
  3491.  *@@changed V0.9.9 (2001-03-11) [umoeller]: rewritten
  3492.  */
  3493.  
  3494. VOID winhSleep(ULONG ulSleep)    // in: sleep time in milliseconds
  3495. {
  3496.     HWND    hwnd;
  3497.  
  3498.     if (hwnd = winhCreateObjectWindow(WC_STATIC, NULL))
  3499.     {
  3500.         QMSG    qmsg;
  3501.         HAB     hab;
  3502.  
  3503.         if (    (hab = WinQueryAnchorBlock(hwnd))
  3504.              && (WinStartTimer(hab,
  3505.                                hwnd,
  3506.                                1,
  3507.                                ulSleep))
  3508.            )
  3509.         {
  3510.             while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
  3511.             {
  3512.                 if (    (qmsg.hwnd == hwnd)
  3513.                      && (qmsg.msg == WM_TIMER)
  3514.                      && (qmsg.mp1 == (MPARAM)1)     // timer ID
  3515.                    )
  3516.                     break;
  3517.  
  3518.                 WinDispatchMsg(hab, &qmsg);
  3519.             }
  3520.             WinStopTimer(hab,
  3521.                          hwnd,
  3522.                          1);
  3523.         }
  3524.         else
  3525.             // timer creation failed:
  3526.             DosSleep(ulSleep);
  3527.  
  3528.         WinDestroyWindow(hwnd);
  3529.     }
  3530.     else
  3531.         DosSleep(ulSleep);
  3532. }
  3533.  
  3534. /*
  3535.  *@@ winhFileDlg:
  3536.  *      one-short function for opening an "Open" file
  3537.  *      dialog.
  3538.  *
  3539.  *      On input, pszFile specifies the directory and
  3540.  *      file specification (e.g. "F:\*.txt").
  3541.  *
  3542.  *      Returns TRUE if the user pressed OK. In that
  3543.  *      case, the fully qualified filename is written
  3544.  *      into pszFile again.
  3545.  *
  3546.  *      Returns FALSE if the user pressed Cancel.
  3547.  *
  3548.  *      Notes about flFlags:
  3549.  *
  3550.  *      -- WINH_FOD_SAVEDLG: display a "Save As" dialog.
  3551.  *         Otherwise an "Open" dialog is displayed.
  3552.  *
  3553.  *      -- WINH_FOD_INILOADDIR: load a directory from the
  3554.  *         specified INI key and switch the dlg to it.
  3555.  *         In that case, on input, pszFile must only
  3556.  *         contain the file filter without any path
  3557.  *         specification, because that is loaded from
  3558.  *         the INI key. If the INI key does not exist,
  3559.  *         the current process directory will be used.
  3560.  *
  3561.  *      -- WINH_FOD_INISAVEDIR: if the user presses OK,
  3562.  *         the directory of the selected file is written
  3563.  *         to the specified INI key so that it can be
  3564.  *         reused later. This flag is independent of
  3565.  *         WINH_FOD_INISAVEDIR: you can specify none,
  3566.  *         one, or both of them.
  3567.  *
  3568.  *@@added V0.9.3 (2000-04-29) [umoeller]
  3569.  *@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
  3570.  */
  3571.  
  3572. BOOL winhFileDlg(HWND hwndOwner,    // in: owner for file dlg
  3573.                  PSZ pszFile,       // in: file mask; out: fully q'd filename
  3574.                                     //    (should be CCHMAXPATH in size)
  3575.                  ULONG flFlags,     // in: any combination of the following:
  3576.                                     // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
  3577.                                     // -- WINH_FOD_INILOADDIR: load FOD path from INI
  3578.                                     // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
  3579.                  HINI hini,         // in: INI file to load/store last path from (can be HINI_USER)
  3580.                  const char *pcszApplication, // in: INI application to load/store last path from
  3581.                  const char *pcszKey)        // in: INI key to load/store last path from
  3582. {
  3583.     FILEDLG fd;
  3584.     FILESTATUS3 fs3;
  3585.  
  3586.     memset(&fd, 0, sizeof(FILEDLG));
  3587.     fd.cbSize = sizeof(FILEDLG);
  3588.     fd.fl = FDS_CENTER;
  3589.  
  3590.     if (flFlags & WINH_FOD_SAVEDLG)
  3591.         fd.fl |= FDS_SAVEAS_DIALOG;
  3592.     else
  3593.         fd.fl |= FDS_OPEN_DIALOG;
  3594.  
  3595.     if (    (hini)
  3596.          && (flFlags & WINH_FOD_INILOADDIR)
  3597.          && (PrfQueryProfileString(hini,
  3598.                                    (PSZ)pcszApplication,
  3599.                                    (PSZ)pcszKey,
  3600.                                    "",      // default string V0.9.9 (2001-02-10) [umoeller]
  3601.                                    fd.szFullFile,
  3602.                                    sizeof(fd.szFullFile)-10)
  3603.                      > 2)
  3604.          // added these checks V0.9.12 (2001-05-21) [umoeller]
  3605.          && (!DosQueryPathInfo(fd.szFullFile,
  3606.                                FIL_STANDARD,
  3607.                                &fs3,
  3608.                                sizeof(fs3)))
  3609.          && (fs3.attrFile & FILE_DIRECTORY)
  3610.        )
  3611.     {
  3612.         // found: append "\*"
  3613.         strcat(fd.szFullFile, "\\");
  3614.         strcat(fd.szFullFile, pszFile);
  3615.     }
  3616.     else
  3617.         // default: copy pszFile
  3618.         strcpy(fd.szFullFile, pszFile);
  3619.         // fixed V0.9.12 (2001-05-21) [umoeller]
  3620.  
  3621.     if (    WinFileDlg(HWND_DESKTOP,    // parent
  3622.                        hwndOwner, // owner
  3623.                        &fd)
  3624.         && (fd.lReturn == DID_OK)
  3625.        )
  3626.     {
  3627.         // save path back?
  3628.         if (    (hini)
  3629.              && (flFlags & WINH_FOD_INISAVEDIR)
  3630.            )
  3631.         {
  3632.             // get the directory that was used
  3633.             PSZ p = strrchr(fd.szFullFile, '\\');
  3634.             if (p)
  3635.             {
  3636.                 // contains directory:
  3637.                 // copy to OS2.INI
  3638.                 PSZ pszDir = strhSubstr(fd.szFullFile, p);
  3639.                 if (pszDir)
  3640.                 {
  3641.                     PrfWriteProfileString(hini,
  3642.                                           (PSZ)pcszApplication,
  3643.                                           (PSZ)pcszKey,
  3644.                                           pszDir);
  3645.                     free(pszDir);
  3646.                 }
  3647.             }
  3648.         }
  3649.  
  3650.         strcpy(pszFile, fd.szFullFile);
  3651.  
  3652.         return (TRUE);
  3653.     }
  3654.  
  3655.     return (FALSE);
  3656. }
  3657.  
  3658. /*
  3659.  *@@ winhSetWaitPointer:
  3660.  *      this sets the mouse pointer to "Wait".
  3661.  *      Returns the previous pointer (HPOINTER),
  3662.  *      which should be stored somewhere to be
  3663.  *      restored later. Example:
  3664.  +          HPOINTER hptrOld = winhSetWaitPointer();
  3665.  +          ...
  3666.  +          WinSetPointer(HWND_DESKTOP, hptrOld);
  3667.  */
  3668.  
  3669. HPOINTER winhSetWaitPointer(VOID)
  3670. {
  3671.     HPOINTER hptr = WinQueryPointer(HWND_DESKTOP);
  3672.     WinSetPointer(HWND_DESKTOP,
  3673.                   WinQuerySysPointer(HWND_DESKTOP,
  3674.                                      SPTR_WAIT,
  3675.                                      FALSE));   // no copy
  3676.     return (hptr);
  3677. }
  3678.  
  3679. /*
  3680.  *@@ winhQueryWindowText:
  3681.  *      this returns the window text of the specified
  3682.  *      HWND in a newly allocated buffer.
  3683.  *
  3684.  *      Returns NULL on error. Use free()
  3685.  *      to free the return value.
  3686.  */
  3687.  
  3688. PSZ winhQueryWindowText(HWND hwnd)
  3689. {
  3690.     PSZ     pszText = NULL;
  3691.     ULONG   cbText = WinQueryWindowTextLength(hwnd);
  3692.                                     // additional null character
  3693.     if (cbText)
  3694.     {
  3695.         if (pszText = (PSZ)malloc(cbText + 1))
  3696.             WinQueryWindowText(hwnd,
  3697.                                cbText + 1,
  3698.                                pszText);
  3699.     }
  3700.     return (pszText);
  3701. }
  3702.  
  3703. /*
  3704.  *@@ winhSetWindowText:
  3705.  *      like WinSetWindowText, but this one accepts
  3706.  *      printf-like arguments.
  3707.  *
  3708.  *      Note that the total string is limited to
  3709.  *      1000 characters.
  3710.  *
  3711.  *@@added V0.9.16 (2001-10-08) [umoeller]
  3712.  */
  3713.  
  3714. BOOL winhSetWindowText(HWND hwnd,
  3715.                        const char *pcszFormat,
  3716.                        ...)
  3717. {
  3718.     CHAR szBuf[1000];
  3719.     va_list     args;
  3720.     int         i;
  3721.     va_start(args, pcszFormat);
  3722.     i = vsprintf(szBuf, pcszFormat, args);
  3723.     va_end(args);
  3724.  
  3725.     return (WinSetWindowText(hwnd,
  3726.                              szBuf));
  3727. }
  3728.  
  3729. /*
  3730.  *@@ winhReplaceWindowText:
  3731.  *      this is a combination of winhQueryWindowText
  3732.  *      and strhFindReplace to replace substrings in a window.
  3733.  *
  3734.  *      This is useful for filling in placeholders
  3735.  *      a la "%1" in control windows, e.g. static
  3736.  *      texts.
  3737.  *
  3738.  *      This replaces only the first occurence of
  3739.  *      pszSearch.
  3740.  *
  3741.  *      Returns TRUE only if the window exists and
  3742.  *      the search string was replaced.
  3743.  *
  3744.  *@@added V0.9.0 [umoeller]
  3745.  */
  3746.  
  3747. BOOL winhReplaceWindowText(HWND hwnd,           // in: window whose text is to be modified
  3748.                            const char *pcszSearch,       // in: search string (e.g. "%1")
  3749.                            const char *pcszReplaceWith)  // in: replacement string for pszSearch
  3750. {
  3751.     BOOL    brc = FALSE;
  3752.     PSZ     pszText = winhQueryWindowText(hwnd);
  3753.     if (pszText)
  3754.     {
  3755.         ULONG ulOfs = 0;
  3756.         if (strhFindReplace(&pszText, &ulOfs, pcszSearch, pcszReplaceWith) > 0)
  3757.         {
  3758.             WinSetWindowText(hwnd, pszText);
  3759.             brc = TRUE;
  3760.         }
  3761.         free(pszText);
  3762.     }
  3763.     return brc;
  3764. }
  3765.  
  3766. /*
  3767.  *@@ winhEnableDlgItems:
  3768.  *      this enables/disables a whole range of controls
  3769.  *      in a window by enumerating the child windows
  3770.  *      until usIDFirst is found. If so, that subwindow
  3771.  *      is enabled/disabled and all the following windows
  3772.  *      in the enumeration also, until usIDLast is found.
  3773.  *
  3774.  *      Note that this affects _all_ controls following
  3775.  *      the usIDFirst window, no matter what ID they have
  3776.  *      (even if "-1"), until usIDLast is found.
  3777.  *
  3778.  *      Returns the no. of controls which were enabled/disabled
  3779.  *      (null if none).
  3780.  *
  3781.  *@@added V0.9.0 [umoeller]
  3782.  *@@changed V0.9.1 (99-12-20) [umoeller]: renamed from winhEnableDlgItems
  3783.  */
  3784.  
  3785. ULONG winhEnableControls(HWND hwndDlg,                  // in: dialog window
  3786.                          USHORT usIDFirst,              // in: first affected control ID
  3787.                          USHORT usIDLast,               // in: last affected  control ID (inclusive)
  3788.                          BOOL fEnable)                  // in: enable or disable?
  3789. {
  3790.     HENUM   henum1 = NULLHANDLE;
  3791.     HWND    hwndThis = NULLHANDLE;
  3792.     ULONG   ulCount = 0;
  3793.  
  3794.     henum1 = WinBeginEnumWindows(hwndDlg);
  3795.     while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
  3796.     {
  3797.         USHORT usIDCheckFirst = WinQueryWindowUShort(hwndThis, QWS_ID),
  3798.                usIDCheckLast;
  3799.         if (usIDCheckFirst == usIDFirst)
  3800.         {
  3801.             WinEnableWindow(hwndThis, fEnable);
  3802.             ulCount++;
  3803.  
  3804.             while ((hwndThis = WinGetNextWindow(henum1)) != NULLHANDLE)
  3805.             {
  3806.                 WinEnableWindow(hwndThis, fEnable);
  3807.                 ulCount++;
  3808.                 usIDCheckLast = WinQueryWindowUShort(hwndThis, QWS_ID);
  3809.                 if (usIDCheckLast == usIDLast)
  3810.                     break;
  3811.             }
  3812.  
  3813.             break;  // outer loop
  3814.         }
  3815.     }
  3816.     WinEndEnumWindows(henum1);
  3817.     return (ulCount);
  3818. }
  3819.  
  3820. /*
  3821.  *@@ winhEnableControls2:
  3822.  *      like winhEnableControls, but instead this
  3823.  *      takes an array of ULONGs as input, which
  3824.  *      is assumed to contain the dialog IDs of
  3825.  *      the controls to be enabled/disabled.
  3826.  *
  3827.  *@@added V0.9.19 (2002-05-28) [umoeller]
  3828.  */
  3829.  
  3830. ULONG winhEnableControls2(HWND hwndDlg,             // in: dialog window
  3831.                           const ULONG *paulIDs,     // in: array of dialog IDs
  3832.                           ULONG cIDs,               // in: array item count (NOT array size)
  3833.                           BOOL fEnable)             // in: enable or disable?
  3834. {
  3835.     ULONG   ul,
  3836.             ulrc = 0;
  3837.     for (ul = 0;
  3838.          ul < cIDs;
  3839.          ++ul)
  3840.     {
  3841.         if (WinEnableControl(hwndDlg, paulIDs[ul], fEnable))
  3842.             ++ulrc;
  3843.     }
  3844.  
  3845.     return ulrc;
  3846. }
  3847.  
  3848. /*
  3849.  *@@ winhCreateStdWindow:
  3850.  *      much like WinCreateStdWindow, but this one
  3851.  *      allows you to have the standard window
  3852.  *      positioned automatically, using a given
  3853.  *      SWP structure (*pswpFrame).
  3854.  *
  3855.  *      The frame is created with the specified parent
  3856.  *      (usually HWND_DESKTOP), but no owner.
  3857.  *
  3858.  *      The client window is created with the frame as
  3859.  *      its parent and owner and gets an ID of FID_CLIENT.
  3860.  *
  3861.  *      Alternatively, you can set pswpFrame to NULL
  3862.  *      and specify FCF_SHELLPOSITION with flFrameCreateFlags.
  3863.  *      If you want the window to be shown, specify
  3864.  *      SWP_SHOW (and maybe SWP_ACTIVATE) in *pswpFrame.
  3865.  *
  3866.  *@@added V0.9.0 [umoeller]
  3867.  *@@changed V0.9.5 (2000-08-13) [umoeller]: flStyleClient never worked, fixed
  3868.  *@@changed V0.9.7 (2000-12-08) [umoeller]: fixed client calc for invisible window
  3869.  */
  3870.  
  3871. HWND winhCreateStdWindow(HWND hwndFrameParent,      // in: normally HWND_DESKTOP
  3872.                          PSWP pswpFrame,            // in: frame wnd pos (ptr can be NULL)
  3873.                          ULONG flFrameCreateFlags,  // in: FCF_* flags
  3874.                          ULONG ulFrameStyle,        // in: WS_* flags (e.g. WS_VISIBLE, WS_ANIMATE)
  3875.                          const char *pcszFrameTitle, // in: frame title (title bar)
  3876.                          ULONG ulResourcesID,       // in: according to FCF_* flags
  3877.                          const char *pcszClassClient, // in: client class name
  3878.                          ULONG flStyleClient,       // in: client style
  3879.                          ULONG ulID,                // in: frame window ID
  3880.                          PVOID pClientCtlData,      // in: pCtlData structure pointer for client
  3881.                          PHWND phwndClient)         // out: created client wnd
  3882. {
  3883.     FRAMECDATA  fcdata;
  3884.     HWND        hwndFrame;
  3885.     RECTL       rclClient;
  3886.  
  3887.     fcdata.cb            = sizeof(FRAMECDATA);
  3888.     fcdata.flCreateFlags = flFrameCreateFlags;
  3889.     fcdata.hmodResources = (HMODULE)NULL;
  3890.     fcdata.idResources   = ulResourcesID;
  3891.  
  3892.     /* Create the frame and client windows.  */
  3893.     hwndFrame = WinCreateWindow(hwndFrameParent,
  3894.                                 WC_FRAME,
  3895.                                 (PSZ)pcszFrameTitle,
  3896.                                 ulFrameStyle,
  3897.                                 0,0,0,0,         // size and position = 0
  3898.                                 NULLHANDLE,      // no owner
  3899.                                 HWND_TOP,        // z-order
  3900.                                 ulID,            // frame window ID
  3901.                                 &fcdata,         // frame class data
  3902.                                 NULL);           // no presparams
  3903.  
  3904.     if (hwndFrame)
  3905.     {
  3906.         *phwndClient = WinCreateWindow(hwndFrame,      // parent
  3907.                                        (PSZ)pcszClassClient, // class
  3908.                                        NULL,           // no title
  3909.                                        flStyleClient,  // style
  3910.                                        0,0,0,0,        // size and position = 0
  3911.                                        hwndFrame,      // owner
  3912.                                        HWND_BOTTOM,    // bottom z-order
  3913.                                        FID_CLIENT,     // frame window ID
  3914.                                        pClientCtlData, // class data
  3915.                                        NULL);          // no presparams
  3916.  
  3917.         if (*phwndClient)
  3918.         {
  3919.             if (pswpFrame)
  3920.             {
  3921.                 // position frame
  3922.                 WinSetWindowPos(hwndFrame,
  3923.                                 pswpFrame->hwndInsertBehind,
  3924.                                 pswpFrame->x,
  3925.                                 pswpFrame->y,
  3926.                                 pswpFrame->cx,
  3927.                                 pswpFrame->cy,
  3928.                                 pswpFrame->fl);
  3929.  
  3930.                 // position client
  3931.                 // WinQueryWindowRect(hwndFrame, &rclClient);
  3932.                 // doesn't work because it might be invisible V0.9.7 (2000-12-08) [umoeller]
  3933.                 rclClient.xLeft = 0;
  3934.                 rclClient.yBottom = 0;
  3935.                 rclClient.xRight = pswpFrame->cx;
  3936.                 rclClient.yTop = pswpFrame->cy;
  3937.                 WinCalcFrameRect(hwndFrame,
  3938.                                  &rclClient,
  3939.                                  TRUE);     // calc client from frame
  3940.                 WinSetWindowPos(*phwndClient,
  3941.                                 HWND_TOP,
  3942.                                 rclClient.xLeft,
  3943.                                 rclClient.yBottom,
  3944.                                 rclClient.xRight - rclClient.xLeft,
  3945.                                 rclClient.yTop - rclClient.yBottom,
  3946.                                 SWP_MOVE | SWP_SIZE | SWP_SHOW);
  3947.             }
  3948.         }
  3949.     }
  3950.     return (hwndFrame);
  3951. }
  3952.  
  3953. /*
  3954.  *@@ winhCreateObjectWindow:
  3955.  *      creates an object window of the specified
  3956.  *      window class, which you should have registered
  3957.  *      before calling this. pvCreateParam will be
  3958.  *      given to the window on WM_CREATE.
  3959.  *
  3960.  *      Returns the HWND of the object window or
  3961.  *      NULLHANDLE on errors.
  3962.  *
  3963.  *@@added V0.9.3 (2000-04-17) [umoeller]
  3964.  *@@changed V0.9.7 (2001-01-17) [umoeller]: made this a function from a macro
  3965.  */
  3966.  
  3967. HWND winhCreateObjectWindow(const char *pcszWindowClass,    // in: PM window class name
  3968.                             PVOID pvCreateParam)            // in: create param
  3969. {
  3970.     return (WinCreateWindow(HWND_OBJECT,
  3971.                             (PSZ)pcszWindowClass,
  3972.                             (PSZ)"",
  3973.                             0,
  3974.                             0,0,0,0,
  3975.                             0,
  3976.                             HWND_BOTTOM,
  3977.                             0,
  3978.                             pvCreateParam,
  3979.                             NULL));
  3980. }
  3981.  
  3982. /*
  3983.  *@@ winhCreateControl:
  3984.  *      creates a control with a size and position of 0.
  3985.  *
  3986.  *@@added V0.9.9 (2001-03-13) [umoeller]
  3987.  */
  3988.  
  3989. HWND winhCreateControl(HWND hwndParentAndOwner,     // in: owner and parent window
  3990.                        const char *pcszClass,       // in: window class (e.g. WC_BUTTON)
  3991.                        const char *pcszText,        // in: window title
  3992.                        ULONG ulStyle,               // in: control style
  3993.                        ULONG ulID)                  // in: control ID
  3994. {
  3995.     return (WinCreateWindow(hwndParentAndOwner,
  3996.                             (PSZ)pcszClass,
  3997.                             (PSZ)pcszText,
  3998.                             ulStyle,
  3999.                             0, 0, 0, 0,
  4000.                             hwndParentAndOwner,
  4001.                             HWND_TOP,
  4002.                             ulID,
  4003.                             NULL,
  4004.                             NULL));
  4005. }
  4006.  
  4007. /*
  4008.  *@@ winhRepaintWindows:
  4009.  *      this repaints all children of hwndParent.
  4010.  *      If this is passed as HWND_DESKTOP, the
  4011.  *      whole screen is repainted.
  4012.  *
  4013.  *@@changed V0.9.7 (2000-12-13) [umoeller]: hwndParent was never respected, fixed
  4014.  */
  4015.  
  4016. VOID winhRepaintWindows(HWND hwndParent)
  4017. {
  4018.     HWND    hwndTop;
  4019.     HENUM   henum = WinBeginEnumWindows(hwndParent);
  4020.     while ((hwndTop = WinGetNextWindow(henum)))
  4021.         if (WinIsWindowShowing(hwndTop))
  4022.             WinInvalidateRect(hwndTop, NULL, TRUE);
  4023.     WinEndEnumWindows(henum);
  4024. }
  4025.  
  4026. /*
  4027.  *@@ winhFindMsgQueue:
  4028.  *      returns the message queue which matches
  4029.  *      the given process and thread IDs. Since,
  4030.  *      per IBM definition, every thread may only
  4031.  *      have one MQ, this should be unique.
  4032.  *
  4033.  *@@added V0.9.2 (2000-03-08) [umoeller]
  4034.  */
  4035.  
  4036. HMQ winhFindMsgQueue(PID pid,           // in: process ID
  4037.                      TID tid,           // in: thread ID
  4038.                      HAB* phab)         // out: anchor block
  4039. {
  4040.     HWND    hwndThis = 0,
  4041.             rc = 0;
  4042.     HENUM   henum = WinBeginEnumWindows(HWND_OBJECT);
  4043.     while ((hwndThis = WinGetNextWindow(henum)))
  4044.     {
  4045.         CHAR    szClass[200];
  4046.         if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
  4047.         {
  4048.             if (strcmp(szClass, "#32767") == 0)
  4049.             {
  4050.                 // message queue window:
  4051.                 PID pidWin = 0;
  4052.                 TID tidWin = 0;
  4053.                 WinQueryWindowProcess(hwndThis,
  4054.                                       &pidWin,
  4055.                                       &tidWin);
  4056.                 if (    (pidWin == pid)
  4057.                      && (tidWin == tid)
  4058.                    )
  4059.                 {
  4060.                     // get HMQ from window words
  4061.                     rc = WinQueryWindowULong(hwndThis, QWL_HMQ);
  4062.                     if (rc)
  4063.                         if (phab)
  4064.                             *phab = WinQueryAnchorBlock(hwndThis);
  4065.                     break;
  4066.                 }
  4067.             }
  4068.         }
  4069.     }
  4070.     WinEndEnumWindows(henum);
  4071.  
  4072.     return (rc);
  4073. }
  4074.  
  4075. /*
  4076.  *@@ winhFindHardErrorWindow:
  4077.  *      this searches all children of HWND_OBJECT
  4078.  *      for the PM hard error windows, which are
  4079.  *      invisible most of the time. When a hard
  4080.  *      error occurs, that window is made a child
  4081.  *      of HWND_DESKTOP instead.
  4082.  *
  4083.  *      Stolen from ProgramCommander/2 (C) Roman Stangl.
  4084.  *
  4085.  *@@added V0.9.3 (2000-04-27) [umoeller]
  4086.  */
  4087.  
  4088. VOID winhFindPMErrorWindows(HWND *phwndHardError,  // out: hard error window
  4089.                             HWND *phwndSysError)   // out: system error window
  4090. {
  4091.     PID     pidObject;  // HWND_OBJECT's process and thread id
  4092.     TID     tidObject;
  4093.     PID     pidObjectChild;     // HWND_OBJECT's child window process and thread id
  4094.     TID     tidObjectChild;
  4095.     HENUM   henumObject;  // HWND_OBJECT enumeration handle
  4096.     HWND    hwndObjectChild;   // Window handle of current HWND_OBJECT child
  4097.     UCHAR   ucClassName[32];  // Window class e.g. #1 for WC_FRAME
  4098.     CLASSINFO classinfoWindow;  // Class info of current HWND_OBJECT child
  4099.  
  4100.     *phwndHardError = NULLHANDLE;
  4101.     *phwndSysError = NULLHANDLE;
  4102.  
  4103.     // query HWND_OBJECT's window process
  4104.     WinQueryWindowProcess(WinQueryObjectWindow(HWND_DESKTOP), &pidObject, &tidObject);
  4105.     // enumerate all child windows of HWND_OBJECT
  4106.     henumObject = WinBeginEnumWindows(HWND_OBJECT);
  4107.     while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
  4108.     {
  4109.         // see if the current HWND_OBJECT child window runs in the
  4110.         // process of HWND_OBJECT (PM)
  4111.         WinQueryWindowProcess(hwndObjectChild, &pidObjectChild, &tidObjectChild);
  4112.         if (pidObject == pidObjectChild)
  4113.         {
  4114.             // get the child window's data
  4115.             WinQueryClassName(hwndObjectChild,
  4116.                               sizeof(ucClassName),
  4117.                               (PCH)ucClassName);
  4118.             WinQueryClassInfo(WinQueryAnchorBlock(hwndObjectChild),
  4119.                               (PSZ)ucClassName,
  4120.                               &classinfoWindow);
  4121.             if (    (!strcmp((PSZ)ucClassName, "#1")
  4122.                  || (classinfoWindow.flClassStyle & CS_FRAME))
  4123.                )
  4124.             {
  4125.                 // if the child window is a frame window and running in
  4126.                 // HWND_OBJECT's (PM's) window process, it must be the
  4127.                 // PM Hard Error or System Error window
  4128.                 WinQueryClassName(WinWindowFromID(hwndObjectChild,
  4129.                                                   FID_CLIENT),
  4130.                                   sizeof(ucClassName),
  4131.                                   (PSZ)ucClassName);
  4132.                 if (!strcmp((PSZ)ucClassName, "PM Hard Error"))
  4133.                 {
  4134.                     *phwndHardError = hwndObjectChild;
  4135.                     if (*phwndSysError)
  4136.                         // we found the other one already:
  4137.                         // stop searching, we got both
  4138.                         break;
  4139.                 }
  4140.                 else
  4141.                 {
  4142.                     printf("Utility: Found System Error %08X\n", (int)hwndObjectChild);
  4143.                     *phwndSysError = hwndObjectChild;
  4144.                     if (*phwndHardError)
  4145.                         // we found the other one already:
  4146.                         // stop searching, we got both
  4147.                         break;
  4148.                 }
  4149.             }
  4150.         } // end if (pidObject == pidObjectChild)
  4151.     } // end while ((hwndObjectChild = WinGetNextWindow(henumObject)) != NULLHANDLE)
  4152.     WinEndEnumWindows(henumObject);
  4153. }
  4154.  
  4155. /*
  4156.  *@@ winhCreateFakeDesktop:
  4157.  *      this routine creates and displays a frameless window over
  4158.  *      the whole screen in the color of PM's Desktop to fool the
  4159.  *      user that all windows have been closed (which in fact might
  4160.  *      not be the case).
  4161.  *
  4162.  *      This window's background color is set to the Desktop's
  4163.  *      (PM's one, not the WPS's one).
  4164.  *
  4165.  *      Returns the HWND of this window.
  4166.  */
  4167.  
  4168. HWND winhCreateFakeDesktop(HWND hwndSibling)
  4169. {
  4170.     // presparam for background
  4171.     typedef struct _BACKGROUND
  4172.     {
  4173.         ULONG   cb;     // length of the aparam parameter, in bytes
  4174.         ULONG   id;     // attribute type identity
  4175.         ULONG   cb2;    // byte count of the ab parameter
  4176.         RGB     rgb;    // attribute value
  4177.     } BACKGROUND;
  4178.  
  4179.     BACKGROUND  background;
  4180.     LONG        lDesktopColor;
  4181.  
  4182.     // create fake desktop window = empty window with
  4183.     // the size of full screen
  4184.     lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
  4185.                                      SYSCLR_BACKGROUND,
  4186.                                      0);
  4187.     background.cb = sizeof(background.id)
  4188.                   + sizeof(background.cb)
  4189.                   + sizeof(background.rgb);
  4190.     background.id = PP_BACKGROUNDCOLOR;
  4191.     background.cb2 = sizeof(RGB);
  4192.     background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
  4193.     background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
  4194.     background.rgb.bRed  = (CHAR3FROMMP(lDesktopColor));
  4195.  
  4196.     return (WinCreateWindow(HWND_DESKTOP,  // parent window
  4197.                             WC_FRAME,      // class name
  4198.                             "",            // window text
  4199.                             WS_VISIBLE,    // window style
  4200.                             0, 0,          // position and size
  4201.                             WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),
  4202.                             WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),
  4203.                             NULLHANDLE,    // owner window
  4204.                             hwndSibling,   // sibling window
  4205.                             1,             // window id
  4206.                             NULL,          // control data
  4207.                             &background)); // presentation parms
  4208. }
  4209.  
  4210. /*
  4211.  *@@ winhAssertWarp4Notebook:
  4212.  *      this takes hwndDlg as a notebook dialog page and
  4213.  *      goes thru all its controls. If a control with an
  4214.  *      ID <= udIdThreshold is found, this is assumed to
  4215.  *      be a button which is to be given the BS_NOTEBOOKBUTTON
  4216.  *      style. You should therefore give all your button
  4217.  *      controls which should be moved such an ID.
  4218.  *
  4219.  *      Note that this function will now automatically
  4220.  *      find out the lowest y coordinate that was used
  4221.  *      for a non-notebook button and move all controls
  4222.  *      down accordingly. As a result, ulDownUnit must
  4223.  *      no longer be specified (V0.9.19).
  4224.  *
  4225.  *      This function is useful if you wish to create
  4226.  *      notebook pages using dlgedit.exe which are compatible
  4227.  *      with both Warp 3 and Warp 4. This should be executed
  4228.  *      in WM_INITDLG of the notebook dlg function if the app
  4229.  *      has determined that it is running on Warp 4.
  4230.  *
  4231.  *@@changed V0.9.16 (2002-02-02) [umoeller]: fixed entry fields
  4232.  *@@changed V0.9.19 (2002-04-24) [umoeller]: removed ulDownUnits
  4233.  *@@changed V0.9.19 (2002-05-02) [umoeller]: fix for combobox
  4234.  */
  4235.  
  4236. BOOL winhAssertWarp4Notebook(HWND hwndDlg,
  4237.                              USHORT usIdThreshold)  // in: ID threshold
  4238. {
  4239.     BOOL brc = FALSE;
  4240.  
  4241.     if (doshIsWarp4())
  4242.     {
  4243.         LONG    yLowest = 10000;
  4244.         HWND    hwndItem;
  4245.         HENUM   henum = 0;
  4246.         PSWP    paswp,
  4247.                 pswpThis;
  4248.         ULONG   cWindows = 0,
  4249.                 ul;
  4250.  
  4251.         BOOL    fIsVisible;
  4252.  
  4253.         if (fIsVisible = WinIsWindowVisible(hwndDlg))
  4254.             // avoid flicker
  4255.             WinEnableWindowUpdate(hwndDlg, FALSE);
  4256.  
  4257.         if (paswp = (PSWP)malloc(sizeof(SWP) * 100))
  4258.         {
  4259.             pswpThis = paswp;
  4260.  
  4261.             // loop 1: set notebook buttons, find lowest y used
  4262.             henum = WinBeginEnumWindows(hwndDlg);
  4263.             while ((hwndItem = WinGetNextWindow(henum)))
  4264.             {
  4265.                 USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
  4266.                 // _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
  4267.                 if (usId <= usIdThreshold)
  4268.                 {
  4269.                     // pushbutton to change:
  4270.                     WinSetWindowBits(hwndItem,
  4271.                                      QWL_STYLE,
  4272.                                      BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
  4273.                     brc = TRUE;
  4274.                 }
  4275.                 else
  4276.                 {
  4277.                     // no pushbutton to change:
  4278.                     CHAR szClass[10];
  4279.  
  4280.                     WinQueryWindowPos(hwndItem, pswpThis);
  4281.  
  4282.                     // special handling for entry fields
  4283.                     // V0.9.16 (2002-02-02) [umoeller]
  4284.                     WinQueryClassName(hwndItem, sizeof(szClass), szClass);
  4285.                     if (!strcmp(szClass, "#6"))
  4286.                     {
  4287.                         pswpThis->x += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER);
  4288.                         pswpThis->y += 3 * WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER);
  4289.                     }
  4290.  
  4291.                     // check lowest y
  4292.                     if (    (pswpThis->y < yLowest)
  4293.                          // ignore combobox, this will distort everything
  4294.                          // AGAIN ... sigh V0.9.19 (2002-05-02) [umoeller]
  4295.                          && (strcmp(szClass, "#2"))
  4296.                        )
  4297.                         yLowest = pswpThis->y ;
  4298.  
  4299.                     ++pswpThis;
  4300.                     if (++cWindows == 100)
  4301.                         break;
  4302.                 }
  4303.             } // end while ((hwndItem = WinGetNextWindow(henum)))
  4304.             WinEndEnumWindows(henum);
  4305.  
  4306.             // now adjust window positions
  4307.             pswpThis = paswp;
  4308.             for (ul = 0;
  4309.                  ul < cWindows;
  4310.                  ++ul, ++pswpThis)
  4311.             {
  4312.                 pswpThis->y -= (yLowest - 8);
  4313.                             // 8 is magic to match the lower border of the
  4314.                             // standard WPS notebook pages V0.9.19 (2002-04-24) [umoeller]
  4315.                 pswpThis->fl = SWP_MOVE;
  4316.             }
  4317.  
  4318.             WinSetMultWindowPos(WinQueryAnchorBlock(hwndDlg),
  4319.                                 paswp,
  4320.                                 cWindows);
  4321.  
  4322.             free(paswp);
  4323.         }
  4324.  
  4325.         if (fIsVisible)
  4326.             WinShowWindow(hwndDlg, TRUE);
  4327.     }
  4328.  
  4329.     return brc;
  4330. }
  4331.  
  4332. /*
  4333.  *@@ winhDrawFormattedText:
  4334.  *      this func takes a rectangle and draws pszText into
  4335.  *      it, breaking the words as neccessary. The line spacing
  4336.  *      is determined from the font currently selected in hps.
  4337.  *
  4338.  *      As opposed to WinDrawText, this can draw several lines
  4339.  *      at once, and format the _complete_ text according to the
  4340.  *      flCmd parameter, which is like with WinDrawText.
  4341.  *
  4342.  *      After this function returns, *prcl is modified like this:
  4343.  *
  4344.  *      -- yTop and yBottom contain the upper and lower boundaries
  4345.  *         which were needed to draw the text. This depends on
  4346.  *         whether DT_TOP etc. were specified.
  4347.  *         To get the height of the rectangle used, calculate the
  4348.  *         delta between yTop and yBottom.
  4349.  *
  4350.  *      -- xLeft and xRight are modified to contain the outmost
  4351.  *         left and right coordinates which were needed to draw
  4352.  *         the text. This will be set to the longest line which
  4353.  *         was encountered.
  4354.  *
  4355.  *      You can specify DT_QUERYEXTENT with flDraw to only have
  4356.  *      these text boundaries calculated without actually drawing.
  4357.  *
  4358.  *      This returns the number of lines drawn.
  4359.  *
  4360.  *      Note that this calls WinDrawText with DT_TEXTATTRS set,
  4361.  *      that is, the current text primitive attributes will be
  4362.  *      used (fonts and colors).
  4363.  *
  4364.  *@@changed V0.9.0 [umoeller]: prcl.xLeft and xRight are now updated too upon return
  4365.  */
  4366.  
  4367. ULONG winhDrawFormattedText(HPS hps,     // in: presentation space; its settings
  4368.                                          // are used, but not altered
  4369.                             PRECTL prcl, // in/out: rectangle to use for drawing
  4370.                                          // (modified)
  4371.                             const char *pcszText, // in: text to draw (zero-terminated)
  4372.                             ULONG flCmd) // in: flags like in WinDrawText; I have
  4373.                                          // only tested DT_TOP and DT_LEFT though.
  4374.                                          // DT_WORDBREAK | DT_TEXTATTRS are always
  4375.                                          // set.
  4376.                                          // You can specify DT_QUERYEXTENT to only
  4377.                                          // have prcl calculated without drawing.
  4378. {
  4379.     PSZ     p = (PSZ)pcszText;
  4380.     LONG    lDrawn = 1,
  4381.             lTotalDrawn = 0,
  4382.             lLineCount = 0,
  4383.             lOrigYTop = prcl->yTop;
  4384.     ULONG   ulTextLen = strlen(pcszText),
  4385.             ulCharHeight,
  4386.             flCmd2,
  4387.             xLeftmost = prcl->xRight,
  4388.             xRightmost = prcl->xLeft;
  4389.     RECTL   rcl2;
  4390.  
  4391.     flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
  4392.  
  4393.     ulCharHeight = gpihQueryLineSpacing(hps);
  4394.  
  4395.     while (    (lDrawn)
  4396.             && (lTotalDrawn < ulTextLen)
  4397.           )
  4398.     {
  4399.         memcpy(&rcl2, prcl, sizeof(rcl2));
  4400.         lDrawn = WinDrawText(hps,
  4401.                              ulTextLen-lTotalDrawn,
  4402.                              p,
  4403.                              &rcl2,
  4404.                              0, 0,                       // colors
  4405.                              flCmd2);
  4406.  
  4407.         // update char counters
  4408.         p += lDrawn;
  4409.         lTotalDrawn += lDrawn;
  4410.  
  4411.         // update x extents
  4412.         if (rcl2.xLeft < xLeftmost)
  4413.             xLeftmost = rcl2.xLeft;
  4414.         if (rcl2.xRight > xRightmost)
  4415.             xRightmost = rcl2.xRight;
  4416.  
  4417.         // update y for next line
  4418.         prcl->yTop -= ulCharHeight;
  4419.  
  4420.         // increase line count
  4421.         lLineCount++;
  4422.     }
  4423.     prcl->xLeft = xLeftmost;
  4424.     prcl->xRight = xRightmost;
  4425.     prcl->yBottom = prcl->yTop;
  4426.     prcl->yTop = lOrigYTop;
  4427.  
  4428.     return (lLineCount);
  4429. }
  4430.  
  4431. /*
  4432.  *@@ winhQuerySwitchList:
  4433.  *      returns the switch list in a newly
  4434.  *      allocated buffer. This does the
  4435.  *      regular double WinQuerySwitchList
  4436.  *      call to first get the no. of items
  4437.  *      and then get the items.
  4438.  *
  4439.  *      The no. of items can be found in
  4440.  *      the returned SWBLOCK.cwsentry.
  4441.  *
  4442.  *      Returns NULL on errors. Use
  4443.  *      free() to free the return value.
  4444.  *
  4445.  *@@added V0.9.7 (2000-12-06) [umoeller]
  4446.  */
  4447.  
  4448. PSWBLOCK winhQuerySwitchList(HAB hab)
  4449. {
  4450.     ULONG   cItems = WinQuerySwitchList(hab, NULL, 0);
  4451.     ULONG   ulBufSize = (cItems * sizeof(SWENTRY)) + sizeof(HSWITCH);
  4452.     PSWBLOCK pSwBlock = (PSWBLOCK)malloc(ulBufSize);
  4453.     if (pSwBlock)
  4454.     {
  4455.         cItems = WinQuerySwitchList(hab, pSwBlock, ulBufSize);
  4456.         if (!cItems)
  4457.         {
  4458.             free(pSwBlock);
  4459.             pSwBlock = NULL;
  4460.         }
  4461.     }
  4462.  
  4463.     return (pSwBlock);
  4464. }
  4465.  
  4466. typedef HSWITCH APIENTRY WINHSWITCHFROMHAPP(HAPP happ);
  4467.  
  4468. /*
  4469.  *@@ winhHSWITCHfromHAPP:
  4470.  *      resolves and calls the undocumented
  4471.  *      WinHSWITCHfromHAPP API.
  4472.  *
  4473.  *@@added V0.9.19 (2002-04-17) [umoeller]
  4474.  */
  4475.  
  4476. HSWITCH winhHSWITCHfromHAPP(HAPP happ)
  4477. {
  4478.     static WINHSWITCHFROMHAPP *pWinHSWITCHfromHAPP = NULL;
  4479.  
  4480.     if (!pWinHSWITCHfromHAPP)
  4481.         // first call: import WinHSWITCHfromHAPP
  4482.         // WinHSWITCHfromHAPP PMMERGE.5199
  4483.         doshQueryProcAddr("PMMERGE",
  4484.                           5199,
  4485.                           (PFN*)&pWinHSWITCHfromHAPP);
  4486.  
  4487.     if (pWinHSWITCHfromHAPP)
  4488.         return pWinHSWITCHfromHAPP(happ);
  4489.  
  4490.     return NULLHANDLE;
  4491. }
  4492.  
  4493. /*
  4494.  *@@ winhQueryTasklistWindow:
  4495.  *      returns the window handle of the PM task list.
  4496.  *
  4497.  *@@added V0.9.7 (2000-12-07) [umoeller]
  4498.  */
  4499.  
  4500. HWND winhQueryTasklistWindow(VOID)
  4501. {
  4502.     SWBLOCK  swblock;
  4503.     // HWND     hwndTasklist = winhQueryTasklistWindow();
  4504.     // the tasklist has entry #0 in the SWBLOCK
  4505.     WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
  4506.     return (swblock.aswentry[0].swctl.hwnd);
  4507. }
  4508.  
  4509. /*
  4510.  *@@ winhKillTasklist:
  4511.  *      this will destroy the Tasklist (window list) window.
  4512.  *      Note: you will only be able to get it back after a
  4513.  *      reboot, not a WPS restart. Only for use at shutdown and such.
  4514.  *      This trick by Uri J. Stern at
  4515.  *      http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
  4516.  */
  4517.  
  4518. VOID winhKillTasklist(VOID)
  4519. {
  4520.     HWND     hwndTasklist = winhQueryTasklistWindow();
  4521.     WinPostMsg(hwndTasklist,
  4522.                0x0454,     // undocumented msg for killing tasklist
  4523.                NULL, NULL);
  4524. }
  4525.  
  4526. // the following must be added for EMX (99-10-22) [umoeller]
  4527. #ifndef NERR_BufTooSmall
  4528.       #define NERR_BASE       2100
  4529.       #define NERR_BufTooSmall        (NERR_BASE+23)
  4530.             // the API return buffer is too small
  4531. #endif
  4532.  
  4533. /*
  4534.  *@@ winhQueryPendingSpoolJobs:
  4535.  *      returns the number of pending print jobs in the spooler
  4536.  *      or 0 if none. Useful for testing before shutdown.
  4537.  */
  4538.  
  4539. ULONG winhQueryPendingSpoolJobs(VOID)
  4540. {
  4541.     // BOOL    rcPending = FALSE;
  4542.     ULONG       ulTotalJobCount = 0;
  4543.  
  4544.     SPLERR      splerr;
  4545.     USHORT      jobCount;
  4546.     ULONG       cbBuf;
  4547.     ULONG       cTotal;
  4548.     ULONG       cReturned;
  4549.     ULONG       cbNeeded;
  4550.     ULONG       ulLevel;
  4551.     ULONG       i,j;
  4552.     PSZ         pszComputerName;
  4553.     PBYTE       pBuf = NULL;
  4554.     PPRQINFO3   prq;
  4555.     PPRJINFO2   prj2;
  4556.  
  4557.     ulLevel = 4L;
  4558.     pszComputerName = (PSZ)NULL;
  4559.     splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
  4560.                           &cReturned, &cTotal,
  4561.                           &cbNeeded, NULL);
  4562.     if (    (splerr == ERROR_MORE_DATA)
  4563.          || (splerr == NERR_BufTooSmall)
  4564.        )
  4565.     {
  4566.         if (!DosAllocMem((PPVOID)&pBuf,
  4567.                          cbNeeded,
  4568.                          PAG_READ | PAG_WRITE | PAG_COMMIT))
  4569.         {
  4570.             cbBuf = cbNeeded;
  4571.             splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
  4572.                                   &cReturned, &cTotal,
  4573.                                   &cbNeeded, NULL);
  4574.             if (splerr == NO_ERROR)
  4575.             {
  4576.                 // set pointer to point to the beginning of the buffer
  4577.                 prq = (PPRQINFO3)pBuf;
  4578.  
  4579.                 // cReturned has the count of the number of PRQINFO3 structures
  4580.                 for (i = 0;
  4581.                      i < cReturned;
  4582.                      i++)
  4583.                 {
  4584.                     // save the count of jobs; there are this many PRJINFO2
  4585.                     // structures following the PRQINFO3 structure
  4586.                     jobCount = prq->cJobs;
  4587.                     // _Pmpf(( "Job count in this queue is %d",jobCount ));
  4588.  
  4589.                     // increment the pointer past the PRQINFO3 structure
  4590.                     prq++;
  4591.  
  4592.                     // set a pointer to point to the first PRJINFO2 structure
  4593.                     prj2=(PPRJINFO2)prq;
  4594.                     for (j = 0;
  4595.                          j < jobCount;
  4596.                          j++)
  4597.                     {
  4598.                         // increment the pointer to point to the next structure
  4599.                         prj2++;
  4600.                         // increase the job count, which we'll return
  4601.                         ulTotalJobCount++;
  4602.  
  4603.                     } // endfor jobCount
  4604.  
  4605.                     // after doing all the job structures, prj2 points to the next
  4606.                     // queue structure; set the pointer for a PRQINFO3 structure
  4607.                     prq = (PPRQINFO3)prj2;
  4608.                 } //endfor cReturned
  4609.             } // endif NO_ERROR
  4610.             DosFreeMem(pBuf);
  4611.         }
  4612.     } // end if Q level given
  4613.  
  4614.     return (ulTotalJobCount);
  4615. }
  4616.  
  4617. /*
  4618.  *@@ winhSetNumLock:
  4619.  *      this sets the NumLock key on or off, depending
  4620.  *      on fState.
  4621.  *
  4622.  *      Based on code from WarpEnhancer, (C) Achim Hasenmüller.
  4623.  *
  4624.  *@@added V0.9.1 (99-12-18) [umoeller]
  4625.  */
  4626.  
  4627. VOID winhSetNumLock(BOOL fState)
  4628. {
  4629.     // BOOL  fRestoreKBD = FALSE;  //  Assume we're not going to close Kbd
  4630.     BYTE KeyStateTable[256];
  4631.     ULONG ulActionTaken; //  Used by DosOpen
  4632.     HFILE hKbd;
  4633.  
  4634.     // read keyboard state table
  4635.     if (WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0],
  4636.                                  FALSE))
  4637.     {
  4638.         // first set the PM state
  4639.         if (fState)
  4640.             KeyStateTable[VK_NUMLOCK] |= 0x01; //  Turn numlock on
  4641.         else
  4642.             KeyStateTable[VK_NUMLOCK] &= 0xFE; //  Turn numlock off
  4643.  
  4644.         // set keyboard state table with new state values
  4645.         WinSetKeyboardStateTable(HWND_DESKTOP, &KeyStateTable[0], TRUE);
  4646.     }
  4647.  
  4648.     // now set the OS/2 keyboard state
  4649.  
  4650.     // try to open OS/2 keyboard driver
  4651.     if (!DosOpen("KBD$",
  4652.                  &hKbd, &ulActionTaken,
  4653.                  0,     // cbFile
  4654.                  FILE_NORMAL,
  4655.                  OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
  4656.                  OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
  4657.                  NULL))
  4658.     {
  4659.         SHIFTSTATE ShiftState;
  4660.         ULONG DataLen = sizeof(SHIFTSTATE);
  4661.  
  4662.         memset(&ShiftState, '\0', DataLen);
  4663.         DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
  4664.                     NULL, 0L, NULL,
  4665.                     &ShiftState, DataLen, &DataLen);
  4666.  
  4667.         if (fState)
  4668.             ShiftState.fsState |= 0x0020; // turn NumLock on
  4669.         else
  4670.             ShiftState.fsState &= 0xFFDF; // turn NumLock off
  4671.  
  4672.         DosDevIOCtl(hKbd, IOCTL_KEYBOARD, KBD_SETSHIFTSTATE,
  4673.                     &ShiftState, DataLen, &DataLen,
  4674.                     NULL, 0L, NULL);
  4675.         // now close OS/2 keyboard driver
  4676.         DosClose(hKbd);
  4677.     }
  4678.     return;
  4679. }
  4680.  
  4681. /*
  4682.  *@@category: Helpers\PM helpers\Extended frame windows
  4683.  */
  4684.  
  4685. /* ******************************************************************
  4686.  *
  4687.  *   Extended frame
  4688.  *
  4689.  ********************************************************************/
  4690.  
  4691. /*
  4692.  *@@ fnwpSubclExtFrame:
  4693.  *      subclassed frame window proc.
  4694.  *
  4695.  *@@added V0.9.16 (2001-09-29) [umoeller]
  4696.  */
  4697.  
  4698. MRESULT EXPENTRY fnwpSubclExtFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
  4699. {
  4700.     MRESULT mrc = 0;
  4701.  
  4702.     PEXTFRAMEDATA pData = (PEXTFRAMEDATA)WinQueryWindowPtr(hwndFrame, QWL_USER);
  4703.  
  4704.     switch (msg)
  4705.     {
  4706.         case WM_QUERYFRAMECTLCOUNT:
  4707.         {
  4708.             // query the standard frame controls count
  4709.             ULONG ulrc = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
  4710.  
  4711.             // if we have a status bar, increment the count
  4712.             ulrc++;
  4713.  
  4714.             mrc = (MPARAM)ulrc;
  4715.         }
  4716.         break;
  4717.  
  4718.         case WM_FORMATFRAME:
  4719.         {
  4720.             // query the number of standard frame controls
  4721.             ULONG ulCount = (ULONG)pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
  4722.  
  4723.             // we have a status bar:
  4724.             // format the frame
  4725.             ULONG       ul;
  4726.             PSWP        swpArr = (PSWP)mp1;
  4727.  
  4728.             for (ul = 0; ul < ulCount; ul++)
  4729.             {
  4730.                 if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
  4731.                                                                  // FID_CLIENT
  4732.                 {
  4733.                     POINTL      ptlBorderSizes;
  4734.                     ULONG       ulStatusBarHeight = 20;
  4735.                     WinSendMsg(hwndFrame,
  4736.                                WM_QUERYBORDERSIZE,
  4737.                                (MPARAM)&ptlBorderSizes,
  4738.                                0);
  4739.  
  4740.                     // first initialize the _new_ SWP for the status bar.
  4741.                     // Since the SWP array for the std frame controls is
  4742.                     // zero-based, and the standard frame controls occupy
  4743.                     // indices 0 thru ulCount-1 (where ulCount is the total
  4744.                     // count), we use ulCount for our static text control.
  4745.                     swpArr[ulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
  4746.                     swpArr[ulCount].x  = ptlBorderSizes.x;
  4747.                     swpArr[ulCount].y  = ptlBorderSizes.y;
  4748.                     swpArr[ulCount].cx = swpArr[ul].cx;  // same as cnr's width
  4749.                     swpArr[ulCount].cy = ulStatusBarHeight;
  4750.                     swpArr[ulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
  4751.                     swpArr[ulCount].hwnd = WinWindowFromID(hwndFrame, FID_STATUSBAR);
  4752.  
  4753.                     // adjust the origin and height of the container to
  4754.                     // accomodate our static text control
  4755.                     swpArr[ul].y  += swpArr[ulCount].cy;
  4756.                     swpArr[ul].cy -= swpArr[ulCount].cy;
  4757.                 }
  4758.             }
  4759.  
  4760.             // increment the number of frame controls
  4761.             // to include our status bar
  4762.             mrc = (MRESULT)(ulCount + 1);
  4763.         }
  4764.         break;
  4765.  
  4766.         case WM_CALCFRAMERECT:
  4767.         {
  4768.             mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
  4769.  
  4770.             // we have a status bar: calculate its rectangle
  4771.             // CalcFrameRect(mp1, mp2);
  4772.         }
  4773.         break;
  4774.  
  4775.         case WM_DESTROY:
  4776.             WinSubclassWindow(hwndFrame, pData->pfnwpOrig);
  4777.             free(pData);
  4778.             WinSetWindowPtr(hwndFrame, QWL_USER, NULL);
  4779.         break;
  4780.  
  4781.         default:
  4782.             mrc = pData->pfnwpOrig(hwndFrame, msg, mp1, mp2);
  4783.     }
  4784.  
  4785.     return mrc;
  4786. }
  4787.  
  4788. /*
  4789.  *@@ winhCreateStatusBar:
  4790.  *      creates a status bar for a frame window.
  4791.  *
  4792.  *      Normally there's no need to call this manually,
  4793.  *      this gets called by winhCreateExtStdWindow
  4794.  *      automatically.
  4795.  *
  4796.  *@@added V0.9.16 (2001-09-29) [umoeller]
  4797.  */
  4798.  
  4799. HWND winhCreateStatusBar(HWND hwndFrame,
  4800.                          HWND hwndOwner,
  4801.                          const char *pcszText,      // in: initial status bar text
  4802.                          const char *pcszFont,      // in: font to use for status bar
  4803.                          LONG lColor)               // in: foreground color for status bar
  4804. {
  4805.     // create status bar
  4806.     HWND        hwndReturn = NULLHANDLE;
  4807.     PPRESPARAMS ppp = NULL;
  4808.     winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pcszFont)+1, (PVOID)pcszFont);
  4809.     lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0);
  4810.     winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
  4811.     lColor = CLR_BLACK;
  4812.     winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor);
  4813.     hwndReturn = WinCreateWindow(hwndFrame,
  4814.                                  WC_STATIC,
  4815.                                  (PSZ)pcszText,
  4816.                                  SS_TEXT | DT_VCENTER | WS_VISIBLE,
  4817.                                  0, 0, 0, 0,
  4818.                                  hwndOwner,
  4819.                                  HWND_TOP,
  4820.                                  FID_STATUSBAR,
  4821.                                  NULL,
  4822.                                  ppp);
  4823.     free(ppp);
  4824.     return (hwndReturn);
  4825. }
  4826.  
  4827. /*
  4828.  *@@ winhCreateExtStdWindow:
  4829.  *      creates an extended frame window.
  4830.  *
  4831.  *      pData must point to an EXTFRAMECDATA structure
  4832.  *      which contains a copy of the parameters to be
  4833.  *      passed to winhCreateStdWindow. In addition,
  4834.  *      this contains the flExtFlags field, which allows
  4835.  *      you to automatically create a status bar for
  4836.  *      the window.
  4837.  *
  4838.  *      Note that we subclass the frame here and require
  4839.  *      QWL_USER for that. The frame's QWL_USER points
  4840.  *      to an EXTFRAMEDATA structure whose pUser parameter
  4841.  *      you may use for additional data, if you want to
  4842.  *      do further subclassing.
  4843.  *
  4844.  *@@added V0.9.16 (2001-09-29) [umoeller]
  4845.  */
  4846.  
  4847. HWND winhCreateExtStdWindow(PEXTFRAMECDATA pData,        // in: extended frame data
  4848.                             PHWND phwndClient)          // out: created client wnd
  4849. {
  4850.     HWND hwndFrame;
  4851.  
  4852.     if (hwndFrame = winhCreateStdWindow(HWND_DESKTOP,
  4853.                                         pData->pswpFrame,
  4854.                                         pData->flFrameCreateFlags,
  4855.                                         pData->ulFrameStyle,
  4856.                                         pData->pcszFrameTitle,
  4857.                                         pData->ulResourcesID,
  4858.                                         pData->pcszClassClient,
  4859.                                         pData->flStyleClient,
  4860.                                         pData->ulID,
  4861.                                         pData->pClientCtlData,
  4862.                                         phwndClient))
  4863.     {
  4864.         if (pData->flExtFlags & XFCF_STATUSBAR)
  4865.         {
  4866.             // create status bar as child of the frame
  4867.             HWND hwndStatusBar = winhCreateStatusBar(hwndFrame,
  4868.                                                      hwndFrame,
  4869.                                                      "",
  4870.                                                      "9.WarpSans",
  4871.                                                      CLR_BLACK);
  4872.  
  4873.             // subclass frame for supporting status bar and msgs
  4874.             PEXTFRAMEDATA pFrameData;
  4875.             if (pFrameData = NEW(EXTFRAMEDATA))
  4876.             {
  4877.                 ZERO(pFrameData),
  4878.                 memcpy(&pFrameData->CData, pData, sizeof(pFrameData->CData));
  4879.                 if (pFrameData->pfnwpOrig = WinSubclassWindow(hwndFrame,
  4880.                                                               fnwpSubclExtFrame))
  4881.                 {
  4882.                     WinSetWindowPtr(hwndFrame, QWL_USER, pFrameData);
  4883.                 }
  4884.                 else
  4885.                     free(pFrameData);
  4886.             }
  4887.         }
  4888.     }
  4889.  
  4890.     return (hwndFrame);
  4891. }
  4892.  
  4893. /*
  4894.  *@@category: Helpers\PM helpers\Workplace Shell\WPS class list
  4895.  */
  4896.  
  4897. /* ******************************************************************
  4898.  *
  4899.  *   WPS Class List helpers
  4900.  *
  4901.  ********************************************************************/
  4902.  
  4903. /*
  4904.  *@@ winhQueryWPSClassList:
  4905.  *      this returns the WPS class list in a newly
  4906.  *      allocated buffer. This is just a shortcut to
  4907.  *      the usual double WinEnumObjectClasses call.
  4908.  *
  4909.  *      The return value is actually of the POBJCLASS type,
  4910.  *      so you better cast this manually. We declare this
  4911.  *      this as PBYTE though because POBJCLASS requires
  4912.  *      INCL_WINWORKPLACE.
  4913.  *      See WinEnumObjectClasses() for details.
  4914.  *
  4915.  *      Returns NULL on error. Use free()
  4916.  *      to free the return value.
  4917.  *
  4918.  *@@added V0.9.0 [umoeller]
  4919.  */
  4920.  
  4921. PBYTE winhQueryWPSClassList(VOID)
  4922. {
  4923.     ULONG       ulSize;
  4924.     POBJCLASS   pObjClass = 0;
  4925.  
  4926.     // get WPS class list size
  4927.     if (WinEnumObjectClasses(NULL, &ulSize))
  4928.     {
  4929.         // allocate buffer
  4930.         pObjClass = (POBJCLASS)malloc(ulSize+1);
  4931.         // and load the classes into it
  4932.         WinEnumObjectClasses(pObjClass, &ulSize);
  4933.     }
  4934.  
  4935.     return ((PBYTE)pObjClass);
  4936. }
  4937.  
  4938. /*
  4939.  *@@ winhQueryWPSClass:
  4940.  *      this returns the POBJCLASS item if pszClass is registered
  4941.  *      with the WPS or NULL if the class could not be found.
  4942.  *
  4943.  *      The return value is actually of the POBJCLASS type,
  4944.  *      so you better cast this manually. We declare this
  4945.  *      this as PBYTE though because POBJCLASS requires
  4946.  *      INCL_WINWORKPLACE.
  4947.  *
  4948.  *      This takes as input the return value of winhQueryWPSClassList,
  4949.  *      which you must call first.
  4950.  *
  4951.  *      <B>Usage:</B>
  4952.  +          PBYTE   pClassList = winhQueryWPSClassList(),
  4953.  +                  pWPFolder;
  4954.  +          if (pClassList)
  4955.  +          {
  4956.  +              if (pWPFolder = winhQueryWPSClass(pClassList, "WPFolder"))
  4957.  +                  ...
  4958.  +              free(pClassList);
  4959.  +          }
  4960.  *
  4961.  *@@added V0.9.0 [umoeller]
  4962.  */
  4963.  
  4964. PBYTE winhQueryWPSClass(PBYTE pObjClass,  // in: buffer returned by
  4965.                                           // winhQueryWPSClassList
  4966.                         const char *pszClass)     // in: class name to query
  4967. {
  4968.     PBYTE   pbReturn = 0;
  4969.  
  4970.     POBJCLASS pocThis = (POBJCLASS)pObjClass;
  4971.     // now go thru the WPS class list
  4972.     while (pocThis)
  4973.     {
  4974.         if (strcmp(pocThis->pszClassName, pszClass) == 0)
  4975.         {
  4976.             pbReturn = (PBYTE)pocThis;
  4977.             break;
  4978.         }
  4979.         // next class
  4980.         pocThis = pocThis->pNext;
  4981.     } // end while (pocThis)
  4982.  
  4983.     return (pbReturn);
  4984. }
  4985.  
  4986. /*
  4987.  *@@ winhRegisterClass:
  4988.  *      this works just like WinRegisterObjectClass,
  4989.  *      except that it returns a more meaningful
  4990.  *      error code than just FALSE in case registering
  4991.  *      fails.
  4992.  *
  4993.  *      This returns NO_ERROR if the class was successfully
  4994.  *      registered (WinRegisterObjectClass returned TRUE).
  4995.  *
  4996.  *      Otherwise, we do a DosLoadModule if maybe the DLL
  4997.  *      couldn't be loaded in the first place. If DosLoadModule
  4998.  *      did not return NO_ERROR, this function returns that
  4999.  *      return code, which can be:
  5000.  *
  5001.  *      --  2   ERROR_FILE_NOT_FOUND: pcszModule does not exist
  5002.  *      --  2   ERROR_FILE_NOT_FOUND
  5003.  *      --  3   ERROR_PATH_NOT_FOUND
  5004.  *      --  4   ERROR_TOO_MANY_OPEN_FILES
  5005.  *      --  5   ERROR_ACCESS_DENIED
  5006.  *      --  8   ERROR_NOT_ENOUGH_MEMORY
  5007.  *      --  11  ERROR_BAD_FORMAT
  5008.  *      --  26  ERROR_NOT_DOS_DISK (unknown media type)
  5009.  *      --  32  ERROR_SHARING_VIOLATION
  5010.  *      --  33  ERROR_LOCK_VIOLATION
  5011.  *      --  36  ERROR_SHARING_BUFFER_EXCEEDED
  5012.  *      --  95  ERROR_INTERRUPT (interrupted system call)
  5013.  *      --  108 ERROR_DRIVE_LOCKED (by another process)
  5014.  *      --  123 ERROR_INVALID_NAME (illegal character or FS name not valid)
  5015.  *      --  127 ERROR_PROC_NOT_FOUND (DosQueryProcAddr error)
  5016.  *      --  180 ERROR_INVALID_SEGMENT_NUMBER
  5017.  *      --  182 ERROR_INVALID_ORDINAL
  5018.  *      --  190 ERROR_INVALID_MODULETYPE (probably an application)
  5019.  *      --  191 ERROR_INVALID_EXE_SIGNATURE (probably not LX DLL)
  5020.  *      --  192 ERROR_EXE_MARKED_INVALID (by linker)
  5021.  *      --  194 ERROR_ITERATED_DATA_EXCEEDS_64K (in a DLL segment)
  5022.  *      --  195 ERROR_INVALID_MINALLOCSIZE
  5023.  *      --  196 ERROR_DYNLINK_FROM_INVALID_RING
  5024.  *      --  198 ERROR_INVALID_SEGDPL
  5025.  *      --  199 ERROR_AUTODATASEG_EXCEEDS_64K
  5026.  *      --  201 ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT
  5027.  *      --  206 ERROR_FILENAME_EXCED_RANGE (not matching 8+3 spec)
  5028.  *      --  295 ERROR_INIT_ROUTINE_FAILED (DLL init routine failed)
  5029.  *
  5030.  *      In all these cases, pszBuf may contain a meaningful
  5031.  *      error message from DosLoadModule, especially if an import
  5032.  *      could not be resolved.
  5033.  *
  5034.  *      Still worse, if DosLoadModule returned NO_ERROR, we
  5035.  *      probably have some SOM internal error. A probable
  5036.  *      reason is that the parent class of pcszClassName
  5037.  *      is not installed, but that's WPS/SOM internal
  5038.  *      and cannot be queried from outside the WPS context.
  5039.  *
  5040.  *      In that case, ERROR_OPEN_FAILED (110) is returned.
  5041.  *      That one sounded good to me. ;-)
  5042.  */
  5043.  
  5044. APIRET winhRegisterClass(const char* pcszClassName, // in: e.g. "XFolder"
  5045.                          const char* pcszModule,    // in: e.g. "C:\XFOLDER\XFLDR.DLL"
  5046.                          PSZ pszBuf,                // out: error message from DosLoadModule
  5047.                          ULONG cbBuf)               // in: sizeof(*pszBuf), passed to DosLoadModule
  5048. {
  5049.     APIRET arc = NO_ERROR;
  5050.  
  5051.     if (!WinRegisterObjectClass((PSZ)pcszClassName, (PSZ)pcszModule))
  5052.     {
  5053.         // failed: do more error checking then, try DosLoadModule
  5054.         HMODULE hmod = NULLHANDLE;
  5055.         arc = DosLoadModule(pszBuf, cbBuf,
  5056.                             (PSZ)pcszModule,
  5057.                             &hmod);
  5058.         if (arc == NO_ERROR)
  5059.         {
  5060.             // DosLoadModule succeeded:
  5061.             // some SOM error then
  5062.             DosFreeModule(hmod);
  5063.             arc = ERROR_OPEN_FAILED;
  5064.         }
  5065.     }
  5066.     // else: ulrc still 0 (== no error)
  5067.  
  5068.     return arc;
  5069. }
  5070.  
  5071. /*
  5072.  *@@ winhIsClassRegistered:
  5073.  *      quick one-shot function which checks if
  5074.  *      a class is currently registered. Calls
  5075.  *      winhQueryWPSClassList and winhQueryWPSClass
  5076.  *      in turn.
  5077.  *
  5078.  *@@added V0.9.2 (2000-02-26) [umoeller]
  5079.  */
  5080.  
  5081. BOOL winhIsClassRegistered(const char *pcszClass)
  5082. {
  5083.     BOOL    brc = FALSE;
  5084.     PBYTE   pClassList = winhQueryWPSClassList();
  5085.     if (pClassList)
  5086.     {
  5087.         if (winhQueryWPSClass(pClassList, pcszClass))
  5088.             brc = TRUE;
  5089.         free(pClassList);
  5090.     }
  5091.  
  5092.     return brc;
  5093. }
  5094.  
  5095. /*
  5096.  *@@category: Helpers\PM helpers\Workplace Shell
  5097.  */
  5098.  
  5099. /*
  5100.  *@@ winhResetWPS:
  5101.  *      restarts the WPS using PrfReset. Returns
  5102.  *      one of the following:
  5103.  *
  5104.  *      -- 0: no error.
  5105.  *      -- 1: PrfReset failed.
  5106.  *      -- 2 or 4: PrfQueryProfile failed.
  5107.  *      -- 3: malloc() failed.
  5108.  *
  5109.  *@@added V0.9.4 (2000-07-01) [umoeller]
  5110.  */
  5111.  
  5112. ULONG winhResetWPS(HAB hab)
  5113. {
  5114.     ULONG ulrc = 0;
  5115.     // find out current profile names
  5116.     PRFPROFILE Profiles;
  5117.     Profiles.cchUserName = Profiles.cchSysName = 0;
  5118.     // first query their file name lengths
  5119.     if (PrfQueryProfile(hab, &Profiles))
  5120.     {
  5121.         // allocate memory for filenames
  5122.         Profiles.pszUserName  = (PSZ)malloc(Profiles.cchUserName);
  5123.         Profiles.pszSysName  = (PSZ)malloc(Profiles.cchSysName);
  5124.  
  5125.         if (Profiles.pszSysName)
  5126.         {
  5127.             // get filenames
  5128.             if (PrfQueryProfile(hab, &Profiles))
  5129.             {
  5130.  
  5131.                 // "change" INIs to these filenames:
  5132.                 // THIS WILL RESET THE WPS
  5133.                 if (PrfReset(hab, &Profiles) == FALSE)
  5134.                     ulrc = 1;
  5135.                 free(Profiles.pszSysName);
  5136.                 free(Profiles.pszUserName);
  5137.             }
  5138.             else
  5139.                 ulrc = 2;
  5140.         }
  5141.         else
  5142.             ulrc = 3;
  5143.     }
  5144.     else
  5145.         ulrc = 4;
  5146.  
  5147.     return (ulrc);
  5148. }
  5149.