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