|
|
1.1 ! 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: ! 12: /****************************** Module Header ******************************* ! 13: * Module Name: drag.c ! 14: * ! 15: * Contains routines for dragging and sizing controls. ! 16: * ! 17: * Functions: ! 18: * ShowTrackRect() ! 19: * HideTrackRect() ! 20: * FitRectToBounds() ! 21: * GetOverHang() ! 22: * GridizeRect() ! 23: * SizeDragToControl() ! 24: * DragWndProc() ! 25: * DrawHandles() ! 26: * HandleHitTest() ! 27: * CtrlButtonDown() ! 28: * DragNewBegin() ! 29: * CtrlMouseMove() ! 30: * PreDragTimeout() ! 31: * DragCancel() ! 32: * CtrlButtonUp() ! 33: * DragEnd() ! 34: * CalcCursorOffset() ! 35: * InitTracking() ! 36: * DrawTrackRect() ! 37: * CancelTracking() ! 38: * AtOrAbove() ! 39: * AtOrBelow() ! 40: * PaintUnderDrag() ! 41: * MouseToDragRect() ! 42: * MouseToDU() ! 43: * DragBegin() ! 44: * CtrlHitTest() ! 45: * DragBegin2() ! 46: * ! 47: * Comments: ! 48: * ! 49: ****************************************************************************/ ! 50: ! 51: #include "dlgedit.h" ! 52: #include "dlgfuncs.h" ! 53: #include "dlgextrn.h" ! 54: ! 55: #include <stdlib.h> ! 56: ! 57: STATICFN VOID CalcCursorOffset(POINT *ppt); ! 58: STATICFN VOID InitTracking(VOID); ! 59: STATICFN VOID DrawTrackRect(PRECT prc, BOOL fDialog, BOOL fDraw); ! 60: STATICFN VOID CancelTracking(VOID); ! 61: STATICFN INT AtOrAbove(INT nStart, INT nGrid); ! 62: STATICFN INT AtOrBelow(INT nStart, INT nGrid); ! 63: STATICFN VOID PaintUnderDrag(HWND hwndDrag); ! 64: STATICFN VOID MouseToDragRect(INT x, INT y, PRECT prc); ! 65: STATICFN VOID MouseToDU(PPOINT ppt); ! 66: STATICFN VOID DragBegin(HWND hwnd, INT x, INT y, BOOL fHandleWindow); ! 67: STATICFN HWND CtrlHitTest(HWND hwnd, PPOINT ppt); ! 68: STATICFN VOID DragBegin2(PPOINT ppt); ! 69: ! 70: /* ! 71: * This contains the initial location of the mouse when going into ! 72: * pre-drag mode. If the mouse pointer is moved too FAR away from ! 73: * this point, we will start the drag operation, even if the pre-drag ! 74: * timer has not elapsed yet. ! 75: */ ! 76: static POINT gptPreDragStart; ! 77: ! 78: ! 79: ! 80: /************************************************************************ ! 81: * CalcCursorOffset ! 82: * ! 83: * This routine updates the gptCursorOffset point. This is used during ! 84: * dragging operations. It contains the offset from the mouse pointer ! 85: * at the time a dragging operation is begun and the upper left corner ! 86: * of the dragging rectangle. This value is needed for determining ! 87: * where mouse events are occuring in relation to where the drag was ! 88: * initially begun from. ! 89: * ! 90: * Arguments: ! 91: * POINT - offset where the mouse pointer began drag. ! 92: * ! 93: ************************************************************************/ ! 94: ! 95: STATICFN VOID CalcCursorOffset( ! 96: POINT *ppt) ! 97: { ! 98: RECT rc; ! 99: POINT pt; ! 100: ! 101: if (gfDlgSelected) { ! 102: gptCursorOffset = *ppt; ! 103: } ! 104: else { ! 105: rc = grcSelected; ! 106: pt = *ppt; ! 107: DUToWinRect(&rc); ! 108: ! 109: ClientToScreen(gnpcSel->hwnd, &pt); ! 110: ScreenToClient(gcd.npc->hwnd, &pt); ! 111: ! 112: gptCursorOffset.x = pt.x - rc.left; ! 113: gptCursorOffset.y = pt.y - rc.top; ! 114: } ! 115: } ! 116: ! 117: ! 118: ! 119: /************************************************************************ ! 120: * InitTracking ! 121: * ! 122: * This function initializes a tracking operation. The pointer is ! 123: * changed to be the system "move" pointer if we are moving the ! 124: * control (not sizing it). ! 125: * ! 126: ************************************************************************/ ! 127: ! 128: STATICFN VOID InitTracking(VOID) ! 129: { ! 130: if (gfDlgSelected) ! 131: ghDCTrack = CreateDC(L"DISPLAY", NULL, NULL, NULL); ! 132: else ! 133: ghDCTrack = GetDC(gcd.npc->hwnd); ! 134: ! 135: SetROP2(ghDCTrack, R2_NOT); ! 136: } ! 137: ! 138: ! 139: ! 140: /************************************************************************ ! 141: * DrawTrackRect ! 142: * ! 143: * This routine draws the drag rectangle. It is assumed that the window ! 144: * has been locked for update appropriately or this could leave garbage ! 145: * around. The rectangle given is in dialog units, and is converted ! 146: * to window coordinates using different rules based on the value of ! 147: * fDialog. After this routine has been called to set the rectangle, ! 148: * the HideTrackRect and ShowTrackRect functions can be called to ! 149: * temporarily hide the track rectangle, but this routine must be called ! 150: * again every time that the tracking rectangle is to be changed. ! 151: * ! 152: * Arguments: ! 153: * PRECT prc - Drag rectangle to draw (in dialog units). ! 154: * BOOL fDialog - TRUE if the control being dragged is the dialog. ! 155: * BOOL fDraw - If TRUE, the rectangle will be drawn. Having this ! 156: * FALSE is useful to just initialize the state globals, ! 157: * but defer the drawing of the rectangle until the mouse ! 158: * is moved from its starting point. ! 159: * ! 160: ************************************************************************/ ! 161: ! 162: STATICFN VOID DrawTrackRect( ! 163: PRECT prc, ! 164: BOOL fDialog, ! 165: BOOL fDraw) ! 166: { ! 167: HideTrackRect(); ! 168: ! 169: grcTrackWin = grcTrackDU = *prc; ! 170: DUToWinRect(&grcTrackWin); ! 171: ! 172: if (fDialog) { ! 173: AdjustWindowRectEx(&grcTrackWin, gcd.npc->flStyle, FALSE, ! 174: (gcd.npc->flStyle & DS_MODALFRAME) ? ! 175: gcd.npc->flExtStyle | WS_EX_DLGMODALFRAME : ! 176: gcd.npc->flExtStyle); ! 177: ClientToScreenRect(ghwndSubClient, &grcTrackWin); ! 178: } ! 179: ! 180: if (fDraw) ! 181: ShowTrackRect(); ! 182: } ! 183: ! 184: ! 185: ! 186: /************************************************************************ ! 187: * ShowTrackRect ! 188: * ! 189: * This routine shows the current tracking rectangle. ! 190: * ! 191: ************************************************************************/ ! 192: ! 193: VOID ShowTrackRect(VOID) ! 194: { ! 195: if (!gfTrackRectShown) { ! 196: MyFrameRect(ghDCTrack, &grcTrackWin, DSTINVERT); ! 197: gfTrackRectShown = TRUE; ! 198: } ! 199: } ! 200: ! 201: ! 202: ! 203: /************************************************************************ ! 204: * HideTrackRect ! 205: * ! 206: * This routine hides the current tracking rectangle. ! 207: * ! 208: ************************************************************************/ ! 209: ! 210: VOID HideTrackRect(VOID) ! 211: { ! 212: if (gfTrackRectShown) { ! 213: MyFrameRect(ghDCTrack, &grcTrackWin, DSTINVERT); ! 214: gfTrackRectShown = FALSE; ! 215: } ! 216: } ! 217: ! 218: ! 219: ! 220: /************************************************************************ ! 221: * CancelTracking ! 222: * ! 223: * This routine is used to cancel the display of the tracking rectangle. ! 224: * It is basically the opposite of InitTracking. ! 225: * ! 226: ************************************************************************/ ! 227: ! 228: STATICFN VOID CancelTracking(VOID) ! 229: { ! 230: if (gfTrackRectShown) { ! 231: HideTrackRect(); ! 232: ! 233: if (gfDlgSelected) ! 234: DeleteDC(ghDCTrack); ! 235: else ! 236: ReleaseDC(gcd.npc->hwnd, ghDCTrack); ! 237: } ! 238: } ! 239: ! 240: ! 241: ! 242: /************************************************************************ ! 243: * FitRectToBounds ! 244: * ! 245: * This routine fits the given rectangle to the appropriate boundary. ! 246: * If fDialog is FALSE, the rectangle is a control and it must fall ! 247: * entirely within the area of the current dialog being edited. If the ! 248: * rectangle is adjusted to fit, the moved edge(s) will be aligned on ! 249: * a grid boundary. The wHandleHit parameter is used to tell this routine ! 250: * what edges are allowed to move, in other words, what edges are ! 251: * "anchored" down and what edges are being tracked. ! 252: * ! 253: * Arguments: ! 254: * PRECT prc - Rectangle to be adjusted to the allowed size. ! 255: * INT nOverHang - How much the control can hang below the dialog. ! 256: * This is primarily for the combobox listboxes. ! 257: * INT HandleHit - One of the DRAG_* constants. ! 258: * BOOL fDialog - TRUE if the rectangle is for a dialog. ! 259: * ! 260: ************************************************************************/ ! 261: ! 262: VOID FitRectToBounds( ! 263: PRECT prc, ! 264: INT nOverHang, ! 265: INT HandleHit, ! 266: BOOL fDialog) ! 267: { ! 268: INT cxDlg; ! 269: INT cyDlg; ! 270: INT dx; ! 271: INT dy; ! 272: ! 273: /* ! 274: * Are we just moving the control (not sizing)? ! 275: */ ! 276: if (HandleHit == DRAG_CENTER) { ! 277: /* ! 278: * We only do range checking if it is a control (not on the dialog). ! 279: */ ! 280: if (!fDialog) { ! 281: dx = prc->right - prc->left; ! 282: dy = prc->bottom - prc->top; ! 283: cxDlg = gcd.npc->rc.right - gcd.npc->rc.left; ! 284: cyDlg = gcd.npc->rc.bottom - gcd.npc->rc.top + nOverHang; ! 285: ! 286: if (prc->right > cxDlg) { ! 287: prc->left = AtOrBelow(cxDlg - dx, gcxGrid); ! 288: prc->right = prc->left + dx; ! 289: } ! 290: ! 291: if (prc->left < 0) { ! 292: prc->left = 0; ! 293: prc->right = prc->left + dx; ! 294: } ! 295: ! 296: if (prc->bottom > cyDlg) { ! 297: prc->top = AtOrBelow(cyDlg - dy, gcyGrid); ! 298: prc->bottom = prc->top + dy; ! 299: } ! 300: ! 301: if (prc->top < 0) { ! 302: prc->top = 0; ! 303: prc->bottom = prc->top + dy; ! 304: } ! 305: } ! 306: ! 307: return; ! 308: } ! 309: ! 310: if (fDialog) { ! 311: /* ! 312: * When dealing with the dialog, we want to take into account ! 313: * the controls so that the dialog is never sized to hide a ! 314: * control. This routine assumes that grcMinDialog has already ! 315: * been set to enclose the controls. If the dialog has no ! 316: * controls, this rectangle is not used, but the dialog's size ! 317: * is still limited so that it never goes negative. ! 318: */ ! 319: /* ! 320: * First deal with the x coordinates. ! 321: */ ! 322: switch (HandleHit) { ! 323: case DRAG_LEFTBOTTOM: ! 324: case DRAG_LEFT: ! 325: case DRAG_LEFTTOP: ! 326: if (npcHead) { ! 327: if (prc->left > grcMinDialog.left) ! 328: prc->left = AtOrBelow(grcMinDialog.left, gcxGrid); ! 329: } ! 330: else { ! 331: if (prc->left > prc->right) ! 332: prc->left = AtOrBelow(prc->right, gcxGrid); ! 333: } ! 334: ! 335: break; ! 336: ! 337: case DRAG_RIGHTBOTTOM: ! 338: case DRAG_RIGHT: ! 339: case DRAG_RIGHTTOP: ! 340: if (npcHead) { ! 341: if (prc->right < grcMinDialog.right) ! 342: prc->right = AtOrAbove(grcMinDialog.right, gcxGrid); ! 343: } ! 344: else { ! 345: if (prc->right < prc->left) ! 346: prc->right = AtOrAbove(prc->left, gcxGrid); ! 347: } ! 348: ! 349: break; ! 350: } ! 351: ! 352: /* ! 353: * Now deal with the y coordinates. ! 354: */ ! 355: switch (HandleHit) { ! 356: case DRAG_LEFTBOTTOM: ! 357: case DRAG_BOTTOM: ! 358: case DRAG_RIGHTBOTTOM: ! 359: if (npcHead) { ! 360: if (prc->bottom < grcMinDialog.bottom) ! 361: prc->bottom = AtOrAbove(grcMinDialog.bottom, gcyGrid); ! 362: } ! 363: else { ! 364: if (prc->bottom < prc->top) ! 365: prc->bottom = AtOrAbove(prc->top, gcyGrid); ! 366: } ! 367: ! 368: break; ! 369: ! 370: case DRAG_LEFTTOP: ! 371: case DRAG_TOP: ! 372: case DRAG_RIGHTTOP: ! 373: if (npcHead) { ! 374: if (prc->top > grcMinDialog.top) ! 375: prc->top = AtOrBelow(grcMinDialog.top, gcyGrid); ! 376: } ! 377: else { ! 378: if (prc->top > prc->bottom) ! 379: prc->top = AtOrBelow(prc->bottom, gcyGrid); ! 380: } ! 381: ! 382: break; ! 383: } ! 384: } ! 385: else { ! 386: /* ! 387: * First deal with the x coordinates. ! 388: */ ! 389: switch (HandleHit) { ! 390: case DRAG_LEFTBOTTOM: ! 391: case DRAG_LEFT: ! 392: case DRAG_LEFTTOP: ! 393: if (prc->left > prc->right) ! 394: prc->left = AtOrBelow(prc->right, gcxGrid); ! 395: ! 396: if (prc->left == prc->right) ! 397: prc->left -= gcxGrid; ! 398: ! 399: if (prc->left < 0) ! 400: prc->left = 0; ! 401: ! 402: break; ! 403: ! 404: case DRAG_RIGHTBOTTOM: ! 405: case DRAG_RIGHT: ! 406: case DRAG_RIGHTTOP: ! 407: cxDlg = gcd.npc->rc.right - gcd.npc->rc.left; ! 408: if (prc->right > cxDlg) ! 409: prc->right = AtOrBelow(cxDlg, gcxGrid); ! 410: ! 411: if (prc->right < prc->left) ! 412: prc->right = AtOrAbove(prc->left, gcxGrid); ! 413: ! 414: if (prc->right == prc->left) ! 415: prc->right += gcxGrid; ! 416: ! 417: break; ! 418: } ! 419: ! 420: /* ! 421: * Now deal with the y coordinates. ! 422: */ ! 423: switch (HandleHit) { ! 424: case DRAG_LEFTTOP: ! 425: case DRAG_TOP: ! 426: case DRAG_RIGHTTOP: ! 427: if (prc->top > prc->bottom) ! 428: prc->top = AtOrBelow(prc->bottom, gcyGrid); ! 429: ! 430: if (prc->top == prc->bottom) ! 431: prc->top -= gcyGrid; ! 432: ! 433: if (prc->top < 0) ! 434: prc->top = 0; ! 435: ! 436: break; ! 437: ! 438: case DRAG_LEFTBOTTOM: ! 439: case DRAG_BOTTOM: ! 440: case DRAG_RIGHTBOTTOM: ! 441: cyDlg = gcd.npc->rc.bottom - gcd.npc->rc.top; ! 442: ! 443: /* ! 444: * Note that if there is an overhang allowed, then ! 445: * we do not limit how FAR down the bottom of the ! 446: * control can be. ! 447: */ ! 448: if (prc->bottom > cyDlg && !nOverHang) ! 449: prc->bottom = AtOrBelow(cyDlg, gcyGrid); ! 450: ! 451: if (prc->bottom < prc->top) ! 452: prc->bottom = AtOrAbove(prc->top, gcyGrid); ! 453: ! 454: if (prc->bottom == prc->top) ! 455: prc->bottom += gcyGrid; ! 456: ! 457: break; ! 458: } ! 459: } ! 460: } ! 461: ! 462: ! 463: ! 464: /************************************************************************ ! 465: * AtOrAbove ! 466: * ! 467: * This routine takes a number, and returns the closest number that ! 468: * is equal to or above that number and is an integral of the given ! 469: * grid value. ! 470: * ! 471: * Arguments: ! 472: * INT nStart - Starting number (can be negative). ! 473: * INT nGrid - Grid value. ! 474: * ! 475: ************************************************************************/ ! 476: ! 477: STATICFN INT AtOrAbove( ! 478: INT nStart, ! 479: INT nGrid) ! 480: { ! 481: register INT nAbove; ! 482: ! 483: nAbove = (nStart / nGrid) * nGrid; ! 484: ! 485: if (nStart > 0 && nStart != nAbove) ! 486: nAbove += nGrid; ! 487: ! 488: return nAbove; ! 489: } ! 490: ! 491: ! 492: ! 493: /************************************************************************ ! 494: * AtOrBelow ! 495: * ! 496: * This routine takes a number, and returns the closest number that ! 497: * is equal to or below that number and is an integral of the given ! 498: * grid value. ! 499: * ! 500: * Arguments: ! 501: * INT nStart - Starting number (can be negative). ! 502: * INT nGrid - Grid value. ! 503: * ! 504: ************************************************************************/ ! 505: ! 506: STATICFN INT AtOrBelow( ! 507: INT nStart, ! 508: INT nGrid) ! 509: { ! 510: register INT nBelow; ! 511: ! 512: nBelow = (nStart / nGrid) * nGrid; ! 513: ! 514: if (nStart < 0 && nStart != nBelow) ! 515: nBelow -= nGrid; ! 516: ! 517: return nBelow; ! 518: } ! 519: ! 520: ! 521: ! 522: /************************************************************************ ! 523: * GetOverHang ! 524: * ! 525: * This function returns the height that the control can overhang the ! 526: * bottom of the dialog. This is currently only meaningful for comboboxes. ! 527: * If the control is not a combobox, zero is returned. ! 528: * ! 529: * Arguments: ! 530: * INT iType - Type of control (W_* constant). ! 531: * INT cy - Height of the control (in DU's). ! 532: * ! 533: ************************************************************************/ ! 534: ! 535: INT GetOverHang( ! 536: INT iType, ! 537: INT cy) ! 538: { ! 539: if (iType != W_COMBOBOX) ! 540: return 0; ! 541: ! 542: return max(cy - COMBOEDITHEIGHT, 0); ! 543: } ! 544: ! 545: ! 546: ! 547: /************************************************************************ ! 548: * GridizeRect ! 549: * ! 550: * This function "gridizes" coordinates in a rectangle. The current ! 551: * grid values are used. The fsGrid flag can contain OR'd together ! 552: * GRIDIZE_* values that specify which points to apply the gridding to. ! 553: * Upon return, all coordinates specified will have been rounded to the ! 554: * nearest grid boundary. ! 555: * ! 556: * If GRIDIZE_SAMESIZE is specified, the size of the control will be ! 557: * kept the same. This overrides the GRIDIZE_RIGHT and GRIDIZE_BOTTOM ! 558: * flags. In other words, any delta applied to left or top will ! 559: * be added to right and bottom to retain the original size of the ! 560: * rectangle. ! 561: * ! 562: * Arguments: ! 563: * PRECT prc - Rectangle to adjust to the current grid. ! 564: * INT fGridFlags - GRIDIZE_* flags. Specifies which points to gridize. ! 565: * ! 566: ************************************************************************/ ! 567: ! 568: VOID GridizeRect( ! 569: PRECT prc, ! 570: INT fGridFlags) ! 571: { ! 572: register INT nTemp; ! 573: INT leftOld = prc->left; ! 574: INT topOld = prc->top; ! 575: ! 576: if (fGridFlags & GRIDIZE_LEFT) { ! 577: nTemp = AtOrBelow(prc->left, gcxGrid); ! 578: ! 579: if (prc->left - nTemp > gcxGrid / 2) ! 580: nTemp += gcxGrid; ! 581: ! 582: prc->left = nTemp; ! 583: } ! 584: ! 585: if (fGridFlags & GRIDIZE_TOP) { ! 586: nTemp = AtOrBelow(prc->top, gcyGrid); ! 587: ! 588: if (prc->top - nTemp > gcyGrid / 2) ! 589: nTemp += gcyGrid; ! 590: ! 591: prc->top = nTemp; ! 592: } ! 593: ! 594: /* ! 595: * Do they want to retain the same size of the rectangle? ! 596: */ ! 597: if (fGridFlags & GRIDIZE_SAMESIZE) { ! 598: /* ! 599: * Shift the right coordinate over by the delta that ! 600: * was applied to the left. ! 601: */ ! 602: prc->right += prc->left - leftOld; ! 603: prc->bottom += prc->top - topOld; ! 604: } ! 605: else { ! 606: if (fGridFlags & GRIDIZE_RIGHT) { ! 607: nTemp = AtOrBelow(prc->right, gcxGrid); ! 608: ! 609: if (prc->right - nTemp > gcxGrid / 2) ! 610: nTemp += gcxGrid; ! 611: ! 612: prc->right = nTemp; ! 613: } ! 614: ! 615: if (fGridFlags & GRIDIZE_BOTTOM) { ! 616: nTemp = AtOrBelow(prc->bottom, gcyGrid); ! 617: ! 618: if (prc->bottom - nTemp > gcyGrid / 2) ! 619: nTemp += gcyGrid; ! 620: ! 621: prc->bottom = nTemp; ! 622: } ! 623: } ! 624: } ! 625: ! 626: ! 627: ! 628: /************************************************************************ ! 629: * SizeDragToControl ! 630: * ! 631: * This routine sizes and positions the drag window associated with a ! 632: * control, based on the current size and position of the control. ! 633: * ! 634: * It takes into account the different origin that controls and dialogs ! 635: * have, and sizes the drag window to fit around the control properly. ! 636: * The Z order of the drag window is NOT changed. ! 637: * ! 638: * This routine should only be called for controls, not the dialog. ! 639: * ! 640: * Arguments: ! 641: * NPCTYPE npc - Control whose drag window needs to be sized. ! 642: * ! 643: ************************************************************************/ ! 644: ! 645: VOID SizeDragToControl( ! 646: NPCTYPE npc) ! 647: { ! 648: RECT rc; ! 649: ! 650: rc = npc->rc; ! 651: DUToWinRect(&rc); ! 652: ! 653: InflateRect(&rc, CHANDLESIZE / 2, CHANDLESIZE / 2); ! 654: ! 655: SetWindowPos(npc->hwndDrag, NULL, rc.left, rc.top, ! 656: rc.right - rc.left, rc.bottom - rc.top, ! 657: SWP_NOACTIVATE | SWP_NOZORDER); ! 658: } ! 659: ! 660: ! 661: ! 662: /************************************************************************ ! 663: * DragWndProc ! 664: * ! 665: * This is the window procedure for the "drag" class. This window ! 666: * is placed behind a control and is what the user grabs to size a ! 667: * window with the mouse. ! 668: * ! 669: ************************************************************************/ ! 670: ! 671: WINDOWPROC DragWndProc( ! 672: HWND hwnd, ! 673: UINT msg, ! 674: WPARAM wParam, ! 675: LPARAM lParam) ! 676: { ! 677: POINT pt; ! 678: ! 679: switch (msg) { ! 680: case WM_PAINT: ! 681: { ! 682: PAINTSTRUCT ps; ! 683: HDC hDC; ! 684: ! 685: PaintUnderDrag(hwnd); ! 686: ! 687: hDC = BeginPaint(hwnd, &ps); ! 688: DrawHandles(hwnd, hDC, ! 689: (gnpcSel && hwnd == gnpcSel->hwndDrag) ? TRUE : FALSE); ! 690: EndPaint(hwnd, &ps); ! 691: } ! 692: ! 693: break; ! 694: ! 695: case WM_NCHITTEST: ! 696: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); ! 697: ScreenToClient(hwnd, &pt); ! 698: ! 699: if (HandleHitTest(hwnd, pt.x, pt.y) == DRAG_CENTER) ! 700: return HTTRANSPARENT; ! 701: else ! 702: return HTCLIENT; ! 703: ! 704: case WM_SETCURSOR: ! 705: /* ! 706: * Defeat the system changing cursors on us. We do it based ! 707: * on our own hit testing. ! 708: */ ! 709: break; ! 710: ! 711: case WM_LBUTTONDOWN: ! 712: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); ! 713: CtrlButtonDown(hwnd, pt.x, pt.y, TRUE); ! 714: break; ! 715: ! 716: case WM_MOUSEMOVE: ! 717: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); ! 718: CtrlMouseMove(hwnd, TRUE, pt.x, pt.y); ! 719: break; ! 720: ! 721: case WM_LBUTTONUP: ! 722: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); ! 723: CtrlButtonUp(pt.x, pt.y); ! 724: break; ! 725: ! 726: case WM_RBUTTONDOWN: ! 727: case WM_MBUTTONDOWN: ! 728: case WM_LBUTTONDBLCLK: ! 729: case WM_RBUTTONDBLCLK: ! 730: case WM_MBUTTONDBLCLK: ! 731: /* ! 732: * Prevents calling SetFocus when the middle or right ! 733: * mouse buttons are pressed (or doubleclicked). ! 734: */ ! 735: break; ! 736: ! 737: case WM_NCCALCSIZE: ! 738: /* ! 739: * The client area is the entire control. ! 740: */ ! 741: break; ! 742: ! 743: case WM_DESTROY: ! 744: /* ! 745: * When destroying the drag window, we must be sure and ! 746: * remove the properties associated with it. ! 747: */ ! 748: UNSETPCINTOHWND(hwnd); ! 749: break; ! 750: ! 751: default: ! 752: return DefWindowProc(hwnd, msg, wParam, lParam); ! 753: } ! 754: ! 755: return 0L; ! 756: } ! 757: ! 758: ! 759: ! 760: /************************************************************************ ! 761: * DrawHandles ! 762: * ! 763: * This routine draws the drag handles for a drag window. The handles ! 764: * will be solid (filled) if fCurrentSelection is TRUE, or hollow if it ! 765: * is FALSE. ! 766: * ! 767: * Arguments: ! 768: * HWND hwnd - Drag window handle. ! 769: * HDC hDC - DC to use to draw in this window. ! 770: * BOOL fCurrentSelection - TRUE if this control is the "current" ! 771: * selection. ! 772: * ! 773: ************************************************************************/ ! 774: ! 775: VOID DrawHandles( ! 776: HWND hwnd, ! 777: HDC hDC, ! 778: BOOL fCurrentSelection) ! 779: { ! 780: RECT rc; ! 781: INT xMid; ! 782: INT yMid; ! 783: INT x2; ! 784: INT y2; ! 785: HBITMAP hbmOld; ! 786: ! 787: GetWindowRect(hwnd, &rc); ! 788: OffsetRect(&rc, -rc.left, -rc.top); ! 789: ! 790: /* ! 791: * Precalculate some points. ! 792: */ ! 793: xMid = ((rc.right + 1) / 2) - (CHANDLESIZE / 2); ! 794: yMid = ((rc.bottom + 1) / 2) - (CHANDLESIZE / 2); ! 795: x2 = rc.right - CHANDLESIZE; ! 796: y2 = rc.bottom - CHANDLESIZE; ! 797: ! 798: /* ! 799: * Draw a solid box if this is the currently selected ! 800: * control, otherwise draw a hollow box. ! 801: */ ! 802: if (fCurrentSelection) ! 803: hbmOld = SelectObject(ghDCMem, ghbmDragHandle); ! 804: else ! 805: hbmOld = SelectObject(ghDCMem, ghbmDragHandle2); ! 806: ! 807: BitBlt(hDC, 0, 0, CHANDLESIZE, CHANDLESIZE, ! 808: ghDCMem, 0, 0, SRCCOPY); ! 809: BitBlt(hDC, xMid, 0, CHANDLESIZE, CHANDLESIZE, ! 810: ghDCMem, 0, 0, SRCCOPY); ! 811: BitBlt(hDC, x2, 0, CHANDLESIZE, CHANDLESIZE, ! 812: ghDCMem, 0, 0, SRCCOPY); ! 813: BitBlt(hDC, x2, yMid, CHANDLESIZE, CHANDLESIZE, ! 814: ghDCMem, 0, 0, SRCCOPY); ! 815: BitBlt(hDC, x2, y2, CHANDLESIZE, CHANDLESIZE, ! 816: ghDCMem, 0, 0, SRCCOPY); ! 817: BitBlt(hDC, xMid, y2, CHANDLESIZE, CHANDLESIZE, ! 818: ghDCMem, 0, 0, SRCCOPY); ! 819: BitBlt(hDC, 0, y2, CHANDLESIZE, CHANDLESIZE, ! 820: ghDCMem, 0, 0, SRCCOPY); ! 821: BitBlt(hDC, 0, yMid, CHANDLESIZE, CHANDLESIZE, ! 822: ghDCMem, 0, 0, SRCCOPY); ! 823: ! 824: SelectObject(ghDCMem, hbmOld); ! 825: } ! 826: ! 827: ! 828: ! 829: /************************************************************************ ! 830: * PaintUnderDrag ! 831: * ! 832: * This function is used during a paint operation for a visible drag ! 833: * window. It checks underneath the area to update and forces any ! 834: * windows down there to paint themselves, in such an order as to cause ! 835: * the last one painted to be the visually top window. This is necessary ! 836: * to implement the drag windows as "transparent" windows. After this ! 837: * routine finishes, the caller can paint the "handles". The caller must ! 838: * call this routine before calling BeginPaint, or the update area ! 839: * will be null and nothing will get painted. ! 840: * ! 841: * It is assumed that the drag windows are for controls, not for the ! 842: * dialog. ! 843: * ! 844: * Arguments: ! 845: * HWND hwndDrag - Drag window to paint under. ! 846: * ! 847: ************************************************************************/ ! 848: ! 849: STATICFN VOID PaintUnderDrag( ! 850: HWND hwndDrag) ! 851: { ! 852: RECT rc; ! 853: RECT rcInt; ! 854: RECT rcUpdate; ! 855: HWND hwnd; ! 856: HWND hwndControl; ! 857: NPCTYPE npc; ! 858: ! 859: /* ! 860: * Get our corresponding control window. ! 861: */ ! 862: hwndControl = (PCFROMHWND(hwndDrag))->hwnd; ! 863: ! 864: /* ! 865: * Get the update rectangle and convert to screen coords. ! 866: */ ! 867: GetUpdateRect(hwndDrag, &rcUpdate, TRUE); ! 868: ClientToScreenRect(hwndDrag, &rcUpdate); ! 869: ! 870: /* ! 871: * Start enumerating windows. ! 872: */ ! 873: hwnd = hwndDrag; ! 874: while (hwnd = GetWindow(hwnd, GW_HWNDNEXT)) { ! 875: /* ! 876: * Skip invisible drag windows. ! 877: */ ! 878: if (IsWindowVisible(hwnd)) { ! 879: /* ! 880: * Does the window rectangle intersect the update rectangle? ! 881: */ ! 882: GetWindowRect(hwnd, &rc); ! 883: if (IntersectRect(&rcInt, &rc, &rcUpdate)) { ! 884: npc = PCFROMHWND(hwnd); ! 885: ! 886: if (npc->hwndDrag == hwnd || !npc->fSelected) { ! 887: ScreenToClientRect(hwnd, &rcInt); ! 888: InvalidateRect(hwnd, &rcInt, TRUE); ! 889: UpdateWindow(hwnd); ! 890: } ! 891: ! 892: if (npc->hwndDrag == hwnd) ! 893: break; ! 894: } ! 895: } ! 896: } ! 897: ! 898: /* ! 899: * Finally, paint the control associated with this drag window. ! 900: */ ! 901: InvalidateRect(hwndControl, NULL, TRUE); ! 902: UpdateWindow(hwndControl); ! 903: } ! 904: ! 905: ! 906: ! 907: /************************************************************************ ! 908: * HandleHitTest ! 909: * ! 910: * This routine takes a point from a mouse button press on a drag window ! 911: * and returns which "handle" was hit, if any. ! 912: * ! 913: * The coordinates are given in zero based coordinates of the drag ! 914: * window. ! 915: * ! 916: * Arguments: ! 917: * HWND hwnd - Drag window handle the x,y point is relative to. ! 918: * INT x - Mouse X location (in the drag's client coordinates). ! 919: * INT y - Mouse Y location (in the drag's client coordinates). ! 920: * ! 921: * Returns: ! 922: * One of the DRAG_* constants. If no handle was hit, the ! 923: * return will be DRAG_CENTER. ! 924: * ! 925: ************************************************************************/ ! 926: ! 927: INT HandleHitTest( ! 928: HWND hwnd, ! 929: INT x, ! 930: INT y) ! 931: { ! 932: RECT rc; ! 933: INT xMidStart; ! 934: INT yMidStart; ! 935: ! 936: /* ! 937: * If there are multiple controls selected, or if the control ! 938: * type does not allow sizing, defeat the ability to size ! 939: * with the handles by returning DRAG_CENTER. ! 940: */ ! 941: if (gcSelected > 1 || !(PCFROMHWND(hwnd))->pwcd->fSizeable) ! 942: return DRAG_CENTER; ! 943: ! 944: /* ! 945: * Get the window rectangle and cause it to be zero-origined. ! 946: */ ! 947: GetWindowRect(hwnd, &rc); ! 948: OffsetRect(&rc, -rc.left, -rc.top); ! 949: ! 950: /* ! 951: * Calculate the starting points for the handles ! 952: * that are not on a corner. ! 953: */ ! 954: xMidStart = ((rc.right + 1) / 2) - (CHANDLESIZE / 2); ! 955: yMidStart = ((rc.bottom + 1) / 2) - (CHANDLESIZE / 2); ! 956: ! 957: if (x < CHANDLESIZE) { ! 958: if (y < CHANDLESIZE) ! 959: return DRAG_LEFTTOP; ! 960: else if (y > rc.bottom - CHANDLESIZE) ! 961: return DRAG_LEFTBOTTOM; ! 962: else if (y >= yMidStart && y < yMidStart + CHANDLESIZE) ! 963: return DRAG_LEFT; ! 964: } ! 965: else if (x > rc.right - CHANDLESIZE) { ! 966: if (y < CHANDLESIZE) ! 967: return DRAG_RIGHTTOP; ! 968: else if (y > rc.bottom - CHANDLESIZE) ! 969: return DRAG_RIGHTBOTTOM; ! 970: else if (y >= yMidStart && y < yMidStart + CHANDLESIZE) ! 971: return DRAG_RIGHT; ! 972: } ! 973: else if (x >= xMidStart && x < xMidStart + CHANDLESIZE) { ! 974: if (y < CHANDLESIZE) ! 975: return DRAG_TOP; ! 976: else if (y > rc.bottom - CHANDLESIZE) ! 977: return DRAG_BOTTOM; ! 978: } ! 979: ! 980: return DRAG_CENTER; ! 981: } ! 982: ! 983: ! 984: ! 985: /************************************************************************ ! 986: * MouseToDragRect ! 987: * ! 988: * This routine takes the mouse pointer coordinates from a mouse message ! 989: * and produces a rectangle that contains the coordinates that the drag ! 990: * rectangle should be displayed as. This is for tracking (moving/sizing) ! 991: * operations. It relies on a number of globals to have been set up ! 992: * prior to the call with such things as the current control, type of ! 993: * drag, old location of the control, offset of the original mouse press ! 994: * from the origin of the control, etc. ! 995: * ! 996: * The returned rectangle is pegged to the boundaries of the dialog (if it ! 997: * is for a control), and is aligned to the current grid units. It is in ! 998: * dialog units relative to the appropriate point based on whether it is ! 999: * for a control or the dialog, and must be converted to window points ! 1000: * before the actual drag rectangle can be drawn on the screen. ! 1001: * ! 1002: * Arguments: ! 1003: * INT x - Mouse X location (in window coordinates). ! 1004: * INT y - Mouse Y location (in window coordinates). ! 1005: * PRECT prc - Rectangle to return the appropriate drag rectangle ! 1006: * in. ! 1007: * ! 1008: ************************************************************************/ ! 1009: ! 1010: STATICFN VOID MouseToDragRect( ! 1011: INT x, ! 1012: INT y, ! 1013: PRECT prc) ! 1014: { ! 1015: POINT pt; ! 1016: INT fGridFlags; ! 1017: ! 1018: pt.x = x; ! 1019: pt.y = y; ! 1020: MouseToDU(&pt); ! 1021: ! 1022: switch (gHandleHit) { ! 1023: case DRAG_LEFTBOTTOM: ! 1024: SetRect(prc, pt.x, grcSelected.top, grcSelected.right, ! 1025: (grcSelected.bottom - grcSelected.top) + pt.y); ! 1026: fGridFlags = GRIDIZE_LEFT | GRIDIZE_BOTTOM; ! 1027: break; ! 1028: ! 1029: case DRAG_BOTTOM: ! 1030: SetRect(prc, grcSelected.left, grcSelected.top, ! 1031: grcSelected.right, ! 1032: (grcSelected.bottom - grcSelected.top) + pt.y); ! 1033: fGridFlags = GRIDIZE_BOTTOM; ! 1034: break; ! 1035: ! 1036: case DRAG_RIGHTBOTTOM: ! 1037: SetRect(prc, grcSelected.left, grcSelected.top, ! 1038: (grcSelected.right - grcSelected.left) + pt.x, ! 1039: (grcSelected.bottom - grcSelected.top) + pt.y); ! 1040: fGridFlags = GRIDIZE_BOTTOM | GRIDIZE_RIGHT; ! 1041: break; ! 1042: ! 1043: case DRAG_RIGHT: ! 1044: SetRect(prc, grcSelected.left, grcSelected.top, ! 1045: (grcSelected.right - grcSelected.left) + pt.x, ! 1046: grcSelected.bottom); ! 1047: fGridFlags = GRIDIZE_RIGHT; ! 1048: break; ! 1049: ! 1050: case DRAG_RIGHTTOP: ! 1051: SetRect(prc, grcSelected.left, pt.y, ! 1052: (grcSelected.right - grcSelected.left) + pt.x, ! 1053: grcSelected.bottom); ! 1054: fGridFlags = GRIDIZE_RIGHT | GRIDIZE_TOP; ! 1055: break; ! 1056: ! 1057: case DRAG_TOP: ! 1058: SetRect(prc, grcSelected.left, pt.y, ! 1059: grcSelected.right, grcSelected.bottom); ! 1060: fGridFlags = GRIDIZE_TOP; ! 1061: break; ! 1062: ! 1063: case DRAG_LEFTTOP: ! 1064: SetRect(prc, pt.x, pt.y, grcSelected.right, grcSelected.bottom); ! 1065: fGridFlags = GRIDIZE_LEFT | GRIDIZE_TOP; ! 1066: break; ! 1067: ! 1068: case DRAG_LEFT: ! 1069: SetRect(prc, pt.x, grcSelected.top, ! 1070: grcSelected.right, grcSelected.bottom); ! 1071: fGridFlags = GRIDIZE_LEFT; ! 1072: break; ! 1073: ! 1074: case DRAG_CENTER: ! 1075: SetRect(prc, pt.x, pt.y, ! 1076: (grcSelected.right - grcSelected.left) + pt.x, ! 1077: (grcSelected.bottom - grcSelected.top) + pt.y); ! 1078: fGridFlags = GRIDIZE_LEFT | GRIDIZE_TOP | GRIDIZE_SAMESIZE; ! 1079: break; ! 1080: } ! 1081: ! 1082: GridizeRect(prc, fGridFlags); ! 1083: FitRectToBounds(prc, gnOverHang, gHandleHit, gfDlgSelected); ! 1084: } ! 1085: ! 1086: ! 1087: ! 1088: /************************************************************************ ! 1089: * MouseToDU ! 1090: * ! 1091: * This routine converts the point at ppt from window coordinates ! 1092: * for the current control into the closest Dialog Unit point. ! 1093: * ! 1094: * This routine normally assumes that the point is relative to the selected ! 1095: * control, and will convert the point into DU's. The current control ! 1096: * can either be the current dialog or one of its child controls. ! 1097: * The DU's returned will be appropriate for the type of control. If ! 1098: * it is the dialog, they will be relative to the apps client area. ! 1099: * If it is a control, they will be relative to the "client" area of ! 1100: * the current dialog. ! 1101: * ! 1102: * If there is no current selection (such as when dropping a new control), ! 1103: * the point is assumed to already be relative to the dialog and it will ! 1104: * will not be mapped. This only applies when called with a point for a ! 1105: * control, not a dialog. ! 1106: * ! 1107: * Arguments: ! 1108: * PPOINT ppt - Point to convert. ! 1109: * ! 1110: ************************************************************************/ ! 1111: ! 1112: STATICFN VOID MouseToDU( ! 1113: PPOINT ppt) ! 1114: { ! 1115: if (gfDlgSelected) { ! 1116: /* ! 1117: * Map the points from the dialog to the app client. ! 1118: */ ! 1119: ClientToScreen(gcd.npc->hwnd, ppt); ! 1120: ScreenToClient(ghwndSubClient, ppt); ! 1121: ! 1122: /* ! 1123: * Subtract the cursor offset. ! 1124: */ ! 1125: ppt->x -= gptCursorOffset.x; ! 1126: ppt->y -= gptCursorOffset.y; ! 1127: } ! 1128: else { ! 1129: /* ! 1130: * Map the points from the control to the dialog window, ! 1131: * but only if there is a current selection. There will ! 1132: * not be a current selection when dropping a new control, ! 1133: * and the point must already be relative to the dialog! ! 1134: */ ! 1135: if (gnpcSel) { ! 1136: ClientToScreen(gnpcSel->hwnd, ppt); ! 1137: ScreenToClient(gcd.npc->hwnd, ppt); ! 1138: } ! 1139: ! 1140: /* ! 1141: * Subtract the cursor offset, then the dialogs frame ! 1142: * controls offset. ! 1143: */ ! 1144: ppt->x -= gptCursorOffset.x; ! 1145: ppt->y -= gptCursorOffset.y; ! 1146: } ! 1147: ! 1148: /* ! 1149: * Convert this position to dialog units. ! 1150: */ ! 1151: WinToDUPoint(ppt); ! 1152: } ! 1153: ! 1154: ! 1155: ! 1156: /************************************************************************ ! 1157: * CtrlButtonDown ! 1158: * ! 1159: * This routine is called by the control and drag window procs when ! 1160: * the left mouse button is pressed. It checks for the Ctrl modifier key ! 1161: * then passes control on to the appropriate routine. ! 1162: * ! 1163: * When hwnd is the dialog itself, fHandleWindow must be TRUE, even ! 1164: * if a handle was not hit, because of the special-case code that must ! 1165: * be done for the dialog (it doesn't have a separate drag window). ! 1166: * ! 1167: * Arguments: ! 1168: * HWND hwnd - Window handle, can be a drag window or control. ! 1169: * INT x - X mouse location (window coordinates). ! 1170: * INT y - Y mouse location (window coordinates). ! 1171: * BOOL fHandleWindow - TRUE if a handle was clicked on, or FALSE if the ! 1172: * body of a control was clicked on. ! 1173: * ! 1174: ************************************************************************/ ! 1175: ! 1176: VOID CtrlButtonDown( ! 1177: HWND hwnd, ! 1178: INT x, ! 1179: INT y, ! 1180: BOOL fHandleWindow) ! 1181: { ! 1182: POINT pt; ! 1183: HWND hwndHit; ! 1184: INT nOverHang; ! 1185: ! 1186: /* ! 1187: * Discard all mouse messages during certain operations. ! 1188: */ ! 1189: if (gfDisabled) ! 1190: return; ! 1191: ! 1192: /* ! 1193: * Also, be sure any outstanding changes get applied ! 1194: * without errors. ! 1195: */ ! 1196: if (!StatusApplyChanges()) ! 1197: return; ! 1198: ! 1199: if (gCurTool != W_NOTHING) { ! 1200: nOverHang = GetOverHang(gpwcdCurTool->iType, gpwcdCurTool->cyDefault); ! 1201: DragNewBegin(gpwcdCurTool->cxDefault, ! 1202: gpwcdCurTool->cyDefault, nOverHang); ! 1203: } ! 1204: else { ! 1205: /* ! 1206: * If the Control key is down, duplicate the control, unless ! 1207: * it is the dialog (mouse duplicate of the dialog is not ! 1208: * supported). ! 1209: */ ! 1210: if ((GetKeyState(VK_CONTROL) & 0x8000) && ! 1211: (PCFROMHWND(hwnd))->pwcd->iType != W_DIALOG) { ! 1212: /* ! 1213: * First, figure out which control was hit and select it. ! 1214: */ ! 1215: if (fHandleWindow) { ! 1216: hwndHit = hwnd; ! 1217: } ! 1218: else { ! 1219: pt.x = x; ! 1220: pt.y = y; ! 1221: hwndHit = CtrlHitTest(hwnd, &pt); ! 1222: } ! 1223: ! 1224: SelectControl(PCFROMHWND(hwndHit), TRUE); ! 1225: ! 1226: /* ! 1227: * If there is still a selection, begin dragging a copy ! 1228: * of it. The check here is necessary because the prior ! 1229: * call to SelectControl can unselect the last selected ! 1230: * control if the Shift key was held down. ! 1231: */ ! 1232: if (gcSelected) ! 1233: Duplicate(); ! 1234: } ! 1235: else { ! 1236: /* ! 1237: * Start a drag operation. This can be either moving or sizing ! 1238: * the control, depending on fHandleWindow and where the mouse ! 1239: * click is at. ! 1240: */ ! 1241: DragBegin(hwnd, x, y, fHandleWindow); ! 1242: } ! 1243: } ! 1244: } ! 1245: ! 1246: ! 1247: ! 1248: /************************************************************************ ! 1249: * DragNewBegin ! 1250: * ! 1251: * This routine begins a drag operation when dropping a new control, or ! 1252: * group of controls. It is NOT used when dragging existing controls. ! 1253: * ! 1254: * Arguments: ! 1255: * INT cx - Width of the new control. ! 1256: * INT cy - Height of the new control. ! 1257: * INT nOverHang - How much the control can overhang the dialog bottom. ! 1258: * ! 1259: ************************************************************************/ ! 1260: ! 1261: VOID DragNewBegin( ! 1262: INT cx, ! 1263: INT cy, ! 1264: INT nOverHang) ! 1265: { ! 1266: POINTS mpt; ! 1267: POINT pt; ! 1268: DWORD dwPos; ! 1269: ! 1270: /* ! 1271: * Always be sure the focus is where we want it, not on something ! 1272: * like the status ribbon or "Esc" won't work to cancel the tracking. ! 1273: */ ! 1274: SetFocus(ghwndMain); ! 1275: ! 1276: /* ! 1277: * Cancel any current selection, and set some state globals. ! 1278: */ ! 1279: CancelSelection(TRUE); ! 1280: gHandleHit = DRAG_CENTER; ! 1281: gState = STATE_DRAGGINGNEW; ! 1282: SetCursor(hcurMove); ! 1283: ! 1284: /* ! 1285: * The cursor offset is set to be located in the middle of the ! 1286: * new control. This causes the pointer to be initially located ! 1287: * exactly in the center. ! 1288: */ ! 1289: gptCursorOffset.x = cx; ! 1290: gptCursorOffset.y = cy; ! 1291: DUToWinPoint(&gptCursorOffset); ! 1292: gptCursorOffset.x /= 2; ! 1293: gptCursorOffset.y /= 2; ! 1294: ! 1295: /* ! 1296: * Set a global with the overhang. This is used all during the ! 1297: * drag operation we are starting. ! 1298: */ ! 1299: gnOverHang = nOverHang; ! 1300: ! 1301: /* ! 1302: * Now we make up a dummy rectangle for the new control. We start ! 1303: * it at (0,0) with a size of cx and cy. The point where the mouse ! 1304: * was when the command was done is obtained and mapped to the dialog ! 1305: * (it is assumed that only controls will be done here, not a dialog). ! 1306: * The new control rectangle is then converted to be at this location, ! 1307: * it is gridized, the tracking rectangle is shown and we are off. ! 1308: */ ! 1309: SetRect(&grcSelected, 0, 0, cx, cy); ! 1310: dwPos = GetMessagePos(); ! 1311: mpt = (*((POINTS *)&(dwPos))); ! 1312: ((pt).x = (mpt).x, (pt).y = (mpt).y); ! 1313: ScreenToClient(gcd.npc->hwnd, &pt); ! 1314: MouseToDragRect(pt.x, pt.y, &grcSelected); ! 1315: InitTracking(); ! 1316: DrawTrackRect(&grcSelected, FALSE, TRUE); ! 1317: ! 1318: /* ! 1319: * Display the initial coordinates. ! 1320: */ ! 1321: StatusSetCoords(&grcSelected); ! 1322: ! 1323: /* ! 1324: * The mouse messages will come through the dialog subclass proc for ! 1325: * these kinds of operations. ! 1326: */ ! 1327: SetCapture(gcd.npc->hwnd); ! 1328: } ! 1329: ! 1330: ! 1331: ! 1332: /************************************************************************ ! 1333: * DragBegin ! 1334: * ! 1335: * This routine begins a drag operation for either moving a control or ! 1336: * sizing it. The tracking rectangle is not actually drawn until the ! 1337: * mouse moves by a grid unit, however. ! 1338: * ! 1339: * To begin a drag on the dialog itself, fHandleWindow must be TRUE, even ! 1340: * if a handle was not hit, because of the special-case code that must ! 1341: * be done for the dialog (it doesn't have a separate drag window). ! 1342: * ! 1343: * Arguments: ! 1344: * HWND hwnd - Window handle, can be a drag window or control. ! 1345: * INT x - Starting X mouse location (window coordinates). ! 1346: * INT y - Starting Y mouse location (window coordinates). ! 1347: * BOOL fHandleWindow - TRUE if the drag is happening because a drag ! 1348: * handle was clicked on, or FALSE if the drag is ! 1349: * happening because the body of a control is ! 1350: * clicked on. ! 1351: * ! 1352: * ! 1353: ************************************************************************/ ! 1354: ! 1355: STATICFN VOID DragBegin( ! 1356: HWND hwnd, ! 1357: INT x, ! 1358: INT y, ! 1359: BOOL fHandleWindow) ! 1360: { ! 1361: NPCTYPE npcT; ! 1362: HWND hwndHit; ! 1363: POINT pt; ! 1364: NPCTYPE npc; ! 1365: BOOL fPrevSelect = FALSE; ! 1366: INT nBottom; ! 1367: ! 1368: /* ! 1369: * Always be sure the focus is where we want it, not on something ! 1370: * like the status ribbon or "Esc" won't work to cancel the tracking. ! 1371: */ ! 1372: SetFocus(ghwndMain); ! 1373: ! 1374: pt.x = x; ! 1375: pt.y = y; ! 1376: ! 1377: /* ! 1378: * Is this drag happening because a drag handle was clicked on? ! 1379: */ ! 1380: if (fHandleWindow) { ! 1381: /* ! 1382: * Find out which handle was clicked on. It is assumed that ! 1383: * hwnd is a drag window. ! 1384: */ ! 1385: gHandleHit = HandleHitTest(hwnd, pt.x, pt.y); ! 1386: hwndHit = hwnd; ! 1387: } ! 1388: else { ! 1389: /* ! 1390: * The body of a control was clicked on. Set a global to say ! 1391: * that we are moving a control, then find out which control ! 1392: * was hit (with our own hit testing). ! 1393: */ ! 1394: gHandleHit = DRAG_CENTER; ! 1395: hwndHit = CtrlHitTest(hwnd, &pt); ! 1396: } ! 1397: ! 1398: /* ! 1399: * Find out if the control clicked on was the currently selected ! 1400: * control already. ! 1401: */ ! 1402: npc = PCFROMHWND(hwndHit); ! 1403: if (npc == gnpcSel) ! 1404: fPrevSelect = TRUE; ! 1405: ! 1406: /* ! 1407: * Select the control. This can return FALSE if the control is ! 1408: * unselected (shift key is down and the control is already selected). ! 1409: */ ! 1410: if (!SelectControl(npc, TRUE)) ! 1411: return; ! 1412: ! 1413: /* ! 1414: * If the dialog is selected, we make a rectangle that encloses all ! 1415: * the controls. This will be used to limit the size that the dialog ! 1416: * can be sized to so that it cannot cover any existing controls. ! 1417: */ ! 1418: if (gfDlgSelected) { ! 1419: /* ! 1420: * Seed the rectangle with impossible values. ! 1421: */ ! 1422: SetRect(&grcMinDialog, 32000, 32000, -32000, -32000); ! 1423: ! 1424: /* ! 1425: * Loop through all the controls, expanding the rectangle to ! 1426: * fit around all the controls. ! 1427: */ ! 1428: for (npcT = npcHead; npcT; npcT = npcT->npcNext) { ! 1429: if (npcT->rc.left < grcMinDialog.left) ! 1430: grcMinDialog.left = npcT->rc.left; ! 1431: ! 1432: if (npcT->rc.right > grcMinDialog.right) ! 1433: grcMinDialog.right = npcT->rc.right; ! 1434: ! 1435: if (npcT->rc.top < grcMinDialog.top) ! 1436: grcMinDialog.top = npcT->rc.top; ! 1437: ! 1438: /* ! 1439: * When calculating the bottom boundary of the controls, ! 1440: * make the rectangle shorter by the ovehang amount. This ! 1441: * allows the dialog to be sized up so that it covers ! 1442: * parts of controls with overhang (comboboxes). ! 1443: */ ! 1444: nBottom = npcT->rc.bottom - GetOverHang(npcT->pwcd->iType, ! 1445: npcT->rc.bottom - npcT->rc.top); ! 1446: if (nBottom > grcMinDialog.bottom) ! 1447: grcMinDialog.bottom = nBottom; ! 1448: } ! 1449: ! 1450: OffsetRect(&grcMinDialog, gcd.npc->rc.left, gcd.npc->rc.top); ! 1451: } ! 1452: ! 1453: /* ! 1454: * If the control clicked on was already the anchor, go right into ! 1455: * dragging mode. If it was not, go into pre-drag mode, which will ! 1456: * defer the calculation of offsets, etc., until after a certain ! 1457: * small amount of time so the mouse can be "debounced". ! 1458: */ ! 1459: if (fPrevSelect) { ! 1460: DragBegin2(&pt); ! 1461: } ! 1462: else { ! 1463: gState = STATE_PREDRAG; ! 1464: ! 1465: /* ! 1466: * Save the point in a global. If the mouse pointer is moved ! 1467: * too FAR away from this point, we will start the drag operation ! 1468: * even if the pre-drag time has not elapsed yet. ! 1469: */ ! 1470: gptPreDragStart = pt; ! 1471: ! 1472: /* ! 1473: * Start the pre-drag timer. ! 1474: */ ! 1475: SetTimer(hwndHit, TID_PREDRAG, gmsecPreDrag, NULL); ! 1476: } ! 1477: ! 1478: /* ! 1479: * The mouse messages from now on will go to the window clicked on, ! 1480: * either the drag window or the control window. ! 1481: */ ! 1482: SetCapture(hwndHit); ! 1483: } ! 1484: ! 1485: ! 1486: ! 1487: /************************************************************************ ! 1488: * DragBegin2 ! 1489: * ! 1490: * This routine continues the initiation of a drag operation started ! 1491: * by DragBegin. It is separate because it calculates offsets based ! 1492: * on where the mouse pointer is, and these calculations can be deferred ! 1493: * until a later time than when DragBegin was called so that the mouse ! 1494: * can be "debounced". ! 1495: * ! 1496: * Arguments: ! 1497: * POINT ppt - Starting mouse location (window coordinates). ! 1498: * ! 1499: ************************************************************************/ ! 1500: ! 1501: STATICFN VOID DragBegin2( ! 1502: PPOINT ppt) ! 1503: { ! 1504: gState = STATE_DRAGGING; ! 1505: ! 1506: /* ! 1507: * Set the pointer to the "move" pointer if we are moving. ! 1508: * Otherwise, the pointer should already be set to the proper ! 1509: * sizing pointer. ! 1510: */ ! 1511: if (gHandleHit == DRAG_CENTER) ! 1512: SetCursor(hcurMove); ! 1513: ! 1514: /* ! 1515: * Save away the initial offset of the cursor. ! 1516: */ ! 1517: CalcCursorOffset(ppt); ! 1518: ! 1519: /* ! 1520: * Initialize the track rectangle. Note we are calling DrawTrackRect ! 1521: * with FALSE. ! 1522: */ ! 1523: DrawTrackRect(&grcSelected, gfDlgSelected, FALSE); ! 1524: } ! 1525: ! 1526: ! 1527: ! 1528: /**************************************************************************** ! 1529: * CtrlHitTest ! 1530: * ! 1531: * This routine walks the list of controls and determines which one is ! 1532: * "hit" by this point. If a hit is found, the point is also converted ! 1533: * to coordinates for the hit window. ! 1534: * ! 1535: * There is a special case when hitting controls over a groupbox. ! 1536: * Controls within a groupbox will always be hit instead of the ! 1537: * groupbox itself. ! 1538: * ! 1539: * Arguments: ! 1540: * HWND hwnd - Window handle the coordinates are relative to. ! 1541: * PPOINT ppt - Window point where the click occurred (window coords). ! 1542: * ! 1543: * Returns: ! 1544: * The hwnd of the "hit" control will be returned. If no control was hit, ! 1545: * the hwnd that was passed in is returned. ! 1546: * ! 1547: ****************************************************************************/ ! 1548: ! 1549: STATICFN HWND CtrlHitTest( ! 1550: HWND hwnd, ! 1551: PPOINT ppt) ! 1552: { ! 1553: NPCTYPE npc; ! 1554: RECT rc; ! 1555: HWND hwndHit = (HWND)NULL; ! 1556: BOOL fGroupHit = FALSE; ! 1557: ! 1558: for (npc = npcHead; npc; npc = npc->npcNext) { ! 1559: GetWindowRect(npc->hwnd, &rc); ! 1560: ScreenToClientRect(npc->hwnd, &rc); ! 1561: MyMapWindowRect(npc->hwnd, hwnd, &rc); ! 1562: ! 1563: /* ! 1564: * Is this a hit, and was there either no control hit as ! 1565: * yet, or this control is not a groupbox, or the control ! 1566: * that was previously hit was a groupbox also? ! 1567: */ ! 1568: if (PtInRect(&rc, *ppt) && ! 1569: (!hwndHit || npc->pwcd->iType != W_GROUPBOX || fGroupHit)) { ! 1570: hwndHit = npc->hwnd; ! 1571: if (npc->pwcd->iType == W_GROUPBOX) ! 1572: fGroupHit = TRUE; ! 1573: else ! 1574: fGroupHit = FALSE; ! 1575: } ! 1576: } ! 1577: ! 1578: if (hwndHit) { ! 1579: MapWindowPoint(hwnd, hwndHit, ppt); ! 1580: return hwndHit; ! 1581: } ! 1582: else { ! 1583: return hwnd; ! 1584: } ! 1585: } ! 1586: ! 1587: ! 1588: ! 1589: /**************************************************************************** ! 1590: * PreDragTimeout ! 1591: * ! 1592: * This function handles the WM_TIMER message from the control window ! 1593: * proc. It is used so that the dragging of a newly selected control ! 1594: * can be deferred for a small period of time to "debounce" the mouse. ! 1595: * ! 1596: * This function is also called if the mouse is moved too much during the ! 1597: * debounce time, effectively cutting the debounce time short. ! 1598: * ! 1599: * Arguments: ! 1600: * HWND hwnd - Window handle the timer came from. ! 1601: * BOOL fTimedOut - TRUE if the predrag is ending because the timer ! 1602: * expired. FALSE if the predrag is ending because ! 1603: * the mouse was moved too FAR. ! 1604: * ! 1605: ****************************************************************************/ ! 1606: ! 1607: VOID PreDragTimeout( ! 1608: HWND hwnd, ! 1609: BOOL fTimedOut) ! 1610: { ! 1611: POINT pt; ! 1612: ! 1613: /* ! 1614: * The debounce time is over and the mouse button is still ! 1615: * down. Get the current mouse pointer location and go into ! 1616: * drag mode. ! 1617: */ ! 1618: if (gState == STATE_PREDRAG) { ! 1619: /* ! 1620: * If we timed out (the mouse was not moved a large distance), ! 1621: * eat any small movement that may have been done during the ! 1622: * predrag time by setting the mouse cursor back to the location ! 1623: * that it started at. Note that we do not do this if the mouse ! 1624: * was moved a large distance, because the efect would be ! 1625: * noticeable for that case, and we want the control to be ! 1626: * moved then anyways. ! 1627: */ ! 1628: if (fTimedOut) { ! 1629: pt = gptPreDragStart; ! 1630: ClientToScreen(hwnd, &pt); ! 1631: SetCursorPos(pt.x, pt.y); ! 1632: } ! 1633: ! 1634: DragBegin2(&gptPreDragStart); ! 1635: } ! 1636: ! 1637: KillTimer(hwnd, TID_PREDRAG); ! 1638: } ! 1639: ! 1640: ! 1641: ! 1642: /************************************************************************ ! 1643: * CtrlMouseMove ! 1644: * ! 1645: * This routine handles the mouse move messages for controls, the dialog, ! 1646: * drag windows and when dropping a new control. ! 1647: * ! 1648: * During a drag operation, the tracking rectangle will be adjusted. ! 1649: * If there is not a drag operation is effect, the mouse cursor will ! 1650: * be changed to a sizing pointer if it is over a drag handle. If not ! 1651: * over a drag handle, the pointer will be changed back to the arrow. ! 1652: * ! 1653: * Arguments: ! 1654: * HWND hwnd - Window handle the x and y are relative to. ! 1655: * BOOL fDragWindow - TRUE if hwnd is a drag window. ! 1656: * INT x - X location of the mouse movement (window coords). ! 1657: * INT y - Y location of the mouse movement (window coords). ! 1658: * ! 1659: ************************************************************************/ ! 1660: ! 1661: VOID CtrlMouseMove( ! 1662: HWND hwnd, ! 1663: BOOL fDragWindow, ! 1664: INT x, ! 1665: INT y) ! 1666: { ! 1667: RECT rc; ! 1668: RECT rc2; ! 1669: HCURSOR hcur = NULL; ! 1670: ! 1671: /* ! 1672: * Discard all mouse messages during certain operations ! 1673: * (but still set the pointer properly). ! 1674: */ ! 1675: if (gfDisabled) { ! 1676: SetCursor(hcurArrow); ! 1677: return; ! 1678: } ! 1679: ! 1680: switch (gState) { ! 1681: case STATE_PREDRAG: ! 1682: /* ! 1683: * If the mouse was moved too FAR, consider the ! 1684: * pre-drag time elapsed and go into drag mode. ! 1685: */ ! 1686: if (abs(gptPreDragStart.x - x) > gcxPreDragMax || ! 1687: abs(gptPreDragStart.y - y) > gcyPreDragMax) ! 1688: PreDragTimeout(hwnd, FALSE); ! 1689: ! 1690: break; ! 1691: ! 1692: case STATE_DRAGGING: ! 1693: case STATE_DRAGGINGNEW: ! 1694: MouseToDragRect(x, y, &rc); ! 1695: ! 1696: if (!EqualRect(&rc, &grcTrackDU)) { ! 1697: /* ! 1698: * If the tracking rectangle is not shown, this means that ! 1699: * this is the first significant mouse move since the start ! 1700: * of a drag operation, and we need to lock the window, get ! 1701: * our clip DC, etc. ! 1702: */ ! 1703: if (!gfTrackRectShown) ! 1704: InitTracking(); ! 1705: ! 1706: DrawTrackRect(&rc, gfDlgSelected, TRUE); ! 1707: ! 1708: if (gcSelected > 1) { ! 1709: /* ! 1710: * Since there are multiple controls selected, ! 1711: * rc will be the rectangle that surrounds them ! 1712: * all. We really want to just show the anchor ! 1713: * controls new position, so we have to do a ! 1714: * little math to calculate and display it. ! 1715: */ ! 1716: rc2 = gnpcSel->rc; ! 1717: OffsetRect(&rc2, rc.left - grcSelected.left, ! 1718: rc.top - grcSelected.top); ! 1719: StatusSetCoords(&rc2); ! 1720: } ! 1721: else { ! 1722: /* ! 1723: * Either a single control is being dragged or ! 1724: * a new control is being dropped. ! 1725: */ ! 1726: StatusSetCoords(&rc); ! 1727: } ! 1728: } ! 1729: ! 1730: break; ! 1731: ! 1732: case STATE_SELECTING: ! 1733: OutlineSelectDraw(x, y); ! 1734: break; ! 1735: ! 1736: default: ! 1737: /* ! 1738: * Is there a tool selected? ! 1739: */ ! 1740: if (gCurTool != W_NOTHING) { ! 1741: hcur = hcurDropTool; ! 1742: } ! 1743: else { ! 1744: /* ! 1745: * If hwnd is a drag window, see if the pointer is over ! 1746: * over any of the handles and change it to one of the ! 1747: * sizing pointers if necessary. Otherwise set the pointer ! 1748: * to the default arrow pointer. ! 1749: */ ! 1750: if (fDragWindow) { ! 1751: switch (HandleHitTest(hwnd, x, y)) { ! 1752: case DRAG_LEFTBOTTOM: ! 1753: case DRAG_RIGHTTOP: ! 1754: hcur = hcurSizeNESW; ! 1755: break; ! 1756: ! 1757: case DRAG_LEFTTOP: ! 1758: case DRAG_RIGHTBOTTOM: ! 1759: hcur = hcurSizeNWSE; ! 1760: break; ! 1761: ! 1762: case DRAG_BOTTOM: ! 1763: case DRAG_TOP: ! 1764: hcur = hcurSizeNS; ! 1765: break; ! 1766: ! 1767: case DRAG_RIGHT: ! 1768: case DRAG_LEFT: ! 1769: hcur = hcurSizeWE; ! 1770: break; ! 1771: ! 1772: case DRAG_CENTER: ! 1773: default: ! 1774: hcur = hcurArrow; ! 1775: break; ! 1776: } ! 1777: } ! 1778: else { ! 1779: hcur = hcurArrow; ! 1780: } ! 1781: } ! 1782: ! 1783: break; ! 1784: } ! 1785: ! 1786: if (hcur) ! 1787: SetCursor(hcur); ! 1788: } ! 1789: ! 1790: ! 1791: ! 1792: /************************************************************************ ! 1793: * DragCancel ! 1794: * ! 1795: * This function cancels any drag operation in effect. It handles ! 1796: * such things as erasing any visible tracking rectangle, freeing any ! 1797: * "copy" data, setting globals and updating the status display. It ! 1798: * can be used no matter how the drag operation was started. ! 1799: * ! 1800: ************************************************************************/ ! 1801: ! 1802: VOID DragCancel(VOID) ! 1803: { ! 1804: HWND hwnd; ! 1805: ! 1806: switch (gState) { ! 1807: case STATE_PREDRAG: ! 1808: /* ! 1809: * Stop the timer. Note that this assumes the timer ! 1810: * was attached to the capture window. This should ! 1811: * be safe (see the associated SetTimer). ! 1812: */ ! 1813: if (hwnd = GetCapture()) ! 1814: KillTimer(hwnd, TID_PREDRAG); ! 1815: ! 1816: break; ! 1817: ! 1818: case STATE_DRAGGING: ! 1819: case STATE_DRAGGINGNEW: ! 1820: CancelTracking(); ! 1821: ! 1822: if (gpResCopy) { ! 1823: MyFree(gpResCopy); ! 1824: gpResCopy = NULL; ! 1825: } ! 1826: ! 1827: break; ! 1828: } ! 1829: ! 1830: gState = STATE_NORMAL; ! 1831: ReleaseCapture(); ! 1832: SetCursor(hcurArrow); ! 1833: ! 1834: StatusUpdate(); ! 1835: StatusSetEnable(); ! 1836: } ! 1837: ! 1838: ! 1839: ! 1840: /************************************************************************ ! 1841: * CtrlButtonUp ! 1842: * ! 1843: * This function is called when the left mouse button is released. Depending ! 1844: * on the mode, it will complete the operation started when the button ! 1845: * was pressed down. ! 1846: * ! 1847: * Arguments: ! 1848: * INT x - Mouse X location (in window coords). ! 1849: * INT y - Mouse Y location (in window coords). ! 1850: * ! 1851: ************************************************************************/ ! 1852: ! 1853: VOID CtrlButtonUp( ! 1854: INT x, ! 1855: INT y) ! 1856: { ! 1857: /* ! 1858: * Discard all mouse messages during certain operations. ! 1859: */ ! 1860: if (gfDisabled) ! 1861: return; ! 1862: ! 1863: switch (gState) { ! 1864: case STATE_PREDRAG: ! 1865: /* ! 1866: * They released the mouse button during the debounce time, ! 1867: * so cancel the drag. ! 1868: */ ! 1869: DragCancel(); ! 1870: break; ! 1871: ! 1872: case STATE_DRAGGING: ! 1873: case STATE_DRAGGINGNEW: ! 1874: DragEnd(x, y); ! 1875: break; ! 1876: ! 1877: case STATE_SELECTING: ! 1878: OutlineSelectEnd(x, y); ! 1879: break; ! 1880: ! 1881: default: ! 1882: break; ! 1883: } ! 1884: } ! 1885: ! 1886: ! 1887: ! 1888: /************************************************************************ ! 1889: * DragEnd ! 1890: * ! 1891: * This function completes all kinds of drag operations. If dragging a ! 1892: * new control, it will be dropped at the specified location. If dragging ! 1893: * an existing control, it will be positioned to the given location. ! 1894: * ! 1895: * Arguments: ! 1896: * INT x - X location the control ended up at (in window coords). ! 1897: * INT y - Y location the control ended up at (in window coords). ! 1898: * ! 1899: ************************************************************************/ ! 1900: ! 1901: VOID DragEnd( ! 1902: INT x, ! 1903: INT y) ! 1904: { ! 1905: PDIALOGBOXHEADER pdbh; ! 1906: PCONTROLDATA pcd; ! 1907: INT cControls; ! 1908: RECT rc; ! 1909: INT i; ! 1910: INT cx; ! 1911: INT cy; ! 1912: ! 1913: CancelTracking(); ! 1914: MouseToDragRect(x, y, &rc); ! 1915: ! 1916: if (gState == STATE_DRAGGING) { ! 1917: PositionControl(&rc); ! 1918: } ! 1919: else { ! 1920: if (gpResCopy) { ! 1921: pdbh = (PDIALOGBOXHEADER)SkipResHeader(gpResCopy); ! 1922: cControls = (INT)pdbh->NumberOfItems; ! 1923: pcd = SkipDialogBoxHeader(pdbh); ! 1924: cx = rc.left - grcCopy.left; ! 1925: cy = rc.top - grcCopy.top; ! 1926: ! 1927: /* ! 1928: * Loop through all the controls, adjusting their position ! 1929: * according to where the drag rectangle ended up. ! 1930: */ ! 1931: for (i = 0; i < cControls; i++) { ! 1932: /* ! 1933: * Add cx and cy to the resource's x and y fields. ! 1934: */ ! 1935: pcd->x += (WORD)cx; ! 1936: pcd->y += (WORD)cy; ! 1937: ! 1938: pcd = SkipControlData(pcd); ! 1939: } ! 1940: ! 1941: /* ! 1942: * Now we go and create all the controls, adding them to ! 1943: * the current dialog. It is assumed that the image in ! 1944: * gpResCopy specifies controls to add, and not a dialog ! 1945: * to create! ! 1946: */ ! 1947: if (ResToDialog(gpResCopy, FALSE)) { ! 1948: gfResChged = gfDlgChanged = TRUE; ! 1949: ShowFileStatus(FALSE); ! 1950: } ! 1951: ! 1952: MyFree(gpResCopy); ! 1953: gpResCopy = NULL; ! 1954: ! 1955: StatusUpdate(); ! 1956: StatusSetEnable(); ! 1957: } ! 1958: else { ! 1959: /* ! 1960: * Drop the new control. ! 1961: */ ! 1962: DropControl(gpwcdCurTool, &rc); ! 1963: ! 1964: if (!gfToolLocked) ! 1965: ToolboxSelectTool(W_NOTHING, FALSE); ! 1966: } ! 1967: } ! 1968: ! 1969: gState = STATE_NORMAL; ! 1970: ReleaseCapture(); ! 1971: SetCursor(hcurArrow); ! 1972: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.