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 "uiattributescontroller.h"
6 
7 #if VSTGUI_LIVE_EDITING
8 
9 #include "uiactions.h"
10 #include "uieditcontroller.h"
11 #include "../uiviewfactory.h"
12 #include "../uiattributes.h"
13 #include "../../lib/controls/coptionmenu.h"
14 #include "../../lib/controls/csearchtextedit.h"
15 #include "../../lib/controls/cslider.h"
16 #include "../../lib/coffscreencontext.h"
17 #include "../../lib/crowcolumnview.h"
18 #include "../../lib/iviewlistener.h"
19 #include "../../lib/cstring.h"
20 #include "../../lib/cgraphicspath.h"
21 #include <sstream>
22 #include <algorithm>
23 #include <cassert>
24 
25 namespace VSTGUI {
26 
27 namespace UIAttributeControllers {
28 
29 //----------------------------------------------------------------------------------------------------
30 class Controller : public NonAtomicReferenceCounted, public DelegationController
31 {
32 public:
Controller(IController * baseController,const std::string & attrName)33 	Controller (IController* baseController, const std::string& attrName)
34 	: DelegationController (baseController), attrName (attrName), differentValues (false) {}
35 
getAttributeName() const36 	const std::string& getAttributeName () const { return attrName; }
37 	virtual void setValue (const std::string& value) = 0;
38 
hasDifferentValues(bool state)39 	virtual void hasDifferentValues (bool state) { differentValues = state; }
hasDifferentValues() const40 	bool hasDifferentValues () const { return differentValues; }
41 protected:
getControlListener(UTF8StringPtr controlTagName)42 	IControlListener* getControlListener (UTF8StringPtr controlTagName) override { return this; }
getAttributesController() const43 	UIAttributesController* getAttributesController () const { return dynamic_cast<UIAttributesController*> (controller); }
performValueChange(UTF8StringPtr value)44 	void performValueChange (UTF8StringPtr value)
45 	{
46 		hasDifferentValues (false);
47 		std::string valueStr = value ? value : "";
48 		UIAttributesController* attrController = getAttributesController ();
49 		if (attrController)
50 			attrController->performAttributeChange (attrName, valueStr);
51 	}
52 	std::string attrName;
53 	bool differentValues;
54 };
55 
56 //----------------------------------------------------------------------------------------------------
57 class TextAlignmentController : public Controller
58 {
59 public:
TextAlignmentController(IController * baseController,const std::string & attrName)60 	TextAlignmentController (IController* baseController, const std::string& attrName)
61 	: Controller (baseController, attrName) {}
62 
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription * description)63 	CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override
64 	{
65 		auto* control = dynamic_cast<CControl*>(view);
66 		if (control)
67 		{
68 			int32_t tag = control->getTag ();
69 			if (tag >= kLeftTag && tag <= kRightTag)
70 				controls[tag] = control;
71 		}
72 		return controller->verifyView (view, attributes, description);
73 	}
74 
setValue(const std::string & value)75 	void setValue (const std::string& value) override
76 	{
77 		if (hasDifferentValues ())
78 		{
79 			for (int32_t i = kLeftTag; i <= kRightTag; i++)
80 			{
81 				controls[i]->setValue (0.f);
82 				controls[i]->invalid ();
83 			}
84 		}
85 		else
86 		{
87 			int32_t alignment = 0;
88 			if (value == "center")
89 				alignment = 1;
90 			else if (value == "right")
91 				alignment = 2;
92 			for (int32_t i = kLeftTag; i <= kRightTag; i++)
93 			{
94 				controls[i]->setValue (i == alignment ? 1.f : 0.f);
95 				controls[i]->invalid ();
96 			}
97 		}
98 	}
99 
valueChanged(CControl * control)100 	void valueChanged (CControl* control) override
101 	{
102 		if (control->getValue () == control->getMax ())
103 		{
104 			switch (control->getTag ())
105 			{
106 				case kLeftTag:
107 				{
108 					performValueChange ("left");
109 					break;
110 				}
111 				case kCenterTag:
112 				{
113 					performValueChange ("center");
114 					break;
115 				}
116 				case kRightTag:
117 				{
118 					performValueChange ("right");
119 					break;
120 				}
121 			}
122 		}
123 		else
124 		{
125 			control->setValue (control->getMax ());
126 			control->invalid ();
127 		}
128 	}
129 
130 protected:
131 	enum {
132 		kLeftTag = 0,
133 		kCenterTag,
134 		kRightTag
135 	};
136 
137 	CControl* controls[3];
138 };
139 
140 //----------------------------------------------------------------------------------------------------
141 class AutosizeController : public Controller
142 {
143 public:
AutosizeController(IController * baseController,UISelection * selection,const std::string & attrName)144 	AutosizeController (IController* baseController, UISelection* selection, const std::string& attrName)
145 	: Controller (baseController, attrName), selection (selection) {}
146 
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription * description)147 	CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override
148 	{
149 		auto* control = dynamic_cast<CControl*>(view);
150 		if (control)
151 		{
152 			int32_t tag = control->getTag ();
153 			if (tag >= kLeftTag && tag <= kColTag)
154 				controls[tag] = control;
155 			if (tag >= kRowTag && tag <= kColTag)
156 			{
157 				for (const auto& selView : *selection)
158 				{
159 					if (selView->asViewContainer () == nullptr)
160 					{
161 						controls[tag]->setVisible (false);
162 						break;
163 					}
164 				}
165 			}
166 		}
167 		return controller->verifyView (view, attributes, description);
168 	}
169 
setValue(const std::string & value)170 	void setValue (const std::string& value) override
171 	{
172 		if (hasDifferentValues ())
173 		{
174 			for (int32_t i = kLeftTag; i <= kColTag; i++)
175 			{
176 				controls[i]->setValue (0.f);
177 			}
178 		}
179 		else
180 		{
181 			controls[kLeftTag]->setValue (value.find ("left") == std::string::npos ? controls[kLeftTag]->getMin () : controls[kLeftTag]->getMax ());
182 			controls[kRightTag]->setValue (value.find ("right") == std::string::npos ? controls[kRightTag]->getMin () : controls[kRightTag]->getMax ());
183 			controls[kTopTag]->setValue (value.find ("top") == std::string::npos ? controls[kTopTag]->getMin () : controls[kTopTag]->getMax ());
184 			controls[kBottomTag]->setValue (value.find ("bottom") == std::string::npos ? controls[kBottomTag]->getMin () : controls[kBottomTag]->getMax ());
185 			controls[kRowTag]->setValue (value.find ("row") == std::string::npos ? controls[kRowTag]->getMin () : controls[kRowTag]->getMax ());
186 			controls[kColTag]->setValue (value.find ("column") == std::string::npos ? controls[kColTag]->getMin () : controls[kColTag]->getMax ());
187 		}
188 		for (int32_t i = kLeftTag; i <= kColTag; i++)
189 		{
190 			controls[i]->invalid ();
191 		}
192 	}
193 
valueChanged(CControl * control)194 	void valueChanged (CControl* control) override
195 	{
196 		if (control == controls[kRowTag])
197 		{
198 			if (control->getValue () == control->getMax ())
199 			{
200 				controls[kColTag]->setValue (control->getMin ());
201 			}
202 		}
203 		else if (control == controls[kColTag])
204 		{
205 			if (control->getValue () == control->getMax ())
206 			{
207 				controls[kRowTag]->setValue (control->getMin ());
208 			}
209 		}
210 		std::string str;
211 		if (controls[kLeftTag]->getValue () == controls[kLeftTag]->getMax ())
212 			str = "left";
213 		if (controls[kRightTag]->getValue () == controls[kRightTag]->getMax ())
214 		{
215 			if (str.empty () == false)
216 				str += " ";
217 			str += "right";
218 		}
219 		if (controls[kTopTag]->getValue () == controls[kTopTag]->getMax ())
220 		{
221 			if (str.empty () == false)
222 				str += " ";
223 			str += "top";
224 		}
225 		if (controls[kBottomTag]->getValue () == controls[kBottomTag]->getMax ())
226 		{
227 			if (str.empty () == false)
228 				str += " ";
229 			str += "bottom";
230 		}
231 		if (controls[kRowTag]->getValue () == controls[kRowTag]->getMax ())
232 		{
233 			if (str.empty () == false)
234 				str += " ";
235 			str += "row";
236 		}
237 		if (controls[kColTag]->getValue () == controls[kColTag]->getMax ())
238 		{
239 			if (str.empty () == false)
240 				str += " ";
241 			str += "column";
242 		}
243 		performValueChange (str.c_str ());
244 	}
245 
246 
247 protected:
248 	enum {
249 		kLeftTag = 0,
250 		kTopTag,
251 		kRightTag,
252 		kBottomTag,
253 		kRowTag,
254 		kColTag
255 	};
256 
257 	CControl* controls[6];
258 	SharedPointer<UISelection> selection;
259 };
260 
261 //----------------------------------------------------------------------------------------------------
262 class BooleanController : public Controller
263 {
264 public:
BooleanController(IController * baseController,const std::string & attrName)265 	BooleanController (IController* baseController, const std::string& attrName)
266 	: Controller (baseController, attrName), control (nullptr) {}
267 
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription * description)268 	CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override
269 	{
270 		if (control == nullptr)
271 		{
272 			control = dynamic_cast<CControl*>(view);
273 		}
274 		return controller->verifyView (view, attributes, description);
275 	}
valueChanged(CControl * pControl)276 	void valueChanged (CControl* pControl) override
277 	{
278 		if (pControl->getValue () == control->getMax ())
279 			performValueChange ("true");
280 		else
281 			performValueChange ("false");
282 	}
setValue(const std::string & value)283 	void setValue (const std::string& value) override
284 	{
285 		if (hasDifferentValues())
286 		{
287 			control->setValue (control->getMin () + (control->getMax () - control->getMin ()) / 2.f);
288 		}
289 		else
290 		{
291 			if (value == "true")
292 				control->setValue (control->getMax ());
293 			else
294 				control->setValue (control->getMin ());
295 		}
296 		control->invalid ();
297 	}
298 
299 protected:
300 	CControl* control;
301 };
302 
303 //----------------------------------------------------------------------------------------------------
304 class TextController : public Controller, public ViewListenerAdapter, public ITextLabelListener
305 {
306 public:
TextController(IController * baseController,const std::string & attrName)307 	TextController (IController* baseController, const std::string& attrName)
308 	: Controller (baseController, attrName) {}
309 
~TextController()310 	~TextController () override
311 	{
312 		if (textLabel)
313 		{
314 			textLabel->unregisterViewListener (this);
315 			textLabel->unregisterTextLabelListener (this);
316 		}
317 	}
318 
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription * description)319 	CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override
320 	{
321 		if (textLabel == nullptr)
322 		{
323 			auto* edit = dynamic_cast<CTextLabel*>(view);
324 			if (edit)
325 			{
326 				textLabel = edit;
327 				originalTextColor = textLabel->getFontColor ();
328 				textLabel->registerTextLabelListener (this);
329 				textLabel->registerViewListener (this);
330 			}
331 		}
332 		if (slider == nullptr)
333 		{
334 			auto* sliderView = dynamic_cast<CSlider*>(view);
335 			if (sliderView)
336 				slider = sliderView;
337 		}
338 		return controller->verifyView (view, attributes, description);
339 	}
340 
controlBeginEdit(VSTGUI::CControl * pControl)341 	void controlBeginEdit (VSTGUI::CControl* pControl) override
342 	{
343 		if (pControl == slider)
344 		{
345 			getAttributesController ()->beginLiveAttributeChange (attrName, UIAttributes::doubleToString (slider->getValue ()));
346 		}
347 		Controller::controlBeginEdit (pControl);
348 	}
349 
controlEndEdit(VSTGUI::CControl * pControl)350 	void controlEndEdit (VSTGUI::CControl* pControl) override
351 	{
352 		if (pControl == slider)
353 		{
354 			getAttributesController ()->endLiveAttributeChange ();
355 		}
356 		Controller::controlEndEdit (pControl);
357 	}
358 
valueChanged(CControl * pControl)359 	void valueChanged (CControl* pControl) override
360 	{
361 		if (textLabel == pControl)
362 		{
363 			textLabel->setFontColor (originalTextColor);
364 			performValueChange (textLabel->getText ());
365 		}
366 		else if (slider == pControl)
367 		{
368 			performValueChange (UIAttributes::doubleToString (slider->getValue ()).data ());
369 		}
370 	}
371 
setValue(const std::string & value)372 	void setValue (const std::string& value) override
373 	{
374 		if (textLabel)
375 		{
376 			if (hasDifferentValues ())
377 			{
378 				CColor newColor (originalTextColor);
379 				newColor.alpha /= 2;
380 				textLabel->setFontColor (newColor);
381 				textLabel->setText ("Multiple Values");
382 			}
383 			else
384 			{
385 				textLabel->setText (value.c_str ());
386 			}
387 		}
388 		if (slider)
389 		{
390 			float floatValue;
391 
392 			std::istringstream sstream (value);
393 			sstream.imbue (std::locale::classic ());
394 			sstream.precision (40);
395 			sstream >> floatValue;
396 
397 			slider->setValue (floatValue);
398 			slider->invalid ();
399 		}
400 	}
401 
valueDisplayTruncated(UTF8StringPtr txt)402 	virtual void valueDisplayTruncated (UTF8StringPtr txt)
403 	{
404 		if (textLabel)
405 		{
406 			if (txt && *txt != 0)
407 				textLabel->setAttribute (kCViewTooltipAttribute, static_cast<uint32_t> (textLabel->getText ().length () + 1), textLabel->getText ().data ());
408 			else
409 				textLabel->removeAttribute (kCViewTooltipAttribute);
410 		}
411 
412 	}
413 
viewLostFocus(CView * view)414 	void viewLostFocus (CView* view) override
415 	{
416 		if (view == textLabel)
417 		{
418 			SharedPointer<CTextEdit> textEdit = textLabel.cast<CTextEdit> ();
419 			if (textEdit && textEdit->bWasReturnPressed)
420 			{
421 				textEdit->getFrame ()->doAfterEventProcessing ([=] () {
422 					textEdit->takeFocus ();
423 				});
424 			}
425 		}
426 	}
427 
onTextLabelTruncatedTextChanged(CTextLabel * label)428 	void onTextLabelTruncatedTextChanged (CTextLabel* label) override
429 	{
430 		UTF8StringPtr txt = label->getTruncatedText ();
431 		valueDisplayTruncated (txt);
432 	}
433 
434 protected:
435 	SharedPointer<CTextLabel> textLabel;
436 	SharedPointer<CSlider> slider;
437 	CColor originalTextColor;
438 };
439 
440 //----------------------------------------------------------------------------------------------------
441 class MenuController : public TextController, public OptionMenuListenerAdapter, public CommandMenuItemTargetAdapter
442 {
443 public:
MenuController(IController * baseController,const std::string & attrName,UIDescription * description,bool addNoneItem=true,bool sortItems=true)444 	MenuController (IController* baseController, const std::string& attrName, UIDescription* description, bool addNoneItem = true, bool sortItems = true)
445 	: TextController (baseController, attrName), description (description), addNoneItem (addNoneItem), sortItems (sortItems) {}
446 
~MenuController()447 	~MenuController () override
448 	{
449 		if (menu)
450 			menu->unregisterOptionMenuListener (this);
451 	}
452 
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription *)453 	CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription*) override
454 	{
455 		if (menu == nullptr)
456 		{
457 			menu = dynamic_cast<COptionMenu*>(view);
458 			if (menu)
459 				menu->registerOptionMenuListener (this);
460 		}
461 		return TextController::verifyView (view, attributes, description);
462 	}
463 
464 	using StringPtrList =std::list<const std::string*>;
465 	virtual void collectMenuItemNames (StringPtrList& names) = 0;
validateMenuEntry(CCommandMenuItem * item)466 	virtual void validateMenuEntry (CCommandMenuItem* item) {}
467 
addMenuEntry(const std::string * entryName)468 	virtual void addMenuEntry (const std::string* entryName)
469 	{
470 		CCommandMenuItem* item = new CCommandMenuItem (CCommandMenuItem::Desc{entryName->data (), this});
471 		validateMenuEntry (item);
472 		menu->addEntry (item);
473 		if (textLabel->getText () == *entryName)
474 		{
475 			int32_t index = menu->getNbEntries () - 1;
476 			menu->setValue ((float)index);
477 			menu->setCurrent (index);
478 		}
479 	}
480 
setValue(const std::string & value)481 	void setValue (const std::string& value) override
482 	{
483 		TextController::setValue (value);
484 	}
485 
onOptionMenuPrePopup(COptionMenu * optMenu)486 	void onOptionMenuPrePopup (COptionMenu* optMenu) override
487 	{
488 		optMenu->removeAllEntry ();
489 		if (addNoneItem)
490 			optMenu->addEntry (new CCommandMenuItem (CCommandMenuItem::Desc{"None", 100, this}));
491 		StringPtrList names;
492 		collectMenuItemNames (names);
493 		if (sortItems)
494 			names.sort (UIEditController::std__stringCompare);
495 		if (addNoneItem && !names.empty ())
496 			optMenu->addSeparator ();
497 		for (const auto& name : names)
498 			addMenuEntry (name);
499 	}
500 
onCommandMenuItemSelected(CCommandMenuItem * item)501 	bool onCommandMenuItemSelected (CCommandMenuItem* item) override
502 	{
503 		performValueChange (item->getTag () == 100 ? "" : item->getTitle ());
504 		return true;
505 	}
506 
valueDisplayTruncated(UTF8StringPtr txt)507 	void valueDisplayTruncated (UTF8StringPtr txt) override
508 	{
509 		if (textLabel && menu)
510 		{
511 			if (txt && *txt != 0)
512 				menu->setAttribute (kCViewTooltipAttribute, static_cast<uint32_t> (textLabel->getText ().length () + 1), textLabel->getText ().data ());
513 			else
514 				menu->removeAttribute (kCViewTooltipAttribute);
515 		}
516 
517 	}
518 
519 protected:
520 	SharedPointer<UIDescription> description;
521 	SharedPointer<COptionMenu> menu;
522 
523 	bool addNoneItem;
524 	bool sortItems;
525 };
526 
527 //----------------------------------------------------------------------------------------------------
528 class ColorController : public MenuController
529 {
530 public:
ColorController(IController * baseController,const std::string & attrName,UIDescription * description)531 	ColorController (IController* baseController, const std::string& attrName, UIDescription* description)
532 	: MenuController (baseController, attrName, description) {}
533 
collectMenuItemNames(StringPtrList & names)534 	void collectMenuItemNames (StringPtrList& names) override
535 	{
536 		description->collectColorNames (names);
537 	}
538 
validateMenuEntry(CCommandMenuItem * item)539 	void validateMenuEntry (CCommandMenuItem* item) override
540 	{
541 		const CCoord size = 15;
542 		CColor color;
543 		if (description->getColor (item->getTitle (), color))
544 		{
545 
546 			if (auto context = COffscreenContext::create (menu->getFrame (), size, size))
547 			{
548 				context->beginDraw ();
549 				context->setFillColor (color);
550 				context->drawRect (CRect (0, 0, size, size), kDrawFilled);
551 				context->endDraw ();
552 				item->setIcon (context->getBitmap ());
553 			}
554 		}
555 	}
556 
setValue(const std::string & value)557 	void setValue (const std::string& value) override
558 	{
559 		MenuController::setValue (value);
560 		if (colorView)
561 		{
562 			if (hasDifferentValues ())
563 			{
564 				colorView->color = kTransparentCColor;
565 			}
566 			else
567 			{
568 				CColor color;
569 				if (description->getColor (value.c_str (), color))
570 				{
571 					colorView->color = color;
572 				}
573 				else
574 				{
575 					colorView->color = kTransparentCColor;
576 				}
577 			}
578 			colorView->invalid ();
579 		}
580 	}
581 
createView(const UIAttributes & attributes,const IUIDescription * description)582 	CView* createView (const UIAttributes& attributes, const IUIDescription* description) override
583 	{
584 		const std::string* attr = attributes.getAttributeValue (IUIDescription::kCustomViewName);
585 		if (attr && *attr == "ColorView")
586 		{
587 			colorView = new ColorView ();
588 			return colorView;
589 		}
590 		return nullptr;
591 	}
592 protected:
593 	class ColorView : public CView
594 	{
595 	public:
ColorView()596 		ColorView () : CView (CRect (0, 0, 0, 0)) ,color (kTransparentCColor) {}
draw(CDrawContext * context)597 		void draw (CDrawContext* context) override
598 		{
599 			context->setFillColor (color);
600 			context->setDrawMode (kAliasing);
601 			context->drawRect (getViewSize (), kDrawFilled);
602 		}
603 		CColor color;
604 	};
605 	SharedPointer<ColorView> colorView;
606 };
607 
608 //----------------------------------------------------------------------------------------------------
609 class GradientController : public MenuController
610 {
611 public:
GradientController(IController * baseController,const std::string & attrName,UIDescription * description)612 	GradientController (IController* baseController, const std::string& attrName, UIDescription* description)
613 	: MenuController (baseController, attrName, description) {}
614 
collectMenuItemNames(StringPtrList & names)615 	void collectMenuItemNames (StringPtrList& names) override
616 	{
617 		description->collectGradientNames (names);
618 	}
619 
validateMenuEntry(CCommandMenuItem * item)620 	void validateMenuEntry (CCommandMenuItem* item) override
621 	{
622 		const CCoord size = 15;
623 		if (CGradient* gradient = description->getGradient (item->getTitle ()))
624 		{
625 			if (auto context = COffscreenContext::create (menu->getFrame (), size, size))
626 			{
627 				context->beginDraw ();
628 				SharedPointer<CGraphicsPath> path = owned (context->createGraphicsPath ());
629 				path->addRect (CRect (0, 0, size, size));
630 				context->fillLinearGradient(path, *gradient, CPoint (0, 0), CPoint (size, 0));
631 				context->endDraw ();
632 				item->setIcon (context->getBitmap ());
633 			}
634 		}
635 	}
636 
setValue(const std::string & value)637 	void setValue (const std::string& value) override
638 	{
639 		MenuController::setValue (value);
640 		if (gradientView)
641 		{
642 			if (hasDifferentValues ())
643 			{
644 				gradientView->gradient = nullptr;
645 			}
646 			else
647 			{
648 				gradientView->gradient = description->getGradient (value.c_str ());
649 			}
650 			gradientView->invalid ();
651 		}
652 	}
653 
createView(const UIAttributes & attributes,const IUIDescription * description)654 	CView* createView (const UIAttributes& attributes, const IUIDescription* description) override
655 	{
656 		const std::string* attr = attributes.getAttributeValue (IUIDescription::kCustomViewName);
657 		if (attr && *attr == "GradientView")
658 		{
659 			gradientView = new GradientView ();
660 			return gradientView;
661 		}
662 		return nullptr;
663 	}
664 protected:
665 	class GradientView : public CView
666 	{
667 	public:
GradientView()668 		GradientView () : CView (CRect (0, 0, 0, 0)) {}
draw(CDrawContext * context)669 		void draw (CDrawContext* context) override
670 		{
671 			if (gradient == nullptr)
672 				return;
673 			CRect r = getViewSize ();
674 			SharedPointer<CGraphicsPath> path = owned (context->createGraphicsPath ());
675 			path->addRect (r);
676 			context->fillLinearGradient (path, *gradient, r.getTopLeft (), r.getTopRight ());
677 		}
678 		SharedPointer<CGradient> gradient;
679 	};
680 	SharedPointer<GradientView> gradientView;
681 };
682 
683 //----------------------------------------------------------------------------------------------------
684 class TagController : public MenuController
685 {
686 public:
TagController(IController * baseController,const std::string & attrName,UIDescription * description)687 	TagController (IController* baseController, const std::string& attrName, UIDescription* description)
688 	: MenuController (baseController, attrName, description, true, false) {}
689 
collectMenuItemNames(StringPtrList & names)690 	void collectMenuItemNames (StringPtrList& names) override
691 	{
692 		description->collectControlTagNames (names);
693 	}
694 
695 };
696 
697 //----------------------------------------------------------------------------------------------------
698 class BitmapController : public MenuController
699 {
700 public:
BitmapController(IController * baseController,const std::string & attrName,UIDescription * description)701 	BitmapController (IController* baseController, const std::string& attrName, UIDescription* description)
702 	: MenuController (baseController, attrName, description) {}
703 
collectMenuItemNames(StringPtrList & names)704 	void collectMenuItemNames (StringPtrList& names) override
705 	{
706 		description->collectBitmapNames (names);
707 	}
708 
709 };
710 
711 //----------------------------------------------------------------------------------------------------
712 class FontController : public MenuController
713 {
714 public:
FontController(IController * baseController,const std::string & attrName,UIDescription * description)715 	FontController (IController* baseController, const std::string& attrName, UIDescription* description)
716 	: MenuController (baseController, attrName, description) {}
717 
collectMenuItemNames(StringPtrList & names)718 	void collectMenuItemNames (StringPtrList& names) override
719 	{
720 		description->collectFontNames (names);
721 	}
722 
723 };
724 
725 //----------------------------------------------------------------------------------------------------
726 class ListController : public MenuController
727 {
728 public:
ListController(IController * baseController,const std::string & attrName,UIDescription * description,UISelection * selection)729 	ListController (IController* baseController, const std::string& attrName, UIDescription* description, UISelection* selection)
730 	: MenuController (baseController, attrName, description, false, false), selection (selection) {}
731 
collectMenuItemNames(StringPtrList & names)732 	void collectMenuItemNames (StringPtrList& names) override
733 	{
734 		const auto* viewFactory = dynamic_cast<const UIViewFactory*>(description->getViewFactory ());
735 		if (viewFactory)
736 		{
737 			viewFactory->getPossibleAttributeListValues (selection->first (), attrName, names);
738 		}
739 	}
740 
741 protected:
742 	SharedPointer<UISelection> selection;
743 };
744 
745 
746 } // VSTGUI
747 
748 
749 //----------------------------------------------------------------------------------------------------
UIAttributesController(IController * baseController,UISelection * selection,UIUndoManager * undoManager,UIDescription * description)750 UIAttributesController::UIAttributesController (IController* baseController, UISelection* selection, UIUndoManager* undoManager, UIDescription* description)
751 : DelegationController (baseController)
752 , selection (selection)
753 , undoManager (undoManager)
754 , editDescription (description)
755 , liveAction (nullptr)
756 , viewNameLabel (nullptr)
757 , attributeView (nullptr)
758 , currentAttributeName (nullptr)
759 {
760 	selection->registerListener (this);
761 	undoManager->registerListener (this);
762 	description->registerListener (this);
763 }
764 
765 //----------------------------------------------------------------------------------------------------
~UIAttributesController()766 UIAttributesController::~UIAttributesController ()
767 {
768 	selection->unregisterListener (this);
769 	undoManager->unregisterListener (this);
770 	editDescription->unregisterListener (this);
771 }
772 
773 //----------------------------------------------------------------------------------------------------
beginLiveAttributeChange(const std::string & name,const std::string & currentValue)774 void UIAttributesController::beginLiveAttributeChange (const std::string& name, const std::string& currentValue)
775 {
776 	liveAction = new AttributeChangeAction (editDescription, selection, name, currentValue);
777 	undoManager->startGroupAction (liveAction->getName ());
778 	undoManager->pushAndPerform (new AttributeChangeAction (editDescription, selection, name, currentValue));
779 }
780 
781 //----------------------------------------------------------------------------------------------------
endLiveAttributeChange()782 void UIAttributesController::endLiveAttributeChange ()
783 {
784 	if (liveAction)
785 	{
786 		liveAction->undo ();
787 		undoManager->pushAndPerform (liveAction);
788 		liveAction = nullptr;
789 		undoManager->endGroupAction ();
790 	}
791 }
792 
793 //----------------------------------------------------------------------------------------------------
performAttributeChange(const std::string & name,const std::string & value)794 void UIAttributesController::performAttributeChange (const std::string& name, const std::string& value)
795 {
796 	IAction* action = new AttributeChangeAction (editDescription, selection, name, value);
797 	if (liveAction)
798 	{
799 		delete liveAction;
800 		liveAction = action;
801 		action->perform ();
802 	}
803 	else
804 		undoManager->pushAndPerform (action);
805 }
806 
807 //----------------------------------------------------------------------------------------------------
valueChanged(CControl * control)808 void UIAttributesController::valueChanged (CControl* control)
809 {
810 	switch (control->getTag ())
811 	{
812 		case kSearchFieldTag:
813 		{
814 			if (auto sf = dynamic_cast<CSearchTextEdit*> (control))
815 			{
816 				filterString = sf->getText ();
817 				rebuildAttributesView ();
818 				auto attributes = editDescription->getCustomAttributes ("UIAttributesController", true);
819 				if (attributes)
820 				{
821 					attributes->setAttribute("UIAttributesController", filterString);
822 				}
823 			}
824 			break;
825 		}
826 	}
827 }
828 
829 //----------------------------------------------------------------------------------------------------
verifyView(CView * view,const UIAttributes & attributes,const IUIDescription * description)830 CView* UIAttributesController::verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description)
831 {
832 	if (attributeView == nullptr)
833 	{
834 		auto* rcv = dynamic_cast<CRowColumnView*>(view);
835 		if (rcv)
836 		{
837 			attributeView = rcv;
838 		}
839 	}
840 	if (searchField == nullptr)
841 	{
842 		auto* textEdit = dynamic_cast<CTextEdit*>(view);
843 		if (textEdit && textEdit->getTag () == kSearchFieldTag)
844 		{
845 			searchField = textEdit;
846 			auto attrs = editDescription->getCustomAttributes ("UIAttributesController", true);
847 			if (attrs)
848 			{
849 				const std::string* searchText = attrs->getAttributeValue ("SearchString");
850 				if (searchText)
851 				{
852 					searchField->setText (searchText->c_str ());
853 				}
854 			}
855 		}
856 	}
857 	if (viewNameLabel == nullptr)
858 	{
859 		auto* textLabel = dynamic_cast<CTextLabel*>(view);
860 		if (textLabel && textLabel->getTag () == kViewNameTag)
861 		{
862 			viewNameLabel = textLabel;
863 			viewNameLabel->setText ("No Selection");
864 		}
865 	}
866 	return DelegationController::verifyView (view, attributes, description);
867 }
868 
869 //----------------------------------------------------------------------------------------------------
getControlListener(UTF8StringPtr name)870 IControlListener* UIAttributesController::getControlListener (UTF8StringPtr name)
871 {
872 	return this;
873 }
874 
875 //----------------------------------------------------------------------------------------------------
createSubController(IdStringPtr _name,const IUIDescription * description)876 IController* UIAttributesController::createSubController (IdStringPtr _name, const IUIDescription* description)
877 {
878 	UTF8StringView name (_name);
879 	if (currentAttributeName)
880 	{
881 		if (name == "TextController")
882 		{
883 			return new UIAttributeControllers::TextController (this, *currentAttributeName);
884 		}
885 		else if (name == "BooleanController")
886 		{
887 			return new UIAttributeControllers::BooleanController (this, *currentAttributeName);
888 		}
889 		else if (name == "ColorController")
890 		{
891 			return new UIAttributeControllers::ColorController (this, *currentAttributeName, editDescription);
892 		}
893 		else if (name == "GradientController")
894 		{
895 			return new UIAttributeControllers::GradientController (this, *currentAttributeName, editDescription);
896 		}
897 		else if (name == "TagController")
898 		{
899 			return new UIAttributeControllers::TagController (this, *currentAttributeName, editDescription);
900 		}
901 		else if (name == "BitmapController")
902 		{
903 			return new UIAttributeControllers::BitmapController (this, *currentAttributeName, editDescription);
904 		}
905 		else if (name == "FontController")
906 		{
907 			return new UIAttributeControllers::FontController (this, *currentAttributeName, editDescription);
908 		}
909 		else if (name == "ListController")
910 		{
911 			return new UIAttributeControllers::ListController (this, *currentAttributeName, editDescription, selection);
912 		}
913 		else if (name == "TextAlignmentController")
914 		{
915 			return new UIAttributeControllers::TextAlignmentController (this, *currentAttributeName);
916 		}
917 		else if (name == "AutosizeController")
918 		{
919 			return new UIAttributeControllers::AutosizeController (this, selection, *currentAttributeName);
920 		}
921 	}
922 	return controller->createSubController (name, description);
923 }
924 
925 //----------------------------------------------------------------------------------------------------
onUIDescTagChanged(UIDescription * desc)926 void UIAttributesController::onUIDescTagChanged (UIDescription* desc)
927 {
928 	validateAttributeViews ();
929 }
930 
931 //----------------------------------------------------------------------------------------------------
onUIDescColorChanged(UIDescription * desc)932 void UIAttributesController::onUIDescColorChanged (UIDescription* desc)
933 {
934 	validateAttributeViews ();
935 }
936 
937 //----------------------------------------------------------------------------------------------------
onUIDescFontChanged(UIDescription * desc)938 void UIAttributesController::onUIDescFontChanged (UIDescription* desc)
939 {
940 	validateAttributeViews ();
941 }
942 
943 //----------------------------------------------------------------------------------------------------
onUIDescBitmapChanged(UIDescription * desc)944 void UIAttributesController::onUIDescBitmapChanged (UIDescription* desc)
945 {
946 	validateAttributeViews ();
947 }
948 
949 //----------------------------------------------------------------------------------------------------
onUIDescTemplateChanged(UIDescription * desc)950 void UIAttributesController::onUIDescTemplateChanged (UIDescription* desc)
951 {
952 	validateAttributeViews ();
953 }
954 
955 //----------------------------------------------------------------------------------------------------
onUIDescGradientChanged(UIDescription * desc)956 void UIAttributesController::onUIDescGradientChanged (UIDescription* desc)
957 {
958 	validateAttributeViews ();
959 }
960 
961 //----------------------------------------------------------------------------------------------------
selectionDidChange(UISelection *)962 void UIAttributesController::selectionDidChange (UISelection*)
963 {
964 	if (!rebuildRequested && attributeView)
965 	{
966 		if (auto frame = attributeView->getFrame ())
967 		{
968 			if (frame->inEventProcessing ())
969 			{
970 				rebuildRequested = true;
971 				frame->doAfterEventProcessing ([this] () {
972 					rebuildAttributesView ();
973 					rebuildRequested = false;
974 				});
975 			}
976 		}
977 		if (!rebuildRequested)
978 			rebuildAttributesView ();
979 	}
980 }
981 
982 //----------------------------------------------------------------------------------------------------
selectionViewsDidChange(UISelection *)983 void UIAttributesController::selectionViewsDidChange (UISelection*)
984 {
985 	validateAttributeViews ();
986 }
987 
988 //----------------------------------------------------------------------------------------------------
onUndoManagerChange()989 void UIAttributesController::onUndoManagerChange ()
990 {
991 	validateAttributeViews ();
992 }
993 
994 //----------------------------------------------------------------------------------------------------
validateAttributeViews()995 void UIAttributesController::validateAttributeViews ()
996 {
997 	const auto* viewFactory = static_cast<const UIViewFactory*> (editDescription->getViewFactory ());
998 
999 	for (auto& controller : attributeControllers)
1000 	{
1001 		std::string attrValue;
1002 		bool first = true;
1003 		bool hasDifferentValues = false;
1004 		for (const auto& view : *selection)
1005 		{
1006 			std::string temp;
1007 			viewFactory->getAttributeValue (view, controller->getAttributeName (), temp, editDescription);
1008 			if (temp != attrValue && !first)
1009 				hasDifferentValues = true;
1010 			attrValue = temp;
1011 			first = false;
1012 		}
1013 		controller->hasDifferentValues (hasDifferentValues);
1014 		controller->setValue (attrValue);
1015 	}
1016 }
1017 
1018 //----------------------------------------------------------------------------------------------------
createValueViewForAttributeType(const UIViewFactory * viewFactory,CView * view,const std::string & attrName,IViewCreator::AttrType attrType)1019 CView* UIAttributesController::createValueViewForAttributeType (const UIViewFactory* viewFactory, CView* view, const std::string& attrName, IViewCreator::AttrType attrType)
1020 {
1021 	auto editorDescription = UIEditController::getEditorDescription ();
1022 	if (!editDescription)
1023 		return nullptr;
1024 	switch (attrType)
1025 	{
1026 		case IViewCreator::kFontType:
1027 			return editorDescription->createView ("attributes.font", this);
1028 		case IViewCreator::kBitmapType:
1029 			return editorDescription->createView ("attributes.bitmap", this);
1030 		case IViewCreator::kTagType:
1031 			return editorDescription->createView ("attributes.tag", this);
1032 		case IViewCreator::kColorType:
1033 			return editorDescription->createView ("attributes.color", this);
1034 		case IViewCreator::kGradientType:
1035 			return editorDescription->createView ("attributes.gradient", this);
1036 		case IViewCreator::kBooleanType:
1037 			return editorDescription->createView ("attributes.boolean", this);
1038 		case IViewCreator::kListType:
1039 			return editorDescription->createView ("attributes.list", this);
1040 		case IViewCreator::kFloatType:
1041 		case IViewCreator::kIntegerType:
1042 		{
1043 			double minValue, maxValue;
1044 			if (viewFactory->getAttributeValueRange (view, attrName, minValue, maxValue))
1045 			{
1046 				CView* valueView = editorDescription->createView ("attributes.number", this);
1047 				if (valueView)
1048 				{
1049 					if (auto container = valueView->asViewContainer ())
1050 					{
1051 						std::vector<CSlider*> sliders;
1052 						if (container->getChildViewsOfType<CSlider> (sliders) == 1)
1053 						{
1054 							sliders[0]->setMin (static_cast<float> (minValue));
1055 							sliders[0]->setMax (static_cast<float> (maxValue));
1056 						}
1057 					}
1058 					return valueView;
1059 				}
1060 			}
1061 		}
1062 		default:
1063 			break;
1064 	}
1065 	return editorDescription->createView ("attributes.text", this);
1066 }
1067 
1068 //----------------------------------------------------------------------------------------------------
createViewForAttribute(const std::string & attrName)1069 CView* UIAttributesController::createViewForAttribute (const std::string& attrName)
1070 {
1071 	const CCoord height = 18;
1072 	const CCoord width = 160;
1073 	const CCoord margin = 2;
1074 	CViewContainer* result = new CViewContainer (CRect (0, 0, width, height+2));
1075 	result->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeColumn);
1076 	result->setTransparency (true);
1077 
1078 	CCoord middle = width/2;
1079 	CTextLabel* label = new CTextLabel (CRect (5, 1, middle - margin, height+1), attrName.c_str ());
1080 	label->setTextTruncateMode (CTextLabel::kTruncateHead);
1081 	label->setTransparency (true);
1082 	label->setHoriAlign (kRightText);
1083 	label->setFontColor (kBlackCColor);
1084 	label->setFont (kNormalFontSmall);
1085 	label->setAutosizeFlags (kAutosizeAll);
1086 
1087 	result->addView (label);
1088 
1089 	bool hasDifferentValues = false;
1090 
1091 	const auto* viewFactory = static_cast<const UIViewFactory*> (editDescription->getViewFactory ());
1092 
1093 	std::string attrValue;
1094 	bool first = true;
1095 	for (const auto& view : *selection)
1096 	{
1097 		std::string temp;
1098 		viewFactory->getAttributeValue (view, attrName, temp, editDescription);
1099 		if (temp != attrValue && !first)
1100 			hasDifferentValues = true;
1101 		attrValue = temp;
1102 		first = false;
1103 	}
1104 
1105 	CRect r (middle+margin, 1, width-5, height+1);
1106 	CView* valueView = nullptr;
1107 
1108 	if (attrName == "text-alignment")
1109 	{
1110 		valueView = UIEditController::getEditorDescription ()->createView ("attributes.text.alignment", this);
1111 	}
1112 	else if (attrName == "autosize")
1113 	{
1114 		valueView = UIEditController::getEditorDescription ()->createView ("attributes.view.autosize", this);
1115 	}
1116 
1117 	if (valueView == nullptr)
1118 	{
1119 		CView* firstView = selection->first ();
1120 		IViewCreator::AttrType attrType = viewFactory->getAttributeType (firstView, attrName);
1121 		valueView = createValueViewForAttributeType (viewFactory, firstView, attrName, attrType);
1122 	}
1123 	if (valueView == nullptr) // fallcack if attributes.text template not defined
1124 	{
1125 		IController* controller = new UIAttributeControllers::TextController (this, *currentAttributeName);
1126 		auto* textEdit = new CTextEdit (r, this, -1);
1127 		textEdit->setText (attrValue.c_str ());
1128 		textEdit->setTransparency (true);
1129 		textEdit->setFontColor (kBlackCColor);
1130 		textEdit->setFont (kNormalFontSmall);
1131 		textEdit->setListener (controller);
1132 		valueView = textEdit;
1133 		valueView->setAttribute (kCViewControllerAttribute, controller);
1134 	}
1135 	if (valueView)
1136 	{
1137 		IController* controller = getViewController (valueView, true);
1138 		if (controller)
1139 		{
1140 			auto* c = dynamic_cast<UIAttributeControllers::Controller*>(controller);
1141 			if (c)
1142 			{
1143 				c->hasDifferentValues (hasDifferentValues);
1144 				c->setValue (attrValue);
1145 				attributeControllers.emplace_back (c);
1146 			}
1147 		}
1148 		r.setHeight (valueView->getHeight ());
1149 		valueView->setViewSize (r);
1150 		valueView->setMouseableArea (r);
1151 		result->addView (valueView);
1152 		r = result->getViewSize ();
1153 		r.setHeight (valueView->getHeight()+2);
1154 		result->setViewSize (r);
1155 		result->setMouseableArea (r);
1156 	}
1157 	return result;
1158 }
1159 
1160 //----------------------------------------------------------------------------------------------------
getConsolidatedAttributeNames(StringList & attrNames,const std::string & filter)1161 void UIAttributesController::getConsolidatedAttributeNames (StringList& attrNames, const std::string& filter)
1162 {
1163 	const auto* viewFactory = dynamic_cast<const UIViewFactory*> (editDescription->getViewFactory ());
1164 	vstgui_assert (viewFactory);
1165 
1166 	for (const auto& view : *selection)
1167 	{
1168 		StringList temp;
1169 		if (viewFactory->getAttributeNamesForView (view, temp))
1170 		{
1171 			StringList toRemove;
1172 			if (attrNames.empty ())
1173 				attrNames = temp;
1174 			else
1175 			{
1176 				for (auto& attrName : attrNames)
1177 				{
1178 					bool found = std::find (temp.begin (), temp.end (), attrName) != temp.end ();
1179 					if (!found)
1180 					{
1181 						toRemove.emplace_back (attrName);
1182 						temp.remove (attrName);
1183 					}
1184 				}
1185 			}
1186 			if (!filter.empty ())
1187 			{
1188 				for (auto rit = temp.rbegin (); rit != temp.rend (); ++rit)
1189 				{
1190 					std::string lowerCaseName (*rit);
1191 					std::transform (lowerCaseName.begin (), lowerCaseName.end (), lowerCaseName.begin (), ::tolower);
1192 					if (lowerCaseName.find (filter) == std::string::npos)
1193 						toRemove.emplace_back (*rit);
1194 				}
1195 			}
1196 			for (auto& attrName : toRemove)
1197 			{
1198 				attrNames.remove (attrName);
1199 			}
1200 		}
1201 	}
1202 }
1203 
1204 //----------------------------------------------------------------------------------------------------
rebuildAttributesView()1205 void UIAttributesController::rebuildAttributesView ()
1206 {
1207 	auto viewFactory = dynamic_cast<const UIViewFactory*> (editDescription->getViewFactory ());
1208 	if (attributeView == nullptr || viewFactory == nullptr)
1209 		return;
1210 
1211 	attributeView->invalid ();
1212 	attributeView->removeAll ();
1213 	attributeControllers.clear ();
1214 
1215 	std::string filter (filterString);
1216 	std::transform (filter.begin (), filter.end (), filter.begin (), ::tolower);
1217 
1218 	if (viewNameLabel)
1219 	{
1220 		int32_t selectedViews = selection->total ();
1221 		if (selectedViews > 0)
1222 		{
1223 			UTF8StringPtr viewname = nullptr;
1224 			for (const auto& view : *selection)
1225 			{
1226 				UTF8StringPtr name = viewFactory->getViewDisplayName (view);
1227 				if (viewname != nullptr && UTF8StringView (name) != viewname)
1228 				{
1229 					viewname = nullptr;
1230 					break;
1231 				}
1232 				viewname = name;
1233 			}
1234 			if (viewname != nullptr)
1235 			{
1236 				if (selectedViews == 1)
1237 					viewNameLabel->setText (viewname);
1238 				else
1239 				{
1240 					std::stringstream str;
1241 					str << selectedViews << "x " << viewname;
1242 					viewNameLabel->setText (str.str ().c_str ());
1243 				}
1244 			}
1245 			else
1246 			{
1247 				std::stringstream str;
1248 				str << selectedViews << "x different views";
1249 				viewNameLabel->setText (str.str ().c_str ());
1250 			}
1251 		}
1252 		else
1253 		{
1254 			viewNameLabel->setText ("No Selection");
1255 		}
1256 	}
1257 
1258 
1259 	StringList attrNames;
1260 	getConsolidatedAttributeNames (attrNames, filter);
1261 	if (attrNames.empty ())
1262 	{
1263 		CRect r (attributeView->getViewSize ());
1264 		r.setHeight (0);
1265 		attributeView->setViewSize (r);
1266 		attributeView->setMouseableArea (r);
1267 	}
1268 	else
1269 	{
1270 		CCoord width = attributeView->getWidth () - (attributeView->getMargin ().left + attributeView->getMargin ().right);
1271 		for (const auto& name : attrNames)
1272 		{
1273 			currentAttributeName = &name;
1274 			CView* view = createViewForAttribute (name);
1275 			if (view)
1276 			{
1277 				CRect r = view->getViewSize ();
1278 				r.setWidth (width);
1279 				view->setViewSize (r);
1280 				view->setMouseableArea (r);
1281 				attributeView->addView (view);
1282 			}
1283 		}
1284 		currentAttributeName = nullptr;
1285 		attributeView->sizeToFit ();
1286 		attributeView->setMouseableArea (attributeView->getViewSize ());
1287 	}
1288 	attributeView->invalid ();
1289 }
1290 
1291 } // VSTGUI
1292 
1293 #endif // VSTGUI_LIVE_EDITING
1294