ultimatepp/uppsrc/CtrlCore/Win32DnD.cpp
2023-09-20 23:10:58 +02:00

547 lines
13 KiB
C++

#include "CtrlCore.h"
#ifdef GUI_WIN
namespace Upp {
#ifdef COMPILER_CLANG
#define CLANG_NOTHROW __attribute__((nothrow))
#else
#define CLANG_NOTHROW
#endif
#define LLOG(x) // RLOG(x)
int GetClipboardFormatCode(const char *format_id);
int ToWin32CF(const char *s)
{
return GetClipboardFormatCode(s);
}
String FromWin32CF(int cf)
{
GuiLock __;
if(cf == CF_TEXT)
return "text";
if(cf == CF_UNICODETEXT)
return "wtext";
if(cf == CF_DIB)
return "dib";
#ifndef PLATFORM_WINCE
if(cf == CF_HDROP)
return "files";
#endif
char h[256];
GetClipboardFormatNameA(cf, h, 255);
return h;
}
FORMATETC ToFORMATETC(const char *s)
{
FORMATETC fmtetc;
fmtetc.cfFormat = ToWin32CF(s);
fmtetc.dwAspect = DVASPECT_CONTENT;
fmtetc.lindex = -1;
fmtetc.ptd = NULL;
fmtetc.tymed = TYMED_HGLOBAL;
return fmtetc;
}
String AsString(POINTL p)
{
return String().Cat() << "[" << p.x << ", " << p.y << "]";
}
struct UDropTarget : public IDropTarget {
ULONG rc;
LPDATAOBJECT data;
Ptr<Ctrl> ctrl;
Index<String> fmt;
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
STDMETHOD(DragLeave)();
STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
void DnD(POINTL p, bool drop, DWORD *effect, DWORD keys);
void FreeData();
void Repeat();
void EndDrag();
String Get(const char *fmt) const;
UDropTarget() { rc = 1; data = NULL; }
~UDropTarget();
};
bool Has(UDropTarget *dt, const char *fmt)
{
return dt->fmt.Find(fmt) >= 0;
}
String Get(UDropTarget *dt, const char *fmt)
{
return dt->Get(fmt);
}
STDMETHODIMP CLANG_NOTHROW UDropTarget::QueryInterface(REFIID iid, void ** ppv)
{
if(iid == IID_IUnknown || iid == IID_IDropTarget) {
*ppv = this;
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
String UDropTarget::Get(const char *fmt) const
{
FORMATETC fmtetc = ToFORMATETC(fmt);
STGMEDIUM s;
if(data->GetData(&fmtetc, &s) == S_OK && s.tymed == TYMED_HGLOBAL) {
char *val = (char *)GlobalLock(s.hGlobal);
String data(val, (int)GlobalSize(s.hGlobal));
GlobalUnlock(s.hGlobal);
ReleaseStgMedium(&s);
return data;
}
return Null;
}
void UDropTarget::DnD(POINTL pl, bool drop, DWORD *effect, DWORD keys)
{
GuiLock __;
LLOG("DnD effect: " << *effect);
dword e = *effect;
*effect = DROPEFFECT_NONE;
if(!ctrl)
return;
PasteClip d;
d.dt = this;
d.paste = drop;
d.accepted = false;
d.allowed = 0;
d.action = 0;
if(e & DROPEFFECT_COPY) {
LLOG("DnD DROPEFFECT_COPY");
d.allowed = DND_COPY;
d.action = DND_COPY;
}
if(e & DROPEFFECT_MOVE) {
LLOG("DnD DROPEFFECT_MOVE");
d.allowed |= DND_MOVE;
if(Ctrl::GetDragAndDropSource())
d.action = DND_MOVE;
}
LLOG("DnD keys & MK_CONTROL:" << (keys & MK_CONTROL));
if((keys & MK_CONTROL) && (d.allowed & DND_COPY))
d.action = DND_COPY;
if((keys & (MK_ALT|MK_SHIFT)) && (d.allowed & DND_MOVE))
d.action = DND_MOVE;
ctrl->DnD(Point(pl.x, pl.y), d);
if(d.IsAccepted()) {
LLOG("DnD accepted, action: " << (int)d.action);
if(d.action == DND_MOVE)
*effect = DROPEFFECT_MOVE;
if(d.action == DND_COPY)
*effect = DROPEFFECT_COPY;
}
}
void UDropTarget::Repeat()
{
Ctrl::DnDRepeat();
}
STDMETHODIMP CLANG_NOTHROW UDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
GuiLock __;
LLOG("DragEnter " << Point(pt.x, pt.y));
data = pDataObj;
data->AddRef();
fmt.Clear();
IEnumFORMATETC *fe;
if(!ctrl || pDataObj->EnumFormatEtc(DATADIR_GET, &fe) != NOERROR) {
*pdwEffect = DROPEFFECT_NONE;
return NOERROR;
}
FORMATETC fmtetc;
while(fe->Next(1, &fmtetc, 0) == S_OK) {
fmt.FindAdd(FromWin32CF(fmtetc.cfFormat));
if(fmtetc.ptd)
CoTaskMemFree(fmtetc.ptd);
}
LLOG("DragEnter fmt: " << fmt);
fe->Release();
DnD(pt, false, pdwEffect, grfKeyState);
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LLOG("DragOver " << Point(pt.x, pt.y) << " keys: " << grfKeyState);
DnD(pt, false, pdwEffect, grfKeyState);
return NOERROR;
}
void UDropTarget::FreeData()
{
if(data) {
data->Release();
data = NULL;
}
}
void UDropTarget::EndDrag()
{
Ctrl::DnDLeave();
}
STDMETHODIMP CLANG_NOTHROW UDropTarget::DragLeave()
{
LLOG("DragLeave");
EndDrag();
FreeData();
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UDropTarget::Drop(LPDATAOBJECT, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LLOG("Drop");
if(Ctrl::GetDragAndDropSource())
Ctrl::OverrideCursor(Null);
DnD(pt, true, pdwEffect, grfKeyState);
EndDrag();
FreeData();
return NOERROR;
}
UDropTarget::~UDropTarget()
{
if(data) data->Release();
EndDrag();
}
// --------------------------------------------------------------------------------------------
Ptr<Ctrl> sDnDSource;
Ctrl * Ctrl::GetDragAndDropSource()
{
return sDnDSource;
}
struct UDataObject : public IDataObject {
ULONG rc;
dword effect;
VectorMap<String, ClipData> data;
STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
STDMETHOD(GetData)(FORMATETC *fmtetc, STGMEDIUM *medium);
STDMETHOD(GetDataHere)(FORMATETC *, STGMEDIUM *);
STDMETHOD(QueryGetData)(FORMATETC *fmtetc);
STDMETHOD(GetCanonicalFormatEtc)(FORMATETC *, FORMATETC *pformatetcOut);
STDMETHOD(SetData)(FORMATETC *fmtetc, STGMEDIUM *medium, BOOL release);
STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ief);
STDMETHOD(DAdvise)(FORMATETC *, DWORD, IAdviseSink *, DWORD *);
STDMETHOD(DUnadvise)(DWORD);
STDMETHOD(EnumDAdvise)(LPENUMSTATDATA *);
UDataObject() { rc = 1; effect = 0; }
};
struct UEnumFORMATETC : public IEnumFORMATETC {
ULONG rc;
int ii;
UDataObject *data;
STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
STDMETHOD(Next)(ULONG n, FORMATETC *fmtetc, ULONG *fetched);
STDMETHOD(Skip)(ULONG n);
STDMETHOD(Reset)(void);
STDMETHOD(Clone)(IEnumFORMATETC **newEnum);
UEnumFORMATETC() { ii = 0; rc = 1; }
~UEnumFORMATETC() { data->Release(); }
};
struct UDropSource : public IDropSource {
ULONG rc;
Image no, move, copy;
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
STDMETHOD(GiveFeedback)(DWORD dwEffect);
UDropSource() { rc = 1; }
};
STDMETHODIMP CLANG_NOTHROW UDataObject::QueryInterface(REFIID iid, void ** ppv)
{
if(iid == IID_IUnknown || iid == IID_IDataObject) {
*ppv = this;
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
void SetMedium(STGMEDIUM *medium, const String& data)
{
int sz = data.GetCount();
HGLOBAL hData = GlobalAlloc(0, sz + 4);
if (hData) {
char *ptr = (char *) GlobalLock(hData);
memcpy(ptr, ~data, sz);
memset(ptr + sz, 0, 4);
GlobalUnlock(hData);
medium->tymed = TYMED_HGLOBAL;
medium->hGlobal = hData;
medium->pUnkForRelease = 0;
}
}
STDMETHODIMP CLANG_NOTHROW UDataObject::GetData(FORMATETC *fmtetc, STGMEDIUM *medium)
{
String fmt = FromWin32CF(fmtetc->cfFormat);
ClipData *s = data.FindPtr(fmt);
if(s) {
String q = s->Render();
SetMedium(medium, q.GetCount() ? q : sDnDSource ? sDnDSource->GetDropData(fmt) : String());
return S_OK;
}
return DV_E_FORMATETC;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::GetDataHere(FORMATETC *, STGMEDIUM *)
{
return DV_E_FORMATETC;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::QueryGetData(FORMATETC *fmtetc)
{
return data.Find(FromWin32CF(fmtetc->cfFormat)) >= 0 ? S_OK : DV_E_FORMATETC;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::GetCanonicalFormatEtc(FORMATETC *, FORMATETC *pformatetcOut)
{
pformatetcOut->ptd = NULL;
return E_NOTIMPL;
}
#ifdef PLATFORM_WINCE
static int CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat(_T("Performed DropEffect"));
#else
static int CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat("Performed DropEffect");
#endif
STDMETHODIMP CLANG_NOTHROW UDataObject::SetData(FORMATETC *fmtetc, STGMEDIUM *medium, BOOL release)
{
if(fmtetc->cfFormat == CF_PERFORMEDDROPEFFECT && medium->tymed == TYMED_HGLOBAL) {
DWORD *val = (DWORD*)GlobalLock(medium->hGlobal);
effect = *val;
GlobalUnlock(medium->hGlobal);
if(release)
ReleaseStgMedium(medium);
return S_OK;
}
return E_NOTIMPL;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ief)
{
UEnumFORMATETC *ef = new UEnumFORMATETC;
ef->data = this;
AddRef();
*ief = ef;
return S_OK;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::DAdvise(FORMATETC *, DWORD, IAdviseSink *, DWORD *)
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::DUnadvise(DWORD)
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP CLANG_NOTHROW UDataObject::EnumDAdvise(LPENUMSTATDATA FAR*)
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
{
if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
*ppvObj = this;
AddRef();
return NOERROR;
}
*ppvObj = NULL;
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Next(ULONG n, FORMATETC *t, ULONG *fetched) {
if(t == NULL)
return E_INVALIDARG;
if(fetched) *fetched = 0;
while(ii < data->data.GetCount() && n > 0) {
if(fetched) (*fetched)++;
n--;
*t++ = ToFORMATETC(data->data.GetKey(ii++));
}
return n ? S_FALSE : NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Skip(ULONG n) {
ii += n;
if(ii >= data->data.GetCount())
return S_FALSE;
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Reset()
{
ii = 0;
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Clone(IEnumFORMATETC **newEnum)
{
if(newEnum == NULL)
return E_INVALIDARG;
UEnumFORMATETC *ef = new UEnumFORMATETC;
ef->data = data;
data->AddRef();
ef->ii = ii;
*newEnum = ef;
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UDropSource::QueryInterface(REFIID riid, void **ppvObj)
{
if (riid == IID_IUnknown || riid == IID_IDropSource) {
*ppvObj = this;
AddRef();
return NOERROR;
}
*ppvObj = NULL;
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP CLANG_NOTHROW UDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
if(fEscapePressed)
return DRAGDROP_S_CANCEL;
else
if(!(grfKeyState & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)))
return DRAGDROP_S_DROP;
Ctrl::ProcessEvents();
return NOERROR;
}
STDMETHODIMP CLANG_NOTHROW UDropSource::GiveFeedback(DWORD dwEffect)
{
LLOG("GiveFeedback");
Image m = IsNull(move) ? copy : move;
if((dwEffect & DROPEFFECT_COPY) == DROPEFFECT_COPY) {
LLOG("GiveFeedback COPY");
if(!IsNull(copy)) m = copy;
}
else
if((dwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) {
LLOG("GiveFeedback MOVE");
if(!IsNull(move)) m = move;
}
else
m = no;
Ctrl::OverrideCursor(m);
Ctrl::SetMouseCursor(m);
return S_OK;
}
Image MakeDragImage(const Image& arrow, Image sample);
Image MakeDragImage(const Image& arrow, const Image& arrow98, Image sample)
{
return MakeDragImage(arrow, sample);
}
int Ctrl::DoDragAndDrop(const char *fmts, const Image& sample, dword actions,
const VectorMap<String, ClipData>& data)
{
UDataObject *obj = new UDataObject;
obj->data <<= data;
if(fmts) {
Vector<String> f = Split(fmts, ';');
for(int i = 0; i < f.GetCount(); i++)
obj->data.GetAdd(f[i]);
}
UDropSource *dsrc = new UDropSource;
DWORD result = 0;
Image m = Ctrl::OverrideCursor(CtrlCoreImg::DndMove());
dsrc->no = MakeDragImage(CtrlCoreImg::DndNone(), CtrlCoreImg::DndNone98(), sample);
if(actions & DND_COPY)
dsrc->copy = actions & DND_EXACTIMAGE ? sample : MakeDragImage(CtrlCoreImg::DndCopy(), CtrlCoreImg::DndCopy98(), sample);
if(actions & DND_MOVE)
dsrc->move = actions & DND_EXACTIMAGE ? sample : MakeDragImage(CtrlCoreImg::DndMove(), CtrlCoreImg::DndMove98(), sample);
sDnDSource = this;
int level = LeaveGuiMutexAll();
HRESULT r = DoDragDrop(obj, dsrc,
(actions & DND_COPY ? DROPEFFECT_COPY : 0) |
(actions & DND_MOVE ? DROPEFFECT_MOVE : 0), &result);
EnterGuiMutex(level);
DWORD re = obj->effect;
obj->Release();
dsrc->Release();
OverrideCursor(m);
SyncCaret();
CheckMouseCtrl();
KillRepeat();
sDnDSource = NULL;
if(r == DRAGDROP_S_DROP) {
if(((result | re) & DROPEFFECT_MOVE) == DROPEFFECT_MOVE && (actions & DND_MOVE))
return DND_MOVE;
if(((result | re) & DROPEFFECT_COPY) == DROPEFFECT_COPY && (actions & DND_COPY))
return DND_COPY;
}
return DND_NONE;
}
void ReleaseUDropTarget(UDropTarget *dt)
{
dt->Release();
}
UDropTarget *NewUDropTarget(Ctrl *ctrl)
{
UDropTarget *dt = new UDropTarget;
dt->ctrl = ctrl;
return dt;
}
void Ctrl::SetSelectionSource(const char *fmts) {}
}
#endif