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.

1238 lines
39KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - 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 this technical preview, this file is not subject to commercial licensing.
  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. JUCE_DECLARE_DEPRECATED_STATIC (const ValueTree ValueTree::invalid;)
  482. ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type))
  483. {
  484. jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name!
  485. }
  486. ValueTree::ValueTree (const Identifier& type,
  487. std::initializer_list<NamedValueSet::NamedValue> properties,
  488. std::initializer_list<ValueTree> subTrees)
  489. : ValueTree (type)
  490. {
  491. object->properties = NamedValueSet (std::move (properties));
  492. for (auto& tree : subTrees)
  493. addChild (tree, -1, nullptr);
  494. }
  495. ValueTree::ValueTree (SharedObject::Ptr so) noexcept : object (std::move (so)) {}
  496. ValueTree::ValueTree (SharedObject& so) noexcept : object (so) {}
  497. ValueTree::ValueTree (const ValueTree& other) noexcept : object (other.object)
  498. {
  499. }
  500. ValueTree& ValueTree::operator= (const ValueTree& other)
  501. {
  502. if (object != other.object)
  503. {
  504. if (listeners.isEmpty())
  505. {
  506. object = other.object;
  507. }
  508. else
  509. {
  510. if (object != nullptr)
  511. object->valueTreesWithListeners.removeValue (this);
  512. if (other.object != nullptr)
  513. other.object->valueTreesWithListeners.add (this);
  514. object = other.object;
  515. listeners.call ([this] (Listener& l) { l.valueTreeRedirected (*this); });
  516. }
  517. }
  518. return *this;
  519. }
  520. ValueTree::ValueTree (ValueTree&& other) noexcept
  521. : object (std::move (other.object))
  522. {
  523. if (object != nullptr)
  524. object->valueTreesWithListeners.removeValue (&other);
  525. }
  526. ValueTree::~ValueTree()
  527. {
  528. if (! listeners.isEmpty() && object != nullptr)
  529. object->valueTreesWithListeners.removeValue (this);
  530. }
  531. bool ValueTree::operator== (const ValueTree& other) const noexcept
  532. {
  533. return object == other.object;
  534. }
  535. bool ValueTree::operator!= (const ValueTree& other) const noexcept
  536. {
  537. return object != other.object;
  538. }
  539. bool ValueTree::isEquivalentTo (const ValueTree& other) const
  540. {
  541. return object == other.object
  542. || (object != nullptr && other.object != nullptr
  543. && object->isEquivalentTo (*other.object));
  544. }
  545. ValueTree ValueTree::createCopy() const
  546. {
  547. if (object != nullptr)
  548. return ValueTree (*new SharedObject (*object));
  549. return {};
  550. }
  551. void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager)
  552. {
  553. jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail!
  554. if (source.object == nullptr)
  555. removeAllProperties (undoManager);
  556. else if (object != nullptr)
  557. object->copyPropertiesFrom (*(source.object), undoManager);
  558. }
  559. void ValueTree::copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager)
  560. {
  561. jassert (object != nullptr || source.object == nullptr); // Trying to copy to a null ValueTree will fail!
  562. copyPropertiesFrom (source, undoManager);
  563. removeAllChildren (undoManager);
  564. if (object != nullptr && source.object != nullptr)
  565. for (auto& child : source.object->children)
  566. object->addChild (createCopyIfNotNull (child), -1, undoManager);
  567. }
  568. bool ValueTree::hasType (const Identifier& typeName) const noexcept
  569. {
  570. return object != nullptr && object->type == typeName;
  571. }
  572. Identifier ValueTree::getType() const noexcept
  573. {
  574. return object != nullptr ? object->type : Identifier();
  575. }
  576. ValueTree ValueTree::getParent() const noexcept
  577. {
  578. if (object != nullptr)
  579. if (auto p = object->parent)
  580. return ValueTree (*p);
  581. return {};
  582. }
  583. ValueTree ValueTree::getRoot() const noexcept
  584. {
  585. if (object != nullptr)
  586. return ValueTree (object->getRoot());
  587. return {};
  588. }
  589. ValueTree ValueTree::getSibling (int delta) const noexcept
  590. {
  591. if (object != nullptr)
  592. if (auto* p = object->parent)
  593. if (auto* c = p->children.getObjectPointer (p->indexOf (*this) + delta))
  594. return ValueTree (*c);
  595. return {};
  596. }
  597. static const var& getNullVarRef() noexcept
  598. {
  599. static var nullVar;
  600. return nullVar;
  601. }
  602. const var& ValueTree::operator[] (const Identifier& name) const noexcept
  603. {
  604. return object == nullptr ? getNullVarRef() : object->properties[name];
  605. }
  606. const var& ValueTree::getProperty (const Identifier& name) const noexcept
  607. {
  608. return object == nullptr ? getNullVarRef() : object->properties[name];
  609. }
  610. var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
  611. {
  612. return object == nullptr ? defaultReturnValue
  613. : object->properties.getWithDefault (name, defaultReturnValue);
  614. }
  615. const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
  616. {
  617. return object == nullptr ? nullptr
  618. : object->properties.getVarPointer (name);
  619. }
  620. ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
  621. {
  622. return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
  623. }
  624. ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name,
  625. const var& newValue, UndoManager* undoManager)
  626. {
  627. jassert (name.toString().isNotEmpty()); // Must have a valid property name!
  628. jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail!
  629. if (object != nullptr)
  630. object->setProperty (name, newValue, undoManager, listenerToExclude);
  631. return *this;
  632. }
  633. bool ValueTree::hasProperty (const Identifier& name) const noexcept
  634. {
  635. return object != nullptr && object->hasProperty (name);
  636. }
  637. void ValueTree::removeProperty (const Identifier& name, UndoManager* undoManager)
  638. {
  639. if (object != nullptr)
  640. object->removeProperty (name, undoManager);
  641. }
  642. void ValueTree::removeAllProperties (UndoManager* undoManager)
  643. {
  644. if (object != nullptr)
  645. object->removeAllProperties (undoManager);
  646. }
  647. int ValueTree::getNumProperties() const noexcept
  648. {
  649. return object == nullptr ? 0 : object->properties.size();
  650. }
  651. Identifier ValueTree::getPropertyName (int index) const noexcept
  652. {
  653. return object == nullptr ? Identifier()
  654. : object->properties.getName (index);
  655. }
  656. int ValueTree::getReferenceCount() const noexcept
  657. {
  658. return object != nullptr ? object->getReferenceCount() : 0;
  659. }
  660. //==============================================================================
  661. struct ValueTreePropertyValueSource : public Value::ValueSource,
  662. private ValueTree::Listener
  663. {
  664. ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um, bool sync)
  665. : tree (vt), property (prop), undoManager (um), updateSynchronously (sync)
  666. {
  667. tree.addListener (this);
  668. }
  669. ~ValueTreePropertyValueSource() override
  670. {
  671. tree.removeListener (this);
  672. }
  673. var getValue() const override { return tree[property]; }
  674. void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); }
  675. private:
  676. ValueTree tree;
  677. const Identifier property;
  678. UndoManager* const undoManager;
  679. const bool updateSynchronously;
  680. void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
  681. {
  682. if (tree == changedTree && property == changedProperty)
  683. sendChangeMessage (updateSynchronously);
  684. }
  685. void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
  686. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
  687. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  688. void valueTreeParentChanged (ValueTree&) override {}
  689. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
  690. };
  691. Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* undoManager, bool updateSynchronously)
  692. {
  693. return Value (new ValueTreePropertyValueSource (*this, name, undoManager, updateSynchronously));
  694. }
  695. //==============================================================================
  696. int ValueTree::getNumChildren() const noexcept
  697. {
  698. return object == nullptr ? 0 : object->children.size();
  699. }
  700. ValueTree ValueTree::getChild (int index) const
  701. {
  702. if (object != nullptr)
  703. if (auto* c = object->children.getObjectPointer (index))
  704. return ValueTree (*c);
  705. return {};
  706. }
  707. ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd)
  708. : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
  709. {
  710. }
  711. ValueTree::Iterator& ValueTree::Iterator::operator++()
  712. {
  713. internal = static_cast<SharedObject**> (internal) + 1;
  714. return *this;
  715. }
  716. bool ValueTree::Iterator::operator== (const Iterator& other) const { return internal == other.internal; }
  717. bool ValueTree::Iterator::operator!= (const Iterator& other) const { return internal != other.internal; }
  718. ValueTree ValueTree::Iterator::operator*() const
  719. {
  720. return ValueTree (SharedObject::Ptr (*static_cast<SharedObject**> (internal)));
  721. }
  722. ValueTree::Iterator ValueTree::begin() const noexcept { return Iterator (*this, false); }
  723. ValueTree::Iterator ValueTree::end() const noexcept { return Iterator (*this, true); }
  724. ValueTree ValueTree::getChildWithName (const Identifier& type) const
  725. {
  726. return object != nullptr ? object->getChildWithName (type) : ValueTree();
  727. }
  728. ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
  729. {
  730. return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
  731. }
  732. ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
  733. {
  734. return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
  735. }
  736. bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
  737. {
  738. return object != nullptr && object->isAChildOf (possibleParent.object.get());
  739. }
  740. int ValueTree::indexOf (const ValueTree& child) const noexcept
  741. {
  742. return object != nullptr ? object->indexOf (child) : -1;
  743. }
  744. void ValueTree::addChild (const ValueTree& child, int index, UndoManager* undoManager)
  745. {
  746. jassert (object != nullptr); // Trying to add a child to a null ValueTree!
  747. if (object != nullptr)
  748. object->addChild (child.object.get(), index, undoManager);
  749. }
  750. void ValueTree::appendChild (const ValueTree& child, UndoManager* undoManager)
  751. {
  752. addChild (child, -1, undoManager);
  753. }
  754. void ValueTree::removeChild (int childIndex, UndoManager* undoManager)
  755. {
  756. if (object != nullptr)
  757. object->removeChild (childIndex, undoManager);
  758. }
  759. void ValueTree::removeChild (const ValueTree& child, UndoManager* undoManager)
  760. {
  761. if (object != nullptr)
  762. object->removeChild (object->children.indexOf (child.object), undoManager);
  763. }
  764. void ValueTree::removeAllChildren (UndoManager* undoManager)
  765. {
  766. if (object != nullptr)
  767. object->removeAllChildren (undoManager);
  768. }
  769. void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
  770. {
  771. if (object != nullptr)
  772. object->moveChild (currentIndex, newIndex, undoManager);
  773. }
  774. //==============================================================================
  775. void ValueTree::createListOfChildren (OwnedArray<ValueTree>& list) const
  776. {
  777. jassert (object != nullptr);
  778. for (auto* o : object->children)
  779. {
  780. jassert (o != nullptr);
  781. list.add (new ValueTree (*o));
  782. }
  783. }
  784. void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
  785. {
  786. jassert (object != nullptr);
  787. object->reorderChildren (newOrder, undoManager);
  788. }
  789. //==============================================================================
  790. void ValueTree::addListener (Listener* listener)
  791. {
  792. if (listener != nullptr)
  793. {
  794. if (listeners.isEmpty() && object != nullptr)
  795. object->valueTreesWithListeners.add (this);
  796. listeners.add (listener);
  797. }
  798. }
  799. void ValueTree::removeListener (Listener* listener)
  800. {
  801. listeners.remove (listener);
  802. if (listeners.isEmpty() && object != nullptr)
  803. object->valueTreesWithListeners.removeValue (this);
  804. }
  805. void ValueTree::sendPropertyChangeMessage (const Identifier& property)
  806. {
  807. if (object != nullptr)
  808. object->sendPropertyChangeMessage (property);
  809. }
  810. //==============================================================================
  811. std::unique_ptr<XmlElement> ValueTree::createXml() const
  812. {
  813. return std::unique_ptr<XmlElement> (object != nullptr ? object->createXml() : nullptr);
  814. }
  815. ValueTree ValueTree::fromXml (const XmlElement& xml)
  816. {
  817. if (! xml.isTextElement())
  818. {
  819. ValueTree v (xml.getTagName());
  820. v.object->properties.setFromXmlAttributes (xml);
  821. forEachXmlChildElement (xml, e)
  822. v.appendChild (fromXml (*e), nullptr);
  823. return v;
  824. }
  825. // ValueTrees don't have any equivalent to XML text elements!
  826. jassertfalse;
  827. return {};
  828. }
  829. ValueTree ValueTree::fromXml (const String& xmlText)
  830. {
  831. if (auto xml = parseXML (xmlText))
  832. return fromXml (*xml);
  833. return {};
  834. }
  835. String ValueTree::toXmlString (const XmlElement::TextFormat& format) const
  836. {
  837. if (auto xml = createXml())
  838. return xml->toString (format);
  839. return {};
  840. }
  841. //==============================================================================
  842. void ValueTree::writeToStream (OutputStream& output) const
  843. {
  844. SharedObject::writeObjectToStream (output, object.get());
  845. }
  846. ValueTree ValueTree::readFromStream (InputStream& input)
  847. {
  848. auto type = input.readString();
  849. if (type.isEmpty())
  850. return {};
  851. ValueTree v (type);
  852. auto numProps = input.readCompressedInt();
  853. if (numProps < 0)
  854. {
  855. jassertfalse; // trying to read corrupted data!
  856. return v;
  857. }
  858. for (int i = 0; i < numProps; ++i)
  859. {
  860. auto name = input.readString();
  861. if (name.isNotEmpty())
  862. v.object->properties.set (name, var::readFromStream (input));
  863. else
  864. jassertfalse; // trying to read corrupted data!
  865. }
  866. auto numChildren = input.readCompressedInt();
  867. v.object->children.ensureStorageAllocated (numChildren);
  868. for (int i = 0; i < numChildren; ++i)
  869. {
  870. auto child = readFromStream (input);
  871. if (! child.isValid())
  872. return v;
  873. v.object->children.add (child.object);
  874. child.object->parent = v.object.get();
  875. }
  876. return v;
  877. }
  878. ValueTree ValueTree::readFromData (const void* data, size_t numBytes)
  879. {
  880. MemoryInputStream in (data, numBytes, false);
  881. return readFromStream (in);
  882. }
  883. ValueTree ValueTree::readFromGZIPData (const void* data, size_t numBytes)
  884. {
  885. MemoryInputStream in (data, numBytes, false);
  886. GZIPDecompressorInputStream gzipStream (in);
  887. return readFromStream (gzipStream);
  888. }
  889. void ValueTree::Listener::valueTreePropertyChanged (ValueTree&, const Identifier&) {}
  890. void ValueTree::Listener::valueTreeChildAdded (ValueTree&, ValueTree&) {}
  891. void ValueTree::Listener::valueTreeChildRemoved (ValueTree&, ValueTree&, int) {}
  892. void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) {}
  893. void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {}
  894. void ValueTree::Listener::valueTreeRedirected (ValueTree&) {}
  895. //==============================================================================
  896. //==============================================================================
  897. #if JUCE_UNIT_TESTS
  898. class ValueTreeTests : public UnitTest
  899. {
  900. public:
  901. ValueTreeTests()
  902. : UnitTest ("ValueTrees", UnitTestCategories::values)
  903. {}
  904. static String createRandomIdentifier (Random& r)
  905. {
  906. char buffer[50] = { 0 };
  907. const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
  908. for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
  909. buffer[i] = chars[r.nextInt (sizeof (chars) - 1)];
  910. String result (buffer);
  911. if (! XmlElement::isValidXmlName (result))
  912. result = createRandomIdentifier (r);
  913. return result;
  914. }
  915. static String createRandomWideCharString (Random& r)
  916. {
  917. juce_wchar buffer[50] = { 0 };
  918. for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
  919. {
  920. if (r.nextBool())
  921. {
  922. do
  923. {
  924. buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
  925. }
  926. while (! CharPointer_UTF16::canRepresent (buffer[i]));
  927. }
  928. else
  929. buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
  930. }
  931. return CharPointer_UTF32 (buffer);
  932. }
  933. static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r)
  934. {
  935. ValueTree v (createRandomIdentifier (r));
  936. for (int i = r.nextInt (10); --i >= 0;)
  937. {
  938. switch (r.nextInt (5))
  939. {
  940. case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break;
  941. case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break;
  942. case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break;
  943. case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break;
  944. case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break;
  945. default: break;
  946. }
  947. }
  948. return v;
  949. }
  950. void runTest() override
  951. {
  952. {
  953. beginTest ("ValueTree");
  954. auto r = getRandom();
  955. for (int i = 10; --i >= 0;)
  956. {
  957. MemoryOutputStream mo;
  958. auto v1 = createRandomTree (nullptr, 0, r);
  959. v1.writeToStream (mo);
  960. MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
  961. auto v2 = ValueTree::readFromStream (mi);
  962. expect (v1.isEquivalentTo (v2));
  963. MemoryOutputStream zipped;
  964. {
  965. GZIPCompressorOutputStream zippedOut (zipped);
  966. v1.writeToStream (zippedOut);
  967. }
  968. expect (v1.isEquivalentTo (ValueTree::readFromGZIPData (zipped.getData(), zipped.getDataSize())));
  969. auto xml1 = v1.createXml();
  970. auto xml2 = v2.createCopy().createXml();
  971. expect (xml1->isEquivalentTo (xml2.get(), false));
  972. auto v4 = v2.createCopy();
  973. expect (v1.isEquivalentTo (v4));
  974. }
  975. }
  976. {
  977. beginTest ("Float formatting");
  978. ValueTree testVT ("Test");
  979. Identifier number ("number");
  980. std::map<double, String> tests;
  981. tests[1] = "1.0";
  982. tests[1.1] = "1.1";
  983. tests[1.01] = "1.01";
  984. tests[0.76378] = "0.76378";
  985. tests[-10] = "-10.0";
  986. tests[10.01] = "10.01";
  987. tests[0.0123] = "0.0123";
  988. tests[-3.7e-27] = "-3.7e-27";
  989. tests[1e+40] = "1.0e40";
  990. tests[-12345678901234567.0] = "-1.234567890123457e16";
  991. tests[192000] = "192000.0";
  992. tests[1234567] = "1.234567e6";
  993. tests[0.00006] = "0.00006";
  994. tests[0.000006] = "6.0e-6";
  995. for (auto& test : tests)
  996. {
  997. testVT.setProperty (number, test.first, nullptr);
  998. auto lines = StringArray::fromLines (testVT.toXmlString());
  999. lines.removeEmptyStrings();
  1000. auto numLines = lines.size();
  1001. expect (numLines > 1);
  1002. expectEquals (lines[numLines - 1], "<Test number=\"" + test.second + "\"/>");
  1003. }
  1004. }
  1005. }
  1006. };
  1007. static ValueTreeTests valueTreeTests;
  1008. #endif
  1009. } // namespace juce