📄 dragdrp.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
* DRAGDRP.C
*
* Purpose:
* Implementation of Richedit's OLE drag drop objects (namely,
* the drop target and drop source objects)
*
* Author:
* alexgo (4/24/95)
*/
#include "_common.h"
#include "_edit.h"
#include "_dragdrp.h"
#include "_disp.h"
#include "_select.h"
#include "_font.h"
ASSERTDATA
//
// CDropSource PUBLIC methods
//
/*
* CDropSource::QueryInterface (riid, ppv)
*/
STDMETHODIMP CDropSource::QueryInterface(REFIID riid, void ** ppv)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::QueryInterface");
if( IsEqualIID(riid, IID_IUnknown) )
{
*ppv = (IUnknown *)this;
}
else if( IsEqualIID(riid, IID_IDropSource) )
{
*ppv = (IDropSource *)this;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
/*
* CDropSource::AddRef
*/
STDMETHODIMP_(ULONG) CDropSource::AddRef()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::AddRef");
return ++_crefs;
}
/*
* CDropSource::Release
*
* @devnote. Do not even think about making an outgoing call here.
* If you do, be sure make sure all callers use a
* SafeReleaseAndNULL (null the pointer before releasing)
* technique to avoid re-entrancy problems.
*/
STDMETHODIMP_(ULONG) CDropSource::Release()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::Release");
_crefs--;
if( _crefs == 0 )
{
delete this;
return 0;
}
return _crefs;
}
/*
* CDropSource::QueryContinueDrag (fEscapePressed, grfKeyState)
*
* Purpose:
* determines whether or not to continue a drag drop operation
*
* Algorithm:
* if the escape key has been pressed, cancel
* if the left mouse button has been release, then attempt to
* do a drop
*/
STDMETHODIMP CDropSource::QueryContinueDrag(BOOL fEscapePressed,
DWORD grfKeyState)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::QueryContinueDrag");
if (fEscapePressed)
{
return DRAGDROP_S_CANCEL;
}
else if (!(grfKeyState & MK_LBUTTON) && !(grfKeyState & MK_RBUTTON))
{
return DRAGDROP_S_DROP;
}
else
{
return NOERROR;
}
}
/*
* CDropSource::GiveFeedback (dwEffect)
*
* Purpose:
* gives feedback during a drag drop operation
*
* Notes:
* FUTURE (alexgo): maybe put in some neater feedback effects
* than the standard OLE stuff??
*/
STDMETHODIMP CDropSource::GiveFeedback(DWORD dwEffect)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::GiveFeedback");
return DRAGDROP_S_USEDEFAULTCURSORS;
}
/*
* CDropSource::CDropSource
*/
CDropSource::CDropSource()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::CDropSource");
_crefs = 1;
}
//
// CDropSource PRIVATE methods
//
/*
* CDropSource::~CDropSource
*/
CDropSource::~CDropSource()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropSource::~CDropSource");
;
}
//
// CDropTarget PUBLIC methods
//
/*
* CDropTarget::QueryInterface (riid, ppv)
*/
STDMETHODIMP CDropTarget::QueryInterface (REFIID riid, void ** ppv)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::QueryInterface");
if( IsEqualIID(riid, IID_IUnknown) )
{
*ppv = (IUnknown *)this;
}
else if( IsEqualIID(riid, IID_IDropTarget) )
{
*ppv = (IDropTarget *)this;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
/*
* CDropTarget::AddRef
*/
STDMETHODIMP_(ULONG) CDropTarget::AddRef()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::AddRef");
return ++_crefs;
}
/*
* CDropTarget::Release()
*/
STDMETHODIMP_(ULONG) CDropTarget::Release()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::Release");
_crefs--;
if( _crefs == 0 )
{
delete this;
return 0;
}
return _crefs;
}
/*
* CDropTarget::DragEnter (pdo, grfKeyState, pt, pdwEffect)
*
* Purpose:
* called when OLE drag drop enters our "window"
*
* Algorithm:
* first we check to see if the data object being transferred contains
* any data that we support. Then we verify that the 'type' of drag
* is acceptable (i.e., currently, we do not accept links).
*
*
* FUTURE: (alexgo): we may want to accept links as well.
*/
STDMETHODIMP CDropTarget::DragEnter(IDataObject *pdo, DWORD grfKeyState,
POINTL pt, DWORD *pdwEffect)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragEnter");
// We don't have a position yet.
_cpCur = -1;
HRESULT hr;
DWORD result;
CTxtSelection *psel;
// At drag enter time, we should have no cached info about what the data
// object supports. This flag should be cleared in DragLeave. Note
// that we slightly override _dwFlags, as it's possible for a data object
// given during drag drop to also generate DOI_NONE.
if( !_ped )
{
return CO_E_RELEASED;
}
Assert(_pcallmgr == NULL);
Assert(_dwFlags == 0);
_pcallmgr = new CCallMgr(_ped);
if( !_pcallmgr )
{
return E_OUTOFMEMORY;
}
// Find out if we can paste the object
result = _ped->GetDTE()->CanPaste(pdo, 0, RECO_DROP);
if( result )
{
if( result == DF_CLIENTCONTROL )
{
_dwFlags |= DF_CLIENTCONTROL;
}
// Create the object that implements the drag caret
_pdrgcrt = new CDropCaret(_ped);
if ((NULL == _pdrgcrt) || !_pdrgcrt->Init())
{
// Initialization failed so go without a caret
delete _pdrgcrt;
_pdrgcrt = NULL;
}
// cache the current selection so we can restore it on return
psel = _ped->GetSel();
if (NULL == psel)
{
Assert(psel);
hr = E_OUTOFMEMORY;
}
else
{
_cpSel = psel->GetCp();
_cchSel = psel->GetCch();
_dwFlags |= DF_CANDROP;
// just call DragOver to handle our visual feedback
hr = DragOver(grfKeyState, pt, pdwEffect);
}
}
else
{
// this is new behaviour for Win95 OLE; if we don't
// understand anything about the data object given to us,
// we return S_FALSE to allow our parent to give the
// drag drop a try.
hr = S_FALSE;
}
if( hr != NOERROR )
{
delete _pcallmgr;
_pcallmgr = NULL;
_dwFlags = 0;
}
return hr;
}
/*
* CDropTarget::DragOver (grfKeyState, pt, pdwEffect)
*
* Purpose:
* handles the visual feedback for a drag drop operation zooming
* around over text
*
* FUTURE (alexgo): maybe we should do some snazzy visuals here
*/
STDMETHODIMP CDropTarget::DragOver(DWORD grfKeyState, POINTL pt,
DWORD *pdwEffect)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragOver");
LONG cpCur = _cpCur;
if( !_ped )
{
return CO_E_RELEASED;
}
Assert(_pcallmgr);
// note if we're doing right mouse drag drop; note that we
// can't do this in UpdateEffect as it's called from Drop
// as well (and the mouse button is up then!)
if( (grfKeyState & MK_RBUTTON) )
{
_dwFlags |= DF_RIGHTMOUSEDRAG;
}
else
{
_dwFlags &= ~DF_RIGHTMOUSEDRAG;
}
UpdateEffect(grfKeyState, pt, pdwEffect);
// only draw if we've changed position
if( *pdwEffect != DROPEFFECT_NONE
&& ((cpCur != _cpCur)
|| (_pdrgcrt && _pdrgcrt->NoCaret())))
{
DrawFeedback();
}
return NOERROR;
}
/*
* CDropTarget::DragLeave
*
* Purpose:
* called when the mouse leaves our window during drag drop. Here we clean
* up any temporary state setup for the drag operation.
*/
STDMETHODIMP CDropTarget::DragLeave()
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::DragLeave");
CTxtSelection *psel = _ped->GetSel();
if (NULL == psel)
{
Assert(psel);
return E_OUTOFMEMORY;
}
if( !_ped )
{
return CO_E_RELEASED;
}
Assert(_pcallmgr);
_dwFlags = 0;
// now restore the selection
psel->SetSelection(_cpSel - _cchSel, _cpSel);
psel->Update(FALSE);
_cpSel = _cchSel = 0;
delete _pcallmgr;
_pcallmgr = NULL;
delete _pdrgcrt;
_pdrgcrt = NULL;
return NOERROR;
}
/*
* CDropTarget::Drop (pdo, grfKeyState, pt, pdwEffect)
*
* Purpose:
* called when the mouse button is released. We should attempt
* to 'paste' the data object into a selection corresponding to
* the mouse location
*
* Algorithm:
* first, we make sure that we can still do a paste (via UpdateEffect).
* If so, then set the selection to the current point and then insert
* the text.
*
*/
STDMETHODIMP CDropTarget::Drop(IDataObject *pdo, DWORD grfKeyState,
POINTL ptl, DWORD *pdwEffect)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::Drop");
LONG cchNext;
LONG cpNext;
HRESULT hr = NOERROR;
CTxtSelection * psel;
if( !_ped )
{
return CO_E_RELEASED;
}
Assert(_pcallmgr);
CDropCleanup cleanup(this);
CGenUndoBuilder undobldr( _ped, UB_AUTOCOMMIT);
// see if we can still drop
UpdateEffect(grfKeyState, ptl, pdwEffect);
// UpdateEffect will show a drop cursor but at this point we don't need one
// so we hide the drop cursor here.
if (_pdrgcrt)
{
_pdrgcrt->HideCaret();
}
if (_dwFlags & DF_OVERSOURCE)
{
*pdwEffect = DROPEFFECT_NONE;
_dwFlags = 0;
return hr;
}
if( (*pdwEffect & (DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK)) )
{
IUndoBuilder *publdr;
// if this is a right mouse drag drop; handle that
if( (_dwFlags & DF_RIGHTMOUSEDRAG) )
{
hr = HandleRightMouseDrop(pdo, ptl);
// if S_FALSE is returned; treat the drag drop normally.
if( hr != S_FALSE )
{
goto Exit;
}
}
// get an undo builder. If we already have one cached, that means
// we are dropping onto the same edit instance that started the drag
// In this case, we want to use the cached undo builder so that
// a drag move can be undone as one "operation".
if( _publdr == NULL )
{
publdr = &undobldr;
}
else
{
publdr = _publdr;
}
psel = _ped->GetSel();
if (NULL == psel)
{
Assert(psel);
hr = E_OUTOFMEMORY;
goto Exit;
}
psel->SetSelection(_cpCur, _cpCur);
if( !_ped->IsProtectedRange(WM_PASTE, 0, 0, psel) )
{
hr = _ped->PasteDataObjectToRange(pdo, (CTxtRange *)psel,
0, NULL, publdr, PDOR_DROP);
}
// if we are dropping onto ourselves, the UI specifies
// that we should select the entire range dragged. We use
// _publdr as an easy way to tell if the drop originated from
// this instance
if (SUCCEEDED(hr) && (_pdrgcrt != NULL))
{
// If the drop worked, then we don't want to restore the area
// where the drop caret used to be since this is not out of date.
_pdrgcrt->CancelRestoreCaretArea();
}
// Now set the selection anti-events. If the selection preceded the
// paste poiont subtract its length from the redo position, since
// the selection will get deleted if we are doing a DRAGMOVE within
// this instance.
cpNext = psel->GetCp();
cchNext = cpNext - _cpCur;
if( (_cpSel < _cpCur) && _publdr && (*pdwEffect & DROPEFFECT_MOVE))
cpNext -= abs(_cchSel);
HandleSelectionAEInfo(_ped, publdr, _cpCur, 0, cpNext, cchNext,
SELAE_FORCEREPLACE);
if( _publdr )
{
// if we are doing a drag move, then *don't* set the
// selection directly on the screen--doing so will result in
// unsightly UI--we'llset the selection to one spot, draw it
// and then immediately move the selection somewhere else
// in this case, just change where the selection range exists.
// Floating ranges and the drag-move code in ldte.c will take
// care of the rest.
if( *pdwEffect == DROPEFFECT_COPY )
{
psel->SetSelection(_cpCur, psel->GetCp());
}
else
{
psel->Set(psel->GetCp(), cchNext);
}
}
else
{
// the drop call landed in us from outside, so we need
// to fire the appropriate notifications. First, however,
// commit the undo builder.
publdr->SetNameID(UID_DRAGDROP);
publdr->Done();
if (SUCCEEDED(hr))
{
// Make this window the foreground window after the drop. Note
// that the host needs to support ITextHost2 to really get to
// be the foreground window. If they don't this is a no-op.
_ped->TxSetForegroundWindow();
}
}
// If nothing changed on the drop && the effect is a move, then return
// failure. This is an ugly hack to improve drag-move scenarios; if
// nothing happened on the drop, then chances are, you don't want
// to have the correspong "Cut" happen on the drag source side.
//
// Of course, this relies on the drag source responding gracefully to
// E_FAIL w/o hitting too much trauma.
if( *pdwEffect == DROPEFFECT_MOVE &&
!_ped->GetCallMgr()->GetChangeEvent() )
{
hr = E_FAIL;
}
}
Exit:
_dwFlags = 0;
return hr;
}
/*
* CDropTarget::CDropTarget (ped)
*
*/
CDropTarget::CDropTarget(CTxtEdit *ped)
{
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CDropTarget::CDropTarget");
_ped = ped;
_crefs = 1;
_dwFlags = 0;
_publdr = NULL;
_cpMin = -1;
_cpMost = -1;
_pcallmgr = NULL;
}
/*
* CDropTarget::SetDragInfo (publdr, cpMin, cpMost)
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -