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