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.

290 lines
9.0KB

  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. }
  78. //=============================================================================
  79. const Identifier ComponentBuilder::idProperty ("id");
  80. ComponentBuilder::ComponentBuilder()
  81. : imageProvider (nullptr)
  82. {
  83. }
  84. ComponentBuilder::ComponentBuilder (const ValueTree& state_)
  85. : state (state_), imageProvider (nullptr)
  86. {
  87. state.addListener (this);
  88. }
  89. ComponentBuilder::~ComponentBuilder()
  90. {
  91. state.removeListener (this);
  92. #if JUCE_DEBUG
  93. // Don't delete the managed component!! The builder owns that component, and will delete
  94. // it automatically when it gets deleted.
  95. jassert (componentRef.get() == static_cast <Component*> (component));
  96. #endif
  97. }
  98. Component* ComponentBuilder::getManagedComponent()
  99. {
  100. if (component == nullptr)
  101. {
  102. component = createComponent();
  103. #if JUCE_DEBUG
  104. componentRef = component;
  105. #endif
  106. }
  107. return component;
  108. }
  109. Component* ComponentBuilder::createComponent()
  110. {
  111. jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
  112. TypeHandler* const type = getHandlerForState (state);
  113. jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree
  114. return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr;
  115. }
  116. void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
  117. {
  118. jassert (type != nullptr);
  119. // Don't try to move your types around! Once a type has been added to a builder, the
  120. // builder owns it, and you should leave it alone!
  121. jassert (type->builder == nullptr);
  122. types.add (type);
  123. type->builder = this;
  124. }
  125. ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
  126. {
  127. const Identifier targetType (s.getType());
  128. for (int i = 0; i < types.size(); ++i)
  129. {
  130. TypeHandler* const t = types.getUnchecked(i);
  131. if (t->type == targetType)
  132. return t;
  133. }
  134. return nullptr;
  135. }
  136. int ComponentBuilder::getNumHandlers() const noexcept
  137. {
  138. return types.size();
  139. }
  140. ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
  141. {
  142. return types [index];
  143. }
  144. void ComponentBuilder::registerStandardComponentTypes()
  145. {
  146. Drawable::registerDrawableTypeHandlers (*this);
  147. }
  148. void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
  149. {
  150. imageProvider = newImageProvider;
  151. }
  152. ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
  153. {
  154. return imageProvider;
  155. }
  156. void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
  157. {
  158. ComponentBuilderHelpers::updateComponent (*this, tree);
  159. }
  160. void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
  161. {
  162. ComponentBuilderHelpers::updateComponent (*this, tree);
  163. }
  164. void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&)
  165. {
  166. ComponentBuilderHelpers::updateComponent (*this, tree);
  167. }
  168. void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree)
  169. {
  170. ComponentBuilderHelpers::updateComponent (*this, tree);
  171. }
  172. void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
  173. {
  174. ComponentBuilderHelpers::updateComponent (*this, tree);
  175. }
  176. //==============================================================================
  177. ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
  178. : type (valueTreeType), builder (nullptr)
  179. {
  180. }
  181. ComponentBuilder::TypeHandler::~TypeHandler()
  182. {
  183. }
  184. ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
  185. {
  186. // A type handler needs to be registered with a ComponentBuilder before using it!
  187. jassert (builder != nullptr);
  188. return builder;
  189. }
  190. void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
  191. {
  192. using namespace ComponentBuilderHelpers;
  193. const int numExistingChildComps = parent.getNumChildComponents();
  194. Array <Component*> componentsInOrder;
  195. componentsInOrder.ensureStorageAllocated (numExistingChildComps);
  196. {
  197. OwnedArray<Component> existingComponents;
  198. existingComponents.ensureStorageAllocated (numExistingChildComps);
  199. for (int i = 0; i < numExistingChildComps; ++i)
  200. existingComponents.add (parent.getChildComponent (i));
  201. const int newNumChildren = children.getNumChildren();
  202. for (int i = 0; i < newNumChildren; ++i)
  203. {
  204. const ValueTree childState (children.getChild (i));
  205. Component* c = removeComponentWithID (existingComponents, getStateId (childState));
  206. if (c == nullptr)
  207. {
  208. TypeHandler* const type = getHandlerForState (childState);
  209. jassert (type != nullptr);
  210. if (type != nullptr)
  211. c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
  212. }
  213. if (c != nullptr)
  214. componentsInOrder.add (c);
  215. }
  216. // (remaining unused items in existingComponents get deleted here as it goes out of scope)
  217. }
  218. // Make sure the z-order is correct..
  219. if (componentsInOrder.size() > 0)
  220. {
  221. componentsInOrder.getLast()->toFront (false);
  222. for (int i = componentsInOrder.size() - 1; --i >= 0;)
  223. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  224. }
  225. }