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
30 class ValueTree::SharedObject : public ReferenceCountedObject
31 {
32 public:
33 using Ptr = ReferenceCountedObjectPtr<SharedObject>;
34
SharedObject(const Identifier & t)35 explicit SharedObject (const Identifier& t) noexcept : type (t) {}
36
SharedObject(const SharedObject & other)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
~SharedObject()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
getRoot()63 SharedObject& getRoot() noexcept
64 {
65 return parent == nullptr ? *this : parent->getRoot();
66 }
67
68 template <typename Function>
callListeners(ValueTree::Listener * listenerToExclude,Function fn) const69 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>
callListenersForAllParents(ValueTree::Listener * listenerToExclude,Function fn) const92 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
sendPropertyChangeMessage(const Identifier & property,ValueTree::Listener * listenerToExclude=nullptr)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
sendChildAddedMessage(ValueTree child)104 void sendChildAddedMessage (ValueTree child)
105 {
106 ValueTree tree (*this);
107 callListenersForAllParents (nullptr, [&] (Listener& l) { l.valueTreeChildAdded (tree, child); });
108 }
109
sendChildRemovedMessage(ValueTree child,int index)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
sendChildOrderChangedMessage(int oldIndex,int newIndex)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
sendParentChangeMessage()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
setProperty(const Identifier & name,const var & newValue,UndoManager * undoManager,ValueTree::Listener * listenerToExclude=nullptr)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
hasProperty(const Identifier & name) const157 bool hasProperty (const Identifier& name) const noexcept
158 {
159 return properties.contains (name);
160 }
161
removeProperty(const Identifier & name,UndoManager * undoManager)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
removeAllProperties(UndoManager * undoManager)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
copyPropertiesFrom(const SharedObject & source,UndoManager * undoManager)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
getChildWithName(const Identifier & typeToMatch) const205 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
getOrCreateChildWithName(const Identifier & typeToMatch,UndoManager * undoManager)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
getChildWithProperty(const Identifier & propertyName,const var & propertyValue) const225 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
isAChildOf(const SharedObject * possibleParent) const234 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
indexOf(const ValueTree & child) const243 int indexOf (const ValueTree& child) const noexcept
244 {
245 return children.indexOf (child.object);
246 }
247
addChild(SharedObject * child,int index,UndoManager * undoManager)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
removeChild(int childIndex,UndoManager * undoManager)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
removeAllChildren(UndoManager * undoManager)307 void removeAllChildren (UndoManager* undoManager)
308 {
309 while (children.size() > 0)
310 removeChild (children.size() - 1, undoManager);
311 }
312
moveChild(int currentIndex,int newIndex,UndoManager * undoManager)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
reorderChildren(const OwnedArray<ValueTree> & newOrder,UndoManager * undoManager)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
isEquivalentTo(const SharedObject & other) const353 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
createXml() const368 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
writeToStream(OutputStream & output) const380 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
writeObjectToStream(OutputStream & output,const SharedObject * object)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 //==============================================================================
412 struct SetPropertyAction : public UndoableAction
413 {
SetPropertyActionjuce::ValueTree::SharedObject::SetPropertyAction414 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
performjuce::ValueTree::SharedObject::SetPropertyAction424 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
undojuce::ValueTree::SharedObject::SetPropertyAction436 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
getSizeInUnitsjuce::ValueTree::SharedObject::SetPropertyAction446 int getSizeInUnits() override
447 {
448 return (int) sizeof (*this); //xxx should be more accurate
449 }
450
createCoalescedActionjuce::ValueTree::SharedObject::SetPropertyAction451 UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
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 //==============================================================================
476 struct AddOrRemoveChildAction : public UndoableAction
477 {
AddOrRemoveChildActionjuce::ValueTree::SharedObject::AddOrRemoveChildAction478 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
performjuce::ValueTree::SharedObject::AddOrRemoveChildAction487 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
undojuce::ValueTree::SharedObject::AddOrRemoveChildAction497 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
getSizeInUnitsjuce::ValueTree::SharedObject::AddOrRemoveChildAction514 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 //==============================================================================
528 struct MoveChildAction : public UndoableAction
529 {
MoveChildActionjuce::ValueTree::SharedObject::MoveChildAction530 MoveChildAction (Ptr parentObject, int fromIndex, int toIndex) noexcept
531 : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex)
532 {
533 }
534
performjuce::ValueTree::SharedObject::MoveChildAction535 bool perform() override
536 {
537 parent->moveChild (startIndex, endIndex, nullptr);
538 return true;
539 }
540
undojuce::ValueTree::SharedObject::MoveChildAction541 bool undo() override
542 {
543 parent->moveChild (endIndex, startIndex, nullptr);
544 return true;
545 }
546
getSizeInUnitsjuce::ValueTree::SharedObject::MoveChildAction547 int getSizeInUnits() override
548 {
549 return (int) sizeof (*this); //xxx should be more accurate
550 }
551
createCoalescedActionjuce::ValueTree::SharedObject::MoveChildAction552 UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
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;
571 ReferenceCountedArray<SharedObject> children;
572 SortedSet<ValueTree*> valueTreesWithListeners;
573 SharedObject* parent = nullptr;
574
575 JUCE_LEAK_DETECTOR (SharedObject)
576 };
577
578 //==============================================================================
ValueTree()579 ValueTree::ValueTree() noexcept
580 {
581 }
582
JUCE_DECLARE_DEPRECATED_STATIC(const ValueTree ValueTree::invalid;)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
ValueTree(const Identifier & type,std::initializer_list<NamedValueSet::NamedValue> properties,std::initializer_list<ValueTree> subTrees)590 ValueTree::ValueTree (const Identifier& type,
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
ValueTree(SharedObject::Ptr so)601 ValueTree::ValueTree (SharedObject::Ptr so) noexcept : object (std::move (so)) {}
ValueTree(SharedObject & so)602 ValueTree::ValueTree (SharedObject& so) noexcept : object (so) {}
603
ValueTree(const ValueTree & other)604 ValueTree::ValueTree (const ValueTree& other) noexcept : object (other.object)
605 {
606 }
607
operator =(const ValueTree & other)608 ValueTree& ValueTree::operator= (const ValueTree& other)
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
ValueTree(ValueTree && other)633 ValueTree::ValueTree (ValueTree&& other) noexcept
634 : object (std::move (other.object))
635 {
636 if (object != nullptr)
637 object->valueTreesWithListeners.removeValue (&other);
638 }
639
~ValueTree()640 ValueTree::~ValueTree()
641 {
642 if (! listeners.isEmpty() && object != nullptr)
643 object->valueTreesWithListeners.removeValue (this);
644 }
645
operator ==(const ValueTree & other) const646 bool ValueTree::operator== (const ValueTree& other) const noexcept
647 {
648 return object == other.object;
649 }
650
operator !=(const ValueTree & other) const651 bool ValueTree::operator!= (const ValueTree& other) const noexcept
652 {
653 return object != other.object;
654 }
655
isEquivalentTo(const ValueTree & other) const656 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
createCopy() const663 ValueTree ValueTree::createCopy() const
664 {
665 if (object != nullptr)
666 return ValueTree (*new SharedObject (*object));
667
668 return {};
669 }
670
copyPropertiesFrom(const ValueTree & source,UndoManager * undoManager)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
copyPropertiesAndChildrenFrom(const ValueTree & source,UndoManager * undoManager)681 void ValueTree::copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager)
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
hasType(const Identifier & typeName) const693 bool ValueTree::hasType (const Identifier& typeName) const noexcept
694 {
695 return object != nullptr && object->type == typeName;
696 }
697
getType() const698 Identifier ValueTree::getType() const noexcept
699 {
700 return object != nullptr ? object->type : Identifier();
701 }
702
getParent() const703 ValueTree ValueTree::getParent() const noexcept
704 {
705 if (object != nullptr)
706 if (auto p = object->parent)
707 return ValueTree (*p);
708
709 return {};
710 }
711
getRoot() const712 ValueTree ValueTree::getRoot() const noexcept
713 {
714 if (object != nullptr)
715 return ValueTree (object->getRoot());
716
717 return {};
718 }
719
getSibling(int delta) const720 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
getNullVarRef()730 static const var& getNullVarRef() noexcept
731 {
732 static var nullVar;
733 return nullVar;
734 }
735
operator [](const Identifier & name) const736 const var& ValueTree::operator[] (const Identifier& name) const noexcept
737 {
738 return object == nullptr ? getNullVarRef() : object->properties[name];
739 }
740
getProperty(const Identifier & name) const741 const var& ValueTree::getProperty (const Identifier& name) const noexcept
742 {
743 return object == nullptr ? getNullVarRef() : object->properties[name];
744 }
745
getProperty(const Identifier & name,const var & defaultReturnValue) const746 var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
747 {
748 return object == nullptr ? defaultReturnValue
749 : object->properties.getWithDefault (name, defaultReturnValue);
750 }
751
getPropertyPointer(const Identifier & name) const752 const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
753 {
754 return object == nullptr ? nullptr
755 : object->properties.getVarPointer (name);
756 }
757
setProperty(const Identifier & name,const var & newValue,UndoManager * undoManager)758 ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
759 {
760 return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
761 }
762
setPropertyExcludingListener(Listener * listenerToExclude,const Identifier & name,const var & newValue,UndoManager * undoManager)763 ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name,
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
hasProperty(const Identifier & name) const775 bool ValueTree::hasProperty (const Identifier& name) const noexcept
776 {
777 return object != nullptr && object->hasProperty (name);
778 }
779
removeProperty(const Identifier & name,UndoManager * undoManager)780 void ValueTree::removeProperty (const Identifier& name, UndoManager* undoManager)
781 {
782 if (object != nullptr)
783 object->removeProperty (name, undoManager);
784 }
785
removeAllProperties(UndoManager * undoManager)786 void ValueTree::removeAllProperties (UndoManager* undoManager)
787 {
788 if (object != nullptr)
789 object->removeAllProperties (undoManager);
790 }
791
getNumProperties() const792 int ValueTree::getNumProperties() const noexcept
793 {
794 return object == nullptr ? 0 : object->properties.size();
795 }
796
getPropertyName(int index) const797 Identifier ValueTree::getPropertyName (int index) const noexcept
798 {
799 return object == nullptr ? Identifier()
800 : object->properties.getName (index);
801 }
802
getReferenceCount() const803 int ValueTree::getReferenceCount() const noexcept
804 {
805 return object != nullptr ? object->getReferenceCount() : 0;
806 }
807
808 //==============================================================================
809 struct ValueTreePropertyValueSource : public Value::ValueSource,
810 private ValueTree::Listener
811 {
ValueTreePropertyValueSourcejuce::ValueTreePropertyValueSource812 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
~ValueTreePropertyValueSourcejuce::ValueTreePropertyValueSource818 ~ValueTreePropertyValueSource() override
819 {
820 tree.removeListener (this);
821 }
822
getValuejuce::ValueTreePropertyValueSource823 var getValue() const override { return tree[property]; }
setValuejuce::ValueTreePropertyValueSource824 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
valueTreePropertyChangedjuce::ValueTreePropertyValueSource832 void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
833 {
834 if (tree == changedTree && property == changedProperty)
835 sendChangeMessage (updateSynchronously);
836 }
837
valueTreeChildAddedjuce::ValueTreePropertyValueSource838 void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
valueTreeChildRemovedjuce::ValueTreePropertyValueSource839 void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
valueTreeChildOrderChangedjuce::ValueTreePropertyValueSource840 void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
valueTreeParentChangedjuce::ValueTreePropertyValueSource841 void valueTreeParentChanged (ValueTree&) override {}
842
843 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
844 };
845
getPropertyAsValue(const Identifier & name,UndoManager * undoManager,bool updateSynchronously)846 Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* undoManager, bool updateSynchronously)
847 {
848 return Value (new ValueTreePropertyValueSource (*this, name, undoManager, updateSynchronously));
849 }
850
851 //==============================================================================
getNumChildren() const852 int ValueTree::getNumChildren() const noexcept
853 {
854 return object == nullptr ? 0 : object->children.size();
855 }
856
getChild(int index) const857 ValueTree ValueTree::getChild (int index) const
858 {
859 if (object != nullptr)
860 if (auto* c = object->children.getObjectPointer (index))
861 return ValueTree (*c);
862
863 return {};
864 }
865
Iterator(const ValueTree & v,bool isEnd)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
operator ++()871 ValueTree::Iterator& ValueTree::Iterator::operator++()
872 {
873 internal = static_cast<SharedObject**> (internal) + 1;
874 return *this;
875 }
876
operator ==(const Iterator & other) const877 bool ValueTree::Iterator::operator== (const Iterator& other) const { return internal == other.internal; }
operator !=(const Iterator & other) const878 bool ValueTree::Iterator::operator!= (const Iterator& other) const { return internal != other.internal; }
879
operator *() const880 ValueTree ValueTree::Iterator::operator*() const
881 {
882 return ValueTree (SharedObject::Ptr (*static_cast<SharedObject**> (internal)));
883 }
884
begin() const885 ValueTree::Iterator ValueTree::begin() const noexcept { return Iterator (*this, false); }
end() const886 ValueTree::Iterator ValueTree::end() const noexcept { return Iterator (*this, true); }
887
getChildWithName(const Identifier & type) const888 ValueTree ValueTree::getChildWithName (const Identifier& type) const
889 {
890 return object != nullptr ? object->getChildWithName (type) : ValueTree();
891 }
892
getOrCreateChildWithName(const Identifier & type,UndoManager * undoManager)893 ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
894 {
895 return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
896 }
897
getChildWithProperty(const Identifier & propertyName,const var & propertyValue) const898 ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
899 {
900 return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
901 }
902
isAChildOf(const ValueTree & possibleParent) const903 bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
904 {
905 return object != nullptr && object->isAChildOf (possibleParent.object.get());
906 }
907
indexOf(const ValueTree & child) const908 int ValueTree::indexOf (const ValueTree& child) const noexcept
909 {
910 return object != nullptr ? object->indexOf (child) : -1;
911 }
912
addChild(const ValueTree & child,int index,UndoManager * undoManager)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
appendChild(const ValueTree & child,UndoManager * undoManager)921 void ValueTree::appendChild (const ValueTree& child, UndoManager* undoManager)
922 {
923 addChild (child, -1, undoManager);
924 }
925
removeChild(int childIndex,UndoManager * undoManager)926 void ValueTree::removeChild (int childIndex, UndoManager* undoManager)
927 {
928 if (object != nullptr)
929 object->removeChild (childIndex, undoManager);
930 }
931
removeChild(const ValueTree & child,UndoManager * undoManager)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
removeAllChildren(UndoManager * undoManager)938 void ValueTree::removeAllChildren (UndoManager* undoManager)
939 {
940 if (object != nullptr)
941 object->removeAllChildren (undoManager);
942 }
943
moveChild(int currentIndex,int newIndex,UndoManager * undoManager)944 void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
945 {
946 if (object != nullptr)
947 object->moveChild (currentIndex, newIndex, undoManager);
948 }
949
950 //==============================================================================
createListOfChildren(OwnedArray<ValueTree> & list) const951 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
reorderChildren(const OwnedArray<ValueTree> & newOrder,UndoManager * undoManager)962 void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
963 {
964 jassert (object != nullptr);
965 object->reorderChildren (newOrder, undoManager);
966 }
967
968 //==============================================================================
addListener(Listener * listener)969 void ValueTree::addListener (Listener* listener)
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
removeListener(Listener * listener)980 void ValueTree::removeListener (Listener* listener)
981 {
982 listeners.remove (listener);
983
984 if (listeners.isEmpty() && object != nullptr)
985 object->valueTreesWithListeners.removeValue (this);
986 }
987
sendPropertyChangeMessage(const Identifier & property)988 void ValueTree::sendPropertyChangeMessage (const Identifier& property)
989 {
990 if (object != nullptr)
991 object->sendPropertyChangeMessage (property);
992 }
993
994 //==============================================================================
createXml() const995 std::unique_ptr<XmlElement> ValueTree::createXml() const
996 {
997 return std::unique_ptr<XmlElement> (object != nullptr ? object->createXml() : nullptr);
998 }
999
fromXml(const XmlElement & xml)1000 ValueTree ValueTree::fromXml (const XmlElement& xml)
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
fromXml(const String & xmlText)1018 ValueTree ValueTree::fromXml (const String& xmlText)
1019 {
1020 if (auto xml = parseXML (xmlText))
1021 return fromXml (*xml);
1022
1023 return {};
1024 }
1025
toXmlString(const XmlElement::TextFormat & format) const1026 String ValueTree::toXmlString (const XmlElement::TextFormat& format) const
1027 {
1028 if (auto xml = createXml())
1029 return xml->toString (format);
1030
1031 return {};
1032 }
1033
1034 //==============================================================================
writeToStream(OutputStream & output) const1035 void ValueTree::writeToStream (OutputStream& output) const
1036 {
1037 SharedObject::writeObjectToStream (output, object.get());
1038 }
1039
readFromStream(InputStream & input)1040 ValueTree ValueTree::readFromStream (InputStream& input)
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
readFromData(const void * data,size_t numBytes)1084 ValueTree ValueTree::readFromData (const void* data, size_t numBytes)
1085 {
1086 MemoryInputStream in (data, numBytes, false);
1087 return readFromStream (in);
1088 }
1089
readFromGZIPData(const void * data,size_t numBytes)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
valueTreePropertyChanged(ValueTree &,const Identifier &)1097 void ValueTree::Listener::valueTreePropertyChanged (ValueTree&, const Identifier&) {}
valueTreeChildAdded(ValueTree &,ValueTree &)1098 void ValueTree::Listener::valueTreeChildAdded (ValueTree&, ValueTree&) {}
valueTreeChildRemoved(ValueTree &,ValueTree &,int)1099 void ValueTree::Listener::valueTreeChildRemoved (ValueTree&, ValueTree&, int) {}
valueTreeChildOrderChanged(ValueTree &,int,int)1100 void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) {}
valueTreeParentChanged(ValueTree &)1101 void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {}
valueTreeRedirected(ValueTree &)1102 void ValueTree::Listener::valueTreeRedirected (ValueTree&) {}
1103
1104
1105 //==============================================================================
1106 //==============================================================================
1107 #if JUCE_UNIT_TESTS
1108
1109 class ValueTreeTests : public UnitTest
1110 {
1111 public:
ValueTreeTests()1112 ValueTreeTests()
1113 : UnitTest ("ValueTrees", UnitTestCategories::values)
1114 {}
1115
createRandomIdentifier(Random & r)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
createRandomWideCharString(Random & r)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
createRandomTree(UndoManager * undoManager,int depth,Random & r)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
runTest()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
1246