2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 namespace ValueTreeSynchroniserHelpers
41 static void getValueTreePath (ValueTree v
, const ValueTree
& topLevelTree
, Array
<int>& path
)
43 while (v
!= topLevelTree
)
45 ValueTree
parent (v
.getParent());
47 if (! parent
.isValid())
50 path
.add (parent
.indexOf (v
));
55 static void writeHeader (MemoryOutputStream
& stream
, ChangeType type
)
57 stream
.writeByte ((char) type
);
60 static void writeHeader (ValueTreeSynchroniser
& target
, MemoryOutputStream
& stream
,
61 ChangeType type
, ValueTree v
)
63 writeHeader (stream
, type
);
66 getValueTreePath (v
, target
.getRoot(), path
);
68 stream
.writeCompressedInt (path
.size());
70 for (int i
= path
.size(); --i
>= 0;)
71 stream
.writeCompressedInt (path
.getUnchecked(i
));
74 static ValueTree
readSubTreeLocation (MemoryInputStream
& input
, ValueTree v
)
76 const int numLevels
= input
.readCompressedInt();
78 if (! isPositiveAndBelow (numLevels
, 65536)) // sanity-check
81 for (int i
= numLevels
; --i
>= 0;)
83 const int index
= input
.readCompressedInt();
85 if (! isPositiveAndBelow (index
, v
.getNumChildren()))
88 v
= v
.getChild (index
);
95 ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree
& tree
) : valueTree (tree
)
97 valueTree
.addListener (this);
100 ValueTreeSynchroniser::~ValueTreeSynchroniser()
102 valueTree
.removeListener (this);
105 void ValueTreeSynchroniser::sendFullSyncCallback()
107 MemoryOutputStream m
;
108 writeHeader (m
, ValueTreeSynchroniserHelpers::fullSync
);
109 valueTree
.writeToStream (m
);
110 stateChanged (m
.getData(), m
.getDataSize());
113 void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree
& vt
, const Identifier
& property
)
115 MemoryOutputStream m
;
117 if (auto* value
= vt
.getPropertyPointer (property
))
119 ValueTreeSynchroniserHelpers::writeHeader (*this, m
, ValueTreeSynchroniserHelpers::propertyChanged
, vt
);
120 m
.writeString (property
.toString());
121 value
->writeToStream (m
);
125 ValueTreeSynchroniserHelpers::writeHeader (*this, m
, ValueTreeSynchroniserHelpers::propertyRemoved
, vt
);
126 m
.writeString (property
.toString());
129 stateChanged (m
.getData(), m
.getDataSize());
132 void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree
& parentTree
, ValueTree
& childTree
)
134 const int index
= parentTree
.indexOf (childTree
);
135 jassert (index
>= 0);
137 MemoryOutputStream m
;
138 ValueTreeSynchroniserHelpers::writeHeader (*this, m
, ValueTreeSynchroniserHelpers::childAdded
, parentTree
);
139 m
.writeCompressedInt (index
);
140 childTree
.writeToStream (m
);
141 stateChanged (m
.getData(), m
.getDataSize());
144 void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree
& parentTree
, ValueTree
&, int oldIndex
)
146 MemoryOutputStream m
;
147 ValueTreeSynchroniserHelpers::writeHeader (*this, m
, ValueTreeSynchroniserHelpers::childRemoved
, parentTree
);
148 m
.writeCompressedInt (oldIndex
);
149 stateChanged (m
.getData(), m
.getDataSize());
152 void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree
& parent
, int oldIndex
, int newIndex
)
154 MemoryOutputStream m
;
155 ValueTreeSynchroniserHelpers::writeHeader (*this, m
, ValueTreeSynchroniserHelpers::childMoved
, parent
);
156 m
.writeCompressedInt (oldIndex
);
157 m
.writeCompressedInt (newIndex
);
158 stateChanged (m
.getData(), m
.getDataSize());
161 bool ValueTreeSynchroniser::applyChange (ValueTree
& root
, const void* data
, size_t dataSize
, UndoManager
* undoManager
)
163 MemoryInputStream
input (data
, dataSize
, false);
165 const ValueTreeSynchroniserHelpers::ChangeType type
= (ValueTreeSynchroniserHelpers::ChangeType
) input
.readByte();
167 if (type
== ValueTreeSynchroniserHelpers::fullSync
)
169 root
= ValueTree::readFromStream (input
);
173 ValueTree
v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input
, root
));
180 case ValueTreeSynchroniserHelpers::propertyChanged
:
182 Identifier
property (input
.readString());
183 v
.setProperty (property
, var::readFromStream (input
), undoManager
);
187 case ValueTreeSynchroniserHelpers::propertyRemoved
:
189 Identifier
property (input
.readString());
190 v
.removeProperty (property
, undoManager
);
194 case ValueTreeSynchroniserHelpers::childAdded
:
196 const int index
= input
.readCompressedInt();
197 v
.addChild (ValueTree::readFromStream (input
), index
, undoManager
);
201 case ValueTreeSynchroniserHelpers::childRemoved
:
203 const int index
= input
.readCompressedInt();
205 if (isPositiveAndBelow (index
, v
.getNumChildren()))
207 v
.removeChild (index
, undoManager
);
211 jassertfalse
; // Either received some corrupt data, or the trees have drifted out of sync
215 case ValueTreeSynchroniserHelpers::childMoved
:
217 const int oldIndex
= input
.readCompressedInt();
218 const int newIndex
= input
.readCompressedInt();
220 if (isPositiveAndBelow (oldIndex
, v
.getNumChildren())
221 && isPositiveAndBelow (newIndex
, v
.getNumChildren()))
223 v
.moveChild (oldIndex
, newIndex
, undoManager
);
227 jassertfalse
; // Either received some corrupt data, or the trees have drifted out of sync
231 case ValueTreeSynchroniserHelpers::fullSync
:
235 jassertfalse
; // Seem to have received some corrupt data?