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