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 / chap20 / patron / pagemous.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-20  |  23.0 KB  |  967 lines

  1. /*
  2.  * PAGEMOUS.CPP
  3.  * Patron Chapter 20
  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::OnRightDown
  51.  *
  52.  * Purpose:
  53.  *  Called when the user clicks with the right button on this
  54.  *  page.  If there is an object here, determined by the last
  55.  *  hit-test code, the we'll make a popup-menu for it.
  56.  *
  57.  * Parameters:
  58.  *  uKeys           UINT carrying the key state.
  59.  *  x, y            UINT coordinates of the click in device units.
  60.  *
  61.  * Return Value:
  62.  *  BOOL            Indicates if the action changed the object.
  63.  */
  64.  
  65. BOOL CPage::OnRightDown(UINT uKeys, UINT x, UINT y)
  66.     {
  67.     HMENU       hMenu;
  68.     HMENU       hMenuRes;
  69.     HINSTANCE   hInst;
  70.     HWND        hWndFrame, hWndT;
  71.     POINT       pt;
  72.     UINT        i, cItems;
  73.  
  74.     //Select the tenant under the mouse, if there is one.
  75.     if (!SelectTenantAtPoint(x, y))
  76.         return FALSE;
  77.  
  78.     /*
  79.      * Get the top-level window to which menu command will go.  This
  80.      * will be whatever parent doesn't have a parent itself...
  81.      */
  82.     hWndT=GetParent(m_hWnd);
  83.  
  84.     while (NULL!=hWndT)
  85.         {
  86.         hWndFrame=hWndT;
  87.         hWndT=GetParent(hWndT);
  88.         }
  89.  
  90.     /*
  91.      * Build a popup menu for this object with Cut, Copy, Delete,
  92.      * and object verbs.
  93.      */
  94.     hInst=GETWINDOWINSTANCE(m_hWnd);    //Macro in BOOK1632.H
  95.     hMenuRes=LoadMenu(hInst, MAKEINTRESOURCE(IDR_RIGHTPOPUPMENU));
  96.  
  97.     if (NULL==hMenuRes)
  98.         return FALSE;
  99.  
  100.     hMenu=CreatePopupMenu();
  101.     cItems=GetMenuItemCount(hMenuRes);
  102.  
  103.     for (i=0; i < cItems; i++)
  104.         {
  105.         TCHAR       szTemp[80];
  106.         int         id, uFlags;
  107.  
  108.         GetMenuString(hMenuRes, i, szTemp, sizeof(szTemp)
  109.             , MF_BYPOSITION);
  110.         id=GetMenuItemID(hMenuRes, i);
  111.  
  112.         uFlags=(0==id) ? MF_SEPARATOR : MF_STRING | MF_ENABLED;
  113.         AppendMenu(hMenu, uFlags, id, szTemp);
  114.         }
  115.  
  116.     DestroyMenu(hMenuRes);
  117.  
  118.     //Munge the Object menu item
  119.     m_pTenantCur->AddVerbMenu(hMenu, MENUPOS_OBJECTONPOPUP);
  120.  
  121.     //CHAPTER20MOD
  122.     //Enable or disable the Links item.
  123.     i=FQueryLinksInPage() ? MF_ENABLED : MF_DISABLED | MF_GRAYED;
  124.     EnableMenuItem(hMenu, IDM_EDITLINKS, i | MF_BYCOMMAND);
  125.     //End CHAPTER20MOD
  126.  
  127.     SETPOINT(pt, x, y);
  128.     ClientToScreen(m_hWnd, &pt);
  129.  
  130.     TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON
  131.         , pt.x, pt.y, 0, hWndFrame, NULL);
  132.  
  133.     DestroyMenu(hMenu);
  134.     return FALSE;
  135.     }
  136.  
  137.  
  138.  
  139.  
  140. /*
  141.  * CPage::SelectTenantAtPoint
  142.  *
  143.  * Purpose:
  144.  *  Selects whatever tenant is at the point (x,y) if there is one,
  145.  *  deselecting the previously selected tenant.
  146.  *
  147.  * Parameters:
  148.  *  x, y            UINT coordinates of the mouse.
  149.  *
  150.  * Return Value:
  151.  *  BOOL            TRUE if there is a tenant here, FALSE otherwise.
  152.  */
  153.  
  154. BOOL CPage::SelectTenantAtPoint(UINT x, UINT y)
  155.     {
  156.     UINT            iTenant;
  157.     PCTenant        pTenant;
  158.     PCDocument      pDoc;
  159.  
  160.     iTenant=TenantFromPoint(x, y, &pTenant);
  161.  
  162.     if (NULL==pTenant)
  163.         return FALSE;
  164.  
  165.     //Make the document window active in any case
  166.     pDoc=(PCDocument)SendMessage(GetParent(m_hWnd), DOCM_PDOCUMENT
  167.         , 0, 0L);
  168.  
  169.     if (NULL!=pDoc)
  170.         BringWindowToTop(pDoc->Window());
  171.  
  172.     //If this one is already current, we might be now sizing.
  173.     if (pTenant==m_pTenantCur)
  174.         return TRUE;
  175.  
  176.     //Deselect the current tenant
  177.     if (NULL!=m_pTenantCur)
  178.         m_pTenantCur->Select(FALSE);
  179.  
  180.     //Move this tenant to the top of the list
  181.     m_iTenantCur=0;
  182.  
  183.     SendMessage(m_hWndTenantList, LB_DELETESTRING, iTenant, 0L);
  184.     SendMessage(m_hWndTenantList, LB_INSERTSTRING, 0
  185.         , (LONG)pTenant);
  186.  
  187.     //Select and repaint the new tenant to show it up front
  188.     m_pTenantCur=pTenant;
  189.  
  190.     m_pTenantCur->Repaint();
  191.     m_pTenantCur->Select(TRUE);
  192.  
  193.     return TRUE;
  194.     }
  195.  
  196.  
  197.  
  198.  
  199.  
  200. /*
  201.  * CPage::OnLeftDown
  202.  *
  203.  * Purpose:
  204.  *  Called when the user clicks with the left button on this page.
  205.  *  We find the object under that position that is visibly on top
  206.  *  (always the first one under this location in the page list since
  207.  *  we paint in reverse order) and select it.
  208.  *
  209.  * Parameters:
  210.  *  uKeys           UINT carrying the key state.
  211.  *  x, y            UINT coordinates of the click in device units.
  212.  *
  213.  * Return Value:
  214.  *  BOOL            Indicates if the action changed the object.
  215.  */
  216.  
  217. BOOL CPage::OnLeftDown(UINT uKeys, UINT x, UINT y)
  218.     {
  219.     /*
  220.      * If the mouse is in a position to start dragging,
  221.      * start the timer as with sizing below.
  222.      */
  223.     if (HTCAPTION==m_uHTCode)
  224.         {
  225.         m_fDragPending=TRUE;
  226.  
  227.         //Save down point and start timer.
  228.         m_ptDown.x=x;
  229.         m_ptDown.y=y;
  230.  
  231.         m_uKeysDown=uKeys;
  232.  
  233.         m_fTimer=TRUE;
  234.         SetTimer(m_hWnd, IDTIMER_DEBOUNCE, m_cDelay, NULL);
  235.         return FALSE;
  236.         }
  237.  
  238.     /*
  239.      * If the mouse is in a position to start sizing, start
  240.      * the debounce timer and note the condition.  The sizing
  241.      * will start in OnTimer or OnMouseMove.  This will always
  242.      * happen on the currently selected tenant, and m_uHTCode is
  243.      * set in OnNCHitTest below.
  244.      */
  245.     if (HTNOWHERE!=m_uHTCode && HTCLIENT!=m_uHTCode)
  246.         {
  247.         m_fSizePending=TRUE;
  248.  
  249.         //Save down point and start timer.
  250.         m_ptDown.x=x;
  251.         m_ptDown.y=y;
  252.  
  253.         m_fTimer=TRUE;
  254.         SetTimer(m_hWnd, IDTIMER_DEBOUNCE, m_cDelay, NULL);
  255.         return FALSE;
  256.         }
  257.  
  258.     SelectTenantAtPoint(x, y);
  259.     return FALSE;
  260.     }
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267. /*
  268.  * CPage::OnLeftUp
  269.  *
  270.  * Purpose:
  271.  *  Called when the user clicks up with the left button on this
  272.  *  page. We stop tracking on this message, if necessary, and
  273.  *  resize the object.
  274.  *
  275.  * Parameters:
  276.  *  uKeys           UINT carrying the key state.
  277.  *  x, y            UINT coordinates of the click in device units.
  278.  *
  279.  * Return Value:
  280.  *  BOOL            Indicates if this action changed the object.
  281.  */
  282.  
  283. BOOL CPage::OnLeftUp(UINT uKeys, UINT x, UINT y)
  284.     {
  285.     RECT    rc, rcT;
  286.  
  287.     if (m_fSizePending || m_fDragPending)
  288.         {
  289.         m_fSizePending=FALSE;
  290.         m_fDragPending=FALSE;
  291.  
  292.         if (m_fTimer)
  293.             {
  294.             KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  295.             m_fTimer=FALSE;
  296.             }
  297.  
  298.         return FALSE;
  299.         }
  300.  
  301.     if (!m_fTracking)
  302.         return FALSE;
  303.  
  304.     //Remove the dotted rectangle.
  305.     RECTFROMRECTL(rc, m_rcl)
  306.     DrawFocusRect(m_hDC, &rc);
  307.     ReleaseDC(m_hWnd, m_hDC);
  308.  
  309.     ReleaseCapture();
  310.     m_fTracking=FALSE;
  311.  
  312.     //If the original and new rects are the same, nothing happened.
  313.     RECTFROMRECTL(rcT, m_rclOrg);
  314.  
  315.     if (EqualRect(&rc, &rcT))
  316.         return FALSE;
  317.  
  318.     RECTFROMRECTL(rcT, m_rclOrg);
  319.     InvalidateRect(m_hWnd, &rcT, TRUE);
  320.  
  321.     //Invalidate on the screen before accounting for scrolling
  322.     InvalidateRect(m_hWnd, &rc, TRUE);
  323.  
  324.     //Factor in scrolling and tell the tenant where it now stands.
  325.     OffsetRect(&rc, (int)m_pPG->m_xPos, (int)m_pPG->m_yPos);
  326.     RECTLFROMRECT(m_rcl, rc);
  327.     m_pTenantCur->RectSet(&m_rcl, TRUE, TRUE);
  328.  
  329.     UpdateWindow(m_hWnd);
  330.     return TRUE;
  331.     }
  332.  
  333.  
  334.  
  335.  
  336.  
  337. /*
  338.  * CPage::OnLeftDoubleClick
  339.  *
  340.  * Purpose:
  341.  *  Called when the user double-clicks with the left button on this
  342.  *  page.  We find the object under that position that is visibly on
  343.  *  top (always the first one under this location in the page list
  344.  *  since we paint in reverse order) and activate it.
  345.  *
  346.  * Parameters:
  347.  *  uKeys           UINT carrying the key state.
  348.  *  x, y            UINT coordinates of the click in device units.
  349.  *
  350.  * Return Value:
  351.  *  BOOL            Indicates if the action changed the object.
  352.  */
  353.  
  354. BOOL CPage::OnLeftDoubleClick(UINT uKeys, UINT x, UINT y)
  355.     {
  356.     /*
  357.      * The current tenant is the only one that can be activated, so
  358.      * we just have to make sure the mouse is there.  For that we
  359.      * can use the last hit-test code we saw since it's updated on
  360.      * every mouse move.
  361.      */
  362.  
  363.     if (HTNOWHERE!=m_uHTCode)
  364.         return m_pTenantCur->Activate(OLEIVERB_PRIMARY);
  365.  
  366.     return FALSE;
  367.     }
  368.  
  369.  
  370.  
  371.  
  372.  
  373.  
  374. /*
  375.  * CPage::OnMouseMove
  376.  *
  377.  * Purpose:
  378.  *  Processes WM_MOUSEMOVE on a page so we can handle tracking
  379.  *  resize of a tenant.
  380.  *
  381.  * Parameters:
  382.  *  x, y            int device coordinates to check.
  383.  *
  384.  * Return Value:
  385.  *  None
  386.  */
  387.  
  388. void CPage::OnMouseMove(UINT uKeys, int x, int y)
  389.     {
  390.     RECT        rc, rcO, rcB;
  391.     int         cxy;
  392.  
  393.     if (m_fSizePending || m_fDragPending)
  394.         {
  395.         int     dx, dy;
  396.  
  397.         dx=(x > m_ptDown.x) ? (x-m_ptDown.x) : (m_ptDown.x-x);
  398.         dy=(y > m_ptDown.y) ? (y-m_ptDown.y) : (m_ptDown.y-y);
  399.  
  400.         /*
  401.          * Has the mouse moved outside the debounce distance?  If
  402.          * so, we can start sizing.  Note that this happens
  403.          * regardless of the timer state.
  404.          */
  405.         if (dx > m_cxyDist || dy > m_cxyDist)
  406.             {
  407.             POINT       pt;
  408.             BOOL        fSize=m_fSizePending;
  409.             BOOL        fDrag=m_fDragPending;
  410.  
  411.             m_fSizePending=FALSE;
  412.             m_fDragPending=FALSE;
  413.  
  414.             if (m_fTimer)
  415.                 {
  416.                 KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  417.                 m_fTimer=FALSE;
  418.                 }
  419.  
  420.             if (fDrag)
  421.                 {
  422.                 //Set dirty flag if drag & drop changed things.
  423.                 m_pPG->m_fDirty |= DragDrop(m_uKeysDown, x, y);
  424.                 return;
  425.                 }
  426.  
  427.             if (fSize)
  428.                 StartSizeTracking();
  429.  
  430.             /*
  431.              * Since we might have moved out of the sizing handle
  432.              * in order to start the operation, we need to set the
  433.              * m_uSizingFlags field based on the original down point
  434.              * for subsequent mouse moves to function properly.
  435.              * Note that OnNCHitTest expects screen coordinates.
  436.              */
  437.             SETPOINT(pt, m_ptDown.x, m_ptDown.y);
  438.             ClientToScreen(m_hWnd, &pt);
  439.             OnNCHitTest(pt.x, pt.y);
  440.             OnSetCursor(m_uHTCode);
  441.             return;
  442.             }
  443.         }
  444.  
  445.     if (!m_fTracking)
  446.         return;
  447.  
  448.     //Get rid of the old rectangle.
  449.     RECTFROMRECTL(rc, m_rcl)
  450.     DrawFocusRect(m_hDC, &rc);
  451.  
  452.     /*
  453.      * Calculate the new.  The flags in m_uSizingFlags tell us what
  454.      * to change.  We limit the object by the page margins and a
  455.      * minimum size of 3*CXYHANDLE in either dimension.
  456.      */
  457.     cxy=3*CXYHANDLE;
  458.  
  459.     RECTFROMRECTL(rcO, m_rclOrg);
  460.     RECTFROMRECTL(rcB, m_rclBounds);
  461.  
  462.     if (m_uSizingFlags & SIZINGTOP)
  463.         {
  464.         if (y >= rcO.bottom-cxy)
  465.             y=rcO.bottom-cxy;
  466.  
  467.         if (y <= rcB.top)           //Limit to top of page.
  468.             y=rcB.top;
  469.  
  470.         m_rcl.top=y;
  471.         }
  472.  
  473.     if (m_uSizingFlags & SIZINGBOTTOM)
  474.         {
  475.         if (y <= rcO.top+cxy)
  476.             y=rcO.top+cxy;
  477.  
  478.         if (y >= rcB.bottom)         //Limit to bottom of page.
  479.             y=rcB.bottom;
  480.  
  481.         m_rcl.bottom=y;
  482.         }
  483.  
  484.     if (m_uSizingFlags & SIZINGLEFT)
  485.         {
  486.         if (x >= rcO.right-cxy)
  487.             x=rcO.right-cxy;
  488.  
  489.         if (x <= rcB.left)           //Limit to left of page.
  490.             x=rcB.left;
  491.  
  492.         m_rcl.left=x;
  493.         }
  494.  
  495.     if (m_uSizingFlags & SIZINGRIGHT)
  496.         {
  497.         if (x <= rcO.left+cxy)
  498.             x=rcO.left+cxy;
  499.  
  500.         if (x >= rcB.right)          //Limit to right of page.
  501.             x=rcB.right;
  502.  
  503.         m_rcl.right=x;
  504.         }
  505.  
  506.  
  507.     //Draw the new
  508.     RECTFROMRECTL(rc, m_rcl)
  509.     DrawFocusRect(m_hDC, &rc);
  510.  
  511.     return;
  512.     }
  513.  
  514.  
  515.  
  516.  
  517. /*
  518.  * CPage::OnTimer
  519.  *
  520.  * Purpose:
  521.  *  Processes WM_TIMER messages to a page used to perform mouse
  522.  *  debouncing.
  523.  *
  524.  * Parameters:
  525.  *  uID             UINT timer ID.
  526.  *
  527.  * Return Value:
  528.  *  None
  529.  */
  530.  
  531. void CPage::OnTimer(UINT uID)
  532.     {
  533.     if (m_fSizePending || m_fDragPending)
  534.         {
  535.         BOOL        fSize=m_fSizePending;
  536.         BOOL        fDrag=m_fDragPending;
  537.  
  538.         /*
  539.          * Having this function called means the delay requirement
  540.          * is satisfied.  Start tracking for sizing or dragging.
  541.          */
  542.  
  543.         m_fSizePending=FALSE;
  544.         m_fDragPending=FALSE;
  545.  
  546.         KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  547.         m_fTimer=FALSE;
  548.  
  549.         if (fDrag)
  550.             {
  551.             POINT       pt;
  552.  
  553.             GetCursorPos(&pt);
  554.             m_pPG->m_fDirty |= DragDrop(m_uKeysDown
  555.                 , m_ptDown.x, m_ptDown.y);
  556.             return;
  557.             }
  558.  
  559.         if (fSize)
  560.             StartSizeTracking();
  561.         }
  562.  
  563.     return;
  564.     }
  565.  
  566.  
  567.  
  568.  
  569.  
  570. /*
  571.  * CPage::StartSizeTracking
  572.  *
  573.  * Purpose:
  574.  *  Begins sizing of a tenant when mouse debounce conditions are
  575.  *  met.
  576.  *
  577.  * Parameters:
  578.  *  uID             UINT timer ID.
  579.  *
  580.  * Return Value:
  581.  *  None
  582.  */
  583.  
  584. void CPage::StartSizeTracking(void)
  585.     {
  586.     RECT        rc;
  587.  
  588.     m_pTenantCur->RectGet(&m_rcl, TRUE);
  589.     SetCapture(m_hWnd);
  590.     m_fTracking=TRUE;
  591.  
  592.     m_hDC=GetDC(m_hWnd);
  593.  
  594.     //Place the rectangle exactly where it is on the screen.
  595.     RECTFROMRECTL(rc, m_rcl)
  596.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  597.     RECTLFROMRECT(m_rcl, rc);
  598.     m_rclOrg=m_rcl;
  599.  
  600.     DrawFocusRect(m_hDC, &rc);
  601.  
  602.     m_pPG->CalcBoundingRect(&rc, TRUE);
  603.     RECTLFROMRECT(m_rclBounds, rc);
  604.     return;
  605.     }
  606.  
  607.  
  608.  
  609.  
  610.  
  611. /*
  612.  * CPage::OnNCHitTest
  613.  *
  614.  * Purpose:
  615.  *  Processes WM_NCHITTEST on a page so we can check for hits on the
  616.  *  handles of the selected object for resizing.  We only save
  617.  *  information for ourselves and do not interfere with normal
  618.  *  hit-testing.
  619.  *
  620.  * Parameters:
  621.  *  x, y            UINT device coordinates to check.
  622.  *
  623.  * Return Value:
  624.  *  None
  625.  */
  626.  
  627. void CPage::OnNCHitTest(UINT x, UINT y)
  628.     {
  629.     RECT        rc;
  630.     RECTL       rcl;
  631.     int         iMid1, iMid2;
  632.     int         xHit, yHit;
  633.     POINT       pt;
  634.     int         x0, y0;
  635.  
  636.     /*
  637.      * Ignore this message if it occurs during tracking to adjust
  638.      * for the behavior of oddball mouse drivers.
  639.      */
  640.     if (m_fSizePending || m_fTracking)
  641.         return;
  642.  
  643.     //Default: don't start sizing on a click, don't hit an object.
  644.     m_uSizingFlags=0;
  645.     m_uHTCode=HTNOWHERE;
  646.  
  647.     if (NULL==m_pTenantCur)
  648.         return;
  649.  
  650.     //Convert device points to our coordinates
  651.     m_pTenantCur->RectGet(&rcl, FALSE);
  652.     RECTFROMRECTL(rc, rcl);
  653.     RectConvertMappings(&rc, NULL, TRUE);
  654.     OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos);
  655.  
  656.     SETPOINT(pt, x, y);
  657.     ScreenToClient(m_hWnd, &pt);
  658.     x0=pt.x;
  659.     y0=pt.y;
  660.  
  661.     if (x0 < rc.left || x0 > rc.right)
  662.         return;
  663.  
  664.     if (y0 < rc.top || y0 > rc.bottom)
  665.         return;
  666.  
  667.     //It's at least in the object.
  668.     m_uHTCode=HTCLIENT;
  669.  
  670.     //Check for hits in horizontal regions
  671.     xHit=NOVALUE;
  672.     iMid1=rc.left+((rc.right-rc.left-CXYHANDLE) >> 1);
  673.     iMid2=rc.left+((rc.right-rc.left+CXYHANDLE) >> 1);
  674.  
  675.     if (x0 >= rc.left && x0 <= rc.left+CXYHANDLE)
  676.         xHit=XLEFT;
  677.     else if (x0 >= iMid1 && x0 <= iMid2)
  678.         xHit=XMID;
  679.     else if (x0 >= rc.right-CXYHANDLE && x0 <= rc.right)
  680.         xHit=XRIGHT;
  681.  
  682.     //Don't exit yet if we didn't hit a handle--might hit a y edge.
  683.  
  684.     //Check for hits in vertical regions
  685.     yHit=NOVALUE;
  686.     iMid1=rc.top+((rc.bottom-rc.top-CXYHANDLE) >> 1);
  687.     iMid2=rc.top+((rc.bottom-rc.top+CXYHANDLE) >> 1);
  688.  
  689.     if (y0 >= rc.top && y0 <= rc.top+CXYHANDLE)
  690.         yHit=YTOP;
  691.     else if (y0 >= iMid1 && y0 <= iMid2)
  692.         yHit=YMID;
  693.     else if (y0 >= rc.bottom-CXYHANDLE && y0 <= rc.bottom)
  694.         yHit=YBOT;
  695.  
  696.     /*
  697.      * If we hit any edge, but didn't hit a handle, then one of xHit
  698.      * and yHit will be NOVALUE and the other something else.  When
  699.      * we hit an edge on the 'something else' then we're on a drag
  700.      * point.
  701.      */
  702.  
  703.     if ((NOVALUE==xHit && NOVALUE==yHit)
  704.         || (XMID==xHit && YMID==yHit)
  705.         || (NOVALUE==xHit && YMID==yHit)
  706.         || (XMID==xHit && NOVALUE==yHit))
  707.         return;
  708.  
  709.     if ((NOVALUE==xHit && (YTOP==yHit || YBOT==yHit))
  710.         || ((XLEFT==xHit || XRIGHT==xHit) && NOVALUE==yHit))
  711.         {
  712.         m_uHTCode=HTCAPTION;
  713.         return;
  714.         }
  715.  
  716.     //We hit a handle, so save our HT code
  717.     m_uSizingFlags=g_rguSizingFlags[xHit+(yHit*3)];
  718.     m_uHTCode=g_rgHTCode[xHit+(yHit*3)];
  719.     return;
  720.     }
  721.  
  722.  
  723.  
  724.  
  725.  
  726. /*
  727.  * CPage::SetCursor
  728.  *
  729.  * Purpose:
  730.  *  Processes WM_SETCURSOR using the code from OnNCHitTest.
  731.  *
  732.  * Parameters:
  733.  *  x, y            UINT device coordinates to check.
  734.  *
  735.  * Return Value:
  736.  *  LRESULT         HT* code for Windows.
  737.  */
  738.  
  739. BOOL CPage::OnSetCursor(UINT uHTCode)
  740.     {
  741.     HCURSOR     hCur;
  742.     UINT        iCur;
  743.  
  744.     /*
  745.      * We really just ignore uHTCode and use the one we saved
  746.      * in OnNCHitTest.
  747.      */
  748.  
  749.     switch (m_uHTCode)
  750.         {
  751.         case HTTOP:
  752.         case HTBOTTOM:
  753.             iCur=IDC_VARROWS;
  754.             break;
  755.  
  756.         case HTLEFT:
  757.         case HTRIGHT:
  758.             iCur=IDC_HARROWS;
  759.             break;
  760.  
  761.  
  762.         case HTTOPLEFT:
  763.         case HTBOTTOMRIGHT:
  764.             iCur=IDC_NWSEARROWS;
  765.             break;
  766.  
  767.         case HTTOPRIGHT:
  768.         case HTBOTTOMLEFT:
  769.             iCur=IDC_NESWARROWS;
  770.             break;
  771.  
  772.         case HTCAPTION:
  773.             iCur=IDC_SMALLARROWS;
  774.             break;
  775.  
  776.         default:
  777.             return FALSE;
  778.         }
  779.  
  780.     hCur=UICursorLoad(iCur);
  781.     SetCursor(hCur);
  782.  
  783.     return TRUE;
  784.     }
  785.  
  786.  
  787.  
  788.  
  789.  
  790. /*
  791.  * CPage::TenantFromPoint
  792.  * (Protected)
  793.  *
  794.  * Purpose:
  795.  *  Finds the tenant under the given device coordinates on this
  796.  *  page.
  797.  *
  798.  * Parmeters:
  799.  *  x, y            UINT coordinates.
  800.  *  ppTenant        PCTenant * in which to return the pointer.
  801.  *
  802.  * Return Value:
  803.  *  UINT            Index of the matched tenant, NOVALUE if not
  804.  *                  found.
  805.  */
  806.  
  807. UINT CPage::TenantFromPoint(UINT x, UINT y, PCTenant *ppTenant)
  808.     {
  809.     PCTenant    pTenant;
  810.     RECTL       rcl;
  811.     UINT        i;
  812.     int         x0, y0;
  813.  
  814.     x0=x+m_pPG->m_xPos;
  815.     y0=y+m_pPG->m_yPos;
  816.  
  817.     for (i=0; i < m_cTenants; i++)
  818.         {
  819.         if (!TenantGet(i, &pTenant, FALSE))
  820.             continue;
  821.  
  822.         pTenant->RectGet(&rcl, TRUE);
  823.  
  824.         //Essentially Perform PointInRECTL
  825.         if (x0 >= rcl.left && x0 <= rcl.right)
  826.             {
  827.             if (y0 <=rcl.bottom && y0 >=rcl.top)
  828.                 {
  829.                 *ppTenant=pTenant;
  830.                 return i;
  831.                 }
  832.             }
  833.         }
  834.  
  835.     *ppTenant=NULL;
  836.     return NOVALUE;
  837.     }
  838.  
  839.  
  840.  
  841.  
  842.  
  843.  
  844.  
  845. /*
  846.  * CPage::DragDrop
  847.  *
  848.  * Purpose:
  849.  *  Performs drag-drop operations from the page window
  850.  *
  851.  * Parmeters:
  852.  *  uKeys           UINT state of the keyboard
  853.  *  x, y            UINT mouse coordinates of the starting click.
  854.  *
  855.  * Return Value:
  856.  *  BOOL            TRUE if we modified the page, FALSE otherwise.
  857.  */
  858.  
  859. BOOL CPage::DragDrop(UINT uKeys, UINT x, UINT y)
  860.     {
  861.     LPDROPSOURCE    pIDropSource;
  862.     LPDATAOBJECT    pIDataObject;
  863.     HRESULT         hr;
  864.     DWORD           dwEffect;
  865.     POINTL          ptl;
  866.     SIZEL           szl;
  867.     RECTL           rcl;
  868.     RECT            rc, rcT;
  869.  
  870.     pIDropSource=new CDropSource();
  871.  
  872.     if (NULL==pIDropSource)
  873.         return FALSE;
  874.  
  875.     pIDropSource->AddRef();
  876.     m_pPG->m_fDragSource=TRUE;
  877.  
  878.  
  879.     /*
  880.      * Store a pick point with the data indicating the offset from
  881.      * the upper left of the rectangle where we grabbed it.  This is
  882.      * so the UI feedback in IDropTarget lines up with this tenant.
  883.      */
  884.  
  885.     m_pTenantCur->RectGet(&rcl, TRUE);
  886.     ptl.x=x+m_pPG->m_xPos-rcl.left;
  887.     ptl.y=y+m_pPG->m_yPos-rcl.top;
  888.     pIDataObject=TransferObjectCreate(&ptl);
  889.  
  890.     if (NULL==pIDataObject)
  891.         {
  892.         pIDropSource->Release();
  893.         return FALSE;
  894.         }
  895.  
  896.     m_pPG->m_fMoveInPage=FALSE;
  897.  
  898.     dwEffect=DROPEFFECT_COPY | DROPEFFECT_MOVE;
  899.     hr=DoDragDrop(pIDataObject, pIDropSource
  900.         , DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
  901.  
  902.     pIDataObject->Release();
  903.     pIDropSource->Release();
  904.  
  905.     m_pPG->m_fDragSource=FALSE;
  906.  
  907.     //No drop-no action.
  908.     if (DRAGDROP_S_DROP!=GetScode(hr) || DROPEFFECT_NONE==dwEffect)
  909.         return FALSE;
  910.  
  911.     /*
  912.      * If m_pPG->m_fMoveInPage is set, then we just change the
  913.      * coordinates on m_pTenantCur and we're done.
  914.      */
  915.     if (m_pPG->m_fMoveInPage)
  916.         {
  917.         m_pTenantCur->Invalidate();
  918.  
  919.         /*
  920.          * Clip to page boundaries.  We know that ptDrop has to be
  921.          * in the page somewhere or we would not have dropped
  922.          * (effect was NONE).  So first make sure that ptDrop is
  923.          * within 3*CXYHANDLE of the right or bottom, and if so,
  924.          * pull it out to 3*CXYHANDLE.  Then we can just clip the
  925.          * size to the page rectangle and we'll always be sure to
  926.          * have at least a sizeable object.
  927.          */
  928.         m_pTenantCur->SizeGet(&szl, TRUE);
  929.         SetRect(&rc, (int)m_pPG->m_ptDrop.x, (int)m_pPG->m_ptDrop.y
  930.             , 0, 0);
  931.         RectConvertMappings(&rc, NULL, TRUE);
  932.  
  933.         m_pPG->CalcBoundingRect(&rcT, FALSE);
  934.         OffsetRect(&rcT, (int)m_pPG->m_xPos, (int)m_pPG->m_yPos);
  935.  
  936.         if (rc.left >= rcT.right-3*CXYHANDLE)
  937.             rc.left=rcT.right-3*CXYHANDLE;
  938.  
  939.         if (rc.top >= rcT.bottom-3*CXYHANDLE)
  940.             rc.top=rcT.bottom-3*CXYHANDLE;
  941.  
  942.         rc.right=rc.left+(int)szl.cx;
  943.         rc.bottom=rc.top+(int)szl.cy;
  944.         IntersectRect(&rc, &rc, &rcT);
  945.  
  946.         RECTLFROMRECT(rcl, rc);
  947.  
  948.         m_pTenantCur->RectSet(&rcl, TRUE, FALSE);
  949.         m_pTenantCur->Repaint();
  950.         return TRUE;
  951.         }
  952.  
  953.     /*
  954.      * Otherwise we may have to delete the old tenant if the effect
  955.      * was move.  This will not happen in the move in page case.
  956.      */
  957.  
  958.     if (DROPEFFECT_MOVE==dwEffect)
  959.         {
  960.         TenantDestroy();
  961.         return TRUE;
  962.         }
  963.  
  964.     //Copy is a clean operation
  965.     return FALSE;
  966.     }
  967.