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