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.

397 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCE_COMPONENTEDITORTREEVIEW_H_F3B95A41__
  19. #define __JUCE_COMPONENTEDITORTREEVIEW_H_F3B95A41__
  20. //==============================================================================
  21. namespace ComponentEditorTreeView
  22. {
  23. //==============================================================================
  24. class Base : public JucerTreeViewBase,
  25. public ValueTree::Listener,
  26. public ChangeListener
  27. {
  28. public:
  29. Base (ComponentEditor& editor_)
  30. : editor (editor_)
  31. {
  32. editor.getSelection().addChangeListener (this);
  33. }
  34. ~Base()
  35. {
  36. editor.getSelection().removeChangeListener (this);
  37. }
  38. //==============================================================================
  39. void valueTreePropertyChanged (ValueTree& tree, const Identifier& property) {}
  40. void valueTreeParentChanged (ValueTree& tree) {}
  41. void valueTreeChildrenChanged (ValueTree& tree) {}
  42. const String getUniqueName() const
  43. {
  44. jassert (getItemId().isNotEmpty());
  45. return getItemId();
  46. }
  47. //==============================================================================
  48. void itemOpennessChanged (bool isNowOpen)
  49. {
  50. if (isNowOpen)
  51. refreshSubItems();
  52. }
  53. virtual void refreshSubItems() = 0;
  54. virtual const String getItemId() const = 0;
  55. void setName (const String& newName) {}
  56. void itemClicked (const MouseEvent& e) {}
  57. void itemDoubleClicked (const MouseEvent& e) {}
  58. void itemSelectionChanged (bool isNowSelected)
  59. {
  60. if (isNowSelected)
  61. editor.getSelection().addToSelection (getItemId());
  62. else
  63. editor.getSelection().deselect (getItemId());
  64. }
  65. void changeListenerCallback (void*) { updateSelectionState(); }
  66. void updateSelectionState()
  67. {
  68. setSelected (editor.getSelection().isSelected (getItemId()), false);
  69. }
  70. bool isMissing() { return false; }
  71. const String getTooltip() { return String::empty; }
  72. protected:
  73. ComponentEditor& editor;
  74. };
  75. static const String getDragIdFor (ComponentEditor& editor)
  76. {
  77. return componentItemDragType + editor.getDocument().getUniqueId();
  78. }
  79. //==============================================================================
  80. class ComponentItem : public Base
  81. {
  82. public:
  83. ComponentItem (ComponentEditor& editor_, const ValueTree& componentState_)
  84. : Base (editor_), componentState (componentState_)
  85. {
  86. componentState.addListener (this);
  87. updateSelectionState();
  88. }
  89. ~ComponentItem()
  90. {
  91. componentState.removeListener (this);
  92. }
  93. //==============================================================================
  94. const String getItemId() const { return componentState [ComponentDocument::idProperty]; }
  95. bool mightContainSubItems() { return false; }
  96. void refreshSubItems() {}
  97. const String getDisplayName() const { return getRenamingName(); }
  98. const String getRenamingName() const { return componentState [ComponentDocument::memberNameProperty]; }
  99. const Image getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultDocumentFileImage(); }
  100. const String getDragSourceDescription() { return getDragIdFor (editor); }
  101. void valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
  102. {
  103. if (property == ComponentDocument::memberNameProperty)
  104. repaintItem();
  105. }
  106. ValueTree componentState;
  107. };
  108. //==============================================================================
  109. class ComponentList : public Base
  110. {
  111. public:
  112. ComponentList (ComponentEditor& editor_)
  113. : Base (editor_), componentTree (editor.getDocument().getComponentGroup())
  114. {
  115. componentTree.addListener (this);
  116. }
  117. ~ComponentList()
  118. {
  119. componentTree.removeListener (this);
  120. }
  121. //==============================================================================
  122. const String getItemId() const { return "components"; }
  123. bool mightContainSubItems() { return true; }
  124. void valueTreeChildrenChanged (ValueTree& tree)
  125. {
  126. if (tree == componentTree)
  127. refreshSubItems();
  128. }
  129. void refreshSubItems()
  130. {
  131. ScopedPointer <XmlElement> oldOpenness (getOpennessState());
  132. clearSubItems();
  133. ComponentDocument& doc = editor.getDocument();
  134. const int num = doc.getNumComponents();
  135. for (int i = 0; i < num; ++i)
  136. addSubItem (new ComponentItem (editor, doc.getComponent (i)));
  137. if (oldOpenness != 0)
  138. restoreOpennessState (*oldOpenness);
  139. }
  140. const String getDisplayName() const { return getRenamingName(); }
  141. const String getRenamingName() const { return "Components"; }
  142. const Image getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  143. const String getDragSourceDescription() { return String::empty; }
  144. bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent)
  145. {
  146. return sourceDescription == getDragIdFor (editor)
  147. && editor.getSelection().getNumSelected() > 0;
  148. }
  149. void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex)
  150. {
  151. if (editor.getSelection().getNumSelected() > 0)
  152. {
  153. TreeView* tree = getOwnerView();
  154. const ScopedPointer <XmlElement> oldOpenness (tree->getOpennessState (false));
  155. Array <ValueTree> selectedComps;
  156. // scan the source tree rather than look at the selection manager, because it might
  157. // be from a different editor, and the order needs to be correct.
  158. getAllSelectedNodesInTree (sourceComponent, selectedComps);
  159. insertItems (selectedComps, insertIndex);
  160. if (oldOpenness != 0)
  161. tree->restoreOpennessState (*oldOpenness);
  162. }
  163. }
  164. static void getAllSelectedNodesInTree (Component* componentInTree, Array<ValueTree>& selectedComps)
  165. {
  166. TreeView* tree = dynamic_cast <TreeView*> (componentInTree);
  167. if (tree == 0)
  168. tree = componentInTree->findParentComponentOfClass ((TreeView*) 0);
  169. if (tree != 0)
  170. {
  171. const int numSelected = tree->getNumSelectedItems();
  172. for (int i = 0; i < numSelected; ++i)
  173. {
  174. const ComponentItem* const item = dynamic_cast <ComponentItem*> (tree->getSelectedItem (i));
  175. if (item != 0)
  176. selectedComps.add (item->componentState);
  177. }
  178. }
  179. }
  180. void insertItems (Array <ValueTree>& comps, int insertIndex)
  181. {
  182. int i;
  183. for (i = comps.size(); --i >= 0;)
  184. if (componentTree == comps.getReference(i) || componentTree.isAChildOf (comps.getReference(i))) // Check for recursion.
  185. return;
  186. // Don't include any nodes that are children of other selected nodes..
  187. for (i = comps.size(); --i >= 0;)
  188. {
  189. const ValueTree& n = comps.getReference(i);
  190. for (int j = comps.size(); --j >= 0;)
  191. {
  192. if (j != i && n.isAChildOf (comps.getReference(j)))
  193. {
  194. comps.remove (i);
  195. break;
  196. }
  197. }
  198. }
  199. // Remove and re-insert them one at a time..
  200. for (i = 0; i < comps.size(); ++i)
  201. {
  202. ValueTree& n = comps.getReference(i);
  203. if (n.getParent() == componentTree && componentTree.indexOf (n) < insertIndex)
  204. --insertIndex;
  205. if (n.getParent() == componentTree)
  206. {
  207. n.getParent().moveChild (componentTree.indexOf (n), insertIndex++, editor.getDocument().getUndoManager());
  208. }
  209. else
  210. {
  211. n.getParent().removeChild (n, editor.getDocument().getUndoManager());
  212. componentTree.addChild (n, insertIndex++, editor.getDocument().getUndoManager());
  213. }
  214. }
  215. }
  216. private:
  217. ValueTree componentTree;
  218. };
  219. //==============================================================================
  220. class MarkerItem : public Base
  221. {
  222. public:
  223. MarkerItem (ComponentEditor& editor_, const ValueTree& markerState_)
  224. : Base (editor_), markerState (markerState_)
  225. {
  226. markerState.addListener (this);
  227. updateSelectionState();
  228. }
  229. ~MarkerItem()
  230. {
  231. markerState.removeListener (this);
  232. }
  233. //==============================================================================
  234. const String getItemId() const { return markerState [Ids::id_]; }
  235. bool mightContainSubItems() { return false; }
  236. void refreshSubItems() {}
  237. const String getDisplayName() const { return getRenamingName(); }
  238. const String getRenamingName() const { return markerState [MarkerListBase::getMarkerNameProperty()]; }
  239. const Image getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultDocumentFileImage(); }
  240. const String getDragSourceDescription() { return String::empty; }
  241. void valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
  242. {
  243. if (property == MarkerListBase::getMarkerNameProperty())
  244. repaintItem();
  245. }
  246. private:
  247. ValueTree markerState;
  248. };
  249. //==============================================================================
  250. class MarkerList : public Base
  251. {
  252. public:
  253. MarkerList (ComponentEditor& editor_, bool isX_)
  254. : Base (editor_), markerList (editor_.getDocument().getMarkerList (isX_).getGroup()), isX (isX_)
  255. {
  256. markerList.addListener (this);
  257. }
  258. ~MarkerList()
  259. {
  260. markerList.removeListener (this);
  261. }
  262. //==============================================================================
  263. const String getItemId() const { return isX ? "markersX" : "markersY"; }
  264. bool mightContainSubItems() { return true; }
  265. void valueTreeChildrenChanged (ValueTree&)
  266. {
  267. refreshSubItems();
  268. }
  269. void refreshSubItems()
  270. {
  271. ScopedPointer <XmlElement> oldOpenness (getOpennessState());
  272. clearSubItems();
  273. ComponentDocument::MarkerList& markers = editor.getDocument().getMarkerList (isX);
  274. const int num = markers.size();
  275. for (int i = 0; i < num; ++i)
  276. addSubItem (new MarkerItem (editor, markers.getMarker (i)));
  277. if (oldOpenness != 0)
  278. restoreOpennessState (*oldOpenness);
  279. }
  280. const String getDisplayName() const { return getRenamingName(); }
  281. const String getRenamingName() const { return isX ? "Markers (X-axis)" : "Markers (Y-axis)"; }
  282. const Image getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  283. const String getDragSourceDescription() { return String::empty; }
  284. private:
  285. ValueTree markerList;
  286. bool isX;
  287. };
  288. //==============================================================================
  289. class Root : public Base
  290. {
  291. public:
  292. Root (ComponentEditor& editor_) : Base (editor_) {}
  293. ~Root() {}
  294. //==============================================================================
  295. const String getItemId() const { return "root"; }
  296. bool mightContainSubItems() { return true; }
  297. void refreshSubItems()
  298. {
  299. ScopedPointer <XmlElement> oldOpenness (getOpennessState());
  300. clearSubItems();
  301. addSubItem (new ComponentList (editor));
  302. addSubItem (new MarkerList (editor, true));
  303. addSubItem (new MarkerList (editor, false));
  304. if (oldOpenness != 0)
  305. restoreOpennessState (*oldOpenness);
  306. }
  307. const String getDisplayName() const { return getRenamingName(); }
  308. const String getRenamingName() const { return editor.getDocument().getClassName().toString(); }
  309. const Image getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  310. const String getDragSourceDescription() { return String::empty; }
  311. };
  312. }
  313. #endif // __JUCE_COMPONENTEDITORTREEVIEW_H_F3B95A41__