TRACK.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:17k
源码类别:

Windows编程

开发平台:

Visual C++

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