|
|
//____________________________________________________________________________
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: dd.cpp
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 8/3/1997 RaviR Created
//____________________________________________________________________________
//
#include "stdafx.h"
#include "AMCDoc.h" // AMC Console Document
#include "amcview.h"
#include "TreeCtrl.h"
#include "cclvctl.h"
#include "amcpriv.h"
#include "mainfrm.h"
#include "rsltitem.h"
#include "conview.h"
/***************************************************************************\
* * CLASS: CMMCDropSource * * PURPOSE: Implements everything required for drop source: * a) com object to register with OLE * b) static method to perform drag&drop operation * * USAGE: All you need is to invoke CMMCDropSource::ScDoDragDrop static method * \***************************************************************************/ class CMMCDropSource : public IDropSource, public CComObjectRoot { public:
BEGIN_COM_MAP(CMMCDropSource) COM_INTERFACE_ENTRY(IDropSource) END_COM_MAP()
// IDropSource methods
STDMETHOD(QueryContinueDrag)( BOOL fEscapePressed, DWORD grfKeyState ); STDMETHOD(GiveFeedback)( DWORD dwEffect );
// method to perform D&D
static SC ScDoDragDrop(IDataObject *pDataObject, bool bCopyAllowed, bool bMoveAllowed); };
/***************************************************************************\
* * CLASS: CMMCDropTarget * * PURPOSE: Implements com object to be osed by OLE for drop target operations * * USAGE: Used by CMMCViewDropTarget, which creates and registers it with OLE * to be invoked on OLE D&D opeartions on the window (target) * \***************************************************************************/ class CMMCDropTarget : public CTiedComObject<CMMCViewDropTarget>, public IDropTarget, public CComObjectRoot { public: typedef CMMCViewDropTarget MyTiedObject;
BEGIN_COM_MAP(CMMCDropTarget) COM_INTERFACE_ENTRY(IDropTarget) END_COM_MAP()
// IDropTarget methods
STDMETHOD(DragEnter)( IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ); STDMETHOD(DragOver)( DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ); STDMETHOD(DragLeave)(void); STDMETHOD(Drop)( IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ); private:
// implementation helpers
SC ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, POINTL pt, bool& bCopyOperation); SC ScRemoveDropTargetHiliting(); static SC ScAddMenuString(CMenu& menu, DWORD id, UINT idString); SC ScDisplayDropMenu(POINTL pt, DWORD dwEffectsAvailable, DWORD& dwSelected); DWORD CalculateEffect(DWORD dwEffectsAvailable, DWORD grfKeyState, bool bCopyPreferred);
private: IDataObjectPtr m_spDataObject; // cached data object
bool m_bRightDrag; // operation is right click drag
bool m_bCopyByDefault; // if default operation is copy (not move)
};
//////////////////////////////////////////////////////////////////////////////
////////// CAMCTreeView methods for supporting d&d ///////////////////////////
//////////////////////////////////////////////////////////////////////////////
/***************************************************************************\
* * METHOD: CAMCTreeView::ScDropOnTarget * * PURPOSE: called to hittest or perform drop operation * * PARAMETERS: * bool bHitTestOnly [in] - HitTest / drop * IDataObject * pDataObject [in] - data object to copy/move * CPoint point [in] - current cursor position * bool& bCopyOperation [in/out] * [in] - operation to perform (HitTest == false) * [out] - default op. (HitTest == true) * * RETURNS: * SC - result code * \***************************************************************************/ SC CAMCTreeView::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, CPoint point, bool& bCopyOperation) { DECLARE_SC(sc, TEXT("CAMCTreeView::ScDropOnTarget"));
// 1. see where it falls
CTreeCtrl& ctc = GetTreeCtrl(); UINT flags; HTREEITEM htiDrop = ctc.HitTest(point, &flags);
if (flags & TVHT_NOWHERE) return sc = S_FALSE; // not an error, but no paste;
// 2. if we missed the tree item...
if (!htiDrop) { // really mad if it was a paste
if (! bHitTestOnly) MessageBeep(0);
return sc = S_FALSE; // not an error, but no paste;
}
// 3. get the target node
HNODE hNode = (HNODE) ctc.GetItemData(htiDrop);
INodeCallback* pNC = GetNodeCallback(); sc = ScCheckPointers(pNC, E_UNEXPECTED); if (sc) return sc;
// 4. ask what snapin thinks about this paste
bool bGetDataObjectFromClipboard = false; bool bPasteAllowed = false; bool bIsCopyDefaultOperation = false; sc = pNC->QueryPaste(hNode, /*bScope*/ true, /*LVDATA*/ NULL, pDataObject, bPasteAllowed, bIsCopyDefaultOperation); if (sc) return sc;
if (!bPasteAllowed) return sc = S_FALSE; // not an error, but no paste;
// 5. visual effect
ctc.SelectDropTarget(htiDrop);
// 6. OK so far. If it was a test - we passed
if (bHitTestOnly) { bCopyOperation = bIsCopyDefaultOperation; return sc; }
// 7. do paste NOW
sc = pNC->Drop(hNode, /*bScope*/TRUE, /*LVDATA*/NULL, pDataObject, !bCopyOperation); if (sc) return sc;
return sc; }
/***************************************************************************\
* * METHOD: CAMCTreeView::RemoveDropTargetHiliting * * PURPOSE: called to remove hiliting put for drop target * * PARAMETERS: * * RETURNS: * void * \***************************************************************************/ void CAMCTreeView::RemoveDropTargetHiliting() { CTreeCtrl& ctc = GetTreeCtrl(); ctc.SelectDropTarget(NULL); }
/***************************************************************************\
* * METHOD: CAMCTreeView::OnBeginRDrag * * PURPOSE: called when the drag operation is initiated with right mouse button * * PARAMETERS: * NMHDR* pNMHDR * LRESULT* pResult * * RETURNS: * void * \***************************************************************************/ void CAMCTreeView::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult) { OnBeginDrag(pNMHDR, pResult); }
/***************************************************************************\
* * METHOD: CAMCTreeView::OnBeginDrag * * PURPOSE: called when the drag operation is initiated with * * PARAMETERS: * NMHDR* pNMHDR * LRESULT* pResult * * RETURNS: * SC - result code * \***************************************************************************/ void CAMCTreeView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) { DECLARE_SC(sc, TEXT("CAMCTreeView::OnBeginDrag"));
// 1. parameter check
sc = ScCheckPointers( pNMHDR, pResult ); if (sc) return;
*pResult = 0;
// 2. get node calback
CTreeCtrl& ctc = GetTreeCtrl(); NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; HNODE hNode = (HNODE) ctc.GetItemData(pNMTreeView->itemNew.hItem);
INodeCallback* pNC = GetNodeCallback(); sc = ScCheckPointers( pNC, E_UNEXPECTED ); if (sc) return;
// 3. get data object
IDataObjectPtr spDO; bool bCopyAllowed = false; bool bMoveAllowed = false; sc = pNC->GetDragDropDataObject(hNode, TRUE, 0, 0, &spDO, bCopyAllowed, bMoveAllowed); if ( sc != S_OK || spDO == NULL) return;
// 4. start d&d
sc = CMMCDropSource::ScDoDragDrop(spDO, bCopyAllowed, bMoveAllowed); if (sc) return; }
//////////////////////////////////////////////////////////////////////////////
////////// CAMCListView methods for supporting d&d ///////////////////////////
//////////////////////////////////////////////////////////////////////////////
// helpers
INodeCallback* CAMCListView::GetNodeCallback() { ASSERT (m_pAMCView != NULL); return (m_pAMCView->GetNodeCallback()); }
HNODE CAMCListView::GetScopePaneSelNode() { ASSERT (m_pAMCView != NULL); return (m_pAMCView->GetSelectedNode()); }
/***************************************************************************\
* * METHOD: CAMCListView::ScDropOnTarget * * PURPOSE: called to hittest or perform drop operation * * PARAMETERS: * bool bHitTestOnly [in] - HitTest / drop * IDataObject * pDataObject [in] - data object to copy/move * CPoint point [in] - current cursor position * bool& bCopyOperation [in/out] * [in] - operation to perform (HitTest == false) * [out] - default op. (HitTest == true) * * RETURNS: * SC - result code * \***************************************************************************/ SC CAMCListView::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, CPoint point, bool& bCopyOperation) { DECLARE_SC(sc, TEXT("CAMCListView::ScDropOnTarget"));
// 1. sanity check
sc = ScCheckPointers( m_pAMCView , E_UNEXPECTED ); if (sc) return sc;
// 2. see if view does not have a paste tabu (listpads do)
if (!m_pAMCView->CanDoDragDrop()) return sc = (bHitTestOnly ? S_FALSE : E_FAIL); // not an error if testing
// 3. HitTest the target
HNODE hNode = NULL; bool bScope = false; LPARAM lvData = NULL; int iDrop = -1;
sc = ScGetDropTarget(point, hNode, bScope, lvData, iDrop); if (sc.IsError() || (sc == SC(S_FALSE))) return sc;
// 4. get the callback
INodeCallback* pNC = GetNodeCallback(); sc = ScCheckPointers(pNC, E_UNEXPECTED); if (sc) return sc;
// 5. ask what snapin thinks about this paste
const bool bGetDataObjectFromClipboard = false; bool bPasteAllowed = false; bool bIsCopyDefaultOperation = false; sc = pNC->QueryPaste(hNode, bScope, lvData, pDataObject, bPasteAllowed, bIsCopyDefaultOperation); if (sc) return sc;
if (!bPasteAllowed) return sc = S_FALSE; // not an error, but no paste;
// 6. visual effect
SelectDropTarget(iDrop);
// 7. OK so far. If it was a test - we passed
if (bHitTestOnly) { bCopyOperation = bIsCopyDefaultOperation; return sc; }
// 8. do paste NOW
sc = pNC->Drop(hNode, bScope, lvData, pDataObject, !bCopyOperation); if (sc) return sc;
return sc; }
/***************************************************************************\
* * METHOD: CAMCListView::RemoveDropTargetHiliting * * PURPOSE: called to remove target hiliting * * PARAMETERS: * * RETURNS: * void * \***************************************************************************/ void CAMCListView::RemoveDropTargetHiliting() { SelectDropTarget(-1); }
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScGetDropTarget
//
// Synopsis: Get the drop target item (result item or scope item).
//
// Arguments: [point] - where the drop is done.
// [hNode] - Owner node of result pane.
// [bScope] - scope or result selected.
// [lvData] - If result the LPARAM of result item.
// [iDrop] - index of the lv item that is drop target.
//
// Returns: SC, S_FALSE means no drop target item.
//
//--------------------------------------------------------------------
SC CAMCListView::ScGetDropTarget(const CPoint& point, HNODE& hNode, bool& bScope, LPARAM& lvData, int& iDrop) { DECLARE_SC(sc, _T("CAMCListView::ScGetDropTarget"));
hNode = NULL; bScope = false; lvData = NULL; iDrop = -1;
CListCtrl& lc = GetListCtrl(); UINT flags; iDrop = lc.HitTest(point, &flags);
// background is drop target.
if (iDrop < 0) { hNode = GetScopePaneSelNode(); bScope = true; return sc; }
// Need to change this to LVIS_DROPHILITED.
if (lc.GetItemState(iDrop, LVIS_SELECTED) & LVIS_SELECTED) { HWND hWnd = ::GetForegroundWindow(); if (hWnd && (hWnd == m_hWnd)) return (sc = S_FALSE); }
/*
* virtual list? lvData is the item index, hNode is the item selected * in the scope pane */ if (m_bVirtual) { hNode = GetScopePaneSelNode(); lvData = iDrop; bScope = false; } else { LPARAM lParam = lc.GetItemData(iDrop); ASSERT (lParam != 0); if (lParam == 0) return (sc = S_FALSE);
CResultItem* pri = CResultItem::FromHandle(lParam);
if (pri == NULL) return (sc = S_FALSE);
if (pri->IsScopeItem()) { hNode = pri->GetScopeNode(); bScope = true; } else { hNode = GetScopePaneSelNode(); lvData = lParam; } }
sc = ScCheckPointers(hNode, E_UNEXPECTED); if (sc) return sc;
return (sc); }
/***************************************************************************\
* * METHOD: CAMCListView::OnBeginRDrag * * PURPOSE: called when the drag operation is initiated with right mouse button * * PARAMETERS: * NMHDR* pNMHDR * LRESULT* pResult * * RETURNS: * void * \***************************************************************************/ void CAMCListView::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult) { OnBeginDrag(pNMHDR, pResult); }
/***************************************************************************\
* * METHOD: CAMCListView::OnBeginDrag * * PURPOSE: called when the drag operation is initiated * * PARAMETERS: * NMHDR* pNMHDR * LRESULT* pResult * * RETURNS: * void * \***************************************************************************/ void CAMCListView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) { DECLARE_SC(sc, TEXT("CAMCListView::OnBeginDrag"));
// 1. parameter check
sc = ScCheckPointers( pNMHDR, pResult ); if (sc) return;
*pResult = 0;
// 2. sanity check
sc = ScCheckPointers( m_pAMCView, E_UNEXPECTED ); if (sc) return;
// 3. see if view does not have a paste tabu (listpads do)
if (!m_pAMCView->CanDoDragDrop()) return;
// 4. get selected items
CListCtrl& lc = GetListCtrl(); UINT cSel = lc.GetSelectedCount(); if (cSel <= 0) { sc = E_UNEXPECTED; return; }
// 5. get node callback
HNODE hNode = GetScopePaneSelNode(); long iSel = lc.GetNextItem(-1, LVIS_SELECTED); LONG_PTR lvData = m_bVirtual ? iSel : lc.GetItemData(iSel);
INodeCallback* pNC = GetNodeCallback(); sc = ScCheckPointers( pNC, E_UNEXPECTED ); if (sc) return;
// 6. retrieve data object
IDataObjectPtr spDO; bool bCopyAllowed = false; bool bMoveAllowed = false; sc = pNC->GetDragDropDataObject(hNode, FALSE, (cSel > 1), lvData, &spDO, bCopyAllowed, bMoveAllowed); if ( sc != S_OK || spDO == NULL) { /*
* Problem: * If snapin does not return dataobject then drag & drop is not possible. * Assume focus in on tree, user downclick's and drags a result item * At this time common control sends LVN_ITEMCHANGED to mmc. MMC translates * this to MMCN_SELECT and tells snapin that result item is selected. * Now when user releases the mouse the tree item still has focus & selection, * but the snapin thinks result item is selected (also verbs correspond to * result item). * * Solution: * So we will set focus to the result pane. */ sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_Results); return; }
// 7. do d&d
sc = CMMCDropSource::ScDoDragDrop(spDO, bCopyAllowed, bMoveAllowed); if (sc) return;
/*
* Problem: * If a result item is dropped into another result item or another * scope item in list-view then focus disappears from that item. * But there should be always an item selected after any de-select. * We cannot change the focus to result pane because if focus is already * in result pane, this change focus does nothing and no item is selected. * So we change the focus to scope pane. For this we first change the focus * to result pane and then to scope pane. Because if tree already has focus, * setting focus to tree does nothing (CAMCView::ScOnTreeViewActivated). * * Solution: * So we change the focus to result pane and then to scope pane. */ sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_Results); if (sc) return;
sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_ScopeTree); if (sc) return;
return; }
//////////////////////////////////////////////////////////////////////////////
///////////////////// CMMCViewDropTarget methods /////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/***************************************************************************\
* * METHOD: CMMCViewDropTarget::CMMCViewDropTarget * * PURPOSE: constructor * * PARAMETERS: * \***************************************************************************/ CMMCViewDropTarget::CMMCViewDropTarget() : m_hwndOwner(0) { }
/***************************************************************************\
* * METHOD: CMMCViewDropTarget::~CMMCViewDropTarget * * PURPOSE: destructor. Revokes drop target for derived view class * \***************************************************************************/ CMMCViewDropTarget::~CMMCViewDropTarget() { DECLARE_SC(sc, TEXT("CViewDropTarget::~CViewDropTarget"));
if (m_hwndOwner != NULL) sc = RevokeDragDrop(m_hwndOwner); }
/***************************************************************************\
* * METHOD: CMMCViewDropTarget::ScRegisterAsDropTarget * * PURPOSE: This method is called by the derived class after the view window * is created. Method registers the drop target for the window. * * PARAMETERS: * HWND hWnd [in] - drop target view handle * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCViewDropTarget::ScRegisterAsDropTarget(HWND hWnd) { DECLARE_SC(sc, TEXT("CMMCViewDropTarget::ScRegister"));
// 1. parameter check
if (hWnd == NULL) return sc = E_INVALIDARG;
// 2. sanity check - should not come here twice
if (m_spTarget != NULL) return sc = E_UNEXPECTED;
// 3. create a drop target com object
IDropTargetPtr spTarget; sc = ScCreateTarget(&spTarget); if (sc) return sc;
// 4. recheck
sc = ScCheckPointers(spTarget, E_UNEXPECTED); if (sc) return sc;
// 5. register with OLE
sc = RegisterDragDrop(hWnd, spTarget); if (sc) return sc;
// 6. store info into members
m_spTarget.Attach( spTarget.Detach() ); m_hwndOwner = hWnd;
return sc; }
/***************************************************************************\
* * METHOD: CMMCViewDropTarget::ScCreateTarget * * PURPOSE: helper. creates tied com object to regiter with OLE * * PARAMETERS: * IDropTarget **ppTarget [out] tied com object * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCViewDropTarget::ScCreateTarget(IDropTarget **ppTarget) { DECLARE_SC(sc, TEXT("CMMCViewDropTarget::ScCreateTarget"));
// 1. check parameters
sc = ScCheckPointers(ppTarget); if (sc) return sc;
// 2. init out parameter
*ppTarget = NULL;
// 3. create com object to register as target
IDropTargetPtr spDropTarget; sc = CTiedComObjectCreator<CMMCDropTarget>::ScCreateAndConnect(*this, spDropTarget); if (sc) return sc;
sc = ScCheckPointers(spDropTarget, E_UNEXPECTED); if (sc) return sc;
// 4. pass reference to client
*ppTarget = spDropTarget.Detach();
return sc; }
/*---------------------------------------------------------------------------*\
| class CMMCDropSource methods | \*---------------------------------------------------------------------------*/
/***************************************************************************\
* * METHOD: CMMCDropSource::QueryContinueDrag * * PURPOSE: implements IDropSource::QueryContinueDrag interface used bu OLE * * PARAMETERS: * BOOL fEscapePressed [in] - ESC was pressed * DWORD grfKeyState [in] - mouse & control button state * * RETURNS: * HRESULT - error or S_OK(continue), DRAGDROP_S_CANCEL(cancel), DRAGDROP_S_DROP(drop) * \***************************************************************************/ STDMETHODIMP CMMCDropSource::QueryContinueDrag( BOOL fEscapePressed, DWORD grfKeyState ) { DECLARE_SC(sc, TEXT("CMMCDropSource::QueryContinueDrag"));
// 1. quit on cancel
if (fEscapePressed) return (sc = DRAGDROP_S_CANCEL).ToHr();
// 2. inspect mouse buttons
DWORD mButtons = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON) ); if ( mButtons == 0 ) // all released?
return (sc = DRAGDROP_S_DROP).ToHr();
// 3. quit also if more than one mouse button is pressed
if ( mButtons != MK_LBUTTON && mButtons != MK_RBUTTON && mButtons != MK_MBUTTON ) return (sc = DRAGDROP_S_CANCEL).ToHr();
// 4. else just continue ...
return sc.ToHr(); }
/***************************************************************************\
* * METHOD: CMMCDropSource::GiveFeedback * * PURPOSE: Gives feedback for d&d operations * * PARAMETERS: * DWORD dwEffect * * RETURNS: * DRAGDROP_S_USEDEFAULTCURSORS * \***************************************************************************/ STDMETHODIMP CMMCDropSource::GiveFeedback( DWORD dwEffect ) { // nothing special by now
return DRAGDROP_S_USEDEFAULTCURSORS; }
/***************************************************************************\
* * METHOD: CMMCDropSource::ScDoDragDrop * * PURPOSE: Performs DragAndDrop operation * This is static method to be used to initiate drag and drop * * PARAMETERS: * IDataObject *pDataObject [in] data object to copy/move * bool bCopyAllowed [in] if copy is allowed * bool bMoveAllowed [in] if move is allowed * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCDropSource::ScDoDragDrop(IDataObject *pDataObject, bool bCopyAllowed, bool bMoveAllowed) { DECLARE_SC(sc, TEXT("CMMCDropSource::ScDoDragDrop"));
// 1. cocreate com object for OLE
typedef CComObject<CMMCDropSource> ComCMMCDropSource; ComCMMCDropSource *pSource; sc = ComCMMCDropSource::CreateInstance(&pSource); if (sc) return sc;
// 2. recheck
sc = ScCheckPointers(pSource, E_UNEXPECTED); if (sc) return sc;
// 3. QI for IDropSource interface
IDropSourcePtr spDropSource = pSource; sc = ScCheckPointers(spDropSource, E_UNEXPECTED); if (sc) { delete pSource; return sc; }
// 4. perform DragDrop
DWORD dwEffect = DROPEFFECT_NONE; const DWORD dwEffectAvailable = (bCopyAllowed ? DROPEFFECT_COPY : 0) |(bMoveAllowed ? DROPEFFECT_MOVE : 0); sc = DoDragDrop(pDataObject, spDropSource, dwEffectAvailable, &dwEffect); if (sc) return sc;
return sc; }
/*---------------------------------------------------------------------------*\
| class CMMCDropTarget methods | \*---------------------------------------------------------------------------*/
/***************************************************************************\
* * METHOD: CMMCDropTarget::DragEnter * * PURPOSE: Invoked by OLE when d&d cursor enters the window for which * this target was registered. * * PARAMETERS: * IDataObject * pDataObject [in] - data object to copy/move * DWORD grfKeyState [in] - current key state * POINTL pt [in] - current cursor position * DWORD * pdwEffect [out] - operations supported * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCDropTarget::DragEnter( IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ) { DECLARE_SC(sc, TEXT("CMMCDropTarget::DragEnter"));
// 1. cache for drag over
m_spDataObject = pDataObject;
// 2. parameter check
sc = ScCheckPointers(pDataObject, pdwEffect); if (sc) return sc.ToHr();
// 3. let it happen - will do more exact filtering on DragOver
*pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY;
return sc.ToHr(); }
/***************************************************************************\
* * METHOD: CMMCDropTarget::DragOver * * PURPOSE: called continuosly while cursor is drgged over the window * * PARAMETERS: * DWORD grfKeyState [in] - current key state * POINTL pt [in] - current cursor position * DWORD * pdwEffect [out] - operations supported * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCDropTarget::DragOver( DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ) { DECLARE_SC(sc, TEXT("CMMCDropTarget::DragOver"));
// 1. parameter check
sc = ScCheckPointers(pdwEffect); if (sc) return sc.ToHr();
// 2. sanity check
sc = ScCheckPointers(m_spDataObject, E_UNEXPECTED); if (sc) return sc.ToHr();
bool bCopyByDefault = false; // initially we are to move
// 3. ask the view to estimate what can be done in this position
sc = ScDropOnTarget( true /*bHitTestOnly*/, m_spDataObject, pt, bCopyByDefault ); if ( sc == S_OK ) *pdwEffect = CalculateEffect( *pdwEffect, grfKeyState, bCopyByDefault ); else *pdwEffect = DROPEFFECT_NONE; // no-op on failure or S_FALSE
return sc.ToHr(); }
/***************************************************************************\
* * METHOD: CMMCDropTarget::DragLeave * * PURPOSE: called when cursor leave the window * * PARAMETERS: * void * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCDropTarget::DragLeave(void) { DECLARE_SC(sc, TEXT("DragLeave"));
// 1. release data object
m_spDataObject = NULL;
// 2. ask the view to remove hiliting it put on target
sc = ScRemoveDropTargetHiliting(); if (sc) sc.TraceAndClear();
return S_OK; }
/***************************************************************************\
* * METHOD: CMMCDropTarget::Drop * * PURPOSE: Called when data is dropped on target * * PARAMETERS: * IDataObject * pDataObject [in] - data object to copy/move * DWORD grfKeyState [in] - current key state * POINTL pt [in] - current cursor position * DWORD * pdwEffect [out] - operation performed * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCDropTarget::Drop( IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect ) { DECLARE_SC(sc, TEXT("CMMCDropTarget::DragEnter"));
// 1. release in case we have anything
m_spDataObject = NULL;
// 2. parameter check
sc = ScCheckPointers(pDataObject, pdwEffect); if (sc) return sc.ToHr();
// 3. init operation with cached value
bool bCopyOperation = m_bCopyByDefault;
// 4. see what operation to perform
if (m_bRightDrag) { // 4.1. give user the choice
DWORD dwSelected = ( m_bCopyByDefault ? DROPEFFECT_COPY : DROPEFFECT_MOVE ); sc = ScDisplayDropMenu( pt, *pdwEffect, dwSelected ); if (sc) return sc.ToHr();
*pdwEffect = dwSelected; } else { // 4.2. inspect keyboard
*pdwEffect = CalculateEffect(*pdwEffect, grfKeyState, bCopyOperation); }
// 5. perform
if (*pdwEffect != DROPEFFECT_NONE) // not canceled yet?
{ // now the final decision - copy or move
bCopyOperation = ( *pdwEffect & DROPEFFECT_COPY );
// Let it happen. Drop.
sc = ScDropOnTarget( false /*bHitTestOnly*/, pDataObject, pt, bCopyOperation ); if ( sc != S_OK ) *pdwEffect = DROPEFFECT_NONE; }
// 6. remove hiliting. reuse DragLeave (don't care about the results)
DragLeave();
return sc.ToHr(); }
/***************************************************************************\
* * METHOD: CMMCDropTarget::ScDropOnTarget * * PURPOSE: helper, forwarding calls to the view * Called as a request to hittest / perform drop operation * * PARAMETERS: * bool bHitTestOnly [in] - HitTest / drop * IDataObject * pDataObject [in] - data object to copy/move * POINTL pt [in] - current cursor position * bool& bCopyOperation [in/out] * [in] - operation to perform (HitTest == false) * [out] - default op. (HitTest == true) * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCDropTarget::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, POINTL pt, bool& bCopyOperation) { DECLARE_SC(sc, TEXT("CMMCDropTarget::ScDropOnTarget"));
// 1. get tied object - view
CMMCViewDropTarget *pTarget = NULL; sc = ScGetTiedObject(pTarget); if (sc) return sc;
// 2. recheck
sc = ScCheckPointers(pTarget, E_UNEXPECTED); if (sc) return sc;
// 3. calculate client coordinates
CPoint point(pt.x, pt.y); ScreenToClient(pTarget->GetWindowHandle(), &point);
// 4. forward to the view
sc = pTarget->ScDropOnTarget( bHitTestOnly, pDataObject, point, bCopyOperation ); if ( sc != S_OK ) ScRemoveDropTargetHiliting(); // remove hiliting if missed the traget
if (sc) return sc;
return sc; }
/***************************************************************************\
* * METHOD: CMMCDropTarget::ScRemoveDropTargetHiliting * * PURPOSE: helper, forwarding calls to the view * Called to cancel visual effects on target * * PARAMETERS: * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCDropTarget::ScRemoveDropTargetHiliting() { DECLARE_SC(sc, TEXT("CMMCDropTarget::ScRemoveDropTargetHiliting"));
// `. get tied object - view
CMMCViewDropTarget *pTarget = NULL; sc = ScGetTiedObject(pTarget); if (sc) return sc;
sc = ScCheckPointers(pTarget, E_UNEXPECTED); if (sc) return sc;
// 2. forward to the view
pTarget->RemoveDropTargetHiliting();
return sc; }
/***************************************************************************\
* * METHOD: CMMCDropTarget::ScAddMenuString * * PURPOSE: Helper. Adds resource string to paste context menu * * PARAMETERS: * CMenu& menu [in] - menu to modify * DWORD id [in] - menu command * UINT idString [in] - id of resource string * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCDropTarget::ScAddMenuString(CMenu& menu, DWORD id, UINT idString) { DECLARE_SC(sc, TEXT("CMMCDropTarget::ScAddMenuString"));
// 1. load the string
CString strItem; bool bOK = LoadString( strItem, idString ); if ( !bOK ) return sc = E_FAIL;
// 2. add to the menu
bOK = menu.AppendMenu( MF_STRING, id, strItem ); if ( !bOK ) return sc = E_FAIL;
return sc; }
/***************************************************************************\
* * METHOD: CMMCDropTarget::ScDisplayDropMenu * * PURPOSE: Helper. Displays paste context menu * * PARAMETERS: * POINTL pt [in] - point where the menu should appear * DWORD dwEffectsAvailable [in] - available commands * DWORD& dwSelected [in] - selected command * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCDropTarget::ScDisplayDropMenu(POINTL pt, DWORD dwEffectsAvailable, DWORD& dwSelected) { DECLARE_SC(sc, TEXT("CMMCDropTarget::ScDisplayDropMenu"));
CMenu menu;
// 0. create the menu
bool bOK = menu.CreatePopupMenu(); if ( !bOK ) return sc = E_FAIL;
// 1. add choice for copy
if ( dwEffectsAvailable & DROPEFFECT_COPY ) { sc = ScAddMenuString(menu, DROPEFFECT_COPY, IDS_DragDrop_CopyHere); if (sc) return sc; }
// 2. add choice for move
if ( dwEffectsAvailable & DROPEFFECT_MOVE ) { sc = ScAddMenuString(menu, DROPEFFECT_MOVE, IDS_DragDrop_MoveHere); if (sc) return sc; }
// 3. add separator if copy or paste was added
if ( dwEffectsAvailable & ( DROPEFFECT_COPY | DROPEFFECT_MOVE ) ) { bool bOK = menu.AppendMenu( MF_SEPARATOR ); if ( !bOK ) return sc = E_FAIL; }
// 4. always add choice for cancel
sc = ScAddMenuString(menu, DROPEFFECT_NONE, IDS_DragDrop_Cancel); if (sc) return sc;
// 5. set the default item
if ( dwSelected != DROPEFFECT_NONE ) { bool bOK = menu.SetDefaultItem( dwSelected ); if ( !bOK ) return sc = E_FAIL; }
// 6. find the tied object
CMMCViewDropTarget *pTarget = NULL; sc = ScGetTiedObject(pTarget); if (sc) return sc;
sc = ScCheckPointers(pTarget, E_UNEXPECTED); if (sc) return sc;
// 7. display the menu
dwSelected = menu.TrackPopupMenu(TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, pt.x, pt.y, CWnd::FromHandlePermanent( pTarget->GetWindowHandle() ) );
return sc; }
/***************************************************************************\
* * METHOD: CMMCDropTarget::CalculateEffect * * PURPOSE: Helper. calculates drop effect by combining: * a) available operations * b) the default operation * c) keyboard key combination * * PARAMETERS: * DWORD dwEffectsAvailable [in] available operations * DWORD grfKeyState [in] keyboard / mouse state * bool bCopyPreferred [in] default operation * * RETURNS: * SC - result code * \***************************************************************************/ DWORD CMMCDropTarget::CalculateEffect(DWORD dwEffectsAvailable, DWORD grfKeyState, bool bCopyPreferred) { const bool bShiftPressed = (grfKeyState & MK_SHIFT); const bool bControlPressed = (grfKeyState & MK_CONTROL); const bool bRightClickDrag = (grfKeyState & MK_RBUTTON);
m_bRightDrag = bRightClickDrag; m_bCopyByDefault = bCopyPreferred;
if (!bRightClickDrag) // affected by keyboard only in non-right-drag
{ // do nothing if user holds on shift+control
if ( bShiftPressed && bControlPressed ) return DROPEFFECT_NONE;
// modify by user interactive preferences
if ( bShiftPressed ) { // if user cannot get what he wants to - indicate it
if ( !(dwEffectsAvailable & DROPEFFECT_MOVE) ) return DROPEFFECT_NONE;
bCopyPreferred = false; } else if ( bControlPressed ) { // if user cannot get what he wants to - indicate it
if ( !(dwEffectsAvailable & DROPEFFECT_COPY) ) return DROPEFFECT_NONE;
bCopyPreferred = true; } }
// return preferred, if available
if ( bCopyPreferred && (dwEffectsAvailable & DROPEFFECT_COPY) ) return DROPEFFECT_COPY;
if ( !bCopyPreferred && (dwEffectsAvailable & DROPEFFECT_MOVE) ) return DROPEFFECT_MOVE;
// preferred not available - return what is available
if ( dwEffectsAvailable & DROPEFFECT_COPY ) { m_bCopyByDefault = true; return DROPEFFECT_COPY; } else if ( dwEffectsAvailable & DROPEFFECT_MOVE ) { m_bCopyByDefault = false; return DROPEFFECT_MOVE; }
return DROPEFFECT_NONE; }
|