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

  1.  
  2. /*
  3.  *@@sourcefile comctl.c:
  4.  *      contains various window procedures for implementing
  5.  *      common controls. The source file name has nothing to do
  6.  *      with the Windoze DLL of the same name.
  7.  *
  8.  *      Usage: All PM programs.
  9.  *
  10.  *      Function prefixes (new with V0.81):
  11.  *      --  ctl*   common control helper functions
  12.  *
  13.  *      The functionality of this code is accessed with the use
  14.  *      of a special helper function. Sometimes that function
  15.  *      registers a new window class, sometimes a static control
  16.  *      needs to be subclassed. See the respective functions for
  17.  *      details.
  18.  *
  19.  *      In detail, we have:
  20.  *
  21.  *      --  a "menu button" control, which displays a menu when
  22.  *          pressed (see ctlMakeMenuButton for details);
  23.  *
  24.  *      --  progress bar support (see ctlProgressBarFromStatic for
  25.  *          details);
  26.  *
  27.  *      --  a "chart" control for displaying pie charts (all new
  28.  *          with V0.9.0; see ctlChartFromStatic for details);
  29.  *
  30.  *      --  split windows support (all new with V0.9.0; see
  31.  *          ctlCreateSplitWindow for details);
  32.  *
  33.  *      --  a subclassed static control for enhanced bitmap and
  34.  *          and icon display (see ctl_fnwpBitmapStatic for details).
  35.  *          This used to be in animate.c and has been enhanced
  36.  *          with V0.9.0;
  37.  *
  38.  *      --  a "tooltip" control, which shows fly-over ("bubble")
  39.  *          help over any window, including controls. This is
  40.  *          largely API-compatible with the Win95 tooltip control.
  41.  *          See ctl_fnwpTooltip for details;
  42.  *
  43.  *      --  a "checkbox container" control, which is a subclassed
  44.  *          container which uses checkboxes as record icons.
  45.  *          See ctlMakeCheckboxContainer for details.
  46.  *
  47.  *      Note: Version numbering in this file relates to XWorkplace version
  48.  *            numbering.
  49.  *
  50.  *@@header "helpers\comctl.h"
  51.  */
  52.  
  53. /*
  54.  *      Copyright (C) 1997-2002 Ulrich Möller.
  55.  *      This file is part of the "XWorkplace helpers" source package.
  56.  *      This is free software; you can redistribute it and/or modify
  57.  *      it under the terms of the GNU General Public License as published
  58.  *      by the Free Software Foundation, in version 2 as it comes in the
  59.  *      "COPYING" file of the XWorkplace main distribution.
  60.  *      This program is distributed in the hope that it will be useful,
  61.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  62.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  63.  *      GNU General Public License for more details.
  64.  */
  65.  
  66. #define OS2EMX_PLAIN_CHAR
  67.     // this is needed for "os2emx.h"; if this is defined,
  68.     // emx will define PSZ as _signed_ char, otherwise
  69.     // as unsigned char
  70.  
  71. #define INCL_DOSEXCEPTIONS
  72. #define INCL_DOSPROCESS
  73. #define INCL_DOSSEMAPHORES
  74. #define INCL_DOSERRORS
  75.  
  76. #define INCL_WINWINDOWMGR
  77. #define INCL_WINFRAMEMGR
  78. #define INCL_WINMESSAGEMGR
  79. #define INCL_WININPUT
  80. #define INCL_WINPOINTERS
  81. #define INCL_WINTRACKRECT
  82. #define INCL_WINTIMER
  83. #define INCL_WINSYS
  84.  
  85. #define INCL_WINRECTANGLES      /// xxx temporary
  86.  
  87. #define INCL_WINMENUS
  88. #define INCL_WINSTATICS
  89. #define INCL_WINBUTTONS
  90. #define INCL_WINSTDCNR
  91. #define INCL_WINENTRYFIELDS
  92.  
  93. #define INCL_GPIPRIMITIVES
  94. #define INCL_GPILOGCOLORTABLE
  95. #define INCL_GPILCIDS
  96. #define INCL_GPIPATHS
  97. #define INCL_GPIREGIONS
  98. #define INCL_GPIBITMAPS             // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
  99. #include <os2.h>
  100.  
  101. #include <stdlib.h>
  102. #include <stdio.h>
  103. #include <string.h>
  104. #include <setjmp.h>             // needed for except.h
  105. #include <assert.h>             // needed for except.h
  106.  
  107. #include "setup.h"                      // code generation and debugging options
  108.  
  109. #include "helpers\cnrh.h"
  110. #include "helpers\except.h"             // exception handling
  111. #include "helpers\gpih.h"
  112. #include "helpers\linklist.h"
  113. #include "helpers\winh.h"
  114.  
  115. #include "helpers\comctl.h"
  116.  
  117. #pragma hdrstop
  118.  
  119. /* ******************************************************************
  120.  *
  121.  *   "XButton" control
  122.  *
  123.  ********************************************************************/
  124.  
  125. /*
  126.  *@@ ctlPaintXButton:
  127.  *      paints an X-button control. Can be called externally
  128.  *      for just painting a button even if this is not really
  129.  *      a window.
  130.  *
  131.  *      WARNING: this is work in progress and will change into
  132.  *      the future. Eventually this will turn into a full
  133.  *      button control replacement.
  134.  *
  135.  *@@added V0.9.13 (2001-06-21) [umoeller]
  136.  *@@changed V0.9.16 (2001-10-24) [umoeller]: fixed wrong hatch color and paint offset
  137.  *@@changed V0.9.16 (2001-10-28) [umoeller]: added bitmap support, fixed bad clip rectangle
  138.  *@@changed V0.9.20 (2002-08-04) [umoeller]: fixed button offset, depressed color
  139.  */
  140.  
  141. VOID ctlPaintXButton(HPS hps,               // in: presentation space (RGB mode)
  142.                      ULONG fl,              // in: XBF_* flags
  143.                      PXBUTTONDATA pxbd)     // in: button data
  144. {
  145.     ULONG   ulBorder = 0,
  146.             cx,
  147.             cy,
  148.             ulOfs = 0;
  149.     LONG    lLeft,
  150.             lRight,
  151.             lColorMiddle = pxbd->lMiddle;
  152.     RECTL   rclWin;
  153.  
  154.     memcpy(&rclWin, &pxbd->rcl, sizeof(RECTL));
  155.  
  156.     if (0 == (fl & XBF_FLAT))
  157.         ulBorder = 2;
  158.  
  159.     gpihSwitchToRGB(hps);
  160.  
  161.     if (fl & XBF_PRESSED)
  162.     {
  163.         // paint button "down":
  164.         lLeft = pxbd->lcol3DDark;
  165.         lRight = pxbd->lcol3DLight;
  166.         // add offset for icon painting at the bottom
  167.         ulOfs += 1;
  168.         if (ulBorder == 0)
  169.             ulBorder = 1;
  170.  
  171.         // make the depressed color darker
  172.         // V0.9.20 (2002-07-31) [umoeller]
  173.         gpihManipulateRGB(&lColorMiddle,
  174.                           .95);
  175.     }
  176.     else
  177.     {
  178.         lLeft = pxbd->lcol3DLight;
  179.         lRight = pxbd->lcol3DDark;
  180.     }
  181.  
  182.     if (ulBorder)
  183.     {
  184.         // button border:
  185.         // now paint button frame
  186.  
  187.         // make rcl inclusive
  188.         rclWin.xRight--;
  189.         rclWin.yTop--;
  190.         gpihDraw3DFrame(hps,
  191.                         &rclWin,        // inclusive
  192.                         ulBorder,
  193.                         lLeft,
  194.                         lRight);
  195.  
  196.         // now paint button middle
  197.         rclWin.xLeft += ulBorder;
  198.         rclWin.yBottom += ulBorder;
  199.         rclWin.xRight -= ulBorder - 1;  // make exclusive again
  200.         rclWin.yTop -= ulBorder - 1;    // make exclusive again
  201.     }
  202.  
  203.     if (fl & XBF_BACKGROUND)
  204.         WinFillRect(hps,
  205.                     &rclWin,        // exclusive
  206.                     lColorMiddle);
  207.  
  208.     // get icon
  209.     if (pxbd->hptr)
  210.     {
  211.         // calculate x and y to be centered in rectangle
  212.         POINTL  ptl;
  213.  
  214.         cx = rclWin.xRight - rclWin.xLeft;
  215.         cy = rclWin.yTop - rclWin.yBottom;
  216.  
  217.         ptl.x = rclWin.xLeft + ((cx - pxbd->cxIconOrBitmap) / 2);
  218.         ptl.y = rclWin.yBottom + ((cy - pxbd->cyIconOrBitmap) / 2);
  219.  
  220.         if (fl & XBF_INUSE)
  221.         {
  222.             // caller wants in-use (hatched) emphasis:
  223.             // draw a box then
  224.             POINTL ptl2;
  225.             ptl2.x = ptl.x - 2;
  226.             ptl2.y = ptl.y - 2;
  227.             GpiMove(hps,
  228.                     &ptl2);     // &ptl
  229.                                 // duh, typo V0.9.16 (2001-10-24) [umoeller]
  230.             GpiSetPattern(hps, PATSYM_DIAG1);
  231.             GpiSetColor(hps, RGBCOL_BLACK);     // V0.9.16 (2001-10-24) [umoeller]
  232.             ptl2.x = ptl.x + pxbd->cxIconOrBitmap + 1; // inclusive!
  233.             ptl2.y = ptl.y + pxbd->cyIconOrBitmap + 1; // inclusive!
  234.             GpiBox(hps,
  235.                    DRO_FILL,
  236.                    &ptl2,
  237.                    0,
  238.                    0);
  239.         }
  240.  
  241.         // now paint icon
  242.  
  243.         // make rcl inclusive           // V0.9.16 (2001-10-28) [umoeller]
  244.         rclWin.xRight--;
  245.         rclWin.yTop--;
  246.         GpiIntersectClipRectangle(hps,
  247.                                   &rclWin);    // inclusive!
  248.  
  249.         // center this in remaining rectl
  250.         ptl.x += ulOfs;
  251.         ptl.y -= ulOfs;
  252.         if (fl & XBF_BITMAP)
  253.             // V0.9.16 (2001-10-28) [umoeller]
  254.             WinDrawBitmap(hps,
  255.                           pxbd->hptr,           // a bitmap really
  256.                           NULL,                 // entire bitmap
  257.                           &ptl,
  258.                           0,
  259.                           0,
  260.                           DBM_NORMAL);
  261.         else
  262.             WinDrawPointer(hps,
  263.                            // center this in remaining rectl
  264.                            ptl.x, //  + ulOfs,
  265.                            ptl.y, //  - ulOfs,
  266.                            pxbd->hptr,
  267.                            DP_MINI);
  268.     }
  269. }
  270.  
  271. /*
  272.  *@@category: Helpers\PM helpers\Window classes\Menu buttons
  273.  *      See comctl.c and ctlMakeMenuButton.
  274.  */
  275.  
  276. /* ******************************************************************
  277.  *
  278.  *   "Menu button" control
  279.  *
  280.  ********************************************************************/
  281.  
  282. /*
  283.  *@@ MENUBUTTONDATA:
  284.  *      internal data for "menu button"
  285.  *
  286.  *@@added V0.9.0 [umoeller]
  287.  */
  288.  
  289. typedef struct _MENUBUTTONDATA
  290. {
  291.     PFNWP       pfnwpButtonOriginal;
  292.     HMODULE     hmodMenu;
  293.     ULONG       idMenu;
  294.     BOOL        fMouseCaptured,         // TRUE if WinSetCapture
  295.                 fMouseButton1Down,      // TRUE in between WM_BUTTON1DOWN and WM_BUTTON1UP
  296.                 fButtonSunk;            // toggle state of the button
  297.     HWND        hwndMenu;
  298. } MENUBUTTONDATA, *PMENUBUTTONDATA;
  299.  
  300. /*
  301.  *@@ ctlDisplayButtonMenu:
  302.  *      displays the specified menu above the button.
  303.  *
  304.  *@@added V0.9.7 (2000-11-29) [umoeller]
  305.  */
  306.  
  307. VOID ctlDisplayButtonMenu(HWND hwndButton,
  308.                           HWND hwndMenu)
  309. {
  310.     SWP     swpButton;
  311.     POINTL  ptlMenu;
  312.     WinQueryWindowPos(hwndButton, &swpButton);
  313.     ptlMenu.x = swpButton.x;
  314.     ptlMenu.y = swpButton.y;
  315.  
  316.     // ptlMenu now has button coordinates
  317.     // relative to the button's parent;
  318.     // convert this to screen coordinates:
  319.     WinMapWindowPoints(WinQueryWindow(hwndButton, QW_PARENT),
  320.                        HWND_DESKTOP,
  321.                        &ptlMenu,
  322.                        1);
  323.  
  324.     // now show the menu on top of the button
  325.     WinPopupMenu(HWND_DESKTOP,               // menu parent
  326.                  hwndButton,                 // owner
  327.                  hwndMenu,
  328.                  (SHORT)(ptlMenu.x),
  329.                  (SHORT)(ptlMenu.y + swpButton.cy - 1),
  330.                  0,                          // ID
  331.                  PU_NONE
  332.                      | PU_MOUSEBUTTON1
  333.                      | PU_KEYBOARD
  334.                      | PU_HCONSTRAIN
  335.                      | PU_VCONSTRAIN);
  336. }
  337.  
  338. /*
  339.  *@@ ctl_fnwpSubclassedMenuButton:
  340.  *      subclassed window proc for "menu button".
  341.  *      See ctlMakeMenuButton for details.
  342.  *
  343.  *@@added V0.9.0 [umoeller]
  344.  *@@changed V0.9.2 (2000-02-28) [umoeller]: menu was displayed even if button was disabled; fixed
  345.  *@@changed V0.9.14 (2001-07-31) [umoeller]: fixed WM_MENUEND submenu quirk
  346.  */
  347.  
  348. MRESULT EXPENTRY ctl_fnwpSubclassedMenuButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2)
  349. {
  350.     MRESULT mrc = 0;
  351.     PMENUBUTTONDATA pmbd = (PMENUBUTTONDATA)WinQueryWindowULong(hwndButton, QWL_USER);
  352.  
  353.     switch (msg)
  354.     {
  355.         /*
  356.          * WM_BUTTON1DOWN:
  357.          * WM_BUTTON1UP:
  358.          *      these show/hide the menu.
  359.          *
  360.          *      Showing the menu follows these steps:
  361.          *          a)  first WM_BUTTON1DOWN hilites the button;
  362.          *          b)  first WM_BUTTON1UP shows the menu.
  363.          *
  364.          *      When the button is pressed again, the open
  365.          *      menu loses focus, which results in WM_MENUEND
  366.          *      and destroys the window automatically.
  367.          */
  368.  
  369.         case WM_BUTTON1DOWN:
  370.         case WM_BUTTON1DBLCLK:
  371.             // only do this if the button is enabled
  372.             // V0.9.2 (2000-02-28) [umoeller]
  373.             if (WinIsWindowEnabled(hwndButton))
  374.             {
  375.                 // _Pmpf(("WM_BUTTON1DOWN"));
  376.                 // since we're not passing the message
  377.                 // to WinDefWndProc, we need to give
  378.                 // ourselves the focus
  379.                 WinSetFocus(HWND_DESKTOP, hwndButton);
  380.  
  381.                 if (!pmbd->fMouseCaptured)
  382.                 {
  383.                     // capture mouse events while the
  384.                     // mouse button is down
  385.                     WinSetCapture(HWND_DESKTOP, hwndButton);
  386.                     pmbd->fMouseCaptured = TRUE;
  387.                 }
  388.  
  389.                 pmbd->fMouseButton1Down = TRUE;
  390.  
  391.                 if (!pmbd->fButtonSunk)
  392.                 {
  393.                     // button not hilited yet (first click):
  394.                     // do it now
  395.                     // _Pmpf(("  Sinking menu WM_BUTTON1DOWN "));
  396.                     pmbd->fButtonSunk = TRUE;
  397.                     WinSendMsg(hwndButton,
  398.                                BM_SETHILITE,
  399.                                (MPARAM)TRUE,
  400.                                (MPARAM)0);
  401.                 }
  402.  
  403.                 // else: the menu has just been destroyed
  404.                 // (WM_MENUEND below)
  405.             }
  406.  
  407.             mrc = (MPARAM)TRUE;     // message processed
  408.         break;
  409.  
  410.         /*
  411.          * WM_BUTTON1UP:
  412.          *
  413.          */
  414.  
  415.         case WM_BUTTON1UP:
  416.             // only do this if the button is enabled
  417.             // V0.9.2 (2000-02-28) [umoeller]
  418.             if (WinIsWindowEnabled(hwndButton))
  419.             {
  420.                 // _Pmpf(("WM_BUTTON1UP sunk %d hwndMenu 0x%lX", pmbd->fButtonSunk, pmbd->hwndMenu));
  421.  
  422.                 // un-capture the mouse first
  423.                 if (pmbd->fMouseCaptured)
  424.                 {
  425.                     WinSetCapture(HWND_DESKTOP, NULLHANDLE);
  426.                     pmbd->fMouseCaptured = FALSE;
  427.                 }
  428.  
  429.                 pmbd->fMouseButton1Down = FALSE;
  430.  
  431.                 if (    (pmbd->fButtonSunk)      // set by WM_BUTTON1DOWN above
  432.                      && (pmbd->hwndMenu)         // menu currently showing
  433.                    )
  434.                 {
  435.                     // button currently depressed:
  436.                     // un-hilite button
  437.                     // _Pmpf(("  Unsinking menu WM_BUTTON1UP 1"));
  438.                     pmbd->fButtonSunk = FALSE;
  439.                     WinSendMsg(hwndButton,
  440.                                BM_SETHILITE,
  441.                                (MPARAM)FALSE,
  442.                                (MPARAM)0);
  443.                 }
  444.                 else
  445.                 {
  446.                     // first button up:
  447.                     // show menu
  448.  
  449.                     if (pmbd->idMenu)
  450.                     {
  451.                         // _Pmpf(("  Loading menu hmod %lX id %lX", pmbd->hmodMenu, pmbd->idMenu));
  452.                         // menu specified: load from
  453.                         // specified resources
  454.                         pmbd->hwndMenu = WinLoadMenu(hwndButton,
  455.                                                      pmbd->hmodMenu,
  456.                                                      pmbd->idMenu);
  457.                     }
  458.                     else
  459.                     {
  460.                         HWND    hwndOwner = WinQueryWindow(hwndButton, QW_OWNER);
  461.                         // _Pmpf(("  Querying menu WM_COMMAND from owner 0x%lX", hwndOwner));
  462.                         // send WM_COMMAND to owner
  463.                         pmbd->hwndMenu
  464.                             = (HWND)WinSendMsg(hwndOwner,
  465.                                                WM_COMMAND,
  466.                                                (MPARAM)(ULONG)WinQueryWindowUShort(hwndButton,
  467.                                                                                    QWS_ID),
  468.                                                (MPARAM)0);
  469.                     }
  470.  
  471.                     // _Pmpf(("  Loaded menu, hwnd: 0x%lX", pmbd->hwndMenu));
  472.  
  473.                     if (pmbd->hwndMenu)
  474.                     {
  475.                         // menu successfully loaded:
  476.                         // find out where to put it
  477.                         ctlDisplayButtonMenu(hwndButton,
  478.                                              pmbd->hwndMenu);
  479.                     } // end if (pmbd->hwndMenu)
  480.                     else
  481.                     {
  482.                         // menu not loaded:
  483.                         // _Pmpf(("  Unsinking menu WM_BUTTON1UP 2"));
  484.                         pmbd->fButtonSunk = FALSE;
  485.                         WinSendMsg(hwndButton,
  486.                                    BM_SETHILITE,
  487.                                    (MPARAM)FALSE,
  488.                                    (MPARAM)0);
  489.                     }
  490.                 }
  491.             }
  492.  
  493.             mrc = (MPARAM)TRUE;     // message processed
  494.         break;
  495.  
  496.         /*
  497.          * WM_BUTTON1CLICK:
  498.          *      swallow this
  499.          */
  500.  
  501.         case WM_BUTTON1CLICK:
  502.             mrc = (MPARAM)TRUE;
  503.         break;
  504.  
  505.         /*
  506.          * WM_SETFOCUS:
  507.          *      swallow this, the button keeps painting
  508.          *      itself otherwise
  509.          */
  510.  
  511.         case WM_SETFOCUS:
  512.         break;
  513.  
  514.         /*
  515.          * WM_MENUEND:
  516.          *      menu is destroyed; we get this
  517.          *      because we're the owner
  518.          */
  519.  
  520.         case WM_MENUEND:
  521.             if ((HWND)mp2 == pmbd->hwndMenu) // V0.9.14 (2001-07-31) [umoeller]
  522.             {
  523.                 BOOL fUnHilite = TRUE;
  524.                 // _Pmpf(("WM_MENUEND"));
  525.                 // At this point, the menu has been
  526.                 // destroyed already.
  527.                 // Since WM_BUTTON1UP handles the
  528.                 // default case that the user presses
  529.                 // the menu button a second time while
  530.                 // the menu is open, we only need
  531.                 // to handle the case that the user
  532.                 // c)   presses some key on the menu (ESC, menu selection) or
  533.                 // a)   selects a menu item (which
  534.                 //      dismisses the menu) or
  535.                 // b)   clicks anywhere else.
  536.  
  537.                 // Case a) if mouse button 1 is not currently down
  538.                 if ((WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) == 0)
  539.                     // button 1 _not_ down:
  540.                     // must be keyboard
  541.                     fUnHilite = TRUE;
  542.                 else
  543.                     // button 1 _is_ down:
  544.                     // query window under mouse pointer
  545.                     ;
  546.  
  547.                 if (fUnHilite)
  548.                 {
  549.                     // _Pmpf(("  Unsinking menu WM_MENUEND"));
  550.                     pmbd->fButtonSunk = FALSE;
  551.                     WinSendMsg(hwndButton,
  552.                                BM_SETHILITE,
  553.                                (MPARAM)FALSE,
  554.                                (MPARAM)0);
  555.                 }
  556.                 pmbd->hwndMenu = NULLHANDLE;
  557.             } // end if ((HWND)mp1 == pmbd->pmbd->hwndMenu) // V0.9.14 (2001-07-31) [umoeller]
  558.         break;
  559.  
  560.         /*
  561.          * WM_COMMAND:
  562.          *      this must be from the menu, so
  563.          *      forward this to the button's owner
  564.          */
  565.  
  566.         case WM_COMMAND:
  567.             WinPostMsg(WinQueryWindow(hwndButton, QW_OWNER),
  568.                        msg,
  569.                        mp1,
  570.                        mp2);
  571.         break;
  572.  
  573.         /*
  574.          * WM_DESTROY:
  575.          *      clean up allocated data
  576.          */
  577.  
  578.         case WM_DESTROY:
  579.             mrc = pmbd->pfnwpButtonOriginal(hwndButton, msg, mp1, mp2);
  580.             free(pmbd);
  581.         break;
  582.  
  583.         default:
  584.             mrc = pmbd->pfnwpButtonOriginal(hwndButton, msg, mp1, mp2);
  585.     }
  586.  
  587.     return mrc;
  588. }
  589.  
  590. /*
  591.  *@@ ctlMakeMenuButton:
  592.  *      this turns the specified button into a "menu button",
  593.  *      which shows a popup menu when the button is depressed.
  594.  *      This is done by subclassing the menu button with
  595.  *      ctl_fnwpSubclassedMenuButton.
  596.  *
  597.  *      Simply call this function upon any button, and it'll
  598.  *      turn in to a menu button.
  599.  *
  600.  *      When the user presses the button, the specified menu
  601.  *      is loaded from the resources. The button will then
  602.  *      be set as the owner, but it will forward all WM_COMMAND
  603.  *      messages from the menu to its own owner (probably your
  604.  *      dialog), so you can handle WM_COMMAND messages just as
  605.  *      if the menu was owned by your dialog.
  606.  *
  607.  *      Alternatively, if you don't want to load a menu from
  608.  *      the resources, you can specify idMenu == 0. In that case,
  609.  *      when the menu button is pressed, it sends (!) a WM_COMMAND
  610.  *      message to its owner with its ID in mp1 (as usual). The
  611.  *      difference is that the return value from that message will
  612.  *      be interpreted as a menu handle (HWND) to use for the button
  613.  *      menu.
  614.  *
  615.  *      The subclassed button also handles menu destruction etc.
  616.  *      by itself.
  617.  *
  618.  *@@added V0.9.0 [umoeller]
  619.  */
  620.  
  621. BOOL ctlMakeMenuButton(HWND hwndButton,      // in: button to subclass
  622.                        HMODULE hmodMenu,     // in: resource module (can be NULLHANDLE for
  623.                                              // current EXE)
  624.                        ULONG idMenu)         // in: resource menu ID (or 0)
  625. {
  626.     BOOL brc = FALSE;
  627.     PMENUBUTTONDATA pmbd;
  628.     if (pmbd = (PMENUBUTTONDATA)malloc(sizeof(MENUBUTTONDATA)))
  629.     {
  630.         memset(pmbd, 0, sizeof(MENUBUTTONDATA));
  631.         if (pmbd->pfnwpButtonOriginal = WinSubclassWindow(hwndButton,
  632.                                                           ctl_fnwpSubclassedMenuButton))
  633.         {
  634.             pmbd->hmodMenu = hmodMenu;
  635.             pmbd->idMenu = idMenu;
  636.             WinSetWindowULong(hwndButton, QWL_USER, (ULONG)pmbd);
  637.             brc = TRUE;
  638.         }
  639.         else
  640.             free(pmbd);
  641.     }
  642.  
  643.     return brc;
  644. }
  645.  
  646. /*
  647.  *@@category: Helpers\PM helpers\Window classes\Static bitmaps
  648.  *      See comctl.c and ctl_fnwpBitmapStatic.
  649.  */
  650.  
  651. /* ******************************************************************
  652.  *
  653.  *   Subclassed Static Bitmap Control
  654.  *
  655.  ********************************************************************/
  656.  
  657. /*
  658.  *@@ ctl_fnwpBitmapStatic:
  659.  *      subclassed wnd proc for both static controls which
  660.  *      should display icons and stretched bitmaps.
  661.  *
  662.  *      This is not a stand-alone window procedure, but must only
  663.  *      be used with static controls subclassed by the functions
  664.  *      listed below.
  665.  *
  666.  *      This is now (V0.9.0) used in two contexts:
  667.  *      --  when subclassed from ctlPrepareStaticIcon,
  668.  *          this displays transparent icons properly
  669.  *          (for regular icons or icon animations);
  670.  *      --  when subclassed from ctlPrepareStretchedBitmap,
  671.  *          this displays a stretched bitmap.
  672.  *
  673.  *      The behavior depends on ANIMATIONDATA.ulFlags,
  674.  *      which is set by both of these functions (either to
  675.  *      ANF_ICON or ANF_BITMAP).
  676.  *
  677.  *      Only one msg is of interest to the "user" application
  678.  *      of this control, which is SM_SETHANDLE,
  679.  *      the normal message sent to a static icon control to change
  680.  *      its icon or bitmap.
  681.  *      The new handle (HPOINTER or HBITMAP) is
  682.  *      in mp1; mp2 must be null.
  683.  *
  684.  *      Depending on ANIMATIONDATA.ulFlags, we do the following:
  685.  *
  686.  *      --  ANF_ICON:  Unfortunately, the
  687.  *          standard static control paints garbage if
  688.  *          the icons contain transparent areas, and the
  689.  *          display often flickers too.
  690.  *          We improve this by creating one bitmap from
  691.  *          the icon that we were given which we can then
  692.  *          simply copy to the screen in one step in
  693.  *          WM_PAINT.
  694.  *
  695.  *      --  ANF_BITMAP: we create a bitmap which is
  696.  *          stretched to the size of the static control,
  697.  *          using gpihStretchBitmap.
  698.  *
  699.  *      In any case, a new bitmap is produced internally and
  700.  *      stored so that it can easily be painted when WM_PAINT
  701.  *      comes in. The source icon/bitmap can therefore
  702.  *      be safely destroyed by the caller after sending
  703.  *      SM_SETHANDLE. This bitmap is automatically deleted when
  704.  *      the window is destroyed.
  705.  *
  706.  *@@changed V0.9.0 [umoeller]: function renamed
  707.  *@@changed V0.9.0 [umoeller]: added support for stretched bitmaps
  708.  *@@changed V0.9.0 [umoeller]: added halftoned display for WS_DISABLED
  709.  *@@changed V0.9.0 [umoeller]: exported gpihIcon2Bitmap function to gpih.c
  710.  *@@changed V0.9.0 [umoeller]: fixed paint errors when SM_SETHANDLE had NULL argument in mp1
  711.  *@@changed V0.9.16 (2001-10-15) [umoeller]: now centering icon in static properly
  712.  *@@changed V0.9.16 (2001-10-15) [umoeller]: this always used the presparam colors of the parent instead of its own ones
  713.  */
  714.  
  715. MRESULT EXPENTRY ctl_fnwpBitmapStatic(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
  716. {
  717.     PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
  718.                 // animation data which was stored in window words
  719.  
  720.     PFNWP   OldStaticProc = NULL;
  721.     MRESULT mrc = NULL;
  722.  
  723.     if (pa)
  724.     {
  725.         OldStaticProc = pa->OldStaticProc;
  726.  
  727.         switch(msg)
  728.         {
  729.             /*
  730.              * WM_TIMER:
  731.              *      this timer is started by the ctl* funcs
  732.              *      below. Proceed to the next animation step.
  733.              *
  734.              *      Note: this timer is only used with
  735.              *      ANF_ICON, and even then, it
  736.              *      is not necessarily running.
  737.              */
  738.  
  739.             case WM_TIMER:
  740.                 pa->usAniCurrent++;
  741.                 if (pa->usAniCurrent >= pa->usAniCount)
  742.                     pa->usAniCurrent = 0;
  743.  
  744.                 WinSendMsg(hwndStatic,
  745.                            SM_SETHANDLE,
  746.                            (MPARAM)pa->ahptrAniIcons[pa->usAniCurrent],
  747.                            (MPARAM)NULL);
  748.             break;
  749.  
  750.             /*
  751.              * SM_SETHANDLE:
  752.              *
  753.              */
  754.  
  755.             case SM_SETHANDLE:
  756.             {
  757.                 HDC hdcMem;
  758.                 HPS hpsMem;
  759.                 SIZEL szlPage;
  760.  
  761.                 LONG lBkgndColor
  762.                     /* = winhQueryPresColor(WinQueryWindow(hwndStatic, QW_PARENT),
  763.                                          PP_BACKGROUNDCOLOR,
  764.                                          FALSE,
  765.                                          SYSCLR_DIALOGBACKGROUND); */
  766.                     // fixed this... V0.9.16 (2001-10-15) [umoeller]
  767.                     = winhQueryPresColor(hwndStatic,
  768.                                          PP_BACKGROUNDCOLOR,
  769.                                          TRUE,
  770.                                          SYSCLR_DIALOGBACKGROUND);
  771.  
  772.                 HPS hps = WinGetPS(hwndStatic);
  773.  
  774.                 // if we have created bitmaps previously, delete them
  775.                 if (pa->hbm)
  776.                 {
  777.                     GpiDeleteBitmap(pa->hbm);
  778.                     pa->hbm = NULLHANDLE;
  779.                 }
  780.                 if (pa->hbmHalftoned)
  781.                 {
  782.                     GpiDeleteBitmap(pa->hbmHalftoned);
  783.                     pa->hbmHalftoned = NULLHANDLE;
  784.                 }
  785.  
  786.                 // switch to RGB mode
  787.                 gpihSwitchToRGB(hps);
  788.  
  789.                 // create a memory PS
  790.                 szlPage.cx = pa->rclIcon.xRight - pa->rclIcon.xLeft;
  791.                 szlPage.cy = pa->rclIcon.yTop - pa->rclIcon.yBottom;
  792.                 if (gpihCreateMemPS(pa->hab,
  793.                                     &szlPage,
  794.                                     &hdcMem,
  795.                                     &hpsMem))
  796.                 {
  797.                     // switch the memory PS to RGB mode too
  798.                     gpihSwitchToRGB(hpsMem);
  799.  
  800.                     // create a suitable bitmap w/ the size of the
  801.                     // static control
  802.                     if (    ((pa->hbm = gpihCreateBitmap(hpsMem,
  803.                                                          szlPage.cx,
  804.                                                          szlPage.cy)))
  805.                             // associate the bit map with the memory PS
  806.                          && (GpiSetBitmap(hpsMem, pa->hbm) != HBM_ERROR)
  807.                        )
  808.                     {
  809.                         // fill the bitmap with the current static
  810.                         // background color
  811.                         POINTL ptl = {0, 0};
  812.                         GpiMove(hpsMem, &ptl);
  813.                         ptl.x = pa->rclIcon.xRight;
  814.                         ptl.y = pa->rclIcon.yTop;
  815.                         GpiSetColor(hpsMem,
  816.                                     lBkgndColor);
  817.                         GpiBox(hpsMem,
  818.                                DRO_FILL, // interior only
  819.                                &ptl,
  820.                                0, 0);    // no corner rounding
  821.  
  822.                         /*
  823.                          * ANF_ICON:
  824.                          *
  825.                          */
  826.  
  827.                         if (pa->ulFlags & ANF_ICON)
  828.                         {
  829.                             // store new icon in our own structure
  830.                             if (pa->hptr = (HPOINTER)mp1)
  831.                             {
  832.                                 // center the icon in the bitmap
  833.                                 // V0.9.16 (2001-10-15) [umoeller]
  834.  
  835.                                 // replaced call V0.9.19 (2002-06-18) [umoeller]
  836.                                 gpihDrawPointer(hpsMem,
  837.                                                 (   (pa->rclIcon.xRight - pa->rclIcon.xLeft)
  838.                                                   - pa->szlIcon.cx
  839.                                                 ) / 2,
  840.                                                 (   (pa->rclIcon.yTop - pa->rclIcon.yBottom)
  841.                                                   - pa->szlIcon.cy
  842.                                                 ) / 2,
  843.                                                 pa->hptr,
  844.                                                 &pa->szlIcon,
  845.                                                 NULL,       // no clipping
  846.                                                 0);         // no mini
  847.                             }
  848.  
  849.                         } // end if (pa->ulFlags & ANF_ICON)
  850.  
  851.                         /*
  852.                          * ANF_BITMAP:
  853.                          *
  854.                          */
  855.  
  856.                         else if (pa->ulFlags & ANF_BITMAP)
  857.                         {
  858.                             // store passed bitmap
  859.                             HBITMAP     hbmSource;
  860.                             if (hbmSource = (HBITMAP)mp1)
  861.                                 gpihStretchBitmap(hpsMem,       // target
  862.                                                   hbmSource,    // source
  863.                                                   NULL,     // use size of bitmap
  864.                                                   &pa->rclIcon,
  865.                                                   ((pa->ulFlags & ANF_PROPORTIONAL)
  866.                                                         != 0));
  867.  
  868.                         } // end if (pa->ulFlags & ANF_BITMAP)
  869.  
  870.                     } // end if (GpiSetBitmap(...
  871.                       // && (pa->hbm = gpihCreateBitmap(hpsMem, &(pa->rclIcon)))
  872.  
  873.                     // in any case, clean up now
  874.                     GpiDestroyPS(hpsMem);
  875.                     DevCloseDC(hdcMem);
  876.                 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
  877.  
  878.                 WinReleasePS(hps);
  879.  
  880.                 // enforce WM_PAINT
  881.                 WinInvalidateRect(hwndStatic, NULL, FALSE);
  882.             }
  883.             break;
  884.  
  885.             /*
  886.              * WM_PAINT:
  887.              *      "normal" paint; this only arrives here if the
  888.              *      icon needs to be repainted. We simply paint
  889.              *      the bitmap we created in WM_SETHANDLE.
  890.              */
  891.  
  892.             case WM_PAINT:
  893.             {
  894.                 RECTL rcl;
  895.                 HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
  896.                 POINTL ptl = {0, 0};
  897.  
  898.                 if (pa->hbm)
  899.                 {
  900.                     if (WinQueryWindowULong(hwndStatic, QWL_STYLE)
  901.                             & WS_DISABLED)
  902.                     {
  903.                         // if the control is currently disabled,
  904.                         // draw in half-toned (grayed)
  905.  
  906.                         LONG lBkgndColor = winhQueryPresColor(
  907.                                         WinQueryWindow(hwndStatic, QW_PARENT),
  908.                                         PP_BACKGROUNDCOLOR,
  909.                                         FALSE,
  910.                                         SYSCLR_DIALOGBACKGROUND);
  911.  
  912.                         // 1) check if we created the half-tone
  913.                         // bitmap already (WinDrawBitmap doesn't
  914.                         // work with half-toned color bitmaps, so
  915.                         // here's yet another thing we have to do
  916.                         // all alone)
  917.                         if (pa->hbmHalftoned == NULLHANDLE)
  918.                             pa->hbmHalftoned = gpihCreateHalftonedBitmap(pa->hab,
  919.                                                                          pa->hbm,
  920.                                                                          lBkgndColor);
  921.  
  922.                         if (pa->hbmHalftoned)
  923.                             WinDrawBitmap(hps,
  924.                                           pa->hbmHalftoned,
  925.                                           NULL,     // whole bmp
  926.                                           &ptl,     // lower left corner
  927.                                           0, 0,     // no colors
  928.                                           DBM_NORMAL);
  929.                     }
  930.                     else
  931.                     {
  932.                         // not disabled: draw regular bitmap
  933.                         // we created previously
  934.                         // draw the bitmap that we created previously
  935.                         WinDrawBitmap(hps,
  936.                                       pa->hbm,
  937.                                       NULL,     // whole bmp
  938.                                       &ptl,     // lower left corner
  939.                                       0, 0,     // no colors
  940.                                       DBM_NORMAL);
  941.                     }
  942.                 }
  943.  
  944.                 WinEndPaint(hps);
  945.             }
  946.             break;
  947.  
  948.             /*
  949.              * WM_DESTROY:
  950.              *      clean up.
  951.              */
  952.  
  953.             case WM_DESTROY:
  954.                 // undo subclassing in case more WM_TIMERs come in
  955.                 WinSubclassWindow(hwndStatic, OldStaticProc);
  956.  
  957.                 if (pa->hbm)
  958.                     GpiDeleteBitmap(pa->hbm);
  959.                 if (pa->hbmHalftoned)
  960.                     GpiDeleteBitmap(pa->hbm);
  961.  
  962.                 // clean up ANIMATIONDATA struct
  963.                 WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
  964.                 free(pa);
  965.  
  966.                 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
  967.             break;
  968.  
  969.             default:
  970.                 mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
  971.        }
  972.     }
  973.     return mrc;
  974. }
  975.  
  976. /* ******************************************************************
  977.  *
  978.  *   Icon animation
  979.  *
  980.  ********************************************************************/
  981.  
  982. /*
  983.  *@@ CreateAnimationData:
  984.  *
  985.  *@@added V0.9.16 (2001-10-15) [umoeller]
  986.  */
  987.  
  988. static PANIMATIONDATA CreateAnimationData(HAB hab,
  989.                                           HWND hwndStatic,
  990.                                           USHORT cAnimations)
  991. {
  992.     PANIMATIONDATA pa = NULL;
  993.  
  994.     if (cAnimations >= 1)
  995.     {
  996.         // create the ANIMATIONDATA structure,
  997.         // initialize some fields,
  998.         // and store it in QWL_USER of the static control
  999.         ULONG   cbStruct =   sizeof(ANIMATIONDATA)
  1000.                            + ((cAnimations - 1) * sizeof(HPOINTER));
  1001.  
  1002.         if (pa = (PANIMATIONDATA)malloc(cbStruct))
  1003.         {
  1004.             memset(pa, 0, cbStruct);
  1005.  
  1006.             WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
  1007.  
  1008.             pa->hab = hab;
  1009.             WinQueryWindowRect(hwndStatic, &pa->rclIcon);
  1010.             pa->OldStaticProc = WinSubclassWindow(hwndStatic, ctl_fnwpBitmapStatic);
  1011.             pa->szlIcon.cx = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
  1012.             pa->szlIcon.cy = WinQuerySysValue(HWND_DESKTOP, SV_CYICON);
  1013.         }
  1014.     }
  1015.  
  1016.     return pa;
  1017. }
  1018.  
  1019. /*
  1020.  *@@ ctlPrepareStaticIcon:
  1021.  *      turns a static control into one which properly
  1022.  *      displays transparent icons when given a
  1023.  *      SM_SETHANDLE msg.
  1024.  *      This is done by subclassing the static control
  1025.  *      with ctl_fnwpBitmapStatic.
  1026.  *
  1027.  *      This function gets called automatically by
  1028.  *      ctlPrepareAnimation, so you need not call it
  1029.  *      independently for animations. See the notes there
  1030.  *      how this can be done.
  1031.  *
  1032.  *      You can, however, call this function if you
  1033.  *      have some static control with transparent icons
  1034.  *      which is not animated but changes sometimes
  1035.  *      (because you have the same repaint problems there).
  1036.  *
  1037.  *      This func does _not_ start an animation yet.
  1038.  *
  1039.  *      To change the icon being displayed, send the control
  1040.  *      a SM_SETHANDLE msg with the icon handle in (HPOINTER)mp1.
  1041.  *
  1042.  *      Returns a PANIMATIONDATA if subclassing succeeded or
  1043.  *      NULL upon errors. If you only call this function, you
  1044.  *      do not need this structure; it is needed by
  1045.  *      ctlPrepareAnimation though.
  1046.  *
  1047.  *      The subclassed static icon func above automatically
  1048.  *      cleans up resources, so you don't have to worry about
  1049.  *      that either.
  1050.  *
  1051.  *@@changed V0.9.0 [umoeller]: adjusted for ctl_fnwpBitmapStatic changes
  1052.  */
  1053.  
  1054. PANIMATIONDATA ctlPrepareStaticIcon(HWND hwndStatic,
  1055.                                     USHORT usAnimCount) // needed for allocating extra memory,
  1056.                                                         // this must be at least 1
  1057. {
  1058.     PANIMATIONDATA pa;
  1059.     HAB     hab;
  1060.     if (    (hwndStatic)
  1061.          && (hab = WinQueryAnchorBlock(hwndStatic))
  1062.          && (WinIsWindow(hab, hwndStatic))
  1063.          && (pa = CreateAnimationData(hab,
  1064.                                       hwndStatic,
  1065.                                       usAnimCount))
  1066.        )
  1067.     {
  1068.         // switch static to icon mode
  1069.         pa->ulFlags = ANF_ICON;
  1070.         return (pa);
  1071.     }
  1072.  
  1073.     return NULL;
  1074. }
  1075.  
  1076. /*
  1077.  *@@ ctlPrepareAnimation:
  1078.  *      this function turns a regular static icon
  1079.  *      control an animated one. This is the one-shot
  1080.  *      function for animating static icon controls.
  1081.  *
  1082.  *      It calls ctlPrepareStaticIcon first, then uses
  1083.  *      the passed parameters to prepare an animation:
  1084.  *
  1085.  *      pahptr must point to an array of HPOINTERs
  1086.  *      which must contain usAnimCount icon handles.
  1087.  *      If (fStartAnimation == TRUE), the animation
  1088.  *      is started already. Otherwise you'll have
  1089.  *      to call ctlStartAnimation yourself.
  1090.  *
  1091.  *      To create an animated icon, simply create a static
  1092.  *      control with the dialog editor (can be any static,
  1093.  *      e.g. a text control). Then do the following in your code:
  1094.  *
  1095.  *      1) load the dlg box template (using WinLoadDlg);
  1096.  *
  1097.  *      2) load all the icons for the animation in an array of
  1098.  *         HPOINTERs (check xsdLoadAnimation in shutdown.c for an
  1099.  *         example);
  1100.  *
  1101.  *      3) call ctlPrepareAnimation, e.g.:
  1102.  +             ctlPrepareAnimation(WinWindowFromID(hwndDlg, ID_ICON), // get icon hwnd
  1103.  +                             8,                   // no. of icons for the anim
  1104.  +                             &ahptr[0],           // ptr to first icon in the array
  1105.  +                             150,                 // delay
  1106.  +                             TRUE);               // start animation now
  1107.  *
  1108.  *      4) call WinProcessDlg(hwndDlg);
  1109.  *
  1110.  *      5) stop the animation, e.g.:
  1111.  +              ctlStopAnimation(WinWindowFromID(hwndDlg, ID_ICON));
  1112.  *
  1113.  *      6) destroy the dlg window;
  1114.  *
  1115.  *      7) free all the HPOINTERS loaded above (check xsdFreeAnimation
  1116.  *         in shutdown.c for an example).
  1117.  *
  1118.  *      When the icon control is destroyed, the
  1119.  *      subclassed window proc (ctl_fnwpBitmapStatic)
  1120.  *      automatically cleans up resources. However,
  1121.  *      the icons in *pahptr are not freed.
  1122.  */
  1123.  
  1124. BOOL ctlPrepareAnimation(HWND hwndStatic,   // icon hwnd
  1125.                          USHORT usAnimCount,         // no. of anim steps
  1126.                          HPOINTER *pahptr,           // array of HPOINTERs
  1127.                          ULONG ulDelay,              // delay per anim step (in ms)
  1128.                          BOOL fStartAnimation)       // TRUE: start animation now
  1129. {
  1130.     PANIMATIONDATA paNew;
  1131.     if (paNew = ctlPrepareStaticIcon(hwndStatic, usAnimCount))
  1132.     {
  1133.         paNew->ulDelay = ulDelay;
  1134.         // paNew->OldStaticProc already set
  1135.         paNew->hptr = NULLHANDLE;
  1136.         // paNew->rclIcon already set
  1137.         paNew->usAniCurrent = 0;
  1138.         paNew->usAniCount = usAnimCount;
  1139.         memcpy(&paNew->ahptrAniIcons,
  1140.                pahptr,
  1141.                (usAnimCount * sizeof(HPOINTER)));
  1142.  
  1143.         if (fStartAnimation)
  1144.         {
  1145.             WinStartTimer(paNew->hab,
  1146.                           hwndStatic,
  1147.                           1,
  1148.                           ulDelay);
  1149.             WinPostMsg(hwndStatic, WM_TIMER, (MPARAM)1, NULL);
  1150.         }
  1151.     }
  1152.  
  1153.     return (paNew != NULL);
  1154. }
  1155.  
  1156. /*
  1157.  *@@ ctlStartAnimation:
  1158.  *      starts an animation that not currently running. You
  1159.  *      must prepare the animation by calling ctlPrepareAnimation
  1160.  *      first.
  1161.  */
  1162.  
  1163. BOOL ctlStartAnimation(HWND hwndStatic)
  1164. {
  1165.     BOOL brc = FALSE;
  1166.     PANIMATIONDATA pa;
  1167.     if (    (pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER))
  1168.          && (WinStartTimer(pa->hab,
  1169.                            hwndStatic,
  1170.                            1,
  1171.                            pa->ulDelay))
  1172.        )
  1173.     {
  1174.         brc = TRUE;
  1175.         WinPostMsg(hwndStatic, WM_TIMER, (MPARAM)1, NULL);
  1176.     }
  1177.  
  1178.     return brc;
  1179. }
  1180.  
  1181. /*
  1182.  *@@ ctlStopAnimation:
  1183.  *      stops an animation that is currently running.
  1184.  *      This does not free any resources.
  1185.  */
  1186.  
  1187. BOOL ctlStopAnimation(HWND hwndStatic)
  1188. {
  1189.     return (WinStopTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic, 1));
  1190. }
  1191.  
  1192. /*
  1193.  *@@category: Helpers\PM helpers\Window classes\Stretched bitmaps
  1194.  *      See comctl.c and ctlPrepareStretchedBitmap.
  1195.  */
  1196.  
  1197. /* ******************************************************************
  1198.  *
  1199.  *   Bitmap functions
  1200.  *
  1201.  ********************************************************************/
  1202.  
  1203. /*
  1204.  *@@ ctlPrepareStretchedBitmap:
  1205.  *      turns a static control into a bitmap control
  1206.  *      which stretches the bitmap to the size of the
  1207.  *      control when given an SM_SETHANDLE msg.
  1208.  *
  1209.  *      If (fPreserveProportions == TRUE), the control
  1210.  *      will size the bitmap proportionally only. Otherwise,
  1211.  *      it will always stretch the bitmap to the whole
  1212.  *      size of the static control (possibly distorting
  1213.  *      the proportions). See gpihStretchBitmap for
  1214.  *      details.
  1215.  *
  1216.  *      This function subclasses the static control
  1217.  *      with ctl_fnwpBitmapStatic, setting the flags as
  1218.  *      necessary. However, this does not set the bitmap
  1219.  *      to display yet.
  1220.  *      To have a bitmap displayed, send the control
  1221.  *      a SM_SETHANDLE message with the bitmap to be
  1222.  *      displayed in (HBITMAP)mp1 after having used this
  1223.  *      function.
  1224.  *
  1225.  *      ctl_fnwpBitmapStatic will then create a private
  1226.  *      copy of that bitmap, stretched to its own size.
  1227.  *      You can therefore delete the "source" bitmap
  1228.  *      after sending SM_SETHANDLE.
  1229.  *
  1230.  *      You can also send SM_SETHANDLE several times.
  1231.  *      The control will then readjust itself and delete
  1232.  *      its old bitmap.
  1233.  *      But note that GpiWCBitBlt is invoked on that new
  1234.  *      bitmap with every new message, so this is not
  1235.  *      terribly fast.
  1236.  *
  1237.  *      Also note that you should not resize the static
  1238.  *      control after creation, because the bitmap will
  1239.  *      _not_ be adjusted.
  1240.  *
  1241.  *      This returns a PANIMATIONDATA if subclassing succeeded or
  1242.  *      NULL upon errors. You do not need to save this structure;
  1243.  *      it is cleaned up automatically when the control is destroyed.
  1244.  *
  1245.  *@@added V0.9.0 [umoeller]
  1246.  *@@changed V0.9.16 (2001-10-15) [umoeller]: some cleanup
  1247.  */
  1248.  
  1249. PANIMATIONDATA ctlPrepareStretchedBitmap(HWND hwndStatic,
  1250.                                          BOOL fPreserveProportions)
  1251. {
  1252.     PANIMATIONDATA pa;
  1253.     HAB hab;
  1254.     if (    (hwndStatic)
  1255.          && (hab = WinQueryAnchorBlock(hwndStatic))
  1256.          && (WinIsWindow(hab, hwndStatic))
  1257.          && (pa = CreateAnimationData(hab, hwndStatic, 1))
  1258.        )
  1259.     {
  1260.         // switch static to bitmap mode
  1261.         pa->ulFlags = ANF_BITMAP;
  1262.         if (fPreserveProportions)
  1263.             pa->ulFlags |= ANF_PROPORTIONAL;
  1264.         return (pa);
  1265.     }
  1266.  
  1267.     return NULL;
  1268. }
  1269.  
  1270. /*
  1271.  *@@category: Helpers\PM helpers\Window classes\Hotkey entry field
  1272.  *      See comctl.c and ctl_fnwpObjectHotkeyEntryField.
  1273.  */
  1274.  
  1275. /* ******************************************************************
  1276.  *
  1277.  *   Hotkey entry field
  1278.  *
  1279.  ********************************************************************/
  1280.  
  1281. /*
  1282.  *@@ ctl_fnwpHotkeyEntryField:
  1283.  *      this is the window proc for a subclassed entry
  1284.  *      field which displays a description of a keyboard
  1285.  *      combination upon receiving WM_CHAR.
  1286.  *
  1287.  *      Use ctlMakeHotkeyEntryField to turn any existing
  1288.  *      entry field into such an "hotkey entry field".
  1289.  *
  1290.  *      The entry field is deleted on every WM_CHAR that
  1291.  *      comes in.
  1292.  *
  1293.  *      For this to work, this window proc needs the
  1294.  *      cooperation of its owner. Whenever a WM_CHAR
  1295.  *      message is received by the entry field which
  1296.  *      looks like a meaningful key or key combination
  1297.  *      (some filtering is done by this func), then
  1298.  *      the owner of the entry field receives a WM_CONTROL
  1299.  *      message with the new EN_HOTKEY notification code
  1300.  *      (see comctl.h for details).
  1301.  *
  1302.  *      It is then the responsibility of the owner to
  1303.  *      check whether the HOTKEYNOTIFY structure pointed
  1304.  *      to by WM_CONTROL's mp2 is a meaningful keyboard
  1305.  *      combination.
  1306.  *
  1307.  *      If not, the owner must return FALSE, and the
  1308.  *      entry field's contents are deleted.
  1309.  *
  1310.  *      If yes however, the owner must fill the szDescription
  1311.  *      field with a description of the keyboard combination
  1312.  *      (e.g. "Shift+Alt+C") and return TRUE. The entry field
  1313.  *      will then be set to that description.
  1314.  *
  1315.  *@@added V0.9.1 (99-12-19) [umoeller]
  1316.  *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed empty entry field on tab key
  1317.  */
  1318.  
  1319. MRESULT EXPENTRY ctl_fnwpObjectHotkeyEntryField(HWND hwndEdit, ULONG msg, MPARAM mp1, MPARAM mp2)
  1320. {
  1321.     MRESULT mrc = (MPARAM)FALSE; // WM_CHAR not-processed flag
  1322.  
  1323.     // get object window data; this was stored in QWL_USER by
  1324.     // WM_INITDLG of obj_fnwpSettingsObjDetails
  1325.     // PXFOBJWINDATA   pWinData = (PXFOBJWINDATA)WinQueryWindowULong(hwndEdit, QWL_USER);
  1326.     PFNWP           pfnwpOrig = (PFNWP)WinQueryWindowULong(hwndEdit, QWL_USER);
  1327.  
  1328.     switch (msg)
  1329.     {
  1330.         case WM_CHAR:
  1331.         {
  1332.             /*
  1333.                   #define KC_CHAR                    0x0001
  1334.                   #define KC_VIRTUALKEY              0x0002
  1335.                   #define KC_SCANCODE                0x0004
  1336.                   #define KC_SHIFT                   0x0008
  1337.                   #define KC_CTRL                    0x0010
  1338.                   #define KC_ALT                     0x0020
  1339.                   #define KC_KEYUP                   0x0040
  1340.                   #define KC_PREVDOWN                0x0080
  1341.                   #define KC_LONEKEY                 0x0100
  1342.                   #define KC_DEADKEY                 0x0200
  1343.                   #define KC_COMPOSITE               0x0400
  1344.                   #define KC_INVALIDCOMP             0x0800
  1345.             */
  1346.  
  1347.             /*
  1348.                 Examples:           usFlags  usKeyCode
  1349.                     F3                02       22
  1350.                     Ctrl+F4           12       23
  1351.                     Ctrl+Shift+F4     1a       23
  1352.                     Ctrl              12       0a
  1353.                     Alt               22       0b
  1354.                     Shift             0a       09
  1355.             */
  1356.  
  1357.             // USHORT usCommand;
  1358.             // USHORT usKeyCode;
  1359.             USHORT usFlags    = SHORT1FROMMP(mp1);
  1360.             // UCHAR  ucRepeat   = CHAR3FROMMP(mp1);
  1361.             UCHAR  ucScanCode = CHAR4FROMMP(mp1);
  1362.             USHORT usch       = SHORT1FROMMP(mp2);
  1363.             USHORT usvk       = SHORT2FROMMP(mp2);
  1364.  
  1365.             if (
  1366.                     // process only key-down messages
  1367.                     ((usFlags & KC_KEYUP) == 0)
  1368.                     // check flags:
  1369.                     // filter out those ugly composite key (accents etc.)
  1370.                  && ((usFlags & (KC_DEADKEY | KC_COMPOSITE | KC_INVALIDCOMP)) == 0)
  1371.                )
  1372.             {
  1373.                 HOTKEYNOTIFY    hkn;
  1374.                 ULONG           flReturned;
  1375.                 HWND            hwndOwner = WinQueryWindow(hwndEdit, QW_OWNER);
  1376.  
  1377.                 usFlags &= (KC_VIRTUALKEY | KC_CTRL | KC_ALT | KC_SHIFT);
  1378.  
  1379.                 hkn.usFlags = usFlags;
  1380.                 hkn.usvk = usvk;
  1381.                 hkn.usch = usch;
  1382.                 hkn.ucScanCode = ucScanCode;
  1383.  
  1384.                 if (usFlags & KC_VIRTUALKEY)
  1385.                     hkn.usKeyCode = usvk;
  1386.                 else
  1387.                     hkn.usKeyCode = usch;
  1388.  
  1389.                 hkn.szDescription[0] = 0;
  1390.  
  1391.                 flReturned = (ULONG)WinSendMsg(hwndOwner,
  1392.                                                WM_CONTROL,
  1393.                                                MPFROM2SHORT(WinQueryWindowUShort(hwndEdit,
  1394.                                                                                  QWS_ID),
  1395.                                                             EN_HOTKEY),  // new notification code
  1396.                                                (MPARAM)&hkn);
  1397.                 if (flReturned & HEFL_SETTEXT)
  1398.                     WinSetWindowText(hwndEdit, hkn.szDescription);
  1399.                 else if (flReturned & HEFL_FORWARD2OWNER)
  1400.                     WinPostMsg(hwndOwner,
  1401.                                WM_CHAR,
  1402.                                mp1, mp2);
  1403.                 else
  1404.                     // fixed V0.9.16 (2001-12-06) [umoeller]:
  1405.                     // do not clear the entry field if we had HEFL_FORWARD2OWNER
  1406.                     WinSetWindowText(hwndEdit, "");
  1407.  
  1408.                 mrc = (MPARAM)TRUE; // WM_CHAR processed flag;
  1409.             }
  1410.         }
  1411.         break;
  1412.  
  1413.         default:
  1414.             mrc = pfnwpOrig(hwndEdit, msg, mp1, mp2);
  1415.     }
  1416.     return mrc;
  1417. }
  1418.  
  1419. /*
  1420.  *@@ ctlMakeHotkeyEntryField:
  1421.  *      this turns any entry field into a "hotkey entry field"
  1422.  *      by subclassing it with ctl_fnwpObjectHotkeyEntryField.
  1423.  *      See remarks there for details.
  1424.  *
  1425.  *      Returns FALSE upon errors, TRUE otherwise.
  1426.  *
  1427.  *@@added V0.9.1 (99-12-19) [umoeller]
  1428.  */
  1429.  
  1430. BOOL ctlMakeHotkeyEntryField(HWND hwndHotkeyEntryField)
  1431. {
  1432.     PFNWP pfnwpOrig;
  1433.     if (pfnwpOrig = WinSubclassWindow(hwndHotkeyEntryField,
  1434.                                       ctl_fnwpObjectHotkeyEntryField))
  1435.     {
  1436.         WinSetWindowPtr(hwndHotkeyEntryField, QWL_USER, (PVOID)pfnwpOrig);
  1437.         return TRUE;
  1438.     }
  1439.  
  1440.     return FALSE;
  1441. }
  1442.  
  1443. /* ******************************************************************
  1444.  *
  1445.  *   Color rectangle
  1446.  *
  1447.  ********************************************************************/
  1448.  
  1449. static PFNWP G_pfnwpOrigStatic = NULL;
  1450.  
  1451. /*
  1452.  *@@ ctl_fnwpSubclassedColorRect:
  1453.  *      window procedure for subclassed static frames representing
  1454.  *      a color.
  1455.  *
  1456.  *      The control simply paints itself as a rectangle with the
  1457.  *      color specified in its PP_BACKGROUNDCOLOR presentation
  1458.  *      parameter. If a window text is set for the control, it is
  1459.  *      painted with the PP_FOREGROUNDCOLOR color.
  1460.  *
  1461.  *      If the user drags a color onto the control, it notifies
  1462.  *      its owner with the WM_CONTROL message and the EN_CHANGE
  1463.  *      notification code, as with any entry field (since the
  1464.  *      static control knows no notifications, we use that code
  1465.  *      instead).
  1466.  *
  1467.  *@@added V0.9.16 (2002-01-05) [umoeller]
  1468.  *@@changed V0.9.19 (2002-06-02) [umoeller]: moved this here from w_pulse.c
  1469.  */
  1470.  
  1471. static MRESULT EXPENTRY ctl_fnwpSubclassedColorRect(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
  1472. {
  1473.     MRESULT mrc = 0;
  1474.  
  1475.     switch (msg)
  1476.     {
  1477.         case WM_PAINT:
  1478.         {
  1479.             LONG    lColor;
  1480.             RECTL   rclPaint;
  1481.             PSZ     pszText;
  1482.  
  1483.             HPS hps = WinBeginPaint(hwndStatic,
  1484.                                     NULLHANDLE, // HPS
  1485.                                     NULL); // PRECTL
  1486.             gpihSwitchToRGB(hps);
  1487.             WinQueryWindowRect(hwndStatic,
  1488.                                &rclPaint);      // exclusive
  1489.             lColor = winhQueryPresColor(hwndStatic,
  1490.                                         PP_BACKGROUNDCOLOR,
  1491.                                         FALSE,      // no inherit
  1492.                                         SYSCLR_DIALOGBACKGROUND);
  1493.  
  1494.             // make rect inclusive
  1495.             rclPaint.xRight--;
  1496.             rclPaint.yTop--;
  1497.  
  1498.             // draw interior
  1499.             GpiSetColor(hps, lColor);
  1500.             gpihBox(hps,
  1501.                     DRO_FILL,
  1502.                     &rclPaint);
  1503.  
  1504.             // draw frame
  1505.             GpiSetColor(hps, RGBCOL_BLACK);
  1506.             gpihBox(hps,
  1507.                     DRO_OUTLINE,
  1508.                     &rclPaint);
  1509.  
  1510.             if (pszText = winhQueryWindowText(hwndStatic))
  1511.             {
  1512.                 GpiSetColor(hps,
  1513.                             winhQueryPresColor(hwndStatic,
  1514.                                                PP_FOREGROUNDCOLOR,
  1515.                                                FALSE,
  1516.                                                -1));
  1517.                 WinDrawText(hps,
  1518.                             strlen(pszText),
  1519.                             pszText,
  1520.                             &rclPaint,
  1521.                             0,
  1522.                             0,
  1523.                             DT_CENTER | DT_VCENTER | DT_TEXTATTRS);
  1524.  
  1525.                 free(pszText);
  1526.             }
  1527.  
  1528.             WinEndPaint(hps);
  1529.         }
  1530.         break;
  1531.  
  1532.         case WM_PRESPARAMCHANGED:
  1533.             switch ((ULONG)mp1)
  1534.             {
  1535.                 case PP_BACKGROUNDCOLOR:
  1536.                     WinInvalidateRect(hwndStatic,
  1537.                                       NULL,
  1538.                                       FALSE);
  1539.                     // notify owner; since the static control
  1540.                     // doesn't send any notifications, we
  1541.                     // use EN_CHANGED
  1542.                     WinSendMsg(WinQueryWindow(hwndStatic, QW_OWNER),
  1543.                                WM_CONTROL,
  1544.                                MPFROM2SHORT(WinQueryWindowUShort(hwndStatic, QWS_ID),
  1545.                                             EN_CHANGE),
  1546.                                (MPARAM)hwndStatic);
  1547.                 break;
  1548.             }
  1549.         break;
  1550.  
  1551.         default:
  1552.             mrc = G_pfnwpOrigStatic(hwndStatic, msg, mp1, mp2);
  1553.         break;
  1554.     }
  1555.  
  1556.     return mrc;
  1557. }
  1558.  
  1559. /*
  1560.  *@@ ctlMakeColorRect:
  1561.  *      turns a static rectangle into a color rectangle.
  1562.  *
  1563.  *@@added V0.9.19 (2002-06-02) [umoeller]
  1564.  */
  1565.  
  1566. BOOL ctlMakeColorRect(HWND hwndStatic)
  1567. {
  1568.     PFNWP pfnwp;
  1569.  
  1570.     if (pfnwp = WinSubclassWindow(hwndStatic,
  1571.                                   ctl_fnwpSubclassedColorRect))
  1572.     {
  1573.         G_pfnwpOrigStatic = pfnwp;
  1574.         return TRUE;
  1575.     }
  1576.  
  1577.     return FALSE;
  1578. }
  1579.  
  1580.  
  1581.