|
|
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.