mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
547 lines
13 KiB
C++
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) // DLOG(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 " << pt);
|
|
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 " << pt << " 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
|