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