|
|
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: /**************************************************************************\
13: * track.c -- support for direct manipulation of parallelogram object.
14: \**************************************************************************/
15:
16: #include <windows.h>
17: #include <math.h>
18: #include "track.h"
19:
20: #define EPSILON (float) 0.0001
21: #define RECTSIZE 60
22:
23:
24:
25: /**************************************************************************\
26: *
27: * function: doTrackObject()
28: *
29: * input parameters:
30: * pto - pointer to a track object.
31: * msg - message selecting what action to take. Values may include WM_*'s
32: * (see case statements below for more information.)
33: * hwnd - Window handle for the window the track object exists within.
34: * lParam - Usually fourth param to window proc. varies based on msg.
35: *
36: * global variables: none.
37: *
38: * coordinate spaces: There are three coordinate spaces of interest here,
39: * and this routine is frequently switching between them...
40: *
41: * WORLD DEVICE SCREEN
42: *
43: * object coordinates input mouse pos used w/ SetCursorPos()
44: * (pto->rect) (lParam for WM_*)
45: *
46: * -----> LPtoDP() ----> ----> ClientToScreen() -->
47: * <----- DPtoLP() <---- <---- ScreenToClient() <--
48: *
49: * in addition, the HDC has an offset origin. Device coordinates for the
50: * mouse (lParam) never take this into account, but it is necessary to
51: * translate them in order to get direct manipulation right.
52: *
53: \**************************************************************************/
54: PTrackObject doTrackObject(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
55: {
56: if ((pto == NULL) && (msg != TROB_NEW)) return NULL;
57:
58: switch (msg) {
59:
60:
61: /**********************************************************************\
62: * TROB_NEW
63: *
64: * Allocate new PTrackObject structure. Fill in default values
65: * for the fields of the structure. Set up the HDC correctly.
66: * return - pointer to the new object.
67: \**********************************************************************/
68: case TROB_NEW: {
69: PTrackObject pto;
70:
71: /* with LPTR returned value is a pointer. */
72: pto = (PTrackObject) LocalAlloc (LPTR, sizeof (TrackObject));
73:
74: /* initialize the HDC and other fields. */
75: pto->hdc = GetDC (hwnd);
76: SetGraphicsMode (pto->hdc, GM_ADVANCED);
77: SetROP2(pto->hdc, R2_NOT);
78: SelectObject (pto->hdc, GetStockObject (NULL_BRUSH));
79: pto->Mode = TMNONE;
80: pto->allowedModes = TMMOVE | TMSIZEXY;
81:
82: GetWorldTransform (pto->hdc, &(pto->xfmChange));
83:
84: /* initialize the size. */
85: pto->rect.top = pto->rect.left = 0;
86: pto->rect.bottom = pto->rect.right = RECTSIZE;
87:
88: return (pto);
89: }
90:
91:
92:
93: /**********************************************************************\
94: * TROB_DELETE
95: *
96: * Complement of TROB_NEW. Free up the memory allocated for the object.
97: \**********************************************************************/
98: case TROB_DELETE:
99: doTrackObject (pto, TROB_PAINT, hwnd, lParam);
100: ReleaseDC (hwnd, pto->hdc);
101: LocalFree (LocalHandle ((LPSTR)pto));
102: return NULL;
103:
104:
105:
106: /**********************************************************************\
107: * TROB_PAINT
108: *
109: * Paint the object into its hdc. Called half the time to erase
110: * the object, and half the time to redraw it.
111: \**********************************************************************/
112: case TROB_PAINT: {
113: MoveToEx (pto->hdc, pto->rect.right, pto->rect.top, NULL);
114: LineTo (pto->hdc, pto->rect.left, pto->rect.top);
115: LineTo (pto->hdc, pto->rect.left, pto->rect.bottom);
116:
117: if (pto->allowedModes & TMSIZEXY) {
118: LineTo (pto->hdc, pto->rect.right, pto->rect.bottom);
119: LineTo (pto->hdc, pto->rect.right, pto->rect.top);
120: }
121:
122: if (pto->allowedModes & TMROTATE) {
123: MoveToEx (pto->hdc, pto->rect.left, pto->rect.bottom/ 4, NULL);
124: AngleArc (pto->hdc, pto->rect.left, pto->rect.top,
125: (DWORD) pto->rect.bottom/ 4, (float) 270.0, (float) 90.0);
126: }
127:
128: } return NULL;
129:
130:
131:
132: /**********************************************************************\
133: * TROB_HITTEST
134: *
135: * Check the point sent in in the lParam to see if it lays within
136: * the bounds of the objects defining rectangle.
137: * return - pointer to the object iff the point is in rectangle,
138: * otherwise return NULL.
139: \**********************************************************************/
140: case TROB_HITTEST:{
141: POINT mouWorld;
142: mouWorld.x = LOWORD(lParam);
143: mouWorld.y = HIWORD(lParam);
144:
145: DPtoLP (pto->hdc, &mouWorld, 1);
146:
147: if (PtInRect (&pto->rect, mouWorld)) return pto;
148: else return NULL;
149: }
150:
151:
152:
153: /**********************************************************************\
154: * WM_LBUTTONDOWN & WM_RBUTTONDOWN
155: *
156: * Capture the mouse, set the tracking mode depending on the mouse
157: * location in world coordinates, reset the mouse position.
158: *
159: \**********************************************************************/
160: case WM_LBUTTONDOWN:
161: case WM_RBUTTONDOWN: {
162: POINT newmouScreen;
163: POINT mouWorld;
164:
165: mouWorld.x = LOWORD(lParam);
166: mouWorld.y = HIWORD(lParam);
167: DPtoLP (pto->hdc, &mouWorld, 1);
168:
169: /* upper left hand corner. right button is no-op. */
170: if ((mouWorld.x <= (pto->rect.right / 2)) &&
171: (mouWorld.y <= (pto->rect.bottom / 2))) {
172: if (msg == WM_RBUTTONDOWN) return NULL;
173: pto->Mode = TMMOVE;
174: newmouScreen.x = pto->rect.left;
175: newmouScreen.y = pto->rect.top;
176:
177: /* lower left hand corner */
178: } else if ((mouWorld.x <= (pto->rect.right / 2)) &&
179: (mouWorld.y > (pto->rect.bottom / 2))) {
180:
181: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARY : TMSIZEY;
182: newmouScreen.x = pto->rect.left;
183: newmouScreen.y = pto->rect.bottom;
184:
185: /* upper right hand corner */
186: } else if ((mouWorld.x > (pto->rect.right / 2)) &&
187: (mouWorld.y <= (pto->rect.bottom / 2))) {
188:
189: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARX : TMSIZEX;
190: newmouScreen.x = pto->rect.right;
191: newmouScreen.y = pto->rect.top;
192:
193: /* lower right hand corner */
194: } else if ((mouWorld.x > (pto->rect.right / 2)) &&
195: (mouWorld.y > (pto->rect.bottom / 2))) {
196:
197: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMROTATE : TMSIZEXY;
198: newmouScreen.x = pto->rect.right;
199: newmouScreen.y = pto->rect.bottom;
200: }
201:
202: if (! (pto->Mode & pto->allowedModes)) {
203: pto->Mode = TMNONE;
204: return NULL;
205: }
206:
207: SetCapture(hwnd);
208: LPtoDP (pto->hdc, &newmouScreen, 1);
209: ClientToScreen (hwnd, &newmouScreen);
210: SetCursorPos (newmouScreen.x,newmouScreen.y);
211:
212: GetWorldTransform (pto->hdc, &pto->xfmDown);
213: } return NULL;
214:
215:
216:
217: /**********************************************************************\
218: * WM_MOUSEMOVE
219: *
220: * this is where almost all of the interesting calculation is done.
221: * First clip the mouse location to be in rectClip, then
222: * call MouseMove() to handle the different tracking modes.
223: \**********************************************************************/
224: case WM_MOUSEMOVE: {
225: if ((short) LOWORD(lParam) < (short)pto->rectClip.left)
226: lParam = MAKELONG ((WORD)pto->rectClip.left, HIWORD(lParam));
227:
228: if (LOWORD(lParam) > (WORD)pto->rectClip.right)
229: lParam = MAKELONG ((WORD)pto->rectClip.right, HIWORD(lParam));
230:
231: if ((short) HIWORD(lParam) < (short)pto->rectClip.top)
232: lParam = MAKELONG (LOWORD(lParam), (WORD)pto->rectClip.top);
233:
234: if (HIWORD(lParam) > (WORD)pto->rectClip.bottom)
235: lParam = MAKELONG (LOWORD(lParam),(WORD)pto->rectClip.bottom);
236:
237: MouseMove (pto, msg, hwnd, lParam);
238:
239: } return NULL;
240:
241:
242:
243: /**********************************************************************\
244: * WM_RBUTTONUP & WM_LBUTTONUP
245: *
246: * simply release the mouse capture, and set the mode to TMNONE.
247: \**********************************************************************/
248: case WM_RBUTTONUP:
249: case WM_LBUTTONUP: {
250: if (pto->Mode) {
251: ReleaseCapture();
252: pto->Mode = TMNONE;
253: }
254: } return NULL;
255:
256: } /* end switch(msg) */
257: }
258:
259:
260:
261:
262:
263:
264: /**************************************************************************\
265: * function: MouseMove()
266: *
267: * input parameters:
268: * pto - pointer to a track object.
269: * msg - not used.
270: * hwnd - Window handle for the window the track object exists within.
271: * lParam - Usually fourth param to window proc. varies based on msg.
272: *
273: * The tracking behavior which the user observers when moving the mouse
274: * is based on the current tracking mode of the object. This is usually
275: * determined on the mouse down event (c.f. TM*). First erase the old
276: * object, then figure out the change to the transform matrix, finally
277: * change the world transform matrix and redraw the object.
278: *
279: * Tranform:
280: * ( eM11 eM12 0 )
281: * ( eM21 eM22 0 )
282: * ( eDx eDy 1 )
283: *
284: * xDevice = (xWorld * eM11) + (yWorld * eM21) + eDx
285: * yDevice = (xWorld * eM12) + (yWorld * eM22) + eDy
286: *
287: * In this routine the Device (mouse location) and World (rectangle corner)
288: * points are known. Therefore, the two equations above are solved for
289: * the desired matrix entry value (e.g. eM11, 1M12, ... eDy). The tracking
290: * mode determines which one of these entries may be changed. E.g. scaling
291: * in X modifies eM11 while shearing in X modifies eM12. So rather than
292: * using the world transform to map from world to device points, we are
293: * back-computing the proper contents of the world transform.
294: *
295: \**************************************************************************/
296: VOID MouseMove(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
297: {
298: POINT mouWorld, mouDevice, orgDevice;
299:
300: UNREFERENCED_PARAMETER(msg);
301:
302: doTrackObject(pto, TROB_PAINT, hwnd, lParam);
303: mouDevice.x = mouWorld.x = LOWORD(lParam);
304: mouDevice.y = mouWorld.y = HIWORD(lParam);
305:
306: SetWorldTransform(pto->hdc, &pto->xfmDown);
307: DPtoLP (pto->hdc, &mouWorld, 1);
308:
309: /* offset the mouse device point for the viewport's origin. */
310: GetViewportOrgEx (pto->hdc, &orgDevice);
311: mouDevice.x -= orgDevice.x;
312: mouDevice.y -= orgDevice.y;
313:
314: GetWorldTransform(pto->hdc, &pto->xfmChange);
315:
316: switch (pto->Mode) {
317: /*******************************************************\
318: * ( 1 xShear 0 )
319: * ( 0 1 0 )
320: * ( 0 0 1 )
321: *
322: * xWorld = rect.left == 0;
323: \*******************************************************/
324: case TMSHEARX: {
325: pto->xfmChange.eM12 = (float) mouDevice.y;
326: pto->xfmChange.eM12 -=pto->xfmChange.eDy;
327: pto->xfmChange.eM12 /=(float) pto->rect.right ;
328: SetWorldTransform (pto->hdc, &pto->xfmChange);
329: } break;
330:
331:
332: /*******************************************************\
333: * ( 1 0 0 )
334: * ( yShear 1 0 )
335: * ( 0 0 1 )
336: *
337: * yWorld = rect.top == 0;
338: \*******************************************************/
339: case TMSHEARY: {
340: pto->xfmChange.eM21 = (float) mouDevice.x;
341: pto->xfmChange.eM21 -=pto->xfmChange.eDx;
342: pto->xfmChange.eM21 /=(float) pto->rect.bottom ;
343: SetWorldTransform (pto->hdc, &pto->xfmChange);
344:
345: } break;
346:
347:
348: /*******************************************************\
349: * ( cos(a) -sin(a) 0 )
350: * ( sin(a) cos(a) 0 )
351: * ( 0 0 1 )
352: *
353: * a == rotation angle. Since mouse in in lower right,
354: * we need to shift this back 45 degrees (assuming that
355: * straight down is 0 degrees). Thus we actually compute
356: * cos(a) = cos(b - 45) = cos(b)sin(45) + cos(45)sin(45)
357: * where b is angle from the origin to the mouse (x,y)
358: * cos(45) = sin(45) ~= 0.707107
359: * cos(b) = y/r sin(b) = x/r
360: *
361: \*******************************************************/
362: case TMROTATE: {
363: float r;
364:
365: /* translate back to the origin. */
366: pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
367: SetWorldTransform (pto->hdc, &pto->xfmChange);
368:
369: /* rotate about the origin. */
370: r = (float) sqrt( (double)(mouWorld.x * mouWorld.x) +
371: (double)(mouWorld.y * mouWorld.y));
372:
373: pto->xfmChange.eM11 = (float) mouWorld.y / r;
374: pto->xfmChange.eM11 += (float) mouWorld.x / r;
375: pto->xfmChange.eM11 *= (float) 0.707107;
376: pto->xfmChange.eM22 = pto->xfmChange.eM11;
377:
378: pto->xfmChange.eM12 = (float) mouWorld.y / r;
379: pto->xfmChange.eM12 -= (float) mouWorld.x / r;
380: pto->xfmChange.eM12 *= (float) 0.707107;
381: pto->xfmChange.eM21 = -pto->xfmChange.eM12;
382:
383: pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
384:
385: ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
386:
387: /* translate back to the original offset. */
388: pto->xfmChange.eM11 =
389: pto->xfmChange.eM22 = (float) 1.0;
390: pto->xfmChange.eM12 =
391: pto->xfmChange.eM21 = (float) 0.0;
392:
393: pto->xfmChange.eDx = pto->xfmDown.eDx;
394: pto->xfmChange.eDy = pto->xfmDown.eDy;
395: ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
396: GetWorldTransform (pto->hdc, &pto->xfmChange);
397: } break;
398:
399:
400: /*******************************************************\
401: * ( Size X 0 0 )
402: * ( 0 Size Y 0 )
403: * ( 0 0 1 )
404: *
405: \*******************************************************/
406: case TMSIZEXY: {
407: pto->xfmChange.eM11 = (float) mouDevice.x;
408: pto->xfmChange.eM11 -=pto->xfmChange.eDx;
409: pto->xfmChange.eM11 -=((float) pto->rect.bottom*pto->xfmChange.eM21);
410: pto->xfmChange.eM11 /=(float) pto->rect.right ;
411: if (fabs(pto->xfmChange.eM11) < EPSILON) // HACK. system bug ?
412: pto->xfmChange.eM11 = EPSILON;
413:
414: pto->xfmChange.eM22 = (float) mouDevice.y;
415: pto->xfmChange.eM22 -=pto->xfmChange.eDy;
416: pto->xfmChange.eM22 -=((float) pto->rect.right*pto->xfmChange.eM12);
417: pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
418: if (fabs(pto->xfmChange.eM22) < EPSILON) // HACK. system bug ?
419: pto->xfmChange.eM22 = EPSILON;
420: SetWorldTransform (pto->hdc, &pto->xfmChange);
421: } break;
422:
423:
424: /*******************************************************\
425: * ( Size X 0 0 )
426: * ( 0 1 0 )
427: * ( 0 0 1 )
428: *
429: * yWorld = rect.top == 0;
430: \*******************************************************/
431: case TMSIZEX: {
432: pto->xfmChange.eM11 = (float) mouDevice.x;
433: pto->xfmChange.eM11 -=pto->xfmChange.eDx;
434: pto->xfmChange.eM11 /=(float) pto->rect.right ;
435: if (fabs(pto->xfmChange.eM11) < EPSILON) // HACK. system bug ?
436: pto->xfmChange.eM11 = EPSILON;
437:
438: SetWorldTransform (pto->hdc, &pto->xfmChange);
439: } break;
440:
441:
442: /*******************************************************\
443: * ( 1 0 0 )
444: * ( 0 Size Y 0 )
445: * ( 0 0 1 )
446: *
447: * xWorld = rect.left == 0;
448: \*******************************************************/
449: case TMSIZEY: {
450: pto->xfmChange.eM22 = (float) mouDevice.y;
451: pto->xfmChange.eM22 -=pto->xfmChange.eDy;
452: pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
453: if (fabs(pto->xfmChange.eM22) < EPSILON) // HACK. system bug ?
454: pto->xfmChange.eM22 = EPSILON;
455: SetWorldTransform (pto->hdc, &pto->xfmChange);
456: } break;
457:
458:
459: /*******************************************************\
460: * ( 1 0 0 )
461: * ( 0 1 0 )
462: * ( Move x Move y 1 )
463: *
464: * xWorld = rect.left == 0;
465: * yWorld = rect.top == 0;
466: \*******************************************************/
467: case TMMOVE: {
468: pto->xfmChange.eDx = (float) mouDevice.x ;
469: pto->xfmChange.eDy = (float) mouDevice.y ;
470: SetWorldTransform (pto->hdc, &pto->xfmChange);
471: } break;
472: } /* end switch */
473:
474: doTrackObject(pto, TROB_PAINT, hwnd, lParam);
475:
476: return;
477: }
478:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.