GIZMO.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:20k
源码类别:

Windows编程

开发平台:

Visual C++

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