Annotation of q_a/samples/subclass/subclass.c, revision 1.1.1.1

1.1       root        1: /**************************************************************************\
                      2: *  subclass.c -- Demonstrate subclassing of standard controls.
                      3: *
                      4: *  written March 92 by Steve Firebaugh
                      5: *
                      6: * This sample allows the user to create an arbitrary number of child
                      7: *  controls on the main window.  These controls are subclassed, and the
                      8: *  subclass procedure provides the user a way to move and size the controls.
                      9: *  A menu item switches in and out of "Test Mode."  When this is on, the
                     10: *  subclass procedure passes all messages through to the old procedure, and
                     11: *  the controls act just like normal.
                     12: *
                     13: * There is a single subclass window procedure for a variety of different
                     14: *  control classes.  This is accomplished by storing the old window procedure
                     15: *  for the control in a structure pointed at by the 4 "user extra bytes."
                     16: *  I.e. the GWL_USERDATA contains a pointer to the following structure:
                     17: *
                     18: *       typedef struct tagExtraBytes{
                     19: *           WNDPROC    pfnOldProc;
                     20: *           RECT       rect;
                     21: *           int        Action;
                     22: *       } ExtraBytes, *PExtraBytes;
                     23: *
                     24: *  and the old window procedure is stored in the pfnOldProc field.
                     25: *  The rect field is used for drawing the temporary rectangle while the
                     26: *  user is dragging, and the Action field holds the type of action that
                     27: *  is allowed (move, size, ...).  Notice that the rect rectangle is in
                     28: *  the coordinate space of the parent window.
                     29: *
                     30: * Warning:  buttons, edit fields, and list boxes are easy.  It is difficult
                     31: *  to extend this to static controls because they do not normally receive
                     32: *  input and thus do not get mousedown messages.  It is difficult to extend
                     33: *  this to comboboxes because of the compound structure and the fact that
                     34: *  the comboboxes children controls (edit field & list box) get the mouse
                     35: *  messages first.
                     36: *
                     37: \**************************************************************************/
                     38: 
                     39: #include <windows.h>
                     40: #include "subclass.h"
                     41: 
                     42: 
                     43: 
                     44: /**************************************************************************\
                     45: *
                     46: *  function:  WinMain()
                     47: *
                     48: *  input parameters:  c.f. generic sample
                     49: *
                     50: \**************************************************************************/
                     51: int APIENTRY WinMain(HANDLE hInstance, HANDLE hPrevInstance,
                     52:                      LPSTR lpCmdLine, int nCmdShow)
                     53: {
                     54:   MSG   msg;
                     55: 
                     56:   UNREFERENCED_PARAMETER( lpCmdLine );
                     57: 
                     58: 
                     59:   /* Check for previous instance.  If none, then register class. */
                     60:   if (!hPrevInstance) {
                     61:       WNDCLASS  wc;
                     62: 
                     63:       wc.style = NULL;
                     64:       wc.lpfnWndProc = (WNDPROC)MainWndProc;
                     65:       wc.cbClsExtra = 0;
                     66:       wc.cbWndExtra = 0;
                     67:       wc.hInstance = hInstance;
                     68:       wc.hIcon = LoadIcon(hInstance, "subclassIcon");
                     69:       wc.hCursor = LoadCursor(NULL, IDC_ARROW);
                     70:       wc.hbrBackground = GetStockObject(GRAY_BRUSH);
                     71:       wc.lpszMenuName =  "scMenu";
                     72:       wc.lpszClassName = "scClass";
                     73: 
                     74:       if (!RegisterClass(&wc)) return (FALSE);
                     75:   }  /* class registered o.k. */
                     76: 
                     77: 
                     78:   /* Create the main window.  Return false if CreateWindow() fails */
                     79:   hInst = hInstance;
                     80: 
                     81:   hwndMain = CreateWindow(
                     82:       "scClass",
                     83:       "Subclass Window Sample",
                     84:       WS_OVERLAPPEDWINDOW,
                     85:       CW_USEDEFAULT,
                     86:       CW_USEDEFAULT,
                     87:       CW_USEDEFAULT,
                     88:       CW_USEDEFAULT,
                     89:       NULL,
                     90:       NULL,
                     91:       hInstance,
                     92:       NULL);
                     93: 
                     94:   if (!hwndMain) return (FALSE);
                     95: 
                     96: 
                     97:   ShowWindow(hwndMain, nCmdShow);
                     98: 
                     99:   /* Loop getting messages and dispatching them. */
                    100:   while (GetMessage(&msg,NULL, NULL, NULL)) {
                    101:     TranslateMessage(&msg);
                    102:     DispatchMessage(&msg);
                    103:   }
                    104: 
                    105:   /* Return the value from PostQuitMessage */
                    106:   return (msg.wParam);
                    107: }
                    108: 
                    109: 
                    110: 
                    111: 
                    112: 
                    113: 
                    114: 
                    115: 
                    116: /**************************************************************************\
                    117: *
                    118: *  function:  MainWndProc()
                    119: *
                    120: *  input parameters:  normal window procedure parameters.
                    121: *  global variables:
                    122: *   hInst     - global instance handle.  used in create window.
                    123: *   fTestMode - global boolean, set by menu, if true ignore subclass actions.
                    124: *
                    125: * The main window actually does very little.  Just respond to menu commands.
                    126: \**************************************************************************/
                    127: LONG APIENTRY MainWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
                    128: {
                    129: HWND hwndChild;
                    130: 
                    131:   switch (message) {
                    132: 
                    133:     /**********************************************************************\
                    134:     *  WM_DESTROY
                    135:     *
                    136:     * Post quit message.
                    137:     \**********************************************************************/
                    138:     case WM_DESTROY:
                    139:         PostQuitMessage(0);
                    140:     break;
                    141: 
                    142: 
                    143:     /**********************************************************************\
                    144:     *  WM_COMMAND
                    145:     *
                    146:     * Switch on the different menu items.  Create a different class of
                    147:     *  window for most of them.  Switch on and off the fTestMode global
                    148:     *  variable on IDM_TEST.
                    149:     \**********************************************************************/
                    150:     case WM_COMMAND: {
                    151:       switch (LOWORD (wParam)) {
                    152:         case IDM_BUTTON:
                    153:           hwndChild = CreateWindow(
                    154:             "BUTTON", "button",
                    155:             WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
                    156:             50,10, 60, 32,
                    157:             hwnd, NULL, hInst, NULL);
                    158: 
                    159:           SubclassWindow (hwndChild, SubclassWndProc);
                    160:         break;
                    161: 
                    162:         case IDM_EDIT:
                    163:           hwndChild = CreateWindow(
                    164:             "EDIT", "edit",
                    165:             WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
                    166:             50,50, 60, 20,
                    167:             hwnd, NULL, hInst, NULL);
                    168: 
                    169:           SubclassWindow (hwndChild, SubclassWndProc);
                    170:         break;
                    171: 
                    172:         case IDM_LIST:
                    173:           hwndChild = CreateWindow(
                    174:             "LISTBOX", NULL,
                    175:             WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
                    176:             50,90, 60, 50,
                    177:             hwnd, NULL, hInst, NULL);
                    178: 
                    179:           SubclassWindow (hwndChild, SubclassWndProc);
                    180:         break;
                    181: 
                    182:         case IDM_TEST:
                    183:           fTestMode = ToggleMenu (GetMenu (hwnd), IDM_TEST);
                    184:         break;
                    185: 
                    186:       }       /* end switch */
                    187:     } break;  /* end WM_COMMAND  */
                    188: 
                    189:     default:
                    190:       return (DefWindowProc(hwnd, message, wParam, lParam));
                    191:   }
                    192:   return (NULL);
                    193: }
                    194: 
                    195: 
                    196: 
                    197: 
                    198: /**************************************************************************\
                    199: *
                    200: *  function:  SubclassWndProc
                    201: *
                    202: *  input parameters:  normal window procedure parameters.
                    203: *  global variables:
                    204: *   hwndMain  - parent window of the control.
                    205: *   fTestMode - global boolean, set by menu, if true ignore subclass actions.
                    206: \**************************************************************************/
                    207: LONG APIENTRY SubclassWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
                    208: {
                    209: PExtraBytes  peb;
                    210: 
                    211: 
                    212:   /* get the pointer to the extra bytes structure out of the user data. */
                    213:   peb = (PExtraBytes)GetWindowLong (hwnd, GWL_USERDATA);
                    214: 
                    215:   /* if running in test mode, just return the old procedure. */
                    216:   if (fTestMode)
                    217:     return (CallWindowProc ((peb->pfnOldProc), hwnd, message, wParam, lParam));
                    218: 
                    219: 
                    220:   switch (message) {
                    221: 
                    222: 
                    223:     /**********************************************************************\
                    224:     *  WM_DESTROY
                    225:     *
                    226:     * Free up the ExtraBytes structure which was allocated at subclass time.
                    227:     \**********************************************************************/
                    228:     case WM_DESTROY:
                    229:       LocalUnlock (LocalHandle ((LPSTR)peb));
                    230:     break;
                    231: 
                    232: 
                    233:     /**********************************************************************\
                    234:     *  WM_SETCURSOR
                    235:     *
                    236:     * Set the mouse pointer conditional on the Action which would be taken
                    237:     *  if the user presses mouse down.  This is set in WM_MOUSEMOVE.
                    238:     \**********************************************************************/
                    239:     case WM_SETCURSOR:
                    240:       switch (peb->Action) {
                    241:         case ACTIONNONE  :SetCursor (LoadCursor (NULL, IDC_ARROW)); break;
                    242:         case ACTIONMOVE  :SetCursor (LoadCursor (NULL, IDC_SIZEALL)); break;
                    243:         case ACTIONSIZEX :SetCursor (LoadCursor (NULL, IDC_SIZEWE)); break;
                    244:         case ACTIONSIZEY :SetCursor (LoadCursor (NULL, IDC_SIZENS)); break;
                    245:         case ACTIONSIZEXY:SetCursor (LoadCursor (NULL, IDC_SIZENWSE)); break;
                    246:       }
                    247:     return (0);
                    248: 
                    249: 
                    250: 
                    251:     /**********************************************************************\
                    252:     *  WM_LBUTTONDOWN
                    253:     *
                    254:     * The user is beginning a drag operation.  Fill in an initial window pos
                    255:     *  in the rect field, figure out which corner the cursor should be set
                    256:     *  to, translate that point to screen coordinates, and finally capture
                    257:     *  the mouse.
                    258:     \**********************************************************************/
                    259:     case WM_LBUTTONDOWN: {
                    260:       POINT  mouse;
                    261: 
                    262:       QueryWindowPos (hwnd, &peb->rect);
                    263: 
                    264:       switch (peb->Action) {
                    265:         case ACTIONMOVE  :
                    266:           mouse.x = peb->rect.left;
                    267:           mouse.y = peb->rect.top ;
                    268:         break;
                    269: 
                    270:         case ACTIONSIZEX :
                    271:           mouse.x = peb->rect.right;
                    272:           mouse.y = peb->rect.top ;
                    273:         break;
                    274: 
                    275:         case ACTIONSIZEY :
                    276:           mouse.x = peb->rect.left;
                    277:           mouse.y = peb->rect.bottom;
                    278:         break;
                    279: 
                    280:         case ACTIONSIZEXY:
                    281:           mouse.x = peb->rect.right;
                    282:           mouse.y = peb->rect.bottom;
                    283:         break;
                    284:       }
                    285: 
                    286:       /* SetCursorPos() works based on screen position, and mouse is
                    287:        *  currently relative to hwndMain.
                    288:        */
                    289:       ClientToScreen (hwndMain, &mouse);
                    290:       SetCursorPos (mouse.x, mouse.y);
                    291:       SetCapture (hwnd);
                    292:     } return 0;
                    293: 
                    294: 
                    295:     /**********************************************************************\
                    296:     *  WM_LBUTTONUP
                    297:     *
                    298:     * Complement of the WM_LBUTTONDOWN message.  Release the capture,
                    299:     *  and reset the actual window's position.
                    300:     \**********************************************************************/
                    301:     case WM_LBUTTONUP:
                    302:       if (GetCapture() == hwnd) {
                    303:         ReleaseCapture();
                    304:         SetWindowPos (hwnd, NULL, peb->rect.left, peb->rect.top,
                    305:                       (peb->rect.right - peb->rect.left),
                    306:                       (peb->rect.bottom - peb->rect.top),
                    307:                       SWP_NOZORDER);
                    308:         InvalidateRect (hwnd, NULL, TRUE);
                    309:       }
                    310:     return 0;
                    311: 
                    312: 
                    313: 
                    314:     /**********************************************************************\
                    315:     *  WM_MOUSEMOVE
                    316:     *
                    317:     * There are two cases of interest here.  If the mouse is captured, then
                    318:     *  change the rect field in the extrabyte structure to reflect the new
                    319:     *  size/position that the user is selection.
                    320:     *
                    321:     *  If the mouse is not captured, then set the Action field dependent on
                    322:     *  the quadrant so that the WM_SETCURSOR will display the correct cursor.
                    323:     *
                    324:     \**********************************************************************/
                    325:     case WM_MOUSEMOVE: {
                    326:       POINT mouse;
                    327:       /* the LO/HI-WORD from lParam will be a signed short relative to
                    328:        *  the child control window.
                    329:        */
                    330:       mouse.x = (int)(short)LOWORD(lParam);
                    331:       mouse.y = (int)(short)HIWORD(lParam);
                    332: 
                    333: 
                    334: 
                    335:       /* if the mouse is captured, then we are doing direct manipulation */
                    336:       if (GetCapture() == hwnd) {
                    337: 
                    338:         /* translate mouse pos to be client relative, not control relative */
                    339:         ClientToScreen (hwnd, &mouse);
                    340:         ScreenToClient (hwndMain, &mouse);
                    341: 
                    342:         /* erase the old rectangle */
                    343:         PaintRect (hwndMain, &peb->rect);
                    344: 
                    345:         switch (peb->Action) {
                    346:           case ACTIONMOVE  : {
                    347:             int width, height;
                    348: 
                    349:             width = peb->rect.right - peb->rect.left;
                    350:             height = peb->rect.bottom - peb->rect.top;
                    351:             peb->rect.left = mouse.x;
                    352:             peb->rect.top  = mouse.y;
                    353:             peb->rect.right  = peb->rect.left + width;
                    354:             peb->rect.bottom = peb->rect.top +  height;
                    355:           }break;
                    356: 
                    357:           case ACTIONSIZEX :
                    358:             peb->rect.right  = mouse.x;
                    359:           break;
                    360: 
                    361:           case ACTIONSIZEY :
                    362:             peb->rect.bottom = mouse.y;
                    363:           break;
                    364: 
                    365:           case ACTIONSIZEXY:
                    366:             peb->rect.right  = mouse.x;
                    367:             peb->rect.bottom = mouse.y;
                    368:           break;
                    369:         }
                    370: 
                    371:         /* Redraw the new rectangle */
                    372:         PaintRect (hwndMain, &peb->rect);
                    373:       }
                    374: 
                    375: 
                    376: 
                    377:       /* if the mouse is not captured, then set the action for the sake of
                    378:        *  the WM_SETCURSOR message.  The action is dependent upon which
                    379:        *  quadrant of the window the mouse cursor is over.
                    380:        */
                    381:       else {
                    382:         int width2, height2;
                    383:         RECT rect;
                    384: 
                    385:         GetClientRect (hwnd, &rect);
                    386:         width2 =  (rect.right -  rect.left)/2;
                    387:         height2 =  (rect.bottom -  rect.top)/2;
                    388: 
                    389:         /* upper left hand corner */
                    390:         if ((mouse.x <= (width2)) && (mouse.y <= (height2))) {
                    391:             peb->Action = ACTIONMOVE;
                    392: 
                    393:         /* lower left hand corner */
                    394:         } else if ((mouse.x <= (width2)) && (mouse.y > (height2))) {
                    395:             peb->Action = ACTIONSIZEY;
                    396: 
                    397:         /* upper right hand corner */
                    398:         } else if ((mouse.x > (width2)) && (mouse.y <= (height2))) {
                    399:             peb->Action = ACTIONSIZEX;
                    400: 
                    401:         /* lower right hand corner */
                    402:         } else if ((mouse.x > (width2)) && (mouse.y > (height2))) {
                    403:             peb->Action = ACTIONSIZEXY;
                    404:         }
                    405:       }
                    406:     } return 0;
                    407: 
                    408: 
                    409: 
                    410:     /**********************************************************************\
                    411:     * for messages that are not handled explicitly here, pass them
                    412:     *  back to the old window procedure.
                    413:     \**********************************************************************/
                    414:     default:
                    415:       return (CallWindowProc ((peb->pfnOldProc), hwnd, message, wParam, lParam));
                    416:     break;
                    417:     } /* end switch */
                    418: 
                    419:   return 0;
                    420: }
                    421: 
                    422: 
                    423: 
                    424: 
                    425: 
                    426: 
                    427: /**************************************************************************\
                    428: *  function:  SubclassWindow
                    429: *
                    430: *  input parameters:
                    431: *   hwnd            - window handle to be subclassed,
                    432: *   SubclassWndProc - the new window procedure.
                    433: *
                    434: *  Set in a new window procedure for this window.  Store the old window
                    435: *   procedure in the first field of the extrabytes structure.  This routine
                    436: *   is specific to this program in the use of this particular extrabyte
                    437: *   structure.  Note that the pointer in the user bytes needs to be freed
                    438: *   later (in WM_DESTROY).
                    439: \**************************************************************************/
                    440: VOID SubclassWindow (HWND hwnd, WNDPROC SubclassWndProc)
                    441: {
                    442: PExtraBytes  peb;
                    443: 
                    444:   peb = (PExtraBytes)LocalAlloc (LPTR, sizeof (ExtraBytes));
                    445:   peb->pfnOldProc = (WNDPROC) GetWindowLong (hwnd, GWL_WNDPROC);
                    446: 
                    447:   SetWindowLong (hwnd, GWL_USERDATA, (LONG) peb);
                    448:   SetWindowLong (hwnd, GWL_WNDPROC,  (LONG) SubclassWndProc);
                    449: }
                    450: 
                    451: 
                    452: 
                    453: 
                    454: 
                    455: /**************************************************************************\
                    456: *  function:  QueryWindowPos
                    457: *
                    458: *  input parameters:
                    459: *   hwnd - window handle,  prect - pointer to rectangle to hold the answer.
                    460: *
                    461: *  global variables:
                    462: *   hwndMain  - parent window of the control.
                    463: *
                    464: *  Return the bounding rectangle for the hwnd control relative to hwndMain.
                    465: \**************************************************************************/
                    466: VOID QueryWindowPos (HWND hwnd, LPRECT prect)
                    467: {
                    468:   GetWindowRect (hwnd, prect);
                    469:   ScreenToClient (hwndMain, (LPPOINT)&(prect->left));
                    470:   ScreenToClient (hwndMain, (LPPOINT)&(prect->right));
                    471: }
                    472: 
                    473: 
                    474: 
                    475: 
                    476: 
                    477: /**************************************************************************\
                    478: *  function:  PaintRect
                    479: *
                    480: *  input parameters:
                    481: *   hwnd - window handle,  prect - pointer to rectangle to be painted.
                    482: *
                    483: *  Get an HDC and draw the rectangle in R2_NOT mode which if done twice will
                    484: *   leave the screen in its initial state.
                    485: \**************************************************************************/
                    486: VOID PaintRect (HWND hwnd, LPRECT prect)
                    487: {
                    488: HDC hdc;
                    489: 
                    490:     hdc = GetDC (hwnd);
                    491:     SetROP2(hdc, R2_NOT);
                    492:     SelectObject (hdc, GetStockObject (NULL_BRUSH));
                    493:     Rectangle (hdc, prect->left, prect->top,
                    494:                     prect->right, prect->bottom);
                    495:     ReleaseDC (hwnd, hdc);
                    496: }
                    497: 
                    498: 
                    499: 
                    500: 
                    501: 
                    502: /**************************************************************************\
                    503: *  function:  ToggleMenu
                    504: *
                    505: *  input parameters:
                    506: *   hmenu - menu handle,  iditem - id of the item to check.
                    507: *
                    508: *  returns:  the new checked state.
                    509: *
                    510: *  Change a menu item to be checked if it is not, or unchecked if it is.
                    511: \**************************************************************************/
                    512: DWORD ToggleMenu (HMENU hmenu, UINT iditem)
                    513: {
                    514: DWORD  state;
                    515: 
                    516:   /* query the checked state.
                    517:    *  unfortunately, this turns checked off.
                    518:    */
                    519:   state = CheckMenuItem (hmenu, iditem, MF_BYCOMMAND);
                    520: 
                    521:   /* switch the truth value of oldstate. */
                    522:   state = (state) ? MF_UNCHECKED : MF_CHECKED;
                    523: 
                    524:   /* reset the menu item. */
                    525:   CheckMenuItem (hmenu, iditem, MF_BYCOMMAND | state);
                    526: 
                    527:   return state;
                    528: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.