mtldragdrop.h

来自「一个使用wtl写的完整的多窗口浏览器」· C头文件 代码 · 共 816 行 · 第 1/2 页

H
816
字号
class IDropTargetImplBase
{
public:
	static bool s_bStaticInit;

	// metrics for drag-scrolling
	static int s_nScrollInset;
	static UINT s_nScrollDelay;
	static UINT s_nScrollInterval;

	IDropTargetImplBase()
	{
		// init static variables
		if (!s_bStaticInit) {
			::EnterCriticalSection(&_Module.m_csStaticDataInit);
			if (!s_bStaticInit) {
				// get scroll metrics from win.ini
				static const TCHAR szWindows[] = _T("windows");
				static const TCHAR szScrollDelay[] = _T("DragScrollDelay");
				static const TCHAR szScrollInset[] = _T("DragScrollInset");
				static const TCHAR szScrollInterval[] = _T("DragScrollInterval");

				s_nScrollInset = ::GetProfileInt(szWindows, szScrollInset, DD_DEFSCROLLINSET);
				s_nScrollDelay = ::GetProfileInt(szWindows, szScrollDelay, DD_DEFSCROLLDELAY);
				s_nScrollInterval = ::GetProfileInt(szWindows, szScrollInterval, DD_DEFSCROLLINTERVAL);

				s_bStaticInit = true;
			}
			::LeaveCriticalSection(&_Module.m_csStaticDataInit);
		}
	}
};

__declspec(selectany) bool IDropTargetImplBase::s_bStaticInit = false;
__declspec(selectany) int IDropTargetImplBase::s_nScrollInset = DD_DEFSCROLLINSET;
__declspec(selectany) UINT IDropTargetImplBase::s_nScrollDelay = DD_DEFSCROLLDELAY;
__declspec(selectany) UINT IDropTargetImplBase::s_nScrollInterval = DD_DEFSCROLLINTERVAL;

template <class T>
class ATL_NO_VTABLE IDropTargetImpl : public _IDropTargetLocator, public IDropTargetImplBase
{
public:
// COM Identity
	STDMETHOD(_LocDTQueryInterface)(REFIID riid, void ** ppvObject)
	{
		if (InlineIsEqualUnknown(riid) ||
			InlineIsEqualGUID(riid, IID_IDropTarget))
		{
			if (ppvObject == NULL)
				return E_POINTER;
			*ppvObject = this;
			AddRef();
#ifdef _ATL_DEBUG_INTERFACES
			_Module.AddThunk((IUnknown**)ppvObject, _T("IDropTargetImpl"), riid);
#endif // _ATL_DEBUG_INTERFACES
			return S_OK;
		}
		else
			return E_NOINTERFACE;
	}
	virtual ULONG STDMETHODCALLTYPE AddRef()
	{
		return 1;
	}
	virtual ULONG STDMETHODCALLTYPE Release()
	{
		return 1;
	}

// Methods
	DROPEFFECT GetOkDropEffect()
	{

	}

// IDropTarget
	STDMETHOD(DragEnter)(IDataObject* pDataObject, DWORD dwKeyState, POINTL pt, DWORD* pdwEffect)
	{
		DTTRACE(_T("IDropTargetImpl::DragEnter\n"));
		ATLASSERT(pdwEffect != NULL);
		ATLASSERT(pDataObject != NULL);

		SCODE sc = E_UNEXPECTED;

		// cache lpDataObject
		DTTRACE(_T(" cache lpDataObject step1\n"));
		m_spDataObject.Release();
		ATLASSERT(m_spDataObject.p == NULL);
		DTTRACE(_T(" cache lpDataObject step2\n"));
		m_spDataObject = pDataObject;

		T* pT = static_cast<T*>(this);
		CPoint point((int)pt.x, (int)pt.y);
		pT->ScreenToClient(&point);

		// check first for entering scroll area
		DROPEFFECT dropEffect = pT->OnDragScroll(dwKeyState, point);
		if ((dropEffect & DROPEFFECT_SCROLL) == 0) {
			// funnel through OnDragEnter since not in scroll region
			dropEffect = pT->OnDragEnter(pDataObject, dwKeyState, point);
		}
		*pdwEffect = _MtlFilterDropEffect(dropEffect, *pdwEffect);
		sc = S_OK;

		return sc;
	}
	
	// If pdwEffect is only DROPEFFECT_COPY, you have to add DROPEFFECT_COPY to pdwEffect.
	// That is, MFC always sucks.
	STDMETHOD(DragOver)(DWORD dwKeyState, POINTL pt, DWORD* pdwEffect)
	{
		DTTRACE(_T("IDropTargetImpl::DragOver\n"));
		ATLASSERT(pdwEffect != NULL);
		ATLASSERT(m_spDataObject != NULL);

		SCODE sc = E_UNEXPECTED;

		T* pT = static_cast<T*>(this);
		CPoint point((int)pt.x, (int)pt.y);
		pT->ScreenToClient(&point);

		// check first for entering scroll area
		DROPEFFECT dropEffect = pT->OnDragScroll(dwKeyState, point);
		if ((dropEffect & DROPEFFECT_SCROLL) == 0) {
			// funnel through OnDragOver
			dropEffect = pT->OnDragOver(m_spDataObject, dwKeyState,
				point, *pdwEffect);
		}
		*pdwEffect = _MtlFilterDropEffect(dropEffect, *pdwEffect);
		sc = S_OK;

		return sc;
	}
	
	STDMETHOD(DragLeave)()
	{
		DTTRACE(_T("IDropTargetImpl::DragLeave\n"));
		// cancel drag scrolling
		m_nTimerID = MAKEWORD(-1, -1);

		T* pT = static_cast<T*>(this);
		pT->OnDragLeave();

		// release cached data object
		m_spDataObject.Release();
		ATLASSERT(m_spDataObject.p == NULL);

		return S_OK;
	}
	
	STDMETHOD(Drop)(IDataObject* pDataObject, DWORD dwKeyState, POINTL pt, DWORD* pdwEffect)
	{
		DTTRACE(_T("IDropTargetImpl::Drop\n"));
		ATLASSERT(pdwEffect != NULL);
		ATLASSERT(pDataObject != NULL);

		SCODE sc = E_UNEXPECTED;

		// cancel drag scrolling
		m_nTimerID = MAKEWORD(-1, -1);

		// prepare for call to OnDragOver
		T* pT = static_cast<T*>(this);
		CPoint point((int)pt.x, (int)pt.y);
		pT->ScreenToClient(&point);

		// verify that drop is legal
		DROPEFFECT dropEffect = _MtlFilterDropEffect(pT->OnDragOver(pDataObject, dwKeyState, point, *pdwEffect), *pdwEffect);

		// execute the drop (try OnDropEx then OnDrop for backward compatibility)
		dropEffect = pT->OnDrop(pDataObject, dropEffect, *pdwEffect, point);

		// release potentially cached data object
		m_spDataObject.Release();

		*pdwEffect = dropEffect;
		sc = S_OK;

		return sc;
	}

// Data members
	CComPtr<IDataObject> m_spDataObject;    // != NULL between OnDragEnter, OnDragLeave
	UINT m_nTimerID;		// != MAKEWORD(-1, -1) when in scroll area
	DWORD m_dwLastTick;     // only valid when m_nTimerID valid
	UINT m_nScrollDelay;    // time to next scroll

// Ctor/dtor
	IDropTargetImpl() : m_nTimerID(MAKEWORD(-1, -1))
	{
	}
	~IDropTargetImpl()
	{
		ATLASSERT(m_spDataObject.p == NULL);
	}

// Methods
	bool RegisterDragDrop()
	{
		DTTRACE(_T("IDropTargetImpl::RegisterDragDrop\n"));
		T* pT = static_cast<T*>(this);
		ATLASSERT(::IsWindow(pT->m_hWnd));

		// the object must be locked externally to keep LRPC connections alive
		if (::CoLockObjectExternal((IUnknown*)this, true, false) != S_OK)
			return false;

		// connect the HWND to the IDropTarget implementation
		if (::RegisterDragDrop(pT->m_hWnd, (IDropTarget*)this) != S_OK)	{
			::CoLockObjectExternal((IUnknown*)this, false, false);
			return false;
		}

		return true;
	}

	void RevokeDragDrop()
	{
		DTTRACE(_T("IDropTargetImpl::RevokeDragDrop\n"));
		T* pT = static_cast<T*>(this);	
		// disconnect from OLE
		::RevokeDragDrop(pT->m_hWnd);
		::CoLockObjectExternal((IUnknown*)this, false, true);
	}

// Overridables
	bool OnScroll(UINT nScrollCode, UINT nPos, bool bDoScroll = true)
	{
		return false;
	}

	DROPEFFECT OnDragEnter(IDataObject* pDataObject, DWORD dwKeyState, CPoint point)
	{
		return _MtlStandardDropEffect(dwKeyState);
	}
	DROPEFFECT OnDragOver(IDataObject* pDataObject, DWORD dwKeyState, CPoint point, DROPEFFECT dropOkEffect)
	{
		return _MtlStandardDropEffect(dwKeyState) | _MtlFollowDropEffect(dropOkEffect);
	}
	DROPEFFECT OnDrop(IDataObject* pDataObject,	DROPEFFECT dropEffect,
		DROPEFFECT dropEffectList, CPoint point)
	{
		return DROPEFFECT_NONE;
	}
	void OnDragLeave()
	{
	}
	// default implementation of drag/drop scrolling
	DROPEFFECT OnDragScroll(DWORD dwKeyState, CPoint point)
	{
		T* pT = static_cast<T*>(this);
		// get client rectangle of destination window
		CRect rectClient;
		pT->GetClientRect(&rectClient);
		CRect rect = rectClient;

		// hit-test against inset region
		UINT nTimerID = MAKEWORD(-1, -1);
		rect.InflateRect(-s_nScrollInset, -s_nScrollInset);
//		CSplitterWnd* pSplitter = NULL;
		if (rectClient.PtInRect(point) && !rect.PtInRect(point))
		{
			// determine which way to scroll along both X & Y axis
			if (point.x < rect.left)
				nTimerID = MAKEWORD(SB_LINEUP, HIBYTE(nTimerID));
			else if (point.x >= rect.right)
				nTimerID = MAKEWORD(SB_LINEDOWN, HIBYTE(nTimerID));
			if (point.y < rect.top)
				nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEUP);
			else if (point.y >= rect.bottom)
				nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEDOWN);
			ATLASSERT(nTimerID != MAKEWORD(-1, -1));	

			// check for valid scroll first
//			pSplitter = CView::GetParentSplitter(pView, false);
			BOOL bEnableScroll = false;
//			if (pSplitter != NULL)
//				bEnableScroll = pSplitter->DoScroll(pView, nTimerID, false);
//			else
				bEnableScroll = pT->OnScroll(nTimerID, 0, false);
			if (!bEnableScroll)
				nTimerID = MAKEWORD(-1, -1);
		}

		if (nTimerID == MAKEWORD(-1, -1))
		{
			if (m_nTimerID != MAKEWORD(-1, -1))
			{
				// send fake OnDragEnter when transition from scroll->normal
				pT->OnDragEnter(m_spDataObject, dwKeyState, point);
				m_nTimerID = MAKEWORD(-1, -1);
			}
			return DROPEFFECT_NONE;
		}

		// save tick count when timer ID changes
		DWORD dwTick = GetTickCount();
		if (nTimerID != m_nTimerID)
		{
			m_dwLastTick = dwTick;
			m_nScrollDelay = s_nScrollDelay;
		}

		// scroll if necessary
		if (dwTick - m_dwLastTick > m_nScrollDelay)
		{
//			if (pSplitter != NULL)
//				pSplitter->DoScroll(pView, nTimerID, true);
//			else
				pT->OnScroll(nTimerID, 0, true);
			m_dwLastTick = dwTick;
			m_nScrollDelay = s_nScrollInterval;
		}
		if (m_nTimerID == MAKEWORD(-1, -1))
		{
			// send fake OnDragLeave when transitioning from normal->scroll
			pT->OnDragLeave();
		}

		m_nTimerID = nTimerID;
		DROPEFFECT dropEffect;

		// check for force link
		if ((dwKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT))
			dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_LINK;
		// check for force copy
		else if ((dwKeyState & MK_CONTROL) == MK_CONTROL)
			dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_COPY;
		// check for force move
		else if ((dwKeyState & MK_ALT) == MK_ALT ||
			(dwKeyState & MK_SHIFT) == MK_SHIFT)
			dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
		// default -- recommended action is move
		else
			dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;

		return dropEffect;
	}
};

// RButtonDrop service
bool MtlOnRButtonDrop(HWND hWnd, DROPEFFECT& dropEffect, CPoint pt, bool bAddShortcut = true)
{
	enum { s_nIDMove = 1, s_nIDCopy = 2, s_nIDShortCut = 3, s_nIDCancel = 4, };
	CMenu menu; menu.CreatePopupMenu();
	menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDMove, _T("偙偙偵堏摦(&M)"));
	menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDCopy, _T("偙偙偵僐僺乕(&C)"));
	if (bAddShortcut)
		menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDShortCut, _T("僔儑乕僩僇僢僩傪偙偙偵嶌惉(&S)"));
	menu.AppendMenu(MF_SEPARATOR);
	menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDCancel, _T("僉儍儞僙儖"));

	if (dropEffect & DROPEFFECT_COPY)
		menu.SetMenuDefaultItem(s_nIDCopy, FALSE);
	else if (dropEffect & DROPEFFECT_MOVE)
		menu.SetMenuDefaultItem(s_nIDMove, FALSE);
	else if (dropEffect & DROPEFFECT_LINK)
		menu.SetMenuDefaultItem(s_nIDShortCut, FALSE);
		
	::ClientToScreen(hWnd, &pt);
	UINT uMenuFlags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_TOPALIGN;
	int nCmdID = menu.TrackPopupMenu(uMenuFlags | TPM_RETURNCMD , pt.x, pt.y, hWnd, NULL);

	if (nCmdID == s_nIDMove)
		dropEffect = DROPEFFECT_MOVE;
	else if (nCmdID == s_nIDCopy)
		dropEffect = DROPEFFECT_COPY;
	else if (nCmdID == s_nIDShortCut)
		dropEffect = DROPEFFECT_LINK;
	else
		return true;// canceled, eat it!

	return false;
}

// RButtonDrop service
bool MtlOnRButtonDropOpenFromApp(HWND hWnd, CPoint pt, const CString& strExePath, const CString& strArg)
{
	if (!MtlIsFileExtExe(strExePath))
		return false;

	enum { s_nIDOpen = 1, s_nIDCancel = 2 };
	CMenu menu; menu.CreatePopupMenu();
	menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDOpen, _T("&Open from application"));
	menu.AppendMenu(MF_SEPARATOR);
	menu.AppendMenu(MF_ENABLED|MF_STRING, s_nIDCancel, _T("Cancel"));

	menu.SetMenuDefaultItem(s_nIDOpen, FALSE);
		
	::ClientToScreen(hWnd, &pt);
	UINT uMenuFlags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_TOPALIGN;
	int nCmdID = menu.TrackPopupMenu(uMenuFlags | TPM_RETURNCMD , pt.x, pt.y, hWnd, NULL);

	if (nCmdID == s_nIDOpen) {
		CString strExePath_(strExePath);
		MtlPreOpenFile(strExePath_, strArg);
		return true;
	}
	else
		return true;// canceled, eat it!

	return false;
}

////////////////////////////////////////////////////////////////////////////
} //namespace MTL

#endif // __MTLDRAGDROP_H__

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?