Annotation of q_a/samples/subclass/subclass.c, revision 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.