PAGEMOUS.CPP
/* 
 * PAGEMOUS.CPP 
 * Patron Chapter 20 
 * 
 * Implementation of mouse-related member functions of CPage. 
 * The remainder is in PAGE.CPP.  This separate file keeps this 
 * grungy hit-testing/drawing code out of our way. 
 * 
 * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved 
 * 
 * Kraig Brockschmidt, Microsoft 
 * Internet  :  kraigb@microsoft.com 
 * Compuserve:  >INTERNET:kraigb@microsoft.com 
 */ 
 
 
#include "patron.h" 
 
 
//Lookups into the array using g_rgHTCode[x+y*3] in PAGEMOUS.CPP 
#define YTOP            0 
#define YMID            1 
#define YBOT            2 
#define XLEFT           0 
#define XMID            1 
#define XRIGHT          2 
 
//Values to restrict sizing in CPage::OnMouseMove 
#define SIZINGTOP       0x0001 
#define SIZINGBOTTOM    0x0002 
#define SIZINGLEFT      0x0004 
#define SIZINGRIGHT     0x0008 
 
 
//This array is for hit-testing lookups 
static UINT g_rgHTCode[9]={HTTOPLEFT, HTTOP, HTTOPRIGHT 
    , HTLEFT, HTCLIENT, HTRIGHT, HTBOTTOMLEFT, HTBOTTOM 
    , HTBOTTOMRIGHT}; 
 
 
//This is for restricting tracking based on the hit-test 
static UINT g_rguSizingFlags[9]={SIZINGTOP | SIZINGLEFT, SIZINGTOP 
    , SIZINGTOP | SIZINGRIGHT, SIZINGLEFT, 0, SIZINGRIGHT 
    , SIZINGBOTTOM | SIZINGLEFT, SIZINGBOTTOM 
    , SIZINGBOTTOM | SIZINGRIGHT}; 
 
 
 
/* 
 * CPage::OnRightDown 
 * 
 * Purpose: 
 *  Called when the user clicks with the right button on this 
 *  page.  If there is an object here, determined by the last 
 *  hit-test code, the we'll make a popup-menu for it. 
 * 
 * Parameters: 
 *  uKeys           UINT carrying the key state. 
 *  x, y            UINT coordinates of the click in device units. 
 * 
 * Return Value: 
 *  BOOL            Indicates if the action changed the object. 
 */ 
 
BOOL CPage::OnRightDown(UINT uKeys, UINT x, UINT y) 
    { 
    HMENU       hMenu; 
    HMENU       hMenuRes; 
    HINSTANCE   hInst; 
    HWND        hWndFrame, hWndT; 
    POINT       pt; 
    UINT        i, cItems; 
 
    //Select the tenant under the mouse, if there is one. 
    if (!SelectTenantAtPoint(x, y)) 
        return FALSE; 
 
    /* 
     * Get the top-level window to which menu command will go.  This 
     * will be whatever parent doesn't have a parent itself... 
     */ 
    hWndT=GetParent(m_hWnd); 
 
    while (NULL!=hWndT) 
        { 
        hWndFrame=hWndT; 
        hWndT=GetParent(hWndT); 
        } 
 
    /* 
     * Build a popup menu for this object with Cut, Copy, Delete, 
     * and object verbs. 
     */ 
    hInst=GETWINDOWINSTANCE(m_hWnd);    //Macro in BOOK1632.H 
    hMenuRes=LoadMenu(hInst, MAKEINTRESOURCE(IDR_RIGHTPOPUPMENU)); 
 
    if (NULL==hMenuRes) 
        return FALSE; 
 
    hMenu=CreatePopupMenu(); 
    cItems=GetMenuItemCount(hMenuRes); 
 
    for (i=0; i < cItems; i++) 
        { 
        TCHAR       szTemp[80]; 
        int         id, uFlags; 
 
        GetMenuString(hMenuRes, i, szTemp, sizeof(szTemp) 
            , MF_BYPOSITION); 
        id=GetMenuItemID(hMenuRes, i); 
 
        uFlags=(0==id) ? MF_SEPARATOR : MF_STRING | MF_ENABLED; 
        AppendMenu(hMenu, uFlags, id, szTemp); 
        } 
 
    DestroyMenu(hMenuRes); 
 
    //Munge the Object menu item 
    m_pTenantCur->AddVerbMenu(hMenu, MENUPOS_OBJECTONPOPUP); 
 
    //CHAPTER20MOD 
    //Enable or disable the Links item. 
    i=FQueryLinksInPage() ? MF_ENABLED : MF_DISABLED | MF_GRAYED; 
    EnableMenuItem(hMenu, IDM_EDITLINKS, i | MF_BYCOMMAND); 
    //End CHAPTER20MOD 
 
    SETPOINT(pt, x, y); 
    ClientToScreen(m_hWnd, &pt); 
 
    TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON 
        , pt.x, pt.y, 0, hWndFrame, NULL); 
 
    DestroyMenu(hMenu); 
    return FALSE; 
    } 
 
 
 
 
/* 
 * CPage::SelectTenantAtPoint 
 * 
 * Purpose: 
 *  Selects whatever tenant is at the point (x,y) if there is one, 
 *  deselecting the previously selected tenant. 
 * 
 * Parameters: 
 *  x, y            UINT coordinates of the mouse. 
 * 
 * Return Value: 
 *  BOOL            TRUE if there is a tenant here, FALSE otherwise. 
 */ 
 
BOOL CPage::SelectTenantAtPoint(UINT x, UINT y) 
    { 
    UINT            iTenant; 
    PCTenant        pTenant; 
    PCDocument      pDoc; 
 
    iTenant=TenantFromPoint(x, y, &pTenant); 
 
    if (NULL==pTenant) 
        return FALSE; 
 
    //Make the document window active in any case 
    pDoc=(PCDocument)SendMessage(GetParent(m_hWnd), DOCM_PDOCUMENT 
        , 0, 0L); 
 
    if (NULL!=pDoc) 
        BringWindowToTop(pDoc->Window()); 
 
    //If this one is already current, we might be now sizing. 
    if (pTenant==m_pTenantCur) 
        return TRUE; 
 
    //Deselect the current tenant 
    if (NULL!=m_pTenantCur) 
        m_pTenantCur->Select(FALSE); 
 
    //Move this tenant to the top of the list 
    m_iTenantCur=0; 
 
    SendMessage(m_hWndTenantList, LB_DELETESTRING, iTenant, 0L); 
    SendMessage(m_hWndTenantList, LB_INSERTSTRING, 0 
        , (LONG)pTenant); 
 
    //Select and repaint the new tenant to show it up front 
    m_pTenantCur=pTenant; 
 
    m_pTenantCur->Repaint(); 
    m_pTenantCur->Select(TRUE); 
 
    return TRUE; 
    } 
 
 
 
 
 
/* 
 * CPage::OnLeftDown 
 * 
 * Purpose: 
 *  Called when the user clicks with the left button on this page. 
 *  We find the object under that position that is visibly on top 
 *  (always the first one under this location in the page list since 
 *  we paint in reverse order) and select it. 
 * 
 * Parameters: 
 *  uKeys           UINT carrying the key state. 
 *  x, y            UINT coordinates of the click in device units. 
 * 
 * Return Value: 
 *  BOOL            Indicates if the action changed the object. 
 */ 
 
BOOL CPage::OnLeftDown(UINT uKeys, UINT x, UINT y) 
    { 
    /* 
     * If the mouse is in a position to start dragging, 
     * start the timer as with sizing below. 
     */ 
    if (HTCAPTION==m_uHTCode) 
        { 
        m_fDragPending=TRUE; 
 
        //Save down point and start timer. 
        m_ptDown.x=x; 
        m_ptDown.y=y; 
 
        m_uKeysDown=uKeys; 
 
        m_fTimer=TRUE; 
        SetTimer(m_hWnd, IDTIMER_DEBOUNCE, m_cDelay, NULL); 
        return FALSE; 
        } 
 
    /* 
     * If the mouse is in a position to start sizing, start 
     * the debounce timer and note the condition.  The sizing 
     * will start in OnTimer or OnMouseMove.  This will always 
     * happen on the currently selected tenant, and m_uHTCode is 
     * set in OnNCHitTest below. 
     */ 
    if (HTNOWHERE!=m_uHTCode && HTCLIENT!=m_uHTCode) 
        { 
        m_fSizePending=TRUE; 
 
        //Save down point and start timer. 
        m_ptDown.x=x; 
        m_ptDown.y=y; 
 
        m_fTimer=TRUE; 
        SetTimer(m_hWnd, IDTIMER_DEBOUNCE, m_cDelay, NULL); 
        return FALSE; 
        } 
 
    SelectTenantAtPoint(x, y); 
    return FALSE; 
    } 
 
 
 
 
 
 
/* 
 * CPage::OnLeftUp 
 * 
 * Purpose: 
 *  Called when the user clicks up with the left button on this 
 *  page. We stop tracking on this message, if necessary, and 
 *  resize the object. 
 * 
 * Parameters: 
 *  uKeys           UINT carrying the key state. 
 *  x, y            UINT coordinates of the click in device units. 
 * 
 * Return Value: 
 *  BOOL            Indicates if this action changed the object. 
 */ 
 
BOOL CPage::OnLeftUp(UINT uKeys, UINT x, UINT y) 
    { 
    RECT    rc, rcT; 
 
    if (m_fSizePending || m_fDragPending) 
        { 
        m_fSizePending=FALSE; 
        m_fDragPending=FALSE; 
 
        if (m_fTimer) 
            { 
            KillTimer(m_hWnd, IDTIMER_DEBOUNCE); 
            m_fTimer=FALSE; 
            } 
 
        return FALSE; 
        } 
 
    if (!m_fTracking) 
        return FALSE; 
 
    //Remove the dotted rectangle. 
    RECTFROMRECTL(rc, m_rcl) 
    DrawFocusRect(m_hDC, &rc); 
    ReleaseDC(m_hWnd, m_hDC); 
 
    ReleaseCapture(); 
    m_fTracking=FALSE; 
 
    //If the original and new rects are the same, nothing happened. 
    RECTFROMRECTL(rcT, m_rclOrg); 
 
    if (EqualRect(&rc, &rcT)) 
        return FALSE; 
 
    RECTFROMRECTL(rcT, m_rclOrg); 
    InvalidateRect(m_hWnd, &rcT, TRUE); 
 
    //Invalidate on the screen before accounting for scrolling 
    InvalidateRect(m_hWnd, &rc, TRUE); 
 
    //Factor in scrolling and tell the tenant where it now stands. 
    OffsetRect(&rc, (int)m_pPG->m_xPos, (int)m_pPG->m_yPos); 
    RECTLFROMRECT(m_rcl, rc); 
    m_pTenantCur->RectSet(&m_rcl, TRUE, TRUE); 
 
    UpdateWindow(m_hWnd); 
    return TRUE; 
    } 
 
 
 
 
 
/* 
 * CPage::OnLeftDoubleClick 
 * 
 * Purpose: 
 *  Called when the user double-clicks with the left button on this 
 *  page.  We find the object under that position that is visibly on 
 *  top (always the first one under this location in the page list 
 *  since we paint in reverse order) and activate it. 
 * 
 * Parameters: 
 *  uKeys           UINT carrying the key state. 
 *  x, y            UINT coordinates of the click in device units. 
 * 
 * Return Value: 
 *  BOOL            Indicates if the action changed the object. 
 */ 
 
BOOL CPage::OnLeftDoubleClick(UINT uKeys, UINT x, UINT y) 
    { 
    /* 
     * The current tenant is the only one that can be activated, so 
     * we just have to make sure the mouse is there.  For that we 
     * can use the last hit-test code we saw since it's updated on 
     * every mouse move. 
     */ 
 
    if (HTNOWHERE!=m_uHTCode) 
        return m_pTenantCur->Activate(OLEIVERB_PRIMARY); 
 
    return FALSE; 
    } 
 
 
 
 
 
 
/* 
 * CPage::OnMouseMove 
 * 
 * Purpose: 
 *  Processes WM_MOUSEMOVE on a page so we can handle tracking 
 *  resize of a tenant. 
 * 
 * Parameters: 
 *  x, y            int device coordinates to check. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CPage::OnMouseMove(UINT uKeys, int x, int y) 
    { 
    RECT        rc, rcO, rcB; 
    int         cxy; 
 
    if (m_fSizePending || m_fDragPending) 
        { 
        int     dx, dy; 
 
        dx=(x > m_ptDown.x) ? (x-m_ptDown.x) : (m_ptDown.x-x); 
        dy=(y > m_ptDown.y) ? (y-m_ptDown.y) : (m_ptDown.y-y); 
 
        /* 
         * Has the mouse moved outside the debounce distance?  If 
         * so, we can start sizing.  Note that this happens 
         * regardless of the timer state. 
         */ 
        if (dx > m_cxyDist || dy > m_cxyDist) 
            { 
            POINT       pt; 
            BOOL        fSize=m_fSizePending; 
            BOOL        fDrag=m_fDragPending; 
 
            m_fSizePending=FALSE; 
            m_fDragPending=FALSE; 
 
            if (m_fTimer) 
                { 
                KillTimer(m_hWnd, IDTIMER_DEBOUNCE); 
                m_fTimer=FALSE; 
                } 
 
            if (fDrag) 
                { 
                //Set dirty flag if drag & drop changed things. 
                m_pPG->m_fDirty |= DragDrop(m_uKeysDown, x, y); 
                return; 
                } 
 
            if (fSize) 
                StartSizeTracking(); 
 
            /* 
             * Since we might have moved out of the sizing handle 
             * in order to start the operation, we need to set the 
             * m_uSizingFlags field based on the original down point 
             * for subsequent mouse moves to function properly. 
             * Note that OnNCHitTest expects screen coordinates. 
             */ 
            SETPOINT(pt, m_ptDown.x, m_ptDown.y); 
            ClientToScreen(m_hWnd, &pt); 
            OnNCHitTest(pt.x, pt.y); 
            OnSetCursor(m_uHTCode); 
            return; 
            } 
        } 
 
    if (!m_fTracking) 
        return; 
 
    //Get rid of the old rectangle. 
    RECTFROMRECTL(rc, m_rcl) 
    DrawFocusRect(m_hDC, &rc); 
 
    /* 
     * Calculate the new.  The flags in m_uSizingFlags tell us what 
     * to change.  We limit the object by the page margins and a 
     * minimum size of 3*CXYHANDLE in either dimension. 
     */ 
    cxy=3*CXYHANDLE; 
 
    RECTFROMRECTL(rcO, m_rclOrg); 
    RECTFROMRECTL(rcB, m_rclBounds); 
 
    if (m_uSizingFlags & SIZINGTOP) 
        { 
        if (y >= rcO.bottom-cxy) 
            y=rcO.bottom-cxy; 
 
        if (y <= rcB.top)           //Limit to top of page. 
            y=rcB.top; 
 
        m_rcl.top=y; 
        } 
 
    if (m_uSizingFlags & SIZINGBOTTOM) 
        { 
        if (y <= rcO.top+cxy) 
            y=rcO.top+cxy; 
 
        if (y >= rcB.bottom)         //Limit to bottom of page. 
            y=rcB.bottom; 
 
        m_rcl.bottom=y; 
        } 
 
    if (m_uSizingFlags & SIZINGLEFT) 
        { 
        if (x >= rcO.right-cxy) 
            x=rcO.right-cxy; 
 
        if (x <= rcB.left)           //Limit to left of page. 
            x=rcB.left; 
 
        m_rcl.left=x; 
        } 
 
    if (m_uSizingFlags & SIZINGRIGHT) 
        { 
        if (x <= rcO.left+cxy) 
            x=rcO.left+cxy; 
 
        if (x >= rcB.right)          //Limit to right of page. 
            x=rcB.right; 
 
        m_rcl.right=x; 
        } 
 
 
    //Draw the new 
    RECTFROMRECTL(rc, m_rcl) 
    DrawFocusRect(m_hDC, &rc); 
 
    return; 
    } 
 
 
 
 
/* 
 * CPage::OnTimer 
 * 
 * Purpose: 
 *  Processes WM_TIMER messages to a page used to perform mouse 
 *  debouncing. 
 * 
 * Parameters: 
 *  uID             UINT timer ID. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CPage::OnTimer(UINT uID) 
    { 
    if (m_fSizePending || m_fDragPending) 
        { 
        BOOL        fSize=m_fSizePending; 
        BOOL        fDrag=m_fDragPending; 
 
        /* 
         * Having this function called means the delay requirement 
         * is satisfied.  Start tracking for sizing or dragging. 
         */ 
 
        m_fSizePending=FALSE; 
        m_fDragPending=FALSE; 
 
        KillTimer(m_hWnd, IDTIMER_DEBOUNCE); 
        m_fTimer=FALSE; 
 
        if (fDrag) 
            { 
            POINT       pt; 
 
            GetCursorPos(&pt); 
            m_pPG->m_fDirty |= DragDrop(m_uKeysDown 
                , m_ptDown.x, m_ptDown.y); 
            return; 
            } 
 
        if (fSize) 
            StartSizeTracking(); 
        } 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CPage::StartSizeTracking 
 * 
 * Purpose: 
 *  Begins sizing of a tenant when mouse debounce conditions are 
 *  met. 
 * 
 * Parameters: 
 *  uID             UINT timer ID. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CPage::StartSizeTracking(void) 
    { 
    RECT        rc; 
 
    m_pTenantCur->RectGet(&m_rcl, TRUE); 
    SetCapture(m_hWnd); 
    m_fTracking=TRUE; 
 
    m_hDC=GetDC(m_hWnd); 
 
    //Place the rectangle exactly where it is on the screen. 
    RECTFROMRECTL(rc, m_rcl) 
    OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
    RECTLFROMRECT(m_rcl, rc); 
    m_rclOrg=m_rcl; 
 
    DrawFocusRect(m_hDC, &rc); 
 
    m_pPG->CalcBoundingRect(&rc, TRUE); 
    RECTLFROMRECT(m_rclBounds, rc); 
    return; 
    } 
 
 
 
 
 
/* 
 * CPage::OnNCHitTest 
 * 
 * Purpose: 
 *  Processes WM_NCHITTEST on a page so we can check for hits on the 
 *  handles of the selected object for resizing.  We only save 
 *  information for ourselves and do not interfere with normal 
 *  hit-testing. 
 * 
 * Parameters: 
 *  x, y            UINT device coordinates to check. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CPage::OnNCHitTest(UINT x, UINT y) 
    { 
    RECT        rc; 
    RECTL       rcl; 
    int         iMid1, iMid2; 
    int         xHit, yHit; 
    POINT       pt; 
    int         x0, y0; 
 
    /* 
     * Ignore this message if it occurs during tracking to adjust 
     * for the behavior of oddball mouse drivers. 
     */ 
    if (m_fSizePending || m_fTracking) 
        return; 
 
    //Default: don't start sizing on a click, don't hit an object. 
    m_uSizingFlags=0; 
    m_uHTCode=HTNOWHERE; 
 
    if (NULL==m_pTenantCur) 
        return; 
 
    //Convert device points to our coordinates 
    m_pTenantCur->RectGet(&rcl, FALSE); 
    RECTFROMRECTL(rc, rcl); 
    RectConvertMappings(&rc, NULL, TRUE); 
    OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
 
    SETPOINT(pt, x, y); 
    ScreenToClient(m_hWnd, &pt); 
    x0=pt.x; 
    y0=pt.y; 
 
    if (x0 < rc.left || x0 > rc.right) 
        return; 
 
    if (y0 < rc.top || y0 > rc.bottom) 
        return; 
 
    //It's at least in the object. 
    m_uHTCode=HTCLIENT; 
 
    //Check for hits in horizontal regions 
    xHit=NOVALUE; 
    iMid1=rc.left+((rc.right-rc.left-CXYHANDLE) >> 1); 
    iMid2=rc.left+((rc.right-rc.left+CXYHANDLE) >> 1); 
 
    if (x0 >= rc.left && x0 <= rc.left+CXYHANDLE) 
        xHit=XLEFT; 
    else if (x0 >= iMid1 && x0 <= iMid2) 
        xHit=XMID; 
    else if (x0 >= rc.right-CXYHANDLE && x0 <= rc.right) 
        xHit=XRIGHT; 
 
    //Don't exit yet if we didn't hit a handle--might hit a y edge. 
 
    //Check for hits in vertical regions 
    yHit=NOVALUE; 
    iMid1=rc.top+((rc.bottom-rc.top-CXYHANDLE) >> 1); 
    iMid2=rc.top+((rc.bottom-rc.top+CXYHANDLE) >> 1); 
 
    if (y0 >= rc.top && y0 <= rc.top+CXYHANDLE) 
        yHit=YTOP; 
    else if (y0 >= iMid1 && y0 <= iMid2) 
        yHit=YMID; 
    else if (y0 >= rc.bottom-CXYHANDLE && y0 <= rc.bottom) 
        yHit=YBOT; 
 
    /* 
     * If we hit any edge, but didn't hit a handle, then one of xHit 
     * and yHit will be NOVALUE and the other something else.  When 
     * we hit an edge on the 'something else' then we're on a drag 
     * point. 
     */ 
 
    if ((NOVALUE==xHit && NOVALUE==yHit) 
        || (XMID==xHit && YMID==yHit) 
        || (NOVALUE==xHit && YMID==yHit) 
        || (XMID==xHit && NOVALUE==yHit)) 
        return; 
 
    if ((NOVALUE==xHit && (YTOP==yHit || YBOT==yHit)) 
        || ((XLEFT==xHit || XRIGHT==xHit) && NOVALUE==yHit)) 
        { 
        m_uHTCode=HTCAPTION; 
        return; 
        } 
 
    //We hit a handle, so save our HT code 
    m_uSizingFlags=g_rguSizingFlags[xHit+(yHit*3)]; 
    m_uHTCode=g_rgHTCode[xHit+(yHit*3)]; 
    return; 
    } 
 
 
 
 
 
/* 
 * CPage::SetCursor 
 * 
 * Purpose: 
 *  Processes WM_SETCURSOR using the code from OnNCHitTest. 
 * 
 * Parameters: 
 *  x, y            UINT device coordinates to check. 
 * 
 * Return Value: 
 *  LRESULT         HT* code for Windows. 
 */ 
 
BOOL CPage::OnSetCursor(UINT uHTCode) 
    { 
    HCURSOR     hCur; 
    UINT        iCur; 
 
    /* 
     * We really just ignore uHTCode and use the one we saved 
     * in OnNCHitTest. 
     */ 
 
    switch (m_uHTCode) 
        { 
        case HTTOP: 
        case HTBOTTOM: 
            iCur=IDC_VARROWS; 
            break; 
 
        case HTLEFT: 
        case HTRIGHT: 
            iCur=IDC_HARROWS; 
            break; 
 
 
        case HTTOPLEFT: 
        case HTBOTTOMRIGHT: 
            iCur=IDC_NWSEARROWS; 
            break; 
 
        case HTTOPRIGHT: 
        case HTBOTTOMLEFT: 
            iCur=IDC_NESWARROWS; 
            break; 
 
        case HTCAPTION: 
            iCur=IDC_SMALLARROWS; 
            break; 
 
        default: 
            return FALSE; 
        } 
 
    hCur=UICursorLoad(iCur); 
    SetCursor(hCur); 
 
    return TRUE; 
    } 
 
 
 
 
 
/* 
 * CPage::TenantFromPoint 
 * (Protected) 
 * 
 * Purpose: 
 *  Finds the tenant under the given device coordinates on this 
 *  page. 
 * 
 * Parmeters: 
 *  x, y            UINT coordinates. 
 *  ppTenant        PCTenant * in which to return the pointer. 
 * 
 * Return Value: 
 *  UINT            Index of the matched tenant, NOVALUE if not 
 *                  found. 
 */ 
 
UINT CPage::TenantFromPoint(UINT x, UINT y, PCTenant *ppTenant) 
    { 
    PCTenant    pTenant; 
    RECTL       rcl; 
    UINT        i; 
    int         x0, y0; 
 
    x0=x+m_pPG->m_xPos; 
    y0=y+m_pPG->m_yPos; 
 
    for (i=0; i < m_cTenants; i++) 
        { 
        if (!TenantGet(i, &pTenant, FALSE)) 
            continue; 
 
        pTenant->RectGet(&rcl, TRUE); 
 
        //Essentially Perform PointInRECTL 
        if (x0 >= rcl.left && x0 <= rcl.right) 
            { 
            if (y0 <=rcl.bottom && y0 >=rcl.top) 
                { 
                *ppTenant=pTenant; 
                return i; 
                } 
            } 
        } 
 
    *ppTenant=NULL; 
    return NOVALUE; 
    } 
 
 
 
 
 
 
 
/* 
 * CPage::DragDrop 
 * 
 * Purpose: 
 *  Performs drag-drop operations from the page window 
 * 
 * Parmeters: 
 *  uKeys           UINT state of the keyboard 
 *  x, y            UINT mouse coordinates of the starting click. 
 * 
 * Return Value: 
 *  BOOL            TRUE if we modified the page, FALSE otherwise. 
 */ 
 
BOOL CPage::DragDrop(UINT uKeys, UINT x, UINT y) 
    { 
    LPDROPSOURCE    pIDropSource; 
    LPDATAOBJECT    pIDataObject; 
    HRESULT         hr; 
    DWORD           dwEffect; 
    POINTL          ptl; 
    SIZEL           szl; 
    RECTL           rcl; 
    RECT            rc, rcT; 
 
    pIDropSource=new CDropSource(); 
 
    if (NULL==pIDropSource) 
        return FALSE; 
 
    pIDropSource->AddRef(); 
    m_pPG->m_fDragSource=TRUE; 
 
 
    /* 
     * Store a pick point with the data indicating the offset from 
     * the upper left of the rectangle where we grabbed it.  This is 
     * so the UI feedback in IDropTarget lines up with this tenant. 
     */ 
 
    m_pTenantCur->RectGet(&rcl, TRUE); 
    ptl.x=x+m_pPG->m_xPos-rcl.left; 
    ptl.y=y+m_pPG->m_yPos-rcl.top; 
    pIDataObject=TransferObjectCreate(&ptl); 
 
    if (NULL==pIDataObject) 
        { 
        pIDropSource->Release(); 
        return FALSE; 
        } 
 
    m_pPG->m_fMoveInPage=FALSE; 
 
    dwEffect=DROPEFFECT_COPY | DROPEFFECT_MOVE; 
    hr=DoDragDrop(pIDataObject, pIDropSource 
        , DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect); 
 
    pIDataObject->Release(); 
    pIDropSource->Release(); 
 
    m_pPG->m_fDragSource=FALSE; 
 
    //No drop-no action. 
    if (DRAGDROP_S_DROP!=GetScode(hr) || DROPEFFECT_NONE==dwEffect) 
        return FALSE; 
 
    /* 
     * If m_pPG->m_fMoveInPage is set, then we just change the 
     * coordinates on m_pTenantCur and we're done. 
     */ 
    if (m_pPG->m_fMoveInPage) 
        { 
        m_pTenantCur->Invalidate(); 
 
        /* 
         * Clip to page boundaries.  We know that ptDrop has to be 
         * in the page somewhere or we would not have dropped 
         * (effect was NONE).  So first make sure that ptDrop is 
         * within 3*CXYHANDLE of the right or bottom, and if so, 
         * pull it out to 3*CXYHANDLE.  Then we can just clip the 
         * size to the page rectangle and we'll always be sure to 
         * have at least a sizeable object. 
         */ 
        m_pTenantCur->SizeGet(&szl, TRUE); 
        SetRect(&rc, (int)m_pPG->m_ptDrop.x, (int)m_pPG->m_ptDrop.y 
            , 0, 0); 
        RectConvertMappings(&rc, NULL, TRUE); 
 
        m_pPG->CalcBoundingRect(&rcT, FALSE); 
        OffsetRect(&rcT, (int)m_pPG->m_xPos, (int)m_pPG->m_yPos); 
 
        if (rc.left >= rcT.right-3*CXYHANDLE) 
            rc.left=rcT.right-3*CXYHANDLE; 
 
        if (rc.top >= rcT.bottom-3*CXYHANDLE) 
            rc.top=rcT.bottom-3*CXYHANDLE; 
 
        rc.right=rc.left+(int)szl.cx; 
        rc.bottom=rc.top+(int)szl.cy; 
        IntersectRect(&rc, &rc, &rcT); 
 
        RECTLFROMRECT(rcl, rc); 
 
        m_pTenantCur->RectSet(&rcl, TRUE, FALSE); 
        m_pTenantCur->Repaint(); 
        return TRUE; 
        } 
 
    /* 
     * Otherwise we may have to delete the old tenant if the effect 
     * was move.  This will not happen in the move in page case. 
     */ 
 
    if (DROPEFFECT_MOVE==dwEffect) 
        { 
        TenantDestroy(); 
        return TRUE; 
        } 
 
    //Copy is a clean operation 
    return FALSE; 
    }