|
|
|
@@ -27,18 +27,16 @@ class ValueTree::SharedObject : public ReferenceCountedObject |
|
|
|
public:
|
|
|
|
typedef ReferenceCountedObjectPtr<SharedObject> Ptr;
|
|
|
|
|
|
|
|
explicit SharedObject (const Identifier& t) noexcept
|
|
|
|
: type (t), parent (nullptr)
|
|
|
|
explicit SharedObject (const Identifier& t) noexcept : type (t)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SharedObject (const SharedObject& other)
|
|
|
|
: ReferenceCountedObject(),
|
|
|
|
type (other.type), properties (other.properties), parent (nullptr)
|
|
|
|
: ReferenceCountedObject(), type (other.type), properties (other.properties)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < other.children.size(); ++i)
|
|
|
|
{
|
|
|
|
SharedObject* const child = new SharedObject (*other.children.getObjectPointerUnchecked(i));
|
|
|
|
auto child = new SharedObject (*other.children.getObjectPointerUnchecked(i));
|
|
|
|
child->parent = this;
|
|
|
|
children.add (child);
|
|
|
|
}
|
|
|
|
@@ -62,132 +60,62 @@ public: |
|
|
|
return parent == nullptr ? this : parent->getRoot();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Method>
|
|
|
|
void callListeners (Method method, ValueTree& tree) const
|
|
|
|
template <typename Function>
|
|
|
|
void callListeners (Function fn) const
|
|
|
|
{
|
|
|
|
const int numListeners = valueTreesWithListeners.size();
|
|
|
|
auto numListeners = valueTreesWithListeners.size();
|
|
|
|
|
|
|
|
if (numListeners == 1)
|
|
|
|
{
|
|
|
|
valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree);
|
|
|
|
fn (valueTreesWithListeners.getUnchecked(0)->listeners);
|
|
|
|
}
|
|
|
|
else if (numListeners > 0)
|
|
|
|
{
|
|
|
|
const SortedSet<ValueTree*> listenersCopy (valueTreesWithListeners);
|
|
|
|
auto listenersCopy = valueTreesWithListeners;
|
|
|
|
|
|
|
|
for (int i = 0; i < numListeners; ++i)
|
|
|
|
{
|
|
|
|
ValueTree* const v = listenersCopy.getUnchecked(i);
|
|
|
|
auto* v = listenersCopy.getUnchecked(i);
|
|
|
|
|
|
|
|
if (i == 0 || valueTreesWithListeners.contains (v))
|
|
|
|
v->listeners.call (method, tree);
|
|
|
|
fn (v->listeners);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Method, typename ParamType>
|
|
|
|
void callListeners (Method method, ValueTree& tree, ParamType& param2) const
|
|
|
|
template <typename Function>
|
|
|
|
void callListenersForAllParents (Function fn) const
|
|
|
|
{
|
|
|
|
const int numListeners = valueTreesWithListeners.size();
|
|
|
|
|
|
|
|
if (numListeners == 1)
|
|
|
|
{
|
|
|
|
valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2);
|
|
|
|
}
|
|
|
|
else if (numListeners > 0)
|
|
|
|
{
|
|
|
|
const SortedSet<ValueTree*> listenersCopy (valueTreesWithListeners);
|
|
|
|
|
|
|
|
for (int i = 0; i < numListeners; ++i)
|
|
|
|
{
|
|
|
|
ValueTree* const v = listenersCopy.getUnchecked(i);
|
|
|
|
|
|
|
|
if (i == 0 || valueTreesWithListeners.contains (v))
|
|
|
|
v->listeners.call (method, tree, param2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Method, typename ParamType>
|
|
|
|
void callListenersExcluding (ValueTree::Listener* listenerToExclude,
|
|
|
|
Method method, ValueTree& tree, ParamType& param2) const
|
|
|
|
{
|
|
|
|
const int numListeners = valueTreesWithListeners.size();
|
|
|
|
|
|
|
|
if (numListeners == 1)
|
|
|
|
{
|
|
|
|
valueTreesWithListeners.getUnchecked(0)->listeners.callExcluding (*listenerToExclude, method, tree, param2);
|
|
|
|
}
|
|
|
|
else if (numListeners > 0)
|
|
|
|
{
|
|
|
|
const SortedSet<ValueTree*> listenersCopy (valueTreesWithListeners);
|
|
|
|
|
|
|
|
for (int i = 0; i < numListeners; ++i)
|
|
|
|
{
|
|
|
|
ValueTree* const v = listenersCopy.getUnchecked(i);
|
|
|
|
|
|
|
|
if (i == 0 || valueTreesWithListeners.contains (v))
|
|
|
|
v->listeners.callExcluding (*listenerToExclude, method, tree, param2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Method, typename ParamType1, typename ParamType2>
|
|
|
|
void callListeners (Method method, ValueTree& tree, ParamType1& param2, ParamType2& param3) const
|
|
|
|
{
|
|
|
|
const int numListeners = valueTreesWithListeners.size();
|
|
|
|
|
|
|
|
if (numListeners == 1)
|
|
|
|
{
|
|
|
|
valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2, param3);
|
|
|
|
}
|
|
|
|
else if (numListeners > 0)
|
|
|
|
{
|
|
|
|
const SortedSet<ValueTree*> listenersCopy (valueTreesWithListeners);
|
|
|
|
|
|
|
|
for (int i = 0; i < numListeners; ++i)
|
|
|
|
{
|
|
|
|
ValueTree* const v = listenersCopy.getUnchecked(i);
|
|
|
|
|
|
|
|
if (i == 0 || valueTreesWithListeners.contains (v))
|
|
|
|
v->listeners.call (method, tree, param2, param3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto* t = this; t != nullptr; t = t->parent)
|
|
|
|
t->callListeners (fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
|
|
|
|
{
|
|
|
|
ValueTree tree (this);
|
|
|
|
|
|
|
|
for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent)
|
|
|
|
if (listenerToExclude == nullptr)
|
|
|
|
t->callListeners (&ValueTree::Listener::valueTreePropertyChanged, tree, property);
|
|
|
|
else
|
|
|
|
t->callListenersExcluding (listenerToExclude, &ValueTree::Listener::valueTreePropertyChanged, tree, property);
|
|
|
|
if (listenerToExclude == nullptr)
|
|
|
|
callListenersForAllParents ([&] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property); });
|
|
|
|
else
|
|
|
|
callListenersForAllParents ([&] (ListenerList<Listener>& list) { list.callExcluding (*listenerToExclude, &ValueTree::Listener::valueTreePropertyChanged, tree, property); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendChildAddedMessage (ValueTree child)
|
|
|
|
{
|
|
|
|
ValueTree tree (this);
|
|
|
|
|
|
|
|
for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent)
|
|
|
|
t->callListeners (&ValueTree::Listener::valueTreeChildAdded, tree, child);
|
|
|
|
callListenersForAllParents ([&] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendChildRemovedMessage (ValueTree child, int index)
|
|
|
|
{
|
|
|
|
ValueTree tree (this);
|
|
|
|
|
|
|
|
for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent)
|
|
|
|
t->callListeners (&ValueTree::Listener::valueTreeChildRemoved, tree, child, index);
|
|
|
|
callListenersForAllParents ([=, &tree, &child] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child, index); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendChildOrderChangedMessage (int oldIndex, int newIndex)
|
|
|
|
{
|
|
|
|
ValueTree tree (this);
|
|
|
|
|
|
|
|
for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent)
|
|
|
|
t->callListeners (&ValueTree::Listener::valueTreeChildOrderChanged, tree, oldIndex, newIndex);
|
|
|
|
callListenersForAllParents ([=, &tree] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree, oldIndex, newIndex); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendParentChangeMessage()
|
|
|
|
@@ -195,10 +123,10 @@ public: |
|
|
|
ValueTree tree (this);
|
|
|
|
|
|
|
|
for (int j = children.size(); --j >= 0;)
|
|
|
|
if (SharedObject* const child = children.getObjectPointer (j))
|
|
|
|
if (auto* child = children.getObjectPointer (j))
|
|
|
|
child->sendParentChangeMessage();
|
|
|
|
|
|
|
|
callListeners (&ValueTree::Listener::valueTreeParentChanged, tree);
|
|
|
|
callListeners ([&] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeParentChanged, tree); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager,
|
|
|
|
@@ -211,14 +139,14 @@ public: |
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (const var* const existingValue = properties.getVarPointer (name))
|
|
|
|
if (auto* existingValue = properties.getVarPointer (name))
|
|
|
|
{
|
|
|
|
if (*existingValue != newValue)
|
|
|
|
undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false, listenerToExclude));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false, listenerToExclude));
|
|
|
|
undoManager->perform (new SetPropertyAction (this, name, newValue, {}, true, false, listenerToExclude));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -238,7 +166,7 @@ public: |
|
|
|
else
|
|
|
|
{
|
|
|
|
if (properties.contains (name))
|
|
|
|
undoManager->perform (new SetPropertyAction (this, name, var(), properties [name], false, true));
|
|
|
|
undoManager->perform (new SetPropertyAction (this, name, {}, properties [name], false, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -248,7 +176,7 @@ public: |
|
|
|
{
|
|
|
|
while (properties.size() > 0)
|
|
|
|
{
|
|
|
|
const Identifier name (properties.getName (properties.size() - 1));
|
|
|
|
auto name = properties.getName (properties.size() - 1);
|
|
|
|
properties.remove (name);
|
|
|
|
sendPropertyChangeMessage (name);
|
|
|
|
}
|
|
|
|
@@ -256,7 +184,7 @@ public: |
|
|
|
else
|
|
|
|
{
|
|
|
|
for (int i = properties.size(); --i >= 0;)
|
|
|
|
undoManager->perform (new SetPropertyAction (this, properties.getName(i), var(),
|
|
|
|
undoManager->perform (new SetPropertyAction (this, properties.getName(i), {},
|
|
|
|
properties.getValueAt(i), false, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -273,26 +201,20 @@ public: |
|
|
|
|
|
|
|
ValueTree getChildWithName (const Identifier& typeToMatch) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < children.size(); ++i)
|
|
|
|
{
|
|
|
|
SharedObject* const s = children.getObjectPointerUnchecked (i);
|
|
|
|
for (auto* s : children)
|
|
|
|
if (s->type == typeToMatch)
|
|
|
|
return ValueTree (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ValueTree();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < children.size(); ++i)
|
|
|
|
{
|
|
|
|
SharedObject* const s = children.getObjectPointerUnchecked (i);
|
|
|
|
for (auto* s : children)
|
|
|
|
if (s->type == typeToMatch)
|
|
|
|
return ValueTree (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
SharedObject* const newObject = new SharedObject (typeToMatch);
|
|
|
|
auto newObject = new SharedObject (typeToMatch);
|
|
|
|
addChild (newObject, -1, undoManager);
|
|
|
|
return ValueTree (newObject);
|
|
|
|
|
|
|
|
@@ -300,19 +222,16 @@ public: |
|
|
|
|
|
|
|
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < children.size(); ++i)
|
|
|
|
{
|
|
|
|
SharedObject* const s = children.getObjectPointerUnchecked (i);
|
|
|
|
for (auto* s : children)
|
|
|
|
if (s->properties[propertyName] == propertyValue)
|
|
|
|
return ValueTree (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ValueTree();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAChildOf (const SharedObject* const possibleParent) const noexcept
|
|
|
|
{
|
|
|
|
for (const SharedObject* p = parent; p != nullptr; p = p->parent)
|
|
|
|
for (auto* p = parent; p != nullptr; p = p->parent)
|
|
|
|
if (p == possibleParent)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
@@ -418,7 +337,7 @@ public: |
|
|
|
|
|
|
|
for (int i = 0; i < children.size(); ++i)
|
|
|
|
{
|
|
|
|
SharedObject* const child = newOrder.getUnchecked(i)->object;
|
|
|
|
auto* child = newOrder.getUnchecked(i)->object.get();
|
|
|
|
|
|
|
|
if (children.getObjectPointerUnchecked (i) != child)
|
|
|
|
{
|
|
|
|
@@ -446,7 +365,7 @@ public: |
|
|
|
|
|
|
|
XmlElement* createXml() const
|
|
|
|
{
|
|
|
|
XmlElement* const xml = new XmlElement (type);
|
|
|
|
auto xml = new XmlElement (type);
|
|
|
|
properties.copyToXmlAttributes (*xml);
|
|
|
|
|
|
|
|
// (NB: it's faster to add nodes to XML elements in reverse order)
|
|
|
|
@@ -629,7 +548,7 @@ public: |
|
|
|
|
|
|
|
UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
|
|
|
|
{
|
|
|
|
if (MoveChildAction* next = dynamic_cast<MoveChildAction*> (nextAction))
|
|
|
|
if (auto* next = dynamic_cast<MoveChildAction*> (nextAction))
|
|
|
|
if (next->parent == parent && next->startIndex == endIndex)
|
|
|
|
return new MoveChildAction (parent, startIndex, next->endIndex);
|
|
|
|
|
|
|
|
@@ -648,7 +567,7 @@ public: |
|
|
|
NamedValueSet properties;
|
|
|
|
ReferenceCountedArray<SharedObject> children;
|
|
|
|
SortedSet<ValueTree*> valueTreesWithListeners;
|
|
|
|
SharedObject* parent;
|
|
|
|
SharedObject* parent = nullptr;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SharedObject& operator= (const SharedObject&);
|
|
|
|
@@ -760,7 +679,7 @@ ValueTree ValueTree::getRoot() const noexcept |
|
|
|
ValueTree ValueTree::getSibling (const int delta) const noexcept
|
|
|
|
{
|
|
|
|
if (object == nullptr || object->parent == nullptr)
|
|
|
|
return ValueTree();
|
|
|
|
return {};
|
|
|
|
|
|
|
|
const int index = object->parent->indexOf (*this) + delta;
|
|
|
|
return ValueTree (object->parent->children.getObjectPointer (index));
|
|
|
|
@@ -1054,7 +973,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) |
|
|
|
|
|
|
|
// ValueTrees don't have any equivalent to XML text elements!
|
|
|
|
jassertfalse;
|
|
|
|
return ValueTree();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
String ValueTree::toXmlString() const
|
|
|
|
@@ -1071,10 +990,10 @@ void ValueTree::writeToStream (OutputStream& output) const |
|
|
|
|
|
|
|
ValueTree ValueTree::readFromStream (InputStream& input)
|
|
|
|
{
|
|
|
|
const String type (input.readString());
|
|
|
|
auto type = input.readString();
|
|
|
|
|
|
|
|
if (type.isEmpty())
|
|
|
|
return ValueTree();
|
|
|
|
return {};
|
|
|
|
|
|
|
|
ValueTree v (type);
|
|
|
|
|
|
|
|
|