Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
7.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. namespace ValueTreeSynchroniserHelpers
  22. {
  23. enum ChangeType
  24. {
  25. propertyChanged = 1,
  26. fullSync = 2,
  27. childAdded = 3,
  28. childRemoved = 4,
  29. childMoved = 5,
  30. propertyRemoved = 6
  31. };
  32. static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
  33. {
  34. while (v != topLevelTree)
  35. {
  36. ValueTree parent (v.getParent());
  37. if (! parent.isValid())
  38. break;
  39. path.add (parent.indexOf (v));
  40. v = parent;
  41. }
  42. }
  43. static void writeHeader (MemoryOutputStream& stream, ChangeType type)
  44. {
  45. stream.writeByte ((char) type);
  46. }
  47. static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
  48. ChangeType type, ValueTree v)
  49. {
  50. writeHeader (stream, type);
  51. Array<int> path;
  52. getValueTreePath (v, target.getRoot(), path);
  53. stream.writeCompressedInt (path.size());
  54. for (int i = path.size(); --i >= 0;)
  55. stream.writeCompressedInt (path.getUnchecked(i));
  56. }
  57. static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
  58. {
  59. const int numLevels = input.readCompressedInt();
  60. if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
  61. return {};
  62. for (int i = numLevels; --i >= 0;)
  63. {
  64. const int index = input.readCompressedInt();
  65. if (! isPositiveAndBelow (index, v.getNumChildren()))
  66. return {};
  67. v = v.getChild (index);
  68. }
  69. return v;
  70. }
  71. }
  72. ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
  73. {
  74. valueTree.addListener (this);
  75. }
  76. ValueTreeSynchroniser::~ValueTreeSynchroniser()
  77. {
  78. valueTree.removeListener (this);
  79. }
  80. void ValueTreeSynchroniser::sendFullSyncCallback()
  81. {
  82. MemoryOutputStream m;
  83. writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
  84. valueTree.writeToStream (m);
  85. stateChanged (m.getData(), m.getDataSize());
  86. }
  87. void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
  88. {
  89. MemoryOutputStream m;
  90. if (auto* value = vt.getPropertyPointer (property))
  91. {
  92. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
  93. m.writeString (property.toString());
  94. value->writeToStream (m);
  95. }
  96. else
  97. {
  98. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyRemoved, vt);
  99. m.writeString (property.toString());
  100. }
  101. stateChanged (m.getData(), m.getDataSize());
  102. }
  103. void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
  104. {
  105. const int index = parentTree.indexOf (childTree);
  106. jassert (index >= 0);
  107. MemoryOutputStream m;
  108. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
  109. m.writeCompressedInt (index);
  110. childTree.writeToStream (m);
  111. stateChanged (m.getData(), m.getDataSize());
  112. }
  113. void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
  114. {
  115. MemoryOutputStream m;
  116. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
  117. m.writeCompressedInt (oldIndex);
  118. stateChanged (m.getData(), m.getDataSize());
  119. }
  120. void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
  121. {
  122. MemoryOutputStream m;
  123. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
  124. m.writeCompressedInt (oldIndex);
  125. m.writeCompressedInt (newIndex);
  126. stateChanged (m.getData(), m.getDataSize());
  127. }
  128. void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here)
  129. bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
  130. {
  131. MemoryInputStream input (data, dataSize, false);
  132. const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
  133. if (type == ValueTreeSynchroniserHelpers::fullSync)
  134. {
  135. root = ValueTree::readFromStream (input);
  136. return true;
  137. }
  138. ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
  139. if (! v.isValid())
  140. return false;
  141. switch (type)
  142. {
  143. case ValueTreeSynchroniserHelpers::propertyChanged:
  144. {
  145. Identifier property (input.readString());
  146. v.setProperty (property, var::readFromStream (input), undoManager);
  147. return true;
  148. }
  149. case ValueTreeSynchroniserHelpers::propertyRemoved:
  150. {
  151. Identifier property (input.readString());
  152. v.removeProperty (property, undoManager);
  153. return true;
  154. }
  155. case ValueTreeSynchroniserHelpers::childAdded:
  156. {
  157. const int index = input.readCompressedInt();
  158. v.addChild (ValueTree::readFromStream (input), index, undoManager);
  159. return true;
  160. }
  161. case ValueTreeSynchroniserHelpers::childRemoved:
  162. {
  163. const int index = input.readCompressedInt();
  164. if (isPositiveAndBelow (index, v.getNumChildren()))
  165. {
  166. v.removeChild (index, undoManager);
  167. return true;
  168. }
  169. jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
  170. break;
  171. }
  172. case ValueTreeSynchroniserHelpers::childMoved:
  173. {
  174. const int oldIndex = input.readCompressedInt();
  175. const int newIndex = input.readCompressedInt();
  176. if (isPositiveAndBelow (oldIndex, v.getNumChildren())
  177. && isPositiveAndBelow (newIndex, v.getNumChildren()))
  178. {
  179. v.moveChild (oldIndex, newIndex, undoManager);
  180. return true;
  181. }
  182. jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
  183. break;
  184. }
  185. default:
  186. jassertfalse; // Seem to have received some corrupt data?
  187. break;
  188. }
  189. return false;
  190. }
  191. } // namespace juce