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

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