// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "uiviewfactory.h" #include "uiattributes.h" #include "../lib/cview.h" #include "../lib/cstring.h" #include "detail/uiviewcreatorattributes.h" #include "../lib/platform/std_unorderedmap.h" namespace VSTGUI { /** @class IViewCreator You can register your own custom views with the UIViewFactory by inheriting from this interface and register it with UIViewFactory::registerViewCreator(). Example for an imaginary view class called MyView which directly inherites from CView: @code class MyViewCreator : public ViewCreatorAdapter { public: // register this class with the view factory MyViewCreator () { UIViewFactory::registerViewCreator (*this); } // return an unique name here IdStringPtr getViewName () const { return "MyView"; } // return the name here from where your custom view inherites. // Your view automatically supports the attributes from it. IdStringPtr getBaseViewName () const { return "CView"; } // create your view here. // Note you don't need to apply attributes here as the apply method will be called with this new view CView* create (const UIAttributes& attributes, const IUIDescription* description) const { return new MyView (); } // apply custom attributes to your view bool apply (CView* view, const UIAttributes& attributes, const IUIDescription* description) const { MyView* myView = dynamic_cast (view); if (myView == 0) return false; int32_t value; if (attributes.getIntegerAttribute ("my-custom-attribute", value)) myView->setCustomAttribute (value); return true; } // add your custom attributes to the list bool getAttributeNames (StringList& attributeNames) const { attributeNames.emplace_back ("my-custom-attribute"); return true; } // return the type of your custom attributes AttrType getAttributeType (const std::string& attributeName) const { if (attributeName == "my-custom-attribute") return kIntegerType; return kUnknownType; } // return the string value of the custom attributes of the view bool getAttributeValue (CView* view, const string& attributeName, string& stringValue, const IUIDescription* desc) const { MyView* myView = dynamic_cast (view); if (myView == 0) return false; if (attributeName == "my-custom-attribute") { stringValue = UIAttributes::integerToString (myView->getCustomAttribute ()); return true; } return false; } }; // create a static instance so that it registers itself with the view factory MyViewCreator __gMyViewCreator; @endcode */ using ViewCreatorRegistryMap = std::unordered_map; //----------------------------------------------------------------------------- class ViewCreatorRegistry : private ViewCreatorRegistryMap { public: using const_iterator = ViewCreatorRegistryMap::const_iterator; const_iterator begin () { return ViewCreatorRegistryMap::begin (); } const_iterator end () { return ViewCreatorRegistryMap::end (); } const_iterator find (IdStringPtr name) { if (name) return ViewCreatorRegistryMap::find (name); return end (); } void add (IdStringPtr name, const IViewCreator* viewCreator) { #if DEBUG if (find (viewCreator->getViewName ()) != end ()) { DebugPrint ("ViewCreateFunction for '%s' already registered\n", viewCreator->getViewName ()); } #endif insert (std::make_pair (viewCreator->getViewName (), viewCreator)); } void remove (const IViewCreator* viewCreator) { auto it = find (viewCreator->getViewName ()); if (it == end ()) return; erase (it); } }; //----------------------------------------------------------------------------- static ViewCreatorRegistry& getCreatorRegistry () { static ViewCreatorRegistry creatorRegistry; return creatorRegistry; } //----------------------------------------------------------------------------- static CViewAttributeID kViewNameAttribute = 'cvcr'; //----------------------------------------------------------------------------- UIViewFactory::UIViewFactory () { } //------------------------------------------------------------------------ bool UIViewFactory::viewIsTypeOf (CView* view, const std::string& typeName) const { auto viewTypeName = getViewName (view); if (typeName == viewTypeName) return true; auto& registry = getCreatorRegistry (); auto iter = registry.find (viewTypeName); while (iter != registry.end ()) { auto baseViewTypeName = iter->second->getBaseViewName (); if (baseViewTypeName == nullptr) return false; if (typeName == baseViewTypeName) return true; iter = registry.find (baseViewTypeName); } return false; } //----------------------------------------------------------------------------- CView* UIViewFactory::createViewByName (const std::string* className, const UIAttributes& attributes, const IUIDescription* description) const { auto& registry = getCreatorRegistry (); auto iter = registry.find (className->c_str ()); if (iter != registry.end ()) { CView* view = (*iter).second->create (attributes, description); if (view) { IdStringPtr viewName = (*iter).second->getViewName (); view->setAttribute (kViewNameAttribute, viewName); UIAttributes evaluatedAttributes; evaluateAttributesAndRemember (view, attributes, evaluatedAttributes, description); while (iter != registry.end () && (*iter).second->apply (view, evaluatedAttributes, description)) { if ((*iter).second->getBaseViewName () == nullptr) break; iter = registry.find ((*iter).second->getBaseViewName ()); } return view; } } else { #if DEBUG DebugPrint ("UIViewFactory::createView(..): Could not find view of class: %s\n", className->c_str ()); #endif } return nullptr; } //----------------------------------------------------------------------------- CView* UIViewFactory::createView (const UIAttributes& attributes, const IUIDescription* description) const { const std::string* className = attributes.getAttributeValue (UIViewCreator::kAttrClass); if (className) return createViewByName (className, attributes, description); std::string viewContainerName ("CViewContainer"); return createViewByName (&viewContainerName, attributes, description); } //----------------------------------------------------------------------------- bool UIViewFactory::applyAttributeValues (CView* view, const UIAttributes& attributes, const IUIDescription* desc) const { bool result = false; auto& registry = getCreatorRegistry (); auto iter = registry.find (getViewName (view)); UIAttributes evaluatedAttributes; evaluateAttributesAndRemember (view, attributes, evaluatedAttributes, desc); while (iter != registry.end () && (result = (*iter).second->apply (view, evaluatedAttributes, desc)) && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return result; } //----------------------------------------------------------------------------- bool UIViewFactory::applyCustomViewAttributeValues (CView* customView, IdStringPtr baseViewName, const UIAttributes& attributes, const IUIDescription* desc) const { bool result = false; auto& registry = getCreatorRegistry (); auto iter = registry.find (baseViewName); if (iter != registry.end ()) { IdStringPtr viewName = (*iter).second->getViewName (); customView->setAttribute (kViewNameAttribute, viewName); } UIAttributes evaluatedAttributes; evaluateAttributesAndRemember (customView, attributes, evaluatedAttributes, desc); while (iter != registry.end () && (result = (*iter).second->apply (customView, evaluatedAttributes, desc)) && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return result; } //----------------------------------------------------------------------------- IdStringPtr IViewFactory::getViewName (CView* view) { IdStringPtr viewName = nullptr; uint32_t size = sizeof (IdStringPtr); view->getAttribute (kViewNameAttribute, size, &viewName, size); return viewName; } //----------------------------------------------------------------------------- void UIViewFactory::evaluateAttributesAndRemember (CView* view, const UIAttributes& attributes, UIAttributes& evaluatedAttributes, const IUIDescription* description) const { std::string evaluatedValue; for (const auto& attr : attributes) { const std::string& value = attr.second; if (description && description->getVariable (value.c_str (), evaluatedValue)) { #if VSTGUI_LIVE_EDITING rememberAttribute (view, attr.first.c_str (), value.c_str ()); #endif evaluatedAttributes.setAttribute (attr.first, evaluatedValue); } else { #if VSTGUI_LIVE_EDITING auto type = getAttributeType (view, attr.first); switch (type) { case IViewCreator::kColorType: case IViewCreator::kTagType: case IViewCreator::kFontType: case IViewCreator::kGradientType: rememberAttribute (view, attr.first.c_str (), value.c_str ()); break; default: break; } #endif evaluatedAttributes.setAttribute (attr.first, value); } } } //----------------------------------------------------------------------------- bool UIViewFactory::getAttributeValue (CView* view, const std::string& attributeName, std::string& stringValue, const IUIDescription* desc) const { #if VSTGUI_LIVE_EDITING bool result = getRememberedAttribute (view, attributeName.c_str (), stringValue); if (result == false) { #else bool result = false; #endif auto& registry = getCreatorRegistry (); auto iter = registry.find (getViewName (view)); while (iter != registry.end () && !(result = (*iter).second->getAttributeValue (view, attributeName, stringValue, desc)) && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } #if VSTGUI_LIVE_EDITING } #endif return result; } #if VSTGUI_LIVE_EDITING //----------------------------------------------------------------------------- bool UIViewFactory::getAttributeNamesForView (CView* view, StringList& attributeNames) const { bool result = false; auto& registry = getCreatorRegistry (); auto iter = registry.find (getViewName (view)); while (iter != registry.end () && (result = (*iter).second->getAttributeNames (attributeNames)) && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return result; } //----------------------------------------------------------------------------- IViewCreator::AttrType UIViewFactory::getAttributeType (CView* view, const std::string& attributeName) const { auto& registry = getCreatorRegistry (); auto type = IViewCreator::kUnknownType; auto iter = registry.find (getViewName (view)); while (iter != registry.end () && (type = (*iter).second->getAttributeType (attributeName)) == IViewCreator::kUnknownType && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return type; } //----------------------------------------------------------------------------- bool UIViewFactory::getPossibleAttributeListValues (CView* view, const std::string& attributeName, StringPtrList& values) const { auto& registry = getCreatorRegistry (); auto iter = registry.find (getViewName (view)); while (iter != registry.end () && (*iter).second->getPossibleListValues (attributeName, values) == false && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return !values.empty (); } //----------------------------------------------------------------------------- bool UIViewFactory::getAttributeValueRange (CView* view, const std::string& attributeName, double& minValue, double& maxValue) const { minValue = maxValue = -1.; auto& registry = getCreatorRegistry (); auto iter = registry.find (getViewName (view)); while (iter != registry.end () && (*iter).second->getAttributeValueRange (attributeName, minValue, maxValue) == false && (*iter).second->getBaseViewName ()) { iter = registry.find ((*iter).second->getBaseViewName ()); } return minValue != maxValue && minValue != -1.; } //----------------------------------------------------------------------------- bool UIViewFactory::getAttributesForView (CView* view, const IUIDescription* desc, UIAttributes& attr) const { bool result = false; StringList attrNames; if (getAttributeNamesForView (view, attrNames)) { auto it = attrNames.begin (); while (it != attrNames.end ()) { std::string value; if (getAttributeValue (view, *it, value, desc)) attr.setAttribute (*it, value); it++; } attr.setAttribute (UIViewCreator::kAttrClass, getViewName (view)); result = true; } return result; } //----------------------------------------------------------------------------- void UIViewFactory::collectRegisteredViewNames (StringPtrList& viewNames, IdStringPtr _baseClassNameFilter) const { UTF8StringView baseClassNameFilter (_baseClassNameFilter); auto& registry = getCreatorRegistry (); auto iter = registry.begin (); while (iter != registry.end ()) { if (baseClassNameFilter) { bool found = false; auto iter2 = iter; while (iter2 != registry.end () && (*iter2).second->getBaseViewName ()) { if (baseClassNameFilter == (*iter2).second->getViewName () || baseClassNameFilter == (*iter2).second->getBaseViewName ()) { found = true; break; } iter2 = registry.find ((*iter2).second->getBaseViewName ()); } if (!found) { iter++; continue; } } viewNames.emplace_back (&(*iter).first); iter++; } viewNames.sort ([] (const std::string* lhs, const std::string* rhs) { return *lhs < *rhs; }); } //----------------------------------------------------------------------------- auto UIViewFactory::collectRegisteredViewAndDisplayNames (IdStringPtr baseClassNameFilter) const -> ViewAndDisplayNameList { ViewAndDisplayNameList list; auto& registry = getCreatorRegistry (); auto iter = registry.begin (); while (iter != registry.end ()) { if (baseClassNameFilter) { UTF8StringView baseClassNameStr (baseClassNameFilter); bool found = false; auto iter2 = iter; while (iter2 != registry.end () && (*iter2).second->getBaseViewName ()) { if (baseClassNameStr == (*iter2).second->getViewName () || baseClassNameStr == (*iter2).second->getBaseViewName ()) { found = true; break; } iter2 = registry.find ((*iter2).second->getBaseViewName ()); } if (!found) { iter++; continue; } } list.emplace_back (&(*iter).first, (*iter).second->getDisplayName ()); iter++; } return list; } //------------------------------------------------------------------------ UTF8StringPtr UIViewFactory::getViewDisplayName (CView* view) const { if (auto viewName = getViewName (view)) { UTF8StringView viewNameStr (viewName); auto& registry = getCreatorRegistry (); for (auto& entry : registry) { if (viewNameStr == entry.second->getViewName ()) return entry.second->getDisplayName (); } } return nullptr; } //----------------------------------------------------------------------------- size_t UIViewFactory::createHash (const std::string& str) { static std::hash hashFunc; return hashFunc (str); } //----------------------------------------------------------------------------- void UIViewFactory::rememberAttribute (CView* view, IdStringPtr attrName, const std::string& value) const { #if ENABLE_UNIT_TESTS if (disableRememberAttributes) return; #endif auto hash = createHash (attrName); view->setAttribute (hash, static_cast (value.size () + 1), value.c_str ()); } //----------------------------------------------------------------------------- bool UIViewFactory::getRememberedAttribute (CView* view, IdStringPtr attrName, std::string& value) const { bool result = false; size_t hash = createHash (attrName); uint32_t attrSize = 0; if (view->getAttributeSize (hash, attrSize)) { char* temp = new char[attrSize]; if (view->getAttribute (hash, attrSize, temp, attrSize)) { value = temp; result = true; } delete [] temp; } return result; } #endif //----------------------------------------------------------------------------- void UIViewFactory::registerViewCreator (const IViewCreator& viewCreator) { auto& registry = getCreatorRegistry (); registry.add (viewCreator.getViewName (), &viewCreator); } //----------------------------------------------------------------------------- void UIViewFactory::unregisterViewCreator (const IViewCreator& viewCreator) { auto& registry = getCreatorRegistry (); registry.remove (&viewCreator); } } // VSTGUI