1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4 
5 #include "win32dragging.h"
6 #include "win32support.h"
7 #include "winstring.h"
8 #include "../../cstring.h"
9 #include "../../cdrawcontext.h"
10 #include "../../cvstguitimer.h"
11 #include "win32dll.h"
12 #include <dwmapi.h>
13 #include <shlobj.h>
14 
15 #ifdef _MSC_VER
16 #pragma comment(lib, "Dwmapi.lib")
17 #endif
18 
19 namespace VSTGUI {
20 
21 //-----------------------------------------------------------------------------
22 class Win32DragBitmapWindow
23 {
24 public:
25 	Win32DragBitmapWindow (const SharedPointer<CBitmap>& bitmap, CPoint offset);
26 	~Win32DragBitmapWindow () noexcept;
27 
28 	void updateBitmap (const SharedPointer<CBitmap>& bitmap, CPoint offset);
29 	void mouseChanged ();
30 private:
31 	static LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
32 	LRESULT proc (UINT message, WPARAM wParam, LPARAM lParam);
33 
34 	void registerWindowClass ();
35 	void unregisterWindowClass ();
36 
37 	void createWindow ();
38 	void releaseWindow ();
39 	void showWindow ();
40 
41 	POINT calculateWindowPosition () const;
42 	void updateScaleFactor (POINT p);
43 	void updateWindowPosition (POINT where);
44 	void setAlphaTransparency (float alpha);
45 
46 	void paint ();
47 
48 	SharedPointer<CBitmap> bitmap;
49 	CPoint offset;
50 	UTF8String windowClassName;
51 	HWND hwnd {nullptr};
52 	CCoord scaleFactor {2.};
53 };
54 
55 //-----------------------------------------------------------------------------
56 class Win32MouseObserverWhileDragging
57 {
58 public:
59 	using Callback = std::function<void ()>;
60 
Win32MouseObserverWhileDragging()61 	Win32MouseObserverWhileDragging ()
62 	{
63 		gInstance = this;
64 		mouseHook = SetWindowsHookEx (WH_MOUSE, MouseHookProc, GetInstance (), GetCurrentThreadId ());
65 	}
66 
~Win32MouseObserverWhileDragging()67 	~Win32MouseObserverWhileDragging () noexcept
68 	{
69 		if (mouseHook)
70 			UnhookWindowsHookEx (mouseHook);
71 		gInstance = nullptr;
72 	}
73 
registerCallback(Callback && c)74 	void registerCallback (Callback&& c) { callbacks.emplace_back (std::move (c)); }
75 private:
76 	static Win32MouseObserverWhileDragging* gInstance;
MouseHookProc(int nCode,WPARAM wParam,LPARAM lParam)77 	static LRESULT CALLBACK MouseHookProc (int nCode, WPARAM wParam, LPARAM lParam)
78 	{
79 		if (gInstance)
80 		{
81 			for (auto& c : gInstance->callbacks)
82 				c ();
83 		}
84 		return CallNextHookEx (nullptr, nCode, wParam, lParam);
85 	}
86 	HHOOK mouseHook {nullptr};
87 	std::vector<Callback> callbacks;
88 };
89 Win32MouseObserverWhileDragging* Win32MouseObserverWhileDragging::gInstance = nullptr;
90 
91 //-----------------------------------------------------------------------------
Win32DraggingSession(Win32Frame * frame)92 Win32DraggingSession::Win32DraggingSession (Win32Frame* frame)
93 : frame (frame)
94 {
95 }
96 
97 //-----------------------------------------------------------------------------
98 Win32DraggingSession::~Win32DraggingSession () noexcept = default;
99 
100 //-----------------------------------------------------------------------------
setBitmap(const SharedPointer<CBitmap> & bitmap,CPoint offset)101 bool Win32DraggingSession::setBitmap (const SharedPointer<CBitmap>& bitmap, CPoint offset)
102 {
103 	if (!dragBitmapWindow && bitmap)
104 	{
105 		dragBitmapWindow = std::make_unique<Win32DragBitmapWindow> (bitmap,
106 		                                                            offset);
107 		if (!mouseObserver)
108 			mouseObserver = std::make_unique<Win32MouseObserverWhileDragging> ();
109 
110 		mouseObserver->registerCallback ([&] () { dragBitmapWindow->mouseChanged (); });
111 	}
112 	if (dragBitmapWindow)
113 	{
114 		dragBitmapWindow->updateBitmap (bitmap, offset);
115 	}
116 	return true;
117 }
118 
119 //-----------------------------------------------------------------------------
doDrag(const DragDescription & dragDescription,const SharedPointer<IDragCallback> & callback)120 bool Win32DraggingSession::doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback)
121 {
122 	auto lastCursor = frame->getLastSetCursor ();
123 	frame->setMouseCursor (kCursorNotAllowed);
124 
125 	if (callback)
126 		mouseObserver = std::make_unique<Win32MouseObserverWhileDragging> ();
127 
128 	if (callback)
129 	{
130 		CPoint location;
131 		frame->getCurrentMousePosition (location);
132 		callback->dragWillBegin (this, location);
133 
134 		if (mouseObserver)
135 		{
136 			mouseObserver->registerCallback ([callback, location, this] () mutable {
137 				CPoint newLocation;
138 				frame->getCurrentMousePosition (newLocation);
139 				if (newLocation != location)
140 				{
141 					callback->dragMoved (this, newLocation);
142 					location = newLocation;
143 				}
144 			});
145 		}
146 	}
147 
148 	setBitmap (dragDescription.bitmap, dragDescription.bitmapOffset);
149 
150 	auto dataObject = new Win32DataObject (dragDescription.data);
151 	auto dropSource = new Win32DropSource ();
152 	DWORD outEffect;
153 
154 	auto hResult = DoDragDrop (dataObject, dropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE, &outEffect);
155 	if (mouseObserver)
156 		mouseObserver = nullptr;
157 	if (dragBitmapWindow)
158 		dragBitmapWindow = nullptr;
159 
160 	frame->setMouseCursor (lastCursor);
161 
162 	if (callback)
163 	{
164 		CPoint location;
165 		frame->getCurrentMousePosition (location);
166 		if (hResult == DRAGDROP_S_DROP)
167 		{
168 			if (outEffect == DROPEFFECT_MOVE)
169 				callback->dragEnded (this, location, DragOperation::Move);
170 			else
171 				callback->dragEnded (this, location, DragOperation::Copy);
172 		}
173 		else
174 		{
175 			callback->dragEnded (this, location, DragOperation::None);
176 		}
177 	}
178 
179 	dataObject->Release ();
180 	dropSource->Release ();
181 
182 	return true;
183 }
184 
185 //-----------------------------------------------------------------------------
CDropTarget(Win32Frame * pFrame)186 CDropTarget::CDropTarget (Win32Frame* pFrame)
187 : refCount (0)
188 , pFrame (pFrame)
189 , dragData (nullptr)
190 {
191 }
192 
193 //-----------------------------------------------------------------------------
~CDropTarget()194 CDropTarget::~CDropTarget () noexcept
195 {
196 }
197 
198 //-----------------------------------------------------------------------------
QueryInterface(REFIID riid,void ** object)199 COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::QueryInterface (REFIID riid, void** object)
200 {
201 	if (riid == IID_IDropTarget || riid == IID_IUnknown)
202 	{
203 		*object = this;
204 		AddRef ();
205       return NOERROR;
206 	}
207 	*object = nullptr;
208 	return E_NOINTERFACE;
209 }
210 
211 //-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)212 COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) CDropTarget::AddRef (void)
213 {
214 	return static_cast<ULONG> (++refCount);
215 }
216 
217 //-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)218 COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) CDropTarget::Release (void)
219 {
220 	refCount--;
221 	if (refCount <= 0)
222 	{
223 		delete this;
224 		return 0;
225 	}
226 	return static_cast<ULONG> (refCount);
227 }
228 
229 //-----------------------------------------------------------------------------
DragEnter(IDataObject * dataObject,DWORD keyState,POINTL pt,DWORD * effect)230 COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragEnter (IDataObject* dataObject, DWORD keyState, POINTL pt, DWORD* effect)
231 {
232 	if (dataObject && pFrame)
233 	{
234 		dragData = new Win32DataPackage (dataObject);
235 
236 		DragEventData data;
237 		data.drag = dragData;
238 		pFrame->getCurrentMousePosition (data.pos);
239 		pFrame->getCurrentMouseButtons (data.modifiers);
240 		auto result = pFrame->getFrame ()->platformOnDragEnter (data);
241 		if (result == DragOperation::Copy)
242 			*effect = DROPEFFECT_COPY;
243 		else if (result == DragOperation::Move)
244 			*effect = DROPEFFECT_MOVE;
245 		else
246 			*effect = DROPEFFECT_NONE;
247 	}
248 	else
249 		*effect = DROPEFFECT_NONE;
250 	return S_OK;
251 }
252 
253 //-----------------------------------------------------------------------------
DragOver(DWORD keyState,POINTL pt,DWORD * effect)254 COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragOver (DWORD keyState, POINTL pt, DWORD* effect)
255 {
256 	if (dragData && pFrame)
257 	{
258 		DragEventData data;
259 		data.drag = dragData;
260 		pFrame->getCurrentMousePosition (data.pos);
261 		pFrame->getCurrentMouseButtons (data.modifiers);
262 		auto result = pFrame->getFrame ()->platformOnDragMove (data);
263 		if (result == DragOperation::Copy)
264 			*effect = DROPEFFECT_COPY;
265 		else if (result == DragOperation::Move)
266 			*effect = DROPEFFECT_MOVE;
267 		else
268 			*effect = DROPEFFECT_NONE;
269 	}
270 	return S_OK;
271 }
272 
273 //-----------------------------------------------------------------------------
DragLeave(void)274 COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragLeave (void)
275 {
276 	if (dragData && pFrame)
277 	{
278 		DragEventData data;
279 		data.drag = dragData;
280 		pFrame->getCurrentMousePosition (data.pos);
281 		pFrame->getCurrentMouseButtons (data.modifiers);
282 		pFrame->getFrame ()->platformOnDragLeave (data);
283 		dragData->forget ();
284 		dragData = nullptr;
285 	}
286 	return S_OK;
287 }
288 
289 //-----------------------------------------------------------------------------
Drop(IDataObject * dataObject,DWORD keyState,POINTL pt,DWORD * effect)290 COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::Drop (IDataObject* dataObject, DWORD keyState, POINTL pt, DWORD* effect)
291 {
292 	if (dragData && pFrame)
293 	{
294 		DragEventData data;
295 		data.drag = dragData;
296 		pFrame->getCurrentMousePosition (data.pos);
297 		pFrame->getCurrentMouseButtons (data.modifiers);
298 		pFrame->getFrame ()->platformOnDrop (data);
299 		dragData->forget ();
300 		dragData = nullptr;
301 	}
302 	return S_OK;
303 }
304 
305 //-----------------------------------------------------------------------------
306 // Win32DropSource
307 //-----------------------------------------------------------------------------
QueryInterface(REFIID riid,void ** object)308 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DropSource::QueryInterface (REFIID riid, void** object)
309 {
310 	if (riid == ::IID_IDropSource)
311 	{
312 		AddRef ();
313 		*object = (::IDropSource*)this;
314 		return S_OK;
315 	}
316 	else if (riid == ::IID_IUnknown)
317 	{
318 		AddRef ();
319 		*object = (::IUnknown*)this;
320 		return S_OK;
321 	}
322 	return E_NOINTERFACE;
323 }
324 
325 //-----------------------------------------------------------------------------
QueryContinueDrag(BOOL escapePressed,DWORD keyState)326 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DropSource::QueryContinueDrag (BOOL escapePressed, DWORD keyState)
327 {
328 	if (escapePressed)
329 		return DRAGDROP_S_CANCEL;
330 
331 	if ((keyState & (MK_LBUTTON|MK_RBUTTON)) == 0)
332 		return DRAGDROP_S_DROP;
333 
334 	return S_OK;
335 }
336 
337 //-----------------------------------------------------------------------------
GiveFeedback(DWORD effect)338 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DropSource::GiveFeedback (DWORD effect)
339 {
340 	return DRAGDROP_S_USEDEFAULTCURSORS;
341 }
342 
343 //-----------------------------------------------------------------------------
344 // DataObject
345 //-----------------------------------------------------------------------------
Win32DataObject(IDataPackage * dataPackage)346 Win32DataObject::Win32DataObject (IDataPackage* dataPackage)
347 : dataPackage (dataPackage)
348 {
349 	dataPackage->remember ();
350 }
351 
352 //-----------------------------------------------------------------------------
~Win32DataObject()353 Win32DataObject::~Win32DataObject () noexcept
354 {
355 	dataPackage->forget ();
356 }
357 
358 //-----------------------------------------------------------------------------
QueryInterface(REFIID riid,void ** object)359 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::QueryInterface (REFIID riid, void** object)
360 {
361 	if (riid == ::IID_IDataObject)
362 	{
363 		AddRef ();
364 		*object = (::IDataObject*)this;
365 		return S_OK;
366 	}
367 	else if (riid == ::IID_IUnknown)
368 	{
369 		AddRef ();
370 		*object = (::IUnknown*)this;
371 		return S_OK;
372 	}
373 	return E_NOINTERFACE;
374 }
375 
376 //-----------------------------------------------------------------------------
GetData(FORMATETC * format,STGMEDIUM * medium)377 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::GetData (FORMATETC* format, STGMEDIUM* medium)
378 {
379 	medium->tymed = 0;
380 	medium->hGlobal = nullptr;
381 	medium->pUnkForRelease = nullptr;
382 
383 	if (format->cfFormat == CF_TEXT || format->cfFormat == CF_UNICODETEXT)
384 	{
385 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
386 		{
387 			if (dataPackage->getDataType (i) == IDataPackage::kText)
388 			{
389 				const void* buffer;
390 				IDataPackage::Type type;
391 				uint32_t bufferSize = dataPackage->getData (i, buffer, type);
392 				UTF8StringHelper utf8String (static_cast<const char*> (buffer), bufferSize);
393 				SIZE_T size = 0;
394 				const void* data = nullptr;
395 				if (format->cfFormat == CF_UNICODETEXT)
396 				{
397 					size = bufferSize * sizeof (WCHAR);
398 					data = utf8String.getWideString ();
399 				}
400 				else
401 				{
402 					size = bufferSize * sizeof (char);
403 					data = buffer;
404 				}
405 				if (data && size > 0)
406 				{
407 					HGLOBAL	memoryHandle = GlobalAlloc (GMEM_MOVEABLE, size);
408 					void* memory = GlobalLock (memoryHandle);
409 					if (memory)
410 					{
411 						memcpy (memory, data, size);
412 						GlobalUnlock (memoryHandle);
413 					}
414 
415 					medium->hGlobal = memoryHandle;
416 					medium->tymed = TYMED_HGLOBAL;
417 					return S_OK;
418 				}
419 			}
420 		}
421 	}
422 	else if (format->cfFormat == CF_HDROP)
423 	{
424 		HRESULT result = E_UNEXPECTED;
425 		UTF8StringHelper** wideStringFileNames = (UTF8StringHelper**)std::malloc (sizeof (UTF8StringHelper*) * dataPackage->getCount ());
426 		memset (wideStringFileNames, 0, sizeof (UTF8StringHelper*) * dataPackage->getCount ());
427 		uint32_t fileNamesIndex = 0;
428 		uint32_t bufferSizeNeeded = 0;
429 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
430 		{
431 			if (dataPackage->getDataType (i) == IDataPackage::kFilePath)
432 			{
433 				const void* buffer;
434 				IDataPackage::Type type;
435 				dataPackage->getData (i, buffer, type);
436 
437 				wideStringFileNames[fileNamesIndex] = new UTF8StringHelper ((UTF8StringPtr)buffer);
438 				bufferSizeNeeded += static_cast<uint32_t> (wcslen (*wideStringFileNames[fileNamesIndex])) + 1;
439 				fileNamesIndex++;
440 			}
441 		}
442 		bufferSizeNeeded++;
443 		bufferSizeNeeded *= sizeof (WCHAR);
444 		bufferSizeNeeded += sizeof (DROPFILES);
445 		HGLOBAL	memoryHandle = GlobalAlloc (GMEM_MOVEABLE, bufferSizeNeeded);
446 		void* memory = GlobalLock (memoryHandle);
447 		if (memory)
448 		{
449 			DROPFILES* dropFiles = (DROPFILES*)memory;
450 			dropFiles->pFiles = sizeof (DROPFILES);
451 			dropFiles->pt.x   = 0;
452 			dropFiles->pt.y   = 0;
453 			dropFiles->fNC    = FALSE;
454 			dropFiles->fWide  = TRUE;
455 			int8_t* memAddr = ((int8_t*)memory) + sizeof (DROPFILES);
456 			for (uint32_t i = 0; i < fileNamesIndex; i++)
457 			{
458 				size_t len = (wcslen (wideStringFileNames[i]->getWideString ()) + 1) * 2;
459 				memcpy (memAddr, wideStringFileNames[i]->getWideString (), len);
460 				memAddr += len;
461 			}
462 			*memAddr = 0;
463 			memAddr++;
464 			*memAddr = 0;
465 			memAddr++;
466 			GlobalUnlock (memoryHandle);
467 			medium->hGlobal = memoryHandle;
468 			medium->tymed = TYMED_HGLOBAL;
469 			result = S_OK;
470 		}
471 		for (uint32_t i = 0; i < fileNamesIndex; i++)
472 			delete wideStringFileNames[i];
473 		std::free (wideStringFileNames);
474 		return result;
475 	}
476 	else if (format->cfFormat == CF_PRIVATEFIRST)
477 	{
478 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
479 		{
480 			if (dataPackage->getDataType (i) == IDataPackage::kBinary)
481 			{
482 				const void* buffer;
483 				IDataPackage::Type type;
484 				uint32_t bufferSize = dataPackage->getData (i, buffer, type);
485 
486 				HGLOBAL	memoryHandle = GlobalAlloc (GMEM_MOVEABLE, bufferSize);
487 				void* memory = GlobalLock (memoryHandle);
488 				if (memory)
489 				{
490 					memcpy (memory, buffer, bufferSize);
491 					GlobalUnlock (memoryHandle);
492 				}
493 
494 				medium->hGlobal = memoryHandle;
495 				medium->tymed = TYMED_HGLOBAL;
496 				return S_OK;
497 			}
498 		}
499 	}
500 
501 	return E_UNEXPECTED;
502 }
503 
504 //-----------------------------------------------------------------------------
GetDataHere(FORMATETC * format,STGMEDIUM * pmedium)505 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::GetDataHere (FORMATETC *format, STGMEDIUM *pmedium)
506 {
507 	return E_NOTIMPL;
508 }
509 
510 //-----------------------------------------------------------------------------
QueryGetData(FORMATETC * format)511 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::QueryGetData (FORMATETC *format)
512 {
513 	if (format->cfFormat == CF_TEXT || format->cfFormat == CF_UNICODETEXT)
514 	{
515 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
516 		{
517 			if (dataPackage->getDataType (i) == IDataPackage::kText)
518 				return S_OK;
519 		}
520 	}
521 	else if (format->cfFormat == CF_PRIVATEFIRST)
522 	{
523 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
524 		{
525 			if (dataPackage->getDataType (i) == IDataPackage::kBinary)
526 				return S_OK;
527 		}
528 	}
529 	else if (format->cfFormat == CF_HDROP)
530 	{
531 		for (uint32_t i = 0; i < dataPackage->getCount (); i++)
532 		{
533 			if (dataPackage->getDataType (i) == IDataPackage::kFilePath)
534 				return S_OK;
535 		}
536 	}
537 	return DV_E_FORMATETC;
538 }
539 
540 //-----------------------------------------------------------------------------
GetCanonicalFormatEtc(FORMATETC * formatIn,FORMATETC * formatOut)541 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::GetCanonicalFormatEtc (FORMATETC *formatIn, FORMATETC *formatOut)
542 {
543 	return E_NOTIMPL;
544 }
545 
546 //-----------------------------------------------------------------------------
SetData(FORMATETC * pformatetc,STGMEDIUM * pmedium,BOOL fRelease)547 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::SetData (FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
548 {
549 	return E_NOTIMPL;
550 }
551 
552 //-----------------------------------------------------------------------------
553 struct Win32DataObjectEnumerator : IEnumFORMATETC, AtomicReferenceCounted
554 {
Win32DataObjectEnumeratorVSTGUI::Win32DataObjectEnumerator555 	Win32DataObjectEnumerator (const SharedPointer<IDataPackage>& data) : data (data) {}
556 
QueryInterfaceVSTGUI::Win32DataObjectEnumerator557 	COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE QueryInterface (REFIID riid, void** object) override
558 	{
559 		if (riid == ::IID_IEnumFORMATETC)
560 		{
561 			AddRef ();
562 			*object = (::IEnumFORMATETC*)this;
563 			return S_OK;
564 		}
565 		else if (riid == ::IID_IUnknown)
566 		{
567 			AddRef ();
568 			*object = (::IUnknown*)this;
569 			return S_OK;
570 		}
571 		return E_NOINTERFACE;
572 	}
573 
AddRefVSTGUI::Win32DataObjectEnumerator574 	COM_DECLSPEC_NOTHROW ULONG STDMETHODCALLTYPE AddRef (void) override
575 	{
576 		remember ();
577 		return static_cast<ULONG> (getNbReference ());
578 	}
ReleaseVSTGUI::Win32DataObjectEnumerator579 	COM_DECLSPEC_NOTHROW ULONG STDMETHODCALLTYPE Release (void) override
580 	{
581 		ULONG refCount = static_cast<ULONG> (getNbReference ()) - 1;
582 		forget ();
583 		return refCount;
584 	}
585 
NextVSTGUI::Win32DataObjectEnumerator586 	COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE Next (ULONG celt, FORMATETC* rgelt, ULONG* pceltFetched) override
587 	{
588 		if (!rgelt)
589 			return E_INVALIDARG;
590 		const void* buffer;
591 		IDataPackage::Type dataType;
592 		auto dataSize = data->getData (index++, buffer, dataType);
593 		if (dataSize)
594 		{
595 			if (dataType == IDataPackage::kText)
596 			{
597 				rgelt->cfFormat = CF_UNICODETEXT;
598 				rgelt->ptd = nullptr;
599 				rgelt->dwAspect = DVASPECT_CONTENT;
600 				rgelt->lindex = -1;
601 				rgelt->tymed = TYMED_HGLOBAL;
602 				return S_OK;
603 			}
604 			if (dataType == IDataPackage::kFilePath)
605 			{
606 #if DEBUG
607 				DebugPrint ("IDataPackage::kFilePath not yet tested!\n");
608 #endif
609 				rgelt->cfFormat = CF_HDROP;
610 				rgelt->ptd = nullptr;
611 				rgelt->dwAspect = DVASPECT_CONTENT;
612 				rgelt->lindex = -1;
613 				rgelt->tymed = TYMED_HGLOBAL;
614 				return S_OK;
615 			}
616 		}
617 		return S_FALSE;
618 	}
619 
SkipVSTGUI::Win32DataObjectEnumerator620 	COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE Skip (ULONG celt) override
621 	{
622 		if (index + celt >= data->getCount ())
623 			return S_FALSE;
624 		index += celt;
625 		return S_OK;
626 	}
627 
ResetVSTGUI::Win32DataObjectEnumerator628 	COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE Reset (void) override
629 	{
630 		index = 0;
631 		return S_OK;
632 	}
633 
CloneVSTGUI::Win32DataObjectEnumerator634 	COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE Clone (IEnumFORMATETC** ppenum) override
635 	{
636 		return E_NOTIMPL;
637 	}
638 
639 	SharedPointer<IDataPackage> data;
640 	uint32_t index {0};
641 };
642 
643 //-----------------------------------------------------------------------------
EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppenumFormatEtc)644 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
645 {
646 	if (dwDirection != DATADIR_GET)
647 		return E_INVALIDARG;
648 
649 	auto enumerator = new Win32DataObjectEnumerator (dataPackage);
650 	*ppenumFormatEtc = enumerator;
651 	return S_OK;
652 }
653 
654 //-----------------------------------------------------------------------------
DAdvise(FORMATETC * pformatetc,DWORD advf,IAdviseSink * pAdvSink,DWORD * pdwConnection)655 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::DAdvise (FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink, DWORD* pdwConnection)
656 {
657 	return E_NOTIMPL;
658 }
659 
660 //-----------------------------------------------------------------------------
DUnadvise(DWORD dwConnection)661 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::DUnadvise (DWORD dwConnection)
662 {
663 	return E_NOTIMPL;
664 }
665 
666 //-----------------------------------------------------------------------------
EnumDAdvise(IEnumSTATDATA ** ppenumAdvise)667 COM_DECLSPEC_NOTHROW STDMETHODIMP Win32DataObject::EnumDAdvise (IEnumSTATDATA** ppenumAdvise)
668 {
669 	return E_NOTIMPL;
670 }
671 
672 //-----------------------------------------------------------------------------
Win32DragBitmapWindow(const SharedPointer<CBitmap> & bitmap,CPoint offset)673 Win32DragBitmapWindow::Win32DragBitmapWindow (const SharedPointer<CBitmap>& bitmap, CPoint offset)
674 : bitmap (bitmap)
675 , offset (offset)
676 {
677 	registerWindowClass ();
678 	auto initialWindowPosition = calculateWindowPosition ();
679 	updateScaleFactor (initialWindowPosition);
680 	createWindow ();
681 	setAlphaTransparency (1.f);
682 	updateWindowPosition (initialWindowPosition);
683 	showWindow ();
684 }
685 
686 //-----------------------------------------------------------------------------
~Win32DragBitmapWindow()687 Win32DragBitmapWindow::~Win32DragBitmapWindow () noexcept
688 {
689 	releaseWindow ();
690 	unregisterWindowClass ();
691 }
692 
693 //-----------------------------------------------------------------------------
updateBitmap(const SharedPointer<CBitmap> & bitmap,CPoint offset)694 void Win32DragBitmapWindow::updateBitmap (const SharedPointer<CBitmap>& bitmap, CPoint offset)
695 {
696 	this->bitmap = bitmap;
697 	this->offset = offset;
698 	updateWindowPosition (calculateWindowPosition ());
699 	RECT r;
700 	GetClientRect (hwnd, &r);
701 	InvalidateRect (hwnd, &r, FALSE);
702 }
703 
704 //-----------------------------------------------------------------------------
createWindow()705 void Win32DragBitmapWindow::createWindow ()
706 {
707 	auto winString = dynamic_cast<WinString*> (windowClassName.getPlatformString ());
708 
709 	DWORD exStyle = WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW;
710 	DWORD dwStyle = WS_POPUP;
711 
712 	auto width = static_cast<int> (bitmap->getWidth () * scaleFactor);
713 	auto height = static_cast<int> (bitmap->getHeight () * scaleFactor);
714 
715 	hwnd = CreateWindowEx (exStyle, winString->getWideString (), nullptr, dwStyle, -1000, -1000, width,
716 	                       height, nullptr, nullptr, GetInstance (), nullptr);
717 	SetWindowLongPtr (hwnd, GWLP_USERDATA, (__int3264) (LONG_PTR) this);
718 
719 	MARGINS margin = { -1 };
720 	auto res = DwmExtendFrameIntoClientArea (hwnd, &margin);
721 	vstgui_assert (res == S_OK);
722 }
723 
724 //-----------------------------------------------------------------------------
releaseWindow()725 void Win32DragBitmapWindow::releaseWindow ()
726 {
727 	if (hwnd)
728 		DestroyWindow (hwnd);
729 }
730 
731 //-----------------------------------------------------------------------------
showWindow()732 void Win32DragBitmapWindow::showWindow ()
733 {
734 	ShowWindow (hwnd, SW_SHOWNOACTIVATE);
735 }
736 
737 //-----------------------------------------------------------------------------
setAlphaTransparency(float alpha)738 void Win32DragBitmapWindow::setAlphaTransparency (float alpha)
739 {
740 	if (!hwnd)
741 		return;
742 	SetLayeredWindowAttributes (hwnd, 0, static_cast<BYTE> (alpha * 255.), LWA_ALPHA);
743 }
744 
745 //-----------------------------------------------------------------------------
calculateWindowPosition() const746 POINT Win32DragBitmapWindow::calculateWindowPosition () const
747 {
748 	POINT where;
749 	GetCursorPos (&where);
750 	where.x += static_cast<int> (offset.x * scaleFactor);
751 	where.y += static_cast<int> (offset.y * scaleFactor);
752 
753 	return where;
754 }
755 
756 //-----------------------------------------------------------------------------
updateScaleFactor(POINT p)757 void Win32DragBitmapWindow::updateScaleFactor (POINT p)
758 {
759 	auto monitor = MonitorFromPoint (p, MONITOR_DEFAULTTONEAREST);
760 	UINT dpiX, dpiY;
761 	HiDPISupport::instance ().getDPIForMonitor (monitor, HiDPISupport::MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
762 	vstgui_assert (dpiX == dpiY);
763 
764 	scaleFactor = static_cast<CCoord> (dpiX) / 96.;
765 }
766 
767 //-----------------------------------------------------------------------------
updateWindowPosition(POINT where)768 void Win32DragBitmapWindow::updateWindowPosition (POINT where)
769 {
770 	auto width = bitmap ? static_cast<int> (bitmap->getWidth () * scaleFactor) : 0;
771 	auto height = bitmap ? static_cast<int> (bitmap->getHeight () * scaleFactor) : 0;
772 
773 	SetWindowPos (hwnd, nullptr, where.x, where.y, width, height, SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
774 }
775 
776 //-----------------------------------------------------------------------------
paint()777 void Win32DragBitmapWindow::paint ()
778 {
779 	PAINTSTRUCT ps;
780 	HDC hdc = BeginPaint (hwnd, &ps);
781 
782 	RECT clientRect;
783 	GetClientRect (hwnd, &clientRect);
784 
785 	CRect rect;
786 	rect.setWidth (clientRect.right - clientRect.left);
787 	rect.setHeight (clientRect.bottom - clientRect.top);
788 
789 	if (auto drawContext = owned (createDrawContext (hwnd, hdc, rect)))
790 	{
791 		drawContext->beginDraw ();
792 
793 		drawContext->clearRect (rect);
794 		drawContext->setGlobalAlpha (0.9f);
795 		CDrawContext::Transform t (*drawContext, CGraphicsTransform ().scale (scaleFactor, scaleFactor));
796 		bitmap->draw (drawContext, rect);
797 
798 		drawContext->endDraw ();
799 	}
800 	EndPaint (hwnd, &ps);
801 }
802 
803 //-----------------------------------------------------------------------------
proc(UINT message,WPARAM wParam,LPARAM lParam)804 LRESULT Win32DragBitmapWindow::proc (UINT message, WPARAM wParam, LPARAM lParam)
805 {
806 	switch (message)
807 	{
808 		case WM_DPICHANGED:
809 		{
810 			scaleFactor = static_cast<CCoord> (HIWORD (wParam)) / 96.;
811 			updateWindowPosition (calculateWindowPosition ());
812 			break;
813 		}
814 		case WM_GETMINMAXINFO:
815 		{
816 			auto minMax = reinterpret_cast<MINMAXINFO*> (lParam);
817 			minMax->ptMinTrackSize.x = 1;
818 			minMax->ptMinTrackSize.y = 1;
819 			break;
820 		}
821 		case WM_ERASEBKGND:
822 		{
823 			return 0;
824 		}
825 		case WM_PAINT:
826 		{
827 			paint ();
828 			return 0;
829 		}
830 		case WM_NCCALCSIZE:
831 		{
832 			return 0;
833 		}
834 		case WM_NCHITTEST:
835 		{
836 			return 0;
837 		}
838 	}
839 	return DefWindowProc (hwnd, message, wParam, lParam);
840 }
841 
842 //-----------------------------------------------------------------------------
mouseChanged()843 void Win32DragBitmapWindow::mouseChanged ()
844 {
845 	updateWindowPosition (calculateWindowPosition ());
846 }
847 
848 //-----------------------------------------------------------------------------
WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)849 LRESULT CALLBACK Win32DragBitmapWindow::WndProc (HWND hWnd, UINT message, WPARAM wParam,
850                                                  LPARAM lParam)
851 {
852 	auto window = reinterpret_cast<Win32DragBitmapWindow*> ((LONG_PTR)GetWindowLongPtr (hWnd, GWLP_USERDATA));
853 	if (window)
854 		return window->proc (message, wParam, lParam);
855 	return DefWindowProc (hWnd, message, wParam, lParam);
856 }
857 
858 //-----------------------------------------------------------------------------
registerWindowClass()859 void Win32DragBitmapWindow::registerWindowClass ()
860 {
861 	char tmp[32];
862 	sprintf_s (tmp, 32, "%p", this);
863 	windowClassName = "VSTGUI DragBitmap Window ";
864 	windowClassName += tmp;
865 
866 	auto winString = dynamic_cast<WinString*> (windowClassName.getPlatformString ());
867 
868 	WNDCLASSEX wcex {};
869 
870 	wcex.cbSize = sizeof (WNDCLASSEX);
871 
872 	wcex.style = 0;
873 	wcex.lpfnWndProc = WndProc;
874 	wcex.hInstance = GetInstance ();
875 	wcex.hCursor = LoadCursor (GetInstance (), IDC_ARROW);
876 	wcex.hbrBackground = nullptr;
877 	wcex.lpszClassName = winString->getWideString ();
878 
879 	RegisterClassEx (&wcex);
880 }
881 
882 //-----------------------------------------------------------------------------
unregisterWindowClass()883 void Win32DragBitmapWindow::unregisterWindowClass ()
884 {
885 	auto winString = dynamic_cast<WinString*> (windowClassName.getPlatformString ());
886 	UnregisterClass (winString->getWideString (), GetInstance ());
887 }
888 
889 } // VSTGUI
890