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.

299 lines
9.1KB

  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 ButtonListener,
  128. private Timer
  129. {
  130. public:
  131. ValueTreesDemo()
  132. : undoButton ("Undo"),
  133. redoButton ("Redo")
  134. {
  135. addAndMakeVisible (tree);
  136. tree.setDefaultOpenness (true);
  137. tree.setMultiSelectEnabled (true);
  138. tree.setRootItem (rootItem = new ValueTreeItem (createRootValueTree(), undoManager));
  139. addAndMakeVisible (undoButton);
  140. addAndMakeVisible (redoButton);
  141. undoButton.addListener (this);
  142. redoButton.addListener (this);
  143. startTimer (500);
  144. }
  145. ~ValueTreesDemo()
  146. {
  147. tree.setRootItem (nullptr);
  148. }
  149. void paint (Graphics& g) override
  150. {
  151. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  152. }
  153. void resized() override
  154. {
  155. Rectangle<int> r (getLocalBounds().reduced (8));
  156. Rectangle<int> buttons (r.removeFromBottom (22));
  157. undoButton.setBounds (buttons.removeFromLeft (100));
  158. buttons.removeFromLeft (6);
  159. redoButton.setBounds (buttons.removeFromLeft (100));
  160. r.removeFromBottom (4);
  161. tree.setBounds (r);
  162. }
  163. static ValueTree createTree (const String& desc)
  164. {
  165. ValueTree t ("Item");
  166. t.setProperty ("name", desc, nullptr);
  167. return t;
  168. }
  169. static ValueTree createRootValueTree()
  170. {
  171. ValueTree vt = createTree ("This demo displays a ValueTree as a treeview.");
  172. vt.addChild (createTree ("You can drag around the nodes to rearrange them"), -1, nullptr);
  173. vt.addChild (createTree ("..and press 'delete' to delete them"), -1, nullptr);
  174. vt.addChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), -1, nullptr);
  175. int n = 1;
  176. vt.addChild (createRandomTree (n, 0), -1, nullptr);
  177. return vt;
  178. }
  179. static ValueTree createRandomTree (int& counter, int depth)
  180. {
  181. ValueTree t = createTree ("Item " + String (counter++));
  182. if (depth < 3)
  183. for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
  184. t.addChild (createRandomTree (counter, depth + 1), -1, nullptr);
  185. return t;
  186. }
  187. void deleteSelectedItems()
  188. {
  189. OwnedArray<ValueTree> selectedItems;
  190. ValueTreeItem::getSelectedTreeViewItems (tree, selectedItems);
  191. for (int i = selectedItems.size(); --i >= 0;)
  192. {
  193. ValueTree& v = *selectedItems.getUnchecked(i);
  194. if (v.getParent().isValid())
  195. v.getParent().removeChild (v, &undoManager);
  196. }
  197. }
  198. bool keyPressed (const KeyPress& key) override
  199. {
  200. if (key == KeyPress::deleteKey)
  201. {
  202. deleteSelectedItems();
  203. return true;
  204. }
  205. if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
  206. {
  207. undoManager.undo();
  208. return true;
  209. }
  210. if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
  211. {
  212. undoManager.redo();
  213. return true;
  214. }
  215. return Component::keyPressed (key);
  216. }
  217. void buttonClicked (Button* b) override
  218. {
  219. if (b == &undoButton)
  220. undoManager.undo();
  221. else if (b == &redoButton)
  222. undoManager.redo();
  223. }
  224. private:
  225. TreeView tree;
  226. TextButton undoButton, redoButton;
  227. ScopedPointer<ValueTreeItem> rootItem;
  228. UndoManager undoManager;
  229. void timerCallback() override
  230. {
  231. undoManager.beginNewTransaction();
  232. }
  233. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo)
  234. };
  235. // This static object will register this demo type in a global list of demos..
  236. static JuceDemoType<ValueTreesDemo> demo ("40 ValueTrees");