Annotation of mstools/samples/plgblt/track.c, revision 1.1.1.2

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.