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.

442 lines
15KB

  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. const Identifier ComponentBuilder::positionID ("position");
  124. ComponentBuilder::ComponentBuilder()
  125. : imageProvider (nullptr)
  126. {
  127. }
  128. ComponentBuilder::ComponentBuilder (const ValueTree& state_)
  129. : state (state_), imageProvider (nullptr)
  130. {
  131. state.addListener (this);
  132. }
  133. ComponentBuilder::~ComponentBuilder()
  134. {
  135. state.removeListener (this);
  136. #if JUCE_DEBUG
  137. // Don't delete the managed component!! The builder owns that component, and will delete
  138. // it automatically when it gets deleted.
  139. jassert (componentRef.get() == static_cast <Component*> (component));
  140. #endif
  141. }
  142. Component* ComponentBuilder::getManagedComponent()
  143. {
  144. if (component == nullptr)
  145. {
  146. component = createComponent();
  147. #if JUCE_DEBUG
  148. componentRef = component;
  149. #endif
  150. }
  151. return component;
  152. }
  153. Component* ComponentBuilder::createComponent()
  154. {
  155. jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
  156. TypeHandler* const type = getHandlerForState (state);
  157. jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree
  158. return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr;
  159. }
  160. void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
  161. {
  162. jassert (type != nullptr);
  163. // Don't try to move your types around! Once a type has been added to a builder, the
  164. // builder owns it, and you should leave it alone!
  165. jassert (type->builder == nullptr);
  166. types.add (type);
  167. type->builder = this;
  168. }
  169. ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
  170. {
  171. const Identifier targetType (s.getType());
  172. for (int i = 0; i < types.size(); ++i)
  173. {
  174. TypeHandler* const t = types.getUnchecked(i);
  175. if (t->type == targetType)
  176. return t;
  177. }
  178. return nullptr;
  179. }
  180. int ComponentBuilder::getNumHandlers() const noexcept
  181. {
  182. return types.size();
  183. }
  184. ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
  185. {
  186. return types [index];
  187. }
  188. void ComponentBuilder::registerStandardComponentTypes()
  189. {
  190. Drawable::registerDrawableTypeHandlers (*this);
  191. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ComboBox>());
  192. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Slider>());
  193. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Label>());
  194. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <Slider>());
  195. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <TextEditor>());
  196. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <GroupComponent>());
  197. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <TextButton>());
  198. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ToggleButton>());
  199. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ImageButton>());
  200. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <ImageComponent>());
  201. registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler <HyperlinkButton>());
  202. }
  203. void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
  204. {
  205. imageProvider = newImageProvider;
  206. }
  207. ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
  208. {
  209. return imageProvider;
  210. }
  211. void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
  212. {
  213. ComponentBuilderHelpers::updateComponent (*this, tree);
  214. }
  215. void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
  216. {
  217. ComponentBuilderHelpers::updateComponent (*this, tree);
  218. }
  219. void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&)
  220. {
  221. ComponentBuilderHelpers::updateComponent (*this, tree);
  222. }
  223. void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree)
  224. {
  225. ComponentBuilderHelpers::updateComponent (*this, tree);
  226. }
  227. void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
  228. {
  229. ComponentBuilderHelpers::updateComponent (*this, tree);
  230. }
  231. //==============================================================================
  232. ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
  233. : type (valueTreeType), builder (nullptr)
  234. {
  235. }
  236. ComponentBuilder::TypeHandler::~TypeHandler()
  237. {
  238. }
  239. ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
  240. {
  241. // A type handler needs to be registered with a ComponentBuilder before using it!
  242. jassert (builder != nullptr);
  243. return builder;
  244. }
  245. void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
  246. {
  247. using namespace ComponentBuilderHelpers;
  248. const int numExistingChildComps = parent.getNumChildComponents();
  249. Array <Component*> componentsInOrder;
  250. componentsInOrder.ensureStorageAllocated (numExistingChildComps);
  251. {
  252. OwnedArray<Component> existingComponents;
  253. existingComponents.ensureStorageAllocated (numExistingChildComps);
  254. int i;
  255. for (i = 0; i < numExistingChildComps; ++i)
  256. existingComponents.add (parent.getChildComponent (i));
  257. const int newNumChildren = children.getNumChildren();
  258. for (i = 0; i < newNumChildren; ++i)
  259. {
  260. const ValueTree childState (children.getChild (i));
  261. Component* c = removeComponentWithID (existingComponents, getStateId (childState));
  262. if (c == nullptr)
  263. {
  264. TypeHandler* const type = getHandlerForState (childState);
  265. jassert (type != nullptr);
  266. if (type != nullptr)
  267. c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
  268. }
  269. if (c != nullptr)
  270. componentsInOrder.add (c);
  271. }
  272. // (remaining unused items in existingComponents get deleted here as it goes out of scope)
  273. }
  274. // Make sure the z-order is correct..
  275. if (componentsInOrder.size() > 0)
  276. {
  277. componentsInOrder.getLast()->toFront (false);
  278. for (int i = componentsInOrder.size() - 1; --i >= 0;)
  279. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  280. }
  281. }
  282. static void updateMarkers (MarkerList* const list, const ValueTree& state)
  283. {
  284. if (list != nullptr)
  285. MarkerList::ValueTreeWrapper (state).applyTo (*list);
  286. }
  287. void ComponentBuilder::initialiseRecursively (Component& comp, const ValueTree& state)
  288. {
  289. refreshBasicComponentProperties (comp, state);
  290. updateMarkers (comp.getMarkers (true), state.getChildWithName ("MARKERS_X"));
  291. updateMarkers (comp.getMarkers (false), state.getChildWithName ("MARKERS_Y"));
  292. const ValueTree childList (state.getChildWithName ("COMPONENTS"));
  293. if (childList.isValid())
  294. {
  295. updateChildComponents (comp, childList);
  296. for (int i = 0; i < childList.getNumChildren(); ++i)
  297. {
  298. const ValueTree childState (childList.getChild(i));
  299. Component* const c = ComponentBuilderHelpers::findComponentWithID (comp, ComponentBuilderHelpers::getStateId (childState));
  300. if (c != nullptr)
  301. {
  302. ComponentBuilder::TypeHandler* const type = getHandlerForState (childState);
  303. if (type != nullptr)
  304. type->updateComponentFromState (c, childState);
  305. else
  306. initialiseRecursively (*c, childState);
  307. }
  308. }
  309. }
  310. }
  311. void ComponentBuilder::initialiseFromValueTree (Component& comp,
  312. const ValueTree& state,
  313. ImageProvider* const imageProvider)
  314. {
  315. ComponentBuilder builder;
  316. builder.setImageProvider (imageProvider);
  317. builder.registerStandardComponentTypes();
  318. builder.initialiseRecursively (comp, state);
  319. }
  320. RelativeRectangle ComponentBuilder::getComponentBounds (const ValueTree& state)
  321. {
  322. try
  323. {
  324. return RelativeRectangle (state [positionID].toString());
  325. }
  326. catch (Expression::ParseError&)
  327. {}
  328. return RelativeRectangle();
  329. }
  330. void ComponentBuilder::refreshBasicComponentProperties (Component& comp, const ValueTree& state)
  331. {
  332. static const Identifier focusOrderID ("focusOrder");
  333. static const Identifier tooltipID ("tooltip");
  334. static const Identifier nameID ("name");
  335. comp.setName (state [nameID].toString());
  336. if (state.hasProperty (positionID))
  337. getComponentBounds (state).applyToComponent (comp);
  338. comp.setExplicitFocusOrder (state [focusOrderID]);
  339. const var tip (state [tooltipID]);
  340. if (! tip.isVoid())
  341. {
  342. SettableTooltipClient* tooltipClient = dynamic_cast <SettableTooltipClient*> (&comp);
  343. if (tooltipClient != nullptr)
  344. tooltipClient->setTooltip (tip.toString());
  345. }
  346. ComponentBuilderHelpers::updateComponentColours (comp, state.getChildWithName ("COLOURS"));
  347. }