home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / oledrop2.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  15KB  |  536 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_OLE4_SEG
  14. #pragma code_seg(AFX_OLE4_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // COleDropTarget implementation
  26.  
  27. AFX_DATADEF int COleDropTarget::nScrollInset;
  28. AFX_DATADEF UINT COleDropTarget::nScrollDelay;
  29. AFX_DATADEF UINT COleDropTarget::nScrollInterval;
  30.  
  31. COleDropTarget::COleDropTarget()
  32. {
  33.     // initialize local state
  34.     m_hWnd = NULL;
  35.     m_lpDataObject = NULL;
  36.     m_nTimerID = MAKEWORD(-1, -1);
  37.  
  38.     AfxLockGlobals(CRIT_DROPTARGET);
  39.     static BOOL bInitialized;
  40.     if (!bInitialized)
  41.     {
  42.         // get scroll metrics from win.ini
  43.         static const TCHAR szWindows[] = _T("windows");
  44.         static const TCHAR szScrollDelay[] = _T("DragScrollDelay");
  45.         static const TCHAR szScrollInset[] = _T("DragScrollInset");
  46.         static const TCHAR szScrollInterval[] = _T("DragScrollInterval");
  47.  
  48.         nScrollInset = GetProfileInt(szWindows, szScrollInset, DD_DEFSCROLLINSET);
  49.         nScrollDelay = GetProfileInt(szWindows, szScrollDelay, DD_DEFSCROLLDELAY);
  50.         nScrollInterval = GetProfileInt(szWindows, szScrollInterval,
  51.             DD_DEFSCROLLINTERVAL);
  52.  
  53.         // now initialized, no need to call Initialize any more
  54.         bInitialized = TRUE;
  55.     }
  56.     AfxUnlockGlobals(CRIT_DROPTARGET);
  57.  
  58.     ASSERT_VALID(this);
  59. }
  60.  
  61. COleDropTarget::~COleDropTarget()
  62. {
  63.     ASSERT_VALID(this);
  64.  
  65.     if (m_hWnd != NULL)
  66.     {
  67.         TRACE0("COleDropTarget::Revoke not called before destructor --\n");
  68.         TRACE0("\tmay cause RIPs under debug Windows.\n");
  69.         Revoke();
  70.     }
  71. }
  72.  
  73. BOOL COleDropTarget::Register(CWnd* pWnd)
  74. {
  75.     ASSERT_VALID(this);
  76.     ASSERT(m_hWnd == NULL);     // registering drop target twice?
  77.     ASSERT_VALID(pWnd);
  78.  
  79.     LPUNKNOWN lpUnknown = (LPUNKNOWN)GetInterface(&IID_IUnknown);
  80.     ASSERT(lpUnknown != NULL);
  81.  
  82.     // the object must be locked externally to keep LRPC connections alive
  83.     if (CoLockObjectExternal(lpUnknown, TRUE, FALSE) != S_OK)
  84.         return FALSE;
  85.  
  86.     // connect the HWND to the IDropTarget implementation
  87.     if (RegisterDragDrop(pWnd->m_hWnd,
  88.         (LPDROPTARGET)GetInterface(&IID_IDropTarget)) != S_OK)
  89.     {
  90.         CoLockObjectExternal(lpUnknown, FALSE, FALSE);
  91.         return FALSE;
  92.     }
  93.  
  94.     // connect internal data
  95.     m_hWnd = pWnd->m_hWnd;
  96.     ASSERT(pWnd->m_pDropTarget == NULL);
  97.     pWnd->m_pDropTarget = this;
  98.  
  99.     return TRUE;
  100. }
  101.  
  102. void COleDropTarget::Revoke()
  103. {
  104.     ASSERT_VALID(this);
  105.     ASSERT(m_lpDataObject == NULL);
  106.  
  107.     if (m_hWnd == NULL)
  108.     {
  109.         ASSERT(m_nTimerID == MAKEWORD(-1, -1));
  110.         return;
  111.     }
  112.  
  113.     // disconnect from OLE
  114.     RevokeDragDrop(m_hWnd);
  115.     CoLockObjectExternal((LPUNKNOWN)GetInterface(&IID_IUnknown), FALSE, TRUE);
  116.  
  117.     // disconnect internal data
  118.     CWnd::FromHandle(m_hWnd)->m_pDropTarget = NULL;
  119.     m_hWnd = NULL;
  120. }
  121.  
  122. /////////////////////////////////////////////////////////////////////////////
  123. // default implementation of drag/drop scrolling
  124.  
  125. DROPEFFECT COleDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState,
  126.     CPoint point)
  127. {
  128.     ASSERT_VALID(this);
  129.     ASSERT_VALID(pWnd);
  130.  
  131.     // CWnds are allowed, but don't support autoscrolling
  132.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  133.         return DROPEFFECT_NONE;
  134.     CView* pView = (CView*)pWnd;
  135.     DROPEFFECT dropEffect = pView->OnDragScroll(dwKeyState, point);
  136.  
  137.     // DROPEFFECT_SCROLL means do the default
  138.     if (dropEffect != DROPEFFECT_SCROLL)
  139.         return dropEffect;
  140.  
  141.     // get client rectangle of destination window
  142.     CRect rectClient;
  143.     pWnd->GetClientRect(&rectClient);
  144.     CRect rect = rectClient;
  145.  
  146.     // hit-test against inset region
  147.     UINT nTimerID = MAKEWORD(-1, -1);
  148.     rect.InflateRect(-nScrollInset, -nScrollInset);
  149.     CSplitterWnd* pSplitter = NULL;
  150.     if (rectClient.PtInRect(point) && !rect.PtInRect(point))
  151.     {
  152.         // determine which way to scroll along both X & Y axis
  153.         if (point.x < rect.left)
  154.             nTimerID = MAKEWORD(SB_LINEUP, HIBYTE(nTimerID));
  155.         else if (point.x >= rect.right)
  156.             nTimerID = MAKEWORD(SB_LINEDOWN, HIBYTE(nTimerID));
  157.         if (point.y < rect.top)
  158.             nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEUP);
  159.         else if (point.y >= rect.bottom)
  160.             nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEDOWN);
  161.         ASSERT(nTimerID != MAKEWORD(-1, -1));
  162.  
  163.         // check for valid scroll first
  164.         pSplitter = CView::GetParentSplitter(pView, FALSE);
  165.         BOOL bEnableScroll = FALSE;
  166.         if (pSplitter != NULL)
  167.             bEnableScroll = pSplitter->DoScroll(pView, nTimerID, FALSE);
  168.         else
  169.             bEnableScroll = pView->OnScroll(nTimerID, 0, FALSE);
  170.         if (!bEnableScroll)
  171.             nTimerID = MAKEWORD(-1, -1);
  172.     }
  173.  
  174.     if (nTimerID == MAKEWORD(-1, -1))
  175.     {
  176.         if (m_nTimerID != MAKEWORD(-1, -1))
  177.         {
  178.             // send fake OnDragEnter when transition from scroll->normal
  179.             COleDataObject dataObject;
  180.             dataObject.Attach(m_lpDataObject, FALSE);
  181.             OnDragEnter(pWnd, &dataObject, dwKeyState, point);
  182.             m_nTimerID = MAKEWORD(-1, -1);
  183.         }
  184.         return DROPEFFECT_NONE;
  185.     }
  186.  
  187.     // save tick count when timer ID changes
  188.     DWORD dwTick = GetTickCount();
  189.     if (nTimerID != m_nTimerID)
  190.     {
  191.         m_dwLastTick = dwTick;
  192.         m_nScrollDelay = nScrollDelay;
  193.     }
  194.  
  195.     // scroll if necessary
  196.     if (dwTick - m_dwLastTick > m_nScrollDelay)
  197.     {
  198.         if (pSplitter != NULL)
  199.             pSplitter->DoScroll(pView, nTimerID, TRUE);
  200.         else
  201.             pView->OnScroll(nTimerID, 0, TRUE);
  202.         m_dwLastTick = dwTick;
  203.         m_nScrollDelay = nScrollInterval;
  204.     }
  205.     if (m_nTimerID == MAKEWORD(-1, -1))
  206.     {
  207.         // send fake OnDragLeave when transitioning from normal->scroll
  208.         OnDragLeave(pWnd);
  209.     }
  210.  
  211.     m_nTimerID = nTimerID;
  212.     // check for force link
  213.     if ((dwKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT))
  214.         dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_LINK;
  215.     // check for force copy
  216.     else if ((dwKeyState & MK_CONTROL) == MK_CONTROL)
  217.         dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_COPY;
  218.     // check for force move
  219.     else if ((dwKeyState & MK_ALT) == MK_ALT ||
  220.         (dwKeyState & MK_SHIFT) == MK_SHIFT)
  221.         dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
  222.     // default -- recommended action is move
  223.     else
  224.         dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
  225.     return dropEffect;
  226. }
  227.  
  228. /////////////////////////////////////////////////////////////////////////////
  229. // COleDropTarget drop/ drop query handling
  230.  
  231. DROPEFFECT COleDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject,
  232.     DWORD dwKeyState, CPoint point)
  233. {
  234.     ASSERT_VALID(this);
  235.  
  236.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  237.         return DROPEFFECT_NONE;
  238.  
  239.     // default delegates to view
  240.     CView* pView = (CView*)pWnd;
  241.     ASSERT_VALID(pView);
  242.     return pView->OnDragEnter(pDataObject, dwKeyState, point);
  243. }
  244.  
  245. DROPEFFECT COleDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject,
  246.     DWORD dwKeyState, CPoint point)
  247. {
  248.     ASSERT_VALID(this);
  249.  
  250.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  251.         return DROPEFFECT_NONE;
  252.  
  253.     // default delegates to view
  254.     CView* pView = (CView*)pWnd;
  255.     ASSERT_VALID(pView);
  256.     return pView->OnDragOver(pDataObject, dwKeyState, point);
  257. }
  258.  
  259. BOOL COleDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
  260.     DROPEFFECT dropEffect, CPoint point)
  261. {
  262.     ASSERT_VALID(this);
  263.  
  264.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  265.         return DROPEFFECT_NONE;
  266.  
  267.     // default delegates to view
  268.     CView* pView = (CView*)pWnd;
  269.     ASSERT_VALID(pView);
  270.     return pView->OnDrop(pDataObject, dropEffect, point);
  271. }
  272.  
  273. DROPEFFECT COleDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject,
  274.     DROPEFFECT dropEffect, DROPEFFECT dropEffectList, CPoint point)
  275. {
  276.     ASSERT_VALID(this);
  277.  
  278.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  279.         return (DROPEFFECT)-1;  // not implemented
  280.  
  281.     // default delegates to view
  282.     CView* pView = (CView*)pWnd;
  283.     ASSERT_VALID(pView);
  284.     return pView->OnDropEx(pDataObject, dropEffect, dropEffectList, point);
  285. }
  286.  
  287. void COleDropTarget::OnDragLeave(CWnd* pWnd)
  288. {
  289.     ASSERT_VALID(this);
  290.  
  291.     if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  292.         return;
  293.  
  294.     // default delegates to view
  295.     CView* pView = (CView*)pWnd;
  296.     ASSERT_VALID(pView);
  297.     pView->OnDragLeave();
  298.     return;
  299. }
  300.  
  301. /////////////////////////////////////////////////////////////////////////////
  302. // COleDropTarget::COleDropTarget implementation
  303.  
  304. BEGIN_INTERFACE_MAP(COleDropTarget, CCmdTarget)
  305.     INTERFACE_PART(COleDropTarget, IID_IDropTarget, DropTarget)
  306. END_INTERFACE_MAP()
  307.  
  308. STDMETHODIMP_(ULONG) COleDropTarget::XDropTarget::AddRef()
  309. {
  310.     METHOD_PROLOGUE_EX_(COleDropTarget, DropTarget)
  311.     return pThis->ExternalAddRef();
  312. }
  313.  
  314. STDMETHODIMP_(ULONG) COleDropTarget::XDropTarget::Release()
  315. {
  316.     METHOD_PROLOGUE_EX_(COleDropTarget, DropTarget)
  317.     return pThis->ExternalRelease();
  318. }
  319.  
  320. STDMETHODIMP COleDropTarget::XDropTarget::QueryInterface(
  321.     REFIID iid, LPVOID* ppvObj)
  322. {
  323.     METHOD_PROLOGUE_EX_(COleDropTarget, DropTarget)
  324.     return pThis->ExternalQueryInterface(&iid, ppvObj);
  325. }
  326.  
  327. // helper to filter out invalid DROPEFFECTs
  328. AFX_STATIC DROPEFFECT AFXAPI
  329. _AfxFilterDropEffect(DROPEFFECT dropEffect, DROPEFFECT dwEffects)
  330. {
  331.     // return allowed dropEffect and DROPEFFECT_NONE
  332.     if ((dropEffect & dwEffects) != 0)
  333.         return dropEffect;
  334.  
  335.     // map common operations (copy/move) to alternates, but give negative
  336.     //  feedback for DROPEFFECT_LINK.
  337.     switch (dropEffect)
  338.     {
  339.     case DROPEFFECT_COPY:
  340.         if (dwEffects & DROPEFFECT_MOVE)
  341.             return DROPEFFECT_MOVE;
  342.         else if (dwEffects & DROPEFFECT_LINK)
  343.             return DROPEFFECT_LINK;
  344.         break;
  345.     case DROPEFFECT_MOVE:
  346.         if (dwEffects & DROPEFFECT_COPY)
  347.             return DROPEFFECT_COPY;
  348.         else if (dwEffects & DROPEFFECT_LINK)
  349.             return DROPEFFECT_LINK;
  350.         break;
  351.     case DROPEFFECT_LINK:
  352.         break;
  353.     }
  354.  
  355.     return DROPEFFECT_NONE;
  356. }
  357.  
  358. STDMETHODIMP COleDropTarget::XDropTarget::DragEnter(THIS_ LPDATAOBJECT lpDataObject,
  359.     DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
  360. {
  361.     METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
  362.     ASSERT_VALID(pThis);
  363.  
  364.     ASSERT(pdwEffect != NULL);
  365.     ASSERT(lpDataObject != NULL);
  366.  
  367.     SCODE sc = E_UNEXPECTED;
  368.     TRY
  369.     {
  370.         // cache lpDataObject
  371.         lpDataObject->AddRef();
  372.         RELEASE(pThis->m_lpDataObject);
  373.         pThis->m_lpDataObject = lpDataObject;
  374.  
  375.         CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
  376.         ASSERT_VALID(pWnd);
  377.         CPoint point((int)pt.x, (int)pt.y);
  378.         pWnd->ScreenToClient(&point);
  379.  
  380.         // check first for entering scroll area
  381.         DROPEFFECT dropEffect = pThis->OnDragScroll(pWnd, dwKeyState, point);
  382.         if ((dropEffect & DROPEFFECT_SCROLL) == 0)
  383.         {
  384.             // funnel through OnDragEnter since not in scroll region
  385.             COleDataObject dataObject;
  386.             dataObject.Attach(lpDataObject, FALSE);
  387.             dropEffect = pThis->OnDragEnter(pWnd, &dataObject, dwKeyState,
  388.                 point);
  389.         }
  390.         *pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
  391.         sc = S_OK;
  392.     }
  393.     END_TRY
  394.  
  395.     return sc;
  396. }
  397.  
  398. STDMETHODIMP COleDropTarget::XDropTarget::DragOver(THIS_ DWORD dwKeyState,
  399.     POINTL pt, LPDWORD pdwEffect)
  400. {
  401.     METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
  402.     ASSERT_VALID(pThis);
  403.  
  404.     ASSERT(pdwEffect != NULL);
  405.     ASSERT(pThis->m_lpDataObject != NULL);
  406.  
  407.     SCODE sc = E_UNEXPECTED;
  408.     TRY
  409.     {
  410.         CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
  411.         ASSERT_VALID(pWnd);
  412.         CPoint point((int)pt.x, (int)pt.y);
  413.         pWnd->ScreenToClient(&point);
  414.  
  415.         // check first for entering scroll area
  416.         DROPEFFECT dropEffect = pThis->OnDragScroll(pWnd, dwKeyState, point);
  417.         if ((dropEffect & DROPEFFECT_SCROLL) == 0)
  418.         {
  419.             // funnel through OnDragOver
  420.             COleDataObject dataObject;
  421.             dataObject.Attach(pThis->m_lpDataObject, FALSE);
  422.             dropEffect = pThis->OnDragOver(pWnd, &dataObject, dwKeyState,
  423.                 point);
  424.         }
  425.         *pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
  426.         sc = S_OK;
  427.     }
  428.     END_TRY
  429.  
  430.     return sc;
  431. }
  432.  
  433. STDMETHODIMP COleDropTarget::XDropTarget::DragLeave(THIS)
  434. {
  435.     METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
  436.     ASSERT_VALID(pThis);
  437.  
  438.     CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
  439.     ASSERT_VALID(pWnd);
  440.  
  441.     // cancel drag scrolling
  442.     pThis->m_nTimerID = MAKEWORD(-1, -1);
  443.  
  444.     // allow derivative to do own cleanup
  445.     COleDataObject dataObject;
  446.     dataObject.Attach(pThis->m_lpDataObject, FALSE);
  447.     pThis->OnDragLeave(pWnd);
  448.  
  449.     // release cached data object
  450.     RELEASE(pThis->m_lpDataObject);
  451.  
  452.     return S_OK;
  453. }
  454.  
  455. STDMETHODIMP COleDropTarget::XDropTarget::Drop(THIS_ LPDATAOBJECT lpDataObject,
  456.     DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
  457. {
  458.     METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
  459.     ASSERT_VALID(pThis);
  460.  
  461.     ASSERT(pdwEffect != NULL);
  462.     ASSERT(lpDataObject != NULL);
  463.  
  464.     SCODE sc = E_UNEXPECTED;
  465.     TRY
  466.     {
  467.         // cancel drag scrolling
  468.         pThis->m_nTimerID = MAKEWORD(-1, -1);
  469.  
  470.         // prepare for call to OnDragOver
  471.         CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
  472.         ASSERT_VALID(pWnd);
  473.         COleDataObject dataObject;
  474.         dataObject.Attach(lpDataObject, FALSE);
  475.         CPoint point((int)pt.x, (int)pt.y);
  476.         pWnd->ScreenToClient(&point);
  477.  
  478.         // verify that drop is legal
  479.         DROPEFFECT dropEffect = _AfxFilterDropEffect(pThis->OnDragOver(pWnd,
  480.             &dataObject, dwKeyState, point), *pdwEffect);
  481.  
  482.         // execute the drop (try OnDropEx then OnDrop for backward compatibility)
  483.         DROPEFFECT temp = pThis->OnDropEx(pWnd, &dataObject, dropEffect, *pdwEffect, point);
  484.         if (temp != -1)
  485.         {
  486.             // OnDropEx was implemented, return its drop effect
  487.             dropEffect = temp;
  488.         }
  489.         else if (dropEffect != DROPEFFECT_NONE)
  490.         {
  491.             // OnDropEx not implemented
  492.             if (!pThis->OnDrop(pWnd, &dataObject, dropEffect, point))
  493.                 dropEffect = DROPEFFECT_NONE;
  494.         }
  495.         else
  496.         {
  497.             // drop not accepted, allow cleanup
  498.             pThis->OnDragLeave(pWnd);
  499.         }
  500.  
  501.         // release potentially cached data object
  502.         RELEASE(pThis->m_lpDataObject);
  503.         *pdwEffect = dropEffect;
  504.         sc = S_OK;
  505.     }
  506.     END_TRY
  507.  
  508.     return sc;
  509. }
  510.  
  511. /////////////////////////////////////////////////////////////////////////////
  512. // COleDropTarget diagnostics
  513.  
  514. #ifdef _DEBUG
  515. void COleDropTarget::AssertValid() const
  516. {
  517.     CCmdTarget::AssertValid();
  518.     if (m_lpDataObject != NULL)
  519.         CWnd::FromHandle(m_hWnd)->AssertValid();
  520. }
  521.  
  522. void COleDropTarget::Dump(CDumpContext& dc) const
  523. {
  524.     CCmdTarget::Dump(dc);
  525.  
  526.     dc << "m_hWnd = " << m_hWnd;
  527.     dc << "\nm_lpDataObject = " << m_lpDataObject;
  528.     dc << "\nm_nTimerID = " << m_nTimerID;
  529.     dc << "\nm_dwLastTick = " << m_dwLastTick;
  530.  
  531.     dc << "\n";
  532. }
  533. #endif
  534.  
  535. /////////////////////////////////////////////////////////////////////////////
  536.