home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap12 / patron / pagemous.cpp < prev    next >
C/C++ Source or Header  |  1996-05-20  |  15KB  |  646 lines

  1. /*
  2.  * PAGEMOUS.CPP
  3.  * Patron Chapter 12
  4.  *
  5.  * Implementation of mouse-related member functions of CPage.
  6.  * The remainder is in PAGE.CPP.  This separate file keeps this
  7.  * grungy hit-testing/drawing code out of our way.
  8.  *
  9.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  10.  *
  11.  * Kraig Brockschmidt, Microsoft
  12.  * Internet  :  kraigb@microsoft.com
  13.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  14.  */
  15.  
  16.  
  17. #include "patron.h"
  18.  
  19.  
  20. //Lookups into the array using g_rgHTCode[x+y*3] in PAGEMOUS.CPP
  21. #define YTOP            0
  22. #define YMID            1
  23. #define YBOT            2
  24. #define XLEFT           0
  25. #define XMID            1
  26. #define XRIGHT          2
  27.  
  28. //Values to restrict sizing in CPage::OnMouseMove
  29. #define SIZINGTOP       0x0001
  30. #define SIZINGBOTTOM    0x0002
  31. #define SIZINGLEFT      0x0004
  32. #define SIZINGRIGHT     0x0008
  33.  
  34.  
  35. //This array is for hit-testing lookups
  36. static UINT g_rgHTCode[9]={HTTOPLEFT, HTTOP, HTTOPRIGHT
  37.     , HTLEFT, HTCLIENT, HTRIGHT, HTBOTTOMLEFT, HTBOTTOM
  38.     , HTBOTTOMRIGHT};
  39.  
  40.  
  41. //This is for restricting tracking based on the hit-test
  42. static UINT g_rguSizingFlags[9]={SIZINGTOP | SIZINGLEFT, SIZINGTOP
  43.     , SIZINGTOP | SIZINGRIGHT, SIZINGLEFT, 0, SIZINGRIGHT
  44.     , SIZINGBOTTOM | SIZINGLEFT, SIZINGBOTTOM
  45.     , SIZINGBOTTOM | SIZINGRIGHT};
  46.  
  47.  
  48.  
  49. /*
  50.  * CPage::OnLeftDown
  51.  *
  52.  * Purpose:
  53.  *  Called when the user clicks with the left button on this page.
  54.  *  We find the object under that position that is visibly on top
  55.  *  (always the first one under this location in the page list since
  56.  *  we paint in reverse order) and select it.
  57.  *
  58.  * Parameters:
  59.  *  uKeys           UINT carrying the key state.
  60.  *  x, y            UINT coordinates of the click in device units.
  61.  *
  62.  * Return Value:
  63.  *  BOOL            Indicates if the action changed the object.
  64.  */
  65.  
  66. BOOL CPage::OnLeftDown(UINT uKeys, UINT x, UINT y)
  67.     {
  68.     UINT        iTenant;
  69.     PCTenant    pTenant;
  70.  
  71.     /*
  72.      * If the mouse is in a position to start sizing, start
  73.      * the debounce timer and note the condition.  The sizing
  74.      * will start in OnTimer or OnMouseMove.  This will always
  75.      * happen on the currently selected tenant, and m_uHTCode is
  76.      * set in OnNCHitTest below.
  77.      */
  78.     if (HTNOWHERE!=m_uHTCode && HTCLIENT!=m_uHTCode)
  79.         {
  80.         m_fSizePending=TRUE;
  81.  
  82.         //Save down point and start timer.
  83.         m_ptDown.x=x;
  84.         m_ptDown.y=y;
  85.  
  86.         m_fTimer=TRUE;
  87.         SetTimer(m_hWnd, IDTIMER_DEBOUNCE, m_cDelay, NULL);
  88.         return FALSE;
  89.         }
  90.  
  91.     iTenant=TenantFromPoint(x, y, &pTenant);
  92.  
  93.     if (NULL==pTenant)
  94.         return FALSE;
  95.  
  96.     //If this one is already current, we might be now sizing.
  97.     if (pTenant==m_pTenantCur)
  98.         return FALSE;
  99.  
  100.     //Deselect the current tenant
  101.     if (NULL!=m_pTenantCur)
  102.         m_pTenantCur->Select(FALSE);
  103.  
  104.     //Move this tenant to the top of the list
  105.     m_iTenantCur=0;
  106.  
  107.     SendMessage(m_hWndTenantList, LB_DELETESTRING, iTenant, 0L);
  108.     SendMessage(m_hWndTenantList, LB_INSERTSTRING, 0,(LONG)pTenant);
  109.  
  110.     //Select and repaint the new tenant to show it up front
  111.     m_pTenantCur=pTenant;
  112.  
  113.     m_pTenantCur->Repaint();
  114.     m_pTenantCur->Select(TRUE);
  115.  
  116.     return FALSE;
  117.     }
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124. /*
  125.  * CPage::OnLeftUp
  126.  *
  127.  * Purpose:
  128.  *  Called when the user clicks up with the left button on this
  129.  *  page. We stop tracking on this message, if necessary, and
  130.  *  resize the object.
  131.  *
  132.  * Parameters:
  133.  *  uKeys           UINT carrying the key state.
  134.  *  x, y            UINT coordinates of the click in device units.
  135.  *
  136.  * Return Value:
  137.  *  BOOL            Indicates if this action changed the object.
  138.  */
  139.  
  140. BOOL CPage::OnLeftUp(UINT uKeys, UINT x, UINT y)
  141.     {
  142.     RECT    rc, rcT;
  143.  
  144.     if (m_fSizePending)
  145.         {
  146.         m_fSizePending=FALSE;
  147.  
  148.         if (m_fTimer)
  149.             {
  150.             KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  151.             m_fTimer=FALSE;
  152.             }
  153.  
  154.         return FALSE;
  155.         }
  156.  
  157.     if (!m_fTracking)
  158.         return FALSE;
  159.  
  160.     //Remove the dotted rectangle.
  161.     RECTFROMRECTL(rc, m_rcl)
  162.     DrawFocusRect(m_hDC, &rc);
  163.     ReleaseDC(m_hWnd, m_hDC);
  164.  
  165.     ReleaseCapture();
  166.     m_fTracking=FALSE;
  167.  
  168.     //If the original and new rects are the same, nothing happened.
  169.     RECTFROMRECTL(rcT, m_rclOrg);
  170.  
  171.     if (EqualRect(&rc, &rcT))
  172.         return FALSE;
  173.  
  174.     RECTFROMRECTL(rcT, m_rclOrg);
  175.     InvalidateRect(m_hWnd, &rcT, TRUE);
  176.  
  177.     //Invalidate on the screen before accounting for scrolling
  178.     InvalidateRect(m_hWnd, &rc, TRUE);
  179.  
  180.     //Factor in scrolling and tell the tenant where it now stands.
  181.     OffsetRect(&rc, (int)m_pPG->m_xPos, (int)m_pPG->m_yPos);
  182.     RECTLFROMRECT(m_rcl, rc);
  183.     m_pTenantCur->RectSet(&m_rcl, TRUE);
  184.  
  185.     UpdateWindow(m_hWnd);
  186.     return TRUE;
  187.     }
  188.  
  189.  
  190.  
  191.  
  192.  
  193. /*
  194.  * CPage::OnLeftDoubleClick
  195.  *
  196.  * Purpose:
  197.  *  Called when the user double-clicks with the left button on this
  198.  *  page.  We find the object under that position that is visibly on
  199.  *  top (always the first one under this location in the page list
  200.  *  since we paint in reverse order) and activate it.
  201.  *
  202.  * Parameters:
  203.  *  uKeys           UINT carrying the key state.
  204.  *  x, y            UINT coordinates of the click in device units.
  205.  *
  206.  * Return Value:
  207.  *  BOOL            Indicates if the action changed the object.
  208.  */
  209.  
  210. BOOL CPage::OnLeftDoubleClick(UINT uKeys, UINT x, UINT y)
  211.     {
  212.     /*
  213.      * The current tenant is the only one that can be activated, so
  214.      * we just have to make sure the mouse is there.  For that we
  215.      * can use the last hit-test code we saw since it's updated on
  216.      * every mouse move.
  217.      */
  218.  
  219.     if (HTNOWHERE!=m_uHTCode)
  220.         return m_pTenantCur->Activate(OLEIVERB_PRIMARY);
  221.  
  222.     return FALSE;
  223.     }
  224.  
  225.  
  226.  
  227.  
  228.  
  229.  
  230. /*
  231.  * CPage::OnMouseMove
  232.  *
  233.  * Purpose:
  234.  *  Processes WM_MOUSEMOVE on a page so we can handle tracking
  235.  *  resize of a tenant.
  236.  *
  237.  * Parameters:
  238.  *  x, y            int device coordinates to check.
  239.  *
  240.  * Return Value:
  241.  *  None
  242.  */
  243.  
  244. void CPage::OnMouseMove(UINT uKeys, int x, int y)
  245.     {
  246.     RECT        rc, rcO, rcB;
  247.     int         cxy;
  248.  
  249.     if (m_fSizePending)
  250.         {
  251.         int     dx, dy;
  252.  
  253.         dx=(x > m_ptDown.x) ? (x-m_ptDown.x) : (m_ptDown.x-x);
  254.         dy=(y > m_ptDown.y) ? (y-m_ptDown.y) : (m_ptDown.y-y);
  255.  
  256.         /*
  257.          * Has the mouse moved outside the debounce distance?  If
  258.          * so, we can start sizing.  Note that this happens
  259.          * regardless of the timer state.
  260.          */
  261.         if (dx > m_cxyDist || dy > m_cxyDist)
  262.             {
  263.             POINT       pt;
  264.  
  265.             m_fSizePending=FALSE;
  266.  
  267.             if (m_fTimer)
  268.                 {
  269.                 KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  270.                 m_fTimer=FALSE;
  271.                 }
  272.  
  273.             StartSizeTracking();
  274.  
  275.             /*
  276.              * Since we might have moved out of the sizing handle
  277.              * in order to start the operation, we need to set the
  278.              * m_uSizingFlags field based on the original down point
  279.              * for subsequent mouse moves to function properly.
  280.              * Note that OnNCHitTest expects screen coordinates.
  281.              */
  282.             SETPOINT(pt, m_ptDown.x, m_ptDown.y);
  283.             ClientToScreen(m_hWnd, &pt);
  284.             OnNCHitTest(pt.x, pt.y);
  285.             OnSetCursor(m_uHTCode);
  286.             return;
  287.             }
  288.         }
  289.  
  290.     if (!m_fTracking)
  291.         return;
  292.  
  293.     //Get rid of the old rectangle.
  294.     RECTFROMRECTL(rc, m_rcl)
  295.     DrawFocusRect(m_hDC, &rc);
  296.  
  297.     /*
  298.      * Calculate the new.  The flags in m_uSizingFlags tell us what
  299.      * to change.  We limit the object by the page margins and a
  300.      * minimum size of 3*CXYHANDLE in either dimension.
  301.      */
  302.     cxy=3*CXYHANDLE;
  303.  
  304.     RECTFROMRECTL(rcO, m_rclOrg);
  305.     RECTFROMRECTL(rcB, m_rclBounds);
  306.  
  307.     if (m_uSizingFlags & SIZINGTOP)
  308.         {
  309.         if (y >= rcO.bottom-cxy)
  310.             y=rcO.bottom-cxy;
  311.  
  312.         if (y <= rcB.top)           //Limit to top of page.
  313.             y=rcB.top;
  314.  
  315.         m_rcl.top=y;
  316.         }
  317.  
  318.     if (m_uSizingFlags & SIZINGBOTTOM)
  319.         {
  320.         if (y <= rcO.top+cxy)
  321.             y=rcO.top+cxy;
  322.  
  323.         if (y >= rcB.bottom)         //Limit to bottom of page.
  324.             y=rcB.bottom;
  325.  
  326.         m_rcl.bottom=y;
  327.         }
  328.  
  329.     if (m_uSizingFlags & SIZINGLEFT)
  330.         {
  331.         if (x >= rcO.right-cxy)
  332.             x=rcO.right-cxy;
  333.  
  334.         if (x <= rcB.left)           //Limit to left of page.
  335.             x=rcB.left;
  336.  
  337.         m_rcl.left=x;
  338.         }
  339.  
  340.     if (m_uSizingFlags & SIZINGRIGHT)
  341.         {
  342.         if (x <= rcO.left+cxy)
  343.             x=rcO.left+cxy;
  344.  
  345.         if (x >= rcB.right)          //Limit to right of page.
  346.             x=rcB.right;
  347.  
  348.         m_rcl.right=x;
  349.         }
  350.  
  351.  
  352.     //Draw the new
  353.     RECTFROMRECTL(rc, m_rcl)
  354.     DrawFocusRect(m_hDC, &rc);
  355.  
  356.     return;
  357.     }
  358.  
  359.  
  360.  
  361.  
  362. /*
  363.  * CPage::OnTimer
  364.  *
  365.  * Purpose:
  366.  *  Processes WM_TIMER messages to a page used to perform mouse
  367.  *  debouncing.
  368.  *
  369.  * Parameters:
  370.  *  uID             UINT timer ID.
  371.  *
  372.  * Return Value:
  373.  *  None
  374.  */
  375.  
  376. void CPage::OnTimer(UINT uID)
  377.     {
  378.     if (m_fSizePending)
  379.         {
  380.         /*
  381.          * Having this function called means the delay requirement
  382.          * is satisfied.  Start tracking.
  383.          */
  384.  
  385.         m_fSizePending=FALSE;
  386.         KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  387.         m_fTimer=FALSE;
  388.         StartSizeTracking();
  389.         }
  390.  
  391.     return;
  392.     }
  393.  
  394.  
  395.  
  396.  
  397.  
  398. /*
  399.  * CPage::StartSizeTracking
  400.  *
  401.  * Purpose:
  402.  *  Begins sizing of a tenant when mouse debounce conditions are
  403.  *  met.
  404.  *
  405.  * Parameters:
  406.  *  uID             UINT timer ID.
  407.  *
  408.  * Return Value:
  409.  *  None
  410.  */
  411.  
  412. void CPage::StartSizeTracking(void)
  413.     {
  414.     RECT        rc;
  415.  
  416.     m_pTenantCur->RectGet(&m_rcl, TRUE);
  417.     SetCapture(m_hWnd);
  418.     m_fTracking=TRUE;
  419.  
  420.     m_hDC=GetDC(m_hWnd);
  421.  
  422.     //Place the rectangle exactly where it is on the screen.
  423.     RECTFROMRECTL(rc, m_rcl)
  424.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  425.     RECTLFROMRECT(m_rcl, rc);
  426.     m_rclOrg=m_rcl;
  427.  
  428.     DrawFocusRect(m_hDC, &rc);
  429.  
  430.     m_pPG->CalcBoundingRect(&rc, TRUE);
  431.     RECTLFROMRECT(m_rclBounds, rc);
  432.     return;
  433.     }
  434.  
  435.  
  436.  
  437.  
  438.  
  439. /*
  440.  * CPage::OnNCHitTest
  441.  *
  442.  * Purpose:
  443.  *  Processes WM_NCHITTEST on a page so we can check for hits on the
  444.  *  handles of the selected object for resizing.  We only save
  445.  *  information for ourselves and do not interfere with normal
  446.  *  hit-testing.
  447.  *
  448.  * Parameters:
  449.  *  x, y            UINT device coordinates to check.
  450.  *
  451.  * Return Value:
  452.  *  None
  453.  */
  454.  
  455. void CPage::OnNCHitTest(UINT x, UINT y)
  456.     {
  457.     RECT        rc;
  458.     RECTL       rcl;
  459.     int         iMid1, iMid2;
  460.     int         xHit, yHit;
  461.     POINT       pt;
  462.     int         x0, y0;
  463.  
  464.     /*
  465.      * Ignore this message if it occurs during tracking to adjust
  466.      * for the behavior of oddball mouse drivers.
  467.      */
  468.     if (m_fSizePending || m_fTracking)
  469.         return;
  470.  
  471.     //Default: don't start sizing on a click, don't hit an object.
  472.     m_uSizingFlags=0;
  473.     m_uHTCode=HTNOWHERE;
  474.  
  475.     if (NULL==m_pTenantCur)
  476.         return;
  477.  
  478.     //Convert device points to our coordinates
  479.     m_pTenantCur->RectGet(&rcl, FALSE);
  480.     RECTFROMRECTL(rc, rcl);
  481.     RectConvertMappings(&rc, NULL, TRUE);
  482.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  483.  
  484.     SETPOINT(pt, x, y);
  485.     ScreenToClient(m_hWnd, &pt);
  486.     x0=pt.x;
  487.     y0=pt.y;
  488.  
  489.     if (x0 < rc.left || x0 > rc.right)
  490.         return;
  491.  
  492.     if (y0 < rc.top || y0 > rc.bottom)
  493.         return;
  494.  
  495.     //It's at least in the object.
  496.     m_uHTCode=HTCLIENT;
  497.  
  498.     //Check for hits in horizontal regions
  499.     xHit=NOVALUE;
  500.     iMid1=rc.left+((rc.right-rc.left-CXYHANDLE) >> 1);
  501.     iMid2=rc.left+((rc.right-rc.left+CXYHANDLE) >> 1);
  502.  
  503.     if (x0 >= rc.left && x0 <= rc.left+CXYHANDLE)
  504.         xHit=XLEFT;
  505.     else if (x0 >= iMid1 && x0 <= iMid2)
  506.         xHit=XMID;
  507.     else if (x0 >= rc.right-CXYHANDLE && x0 <= rc.right)
  508.         xHit=XRIGHT;
  509.  
  510.     if (NOVALUE==xHit)
  511.         return;
  512.  
  513.     //Check for hits in vertical regions
  514.     yHit=NOVALUE;
  515.     iMid1=rc.top+((rc.bottom-rc.top-CXYHANDLE) >> 1);
  516.     iMid2=rc.top+((rc.bottom-rc.top+CXYHANDLE) >> 1);
  517.  
  518.     if (y0 >= rc.top && y0 <= rc.top+CXYHANDLE)
  519.         yHit=YTOP;
  520.     else if (y0 >= iMid1 && y0 <= iMid2)
  521.         yHit=YMID;
  522.     else if (y0 >= rc.bottom-CXYHANDLE && y0 <= rc.bottom)
  523.         yHit=YBOT;
  524.  
  525.     if (NOVALUE==yHit)
  526.         return;
  527.  
  528.     //We hit a handle, so save our HT code
  529.     m_uSizingFlags=g_rguSizingFlags[xHit+(yHit*3)];
  530.     m_uHTCode=g_rgHTCode[xHit+(yHit*3)];
  531.     return;
  532.     }
  533.  
  534.  
  535.  
  536.  
  537.  
  538. /*
  539.  * CPage::SetCursor
  540.  *
  541.  * Purpose:
  542.  *  Processes WM_SETCURSOR using the code from OnNCHitTest.
  543.  *
  544.  * Parameters:
  545.  *  x, y            UINT device coordinates to check.
  546.  *
  547.  * Return Value:
  548.  *  LRESULT         HT* code for Windows.
  549.  */
  550.  
  551. BOOL CPage::OnSetCursor(UINT uHTCode)
  552.     {
  553.     HCURSOR     hCur;
  554.     UINT        iCur;
  555.  
  556.     /*
  557.      * We really just ignore uHTCode and use the one we saved
  558.      * in OnNCHitTest.
  559.      */
  560.  
  561.     switch (m_uHTCode)
  562.         {
  563.         case HTTOP:
  564.         case HTBOTTOM:
  565.             iCur=IDC_VARROWS;
  566.             break;
  567.  
  568.         case HTLEFT:
  569.         case HTRIGHT:
  570.             iCur=IDC_HARROWS;
  571.             break;
  572.  
  573.  
  574.         case HTTOPLEFT:
  575.         case HTBOTTOMRIGHT:
  576.             iCur=IDC_NWSEARROWS;
  577.             break;
  578.  
  579.         case HTTOPRIGHT:
  580.         case HTBOTTOMLEFT:
  581.             iCur=IDC_NESWARROWS;
  582.             break;
  583.  
  584.         default:
  585.             return FALSE;
  586.         }
  587.  
  588.     hCur=UICursorLoad(iCur);
  589.     SetCursor(hCur);
  590.  
  591.     return TRUE;
  592.     }
  593.  
  594.  
  595.  
  596.  
  597.  
  598. /*
  599.  * CPage::TenantFromPoint
  600.  * (Protected)
  601.  *
  602.  * Purpose:
  603.  *  Finds the tenant under the given device coordinates on this
  604.  *  page.
  605.  *
  606.  * Parmeters:
  607.  *  x, y            UINT coordinates.
  608.  *  ppTenant        PCTenant * in which to return the pointer.
  609.  *
  610.  * Return Value:
  611.  *  UINT            Index of the matched tenant, NOVALUE if not
  612.  *                  found.
  613.  */
  614.  
  615. UINT CPage::TenantFromPoint(UINT x, UINT y, PCTenant *ppTenant)
  616.     {
  617.     PCTenant    pTenant;
  618.     RECTL       rcl;
  619.     UINT        i;
  620.     int         x0, y0;
  621.  
  622.     x0=x+m_pPG->m_xPos;
  623.     y0=y+m_pPG->m_yPos;
  624.  
  625.     for (i=0; i < m_cTenants; i++)
  626.         {
  627.         if (!TenantGet(i, &pTenant, FALSE))
  628.             continue;
  629.  
  630.         pTenant->RectGet(&rcl, TRUE);
  631.  
  632.         //Essentially Perform PointInRECTL
  633.         if (x0 >= rcl.left && x0 <= rcl.right)
  634.             {
  635.             if (y0 <=rcl.bottom && y0 >=rcl.top)
  636.                 {
  637.                 *ppTenant=pTenant;
  638.                 return i;
  639.                 }
  640.             }
  641.         }
  642.  
  643.     *ppTenant=NULL;
  644.     return NOVALUE;
  645.     }
  646.