1 #include "CtrlCore.h"
2
3 #ifdef GUI_WIN
4
5 namespace Upp {
6
7 #ifdef COMPILER_CLANG
8 #define CLANG_NOTHROW __attribute__((nothrow))
9 #else
10 #define CLANG_NOTHROW
11 #endif
12
13
14 #define LLOG(x) // DLOG(x)
15
16 int GetClipboardFormatCode(const char *format_id);
17
ToWin32CF(const char * s)18 int ToWin32CF(const char *s)
19 {
20 return GetClipboardFormatCode(s);
21 }
22
FromWin32CF(int cf)23 String FromWin32CF(int cf)
24 {
25 GuiLock __;
26 if(cf == CF_TEXT)
27 return "text";
28 if(cf == CF_UNICODETEXT)
29 return "wtext";
30 if(cf == CF_DIB)
31 return "dib";
32 #ifndef PLATFORM_WINCE
33 if(cf == CF_HDROP)
34 return "files";
35 #endif
36 char h[256];
37 GetClipboardFormatNameA(cf, h, 255);
38 return h;
39 }
40
ToFORMATETC(const char * s)41 FORMATETC ToFORMATETC(const char *s)
42 {
43 FORMATETC fmtetc;
44 fmtetc.cfFormat = ToWin32CF(s);
45 fmtetc.dwAspect = DVASPECT_CONTENT;
46 fmtetc.lindex = -1;
47 fmtetc.ptd = NULL;
48 fmtetc.tymed = TYMED_HGLOBAL;
49 return fmtetc;
50 }
51
AsString(POINTL p)52 String AsString(POINTL p)
53 {
54 return String().Cat() << "[" << p.x << ", " << p.y << "]";
55 }
56
57 struct UDropTarget : public IDropTarget {
58 ULONG rc;
59 LPDATAOBJECT data;
60 Ptr<Ctrl> ctrl;
61 Index<String> fmt;
62
63 STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
STDMETHOD_Upp::UDropTarget64 STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_Upp::UDropTarget65 STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
66
67 STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
68 STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
69 STDMETHOD(DragLeave)();
70 STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
71
72 void DnD(POINTL p, bool drop, DWORD *effect, DWORD keys);
73 void FreeData();
74 void Repeat();
75 void EndDrag();
76 String Get(const char *fmt) const;
77
UDropTargetUpp::UDropTarget78 UDropTarget() { rc = 1; data = NULL; }
79 ~UDropTarget();
80 };
81
Has(UDropTarget * dt,const char * fmt)82 bool Has(UDropTarget *dt, const char *fmt)
83 {
84 return dt->fmt.Find(fmt) >= 0;
85 }
86
Get(UDropTarget * dt,const char * fmt)87 String Get(UDropTarget *dt, const char *fmt)
88 {
89 return dt->Get(fmt);
90 }
91
QueryInterface(REFIID iid,void ** ppv)92 STDMETHODIMP CLANG_NOTHROW UDropTarget::QueryInterface(REFIID iid, void ** ppv)
93 {
94 if(iid == IID_IUnknown || iid == IID_IDropTarget) {
95 *ppv = this;
96 AddRef();
97 return S_OK;
98 }
99 *ppv = NULL;
100 return E_NOINTERFACE;
101 }
102
Get(const char * fmt) const103 String UDropTarget::Get(const char *fmt) const
104 {
105 FORMATETC fmtetc = ToFORMATETC(fmt);
106 STGMEDIUM s;
107 if(data->GetData(&fmtetc, &s) == S_OK && s.tymed == TYMED_HGLOBAL) {
108 char *val = (char *)GlobalLock(s.hGlobal);
109 String data(val, (int)GlobalSize(s.hGlobal));
110 GlobalUnlock(s.hGlobal);
111 ReleaseStgMedium(&s);
112 return data;
113 }
114 return Null;
115 }
116
DnD(POINTL pl,bool drop,DWORD * effect,DWORD keys)117 void UDropTarget::DnD(POINTL pl, bool drop, DWORD *effect, DWORD keys)
118 {
119 GuiLock __;
120 LLOG("DnD effect: " << *effect);
121 dword e = *effect;
122 *effect = DROPEFFECT_NONE;
123 if(!ctrl)
124 return;
125 PasteClip d;
126 d.dt = this;
127 d.paste = drop;
128 d.accepted = false;
129 d.allowed = 0;
130 d.action = 0;
131 if(e & DROPEFFECT_COPY) {
132 LLOG("DnD DROPEFFECT_COPY");
133 d.allowed = DND_COPY;
134 d.action = DND_COPY;
135 }
136 if(e & DROPEFFECT_MOVE) {
137 LLOG("DnD DROPEFFECT_MOVE");
138 d.allowed |= DND_MOVE;
139 if(Ctrl::GetDragAndDropSource())
140 d.action = DND_MOVE;
141 }
142 LLOG("DnD keys & MK_CONTROL:" << (keys & MK_CONTROL));
143 if((keys & MK_CONTROL) && (d.allowed & DND_COPY))
144 d.action = DND_COPY;
145 if((keys & (MK_ALT|MK_SHIFT)) && (d.allowed & DND_MOVE))
146 d.action = DND_MOVE;
147 ctrl->DnD(Point(pl.x, pl.y), d);
148 if(d.IsAccepted()) {
149 LLOG("DnD accepted, action: " << (int)d.action);
150 if(d.action == DND_MOVE)
151 *effect = DROPEFFECT_MOVE;
152 if(d.action == DND_COPY)
153 *effect = DROPEFFECT_COPY;
154 }
155 }
156
Repeat()157 void UDropTarget::Repeat()
158 {
159 Ctrl::DnDRepeat();
160 }
161
DragEnter(LPDATAOBJECT pDataObj,DWORD grfKeyState,POINTL pt,LPDWORD pdwEffect)162 STDMETHODIMP CLANG_NOTHROW UDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
163 {
164 GuiLock __;
165 LLOG("DragEnter " << pt);
166 data = pDataObj;
167 data->AddRef();
168 fmt.Clear();
169 IEnumFORMATETC *fe;
170 if(!ctrl || pDataObj->EnumFormatEtc(DATADIR_GET, &fe) != NOERROR) {
171 *pdwEffect = DROPEFFECT_NONE;
172 return NOERROR;
173 }
174 FORMATETC fmtetc;
175 while(fe->Next(1, &fmtetc, 0) == S_OK) {
176 fmt.FindAdd(FromWin32CF(fmtetc.cfFormat));
177 if(fmtetc.ptd)
178 CoTaskMemFree(fmtetc.ptd);
179 }
180 LLOG("DragEnter fmt: " << fmt);
181 fe->Release();
182 DnD(pt, false, pdwEffect, grfKeyState);
183 return NOERROR;
184 }
185
186
DragOver(DWORD grfKeyState,POINTL pt,LPDWORD pdwEffect)187 STDMETHODIMP CLANG_NOTHROW UDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
188 {
189 LLOG("DragOver " << pt << " keys: " << grfKeyState);
190 DnD(pt, false, pdwEffect, grfKeyState);
191 return NOERROR;
192 }
193
FreeData()194 void UDropTarget::FreeData()
195 {
196 if(data) {
197 data->Release();
198 data = NULL;
199 }
200 }
201
EndDrag()202 void UDropTarget::EndDrag()
203 {
204 Ctrl::DnDLeave();
205 }
206
DragLeave()207 STDMETHODIMP CLANG_NOTHROW UDropTarget::DragLeave()
208 {
209 LLOG("DragLeave");
210 EndDrag();
211 FreeData();
212 return NOERROR;
213 }
214
Drop(LPDATAOBJECT,DWORD grfKeyState,POINTL pt,LPDWORD pdwEffect)215 STDMETHODIMP CLANG_NOTHROW UDropTarget::Drop(LPDATAOBJECT, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
216 {
217 LLOG("Drop");
218 if(Ctrl::GetDragAndDropSource())
219 Ctrl::OverrideCursor(Null);
220 DnD(pt, true, pdwEffect, grfKeyState);
221 EndDrag();
222 FreeData();
223 return NOERROR;
224 }
225
~UDropTarget()226 UDropTarget::~UDropTarget()
227 {
228 if(data) data->Release();
229 EndDrag();
230 }
231
232 // --------------------------------------------------------------------------------------------
233
234 Ptr<Ctrl> sDnDSource;
235
GetDragAndDropSource()236 Ctrl * Ctrl::GetDragAndDropSource()
237 {
238 return sDnDSource;
239 }
240
241 struct UDataObject : public IDataObject {
242 ULONG rc;
243 dword effect;
244 VectorMap<String, ClipData> data;
245
246 STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
STDMETHOD_Upp::UDataObject247 STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_Upp::UDataObject248 STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
249
250 STDMETHOD(GetData)(FORMATETC *fmtetc, STGMEDIUM *medium);
251 STDMETHOD(GetDataHere)(FORMATETC *, STGMEDIUM *);
252 STDMETHOD(QueryGetData)(FORMATETC *fmtetc);
253 STDMETHOD(GetCanonicalFormatEtc)(FORMATETC *, FORMATETC *pformatetcOut);
254 STDMETHOD(SetData)(FORMATETC *fmtetc, STGMEDIUM *medium, BOOL release);
255 STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ief);
256 STDMETHOD(DAdvise)(FORMATETC *, DWORD, IAdviseSink *, DWORD *);
257 STDMETHOD(DUnadvise)(DWORD);
258 STDMETHOD(EnumDAdvise)(LPENUMSTATDATA *);
259
UDataObjectUpp::UDataObject260 UDataObject() { rc = 1; effect = 0; }
261 };
262
263 struct UEnumFORMATETC : public IEnumFORMATETC {
264 ULONG rc;
265 int ii;
266 UDataObject *data;
267
268 STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
STDMETHOD_Upp::UEnumFORMATETC269 STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_Upp::UEnumFORMATETC270 STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
271
272 STDMETHOD(Next)(ULONG n, FORMATETC *fmtetc, ULONG *fetched);
273 STDMETHOD(Skip)(ULONG n);
274 STDMETHOD(Reset)(void);
275 STDMETHOD(Clone)(IEnumFORMATETC **newEnum);
276
UEnumFORMATETCUpp::UEnumFORMATETC277 UEnumFORMATETC() { ii = 0; rc = 1; }
~UEnumFORMATETCUpp::UEnumFORMATETC278 ~UEnumFORMATETC() { data->Release(); }
279 };
280
281 struct UDropSource : public IDropSource {
282 ULONG rc;
283 Image no, move, copy;
284
285 STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
STDMETHOD_Upp::UDropSource286 STDMETHOD_(ULONG, AddRef)(void) { return ++rc; }
STDMETHOD_Upp::UDropSource287 STDMETHOD_(ULONG, Release)(void) { if(--rc == 0) { delete this; return 0; } return rc; }
288
289 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
290 STDMETHOD(GiveFeedback)(DWORD dwEffect);
291
UDropSourceUpp::UDropSource292 UDropSource() { rc = 1; }
293 };
294
QueryInterface(REFIID iid,void ** ppv)295 STDMETHODIMP CLANG_NOTHROW UDataObject::QueryInterface(REFIID iid, void ** ppv)
296 {
297 if(iid == IID_IUnknown || iid == IID_IDataObject) {
298 *ppv = this;
299 AddRef();
300 return S_OK;
301 }
302 *ppv = NULL;
303 return E_NOINTERFACE;
304 }
305
SetMedium(STGMEDIUM * medium,const String & data)306 void SetMedium(STGMEDIUM *medium, const String& data)
307 {
308 int sz = data.GetCount();
309 HGLOBAL hData = GlobalAlloc(0, sz + 4);
310 if (hData) {
311 char *ptr = (char *) GlobalLock(hData);
312 memcpy(ptr, ~data, sz);
313 memset(ptr + sz, 0, 4);
314 GlobalUnlock(hData);
315 medium->tymed = TYMED_HGLOBAL;
316 medium->hGlobal = hData;
317 medium->pUnkForRelease = 0;
318 }
319 }
320
GetData(FORMATETC * fmtetc,STGMEDIUM * medium)321 STDMETHODIMP CLANG_NOTHROW UDataObject::GetData(FORMATETC *fmtetc, STGMEDIUM *medium)
322 {
323 String fmt = FromWin32CF(fmtetc->cfFormat);
324 ClipData *s = data.FindPtr(fmt);
325 if(s) {
326 String q = s->Render();
327 SetMedium(medium, q.GetCount() ? q : sDnDSource ? sDnDSource->GetDropData(fmt) : String());
328 return S_OK;
329 }
330 return DV_E_FORMATETC;
331 }
332
GetDataHere(FORMATETC *,STGMEDIUM *)333 STDMETHODIMP CLANG_NOTHROW UDataObject::GetDataHere(FORMATETC *, STGMEDIUM *)
334 {
335 return DV_E_FORMATETC;
336 }
337
QueryGetData(FORMATETC * fmtetc)338 STDMETHODIMP CLANG_NOTHROW UDataObject::QueryGetData(FORMATETC *fmtetc)
339 {
340 return data.Find(FromWin32CF(fmtetc->cfFormat)) >= 0 ? S_OK : DV_E_FORMATETC;
341 }
342
GetCanonicalFormatEtc(FORMATETC *,FORMATETC * pformatetcOut)343 STDMETHODIMP CLANG_NOTHROW UDataObject::GetCanonicalFormatEtc(FORMATETC *, FORMATETC *pformatetcOut)
344 {
345 pformatetcOut->ptd = NULL;
346 return E_NOTIMPL;
347 }
348
349 #ifdef PLATFORM_WINCE
350 static int CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat(_T("Performed DropEffect"));
351 #else
352 static int CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat("Performed DropEffect");
353 #endif
354
SetData(FORMATETC * fmtetc,STGMEDIUM * medium,BOOL release)355 STDMETHODIMP CLANG_NOTHROW UDataObject::SetData(FORMATETC *fmtetc, STGMEDIUM *medium, BOOL release)
356 {
357 if(fmtetc->cfFormat == CF_PERFORMEDDROPEFFECT && medium->tymed == TYMED_HGLOBAL) {
358 DWORD *val = (DWORD*)GlobalLock(medium->hGlobal);
359 effect = *val;
360 GlobalUnlock(medium->hGlobal);
361 if(release)
362 ReleaseStgMedium(medium);
363 return S_OK;
364 }
365 return E_NOTIMPL;
366 }
367
EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ief)368 STDMETHODIMP CLANG_NOTHROW UDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ief)
369 {
370 UEnumFORMATETC *ef = new UEnumFORMATETC;
371 ef->data = this;
372 AddRef();
373 *ief = ef;
374 return S_OK;
375 }
376
DAdvise(FORMATETC *,DWORD,IAdviseSink *,DWORD *)377 STDMETHODIMP CLANG_NOTHROW UDataObject::DAdvise(FORMATETC *, DWORD, IAdviseSink *, DWORD *)
378 {
379 return OLE_E_ADVISENOTSUPPORTED;
380 }
381
382
DUnadvise(DWORD)383 STDMETHODIMP CLANG_NOTHROW UDataObject::DUnadvise(DWORD)
384 {
385 return OLE_E_ADVISENOTSUPPORTED;
386 }
387
EnumDAdvise(LPENUMSTATDATA FAR *)388 STDMETHODIMP CLANG_NOTHROW UDataObject::EnumDAdvise(LPENUMSTATDATA FAR*)
389 {
390 return OLE_E_ADVISENOTSUPPORTED;
391 }
392
QueryInterface(REFIID riid,void FAR * FAR * ppvObj)393 STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
394 {
395 if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
396 *ppvObj = this;
397 AddRef();
398 return NOERROR;
399 }
400 *ppvObj = NULL;
401 return ResultFromScode(E_NOINTERFACE);
402 }
403
Next(ULONG n,FORMATETC * t,ULONG * fetched)404 STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Next(ULONG n, FORMATETC *t, ULONG *fetched) {
405 if(t == NULL)
406 return E_INVALIDARG;
407 if(fetched) *fetched = 0;
408 while(ii < data->data.GetCount() && n > 0) {
409 if(fetched) (*fetched)++;
410 n--;
411 *t++ = ToFORMATETC(data->data.GetKey(ii++));
412 }
413 return n ? S_FALSE : NOERROR;
414 }
415
Skip(ULONG n)416 STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Skip(ULONG n) {
417 ii += n;
418 if(ii >= data->data.GetCount())
419 return S_FALSE;
420 return NOERROR;
421 }
422
Reset()423 STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Reset()
424 {
425 ii = 0;
426 return NOERROR;
427 }
428
Clone(IEnumFORMATETC ** newEnum)429 STDMETHODIMP CLANG_NOTHROW UEnumFORMATETC::Clone(IEnumFORMATETC **newEnum)
430 {
431 if(newEnum == NULL)
432 return E_INVALIDARG;
433 UEnumFORMATETC *ef = new UEnumFORMATETC;
434 ef->data = data;
435 data->AddRef();
436 ef->ii = ii;
437 *newEnum = ef;
438 return NOERROR;
439 }
440
QueryInterface(REFIID riid,void ** ppvObj)441 STDMETHODIMP CLANG_NOTHROW UDropSource::QueryInterface(REFIID riid, void **ppvObj)
442 {
443 if (riid == IID_IUnknown || riid == IID_IDropSource) {
444 *ppvObj = this;
445 AddRef();
446 return NOERROR;
447 }
448 *ppvObj = NULL;
449 return ResultFromScode(E_NOINTERFACE);
450 }
451
QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)452 STDMETHODIMP CLANG_NOTHROW UDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
453 {
454 if(fEscapePressed)
455 return DRAGDROP_S_CANCEL;
456 else
457 if(!(grfKeyState & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)))
458 return DRAGDROP_S_DROP;
459 Ctrl::ProcessEvents();
460 return NOERROR;
461 }
462
GiveFeedback(DWORD dwEffect)463 STDMETHODIMP CLANG_NOTHROW UDropSource::GiveFeedback(DWORD dwEffect)
464 {
465 LLOG("GiveFeedback");
466 Image m = IsNull(move) ? copy : move;
467 if((dwEffect & DROPEFFECT_COPY) == DROPEFFECT_COPY) {
468 LLOG("GiveFeedback COPY");
469 if(!IsNull(copy)) m = copy;
470 }
471 else
472 if((dwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) {
473 LLOG("GiveFeedback MOVE");
474 if(!IsNull(move)) m = move;
475 }
476 else
477 m = no;
478 Ctrl::OverrideCursor(m);
479 Ctrl::SetMouseCursor(m);
480 return S_OK;
481 }
482
483 Image MakeDragImage(const Image& arrow, Image sample);
484
MakeDragImage(const Image & arrow,const Image & arrow98,Image sample)485 Image MakeDragImage(const Image& arrow, const Image& arrow98, Image sample)
486 {
487 if(IsWin2K())
488 return MakeDragImage(arrow, sample);
489 else
490 return arrow98;
491 }
492
DoDragAndDrop(const char * fmts,const Image & sample,dword actions,const VectorMap<String,ClipData> & data)493 int Ctrl::DoDragAndDrop(const char *fmts, const Image& sample, dword actions,
494 const VectorMap<String, ClipData>& data)
495 {
496 UDataObject *obj = new UDataObject;
497 obj->data <<= data;
498 if(fmts) {
499 Vector<String> f = Split(fmts, ';');
500 for(int i = 0; i < f.GetCount(); i++)
501 obj->data.GetAdd(f[i]);
502 }
503 UDropSource *dsrc = new UDropSource;
504 DWORD result = 0;
505 Image m = Ctrl::OverrideCursor(CtrlCoreImg::DndMove());
506 dsrc->no = MakeDragImage(CtrlCoreImg::DndNone(), CtrlCoreImg::DndNone98(), sample);
507 if(actions & DND_COPY)
508 dsrc->copy = actions & DND_EXACTIMAGE ? sample : MakeDragImage(CtrlCoreImg::DndCopy(), CtrlCoreImg::DndCopy98(), sample);
509 if(actions & DND_MOVE)
510 dsrc->move = actions & DND_EXACTIMAGE ? sample : MakeDragImage(CtrlCoreImg::DndMove(), CtrlCoreImg::DndMove98(), sample);
511 sDnDSource = this;
512 int level = LeaveGuiMutexAll();
513 HRESULT r = DoDragDrop(obj, dsrc,
514 (actions & DND_COPY ? DROPEFFECT_COPY : 0) |
515 (actions & DND_MOVE ? DROPEFFECT_MOVE : 0), &result);
516 EnterGuiMutex(level);
517 DWORD re = obj->effect;
518 obj->Release();
519 dsrc->Release();
520 OverrideCursor(m);
521 SyncCaret();
522 CheckMouseCtrl();
523 KillRepeat();
524 sDnDSource = NULL;
525 if(r == DRAGDROP_S_DROP) {
526 if(((result | re) & DROPEFFECT_MOVE) == DROPEFFECT_MOVE && (actions & DND_MOVE))
527 return DND_MOVE;
528 if(((result | re) & DROPEFFECT_COPY) == DROPEFFECT_COPY && (actions & DND_COPY))
529 return DND_COPY;
530 }
531 return DND_NONE;
532 }
533
ReleaseUDropTarget(UDropTarget * dt)534 void ReleaseUDropTarget(UDropTarget *dt)
535 {
536 dt->Release();
537 }
538
NewUDropTarget(Ctrl * ctrl)539 UDropTarget *NewUDropTarget(Ctrl *ctrl)
540 {
541 UDropTarget *dt = new UDropTarget;
542 dt->ctrl = ctrl;
543 return dt;
544 }
545
SetSelectionSource(const char * fmts)546 void Ctrl::SetSelectionSource(const char *fmts) {}
547
548 }
549
550 #endif
551