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 + -
显示快捷键?