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

  1.  
  2. /*
  3.  *@@sourcefile cctl.splitwin.c:
  4.  *      implementation for split windows.
  5.  *      See comctl.c for an overview.
  6.  *
  7.  *      This has been extracted from comctl.c with V0.9.3 (2000-05-21) [umoeller].
  8.  *
  9.  *      Note: Version numbering in this file relates to XWorkplace version
  10.  *            numbering.
  11.  *
  12.  *@@header "helpers\comctl.h"
  13.  *@@added V0.9.3 (2000-05-21) [umoeller].
  14.  */
  15.  
  16. /*
  17.  *      Copyright (C) 1997-2000 Ulrich Möller.
  18.  *      This file is part of the "XWorkplace helpers" source package.
  19.  *      This is free software; you can redistribute it and/or modify
  20.  *      it under the terms of the GNU General Public License as published
  21.  *      by the Free Software Foundation, in version 2 as it comes in the
  22.  *      "COPYING" file of the XWorkplace main distribution.
  23.  *      This program is distributed in the hope that it will be useful,
  24.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26.  *      GNU General Public License for more details.
  27.  */
  28.  
  29. #define OS2EMX_PLAIN_CHAR
  30.     // this is needed for "os2emx.h"; if this is defined,
  31.     // emx will define PSZ as _signed_ char, otherwise
  32.     // as unsigned char
  33.  
  34. #define INCL_DOSEXCEPTIONS
  35. #define INCL_DOSPROCESS
  36. #define INCL_DOSSEMAPHORES
  37. #define INCL_DOSERRORS
  38.  
  39. #define INCL_WINWINDOWMGR
  40. #define INCL_WINFRAMEMGR
  41. #define INCL_WINMESSAGEMGR
  42. #define INCL_WININPUT
  43. #define INCL_WINPOINTERS
  44. #define INCL_WINTRACKRECT
  45. #define INCL_WINTIMER
  46. #define INCL_WINSYS
  47.  
  48. #define INCL_WINRECTANGLES      /// xxx temporary
  49.  
  50. #define INCL_WINMENUS
  51. #define INCL_WINSTATICS
  52. #define INCL_WINBUTTONS
  53. #define INCL_WINSTDCNR
  54.  
  55. #define INCL_GPIPRIMITIVES
  56. #define INCL_GPILOGCOLORTABLE
  57. #define INCL_GPILCIDS
  58. #define INCL_GPIPATHS
  59. #define INCL_GPIREGIONS
  60. #define INCL_GPIBITMAPS             // added V0.9.1 (2000-01-04) [umoeller]: needed for EMX headers
  61. #include <os2.h>
  62.  
  63. #include <stdlib.h>
  64. #include <stdio.h>
  65. #include <string.h>
  66. #include <setjmp.h>             // needed for except.h
  67. #include <assert.h>             // needed for except.h
  68.  
  69. #include "setup.h"                      // code generation and debugging options
  70.  
  71. #include "helpers\cnrh.h"
  72. #include "helpers\except.h"             // exception handling
  73. #include "helpers\gpih.h"
  74. #include "helpers\linklist.h"
  75. #include "helpers\winh.h"
  76.  
  77. #include "helpers\comctl.h"
  78.  
  79. #pragma hdrstop
  80.  
  81. /*
  82.  *@@category: Helpers\PM helpers\Window classes\Split windows
  83.  *      See cctl_splitwin.c.
  84.  */
  85.  
  86. /* ******************************************************************
  87.  *
  88.  *   Split windows
  89.  *
  90.  ********************************************************************/
  91.  
  92. /*
  93.  *@@ PaintSplitWindow:
  94.  *      implementation for WM_PAINT in ctl_fnwpSplitWindow.
  95.  *
  96.  *@@added V0.9.1 (2000-02-05) [umoeller]
  97.  */
  98.  
  99. static VOID PaintSplitWindow(HWND hwndSplit)
  100. {
  101.     HPS     hps = WinBeginPaint(hwndSplit, (HPS)0, NULL);
  102.     HWND    hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
  103.     PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar,
  104.                                                              QWL_USER);
  105.  
  106.     if ((pData->hwndLinked1) && (pData->hwndLinked2))
  107.     {
  108.         // "3D-sunk" style?
  109.         if (pData->sbcd.ulCreateFlags & SBCF_3DSUNK)
  110.         {
  111.             // yes: draw sunk frame around split windows
  112.             POINTL  ptl1;
  113.             SWP     swp;
  114.  
  115.             // switch to RGB mode
  116.             GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
  117.  
  118.             WinQueryWindowPos(pData->hwndLinked1, &swp);
  119.             GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0));
  120.             ptl1.x = swp.x - 1;
  121.             ptl1.y = swp.y - 1;
  122.             GpiMove(hps, &ptl1);
  123.             ptl1.y = swp.y + swp.cy;
  124.             GpiLine(hps, &ptl1);
  125.             ptl1.x = swp.x + swp.cx;
  126.             GpiLine(hps, &ptl1);
  127.             GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0));
  128.             ptl1.y = swp.y - 1;
  129.             GpiLine(hps, &ptl1);
  130.             ptl1.x = swp.x - 1;
  131.             GpiLine(hps, &ptl1);
  132.  
  133.             WinQueryWindowPos(pData->hwndLinked2, &swp);
  134.             GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0));
  135.             ptl1.x = swp.x - 1;
  136.             ptl1.y = swp.y - 1;
  137.             GpiMove(hps, &ptl1);
  138.             ptl1.y = swp.y + swp.cy;
  139.             GpiLine(hps, &ptl1);
  140.             ptl1.x = swp.x + swp.cx;
  141.             GpiLine(hps, &ptl1);
  142.             GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0));
  143.             ptl1.y = swp.y - 1;
  144.             GpiLine(hps, &ptl1);
  145.             ptl1.x = swp.x - 1;
  146.             GpiLine(hps, &ptl1);
  147.         }
  148.     }
  149.  
  150.     WinEndPaint(hps);
  151. }
  152.  
  153. /*
  154.  *@@ ctl_fnwpSplitWindow:
  155.  *      this is the window procedure of the "invisible"
  156.  *      split window. One of these windows is created
  157.  *      for each split view and has exactly three children:
  158.  *
  159.  *      1)  the split bar (ctl_fnwpSplitBar);
  160.  *
  161.  *      2)  the left or bottom window linked to the split bar;
  162.  *
  163.  *      3)  the right or top window linked to the split bar.
  164.  *
  165.  *      See ctlCreateSplitWindow for more info and a detailed
  166.  *      window hierarchy.
  167.  *
  168.  *      This stand-alone window procedure must be registered.
  169.  *      ctlCreateSplitWindow does this for you.
  170.  *
  171.  *@@added V0.9.0 [umoeller]
  172.  */
  173.  
  174. MRESULT EXPENTRY ctl_fnwpSplitWindow(HWND hwndSplit, ULONG msg, MPARAM mp1, MPARAM mp2)
  175. {
  176.     MRESULT mrc = (MRESULT)0;
  177.  
  178.     switch(msg)
  179.     {
  180.         /*
  181.          * WM_WINDOWPOSCHANGED:
  182.          *
  183.          */
  184.  
  185.         case WM_WINDOWPOSCHANGED:
  186.         {
  187.             // this msg is passed two SWP structs:
  188.             // one for the old, one for the new data
  189.             // (from PM docs)
  190.             PSWP pswpNew = (PSWP)mp1;
  191.  
  192.             if (pswpNew->fl & SWP_SIZE)
  193.             {
  194.                 // _Pmpf(("ctl_fnwpSplitWindow, WM_WINDOWPOSCHANGED"));
  195.                 ctlUpdateSplitWindow(hwndSplit);
  196.             }
  197.             mrc = WinDefWindowProc(hwndSplit, msg, mp1, mp2);
  198.         break; }
  199.  
  200.         /*
  201.          * WM_PAINT:
  202.          *
  203.          */
  204.  
  205.         case WM_PAINT:
  206.             PaintSplitWindow(hwndSplit);
  207.         break;
  208.  
  209.         /*
  210.          *@@ SPLM_SETLINKS:
  211.          *      this specifies the windows which the
  212.          *      split window will link. This updates
  213.          *      the internal SPLITBARDATA and changes
  214.          *      the parents of the two subwindows to
  215.          *      the split window.
  216.          *
  217.          *      Parameters:
  218.          *      -- HWND mp1:   left or bottom subwindow
  219.          *      -- HWND mp2:   right or top  subwindow
  220.          */
  221.  
  222.         case SPLM_SETLINKS:
  223.         {
  224.             HWND    hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
  225.             PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar,
  226.                                                                      QWL_USER);
  227.             if (pData)
  228.             {
  229.                 pData->hwndLinked1 = (HWND)mp1;
  230.                 pData->hwndLinked2 = (HWND)mp2;
  231.                 // change parents of the windows to link
  232.                 WinSetParent(pData->hwndLinked1, hwndSplit,
  233.                              FALSE);        // no redraw
  234.                 WinSetParent(pData->hwndLinked2, hwndSplit,
  235.                              FALSE);        // no redraw
  236.             }
  237.         break; }
  238.  
  239.         default:
  240.             mrc = WinDefWindowProc(hwndSplit, msg, mp1, mp2);
  241.     }
  242.  
  243.     return mrc;
  244. }
  245.  
  246. /*
  247.  *@@ TrackSplitBar:
  248.  *      implementation for WM_BUTTON1DOWN/WM_BUTTON2DOWN in ctl_fnwpSplitBar.
  249.  *
  250.  *@@added V0.9.1 (2000-02-05) [umoeller]
  251.  */
  252.  
  253. static VOID TrackSplitBar(HWND hwndBar,
  254.                           PSPLITBARDATA pData)
  255. {
  256.     TRACKINFO   track;
  257.     RECTL       rclBar;
  258.  
  259.     track.cxBorder = 2;
  260.     track.cyBorder = 2;
  261.     track.cxGrid   = 1;
  262.     track.cyGrid   = 1;
  263.     track.cxKeyboard = 8;
  264.     track.cyKeyboard = 8;
  265.  
  266.     WinQueryWindowRect(hwndBar, &rclBar);
  267.     WinMapWindowPoints(hwndBar,
  268.                        HWND_DESKTOP,        // map to screen coords.
  269.                        (PPOINTL)&rclBar,
  270.                        2);          // 2 points == rectangle
  271.  
  272.     // limit tracking space to parent window;
  273.     // this is either the client or another split window
  274.     WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
  275.                        &track.rclBoundary);
  276.     WinMapWindowPoints(pData->sbcd.hwndParentAndOwner,
  277.                        HWND_DESKTOP,        // map to screen coords.
  278.                        (PPOINTL)&track.rclBoundary,
  279.                        2);      // 2 points, since we have a RECTL
  280.  
  281.     // decrease tracking limits by what was
  282.     // specified by the caller
  283.     if (pData->sbcd.ulCreateFlags & SBCF_HORIZONTAL)
  284.     {
  285.         // horizontal split bar
  286.         track.rclBoundary.yBottom += pData->sbcd.ulLeftOrBottomLimit;
  287.         track.rclBoundary.yTop -= pData->sbcd.ulRightOrTopLimit;
  288.         track.rclBoundary.xLeft = rclBar.xLeft;
  289.         track.rclBoundary.xRight = rclBar.xRight;
  290.     }
  291.     else
  292.     {
  293.         // vertical split bar
  294.         track.rclBoundary.xLeft += pData->sbcd.ulLeftOrBottomLimit;
  295.         track.rclBoundary.xRight -= pData->sbcd.ulRightOrTopLimit;
  296.         track.rclBoundary.yBottom = rclBar.yBottom;
  297.         track.rclBoundary.yTop = rclBar.yTop;
  298.     }
  299.  
  300.     // initial tracking rectangle = split bar
  301.     memcpy(&track.rclTrack, &rclBar, sizeof(rclBar));
  302.  
  303.     track.ptlMinTrackSize.x =
  304.     track.ptlMaxTrackSize.x = track.rclTrack.xRight
  305.                             - track.rclTrack.xLeft;     // width of status bar
  306.     track.ptlMinTrackSize.y =
  307.     track.ptlMaxTrackSize.y = track.rclTrack.yTop
  308.                             - track.rclTrack.yBottom;   // height of status bar
  309.  
  310.     track.fs = TF_MOVE | TF_ALLINBOUNDARY;
  311.  
  312.     // now do the tracking: this is a modal
  313.     // operation and returns only after the
  314.     // mouse has been released
  315.     if (WinTrackRect(HWND_DESKTOP, 0, &track))
  316.     {
  317.         // OK, successfully moved: reposition the
  318.         // windows which are linked to the split bar
  319.  
  320.         if (pData->sbcd.ulCreateFlags & SBCF_HORIZONTAL)
  321.         {
  322.             // horizontal split bar
  323.             ULONG ulNewYPos = track.rclTrack.yBottom
  324.                             - track.rclBoundary.yBottom;
  325.             // add the limit specified by the caller
  326.             ulNewYPos += pData->sbcd.ulLeftOrBottomLimit;
  327.  
  328.             if (pData->sbcd.ulCreateFlags & SBCF_PERCENTAGE)
  329.             {
  330.                 // status bar pos is determined by
  331.                 // a percentage:
  332.                 // well, we'll need a new percentage then
  333.                 RECTL rclClient;
  334.                 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
  335.                                    &rclClient);
  336.                 pData->sbcd.lPos = ulNewYPos
  337.                                    * 100 / (rclClient.yTop - rclClient.yBottom);
  338.             }
  339.             else
  340.                 // absolute split bar positioning:
  341.                 if (pData->sbcd.lPos > 0)
  342.                     // from bottom: easy
  343.                     pData->sbcd.lPos = ulNewYPos;
  344.                 else
  345.                 {
  346.                     // negative -> from top:
  347.                     RECTL rclClient;
  348.                     WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
  349.                                        &rclClient);
  350.                     // calc new negative
  351.                     pData->sbcd.lPos = ulNewYPos - rclClient.yTop;
  352.                 }
  353.         }
  354.         else
  355.         {
  356.             // vertical split bar:
  357.             ULONG ulNewXPos = track.rclTrack.xLeft
  358.                             - track.rclBoundary.xLeft;
  359.             // add the limit specified by the caller
  360.             ulNewXPos += pData->sbcd.ulLeftOrBottomLimit;
  361.  
  362.             if (pData->sbcd.ulCreateFlags & SBCF_PERCENTAGE)
  363.             {
  364.                 // status bar pos is determined by
  365.                 // a percentage:
  366.                 // well, we'll need a new percentage then
  367.                 RECTL rclClient;
  368.                 WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
  369.                                    &rclClient);
  370.                 pData->sbcd.lPos = ulNewXPos
  371.                                    * 100 / (rclClient.xRight - rclClient.xLeft);
  372.             }
  373.             else
  374.                 // absolute split bar positioning:
  375.                 if (pData->sbcd.lPos > 0)
  376.                     // from left: easy
  377.                     pData->sbcd.lPos = ulNewXPos;
  378.                 else
  379.                 {
  380.                     // negative -> from right:
  381.                     RECTL rclClient;
  382.                     WinQueryWindowRect(pData->sbcd.hwndParentAndOwner,
  383.                                        &rclClient);
  384.                     // calc new negative
  385.                     pData->sbcd.lPos = ulNewXPos - rclClient.xRight;
  386.                 }
  387.         }
  388.  
  389.         // update parent (== "split window")
  390.         ctlUpdateSplitWindow(WinQueryWindow(hwndBar, QW_PARENT));
  391.     }
  392. }
  393.  
  394. /*
  395.  *@@ PaintSplitBar:
  396.  *      implementation for WM_PAINT in ctl_fnwpSplitBar.
  397.  *
  398.  *@@added V0.9.1 (2000-02-05) [umoeller]
  399.  *@@changed V0.9.1 (2000-02-05) [umoeller]: fixed paint errors with sunken 3D style
  400.  */
  401.  
  402. static VOID PaintSplitBar(HWND hwndBar,
  403.                           PSPLITBARDATA pData)
  404. {
  405.     HPS     hps;
  406.     RECTL   rcl,
  407.             rclBar;
  408.     POINTL  ptl1;
  409.     hps = WinBeginPaint(hwndBar, NULLHANDLE, &rcl);
  410.     WinQueryWindowRect(hwndBar, &rclBar);
  411.     // switch to RGB mode
  412.     GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
  413.  
  414.     // inflate top and right, because WinFillRect
  415.     // considers this inclusive
  416.     /* rclBar.xRight++;
  417.     rclBar.yTop++; */
  418.  
  419.     WinFillRect(hps,
  420.                 &rclBar,
  421.                 WinQuerySysColor(HWND_DESKTOP,
  422.                                  SYSCLR_INACTIVEBORDER,
  423.                                  0));
  424.  
  425.     if ((pData->sbcd.ulCreateFlags & SBCF_3DSUNK) == 0)
  426.     {
  427.         GpiSetColor(hps,
  428.                     WinQuerySysColor(HWND_DESKTOP,
  429.                                      SYSCLR_BUTTONLIGHT,
  430.                                      0));
  431.         // draw left border (bottom to up)
  432.         ptl1.x = 0;
  433.         ptl1.y = 0;
  434.         GpiMove(hps, &ptl1);
  435.         ptl1.y = (rclBar.yTop - rclBar.yBottom) - 1;
  436.         if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
  437.             // vertical:
  438.             GpiLine(hps, &ptl1);
  439.         else
  440.             GpiMove(hps, &ptl1);
  441.  
  442.         // draw top border (to right)
  443.         ptl1.x = (rclBar.xRight - rclBar.xLeft) - 1;
  444.         if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
  445.             // vertical:
  446.             GpiMove(hps, &ptl1);
  447.         else
  448.             GpiLine(hps, &ptl1);
  449.  
  450.         GpiSetColor(hps,
  451.                     WinQuerySysColor(HWND_DESKTOP,
  452.                                      SYSCLR_BUTTONDARK,
  453.                                      0));
  454.         // draw right border (to bottom)
  455.         ptl1.y = 0;
  456.         if (pData->sbcd.ulCreateFlags & SBCF_VERTICAL)
  457.             // vertical:
  458.             GpiLine(hps, &ptl1);
  459.         else
  460.             GpiMove(hps, &ptl1);
  461.  
  462.         if (!(pData->sbcd.ulCreateFlags & SBCF_VERTICAL))
  463.         {
  464.             // horizontal:
  465.             // draw bottom border
  466.             ptl1.x = 0;
  467.             GpiLine(hps, &ptl1);
  468.         }
  469.     }
  470.  
  471.     WinEndPaint(hps);
  472. }
  473.  
  474. /*
  475.  *@@ ctl_fnwpSplitBar:
  476.  *      window procedure for all split bars, horizontal and vertical.
  477.  *      This paints the split bar and handles dragging the split bar
  478.  *      and repositioning the "linked" windows afterwards.
  479.  *
  480.  *      This is not a stand-alone window procedure, but must only
  481.  *      be used with ctlCreateSplitWindow, which creates and subclasses
  482.  *      a static control as necessary.
  483.  *
  484.  *      See ctlCreateSplitWindow for more info and a detailed
  485.  *      window hierarchy.
  486.  *
  487.  *@@added V0.9.0 [umoeller]
  488.  *@@changed V0.9.1 (99-12-07): fixed memory leak
  489.  *@@changed V0.9.9 (2001-02-01) [lafaix]: added MB2 drag
  490.  *@@changed V0.9.14 (2001-08-21) [umoeller]: SBCF_MOVEABLE was always ignored, fixed
  491.  */
  492.  
  493. MRESULT EXPENTRY ctl_fnwpSplitBar(HWND hwndBar, ULONG msg, MPARAM mp1, MPARAM mp2)
  494. {
  495.     PSPLITBARDATA pData = (PSPLITBARDATA)WinQueryWindowULong(hwndBar, QWL_USER);
  496.  
  497.     PFNWP   OldStaticProc = NULL;
  498.  
  499.     MRESULT mrc = NULL;
  500.  
  501.     if (pData)
  502.     {
  503.         OldStaticProc = pData->OldStaticProc;
  504.  
  505.         switch(msg)
  506.         {
  507.             /*
  508.              * WM_MOUSEMOVE:
  509.              *      change mouse pointer when over split bar.
  510.              */
  511.  
  512.             case WM_MOUSEMOVE:
  513.                 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
  514.                     // V0.9.14 (2001-08-21) [umoeller]
  515.                     // split bar is moveable:
  516.                     pData->hptrOld = WinSetPointer(HWND_DESKTOP, pData->hptrMove);
  517.                 else
  518.                     mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
  519.             break;
  520.  
  521.             /*
  522.              * WM_BUTTON1DOWN:
  523.              *      this will start moving the split bar.
  524.              *      We use WinTrackRect for this.
  525.              */
  526.  
  527.             case WM_BUTTON1DOWN:
  528.             case WM_BUTTON2DOWN:
  529.                 if (pData->sbcd.ulCreateFlags & SBCF_MOVEABLE)
  530.                     // V0.9.14 (2001-08-21) [umoeller]
  531.                     // split bar is moveable:
  532.                     TrackSplitBar(hwndBar, pData);
  533.                 else
  534.                     mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
  535.             break;
  536.  
  537.             /*
  538.              * WM_PAINT:
  539.              *      paint the split bar
  540.              */
  541.  
  542.             case WM_PAINT:
  543.                 PaintSplitBar(hwndBar, pData);
  544.             break;
  545.  
  546.             case WM_DESTROY:
  547.                 free (pData);       // added V0.9.1 (99-12-07)
  548.                 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
  549.             break;
  550.  
  551.             default:
  552.                 mrc = OldStaticProc(hwndBar, msg, mp1, mp2);
  553.         }
  554.     }
  555.     return mrc;
  556. }
  557.  
  558. /*
  559.  *@@ ctlCreateSplitWindow:
  560.  *      this creates a new split window view according to
  561.  *      the given SPLITBARCDATA structure. A split view
  562.  *      links two existing windows together with a split
  563.  *      bar in between, which can be moved with the mouse.
  564.  *
  565.  *      Split windows are used for example in XWorkplace
  566.  *      for the WPS class list object and in WarpIN for
  567.  *      the database.
  568.  *
  569.  *      After the split view has been created using this
  570.  *      function, all window resizing/positioning is
  571.  *      automatically performed, that is:
  572.  *
  573.  *      a) if the parent window of the split view (e.g.
  574.  *         the frame) is resized, the subwindows are
  575.  *         adjusted;
  576.  *
  577.  *      b) if the split bar is moved, the linked windows
  578.  *         are adjusted also.
  579.  *
  580.  *      <B>Creating a split view</B>
  581.  *
  582.  *      To create a split view, you need to perform the
  583.  *      following steps:
  584.  *
  585.  *      1)  Create two windows that shall be separated by
  586.  *          a split bar. These can be _any_ PM windows:
  587.  *          a simple control window (e.g. a container),
  588.  *          a frame you have created, or even a dialog
  589.  *          which has been loaded using WinLoadDlg.
  590.  *
  591.  *          Note: With frame windows, make sure they have the
  592.  *          FCF_NOBYTEALIGN flag set, or they'll look funny.
  593.  *
  594.  *      2)  Fill a SPLITBARCDATA structure with the necessary
  595.  *          data for the split view (see comctl.h for details).
  596.  *
  597.  *      3)  Call this function with that structure, which creates
  598.  *          and returns the "split window", the invisible parent
  599.  *          of the windows to be split.
  600.  *          See the window hierarchy below.
  601.  *
  602.  *      4)  Send a SPLM_SETLINKS message to the split window
  603.  *          (returned by this function), with the two windows
  604.  *          to be linked in mp1 and mp2. This has been implemented
  605.  *          using a message so that you can re-link windows later
  606.  *          at any time. (This is how the XWorkplace "WPS Class List"
  607.  *          window does it when the settings notebook is changed.)
  608.  *
  609.  *          Sending this message will change parentship (not ownership)
  610.  *          of those two windows to the invisible split window.
  611.  *
  612.  *      5)  Position the split window (which is returned from this
  613.  *          function) within your existing windows using
  614.  *          WinSetWindowPos(..., SWP_SIZE), which will automatically
  615.  *          reposition the two linked subwindows of the split window.
  616.  *          This works because the window procedure of the split
  617.  *          window (ctl_fnwpSplitWindow) handles WM_WINDOWPOSCHANGED to
  618.  *          recalculate the positions of the child windows.
  619.  *
  620.  *      This function returns the HWND of the "split window".
  621.  *      The handle of the split _bar_, if you absolutely need it,
  622.  *      can be found using
  623.  +          WinWindowFromID(hwndWhatThisReturns, ID_SPLITBAR).
  624.  *
  625.  *      After SPLM_SETLINKS, the following window hierarchy
  626.  *      is established (parentship):
  627.  *
  628.  +      SPLITBARCDATA.hwndClient (whatever you have specified;
  629.  +         |                      e.g. FID_CLIENT of your frame window.
  630.  +         |                      You must intercept WM_SIZE and
  631.  +         |                      call WinSetWindowPos on the "split window".)
  632.  +         |
  633.  +         +--  hwndReturn ("split window" created and returned by this
  634.  +                 |       function; uses ctl_fnwpSplitWindow)
  635.  +                 |
  636.  +                 +-- hwndLink1 with SPLM_SETLINKS (parent changed)
  637.  +                 |      |
  638.  +                 |      +-- ... (your child windows remain untouched)
  639.  +                 |
  640.  +                 +-- ID_SPLITBAR (uses ctl_fnwpSplitBar)
  641.  +                 |
  642.  +                 +-- hwndLink2 with SPLM_SETLINKS (parent changed)
  643.  +                        |
  644.  +                        +-- ... (your child windows remain untouched)
  645.  *
  646.  *      Note that only parentship of hwndSplit1 and hwndSplit2
  647.  *      is changed. Ownership remains untouched. So you can specify
  648.  *      any window as the owner of hwndLink1/2 so that messages
  649.  *      can be forwarded properly.
  650.  *
  651.  *      After the above hierarchy has been established, there are
  652.  *      two situations where the "linked" windows will be repositioned
  653.  *      and/or resized:
  654.  *
  655.  *      1)  ctl_fnwpSplitBar will automatically resize and reposition
  656.  *          the left and right "linked" windows if the user moves the
  657.  *          split bar.
  658.  *
  659.  *      2)  If the "split window" itself receives WM_WINDOWPOSCHANGED,
  660.  *          e.g. because WinSetWindowPos has been invoked on the
  661.  *          split window (which you should do when WM_SIZE is received
  662.  *          by your parent window), the "linked" windows will
  663.  *          automatically be repositioned and resized. This is done
  664.  *          in ctl_fnwpSplitWindow.
  665.  *
  666.  *      As a result, you must implement the following in your window
  667.  *      procedures for the windows passed to this function:
  668.  *
  669.  *      1)  The window procedure of SPLITBARCDATA.hwndClient (the
  670.  *          parent of the split window) must intercept WM_SIZE and
  671.  *          do a WinSetWindowPos on the split window. This will
  672.  *          readjust the split bar and SPLITBARCDATA.hwndLink1/2.
  673.  *
  674.  *      2)  The two linked windows should in turn handle WM_SIZE
  675.  *          correctly because whenever the split window is resized,
  676.  *          it invokes a WinSetWindowPos on SPLITBARCDATA.hwndLink1/2
  677.  *          in turn.
  678.  *
  679.  *          If you're using a single standard control as your subwindow
  680.  *          (e.g. a container), this is no problem. However, if you
  681.  *          specify frame windows, you might want to reposition
  682.  *          the controls in those windows in turn.
  683.  *
  684.  *          Note that dialog windows do not receive WM_SIZE;
  685.  *          you'll need to handle WM_WINDOWPOSCHANGED instead.
  686.  *
  687.  *      3)  Your window procedure should forward WM_SYSCOMMAND to
  688.  *          the owner (top) window so that Alt-F4 etc. hotkeys
  689.  *          still work.
  690.  *
  691.  *      If you wish to <B>nest split windows,</B> you can do so by
  692.  *      specifying the "split window" (the HWND which is returned
  693.  *      by this function) as the "hwndLink1/2" to another call
  694.  *      of this function. This way you can create a complex
  695.  *      split window hierarchy (similar to what Netscape does
  696.  *      with the FRAMESET tag). So to create a split view like
  697.  *      this:
  698.  *
  699.  +          +---------+------------+
  700.  +          |         |            |
  701.  +          |         |  2         |
  702.  +          |         |            |
  703.  +          |  1      +------------+
  704.  +          |         |            |
  705.  +          |         |  3         |
  706.  +          |         |            |
  707.  +          +---------+------------+
  708.  *
  709.  *      First invoke ctlCreateSplitWindow to link "2" and "3"
  710.  *      together (creating a horizontal split bar), which returns
  711.  *      a "split window"; then link "1" and that split window
  712.  *      together again.
  713.  *
  714.  *@@added V0.9.0 [umoeller]
  715.  */
  716.  
  717. HWND ctlCreateSplitWindow(HAB hab,
  718.                           PSPLITBARCDATA psbcd) // in: split window control data
  719. {
  720.     HWND    hwndSplit = NULLHANDLE,
  721.             hwndBar = NULLHANDLE;
  722.     static  s_Registered = FALSE;
  723.  
  724.     if (psbcd)
  725.     {
  726.         // register "split window" class
  727.         if (!s_Registered)
  728.         {
  729.             WinRegisterClass(hab,
  730.                              WC_SPLITWINDOW,
  731.                              ctl_fnwpSplitWindow,
  732.                              CS_SIZEREDRAW | CS_SYNCPAINT,
  733.                              0);        // additional bytes to reserve
  734.             s_Registered = TRUE;
  735.         }
  736.  
  737.         if (    (hwndSplit = WinCreateWindow(psbcd->hwndParentAndOwner,  // parent
  738.                                              WC_SPLITWINDOW,
  739.                                              "",
  740.                                              WS_VISIBLE,
  741.                                              0, 0, 10, 10,
  742.                                              psbcd->hwndParentAndOwner,  // owner
  743.                                              HWND_TOP,
  744.                                              psbcd->ulSplitWindowID,
  745.                                              NULL,
  746.                                              NULL))
  747.              && (hwndBar = WinCreateWindow(psbcd->hwndParentAndOwner,  // parent
  748.                                            WC_STATIC,
  749.                                            "",
  750.                                            WS_VISIBLE            // wnd style
  751.                                              | SS_TEXT,
  752.                                            0, 0, 10, 10,
  753.                                            psbcd->hwndParentAndOwner,  // owner
  754.                                            HWND_TOP,
  755.                                            ID_SPLITBAR,          // win ID
  756.                                            NULL,                 // cdata
  757.                                            NULL))                // presparams
  758.            )
  759.         {
  760.             // create SPLITBARDATA to store in split bar's QWL_USER
  761.             PSPLITBARDATA pData;
  762.             if (pData = (PSPLITBARDATA)malloc(sizeof(SPLITBARDATA)))
  763.             {
  764.                 // set parent for split bar
  765.                 WinSetParent(hwndBar, hwndSplit, FALSE);
  766.  
  767.                 memset(pData, 0, sizeof(SPLITBARDATA));
  768.  
  769.                 // copy control data
  770.                 memcpy(&(pData->sbcd), psbcd, sizeof(SPLITBARCDATA));
  771.                 // set other data
  772.                 /* WinQueryWindowRect(hwndBar, &(pData->rclBar));
  773.                 (pData->rclBar.xRight)--;
  774.                 (pData->rclBar.yTop)--; */
  775.                 // subclass static control to make it a split bar
  776.                 pData->OldStaticProc = WinSubclassWindow(hwndBar, ctl_fnwpSplitBar);
  777.                 pData->hptrOld = NULLHANDLE;
  778.                 pData->hptrMove = WinQuerySysPointer(HWND_DESKTOP,
  779.                             (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
  780.                                 ? SPTR_SIZENS
  781.                                 : SPTR_SIZEWE,
  782.                             FALSE);     // don't make copy
  783.                 pData->fCaptured = FALSE;
  784.                 pData->hwndLinked1 =
  785.                 pData->hwndLinked2 = NULLHANDLE;
  786.  
  787.                 WinSetWindowULong(hwndBar, QWL_USER, (ULONG)pData);
  788.             }
  789.         }
  790.     }
  791.  
  792.     return hwndSplit;
  793. }
  794.  
  795. /*
  796.  *@@ ctlUpdateSplitWindow:
  797.  *      this function takes a split window as input and
  798.  *      repositions all its children (the split bars
  799.  *      and the other subwindows which are separated by the
  800.  *      split bars) according to the parent.
  801.  *
  802.  *      This function gets called from ctl_fnwpSplitWindow
  803.  *      when WM_SIZE is received. Normally, you won't have
  804.  *      to call this function independently; you should do
  805.  *      a WinSetWindowPos on the split window instead.
  806.  *
  807.  *@@added V0.9.0 [umoeller]
  808.  */
  809.  
  810. BOOL ctlUpdateSplitWindow(HWND hwndSplit)
  811. {
  812.     BOOL    brc = FALSE;
  813.     HWND    hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR);
  814.  
  815.     if (hwndSplitBar)
  816.     {
  817.         PSPLITBARDATA psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER);
  818.  
  819.         if (psbd)
  820.         {
  821.             PSPLITBARCDATA  psbcd = &(psbd->sbcd);
  822.             RECTL           rclSplit,
  823.                             rclBar;
  824.             ULONG           ul3DOfs = 0;
  825.  
  826.             // _Pmpf(("Entering ctlUpdateSplitWindow for hwndSplit 0x%lX", hwndSplit));
  827.  
  828.             // query the rectangle of the split window's parent;
  829.             // this is either the client or another split window
  830.             WinQueryWindowRect(hwndSplit, &rclSplit);
  831.  
  832.             /* _Pmpf(("  rect: %d, %d, %d, %d",
  833.                             rclSplit.xLeft,
  834.                             rclSplit.yBottom,
  835.                             rclSplit.xRight - rclSplit.xLeft,
  836.                             rclSplit.yTop - rclSplit.yBottom)); */
  837.  
  838.             // set anti-recursion flag;
  839.             // this is neccessary, because ctl_fnwpSplitWindow
  840.             // calls this func again when
  841.             // WM_WINDOWPOSCHANGED comes in
  842.             // psbd->fNoAdjust = TRUE;
  843.             // set split window to the same
  844.             /* WinSetWindowPos(hwndSplit, HWND_TOP,
  845.                             rclSplit.xLeft,
  846.                             rclSplit.yBottom,
  847.                             rclSplit.xRight - rclSplit.xLeft,
  848.                             rclSplit.yTop - rclSplit.yBottom,
  849.                             SWP_MOVE | SWP_SIZE); */
  850.             // psbd->fNoAdjust = FALSE;
  851.  
  852.             // update split bar
  853.             if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
  854.             {
  855.                 // _Pmpf(("  Calc horizontal"));
  856.                 // horizontal split bar:
  857.                 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
  858.                     // take height of client and apply percentage
  859.                     rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
  860.                                     * psbcd->lPos
  861.                                     / 100;
  862.                 else
  863.                     if (psbcd->lPos > 0)
  864.                         // offset from bottom:
  865.                         rclBar.yBottom = psbcd->lPos;
  866.                     else
  867.                         // offset from right:
  868.                         rclBar.yBottom = (rclSplit.yTop - rclSplit.yBottom)
  869.                                          + psbcd->lPos;  // which is negative
  870.  
  871.                 rclBar.yTop = rclBar.yBottom + WinQuerySysValue(HWND_DESKTOP,
  872.                                                                 SV_CXSIZEBORDER);
  873.                 rclBar.xLeft = 0;
  874.                 // take width of client
  875.                 rclBar.xRight = (rclSplit.xRight - rclSplit.xLeft);
  876.             }
  877.             else
  878.             {
  879.                 // _Pmpf(("  Calc vertical"));
  880.                 // vertical split bar:
  881.                 if (psbcd->ulCreateFlags & SBCF_PERCENTAGE)
  882.                     // take width of client and apply percentage
  883.                     rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
  884.                                     * psbcd->lPos
  885.                                     / 100;
  886.                 else
  887.                     if (psbcd->lPos > 0)
  888.                         // offset from left:
  889.                         rclBar.xLeft = psbcd->lPos;
  890.                     else
  891.                         // offset from right:
  892.                         rclBar.xLeft = (rclSplit.xRight - rclSplit.xLeft)
  893.                                        + psbcd->lPos;  // which is negative
  894.  
  895.                 rclBar.xRight = rclBar.xLeft + WinQuerySysValue(HWND_DESKTOP,
  896.                                                                 SV_CXSIZEBORDER);
  897.                 rclBar.yBottom = 0;
  898.                 // take height of client
  899.                 rclBar.yTop = (rclSplit.yTop - rclSplit.yBottom);
  900.             }
  901.  
  902.             // reposition split bar
  903.             brc = WinSetWindowPos(hwndSplitBar,
  904.                                   HWND_TOP,
  905.                                   rclBar.xLeft,
  906.                                   rclBar.yBottom,
  907.                                   rclBar.xRight - rclBar.xLeft,
  908.                                   rclBar.yTop - rclBar.yBottom,
  909.                                   SWP_MOVE | SWP_SIZE);
  910.  
  911.             /* _Pmpf(("  Set splitbar hwnd %lX to %d, %d, %d, %d; rc: %d",
  912.                             hwndSplitBar,
  913.                             rclBar.xLeft,
  914.                             rclBar.yBottom,
  915.                             rclBar.xRight - rclBar.xLeft,
  916.                             rclBar.yTop - rclBar.yBottom,
  917.                             brc)); */
  918.  
  919.             // reposition left/bottom window of split bar
  920.             if (psbcd->ulCreateFlags & SBCF_3DSUNK)
  921.                 ul3DOfs = 1;
  922.             // else 0
  923.  
  924.             // now reposition the linked windows
  925.             if (psbcd->ulCreateFlags & SBCF_HORIZONTAL)
  926.             {
  927.                 // horizontal:
  928.                 // reposition bottom window of split bar
  929.                 WinSetWindowPos(psbd->hwndLinked1,
  930.                                 HWND_TOP,
  931.                                 ul3DOfs,
  932.                                 ul3DOfs,
  933.                                 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
  934.                                 rclBar.yBottom - ul3DOfs*2,       // the window rect is non-inclusive
  935.                                 SWP_MOVE | SWP_SIZE);
  936.  
  937.                 // reposition top window of split bar
  938.                 WinSetWindowPos(psbd->hwndLinked2,
  939.                                 HWND_TOP,
  940.                                 ul3DOfs,
  941.                                 rclBar.yTop + ul3DOfs,    // the window rect is non-inclusive
  942.                                 rclBar.xRight - rclBar.xLeft - ul3DOfs*2,
  943.                                 rclSplit.yTop - rclBar.yTop - ul3DOfs*2,
  944.                                 SWP_MOVE | SWP_SIZE);
  945.             }
  946.             else
  947.             {
  948.                 // vertical:
  949.                 // reposition left window of split bar
  950.                 WinSetWindowPos(psbd->hwndLinked1,
  951.                                 HWND_TOP,
  952.                                 ul3DOfs,
  953.                                 ul3DOfs,
  954.                                 rclBar.xLeft - ul3DOfs*2,       // the window rect is non-inclusive
  955.                                 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
  956.                                 SWP_MOVE | SWP_SIZE);
  957.  
  958.                 // reposition right window of split bar
  959.                 WinSetWindowPos(psbd->hwndLinked2,
  960.                                 HWND_TOP,
  961.                                 rclBar.xRight + ul3DOfs,    // the window rect is non-inclusive
  962.                                 ul3DOfs,
  963.                                 rclSplit.xRight - rclBar.xRight - ul3DOfs*2,
  964.                                 rclBar.yTop - rclBar.yBottom - ul3DOfs*2,
  965.                                 SWP_MOVE | SWP_SIZE);
  966.             }
  967.  
  968.             // repaint split window (3D frame)
  969.             WinInvalidateRect(hwndSplit,
  970.                               NULL,         // all
  971.                               FALSE);       // don't repaint children
  972.         }
  973.     }
  974.  
  975.     return brc;
  976. }
  977.  
  978. /*
  979.  *@@ ctlQuerySplitPos:
  980.  *      this returns the position of the split bar
  981.  *      (the child control of hwndSplit; see ctlCreateSplitWindow
  982.  *      for the window hierarchy).
  983.  *
  984.  *      The meaning of the return value depends on what you
  985.  *      specified with ulCreateFlags in the SPLITBARCDATA
  986.  *      structure passed to ctlCreateSplitWindow, that is,
  987.  *      it can be a percentage or an offset from the left
  988.  *      or from the right of the split window.
  989.  *
  990.  *      This returns NULL upon errors.
  991.  *
  992.  *@@added V0.9.0 [umoeller]
  993.  */
  994.  
  995. LONG ctlQuerySplitPos(HWND hwndSplit)
  996. {
  997.     // the split bar data is stored in QWL_USER of the
  998.     // split bar (not the split window)
  999.     HWND    hwndSplitBar;
  1000.     PSPLITBARDATA psbd;
  1001.     if (    (hwndSplitBar = WinWindowFromID(hwndSplit, ID_SPLITBAR))
  1002.          && (psbd = (PSPLITBARDATA)WinQueryWindowULong(hwndSplitBar, QWL_USER))
  1003.        )
  1004.         return psbd->sbcd.lPos;
  1005.  
  1006.     return 0;
  1007. }
  1008.  
  1009.  
  1010.