1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 29 //============================================================================== 30 /** 31 A powerful tree structure that can be used to hold free-form data, and which can 32 handle its own undo and redo behaviour. 33 34 A ValueTree contains a list of named properties as var objects, and also holds 35 any number of sub-trees. 36 37 Create ValueTree objects on the stack, and don't be afraid to copy them around, as 38 they're simply a lightweight reference to a shared data container. Creating a copy 39 of another ValueTree simply creates a new reference to the same underlying object - to 40 make a separate, deep copy of a tree you should explicitly call createCopy(). 41 42 Each ValueTree has a type name, in much the same way as an XmlElement has a tag name, 43 and much of the structure of a ValueTree is similar to an XmlElement tree. 44 You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't 45 contain text elements, the conversion works well and makes a good serialisation 46 format. They can also be serialised to a binary format, which is very fast and compact. 47 48 All the methods that change data take an optional UndoManager, which will be used 49 to track any changes to the object. For this to work, you have to be careful to 50 consistently always use the same UndoManager for all operations to any sub-tree inside 51 the tree. 52 53 A ValueTree can only be a child of one parent at a time, so if you're moving one from 54 one tree to another, be careful to always remove it first, before adding it. This 55 could also mess up your undo/redo chain, so be wary! In a debug build you should hit 56 assertions if you try to do anything dangerous, but there are still plenty of ways it 57 could go wrong. 58 59 Note that although the children in a tree have a fixed order, the properties are not 60 guaranteed to be stored in any particular order, so don't expect that a property's index 61 will correspond to the order in which the property was added, or that it will remain 62 constant when other properties are added or removed. 63 64 Listeners can be added to a ValueTree to be told when properties change and when 65 sub-trees are added or removed. 66 67 @see var, XmlElement 68 69 @tags{DataStructures} 70 */ 71 class JUCE_API ValueTree final 72 { 73 public: 74 //============================================================================== 75 /** Creates an empty, invalid ValueTree. 76 77 A ValueTree that is created with this constructor can't actually be used for anything, 78 it's just a default 'null' ValueTree that can be returned to indicate some sort of failure. 79 To create a real one, use the constructor that takes a string. 80 */ 81 ValueTree() noexcept; 82 83 /** Creates an empty ValueTree with the given type name. 84 85 Like an XmlElement, each ValueTree has a type, which you can access with 86 getType() and hasType(). 87 */ 88 explicit ValueTree (const Identifier& type); 89 90 /** Creates a value tree from nested lists of properties and ValueTrees. 91 92 This code, 93 94 @code 95 ValueTree groups 96 { "ParameterGroups", {}, 97 { 98 { "Group", {{ "name", "Tone Controls" }}, 99 { 100 { "Parameter", {{ "id", "distortion" }, { "value", 0.5 }}}, 101 { "Parameter", {{ "id", "reverb" }, { "value", 0.5 }}} 102 } 103 }, 104 { "Group", {{ "name", "Other Controls" }}, 105 { 106 { "Parameter", {{ "id", "drywet" }, { "value", 0.5 }}}, 107 { "Parameter", {{ "id", "gain" }, { "value", 0.5 }}} 108 } 109 } 110 } 111 }; 112 @endcode 113 114 produces this tree: 115 116 @verbatim 117 <ParameterGroups> 118 <Group name="Tone Controls"> 119 <Parameter id="distortion" value="0.5"/> 120 <Parameter id="reverb" value="0.5"/> 121 </Group> 122 <Group name="Other Controls"> 123 <Parameter id="drywet" value="0.5"/> 124 <Parameter id="gain" value="0.5"/> 125 </Group> 126 </ParameterGroups> 127 @endverbatim 128 */ 129 ValueTree (const Identifier& type, 130 std::initializer_list<NamedValueSet::NamedValue> properties, 131 std::initializer_list<ValueTree> subTrees = {}); 132 133 /** Creates a reference to another ValueTree. */ 134 ValueTree (const ValueTree&) noexcept; 135 136 /** Move constructor */ 137 ValueTree (ValueTree&&) noexcept; 138 139 /** Changes this object to be a reference to the given tree. 140 Note that calling this just points this at the new object and invokes the 141 Listener::valueTreeRedirected callback, but it's not an undoable operation. If 142 you're trying to replace an entire tree in an undoable way, you probably want 143 to use copyPropertiesAndChildrenFrom() instead. 144 */ 145 ValueTree& operator= (const ValueTree&); 146 147 /** Destructor. */ 148 ~ValueTree(); 149 150 /** Returns true if both this and the other tree refer to the same underlying structure. 151 Note that this isn't a value comparison - two independently-created trees which 152 contain identical data are NOT considered equal. 153 */ 154 bool operator== (const ValueTree&) const noexcept; 155 156 /** Returns true if this and the other tree refer to different underlying structures. 157 Note that this isn't a value comparison - two independently-created trees which 158 contain identical data are not considered equal. 159 */ 160 bool operator!= (const ValueTree&) const noexcept; 161 162 /** Performs a deep comparison between the properties and children of two trees. 163 If all the properties and children of the two trees are the same (recursively), this 164 returns true. 165 The normal operator==() only checks whether two trees refer to the same shared data 166 structure, so use this method if you need to do a proper value comparison. 167 */ 168 bool isEquivalentTo (const ValueTree&) const; 169 170 //============================================================================== 171 /** Returns true if this tree refers to some valid data. 172 An invalid tree is one that was created with the default constructor. 173 */ isValid()174 bool isValid() const noexcept { return object != nullptr; } 175 176 /** Returns a deep copy of this tree and all its sub-trees. */ 177 ValueTree createCopy() const; 178 179 /** Overwrites all the properties in this tree with the properties of the source tree. 180 Any properties that already exist will be updated; and new ones will be added, and 181 any that are not present in the source tree will be removed. 182 @see copyPropertiesAndChildrenFrom 183 */ 184 void copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager); 185 186 /** Replaces all children and properties of this object with copies of those from 187 the source object. 188 @see copyPropertiesFrom 189 */ 190 void copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager); 191 192 //============================================================================== 193 /** Returns the type of this tree. 194 The type is specified when the ValueTree is created. 195 @see hasType 196 */ 197 Identifier getType() const noexcept; 198 199 /** Returns true if the tree has this type. 200 The comparison is case-sensitive. 201 @see getType 202 */ 203 bool hasType (const Identifier& typeName) const noexcept; 204 205 //============================================================================== 206 /** Returns the value of a named property. 207 If no such property has been set, this will return a void variant. 208 You can also use operator[] to get a property. 209 @see var, setProperty, getPropertyPointer, hasProperty 210 */ 211 const var& getProperty (const Identifier& name) const noexcept; 212 213 /** Returns the value of a named property, or the value of defaultReturnValue 214 if the property doesn't exist. 215 You can also use operator[] and getProperty to get a property. 216 @see var, getProperty, getPropertyPointer, setProperty, hasProperty 217 */ 218 var getProperty (const Identifier& name, const var& defaultReturnValue) const; 219 220 /** Returns a pointer to the value of a named property, or nullptr if the property 221 doesn't exist. 222 @see var, getProperty, setProperty, hasProperty 223 */ 224 const var* getPropertyPointer (const Identifier& name) const noexcept; 225 226 /** Returns the value of a named property. 227 If no such property has been set, this will return a void variant. This is the same as 228 calling getProperty(). 229 @see getProperty 230 */ 231 const var& operator[] (const Identifier& name) const noexcept; 232 233 /** Changes a named property of the tree. 234 The name identifier must not be an empty string. 235 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 236 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 237 @see var, getProperty, removeProperty 238 @returns a reference to the value tree, so that you can daisy-chain calls to this method. 239 */ 240 ValueTree& setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager); 241 242 /** Returns true if the tree contains a named property. */ 243 bool hasProperty (const Identifier& name) const noexcept; 244 245 /** Removes a property from the tree. 246 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 247 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 248 */ 249 void removeProperty (const Identifier& name, UndoManager* undoManager); 250 251 /** Removes all properties from the tree. 252 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 253 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 254 */ 255 void removeAllProperties (UndoManager* undoManager); 256 257 /** Returns the total number of properties that the tree contains. 258 @see getProperty. 259 */ 260 int getNumProperties() const noexcept; 261 262 /** Returns the identifier of the property with a given index. 263 Note that properties are not guaranteed to be stored in any particular order, so don't 264 expect that the index will correspond to the order in which the property was added, or 265 that it will remain constant when other properties are added or removed. 266 @see getNumProperties 267 */ 268 Identifier getPropertyName (int index) const noexcept; 269 270 /** Returns a Value object that can be used to control and respond to one of the tree's properties. 271 272 The Value object will maintain a reference to this tree, and will use the undo manager when 273 it needs to change the value. Attaching a Value::Listener to the value object will provide 274 callbacks whenever the property changes. 275 If shouldUpdateSynchronously is true the Value::Listener will be updated synchronously. 276 @see ValueSource::sendChangeMessage (bool) 277 */ 278 Value getPropertyAsValue (const Identifier& name, UndoManager* undoManager, 279 bool shouldUpdateSynchronously = false); 280 281 //============================================================================== 282 /** Returns the number of child trees inside this one. 283 @see getChild 284 */ 285 int getNumChildren() const noexcept; 286 287 /** Returns one of this tree's sub-trees. 288 If the index is out of range, it'll return an invalid tree. (You can use isValid() to 289 check whether a tree is valid) 290 */ 291 ValueTree getChild (int index) const; 292 293 /** Returns the first sub-tree with the specified type name. 294 If no such child tree exists, it'll return an invalid tree. (You can use isValid() to 295 check whether a tree is valid) 296 @see getOrCreateChildWithName 297 */ 298 ValueTree getChildWithName (const Identifier& type) const; 299 300 /** Returns the first sub-tree with the specified type name, creating and adding 301 a child with this name if there wasn't already one there. 302 The only time this will return an invalid object is when the object that you're calling 303 the method on is itself invalid. 304 @see getChildWithName 305 */ 306 ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); 307 308 /** Looks for the first sub-tree that has the specified property value. 309 This will scan the child trees in order, until it finds one that has property that matches 310 the specified value. 311 If no such tree is found, it'll return an invalid object. (You can use isValid() to 312 check whether a tree is valid) 313 */ 314 ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; 315 316 /** Adds a child to this tree. 317 Make sure that the child being added has first been removed from any former parent before 318 calling this, or else you'll hit an assertion. 319 If the index is < 0 or greater than the current number of sub-trees, the new one will be 320 added at the end of the list. 321 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 322 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 323 @see appendChild, removeChild 324 */ 325 void addChild (const ValueTree& child, int index, UndoManager* undoManager); 326 327 /** Appends a new child sub-tree to this tree. 328 This is equivalent to calling addChild() with an index of -1. See addChild() for more details. 329 @see addChild, removeChild 330 */ 331 void appendChild (const ValueTree& child, UndoManager* undoManager); 332 333 /** Removes the specified child from this tree's child-list. 334 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 335 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 336 */ 337 void removeChild (const ValueTree& child, UndoManager* undoManager); 338 339 /** Removes a sub-tree from this tree. 340 If the index is out-of-range, nothing will be changed. 341 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 342 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 343 */ 344 void removeChild (int childIndex, UndoManager* undoManager); 345 346 /** Removes all child-trees. 347 If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used, 348 so that this change can be undone. Be very careful not to mix undoable and non-undoable changes! 349 */ 350 void removeAllChildren (UndoManager* undoManager); 351 352 /** Moves one of the sub-trees to a different index. 353 This will move the child to a specified index, shuffling along any intervening 354 items as required. So for example, if you have a list of { 0, 1, 2, 3, 4, 5 }, then 355 calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. 356 357 @param currentIndex the index of the item to be moved. If this isn't a 358 valid index, then nothing will be done 359 @param newIndex the index at which you'd like this item to end up. If this 360 is less than zero, the value will be moved to the end 361 of the list 362 @param undoManager the optional UndoManager to use to store this transaction 363 */ 364 void moveChild (int currentIndex, int newIndex, UndoManager* undoManager); 365 366 /** Returns true if this tree is a sub-tree (at any depth) of the given parent. 367 This searches recursively, so returns true if it's a sub-tree at any level below the parent. 368 */ 369 bool isAChildOf (const ValueTree& possibleParent) const noexcept; 370 371 /** Returns the index of a child item in this parent. 372 If the child isn't found, this returns -1. 373 */ 374 int indexOf (const ValueTree& child) const noexcept; 375 376 /** Returns the parent tree that contains this one. 377 If the tree has no parent, this will return an invalid object. (You can use isValid() to 378 check whether a tree is valid) 379 */ 380 ValueTree getParent() const noexcept; 381 382 /** Recursively finds the highest-level parent tree that contains this one. 383 If the tree has no parent, this will return itself. 384 */ 385 ValueTree getRoot() const noexcept; 386 387 /** Returns one of this tree's siblings in its parent's child list. 388 The delta specifies how far to move through the list, so a value of 1 would return the tree 389 that follows this one, -1 would return the tree before it, 0 will return this one, etc. 390 If the requested position is beyond the start or end of the child list, this will return an 391 invalid object. 392 */ 393 ValueTree getSibling (int delta) const noexcept; 394 395 //============================================================================== 396 /** Iterator for a ValueTree. 397 You shouldn't ever need to use this class directly - it's used internally by ValueTree::begin() 398 and ValueTree::end() to allow range-based-for loops on a ValueTree. 399 */ 400 struct Iterator 401 { 402 Iterator (const ValueTree&, bool isEnd); 403 Iterator& operator++(); 404 405 bool operator== (const Iterator&) const; 406 bool operator!= (const Iterator&) const; 407 ValueTree operator*() const; 408 409 using difference_type = std::ptrdiff_t; 410 using value_type = ValueTree; 411 using reference = ValueTree&; 412 using pointer = ValueTree*; 413 using iterator_category = std::forward_iterator_tag; 414 415 private: 416 void* internal; 417 }; 418 419 /** Returns a start iterator for the children in this tree. */ 420 Iterator begin() const noexcept; 421 422 /** Returns an end iterator for the children in this tree. */ 423 Iterator end() const noexcept; 424 425 //============================================================================== 426 /** Creates an XmlElement that holds a complete image of this tree and all its children. 427 If this tree is invalid, this may return nullptr. Otherwise, the XML that is produced can 428 be used to recreate a similar tree by calling ValueTree::fromXml(). 429 @see fromXml, toXmlString 430 */ 431 std::unique_ptr<XmlElement> createXml() const; 432 433 /** Tries to recreate a tree from its XML representation. 434 This isn't designed to cope with random XML data - it should only be fed XML that was created 435 by the createXml() method. 436 */ 437 static ValueTree fromXml (const XmlElement& xml); 438 439 /** Tries to recreate a tree from its XML representation. 440 This isn't designed to cope with random XML data - it should only be fed XML that was created 441 by the createXml() method. 442 */ 443 static ValueTree fromXml (const String& xmlText); 444 445 /** This returns a string containing an XML representation of the tree. 446 This is quite handy for debugging purposes, as it provides a quick way to view a tree. 447 @see createXml() 448 */ 449 String toXmlString (const XmlElement::TextFormat& format = {}) const; 450 451 //============================================================================== 452 /** Stores this tree (and all its children) in a binary format. 453 454 Once written, the data can be read back with readFromStream(). 455 456 It's much faster to load/save your tree in binary form than as XML, but 457 obviously isn't human-readable. 458 */ 459 void writeToStream (OutputStream& output) const; 460 461 /** Reloads a tree from a stream that was written with writeToStream(). */ 462 static ValueTree readFromStream (InputStream& input); 463 464 /** Reloads a tree from a data block that was written with writeToStream(). */ 465 static ValueTree readFromData (const void* data, size_t numBytes); 466 467 /** Reloads a tree from a data block that was written with writeToStream() and 468 then zipped using GZIPCompressorOutputStream. 469 */ 470 static ValueTree readFromGZIPData (const void* data, size_t numBytes); 471 472 //============================================================================== 473 /** Listener class for events that happen to a ValueTree. 474 475 To get events from a ValueTree, make your class implement this interface, and use 476 ValueTree::addListener() and ValueTree::removeListener() to register it. 477 */ 478 class JUCE_API Listener 479 { 480 public: 481 /** Destructor. */ 482 virtual ~Listener() = default; 483 484 /** This method is called when a property of this tree (or of one of its sub-trees) is changed. 485 Note that when you register a listener to a tree, it will receive this callback for 486 property changes in that tree, and also for any of its children, (recursively, at any depth). 487 If your tree has sub-trees but you only want to know about changes to the top level tree, 488 simply check the tree parameter in this callback to make sure it's the tree you're interested in. 489 */ 490 virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, 491 const Identifier& property); 492 493 /** This method is called when a child sub-tree is added. 494 Note that when you register a listener to a tree, it will receive this callback for 495 child changes in both that tree and any of its children, (recursively, at any depth). 496 If your tree has sub-trees but you only want to know about changes to the top level tree, 497 just check the parentTree parameter to make sure it's the one that you're interested in. 498 */ 499 virtual void valueTreeChildAdded (ValueTree& parentTree, 500 ValueTree& childWhichHasBeenAdded); 501 502 /** This method is called when a child sub-tree is removed. 503 504 Note that when you register a listener to a tree, it will receive this callback for 505 child changes in both that tree and any of its children, (recursively, at any depth). 506 If your tree has sub-trees but you only want to know about changes to the top level tree, 507 just check the parentTree parameter to make sure it's the one that you're interested in. 508 */ 509 virtual void valueTreeChildRemoved (ValueTree& parentTree, 510 ValueTree& childWhichHasBeenRemoved, 511 int indexFromWhichChildWasRemoved); 512 513 /** This method is called when a tree's children have been re-shuffled. 514 515 Note that when you register a listener to a tree, it will receive this callback for 516 child changes in both that tree and any of its children, (recursively, at any depth). 517 If your tree has sub-trees but you only want to know about changes to the top level tree, 518 just check the parameter to make sure it's the tree that you're interested in. 519 */ 520 virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, 521 int oldIndex, int newIndex); 522 523 /** This method is called when a tree has been added or removed from a parent. 524 525 This callback happens when the tree to which the listener was registered is added or 526 removed from a parent. Unlike the other callbacks, it applies only to the tree to which 527 the listener is registered, and not to any of its children. 528 */ 529 virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged); 530 531 /** This method is called when a tree is made to point to a different internal shared object. 532 When operator= is used to make a ValueTree refer to a different object, this callback 533 will be made. 534 */ 535 virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged); 536 }; 537 538 /** Adds a listener to receive callbacks when this tree is changed in some way. 539 540 The listener is added to this specific ValueTree object, and not to the shared 541 object that it refers to. When this object is deleted, all the listeners will 542 be lost, even if other references to the same ValueTree still exist. And if you 543 use the operator= to make this refer to a different ValueTree, any listeners will 544 begin listening to changes to the new tree instead of the old one. 545 546 When you're adding a listener, make sure that you add it to a ValueTree instance that 547 will last for as long as you need the listener. In general, you'd never want to add a 548 listener to a local stack-based ValueTree, and would usually add one to a member variable. 549 550 @see removeListener 551 */ 552 void addListener (Listener* listener); 553 554 /** Removes a listener that was previously added with addListener(). */ 555 void removeListener (Listener* listener); 556 557 /** Changes a named property of the tree, but will not notify a specified listener of the change. 558 @see setProperty 559 */ 560 ValueTree& setPropertyExcludingListener (Listener* listenerToExclude, 561 const Identifier& name, const var& newValue, 562 UndoManager* undoManager); 563 564 /** Causes a property-change callback to be triggered for the specified property, 565 calling any listeners that are registered. 566 */ 567 void sendPropertyChangeMessage (const Identifier& property); 568 569 //============================================================================== 570 /** This method uses a comparator object to sort the tree's children into order. 571 572 The object provided must have a method of the form: 573 @code 574 int compareElements (const ValueTree& first, const ValueTree& second); 575 @endcode 576 577 ..and this method must return: 578 - a value of < 0 if the first comes before the second 579 - a value of 0 if the two objects are equivalent 580 - a value of > 0 if the second comes before the first 581 582 To improve performance, the compareElements() method can be declared as static or const. 583 584 @param comparator the comparator to use for comparing elements. 585 @param undoManager optional UndoManager for storing the changes 586 @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are 587 equivalent will be kept in the order in which they currently appear in the array. 588 This is slower to perform, but may be important in some cases. If it's false, a 589 faster algorithm is used, but equivalent elements may be rearranged. 590 */ 591 template <typename ElementComparator> sort(ElementComparator & comparator,UndoManager * undoManager,bool retainOrderOfEquivalentItems)592 void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems) 593 { 594 if (object != nullptr) 595 { 596 OwnedArray<ValueTree> sortedList; 597 createListOfChildren (sortedList); 598 ComparatorAdapter<ElementComparator> adapter (comparator); 599 sortedList.sort (adapter, retainOrderOfEquivalentItems); 600 reorderChildren (sortedList, undoManager); 601 } 602 } 603 604 /** Returns the total number of references to the shared underlying data structure that this 605 ValueTree is using. 606 */ 607 int getReferenceCount() const noexcept; 608 609 /* An invalid ValueTree that can be used if you need to return one as an error condition, etc. 610 @deprecated If you need an empty ValueTree object, just use ValueTree() or {}. 611 */ 612 JUCE_DEPRECATED_STATIC (static const ValueTree invalid;) 613 614 private: 615 //============================================================================== 616 JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject) 617 friend class SharedObject; 618 619 ReferenceCountedObjectPtr<SharedObject> object; 620 ListenerList<Listener> listeners; 621 622 template <typename ElementComparator> 623 struct ComparatorAdapter 624 { ComparatorAdapterComparatorAdapter625 ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {} 626 compareElementsComparatorAdapter627 int compareElements (const ValueTree* const first, const ValueTree* const second) 628 { 629 return comparator.compareElements (*first, *second); 630 } 631 632 private: 633 ElementComparator& comparator; 634 JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter) 635 }; 636 637 void createListOfChildren (OwnedArray<ValueTree>&) const; 638 void reorderChildren (const OwnedArray<ValueTree>&, UndoManager*); 639 640 explicit ValueTree (ReferenceCountedObjectPtr<SharedObject>) noexcept; 641 explicit ValueTree (SharedObject&) noexcept; 642 }; 643 644 } // namespace juce 645