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.

439 lines
14KB

  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. BEGIN_JUCE_NAMESPACE
  19. //=============================================================================
  20. namespace ComponentBuilderHelpers
  21. {
  22. String getStateId (const ValueTree& state)
  23. {
  24. return state [ComponentBuilder::idProperty].toString();
  25. }
  26. Component* findComponentWithID (OwnedArray<Component>& components, const String& compId)
  27. {
  28. jassert (compId.isNotEmpty());
  29. for (int i = components.size(); --i >= 0;)
  30. {
  31. Component* const c = components.getUnchecked (i);
  32. if (c->getComponentID() == compId)
  33. return components.removeAndReturn (i);
  34. }
  35. return nullptr;
  36. }
  37. Component* findComponentWithID (Component& c, const String& compId)
  38. {
  39. jassert (compId.isNotEmpty());
  40. if (c.getComponentID() == compId)
  41. return &c;
  42. for (int i = c.getNumChildComponents(); --i >= 0;)
  43. {
  44. Component* const child = findComponentWithID (*c.getChildComponent (i), compId);
  45. if (child != nullptr)
  46. return child;
  47. }
  48. return nullptr;
  49. }
  50. Component* createNewComponent (ComponentBuilder::TypeHandler& type,
  51. const ValueTree& state, Component* parent)
  52. {
  53. Component* const c = type.addNewComponentFromState (state, parent);
  54. jassert (c != nullptr && c->getParentComponent() == parent);
  55. c->setComponentID (getStateId (state));
  56. return c;
  57. }
  58. void updateComponent (ComponentBuilder& builder, const ValueTree& state)
  59. {
  60. Component* topLevelComp = builder.getManagedComponent();
  61. if (topLevelComp != nullptr)
  62. {
  63. ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
  64. const String uid (getStateId (state));
  65. if (type == nullptr || uid.isEmpty())
  66. {
  67. // ..handle the case where a child of the actual state node has changed.
  68. if (state.getParent().isValid())
  69. updateComponent (builder, state.getParent());
  70. }
  71. else
  72. {
  73. Component* const changedComp = findComponentWithID (*topLevelComp, uid);
  74. if (changedComp != nullptr)
  75. type->updateComponentFromState (changedComp, state);
  76. }
  77. }
  78. }
  79. void updateComponentColours (Component& component, const ValueTree& colourState)
  80. {
  81. NamedValueSet& properties = component.getProperties();
  82. for (int i = properties.size(); --i >= 0;)
  83. {
  84. const Identifier name (properties.getName (i));
  85. if (name.toString().startsWith ("jcclr_"))
  86. {
  87. const String colourName (name.toString().substring (6));
  88. if (colourState [colourName].isVoid())
  89. component.removeColour (colourName.getHexValue32());
  90. }
  91. }
  92. for (int i = 0; i < colourState.getNumProperties(); ++i)
  93. {
  94. const Identifier colourName (colourState.getPropertyName (i));
  95. const String colour (colourState [colourName].toString());
  96. if (colour.isNotEmpty())
  97. component.setColour (colourName.toString().getHexValue32(), Colour::fromString (colour));
  98. }
  99. }
  100. template <class ComponentClass>
  101. class StandardTypeHandler : public ComponentBuilder::TypeHandler
  102. {
  103. public:
  104. StandardTypeHandler() : ComponentBuilder::TypeHandler (ComponentClass::Ids::tagType)
  105. {}
  106. Component* addNewComponentFromState (const ValueTree& state, Component* parent)
  107. {
  108. ComponentClass* const c = new ComponentClass();
  109. if (parent != nullptr)
  110. parent->addAndMakeVisible (c);
  111. updateComponentFromState (c, state);
  112. return c;
  113. }
  114. void updateComponentFromState (Component* component, const ValueTree& state)
  115. {
  116. ComponentClass* const c = dynamic_cast <ComponentClass*> (component);
  117. jassert (c != nullptr);
  118. c->setComponentID (state [ComponentBuilder::idProperty]);
  119. c->refreshFromValueTree (state, *this->getBuilder());
  120. }
  121. };
  122. }
  123. //=============================================================================
  124. const Identifier ComponentBuilder::idProperty ("id");
  125. ComponentBuilder::ComponentBuilder()
  126. : imageProvider (nullptr)
  127. {
  128. }
  129. ComponentBuilder::ComponentBuilder (const ValueTree& state_)
  130. : state (state_), imageProvider (nullptr)
  131. {
  132. state.addListener (this);
  133. }
  134. ComponentBuilder::~ComponentBuilder()
  135. {
  136. state.removeListener (this);
  137. #if JUCE_DEBUG
  138. // Don't delete the managed component!! The builder owns that component, and will delete
  139. // it automatically when it gets deleted.
  140. jassert (componentRef.get() == static_cast <Component*> (component));
  141. #endif
  142. }
  143. Component* ComponentBuilder::getManagedComponent()
  144. {
  145. if (component == nullptr)
  146. {
  147. component = createComponent();
  148. #if JUCE_DEBUG
  149. componentRef = component;
  150. #endif
  151. }
  152. return component;
  153. }
  154. Component* ComponentBuilder::createComponent()
  155. {
  156. jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
  157. TypeHandler* const type = getHandlerForState (state);
  158. jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree
  159. return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr;
  160. }
  161. void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
  162. {
  163. jassert (type != nullptr);
  164. // Don't try to move your types around! Once a type has been added to a builder, the
  165. // builder owns it, and you should leave it alone!
  166. jassert (type->builder == nullptr);
  167. types.add (type);
  168. type->builder = this;
  169. }
  170. ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
  171. {
  172. const Identifier targetType (s.getType());
  173. for (int i = 0; i < types.size(); ++i)
  174. {
  175. TypeHandler* const t = types.getUnchecked(i);
  176. if (t->type == targetType)
  177. return t;
  178. }
  179. return nullptr;
  180. }
  181. int ComponentBuilder::getNumHandlers() const noexcept
  182. {
  183. return types.size();
  184. }
  185. ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
  186. {
  187. return types [index];
  188. }
  189. void ComponentBuilder::registerStandardComponentTypes()
  190. {
  191. Drawable::registerDrawableTypeHandlers (*this);
  192. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ComboBox>());
  193. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Slider>());
  194. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Label>());
  195. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Slider>());
  196. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <TextEditor>());
  197. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <GroupComponent>());
  198. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <TextButton>());
  199. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ToggleButton>());
  200. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ImageButton>());
  201. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ImageComponent>());
  202. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <HyperlinkButton>());
  203. }
  204. void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
  205. {
  206. imageProvider = newImageProvider;
  207. }
  208. ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
  209. {
  210. return imageProvider;
  211. }
  212. void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
  213. {
  214. ComponentBuilderHelpers::updateComponent (*this, tree);
  215. }
  216. void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
  217. {
  218. ComponentBuilderHelpers::updateComponent (*this, tree);
  219. }
  220. void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&)
  221. {
  222. ComponentBuilderHelpers::updateComponent (*this, tree);
  223. }
  224. void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree)
  225. {
  226. ComponentBuilderHelpers::updateComponent (*this, tree);
  227. }
  228. void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
  229. {
  230. ComponentBuilderHelpers::updateComponent (*this, tree);
  231. }
  232. //==============================================================================
  233. ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
  234. : type (valueTreeType), builder (nullptr)
  235. {
  236. }
  237. ComponentBuilder::TypeHandler::~TypeHandler()
  238. {
  239. }
  240. ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
  241. {
  242. // A type handler needs to be registered with a ComponentBuilder before using it!
  243. jassert (builder != nullptr);
  244. return builder;
  245. }
  246. void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
  247. {
  248. using namespace ComponentBuilderHelpers;
  249. const int numExistingChildComps = parent.getNumChildComponents();
  250. Array <Component*> componentsInOrder;
  251. componentsInOrder.ensureStorageAllocated (numExistingChildComps);
  252. {
  253. OwnedArray<Component> existingComponents;
  254. existingComponents.ensureStorageAllocated (numExistingChildComps);
  255. int i;
  256. for (i = 0; i < numExistingChildComps; ++i)
  257. existingComponents.add (parent.getChildComponent (i));
  258. const int newNumChildren = children.getNumChildren();
  259. for (i = 0; i < newNumChildren; ++i)
  260. {
  261. const ValueTree childState (children.getChild (i));
  262. Component* c = findComponentWithID (existingComponents, getStateId (childState));
  263. if (c == nullptr)
  264. {
  265. TypeHandler* const type = getHandlerForState (childState);
  266. jassert (type != nullptr);
  267. if (type != nullptr)
  268. c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
  269. }
  270. if (c != nullptr)
  271. componentsInOrder.add (c);
  272. }
  273. // (remaining unused items in existingComponents get deleted here as it goes out of scope)
  274. }
  275. // Make sure the z-order is correct..
  276. if (componentsInOrder.size() > 0)
  277. {
  278. componentsInOrder.getLast()->toFront (false);
  279. for (int i = componentsInOrder.size() - 1; --i >= 0;)
  280. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  281. }
  282. }
  283. static void updateMarkers (MarkerList* const list, const ValueTree& state)
  284. {
  285. if (list != nullptr)
  286. MarkerList::ValueTreeWrapper (state).applyTo (*list);
  287. }
  288. void ComponentBuilder::initialiseFromValueTree (Component& comp,
  289. const ValueTree& state,
  290. ImageProvider* const imageProvider)
  291. {
  292. using namespace ComponentBuilderHelpers;
  293. ComponentBuilder builder;
  294. builder.setImageProvider (imageProvider);
  295. builder.registerStandardComponentTypes();
  296. updateMarkers (comp.getMarkers (true), state.getChildWithName ("MARKERS_X"));
  297. updateMarkers (comp.getMarkers (false), state.getChildWithName ("MARKERS_Y"));
  298. const ValueTree childList (state.getChildWithName ("COMPONENTS"));
  299. builder.updateChildComponents (comp, childList);
  300. for (int i = 0; i < childList.getNumChildren(); ++i)
  301. {
  302. const ValueTree state (childList.getChild(i));
  303. Component* const c = findComponentWithID (comp, getStateId (state));
  304. if (c != nullptr)
  305. {
  306. ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
  307. if (type != nullptr)
  308. type->updateComponentFromState (c, state);
  309. else
  310. refreshBasicComponentProperties (*c, state);
  311. }
  312. }
  313. }
  314. RelativeRectangle ComponentBuilder::getComponentBounds (const ValueTree& state)
  315. {
  316. static const Identifier positionID ("position");
  317. try
  318. {
  319. return RelativeRectangle (state [positionID].toString());
  320. }
  321. catch (Expression::ParseError&)
  322. {}
  323. return RelativeRectangle();
  324. }
  325. void ComponentBuilder::refreshBasicComponentProperties (Component& comp, const ValueTree& state)
  326. {
  327. static const Identifier focusOrderID ("focusOrder");
  328. static const Identifier tooltipID ("tooltip");
  329. static const Identifier nameID ("name");
  330. comp.setName (state [nameID].toString());
  331. getComponentBounds (state).applyToComponent (comp);
  332. comp.setExplicitFocusOrder (state [focusOrderID]);
  333. const var tip (state [tooltipID]);
  334. if (! tip.isVoid())
  335. {
  336. SettableTooltipClient* tooltipClient = dynamic_cast <SettableTooltipClient*> (&comp);
  337. if (tooltipClient != nullptr)
  338. tooltipClient->setTooltip (tip.toString());
  339. }
  340. ComponentBuilderHelpers::updateComponentColours (comp, state.getChildWithName ("COLOURS"));
  341. }
  342. END_JUCE_NAMESPACE