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 / droptgt.cpp < prev    next >
C/C++ Source or Header  |  1995-05-03  |  15KB  |  590 lines

  1. /*
  2.  * DROPTGT.CPP
  3.  * Patron Chapter 22
  4.  *
  5.  * Implementation of a DropTarget object
  6.  *
  7.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  8.  *
  9.  * Kraig Brockschmidt, Microsoft
  10.  * Internet  :  kraigb@microsoft.com
  11.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  12.  */
  13.  
  14.  
  15. #include "patron.h"
  16.  
  17.  
  18. /*
  19.  * CDropTarget::CDropTarget
  20.  * CDropTarget::~CDropTarget
  21.  *
  22.  * Constructor Parameters:
  23.  *  pDoc            PCPatronDoc of the window containing us.
  24.  */
  25.  
  26. CDropTarget::CDropTarget(PCPatronDoc pDoc)
  27.     {
  28.     m_cRef=0;
  29.     m_pDoc=pDoc;
  30.  
  31.     m_pIDataObject=NULL;
  32.     return;
  33.     }
  34.  
  35.  
  36. CDropTarget::~CDropTarget(void)
  37.     {
  38.     return;
  39.     }
  40.  
  41.  
  42.  
  43.  
  44. /*
  45.  * CDropTarget::QueryInterface
  46.  * CDropTarget::AddRef
  47.  * CDropTarget::Release
  48.  *
  49.  * Purpose:
  50.  *  IUnknown members for CDropTarget object.
  51.  */
  52.  
  53. STDMETHODIMP CDropTarget::QueryInterface(REFIID riid, PPVOID ppv)
  54.     {
  55.     *ppv=NULL;
  56.  
  57.     if (IID_IUnknown==riid || IID_IDropTarget==riid)
  58.         *ppv=this;
  59.  
  60.     if (NULL!=*ppv)
  61.         {
  62.         ((LPUNKNOWN)*ppv)->AddRef();
  63.         return NOERROR;
  64.         }
  65.  
  66.     return ResultFromScode(E_NOINTERFACE);
  67.     }
  68.  
  69.  
  70. STDMETHODIMP_(ULONG) CDropTarget::AddRef(void)
  71.     {
  72.     return ++m_cRef;
  73.     }
  74.  
  75. STDMETHODIMP_(ULONG) CDropTarget::Release(void)
  76.     {
  77.     if (0!=--m_cRef)
  78.         return m_cRef;
  79.  
  80.     delete this;
  81.     return 0;
  82.     }
  83.  
  84.  
  85.  
  86.  
  87.  
  88. /*
  89.  * CDropTarget::DragEnter
  90.  *
  91.  * Purpose:
  92.  *  Indicates that data in a drag operation has been dragged over
  93.  *  our window that's a potential target.  We are to decide if it's
  94.  *  something in which we're interested.
  95.  *
  96.  * Parameters:
  97.  *  pIDataSource    LPDATAOBJECT providing the source data.
  98.  *  grfKeyState     DWORD flags: states of keys and mouse buttons.
  99.  *  pt              POINTL coordinates in the client space of
  100.  *                  the document.
  101.  *  pdwEffect       LPDWORD into which we'll place the appropriate
  102.  *                  effect flag for this point.
  103.  *
  104.  * Return Value:
  105.  *  HRESULT         NOERROR
  106.  */
  107.  
  108. STDMETHODIMP CDropTarget::DragEnter(LPDATAOBJECT pIDataSource
  109.     , DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
  110.     {
  111.     PCPages         ppg=m_pDoc->m_pPG;
  112.     HWND            hWnd;
  113.     FORMATETC       fe;
  114.     STGMEDIUM       stm;
  115.     UINT            uRet;
  116.  
  117.     m_fFeedback=FALSE;
  118.     m_pIDataObject=NULL;
  119.  
  120.     if (!m_pDoc->FQueryPasteFromData(pIDataSource, &fe, NULL))
  121.         {
  122.         *pdwEffect=DROPEFFECT_NONE;
  123.         return NOERROR;
  124.         }
  125.  
  126.     //Check if we can link from this data object as well.
  127.     ppg->m_fLinkAllowed
  128.         =(NOERROR==OleQueryLinkFromData(pIDataSource));
  129.  
  130.     //We never allow it dragging in ourselves.
  131.     ppg->m_fLinkAllowed &= !ppg->m_fDragSource;
  132.  
  133.     //Check if this is a valid drop point.
  134.     uRet=ppg->UTestDroppablePoint(&pt);
  135.     ppg->m_uLastTest=uRet;
  136.  
  137.     if (UDROP_NONE==uRet)
  138.         *pdwEffect=DROPEFFECT_NONE;
  139.     else
  140.         {
  141.         //Default is move if we can, in fact drop here.
  142.         *pdwEffect=DROPEFFECT_MOVE;
  143.  
  144.         if (grfKeyState & MK_CONTROL)
  145.             {
  146.             if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
  147.                 *pdwEffect=DROPEFFECT_LINK;
  148.             else
  149.                 *pdwEffect=DROPEFFECT_COPY;
  150.             }
  151.         }
  152.  
  153.     m_pIDataObject=pIDataSource;
  154.     m_pIDataObject->AddRef();
  155.  
  156.     /*
  157.      * Determine the size of the data, if we can.  The default is
  158.      * a small rectangle since we can't easily tell what size
  159.      * something will be if we're pulling in a metafile or bitmap.
  160.      * It's not a good idea to render it here with GetData just to
  161.      * find that out. We only know the size if it's our own object
  162.      * in which case a GetData will be fast.
  163.      */
  164.  
  165.     if (fe.cfFormat==m_pDoc->m_cf)
  166.         {
  167.         if (SUCCEEDED(pIDataSource->GetData(&fe, &stm)))
  168.             {
  169.             PPATRONOBJECT   ppo;
  170.             RECT            rc;
  171.  
  172.             ppo=(PPATRONOBJECT)GlobalLock(stm.hGlobal);
  173.  
  174.             SetRect(&rc, (int)ppo->szl.cx, -(int)ppo->szl.cy, 0, 0);
  175.             RectConvertMappings(&rc, NULL, TRUE);
  176.             SETSIZEL(m_szl, rc.left, rc.top);
  177.  
  178.             m_ptPick=ppo->ptlPick;
  179.             m_fe=ppo->fe;
  180.  
  181.             GlobalUnlock(stm.hGlobal);
  182.             ReleaseStgMedium(&stm);
  183.             }
  184.         }
  185.     else
  186.         {
  187.         SETSIZEL(m_szl, 30, 30);
  188.         m_ptPick.x=0;
  189.         m_ptPick.y=0;
  190.         m_fe.cfFormat=0;
  191.  
  192.         /*
  193.          * Try to get CFSTR_OBJECTDESCRIPTOR which might have a size
  194.          * and a pick point.  If it exists, then always use the
  195.          * point but still default to a 30*30 size if the sizes
  196.          * are zero.
  197.          */
  198.         uRet=RegisterClipboardFormat(CFSTR_OBJECTDESCRIPTOR);
  199.         SETDefFormatEtc(fe, uRet, TYMED_HGLOBAL);
  200.  
  201.         if (SUCCEEDED(pIDataSource->GetData(&fe, &stm)))
  202.             {
  203.             LPOBJECTDESCRIPTOR  pOD;
  204.  
  205.             pOD=(LPOBJECTDESCRIPTOR)GlobalLock(stm.hGlobal);
  206.  
  207.             //Get the size, converting to LOMETRIC.
  208.             if (0!=pOD->sizel.cx && 0!=pOD->sizel.cy)
  209.                 {
  210.                 XformSizeInHimetricToPixels(NULL, &pOD->sizel
  211.                     , &m_szl);
  212.                 }
  213.  
  214.             //POINTL and SIZEL are interchangeable
  215.             XformSizeInHimetricToPixels(NULL, (LPSIZEL)&pOD->pointl
  216.                 , (LPSIZEL)&m_ptPick);
  217.  
  218.             GlobalUnlock(stm.hGlobal);
  219.             ReleaseStgMedium(&stm);
  220.             }
  221.         }
  222.  
  223.  
  224.     //Bring the document window up front, show what a drop will do.
  225.     hWnd=m_pDoc->Window();
  226.     BringWindowToTop(hWnd);
  227.     UpdateWindow(hWnd);
  228.  
  229.     ppg->m_uVScrollCode=NOVALUE;
  230.     ppg->m_uHScrollCode=NOVALUE;
  231.     m_fPendingRepaint=FALSE;
  232.  
  233.     pt.x-=m_ptPick.x;
  234.     pt.y-=m_ptPick.y;
  235.  
  236.     m_ptLast=pt;
  237.     m_fFeedback=TRUE;
  238.     ppg->DrawDropTargetRect(&pt, &m_szl);
  239.  
  240.     return NOERROR;
  241.     }
  242.  
  243.  
  244.  
  245.  
  246.  
  247.  
  248. /*
  249.  * CDropTarget::DragOver
  250.  *
  251.  * Purpose:
  252.  *  Indicates that the mouse was moved inside the window represented
  253.  *  by this drop target.  This happens on every WM_MOUSEMOVE, so
  254.  *  this function should be very efficient.
  255.  *
  256.  * Parameters:
  257.  *  grfKeyState     DWORD providing the current keyboard and
  258.  *                  mouse states
  259.  *  pt              POINTL where the mouse currently is.
  260.  *  pdwEffect       LPDWORD in which to store the effect flag
  261.  *                  for this point.
  262.  *
  263.  * Return Value:
  264.  *  HRESULT         NOERROR
  265.  */
  266.  
  267. STDMETHODIMP CDropTarget::DragOver(DWORD grfKeyState, POINTL pt
  268.     , LPDWORD pdwEffect)
  269.     {
  270.     PCPages     ppg=m_pDoc->m_pPG;
  271.     UINT        uRet, uLast;
  272.     UINT        xPos, yPos;
  273.  
  274.     *pdwEffect=DROPEFFECT_NONE;
  275.  
  276.     if (NULL==m_pIDataObject)
  277.         return NOERROR;
  278.  
  279.     //Check if this is still a valid point.  uRet used below as well
  280.     uRet=ppg->UTestDroppablePoint(&pt);
  281.  
  282.     if (UDROP_NONE==uRet)
  283.         *pdwEffect=DROPEFFECT_NONE;
  284.     else
  285.         {
  286.         //Store these before possibly ORing in DROPEFFECT_SCROLL
  287.         *pdwEffect=DROPEFFECT_MOVE;
  288.  
  289.         if (grfKeyState & MK_CONTROL)
  290.             {
  291.             if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
  292.                 *pdwEffect=DROPEFFECT_LINK;
  293.             else
  294.                 *pdwEffect=DROPEFFECT_COPY;
  295.             }
  296.         }
  297.  
  298.     //If we haven't moved and we are not scrolling, then we're done.
  299.     if ((pt.x-m_ptPick.x==m_ptLast.x)
  300.         && (pt.y-m_ptPick.y==m_ptLast.y)
  301.         && !((UDROP_INSETHORZ|UDROP_INSETVERT) & ppg->m_uLastTest))
  302.         {
  303.         return NOERROR;
  304.         }
  305.  
  306.     //Remove the last feedback rectangle.
  307.     if (m_fFeedback)
  308.         ppg->DrawDropTargetRect(&m_ptLast, &m_szl);
  309.  
  310.     uLast=ppg->m_uLastTest;
  311.     ppg->m_uLastTest=uRet;
  312.  
  313.     if (UDROP_NONE==uRet)
  314.         {
  315.         //If we're now an invalid point, better repaint as necessary
  316.         if (m_fPendingRepaint)
  317.             {
  318.             UpdateWindow(ppg->m_hWnd);
  319.             m_fPendingRepaint=FALSE;
  320.             }
  321.  
  322.         ppg->m_uVScrollCode=NOVALUE;
  323.         ppg->m_uHScrollCode=NOVALUE;
  324.         m_fFeedback=FALSE;
  325.         return NOERROR;
  326.         }
  327.  
  328.  
  329.     /*
  330.      * Scrolling is a little tricky:  We get a DragOver pulse even
  331.      * if we didn't move.  First we have to delay scrolling for
  332.      * ppg->m_uScrollDelay clock ticks which we can determine using
  333.      * GetTickCount.  Timers do not work here since we may not be
  334.      * yielding to our message loop.
  335.      *
  336.      * Once we know we are scrolling then we determine if we
  337.      * scroll again or if we reset the scrolling state.
  338.      */
  339.  
  340.     if ((UDROP_INSETHORZ & uLast) && !(UDROP_INSETHORZ & uRet))
  341.         ppg->m_uHScrollCode=NOVALUE;
  342.  
  343.     if (!(UDROP_INSETHORZ & uLast) && (UDROP_INSETHORZ & uRet))
  344.         {
  345.         ppg->m_dwTimeLast=GetTickCount();
  346.         ppg->m_uHScrollCode=(0!=(UDROP_INSETLEFT & uRet))
  347.             ? SB_LINELEFT : SB_LINERIGHT; //Same as UP & DOWN codes.
  348.         }
  349.  
  350.     if ((UDROP_INSETVERT & uLast) && !(UDROP_INSETVERT & uRet))
  351.         ppg->m_uVScrollCode=NOVALUE;
  352.  
  353.     if (!(UDROP_INSETVERT & uLast) && (UDROP_INSETVERT & uRet))
  354.         {
  355.         ppg->m_dwTimeLast=GetTickCount();
  356.         ppg->m_uVScrollCode=(0!=(UDROP_INSETTOP & uRet))
  357.             ? SB_LINEUP : SB_LINEDOWN;
  358.         }
  359.  
  360.     //Only change the last time if ALL scrolling stops.
  361.     if (NOVALUE==ppg->m_uHScrollCode && NOVALUE==ppg->m_uVScrollCode)
  362.         ppg->m_dwTimeLast=0L;
  363.  
  364.     //Set the scroll effect on any inset hit.
  365.     if ((UDROP_INSETHORZ | UDROP_INSETVERT) & uRet)
  366.         *pdwEffect |= DROPEFFECT_SCROLL;
  367.  
  368.     xPos=ppg->m_xPos;
  369.     yPos=ppg->m_yPos;
  370.  
  371.     //Has the delay elapsed?  We can scroll if so
  372.     if (ppg->m_dwTimeLast!=0
  373.         && (GetTickCount()-ppg->m_dwTimeLast)
  374.         > (DWORD)ppg->m_uScrollDelay)
  375.         {
  376.         if (NOVALUE!=ppg->m_uHScrollCode)
  377.             {
  378.             m_fPendingRepaint=TRUE;
  379.             SendMessage(ppg->m_hWnd, WM_HSCROLL
  380.                 , ppg->m_uHScrollCode, 0L);
  381.             }
  382.  
  383.         if (NOVALUE!=ppg->m_uVScrollCode)
  384.             {
  385.             m_fPendingRepaint=TRUE;
  386.             SendMessage(ppg->m_hWnd, WM_VSCROLL
  387.                 , ppg->m_uVScrollCode, 0L);
  388.             }
  389.         }
  390.  
  391.     //If we didn't scroll but have a pending repaint, do it now.
  392.     if (xPos==ppg->m_xPos && yPos==ppg->m_yPos && m_fPendingRepaint)
  393.         {
  394.         UpdateWindow(ppg->m_hWnd);
  395.         m_fPendingRepaint=FALSE;
  396.         }
  397.  
  398.     pt.x-=m_ptPick.x;
  399.     pt.y-=m_ptPick.y;
  400.  
  401.     m_ptLast=pt;
  402.     m_fFeedback=TRUE;
  403.     ppg->DrawDropTargetRect(&pt, &m_szl);
  404.  
  405.     return NOERROR;
  406.     }
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413. /*
  414.  * CDropTarget::DragLeave
  415.  *
  416.  * Purpose:
  417.  *  Informs the drop target that the operation has left its window.
  418.  *
  419.  * Parameters:
  420.  *  None
  421.  *
  422.  * Return Value:
  423.  *  HRESULT         NOERROR
  424.  */
  425.  
  426. STDMETHODIMP CDropTarget::DragLeave(void)
  427.     {
  428.     PCPages         ppg=m_pDoc->m_pPG;
  429.  
  430.     if (NULL==m_pIDataObject)
  431.         return NOERROR;
  432.  
  433.     //Stop scrolling
  434.     ppg->m_uHScrollCode=NOVALUE;
  435.     ppg->m_uVScrollCode=NOVALUE;
  436.  
  437.     if (m_fPendingRepaint)
  438.         UpdateWindow(ppg->m_hWnd);
  439.  
  440.     //Remove the last feedback rectangle.
  441.     if (m_fFeedback)
  442.         ppg->DrawDropTargetRect(&m_ptLast, &m_szl);
  443.  
  444.     m_fFeedback=FALSE;
  445.     m_pIDataObject->Release();
  446.     return NOERROR;
  447.     }
  448.  
  449.  
  450.  
  451.  
  452.  
  453. /*
  454.  * CDropTarget::Drop
  455.  *
  456.  * Purpose:
  457.  *  Instructs the drop target to paste the data that was just now
  458.  *  dropped on it.
  459.  *
  460.  * Parameters:
  461.  *  pIDataSource    LPDATAOBJECT from which we'll paste.
  462.  *  grfKeyState     DWORD providing current keyboard/mouse state.
  463.  *  pt              POINTL at which the drop occurred.
  464.  *  pdwEffect       LPDWORD in which to store what you did.
  465.  *
  466.  * Return Value:
  467.  *  HRESULT         NOERROR
  468.  */
  469.  
  470. STDMETHODIMP CDropTarget::Drop(LPDATAOBJECT pIDataSource
  471.     , DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
  472.     {
  473.     PCPages         ppg=m_pDoc->m_pPG;
  474.     BOOL            fRet=TRUE;
  475.     FORMATETC       fe;
  476.     TENANTTYPE      tType;
  477.     PATRONOBJECT    po;
  478.     POINT           ptS;
  479.  
  480.     *pdwEffect=DROPEFFECT_NONE;
  481.  
  482.     if (NULL==m_pIDataObject)
  483.         return ResultFromScode(E_FAIL);
  484.  
  485.     if (UDROP_NONE==ppg->UTestDroppablePoint(&pt))
  486.         return ResultFromScode(E_FAIL);
  487.  
  488.     //Stop scrolling
  489.     ppg->m_uHScrollCode=NOVALUE;
  490.     ppg->m_uVScrollCode=NOVALUE;
  491.  
  492.     if (m_fPendingRepaint)
  493.         UpdateWindow(ppg->m_hWnd);
  494.  
  495.     //2.  Remove the UI feedback
  496.     if (m_fFeedback)
  497.         ppg->DrawDropTargetRect(&m_ptLast, &m_szl);
  498.  
  499.     m_pIDataObject->Release();
  500.  
  501.  
  502.     /*
  503.      * Check if we can do the paste, and if so, tell our pasting
  504.      * mechanism exactly where to place us.
  505.      */
  506.     pt.x-=m_ptPick.x;
  507.     pt.y-=m_ptPick.y;
  508.  
  509.     POINTFROMPOINTL(ptS, pt);
  510.     ScreenToClient(ppg->Window(), &ptS);
  511.     POINTLFROMPOINT(po.ptl, ptS);
  512.  
  513.     //This is true if we didn't see placement data in DragEnter
  514.     if (0!=m_fe.cfFormat)
  515.         {
  516.         po.szl.cx=m_szl.cx;         //We stored these positive
  517.         po.szl.cy=-m_szl.cy;
  518.         }
  519.     else
  520.         SETSIZEL(po.szl, 0, 0); //Ask object for its size.
  521.  
  522.     //Adjust for scrolling and mapping mode.
  523.     ppg->AdjustPosition(&po.ptl, &po.szl);
  524.  
  525.  
  526.     /*
  527.      * If we're in the same document and moving, then we can just
  528.      * stuff the Pages' m_ptDrop which will move us and return.
  529.      */
  530.     if (ppg->m_fDragSource && !(grfKeyState & MK_CONTROL))
  531.         {
  532.         *pdwEffect=DROPEFFECT_MOVE;
  533.         ppg->m_fMoveInPage=TRUE;
  534.         ppg->m_ptDrop=po.ptl;
  535.         return NOERROR;
  536.         }
  537.  
  538.     /*
  539.      * Otherwise, paste either from another document or from
  540.      * the same document which will always be a copy to the new
  541.      * point.
  542.      */
  543.  
  544.     *pdwEffect=DROPEFFECT_MOVE;
  545.  
  546.     if (grfKeyState & MK_CONTROL)
  547.         {
  548.         if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
  549.             *pdwEffect=DROPEFFECT_LINK;
  550.         else
  551.             *pdwEffect=DROPEFFECT_COPY;
  552.         }
  553.  
  554.     ppg->m_fMoveInPage=FALSE;
  555.  
  556.     /*
  557.      * We know linking is desired if effect flag is set, so this
  558.      * function will get the FORMATETC for linking.  Otherwise
  559.      * FQueryPasteFromData will get the other FORMATETC to use.
  560.      */
  561.     if (DROPEFFECT_LINK==*pdwEffect)
  562.         {
  563.         fRet=m_pDoc->FQueryPasteLinkFromData(pIDataSource, &fe
  564.             , &tType);
  565.         }
  566.     else
  567.         fRet=m_pDoc->FQueryPasteFromData(pIDataSource, &fe, &tType);
  568.  
  569.     if (fRet)
  570.         {
  571.         //Copy the real format if we have placement data.
  572.         po.fe=(m_pDoc->m_cf==fe.cfFormat) ? m_fe : fe;
  573.  
  574.         //Flag PasteFromData to use CFSTR_OBJECTDESCRIPTOR
  575.         fRet=m_pDoc->PasteFromData(pIDataSource, &fe, tType
  576.             , &po, 0, TRUE);
  577.         }
  578.  
  579.     if (!fRet)
  580.         return ResultFromScode(E_FAIL);
  581.  
  582.  
  583.     *pdwEffect=DROPEFFECT_MOVE;
  584.  
  585.     if (grfKeyState & MK_CONTROL)
  586.         *pdwEffect=DROPEFFECT_COPY;
  587.  
  588.     return NOERROR;
  589.     }
  590.