OpenShot Library | OpenShotAudio  0.2.2
juce_ValueTree.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
11  Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12  27th April 2017).
13 
14  End User License Agreement: www.juce.com/juce-5-licence
15  Privacy Policy: www.juce.com/juce-5-privacy-policy
16 
17  Or: You may also use this code under the terms of the GPL v3 (see
18  www.gnu.org/licenses).
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
31 {
32 public:
34 
35  explicit SharedObject (const Identifier& t) noexcept : type (t) {}
36 
37  SharedObject (const SharedObject& other)
38  : ReferenceCountedObject(), type (other.type), properties (other.properties)
39  {
40  for (auto* c : other.children)
41  {
42  auto* child = new SharedObject (*c);
43  child->parent = this;
44  children.add (child);
45  }
46  }
47 
48  SharedObject& operator= (const SharedObject&) = delete;
49 
50  ~SharedObject()
51  {
52  jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting!
53 
54  for (auto i = children.size(); --i >= 0;)
55  {
56  const Ptr c (children.getObjectPointerUnchecked (i));
57  c->parent = nullptr;
58  children.remove (i);
59  c->sendParentChangeMessage();
60  }
61  }
62 
63  SharedObject& getRoot() noexcept
64  {
65  return parent == nullptr ? *this : parent->getRoot();
66  }
67 
68  template <typename Function>
69  void callListeners (ValueTree::Listener* listenerToExclude, Function fn) const
70  {
71  auto numListeners = valueTreesWithListeners.size();
72 
73  if (numListeners == 1)
74  {
75  valueTreesWithListeners.getUnchecked (0)->listeners.callExcluding (listenerToExclude, fn);
76  }
77  else if (numListeners > 0)
78  {
79  auto listenersCopy = valueTreesWithListeners;
80 
81  for (int i = 0; i < numListeners; ++i)
82  {
83  auto* v = listenersCopy.getUnchecked (i);
84 
85  if (i == 0 || valueTreesWithListeners.contains (v))
86  v->listeners.callExcluding (listenerToExclude, fn);
87  }
88  }
89  }
90 
91  template <typename Function>
92  void callListenersForAllParents (ValueTree::Listener* listenerToExclude, Function fn) const
93  {
94  for (auto* t = this; t != nullptr; t = t->parent)
95  t->callListeners (listenerToExclude, fn);
96  }
97 
98  void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
99  {
100  ValueTree tree (*this);
101  callListenersForAllParents (listenerToExclude, [&] (Listener& l) { l.valueTreePropertyChanged (tree, property); });
102  }
103 
104  void sendChildAddedMessage (ValueTree child)
105  {
106  ValueTree tree (*this);
107  callListenersForAllParents (nullptr, [&] (Listener& l) { l.valueTreeChildAdded (tree, child); });
108  }
109 
110  void sendChildRemovedMessage (ValueTree child, int index)
111  {
112  ValueTree tree (*this);
113  callListenersForAllParents (nullptr, [=, &tree, &child] (Listener& l) { l.valueTreeChildRemoved (tree, child, index); });
114  }
115 
116  void sendChildOrderChangedMessage (int oldIndex, int newIndex)
117  {
118  ValueTree tree (*this);
119  callListenersForAllParents (nullptr, [=, &tree] (Listener& l) { l.valueTreeChildOrderChanged (tree, oldIndex, newIndex); });
120  }
121 
122  void sendParentChangeMessage()
123  {
124  ValueTree tree (*this);
125 
126  for (auto j = children.size(); --j >= 0;)
127  if (auto* child = children.getObjectPointer (j))
128  child->sendParentChangeMessage();
129 
130  callListeners (nullptr, [&] (Listener& l) { l.valueTreeParentChanged (tree); });
131  }
132 
133  void setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager,
134  ValueTree::Listener* listenerToExclude = nullptr)
135  {
136  if (undoManager == nullptr)
137  {
138  if (properties.set (name, newValue))
139  sendPropertyChangeMessage (name, listenerToExclude);
140  }
141  else
142  {
143  if (auto* existingValue = properties.getVarPointer (name))
144  {
145  if (*existingValue != newValue)
146  undoManager->perform (new SetPropertyAction (*this, name, newValue, *existingValue,
147  false, false, listenerToExclude));
148  }
149  else
150  {
151  undoManager->perform (new SetPropertyAction (*this, name, newValue, {},
152  true, false, listenerToExclude));
153  }
154  }
155  }
156 
157  bool hasProperty (const Identifier& name) const noexcept
158  {
159  return properties.contains (name);
160  }
161 
162  void removeProperty (const Identifier& name, UndoManager* undoManager)
163  {
164  if (undoManager == nullptr)
165  {
166  if (properties.remove (name))
167  sendPropertyChangeMessage (name);
168  }
169  else
170  {
171  if (properties.contains (name))
172  undoManager->perform (new SetPropertyAction (*this, name, {}, properties[name], false, true));
173  }
174  }
175 
176  void removeAllProperties (UndoManager* undoManager)
177  {
178  if (undoManager == nullptr)
179  {
180  while (properties.size() > 0)
181  {
182  auto name = properties.getName (properties.size() - 1);
183  properties.remove (name);
184  sendPropertyChangeMessage (name);
185  }
186  }
187  else
188  {
189  for (auto i = properties.size(); --i >= 0;)
190  undoManager->perform (new SetPropertyAction (*this, properties.getName (i), {},
191  properties.getValueAt (i), false, true));
192  }
193  }
194 
195  void copyPropertiesFrom (const SharedObject& source, UndoManager* undoManager)
196  {
197  for (auto i = properties.size(); --i >= 0;)
198  if (! source.properties.contains (properties.getName (i)))
199  removeProperty (properties.getName (i), undoManager);
200 
201  for (int i = 0; i < source.properties.size(); ++i)
202  setProperty (source.properties.getName (i), source.properties.getValueAt (i), undoManager);
203  }
204 
205  ValueTree getChildWithName (const Identifier& typeToMatch) const
206  {
207  for (auto* s : children)
208  if (s->type == typeToMatch)
209  return ValueTree (*s);
210 
211  return {};
212  }
213 
214  ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
215  {
216  for (auto* s : children)
217  if (s->type == typeToMatch)
218  return ValueTree (*s);
219 
220  auto newObject = new SharedObject (typeToMatch);
221  addChild (newObject, -1, undoManager);
222  return ValueTree (*newObject);
223  }
224 
225  ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
226  {
227  for (auto* s : children)
228  if (s->properties[propertyName] == propertyValue)
229  return ValueTree (*s);
230 
231  return {};
232  }
233 
234  bool isAChildOf (const SharedObject* possibleParent) const noexcept
235  {
236  for (auto* p = parent; p != nullptr; p = p->parent)
237  if (p == possibleParent)
238  return true;
239 
240  return false;
241  }
242 
243  int indexOf (const ValueTree& child) const noexcept
244  {
245  return children.indexOf (child.object);
246  }
247 
248  void addChild (SharedObject* child, int index, UndoManager* undoManager)
249  {
250  if (child != nullptr && child->parent != this)
251  {
252  if (child != this && ! isAChildOf (child))
253  {
254  // You should always make sure that a child is removed from its previous parent before
255  // adding it somewhere else - otherwise, it's ambiguous as to whether a different
256  // undomanager should be used when removing it from its current parent..
257  jassert (child->parent == nullptr);
258 
259  if (child->parent != nullptr)
260  {
261  jassert (child->parent->children.indexOf (child) >= 0);
262  child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
263  }
264 
265  if (undoManager == nullptr)
266  {
267  children.insert (index, child);
268  child->parent = this;
269  sendChildAddedMessage (ValueTree (*child));
270  child->sendParentChangeMessage();
271  }
272  else
273  {
274  if (! isPositiveAndBelow (index, children.size()))
275  index = children.size();
276 
277  undoManager->perform (new AddOrRemoveChildAction (*this, index, child));
278  }
279  }
280  else
281  {
282  // You're attempting to create a recursive loop! A node
283  // can't be a child of one of its own children!
284  jassertfalse;
285  }
286  }
287  }
288 
289  void removeChild (int childIndex, UndoManager* undoManager)
290  {
291  if (auto child = Ptr (children.getObjectPointer (childIndex)))
292  {
293  if (undoManager == nullptr)
294  {
295  children.remove (childIndex);
296  child->parent = nullptr;
297  sendChildRemovedMessage (ValueTree (child), childIndex);
298  child->sendParentChangeMessage();
299  }
300  else
301  {
302  undoManager->perform (new AddOrRemoveChildAction (*this, childIndex, {}));
303  }
304  }
305  }
306 
307  void removeAllChildren (UndoManager* undoManager)
308  {
309  while (children.size() > 0)
310  removeChild (children.size() - 1, undoManager);
311  }
312 
313  void moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
314  {
315  // The source index must be a valid index!
316  jassert (isPositiveAndBelow (currentIndex, children.size()));
317 
318  if (currentIndex != newIndex
319  && isPositiveAndBelow (currentIndex, children.size()))
320  {
321  if (undoManager == nullptr)
322  {
323  children.move (currentIndex, newIndex);
324  sendChildOrderChangedMessage (currentIndex, newIndex);
325  }
326  else
327  {
328  if (! isPositiveAndBelow (newIndex, children.size()))
329  newIndex = children.size() - 1;
330 
331  undoManager->perform (new MoveChildAction (*this, currentIndex, newIndex));
332  }
333  }
334  }
335 
336  void reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
337  {
338  jassert (newOrder.size() == children.size());
339 
340  for (int i = 0; i < children.size(); ++i)
341  {
342  auto* child = newOrder.getUnchecked (i)->object.get();
343 
344  if (children.getObjectPointerUnchecked (i) != child)
345  {
346  auto oldIndex = children.indexOf (child);
347  jassert (oldIndex >= 0);
348  moveChild (oldIndex, i, undoManager);
349  }
350  }
351  }
352 
353  bool isEquivalentTo (const SharedObject& other) const noexcept
354  {
355  if (type != other.type
356  || properties.size() != other.properties.size()
357  || children.size() != other.children.size()
358  || properties != other.properties)
359  return false;
360 
361  for (int i = 0; i < children.size(); ++i)
362  if (! children.getObjectPointerUnchecked (i)->isEquivalentTo (*other.children.getObjectPointerUnchecked (i)))
363  return false;
364 
365  return true;
366  }
367 
368  XmlElement* createXml() const
369  {
370  auto* xml = new XmlElement (type);
371  properties.copyToXmlAttributes (*xml);
372 
373  // (NB: it's faster to add nodes to XML elements in reverse order)
374  for (auto i = children.size(); --i >= 0;)
375  xml->prependChildElement (children.getObjectPointerUnchecked (i)->createXml());
376 
377  return xml;
378  }
379 
380  void writeToStream (OutputStream& output) const
381  {
382  output.writeString (type.toString());
383  output.writeCompressedInt (properties.size());
384 
385  for (int j = 0; j < properties.size(); ++j)
386  {
387  output.writeString (properties.getName (j).toString());
388  properties.getValueAt (j).writeToStream (output);
389  }
390 
391  output.writeCompressedInt (children.size());
392 
393  for (auto* c : children)
394  writeObjectToStream (output, c);
395  }
396 
397  static void writeObjectToStream (OutputStream& output, const SharedObject* object)
398  {
399  if (object != nullptr)
400  {
401  object->writeToStream (output);
402  }
403  else
404  {
405  output.writeString ({});
406  output.writeCompressedInt (0);
407  output.writeCompressedInt (0);
408  }
409  }
410 
411  //==============================================================================
413  {
414  SetPropertyAction (Ptr targetObject, const Identifier& propertyName,
415  const var& newVal, const var& oldVal, bool isAdding, bool isDeleting,
416  ValueTree::Listener* listenerToExclude = nullptr)
417  : target (std::move (targetObject)),
418  name (propertyName), newValue (newVal), oldValue (oldVal),
419  isAddingNewProperty (isAdding), isDeletingProperty (isDeleting),
420  excludeListener (listenerToExclude)
421  {
422  }
423 
424  bool perform() override
425  {
426  jassert (! (isAddingNewProperty && target->hasProperty (name)));
427 
428  if (isDeletingProperty)
429  target->removeProperty (name, nullptr);
430  else
431  target->setProperty (name, newValue, nullptr, excludeListener);
432 
433  return true;
434  }
435 
436  bool undo() override
437  {
438  if (isAddingNewProperty)
439  target->removeProperty (name, nullptr);
440  else
441  target->setProperty (name, oldValue, nullptr);
442 
443  return true;
444  }
445 
446  int getSizeInUnits() override
447  {
448  return (int) sizeof (*this); //xxx should be more accurate
449  }
450 
452  {
453  if (! (isAddingNewProperty || isDeletingProperty))
454  {
455  if (auto* next = dynamic_cast<SetPropertyAction*> (nextAction))
456  if (next->target == target && next->name == name
457  && ! (next->isAddingNewProperty || next->isDeletingProperty))
458  return new SetPropertyAction (*target, name, next->newValue, oldValue, false, false);
459  }
460 
461  return nullptr;
462  }
463 
464  private:
465  const Ptr target;
466  const Identifier name;
467  const var newValue;
468  var oldValue;
469  const bool isAddingNewProperty : 1, isDeletingProperty : 1;
470  ValueTree::Listener* excludeListener;
471 
472  JUCE_DECLARE_NON_COPYABLE (SetPropertyAction)
473  };
474 
475  //==============================================================================
477  {
478  AddOrRemoveChildAction (Ptr parentObject, int index, SharedObject* newChild)
479  : target (std::move (parentObject)),
480  child (newChild != nullptr ? newChild : target->children.getObjectPointer (index)),
481  childIndex (index),
482  isDeleting (newChild == nullptr)
483  {
484  jassert (child != nullptr);
485  }
486 
487  bool perform() override
488  {
489  if (isDeleting)
490  target->removeChild (childIndex, nullptr);
491  else
492  target->addChild (child.get(), childIndex, nullptr);
493 
494  return true;
495  }
496 
497  bool undo() override
498  {
499  if (isDeleting)
500  {
501  target->addChild (child.get(), childIndex, nullptr);
502  }
503  else
504  {
505  // If you hit this, it seems that your object's state is getting confused - probably
506  // because you've interleaved some undoable and non-undoable operations?
507  jassert (childIndex < target->children.size());
508  target->removeChild (childIndex, nullptr);
509  }
510 
511  return true;
512  }
513 
514  int getSizeInUnits() override
515  {
516  return (int) sizeof (*this); //xxx should be more accurate
517  }
518 
519  private:
520  const Ptr target, child;
521  const int childIndex;
522  const bool isDeleting;
523 
524  JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction)
525  };
526 
527  //==============================================================================
529  {
530  MoveChildAction (Ptr parentObject, int fromIndex, int toIndex) noexcept
531  : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex)
532  {
533  }
534 
535  bool perform() override
536  {
537  parent->moveChild (startIndex, endIndex, nullptr);
538  return true;
539  }
540 
541  bool undo() override
542  {
543  parent->moveChild (endIndex, startIndex, nullptr);
544  return true;
545  }
546 
547  int getSizeInUnits() override
548  {
549  return (int) sizeof (*this); //xxx should be more accurate
550  }
551 
553  {
554  if (auto* next = dynamic_cast<MoveChildAction*> (nextAction))
555  if (next->parent == parent && next->startIndex == endIndex)
556  return new MoveChildAction (parent, startIndex, next->endIndex);
557 
558  return nullptr;
559  }
560 
561  private:
562  const Ptr parent;
563  const int startIndex, endIndex;
564 
565  JUCE_DECLARE_NON_COPYABLE (MoveChildAction)
566  };
567 
568  //==============================================================================
569  const Identifier type;
570  NamedValueSet properties;
572  SortedSet<ValueTree*> valueTreesWithListeners;
573  SharedObject* parent = nullptr;
574 
575  JUCE_LEAK_DETECTOR (SharedObject)
576 };
577 
578 //==============================================================================
580 {
581 }
582 
583 JUCE_DECLARE_DEPRECATED_STATIC (const ValueTree ValueTree::invalid;)
584 
585 ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type))
586 {
587  jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name!
588 }
589 
591  std::initializer_list<NamedValueSet::NamedValue> properties,
592  std::initializer_list<ValueTree> subTrees)
593  : ValueTree (type)
594 {
595  object->properties = NamedValueSet (std::move (properties));
596 
597  for (auto& tree : subTrees)
598  addChild (tree, -1, nullptr);
599 }
600 
601 ValueTree::ValueTree (SharedObject::Ptr so) noexcept : object (std::move (so)) {}
602 ValueTree::ValueTree (SharedObject& so) noexcept : object (so) {}
603 
604 ValueTree::ValueTree (const ValueTree& other) noexcept : object (other.object)
605 {
606 }
607 
609 {
610  if (object != other.object)
611  {
612  if (listeners.isEmpty())
613  {
614  object = other.object;
615  }
616  else
617  {
618  if (object != nullptr)
619  object->valueTreesWithListeners.removeValue (this);
620 
621  if (other.object != nullptr)
622  other.object->valueTreesWithListeners.add (this);
623 
624  object = other.object;
625 
626  listeners.call ([this] (Listener& l) { l.valueTreeRedirected (*this); });
627  }
628  }
629 
630  return *this;
631 }
632 
633 ValueTree::ValueTree (ValueTree&& other) noexcept
634  : object (std::move (other.object))
635 {
636  if (object != nullptr)
637  object->valueTreesWithListeners.removeValue (&other);
638 }
639 
641 {
642  if (! listeners.isEmpty() && object != nullptr)
643  object->valueTreesWithListeners.removeValue (this);
644 }
645 
646 bool ValueTree::operator== (const ValueTree& other) const noexcept
647 {
648  return object == other.object;
649 }
650 
651 bool ValueTree::operator!= (const ValueTree& other) const noexcept
652 {
653  return object != other.object;
654 }
655 
656 bool ValueTree::isEquivalentTo (const ValueTree& other) const
657 {
658  return object == other.object
659  || (object != nullptr && other.object != nullptr
660  && object->isEquivalentTo (*other.object));
661 }
662 
664 {
665  if (object != nullptr)
666  return ValueTree (*new SharedObject (*object));
667 
668  return {};
669 }
670 
671 void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager)
672 {
673  jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail!
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  copyPropertiesFrom (source, undoManager);
686  removeAllChildren (undoManager);
687 
688  if (object != nullptr && source.object != nullptr)
689  for (auto& child : source.object->children)
690  object->addChild (createCopyIfNotNull (child), -1, undoManager);
691 }
692 
693 bool ValueTree::hasType (const Identifier& typeName) const noexcept
694 {
695  return object != nullptr && object->type == typeName;
696 }
697 
699 {
700  return object != nullptr ? object->type : Identifier();
701 }
702 
704 {
705  if (object != nullptr)
706  if (auto p = object->parent)
707  return ValueTree (*p);
708 
709  return {};
710 }
711 
713 {
714  if (object != nullptr)
715  return ValueTree (object->getRoot());
716 
717  return {};
718 }
719 
720 ValueTree ValueTree::getSibling (int delta) const noexcept
721 {
722  if (object != nullptr)
723  if (auto* p = object->parent)
724  if (auto* c = p->children.getObjectPointer (p->indexOf (*this) + delta))
725  return ValueTree (*c);
726 
727  return {};
728 }
729 
730 static const var& getNullVarRef() noexcept
731 {
732  static var nullVar;
733  return nullVar;
734 }
735 
736 const var& ValueTree::operator[] (const Identifier& name) const noexcept
737 {
738  return object == nullptr ? getNullVarRef() : object->properties[name];
739 }
740 
741 const var& ValueTree::getProperty (const Identifier& name) const noexcept
742 {
743  return object == nullptr ? getNullVarRef() : object->properties[name];
744 }
745 
746 var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
747 {
748  return object == nullptr ? defaultReturnValue
749  : object->properties.getWithDefault (name, defaultReturnValue);
750 }
751 
752 const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
753 {
754  return object == nullptr ? nullptr
755  : object->properties.getVarPointer (name);
756 }
757 
758 ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
759 {
760  return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
761 }
762 
764  const var& newValue, UndoManager* undoManager)
765 {
766  jassert (name.toString().isNotEmpty()); // Must have a valid property name!
767  jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail!
768 
769  if (object != nullptr)
770  object->setProperty (name, newValue, undoManager, listenerToExclude);
771 
772  return *this;
773 }
774 
775 bool ValueTree::hasProperty (const Identifier& name) const noexcept
776 {
777  return object != nullptr && object->hasProperty (name);
778 }
779 
780 void ValueTree::removeProperty (const Identifier& name, UndoManager* undoManager)
781 {
782  if (object != nullptr)
783  object->removeProperty (name, undoManager);
784 }
785 
787 {
788  if (object != nullptr)
789  object->removeAllProperties (undoManager);
790 }
791 
792 int ValueTree::getNumProperties() const noexcept
793 {
794  return object == nullptr ? 0 : object->properties.size();
795 }
796 
797 Identifier ValueTree::getPropertyName (int index) const noexcept
798 {
799  return object == nullptr ? Identifier()
800  : object->properties.getName (index);
801 }
802 
803 int ValueTree::getReferenceCount() const noexcept
804 {
805  return object != nullptr ? object->getReferenceCount() : 0;
806 }
807 
808 //==============================================================================
810  private ValueTree::Listener
811 {
812  ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um, bool sync)
813  : tree (vt), property (prop), undoManager (um), updateSynchronously (sync)
814  {
815  tree.addListener (this);
816  }
817 
818  ~ValueTreePropertyValueSource() override
819  {
820  tree.removeListener (this);
821  }
822 
823  var getValue() const override { return tree[property]; }
824  void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); }
825 
826 private:
827  ValueTree tree;
828  const Identifier property;
829  UndoManager* const undoManager;
830  const bool updateSynchronously;
831 
832  void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
833  {
834  if (tree == changedTree && property == changedProperty)
835  sendChangeMessage (updateSynchronously);
836  }
837 
838  void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
839  void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
840  void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
841  void valueTreeParentChanged (ValueTree&) override {}
842 
843  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
844 };
845 
846 Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* undoManager, bool updateSynchronously)
847 {
848  return Value (new ValueTreePropertyValueSource (*this, name, undoManager, updateSynchronously));
849 }
850 
851 //==============================================================================
852 int ValueTree::getNumChildren() const noexcept
853 {
854  return object == nullptr ? 0 : object->children.size();
855 }
856 
858 {
859  if (object != nullptr)
860  if (auto* c = object->children.getObjectPointer (index))
861  return ValueTree (*c);
862 
863  return {};
864 }
865 
866 ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd)
867  : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
868 {
869 }
870 
871 ValueTree::Iterator& ValueTree::Iterator::operator++()
872 {
873  internal = static_cast<SharedObject**> (internal) + 1;
874  return *this;
875 }
876 
877 bool ValueTree::Iterator::operator== (const Iterator& other) const { return internal == other.internal; }
878 bool ValueTree::Iterator::operator!= (const Iterator& other) const { return internal != other.internal; }
879 
880 ValueTree ValueTree::Iterator::operator*() const
881 {
882  return ValueTree (SharedObject::Ptr (*static_cast<SharedObject**> (internal)));
883 }
884 
885 ValueTree::Iterator ValueTree::begin() const noexcept { return Iterator (*this, false); }
886 ValueTree::Iterator ValueTree::end() const noexcept { return Iterator (*this, true); }
887 
889 {
890  return object != nullptr ? object->getChildWithName (type) : ValueTree();
891 }
892 
894 {
895  return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
896 }
897 
898 ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
899 {
900  return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
901 }
902 
903 bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
904 {
905  return object != nullptr && object->isAChildOf (possibleParent.object.get());
906 }
907 
908 int ValueTree::indexOf (const ValueTree& child) const noexcept
909 {
910  return object != nullptr ? object->indexOf (child) : -1;
911 }
912 
913 void ValueTree::addChild (const ValueTree& child, int index, UndoManager* undoManager)
914 {
915  jassert (object != nullptr); // Trying to add a child to a null ValueTree!
916 
917  if (object != nullptr)
918  object->addChild (child.object.get(), index, undoManager);
919 }
920 
921 void ValueTree::appendChild (const ValueTree& child, UndoManager* undoManager)
922 {
923  addChild (child, -1, undoManager);
924 }
925 
926 void ValueTree::removeChild (int childIndex, UndoManager* undoManager)
927 {
928  if (object != nullptr)
929  object->removeChild (childIndex, undoManager);
930 }
931 
932 void ValueTree::removeChild (const ValueTree& child, UndoManager* undoManager)
933 {
934  if (object != nullptr)
935  object->removeChild (object->children.indexOf (child.object), undoManager);
936 }
937 
939 {
940  if (object != nullptr)
941  object->removeAllChildren (undoManager);
942 }
943 
944 void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
945 {
946  if (object != nullptr)
947  object->moveChild (currentIndex, newIndex, undoManager);
948 }
949 
950 //==============================================================================
951 void ValueTree::createListOfChildren (OwnedArray<ValueTree>& list) const
952 {
953  jassert (object != nullptr);
954 
955  for (auto* o : object->children)
956  {
957  jassert (o != nullptr);
958  list.add (new ValueTree (*o));
959  }
960 }
961 
962 void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
963 {
964  jassert (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  forEachXmlChildElement (xml, e)
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 //==============================================================================
1106 //==============================================================================
1107 #if JUCE_UNIT_TESTS
1108 
1109 class ValueTreeTests : public UnitTest
1110 {
1111 public:
1112  ValueTreeTests()
1113  : UnitTest ("ValueTrees", UnitTestCategories::values)
1114  {}
1115 
1116  static String createRandomIdentifier (Random& r)
1117  {
1118  char buffer[50] = { 0 };
1119  const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
1120 
1121  for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
1122  buffer[i] = chars[r.nextInt (sizeof (chars) - 1)];
1123 
1124  String result (buffer);
1125 
1126  if (! XmlElement::isValidXmlName (result))
1127  result = createRandomIdentifier (r);
1128 
1129  return result;
1130  }
1131 
1132  static String createRandomWideCharString (Random& r)
1133  {
1134  juce_wchar buffer[50] = { 0 };
1135 
1136  for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
1137  {
1138  if (r.nextBool())
1139  {
1140  do
1141  {
1142  buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
1143  }
1144  while (! CharPointer_UTF16::canRepresent (buffer[i]));
1145  }
1146  else
1147  buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
1148  }
1149 
1150  return CharPointer_UTF32 (buffer);
1151  }
1152 
1153  static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r)
1154  {
1155  ValueTree v (createRandomIdentifier (r));
1156 
1157  for (int i = r.nextInt (10); --i >= 0;)
1158  {
1159  switch (r.nextInt (5))
1160  {
1161  case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break;
1162  case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break;
1163  case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break;
1164  case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break;
1165  case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break;
1166  default: break;
1167  }
1168  }
1169 
1170  return v;
1171  }
1172 
1173  void runTest() override
1174  {
1175  {
1176  beginTest ("ValueTree");
1177 
1178  auto r = getRandom();
1179 
1180  for (int i = 10; --i >= 0;)
1181  {
1182  MemoryOutputStream mo;
1183  auto v1 = createRandomTree (nullptr, 0, r);
1184  v1.writeToStream (mo);
1185 
1186  MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
1187  auto v2 = ValueTree::readFromStream (mi);
1188  expect (v1.isEquivalentTo (v2));
1189 
1190  MemoryOutputStream zipped;
1191  {
1192  GZIPCompressorOutputStream zippedOut (zipped);
1193  v1.writeToStream (zippedOut);
1194  }
1195  expect (v1.isEquivalentTo (ValueTree::readFromGZIPData (zipped.getData(), zipped.getDataSize())));
1196 
1197  auto xml1 = v1.createXml();
1198  auto xml2 = v2.createCopy().createXml();
1199  expect (xml1->isEquivalentTo (xml2.get(), false));
1200 
1201  auto v4 = v2.createCopy();
1202  expect (v1.isEquivalentTo (v4));
1203  }
1204  }
1205 
1206  {
1207  beginTest ("Float formatting");
1208 
1209  ValueTree testVT ("Test");
1210  Identifier number ("number");
1211 
1212  std::map<double, String> tests;
1213  tests[1] = "1.0";
1214  tests[1.1] = "1.1";
1215  tests[1.01] = "1.01";
1216  tests[0.76378] = "0.76378";
1217  tests[-10] = "-10.0";
1218  tests[10.01] = "10.01";
1219  tests[0.0123] = "0.0123";
1220  tests[-3.7e-27] = "-3.7e-27";
1221  tests[1e+40] = "1.0e40";
1222  tests[-12345678901234567.0] = "-1.234567890123457e16";
1223  tests[192000] = "192000.0";
1224  tests[1234567] = "1.234567e6";
1225  tests[0.00006] = "0.00006";
1226  tests[0.000006] = "6.0e-6";
1227 
1228  for (auto& test : tests)
1229  {
1230  testVT.setProperty (number, test.first, nullptr);
1231  auto lines = StringArray::fromLines (testVT.toXmlString());
1232  lines.removeEmptyStrings();
1233  auto numLines = lines.size();
1234  expect (numLines > 1);
1235  expectEquals (lines[numLines - 1], "<Test number=\"" + test.second + "\"/>");
1236  }
1237  }
1238  }
1239 };
1240 
1241 static ValueTreeTests valueTreeTests;
1242 
1243 #endif
1244 
1245 } // namespace juce
static bool canRepresent(juce_wchar character) noexcept
Returns true if the given unicode character can be represented in this encoding.
This stream will decompress a source-stream using zlib.
Represents a string identifier, designed for accessing properties by name.
const String & toString() const noexcept
Returns this identifier as a string.
The base class for streams that read data.
virtual int readCompressedInt()
Reads an encoded 32-bit number from the stream using a space-saving compressed format.
virtual String readString()
Reads a zero-terminated UTF-8 string from the stream.
Allows a block of data to be accessed as a stream.
Holds a set of named var objects.
bool set(const Identifier &name, const var &newValue)
Changes or adds a named value.
bool contains(const Identifier &name) const noexcept
Returns true if the set contains an item with the specified name.
bool remove(const Identifier &name)
Removes a value from the set.
const var & getValueAt(int index) const noexcept
Returns the value of the item at a given index.
Identifier getName(int index) const noexcept
Returns the name of the value at a given index.
int size() const noexcept
Returns the total number of values that the set contains.
var * getVarPointer(const Identifier &name) noexcept
Returns a pointer to the var that holds a named value, or null if there is no value with this name.
void copyToXmlAttributes(XmlElement &xml) const
Sets attributes in an XML element corresponding to each of this object's properties.
The base class for streams that write data to some kind of destination.
virtual bool writeCompressedInt(int value)
Writes a condensed binary encoding of a 32-bit integer.
virtual bool writeString(const String &text)
Stores a string in the stream in a binary format.
An array designed for holding objects.
int size() const noexcept
Returns the number of items currently in the array.
ObjectClass * getUnchecked(int index) const noexcept
Returns a pointer to the object at this index in the array, without checking whether the index is in-...
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
A random number generator.
Definition: juce_Random.h:39
int nextInt() noexcept
Returns the next random 32 bit integer.
Definition: juce_Random.cpp:78
Holds a list of objects derived from ReferenceCountedObject, or which implement basic reference-count...
A smart-pointer class which points to a reference-counted object.
A base class which provides methods for reference-counting.
ReferenceCountedObject()=default
Creates the reference-counted object (with an initial ref count of zero).
Holds a set of unique primitive objects, such as ints or doubles.
static StringArray fromLines(StringRef stringToBreakUp)
Returns an array containing the lines in a given string.
The JUCE String class!
Definition: juce_String.h:43
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
Manages a list of undo/redo commands.
bool perform(UndoableAction *action)
Performs an action and adds it to the undo history list.
Used by the UndoManager class to store an action which can be done and undone.
This is a base class for classes that perform a unit test.
Definition: juce_UnitTest.h:74
Listener class for events that happen to a ValueTree.
virtual void valueTreeRedirected(ValueTree &treeWhichHasBeenChanged)
This method is called when a tree is made to point to a different internal shared object.
virtual void valueTreeChildRemoved(ValueTree &parentTree, ValueTree &childWhichHasBeenRemoved, int indexFromWhichChildWasRemoved)
This method is called when a child sub-tree is removed.
virtual void valueTreeChildOrderChanged(ValueTree &parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex)
This method is called when a tree's children have been re-shuffled.
virtual void valueTreeParentChanged(ValueTree &treeWhoseParentHasChanged)
This method is called when a tree has been added or removed from a parent.
virtual void valueTreePropertyChanged(ValueTree &treeWhosePropertyHasChanged, const Identifier &property)
This method is called when a property of this tree (or of one of its sub-trees) is changed.
virtual void valueTreeChildAdded(ValueTree &parentTree, ValueTree &childWhichHasBeenAdded)
This method is called when a child sub-tree is added.
A powerful tree structure that can be used to hold free-form data, and which can handle its own undo ...
Value getPropertyAsValue(const Identifier &name, UndoManager *undoManager, bool shouldUpdateSynchronously=false)
Returns a Value object that can be used to control and respond to one of the tree's properties.
Identifier getPropertyName(int index) const noexcept
Returns the identifier of the property with a given index.
Iterator begin() const noexcept
Returns a start iterator for the children in this tree.
bool operator!=(const ValueTree &) const noexcept
Returns true if this and the other tree refer to different underlying structures.
std::unique_ptr< XmlElement > createXml() const
Creates an XmlElement that holds a complete image of this tree and all its children.
bool hasType(const Identifier &typeName) const noexcept
Returns true if the tree has this type.
static ValueTree readFromStream(InputStream &input)
Reloads a tree from a stream that was written with writeToStream().
void removeChild(const ValueTree &child, UndoManager *undoManager)
Removes the specified child from this tree's child-list.
String toXmlString(const XmlElement::TextFormat &format={}) const
This returns a string containing an XML representation of the tree.
static ValueTree readFromGZIPData(const void *data, size_t numBytes)
Reloads a tree from a data block that was written with writeToStream() and then zipped using GZIPComp...
ValueTree getChild(int index) const
Returns one of this tree's sub-trees.
int getNumProperties() const noexcept
Returns the total number of properties that the tree contains.
int getNumChildren() const noexcept
Returns the number of child trees inside this one.
void copyPropertiesFrom(const ValueTree &source, UndoManager *undoManager)
Overwrites all the properties in this tree with the properties of the source tree.
~ValueTree()
Destructor.
int getReferenceCount() const noexcept
Returns the total number of references to the shared underlying data structure that this ValueTree is...
const var * getPropertyPointer(const Identifier &name) const noexcept
Returns a pointer to the value of a named property, or nullptr if the property doesn't exist.
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
Changes a named property of the tree.
void removeAllChildren(UndoManager *undoManager)
Removes all child-trees.
bool isAChildOf(const ValueTree &possibleParent) const noexcept
Returns true if this tree is a sub-tree (at any depth) of the given parent.
void removeAllProperties(UndoManager *undoManager)
Removes all properties from the tree.
void addListener(Listener *listener)
Adds a listener to receive callbacks when this tree is changed in some way.
void appendChild(const ValueTree &child, UndoManager *undoManager)
Appends a new child sub-tree to this tree.
int indexOf(const ValueTree &child) const noexcept
Returns the index of a child item in this parent.
bool operator==(const ValueTree &) const noexcept
Returns true if both this and the other tree refer to the same underlying structure.
ValueTree & setPropertyExcludingListener(Listener *listenerToExclude, const Identifier &name, const var &newValue, UndoManager *undoManager)
Changes a named property of the tree, but will not notify a specified listener of the change.
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
Adds a child to this tree.
ValueTree getParent() const noexcept
Returns the parent tree that contains this one.
const var & getProperty(const Identifier &name) const noexcept
Returns the value of a named property.
ValueTree() noexcept
Creates an empty, invalid ValueTree.
static ValueTree fromXml(const XmlElement &xml)
Tries to recreate a tree from its XML representation.
ValueTree createCopy() const
Returns a deep copy of this tree and all its sub-trees.
Identifier getType() const noexcept
Returns the type of this tree.
ValueTree getChildWithName(const Identifier &type) const
Returns the first sub-tree with the specified type name.
Iterator end() const noexcept
Returns an end iterator for the children in this tree.
ValueTree & operator=(const ValueTree &)
Changes this object to be a reference to the given tree.
void removeListener(Listener *listener)
Removes a listener that was previously added with addListener().
void writeToStream(OutputStream &output) const
Stores this tree (and all its children) in a binary format.
bool isEquivalentTo(const ValueTree &) const
Performs a deep comparison between the properties and children of two trees.
ValueTree getOrCreateChildWithName(const Identifier &type, UndoManager *undoManager)
Returns the first sub-tree with the specified type name, creating and adding a child with this name i...
void moveChild(int currentIndex, int newIndex, UndoManager *undoManager)
Moves one of the sub-trees to a different index.
void sendPropertyChangeMessage(const Identifier &property)
Causes a property-change callback to be triggered for the specified property, calling any listeners t...
void removeProperty(const Identifier &name, UndoManager *undoManager)
Removes a property from the tree.
const var & operator[](const Identifier &name) const noexcept
Returns the value of a named property.
ValueTree getSibling(int delta) const noexcept
Returns one of this tree's siblings in its parent's child list.
ValueTree getChildWithProperty(const Identifier &propertyName, const var &propertyValue) const
Looks for the first sub-tree that has the specified property value.
void copyPropertiesAndChildrenFrom(const ValueTree &source, UndoManager *undoManager)
Replaces all children and properties of this object with copies of those from the source object.
ValueTree getRoot() const noexcept
Recursively finds the highest-level parent tree that contains this one.
static ValueTree readFromData(const void *data, size_t numBytes)
Reloads a tree from a data block that was written with writeToStream().
bool hasProperty(const Identifier &name) const noexcept
Returns true if the tree contains a named property.
Used internally by the Value class as the base class for its shared value objects.
Definition: juce_Value.h:184
void sendChangeMessage(bool dispatchSynchronously)
Delivers a change message to all the listeners that are registered with this value.
Definition: juce_Value.cpp:44
Represents a shared variant value.
Definition: juce_Value.h:56
Used to build a tree of elements representing an XML document.
bool isTextElement() const noexcept
Returns true if this element is a section of text.
const String & getTagName() const noexcept
Returns this element's tag type name.
static bool isValidXmlName(StringRef possibleName) noexcept
Checks if a given string is a valid XML name.
A variant class, that can be used to hold a range of primitive values.
Definition: juce_Variant.h:46
void writeToStream(OutputStream &output) const
Writes a binary representation of this value to a stream.
static var readFromStream(InputStream &input)
Reads back a stored binary representation of a value.
var getValue() const override
Returns the current value of this object.
void setValue(const var &newValue) override
Changes the current value.
Iterator for a ValueTree.
int getSizeInUnits() override
Returns a value to indicate how much memory this object takes up.
bool undo() override
Overridden by a subclass to undo the action.
bool perform() override
Overridden by a subclass to perform the action.
bool perform() override
Overridden by a subclass to perform the action.
UndoableAction * createCoalescedAction(UndoableAction *nextAction) override
Allows multiple actions to be coalesced into a single action object, to reduce storage space.
bool undo() override
Overridden by a subclass to undo the action.
int getSizeInUnits() override
Returns a value to indicate how much memory this object takes up.
bool undo() override
Overridden by a subclass to undo the action.
int getSizeInUnits() override
Returns a value to indicate how much memory this object takes up.
UndoableAction * createCoalescedAction(UndoableAction *nextAction) override
Allows multiple actions to be coalesced into a single action object, to reduce storage space.
bool perform() override
Overridden by a subclass to perform the action.
A struct containing options for formatting the text when representing an XML element as a string.