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

  1.  
  2. /*
  3.  *@@sourcefile cctl_checkcnr.c:
  4.  *      implementation for the checkbox container common control.
  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-2002 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\Checkbox containers
  83.  *      See cctl_checkcnr.c.
  84.  */
  85.  
  86. /* ******************************************************************
  87.  *
  88.  *   Global variables
  89.  *
  90.  ********************************************************************/
  91.  
  92. // linked list of CHEXKBOXCNROWNER struct pointers
  93. HMTX        G_hmtxCnrOwnersList = 0;
  94. PLINKLIST   G_pllCnrOwners = 0;
  95.  
  96. /* ******************************************************************
  97.  *
  98.  *   Checkbox container record cores
  99.  *
  100.  ********************************************************************/
  101.  
  102. HBITMAP  G_hbmCheckboxes = NULLHANDLE;
  103. ULONG    G_cxCheckbox = 0;
  104.  
  105. /*
  106.  *@@ ctlDrawCheckbox:
  107.  *
  108.  *      The system checkbox bitmap (G_hbmCheckboxes)
  109.  *      is organized into 4 columns and three rows as
  110.  *      follows:
  111.  *
  112.  +                         not depressed     depressed
  113.  +                       ┌───────┬────────┬───────┬───────┐
  114.  +         checkbox      │       │  chk   │       │  chk  │
  115.  +                       ├───────┼────────┼───────┼───────┤
  116.  +         radio button  │       │  chk   │       │  chk  │
  117.  +                       ├───────┼────────┼───────┼───────┤
  118.  +         chbx indeterm.│       │  ind   │       │  ind  │
  119.  +                       └───────┴────────┴───────┴───────┘
  120.  *
  121.  *@@added V0.9.0 (99-11-28) [umoeller]
  122.  */
  123.  
  124. BOOL ctlDrawCheckbox(HPS hps,               // in: paint PS
  125.                      LONG x,                // in: target lower left corner x
  126.                      LONG y,                // in: target lower left corner y
  127.                      USHORT usRow,          // in: 0 - 2
  128.                      USHORT usColumn,       // in: 0 - 3
  129.                      BOOL fHalftoned)       // in: if TRUE, the checkbox will be halftoned (grayed)
  130. {
  131.     POINTL  aptl[4];
  132.     BOOL    brc = FALSE;
  133.  
  134.     if (G_hbmCheckboxes)
  135.     {
  136.         // paint checkbox;
  137.         LONG lHits;
  138.  
  139.         memset(aptl, 0, sizeof(POINTL) * 4);
  140.  
  141.         // aptl[0]: target bottom-left
  142.         aptl[0].x = x;
  143.         aptl[0].y = y;
  144.  
  145.         // aptl[1]: target top-right (inclusive!)
  146.         aptl[1].x = aptl[0].x + G_cxCheckbox - 1;
  147.         aptl[1].y = aptl[0].y + G_cxCheckbox - 1;
  148.  
  149.         // aptl[2]: source bottom-left:
  150.         // depends on record selection
  151.         aptl[2].x = usColumn * G_cxCheckbox;
  152.         aptl[2].y = usRow * G_cxCheckbox;       // top checkbox row
  153.  
  154.         // aptl[3]: source top-right (non-inclusive!)
  155.         aptl[3].x = aptl[2].x + G_cxCheckbox;
  156.         aptl[3].y = aptl[2].y + G_cxCheckbox;
  157.  
  158.         if (fHalftoned)
  159.         {
  160.             HDC     hdc = GpiQueryDevice(hps);
  161.             HWND    hwnd = WinWindowFromDC(hdc);
  162.             HAB     hab = WinQueryAnchorBlock(hwnd);
  163.             HBITMAP hbmHalftoned = gpihCreateHalftonedBitmap(hab,
  164.                                                              G_hbmCheckboxes,
  165.                                                              CLR_WHITE);
  166.             lHits = GpiWCBitBlt(hps,           // hpsTarget
  167.                                 hbmHalftoned,
  168.                                 4,
  169.                                 &aptl[0],
  170.                                 ROP_SRCCOPY,        // mix
  171.                                 0);                 // options
  172.             GpiDeleteBitmap(hbmHalftoned);
  173.         }
  174.         else
  175.             lHits = GpiWCBitBlt(hps,           // hpsTarget
  176.                                 G_hbmCheckboxes,
  177.                                 4,
  178.                                 &aptl[0],
  179.                                 ROP_SRCCOPY,        // mix
  180.                                 0);                 // options
  181.         if (lHits == GPI_OK)
  182.             brc = TRUE;
  183.     }
  184.  
  185.     return brc;
  186. }
  187.  
  188. /*
  189.  *@@ CnrCheckboxClicked:
  190.  *
  191.  *
  192.  *@@added V0.9.0 (99-11-28) [umoeller]
  193.  */
  194.  
  195. static VOID CnrCheckboxClicked(PCHECKBOXCNROWNER pcbco,
  196.                                PCHECKBOXRECORDCORE precc,
  197.                                BOOL fToggleAndNotify) // if TRUE, toggle state and notify owner (CN_RECORDCHECKED)
  198. {
  199.     if (precc->ulStyle & WS_VISIBLE)
  200.     {
  201.         if (precc->recc.flRecordAttr & CRA_DISABLED)
  202.             // disabled:
  203.             WinAlarm(HWND_DESKTOP, WA_WARNING);
  204.         else
  205.         {
  206.             if (precc->ulStyle & BS_AUTOCHECKBOX)
  207.             {
  208.                 if (fToggleAndNotify)
  209.                     precc->usCheckState = 1 - precc->usCheckState;
  210.  
  211.                 WinSendMsg(pcbco->hwndCnr,
  212.                            CM_INVALIDATERECORD,
  213.                            (MPARAM)&precc,
  214.                            MPFROM2SHORT(1,
  215.                                         CMA_NOREPOSITION));
  216.             }
  217.  
  218.             if (fToggleAndNotify)
  219.                 WinPostMsg(pcbco->hwndOwner,        // was: WinSendMsg
  220.                            WM_CONTROL,
  221.                            MPFROM2SHORT(pcbco->usCnrID,
  222.                                         CN_RECORDCHECKED),
  223.                            (MPARAM)precc);
  224.         }
  225.     }
  226. }
  227.  
  228. /*
  229.  *@@ ctlDrawCheckBoxRecord:
  230.  *
  231.  *@@added V0.9.18 (2002-03-03) [umoeller]
  232.  */
  233.  
  234. MRESULT ctlDrawCheckBoxRecord(MPARAM mp2)
  235. {
  236.     MRESULT mrc = 0;
  237.  
  238.     // get generic DRAWITEM structure
  239.     POWNERITEM poi = (POWNERITEM)mp2;
  240.  
  241.     // _Pmpf(("WM_DRAWITEM poi->idItem %d", poi->idItem));
  242.  
  243.     // check if we're to draw the icon
  244.     // (and not the text)
  245.     if (poi->idItem == CMA_ICON)
  246.     {
  247.         PCNRDRAWITEMINFO pcdi = (PCNRDRAWITEMINFO)poi->hItem;
  248.         PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pcdi->pRecord;
  249.  
  250.         if (precc->ulStyle & WS_VISIBLE)
  251.         {
  252.             USHORT usRow,
  253.                    usColumn;
  254.  
  255.             switch (precc->usCheckState)
  256.             {
  257.                 case 0: // unchecked
  258.                     usRow = 2;
  259.                     usColumn = 0;
  260.                 break;
  261.  
  262.                 case 1: // checked
  263.                     usRow = 2;
  264.                     usColumn = 1;
  265.                 break;
  266.  
  267.                 case 2: // indeterminate
  268.                     usRow = 0;
  269.                     usColumn = 1;
  270.                 break;
  271.             }
  272.  
  273.             if (precc->ulStyle & BS_BITMAP)
  274.                 // button currently depressed:
  275.                 // add two to column
  276.                 usColumn += 2;
  277.  
  278.             ctlDrawCheckbox(poi->hps,
  279.                             poi->rclItem.xLeft,
  280.                             poi->rclItem.yBottom,
  281.                             usRow,
  282.                             usColumn,
  283.                             // halftoned?
  284.                             ((precc->recc.flRecordAttr & CRA_DISABLED) != 0));
  285.             mrc = (MPARAM)FALSE;
  286.                         // we still need the cnr to draw the
  287.                         // emphasis
  288.         }
  289.         else
  290.             mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item;
  291.                     // don't even draw emphasis
  292.     }
  293.     else if (poi->idItem == CMA_TEXT)
  294.     {
  295.         // for text, buttons etc.:
  296.         PCNRDRAWITEMINFO pcdi = (PCNRDRAWITEMINFO)poi->hItem;
  297.         PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pcdi->pRecord;
  298.         if (precc->recc.flRecordAttr & CRA_DISABLED)
  299.         {
  300.             RECTL rcl2;
  301.             LONG lBackground, lForeground;
  302.             ULONG flCmd = DT_LEFT | DT_TOP | DT_ERASERECT;
  303.             if ((pcdi->pRecord->flRecordAttr) & CRA_SELECTED)
  304.             {
  305.                 // disabled and selected:
  306.                 lBackground = WinQuerySysColor(HWND_DESKTOP,
  307.                                                SYSCLR_SHADOWTEXT, 0);
  308.                 lForeground = winhQueryPresColor(poi->hwnd,
  309.                                                  PP_BACKGROUNDCOLOR,
  310.                                                  FALSE, // no inherit
  311.                                                  SYSCLR_WINDOW);
  312.             }
  313.             else
  314.             {
  315.                 // disabled and not selected:
  316.                 lBackground = winhQueryPresColor(poi->hwnd,
  317.                                                  PP_BACKGROUNDCOLOR,
  318.                                                  FALSE,
  319.                                                  SYSCLR_WINDOW);
  320.                 lForeground = winhQueryPresColor(poi->hwnd,
  321.                                                  PP_FOREGROUNDCOLOR,
  322.                                                  FALSE, // no inherit
  323.                                                  SYSCLR_WINDOWTEXT);
  324.                 flCmd |= DT_HALFTONE;
  325.             }
  326.  
  327.             // _Pmpf(("back: 0x%lX, fore: 0x%lX", lBackground, lForeground));
  328.  
  329.             GpiCreateLogColorTable(poi->hps, 0, LCOLF_RGB, 0, 0, NULL);
  330.             GpiSetBackColor(poi->hps, lBackground);
  331.             GpiSetColor(poi->hps, lForeground);
  332.  
  333.             memcpy(&rcl2, &(poi->rclItem), sizeof(rcl2));
  334.  
  335.             winhDrawFormattedText(poi->hps,
  336.                                   &rcl2,
  337.                                   precc->recc.pszTree,
  338.                                   flCmd);
  339.             mrc = (MPARAM)TRUE;
  340.         }
  341.         else
  342.             // tell cnr to draw the item
  343.             mrc = (MPARAM)FALSE;
  344.     }
  345.     else
  346.         // tell cnr to draw the item
  347.         mrc = (MPARAM)FALSE;
  348.  
  349.     return mrc;
  350. }
  351.  
  352. /*
  353.  *@@ fnwpSubclCheckboxCnr:
  354.  *      window proc for subclassed containers.
  355.  *      See ctlMakeCheckboxContainer for details.
  356.  *
  357.  *@@added V0.9.0 (99-11-29) [umoeller]
  358.  *@@changed V0.9.18 (2002-03-03) [umoeller]: fixed bad orig win msg, other optimizations
  359.  */
  360.  
  361. static MRESULT EXPENTRY fnwpSubclCheckboxCnr(HWND hwndCnr, ULONG msg, MPARAM mp1, MPARAM mp2)
  362. {
  363.     MRESULT             mrc = 0;
  364.     PCHECKBOXCNROWNER   pcbco = 0;
  365.     PFNWP               pfnwpOrig = 0;
  366.  
  367.     if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
  368.     {
  369.         PLISTNODE   pNode = lstQueryFirstNode(G_pllCnrOwners);
  370.         while (pNode)
  371.         {
  372.             pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
  373.             if (pcbco->hwndCnr == hwndCnr)
  374.             {
  375.                 pfnwpOrig = pcbco->pfnwpCnrOrig; // fixed V0.9.18 (2002-03-03) [umoeller]
  376.                 break;
  377.             }
  378.             pNode = pNode->pNext;
  379.         }
  380.         DosReleaseMutexSem(G_hmtxCnrOwnersList);
  381.     }
  382.  
  383.     if (pfnwpOrig)
  384.     {
  385.         switch (msg)
  386.         {
  387.             case WM_BUTTON1DOWN:
  388.             {
  389.                 POINTL ptlClick;
  390.                 ptlClick.x = SHORT1FROMMP(mp1);
  391.                 ptlClick.y = SHORT2FROMMP(mp1);
  392.  
  393.                 // find record whose icon has this point
  394.                 pcbco->preccClicked
  395.                     = (PCHECKBOXRECORDCORE)cnrhFindRecordFromPoint(hwndCnr,
  396.                                                                    &ptlClick,
  397.                                                                    &pcbco->rclReccClicked,
  398.                                                                    CMA_ICON,
  399.                                                                    0);
  400.  
  401.                 if (pcbco->preccClicked)
  402.                 {
  403.                     if (pcbco->preccClicked->ulStyle & WS_VISIBLE)
  404.                     {
  405.                         // add "depressed" style
  406.                         pcbco->preccClicked->ulStyle |= BS_BITMAP;
  407.                         // mouse was clicked on icon (checkbox):
  408.                         CnrCheckboxClicked(pcbco,
  409.                                            pcbco->preccClicked,
  410.                                            FALSE);
  411.                     }
  412.                     else
  413.                         // not visible:
  414.                         pcbco->preccClicked = NULL;
  415.                 }
  416.  
  417.                 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
  418.                         // apperently, the cnr captures the mouse
  419.             }
  420.             break;
  421.  
  422.             case WM_MOUSEMOVE:
  423.                 if (pcbco->preccClicked)
  424.                 {
  425.                     // mouse moved while checkbox is depressed:
  426.                     POINTL ptlMove;
  427.                     ptlMove.x = SHORT1FROMMP(mp1);
  428.                     ptlMove.y = SHORT2FROMMP(mp1);
  429.  
  430.                     if (WinPtInRect(pcbco->habCnr,
  431.                                     &pcbco->rclReccClicked,
  432.                                     &ptlMove))
  433.                     {
  434.                         // mouse is in checkbox:
  435.                         if ((pcbco->preccClicked->ulStyle & BS_BITMAP) == 0)
  436.                         {
  437.                             // checkbox is not drawn depressed:
  438.                             // add "depressed" style
  439.                             pcbco->preccClicked->ulStyle |= BS_BITMAP;
  440.                             CnrCheckboxClicked(pcbco,
  441.                                                pcbco->preccClicked,
  442.                                                FALSE);
  443.                         }
  444.                     }
  445.                     else
  446.                     {
  447.                         // mouse is outside of checkbox:
  448.                         if (pcbco->preccClicked->ulStyle & BS_BITMAP)
  449.                         {
  450.                             // checkbox is drawn depressed:
  451.                             // remove "depressed" style
  452.                             pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
  453.                             CnrCheckboxClicked(pcbco,
  454.                                                pcbco->preccClicked,
  455.                                                FALSE);
  456.                         }
  457.                     }
  458.                 }
  459.             break;
  460.  
  461.             case WM_BUTTON1UP:
  462.                 if (pcbco->preccClicked)
  463.                 {
  464.                     if (pcbco->preccClicked->ulStyle & BS_BITMAP)
  465.                     {
  466.                         pcbco->preccClicked->ulStyle &= ~BS_BITMAP;
  467.                         CnrCheckboxClicked(pcbco,
  468.                                            pcbco->preccClicked,
  469.                                            TRUE);
  470.                     }
  471.  
  472.                     pcbco->preccClicked = NULL;
  473.                 }
  474.                 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
  475.             break;
  476.  
  477.             /*
  478.              * WM_CHAR:
  479.              *
  480.              */
  481.  
  482.             case WM_CHAR:
  483.             {
  484.                 USHORT fsFlags = SHORT1FROMMP(mp1);
  485.                 CHAR   ch = CHAR1FROMMP(mp2);
  486.                             // never use the USHORT, because for
  487.                             // the "key-up" message, the hi-char
  488.                             // is different (duh...)
  489.  
  490.                 // _Pmpf(("WM_CHAR fsFlags %lX, usch %d", fsFlags, ch));
  491.  
  492.                 if (ch == ' ')
  493.                 {
  494.                     // space: toggle checkbox
  495.                     if ((fsFlags & KC_KEYUP) == 0)
  496.                     {
  497.                         // space down:
  498.                         // filter up repetitive key msgs
  499.                         if (pcbco->preccSpace == NULL)
  500.                         {
  501.                             PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)WinSendMsg(
  502.                                                                    hwndCnr,
  503.                                                                    CM_QUERYRECORDEMPHASIS,
  504.                                                                    (MPARAM)CMA_FIRST,
  505.                                                                    (MPARAM)CRA_CURSORED);
  506.                             if ((precc) && (precc != (PCHECKBOXRECORDCORE)-1))
  507.                             {
  508.                                 if (precc->ulStyle & WS_VISIBLE)
  509.                                 {
  510.                                     pcbco->preccSpace = precc;
  511.  
  512.                                     // add "depressed" style
  513.                                     pcbco->preccSpace->ulStyle |= BS_BITMAP;
  514.                                     CnrCheckboxClicked(pcbco,
  515.                                                        pcbco->preccSpace,
  516.                                                        FALSE);
  517.                                 }
  518.                             }
  519.                         }
  520.                     }
  521.                     else
  522.                     {
  523.                         // space up:
  524.                         if (pcbco->preccSpace)
  525.                         {
  526.                             if (pcbco->preccSpace->ulStyle & BS_BITMAP)
  527.                             {
  528.                                 pcbco->preccSpace->ulStyle &= ~BS_BITMAP;
  529.                                 CnrCheckboxClicked(pcbco,
  530.                                                    pcbco->preccSpace,
  531.                                                    TRUE);
  532.                             }
  533.  
  534.                             pcbco->preccSpace = NULL;
  535.                         }
  536.                     }
  537.  
  538.                     // do not pass spaces on
  539.                     mrc = (MPARAM)TRUE;     // processed
  540.                     break;
  541.                 }
  542.  
  543.                 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
  544.             }
  545.             break;
  546.  
  547.             /*
  548.              * WM_DESTROY:
  549.              *      remove list item; this gets sent
  550.              *      to the parent first, then the children,
  551.              *      so we remove this in the container
  552.              *      (this way the list item is still valid
  553.              *      in ctl_fnwpSubclCheckboxCnrOwner)
  554.              */
  555.  
  556.             case WM_DESTROY:
  557.                 if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
  558.                 {
  559.                     if (WinIsWindow(pcbco->habCnr,
  560.                                     pcbco->hwndOwner))
  561.                         // un-subclass the owner
  562.                         WinSubclassWindow(pcbco->hwndOwner,
  563.                                           pcbco->pfnwpOwnerOrig);
  564.  
  565.                     lstRemoveItem(G_pllCnrOwners, pcbco);
  566.  
  567.                     if (lstCountItems(G_pllCnrOwners) == 0)
  568.                         lstFree(&G_pllCnrOwners);
  569.  
  570.                     DosReleaseMutexSem(G_hmtxCnrOwnersList);
  571.                 }
  572.  
  573.                 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
  574.             break;
  575.  
  576.             default:
  577.                 mrc = pfnwpOrig(hwndCnr, msg, mp1, mp2);
  578.         }
  579.     }
  580.     else
  581.         mrc = WinDefWindowProc(hwndCnr, msg, mp1, mp2);
  582.  
  583.     return mrc;
  584. }
  585.  
  586.  
  587. /*
  588.  *@@ fnwpSubclCheckboxCnrOwner:
  589.  *      window proc for subclassed container owners.
  590.  *      See ctlMakeCheckboxContainer for details.
  591.  *
  592.  *@@added V0.9.0 (99-11-28) [umoeller]
  593.  */
  594.  
  595. static MRESULT EXPENTRY fnwpSubclCheckboxCnrOwner(HWND hwndOwner, ULONG msg, MPARAM mp1, MPARAM mp2)
  596. {
  597.     MRESULT             mrc = 0;
  598.     PCHECKBOXCNROWNER   pcbco = 0;
  599.     PFNWP               pfnwpOrig = 0;
  600.  
  601.     if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
  602.     {
  603.         PLISTNODE   pNode = lstQueryFirstNode(G_pllCnrOwners);
  604.         while (pNode)
  605.         {
  606.             pcbco = (PCHECKBOXCNROWNER)pNode->pItemData;
  607.             if (pcbco->hwndOwner == hwndOwner)
  608.             {
  609.                 pfnwpOrig = pcbco->pfnwpOwnerOrig;
  610.                 break;
  611.             }
  612.             pNode = pNode->pNext;
  613.         }
  614.         DosReleaseMutexSem(G_hmtxCnrOwnersList);
  615.     }
  616.  
  617.     if (pfnwpOrig)
  618.     {
  619.         switch (msg)
  620.         {
  621.             /*
  622.              * WM_CONTROL:
  623.              *
  624.              */
  625.  
  626.             case WM_CONTROL:
  627.             {
  628.                 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
  629.                 {
  630.                     switch (SHORT2FROMMP(mp1))
  631.                     {
  632.                         case CN_ENTER:
  633.                         {
  634.                             PNOTIFYRECORDENTER pnre = (PNOTIFYRECORDENTER)mp2;
  635.                             PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)pnre->pRecord;
  636.  
  637.                             if (precc)
  638.                                 CnrCheckboxClicked(pcbco,
  639.                                                    precc,
  640.                                                    TRUE);
  641.                         }
  642.                         break;
  643.  
  644.                         default:
  645.                             mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
  646.                     }
  647.                 }
  648.                 else
  649.                     mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
  650.             }
  651.             break;
  652.  
  653.             /*
  654.              * WM_DRAWITEM:
  655.              *      cnr owner draw; this is where we draw
  656.              *      the checkboxes
  657.              */
  658.  
  659.             case WM_DRAWITEM:
  660.             {
  661.                 if (SHORT1FROMMP(mp1) == pcbco->usCnrID)
  662.                     mrc = ctlDrawCheckBoxRecord(mp2);
  663.                 else
  664.                     mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
  665.             }
  666.             break;
  667.  
  668.             default:
  669.                 mrc = pfnwpOrig(hwndOwner, msg, mp1, mp2);
  670.         }
  671.     }
  672.     else
  673.         mrc = WinDefWindowProc(hwndOwner, msg, mp1, mp2);
  674.  
  675.     return mrc;
  676. }
  677.  
  678. /*
  679.  *@@ ctlQueryCheckboxSize:
  680.  *
  681.  *@@added V0.9.19 (2002-04-24) [umoeller]
  682.  */
  683.  
  684. ULONG ctlQueryCheckboxSize(VOID)
  685. {
  686.     if (G_hbmCheckboxes == NULLHANDLE)
  687.     {
  688.         // first call:
  689.         BITMAPINFOHEADER bmih;
  690.         // load checkboxes bitmap
  691.         G_hbmCheckboxes = WinGetSysBitmap(HWND_DESKTOP,
  692.                                           SBMP_CHECKBOXES);
  693.  
  694.         // and compute size of one checkbox
  695.         // (4 columns, 3 rows)
  696.         bmih.cbFix = sizeof(bmih);      // V0.9.19 (2002-04-24) [umoeller]
  697.         GpiQueryBitmapParameters(G_hbmCheckboxes,
  698.                                  &bmih);
  699.         G_cxCheckbox = bmih.cx / 4;
  700.     }
  701.  
  702.     return G_cxCheckbox;
  703. }
  704.  
  705. /*
  706.  *@@ ctlInitCheckboxContainer:
  707.  *
  708.  *@@added V0.9.18 (2002-03-03) [umoeller]
  709.  */
  710.  
  711. VOID ctlInitCheckboxContainer(HWND hwndCnr)
  712. {
  713.     ctlQueryCheckboxSize();
  714.  
  715.     BEGIN_CNRINFO()
  716.     {
  717.         cnrhSetView(CV_TREE | CV_ICON | CA_TREELINE | CA_OWNERDRAW | CV_MINI);
  718.         cnrhSetTreeIndent(20);
  719.         cnrhSetBmpOrIconSize(G_cxCheckbox, G_cxCheckbox);
  720.     } END_CNRINFO(hwndCnr);
  721. }
  722.  
  723. /*
  724.  *@@ ctlSubclassCheckboxContainer:
  725.  *
  726.  *@@added V0.9.18 (2002-03-03) [umoeller]
  727.  */
  728.  
  729. PCHECKBOXCNROWNER ctlSubclassCheckboxContainer(HWND hwndCnr)
  730. {
  731.     PFNWP pfnwpCnrOrig;
  732.     if (pfnwpCnrOrig = WinSubclassWindow(hwndCnr, fnwpSubclCheckboxCnr))
  733.     {
  734.         // cnr successfully subclassed:
  735.         // create storage for both subclassed cnr and owner
  736.         PCHECKBOXCNROWNER pcbco;
  737.         if (pcbco = (PCHECKBOXCNROWNER)malloc(sizeof(CHECKBOXCNROWNER)))
  738.         {
  739.             memset(pcbco, 0, sizeof(CHECKBOXCNROWNER));
  740.             pcbco->hwndCnr = hwndCnr;
  741.             pcbco->usCnrID = WinQueryWindowUShort(hwndCnr, QWS_ID);
  742.             pcbco->hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER);
  743.             pcbco->pfnwpCnrOrig = pfnwpCnrOrig;
  744.  
  745.             pcbco->habCnr = WinQueryAnchorBlock(hwndCnr);
  746.  
  747.             return pcbco;
  748.         }
  749.     }
  750.  
  751.     return NULL;
  752. }
  753.  
  754. /*
  755.  *@@ ctlMakeCheckboxContainer:
  756.  *      this turns a regular container into a "checkbox"
  757.  *      container. This means that the container record
  758.  *      icons are painted as checkboxes, whose status
  759.  *      is determined using an extended record core
  760.  *      structure (CHECKBOXRECORDCORE).
  761.  *
  762.  *      This function assumes that all records in the
  763.  *      container use this special extended record core
  764.  *      ONLY. You cannot use regular RECORDCORE's when
  765.  *      using this function. Of course, you can extend
  766.  *      the CHECKBOXRECORDCORE structure to the bottom
  767.  *      if you need more fields.
  768.  *
  769.  *      This subclasses BOTH the container and the container
  770.  *      owner to intercept a number of messages and to
  771.  *      implement owner draw. This is totally transparent
  772.  *      to the caller; QWL_USER is not used for this, so
  773.  *      you can still store your own stuff in there.
  774.  *
  775.  *      Returns FALSE upon errors.
  776.  *
  777.  *      To set up a checkbox container, call this function
  778.  *      (which switches the container to Tree view automatically).
  779.  *      Then, allocate and insert CHECKBOXRECORDCORE's as usual.
  780.  *
  781.  *      To set and query a record's check state easily, use
  782.  *      ctlSetRecordChecked and ctlQueryRecordChecked.
  783.  *
  784.  *      The checkboxes work almost identically to regular
  785.  *      checkboxes. The user can interact with the checkbox
  786.  *      by clicking on the checkbox itself or by double-clicking
  787.  *      on the record text or by pressing the Enter or Space keys.
  788.  *
  789.  *      Set CHECKBOXRECORDCORE.ulStyle to one of the regular
  790.  *      checkbox button style bits; if one of the "auto" flags
  791.  *      is set, the checkbox is toggled automatically.
  792.  *
  793.  *      Even if "auto" is off, when the user changes the check
  794.  *      box selection, hwndCnrOwner gets sent a WM_CONTROL message
  795.  *      with the following parameters:
  796.  *
  797.  *      -- mp1: USHORT id: usCnrID, as passed to this func
  798.  *      -- mp1: USHORT usNotifyCode: CN_RECORDCHECKED (999)
  799.  *      -- mp2: PCHECKRECORDCORE precc: the record which was (un)checked.
  800.  *
  801.  *      Note that the subclassed container owner proc filters out
  802.  *      CN_ENTER, so you'll never receive that (but CN_RECORDCHECKED
  803.  *      instead).
  804.  *
  805.  *      If the "auto" style bits have not been set, you must call
  806.  *      ctlSetRecordChecked yourself then.
  807.  *
  808.  *@@added V0.9.0 (99-11-28) [umoeller]
  809.  */
  810.  
  811. BOOL ctlMakeCheckboxContainer(HWND hwndCnrOwner,    // in: owner (and parent) of container
  812.                               USHORT usCnrID)       // in: ID of container in hwndCnrOwner
  813. {
  814.     BOOL    brc = FALSE;
  815.  
  816.     HWND    hwndCnr = WinWindowFromID(hwndCnrOwner, usCnrID);
  817.  
  818.     // create mutex-protected list of container owners
  819.     if (G_hmtxCnrOwnersList == 0)
  820.         if (DosCreateMutexSem(NULL,
  821.                               &G_hmtxCnrOwnersList, 0, FALSE) != NO_ERROR)
  822.             return (FALSE);
  823.  
  824.     if (G_pllCnrOwners == NULL)
  825.         G_pllCnrOwners = lstCreate(TRUE);
  826.  
  827.     if (hwndCnr)
  828.     {
  829.         ctlInitCheckboxContainer(hwndCnr);
  830.  
  831.         if (    (hwndCnrOwner)
  832.              && (G_hmtxCnrOwnersList)
  833.            )
  834.         {
  835.             // subclass container owner
  836.             PFNWP pfnwpOwnerOrig = WinSubclassWindow(hwndCnrOwner, fnwpSubclCheckboxCnrOwner);
  837.             /* _Pmpf(("Subclassed hwnd 0x%lX: orig 0x%lX",
  838.                     hwndCnrOwner, pfnwpOwnerOrig)); */
  839.             if (pfnwpOwnerOrig)
  840.             {
  841.                 // owner successfully subclassed:
  842.                 // subclass container too
  843.                 PCHECKBOXCNROWNER pcbco;
  844.                 if (pcbco = ctlSubclassCheckboxContainer(hwndCnr))
  845.                 {
  846.                     if (!DosRequestMutexSem(G_hmtxCnrOwnersList, 5000))
  847.                     {
  848.                         lstAppendItem(G_pllCnrOwners, pcbco);
  849.                         DosReleaseMutexSem(G_hmtxCnrOwnersList);
  850.                         brc = TRUE;
  851.  
  852.                         pcbco->pfnwpOwnerOrig = pfnwpOwnerOrig;
  853.                     }
  854.                 }
  855.             }
  856.         }
  857.     }
  858.  
  859.     return brc;
  860. }
  861.  
  862. /*
  863.  *@@ fncbFindCheckRecord:
  864.  *      helper callback for finding a checkbox
  865.  *      record according to an item ID. Used
  866.  *      with cnrhForAllRecords.
  867.  *
  868.  *      Returns 1 if found to make
  869.  *      cnrhForAllRecords stop searching.
  870.  *
  871.  *@@added V0.9.0 (99-11-28) [umoeller]
  872.  */
  873.  
  874. static ULONG EXPENTRY fncbFindCheckRecord(HWND hwndCnr,             // in: container
  875.                                           PRECORDCORE preccThis,    // in: current record (from cnrhForAllRecords)
  876.                                           ULONG ulItemID,           // in: item ID to find
  877.                                           ULONG ulppRecc)           // out: PRECORDCORE* if found
  878. {
  879.     ULONG   ulrc = 0;
  880.     PCHECKBOXRECORDCORE precc = (PCHECKBOXRECORDCORE)preccThis;
  881.     if (precc)
  882.     {
  883.         if (precc->ulItemID == ulItemID)
  884.         {
  885.             // found:
  886.             PCHECKBOXRECORDCORE *pprecc = (PCHECKBOXRECORDCORE*)ulppRecc;
  887.             // store
  888.             *pprecc = precc;
  889.             // and stop
  890.             ulrc = 1;
  891.         }
  892.     }
  893.  
  894.     return ulrc;
  895. }
  896.  
  897. /*
  898.  *@@ ctlFindCheckRecord:
  899.  *      this searches the given checkbox container
  900.  *      for the record which matches the given item
  901.  *      ID. Returns NULL if not found.
  902.  *
  903.  *@@added V0.9.1 (99-12-03) [umoeller]
  904.  */
  905.  
  906. PCHECKBOXRECORDCORE ctlFindCheckRecord(HWND hwndCnr,
  907.                                        ULONG ulItemID)
  908. {
  909.     PCHECKBOXRECORDCORE precc = 0;
  910.  
  911.     cnrhForAllRecords(hwndCnr,
  912.                       NULL,         // start with root
  913.                       fncbFindCheckRecord,
  914.                       (ULONG)ulItemID,          // input
  915.                       (ULONG)&precc);
  916.  
  917.     return precc;
  918. }
  919.  
  920. /*
  921.  *@@ ctlSetRecordChecked:
  922.  *      this searches the given checkbox container
  923.  *      for the record which matches the given item
  924.  *      ID and updates that record check state.
  925.  *
  926.  *      The check state may be:
  927.  *      -- 0: not checked (as with regular checkboxes)
  928.  *      -- 1: checked (as with regular checkboxes)
  929.  *      -- 2: indeterminate (as with regular checkboxes)
  930.  *
  931.  *      Returns FALSE if the record could not be found.
  932.  *
  933.  *@@added V0.9.0 (99-11-28) [umoeller]
  934.  */
  935.  
  936. BOOL ctlSetRecordChecked(HWND hwndCnr,          // in: container prepared with
  937.                                                 // ctlMakeCheckboxContainer
  938.                          ULONG ulItemID,        // in: record item ID
  939.                          USHORT usCheckState)   // in: 0, 1, 2, 3
  940. {
  941.     PCHECKBOXRECORDCORE precc;
  942.     if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
  943.     {
  944.         precc->usCheckState = usCheckState;
  945.         WinSendMsg(hwndCnr,
  946.                    CM_INVALIDATERECORD,
  947.                    (MPARAM)&precc,
  948.                    MPFROM2SHORT(1,
  949.                                 CMA_NOREPOSITION));
  950.         return TRUE;
  951.     }
  952.  
  953.     return FALSE;
  954. }
  955.  
  956. /*
  957.  *@@ ctlQueryRecordChecked:
  958.  *      this searches the given checkbox container
  959.  *      for the record which matches the given item
  960.  *      ID and returns that record's check state.
  961.  *
  962.  *      Returns:
  963.  *      -- 0: not checked (as with regular checkboxes)
  964.  *      -- 1: checked (as with regular checkboxes)
  965.  *      -- 2: indeterminate (as with regular checkboxes)
  966.  *      -- -1: record not found.
  967.  *
  968.  *@@added V0.9.0 (99-11-28) [umoeller]
  969.  */
  970.  
  971. ULONG ctlQueryRecordChecked(HWND hwndCnr,          // in: container prepared with
  972.                                                    // ctlMakeCheckboxContainer
  973.                             ULONG ulItemID,        // in: record item ID
  974.                             USHORT usCheckState)   // in: 0, 1, 2, 3
  975. {
  976.     PCHECKBOXRECORDCORE precc;
  977.     if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
  978.         return precc->usCheckState;
  979.  
  980.     return -1;
  981. }
  982.  
  983. /*
  984.  *@@ ctlEnableRecord:
  985.  *      this searches the given checkbox container
  986.  *      for the record which matches the given item
  987.  *      ID and updates that record enablement. If
  988.  *      the record is disabled, it's painted halftoned
  989.  *      by fnwpSubclCheckboxCnr.
  990.  *
  991.  *@@added V0.9.1 (99-12-03) [umoeller]
  992.  */
  993.  
  994. BOOL ctlEnableRecord(HWND hwndCnr,
  995.                      ULONG ulItemID,
  996.                      BOOL fEnable)
  997. {
  998.     PCHECKBOXRECORDCORE precc;
  999.  
  1000.     if (precc = ctlFindCheckRecord(hwndCnr, ulItemID))
  1001.     {
  1002.         ULONG ulAttr = precc->recc.flRecordAttr;
  1003.         if (fEnable)
  1004.             precc->recc.flRecordAttr &= ~CRA_DISABLED;
  1005.         else
  1006.             precc->recc.flRecordAttr |= CRA_DISABLED;
  1007.  
  1008.         if (precc->recc.flRecordAttr != ulAttr)
  1009.             // attrs changed: repaint
  1010.             WinSendMsg(hwndCnr,
  1011.                        CM_INVALIDATERECORD,
  1012.                        (MPARAM)&precc,
  1013.                        MPFROM2SHORT(1,
  1014.                                     CMA_NOREPOSITION));
  1015.  
  1016.         return TRUE;
  1017.     }
  1018.  
  1019.     return FALSE;
  1020. }
  1021.  
  1022.  
  1023.