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 "cframe.h"
6 #include "coffscreencontext.h"
7 #include "ctooltipsupport.h"
8 #include "itouchevent.h"
9 #include "iscalefactorchangedlistener.h"
10 #include "idatapackage.h"
11 #include "animation/animator.h"
12 #include "controls/ctextedit.h"
13 #include "platform/iplatformframe.h"
14 #include <cassert>
15 #include <vector>
16 #include <queue>
17 #include <limits>
18 
19 namespace VSTGUI {
20 
21 IdStringPtr kMsgNewFocusView = "kMsgNewFocusView";
22 IdStringPtr kMsgOldFocusView = "kMsgOldFocusView";
23 
24 #define DEBUG_MOUSE_VIEWS	0//DEBUG
25 
26 //------------------------------------------------------------------------
27 struct CFrame::CollectInvalidRects
28 {
29 	explicit CollectInvalidRects (CFrame* frame);
30 	~CollectInvalidRects () noexcept;
31 
32 	void addRect (const CRect& rect);
33 	void flush ();
34 
35 private:
36 	using InvalidRects = std::vector<CRect>;
37 
38 	SharedPointer<CFrame> frame;
39 	InvalidRects invalidRects;
40 	uint32_t lastTicks;
41 #if VSTGUI_LOG_COLLECT_INVALID_RECTS
42 	uint32_t numAddedRects;
43 #endif
44 };
45 
46 //------------------------------------------------------------------------
47 struct ModalViewSession
48 {
49 	ModalViewSessionID identifier {};
50 	SharedPointer<CView> view;
51 };
52 
53 //------------------------------------------------------------------------
54 struct CFrame::Impl
55 {
56 	using ViewList = std::list<CView*>;
57 	using FunctionQueue = std::queue<EventProcessingFunction>;
58 	using ModalViewSessionStack = std::stack<ModalViewSession>;
59 
60 	SharedPointer<IPlatformFrame> platformFrame;
61 	VSTGUIEditorInterface* editor {nullptr};
62 	IViewAddedRemovedObserver* viewAddedRemovedObserver {nullptr};
63 	SharedPointer<CTooltipSupport> tooltips;
64 	SharedPointer<Animation::Animator> animator;
65 #if VSTGUI_ENABLE_DEPRECATED_METHODS
66 	Optional<ModalViewSessionID> legacyModalViewSessionID;
67 #endif
68 	CView* focusView {nullptr};
69 	CView* activeFocusView {nullptr};
70 	CollectInvalidRects* collectInvalidRects {nullptr};
71 
72 	ViewList mouseViews;
73 	ModalViewSessionStack modalViewSessionStack;
74 	DispatchList<CView*> windowActiveStateChangeViews;
75 	DispatchList<IScaleFactorChangedListener*> scaleFactorChangedListenerList;
76 	DispatchList<IMouseObserver*> mouseObservers;
77 	DispatchList<IFocusViewObserver*> focusViewObservers;
78 	DispatchList<IKeyboardHook*> keyboardHooks;
79 	FunctionQueue postEventFunctionQueue;
80 
81 	ModalViewSessionID modalViewSessionIDCounter {0};
82 	double userScaleFactor {1.};
83 	double platformScaleFactor {1.};
84 	bool active {false};
85 	bool windowActive {false};
86 	bool inEventHandling {false};
87 	BitmapInterpolationQuality bitmapQuality {BitmapInterpolationQuality::kDefault};
88 
89 	struct PostEventHandler
90 	{
PostEventHandlerVSTGUI::CFrame::Impl::PostEventHandler91 		PostEventHandler (Impl& impl) : impl (impl)
92 		{
93 			wasInEventHandling = impl.inEventHandling;
94 			impl.inEventHandling = true;
95 		}
~PostEventHandlerVSTGUI::CFrame::Impl::PostEventHandler96 		~PostEventHandler () noexcept
97 		{
98 			vstgui_assert (impl.inEventHandling == true);
99 			impl.inEventHandling = wasInEventHandling;
100 			FunctionQueue fl;
101 			impl.postEventFunctionQueue.swap (fl);
102 			while (!fl.empty ())
103 			{
104 				fl.front () ();
105 				fl.pop ();
106 			}
107 		}
108 
109 	private:
110 		Impl& impl;
111 		bool wasInEventHandling;
112 	};
113 };
114 
115 //-----------------------------------------------------------------------------
116 // CFrame Implementation
117 //-----------------------------------------------------------------------------
118 /*! @class CFrame
119 It creates a platform dependend view object.
120 
121 On Mac OS X it is a HIView or NSView.\n
122 On Windows it's a WS_CHILD Window.
123 
124 */
125 //-----------------------------------------------------------------------------
CFrame(const CRect & inSize,VSTGUIEditorInterface * inEditor)126 CFrame::CFrame (const CRect& inSize, VSTGUIEditorInterface* inEditor) : CViewContainer (inSize)
127 {
128 	pImpl = new Impl;
129 	pImpl->editor = inEditor;
130 
131 	setParentFrame (this);
132 }
133 
134 //-----------------------------------------------------------------------------
beforeDelete()135 void CFrame::beforeDelete ()
136 {
137 	clearMouseViews (CPoint (0, 0), 0, false);
138 
139 	clearModalViewSessions ();
140 
141 	setCursor (kCursorDefault);
142 
143 	setParentFrame (nullptr);
144 	removeAll ();
145 
146 	pImpl->tooltips = nullptr;
147 	pImpl->animator = nullptr;
148 
149 #if DEBUG
150 	if (!pImpl->scaleFactorChangedListenerList.empty ())
151 	{
152 		DebugPrint ("Warning: Scale Factor Changed Listeners are not cleaned up correctly.\n If you register a change listener you must also unregister it !\n");
153 	}
154 
155 	if (!pImpl->mouseObservers.empty ())
156 	{
157 		DebugPrint ("Warning: Mouse Observers are not cleaned up correctly.\n If you register a mouse oberver you must also unregister it !\n");
158 	}
159 
160 	if (!pImpl->keyboardHooks.empty ())
161 	{
162 		DebugPrint ("Warning: Keyboard Hooks are not cleaned up correctly.\n If you register a keyboard hook you must also unregister it !\n");
163 	}
164 #endif
165 
166 	if (pImpl->platformFrame)
167 	{
168 		pImpl->platformFrame->onFrameClosed ();
169 		pImpl->platformFrame = nullptr;
170 	}
171 
172 	setViewFlag (kIsAttached, false);
173 
174 	delete pImpl;
175 	pImpl = nullptr;
176 
177 	CViewContainer::beforeDelete ();
178 }
179 
180 //-----------------------------------------------------------------------------
close()181 void CFrame::close ()
182 {
183 	clearMouseViews (CPoint (0, 0), 0, false);
184 
185 	clearModalViewSessions ();
186 
187 	setCursor (kCursorDefault);
188 	setParentFrame (nullptr);
189 	removeAll ();
190 	if (pImpl->platformFrame)
191 	{
192 		pImpl->platformFrame->onFrameClosed ();
193 		pImpl->platformFrame = nullptr;
194 	}
195 	forget ();
196 }
197 
198 //-----------------------------------------------------------------------------
open(void * systemWin,PlatformType systemWindowType,IPlatformFrameConfig * config)199 bool CFrame::open (void* systemWin, PlatformType systemWindowType, IPlatformFrameConfig* config)
200 {
201 	if (!systemWin || isAttached ())
202 		return false;
203 
204 	pImpl->platformFrame = owned (IPlatformFrame::createPlatformFrame (this, getViewSize (), systemWin, systemWindowType, config));
205 	if (!pImpl->platformFrame)
206 	{
207 		return false;
208 	}
209 
210 	CollectInvalidRects cir (this);
211 
212 	attached (this);
213 
214 	setParentView (nullptr);
215 
216 	invalid ();
217 
218 	return true;
219 }
220 
221 //-----------------------------------------------------------------------------
attached(CView * parent)222 bool CFrame::attached (CView* parent)
223 {
224 	if (isAttached ())
225 		return false;
226 	vstgui_assert (parent == this);
227 	if (CView::attached (parent))
228 	{
229 		setParentView (nullptr);
230 
231 		for (const auto& pV : getChildren ())
232 			pV->attached (this);
233 
234 		return true;
235 	}
236 	return false;
237 }
238 
239 //-----------------------------------------------------------------------------
setZoom(double zoomFactor)240 bool CFrame::setZoom (double zoomFactor)
241 {
242 	if (zoomFactor == 0.)
243 		return false;
244 
245 	bool result = true;
246 	CGraphicsTransform currentTransform = getTransform ();
247 	CCoord origWidth = getWidth () / currentTransform.m11;
248 	CCoord origHeight = getHeight () / currentTransform.m22;
249 	CCoord newWidth = std::round (origWidth * zoomFactor);
250 	CCoord newHeight = std::round (origHeight * zoomFactor);
251 	setAutosizingEnabled (false);
252 	setTransform (CGraphicsTransform ().scale (zoomFactor, zoomFactor));
253 	if (!setSize (newWidth, newHeight))
254 	{
255 		setTransform (currentTransform);
256 		setSize (origWidth * currentTransform.m11, origHeight * currentTransform.m22);
257 		result = false;
258 	}
259 	invalid ();
260 	setAutosizingEnabled (true);
261 	if (result)
262 	{
263 		pImpl->userScaleFactor = zoomFactor;
264 		dispatchNewScaleFactor (getScaleFactor ());
265 	}
266 	return result;
267 }
268 
269 //-----------------------------------------------------------------------------
getZoom() const270 double CFrame::getZoom () const
271 {
272 	return pImpl->userScaleFactor;
273 }
274 
275 //-----------------------------------------------------------------------------
setBitmapInterpolationQuality(BitmapInterpolationQuality quality)276 void CFrame::setBitmapInterpolationQuality (BitmapInterpolationQuality quality)
277 {
278 	if (pImpl && pImpl->bitmapQuality != quality)
279 	{
280 		pImpl->bitmapQuality = quality;
281 		invalid ();
282 	}
283 }
284 
285 //-----------------------------------------------------------------------------
getBitmapInterpolationQuality() const286 BitmapInterpolationQuality CFrame::getBitmapInterpolationQuality () const
287 {
288 	if (pImpl)
289 		return pImpl->bitmapQuality;
290 	return BitmapInterpolationQuality::kDefault;
291 }
292 
293 //-----------------------------------------------------------------------------
getScaleFactor() const294 double CFrame::getScaleFactor () const
295 {
296 	return pImpl->platformScaleFactor * pImpl->userScaleFactor;
297 }
298 
299 //-----------------------------------------------------------------------------
enableTooltips(bool state,uint32_t delayTimeInMs)300 void CFrame::enableTooltips (bool state, uint32_t delayTimeInMs)
301 {
302 	if (state)
303 	{
304 		if (pImpl->tooltips == nullptr)
305 			pImpl->tooltips = makeOwned<CTooltipSupport> (this, delayTimeInMs);
306 	}
307 	else if (pImpl->tooltips)
308 	{
309 		pImpl->tooltips = nullptr;
310 	}
311 }
312 
313 //-----------------------------------------------------------------------------
draw(CDrawContext * pContext)314 void CFrame::draw (CDrawContext* pContext)
315 {
316 	return CFrame::drawRect (pContext, getViewSize ());
317 }
318 
319 //-----------------------------------------------------------------------------
drawRect(CDrawContext * pContext,const CRect & updateRect)320 void CFrame::drawRect (CDrawContext* pContext, const CRect& updateRect)
321 {
322 	if (updateRect.getWidth () <= 0 || updateRect.getHeight () <= 0 || pContext == nullptr)
323 		return;
324 
325 	auto lifeGuard = shared (pContext);
326 
327 	if (pImpl)
328 		pContext->setBitmapInterpolationQuality (pImpl->bitmapQuality);
329 
330 	drawClipped (pContext, updateRect, [&] () {
331 		// draw the background and the children
332 		CViewContainer::drawRect (pContext, updateRect);
333 	});
334 }
335 
336 //-----------------------------------------------------------------------------
clearMouseViews(const CPoint & where,const CButtonState & buttons,bool callMouseExit)337 void CFrame::clearMouseViews (const CPoint& where, const CButtonState& buttons, bool callMouseExit)
338 {
339 	CPoint lp;
340 	auto it = pImpl->mouseViews.rbegin ();
341 	while (it != pImpl->mouseViews.rend ())
342 	{
343 		if (callMouseExit)
344 		{
345 			lp = where;
346 			(*it)->frameToLocal (lp);
347 			(*it)->onMouseExited (lp, buttons);
348 		#if DEBUG_MOUSE_VIEWS
349 			DebugPrint ("mouseExited : %p\n", (*it));
350 		#endif
351 		}
352 		if (pImpl->tooltips)
353 			pImpl->tooltips->onMouseExited ((*it));
354 
355 		callMouseObserverMouseExited ((*it));
356 
357 		(*it)->forget ();
358 		++it;
359 	}
360 	pImpl->mouseViews.clear ();
361 }
362 
363 //-----------------------------------------------------------------------------
removeFromMouseViews(CView * view)364 void CFrame::removeFromMouseViews (CView* view)
365 {
366 	bool found = false;
367 	auto it = pImpl->mouseViews.begin ();
368 	while (it != pImpl->mouseViews.end ())
369 	{
370 		if (found || (*it) == view)
371 		{
372 			if (pImpl->tooltips)
373 				pImpl->tooltips->onMouseExited ((*it));
374 
375 			callMouseObserverMouseExited ((*it));
376 
377 			(*it)->forget ();
378 			pImpl->mouseViews.erase (it++);
379 			found = true;
380 		}
381 		else
382 			++it;
383 	}
384 }
385 
386 //-----------------------------------------------------------------------------
checkMouseViews(const CPoint & where,const CButtonState & buttons)387 void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons)
388 {
389 	if (getMouseDownView ())
390 		return;
391 	CPoint lp;
392 	CView* mouseView = getViewAt (where, GetViewOptions ().deep ().mouseEnabled ().includeViewContainer ());
393 	CView* currentMouseView = pImpl->mouseViews.empty () == false ? pImpl->mouseViews.back () : nullptr;
394 	if (currentMouseView == mouseView)
395 		return; // no change
396 
397 	if (pImpl->tooltips)
398 	{
399 		if (currentMouseView)
400 			pImpl->tooltips->onMouseExited (currentMouseView);
401 		if (mouseView && mouseView != this)
402 			pImpl->tooltips->onMouseEntered (mouseView);
403 	}
404 
405 	if (mouseView == nullptr || mouseView == this)
406 	{
407 		clearMouseViews (where, buttons);
408 		return;
409 	}
410 	CViewContainer* vc = currentMouseView ? currentMouseView->asViewContainer () : nullptr;
411 	// if the currentMouseView is not a view container, we know that the new mouseView won't be a child of it and that all other
412 	// views in the list are viewcontainers
413 	if (vc == nullptr && currentMouseView)
414 	{
415 		lp = where;
416 		currentMouseView->frameToLocal (lp);
417 		currentMouseView->onMouseExited (lp, buttons);
418 		callMouseObserverMouseExited (currentMouseView);
419 	#if DEBUG_MOUSE_VIEWS
420 		DebugPrint ("mouseExited : %p\n", currentMouseView);
421 	#endif
422 		currentMouseView->forget ();
423 		pImpl->mouseViews.remove (currentMouseView);
424 	}
425 	auto it = pImpl->mouseViews.rbegin ();
426 	while (it != pImpl->mouseViews.rend ())
427 	{
428 		vc = static_cast<CViewContainer*> ((*it));
429 		if (vc == mouseView)
430 			return;
431 		if (vc->isChild (mouseView, true) == false)
432 		{
433 			lp = where;
434 			vc->frameToLocal (lp);
435 			vc->onMouseExited (lp, buttons);
436 			callMouseObserverMouseExited (vc);
437 		#if DEBUG_MOUSE_VIEWS
438 			DebugPrint ("mouseExited : %p\n", vc);
439 		#endif
440 			vc->forget ();
441 			pImpl->mouseViews.erase (--it.base ());
442 		}
443 		else
444 			break;
445 	}
446 	vc = pImpl->mouseViews.empty () == false ? pImpl->mouseViews.back ()->asViewContainer () : nullptr;
447 	if (vc)
448 	{
449 		auto it2 = pImpl->mouseViews.end ();
450 		--it2;
451 		CView* container = mouseView;
452 		while ((vc = static_cast<CViewContainer*> (container->getParentView ())) != *it2 && vc)
453 		{
454 			pImpl->mouseViews.emplace_back (vc);
455 			vc->remember ();
456 			container = vc;
457 		}
458 		pImpl->mouseViews.emplace_back (mouseView);
459 		mouseView->remember ();
460 		++it2;
461 		while (it2 != pImpl->mouseViews.end ())
462 		{
463 			lp = where;
464 			(*it2)->frameToLocal (lp);
465 			(*it2)->onMouseEntered (lp, buttons);
466 			callMouseObserverMouseEntered ((*it2));
467 		#if DEBUG_MOUSE_VIEWS
468 			DebugPrint ("mouseEntered : %p\n", (*it2));
469 		#endif
470 			++it2;
471 		}
472 	}
473 	else
474 	{
475 		// must be pMouseViews.size () == 0
476 		vstgui_assert (pImpl->mouseViews.empty ());
477 		pImpl->mouseViews.emplace_back (mouseView);
478 		mouseView->remember ();
479 		while ((vc = static_cast<CViewContainer*> (mouseView->getParentView ())) != this && vc)
480 		{
481 			pImpl->mouseViews.push_front (vc);
482 			vc->remember ();
483 			mouseView = vc;
484 		}
485 		auto it2 = pImpl->mouseViews.begin ();
486 		while (it2 != pImpl->mouseViews.end ())
487 		{
488 			lp = where;
489 			(*it2)->frameToLocal (lp);
490 			(*it2)->onMouseEntered (lp, buttons);
491 			callMouseObserverMouseEntered ((*it2));
492 		#if DEBUG_MOUSE_VIEWS
493 			DebugPrint ("mouseEntered : %p\n", (*it2));
494 		#endif
495 			++it2;
496 		}
497 	}
498 }
499 
500 //------------------------------------------------------------------------
hitTestSubViews(const CPoint & where,const CButtonState & buttons)501 bool CFrame::hitTestSubViews (const CPoint& where, const CButtonState& buttons)
502 {
503 	if (auto modalView = getModalView ())
504 	{
505 		CPoint where2 (where);
506 		getTransform ().inverse ().transform (where2);
507 		if (modalView->isVisible () && modalView->getMouseEnabled () && modalView->hitTest (where2, buttons))
508 		{
509 			if (auto viewContainer = modalView->asViewContainer ())
510 			{
511 				return viewContainer->hitTestSubViews (where2, buttons);
512 			}
513 			return true;
514 		}
515 		return false;
516 	}
517 	return CViewContainer::hitTestSubViews (where, buttons);
518 }
519 
520 //-----------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)521 CMouseEventResult CFrame::onMouseDown (CPoint &where, const CButtonState& buttons)
522 {
523 	CPoint where2 (where);
524 	getTransform ().inverse ().transform (where2);
525 
526 	if (pImpl->tooltips)
527 		pImpl->tooltips->onMouseDown (where2);
528 
529 	CMouseEventResult result = callMouseObserverMouseDown (where, buttons);
530 	if (result != kMouseEventNotHandled)
531 		return result;
532 
533 	// reset views
534 	setMouseDownView (nullptr);
535 	if (pImpl->focusView && dynamic_cast<CTextEdit*> (pImpl->focusView))
536 		setFocusView (nullptr);
537 
538 	if (auto modalView = getModalView ())
539 	{
540 		CBaseObjectGuard rg (modalView);
541 
542 		if (modalView->isVisible () && modalView->getMouseEnabled ())
543 		{
544 			result = modalView->callMouseListener (MouseListenerCall::MouseDown, where2, buttons);
545 			if (result == kMouseEventNotHandled || result == kMouseEventNotImplemented)
546 				result = modalView->onMouseDown (where2, buttons);
547 			if (result == kMouseEventHandled)
548 			{
549 				setMouseDownView (modalView);
550 				return kMouseEventHandled;
551 			}
552 			return result;
553 		}
554 	}
555 	else
556 		return CViewContainer::onMouseDown (where, buttons);
557 	return kMouseEventNotHandled;
558 }
559 
560 //-----------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)561 CMouseEventResult CFrame::onMouseUp (CPoint &where, const CButtonState& buttons)
562 {
563 	CMouseEventResult result = CViewContainer::onMouseUp (where, buttons);
564 	CButtonState modifiers = buttons & (kShift | kControl | kAlt | kApple);
565 	checkMouseViews (where, modifiers);
566 	return result;
567 }
568 
569 //-----------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)570 CMouseEventResult CFrame::onMouseMoved (CPoint &where, const CButtonState& buttons)
571 {
572 	CPoint where2 (where);
573 	getTransform ().inverse ().transform (where2);
574 
575 	if (pImpl->tooltips)
576 		pImpl->tooltips->onMouseMoved (where2);
577 
578 	checkMouseViews (where, buttons);
579 
580 	CMouseEventResult result = callMouseObserverMouseMoved (where, buttons);
581 	if (result != kMouseEventNotHandled)
582 		return result;
583 
584 	if (auto modalView = getModalView ())
585 	{
586 		CBaseObjectGuard rg (modalView);
587 		result = modalView->callMouseListener (MouseListenerCall::MouseMoved, where2, buttons);
588 		if (result == kMouseEventNotHandled || result == kMouseEventNotImplemented)
589 			result = modalView->onMouseMoved (where2, buttons);
590 	}
591 	else
592 	{
593 		result = CViewContainer::onMouseMoved (where, buttons);
594 	}
595 	if (result == kMouseEventNotHandled)
596 	{
597 		CButtonState buttons2 = (buttons & (kShift | kControl | kAlt | kApple));
598 		auto it = pImpl->mouseViews.rbegin ();
599 		while (it != pImpl->mouseViews.rend ())
600 		{
601 			CPoint p = where2;
602 			auto parent = (*it)->getParentView ();
603 			if (parent)
604 			{
605 				parent->frameToLocal (p);
606 				result = (*it)->onMouseMoved (p, buttons2);
607 				if (result == kMouseEventHandled)
608 					break;
609 			}
610 			++it;
611 		}
612 	}
613 	return result;
614 }
615 
616 //-----------------------------------------------------------------------------
onMouseExited(CPoint & where,const CButtonState & buttons)617 CMouseEventResult CFrame::onMouseExited (CPoint &where, const CButtonState& buttons)
618 { // this should only get called from the platform implementation
619 
620 	if (getMouseDownView () == nullptr)
621 	{
622 		clearMouseViews (where, buttons);
623 		if (pImpl->tooltips)
624 			pImpl->tooltips->hideTooltip ();
625 	}
626 
627 	return kMouseEventHandled;
628 }
629 
630 //-----------------------------------------------------------------------------
onKeyDown(VstKeyCode & keyCode)631 int32_t CFrame::onKeyDown (VstKeyCode& keyCode)
632 {
633 	int32_t result = keyboardHooksOnKeyDown (keyCode);
634 
635 	if (result == -1 && pImpl->focusView)
636 	{
637 		CBaseObjectGuard og (pImpl->focusView);
638 		if (pImpl->focusView->getMouseEnabled ())
639 			result = pImpl->focusView->onKeyDown (keyCode);
640 		if (result == -1)
641 		{
642 			CView* parent = pImpl->focusView->getParentView ();
643 			while (parent && parent != this && result == -1)
644 			{
645 				if (parent->getMouseEnabled ())
646 					result = parent->onKeyDown (keyCode);
647 				parent = parent->getParentView ();
648 			}
649 		}
650 	}
651 
652 	if (result == -1)
653 	{
654 		if (auto modalView = getModalView ())
655 		{
656 			CBaseObjectGuard og (modalView);
657 			result = modalView->onKeyDown (keyCode);
658 		}
659 	}
660 
661 	if (result == -1 && keyCode.virt == VKEY_TAB)
662 	{
663 		if (keyCode.modifier == 0 || keyCode.modifier == MODIFIER_SHIFT)
664 			result = advanceNextFocusView (pImpl->focusView, (keyCode.modifier & MODIFIER_SHIFT) ? true : false) ? 1 : -1;
665 	}
666 
667 	return result;
668 }
669 
670 //-----------------------------------------------------------------------------
onKeyUp(VstKeyCode & keyCode)671 int32_t CFrame::onKeyUp (VstKeyCode& keyCode)
672 {
673 	int32_t result = keyboardHooksOnKeyUp (keyCode);
674 
675 	if (result == -1 && pImpl->focusView)
676 	{
677 		if (pImpl->focusView->getMouseEnabled ())
678 			result = pImpl->focusView->onKeyUp (keyCode);
679 		if (result == -1)
680 		{
681 			CView* parent = pImpl->focusView->getParentView ();
682 			while (parent && parent != this && result == -1)
683 			{
684 				if (parent->getMouseEnabled ())
685 					result = parent->onKeyUp (keyCode);
686 				parent = parent->getParentView ();
687 			}
688 		}
689 	}
690 
691 	if (result == -1)
692 	{
693 		if (auto modalView = getModalView ())
694 			result = modalView->onKeyUp (keyCode);
695 	}
696 
697 	return result;
698 }
699 
700 //------------------------------------------------------------------------
onWheel(const CPoint & where,const CMouseWheelAxis & axis,const float & distance,const CButtonState & buttons)701 bool CFrame::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons)
702 {
703 	if (auto modalView = getModalView ())
704 	{
705 		CPoint where2 (where);
706 		getTransform ().inverse ().transform (where2);
707 		return modalView->onWheel (where2, axis, distance, buttons);
708 	}
709 
710 	bool result = false;
711 	if (getMouseDownView () == nullptr)
712 	{
713 		result = CViewContainer::onWheel (where, axis, distance, buttons);
714 		checkMouseViews (where, buttons);
715 	}
716 	return result;
717 }
718 
719 //-----------------------------------------------------------------------------
getClipboard()720 SharedPointer<IDataPackage> CFrame::getClipboard ()
721 {
722 	if (pImpl->platformFrame)
723 		return pImpl->platformFrame->getClipboard ();
724 	return nullptr;
725 }
726 
727 //-----------------------------------------------------------------------------
setClipboard(const SharedPointer<IDataPackage> & data)728 void CFrame::setClipboard (const SharedPointer<IDataPackage>& data)
729 {
730 	if (pImpl->platformFrame)
731 		pImpl->platformFrame->setClipboard (data);
732 }
733 
734 //-----------------------------------------------------------------------------
idle()735 void CFrame::idle ()
736 {
737 	if (CView::kDirtyCallAlwaysOnMainThread)
738 		return;
739 	invalidateDirtyViews ();
740 }
741 
742 //-----------------------------------------------------------------------------
getAnimator()743 Animation::Animator* CFrame::getAnimator ()
744 {
745 	if (pImpl->animator == nullptr)
746 		pImpl->animator = makeOwned<Animation::Animator> ();
747 	return pImpl->animator;
748 }
749 
750 //-----------------------------------------------------------------------------
751 /**
752  * @return tick count in milliseconds
753  */
getTicks() const754 uint32_t CFrame::getTicks () const
755 {
756 	if (pImpl->platformFrame)
757 		return pImpl->platformFrame->getTicks ();
758 	return std::numeric_limits<uint32_t>::max ();
759 }
760 
761 //-----------------------------------------------------------------------------
762 int32_t CFrame::kDefaultKnobMode = kCircularMode;
763 
764 //-----------------------------------------------------------------------------
getKnobMode() const765 int32_t CFrame::getKnobMode () const
766 {
767 	int32_t result = pImpl->editor ? pImpl->editor->getKnobMode () : -1;
768 	if (result == -1)
769 		result = kDefaultKnobMode;
770 	return result;
771 }
772 
773 //-----------------------------------------------------------------------------
774 /**
775  * repositions the frame
776  * @param x x coordinate
777  * @param y y coordinate
778  * @return true on success
779  */
setPosition(CCoord x,CCoord y)780 bool CFrame::setPosition (CCoord x, CCoord y)
781 {
782 	if (pImpl->platformFrame)
783 	{
784 		CRect rect (getViewSize ());
785 		rect.offset (x - getViewSize ().left, y - getViewSize ().top);
786 		if (pImpl->platformFrame->setSize (rect))
787 		{
788 			setViewSize (rect, false);
789 			return true;
790 		}
791 	}
792 	return false;
793 }
794 
795 //-----------------------------------------------------------------------------
796 /**
797  * get global position of frame
798  * @param x x coordinate
799  * @param y y coordinate
800  * @return true on success
801  */
getPosition(CCoord & x,CCoord & y) const802 bool CFrame::getPosition (CCoord &x, CCoord &y) const
803 {
804 	if (pImpl->platformFrame)
805 	{
806 		CPoint p;
807 		if (pImpl->platformFrame->getGlobalPosition (p))
808 		{
809 			x = p.x;
810 			y = p.y;
811 			return true;
812 		}
813 	}
814 	return false;
815 }
816 
817 //-----------------------------------------------------------------------------
setViewSize(const CRect & rect,bool invalid)818 void CFrame::setViewSize (const CRect& rect, bool invalid)
819 {
820 	CViewContainer::setViewSize (rect, invalid);
821 }
822 
823 //-----------------------------------------------------------------------------
824 /**
825  * set size of frame (and the platform representation)
826  * @param width new width
827  * @param height new height
828  * @return true on success
829  */
setSize(CCoord width,CCoord height)830 bool CFrame::setSize (CCoord width, CCoord height)
831 {
832 	if ((width == getViewSize ().getWidth ()) && (height == getViewSize ().getHeight ()))
833 		return false;
834 
835 	CRect newSize (getViewSize ());
836 	newSize.setWidth (width);
837 	newSize.setHeight (height);
838 
839 	if (getEditor ())
840 	{
841 		if (getEditor ()->beforeSizeChange (newSize, getViewSize ()) == false)
842 			return false;
843 	}
844 	if (pImpl->platformFrame)
845 	{
846 		if (pImpl->platformFrame->setSize (newSize))
847 		{
848 			CViewContainer::setViewSize (newSize);
849 			return true;
850 		}
851 		return false;
852 	}
853 	CViewContainer::setViewSize (newSize);
854 	return true;
855 }
856 
857 //-----------------------------------------------------------------------------
858 /**
859  * get size relative to parent
860  * @param pRect size
861  * @return true on success
862  */
getSize(CRect * pRect) const863 bool CFrame::getSize (CRect* pRect) const
864 {
865 	if (pImpl->platformFrame && pRect)
866 		return pImpl->platformFrame->getSize (*pRect);
867 	return false;
868 }
869 
870 //-----------------------------------------------------------------------------
getSize(CRect & outSize) const871 bool CFrame::getSize (CRect& outSize) const
872 {
873 	return getSize (&outSize);
874 }
875 
876 #if VSTGUI_ENABLE_DEPRECATED_METHODS
877 //-----------------------------------------------------------------------------
878 /**
879  * @param pView the view which should be set to modal.
880  * @return true if view could be set as the modal view. false if there is a already a modal view or the view to be set as modal is already attached.
881  */
setModalView(CView * pView)882 bool CFrame::setModalView (CView* pView)
883 {
884 	if (pImpl->modalViewSessionStack.empty () && pView == nullptr)
885 		return true;
886 
887 	if (pView && !pImpl->modalViewSessionStack.empty ())
888 		return false;
889 
890 	if (!pView)
891 		endLegacyModalViewSession ();
892 	else
893 		pImpl->legacyModalViewSessionID = beginModalViewSession (pView);
894 
895 	return true;
896 }
897 
898 //-----------------------------------------------------------------------------
endLegacyModalViewSession()899 void CFrame::endLegacyModalViewSession ()
900 {
901 	vstgui_assert (pImpl->legacyModalViewSessionID);
902 	vstgui_assert (pImpl->modalViewSessionStack.top ().identifier ==
903 	               *pImpl->legacyModalViewSessionID);
904 	pImpl->modalViewSessionStack.top ().view->remember ();
905 	endModalViewSession (*pImpl->legacyModalViewSessionID);
906 	pImpl->legacyModalViewSessionID = {};
907 }
908 
909 #endif
910 
911 //-----------------------------------------------------------------------------
getModalView() const912 CView* CFrame::getModalView () const
913 {
914 	if (!pImpl->modalViewSessionStack.empty ())
915 		return pImpl->modalViewSessionStack.top ().view;
916 	return nullptr;
917 }
918 
919 //-----------------------------------------------------------------------------
initModalViewSession(const ModalViewSession & session)920 void CFrame::initModalViewSession (const ModalViewSession& session)
921 {
922 	if (auto view = getMouseDownView ())
923 	{
924 		onMouseCancel ();
925 	}
926 	clearMouseViews (CPoint (0, 0), 0, true);
927 	if (auto container = session.view->asViewContainer ())
928 		container->advanceNextFocusView (nullptr, false);
929 	else
930 		setFocusView (session.view->wantsFocus () ? session.view : nullptr);
931 
932 	if (isAttached ())
933 	{
934 		CPoint where;
935 		getCurrentMouseLocation (where);
936 		checkMouseViews (where, getCurrentMouseButtons ());
937 	}
938 }
939 
940 //-----------------------------------------------------------------------------
clearModalViewSessions()941 void CFrame::clearModalViewSessions ()
942 {
943 #if VSTGUI_ENABLE_DEPRECATED_METHODS
944 	if (pImpl->legacyModalViewSessionID)
945 		endLegacyModalViewSession ();
946 #endif
947 	while (!pImpl->modalViewSessionStack.empty ())
948 		endModalViewSession (pImpl->modalViewSessionStack.top ().identifier);
949 }
950 
951 //-----------------------------------------------------------------------------
beginModalViewSession(CView * view)952 Optional<ModalViewSessionID> CFrame::beginModalViewSession (CView* view)
953 {
954 	if (view->isAttached ())
955 	{
956 #if DEBUG
957 		DebugPrint ("the view must not be attached when used for beginModalViewSession");
958 #endif
959 		return {};
960 	}
961 
962 	if (!addView (view))
963 	{
964 		return {};
965 	}
966 
967 	ModalViewSession session;
968 	session.identifier = ++pImpl->modalViewSessionIDCounter;
969 	session.view = view;
970 	pImpl->modalViewSessionStack.push (session);
971 
972 	initModalViewSession (session);
973 
974 	return makeOptional (session.identifier);
975 }
976 
977 //-----------------------------------------------------------------------------
endModalViewSession(ModalViewSessionID sessionID)978 bool CFrame::endModalViewSession (ModalViewSessionID sessionID)
979 {
980 	if (pImpl->modalViewSessionStack.empty ())
981 		return false;
982 	if (pImpl->modalViewSessionStack.top ().identifier != sessionID)
983 		return false;
984 
985 	auto view = pImpl->modalViewSessionStack.top ().view;
986 	pImpl->modalViewSessionStack.pop ();
987 
988 	removeView (view);
989 
990 	if (!pImpl->modalViewSessionStack.empty ())
991 		initModalViewSession (pImpl->modalViewSessionStack.top ());
992 
993 	return true;
994 }
995 
996 //-----------------------------------------------------------------------------
beginEdit(int32_t index)997 void CFrame::beginEdit (int32_t index)
998 {
999 	if (pImpl->editor)
1000 		pImpl->editor->beginEdit (index);
1001 }
1002 
1003 //-----------------------------------------------------------------------------
endEdit(int32_t index)1004 void CFrame::endEdit (int32_t index)
1005 {
1006 	if (pImpl->editor)
1007 		pImpl->editor->endEdit (index);
1008 }
1009 
1010 //-----------------------------------------------------------------------------
1011 /**
1012  * @param where location of mouse
1013  * @return true on success
1014  */
getCurrentMouseLocation(CPoint & where) const1015 bool CFrame::getCurrentMouseLocation (CPoint &where) const
1016 {
1017 	if (pImpl->platformFrame)
1018 	{
1019 		if (pImpl->platformFrame->getCurrentMousePosition (where))
1020 		{
1021 			getTransform().transform (where);
1022 			return true;
1023 		}
1024 	}
1025 	return false;
1026 }
1027 
1028 //-----------------------------------------------------------------------------
1029 /**
1030  * @return mouse and modifier state
1031  */
getCurrentMouseButtons() const1032 CButtonState CFrame::getCurrentMouseButtons () const
1033 {
1034 	CButtonState buttons = 0;
1035 
1036 	if (pImpl->platformFrame)
1037 		pImpl->platformFrame->getCurrentMouseButtons (buttons);
1038 
1039 	return buttons;
1040 }
1041 
1042 //-----------------------------------------------------------------------------
1043 /**
1044  * @param type cursor type see #CCursorType
1045  */
setCursor(CCursorType type)1046 void CFrame::setCursor (CCursorType type)
1047 {
1048 	if (pImpl->platformFrame)
1049 		pImpl->platformFrame->setMouseCursor (type);
1050 }
1051 
1052 //-----------------------------------------------------------------------------
1053 /**
1054  * @param pView view which was removed
1055  */
onViewRemoved(CView * pView)1056 void CFrame::onViewRemoved (CView* pView)
1057 {
1058 	removeFromMouseViews (pView);
1059 
1060 	if (pImpl->activeFocusView == pView)
1061 		pImpl->activeFocusView = nullptr;
1062 	if (pImpl->focusView == pView)
1063 	{
1064 		if (pImpl->active)
1065 			setFocusView (nullptr);
1066 		else
1067 			pImpl->focusView = nullptr;
1068 	}
1069 	if (auto container = pView->asViewContainer ())
1070 	{
1071 		if (container->isChild (pImpl->focusView, true))
1072 			setFocusView (nullptr);
1073 	}
1074 	if (getViewAddedRemovedObserver ())
1075 		getViewAddedRemovedObserver ()->onViewRemoved (this, pView);
1076 	if (pView->wantsWindowActiveStateChangeNotification ())
1077 		pImpl->windowActiveStateChangeViews.remove (pView);
1078 	if (pImpl->animator)
1079 		pImpl->animator->removeAnimations (pView);
1080 }
1081 
1082 //-----------------------------------------------------------------------------
1083 /**
1084  * @param pView view which was added
1085  */
onViewAdded(CView * pView)1086 void CFrame::onViewAdded (CView* pView)
1087 {
1088 	if (getViewAddedRemovedObserver ())
1089 		getViewAddedRemovedObserver ()->onViewAdded (this, pView);
1090 	if (pView->wantsWindowActiveStateChangeNotification ())
1091 	{
1092 		pImpl->windowActiveStateChangeViews.add (pView);
1093 		pView->onWindowActivate (pImpl->windowActive);
1094 	}
1095 }
1096 
1097 //-----------------------------------------------------------------------------
1098 /**
1099  * @param pView new focus view
1100  */
setFocusView(CView * pView)1101 void CFrame::setFocusView (CView *pView)
1102 {
1103 	static bool recursion = false;
1104 	if (pView == pImpl->focusView || (recursion && pImpl->focusView != nullptr))
1105 		return;
1106 
1107 	if (pView && !pImpl->modalViewSessionStack.empty ())
1108 	{
1109 		if (auto modalContainer = pImpl->modalViewSessionStack.top ().view->asViewContainer ())
1110 		{
1111 			if (!modalContainer->isChild (pView, true))
1112 			{
1113 #if DEBUG
1114 				DebugPrint (
1115 				    "Could not set the focus view " \
1116 				     "as it is not a child of the currently displayed modal view\n");
1117 #endif
1118 				return;
1119 			}
1120 		}
1121 	}
1122 
1123 	if (!pImpl->active)
1124 	{
1125 		pImpl->activeFocusView = pView;
1126 		return;
1127 	}
1128 
1129 	recursion = true;
1130 
1131 	CView *pOldFocusView = pImpl->focusView;
1132 	if (pView == nullptr  || (pView && pView->isAttached () == false))
1133 		pImpl->focusView = nullptr;
1134 	else
1135 		pImpl->focusView = pView;
1136 	if (pImpl->focusView && pImpl->focusView->wantsFocus ())
1137 	{
1138 		pImpl->focusView->invalid ();
1139 
1140 		CView* receiver = pImpl->focusView->getParentView ();
1141 		while (receiver != this && receiver != nullptr)
1142 		{
1143 			receiver->notify (pImpl->focusView, kMsgNewFocusView);
1144 			receiver = receiver->getParentView ();
1145 		}
1146 		notify (pImpl->focusView, kMsgNewFocusView);
1147 	}
1148 
1149 	if (pOldFocusView)
1150 	{
1151 		if (pOldFocusView->wantsFocus ())
1152 		{
1153 			pOldFocusView->invalid ();
1154 
1155 			CView* receiver = pOldFocusView->getParentView ();
1156 			while (receiver != this && receiver != nullptr)
1157 			{
1158 				receiver->notify (pOldFocusView, kMsgOldFocusView);
1159 				receiver = receiver->getParentView ();
1160 			}
1161 			notify (pOldFocusView, kMsgOldFocusView);
1162 		}
1163 		pOldFocusView->looseFocus ();
1164 	}
1165 	if (pImpl->focusView && pImpl->focusView->wantsFocus ())
1166 		pImpl->focusView->takeFocus ();
1167 
1168 	pImpl->focusViewObservers.forEach ([&] (IFocusViewObserver* observer) {
1169 		observer->onFocusViewChanged (this, pImpl->focusView, pOldFocusView);
1170 	});
1171 
1172 	recursion = false;
1173 }
1174 
1175 //-----------------------------------------------------------------------------
getFocusView() const1176 CView* CFrame::getFocusView () const
1177 {
1178 	return pImpl->focusView;
1179 }
1180 
1181 //-----------------------------------------------------------------------------
advanceNextFocusView(CView * oldFocus,bool reverse)1182 bool CFrame::advanceNextFocusView (CView* oldFocus, bool reverse)
1183 {
1184 	if (auto modalView = getModalView ())
1185 	{
1186 		if (auto container = modalView->asViewContainer ())
1187 		{
1188 			if (oldFocus == nullptr || container->isChild (oldFocus, true) == false)
1189 				return container->advanceNextFocusView (nullptr, reverse);
1190 			else
1191 			{
1192 				if (auto* parentView = static_cast<CViewContainer*> (oldFocus->getParentView ()))
1193 				{
1194 					CView* tempOldFocus = oldFocus;
1195 					while (parentView != container)
1196 					{
1197 						if (parentView->advanceNextFocusView (tempOldFocus, reverse))
1198 							return true;
1199 						else
1200 						{
1201 							tempOldFocus = parentView;
1202 							parentView = static_cast<CViewContainer*> (parentView->getParentView ());
1203 						}
1204 					}
1205 					if (container->advanceNextFocusView (tempOldFocus, reverse))
1206 						return true;
1207 					return container->advanceNextFocusView (nullptr, reverse);
1208 				}
1209 			}
1210 		}
1211 		else if (oldFocus != modalView)
1212 		{
1213 			setFocusView (modalView);
1214 			return true;
1215 		}
1216 		return false; // currently not supported, but should be done sometime
1217 	}
1218 	if (oldFocus == nullptr)
1219 	{
1220 		if (pImpl->focusView == nullptr)
1221 			return CViewContainer::advanceNextFocusView (nullptr, reverse);
1222 		oldFocus = pImpl->focusView;
1223 	}
1224 	if (isChild (oldFocus))
1225 	{
1226 		if (CViewContainer::advanceNextFocusView (oldFocus, reverse))
1227 			return true;
1228 		else
1229 		{
1230 			setFocusView (nullptr);
1231 			return false;
1232 		}
1233 	}
1234 	if (auto* parentView = static_cast<CViewContainer*> (oldFocus->getParentView ()))
1235 	{
1236 		CView* tempOldFocus = oldFocus;
1237 		while (parentView)
1238 		{
1239 			if (parentView->advanceNextFocusView (tempOldFocus, reverse))
1240 				return true;
1241 			else
1242 			{
1243 				tempOldFocus = parentView;
1244 				parentView = static_cast<CViewContainer*> (parentView->getParentView ());
1245 			}
1246 		}
1247 	}
1248 	return CViewContainer::advanceNextFocusView (oldFocus, reverse);
1249 }
1250 
1251 //-----------------------------------------------------------------------------
removeView(CView * pView,bool withForget)1252 bool CFrame::removeView (CView* pView, bool withForget)
1253 {
1254 #if DEBUG
1255 	vstgui_assert (getModalView () != pView);
1256 #endif
1257 	return CViewContainer::removeView (pView, withForget);
1258 }
1259 
1260 //-----------------------------------------------------------------------------
removeAll(bool withForget)1261 bool CFrame::removeAll (bool withForget)
1262 {
1263 	clearModalViewSessions ();
1264 	if (pImpl->focusView)
1265 	{
1266 		pImpl->focusView->looseFocus ();
1267 		pImpl->focusView = nullptr;
1268 	}
1269 	pImpl->activeFocusView = nullptr;
1270 	clearMouseViews (CPoint (0, 0), 0, false);
1271 	return CViewContainer::removeAll (withForget);
1272 }
1273 
1274 //-----------------------------------------------------------------------------
getViewAt(const CPoint & where,const GetViewOptions & options) const1275 CView* CFrame::getViewAt (const CPoint& where, const GetViewOptions& options) const
1276 {
1277 	if (auto modalView = getModalView ())
1278 	{
1279 		CPoint where2 (where);
1280 		getTransform ().inverse ().transform (where2);
1281 		if (modalView->getViewSize ().pointInside (where2))
1282 		{
1283 			if (options.getDeep ())
1284 			{
1285 				if (auto container = modalView->asViewContainer ())
1286 				{
1287 					return container->getViewAt (where2, options);
1288 				}
1289 			}
1290 			return modalView;
1291 		}
1292 		return nullptr;
1293 	}
1294 	return CViewContainer::getViewAt (where, options);
1295 }
1296 
1297 //-----------------------------------------------------------------------------
getContainerAt(const CPoint & where,const GetViewOptions & options) const1298 CViewContainer* CFrame::getContainerAt (const CPoint& where, const GetViewOptions& options) const
1299 {
1300 	if (auto modalView = getModalView ())
1301 	{
1302 		CPoint where2 (where);
1303 		getTransform ().inverse ().transform (where2);
1304 		if (modalView->getViewSize ().pointInside (where2))
1305 		{
1306 			if (auto container = modalView->asViewContainer ())
1307 			{
1308 				if (options.getDeep ())
1309 					return container->getContainerAt (where2, options);
1310 				return container;
1311 			}
1312 		}
1313 		return nullptr;
1314 	}
1315 	return CViewContainer::getContainerAt (where, options);
1316 }
1317 
1318 //------------------------------------------------------------------------
getViewsAt(const CPoint & where,ViewList & views,const GetViewOptions & options) const1319 bool CFrame::getViewsAt (const CPoint& where, ViewList& views, const GetViewOptions& options) const
1320 {
1321 	if (auto modalView = getModalView ())
1322 	{
1323 		CPoint where2 (where);
1324 		getTransform ().inverse ().transform (where2);
1325 		if (modalView->getViewSize ().pointInside (where2))
1326 		{
1327 			if (options.getDeep ())
1328 			{
1329 				if (auto container = modalView->asViewContainer ())
1330 				{
1331 					container->getViewsAt (where2, views, options);
1332 				}
1333 			}
1334 			if (!options.getIncludeViewContainer () && modalView->asViewContainer ())
1335 				return true;
1336 			if (options.getMouseEnabled () && modalView->getMouseEnabled () == false)
1337 				return true;
1338 			if (!options.getIncludeInvisible () && !modalView->isVisible ())
1339 				return true;
1340 			views.emplace_back (modalView);
1341 			return true;
1342 		}
1343 		return false;
1344 	}
1345 	return CViewContainer::getViewsAt (where, views, options);
1346 }
1347 
1348 //-----------------------------------------------------------------------------
onActivate(bool state)1349 void CFrame::onActivate (bool state)
1350 {
1351 	if (pImpl->active != state)
1352 	{
1353 		if (state)
1354 		{
1355 			pImpl->active = true;
1356 			if (pImpl->activeFocusView)
1357 			{
1358 				setFocusView (pImpl->activeFocusView);
1359 				pImpl->activeFocusView = nullptr;
1360 			}
1361 			else
1362 				advanceNextFocusView (nullptr, false);
1363 		}
1364 		else
1365 		{
1366 			if (pImpl->tooltips)
1367 				pImpl->tooltips->hideTooltip ();
1368 			pImpl->activeFocusView = getFocusView ();
1369 			setFocusView (nullptr);
1370 			pImpl->active = false;
1371 		}
1372 	}
1373 }
1374 
1375 //-----------------------------------------------------------------------------
focusDrawingEnabled() const1376 bool CFrame::focusDrawingEnabled () const
1377 {
1378 	uint32_t attrSize;
1379 	if (getAttributeSize ('vfde', attrSize))
1380 		return true;
1381 	return false;
1382 }
1383 
1384 //-----------------------------------------------------------------------------
getFocusColor() const1385 CColor CFrame::getFocusColor () const
1386 {
1387 	CColor focusColor (kRedCColor);
1388 	getAttribute ('vfco', focusColor);
1389 	return focusColor;
1390 }
1391 
1392 //-----------------------------------------------------------------------------
getFocusWidth() const1393 CCoord CFrame::getFocusWidth () const
1394 {
1395 	CCoord focusWidth = 2;
1396 	getAttribute ('vfwi', focusWidth);
1397 	return focusWidth;
1398 }
1399 
1400 //-----------------------------------------------------------------------------
setFocusDrawingEnabled(bool state)1401 void CFrame::setFocusDrawingEnabled (bool state)
1402 {
1403 	if (state)
1404 		setAttribute ('vfde', state);
1405 	else
1406 		removeAttribute ('vfde');
1407 }
1408 
1409 //-----------------------------------------------------------------------------
setFocusColor(const CColor & color)1410 void CFrame::setFocusColor (const CColor& color)
1411 {
1412 	setAttribute ('vfco', color);
1413 }
1414 
1415 //-----------------------------------------------------------------------------
setFocusWidth(CCoord width)1416 void CFrame::setFocusWidth (CCoord width)
1417 {
1418 	setAttribute ('vfwi', width);
1419 }
1420 
1421 //-----------------------------------------------------------------------------
1422 /**
1423  * @param src rect which to scroll
1424  * @param distance point of distance
1425  */
scrollRect(const CRect & src,const CPoint & distance)1426 void CFrame::scrollRect (const CRect& src, const CPoint& distance)
1427 {
1428 	CRect rect (src);
1429 	rect.offset (getViewSize ().left, getViewSize ().top);
1430 
1431 	if (pImpl->platformFrame)
1432 	{
1433 		if (pImpl->platformFrame->scrollRect (src, distance))
1434 			return;
1435 	}
1436 	invalidRect (src);
1437 }
1438 
1439 //-----------------------------------------------------------------------------
invalidate(const CRect & rect)1440 void CFrame::invalidate (const CRect &rect)
1441 {
1442 	for (const auto& pV : getChildren ())
1443 	{
1444 		CRect rectView = pV->getViewSize ();
1445 		if (rect.rectOverlap (rectView))
1446 			pV->setDirty (true);
1447 	}
1448 }
1449 
1450 //-----------------------------------------------------------------------------
invalidRect(const CRect & rect)1451 void CFrame::invalidRect (const CRect& rect)
1452 {
1453 	if (!isVisible () || !pImpl->platformFrame)
1454 		return;
1455 
1456 	CRect _rect (rect);
1457 	getTransform ().transform (_rect);
1458 	_rect.makeIntegral ();
1459 	if (pImpl->collectInvalidRects)
1460 		pImpl->collectInvalidRects->addRect (_rect);
1461 	else
1462 		pImpl->platformFrame->invalidRect (_rect);
1463 }
1464 
1465 //-----------------------------------------------------------------------------
getViewAddedRemovedObserver() const1466 IViewAddedRemovedObserver* CFrame::getViewAddedRemovedObserver () const
1467 {
1468 	return pImpl->viewAddedRemovedObserver;
1469 }
1470 
1471 //-----------------------------------------------------------------------------
setViewAddedRemovedObserver(IViewAddedRemovedObserver * observer)1472 void CFrame::setViewAddedRemovedObserver (IViewAddedRemovedObserver* observer)
1473 {
1474 	pImpl->viewAddedRemovedObserver = observer;
1475 }
1476 
1477 //-----------------------------------------------------------------------------
registerKeyboardHook(IKeyboardHook * hook)1478 void CFrame::registerKeyboardHook (IKeyboardHook* hook)
1479 {
1480 	pImpl->keyboardHooks.add (hook);
1481 }
1482 
1483 //-----------------------------------------------------------------------------
unregisterKeyboardHook(IKeyboardHook * hook)1484 void CFrame::unregisterKeyboardHook (IKeyboardHook* hook)
1485 {
1486 	pImpl->keyboardHooks.remove (hook);
1487 }
1488 
1489 //-----------------------------------------------------------------------------
keyboardHooksOnKeyDown(const VstKeyCode & key)1490 int32_t CFrame::keyboardHooksOnKeyDown (const VstKeyCode& key)
1491 {
1492 	int32_t result = -1;
1493 	pImpl->keyboardHooks.forEachReverse ([&] (IKeyboardHook* hook) {
1494 		if (result <= 0)
1495 		{
1496 			result = hook->onKeyDown (key, this);
1497 		}
1498 	});
1499 	return result;
1500 }
1501 
1502 //-----------------------------------------------------------------------------
keyboardHooksOnKeyUp(const VstKeyCode & key)1503 int32_t CFrame::keyboardHooksOnKeyUp (const VstKeyCode& key)
1504 {
1505 	int32_t result = -1;
1506 	pImpl->keyboardHooks.forEachReverse ([&] (IKeyboardHook* hook) {
1507 		if (result <= 0)
1508 		{
1509 			result = hook->onKeyUp (key, this);
1510 		}
1511 	});
1512 	return result;
1513 }
1514 
1515 //-----------------------------------------------------------------------------
registerScaleFactorChangedListeneer(IScaleFactorChangedListener * listener)1516 void CFrame::registerScaleFactorChangedListeneer (IScaleFactorChangedListener* listener)
1517 {
1518 	pImpl->scaleFactorChangedListenerList.add (listener);
1519 }
1520 
1521 //-----------------------------------------------------------------------------
unregisterScaleFactorChangedListeneer(IScaleFactorChangedListener * listener)1522 void CFrame::unregisterScaleFactorChangedListeneer (IScaleFactorChangedListener* listener)
1523 {
1524 	pImpl->scaleFactorChangedListenerList.remove (listener);
1525 }
1526 
1527 //-----------------------------------------------------------------------------
registerMouseObserver(IMouseObserver * observer)1528 void CFrame::registerMouseObserver (IMouseObserver* observer)
1529 {
1530 	pImpl->mouseObservers.add (observer);
1531 }
1532 
1533 //-----------------------------------------------------------------------------
registerFocusViewObserver(IFocusViewObserver * observer)1534 void CFrame::registerFocusViewObserver (IFocusViewObserver* observer)
1535 {
1536 	pImpl->focusViewObservers.add (observer);
1537 }
1538 
1539 //-----------------------------------------------------------------------------
unregisterFocusViewObserver(IFocusViewObserver * observer)1540 void CFrame::unregisterFocusViewObserver (IFocusViewObserver* observer)
1541 {
1542 	pImpl->focusViewObservers.remove (observer);
1543 }
1544 
1545 //-----------------------------------------------------------------------------
unregisterMouseObserver(IMouseObserver * observer)1546 void CFrame::unregisterMouseObserver (IMouseObserver* observer)
1547 {
1548 	pImpl->mouseObservers.remove (observer);
1549 }
1550 
1551 //-----------------------------------------------------------------------------
callMouseObserverMouseEntered(CView * view)1552 void CFrame::callMouseObserverMouseEntered (CView* view)
1553 {
1554 	view->callMouseListenerEnteredExited (true);
1555 	pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) {
1556 		observer->onMouseEntered (view, this);
1557 	});
1558 }
1559 
1560 //-----------------------------------------------------------------------------
callMouseObserverMouseExited(CView * view)1561 void CFrame::callMouseObserverMouseExited (CView* view)
1562 {
1563 	pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) {
1564 		observer->onMouseExited (view, this);
1565 	});
1566 	view->callMouseListenerEnteredExited (false);
1567 }
1568 
1569 //-----------------------------------------------------------------------------
callMouseObserverMouseDown(const CPoint & _where,const CButtonState & buttons)1570 CMouseEventResult CFrame::callMouseObserverMouseDown (const CPoint& _where, const CButtonState& buttons)
1571 {
1572 	CMouseEventResult eventResult = kMouseEventNotHandled;
1573 	if (pImpl->mouseObservers.empty ())
1574 		return eventResult;
1575 
1576 	CPoint where (_where);
1577 	getTransform ().inverse ().transform (where);
1578 
1579 	pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) {
1580 		CMouseEventResult result2 = observer->onMouseDown (this, where, buttons);
1581 		if (result2 != kMouseEventNotHandled)
1582 			eventResult = result2;
1583 	});
1584 
1585 	return eventResult;
1586 }
1587 
1588 //-----------------------------------------------------------------------------
callMouseObserverMouseMoved(const CPoint & _where,const CButtonState & buttons)1589 CMouseEventResult CFrame::callMouseObserverMouseMoved (const CPoint& _where, const CButtonState& buttons)
1590 {
1591 	CMouseEventResult eventResult = kMouseEventNotHandled;
1592 	if (pImpl->mouseObservers.empty ())
1593 		return eventResult;
1594 
1595 	CPoint where (_where);
1596 	getTransform ().inverse ().transform (where);
1597 
1598 	pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) {
1599 		CMouseEventResult result2 = observer->onMouseMoved (this, where, buttons);
1600 		if (result2 == kMouseEventHandled)
1601 			eventResult = kMouseEventHandled;
1602 	});
1603 
1604 	return eventResult;
1605 }
1606 
1607 //------------------------------------------------------------------------
performDrag(const DragDescription & desc,const SharedPointer<IDragCallback> & callback)1608 bool CFrame::performDrag (const DragDescription& desc, const SharedPointer<IDragCallback>& callback)
1609 {
1610 	if (auto platformFrame = getPlatformFrame ())
1611 	{
1612 		if (platformFrame->doDrag (desc, callback))
1613 		{
1614 			setMouseDownView (nullptr);
1615 			return true;
1616 		}
1617 	}
1618 	return false;
1619 }
1620 
1621 #if DEBUG
1622 //-----------------------------------------------------------------------------
dumpHierarchy()1623 void CFrame::dumpHierarchy ()
1624 {
1625 	dumpInfo ();
1626 	DebugPrint ("\n");
1627 	CViewContainer::dumpHierarchy ();
1628 }
1629 #endif
1630 
1631 //-----------------------------------------------------------------------------
getEditor() const1632 VSTGUIEditorInterface* CFrame::getEditor () const
1633 {
1634 	return pImpl->editor;
1635 }
1636 
1637 //-----------------------------------------------------------------------------
getPlatformFrame() const1638 IPlatformFrame* CFrame::getPlatformFrame () const
1639 {
1640 	return pImpl->platformFrame;
1641 }
1642 
1643 //-----------------------------------------------------------------------------
platformDrawRect(CDrawContext * context,const CRect & rect)1644 bool CFrame::platformDrawRect (CDrawContext* context, const CRect& rect)
1645 {
1646 	drawRect (context, rect);
1647 	return true;
1648 }
1649 
1650 //-----------------------------------------------------------------------------
platformOnMouseDown(CPoint & where,const CButtonState & buttons)1651 CMouseEventResult CFrame::platformOnMouseDown (CPoint& where, const CButtonState& buttons)
1652 {
1653 	if (!getMouseEnabled ())
1654 		return kMouseEventNotHandled;
1655 	Impl::PostEventHandler peh (*pImpl);
1656 	CollectInvalidRects cir (this);
1657 	return onMouseDown (where, buttons);
1658 }
1659 
1660 //-----------------------------------------------------------------------------
platformOnMouseMoved(CPoint & where,const CButtonState & buttons)1661 CMouseEventResult CFrame::platformOnMouseMoved (CPoint& where, const CButtonState& buttons)
1662 {
1663 	if (!getMouseEnabled ())
1664 		return kMouseEventNotHandled;
1665 	Impl::PostEventHandler peh (*pImpl);
1666 	CollectInvalidRects cir (this);
1667 	return onMouseMoved (where, buttons);
1668 }
1669 
1670 //-----------------------------------------------------------------------------
platformOnMouseUp(CPoint & where,const CButtonState & buttons)1671 CMouseEventResult CFrame::platformOnMouseUp (CPoint& where, const CButtonState& buttons)
1672 {
1673 	if (!getMouseEnabled ())
1674 		return kMouseEventNotHandled;
1675 	Impl::PostEventHandler peh (*pImpl);
1676 	CollectInvalidRects cir (this);
1677 	return onMouseUp (where, buttons);
1678 }
1679 
1680 //-----------------------------------------------------------------------------
platformOnMouseExited(CPoint & where,const CButtonState & buttons)1681 CMouseEventResult CFrame::platformOnMouseExited (CPoint& where, const CButtonState& buttons)
1682 {
1683 	if (!getMouseEnabled ())
1684 		return kMouseEventNotHandled;
1685 	Impl::PostEventHandler peh (*pImpl);
1686 	CollectInvalidRects cir (this);
1687 	return onMouseExited (where, buttons);
1688 }
1689 
1690 //-----------------------------------------------------------------------------
platformOnMouseWheel(const CPoint & where,const CMouseWheelAxis & axis,const float & distance,const CButtonState & buttons)1691 bool CFrame::platformOnMouseWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons)
1692 {
1693 	if (!getMouseEnabled ())
1694 		return false;
1695 	Impl::PostEventHandler peh (*pImpl);
1696 	CollectInvalidRects cir (this);
1697 	return onWheel (where, axis, distance, buttons);
1698 }
1699 
1700 //-----------------------------------------------------------------------------
platformOnDragEnter(DragEventData data)1701 DragOperation CFrame::platformOnDragEnter (DragEventData data)
1702 {
1703 	if (!getMouseEnabled ())
1704 		return DragOperation::None;
1705 	Impl::PostEventHandler peh (*pImpl);
1706 	CollectInvalidRects cir (this);
1707 	data.modifiers = data.modifiers.getModifierState ();
1708 	return getDropTarget ()->onDragEnter (data);
1709 }
1710 
1711 //-----------------------------------------------------------------------------
platformOnDragMove(DragEventData data)1712 DragOperation CFrame::platformOnDragMove (DragEventData data)
1713 {
1714 	if (!getMouseEnabled ())
1715 		return DragOperation::None;
1716 	Impl::PostEventHandler peh (*pImpl);
1717 	CollectInvalidRects cir (this);
1718 	data.modifiers = data.modifiers.getModifierState ();
1719 	return getDropTarget ()->onDragMove (data);
1720 }
1721 
1722 //-----------------------------------------------------------------------------
platformOnDragLeave(DragEventData data)1723 void CFrame::platformOnDragLeave (DragEventData data)
1724 {
1725 	if (!getMouseEnabled ())
1726 		return;
1727 	Impl::PostEventHandler peh (*pImpl);
1728 	CollectInvalidRects cir (this);
1729 	data.modifiers = data.modifiers.getModifierState ();
1730 	getDropTarget ()->onDragLeave (data);
1731 }
1732 
1733 //-----------------------------------------------------------------------------
platformOnDrop(DragEventData data)1734 bool CFrame::platformOnDrop (DragEventData data)
1735 {
1736 	if (!getMouseEnabled ())
1737 		return false;
1738 	Impl::PostEventHandler peh (*pImpl);
1739 	CollectInvalidRects cir (this);
1740 	data.modifiers = data.modifiers.getModifierState ();
1741 	return getDropTarget ()->onDrop (data);
1742 }
1743 
1744 //-----------------------------------------------------------------------------
platformOnKeyDown(VstKeyCode & keyCode)1745 bool CFrame::platformOnKeyDown (VstKeyCode& keyCode)
1746 {
1747 	if (!getMouseEnabled ())
1748 		return false;
1749 	Impl::PostEventHandler peh (*pImpl);
1750 	CollectInvalidRects cir (this);
1751 	return onKeyDown (keyCode) == 1;
1752 }
1753 
1754 //-----------------------------------------------------------------------------
platformOnKeyUp(VstKeyCode & keyCode)1755 bool CFrame::platformOnKeyUp (VstKeyCode& keyCode)
1756 {
1757 	if (!getMouseEnabled ())
1758 		return false;
1759 	Impl::PostEventHandler peh (*pImpl);
1760 	CollectInvalidRects cir (this);
1761 	return onKeyUp (keyCode) == 1;
1762 }
1763 
1764 //-----------------------------------------------------------------------------
platformOnActivate(bool state)1765 void CFrame::platformOnActivate (bool state)
1766 {
1767 	if (getFrame ())
1768 	{
1769 		CollectInvalidRects cir (this);
1770 		onActivate (state);
1771 	}
1772 }
1773 
1774 //------------------------------------------------------------------------
platformOnWindowActivate(bool state)1775 void CFrame::platformOnWindowActivate (bool state)
1776 {
1777 	if (pImpl->windowActive == state)
1778 		return;
1779 	pImpl->windowActive = state;
1780 	CollectInvalidRects cir (this);
1781 	pImpl->windowActiveStateChangeViews.forEach ([&] (CView* view) {
1782 		view->onWindowActivate (state);
1783 	});
1784 }
1785 
1786 //-----------------------------------------------------------------------------
platformScaleFactorChanged(double newScaleFactor)1787 void CFrame::platformScaleFactorChanged (double newScaleFactor)
1788 {
1789 	if (pImpl->platformScaleFactor == newScaleFactor)
1790 		return;
1791 	pImpl->platformScaleFactor = newScaleFactor;
1792 	dispatchNewScaleFactor (getScaleFactor ());
1793 }
1794 
1795 //-----------------------------------------------------------------------------
dispatchNewScaleFactor(double newScaleFactor)1796 void CFrame::dispatchNewScaleFactor (double newScaleFactor)
1797 {
1798 	pImpl->scaleFactorChangedListenerList.forEach ([&] (IScaleFactorChangedListener* listener) {
1799 		listener->onScaleFactorChanged (this, newScaleFactor);
1800 	});
1801 }
1802 
1803 #if VSTGUI_TOUCH_EVENT_HANDLING
1804 //-----------------------------------------------------------------------------
platformOnTouchEvent(ITouchEvent & event)1805 void CFrame::platformOnTouchEvent (ITouchEvent& event)
1806 {
1807 	Impl::PostEventHandler peh (*pImpl);
1808 	std::vector<CView*> targetDispatched;
1809 	bool hasBeganTouch = false;
1810 	for (const auto& e : event)
1811 	{
1812 		CView* target = e.second.target;
1813 		if (target)
1814 		{
1815 			if (e.second.targetIsSingleTouch)
1816 			{
1817 				CButtonState buttons (kLButton);
1818 				CPoint where (e.second.location);
1819 				target->frameToLocal (where);
1820 				switch (e.second.state)
1821 				{
1822 					case ITouchEvent::kMoved:
1823 					{
1824 						CMouseEventResult result = target->onMouseMoved (where, buttons);
1825 						if (result == kMouseMoveEventHandledButDontNeedMoreEvents)
1826 						{
1827 							event.unsetTouchTarget(e.first, target);
1828 							if (target->hitTest (where, buttons) == false)
1829 							{
1830 								// when the touch goes out of the target and it tells us to
1831 								const_cast<ITouchEvent::Touch&> (e.second).state = ITouchEvent::kBegan;
1832 								hasBeganTouch = true;
1833 							}
1834 						}
1835 						break;
1836 					}
1837 					case ITouchEvent::kCanceled:
1838 					{
1839 						if (target->onMouseCancel () != kMouseEventHandled)
1840 							target->onMouseUp (where, buttons);
1841 						event.unsetTouchTarget (e.first, target);
1842 						break;
1843 					}
1844 					case ITouchEvent::kEnded:
1845 					{
1846 						target->onMouseUp (where, buttons);
1847 						event.unsetTouchTarget (e.first, target);
1848 						break;
1849 					}
1850 					default:
1851 					{
1852 						// do nothing
1853 						break;
1854 					}
1855 				}
1856 			}
1857 			else
1858 			{
1859 				if (std::find (targetDispatched.begin (), targetDispatched.end (), target) == targetDispatched.end ())
1860 				{
1861 					target->onTouchEvent (event);
1862 					targetDispatched.emplace_back (target);
1863 				}
1864 			}
1865 		}
1866 		else if (e.second.state == ITouchEvent::kBegan)
1867 		{
1868 			hasBeganTouch = true;
1869 		}
1870 	}
1871 	if (hasBeganTouch)
1872 	{
1873 		if (CView* focusView = getFocusView ())
1874 		{
1875 			if (dynamic_cast<CTextEdit*> (focusView))
1876 				setFocusView (0);
1877 		}
1878 		for (const auto& e : event)
1879 		{
1880 			if (e.second.target == 0 && e.second.state == ITouchEvent::kBegan)
1881 			{
1882 				findSingleTouchEventTarget (const_cast<ITouchEvent::Touch&> (e.second));
1883 			}
1884 		}
1885 		onTouchEvent (event);
1886 	}
1887 }
1888 
1889 #endif
1890 
1891 //-----------------------------------------------------------------------------
doAfterEventProcessing(EventProcessingFunction && func)1892 bool CFrame::doAfterEventProcessing (EventProcessingFunction&& func)
1893 {
1894 	if (pImpl->inEventHandling)
1895 		pImpl->postEventFunctionQueue.push (std::move (func));
1896 	return pImpl->inEventHandling;
1897 }
1898 
1899 //-----------------------------------------------------------------------------
doAfterEventProcessing(const EventProcessingFunction & func)1900 bool CFrame::doAfterEventProcessing (const EventProcessingFunction& func)
1901 {
1902 	if (pImpl->inEventHandling)
1903 		pImpl->postEventFunctionQueue.push (func);
1904 	return pImpl->inEventHandling;
1905 }
1906 
1907 //-----------------------------------------------------------------------------
inEventProcessing() const1908 bool CFrame::inEventProcessing () const
1909 {
1910 	return pImpl->inEventHandling;
1911 }
1912 
1913 //-----------------------------------------------------------------------------
onStartLocalEventLoop()1914 void CFrame::onStartLocalEventLoop ()
1915 {
1916 	if (pImpl->collectInvalidRects)
1917 	{
1918 		pImpl->collectInvalidRects->flush ();
1919 		pImpl->collectInvalidRects = nullptr;
1920 	}
1921 }
1922 
1923 //-----------------------------------------------------------------------------
setCollectInvalidRects(CollectInvalidRects * cir)1924 void CFrame::setCollectInvalidRects (CollectInvalidRects* cir)
1925 {
1926 	if (pImpl->collectInvalidRects)
1927 		pImpl->collectInvalidRects->flush ();
1928 	pImpl->collectInvalidRects = cir;
1929 }
1930 
1931 //-----------------------------------------------------------------------------
CollectInvalidRects(CFrame * frame)1932 CFrame::CollectInvalidRects::CollectInvalidRects (CFrame* frame)
1933 : frame (frame)
1934 , lastTicks (frame->getTicks ())
1935 {
1936 #if VSTGUI_LOG_COLLECT_INVALID_RECTS
1937 	numAddedRects = 0;
1938 #endif
1939 	frame->setCollectInvalidRects (this);
1940 }
1941 
1942 //-----------------------------------------------------------------------------
~CollectInvalidRects()1943 CFrame::CollectInvalidRects::~CollectInvalidRects () noexcept
1944 {
1945 	frame->setCollectInvalidRects (nullptr);
1946 }
1947 
1948 //-----------------------------------------------------------------------------
flush()1949 void CFrame::CollectInvalidRects::flush ()
1950 {
1951 	if (!invalidRects.empty ())
1952 	{
1953 		if (frame->isVisible () && frame->pImpl->platformFrame)
1954 		{
1955 			for (auto& rect : invalidRects)
1956 				frame->pImpl->platformFrame->invalidRect (rect);
1957 		#if VSTGUI_LOG_COLLECT_INVALID_RECTS
1958 			DebugPrint ("%d -> %d\n", numAddedRects, invalidRects.size ());
1959 			numAddedRects = 0;
1960 		#endif
1961 		}
1962 		invalidRects.clear ();
1963 	}
1964 }
1965 
1966 //-----------------------------------------------------------------------------
addRect(const CRect & rect)1967 void CFrame::CollectInvalidRects::addRect (const CRect& rect)
1968 {
1969 #if VSTGUI_LOG_COLLECT_INVALID_RECTS
1970 	numAddedRects++;
1971 #endif
1972 	bool add = true;
1973 	for (auto it = invalidRects.begin (), end = invalidRects.end (); it != end; ++it)
1974 	{
1975 		if (it->rectInside (rect))
1976 		{
1977 			add = false;
1978 			break;
1979 		}
1980 
1981 		CRect r (rect);
1982 		if (r.bound (*it) == rect)
1983 		{
1984 			add = false;
1985 			break;
1986 		}
1987 		r = *it;
1988 		if (r.bound (rect) == *it)
1989 		{
1990 			invalidRects.erase (it);
1991 			break;
1992 		}
1993 	}
1994 	if (add)
1995 		invalidRects.emplace_back (rect);
1996 	uint32_t now = frame->getTicks ();
1997 	if (now - lastTicks > 16)
1998 	{
1999 		flush ();
2000 		lastTicks = now;
2001 	}
2002 }
2003 
2004 } // VSTGUI
2005