|
|
1.1 root 1: /**************************************************************************\
2: * wxform.c -- sample program demonstrating the new "World Transform."
3: *
4: * design: There are a few global handles or pointers in this application,
5: * and different routines to operate on them. The obvious case of this
6: * is the three window handles and their associated window procedures.
7: * There is also a unique pointer to a track object and a routine to
8: * operate on it (i.e. doTrackObject). All communication is accomplished
9: * by sending messages between these procedures. Each window procedure,
10: * and the track object procedure, operate on some set of messages which
11: * include some of the standard Windows messages, and also miscellaneous
12: * "WM_USER" messages (c.f. wxform.h).
13: \**************************************************************************/
14:
15: #include <windows.h>
16: #include <string.h>
17: #include <stdio.h>
18: #include <math.h>
19: #include <limits.h>
20: #include "wxform.h"
21:
22:
23:
24: /**************************************************************************\
25: *
26: * function: WinMain()
27: *
28: * input parameters: c.f. generic sample
29: *
30: \**************************************************************************/
31: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
32: LPSTR lpCmdLine, int nCmdShow)
33: {
34: MSG msg;
35: HICON hicon;
36:
37: UNREFERENCED_PARAMETER( lpCmdLine );
38:
39:
40: /* Check for previous instance. If none, then register class. */
41: if (!hPrevInstance) {
42: WNDCLASS wc;
43:
44: wc.style = NULL;
45: wc.lpfnWndProc = (WNDPROC)MainWndProc;
46:
47: wc.cbClsExtra = 0;
48: wc.cbWndExtra = 0;
49: wc.hInstance = hInstance;
50: wc.hIcon = LoadIcon(hInstance, "TransformIcon");
51: wc.hCursor = LoadCursor(NULL, IDC_ARROW);
52: wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
53: wc.lpszMenuName = NULL;
54: wc.lpszClassName = "wxform";
55:
56: if (!RegisterClass(&wc)) return (FALSE);
57: } /* class registered o.k. */
58:
59:
60: /* Create the main window. Return false if CreateWindow() fails */
61: hInst = hInstance;
62:
63: hwndMain = CreateWindow(
64: "wxform",
65: "World Transform Demonstration",
66: WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
67: CW_USEDEFAULT,
68: CW_USEDEFAULT,
69: CW_USEDEFAULT,
70: CW_USEDEFAULT,
71: NULL,
72: NULL,
73: hInstance,
74: NULL);
75:
76: if (!hwndMain) return (FALSE);
77: ShowWindow(hwndMain, nCmdShow);
78: UpdateWindow(hwndMain);
79:
80:
81: /* create a new track object and paint it for the first time. */
82: ptoRect = doTrackObject(NULL, TROB_NEW, hwndMain, 0);
83: doTrackObject(ptoRect, TROB_PAINT, hwndMain, 0);
84:
85:
86: /* load and display dialog for the world transform matrix.
87: * then fill its entry fields. Also, get the HICON from the
88: * main window and fill it into the dialog's class structure
89: * for this application.
90: */
91: hwndTransform = CreateDialog(hInst, "TransformDlg",
92: hwndMain, (DLGPROC)TransformDlgProc);
93: hicon = (HICON) GetClassLong (hwndMain, GCL_HICON);
94: SetClassLong (hwndTransform, GCL_HICON, (LONG)hicon);
95: showTransform = TRUE;
96: SendMessage (hwndTransform, WM_PUTUPFLOATS, 0, (LONG) &ptoRect->xfmChange);
97:
98: /* load and display the dialog for the mouse position.
99: * minimize it initially.
100: */
101: hwndMouse = CreateDialog(hInst, "MouseDlg",
102: hwndMain, (DLGPROC)MouseDlgProc);
103: ShowWindow (hwndMouse, SW_SHOWMINIMIZED);
104: showMouse = FALSE;
105:
106:
107: /* load and display the dialog with the direct manipulation help.
108: * minimize it initially. (Don't need a unique window procedure.)
109: */
110: hwndHelp = CreateDialog(hInst, "helpDlg", hwndMain, NULL);
111: ShowWindow (hwndHelp, SW_SHOWMINIMIZED);
112:
113:
114:
115: /* Loop getting messages and dispatching them. */
116: while (GetMessage(&msg,NULL, NULL, NULL)) {
117: if (!IsDialogMessage (hwndTransform, &msg))
118: if (!IsDialogMessage (hwndMouse, &msg))
119: if (!IsDialogMessage (hwndHelp, &msg)){
120: TranslateMessage(&msg);
121: DispatchMessage(&msg);
122: }
123: }
124: return (msg.wParam);
125: }
126:
127:
128:
129:
130:
131:
132:
133:
134:
135: /**************************************************************************\
136: *
137: * function: MainWndProc()
138: *
139: * input parameters: normal window procedure parameters.
140: *
141: * global variables:
142: * hwndTransform,
143: * hwndMouse - information dialog box window handles.
144: * showTransform,
145: * showMouse - Booleans recording the retore/minimize state of the dialogs.
146: * ptoRect - pointer to track object in middle of screen.
147: *
148: \**************************************************************************/
149: LRESULT MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
150: {
151: static HANDLE hPenGrid;
152:
153: switch (message) {
154:
155: /**********************************************************************\
156: * WM_CREATE
157: *
158: * create a pen for later use.
159: \**********************************************************************/
160: case WM_CREATE:
161: hPenGrid = CreatePen (PS_SOLID, 1, GRIDCOLOR);
162: break;
163:
164:
165:
166: /**********************************************************************\
167: * WM_DESTROY
168: *
169: * Complement of WM_CREATE. send the track object the delete messages,
170: * then call PostQuitMessage.
171: \**********************************************************************/
172: case WM_DESTROY:
173: DeleteObject(hPenGrid);
174: doTrackObject(ptoRect, TROB_DELETE, hwnd, lParam);
175: PostQuitMessage(0);
176: break;
177:
178:
179:
180: /**********************************************************************\
181: * WM_SIZE
182: *
183: * Invalidate the whole window because we reset the origin on paint
184: * messages according to the size. Also, send the track object a
185: * message so that it will also change its HDC's viewport origin.
186: \**********************************************************************/
187: case WM_SIZE:
188: InvalidateRect (hwnd, NULL, TRUE);
189: doTrackObject (ptoRect, TROB_CENTER, hwnd, lParam);
190: break;
191:
192:
193:
194: /**********************************************************************\
195: * WM_PAINT
196: *
197: * First invalidate the whole window (forces the object to be painted
198: * fresh, and thus it won't XOR its old self out). Then draw the
199: * grid and finally draw the object.
200: \**********************************************************************/
201: case WM_PAINT : {
202: PAINTSTRUCT ps;
203: HDC hdc;
204: RECT rect;
205: POINT point;
206: int i;
207:
208: InvalidateRect (hwnd, NULL, TRUE);
209:
210: hdc = BeginPaint(hwnd, &ps);
211:
212: CenterOrigin (hwnd, hdc);
213: GetClientRect (hwnd, &rect);
214: GetViewportOrgEx(hdc, &point);
215: OffsetRect(&rect, -point.x, -point.y );
216:
217:
218: /* Draw vertical lines. Draw three at the origin. */
219: SelectObject(hdc, hPenGrid);
220: for (i = 0; i<= rect.right; i+=TICKSPACE){
221: MoveToEx (hdc, i, rect.top, NULL);
222: LineTo (hdc, i, rect.bottom);
223: MoveToEx (hdc, -i, rect.top, NULL);
224: LineTo (hdc, -i, rect.bottom);
225: }
226: MoveToEx (hdc, -1, rect.top, NULL);
227: LineTo (hdc, -1, rect.bottom);
228: MoveToEx (hdc, 1, rect.top, NULL);
229: LineTo (hdc, 1, rect.bottom);
230:
231:
232: /* Draw horizontal lines. Draw three at the origin. */
233: for (i = 0; i<= rect.bottom; i+=TICKSPACE){
234: MoveToEx (hdc, rect.left, i, NULL);
235: LineTo (hdc, rect.right, i);
236: MoveToEx (hdc, rect.left, -i, NULL);
237: LineTo (hdc, rect.right, -i);
238: }
239: MoveToEx (hdc, rect.left, -1, NULL);
240: LineTo (hdc, rect.right, -1);
241: MoveToEx (hdc, rect.left, 1, NULL);
242: LineTo (hdc, rect.right, 1);
243:
244: doTrackObject(ptoRect, TROB_PAINT, hwnd, lParam);
245:
246: EndPaint (hwnd, &ps);
247: } break;
248:
249:
250:
251: /**********************************************************************\
252: * WM_LBUTTONDOWN & WM_RBUTTONDOWN
253: * On button down messages, hittest on the track object, and if
254: * it returns true, then send these messages to the track object.
255: \**********************************************************************/
256: case WM_RBUTTONDOWN:
257: case WM_LBUTTONDOWN:
258: if (doTrackObject(ptoRect, TROB_HITTEST, hwnd, lParam))
259: doTrackObject(ptoRect, message, hwnd, lParam);
260: break;
261:
262:
263:
264: /**********************************************************************\
265: * WM_LBUTTONUP & WM_RBUTTONDOWN & MW_MOUSEMOVE
266: * If the track object is in a "tracking mode" then send it these messages.
267: * If the transform dialog is not minimized, fill it with numbers.
268: * If the mouse dialog is not minimized, fill it with numbers.
269: \**********************************************************************/
270: case WM_RBUTTONUP:
271: case WM_LBUTTONUP:
272: case WM_MOUSEMOVE:
273: if (ptoRect->Mode) {
274: doTrackObject(ptoRect, message, hwnd, lParam);
275: if (showTransform)
276: SendMessage (hwndTransform, WM_PUTUPFLOATS, 0,
277: (LONG) &ptoRect->xfmChange);
278: }
279:
280: if (showMouse)
281: SendMessage (hwndMouse, WM_PUTUPFLOATS, (DWORD) hwnd, lParam);
282:
283: break;
284:
285:
286: default:
287: return (DefWindowProc(hwnd, message, wParam, lParam));
288: }
289: return (NULL);
290: }
291:
292:
293:
294:
295:
296:
297: /**************************************************************************\
298: *
299: * function: TransformDlgProc()
300: *
301: * input parameters: normal window procedure parameters.
302: *
303: * global variables:
304: * showTransform - TRUE if window is restored, FALSE if minimized.
305: * maintain the value in this routine for other windows' use.
306: * ptoRect - pointer to the track object.
307: * showMouse, hwndMain.
308: *
309: * nonstandard messages:
310: * WM_PUTUPFLOATS - fill the entry fields with the contents of an XFORM.
311: \**************************************************************************/
312: LRESULT TransformDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
313: {
314: XFORM xform;
315: char buffer[MAXCHARS];
316:
317:
318: switch (message) {
319:
320:
321: /**********************************************************************\
322: * WM_INITDIALOG
323: *
324: * Fill the entry fields with sensible original values.
325: \**********************************************************************/
326: case WM_INITDIALOG:
327: SetDlgItemText(hwnd, IDD_13, "0");
328: SetDlgItemText(hwnd, IDD_23, "0");
329: SetDlgItemText(hwnd, IDD_33, "1");
330: return TRUE;
331:
332:
333: /******************************************************************\
334: * WM_PUTUPFLOATS
335: *
336: * lParam - pointer to an XFORM structure.
337: * fill the entry fields with the XFORM values.
338: \******************************************************************/
339: case WM_PUTUPFLOATS: {
340: PXFORM pxform;
341: pxform = (PXFORM) lParam;
342:
343: sprintf (buffer, FORMATFLOAT,pxform->eM11);
344: SetDlgItemText(hwnd, IDD_EM11, buffer);
345: sprintf (buffer, FORMATFLOAT,pxform->eM12);
346: SetDlgItemText(hwnd, IDD_EM12, buffer);
347: sprintf (buffer, FORMATFLOAT,pxform->eDx);
348: SetDlgItemText(hwnd, IDD_EDX, buffer);
349:
350: sprintf (buffer, FORMATFLOAT,pxform->eM21);
351: SetDlgItemText(hwnd, IDD_EM21, buffer);
352: sprintf (buffer, FORMATFLOAT,pxform->eM22);
353: SetDlgItemText(hwnd, IDD_EM22, buffer);
354: sprintf (buffer, FORMATFLOAT,pxform->eDy);
355: SetDlgItemText(hwnd, IDD_EDY, buffer);
356:
357: } return FALSE;
358:
359:
360:
361: /******************************************************************\
362: * WM_SIZE
363: *
364: * toggle the global variable keeping track of the iconized state
365: * of this window.
366: \******************************************************************/
367: case WM_SIZE :
368: if (wParam == SIZEICONIC)
369: showTransform = FALSE;
370: else {
371: showTransform = TRUE;
372: SendMessage (hwnd, WM_PUTUPFLOATS, 0, (LONG) &ptoRect->xfmChange);
373: }
374: return FALSE;
375:
376:
377: case WM_COMMAND:
378: /******************************************************************\
379: * WM_COMMAND, IDD_SETXFORM
380: *
381: * take the values from the entry field, fill them into an XFORM
382: * structure and then send the track object the message to use
383: * these values. Finally, reformat and repaint the entry fields.
384: \******************************************************************/
385: if (LOWORD(wParam) == IDD_SETXFORM) {
386: GetDlgItemText(hwnd, IDD_EM11, buffer, MAXCHARS);
387: xform.eM11 = (float) atof (buffer);
388: GetDlgItemText(hwnd, IDD_EM12, buffer, MAXCHARS);
389: xform.eM12 = (float) atof (buffer);
390: GetDlgItemText(hwnd, IDD_EDX, buffer, MAXCHARS);
391: xform.eDx = (float) atof (buffer);
392:
393: GetDlgItemText(hwnd, IDD_EM21, buffer, MAXCHARS);
394: xform.eM21 = (float) atof (buffer);
395: GetDlgItemText(hwnd, IDD_EM22, buffer, MAXCHARS);
396: xform.eM22 = (float) atof (buffer);
397: GetDlgItemText(hwnd, IDD_EDY, buffer, MAXCHARS);
398: xform.eDy = (float) atof (buffer);
399:
400: // HACK. The WM_SIZE here is used to flush the GDI buffer in order
401: // to eliminate a very strange bug whereby DPtoLP() doesn't work.
402: if (showMouse) SendMessage (hwndMain, WM_SIZE, NULL, NULL);
403:
404:
405: doTrackObject (ptoRect, TROB_SETXFORM, hwnd, (LONG) &xform);
406: SendMessage (hwnd, WM_PUTUPFLOATS, 0, (LONG) &xform);
407:
408:
409: /******************************************************************\
410: * WM_COMMAND, IDD_IDENTITY
411: *
412: * fill a local XFORM structure with the identity matrix. Now
413: * send the track object the message to use these values.
414: * Finally, reformat and repaint the entry fields.
415: \******************************************************************/
416: } else if (LOWORD(wParam) == IDD_IDENTITY) {
417: xform.eM11 =
418: xform.eM22 = (float) 1.0;
419: xform.eDx =
420: xform.eDy =
421: xform.eM12 =
422: xform.eM21 = (float) 0.0;
423:
424: // HACK. The WM_SIZE here is used to flush the GDI buffer in order
425: // to eliminate a very strange bug whereby DPtoLP() doesn't work.
426: if (showMouse) SendMessage (hwndMain, WM_SIZE, NULL, NULL);
427:
428: doTrackObject (ptoRect, TROB_SETXFORM, hwnd, (LONG) &xform);
429: SendMessage (hwnd, WM_PUTUPFLOATS, 0, (LONG) &xform);
430: } /* end WM_COMMAND */
431: return FALSE;
432:
433:
434: } /* end switch */
435: return FALSE;
436: }
437:
438:
439:
440:
441:
442: /**************************************************************************\
443: *
444: * function: MouseDlgProc()
445: *
446: * input parameters: normal window procedure parameters.
447: *
448: * global variables:
449: * showMouse -- TRUE if window is restored, FALSE if minimized.
450: * maintain the value in this routine for other windows' use.
451: * ptoRect - pointer to the track object. Needed for DPtoLP()
452: *
453: * nonstandard messages:
454: * WM_PUTUPFLOATS - fill the entry fields with the mouse position.
455: *
456: \**************************************************************************/
457: LRESULT MouseDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
458: {
459: char buffer[MAXCHARS];
460:
461: switch (message) {
462:
463: /******************************************************************\
464: * WM_PUTUPFLOATS
465: *
466: * wParam - contains the hwnd for the main window.
467: * lParam - contains the mouse position in device coordinates.
468: * (c.f. WM_MOUSEMOVE)
469: \******************************************************************/
470: case WM_PUTUPFLOATS: {
471: POINT pScreen, pWorld;
472: HWND hwndMain;
473:
474: hwndMain = (HWND) wParam;
475: pScreen.x = pWorld.x = LOWORD(lParam);
476: pScreen.y = pWorld.y = HIWORD(lParam);
477:
478: sprintf (buffer, "%d", pScreen.x);
479: SetDlgItemText(hwnd, IDD_DEVICEX, buffer);
480: sprintf (buffer, "%d", pScreen.y);
481: SetDlgItemText(hwnd, IDD_DEVICEY, buffer);
482:
483: ClientToScreen (hwndMain, &pScreen);
484: sprintf (buffer, "%d", pScreen.x);
485: SetDlgItemText(hwnd, IDD_SCREENX, buffer);
486: sprintf (buffer, "%d", pScreen.y);
487: SetDlgItemText(hwnd, IDD_SCREENY, buffer);
488:
489: DPtoLP (ptoRect->hdc, &pWorld, 1);
490: sprintf (buffer, FORMATFLOAT, (float) pWorld.x);
491: SetDlgItemText(hwnd, IDD_WORLDX , buffer);
492: sprintf (buffer, FORMATFLOAT, (float) pWorld.y);
493: SetDlgItemText(hwnd, IDD_WORLDY , buffer);
494:
495: } return FALSE;
496:
497:
498:
499: /******************************************************************\
500: * WM_SIZE
501: *
502: * toggle the global variable keeping track of the iconized state
503: * of this window.
504: \******************************************************************/
505: case WM_SIZE :
506: if (wParam == SIZEICONIC)
507: showMouse = FALSE;
508: else
509: showMouse = TRUE;
510: return FALSE;
511:
512: }
513: return FALSE;
514: }
515:
516:
517:
518:
519:
520:
521: /**************************************************************************\
522: * function: CenterOrigin()
523: *
524: * input parameters:
525: * hwnd - window with client we want the center of.
526: * hdc - device context which we set the Viewport origin of.
527: *
528: \**************************************************************************/
529: VOID CenterOrigin (HWND hwnd, HDC hdc)
530: {
531: RECT rect;
532: POINT center;
533:
534: GetClientRect (hwnd, &rect);
535: center.x = rect.right / 2;
536: center.y = rect.bottom /2;
537:
538: SetViewportOrgEx (hdc, center.x, center.y, NULL);
539: return;
540: }
541:
542:
543:
544:
545:
546:
547: /**************************************************************************\
548: *
549: * function: doTrackObject()
550: *
551: * input parameters:
552: * pto - pointer to a track object.
553: * msg - message selecting what action to take. Values may include WM_*'s
554: * (see case statements below for more information.)
555: * hwnd - Window handle for the window the track object exists within.
556: * lParam - Usually fourth param to window proc. varies based on msg.
557: *
558: * global variables: none.
559: *
560: * coordinate spaces: There are three coordinate spaces of interest here,
561: * and this routine is frequently switching between them...
562: *
563: * WORLD DEVICE SCREEN
564: *
565: * object coordinates input mouse pos used w/ SetCursorPos()
566: * (pto->rect) (lParam for WM_*)
567: *
568: * -----> LPtoDP() ----> ----> ClientToScreen() -->
569: * <----- DPtoLP() <---- <---- ScreenToClient() <--
570: *
571: * in addition, the HDC has an offset origin. Device coordinates for the
572: * mouse (lParam) never take this into account, but it is necessary to
573: * translate them in order to get direct manipulation right.
574: *
575: \**************************************************************************/
576: PTrackObject doTrackObject(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
577: {
578: if ((pto == NULL) && (msg != TROB_NEW)) return NULL;
579:
580: switch (msg) {
581:
582:
583: /**********************************************************************\
584: * TROB_NEW
585: *
586: * Allocate new PTrackObject structure. Fill in default values
587: * for the fields of the structure. Set up the HDC correctly.
588: * return - pointer to the new object.
589: \**********************************************************************/
590: case TROB_NEW: {
591: PTrackObject pto;
592:
593: /* with LPTR returned value is a pointer. */
594: pto = (PTrackObject) LocalAlloc (LPTR, sizeof (TrackObject));
595:
596: /* initialize the HDC and other fields. */
597: pto->hdc = GetDC(hwnd);
598: SetROP2(pto->hdc, R2_NOT);
599: SelectObject (pto->hdc, GetStockObject (NULL_BRUSH));
600: SelectObject(pto->hdc, CreatePen (PS_SOLID, 2, (COLORREF) 0x01000009));
601: pto->Mode = TMNONE;
602: doTrackObject (pto, TROB_CENTER, hwnd, lParam);
603: GetWorldTransform (pto->hdc, &(pto->xfmChange));
604:
605: /* initialize the size. */
606: pto->rect.top = pto->rect.left = 0;
607: pto->rect.bottom = pto->rect.right = TICKSPACE*5;
608:
609: return (pto);
610: }
611:
612:
613: /**********************************************************************\
614: * TROB_DELETE
615: *
616: * Delete the pen that we created, release the DC,
617: * free up the memory allocated for the object.
618: \**********************************************************************/
619: case TROB_DELETE:
620: DeleteObject (SelectObject (pto->hdc, GetStockObject (BLACK_PEN)));
621: doTrackObject (pto, TROB_PAINT, hwnd, lParam);
622: ReleaseDC (hwnd, pto->hdc);
623: LocalFree (LocalHandle ((LPSTR)pto));
624: return NULL;
625:
626:
627:
628: /**********************************************************************\
629: * TROB_CENTER
630: *
631: * Called in order to reset the view port origin in the track objects
632: * hdc whenever the client window changes size. This hdc is thus kept
633: * synchronized with the hdc that the axes are painted into.
634: \**********************************************************************/
635: case TROB_CENTER: {
636: CenterOrigin (hwnd, pto->hdc);
637: return (pto);
638: }
639:
640:
641:
642: /**********************************************************************\
643: * TROB_PAINT
644: *
645: * Paint the object into its hdc. Called half the time to erase
646: * the object, and half the time to redraw it.
647: \**********************************************************************/
648: case TROB_PAINT: {
649: Rectangle (pto->hdc, pto->rect.left+1, pto->rect.top+1,
650: pto->rect.left+INC, pto->rect.top+INC);
651:
652: Rectangle (pto->hdc, pto->rect.left, pto->rect.top,
653: pto->rect.right, pto->rect.bottom);
654: } return NULL;
655:
656:
657:
658: /**********************************************************************\
659: * TROB_SETXFORM
660: *
661: * lParam - pointer to the new transform.
662: * set the new transform into the HDC, then update xfmChange.
663: \**********************************************************************/
664: case TROB_SETXFORM: {
665: doTrackObject (pto, TROB_PAINT, hwnd, lParam);
666: SetWorldTransform(pto->hdc, (PXFORM) lParam);
667: GetWorldTransform(pto->hdc, &pto->xfmChange);
668: doTrackObject (pto, TROB_PAINT, hwnd, lParam);
669: } return NULL;
670:
671:
672:
673: /**********************************************************************\
674: * TROB_HITTEST
675: *
676: * Check the point sent in in the lParam to see if it lays within
677: * the bounds of the objects defining rectangle.
678: * return - pointer to the object iff the point is in rectangle,
679: * otherwise return NULL.
680: \**********************************************************************/
681: case TROB_HITTEST:{
682: POINT mouWorld;
683: mouWorld.x = LOWORD(lParam);
684: mouWorld.y = HIWORD(lParam);
685:
686: DPtoLP (pto->hdc, &mouWorld, 1);
687:
688: if (PtInRect (&pto->rect, mouWorld)) return pto;
689: else return NULL;
690: }
691:
692:
693:
694: /**********************************************************************\
695: * WM_LBUTTONDOWN & WM_RBUTTONDOWN
696: *
697: * Capture the mouse, set the tracking mode depending on the mouse
698: * location in world coordinates, reset the mouse position.
699: *
700: \**********************************************************************/
701: case WM_LBUTTONDOWN:
702: case WM_RBUTTONDOWN: {
703: POINT newmouScreen;
704: POINT mouWorld;
705:
706: mouWorld.x = LOWORD(lParam);
707: mouWorld.y = HIWORD(lParam);
708: DPtoLP (pto->hdc, &mouWorld, 1);
709:
710: /* upper left hand corner. right button is no-op. */
711: if ((mouWorld.x <= (pto->rect.right / 2)) &&
712: (mouWorld.y <= (pto->rect.bottom / 2))) {
713: if (msg == WM_RBUTTONDOWN) return NULL;
714: pto->Mode = TMMOVE;
715: newmouScreen.x = pto->rect.left;
716: newmouScreen.y = pto->rect.top;
717:
718: /* lower left hand corner */
719: } else if ((mouWorld.x <= (pto->rect.right / 2)) &&
720: (mouWorld.y > (pto->rect.bottom / 2))) {
721:
722: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARY : TMSIZEY;
723: newmouScreen.x = pto->rect.left;
724: newmouScreen.y = pto->rect.bottom;
725:
726: /* upper right hand corner */
727: } else if ((mouWorld.x > (pto->rect.right / 2)) &&
728: (mouWorld.y <= (pto->rect.bottom / 2))) {
729:
730: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARX : TMSIZEX;
731: newmouScreen.x = pto->rect.right;
732: newmouScreen.y = pto->rect.top;
733:
734: /* lower right hand corner */
735: } else if ((mouWorld.x > (pto->rect.right / 2)) &&
736: (mouWorld.y > (pto->rect.bottom / 2))) {
737:
738: pto->Mode = (msg == WM_RBUTTONDOWN) ? TMROTATE : TMSIZEXY;
739: newmouScreen.x = pto->rect.right;
740: newmouScreen.y = pto->rect.bottom;
741: }
742:
743: SetCapture(hwnd);
744: LPtoDP (pto->hdc, &newmouScreen, 1);
745: ClientToScreen (hwnd, &newmouScreen);
746: SetCursorPos (newmouScreen.x,newmouScreen.y);
747:
748: GetWorldTransform (pto->hdc, &pto->xfmDown);
749: } return NULL;
750:
751:
752:
753: /**********************************************************************\
754: * WM_MOUSEMOVE
755: *
756: * this is where almost all of the interesting calculation is done.
757: * First clip the mouse location to be in client rectangle, then
758: * call MouseMove() to handle the different tracking modes.
759: \**********************************************************************/
760: case WM_MOUSEMOVE: {
761: RECT rect;
762: GetClientRect (hwnd, &rect);
763:
764: if ((short) LOWORD(lParam) < (short)rect.left)
765: lParam = MAKELONG ((WORD)rect.left, HIWORD(lParam));
766:
767: if (LOWORD(lParam) > (WORD)rect.right)
768: lParam = MAKELONG ((WORD)rect.right, HIWORD(lParam));
769:
770: if ((short) HIWORD(lParam) < (short)rect.top)
771: lParam = MAKELONG (LOWORD(lParam), (WORD)rect.top);
772:
773: if (HIWORD(lParam) > (WORD)rect.bottom)
774: lParam = MAKELONG (LOWORD(lParam),(WORD)rect.bottom);
775:
776: MouseMove (pto, msg, hwnd, lParam);
777:
778: } return NULL;
779:
780:
781:
782: /**********************************************************************\
783: * WM_RBUTTONUP & WM_LBUTTONUP
784: *
785: * simply release the mouse capture, and set the mode to TMNONE.
786: \**********************************************************************/
787: case WM_RBUTTONUP:
788: case WM_LBUTTONUP: {
789: if (pto->Mode) {
790: ReleaseCapture();
791: pto->Mode = TMNONE;
792: }
793: } return NULL;
794:
795: } /* end switch(msg) */
796: }
797:
798:
799:
800:
801:
802:
803: /**************************************************************************\
804: * function: MouseMove()
805: *
806: * input parameters:
807: * pto - pointer to a track object.
808: * msg - not used.
809: * hwnd - Window handle for the window the track object exists within.
810: * lParam - Usually fourth param to window proc. varies based on msg.
811: *
812: * The tracking behavior which the user observers when moving the mouse
813: * is based on the current tracking mode of the object. This is usually
814: * determined on the mouse down event (c.f. TM*). First erase the old
815: * object, then figure out the change to the transform matrix, finally
816: * change the world transform matrix and redraw the object.
817: *
818: * Tranform:
819: * ( eM11 eM12 0 )
820: * ( eM21 eM22 0 )
821: * ( eDx eDy 1 )
822: *
823: * xDevice = (xWorld * eM11) + (yWorld * eM21) + eDx
824: * yDevice = (xWorld * eM12) + (yWorld * eM22) + eDy
825: *
826: * In this routine the Device (mouse location) and World (rectangle corner)
827: * points are known. Therefore, the two equations above are solved for
828: * the desired matrix entry value (e.g. eM11, 1M12, ... eDy). The tracking
829: * mode determines which one of these entries may be changed. E.g. scaling
830: * in X modifies eM11 while shearing in X modifies eM12. So rather than
831: * using the world transform to map from world to device points, we are
832: * back-computing the proper contents of the world transform.
833: *
834: \**************************************************************************/
835: VOID MouseMove(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
836: {
837: POINT mouWorld, mouDevice, orgDevice;
838:
839: UNREFERENCED_PARAMETER(msg);
840:
841: doTrackObject(pto, TROB_PAINT, hwnd, lParam);
842: mouDevice.x = mouWorld.x = LOWORD(lParam);
843: mouDevice.y = mouWorld.y = HIWORD(lParam);
844:
845: SetWorldTransform(pto->hdc, &pto->xfmDown);
846: DPtoLP (pto->hdc, &mouWorld, 1);
847:
848: /* offset the mouse device point for the viewport's origin. */
849: GetViewportOrgEx (pto->hdc, &orgDevice);
850: mouDevice.x -= orgDevice.x;
851: mouDevice.y -= orgDevice.y;
852:
853: GetWorldTransform(pto->hdc, &pto->xfmChange);
854:
855: switch (pto->Mode) {
856: /*******************************************************\
857: * ( 1 xShear 0 )
858: * ( 0 1 0 )
859: * ( 0 0 1 )
860: *
861: * xWorld = rect.left == 0;
862: \*******************************************************/
863: case TMSHEARX: {
864: pto->xfmChange.eM12 = (float) mouDevice.y;
865: pto->xfmChange.eM12 -=pto->xfmChange.eDy;
866: pto->xfmChange.eM12 /=(float) pto->rect.right ;
867: SetWorldTransform (pto->hdc, &pto->xfmChange);
868: } break;
869:
870:
871: /*******************************************************\
872: * ( 1 0 0 )
873: * ( yShear 1 0 )
874: * ( 0 0 1 )
875: *
876: * yWorld = rect.top == 0;
877: \*******************************************************/
878: case TMSHEARY: {
879: pto->xfmChange.eM21 = (float) mouDevice.x;
880: pto->xfmChange.eM21 -=pto->xfmChange.eDx;
881: pto->xfmChange.eM21 /=(float) pto->rect.bottom ;
882: SetWorldTransform (pto->hdc, &pto->xfmChange);
883:
884: } break;
885:
886:
887: /*******************************************************\
888: * ( cos(a) -sin(a) 0 )
889: * ( sin(a) cos(a) 0 )
890: * ( 0 0 1 )
891: *
892: * a == rotation angle. Since mouse in in lower right,
893: * we need to shift this back 45 degrees (assuming that
894: * straight down is 0 degrees). Thus we actually compute
895: * cos(a) = cos(b - 45) = cos(b)sin(45) + cos(45)sin(45)
896: * where b is angle from the origin to the mouse (x,y)
897: * cos(45) = sin(45) ~= 0.707107
898: * cos(b) = y/r sin(b) = x/r
899: *
900: \*******************************************************/
901: case TMROTATE: {
902: float r;
903:
904: /* translate back to the origin. */
905: pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
906: SetWorldTransform (pto->hdc, &pto->xfmChange);
907:
908: /* rotate about the origin. */
909: r = (float) sqrt( (double)(mouWorld.x * mouWorld.x) +
910: (double)(mouWorld.y * mouWorld.y));
911:
912: pto->xfmChange.eM11 = (float) mouWorld.y / r;
913: pto->xfmChange.eM11 += (float) mouWorld.x / r;
914: pto->xfmChange.eM11 *= (float) 0.707107;
915: pto->xfmChange.eM22 = pto->xfmChange.eM11;
916:
917: pto->xfmChange.eM12 = (float) mouWorld.y / r;
918: pto->xfmChange.eM12 -= (float) mouWorld.x / r;
919: pto->xfmChange.eM12 *= (float) 0.707107;
920: pto->xfmChange.eM21 = -pto->xfmChange.eM12;
921:
922: pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
923:
924: ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
925:
926: /* translate back to the original offset. */
927: pto->xfmChange.eM11 =
928: pto->xfmChange.eM22 = (float) 1.0;
929: pto->xfmChange.eM12 =
930: pto->xfmChange.eM21 = (float) 0.0;
931:
932: pto->xfmChange.eDx = pto->xfmDown.eDx;
933: pto->xfmChange.eDy = pto->xfmDown.eDy;
934: ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
935: GetWorldTransform (pto->hdc, &pto->xfmChange);
936: } break;
937:
938:
939: /*******************************************************\
940: * ( Size X 0 0 )
941: * ( 0 Size Y 0 )
942: * ( 0 0 1 )
943: *
944: \*******************************************************/
945: case TMSIZEXY: {
946: pto->xfmChange.eM11 = (float) mouDevice.x;
947: pto->xfmChange.eM11 -=pto->xfmChange.eDx;
948: pto->xfmChange.eM11 -=((float) pto->rect.bottom * pto->xfmChange.eM21);
949: pto->xfmChange.eM11 /=(float) pto->rect.right ;
950:
951: pto->xfmChange.eM22 = (float) mouDevice.y;
952: pto->xfmChange.eM22 -=pto->xfmChange.eDy;
953: pto->xfmChange.eM22 -=((float) pto->rect.right * pto->xfmChange.eM12);
954: pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
955: SetWorldTransform (pto->hdc, &pto->xfmChange);
956: } break;
957:
958:
959: /*******************************************************\
960: * ( Size X 0 0 )
961: * ( 0 1 0 )
962: * ( 0 0 1 )
963: *
964: * yWorld = rect.top == 0;
965: \*******************************************************/
966: case TMSIZEX: {
967: pto->xfmChange.eM11 = (float) mouDevice.x;
968: pto->xfmChange.eM11 -=pto->xfmChange.eDx;
969: pto->xfmChange.eM11 /=(float) pto->rect.right ;
970: SetWorldTransform (pto->hdc, &pto->xfmChange);
971: } break;
972:
973:
974: /*******************************************************\
975: * ( 1 0 0 )
976: * ( 0 Size Y 0 )
977: * ( 0 0 1 )
978: *
979: * xWorld = rect.left == 0;
980: \*******************************************************/
981: case TMSIZEY: {
982: pto->xfmChange.eM22 = (float) mouDevice.y;
983: pto->xfmChange.eM22 -=pto->xfmChange.eDy;
984: pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
985: SetWorldTransform (pto->hdc, &pto->xfmChange);
986: } break;
987:
988:
989: /*******************************************************\
990: * ( 1 0 0 )
991: * ( 0 1 0 )
992: * ( Move x Move y 1 )
993: *
994: * xWorld = rect.left == 0;
995: * yWorld = rect.top == 0;
996: \*******************************************************/
997: case TMMOVE: {
998: pto->xfmChange.eDx = (float) mouDevice.x ;
999: pto->xfmChange.eDy = (float) mouDevice.y ;
1000: SetWorldTransform (pto->hdc, &pto->xfmChange);
1001: } break;
1002: } /* end switch */
1003:
1004: doTrackObject(pto, TROB_PAINT, hwnd, lParam);
1005:
1006: return;
1007: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.