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 "../../cdropsource.h"
21 #include "../../cgradient.h"
22 
23 #if VSTGUI_OPENGL_SUPPORT
24 #include "win32openglview.h"
25 #endif
26 
27 // windows libraries VSTGUI depends on
28 #ifdef _MSC_VER
29 #pragma comment(lib, "Shlwapi.lib")
30 #endif
31 
32 namespace VSTGUI {
33 
34 #define DEBUG_DRAWING	0
35 
36 //-----------------------------------------------------------------------------
37 static TCHAR gClassName[100];
38 static bool bSwapped_mouse_buttons = false;
39 
40 //-----------------------------------------------------------------------------
createPlatformFrame(IPlatformFrameCallback * frame,const CRect & size,void * parent,PlatformType parentType,IPlatformFrameConfig * config)41 IPlatformFrame* IPlatformFrame::createPlatformFrame (IPlatformFrameCallback* frame, const CRect& size, void* parent, PlatformType parentType, IPlatformFrameConfig* config)
42 {
43 	return new Win32Frame (frame, size, (HWND)parent, parentType);
44 }
45 
46 //-----------------------------------------------------------------------------
isParentLayered(HWND parent)47 static bool isParentLayered (HWND parent)
48 {
49 	WINDOWINFO info;
50 	info.cbSize = sizeof (info);
51 	while (parent)
52 	{
53 		if (GetWindowInfo (parent, &info))
54 		{
55 			if (info.dwStyle & WS_CHILD)
56 				parent = GetParent (parent);
57 			else
58 				break;
59 		}
60 	}
61 	if (parent)
62 	{
63 		if (info.dwExStyle & WS_EX_LAYERED)
64 			return true;
65 	}
66 	return false;
67 }
68 
69 //-----------------------------------------------------------------------------
Win32Frame(IPlatformFrameCallback * frame,const CRect & size,HWND parent,PlatformType parentType)70 Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND parent, PlatformType parentType)
71 : IPlatformFrame (frame)
72 , parentWindow (parent)
73 , windowHandle (0)
74 , tooltipWindow (0)
75 , oldFocusWindow (0)
76 , backBuffer (0)
77 , deviceContext (0)
78 , inPaint (false)
79 , mouseInside (false)
80 , updateRegionList (0)
81 , updateRegionListSize (0)
82 , isTouchActive(false)
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 = 0;
157 
158 		windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
159 		#if DEBUG_DRAWING
160 		windowClass.hbrBackground = GetSysColorBrush (COLOR_BTNFACE);
161 		#else
162 		windowClass.hbrBackground = 0;
163 		#endif
164 		windowClass.lpszMenuName  = 0;
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 == 0 && 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 = 0;
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 = 0;
342 	switch (type)
343 	{
344 		case kCursorWait:
345 			cursor = LoadCursor (0, IDC_WAIT);
346 			break;
347 		case kCursorHSize:
348 			cursor = LoadCursor (0, IDC_SIZEWE);
349 			break;
350 		case kCursorVSize:
351 			cursor = LoadCursor (0, IDC_SIZENS);
352 			break;
353 		case kCursorNESWSize:
354 			cursor = LoadCursor (0, IDC_SIZENESW);
355 			break;
356 		case kCursorNWSESize:
357 			cursor = LoadCursor (0, IDC_SIZENWSE);
358 			break;
359 		case kCursorSizeAll:
360 			cursor = LoadCursor (0, IDC_SIZEALL);
361 			break;
362 		case kCursorNotAllowed:
363 			cursor = LoadCursor (0, IDC_NO);
364 			break;
365 		case kCursorHand:
366 			cursor = LoadCursor (0, IDC_HAND);
367 			break;
368 		default:
369 			cursor = LoadCursor (0, 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.c_str ());
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 = {0};
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 = {0};
443 		ti.cbSize = sizeof(TOOLINFO);
444 		ti.hwnd = windowHandle;
445 		ti.uId = 0;
446 		ti.lpszText = 0;
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 	inPopup = true;
463 	return owned<IPlatformOptionMenu> (new Win32OptionMenu (windowHandle));
464 }
465 
466 #if VSTGUI_OPENGL_SUPPORT
467 //-----------------------------------------------------------------------------
createPlatformOpenGLView()468 SharedPointer<IPlatformOpenGLView> Win32Frame::createPlatformOpenGLView ()
469 {
470 	return owned<IPlatformOpenGLView> (new Win32OpenGLView (this));
471 }
472 #endif
473 
474 //-----------------------------------------------------------------------------
createOffscreenContext(CCoord width,CCoord height,double scaleFactor)475 SharedPointer<COffscreenContext> Win32Frame::createOffscreenContext (CCoord width, CCoord height, double scaleFactor)
476 {
477 	D2DBitmap* bitmap = new D2DBitmap (CPoint (width * scaleFactor, height * scaleFactor));
478 	bitmap->setScaleFactor (scaleFactor);
479 	auto context = owned<COffscreenContext> (new D2DDrawContext (bitmap));
480 	bitmap->forget ();
481 	return context;
482 }
483 
484 #if VSTGUI_ENABLE_DEPRECATED_METHODS
485 class Win32LegacyDragSupport : virtual public DragCallbackAdapter, virtual public NonAtomicReferenceCounted
486 {
487 public:
dragEnded(IDraggingSession *,CPoint,DragOperation r)488 	void dragEnded (IDraggingSession*, CPoint, DragOperation r) final { result = r; }
489 	DragOperation result {DragOperation::None};
490 };
491 
492 //------------------------------------------------------------------------------------
doDrag(IDataPackage * source,const CPoint & offset,CBitmap * dragBitmap)493 DragResult Win32Frame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap)
494 {
495 	Win32LegacyDragSupport dragSupport;
496 
497 	Win32DraggingSession session (this);
498 	if (session.doDrag (DragDescription (source, offset, dragBitmap), &dragSupport))
499 	{
500 		switch (dragSupport.result)
501 		{
502 			case DragOperation::Copy: return kDragCopied;
503 			case DragOperation::Move: return kDragMoved;
504 			case DragOperation::None: return kDragRefused;
505 		}
506 	}
507 	return kDragRefused;
508 }
509 #endif
510 
511 //-----------------------------------------------------------------------------
doDrag(const DragDescription & dragDescription,const SharedPointer<IDragCallback> & callback)512 bool Win32Frame::doDrag (const DragDescription& dragDescription, const SharedPointer<IDragCallback>& callback)
513 {
514 	Win32DraggingSession session (this);
515 	return session.doDrag (dragDescription, callback);
516 }
517 
518 //-----------------------------------------------------------------------------
setClipboard(const SharedPointer<IDataPackage> & data)519 void Win32Frame::setClipboard (const SharedPointer<IDataPackage>& data)
520 {
521 	auto dataObject = makeOwned<Win32DataObject> (data);
522 	auto hr = OleSetClipboard (dataObject);
523 	if (hr != S_OK)
524 	{
525 #if DEBUG
526 		DebugPrint ("Setting clipboard failed!\n");
527 #endif
528 	}
529 }
530 
531 //-----------------------------------------------------------------------------
getClipboard()532 SharedPointer<IDataPackage> Win32Frame::getClipboard ()
533 {
534 	IDataObject* dataObject = nullptr;;
535 	if (OleGetClipboard (&dataObject) != S_OK)
536 		return nullptr;
537 	return makeOwned<Win32DataPackage> (dataObject);
538 }
539 
540 //-----------------------------------------------------------------------------
onFrameClosed()541 void Win32Frame::onFrameClosed ()
542 {
543 	frame = nullptr;
544 }
545 
546 //-----------------------------------------------------------------------------
paint(HWND hwnd)547 void Win32Frame::paint (HWND hwnd)
548 {
549 	HRGN rgn = CreateRectRgn (0, 0, 0, 0);
550 	if (GetUpdateRgn (hwnd, rgn, false) == NULLREGION)
551 	{
552 		DeleteObject (rgn);
553 		return;
554 	}
555 
556 	inPaint = true;
557 
558 	PAINTSTRUCT ps;
559 	HDC hdc = BeginPaint (hwnd, &ps);
560 
561 	if (hdc)
562 	{
563 		CRect updateRect ((CCoord)ps.rcPaint.left, (CCoord)ps.rcPaint.top, (CCoord)ps.rcPaint.right, (CCoord)ps.rcPaint.bottom);
564 		CRect frameSize;
565 		getSize (frameSize);
566 		frameSize.offset (-frameSize.left, -frameSize.top);
567 		if (deviceContext == 0)
568 			deviceContext = createDrawContext (hwnd, hdc, frameSize);
569 		if (deviceContext)
570 		{
571 			deviceContext->setClipRect (updateRect);
572 
573 			CDrawContext* drawContext = backBuffer ? backBuffer : deviceContext;
574 			drawContext->beginDraw ();
575 			DWORD len = GetRegionData (rgn, 0, NULL);
576 			if (len)
577 			{
578 				if (len > updateRegionListSize)
579 				{
580 					if (updateRegionList)
581 						std::free (updateRegionList);
582 					updateRegionListSize = len;
583 					updateRegionList = (RGNDATA*) std::malloc (updateRegionListSize);
584 				}
585 				GetRegionData (rgn, len, updateRegionList);
586 				if (updateRegionList->rdh.nCount > 0)
587 				{
588 					RECT* rp = (RECT*)updateRegionList->Buffer;
589 					for (uint32_t i = 0; i < updateRegionList->rdh.nCount; i++)
590 					{
591 						CRect ur (rp->left, rp->top, rp->right, rp->bottom);
592 						paintRect = ur;
593 						drawContext->clearRect (ur);
594 						getFrame ()->platformDrawRect (drawContext, ur);
595 						rp++;
596 					}
597 				}
598 				else
599 				{
600 					getFrame ()->platformDrawRect (drawContext, updateRect);
601 				}
602 			}
603 			drawContext->endDraw ();
604 			if (backBuffer)
605 			{
606 				deviceContext->beginDraw ();
607 				deviceContext->clearRect (updateRect);
608 				backBuffer->copyFrom (deviceContext, updateRect, CPoint (updateRect.left, updateRect.top));
609 				deviceContext->endDraw ();
610 			}
611 		}
612 	}
613 
614 	EndPaint (hwnd, &ps);
615 	DeleteObject (rgn);
616 
617 	inPaint = false;
618 }
619 
translateWinVirtualKey(WPARAM winVKey)620 static unsigned char translateWinVirtualKey (WPARAM winVKey)
621 {
622 	switch (winVKey)
623 	{
624 		case VK_BACK: return VKEY_BACK;
625 		case VK_TAB: return VKEY_TAB;
626 		case VK_CLEAR: return VKEY_CLEAR;
627 		case VK_RETURN: return VKEY_RETURN;
628 		case VK_PAUSE: return VKEY_PAUSE;
629 		case VK_ESCAPE: return VKEY_ESCAPE;
630 		case VK_SPACE: return VKEY_SPACE;
631 // TODO:		case VK_NEXT: return VKEY_NEXT;
632 		case VK_END: return VKEY_END;
633 		case VK_HOME: return VKEY_HOME;
634 		case VK_LEFT: return VKEY_LEFT;
635 		case VK_RIGHT: return VKEY_RIGHT;
636 		case VK_UP: return VKEY_UP;
637 		case VK_DOWN: return VKEY_DOWN;
638 		case VK_PRIOR: return VKEY_PAGEUP;
639 		case VK_NEXT: return VKEY_PAGEDOWN;
640 		case VK_SELECT: return VKEY_SELECT;
641 		case VK_PRINT: return VKEY_PRINT;
642 		case VK_SNAPSHOT: return VKEY_SNAPSHOT;
643 		case VK_INSERT: return VKEY_INSERT;
644 		case VK_DELETE: return VKEY_DELETE;
645 		case VK_HELP: return VKEY_HELP;
646 		case VK_NUMPAD0: return VKEY_NUMPAD0;
647 		case VK_NUMPAD1: return VKEY_NUMPAD1;
648 		case VK_NUMPAD2: return VKEY_NUMPAD2;
649 		case VK_NUMPAD3: return VKEY_NUMPAD3;
650 		case VK_NUMPAD4: return VKEY_NUMPAD4;
651 		case VK_NUMPAD5: return VKEY_NUMPAD5;
652 		case VK_NUMPAD6: return VKEY_NUMPAD6;
653 		case VK_NUMPAD7: return VKEY_NUMPAD7;
654 		case VK_NUMPAD8: return VKEY_NUMPAD8;
655 		case VK_NUMPAD9: return VKEY_NUMPAD9;
656 		case VK_MULTIPLY: return VKEY_MULTIPLY;
657 		case VK_ADD: return VKEY_ADD;
658 		case VK_SEPARATOR: return VKEY_SEPARATOR;
659 		case VK_SUBTRACT: return VKEY_SUBTRACT;
660 		case VK_DECIMAL: return VKEY_DECIMAL;
661 		case VK_DIVIDE: return VKEY_DIVIDE;
662 		case VK_F1: return VKEY_F1;
663 		case VK_F2: return VKEY_F2;
664 		case VK_F3: return VKEY_F3;
665 		case VK_F4: return VKEY_F4;
666 		case VK_F5: return VKEY_F5;
667 		case VK_F6: return VKEY_F6;
668 		case VK_F7: return VKEY_F7;
669 		case VK_F8: return VKEY_F8;
670 		case VK_F9: return VKEY_F9;
671 		case VK_F10: return VKEY_F10;
672 		case VK_F11: return VKEY_F11;
673 		case VK_F12: return VKEY_F12;
674 		case VK_NUMLOCK: return VKEY_NUMLOCK;
675 		case VK_SCROLL: return VKEY_SCROLL;
676 		case VK_SHIFT: return VKEY_SHIFT;
677 		case VK_CONTROL: return VKEY_CONTROL;
678 		case VK_MENU: return VKEY_ALT;
679 		case VKEY_EQUALS: return VKEY_EQUALS;
680 	}
681 	return 0;
682 }
683 
684 //-----------------------------------------------------------------------------
proc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)685 LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
686 {
687 	if (getFrame () == nullptr)
688 		return DefWindowProc (hwnd, message, wParam, lParam);
689 
690 	SharedPointer<Win32Frame> lifeGuard (this);
691 	IPlatformFrameCallback* pFrame = getFrame ();
692 	bool doubleClick = false;
693 
694 #define WM_POINTERDOWN                   0x0246
695 #define WM_POINTERUP                     0x0247
696 #define WM_POINTERROUTEDRELEASED         0x0253
697 #define WM_POINTERCAPTURECHANGED         0x024C
698 
699 	switch (message)
700 	{
701 		case WM_POINTERDOWN:
702 		{
703 			isTouchActive = true;
704 			break;
705 		}
706 		case WM_POINTERUP:
707 		case WM_POINTERROUTEDRELEASED:
708 		case WM_POINTERCAPTURECHANGED:
709 		{
710 			isTouchActive = false;
711 			break;
712 		}
713 		case WM_MOUSEWHEEL:
714 		{
715 			CButtonState buttons = 0;
716 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
717 				buttons |= kShift;
718 			if (GetAsyncKeyState (VK_CONTROL) < 0)
719 				buttons |= kControl;
720 			if (GetAsyncKeyState (VK_MENU)    < 0)
721 				buttons |= kAlt;
722 			short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam);
723 			POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)};
724 			ScreenToClient (windowHandle, &p);
725 			CPoint where (p.x, p.y);
726 			pFrame->platformOnMouseWheel (where, kMouseWheelAxisY, ((float)zDelta / WHEEL_DELTA), buttons);
727 			break;
728 		}
729 		case WM_MOUSEHWHEEL:	// new since vista
730 		{
731 			CButtonState buttons = 0;
732 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
733 				buttons |= kShift;
734 			if (GetAsyncKeyState (VK_CONTROL) < 0)
735 				buttons |= kControl;
736 			if (GetAsyncKeyState (VK_MENU)    < 0)
737 				buttons |= kAlt;
738 			short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam);
739 			POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)};
740 			ScreenToClient (windowHandle, &p);
741 			CPoint where (p.x, p.y);
742 			pFrame->platformOnMouseWheel (where, kMouseWheelAxisX, ((float)-zDelta / WHEEL_DELTA), buttons);
743 			break;
744 		}
745 		case WM_CTLCOLOREDIT:
746 		{
747 			Win32TextEdit* win32TextEdit = (Win32TextEdit*)(LONG_PTR) GetWindowLongPtr ((HWND)lParam, GWLP_USERDATA);
748 			if (win32TextEdit)
749 			{
750 				CColor fontColor = win32TextEdit->getTextEdit ()->platformGetFontColor ();
751 				SetTextColor ((HDC) wParam, RGB (fontColor.red, fontColor.green, fontColor.blue));
752 #if 1 // TODO: I don't know why the transparent part does not work anymore. Needs more investigation.
753 				CColor backColor = win32TextEdit->getTextEdit ()->platformGetBackColor ();
754 				SetBkColor ((HDC) wParam, RGB (backColor.red, backColor.green, backColor.blue));
755 				return (LRESULT)(win32TextEdit->getPlatformBackColor ());
756 #else
757 				SetBkMode ((HDC)wParam, TRANSPARENT);
758 				return (LRESULT) ::GetStockObject (HOLLOW_BRUSH);
759 #endif
760 			}
761 			break;
762 		}
763 
764 		case WM_PAINT:
765 		{
766 			paint (hwnd);
767 			return 0;
768 		}
769 
770 		case WM_RBUTTONDBLCLK:
771 		case WM_MBUTTONDBLCLK:
772 		case WM_LBUTTONDBLCLK:
773 		case WM_XBUTTONDBLCLK:
774 			doubleClick = true;
775 		case WM_RBUTTONDOWN:
776 		case WM_MBUTTONDOWN:
777 		case WM_LBUTTONDOWN:
778 		case WM_XBUTTONDOWN:
779 		{
780 			CButtonState buttons = 0;
781 			if (wParam & MK_LBUTTON)
782 				buttons |= kLButton;
783 			if (wParam & MK_RBUTTON)
784 				buttons |= kRButton;
785 			if (wParam & MK_MBUTTON)
786 				buttons |= kMButton;
787 			if (wParam & MK_XBUTTON1)
788 				buttons |= kButton4;
789 			if (wParam & MK_XBUTTON2)
790 				buttons |= kButton5;
791 			if (wParam & MK_CONTROL)
792 				buttons |= kControl;
793 			if (wParam & MK_SHIFT)
794 				buttons |= kShift;
795 			if (GetAsyncKeyState (VK_MENU)    < 0)
796 				buttons |= kAlt;
797 			if (doubleClick)
798 				buttons |= kDoubleClick;
799 			HWND oldFocus = SetFocus(getPlatformWindow());
800 			if(oldFocus != hwnd)
801 				oldFocusWindow = oldFocus;
802 			if (isTouchActive)
803 				buttons |= kTouch;
804 
805 			CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
806 			if (pFrame->platformOnMouseDown (where, buttons) == kMouseEventHandled && getPlatformWindow ())
807 				SetCapture (getPlatformWindow ());
808 			return 0;
809 		}
810 		case WM_MOUSELEAVE:
811 		{
812 			CPoint where;
813 			getCurrentMousePosition (where);
814 			CButtonState buttons;
815 			getCurrentMouseButtons (buttons);
816 			pFrame->platformOnMouseExited (where, buttons);
817 			mouseInside = false;
818 			return 0;
819 		}
820 		case WM_MOUSEMOVE:
821 		{
822 			CButtonState buttons = 0;
823 			if (wParam & MK_LBUTTON)
824 				buttons |= kLButton;
825 			if (wParam & MK_RBUTTON)
826 				buttons |= kRButton;
827 			if (wParam & MK_MBUTTON)
828 				buttons |= kMButton;
829 			if (wParam & MK_XBUTTON1)
830 				buttons |= kButton4;
831 			if (wParam & MK_XBUTTON2)
832 				buttons |= kButton5;
833 			if (wParam & MK_CONTROL)
834 				buttons |= kControl;
835 			if (wParam & MK_SHIFT)
836 				buttons |= kShift;
837 			if (GetAsyncKeyState (VK_MENU) < 0)
838 				buttons |= kAlt;
839 			if (isTouchActive)
840 				buttons |= kTouch;
841 			if (!mouseInside)
842 			{
843 				// this makes sure that WM_MOUSELEAVE will be generated by the system
844 				mouseInside = true;
845 				TRACKMOUSEEVENT tme = {0};
846 				tme.cbSize = sizeof (tme);
847 				tme.dwFlags = TME_LEAVE;
848 				tme.hwndTrack = windowHandle;
849 				TrackMouseEvent (&tme);
850 			}
851 			CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
852 			pFrame->platformOnMouseMoved (where, buttons);
853 			return 0;
854 		}
855 		case WM_LBUTTONUP:
856 		case WM_RBUTTONUP:
857 		case WM_MBUTTONUP:
858 		case WM_XBUTTONUP:
859 		{
860 			CButtonState buttons = 0;
861 			if (message & MK_LBUTTON || message == WM_LBUTTONUP)
862 				buttons |= kLButton;
863 			if (wParam & MK_RBUTTON || message == WM_RBUTTONUP)
864 				buttons |= kRButton;
865 			if (wParam & MK_MBUTTON || message == WM_MBUTTONUP)
866 				buttons |= kMButton;
867 			if (wParam & MK_XBUTTON1)
868 				buttons |= kButton4;
869 			if (wParam & MK_XBUTTON2)
870 				buttons |= kButton5;
871 			if (wParam & MK_CONTROL)
872 				buttons |= kControl;
873 			if (wParam & MK_SHIFT)
874 				buttons |= kShift;
875 			if (GetAsyncKeyState (VK_MENU) < 0)
876 				buttons |= kAlt;
877 			if (isTouchActive)
878 				buttons |= kTouch;
879 
880 			POINT p{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
881 			if (inPopup)
882 			{
883 				// Win32OptionMenu::popup does ClientToScreen for TrackPopupMenu, must do the opposite for MouseUp.
884 				ScreenToClient(windowHandle, &p);
885 				inPopup = false;
886 			}
887 
888 			CPoint where(p.x, p.y);
889 			pFrame->platformOnMouseUp (where, buttons);
890 			ReleaseCapture ();
891 			return 0;
892 		}
893 		case WM_KEYDOWN:
894 		{
895 			VstKeyCode key {};
896 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
897 				key.modifier |= MODIFIER_SHIFT;
898 			if (GetAsyncKeyState (VK_CONTROL) < 0)
899 				key.modifier |= MODIFIER_CONTROL;
900 			if (GetAsyncKeyState (VK_MENU)    < 0)
901 				key.modifier |= MODIFIER_ALTERNATE;
902 			key.virt = translateWinVirtualKey (wParam);
903 			key.character = MapVirtualKey (static_cast<UINT> (wParam), MAPVK_VK_TO_CHAR);
904 			if (key.virt || key.character)
905 			{
906 				key.character = std::tolower (key.character);
907 				if (pFrame->platformOnKeyDown (key))
908 					return 0;
909 			}
910 
911 			if (IsWindow (oldFocusWindow))
912 			{
913 				auto oldProc = reinterpret_cast<WNDPROC> (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC));
914 				if (oldProc && oldProc != WindowProc)
915 					return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
916 			}
917 			break;
918 		}
919 		case WM_KEYUP:
920 		{
921 			VstKeyCode key {};
922 			if (GetAsyncKeyState (VK_SHIFT)   < 0)
923 				key.modifier |= MODIFIER_SHIFT;
924 			if (GetAsyncKeyState (VK_CONTROL) < 0)
925 				key.modifier |= MODIFIER_CONTROL;
926 			if (GetAsyncKeyState (VK_MENU)    < 0)
927 				key.modifier |= MODIFIER_ALTERNATE;
928 			key.virt = translateWinVirtualKey (wParam);
929 			key.character = MapVirtualKey (static_cast<UINT> (wParam), MAPVK_VK_TO_CHAR);
930 			if (key.virt || key.character)
931 			{
932 				if (pFrame->platformOnKeyUp (key))
933 					return 0;
934 			}
935 
936 			if (IsWindow (oldFocusWindow))
937 			{
938 				auto oldProc = reinterpret_cast<WNDPROC> (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC));
939 				if (oldProc && oldProc != WindowProc)
940 					return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
941 			}
942 			break;
943 		}
944 		case WM_SETFOCUS:
945 		{
946 			pFrame->platformOnActivate (true);
947 			break;
948 		}
949 		case WM_KILLFOCUS:
950 		{
951 			oldFocusWindow = 0;
952 
953 			HWND focusWindow = GetFocus ();
954 			if (GetParent (focusWindow) != windowHandle)
955 				pFrame->platformOnActivate (false);
956 			break;
957 		}
958 		case WM_DESTROY:
959 		{
960 #if DEBUG
961 			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");
962 #endif
963 			if (parentWindow)
964 				windowHandle = 0;
965 			break;
966 		}
967 		case WM_ERASEBKGND:
968 		{
969 			return 1; // don't draw background
970 		}
971 		case WM_COMMAND:
972 		{
973 			if (HIWORD (wParam) == EN_CHANGE)
974 			{
975 				// text control changes will be forwarded to the text control window proc
976 				HWND controlWindow = (HWND)lParam;
977 				WINDOWSPROC textEditWindowProc = (WINDOWSPROC)(LONG_PTR)GetWindowLongPtr (controlWindow, GWLP_WNDPROC);
978 				if (textEditWindowProc)
979 				{
980 					textEditWindowProc (controlWindow, WM_COMMAND, wParam, lParam);
981 				}
982 			}
983 			break;
984 		}
985 	}
986 	return DefWindowProc (hwnd, message, wParam, lParam);
987 }
988 
989 //-----------------------------------------------------------------------------
WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)990 LONG_PTR WINAPI Win32Frame::WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
991 {
992 	Win32Frame* win32Frame = (Win32Frame*)(LONG_PTR)GetWindowLongPtr (hwnd, GWLP_USERDATA);
993 	if (win32Frame)
994 	{
995 		return win32Frame->proc (hwnd, message, wParam, lParam);
996 	}
997 	return DefWindowProc (hwnd, message, wParam, lParam);
998 }
999 
1000 //-----------------------------------------------------------------------------
create(const ColorStopMap & colorStopMap)1001 CGradient* CGradient::create (const ColorStopMap& colorStopMap)
1002 {
1003 	return new CGradient (colorStopMap);
1004 }
1005 
1006 } // namespace
1007 
1008 #endif // WINDOWS
1009