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 / chap24 / patron / pagemous.cpp < prev    next >
C/C++ Source or Header  |  1996-05-20  |  25KB  |  1,004 lines

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