40 for (
auto* c : other.children)
52 jassert (parent ==
nullptr);
54 for (
auto i = children.size(); --i >= 0;)
56 const Ptr c (children.getObjectPointerUnchecked (i));
59 c->sendParentChangeMessage();
65 return parent ==
nullptr ? *this : parent->getRoot();
68 template <
typename Function>
71 auto numListeners = valueTreesWithListeners.size();
73 if (numListeners == 1)
75 valueTreesWithListeners.getUnchecked (0)->listeners.callExcluding (listenerToExclude, fn);
77 else if (numListeners > 0)
79 auto listenersCopy = valueTreesWithListeners;
81 for (
int i = 0; i < numListeners; ++i)
83 auto* v = listenersCopy.getUnchecked (i);
85 if (i == 0 || valueTreesWithListeners.contains (v))
86 v->listeners.callExcluding (listenerToExclude, fn);
91 template <
typename Function>
94 for (
auto* t =
this; t !=
nullptr; t = t->parent)
95 t->callListeners (listenerToExclude, fn);
104 void sendChildAddedMessage (
ValueTree child)
110 void sendChildRemovedMessage (
ValueTree child,
int index)
116 void sendChildOrderChangedMessage (
int oldIndex,
int newIndex)
122 void sendParentChangeMessage()
126 for (
auto j = children.size(); --j >= 0;)
127 if (
auto* child = children.getObjectPointer (j))
128 child->sendParentChangeMessage();
136 if (undoManager ==
nullptr)
138 if (properties.
set (name, newValue))
139 sendPropertyChangeMessage (name, listenerToExclude);
145 if (*existingValue != newValue)
147 false,
false, listenerToExclude));
152 true,
false, listenerToExclude));
157 bool hasProperty (
const Identifier& name)
const noexcept
164 if (undoManager ==
nullptr)
166 if (properties.
remove (name))
167 sendPropertyChangeMessage (name);
176 void removeAllProperties (
UndoManager* undoManager)
178 if (undoManager ==
nullptr)
180 while (properties.
size() > 0)
182 auto name = properties.
getName (properties.
size() - 1);
184 sendPropertyChangeMessage (name);
189 for (
auto i = properties.
size(); --i >= 0;)
197 for (
auto i = properties.
size(); --i >= 0;)
199 removeProperty (properties.
getName (i), undoManager);
201 for (
int i = 0; i < source.properties.
size(); ++i)
202 setProperty (source.properties.
getName (i), source.properties.
getValueAt (i), undoManager);
207 for (
auto* s : children)
208 if (s->type == typeToMatch)
216 for (
auto* s : children)
217 if (s->type == typeToMatch)
221 addChild (newObject, -1, undoManager);
227 for (
auto* s : children)
228 if (s->properties[propertyName] == propertyValue)
234 bool isAChildOf (
const SharedObject* possibleParent)
const noexcept
236 for (
auto* p = parent; p !=
nullptr; p = p->parent)
237 if (p == possibleParent)
243 int indexOf (
const ValueTree& child)
const noexcept
245 return children.indexOf (child.object);
250 if (child !=
nullptr && child->parent !=
this)
252 if (child !=
this && ! isAChildOf (child))
257 jassert (child->parent ==
nullptr);
259 if (child->parent !=
nullptr)
261 jassert (child->parent->children.indexOf (child) >= 0);
262 child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
265 if (undoManager ==
nullptr)
267 children.insert (index, child);
268 child->parent =
this;
269 sendChildAddedMessage (
ValueTree (*child));
270 child->sendParentChangeMessage();
274 if (! isPositiveAndBelow (index, children.size()))
275 index = children.size();
289 void removeChild (
int childIndex,
UndoManager* undoManager)
291 if (
auto child =
Ptr (children.getObjectPointer (childIndex)))
293 if (undoManager ==
nullptr)
295 children.remove (childIndex);
296 child->parent =
nullptr;
297 sendChildRemovedMessage (
ValueTree (child), childIndex);
298 child->sendParentChangeMessage();
309 while (children.size() > 0)
310 removeChild (children.size() - 1, undoManager);
313 void moveChild (
int currentIndex,
int newIndex,
UndoManager* undoManager)
316 jassert (isPositiveAndBelow (currentIndex, children.size()));
318 if (currentIndex != newIndex
319 && isPositiveAndBelow (currentIndex, children.size()))
321 if (undoManager ==
nullptr)
323 children.move (currentIndex, newIndex);
324 sendChildOrderChangedMessage (currentIndex, newIndex);
328 if (! isPositiveAndBelow (newIndex, children.size()))
329 newIndex = children.size() - 1;
338 jassert (newOrder.
size() == children.size());
340 for (
int i = 0; i < children.size(); ++i)
344 if (children.getObjectPointerUnchecked (i) != child)
346 auto oldIndex = children.indexOf (child);
347 jassert (oldIndex >= 0);
348 moveChild (oldIndex, i, undoManager);
353 bool isEquivalentTo (
const SharedObject& other)
const noexcept
355 if (type != other.type
356 || properties.
size() != other.properties.
size()
357 || children.size() != other.children.size()
358 || properties != other.properties)
361 for (
int i = 0; i < children.size(); ++i)
362 if (! children.getObjectPointerUnchecked (i)->isEquivalentTo (*other.children.getObjectPointerUnchecked (i)))
374 for (
auto i = children.size(); --i >= 0;)
375 xml->prependChildElement (children.getObjectPointerUnchecked (i)->createXml());
385 for (
int j = 0; j < properties.
size(); ++j)
393 for (
auto* c : children)
394 writeObjectToStream (output, c);
399 if (
object !=
nullptr)
401 object->writeToStream (output);
415 const var& newVal,
const var& oldVal,
bool isAdding,
bool isDeleting,
417 : target (std::move (targetObject)),
418 name (propertyName), newValue (newVal), oldValue (oldVal),
419 isAddingNewProperty (isAdding), isDeletingProperty (isDeleting),
420 excludeListener (listenerToExclude)
426 jassert (! (isAddingNewProperty && target->hasProperty (name)));
428 if (isDeletingProperty)
429 target->removeProperty (name,
nullptr);
431 target->setProperty (name, newValue,
nullptr, excludeListener);
438 if (isAddingNewProperty)
439 target->removeProperty (name,
nullptr);
441 target->setProperty (name, oldValue,
nullptr);
448 return (
int)
sizeof (*this);
453 if (! (isAddingNewProperty || isDeletingProperty))
456 if (next->target == target && next->name == name
457 && ! (next->isAddingNewProperty || next->isDeletingProperty))
458 return new SetPropertyAction (*target, name, next->newValue, oldValue,
false,
false);
469 const bool isAddingNewProperty : 1, isDeletingProperty : 1;
479 : target (std::move (parentObject)),
480 child (newChild !=
nullptr ? newChild : target->children.getObjectPointer (index)),
482 isDeleting (newChild ==
nullptr)
484 jassert (child !=
nullptr);
490 target->removeChild (childIndex,
nullptr);
492 target->addChild (child.get(), childIndex,
nullptr);
501 target->addChild (child.get(), childIndex,
nullptr);
507 jassert (childIndex < target->children.size());
508 target->removeChild (childIndex,
nullptr);
516 return (
int)
sizeof (*this);
520 const Ptr target, child;
521 const int childIndex;
522 const bool isDeleting;
531 : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex)
537 parent->moveChild (startIndex, endIndex,
nullptr);
543 parent->moveChild (endIndex, startIndex,
nullptr);
549 return (
int)
sizeof (*this);
555 if (next->parent == parent && next->startIndex == endIndex)
563 const int startIndex, endIndex;
583 JUCE_DECLARE_DEPRECATED_STATIC (
const ValueTree ValueTree::invalid;)
591 std::initializer_list<NamedValueSet::NamedValue> properties,
592 std::initializer_list<ValueTree> subTrees)
597 for (
auto& tree : subTrees)
610 if (
object != other.object)
612 if (listeners.isEmpty())
614 object = other.object;
618 if (
object !=
nullptr)
619 object->valueTreesWithListeners.removeValue (
this);
621 if (other.object !=
nullptr)
622 other.object->valueTreesWithListeners.add (
this);
624 object = other.object;
634 : object (std::move (other.object))
636 if (
object !=
nullptr)
637 object->valueTreesWithListeners.removeValue (&other);
642 if (! listeners.isEmpty() &&
object !=
nullptr)
643 object->valueTreesWithListeners.removeValue (
this);
648 return object == other.object;
653 return object != other.object;
658 return object == other.object
659 || (
object !=
nullptr && other.object !=
nullptr
660 &&
object->isEquivalentTo (*other.object));
665 if (
object !=
nullptr)
673 jassert (
object !=
nullptr || source.object ==
nullptr);
675 if (source.object ==
nullptr)
677 else if (
object !=
nullptr)
678 object->copyPropertiesFrom (*(source.object), undoManager);
683 jassert (
object !=
nullptr || source.object ==
nullptr);
688 if (
object !=
nullptr && source.object !=
nullptr)
689 for (
auto& child : source.object->children)
690 object->addChild (createCopyIfNotNull (child), -1, undoManager);
695 return object !=
nullptr &&
object->type == typeName;
700 return object !=
nullptr ?
object->type :
Identifier();
705 if (
object !=
nullptr)
706 if (
auto p = object->parent)
714 if (
object !=
nullptr)
722 if (
object !=
nullptr)
723 if (
auto* p = object->parent)
724 if (
auto* c = p->children.getObjectPointer (p->indexOf (*
this) + delta))
730 static const var& getNullVarRef() noexcept
738 return object ==
nullptr ? getNullVarRef() :
object->properties[name];
743 return object ==
nullptr ? getNullVarRef() :
object->properties[name];
748 return object ==
nullptr ? defaultReturnValue
749 :
object->properties.getWithDefault (name, defaultReturnValue);
754 return object ==
nullptr ? nullptr
755 :
object->properties.getVarPointer (name);
767 jassert (
object !=
nullptr);
769 if (
object !=
nullptr)
770 object->setProperty (name, newValue, undoManager, listenerToExclude);
777 return object !=
nullptr &&
object->hasProperty (name);
782 if (
object !=
nullptr)
783 object->removeProperty (name, undoManager);
788 if (
object !=
nullptr)
789 object->removeAllProperties (undoManager);
794 return object ==
nullptr ? 0 :
object->properties.size();
800 :
object->properties.getName (index);
805 return object !=
nullptr ?
object->getReferenceCount() : 0;
813 : tree (vt), property (prop), undoManager (um), updateSynchronously (sync)
830 const bool updateSynchronously;
832 void valueTreePropertyChanged (
ValueTree& changedTree,
const Identifier& changedProperty)
override
834 if (tree == changedTree && property == changedProperty)
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 {}
843 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
854 return object ==
nullptr ? 0 :
object->children.size();
859 if (
object !=
nullptr)
860 if (
auto* c = object->children.getObjectPointer (index))
866 ValueTree::Iterator::Iterator (
const ValueTree& v,
bool isEnd)
867 : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
871 ValueTree::Iterator& ValueTree::Iterator::operator++()
873 internal =
static_cast<SharedObject**
> (
internal) + 1;
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; }
880 ValueTree ValueTree::Iterator::operator*()
const
882 return ValueTree (SharedObject::Ptr (*
static_cast<SharedObject**
> (
internal)));
905 return object !=
nullptr &&
object->isAChildOf (possibleParent.object.get());
910 return object !=
nullptr ?
object->indexOf (child) : -1;
915 jassert (
object !=
nullptr);
917 if (
object !=
nullptr)
918 object->addChild (child.object.get(), index, undoManager);
928 if (
object !=
nullptr)
929 object->removeChild (childIndex, undoManager);
934 if (
object !=
nullptr)
935 object->removeChild (object->children.indexOf (child.object), undoManager);
940 if (
object !=
nullptr)
941 object->removeAllChildren (undoManager);
946 if (
object !=
nullptr)
947 object->moveChild (currentIndex, newIndex, undoManager);
953 jassert (
object !=
nullptr);
955 for (
auto* o : object->children)
957 jassert (o !=
nullptr);
962 void ValueTree::reorderChildren (
const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
964 jassert (
object !=
nullptr);
965 object->reorderChildren (newOrder, undoManager);
971 if (listener !=
nullptr)
973 if (listeners.isEmpty() &&
object !=
nullptr)
974 object->valueTreesWithListeners.add (
this);
976 listeners.add (listener);
982 listeners.remove (listener);
984 if (listeners.isEmpty() &&
object !=
nullptr)
985 object->valueTreesWithListeners.removeValue (
this);
990 if (
object !=
nullptr)
991 object->sendPropertyChangeMessage (property);
997 return object !=
nullptr ?
object->createXml() :
nullptr;
1005 v.object->properties.setFromXmlAttributes (xml);
1007 forEachXmlChildElement (xml, e)
1020 std::unique_ptr<XmlElement> xml (
createXml());
1023 return xml->createDocument ({});
1031 SharedObject::writeObjectToStream (output,
object.get());
1051 for (
int i = 0; i < numProps; ++i)
1055 if (name.isNotEmpty())
1062 v.object->children.ensureStorageAllocated (numChildren);
1064 for (
int i = 0; i < numChildren; ++i)
1068 if (! child.isValid())
1071 v.object->children.add (child.object);
1072 child.object->parent = v.object.get();
1096 class ValueTreeTests :
public UnitTest
1099 ValueTreeTests() :
UnitTest (
"ValueTrees",
"Values") {}
1103 char buffer[50] = { 0 };
1104 const char chars[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
1106 for (
int i = 1 + r.
nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
1107 buffer[i] = chars[r.
nextInt (sizeof (chars) - 1)];
1109 String result (buffer);
1112 result = createRandomIdentifier (r);
1117 static String createRandomWideCharString (Random& r)
1119 juce_wchar buffer[50] = { 0 };
1121 for (
int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
1127 buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
1132 buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
1135 return CharPointer_UTF32 (buffer);
1138 static ValueTree createRandomTree (UndoManager* undoManager,
int depth, Random& r)
1140 ValueTree v (createRandomIdentifier (r));
1142 for (
int i = r.nextInt (10); --i >= 0;)
1144 switch (r.nextInt (5))
1146 case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager);
break;
1147 case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager);
break;
1148 case 2:
if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager);
break;
1149 case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager);
break;
1150 case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager);
break;
1158 void runTest()
override
1161 beginTest (
"ValueTree");
1163 auto r = getRandom();
1165 for (
int i = 10; --i >= 0;)
1167 MemoryOutputStream mo;
1168 auto v1 = createRandomTree (
nullptr, 0, r);
1169 v1.writeToStream (mo);
1171 MemoryInputStream mi (mo.getData(), mo.getDataSize(),
false);
1173 expect (v1.isEquivalentTo (v2));
1175 MemoryOutputStream zipped;
1177 GZIPCompressorOutputStream zippedOut (zipped);
1178 v1.writeToStream (zippedOut);
1182 std::unique_ptr<XmlElement> xml1 (v1.createXml());
1183 std::unique_ptr<XmlElement> xml2 (v2.createCopy().createXml());
1184 expect (xml1->isEquivalentTo (xml2.get(),
false));
1186 auto v4 = v2.createCopy();
1187 expect (v1.isEquivalentTo (v4));
1192 beginTest (
"Float formatting");
1195 Identifier number (
"number");
1197 std::map<double, String> tests;
1200 tests[1.01] =
"1.01";
1201 tests[0.76378] =
"0.76378";
1202 tests[-10] =
"-10.0";
1203 tests[10.01] =
"10.01";
1204 tests[0.0123] =
"0.0123";
1205 tests[-3.7e-27] =
"-3.7e-27";
1206 tests[1e+40] =
"1.0e40";
1207 tests[-12345678901234567.0] =
"-1.234567890123457e16";
1208 tests[192000] =
"192000.0";
1209 tests[1234567] =
"1.234567e6";
1210 tests[0.00006] =
"0.00006";
1211 tests[0.000006] =
"6.0e-6";
1213 for (
auto& test : tests)
1215 testVT.setProperty (number, test.first,
nullptr);
1217 lines.removeEmptyStrings();
1218 auto numLines = lines.size();
1219 expect (numLines > 1);
1220 expectEquals (lines[numLines - 1],
"<Test number=\"" + test.second +
"\"/>");
1226 static ValueTreeTests valueTreeTests;