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 "win32frame.h"
6 
7 #if WINDOWS
8 
9 #include <commctrl.h>
10 #include <cmath>
11 #include <windowsx.h>
12 #include "direct2d/d2ddrawcontext.h"
13 #include "direct2d/d2dbitmap.h"
14 #include "direct2d/d2dgraphicspath.h"
15 #include "win32textedit.h"
16 #include "win32optionmenu.h"
17 #include "win32support.h"
18 #include "win32datapackage.h"
19 #include "win32dragging.h"
20 #include "../common/genericoptionmenu.h"
21 #include "../../cdropsource.h"
22 #include "../../cgradient.h"
23 
24 #if VSTGUI_OPENGL_SUPPORT
25 #include "win32openglview.h"
26 #endif
27 
28 // windows libraries VSTGUI depends on
29 #ifdef _MSC_VER
30 #pragma comment(lib, "Shlwapi.lib")
31 #endif
32 
33 namespace VSTGUI {
34 
35 #define DEBUG_DRAWING	0
36 
37 //-----------------------------------------------------------------------------
38 static TCHAR gClassName[100];
39 static bool bSwapped_mouse_buttons = false;
40 
41 //-----------------------------------------------------------------------------
createPlatformFrame(IPlatformFrameCallback * frame,const CRect & size,void * parent,PlatformType parentType,IPlatformFrameConfig * config)42 IPlatformFrame* IPlatformFrame::createPlatformFrame (IPlatformFrameCallback* frame, const CRect& size, void* parent, PlatformType parentType, IPlatformFrameConfig* config)
43 {
44 	return new Win32Frame (frame, size, (HWND)parent, parentType);
45 }
46 
47 //-----------------------------------------------------------------------------
isParentLayered(HWND parent)48 static bool isParentLayered (HWND parent)
49 {
50 	WINDOWINFO info;
51 	info.cbSize = sizeof (info);
52 	while (parent)
53 	{
54 		if (GetWindowInfo (parent, &info))
55 		{
56 			if (info.dwStyle & WS_CHILD)
57 				parent = GetParent (parent);
58 			else
59 				break;
60 		}
61 	}
62 	if (parent)
63 	{
64 		if (info.dwExStyle & WS_EX_LAYERED)
65 			return true;
66 	}
67 	return false;
68 }
69 
70 //-----------------------------------------------------------------------------
Win32Frame(IPlatformFrameCallback * frame,const CRect & size,HWND parent,PlatformType parentType)71 Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND parent, PlatformType parentType)
72 : IPlatformFrame (frame)
73 , parentWindow (parent)
74 , windowHandle (nullptr)
75 , tooltipWindow (nullptr)
76 , oldFocusWindow (nullptr)
77 , backBuffer (nullptr)
78 , deviceContext (nullptr)
79 , inPaint (false)
80 , mouseInside (false)
81 , updateRegionList (nullptr)
82 , updateRegionListSize (0)
83 {
84 	useD2D ();
85 	if (parentType == PlatformType::kHWNDTopLevel)
86 	{
87 		windowHandle = parent;
88 		parentWindow = nullptr;
89 		RegisterDragDrop (windowHandle, new CDropTarget (this));
90 	}
91 	else
92 	{
93 		initWindowClass ();
94 
95 		DWORD style = isParentLayered (parent) ? WS_EX_TRANSPARENT : 0;
96 		windowHandle = CreateWindowEx (style, gClassName, TEXT("Window"),
97 										WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
98 										0, 0, (int)size.getWidth (), (int)size.getHeight (),
99 										parentWindow, NULL, GetInstance (), NULL);
100 
101 		if (windowHandle)
102 		{
103 			SetWindowLongPtr (windowHandle, GWLP_USERDATA, (__int3264)(LONG_PTR)this);
104 			RegisterDragDrop (windowHandle, new CDropTarget (this));
105 		}
106 	}
107 	setMouseCursor (kCursorDefault);
108 }
109 
110 //-----------------------------------------------------------------------------
~Win32Frame()111 Win32Frame::~Win32Frame () noexcept
112 {
113 	if (updateRegionList)
114 		std::free (updateRegionList);
115 	if (deviceContext)
116 		deviceContext->forget ();
117 	if (tooltipWindow)
118 		DestroyWindow (tooltipWindow);
119 	if (backBuffer)
120 		backBuffer->forget ();
121 	if (windowHandle)
122 		RevokeDragDrop (windowHandle);
123 	if (parentWindow)
124 	{
125 		if (windowHandle)
126 		{
127 			SetWindowLongPtr (windowHandle, GWLP_USERDATA, (LONG_PTR)NULL);
128 			DestroyWindow (windowHandle);
129 		}
130 		destroyWindowClass ();
131 	}
132 
133 	unuseD2D ();
134 }
135 
136 //-----------------------------------------------------------------------------
137 int32_t Win32Frame::gUseCount = 0;
138 
139 //-----------------------------------------------------------------------------
initWindowClass()140 void Win32Frame::initWindowClass ()
141 {
142 	gUseCount++;
143 	if (gUseCount == 1)
144 	{
145 		OleInitialize (0);
146 
147 		VSTGUI_SPRINTF (gClassName, TEXT("VSTGUI%p"), GetInstance ());
148 
149 		WNDCLASS windowClass;
150 		windowClass.style = CS_GLOBALCLASS | CS_DBLCLKS;//|CS_OWNDC; // add Private-DC constant
151 
152 		windowClass.lpfnWndProc = WindowProc;
153 		windowClass.cbClsExtra  = 0;
154 		windowClass.cbWndExtra  = 0;
155 		windowClass.hInstance   = GetInstance ();
156 		windowClass.hIcon = nullptr;
157 
158 		windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
159 		#if DEBUG_DRAWING
160 		windowClass.hbrBackground = GetSysColorBrush (COLOR_BTNFACE);
161 		#else
162 		windowClass.hbrBackground = nullptr;
163 		#endif
164 		windowClass.lpszMenuName  = nullptr;
165 		windowClass.lpszClassName = gClassName;
166 		RegisterClass (&windowClass);
167 
168 		bSwapped_mouse_buttons = GetSystemMetrics (SM_SWAPBUTTON) > 0;
169 	}
170 }
171 
172 //-----------------------------------------------------------------------------
destroyWindowClass()173 void Win32Frame::destroyWindowClass ()
174 {
175 	gUseCount--;
176 	if (gUseCount == 0)
177 	{
178 		UnregisterClass (gClassName, GetInstance ());
179 		OleUninitialize ();
180 	}
181 }
182 
183 //-----------------------------------------------------------------------------
initTooltip()184 void Win32Frame::initTooltip ()
185 {
186 	if (tooltipWindow == nullptr && windowHandle)
187 	{
188 		TOOLINFO    ti;
189 		// Create the ToolTip control.
190 		HWND hwndTT = CreateWindow (TOOLTIPS_CLASS, TEXT(""),
191 							  WS_POPUP,
192 							  CW_USEDEFAULT, CW_USEDEFAULT,
193 							  CW_USEDEFAULT, CW_USEDEFAULT,
194 							  NULL, (HMENU)NULL, GetInstance (),
195 							  NULL);
196 
197 		// Prepare TOOLINFO structure for use as tracking ToolTip.
198 		ti.cbSize = sizeof(TOOLINFO);
199 		ti.uFlags = TTF_SUBCLASS;
200 		ti.hwnd   = (HWND)windowHandle;
201 		ti.uId    = (UINT)0;
202 		ti.hinst  = GetInstance ();
203 		ti.lpszText  = const_cast<TCHAR*> (TEXT("This is a tooltip"));
204 		ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
205 
206 		// Add the tool to the control
207 		if (!SendMessage (hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti))
208 		{
209 			DestroyWindow (hwndTT);
210 			return;
211 		}
212 
213 		tooltipWindow = hwndTT;
214 	}
215 }
216 
217 //-----------------------------------------------------------------------------
getOuterWindow() const218 HWND Win32Frame::getOuterWindow () const
219 {
220 	int diffWidth, diffHeight;
221 	RECT  rctTempWnd, rctPluginWnd;
222 	HWND  hTempWnd = windowHandle;
223 	GetWindowRect (hTempWnd, &rctPluginWnd);
224 
225 	while (hTempWnd != NULL)
226 	{
227 		// Looking for caption bar
228 		if (GetWindowLong (hTempWnd, GWL_STYLE) & WS_CAPTION)
229 			return hTempWnd;
230 
231 		// Looking for last parent
232 		if (!GetParent (hTempWnd))
233 			return hTempWnd;
234 
235 		// get difference between plugin-window and current parent
236 		GetWindowRect (GetParent (hTempWnd), &rctTempWnd);
237 
238 		diffWidth  = (rctTempWnd.right - rctTempWnd.left) - (rctPluginWnd.right - rctPluginWnd.left);
239 		diffHeight = (rctTempWnd.bottom - rctTempWnd.top) - (rctPluginWnd.bottom - rctPluginWnd.top);
240 
241 		// Looking for size mismatch
242 		if ((abs (diffWidth) > 60) || (abs (diffHeight) > 60)) // parent belongs to host
243 			return (hTempWnd);
244 
245 		if (diffWidth < 0)
246 			diffWidth = 0;
247         if (diffHeight < 0)
248 			diffHeight = 0;
249 
250 		// get the next parent window
251 		hTempWnd = GetParent (hTempWnd);
252 	}
253 
254 	return NULL;
255 }
256 
257 // IPlatformFrame
258 //-----------------------------------------------------------------------------
getGlobalPosition(CPoint & pos) const259 bool Win32Frame::getGlobalPosition (CPoint& pos) const
260 {
261 	RECT r;
262 	GetWindowRect (windowHandle, &r);
263 	pos.x = r.left;
264 	pos.y = r.top;
265 	return true;
266 }
267 
268 //-----------------------------------------------------------------------------
setSize(const CRect & newSize)269 bool Win32Frame::setSize (const CRect& newSize)
270 {
271 	if (deviceContext)
272 	{
273 		deviceContext->forget ();
274 		deviceContext = nullptr;
275 	}
276 	if (backBuffer)
277 	{
278 		backBuffer->forget ();
279 		backBuffer = createOffscreenContext (newSize.getWidth (), newSize.getHeight ());
280 	}
281 	if (!parentWindow)
282 		return true;
283 	SetWindowPos (windowHandle, HWND_TOP, (int)newSize.left, (int)newSize.top, (int)newSize.getWidth (), (int)newSize.getHeight (), SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOREDRAW|SWP_DEFERERASE);
284 	invalidRect (newSize);
285 	return true;
286 }
287 
288 //-----------------------------------------------------------------------------
getSize(CRect & size) const289 bool Win32Frame::getSize (CRect& size) const
290 {
291 	RECT r;
292 	GetWindowRect (windowHandle, &r);
293 	POINT p;
294 	p.x = r.left;
295 	p.y = r.top;
296 	MapWindowPoints (HWND_DESKTOP, windowHandle, &p, 1);
297 	MapWindowPoints (windowHandle, GetParent (windowHandle), &p, 1);
298 	size.left = p.x;
299 	size.top = p.y;
300 	size.right = p.x + (r.right - r.left);
301 	size.bottom = p.y + (r.bottom - r.top);
302 	return true;
303 }
304 
305 //-----------------------------------------------------------------------------
getCurrentMousePosition(CPoint & mousePosition) const306 bool Win32Frame::getCurrentMousePosition (CPoint& mousePosition) const
307 {
308 	POINT _where;
309 	GetCursorPos (&_where);
310 	mousePosition (static_cast<CCoord> (_where.x), static_cast<CCoord> (_where.y));
311 	if (auto hwnd = getHWND ())
312 	{
313 		ScreenToClient (hwnd, &_where);
314 		mousePosition (static_cast<CCoord> (_where.x), static_cast<CCoord> (_where.y));
315 		return true;
316 	}
317 	return false;
318 }
319 
320 //-----------------------------------------------------------------------------
getCurrentMouseButtons(CButtonState & buttons) const321 bool Win32Frame::getCurrentMouseButtons (CButtonState& buttons) const
322 {
323 	if (GetAsyncKeyState (VK_LBUTTON) < 0)
324 		buttons |= (bSwapped_mouse_buttons ? kRButton : kLButton);
325 	if (GetAsyncKeyState (VK_MBUTTON) < 0)
326 		buttons |= kMButton;
327 	if (GetAsyncKeyState (VK_RBUTTON) < 0)
328 		buttons |= (bSwapped_mouse_buttons ? kLButton : kRButton);
329 	if (GetAsyncKeyState (VK_SHIFT)   < 0)
330 		buttons |= kShift;
331 	if (GetAsyncKeyState (VK_CONTROL) < 0)
332 		buttons |= kControl;
333 	if (GetAsyncKeyState (VK_MENU)    < 0)
334 		buttons |= kAlt;
335 	return true;
336 }
337 
338 //-----------------------------------------------------------------------------
setMouseCursor(CCursorType type)339 bool Win32Frame::setMouseCursor (CCursorType type)
340 {
341 	HCURSOR cursor = nullptr;
342 	switch (type)
343 	{
344 		case kCursorWait:
345 			cursor = LoadCursor (nullptr, IDC_WAIT);
346 			break;
347 		case kCursorHSize:
348 			cursor = LoadCursor (nullptr, IDC_SIZEWE);
349 			break;
350 		case kCursorVSize:
351 			cursor = LoadCursor (nullptr, IDC_SIZENS);
352 			break;
353 		case kCursorNESWSize:
354 			cursor = LoadCursor (nullptr, IDC_SIZENESW);
355 			break;
356 		case kCursorNWSESize:
357 			cursor = LoadCursor (nullptr, IDC_SIZENWSE);
358 			break;
359 		case kCursorSizeAll:
360 			cursor = LoadCursor (nullptr, IDC_SIZEALL);
361 			break;
362 		case kCursorNotAllowed:
363 			cursor = LoadCursor (nullptr, IDC_NO);
364 			break;
365 		case kCursorHand:
366 			cursor = LoadCursor (nullptr, IDC_HAND);
367 			break;
368 		default:
369 			cursor = LoadCursor (nullptr, IDC_ARROW);
370 			break;
371 	}
372 	lastSetCursor = type;
373 	SetClassLongPtr (getPlatformWindow (), GCLP_HCURSOR, (__int3264)(LONG_PTR)(cursor));
374 	return true;
375 }
376 
377 //-----------------------------------------------------------------------------
invalidRect(const CRect & rect)378 bool Win32Frame::invalidRect (const CRect& rect)
379 {
380 	if (inPaint)
381 		return false;
382 	if (!rect.isEmpty ())
383 	{
384 		RECT r = {(LONG)rect.left, (LONG)rect.top, (LONG)ceil (rect.right), (LONG)ceil (rect.bottom)};
385 		InvalidateRect (windowHandle, &r, true);
386 	}
387 	return true;
388 }
389 
390 //-----------------------------------------------------------------------------
scrollRect(const CRect & src,const CPoint & distance)391 bool Win32Frame::scrollRect (const CRect& src, const CPoint& distance)
392 {
393 	return false;
394 }
395 
396 //-----------------------------------------------------------------------------
getTicks()397 uint32_t IPlatformFrame::getTicks ()
398 {
399 	return (uint32_t)GetTickCount ();
400 }
401 
402 //-----------------------------------------------------------------------------
showTooltip(const CRect & rect,const char * utf8Text)403 bool Win32Frame::showTooltip (const CRect& rect, const char* utf8Text)
404 {
405 	initTooltip ();
406 	if (tooltipWindow)
407 	{
408 		std::string str (utf8Text);
409 		size_t pos = 0;
410 		while ((pos = str.find ("\\n", pos)) != std::string::npos)
411 		{
412 			str.erase (pos, 2);
413 			str.insert (pos, "\r\n");
414 		}
415 		UTF8StringHelper tooltipText (str.data ());
416 		RECT rc;
417 		rc.left = (LONG)rect.left;
418 		rc.top = (LONG)rect.top;
419 		rc.right = (LONG)rect.right;
420 		rc.bottom = (LONG)rect.bottom;
421 		TOOLINFO ti = {};
422 		ti.cbSize = sizeof(TOOLINFO);
423 		ti.hwnd = windowHandle;
424 		ti.uId = 0;
425 		ti.rect = rc;
426 		ti.lpszText = (TCHAR*)(const TCHAR*)tooltipText;
427 		SendMessage (tooltipWindow, TTM_SETMAXTIPWIDTH, 0, 0);
428 		SendMessage (tooltipWindow, TTM_SETDELAYTIME, 0, 2000);
429 		SendMessage (tooltipWindow, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
430 		SendMessage (tooltipWindow, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
431 		SendMessage (tooltipWindow, TTM_POPUP, 0, 0);
432 		return true;
433 	}
434 	return false;
435 }
436 
437 //-----------------------------------------------------------------------------
hideTooltip()438 bool Win32Frame::hideTooltip ()
439 {
440 	if (tooltipWindow)
441 	{
442 		TOOLINFO ti = {};
443 		ti.cbSize = sizeof(TOOLINFO);
444 		ti.hwnd = windowHandle;
445 		ti.uId = 0;
446 		ti.lpszText = nullptr;
447 		SendMessage (tooltipWindow, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
448 		SendMessage (tooltipWindow, TTM_POP, 0, 0);
449 	}
450 	return false;
451 }
452 
453 //-----------------------------------------------------------------------------
createPlatformTextEdit(IPlatformTextEditCallback * textEdit)454 SharedPointer<IPlatformTextEdit> Win32Frame::createPlatformTextEdit (IPlatformTextEditCallback* textEdit)
455 {
456 	return owned<IPlatformTextEdit> (new Win32TextEdit (windowHandle, textEdit));
457 }
458 
459 //-----------------------------------------------------------------------------
createPlatformOptionMenu()460 SharedPointer<IPlatformOptionMenu> Win32Frame::createPlatformOptionMenu ()
461 {
462 	if (genericOptionMenuTheme)
463 	{
464 		CButtonState buttons;
465 		getCurrentMouseButtons (buttons);
466 		return makeOwned<GenericOptionMenu> (dynamic_cast<CFrame*> (frame), buttons,
467 		                                     *genericOptionMenuTheme.get ());
468 	}
469 	return owned<IPlatformOptionMenu> (new Win32OptionMenu (windowHandle));
470 }
471 
472 #if VSTGUI_OPENGL_SUPPORT
473 //-----------------------------------------------------------------------------
createPlatformOpenGLView()474 SharedPointer<IPlatformOpenGLView> Win32Frame::createPlatformOpenGLView ()
475 {
476 	return owned<IPlatformOpenGLView> (new Win32OpenGLView (this));
477 }
478 #endif
479 
480 //-----------------------------------------------------------------------------
createOffscreenContext(CCoord width,CCoord height,double scaleFactor)481 SharedPointer<COffscreenContext> Win32Frame::createOffscreenContext (CCoord width, CCoord height, double scaleFactor)
482 {
483 	D2DBitmap* bitmap = new D2DBitmap (CPoint (width * scaleFactor, height * scaleFactor));
484 	bitmap->setScaleFactor (scaleFactor);
485 	auto context = owned<COffscreenContext> (new D2DDrawContext (bitmap));
486 	bitmap->forget ();
487 	return context;
488 }
489 
490 #if VSTGUI_ENABLE_DEPRECATED_METHODS
491 class Win32LegacyDragSupport final : virtual public DragCallbackAdapter, virtual public NonAtomicReferenceCounted
492 {
493 public:
dragEnded(IDraggingSession *,CPoint,DragOperation r)494 	void dragEnded (IDraggingSession*, CPoint, DragOperation r) final { result = r; }
495 	DragOperation result {DragOperation::None};
496 };
497 
498 //------------------------------------------------------------------------------------
doDrag(IDataPackage * source,const CPoint & offset,CBitmap * dragBitmap)499 DragResult Win32Frame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap)
500 {
501 	Win32LegacyDragSupport dragSupport;
502 
503 	Win32DraggingSession session (this);
504 	if (session.doDrag (DragDescription (source, offset, dragBitmap), &dragSupport))
505 	{
506 		switch (dragSupport.result)
507 		{
508 			case DragOperation::Copy: return kDragCopied;
509 			case DragOperation::Move: return kDragMoved;
510 			case DragOperation::None: return kDragRefused;
511 		}
512 	}
513 	return kDragRefused;
514 }
515 #endif
516 
517 //-----------------------------------------------------------------------------
doDrag(const DragDescription & dragDescription,const SharedPointer<IDragCallback> & callback)518 bool Win32Frame::doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback)
519 {
520 	Win32DraggingSession session (this);
521 	return session.doDrag (dragDescription, callback);
522 }
523 
524 //-----------------------------------------------------------------------------
setClipboard(const SharedPointer<IDataPackage> & data)525 void Win32Frame::setClipboard (const SharedPointer<IDataPackage>& data)
526 {
527 	auto dataObject = makeOwned<Win32DataObject> (data);
528 	auto hr = OleSetClipboard (dataObject);
529 	if (hr != S_OK)
530 	{
531 #if DEBUG
532 		DebugPrint ("Setting clipboard failed!\n");
533 #endif
534 	}
535 }
536 
537 //-----------------------------------------------------------------------------
getClipboard()538 SharedPointer<IDataPackage> Win32Frame::getClipboard ()
539 {
540 	IDataObject* dataObject = nullptr;;
541 	if (OleGetClipboard (&dataObject) != S_OK)
542 		return nullptr;
543 	return makeOwned<Win32DataPackage> (dataObject);
544 }
545 
546 //-----------------------------------------------------------------------------
onFrameClosed()547 void Win32Frame::onFrameClosed ()
548 {
549 	frame = nullptr;
550 }
551 
552 //-----------------------------------------------------------------------------
setupGenericOptionMenu(bool use,GenericOptionMenuTheme * theme)553 bool Win32Frame::setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme)
554 {
555 	if (!use)
556 	{
557 		genericOptionMenuTheme = nullptr;
558 	}
559 	else
560 	{
561 		if (theme)
562 			genericOptionMenuTheme = std::unique_ptr<GenericOptionMenuTheme> (new GenericOptionMenuTheme (*theme));
563 		else
564 			genericOptionMenuTheme = std::unique_ptr<GenericOptionMenuTheme> (new GenericOptionMenuTheme);
565 	}
566 	return true;
567 }
568 
569 //-----------------------------------------------------------------------------
paint(HWND hwnd)570 void Win32Frame::paint (HWND hwnd)
571 {
572 	HRGN rgn = CreateRectRgn (0, 0, 0, 0);
573 	if (GetUpdateRgn (hwnd, rgn, false) == NULLREGION)
574 	{
575 		DeleteObject (rgn);
576 		return;
577 	}
578 
579 	inPaint = true;
580 
581 	PAINTSTRUCT ps;
582 	HDC hdc = BeginPaint (hwnd, &ps);
583 
584 	if (hdc)
585 	{
586 		CRect updateRect ((CCoord)ps.rcPaint.left, (CCoord)ps.rcPaint.top, (CCoord)ps.rcPaint.right, (CCoord)ps.rcPaint.bottom);
587 		CRect frameSize;
588 		getSize (frameSize);
589 		frameSize.offset (-frameSize.left, -frameSize.top);
590 		if (deviceContext == nullptr)
591 			deviceContext = createDrawContext (hwnd, hdc, frameSize);
592 		if (deviceContext)
593 		{
594 			deviceContext->setClipRect (updateRect);
595 
596 			CDrawContext* drawContext = backBuffer ? backBuffer : deviceContext;
597 			drawContext->beginDraw ();
598 			DWORD len = GetRegionData (rgn, 0, NULL);
599 			if (len)
600 			{
601 				if (len > updateRegionListSize)
602 				{
603 					if (updateRegionList)
604 						std::free (updateRegionList);
605 					updateRegionListSize = len;
606 					updateRegionList = (RGNDATA*) std::malloc (updateRegionListSize);
607 				}
608 				GetRegionData (rgn, len, updateRegionList);
609 				if (updateRegionList->rdh.nCount > 1)
610 				{
611 					std::vector<CRect> dirtyRects;
612 					dirtyRects.reserve (updateRegionList->rdh.nCount);
613 					RECT* rp = (RECT*)updateRegionList->Buffer;
614 					dirtyRects.emplace_back (CRect (rp->left, rp->top, rp->right, rp->bottom));
615 					++rp;
616 					for (uint32_t i = 1; i < updateRegionList->rdh.nCount; ++i, ++rp)
617 					{
618 						CRect ur (rp->left, rp->top, rp->right, rp->bottom);
619 						auto mustAdd = true;
620 						for (auto& r : dirtyRects)
621 						{
622 							auto cr = ur;
623 							cr.unite (r);
624 							if (cr.getWidth () * cr.getHeight () ==
625 							    ur.getWidth () * ur.getHeight () + r.getWidth () * r.getHeight ())
626 							{
627 								r = cr;
628 								mustAdd = false;
629 								break;
630 							}
631 						}
632 						if (mustAdd)
633 							dirtyRects.emplace_back (ur);
634 					}
635 					for (auto& updateRect : dirtyRects)
636 					{
637 						drawContext->clearRect (updateRect);
638 						getFrame ()->platformDrawRect (drawContext, updateRect);
639 					}
640 				}
641 				else
642 				{
643 					drawContext->clearRect (updateRect);
644 					getFrame ()->platformDrawRect (drawContext, updateRect);
645 				}
646 			}
647 			drawContext->endDraw ();
648 			if (backBuffer)
649 			{
650 				deviceContext->beginDraw ();
651 				deviceContext->clearRect (updateRect);
652 				backBuffer->copyFrom (deviceContext, updateRect, CPoint (updateRect.left, updateRect.top));
653 				deviceContext->endDraw ();
654 			}
655 		}
656 	}
657 
658 	EndPaint (hwnd, &ps);
659 	DeleteObject (rgn);
660 
661 	inPaint = false;
662 }
663 
translateWinVirtualKey(WPARAM winVKey)664 static unsigned char translateWinVirtualKey (WPARAM winVKey)
665 {
666 	switch (winVKey)
667 	{
668 		case VK_BACK: return VKEY_BACK;
669 		case VK_TAB: return VKEY_TAB;
670 		case VK_CLEAR: return VKEY_CLEAR;
671 		case VK_RETURN: return VKEY_RETURN;
672 		case VK_PAUSE: return VKEY_PAUSE;
673 		case VK_ESCAPE: return VKEY_ESCAPE;
674 		case VK_SPACE: return VKEY_SPACE;
675 // TODO:		case VK_NEXT: return VKEY_NEXT;
676 		case VK_END: return VKEY_END;
677 		case VK_HOME: return VKEY_HOME;
678 		case VK_LEFT: return VKEY_LEFT;
679 		case VK_RIGHT: return VKEY_RIGHT;
680 		case VK_UP: return VKEY_UP;
681 		case VK_DOWN: return VKEY_DOWN;
682 		case VK_PRIOR: return VKEY_PAGEUP;
683 		case VK_NEXT: return VKEY_PAGEDOWN;
684 		case VK_SELECT: return VKEY_SELECT;
685 		case VK_PRINT: return VKEY_PRINT;
686 		case VK_SNAPSHOT: return VKEY_SNAPSHOT;
687 		case VK_INSERT: return VKEY_INSERT;
688 		case VK_DELETE: return VKEY_DELETE;
689 		case VK_HELP: return VKEY_HELP;
690 		case VK_NUMPAD0: return VKEY_NUMPAD0;
691 		case VK_NUMPAD1: return VKEY_NUMPAD1;
692 		case VK_NUMPAD2: return VKEY_NUMPAD2;
693 		case VK_NUMPAD3: return VKEY_NUMPAD3;
694 		case VK_NUMPAD4: return VKEY_NUMPAD4;
695 		case VK_NUMPAD5: return VKEY_NUMPAD5;
696 		case VK_NUMPAD6: return VKEY_NUMPAD6;
697 		case VK_NUMPAD7: return VKEY_NUMPAD7;
698 		case VK_NUMPAD8: return VKEY_NUMPAD8;
699 		case VK_NUMPAD9: return VKEY_NUMPAD9;
700 		case VK_MULTIPLY: return VKEY_MULTIPLY;
701 		case VK_ADD: return VKEY_ADD;
702 		case VK_SEPARATOR: return VKEY_SEPARATOR;
703 		case VK_SUBTRACT: return VKEY_SUBTRACT;
704 		case VK_DECIMAL: return VKEY_DECIMAL;
705 		case VK_DIVIDE: return VKEY_DIVIDE;
706 		case VK_F1: return VKEY_F1;
707 		case VK_F2: return VKEY_F2;
708 		case VK_F3: return VKEY_F3;
709 		case VK_F4: return VKEY_F4;
710 		case VK_F5: return VKEY_F5;
711 		case VK_F6: return VKEY_F6;
712 		case VK_F7: return VKEY_F7;
713 		case VK_F8: return VKEY_F8;
714 		case VK_F9: return VKEY_F9;
715 		case VK_F10: return VKEY_F10;
716 		case VK_F11: return VKEY_F11;
717 		case VK_F12: return VKEY_F12;
718 		case VK_NUMLOCK: return VKEY_NUMLOCK;
719 		case VK_SCROLL: return VKEY_SCROLL;
720 		case VK_SHIFT: return VKEY_SHIFT;
721 		case VK_CONTROL: return VKEY_CONTROL;
722 		case VK_MENU: return VKEY_ALT;
723 		case VKEY_EQUALS: return VKEY_EQUALS;
724 	}
725 	return 0;
726 }
727 
728 //-----------------------------------------------------------------------------
proc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)729 LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
730 {
731 	if (getFrame () == nullptr)
732 		return DefWindowProc (hwnd, message, wParam, lParam);
733 
734 	SharedPointer<Win32Frame> lifeGuard (this);
735 	IPlatformFrameCallback* pFrame = getFrame ();
736 	bool doubleClick = false;
737 
738 	switch (message)
739 	{
740 		case WM_MOUSEWHEEL:
741 		{
742 			CButtonState buttons = 0;
743 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
744 				buttons |= kShift;
745 			if (GetAsyncKeyState (VK_CONTROL) < 0)
746 				buttons |= kControl;
747 			if (GetAsyncKeyState (VK_MENU)    < 0)
748 				buttons |= kAlt;
749 			short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam);
750 			POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)};
751 			ScreenToClient (windowHandle, &p);
752 			CPoint where (p.x, p.y);
753 			if (pFrame->platformOnMouseWheel (where, kMouseWheelAxisY, ((float)zDelta / WHEEL_DELTA), buttons))
754 				return 0;
755 			break;
756 		}
757 		case WM_MOUSEHWHEEL:	// new since vista
758 		{
759 			CButtonState buttons = 0;
760 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
761 				buttons |= kShift;
762 			if (GetAsyncKeyState (VK_CONTROL) < 0)
763 				buttons |= kControl;
764 			if (GetAsyncKeyState (VK_MENU)    < 0)
765 				buttons |= kAlt;
766 			short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam);
767 			POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)};
768 			ScreenToClient (windowHandle, &p);
769 			CPoint where (p.x, p.y);
770 			if (pFrame->platformOnMouseWheel (where, kMouseWheelAxisX, ((float)-zDelta / WHEEL_DELTA), buttons))
771 				return 0;
772 			break;
773 		}
774 		case WM_CTLCOLOREDIT:
775 		{
776 			Win32TextEdit* win32TextEdit = (Win32TextEdit*)(LONG_PTR) GetWindowLongPtr ((HWND)lParam, GWLP_USERDATA);
777 			if (win32TextEdit)
778 			{
779 				CColor fontColor = win32TextEdit->getTextEdit ()->platformGetFontColor ();
780 				SetTextColor ((HDC) wParam, RGB (fontColor.red, fontColor.green, fontColor.blue));
781 #if 1 // TODO: I don't know why the transparent part does not work anymore. Needs more investigation.
782 				CColor backColor = win32TextEdit->getTextEdit ()->platformGetBackColor ();
783 				SetBkColor ((HDC) wParam, RGB (backColor.red, backColor.green, backColor.blue));
784 				return (LRESULT)(win32TextEdit->getPlatformBackColor ());
785 #else
786 				SetBkMode ((HDC)wParam, TRANSPARENT);
787 				return (LRESULT) ::GetStockObject (HOLLOW_BRUSH);
788 #endif
789 			}
790 			break;
791 		}
792 
793 		case WM_PAINT:
794 		{
795 			paint (hwnd);
796 			return 0;
797 		}
798 
799 		case WM_RBUTTONDBLCLK:
800 		case WM_MBUTTONDBLCLK:
801 		case WM_LBUTTONDBLCLK:
802 		case WM_XBUTTONDBLCLK:
803 			doubleClick = true;
804 		case WM_RBUTTONDOWN:
805 		case WM_MBUTTONDOWN:
806 		case WM_LBUTTONDOWN:
807 		case WM_XBUTTONDOWN:
808 		{
809 			CButtonState buttons = 0;
810 			if (wParam & MK_LBUTTON)
811 				buttons |= kLButton;
812 			if (wParam & MK_RBUTTON)
813 				buttons |= kRButton;
814 			if (wParam & MK_MBUTTON)
815 				buttons |= kMButton;
816 			if (wParam & MK_XBUTTON1)
817 				buttons |= kButton4;
818 			if (wParam & MK_XBUTTON2)
819 				buttons |= kButton5;
820 			if (wParam & MK_CONTROL)
821 				buttons |= kControl;
822 			if (wParam & MK_SHIFT)
823 				buttons |= kShift;
824 			if (GetAsyncKeyState (VK_MENU)    < 0)
825 				buttons |= kAlt;
826 			if (doubleClick)
827 				buttons |= kDoubleClick;
828 			HWND oldFocus = SetFocus(getPlatformWindow());
829 			if(oldFocus != hwnd)
830 				oldFocusWindow = oldFocus;
831 
832 			CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
833 			if (pFrame->platformOnMouseDown (where, buttons) == kMouseEventHandled && getPlatformWindow ())
834 				SetCapture (getPlatformWindow ());
835 			return 0;
836 		}
837 		case WM_MOUSELEAVE:
838 		{
839 			CPoint where;
840 			getCurrentMousePosition (where);
841 			CButtonState buttons;
842 			getCurrentMouseButtons (buttons);
843 			pFrame->platformOnMouseExited (where, buttons);
844 			mouseInside = false;
845 			return 0;
846 		}
847 		case WM_MOUSEMOVE:
848 		{
849 			CButtonState buttons = 0;
850 			if (wParam & MK_LBUTTON)
851 				buttons |= kLButton;
852 			if (wParam & MK_RBUTTON)
853 				buttons |= kRButton;
854 			if (wParam & MK_MBUTTON)
855 				buttons |= kMButton;
856 			if (wParam & MK_XBUTTON1)
857 				buttons |= kButton4;
858 			if (wParam & MK_XBUTTON2)
859 				buttons |= kButton5;
860 			if (wParam & MK_CONTROL)
861 				buttons |= kControl;
862 			if (wParam & MK_SHIFT)
863 				buttons |= kShift;
864 			if (GetAsyncKeyState (VK_MENU) < 0)
865 				buttons |= kAlt;
866 			if (!mouseInside)
867 			{
868 				// this makes sure that WM_MOUSELEAVE will be generated by the system
869 				mouseInside = true;
870 				TRACKMOUSEEVENT tme = {};
871 				tme.cbSize = sizeof (tme);
872 				tme.dwFlags = TME_LEAVE;
873 				tme.hwndTrack = windowHandle;
874 				TrackMouseEvent (&tme);
875 			}
876 			CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
877 			pFrame->platformOnMouseMoved (where, buttons);
878 			return 0;
879 		}
880 		case WM_LBUTTONUP:
881 		case WM_RBUTTONUP:
882 		case WM_MBUTTONUP:
883 		case WM_XBUTTONUP:
884 		{
885 			CButtonState buttons = 0;
886 			if (message & MK_LBUTTON || message == WM_LBUTTONUP)
887 				buttons |= kLButton;
888 			if (wParam & MK_RBUTTON || message == WM_RBUTTONUP)
889 				buttons |= kRButton;
890 			if (wParam & MK_MBUTTON || message == WM_MBUTTONUP)
891 				buttons |= kMButton;
892 			if (wParam & MK_XBUTTON1)
893 				buttons |= kButton4;
894 			if (wParam & MK_XBUTTON2)
895 				buttons |= kButton5;
896 			if (wParam & MK_CONTROL)
897 				buttons |= kControl;
898 			if (wParam & MK_SHIFT)
899 				buttons |= kShift;
900 			if (GetAsyncKeyState (VK_MENU) < 0)
901 				buttons |= kAlt;
902 			CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
903 			pFrame->platformOnMouseUp (where, buttons);
904 			ReleaseCapture ();
905 			return 0;
906 		}
907 		case WM_KEYDOWN:
908 		{
909 			VstKeyCode key {};
910 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
911 				key.modifier |= MODIFIER_SHIFT;
912 			if (GetAsyncKeyState (VK_CONTROL) < 0)
913 				key.modifier |= MODIFIER_CONTROL;
914 			if (GetAsyncKeyState (VK_MENU)    < 0)
915 				key.modifier |= MODIFIER_ALTERNATE;
916 			key.virt = translateWinVirtualKey (wParam);
917 			key.character = MapVirtualKey (static_cast<UINT> (wParam), MAPVK_VK_TO_CHAR);
918 			if (key.virt || key.character)
919 			{
920 				key.character = std::tolower (key.character);
921 				if (pFrame->platformOnKeyDown (key))
922 					return 0;
923 			}
924 
925 			if (IsWindow (oldFocusWindow))
926 			{
927 				auto oldProc = reinterpret_cast<WNDPROC> (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC));
928 				if (oldProc && oldProc != WindowProc)
929 					return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
930 			}
931 			break;
932 		}
933 		case WM_KEYUP:
934 		{
935 			VstKeyCode key {};
936 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
937 				key.modifier |= MODIFIER_SHIFT;
938 			if (GetAsyncKeyState (VK_CONTROL) < 0)
939 				key.modifier |= MODIFIER_CONTROL;
940 			if (GetAsyncKeyState (VK_MENU)    < 0)
941 				key.modifier |= MODIFIER_ALTERNATE;
942 			key.virt = translateWinVirtualKey (wParam);
943 			key.character = MapVirtualKey (static_cast<UINT> (wParam), MAPVK_VK_TO_CHAR);
944 			if (key.virt || key.character)
945 			{
946 				if (pFrame->platformOnKeyUp (key))
947 					return 0;
948 			}
949 
950 			if (IsWindow (oldFocusWindow))
951 			{
952 				auto oldProc = reinterpret_cast<WNDPROC> (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC));
953 				if (oldProc && oldProc != WindowProc)
954 					return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
955 			}
956 			break;
957 		}
958 		case WM_SETFOCUS:
959 		{
960 			pFrame->platformOnActivate (true);
961 			break;
962 		}
963 		case WM_KILLFOCUS:
964 		{
965 			oldFocusWindow = nullptr;
966 
967 			HWND focusWindow = GetFocus ();
968 			if (GetParent (focusWindow) != windowHandle)
969 				pFrame->platformOnActivate (false);
970 			break;
971 		}
972 		case WM_DESTROY:
973 		{
974 #if DEBUG
975 			DebugPrint ("This sometimes happens, only when we are currently processing a mouse down event and via a callback into the host the window gets destroyed. Otherwise this should never get called. We are the owner of the window and we are responsible of destroying it.\n");
976 #endif
977 			if (parentWindow)
978 				windowHandle = nullptr;
979 			break;
980 		}
981 		case WM_ERASEBKGND:
982 		{
983 			return 1; // don't draw background
984 		}
985 		case WM_COMMAND:
986 		{
987 			if (HIWORD (wParam) == EN_CHANGE)
988 			{
989 				// text control changes will be forwarded to the text control window proc
990 				HWND controlWindow = (HWND)lParam;
991 				WINDOWSPROC textEditWindowProc = (WINDOWSPROC)(LONG_PTR)GetWindowLongPtr (controlWindow, GWLP_WNDPROC);
992 				if (textEditWindowProc)
993 				{
994 					textEditWindowProc (controlWindow, WM_COMMAND, wParam, lParam);
995 				}
996 			}
997 			break;
998 		}
999 	}
1000 	return DefWindowProc (hwnd, message, wParam, lParam);
1001 }
1002 
1003 //-----------------------------------------------------------------------------
WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1004 LONG_PTR WINAPI Win32Frame::WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1005 {
1006 	Win32Frame* win32Frame = (Win32Frame*)(LONG_PTR)GetWindowLongPtr (hwnd, GWLP_USERDATA);
1007 	if (win32Frame)
1008 	{
1009 		return win32Frame->proc (hwnd, message, wParam, lParam);
1010 	}
1011 	return DefWindowProc (hwnd, message, wParam, lParam);
1012 }
1013 
1014 //-----------------------------------------------------------------------------
create(const ColorStopMap & colorStopMap)1015 CGradient* CGradient::create (const ColorStopMap& colorStopMap)
1016 {
1017 	return new CGradient (colorStopMap);
1018 }
1019 
1020 } // VSTGUI
1021 
1022 #endif // WINDOWS
1023