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.

218 lines
6.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace ValueTreeSynchroniserHelpers
  18. {
  19. enum ChangeType
  20. {
  21. propertyChanged = 1,
  22. fullSync = 2,
  23. childAdded = 3,
  24. childRemoved = 4,
  25. childMoved = 5
  26. };
  27. static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
  28. {
  29. while (v != topLevelTree)
  30. {
  31. ValueTree parent (v.getParent());
  32. if (! parent.isValid())
  33. break;
  34. path.add (parent.indexOf (v));
  35. v = parent;
  36. }
  37. }
  38. static void writeHeader (MemoryOutputStream& stream, ChangeType type)
  39. {
  40. stream.writeByte ((char) type);
  41. }
  42. static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
  43. ChangeType type, ValueTree v)
  44. {
  45. writeHeader (stream, type);
  46. Array<int> path;
  47. getValueTreePath (v, target.getRoot(), path);
  48. stream.writeCompressedInt (path.size());
  49. for (int i = path.size(); --i >= 0;)
  50. stream.writeCompressedInt (path.getUnchecked(i));
  51. }
  52. static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
  53. {
  54. const int numLevels = input.readCompressedInt();
  55. if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
  56. return ValueTree();
  57. for (int i = numLevels; --i >= 0;)
  58. {
  59. const int index = input.readCompressedInt();
  60. if (! isPositiveAndBelow (index, v.getNumChildren()))
  61. return ValueTree();
  62. v = v.getChild (index);
  63. }
  64. return v;
  65. }
  66. }
  67. ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
  68. {
  69. valueTree.addListener (this);
  70. }
  71. ValueTreeSynchroniser::~ValueTreeSynchroniser()
  72. {
  73. valueTree.removeListener (this);
  74. }
  75. void ValueTreeSynchroniser::sendFullSyncCallback()
  76. {
  77. MemoryOutputStream m;
  78. writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
  79. valueTree.writeToStream (m);
  80. stateChanged (m.getData(), m.getDataSize());
  81. }
  82. void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
  83. {
  84. MemoryOutputStream m;
  85. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
  86. m.writeString (property.toString());
  87. vt.getProperty (property).writeToStream (m);
  88. stateChanged (m.getData(), m.getDataSize());
  89. }
  90. void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
  91. {
  92. const int index = parentTree.indexOf (childTree);
  93. jassert (index >= 0);
  94. MemoryOutputStream m;
  95. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
  96. m.writeCompressedInt (index);
  97. childTree.writeToStream (m);
  98. stateChanged (m.getData(), m.getDataSize());
  99. }
  100. void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
  101. {
  102. MemoryOutputStream m;
  103. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
  104. m.writeCompressedInt (oldIndex);
  105. stateChanged (m.getData(), m.getDataSize());
  106. }
  107. void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
  108. {
  109. MemoryOutputStream m;
  110. ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
  111. m.writeCompressedInt (oldIndex);
  112. m.writeCompressedInt (newIndex);
  113. stateChanged (m.getData(), m.getDataSize());
  114. }
  115. void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here)
  116. bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
  117. {
  118. MemoryInputStream input (data, dataSize, false);
  119. const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
  120. if (type == ValueTreeSynchroniserHelpers::fullSync)
  121. {
  122. root = ValueTree::readFromStream (input);
  123. return true;
  124. }
  125. ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
  126. if (! v.isValid())
  127. return false;
  128. switch (type)
  129. {
  130. case ValueTreeSynchroniserHelpers::propertyChanged:
  131. {
  132. Identifier property (input.readString());
  133. v.setProperty (property, var::readFromStream (input), undoManager);
  134. return true;
  135. }
  136. case ValueTreeSynchroniserHelpers::childAdded:
  137. {
  138. const int index = input.readCompressedInt();
  139. v.addChild (ValueTree::readFromStream (input), index, undoManager);
  140. return true;
  141. }
  142. case ValueTreeSynchroniserHelpers::childRemoved:
  143. {
  144. const int index = input.readCompressedInt();
  145. if (isPositiveAndBelow (index, v.getNumChildren()))
  146. {
  147. v.removeChild (index, undoManager);
  148. return true;
  149. }
  150. jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
  151. break;
  152. }
  153. case ValueTreeSynchroniserHelpers::childMoved:
  154. {
  155. const int oldIndex = input.readCompressedInt();
  156. const int newIndex = input.readCompressedInt();
  157. if (isPositiveAndBelow (oldIndex, v.getNumChildren())
  158. && isPositiveAndBelow (newIndex, v.getNumChildren()))
  159. {
  160. v.moveChild (oldIndex, newIndex, undoManager);
  161. return true;
  162. }
  163. jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
  164. break;
  165. }
  166. default:
  167. jassertfalse; // Seem to have received some corrupt data?
  168. break;
  169. }
  170. return false;
  171. }