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