The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace ComponentBuilderHelpers
  19. {
  20. static String getStateId (const ValueTree& state)
  21. {
  22. return state [ComponentBuilder::idProperty].toString();
  23. }
  24. static Component* removeComponentWithID (OwnedArray<Component>& components, const String& compId)
  25. {
  26. jassert (compId.isNotEmpty());
  27. for (int i = components.size(); --i >= 0;)
  28. {
  29. Component* const c = components.getUnchecked (i);
  30. if (c->getComponentID() == compId)
  31. return components.removeAndReturn (i);
  32. }
  33. return nullptr;
  34. }
  35. static Component* findComponentWithID (Component& c, const String& compId)
  36. {
  37. jassert (compId.isNotEmpty());
  38. if (c.getComponentID() == compId)
  39. return &c;
  40. for (int i = c.getNumChildComponents(); --i >= 0;)
  41. {
  42. Component* const child = findComponentWithID (*c.getChildComponent (i), compId);
  43. if (child != nullptr)
  44. return child;
  45. }
  46. return nullptr;
  47. }
  48. static Component* createNewComponent (ComponentBuilder::TypeHandler& type,
  49. const ValueTree& state, Component* parent)
  50. {
  51. Component* const c = type.addNewComponentFromState (state, parent);
  52. jassert (c != nullptr && c->getParentComponent() == parent);
  53. c->setComponentID (getStateId (state));
  54. return c;
  55. }
  56. static void updateComponent (ComponentBuilder& builder, const ValueTree& state)
  57. {
  58. Component* topLevelComp = builder.getManagedComponent();
  59. if (topLevelComp != nullptr)
  60. {
  61. ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
  62. const String uid (getStateId (state));
  63. if (type == nullptr || uid.isEmpty())
  64. {
  65. // ..handle the case where a child of the actual state node has changed.
  66. if (state.getParent().isValid())
  67. updateComponent (builder, state.getParent());
  68. }
  69. else
  70. {
  71. Component* const changedComp = findComponentWithID (*topLevelComp, uid);
  72. if (changedComp != nullptr)
  73. type->updateComponentFromState (changedComp, state);
  74. }
  75. }
  76. }
  77. static void updateComponentColours (Component& component, const ValueTree& colourState)
  78. {
  79. NamedValueSet& properties = component.getProperties();
  80. for (int i = properties.size(); --i >= 0;)
  81. {
  82. const Identifier name (properties.getName (i));
  83. if (name.toString().startsWith ("jcclr_"))
  84. {
  85. const String colourName (name.toString().substring (6));
  86. if (colourState [colourName].isVoid())
  87. component.removeColour (colourName.getHexValue32());
  88. }
  89. }
  90. for (int i = 0; i < colourState.getNumProperties(); ++i)
  91. {
  92. const Identifier colourName (colourState.getPropertyName (i));
  93. const String colour (colourState [colourName].toString());
  94. if (colour.isNotEmpty())
  95. component.setColour (colourName.toString().getHexValue32(), Colour::fromString (colour));
  96. }
  97. }
  98. template <class ComponentClass>
  99. class StandardTypeHandler : public ComponentBuilder::TypeHandler
  100. {
  101. public:
  102. StandardTypeHandler() : ComponentBuilder::TypeHandler (ComponentClass::Ids::tagType)
  103. {}
  104. Component* addNewComponentFromState (const ValueTree& state, Component* parent)
  105. {
  106. ComponentClass* const c = new ComponentClass();
  107. if (parent != nullptr)
  108. parent->addAndMakeVisible (c);
  109. updateComponentFromState (c, state);
  110. return c;
  111. }
  112. void updateComponentFromState (Component* component, const ValueTree& state)
  113. {
  114. ComponentClass* const c = dynamic_cast <ComponentClass*> (component);
  115. jassert (c != nullptr);
  116. c->setComponentID (state [ComponentBuilder::idProperty]);
  117. c->refreshFromValueTree (state, *this->getBuilder());
  118. }
  119. };
  120. }
  121. //=============================================================================
  122. const Identifier ComponentBuilder::idProperty ("id");
  123. ComponentBuilder::ComponentBuilder()
  124. : imageProvider (nullptr)
  125. {
  126. }
  127. ComponentBuilder::ComponentBuilder (const ValueTree& state_)
  128. : state (state_), imageProvider (nullptr)
  129. {
  130. state.addListener (this);
  131. }
  132. ComponentBuilder::~ComponentBuilder()
  133. {
  134. state.removeListener (this);
  135. #if JUCE_DEBUG
  136. // Don't delete the managed component!! The builder owns that component, and will delete
  137. // it automatically when it gets deleted.
  138. jassert (componentRef.get() == static_cast <Component*> (component));
  139. #endif
  140. }
  141. Component* ComponentBuilder::getManagedComponent()
  142. {
  143. if (component == nullptr)
  144. {
  145. component = createComponent();
  146. #if JUCE_DEBUG
  147. componentRef = component;
  148. #endif
  149. }
  150. return component;
  151. }
  152. Component* ComponentBuilder::createComponent()
  153. {
  154. jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
  155. TypeHandler* const type = getHandlerForState (state);
  156. jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree
  157. return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr;
  158. }
  159. void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
  160. {
  161. jassert (type != nullptr);
  162. // Don't try to move your types around! Once a type has been added to a builder, the
  163. // builder owns it, and you should leave it alone!
  164. jassert (type->builder == nullptr);
  165. types.add (type);
  166. type->builder = this;
  167. }
  168. ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
  169. {
  170. const Identifier targetType (s.getType());
  171. for (int i = 0; i < types.size(); ++i)
  172. {
  173. TypeHandler* const t = types.getUnchecked(i);
  174. if (t->type == targetType)
  175. return t;
  176. }
  177. return nullptr;
  178. }
  179. int ComponentBuilder::getNumHandlers() const noexcept
  180. {
  181. return types.size();
  182. }
  183. ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
  184. {
  185. return types [index];
  186. }
  187. void ComponentBuilder::registerStandardComponentTypes()
  188. {
  189. Drawable::registerDrawableTypeHandlers (*this);
  190. }
  191. void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
  192. {
  193. imageProvider = newImageProvider;
  194. }
  195. ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
  196. {
  197. return imageProvider;
  198. }
  199. void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
  200. {
  201. ComponentBuilderHelpers::updateComponent (*this, tree);
  202. }
  203. void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
  204. {
  205. ComponentBuilderHelpers::updateComponent (*this, tree);
  206. }
  207. void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&)
  208. {
  209. ComponentBuilderHelpers::updateComponent (*this, tree);
  210. }
  211. void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree)
  212. {
  213. ComponentBuilderHelpers::updateComponent (*this, tree);
  214. }
  215. void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
  216. {
  217. ComponentBuilderHelpers::updateComponent (*this, tree);
  218. }
  219. //==============================================================================
  220. ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
  221. : type (valueTreeType), builder (nullptr)
  222. {
  223. }
  224. ComponentBuilder::TypeHandler::~TypeHandler()
  225. {
  226. }
  227. ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
  228. {
  229. // A type handler needs to be registered with a ComponentBuilder before using it!
  230. jassert (builder != nullptr);
  231. return builder;
  232. }
  233. void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
  234. {
  235. using namespace ComponentBuilderHelpers;
  236. const int numExistingChildComps = parent.getNumChildComponents();
  237. Array <Component*> componentsInOrder;
  238. componentsInOrder.ensureStorageAllocated (numExistingChildComps);
  239. {
  240. OwnedArray<Component> existingComponents;
  241. existingComponents.ensureStorageAllocated (numExistingChildComps);
  242. for (int i = 0; i < numExistingChildComps; ++i)
  243. existingComponents.add (parent.getChildComponent (i));
  244. const int newNumChildren = children.getNumChildren();
  245. for (int i = 0; i < newNumChildren; ++i)
  246. {
  247. const ValueTree childState (children.getChild (i));
  248. Component* c = removeComponentWithID (existingComponents, getStateId (childState));
  249. if (c == nullptr)
  250. {
  251. TypeHandler* const type = getHandlerForState (childState);
  252. jassert (type != nullptr);
  253. if (type != nullptr)
  254. c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
  255. }
  256. if (c != nullptr)
  257. componentsInOrder.add (c);
  258. }
  259. // (remaining unused items in existingComponents get deleted here as it goes out of scope)
  260. }
  261. // Make sure the z-order is correct..
  262. if (componentsInOrder.size() > 0)
  263. {
  264. componentsInOrder.getLast()->toFront (false);
  265. for (int i = componentsInOrder.size() - 1; --i >= 0;)
  266. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  267. }
  268. }
  269. static void updateMarkers (MarkerList* const list, const ValueTree& state)
  270. {
  271. if (list != nullptr)
  272. MarkerList::ValueTreeWrapper (state).applyTo (*list);
  273. }