The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

290 lines
8.9KB

  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. #include "../JuceDemoHeader.h"
  20. //==============================================================================
  21. class ValueTreeItem : public TreeViewItem,
  22. private ValueTree::Listener
  23. {
  24. public:
  25. ValueTreeItem (const ValueTree& v, UndoManager& um)
  26. : tree (v), undoManager (um)
  27. {
  28. tree.addListener (this);
  29. }
  30. String getUniqueName() const override
  31. {
  32. return tree["name"].toString();
  33. }
  34. bool mightContainSubItems() override
  35. {
  36. return tree.getNumChildren() > 0;
  37. }
  38. void paintItem (Graphics& g, int width, int height) override
  39. {
  40. g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
  41. Colours::black));
  42. g.setFont (15.0f);
  43. g.drawText (tree["name"].toString(),
  44. 4, 0, width - 4, height,
  45. Justification::centredLeft, true);
  46. }
  47. void itemOpennessChanged (bool isNowOpen) override
  48. {
  49. if (isNowOpen && getNumSubItems() == 0)
  50. refreshSubItems();
  51. else
  52. clearSubItems();
  53. }
  54. var getDragSourceDescription() override
  55. {
  56. return "Drag Demo";
  57. }
  58. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  59. {
  60. return dragSourceDetails.description == "Drag Demo";
  61. }
  62. void itemDropped (const DragAndDropTarget::SourceDetails&, int insertIndex) override
  63. {
  64. OwnedArray<ValueTree> selectedTrees;
  65. getSelectedTreeViewItems (*getOwnerView(), selectedTrees);
  66. moveItems (*getOwnerView(), selectedTrees, tree, insertIndex, undoManager);
  67. }
  68. static void moveItems (TreeView& treeView, const OwnedArray<ValueTree>& items,
  69. ValueTree newParent, int insertIndex, UndoManager& undoManager)
  70. {
  71. if (items.size() > 0)
  72. {
  73. ScopedPointer<XmlElement> oldOpenness (treeView.getOpennessState (false));
  74. for (int i = items.size(); --i >= 0;)
  75. {
  76. ValueTree& v = *items.getUnchecked(i);
  77. if (v.getParent().isValid() && newParent != v && ! newParent.isAChildOf (v))
  78. {
  79. if (v.getParent() == newParent && newParent.indexOf(v) < insertIndex)
  80. --insertIndex;
  81. v.getParent().removeChild (v, &undoManager);
  82. newParent.addChild (v, insertIndex, &undoManager);
  83. }
  84. }
  85. if (oldOpenness != nullptr)
  86. treeView.restoreOpennessState (*oldOpenness, false);
  87. }
  88. }
  89. static void getSelectedTreeViewItems (TreeView& treeView, OwnedArray<ValueTree>& items)
  90. {
  91. const int numSelected = treeView.getNumSelectedItems();
  92. for (int i = 0; i < numSelected; ++i)
  93. if (const ValueTreeItem* vti = dynamic_cast<ValueTreeItem*> (treeView.getSelectedItem (i)))
  94. items.add (new ValueTree (vti->tree));
  95. }
  96. private:
  97. ValueTree tree;
  98. UndoManager& undoManager;
  99. void refreshSubItems()
  100. {
  101. clearSubItems();
  102. for (int i = 0; i < tree.getNumChildren(); ++i)
  103. addSubItem (new ValueTreeItem (tree.getChild (i), undoManager));
  104. }
  105. void valueTreePropertyChanged (ValueTree&, const Identifier&) override
  106. {
  107. repaintItem();
  108. }
  109. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
  110. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { treeChildrenChanged (parentTree); }
  111. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { treeChildrenChanged (parentTree); }
  112. void valueTreeParentChanged (ValueTree&) override {}
  113. void treeChildrenChanged (const ValueTree& parentTree)
  114. {
  115. if (parentTree == tree)
  116. {
  117. refreshSubItems();
  118. treeHasChanged();
  119. setOpen (true);
  120. }
  121. }
  122. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeItem)
  123. };
  124. //==============================================================================
  125. class ValueTreesDemo : public Component,
  126. public DragAndDropContainer,
  127. private Timer
  128. {
  129. public:
  130. ValueTreesDemo()
  131. : undoButton ("Undo"),
  132. redoButton ("Redo")
  133. {
  134. addAndMakeVisible (tree);
  135. tree.setDefaultOpenness (true);
  136. tree.setMultiSelectEnabled (true);
  137. tree.setRootItem (rootItem = new ValueTreeItem (createRootValueTree(), undoManager));
  138. addAndMakeVisible (undoButton);
  139. addAndMakeVisible (redoButton);
  140. undoButton.onClick = [this] { undoManager.undo(); };
  141. redoButton.onClick = [this] { undoManager.redo(); };
  142. startTimer (500);
  143. }
  144. ~ValueTreesDemo()
  145. {
  146. tree.setRootItem (nullptr);
  147. }
  148. void paint (Graphics& g) override
  149. {
  150. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  151. }
  152. void resized() override
  153. {
  154. Rectangle<int> r (getLocalBounds().reduced (8));
  155. Rectangle<int> buttons (r.removeFromBottom (22));
  156. undoButton.setBounds (buttons.removeFromLeft (100));
  157. buttons.removeFromLeft (6);
  158. redoButton.setBounds (buttons.removeFromLeft (100));
  159. r.removeFromBottom (4);
  160. tree.setBounds (r);
  161. }
  162. static ValueTree createTree (const String& desc)
  163. {
  164. ValueTree t ("Item");
  165. t.setProperty ("name", desc, nullptr);
  166. return t;
  167. }
  168. static ValueTree createRootValueTree()
  169. {
  170. ValueTree vt = createTree ("This demo displays a ValueTree as a treeview.");
  171. vt.appendChild (createTree ("You can drag around the nodes to rearrange them"), nullptr);
  172. vt.appendChild (createTree ("..and press 'delete' to delete them"), nullptr);
  173. vt.appendChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), nullptr);
  174. int n = 1;
  175. vt.appendChild (createRandomTree (n, 0), nullptr);
  176. return vt;
  177. }
  178. static ValueTree createRandomTree (int& counter, int depth)
  179. {
  180. ValueTree t = createTree ("Item " + String (counter++));
  181. if (depth < 3)
  182. for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
  183. t.appendChild (createRandomTree (counter, depth + 1), nullptr);
  184. return t;
  185. }
  186. void deleteSelectedItems()
  187. {
  188. OwnedArray<ValueTree> selectedItems;
  189. ValueTreeItem::getSelectedTreeViewItems (tree, selectedItems);
  190. for (int i = selectedItems.size(); --i >= 0;)
  191. {
  192. ValueTree& v = *selectedItems.getUnchecked(i);
  193. if (v.getParent().isValid())
  194. v.getParent().removeChild (v, &undoManager);
  195. }
  196. }
  197. bool keyPressed (const KeyPress& key) override
  198. {
  199. if (key == KeyPress::deleteKey)
  200. {
  201. deleteSelectedItems();
  202. return true;
  203. }
  204. if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
  205. {
  206. undoManager.undo();
  207. return true;
  208. }
  209. if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
  210. {
  211. undoManager.redo();
  212. return true;
  213. }
  214. return Component::keyPressed (key);
  215. }
  216. private:
  217. TreeView tree;
  218. TextButton undoButton, redoButton;
  219. ScopedPointer<ValueTreeItem> rootItem;
  220. UndoManager undoManager;
  221. void timerCallback() override
  222. {
  223. undoManager.beginNewTransaction();
  224. }
  225. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo)
  226. };
  227. // This static object will register this demo type in a global list of demos..
  228. static JuceDemoType<ValueTreesDemo> demo ("40 ValueTrees");