home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / gdi / winnt / wxform / wxform.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  37KB  |  1,066 lines

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