home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xfld085s.zip / helpers / winh.c < prev    next >
C/C++ Source or Header  |  1999-02-24  |  40KB  |  1,064 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 XFolder source in any PM
  7.  *      program.
  8.  *
  9.  *      Function prefixes (new with V0.81):
  10.  *      --  winh*   Win (Presentation Manager) helper functions
  11.  *
  12.  *@@include #define INCL_WINWINDOWMGR
  13.  *@@include #define INCL_WINMESSAGEMGR
  14.  *@@include #define INCL_WINDIALOGS
  15.  *@@include #define INCL_WINSTDCNR
  16.  *@@include #define INCL_WININPUT
  17.  *@@include #define INCL_WINSYS
  18.  *@@include #define INCL_WINSHELLDATA
  19.  *@@include #include <os2.h>
  20.  *@@include #include "winh.h"
  21.  */
  22.  
  23. /*
  24.  *      Copyright (C) 1997-99 Ulrich Möller.
  25.  *      This file is part of the XFolder source package.
  26.  *      XFolder is free software; you can redistribute it and/or modify
  27.  *      it under the terms of the GNU General Public License as published
  28.  *      by the Free Software Foundation, in version 2 as it comes in the
  29.  *      "COPYING" file of the XFolder main distribution.
  30.  *      This program is distributed in the hope that it will be useful,
  31.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  32.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  33.  *      GNU General Public License for more details.
  34.  */
  35.  
  36. #define INCL_DOSDEVIOCTL
  37. #define INCL_DOS
  38. #define INCL_DOSERRORS
  39. #define INCL_WIN
  40. #define INCL_GPI
  41.  
  42. // spooler #include's
  43. #define INCL_BASE
  44. #define INCL_SPL
  45. #define INCL_SPLDOSPRINT
  46. #define INCL_SPLERRORS
  47.  
  48. #include <os2.h>
  49.  
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <stdio.h>
  53.  
  54. #include "dosh.h"
  55. #include "winh.h"
  56. #include "prfh.h"
  57. #include "gpih.h"
  58.  
  59. #include "undoc.h"
  60.  
  61. // #define _PMPRINTF_
  62. #include "pmprintf.h"
  63.  
  64. /*
  65.  *@@ winhInsertMenuItem:
  66.  *      this inserts one one menu item into a given menu.
  67.  *      Returns the return value of the MM_INSERTITEM msg:
  68.  *      --  MIT_MEMERROR:    space allocation for menu item failed
  69.  *      --  MIT_ERROR:       other error
  70.  *      --  other:           zero-based index of new item in menu.
  71.  */
  72.  
  73. SHORT winhInsertMenuItem(HWND hwndMenu,     // in:  menu to insert item into
  74.                          SHORT iPosition,   // in:  zero-based index of where to
  75.                                             //      insert or MIT_END
  76.                          SHORT sItemId,     // in:  ID of new menu item
  77.                          PSZ pszItemTitle,  // in:  title of new menu item
  78.                          SHORT afStyle,     // in:  MIS_* style flags
  79.                          SHORT afAttr)      // in:  MIA_* attribute flags
  80. {
  81.     MENUITEM mi;
  82.     SHORT    src = MIT_ERROR;
  83.  
  84.     mi.iPosition = iPosition;
  85.     mi.afStyle = afStyle;
  86.     mi.afAttribute = afAttr;
  87.     mi.id = sItemId;
  88.     mi.hwndSubMenu = 0;
  89.     mi.hItem = 0;
  90.     src = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_INSERTITEM, (MPARAM)&mi, (MPARAM)pszItemTitle));
  91.     return (src);
  92. }
  93.  
  94. /*
  95.  *@@ winhInsertSubmenu:
  96.  *      this inserts a submenu into a given menu and, if
  97.  *      sItemId != 0, inserts one item into this new submenu also.
  98.  *      Returns the HWND of the new submenu.
  99.  */
  100.  
  101. HWND winhInsertSubmenu(HWND hwndMenu,       // in: menu to add submenu to
  102.                        ULONG iPosition,     // in: index where to add submenu or MIT_END
  103.                        SHORT sMenuId,       // in: menu ID of new submenu
  104.                        PSZ pszSubmenuTitle, // in: title of new submenu
  105.                        USHORT afMenuStyle,  // in: MIS* style flags for submenu;
  106.                                             // MIS_SUBMENU will always be added
  107.                        SHORT sItemId,       // in: ID of first item to add to submenu;
  108.                                             // if 0, no first item is inserted
  109.                        PSZ pszItemTitle,    // in: title of this item
  110.                                             // (if sItemID != 0)
  111.                        USHORT afItemStyle,  // in: style flags for this item, e.g. MIS_TEXT
  112.                                             // (this is ignored if sItemID == 0)
  113.                        USHORT afAttribute)  // in: attributes for this item, e.g. MIA_DISABLED
  114.                                             // (this is ignored if sItemID == 0)
  115. {
  116.     MENUITEM mi;
  117.     SHORT    src = MIT_ERROR;
  118.     HWND     hwndNewMenu;
  119.  
  120.     // create new, empty menu
  121.     hwndNewMenu = WinCreateMenu(hwndMenu,
  122.                                 NULL); // no menu template
  123.     if (hwndNewMenu)
  124.     {
  125.         // add "submenu item" to this empty menu;
  126.         // for some reason, PM always needs submenus
  127.         // to be a menu item
  128.         mi.iPosition = iPosition;
  129.         mi.afStyle = afMenuStyle | MIS_SUBMENU;
  130.         mi.afAttribute = 0;
  131.         mi.id = sMenuId;
  132.         mi.hwndSubMenu = hwndNewMenu;
  133.         mi.hItem = 0;
  134.         src = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_INSERTITEM, (MPARAM)&mi, (MPARAM)pszSubmenuTitle));
  135.         if (    (src != MIT_MEMERROR)
  136.             &&  (src != MIT_ERROR)
  137.            )
  138.         {
  139.             // set the new menu's ID to the same as the
  140.             // submenu item
  141.             WinSetWindowUShort(hwndNewMenu, QWS_ID, sMenuId);
  142.  
  143.             if (sItemId) {
  144.                 // item id given: insert first menu item also
  145.                 mi.iPosition = 0;
  146.                 mi.afStyle = afItemStyle;
  147.                 mi.afAttribute = afAttribute;
  148.                 mi.id = sItemId;
  149.                 mi.hwndSubMenu = 0;
  150.                 mi.hItem = 0;
  151.                 WinSendMsg(hwndNewMenu,
  152.                            MM_INSERTITEM,
  153.                            (MPARAM)&mi,
  154.                            (MPARAM)pszItemTitle);
  155.             }
  156.         }
  157.     }
  158.     return (hwndNewMenu);
  159. }
  160.  
  161. /*
  162.  *@@ winhInsertMenuSeparator:
  163.  *      this inserts a separator into a given menu at
  164.  *      the given position (which may be MIT_END);
  165.  *      returns the position at which the item was
  166.  *      inserted.
  167.  */
  168.  
  169. SHORT winhInsertMenuSeparator(HWND hMenu,       // in: menu to add separator to
  170.                               SHORT iPosition,  // in: index where to add separator or MIT_END
  171.                               SHORT sId)        // in: separator menu ID (doesn't really matter)
  172. {
  173.     MENUITEM mi;
  174.     mi.iPosition = iPosition;
  175.     mi.afStyle = MIS_SEPARATOR;             // append separator
  176.     mi.afAttribute = 0;
  177.     mi.id = sId;
  178.     mi.hwndSubMenu = 0;
  179.     mi.hItem = 0;
  180.  
  181.     return (SHORT1FROMMR(WinSendMsg(hMenu,
  182.                     MM_INSERTITEM,
  183.                     (MPARAM)&mi,
  184.                     (MPARAM)"")));
  185. }
  186.  
  187. /*
  188.  *@@ winhMenuRemoveEllipse:
  189.  *      removes a "..." substring from a menu item
  190.  *      title, if found. This is useful if confirmations
  191.  *      have been turned off for a certain menu item, which
  192.  *      should be reflected in the menu.
  193.  */
  194.  
  195. VOID winhMenuRemoveEllipse(HWND hwndMenu,
  196.                            USHORT usItemId)    // in:  item to remove "..." from
  197. {
  198.     CHAR szBuf[255];
  199.     CHAR *p;
  200.     WinSendMsg(hwndMenu, MM_QUERYITEMTEXT,
  201.             MPFROM2SHORT(usItemId, sizeof(szBuf)-1),
  202.             (MPARAM)&szBuf);
  203.     if (p = strstr(szBuf, "..."))
  204.         strcpy(p, p+3);
  205.     WinSendMsg(hwndMenu, MM_SETITEMTEXT,
  206.             MPFROMSHORT(usItemId),
  207.             (MPARAM)&szBuf);
  208. }
  209.  
  210. /*
  211.  * winhSetDlgItemSpinData:
  212.  *      sets a spin button's limits and data within a dialog window.
  213.  *      This only works for decimal spin buttons.
  214.  */
  215.  
  216. VOID winhSetDlgItemSpinData(HWND hwndDlg,       // in: dlg window
  217.                            ULONG idSpinButton,  // in: item ID of spin button
  218.                            ULONG min,           // in: minimum allowed value
  219.                            ULONG max,           // in: maximum allowed value
  220.                            ULONG current)       // in: new current value
  221. {
  222.     HWND hwndSpinButton = WinWindowFromID(hwndDlg, idSpinButton);
  223.     if (hwndSpinButton) {
  224.         WinSendMsg(hwndSpinButton,
  225.                       SPBM_SETLIMITS,          // Set limits message
  226.                       (MPARAM)max,             // Spin Button maximum setting
  227.                       (MPARAM)min);             // Spin Button minimum setting
  228.  
  229.         WinSendMsg(hwndSpinButton,
  230.                       SPBM_SETCURRENTVALUE,    // Set current value message
  231.                       (MPARAM)current,
  232.                       (MPARAM)NULL);
  233.     }
  234. }
  235.  
  236. /*
  237.  *@@ winhAdjustDlgItemSpinData:
  238.  *      this can be called on a spin button control to
  239.  *      have its current data snap to a grid. This only
  240.  *      works for LONG integer values.
  241.  *
  242.  *      For example, if you specify 100 for the grid and call
  243.  *      this func after you have received SPBN_UP/DOWNARROW,
  244.  *      the spin button's value will always in/decrease
  245.  *      in steps of 100.
  246.  *
  247.  *      This returns the "snapped" value to which the spin
  248.  *      button was set.
  249.  */
  250.  
  251. LONG winhAdjustDlgItemSpinData(HWND hwndDlg,     // in: dlg window
  252.                                USHORT usItemID,  // in: item ID of spin button
  253.                                LONG lGrid,       // in: grid
  254.                                USHORT usNotifyCode) // in: SPBN_UP* or *DOWNARROW of WM_CONTROL message
  255. {
  256.     HWND hwndSpin = WinWindowFromID(hwndDlg, usItemID);
  257.     LONG lBottom, lTop, lValue;
  258.     // get value, which has already increased /
  259.     // decreased by 1
  260.     WinSendMsg(hwndSpin,
  261.           SPBM_QUERYVALUE,
  262.           (MPARAM)&lValue,
  263.           MPFROM2SHORT(0, SPBQ_ALWAYSUPDATE));
  264.  
  265.     if (    (usNotifyCode == SPBN_UPARROW)
  266.          || (usNotifyCode == SPBN_DOWNARROW)
  267.        )
  268.     {
  269.         // only if the up/down buttons were pressed,
  270.         // snap to the nearest grid; if the user
  271.         // manually enters something (SPBN_CHANGE),
  272.         // we'll accept that value
  273.         lValue = (lValue / lGrid) * lGrid;
  274.         // add /subtract grid
  275.         if (usNotifyCode == SPBN_UPARROW)
  276.             lValue += lGrid;
  277.         // else we'll have -= lGrid already
  278.  
  279.         // balance with spin button limits
  280.         WinSendMsg(hwndSpin,
  281.               SPBM_QUERYLIMITS,
  282.               (MPARAM)&lTop,
  283.               (MPARAM)&lBottom);
  284.         if (lValue < lBottom)
  285.             lValue = lTop;
  286.         else if (lValue > lTop)
  287.             lValue = lBottom;
  288.  
  289.         WinSendMsg(hwndSpin,
  290.               SPBM_SETCURRENTVALUE,
  291.               (MPARAM)(lValue),
  292.               MPNULL);
  293.     }
  294.     return (lValue);
  295. }
  296.  
  297. /*
  298.  *@@ winhCnrScrollToRecord:
  299.  *      scrolls a given container control to make a given
  300.  *      record visible.
  301.  *
  302.  *      Returns:
  303.  *      --  0:       OK, scrolled
  304.  *      --  1:       record rectangle query failed (error)
  305.  *      --  2:       cnr viewport query failed (error)
  306.  *      --  3:       record is already visible (scrolling not necessary)
  307.  *      --  4:       cnrinfo query failed (error)
  308.  *      --  5:       parent record rectangle query failed (error)
  309.  *
  310.  *      Note: All messages are _sent_ to the container, not posted.
  311.  *      Scrolling therefore occurs synchroneously before this
  312.  *      function returns.
  313.  *
  314.  *      This function an improved version of the one (W)(C) Dan Libby, found at
  315.  *      http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt6364.HTML
  316.  *      Improvements (C) 1998 Ulrich Möller.
  317.  */
  318.  
  319. ULONG winhCnrScrollToRecord(HWND hwndCnr,       // in: container window
  320.                             PRECORDCORE pRec,   // in: record to scroll to
  321.                             ULONG fsExtent,
  322.                                     // in: this determines what parts of pRec
  323.                                     // should be made visible. OR the following
  324.                                     // flags:
  325.                                     // -- CMA_ICON the icon rectangle
  326.                                     // -- CMA_TEXT the record text
  327.                                     // -- CMA_TREEICON the "+" sign in tree view
  328.                             BOOL KeepParent)
  329.                                     // for tree views only: whether to keep
  330.                                     // the parent record of pRec visible when scrolling.
  331.                                     // If scrolling to pRec would make the parent
  332.                                     // record invisible, we instead scroll so that
  333.                                     // the parent record appears at the top of the
  334.                                     // container workspace (Win95 style).
  335.  
  336. {
  337.     QUERYRECORDRECT qRect, qRect2;
  338.     RECTL           rclRecord, rclParentRecord, rclCnr, rclCnr2;
  339.     POINTL          ptlRecord, ptlParentRecord;
  340.     CNRINFO         CnrInfo;
  341.     HAB             hab = WinQueryAnchorBlock(hwndCnr);
  342.     BOOL            KeepParent2;
  343.     LONG            lYOfs;
  344.  
  345.     qRect.cb = sizeof(qRect);
  346.     qRect.pRecord = (PRECORDCORE)pRec;
  347.     qRect.fsExtent = fsExtent;
  348.  
  349.     // query record location and size of container
  350.     if (!WinSendMsg(hwndCnr, CM_QUERYRECORDRECT, &rclRecord, &qRect))
  351.         return 1;
  352.     if (!WinSendMsg(hwndCnr, CM_QUERYVIEWPORTRECT, &rclCnr, MPFROM2SHORT(CMA_WINDOW, FALSE)) )
  353.         return 2;
  354.  
  355.     // check if left bottom point of pRec is currently visible in container
  356.     ptlRecord.x = (rclRecord.xLeft);
  357.     ptlRecord.y = (rclRecord.yBottom);
  358.     // ptlRecord.x = (rclRecord.xLeft + rclRecord.xRight) / 2;
  359.     // ptlRecord.y = (rclRecord.yBottom + rclRecord.yTop) / 2;
  360.     if (WinPtInRect(hab, &rclCnr, &ptlRecord))
  361.          return 3;
  362.  
  363.     if (KeepParent)
  364.         if (!WinSendMsg(hwndCnr, CM_QUERYCNRINFO, (MPARAM)&CnrInfo, (MPARAM)sizeof(CnrInfo)))
  365.             return 4;
  366.         else KeepParent2 = (CnrInfo.flWindowAttr & CV_TREE);
  367.     else KeepParent2 = FALSE;
  368.  
  369.     // calculate offset to scroll to make pRec visible
  370.     lYOfs = (rclCnr.yBottom - rclRecord.yBottom)    // this would suffice
  371.           + (rclRecord.yTop - rclRecord.yBottom);  // but we make the next rcl visible too
  372.  
  373.     if (KeepParent2) {
  374.         qRect2.cb = sizeof(qRect2);
  375.         qRect2.pRecord = (PRECORDCORE)WinSendMsg(hwndCnr, CM_QUERYRECORD,
  376.                 (MPARAM)pRec, MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER));
  377.         qRect2.fsExtent = fsExtent;
  378.  
  379.         // now query PARENT record location and size of container
  380.         if (!WinSendMsg(hwndCnr, CM_QUERYRECORDRECT, &rclParentRecord, &qRect2))
  381.             return 5;
  382.  
  383.         ptlParentRecord.x = (rclParentRecord.xLeft);
  384.         ptlParentRecord.y = (rclParentRecord.yTop);
  385.         // ptlParentRecord.x = (rclParentRecord.xLeft + rclParentRecord.xRight) / 2;
  386.         // ptlParentRecord.y = (rclParentRecord.yBottom + rclParentRecord.yTop) / 2;
  387.         rclCnr2 = rclCnr;
  388.         WinOffsetRect(hab, &rclCnr2, 0, -lYOfs);
  389.         // if ( (rclParentRecord.yBottom - rclRecord.yBottom) > (rclCnr.yTop - rclCnr.yBottom) )
  390.         if (!(WinPtInRect(hab, &rclCnr2, &ptlParentRecord)))
  391.         {
  392.             lYOfs = (rclCnr.yTop - rclParentRecord.yTop) // this would suffice
  393.                   - (rclRecord.yTop - rclRecord.yBottom);  // but we make the previous rcl visible too
  394.         }
  395.     }
  396.  
  397.     if (!KeepParent2)
  398.         WinSendMsg(hwndCnr, CM_SCROLLWINDOW, (MPARAM)CMA_HORIZONTAL,
  399.             (MPARAM)(rclRecord.xLeft - rclCnr.xLeft));
  400.     WinSendMsg(hwndCnr, CM_SCROLLWINDOW,
  401.         (MPARAM)CMA_VERTICAL, (MPARAM)lYOfs);
  402.  
  403.     return 0;
  404. }
  405.  
  406. /*
  407.  *@@ winhCnrAllocRecord:
  408.  *      this is a shortcut to allocating memory for
  409.  *      a container record core. Returns the new recc.
  410.  */
  411.  
  412. PRECORDCORE winhCnrAllocRecord(HWND hwndCnr,    // in: cnr to allocate from
  413.                                ULONG cbrecc)
  414.                                     // in: total size of your record core;
  415.                                     // if you're using the default recc's, this
  416.                                     // must be sizeof(RECORDCORE)
  417. {
  418.     PRECORDCORE      precc;
  419.     precc = (PRECORDCORE)WinSendMsg(hwndCnr, CM_ALLOCRECORD,
  420.             (MPARAM)(cbrecc-sizeof(RECORDCORE)),
  421.             (MPARAM)1);
  422.     precc->cb = cbrecc;
  423.     return (precc);
  424. }
  425.  
  426. /*
  427.  *@@ winhCnrInsertRecord:
  428.  *      shortcut to inserting a record core which has been
  429.  *      allocated using winhCnrAllocRecord into a container.
  430.  *      flRecordAttr should have the record attributes as
  431.  *      specified in the RECORDCORE structure, which are:
  432.  *      --  CRA_SELECTED       record is selected
  433.  *      --  CRA_CURSORED       cursor (keyboard focus) is on the record
  434.  *      --  CRA_SOURCE         record has source emphasis (drag'n'drop)
  435.  *      --  CRA_TARGET         record has target emphasis (drag'n'drop)
  436.  *      --  CRA_INUSE          record has in-use emphasis
  437.  *      --  CRA_FILTERED       record has been filtered
  438.  *      --  CRA_DROPONABLE     record can be dropped something upon
  439.  *      --  CRA_RECORDREADONLY record is read-only (no text edit)
  440.  *      --  CRA_EXPANDED       record is expanded (tree view)
  441.  *      --  CRA_COLLAPSED      record is collapsed (tree view)
  442.  *      --  CRA_PICKED         record picked (Lazy Drag)
  443.  *
  444.  *      plus the following undocumented (haven't tested these):
  445.  *      --  CRA_IGNORE
  446.  *      --  CRA_DISABLED
  447.  *      --  CRA_OWNERFREE
  448.  *      --  CRA_OWNERDRAW
  449.  *
  450.  *      This func returns precc.
  451.  */
  452.  
  453. PRECORDCORE winhCnrInsertRecord(HWND hwndCnr,   // in: container to insert into
  454.                                 PRECORDCORE preccParent,
  455.                                     // in: record core below which precc should
  456.                                     // be inserted (tree view only); if NULL, precc
  457.                                     // is inserted at "root" level
  458.                                 PRECORDCORE precc,
  459.                                     // in: record core to insert (allocated using
  460.                                     // winhCnrAllocRecord)
  461.                                 PSZ pszText,
  462.                                     // in: text for recc. in all views
  463.                                 ULONG flRecordAttr)
  464.                                     // in: CRA_* flags
  465. {
  466.     RECORDINSERT     ri;
  467.  
  468.     if (precc) {
  469.         // RECORDCORE stuff
  470.         precc->flRecordAttr = flRecordAttr;
  471.         precc->preccNextRecord = NULL;
  472.         precc->pszIcon = pszText;
  473.         precc->pszText = pszText;
  474.         precc->pszName = pszText;
  475.         precc->pszTree = pszText;
  476.  
  477.         // setup RECORDINSERT struct
  478.         ri.cb = sizeof(RECORDINSERT);
  479.         ri.pRecordOrder = (PRECORDCORE)CMA_END;
  480.         ri.pRecordParent = (PRECORDCORE)preccParent;
  481.         ri.zOrder = CMA_TOP;
  482.         ri.fInvalidateRecord = TRUE;
  483.         ri.cRecordsInsert = 1;
  484.  
  485.         // printf("  Sending CM_INSERTRECORD\n");
  486.         WinSendMsg(hwndCnr, CM_INSERTRECORD, (MPARAM)precc, (MPARAM)&ri);
  487.     }
  488.     return (precc);
  489. }
  490.  
  491. /*
  492.  *@@ winhSaveWindowPos:
  493.  *      saves the position of a certain window. As opposed
  494.  *      to the barely documented WinStoreWindowPos API, this
  495.  *      one only saves one regular SWP structure for the given
  496.  *      window. It does _not_ save minimized/maximized positions,
  497.  *      presparams or anything about child windows.
  498.  *      The window should still be visible on the screen
  499.  *      when calling this function. Do not call it in WM_DESTROY,
  500.  *      because then the SWP data is no longer valid.
  501.  *      Returns TRUE if saving was successful.
  502.  */
  503.  
  504. BOOL winhSaveWindowPos(HWND hwnd,   // in: window to save
  505.                        HINI hIni,   // in: INI file (or HINI_USER/SYSTEM)
  506.                        PSZ pszApp,  // in: INI application name
  507.                        PSZ pszKey)  // in: INI key name
  508. {
  509.     BOOL brc = FALSE;
  510.     SWP swp;
  511.     if (WinQueryWindowPos(hwnd, &swp))
  512.     {
  513.         brc = PrfWriteProfileData(hIni, pszApp, pszKey, &swp, sizeof(swp));
  514.     }
  515.     return (brc);
  516. }
  517.  
  518. /*
  519.  *@@ winhRestoreWindowPos:
  520.  *      this will retrieve a window position which was
  521.  *      previously stored using winhSaveWindowPos.
  522.  *      The window should not be visible to avoid flickering.
  523.  *      "fl" must contain the SWP_flags as in WinSetWindowPos.
  524.  *      Note that only the following may be used:
  525.  *      --  SWP_MOVE        reposition the window
  526.  *      --  SWP_SIZE        also resize the window to
  527.  *                          the stored position; this might
  528.  *                          lead to problems with different
  529.  *                          video resolutions, so be careful.
  530.  *      --  SWP_SHOW        make window visible too
  531.  *      --  SWP_NOREDRAW    changes are not redrawn
  532.  *      --  SWP_NOADJUST    do not send a WM_ADJUSTWINDOWPOS message
  533.  *                          before moving or sizing
  534.  *      --  SWP_ACTIVATE    activate window (make topmost)
  535.  *      --  SWP_DEACTIVATE  deactivate window (make bottommost)
  536.  *
  537.  *      Do not specify any other SWP_* flags.
  538.  *
  539.  *      If SWP_SIZE is not set, the window will be moved only
  540.  *      (recommended).
  541.  *
  542.  *      This function automatically checks for whether the
  543.  *      window would be positioned outside the visible screen
  544.  *      area and will adjust coordinates accordingly. This can
  545.  *      happen when changing video resolutions.
  546.  */
  547.  
  548. BOOL winhRestoreWindowPos(HWND hwnd,   // in: window to save
  549.                        HINI hIni,   // in: INI file (or HINI_USER/SYSTEM)
  550.                        PSZ pszApp,  // in: INI application name
  551.                        PSZ pszKey,  // in: INI key name
  552.                        ULONG fl)    // in: "fl" parameter for WinSetWindowPos
  553. {
  554.     BOOL    brc = FALSE;
  555.     SWP     swp, swpNow;
  556.     ULONG   cbswp = sizeof(swp);
  557.     ULONG   fl2 = fl;
  558.  
  559.     if (PrfQueryProfileData(hIni, pszApp, pszKey, &swp, &cbswp))
  560.     {
  561.         ULONG ulScreenCX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  562.         ULONG ulScreenCY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  563.  
  564.         brc = TRUE;
  565.  
  566.         if ((fl & SWP_SIZE) == 0) {
  567.             // if no resize, we need to get the current position
  568.             brc = WinQueryWindowPos(hwnd, &swpNow);
  569.             swp.cx = swpNow.cx;
  570.             swp.cy = swpNow.cy;
  571.         }
  572.  
  573.         if (brc) {
  574.             // check for full visibility
  575.             if ( (swp.x + swp.cx) > ulScreenCX)
  576.                 swp.x = ulScreenCX - swp.cx;
  577.             if ( (swp.y + swp.cy) > ulScreenCY)
  578.                 swp.y = ulScreenCY - swp.cy;
  579.         }
  580.     } else {
  581.         // window pos not found in INI: unset SWP_MOVE etc.
  582.         fl2 &= ~(SWP_MOVE | SWP_SIZE);
  583.     }
  584.  
  585.     brc = WinSetWindowPos(hwnd,
  586.                     NULLHANDLE,       // insert-behind window
  587.                     swp.x,
  588.                     swp.y,
  589.                     swp.cx,
  590.                     swp.cy,
  591.                     fl2);        // SWP_* flags
  592.     return (brc);
  593. }
  594.  
  595. /*
  596.  *@@ winhCenterWindow:
  597.  *      centers a window within its parent window. If that's
  598.  *      the PM desktop, it will be centered according to the
  599.  *      whole screen.
  600.  *      For dialog boxes, use WinCenteredDlgBox as a one-shot
  601.  *      function.
  602.  *
  603.  *      Note: When calling this function, the window should
  604.  *      not be visible to avoid flickering.
  605.  *      This func does not show the window either, so call
  606.  *      WinShowWindow afterwards.
  607.  */
  608.  
  609. void winhCenterWindow(HWND hwnd)
  610. {
  611.    RECTL rclParent;
  612.    RECTL rclWindow;
  613.  
  614.    WinQueryWindowRect(hwnd, &rclWindow);
  615.    WinQueryWindowRect(WinQueryWindow(hwnd, QW_PARENT), &rclParent);
  616.  
  617.    rclWindow.xLeft   = (rclParent.xRight - rclWindow.xRight) / 2;
  618.    rclWindow.yBottom = (rclParent.yTop   - rclWindow.yTop  ) / 2;
  619.  
  620.    WinSetWindowPos(hwnd, NULLHANDLE, rclWindow.xLeft, rclWindow.yBottom,
  621.                     0, 0, SWP_MOVE);
  622. }
  623.  
  624. /*
  625.  *@@ winhCenteredDlgBox:
  626.  *      just like WinDlgBox, but the dlg box is centered on the screen;
  627.  *      you should mark the dlg template as not visible in the dlg
  628.  *      editor, or display will flicker.
  629.  *      As opposed to winhCenterWindow, this _does_ show the window.
  630.  */
  631.  
  632. ULONG winhCenteredDlgBox(HWND hwndParent, HWND hwndOwner,
  633.               PFNWP pfnDlgProc, HMODULE hmod, ULONG idDlg, PVOID pCreateParams)
  634. {
  635.     ULONG   ulReply;
  636.     HWND    hwndDlg = WinLoadDlg(hwndParent, hwndOwner, pfnDlgProc,
  637.                                 hmod, idDlg, pCreateParams);
  638.     winhCenterWindow(hwndDlg);
  639.     ulReply = WinProcessDlg(hwndDlg);
  640.     WinDestroyWindow(hwndDlg);
  641.     return (ulReply);
  642. }
  643.  
  644. /*
  645.  *@@ winhQueryPresColor:
  646.  *      returns the specified color. This is queried in the
  647.  *      following order:
  648.  *      1)  hwnd's pres params are searched for ulPP;
  649.  *      2)  if this fails, PM_Colors in OS2.INI is searched
  650.  *          for pszKeyName;
  651.  *      3)  if this fails also, pszDefault is evaluated.
  652.  *
  653.  *      The return value is always an RGB LONG.
  654.  *      The following ulPP / pszKeyName pairs are useful:
  655.  *      --  PP_FOREGROUNDCOLOR             ("WindowText")
  656.  *             Foreground color
  657.  *      --  PP_BACKGROUNDCOLOR             ("Window" or "DialogBackground")
  658.  *             Background color
  659.  *      --  PP_HILITEFOREGROUNDCOLOR       ("HiliteForeground" or "MenuHiliteText")
  660.  *             Highlighted foreground color, for example for selected menu
  661.  *      --  PP_HILITEBACKGROUNDCOLOR       ("HiliteBackground" or "MenuHilite")
  662.  *             Highlighted background color
  663.  *      --  PP_DISABLEDFOREGROUNDCOLOR
  664.  *             Disabled foreground color
  665.  *      --  PP_DISABLEDBACKGROUNDCOLOR
  666.  *             Disabled background color
  667.  *      --  PP_BORDERCOLOR                 ("WindowFrame")
  668.  *             Border color
  669.  *      --  PP_ACTIVECOLOR
  670.  *             Active color
  671.  *      --  PP_INACTIVECOLOR
  672.  *             Inactive color
  673.  *      --  PP_ACTIVETEXTFGNDCOLOR
  674.  *             Active text foreground color
  675.  *      --  PP_ACTIVETEXTBGNDCOLOR
  676.  *             Active text background color
  677.  *      --  PP_INACTIVETEXTFGNDCOLOR
  678.  *             Inactive text foreground color
  679.  *      --  PP_INACTIVETEXTBGNDCOLOR
  680.  *             Inactive text background color
  681.  */
  682.  
  683. LONG winhQueryPresColor(HWND hwnd,          // in: window to query
  684.                         ULONG ulPP,         // in: PP_* index
  685.                         PSZ pszKeyName,     // in: INI key for PM_Colors
  686.                         PSZ pszDefault)     // in: e.g. "255 255 255"
  687. {
  688.     ULONG ul, attrFound, abValue[32];
  689.     if (ulPP != (ULONG)-1)
  690.         if (ul = WinQueryPresParam(hwnd,
  691.                     ulPP,
  692.                     0,
  693.                     &attrFound,
  694.                     (ULONG)sizeof(abValue),
  695.                     (PVOID)&abValue,
  696.                     0))
  697.             return (abValue[0]);
  698.  
  699.     return (prfhQueryColor(pszKeyName, pszDefault));
  700. }
  701.  
  702. /*
  703.  *@@ winhCreateFakeDesktop:
  704.  *      this routine creates and displays a frameless window over
  705.  *      the whole screen to fool the user that all windows
  706.  *      have been closed (which in fact might not be the
  707.  *      case).
  708.  *      This window's background color is set to the Desktop's
  709.  *      (PM's one, not the WPS's one).
  710.  *      Returns the hwnd of this window.
  711.  */
  712.  
  713. HWND winhCreateFakeDesktop(HWND hwndSibling)
  714. {
  715.     typedef struct _BACKGROUND {
  716.         ULONG   cb;         //  Length of the aparam parameter, in bytes.
  717.         ULONG   id;     //  Attribute type identity.
  718.         ULONG   cb2;     //  Byte count of the ab parameter.
  719.         RGB     rgb;    //  Attribute value.
  720.      } BACKGROUND;
  721.     RECTL       R;
  722.     BACKGROUND  background;
  723.     ULONG       flStyle;          // window style
  724.     LONG        lDesktopColor;
  725.  
  726.     /* create fake desktop window = empty window with
  727.        the size of full screen */
  728.     lDesktopColor = WinQuerySysColor(HWND_DESKTOP,
  729.         SYSCLR_BACKGROUND, 0);
  730.     flStyle = WS_VISIBLE;
  731.     R.xLeft      = 0;
  732.     R.yBottom    = 0;
  733.     R.xRight     = WinQuerySysValue(HWND_DESKTOP,SV_CXSCREEN);
  734.     R.yTop       = WinQuerySysValue(HWND_DESKTOP,SV_CYSCREEN);
  735.     background.cb = sizeof(background.id)
  736.                   + sizeof(background.cb)
  737.                   + sizeof(background.rgb);
  738.     background.id = PP_BACKGROUNDCOLOR;
  739.     background.cb2 = sizeof(RGB);
  740.     background.rgb.bBlue = (CHAR1FROMMP(lDesktopColor));
  741.     background.rgb.bGreen= (CHAR2FROMMP(lDesktopColor));
  742.     background.rgb.bRed  = (CHAR3FROMMP(lDesktopColor));
  743.  
  744.     return (WinCreateWindow(HWND_DESKTOP,  // parent window
  745.                            WC_FRAME,      // class name
  746.                            "",            // window text
  747.                            flStyle,       // window style
  748.                            0, 0,          // position (x,y)
  749.                            R.xRight,
  750.                            R.yTop,
  751.                            NULLHANDLE,    // owner window
  752.                            hwndSibling,    // sibling window
  753.                            1,             // window id
  754.                            NULL,          // control data
  755.                            &background)); // presentation parms
  756. }
  757.  
  758. /*
  759.  *@@ winhQueryCnrFromFrame:
  760.  *      find the handle of a frame window's container; we do this
  761.  *      by enumerating all the frame's child windows and comparing
  762.  *      their window classes to the WC_CONTAINER code.
  763.  *      This is not terribly fast, so use this func economically.
  764.  *      Returns NULLHANDLE if not found.
  765.  */
  766.  
  767. HWND winhQueryCnrFromFrame(HWND hwndFrame)
  768. {
  769.     HENUM               henum;
  770.     CHAR                szClassName[256];
  771.     HWND                hwndCnr = NULLHANDLE,
  772.                         hwndTemp = NULLHANDLE;
  773.  
  774.     if (hwndFrame) {
  775.         henum = WinBeginEnumWindows(hwndFrame);
  776.         if (henum) {
  777.             do {
  778.                 hwndTemp = WinGetNextWindow(henum);
  779.                 if (hwndTemp) {
  780.                     if (WinQueryClassName(hwndTemp, 250, szClassName))
  781.                         if (strcmp(szClassName, "#37") == 0)
  782.                             // unbelievable, this is PM's code for WC_CONTAINER...
  783.                             hwndCnr = hwndTemp;
  784.                 }
  785.             } while (hwndTemp);
  786.         }
  787.         WinEndEnumWindows(henum);
  788.     }
  789.  
  790.     return hwndCnr;
  791. }
  792.  
  793. /*
  794.  *@@ winhAssertWarp4Notebook:
  795.  *      this takes hwndDlg as a notebook dialog page and
  796.  *      goes thru all its controls. If a control with an
  797.  *      ID <= udIdThreshold is found, this is assumed to
  798.  *      be a button which is to be given the BS_NOTEBOOKBUTTON
  799.  *      style. You should therefore give all your button
  800.  *      controls which should be moved such an ID.
  801.  *      You can also specify how many dialog units
  802.  *      all the other controls will be moved downward in
  803.  *      ulDownUnits; this is useful to fill up the space
  804.  *      which was used by the buttons before moving them.
  805.  *      Returns TRUE if anything was changed.
  806.  *
  807.  *      This function is useful if you wish to create
  808.  *      notebook pages using dlgedit.exe which are compatible
  809.  *      with both Warp 3 and Warp 4. This should be executed
  810.  *      in WM_INITDLG of the notebook dlg function if the app
  811.  *      has determined that it is running on Warp 4.
  812.  */
  813.  
  814. BOOL winhAssertWarp4Notebook(HWND hwndDlg,
  815.                     USHORT usIdThreshold,    // in: ID threshold
  816.                     ULONG ulDownUnits)       // in: dialog units or 0
  817. {
  818.     BOOL brc = FALSE;
  819.     POINTL ptl;
  820.     HAB hab = WinQueryAnchorBlock(hwndDlg);
  821.     BOOL    fIsVisible = WinIsWindowVisible(hwndDlg);
  822.     if (ulDownUnits) {
  823.         ptl.x = 0;
  824.         ptl.y = ulDownUnits;
  825.         WinMapDlgPoints(hwndDlg, &ptl, 1, TRUE);
  826.     }
  827.  
  828.     WinEnableWindowUpdate(hwndDlg, FALSE);
  829.  
  830.     if (doshIsWarp4()) {
  831.         HENUM henum = WinBeginEnumWindows(hwndDlg);
  832.         HWND hwndItem;
  833.         while (hwndItem = WinGetNextWindow(henum)) {
  834.             USHORT usId = WinQueryWindowUShort(hwndItem, QWS_ID);
  835.             _Pmpf(("hwndItem: 0x%lX, ID: 0x%lX", hwndItem, usId));
  836.             if (usId <= usIdThreshold) {
  837.                 // pushbutton to change:
  838.                 _Pmpf(("  Setting bit"));
  839.                 WinSetWindowBits(hwndItem, QWL_STYLE,
  840.                         BS_NOTEBOOKBUTTON, BS_NOTEBOOKBUTTON);
  841.                 brc = TRUE;
  842.             } else
  843.                 // no pushbutton to change: move downwards
  844.                 // if desired
  845.                 if (ulDownUnits)
  846.                 {
  847.                     SWP swp;
  848.                     _Pmpf(("Moving downwards %d pixels", ptl.y));
  849.                     WinQueryWindowPos(hwndItem, &swp);
  850.                     WinSetWindowPos(hwndItem, 0,
  851.                         swp.x,
  852.                         swp.y - ptl.y,
  853.                         0, 0,
  854.                         SWP_MOVE);
  855.                 }
  856.         }
  857.         WinEndEnumWindows(henum);
  858.     }
  859.     if (fIsVisible)
  860.         WinShowWindow(hwndDlg, TRUE);
  861.     return (brc);
  862. }
  863.  
  864. /*
  865.  *@@ winhQueryItemUnderMouse:
  866.  *      this queries the menu item which corresponds
  867.  *      to the given mouse coordinates.
  868.  *      Returns the ID of the menu item and stores its
  869.  *      rectangle in *prtlItem; returns (-1) upon errors.
  870.  */
  871.  
  872. SHORT winhQueryItemUnderMouse(HWND hwndMenu,      // in: menu handle
  873.                               POINTL *pptlMouse,  // in: mouse coordinates
  874.                               RECTL *prtlItem)    // out: rectangle of menu item
  875. {
  876.     SHORT   s, sItemId, sItemCount;
  877.     HAB     habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
  878.  
  879.     sItemCount = SHORT1FROMMR(WinSendMsg(hwndMenu, MM_QUERYITEMCOUNT, MPNULL, MPNULL));
  880.  
  881.     for (s = 0;
  882.          s <= sItemCount;
  883.          s++)
  884.     {
  885.         sItemId = SHORT1FROMMR(WinSendMsg(hwndMenu,
  886.                                           MM_ITEMIDFROMPOSITION,
  887.                                           (MPARAM)s, MPNULL));
  888.         WinSendMsg(hwndMenu, MM_QUERYITEMRECT,
  889.                 MPFROM2SHORT(sItemId, FALSE),
  890.                 (MPARAM)prtlItem);
  891.         if (WinPtInRect(habDesktop, prtlItem, pptlMouse))
  892.             return (sItemId);
  893.     }
  894.     /* sItemId = (SHORT)WinSendMsg(hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)(sItemCount-1), MPNULL);
  895.     return (sItemId); */
  896.     return (-1); // error: no valid menu item
  897. }
  898.  
  899. /*
  900.  *@@ winhDrawFormattedText:
  901.  *      this func takes a rectangle and draws pszText into
  902.  *      it, breaking the words as neccessary. The line spacing
  903.  *      is determined from the font currently selected in hps.
  904.  *      As opposed to WinDrawText, this can draw several lines
  905.  *      at once.
  906.  *      This returns the number of lines drawn.
  907.  */
  908.  
  909. ULONG winhDrawFormattedText(HPS hps,     // in: presentation space; its settings
  910.                                          // are used, but not altered
  911.                             PRECTL prcl, // in/out: rectangle to use for drawing;
  912.                                          // both y coords are modified to return
  913.                                          // the space which was used for drawing
  914.                             PSZ pszText, // in: text to draw
  915.                             ULONG flCmd) // in: flags like in WinDrawText; I have
  916.                                          // only tested DT_TOP and DT_LEFT though.
  917.                                          // DT_WORDBREAK | DT_TEXTATTRS are always
  918.                                          // set.
  919.                                          // You can specify DT_QUERYEXTENT to only
  920.                                          // have prcl calculated without drawing.
  921. {
  922.     PSZ     p = pszText;
  923.     LONG    lDrawn = 1,
  924.             lTotalDrawn = 0,
  925.             lLineCount = 0,
  926.             lOrigYTop = prcl->yTop;
  927.     ULONG   ulTextLen = strlen(pszText),
  928.             ulCharHeight,
  929.             flCmd2;
  930.     // POINTL  aptlText[TXTBOX_COUNT];
  931.     RECTL   rcl2;
  932.  
  933.     // printf("Entering winhDrawFormattedText\n");
  934.  
  935.     flCmd2 = flCmd | DT_WORDBREAK | DT_TEXTATTRS;
  936.  
  937.     ulCharHeight = gpihQueryLineSpacing(hps, pszText);
  938.     // _Pmpf(("  ulCharHeight: %d\n", ulCharHeight));
  939.  
  940.     while (   (lDrawn)
  941.            && (lTotalDrawn < ulTextLen)
  942.           )
  943.     {
  944.         memcpy(&rcl2, prcl, sizeof(rcl2));
  945.         lDrawn = WinDrawText(hps,
  946.                 ulTextLen-lTotalDrawn,
  947.                 p,
  948.                 &rcl2,
  949.                 0, 0,                       // colors
  950.                 flCmd2
  951.             );
  952.         p += lDrawn;
  953.         lTotalDrawn += lDrawn;
  954.         prcl->yTop -= ulCharHeight;
  955.         lLineCount++;
  956.         // printf("  lDrawn: %d, lTotalDrawn: %d\n", lDrawn, lTotalDrawn);
  957.     }
  958.     // printf("  LineCount: %d\n", lLineCount);
  959.     prcl->yBottom = prcl->yTop;
  960.     prcl->yTop = lOrigYTop;
  961.  
  962.     return (lLineCount);
  963. }
  964.  
  965. /*
  966.  *@@ winhKillTasklist:
  967.  *      this will destroy the Tasklist (window list) window.
  968.  *      Note: you will only be able to get it back after a
  969.  *      reboot, not a WPS restart. Only for use at shutdown and such.
  970.  *      This trick by Uri J. Stern at
  971.  *      http://zebra.asta.fh-weingarten.de/os2/Snippets/Howt8881.HTML
  972.  */
  973.  
  974. VOID winhKillTasklist(VOID)
  975. {
  976.     SWBLOCK  swblock;
  977.     HWND     hwndTasklist;
  978.     // the tasklist has entry #0 in the SWBLOCK
  979.     WinQuerySwitchList(NULLHANDLE, &swblock, sizeof(SWBLOCK));
  980.     hwndTasklist = swblock.aswentry[0].swctl.hwnd;
  981.     WinPostMsg(hwndTasklist,
  982.                 0x0454,     // undocumented msg for killing tasklist
  983.                 NULL, NULL);
  984. }
  985.  
  986. /*
  987.  *@@ winhQueryPendingSpoolJobs:
  988.  *      returns the number of pending print jobs in the spooler
  989.  *      or 0 if none. Useful for testing before shutdown.
  990.  */
  991.  
  992. ULONG winhQueryPendingSpoolJobs(VOID)
  993. {
  994.     // BOOL    rcPending = FALSE;
  995.     ULONG   ulTotalJobCount = 0;
  996.  
  997.     SPLERR splerr;
  998.     USHORT jobCount ;
  999.     ULONG  cbBuf ;
  1000.     ULONG  cTotal;
  1001.     ULONG  cReturned ;
  1002.     ULONG  cbNeeded ;
  1003.     ULONG  ulLevel ;
  1004.     ULONG  i,j ;
  1005.     PSZ    pszComputerName ;
  1006.     PBYTE  pBuf = NULL;
  1007.     PPRQINFO3 prq ;
  1008.     PPRJINFO2 prj2 ;
  1009.  
  1010.     ulLevel = 4L;
  1011.     pszComputerName = (PSZ)NULL ;
  1012.     splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, // cbBuf
  1013.                           &cReturned, &cTotal,
  1014.                           &cbNeeded, NULL);
  1015.     if ( splerr == ERROR_MORE_DATA || splerr == NERR_BufTooSmall )
  1016.     {
  1017.         if (!DosAllocMem( (PPVOID)&pBuf, cbNeeded,
  1018.                           PAG_READ | PAG_WRITE | PAG_COMMIT) )
  1019.         {
  1020.             cbBuf = cbNeeded ;
  1021.             splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
  1022.                                     &cReturned, &cTotal,
  1023.                                     &cbNeeded, NULL);
  1024.             if (splerr == NO_ERROR)
  1025.             {
  1026.                 // set pointer to point to the beginning of the buffer
  1027.                 prq = (PPRQINFO3)pBuf ;
  1028.  
  1029.                 // cReturned has the count of the number of PRQINFO3 structures
  1030.                 for (i=0;i < cReturned ; i++)
  1031.                 {
  1032.                     // save the count of jobs; there are this many PRJINFO2
  1033.                     // structures following the PRQINFO3 structure
  1034.                     jobCount = prq->cJobs;
  1035.                     // _Pmpf(( "Job count in this queue is %d",jobCount ));
  1036.  
  1037.                     // increment the pointer past the PRQINFO3 structure
  1038.                     prq++;
  1039.  
  1040.                     // set a pointer to point to the first PRJINFO2 structure
  1041.                     prj2=(PPRJINFO2)prq;
  1042.                     for (j=0;j < jobCount ; j++)
  1043.                     {
  1044.                         // increment the pointer to point to the next structure
  1045.                         prj2++;
  1046.                         // increase the job count, which we'll return
  1047.                         ulTotalJobCount++;
  1048.  
  1049.                     } // endfor jobCount
  1050.  
  1051.                     // after doing all the job structures, prj2 points to the next
  1052.                     // queue structure; set the pointer for a PRQINFO3 structure
  1053.                     prq = (PPRQINFO3)prj2;
  1054.                 } //endfor cReturned
  1055.             } // endif NO_ERROR
  1056.             DosFreeMem(pBuf) ;
  1057.         }
  1058.     } // end if Q level given
  1059.  
  1060.     return (ulTotalJobCount);
  1061. }
  1062.  
  1063.  
  1064.