home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / inole / gizmo.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  21KB  |  797 lines

  1. /*
  2.  * GIZMO.C
  3.  *
  4.  * Allocate, free, find, and enumerate functions for the GIZMO
  5.  * structure and a generic subclass procedure to handle tabbing
  6.  * between gizmos.
  7.  *
  8.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Microsoft
  11.  * Internet  :  kraigb@microsoft.com
  12.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  13.  */
  14.  
  15.  
  16. #include "inoledll.h"
  17.  
  18.  
  19. /*
  20.  * In order to control tabbing in the gizmos, we need to subclass
  21.  * real pushbuttons, edit controls, listboxes, and comboboxes.  So
  22.  * we keep an array of the four original procs for such controls.
  23.  */
  24. WNDPROC     pfnOrg[CSUBGIZMOS]={NULL, NULL, NULL, NULL};
  25.  
  26.  
  27. TCHAR szStatic[]=TEXT("static");
  28. TCHAR szEdit[]=TEXT("edit");
  29. TCHAR szCombobox[]=TEXT("combobox");
  30. TCHAR szListbox[]=TEXT("listbox");
  31. TCHAR szButton[]=TEXT("button");
  32.  
  33.  
  34. //Here so GIZMOBAR.C can get at it.
  35. TOOLDISPLAYDATA tdd;
  36.  
  37.  
  38.  
  39. /*
  40.  * GizmoPAllocate
  41.  *
  42.  * Purpose:
  43.  *  Allocates and initializes a GIZMO data structure.
  44.  *
  45.  * Parameters:
  46.  *  pfSuccess       int * flag indicating success of failure.
  47.  *  ppFirst         PPGIZMO providing the first gizmo in this list.
  48.  *  hWndParent      HWND of the parent of this gizmo.  Can be NULL
  49.  *                  for iType==GIZMOTYPE_BUTTON* or
  50.  *                  GIZMOTYPE_SEPARATOR.
  51.  *  iType           UINT gizmo control type.
  52.  *  iGizmo          UINT index of this gizmo in the GizmoBar.
  53.  *  uID             UINT identifier to send with WM_COMMAND for this
  54.  *                  control.
  55.  *  dx, dy          UINT width and height of the gizmo.
  56.  *  pszText         LPTSTR to the text for edits, listboxes, combobox,
  57.  *                  and text.
  58.  *  dwStyle         DWORD style for edits, lists, and combos, and
  59.  *                  texts.
  60.  *  hBmp            HBITMAP for button gizmos, is applicable.
  61.  *  iImage          UINT index into hBmp for the button image, if
  62.  *                  applicable.
  63.  *  uState          UINT initial state of the control.
  64.  *
  65.  * Return Value:
  66.  *  PGIZMO          If NULL returned then GizmoPAllocate could not
  67.  *                  allocate memory.  If a non-NULL pointer is
  68.  *                  returned with *pfSuccess, then call GizmoPFree
  69.  *                  immediately.  If you get a non-NULL pointer and
  70.  *                  *pfSuccess==TRUE then the function succeeded.
  71.  */
  72.  
  73. PGIZMO GizmoPAllocate(int *pfSuccess, PPGIZMO ppFirst
  74.     , HWND hWndParent, UINT iType, UINT iGizmo, UINT uID, UINT dx
  75.     , UINT dy, LPTSTR pszText, HBITMAP hBmp, UINT iImage, UINT uState)
  76.     {
  77.     PGIZMO          pGizmo;
  78.     PGIZMO          pCur, pPrev;
  79.     LPTSTR          pszClass;
  80.     HINSTANCE       hInst;
  81.     UINT            i;
  82.     DWORD           dwStyle;
  83.     HWND            hWndE;
  84.  
  85.     if (NULL==pfSuccess)
  86.         return NULL;
  87.  
  88.     //Make sure we know of this gizmo type.
  89.     if (GIZMOTYPE_MIN > iType || GIZMOTYPE_MAX < iType)
  90.         return NULL;
  91.  
  92.     *pfSuccess=FALSE;
  93.  
  94.     //Allocate the structure
  95.     pGizmo=(PGIZMO)(TCHAR *)LocalAlloc(LPTR, CBGIZMO);
  96.  
  97.     if (NULL==pGizmo)
  98.         return NULL;
  99.  
  100.  
  101.     //Store the necessary information for this gizmo.
  102.     pGizmo->iType   =iType;
  103.     pGizmo->uID     =uID;
  104.     pGizmo->hBmp    =hBmp;
  105.     pGizmo->iBmp    =iImage;
  106.     pGizmo->uState  =uState;
  107.     pGizmo->fNotify =TRUE;
  108.  
  109.  
  110.     /*
  111.      * Insert this structure into our gizmo list.  Each time we scan
  112.      * we increment the index counter (starting at zero) comparing it
  113.      * to the desired index of insertion.  We then know exactly where
  114.      * to insert this new gizmo.  Note that we insert the new gizmo
  115.      * in the list appropriately for the given owner, so enumerations
  116.      * will come out ordered in the same way for that owner.
  117.      */
  118.  
  119.     i=0;
  120.     pCur=*ppFirst;
  121.     pPrev=NULL;
  122.  
  123.     while (NULL!=pCur && i++ < iGizmo)
  124.         {
  125.         pPrev=pCur;
  126.         pCur =pCur->pNext;
  127.         }
  128.  
  129.     //Point to our neighbors
  130.     pGizmo->pPrev=pPrev;
  131.     pGizmo->pNext=pCur;
  132.  
  133.  
  134.     //Point out neighbors to us.
  135.     if (NULL==pPrev)
  136.         *ppFirst=pGizmo;
  137.     else
  138.         pPrev->pNext=pGizmo;
  139.  
  140.     if (NULL!=pCur)
  141.         pCur->pPrev=pGizmo;
  142.  
  143.  
  144.     //Our x-coordinate is the x of the previous gizmo plus its width.
  145.     if (NULL!=pPrev)
  146.         pGizmo->x=pGizmo->pPrev->x+pGizmo->pPrev->dx;
  147.     else
  148.         pGizmo->x=4;    //First gizmo is at x=4
  149.  
  150.  
  151.     //If we're a separator or image button, force standards on dx.
  152.     UIToolConfigureForDisplay(&tdd);
  153.     pGizmo->cxImage=tdd.cxImage;
  154.     pGizmo->cyImage=tdd.cyImage;
  155.  
  156.     if ((GIZMOTYPE_DRAWN & iType) && NULL==hBmp)
  157.         dx=tdd.cxButton;
  158.  
  159.     if (GIZMOTYPE_SEPARATOR==iType)
  160.         dx=6;
  161.  
  162.     /*
  163.      * Now create windows for edits, texts, lists, and comboboxes.
  164.      * First calculate the most often defaults used in the switch.
  165.      */
  166.     pGizmo->dx=dx+6;
  167.     pGizmo->dy=min(dy, tdd.cyButton);
  168.     pGizmo->y=2;
  169.     pszClass=NULL;
  170.  
  171.     //If this is new gizmo is a window, create it.
  172.     switch (iType)
  173.         {
  174.         case GIZMOTYPE_TEXT:
  175.             pGizmo->dx=dx;
  176.  
  177.             //Center vertically.
  178.             pGizmo->y=(tdd.cyBar-1-pGizmo->dy) >> 1;
  179.             pszClass=szStatic;
  180.             dwStyle=SS_LEFT;
  181.             break;
  182.  
  183.         case GIZMOTYPE_EDIT:
  184.             //Center vertically.
  185.             pGizmo->y=(tdd.cyBar-1-pGizmo->dy) >> 1;
  186.             pszClass=szEdit;
  187.             dwStyle=ES_LEFT | WS_BORDER | WS_TABSTOP;
  188.             break;
  189.  
  190.         case GIZMOTYPE_LISTBOX:
  191.             pGizmo->dy=dy;
  192.             pszClass=szCombobox;
  193.             dwStyle=CBS_DROPDOWNLIST | WS_TABSTOP;
  194.             break;
  195.  
  196.         case GIZMOTYPE_COMBOBOX:
  197.             pGizmo->dy=dy;
  198.             pszClass=szCombobox;
  199.             dwStyle=CBS_DROPDOWN | WS_TABSTOP;
  200.             break;
  201.  
  202.         case GIZMOTYPE_BUTTONNORMAL:
  203.             pGizmo->dy=dy;
  204.             pszClass=szButton;
  205.             dwStyle=BS_PUSHBUTTON | WS_TABSTOP;
  206.             break;
  207.  
  208.         case GIZMOTYPE_SEPARATOR:
  209.             pGizmo->dx=dx;
  210.             pGizmo->y=3;
  211.             break;
  212.  
  213.         case GIZMOTYPE_BUTTONATTRIBUTEIN:
  214.         case GIZMOTYPE_BUTTONATTRIBUTEEX:
  215.         case GIZMOTYPE_BUTTONCOMMAND:
  216.             pGizmo->dx=dx;
  217.             pGizmo->y=3;
  218.             break;
  219.         }
  220.  
  221.  
  222.     //If we matched a classname, create a window.
  223.     if (GIZMOTYPE_WINDOWS & iType)
  224.         {
  225.         if (!IsWindow(hWndParent))
  226.             return pGizmo;
  227.  
  228.         hInst=GETWINDOWINSTANCE(hWndParent);    //Macro in book1632.h
  229.  
  230.         pGizmo->hWnd=CreateWindow(pszClass, pszText
  231.             , dwStyle | WS_CHILD | WS_VISIBLE, pGizmo->x, pGizmo->y
  232.             , dx, pGizmo->dy, hWndParent, (HMENU)uID, hInst, NULL);
  233.  
  234.         if (NULL==pGizmo->hWnd)
  235.             return pGizmo;
  236.  
  237.         /*
  238.          * Subclass comboboxes, listboxes, edits, and windowed
  239.          * buttons.  We use iType to index the original proc array
  240.          * so we can use a single subclass procedure for all
  241.          * controls.  If you mess with the gizmo type definitions,
  242.          * this is going to break.
  243.          */
  244.  
  245.         if (GIZMOTYPE_WINDOWS & iType && GIZMOTYPE_TEXT!=iType)
  246.             {
  247.             //Give the window its type.
  248.             BITPOSITION(iType, i);
  249.             SetProp(pGizmo->hWnd, SZTYPEPROP, (HANDLE)i);
  250.  
  251.             if (NULL==pfnOrg[i])
  252.                 {
  253.                 pfnOrg[i]=(WNDPROC)GetWindowLong(pGizmo->hWnd
  254.                     , GWL_WNDPROC);
  255.                 }
  256.  
  257.             SetWindowLong(pGizmo->hWnd, GWL_WNDPROC
  258.                 , (LONG)GenericSubProc);
  259.  
  260.             //If we're a combobox, subclass edit control
  261.             if (GIZMOTYPE_COMBOBOX==iType)
  262.                 {
  263.                 hWndE=GetDlgItem(pGizmo->hWnd, ID_COMBOEDIT);
  264.                 SetProp(hWndE, SZTYPEPROP, (HANDLE)-1);
  265.  
  266.                 if (NULL==pfnOrg[0])
  267.                     {
  268.                     pfnOrg[0]=(WNDPROC)GetWindowLong(pGizmo->hWnd
  269.                         , GWL_WNDPROC);
  270.                     }
  271.  
  272.                 SetWindowLong(hWndE, GWL_WNDPROC
  273.                     , (LONG)GenericSubProc);
  274.                 }
  275.             }
  276.         }
  277.  
  278.  
  279.     //Finally, move all our neighbors to the right to accomodate us.
  280.     GizmosExpand(pGizmo);
  281.  
  282.     *pfSuccess=TRUE;
  283.     return pGizmo;
  284.     }
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291. /*
  292.  * GizmoPFree
  293.  *
  294.  * Purpose:
  295.  *  Reverses all initialization done by GizmoPAllocate, cleaning up
  296.  *  any allocations including the application structure itself.
  297.  *
  298.  * Parameters:
  299.  *  ppFirst         PPGIZMO providing the first gizmo in this list.
  300.  *  pGizmo          PGIZMO to the structure
  301.  *
  302.  * Return Value:
  303.  *  PGIZMO          NULL if successful, pGizmo if not, meaning we
  304.  *                  couldn't free something.
  305.  */
  306.  
  307. PGIZMO GizmoPFree(PPGIZMO ppFirst, PGIZMO pGizmo)
  308.     {
  309.     int     i;
  310.  
  311.     if (NULL==pGizmo)
  312.         return NULL;
  313.  
  314.     //Move other gizmos to fill in this gap.
  315.     GizmosCompact(pGizmo);
  316.  
  317.     //Unsubclass
  318.     if (GIZMOTYPE_WINDOWS & pGizmo->iType
  319.         && GIZMOTYPE_TEXT!=pGizmo->iType)
  320.         {
  321.         i=(int)GetProp(pGizmo->hWnd, SZTYPEPROP);
  322.         RemoveProp(pGizmo->hWnd, SZTYPEPROP);
  323.  
  324.         if (GIZMOTYPE_COMBOBOX==pGizmo->iType)
  325.             {
  326.             HWND        hWndE;
  327.  
  328.             hWndE=GetDlgItem(pGizmo->hWnd, ID_COMBOEDIT);
  329.             RemoveProp(hWndE, SZTYPEPROP);
  330.             }
  331.  
  332.         SetWindowLong(pGizmo->hWnd, GWL_WNDPROC, (LONG)pfnOrg[i]);
  333.         }
  334.  
  335.     //If this was a window gizmo, destroy the window.
  336.     if (NULL!=pGizmo->hWnd && IsWindow(pGizmo->hWnd))
  337.         DestroyWindow(pGizmo->hWnd);
  338.  
  339.     //Unlink ourselves.
  340.     if (NULL!=pGizmo->pNext)
  341.         pGizmo->pNext->pPrev=pGizmo->pPrev;
  342.  
  343.     if (NULL!=pGizmo->pPrev)
  344.         pGizmo->pPrev->pNext=pGizmo->pNext;
  345.     else
  346.         *ppFirst=pGizmo->pNext;
  347.  
  348.     return (PGIZMO)LocalFree((HLOCAL)(UINT)(LONG)pGizmo);
  349.     }
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356. /*
  357.  * GizmosExpand
  358.  *
  359.  * Purpose:
  360.  *  Given a starting gizmo and a width, moves it and all gizmos to
  361.  *  its right to the right by the width to make space for showing
  362.  *  or creating a new gizmo.
  363.  *
  364.  * Parameters:
  365.  *  pGizmo          PGIZMO specifying the gizmo that was inserted.
  366.  *
  367.  * Return Value:
  368.  *  None
  369.  */
  370.  
  371. void GizmosExpand(PGIZMO pGizmo)
  372.     {
  373.     int         cx;
  374.  
  375.     cx=(int)pGizmo->dx;
  376.  
  377.     /*
  378.      * If we and the next control are buttons, use our width-1 to
  379.      * expand so we overlap borders with our neighboring button.
  380.      */
  381.  
  382.     if (NULL!=pGizmo->pNext)
  383.         {
  384.         if ((GIZMOTYPE_BUTTONS & pGizmo->pNext->iType)
  385.             && (GIZMOTYPE_BUTTONS & pGizmo->iType))
  386.             cx-=1;
  387.         }
  388.  
  389.     //Walk the gizmo list moving them right by our width.
  390.     pGizmo=pGizmo->pNext;
  391.  
  392.     while (NULL!=pGizmo)
  393.         {
  394.         pGizmo->x+=cx;
  395.  
  396.         //hWnd is NULL for buttons and separators.
  397.         if (NULL!=pGizmo->hWnd)
  398.             {
  399.             SetWindowPos(pGizmo->hWnd, NULL, pGizmo->x, pGizmo->y
  400.                 , 0, 0, SWP_NOZORDER | SWP_NOSIZE);
  401.             }
  402.  
  403.         pGizmo=pGizmo->pNext;
  404.         }
  405.  
  406.     return;
  407.     }
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415. /*
  416.  * GizmosCompact
  417.  *
  418.  * Purpose:
  419.  *  Given a gizmo, moves all other gizmos to the right of it to the
  420.  *  left by its width on the GizmoBar.  Used when removing or hiding
  421.  *  the gizmo.
  422.  *
  423.  * Parameters:
  424.  *  pGizmo          PGIZMO that is going away, visibly or physically.
  425.  *
  426.  * Return Value:
  427.  *  None
  428.  */
  429.  
  430. void GizmosCompact(PGIZMO pGizmo)
  431.     {
  432.     UINT        cx;
  433.     PGIZMO      pCur;
  434.  
  435.     //Move all the gizmos beyond us back by our width.
  436.     if (NULL!=pGizmo->pNext)
  437.         {
  438.         cx=pGizmo->pNext->x - pGizmo->x;
  439.         pCur=pGizmo->pNext;
  440.  
  441.         while (NULL!=pCur)
  442.             {
  443.             pCur->x-=cx;
  444.  
  445.             if (NULL!=pCur->hWnd)
  446.                 {
  447.                 SetWindowPos(pCur->hWnd, NULL, pCur->x, pCur->y
  448.                              , 0, 0, SWP_NOZORDER | SWP_NOSIZE);
  449.                 }
  450.  
  451.             pCur=pCur->pNext;
  452.             }
  453.         }
  454.  
  455.     return;
  456.     }
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463. /*
  464.  * GizmoPFind
  465.  *
  466.  * Purpose:
  467.  *  Given a GIZMO identifier, locates and returns a pointer to the
  468.  *  structure for that position.
  469.  *
  470.  * Parameters:
  471.  *  ppFirst         PPGIZMO providing the first gizmo in this list.
  472.  *  uID             UINT identifier to find.
  473.  *
  474.  * Return Value:
  475.  *  PGIZMO          A pointer to a GIZMO structure allocated through
  476.  *                  GizmoPAllocate, NULL if iGizmo is out of range.
  477.  */
  478.  
  479. PGIZMO GizmoPFind(PPGIZMO ppFirst, UINT uID)
  480.     {
  481.     PGIZMO       pGizmo;
  482.  
  483.     pGizmo=*ppFirst;
  484.  
  485.     /*
  486.      * Yep, linear search, but a better search algorithm won't
  487.      * improve things appreciably.  The better thing to optimize
  488.      * is what the caller passes as ppFirst.
  489.      */
  490.     while (NULL!=pGizmo && uID!=pGizmo->uID)
  491.         pGizmo=pGizmo->pNext;
  492.  
  493.     return pGizmo;
  494.     }
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501. /*
  502.  * GizmoFEnum
  503.  *
  504.  * Purpose:
  505.  *  Enumerates the list of GIZMO structures, passing each one to
  506.  *  an application-defined callback.
  507.  *
  508.  * Parameters:
  509.  *  ppFirst         PPGIZMO providing the first gizmo in this list.
  510.  *  pfnEnum         PFNGIZMOENUM to call for each enumerated
  511.  *                  structure.
  512.  *  dw              DWORD extra data to pass to the enumeration
  513.  *                  function.
  514.  *
  515.  * Return Value:
  516.  *  PGIZMO          NULL if the enumeration completed.  Otherwise a
  517.  *                  pointer to the gizmo that enumeration stopped on.
  518.  */
  519.  
  520. PGIZMO GizmoPEnum(PPGIZMO ppFirst, PFNGIZMOENUM pfnEnum, DWORD dw)
  521.     {
  522.     PGIZMO  pGizmo;
  523.     UINT    i=0;
  524.  
  525.     pGizmo=*ppFirst;
  526.  
  527.     while (NULL!=pGizmo)
  528.         {
  529.         if (!(*pfnEnum)(pGizmo, i++, dw))
  530.             break;
  531.  
  532.         pGizmo=pGizmo->pNext;
  533.         }
  534.  
  535.     return pGizmo;
  536.     }
  537.  
  538.  
  539.  
  540.  
  541. /*
  542.  * GizmoPStateSet
  543.  *
  544.  * Purpose:
  545.  *  State maniuplation functions.  Set and Clear also invalidate
  546.  *  this gizmo's rectangle on the given window and forces a repaint.
  547.  *
  548.  * Parameters:
  549.  *  hWnd            HWND of the window to repaint.
  550.  *  pGizmo          PGIZMO affected.
  551.  *  dwNew           DWORD new state flags.
  552.  *
  553.  * Return Value:
  554.  *  UINT            Previous state.
  555.  */
  556.  
  557. UINT  GizmoPStateSet(HWND hWnd, PGIZMO pGizmo, UINT uNew)
  558.     {
  559.     UINT        uRet;
  560.     RECT        rc;
  561.  
  562.     if (GIZMOTYPE_SEPARATOR==pGizmo->iType)
  563.         return pGizmo->uState;
  564.  
  565.     //Preserve the color conversion flags across this state change.
  566.     uRet=pGizmo->uState;
  567.     pGizmo->uState=(uNew & 0x00FF) | (uRet & 0xFF00);
  568.  
  569.     //Adjust the rectangle by  one to avoid repainting  borders.
  570.     SetRect(&rc, pGizmo->x+1, pGizmo->y+1, pGizmo->x+pGizmo->dx-1
  571.         , pGizmo->y+pGizmo->dy-1);
  572.     InvalidateRect(hWnd, &rc, FALSE);
  573.     UpdateWindow(hWnd);
  574.  
  575.     return uRet;
  576.     }
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585. /*
  586.  * GizmoPCheck
  587.  *
  588.  * Purpose:
  589.  *  Handles checking a single button in a group of attribute buttons.
  590.  *  If the gizmo belongs to a group of mutually exclusive buttons
  591.  *  then the others surrounding it are unchecked appropriately.
  592.  *
  593.  * Parameters:
  594.  *  hWnd            HWND of the GizmoBar.
  595.  *  pGizmo          PGIZMO of the gizmo affected.
  596.  *  fCheck          BOOL TRUE to check the button, FALSE to uncheck.
  597.  *
  598.  * Return Value:
  599.  *  BOOL            TRUE if the gizmo was previously checked, FALSE
  600.  *                  otherwise.
  601.  */
  602.  
  603. BOOL GizmoPCheck(HWND hWnd, PGIZMO pGizmo, BOOL fCheck)
  604.     {
  605.     BOOL        fPrevCheck;
  606.     PGIZMO      pCur;
  607.  
  608.  
  609.     //Ignore command buttons.
  610.     if (GIZMOTYPE_BUTTONCOMMAND==pGizmo->iType)
  611.         return FALSE;
  612.  
  613.     //Get the previous state
  614.     fPrevCheck=(BOOL)(BUTTONGROUP_DOWN & pGizmo->uState);
  615.  
  616.  
  617.     //Simply set the state for inclusive attribute buttons.
  618.     if (GIZMOTYPE_BUTTONATTRIBUTEIN==pGizmo->iType)
  619.         {
  620.         if (pGizmo->fDisabled)
  621.             {
  622.             GizmoPStateSet(hWnd, pGizmo
  623.                 , fCheck ? ATTRIBUTEBUTTON_DOWNDISABLED
  624.                 : ATTRIBUTEBUTTON_DISABLED);
  625.             }
  626.         else
  627.             {
  628.             GizmoPStateSet(hWnd, pGizmo, fCheck
  629.                 ? ATTRIBUTEBUTTON_DOWN : ATTRIBUTEBUTTON_UP);
  630.             }
  631.         }
  632.  
  633.  
  634.     if (GIZMOTYPE_BUTTONATTRIBUTEEX==pGizmo->iType)
  635.         {
  636.         //We cannot uncheck an exclusive attribute
  637.         if (!fCheck)
  638.             return fPrevCheck;
  639.  
  640.         /*
  641.          * For exclusive buttons we have to do more work.  First, if
  642.          * we're already checked (incliding DOWN and MOUSEDOWN) then
  643.          * we set DOWN and exit.  If we're not already checked, then
  644.          * we look for the gizmo around us, backwards and forwards,
  645.          * that is checked and uncheck him.
  646.          */
  647.  
  648.         //Search  backwards.
  649.         pCur=pGizmo->pPrev;
  650.  
  651.         while (NULL!=pCur)
  652.             {
  653.             //Stop at any non-exclusive attribute.
  654.             if (GIZMOTYPE_BUTTONATTRIBUTEEX!=pCur->iType)
  655.                 {
  656.                 pCur=NULL;
  657.                 break;
  658.                 }
  659.  
  660.             //If it's down, set it up and we've finished.
  661.             if (BUTTONGROUP_DOWN & pCur->uState)
  662.                 break;
  663.  
  664.             pCur=pCur->pPrev;
  665.             }
  666.  
  667.  
  668.         //If we didn't find a previous one, pCur is NULL, look ahead.
  669.         if (NULL==pCur)
  670.             {
  671.             pCur=pGizmo->pNext;
  672.  
  673.             while (NULL!=pCur)
  674.                 {
  675.                 //Stop at any non-exclusive attribute.
  676.                 if (GIZMOTYPE_BUTTONATTRIBUTEEX!=pCur->iType)
  677.                     {
  678.                     pCur=NULL;
  679.                     break;
  680.                     }
  681.  
  682.                 //If it's down, set it up and we've finished.
  683.                 if (BUTTONGROUP_DOWN & pCur->uState)
  684.                     break;
  685.  
  686.                 pCur=pCur->pNext;
  687.                 }
  688.             }
  689.  
  690.         //If pCur is non-NULL, we found a neighbor, so uncheck it
  691.         if (NULL!=pCur)
  692.             {
  693.             GizmoPStateSet(hWnd, pCur, (pGizmo->fDisabled)
  694.                 ? ATTRIBUTEBUTTON_DISABLED : ATTRIBUTEBUTTON_UP);
  695.             }
  696.  
  697.         //Always set ourselves down
  698.         GizmoPStateSet(hWnd, pGizmo, (pGizmo->fDisabled)
  699.             ? ATTRIBUTEBUTTON_DOWNDISABLED : ATTRIBUTEBUTTON_DOWN);
  700.         }
  701.  
  702.     return fPrevCheck;
  703.     }
  704.  
  705.  
  706.  
  707.  
  708.  
  709.  
  710.  
  711. /*
  712.  * GenericSubProc
  713.  *
  714.  * Purpose:
  715.  *  Subclasses window controls in Gizmos so we can trap the tab key
  716.  *  and tab to the next control.  We can have one shared generic
  717.  *  subclass procedure because we save the type index for this
  718.  *  control in the property "iType."  This allows us to look up the
  719.  *  original procedure in the pfnOrg array.
  720.  *
  721.  * Parameters:
  722.  *  Standard
  723.  *
  724.  * Return Value:
  725.  *  Standard
  726.  */
  727.  
  728. LRESULT APIENTRY GenericSubProc(HWND hWnd, UINT iMsg
  729.     , WPARAM wParam, LPARAM lParam)
  730.     {
  731.     LONG        lRet;
  732.     RECT        rc;
  733.     RECT        rcE;
  734.     HWND        hWndE;
  735.     HBRUSH      hBr;
  736.     HDC         hDC;
  737.     UINT        dx;
  738.     UINT        iType, i;
  739.  
  740.     i=(int)GetProp(hWnd, SZTYPEPROP);
  741.     iType=POSITIONBIT(i);
  742.  
  743.     //Special:  paint the gap in drop-down comboboxes.
  744.     if (GIZMOTYPE_COMBOBOX==iType && WM_PAINT==iMsg)
  745.         {
  746.         //Do default painting.
  747.         lRet=(*pfnOrg[i])(hWnd, iMsg, wParam, lParam);
  748.  
  749.         hWndE=GetDlgItem(hWnd, ID_COMBOEDIT);
  750.  
  751.         GetClientRect(hWnd, &rc);
  752.         GetClientRect(hWndE, &rcE);
  753.  
  754.         //The width of the button is the scroll bar width.
  755.         dx=GetSystemMetrics(SM_CXVSCROLL);
  756.  
  757.         //Calculate the rectangle
  758.         rc.right -=dx;
  759.         rc.left   =rcE.right;
  760.         rc.bottom+=1;
  761.  
  762.         //Paint the gap
  763.         hDC=GetDC(hWnd);   //Already did BeginPaint and EndPaint
  764.  
  765.         hBr=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  766.         FillRect(hDC, &rc, hBr);
  767.         DeleteObject(hBr);
  768.  
  769.         ReleaseDC(hWnd, hDC);
  770.         return lRet;
  771.         }
  772.  
  773.     //Control tabbing to the next or previous control
  774.     if (WM_KEYDOWN==iMsg && VK_TAB==wParam)
  775.         {
  776.         hWndE=hWnd;
  777.  
  778.         if (-1==i)
  779.             hWndE=GetParent(hWnd);
  780.  
  781.         hWndE=GetNextDlgTabItem(GetParent(hWndE), hWnd
  782.             , (BOOL)(GetKeyState(VK_SHIFT)));
  783.         SetFocus(hWndE);
  784.         return 0L;
  785.         }
  786.  
  787.     if (-1==i) i=0;
  788.  
  789.     //Eat tab chars in edit controls to prevent beeping.
  790.     if (0==i && WM_CHAR==iMsg && VK_TAB==wParam)
  791.         return 0L;
  792.  
  793.  
  794.     //Do this or edit controls bomb big-time.
  795.     return CallWindowProc(pfnOrg[i], hWnd, iMsg, wParam, lParam);
  796.     }
  797.