|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.