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

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

unix.superglobalmegacorp.com

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