Audio plugin host https://kx.studio/carla
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.

1245 lines
40KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. class ValueTree::SharedObject : public ReferenceCountedObject
  16. {
  17. public:
  18. using Ptr = ReferenceCountedObjectPtr<SharedObject>;
  19. explicit SharedObject (const Identifier& t) noexcept : type (t) {}
  20. SharedObject (const SharedObject& other)
  21. : ReferenceCountedObject(), type (other.type), properties (other.properties)
  22. {
  23. for (auto* c : other.children)
  24. {
  25. auto* child = new SharedObject (*c);
  26. child->parent = this;
  27. children.add (child);
  28. }
  29. }
  30. SharedObject& operator= (const SharedObject&) = delete;
  31. ~SharedObject()
  32. {
  33. jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting!
  34. for (auto i = children.size(); --i >= 0;)
  35. {
  36. const Ptr c (children.getObjectPointerUnchecked (i));
  37. c->parent = nullptr;
  38. children.remove (i);
  39. c->sendParentChangeMessage();
  40. }
  41. }
  42. SharedObject& getRoot() noexcept
  43. {
  44. return parent == nullptr ? *this : parent->getRoot();
  45. }
  46. template <typename Function>
  47. void callListeners (ValueTree::Listener* listenerToExclude, Function fn) const
  48. {
  49. auto numListeners = valueTreesWithListeners.size();
  50. if (numListeners == 1)
  51. {
  52. valueTreesWithListeners.getUnchecked (0)->listeners.callExcluding (listenerToExclude, fn);
  53. }
  54. else if (numListeners > 0)
  55. {
  56. auto listenersCopy = valueTreesWithListeners;
  57. for (int i = 0; i < numListeners; ++i)
  58. {
  59. auto* v = listenersCopy.getUnchecked (i);
  60. if (i == 0 || valueTreesWithListeners.contains (v))
  61. v->listeners.callExcluding (listenerToExclude, fn);
  62. }
  63. }
  64. }
  65. template <typename Function>
  66. void callListenersForAllParents (ValueTree::Listener* listenerToExclude, Function fn) const
  67. {
  68. for (auto* t = this; t != nullptr; t = t->parent)
  69. t->callListeners (listenerToExclude, fn);
  70. }
  71. void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
  72. {
  73. ValueTree tree (*this);
  74. callListenersForAllParents (listenerToExclude, [&] (Listener& l) { l.valueTreePropertyChanged (tree, property); });
  75. }
  76. void sendChildAddedMessage (ValueTree child)
  77. {
  78. ValueTree tree (*this);
  79. callListenersForAllParents (nullptr, [&] (Listener& l) { l.valueTreeChildAdded (tree, child); });
  80. }
  81. void sendChildRemovedMessage (ValueTree child, int index)
  82. {
  83. ValueTree tree (*this);
  84. callListenersForAllParents (nullptr, [=, &tree, &child] (Listener& l) { l.valueTreeChildRemoved (tree, child, index); });
  85. }
  86. void sendChildOrderChangedMessage (int oldIndex, int newIndex)
  87. {
  88. ValueTree tree (*this);
  89. callListenersForAllParents (nullptr, [=, &tree] (Listener& l) { l.valueTreeChildOrderChanged (tree, oldIndex, newIndex); });
  90. }
  91. void sendParentChangeMessage()
  92. {
  93. ValueTree tree (*this);
  94. for (auto j = children.size(); --j >= 0;)
  95. if (auto* child = children.getObjectPointer (j))
  96. child->sendParentChangeMessage();
  97. callListeners (nullptr, [&] (Listener& l) { l.valueTreeParentChanged (tree); });
  98. }
  99. void setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager,
  100. ValueTree::Listener* listenerToExclude = nullptr)
  101. {
  102. if (undoManager == nullptr)
  103. {
  104. if (properties.set (name, newValue))
  105. sendPropertyChangeMessage (name, listenerToExclude);
  106. }
  107. else
  108. {
  109. if (auto* existingValue = properties.getVarPointer (name))
  110. {
  111. if (*existingValue != newValue)
  112. undoManager->perform (new SetPropertyAction (*this, name, newValue, *existingValue,
  113. false, false, listenerToExclude));
  114. }
  115. else
  116. {
  117. undoManager->perform (new SetPropertyAction (*this, name, newValue, {},
  118. true, false, listenerToExclude));
  119. }
  120. }
  121. }
  122. bool hasProperty (const Identifier& name) const noexcept
  123. {
  124. return properties.contains (name);
  125. }
  126. void removeProperty (const Identifier& name, UndoManager* undoManager)
  127. {
  128. if (undoManager == nullptr)
  129. {
  130. if (properties.remove (name))
  131. sendPropertyChangeMessage (name);
  132. }
  133. else
  134. {
  135. if (properties.contains (name))
  136. undoManager->perform (new SetPropertyAction (*this, name, {}, properties[name], false, true));
  137. }
  138. }
  139. void removeAllProperties (UndoManager* undoManager)
  140. {
  141. if (undoManager == nullptr)
  142. {
  143. while (properties.size() > 0)
  144. {
  145. auto name = properties.getName (properties.size() - 1);
  146. properties.remove (name);
  147. sendPropertyChangeMessage (name);
  148. }
  149. }
  150. else
  151. {
  152. for (auto i = properties.size(); --i >= 0;)
  153. undoManager->perform (new SetPropertyAction (*this, properties.getName (i), {},
  154. properties.getValueAt (i), false, true));
  155. }
  156. }
  157. void copyPropertiesFrom (const SharedObject& source, UndoManager* undoManager)
  158. {
  159. for (auto i = properties.size(); --i >= 0;)
  160. if (! source.properties.contains (properties.getName (i)))
  161. removeProperty (properties.getName (i), undoManager);
  162. for (int i = 0; i < source.properties.size(); ++i)
  163. setProperty (source.properties.getName (i), source.properties.getValueAt (i), undoManager);
  164. }
  165. ValueTree getChildWithName (const Identifier& typeToMatch) const
  166. {
  167. for (auto* s : children)
  168. if (s->type == typeToMatch)
  169. return ValueTree (*s);
  170. return {};
  171. }
  172. ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
  173. {
  174. for (auto* s : children)
  175. if (s->type == typeToMatch)
  176. return ValueTree (*s);
  177. auto newObject = new SharedObject (typeToMatch);
  178. addChild (newObject, -1, undoManager);
  179. return ValueTree (*newObject);
  180. }
  181. ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
  182. {
  183. for (auto* s : children)
  184. if (s->properties[propertyName] == propertyValue)
  185. return ValueTree (*s);
  186. return {};
  187. }
  188. bool isAChildOf (const SharedObject* possibleParent) const noexcept
  189. {
  190. for (auto* p = parent; p != nullptr; p = p->parent)
  191. if (p == possibleParent)
  192. return true;
  193. return false;
  194. }
  195. int indexOf (const ValueTree& child) const noexcept
  196. {
  197. return children.indexOf (child.object);
  198. }
  199. void addChild (SharedObject* child, int index, UndoManager* undoManager)
  200. {
  201. if (child != nullptr && child->parent != this)
  202. {
  203. if (child != this && ! isAChildOf (child))
  204. {
  205. // You should always make sure that a child is removed from its previous parent before
  206. // adding it somewhere else - otherwise, it's ambiguous as to whether a different
  207. // undomanager should be used when removing it from its current parent..
  208. jassert (child->parent == nullptr);
  209. if (child->parent != nullptr)
  210. {
  211. jassert (child->parent->children.indexOf (child) >= 0);
  212. child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
  213. }
  214. if (undoManager == nullptr)
  215. {
  216. children.insert (index, child);
  217. child->parent = this;
  218. sendChildAddedMessage (ValueTree (*child));
  219. child->sendParentChangeMessage();
  220. }
  221. else
  222. {
  223. if (! isPositiveAndBelow (index, children.size()))
  224. index = children.size();
  225. undoManager->perform (new AddOrRemoveChildAction (*this, index, child));
  226. }
  227. }
  228. else
  229. {
  230. // You're attempting to create a recursive loop! A node
  231. // can't be a child of one of its own children!
  232. jassertfalse;
  233. }
  234. }
  235. }
  236. void removeChild (int childIndex, UndoManager* undoManager)
  237. {
  238. if (auto child = Ptr (children.getObjectPointer (childIndex)))
  239. {
  240. if (undoManager == nullptr)
  241. {
  242. children.remove (childIndex);
  243. child->parent = nullptr;
  244. sendChildRemovedMessage (ValueTree (child), childIndex);
  245. child->sendParentChangeMessage();
  246. }
  247. else
  248. {
  249. undoManager->perform (new AddOrRemoveChildAction (*this, childIndex, {}));
  250. }
  251. }
  252. }
  253. void removeAllChildren (UndoManager* undoManager)
  254. {
  255. while (children.size() > 0)
  256. removeChild (children.size() - 1, undoManager);
  257. }
  258. void moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
  259. {
  260. // The source index must be a valid index!
  261. jassert (isPositiveAndBelow (currentIndex, children.size()));
  262. if (currentIndex != newIndex
  263. && isPositiveAndBelow (currentIndex, children.size()))
  264. {
  265. if (undoManager == nullptr)
  266. {
  267. children.move (currentIndex, newIndex);
  268. sendChildOrderChangedMessage (currentIndex, newIndex);
  269. }
  270. else
  271. {
  272. if (! isPositiveAndBelow (newIndex, children.size()))
  273. newIndex = children.size() - 1;
  274. undoManager->perform (new MoveChildAction (*this, currentIndex, newIndex));
  275. }
  276. }
  277. }
  278. void reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
  279. {
  280. jassert (newOrder.size() == children.size());
  281. for (int i = 0; i < children.size(); ++i)
  282. {
  283. auto* child = newOrder.getUnchecked (i)->object.get();
  284. if (children.getObjectPointerUnchecked (i) != child)
  285. {
  286. auto oldIndex = children.indexOf (child);
  287. jassert (oldIndex >= 0);
  288. moveChild (oldIndex, i, undoManager);
  289. }
  290. }
  291. }
  292. bool isEquivalentTo (const SharedObject& other) const noexcept
  293. {
  294. if (type != other.type
  295. || properties.size() != other.properties.size()
  296. || children.size() != other.children.size()
  297. || properties != other.properties)
  298. return false;
  299. for (int i = 0; i < children.size(); ++i)
  300. if (! children.getObjectPointerUnchecked (i)->isEquivalentTo (*other.children.getObjectPointerUnchecked (i)))
  301. return false;
  302. return true;
  303. }
  304. XmlElement* createXml() const
  305. {
  306. auto* xml = new XmlElement (type);
  307. properties.copyToXmlAttributes (*xml);
  308. // (NB: it's faster to add nodes to XML elements in reverse order)
  309. for (auto i = children.size(); --i >= 0;)
  310. xml->prependChildElement (children.getObjectPointerUnchecked (i)->createXml());
  311. return xml;
  312. }
  313. void writeToStream (OutputStream& output) const
  314. {
  315. output.writeString (type.toString());
  316. output.writeCompressedInt (properties.size());
  317. for (int j = 0; j < properties.size(); ++j)
  318. {
  319. output.writeString (properties.getName (j).toString());
  320. properties.getValueAt (j).writeToStream (output);
  321. }
  322. output.writeCompressedInt (children.size());
  323. for (auto* c : children)
  324. writeObjectToStream (output, c);
  325. }
  326. static void writeObjectToStream (OutputStream& output, const SharedObject* object)
  327. {
  328. if (object != nullptr)
  329. {
  330. object->writeToStream (output);
  331. }
  332. else
  333. {
  334. output.writeString ({});
  335. output.writeCompressedInt (0);
  336. output.writeCompressedInt (0);
  337. }
  338. }
  339. //==============================================================================
  340. struct SetPropertyAction : public UndoableAction
  341. {
  342. SetPropertyAction (Ptr targetObject, const Identifier& propertyName,
  343. const var& newVal, const var& oldVal, bool isAdding, bool isDeleting,
  344. ValueTree::Listener* listenerToExclude = nullptr)
  345. : target (std::move (targetObject)),
  346. name (propertyName), newValue (newVal), oldValue (oldVal),
  347. isAddingNewProperty (isAdding), isDeletingProperty (isDeleting),
  348. excludeListener (listenerToExclude)
  349. {
  350. }
  351. bool perform() override
  352. {
  353. jassert (! (isAddingNewProperty && target->hasProperty (name)));
  354. if (isDeletingProperty)
  355. target->removeProperty (name, nullptr);
  356. else
  357. target->setProperty (name, newValue, nullptr, excludeListener);
  358. return true;
  359. }
  360. bool undo() override
  361. {
  362. if (isAddingNewProperty)
  363. target->removeProperty (name, nullptr);
  364. else
  365. target->setProperty (name, oldValue, nullptr);
  366. return true;
  367. }
  368. int getSizeInUnits() override
  369. {
  370. return (int) sizeof (*this); //xxx should be more accurate
  371. }
  372. UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
  373. {
  374. if (! (isAddingNewProperty || isDeletingProperty))
  375. {
  376. if (auto* next = dynamic_cast<SetPropertyAction*> (nextAction))
  377. if (next->target == target && next->name == name
  378. && ! (next->isAddingNewProperty || next->isDeletingProperty))
  379. return new SetPropertyAction (*target, name, next->newValue, oldValue, false, false);
  380. }
  381. return nullptr;
  382. }
  383. private:
  384. const Ptr target;
  385. const Identifier name;
  386. const var newValue;
  387. var oldValue;
  388. const bool isAddingNewProperty : 1, isDeletingProperty : 1;
  389. ValueTree::Listener* excludeListener;
  390. JUCE_DECLARE_NON_COPYABLE (SetPropertyAction)
  391. };
  392. //==============================================================================
  393. struct AddOrRemoveChildAction : public UndoableAction
  394. {
  395. AddOrRemoveChildAction (Ptr parentObject, int index, SharedObject* newChild)
  396. : target (std::move (parentObject)),
  397. child (newChild != nullptr ? newChild : target->children.getObjectPointer (index)),
  398. childIndex (index),
  399. isDeleting (newChild == nullptr)
  400. {
  401. jassert (child != nullptr);
  402. }
  403. bool perform() override
  404. {
  405. if (isDeleting)
  406. target->removeChild (childIndex, nullptr);
  407. else
  408. target->addChild (child.get(), childIndex, nullptr);
  409. return true;
  410. }
  411. bool undo() override
  412. {
  413. if (isDeleting)
  414. {
  415. target->addChild (child.get(), childIndex, nullptr);
  416. }
  417. else
  418. {
  419. // If you hit this, it seems that your object's state is getting confused - probably
  420. // because you've interleaved some undoable and non-undoable operations?
  421. jassert (childIndex < target->children.size());
  422. target->removeChild (childIndex, nullptr);
  423. }
  424. return true;
  425. }
  426. int getSizeInUnits() override
  427. {
  428. return (int) sizeof (*this); //xxx should be more accurate
  429. }
  430. private:
  431. const Ptr target, child;
  432. const int childIndex;
  433. const bool isDeleting;
  434. JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction)
  435. };
  436. //==============================================================================
  437. struct MoveChildAction : public UndoableAction
  438. {
  439. MoveChildAction (Ptr parentObject, int fromIndex, int toIndex) noexcept
  440. : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex)
  441. {
  442. }
  443. bool perform() override
  444. {
  445. parent->moveChild (startIndex, endIndex, nullptr);
  446. return true;
  447. }
  448. bool undo() override
  449. {
  450. parent->moveChild (endIndex, startIndex, nullptr);
  451. return true;
  452. }
  453. int getSizeInUnits() override
  454. {
  455. return (int) sizeof (*this); //xxx should be more accurate
  456. }
  457. UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
  458. {
  459. if (auto* next = dynamic_cast<MoveChildAction*> (nextAction))
  460. if (next->parent == parent && next->startIndex == endIndex)
  461. return new MoveChildAction (parent, startIndex, next->endIndex);
  462. return nullptr;
  463. }
  464. private:
  465. const Ptr parent;
  466. const int startIndex, endIndex;
  467. JUCE_DECLARE_NON_COPYABLE (MoveChildAction)
  468. };
  469. //==============================================================================
  470. const Identifier type;
  471. NamedValueSet properties;
  472. ReferenceCountedArray<SharedObject> children;
  473. SortedSet<ValueTree*> valueTreesWithListeners;
  474. SharedObject* parent = nullptr;
  475. JUCE_LEAK_DETECTOR (SharedObject)
  476. };
  477. //==============================================================================
  478. ValueTree::ValueTree() noexcept
  479. {
  480. }
  481. ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type))
  482. {
  483. jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name!
  484. }
  485. ValueTree::ValueTree (const Identifier& type,
  486. std::initializer_list<NamedValueSet::NamedValue> properties,
  487. std::initializer_list<ValueTree> subTrees)
  488. : ValueTree (type)
  489. {
  490. object->properties = NamedValueSet (std::move (properties));
  491. for (auto& tree : subTrees)
  492. addChild (tree, -1, nullptr);
  493. }
  494. ValueTree::ValueTree (SharedObject::Ptr so) noexcept : object (std::move (so)) {}
  495. ValueTree::ValueTree (SharedObject& so) noexcept : object (so) {}
  496. ValueTree::ValueTree (const ValueTree& other) noexcept : object (other.object)
  497. {
  498. }
  499. ValueTree& ValueTree::operator= (const ValueTree& other)
  500. {
  501. if (object != other.object)
  502. {
  503. if (listeners.isEmpty())
  504. {
  505. object = other.object;
  506. }
  507. else
  508. {
  509. if (object != nullptr)
  510. object->valueTreesWithListeners.removeValue (this);
  511. if (other.object != nullptr)
  512. other.object->valueTreesWithListeners.add (this);
  513. object = other.object;
  514. listeners.call ([this] (Listener& l) { l.valueTreeRedirected (*this); });
  515. }
  516. }
  517. return *this;
  518. }
  519. ValueTree::ValueTree (ValueTree&& other) noexcept
  520. : object (std::move (other.object))
  521. {
  522. if (object != nullptr)
  523. object->valueTreesWithListeners.removeValue (&other);
  524. }
  525. ValueTree::~ValueTree()
  526. {
  527. if (! listeners.isEmpty() && object != nullptr)
  528. object->valueTreesWithListeners.removeValue (this);
  529. }
  530. bool ValueTree::operator== (const ValueTree& other) const noexcept
  531. {
  532. return object == other.object;
  533. }
  534. bool ValueTree::operator!= (const ValueTree& other) const noexcept
  535. {
  536. return object != other.object;
  537. }
  538. bool ValueTree::isEquivalentTo (const ValueTree& other) const
  539. {
  540. return object == other.object
  541. || (object != nullptr && other.object != nullptr
  542. && object->isEquivalentTo (*other.object));
  543. }
  544. ValueTree ValueTree::createCopy() const
  545. {
  546. if (object != nullptr)
  547. return ValueTree (*new SharedObject (*object));
  548. return {};
  549. }
  550. void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager)
  551. {
  552. jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail!
  553. if (source.object == nullptr)
  554. removeAllProperties (undoManager);
  555. else if (object != nullptr)
  556. object->copyPropertiesFrom (*(source.object), undoManager);
  557. }
  558. void ValueTree::copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager)
  559. {
  560. jassert (object != nullptr || source.object == nullptr); // Trying to copy to a null ValueTree will fail!
  561. copyPropertiesFrom (source, undoManager);
  562. removeAllChildren (undoManager);
  563. if (object != nullptr && source.object != nullptr)
  564. for (auto& child : source.object->children)
  565. object->addChild (createCopyIfNotNull (child), -1, undoManager);
  566. }
  567. bool ValueTree::hasType (const Identifier& typeName) const noexcept
  568. {
  569. return object != nullptr && object->type == typeName;
  570. }
  571. Identifier ValueTree::getType() const noexcept
  572. {
  573. return object != nullptr ? object->type : Identifier();
  574. }
  575. ValueTree ValueTree::getParent() const noexcept
  576. {
  577. if (object != nullptr)
  578. if (auto p = object->parent)
  579. return ValueTree (*p);
  580. return {};
  581. }
  582. ValueTree ValueTree::getRoot() const noexcept
  583. {
  584. if (object != nullptr)
  585. return ValueTree (object->getRoot());
  586. return {};
  587. }
  588. ValueTree ValueTree::getSibling (int delta) const noexcept
  589. {
  590. if (object != nullptr)
  591. if (auto* p = object->parent)
  592. if (auto* c = p->children.getObjectPointer (p->indexOf (*this) + delta))
  593. return ValueTree (*c);
  594. return {};
  595. }
  596. static const var& getNullVarRef() noexcept
  597. {
  598. static var nullVar;
  599. return nullVar;
  600. }
  601. const var& ValueTree::operator[] (const Identifier& name) const noexcept
  602. {
  603. return object == nullptr ? getNullVarRef() : object->properties[name];
  604. }
  605. const var& ValueTree::getProperty (const Identifier& name) const noexcept
  606. {
  607. return object == nullptr ? getNullVarRef() : object->properties[name];
  608. }
  609. var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
  610. {
  611. return object == nullptr ? defaultReturnValue
  612. : object->properties.getWithDefault (name, defaultReturnValue);
  613. }
  614. const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
  615. {
  616. return object == nullptr ? nullptr
  617. : object->properties.getVarPointer (name);
  618. }
  619. ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
  620. {
  621. return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
  622. }
  623. ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name,
  624. const var& newValue, UndoManager* undoManager)
  625. {
  626. jassert (name.toString().isNotEmpty()); // Must have a valid property name!
  627. jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail!
  628. if (object != nullptr)
  629. object->setProperty (name, newValue, undoManager, listenerToExclude);
  630. return *this;
  631. }
  632. bool ValueTree::hasProperty (const Identifier& name) const noexcept
  633. {
  634. return object != nullptr && object->hasProperty (name);
  635. }
  636. void ValueTree::removeProperty (const Identifier& name, UndoManager* undoManager)
  637. {
  638. if (object != nullptr)
  639. object->removeProperty (name, undoManager);
  640. }
  641. void ValueTree::removeAllProperties (UndoManager* undoManager)
  642. {
  643. if (object != nullptr)
  644. object->removeAllProperties (undoManager);
  645. }
  646. int ValueTree::getNumProperties() const noexcept
  647. {
  648. return object == nullptr ? 0 : object->properties.size();
  649. }
  650. Identifier ValueTree::getPropertyName (int index) const noexcept
  651. {
  652. return object == nullptr ? Identifier()
  653. : object->properties.getName (index);
  654. }
  655. int ValueTree::getReferenceCount() const noexcept
  656. {
  657. return object != nullptr ? object->getReferenceCount() : 0;
  658. }
  659. //==============================================================================
  660. struct ValueTreePropertyValueSource : public Value::ValueSource,
  661. private ValueTree::Listener
  662. {
  663. ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um, bool sync)
  664. : tree (vt), property (prop), undoManager (um), updateSynchronously (sync)
  665. {
  666. tree.addListener (this);
  667. }
  668. ~ValueTreePropertyValueSource() override
  669. {
  670. tree.removeListener (this);
  671. }
  672. var getValue() const override { return tree[property]; }
  673. void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); }
  674. private:
  675. ValueTree tree;
  676. const Identifier property;
  677. UndoManager* const undoManager;
  678. const bool updateSynchronously;
  679. void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
  680. {
  681. if (tree == changedTree && property == changedProperty)
  682. sendChangeMessage (updateSynchronously);
  683. }
  684. void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
  685. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
  686. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  687. void valueTreeParentChanged (ValueTree&) override {}
  688. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
  689. };
  690. Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* undoManager, bool updateSynchronously)
  691. {
  692. return Value (new ValueTreePropertyValueSource (*this, name, undoManager, updateSynchronously));
  693. }
  694. //==============================================================================
  695. int ValueTree::getNumChildren() const noexcept
  696. {
  697. return object == nullptr ? 0 : object->children.size();
  698. }
  699. ValueTree ValueTree::getChild (int index) const
  700. {
  701. if (object != nullptr)
  702. if (auto* c = object->children.getObjectPointer (index))
  703. return ValueTree (*c);
  704. return {};
  705. }
  706. ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd)
  707. : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
  708. {
  709. }
  710. ValueTree::Iterator& ValueTree::Iterator::operator++()
  711. {
  712. internal = static_cast<SharedObject**> (internal) + 1;
  713. return *this;
  714. }
  715. bool ValueTree::Iterator::operator== (const Iterator& other) const { return internal == other.internal; }
  716. bool ValueTree::Iterator::operator!= (const Iterator& other) const { return internal != other.internal; }
  717. ValueTree ValueTree::Iterator::operator*() const
  718. {
  719. return ValueTree (SharedObject::Ptr (*static_cast<SharedObject**> (internal)));
  720. }
  721. ValueTree::Iterator ValueTree::begin() const noexcept { return Iterator (*this, false); }
  722. ValueTree::Iterator ValueTree::end() const noexcept { return Iterator (*this, true); }
  723. ValueTree ValueTree::getChildWithName (const Identifier& type) const
  724. {
  725. return object != nullptr ? object->getChildWithName (type) : ValueTree();
  726. }
  727. ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
  728. {
  729. return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
  730. }
  731. ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
  732. {
  733. return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
  734. }
  735. bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
  736. {
  737. return object != nullptr && object->isAChildOf (possibleParent.object.get());
  738. }
  739. int ValueTree::indexOf (const ValueTree& child) const noexcept
  740. {
  741. return object != nullptr ? object->indexOf (child) : -1;
  742. }
  743. void ValueTree::addChild (const ValueTree& child, int index, UndoManager* undoManager)
  744. {
  745. jassert (object != nullptr); // Trying to add a child to a null ValueTree!
  746. if (object != nullptr)
  747. object->addChild (child.object.get(), index, undoManager);
  748. }
  749. void ValueTree::appendChild (const ValueTree& child, UndoManager* undoManager)
  750. {
  751. addChild (child, -1, undoManager);
  752. }
  753. void ValueTree::removeChild (int childIndex, UndoManager* undoManager)
  754. {
  755. if (object != nullptr)
  756. object->removeChild (childIndex, undoManager);
  757. }
  758. void ValueTree::removeChild (const ValueTree& child, UndoManager* undoManager)
  759. {
  760. if (object != nullptr)
  761. object->removeChild (object->children.indexOf (child.object), undoManager);
  762. }
  763. void ValueTree::removeAllChildren (UndoManager* undoManager)
  764. {
  765. if (object != nullptr)
  766. object->removeAllChildren (undoManager);
  767. }
  768. void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
  769. {
  770. if (object != nullptr)
  771. object->moveChild (currentIndex, newIndex, undoManager);
  772. }
  773. //==============================================================================
  774. void ValueTree::createListOfChildren (OwnedArray<ValueTree>& list) const
  775. {
  776. if (object != nullptr)
  777. for (auto* o : object->children)
  778. if (o != nullptr)
  779. list.add (new ValueTree (*o));
  780. }
  781. void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
  782. {
  783. if (object != nullptr)
  784. object->reorderChildren (newOrder, undoManager);
  785. }
  786. //==============================================================================
  787. void ValueTree::addListener (Listener* listener)
  788. {
  789. if (listener != nullptr)
  790. {
  791. if (listeners.isEmpty() && object != nullptr)
  792. object->valueTreesWithListeners.add (this);
  793. listeners.add (listener);
  794. }
  795. }
  796. void ValueTree::removeListener (Listener* listener)
  797. {
  798. listeners.remove (listener);
  799. if (listeners.isEmpty() && object != nullptr)
  800. object->valueTreesWithListeners.removeValue (this);
  801. }
  802. void ValueTree::sendPropertyChangeMessage (const Identifier& property)
  803. {
  804. if (object != nullptr)
  805. object->sendPropertyChangeMessage (property);
  806. }
  807. //==============================================================================
  808. std::unique_ptr<XmlElement> ValueTree::createXml() const
  809. {
  810. return std::unique_ptr<XmlElement> (object != nullptr ? object->createXml() : nullptr);
  811. }
  812. ValueTree ValueTree::fromXml (const XmlElement& xml)
  813. {
  814. if (! xml.isTextElement())
  815. {
  816. ValueTree v (xml.getTagName());
  817. v.object->properties.setFromXmlAttributes (xml);
  818. for (auto* e : xml.getChildIterator())
  819. v.appendChild (fromXml (*e), nullptr);
  820. return v;
  821. }
  822. // ValueTrees don't have any equivalent to XML text elements!
  823. jassertfalse;
  824. return {};
  825. }
  826. ValueTree ValueTree::fromXml (const String& xmlText)
  827. {
  828. if (auto xml = parseXML (xmlText))
  829. return fromXml (*xml);
  830. return {};
  831. }
  832. String ValueTree::toXmlString (const XmlElement::TextFormat& format) const
  833. {
  834. if (auto xml = createXml())
  835. return xml->toString (format);
  836. return {};
  837. }
  838. //==============================================================================
  839. void ValueTree::writeToStream (OutputStream& output) const
  840. {
  841. SharedObject::writeObjectToStream (output, object.get());
  842. }
  843. ValueTree ValueTree::readFromStream (InputStream& input)
  844. {
  845. auto type = input.readString();
  846. if (type.isEmpty())
  847. return {};
  848. ValueTree v (type);
  849. auto numProps = input.readCompressedInt();
  850. if (numProps < 0)
  851. {
  852. jassertfalse; // trying to read corrupted data!
  853. return v;
  854. }
  855. for (int i = 0; i < numProps; ++i)
  856. {
  857. auto name = input.readString();
  858. if (name.isNotEmpty())
  859. v.object->properties.set (name, var::readFromStream (input));
  860. else
  861. jassertfalse; // trying to read corrupted data!
  862. }
  863. auto numChildren = input.readCompressedInt();
  864. v.object->children.ensureStorageAllocated (numChildren);
  865. for (int i = 0; i < numChildren; ++i)
  866. {
  867. auto child = readFromStream (input);
  868. if (! child.isValid())
  869. return v;
  870. v.object->children.add (child.object);
  871. child.object->parent = v.object.get();
  872. }
  873. return v;
  874. }
  875. ValueTree ValueTree::readFromData (const void* data, size_t numBytes)
  876. {
  877. MemoryInputStream in (data, numBytes, false);
  878. return readFromStream (in);
  879. }
  880. ValueTree ValueTree::readFromGZIPData (const void* data, size_t numBytes)
  881. {
  882. MemoryInputStream in (data, numBytes, false);
  883. GZIPDecompressorInputStream gzipStream (in);
  884. return readFromStream (gzipStream);
  885. }
  886. void ValueTree::Listener::valueTreePropertyChanged (ValueTree&, const Identifier&) {}
  887. void ValueTree::Listener::valueTreeChildAdded (ValueTree&, ValueTree&) {}
  888. void ValueTree::Listener::valueTreeChildRemoved (ValueTree&, ValueTree&, int) {}
  889. void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) {}
  890. void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {}
  891. void ValueTree::Listener::valueTreeRedirected (ValueTree&) {}
  892. //==============================================================================
  893. #if JUCE_ALLOW_STATIC_NULL_VARIABLES
  894. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  895. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
  896. const ValueTree ValueTree::invalid;
  897. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  898. JUCE_END_IGNORE_WARNINGS_MSVC
  899. #endif
  900. //==============================================================================
  901. //==============================================================================
  902. #if JUCE_UNIT_TESTS
  903. class ValueTreeTests : public UnitTest
  904. {
  905. public:
  906. ValueTreeTests()
  907. : UnitTest ("ValueTrees", UnitTestCategories::values)
  908. {}
  909. static String createRandomIdentifier (Random& r)
  910. {
  911. char buffer[50] = { 0 };
  912. const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
  913. for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
  914. buffer[i] = chars[r.nextInt (sizeof (chars) - 1)];
  915. String result (buffer);
  916. if (! XmlElement::isValidXmlName (result))
  917. result = createRandomIdentifier (r);
  918. return result;
  919. }
  920. static String createRandomWideCharString (Random& r)
  921. {
  922. juce_wchar buffer[50] = { 0 };
  923. for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
  924. {
  925. if (r.nextBool())
  926. {
  927. do
  928. {
  929. buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
  930. }
  931. while (! CharPointer_UTF16::canRepresent (buffer[i]));
  932. }
  933. else
  934. buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
  935. }
  936. return CharPointer_UTF32 (buffer);
  937. }
  938. static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r)
  939. {
  940. ValueTree v (createRandomIdentifier (r));
  941. for (int i = r.nextInt (10); --i >= 0;)
  942. {
  943. switch (r.nextInt (5))
  944. {
  945. case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break;
  946. case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break;
  947. case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break;
  948. case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break;
  949. case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break;
  950. default: break;
  951. }
  952. }
  953. return v;
  954. }
  955. void runTest() override
  956. {
  957. {
  958. beginTest ("ValueTree");
  959. auto r = getRandom();
  960. for (int i = 10; --i >= 0;)
  961. {
  962. MemoryOutputStream mo;
  963. auto v1 = createRandomTree (nullptr, 0, r);
  964. v1.writeToStream (mo);
  965. MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
  966. auto v2 = ValueTree::readFromStream (mi);
  967. expect (v1.isEquivalentTo (v2));
  968. MemoryOutputStream zipped;
  969. {
  970. GZIPCompressorOutputStream zippedOut (zipped);
  971. v1.writeToStream (zippedOut);
  972. }
  973. expect (v1.isEquivalentTo (ValueTree::readFromGZIPData (zipped.getData(), zipped.getDataSize())));
  974. auto xml1 = v1.createXml();
  975. auto xml2 = v2.createCopy().createXml();
  976. expect (xml1->isEquivalentTo (xml2.get(), false));
  977. auto v4 = v2.createCopy();
  978. expect (v1.isEquivalentTo (v4));
  979. }
  980. }
  981. {
  982. beginTest ("Float formatting");
  983. ValueTree testVT ("Test");
  984. Identifier number ("number");
  985. std::map<double, String> tests;
  986. tests[1] = "1.0";
  987. tests[1.1] = "1.1";
  988. tests[1.01] = "1.01";
  989. tests[0.76378] = "0.76378";
  990. tests[-10] = "-10.0";
  991. tests[10.01] = "10.01";
  992. tests[0.0123] = "0.0123";
  993. tests[-3.7e-27] = "-3.7e-27";
  994. tests[1e+40] = "1.0e40";
  995. tests[-12345678901234567.0] = "-1.234567890123457e16";
  996. tests[192000] = "192000.0";
  997. tests[1234567] = "1.234567e6";
  998. tests[0.00006] = "0.00006";
  999. tests[0.000006] = "6.0e-6";
  1000. for (auto& test : tests)
  1001. {
  1002. testVT.setProperty (number, test.first, nullptr);
  1003. auto lines = StringArray::fromLines (testVT.toXmlString());
  1004. lines.removeEmptyStrings();
  1005. auto numLines = lines.size();
  1006. expect (numLines > 1);
  1007. expectEquals (lines[numLines - 1], "<Test number=\"" + test.second + "\"/>");
  1008. }
  1009. }
  1010. }
  1011. };
  1012. static ValueTreeTests valueTreeTests;
  1013. #endif
  1014. } // namespace juce