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.

277 lines
8.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. #include "../jucer_Headers.h"
  20. #include "jucer_JucerTreeViewBase.h"
  21. #include "../Project/jucer_ProjectContentComponent.h"
  22. //==============================================================================
  23. void TreePanelBase::setRoot (JucerTreeViewBase* root)
  24. {
  25. rootItem = root;
  26. tree.setRootItem (root);
  27. tree.getRootItem()->setOpen (true);
  28. if (project != nullptr)
  29. {
  30. const ScopedPointer<XmlElement> treeOpenness (project->getStoredProperties()
  31. .getXmlValue (opennessStateKey));
  32. if (treeOpenness != nullptr)
  33. {
  34. tree.restoreOpennessState (*treeOpenness, true);
  35. for (int i = tree.getNumSelectedItems(); --i >= 0;)
  36. if (JucerTreeViewBase* item = dynamic_cast<JucerTreeViewBase*> (tree.getSelectedItem (i)))
  37. item->cancelDelayedSelectionTimer();
  38. }
  39. }
  40. }
  41. void TreePanelBase::saveOpenness()
  42. {
  43. if (project != nullptr)
  44. {
  45. const ScopedPointer<XmlElement> opennessState (tree.getOpennessState (true));
  46. if (opennessState != nullptr)
  47. project->getStoredProperties().setValue (opennessStateKey, opennessState);
  48. else
  49. project->getStoredProperties().removeValue (opennessStateKey);
  50. }
  51. }
  52. //==============================================================================
  53. JucerTreeViewBase::JucerTreeViewBase() : textX (0)
  54. {
  55. setLinesDrawnForSubItems (false);
  56. setDrawsInLeftMargin (true);
  57. }
  58. JucerTreeViewBase::~JucerTreeViewBase()
  59. {
  60. masterReference.clear();
  61. }
  62. void JucerTreeViewBase::refreshSubItems()
  63. {
  64. WholeTreeOpennessRestorer wtor (*this);
  65. clearSubItems();
  66. addSubItems();
  67. }
  68. Font JucerTreeViewBase::getFont() const
  69. {
  70. return Font (getItemHeight() * 0.6f);
  71. }
  72. void JucerTreeViewBase::paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour /*backgroundColour*/, bool isMouseOver)
  73. {
  74. g.setColour (getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId : treeIconColourId));
  75. TreeViewItem::paintOpenCloseButton (g, area, getOwnerView()->findColour (defaultIconColourId), isMouseOver);
  76. }
  77. void JucerTreeViewBase::paintIcon (Graphics &g, Rectangle<float> area)
  78. {
  79. g.setColour (getContentColour (true));
  80. getIcon().draw (g, area, isIconCrossedOut());
  81. textX = roundToInt (area.getRight());
  82. }
  83. void JucerTreeViewBase::paintItem (Graphics& g, int width, int height)
  84. {
  85. ignoreUnused (width, height);
  86. auto bounds = g.getClipBounds().withY (0).withHeight (height).toFloat();
  87. g.setColour (getOwnerView()->findColour (treeIconColourId).withMultipliedAlpha (0.4f));
  88. g.fillRect (bounds.removeFromBottom (0.5f).reduced (5, 0));
  89. }
  90. Colour JucerTreeViewBase::getContentColour (bool isIcon) const
  91. {
  92. if (isMissing())
  93. return Colours::red;
  94. if (isSelected())
  95. return getOwnerView()->findColour (defaultHighlightedTextColourId);
  96. return getOwnerView()->findColour (isIcon ? treeIconColourId : defaultTextColourId);
  97. }
  98. void JucerTreeViewBase::paintContent (Graphics& g, const Rectangle<int>& area)
  99. {
  100. g.setFont (getFont());
  101. g.setColour (getContentColour (false));
  102. g.drawFittedText (getDisplayName(), area, Justification::centredLeft, 1, 1.0f);
  103. }
  104. Component* JucerTreeViewBase::createItemComponent()
  105. {
  106. return new TreeItemComponent (*this);
  107. }
  108. //==============================================================================
  109. class RenameTreeItemCallback : public ModalComponentManager::Callback,
  110. public TextEditorListener
  111. {
  112. public:
  113. RenameTreeItemCallback (JucerTreeViewBase& ti, Component& parent, const Rectangle<int>& bounds)
  114. : item (ti)
  115. {
  116. ed.setMultiLine (false, false);
  117. ed.setPopupMenuEnabled (false);
  118. ed.setSelectAllWhenFocused (true);
  119. ed.setFont (item.getFont());
  120. ed.addListener (this);
  121. ed.setText (item.getRenamingName());
  122. ed.setBounds (bounds);
  123. parent.addAndMakeVisible (ed);
  124. ed.enterModalState (true, this);
  125. }
  126. void modalStateFinished (int resultCode) override
  127. {
  128. if (resultCode != 0)
  129. item.setName (ed.getText());
  130. }
  131. void textEditorTextChanged (TextEditor&) override {}
  132. void textEditorReturnKeyPressed (TextEditor& editor) override { editor.exitModalState (1); }
  133. void textEditorEscapeKeyPressed (TextEditor& editor) override { editor.exitModalState (0); }
  134. void textEditorFocusLost (TextEditor& editor) override { editor.exitModalState (0); }
  135. private:
  136. struct RenameEditor : public TextEditor
  137. {
  138. void inputAttemptWhenModal() override { exitModalState (0); }
  139. };
  140. RenameEditor ed;
  141. JucerTreeViewBase& item;
  142. JUCE_DECLARE_NON_COPYABLE (RenameTreeItemCallback)
  143. };
  144. void JucerTreeViewBase::showRenameBox()
  145. {
  146. Rectangle<int> r (getItemPosition (true));
  147. r.setLeft (r.getX() + textX);
  148. r.setHeight (getItemHeight());
  149. new RenameTreeItemCallback (*this, *getOwnerView(), r);
  150. }
  151. void JucerTreeViewBase::itemClicked (const MouseEvent& e)
  152. {
  153. if (e.mods.isPopupMenu())
  154. {
  155. if (getOwnerView()->getNumSelectedItems() > 1)
  156. showMultiSelectionPopupMenu();
  157. else
  158. showPopupMenu();
  159. }
  160. else if (isSelected())
  161. {
  162. itemSelectionChanged (true);
  163. }
  164. }
  165. void JucerTreeViewBase::deleteItem() {}
  166. void JucerTreeViewBase::deleteAllSelectedItems() {}
  167. void JucerTreeViewBase::showDocument() {}
  168. void JucerTreeViewBase::showPopupMenu() {}
  169. void JucerTreeViewBase::showPlusMenu() {}
  170. void JucerTreeViewBase::showMultiSelectionPopupMenu() {}
  171. static void treeViewMenuItemChosen (int resultCode, WeakReference<JucerTreeViewBase> item)
  172. {
  173. if (item != nullptr)
  174. item->handlePopupMenuResult (resultCode);
  175. }
  176. void JucerTreeViewBase::launchPopupMenu (PopupMenu& m)
  177. {
  178. m.showMenuAsync (PopupMenu::Options(),
  179. ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference<JucerTreeViewBase> (this)));
  180. }
  181. void JucerTreeViewBase::handlePopupMenuResult (int)
  182. {
  183. }
  184. ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const
  185. {
  186. for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent())
  187. if (ProjectContentComponent* pcc = dynamic_cast<ProjectContentComponent*> (c))
  188. return pcc;
  189. return nullptr;
  190. }
  191. //==============================================================================
  192. class JucerTreeViewBase::ItemSelectionTimer : public Timer
  193. {
  194. public:
  195. ItemSelectionTimer (JucerTreeViewBase& tvb) : owner (tvb) {}
  196. void timerCallback() override { owner.invokeShowDocument(); }
  197. private:
  198. JucerTreeViewBase& owner;
  199. JUCE_DECLARE_NON_COPYABLE (ItemSelectionTimer)
  200. };
  201. void JucerTreeViewBase::itemSelectionChanged (bool isNowSelected)
  202. {
  203. if (isNowSelected)
  204. {
  205. delayedSelectionTimer = new ItemSelectionTimer (*this);
  206. delayedSelectionTimer->startTimer (getMillisecsAllowedForDragGesture());
  207. }
  208. else
  209. {
  210. cancelDelayedSelectionTimer();
  211. }
  212. }
  213. void JucerTreeViewBase::invokeShowDocument()
  214. {
  215. cancelDelayedSelectionTimer();
  216. showDocument();
  217. }
  218. void JucerTreeViewBase::itemDoubleClicked (const MouseEvent&)
  219. {
  220. invokeShowDocument();
  221. }
  222. void JucerTreeViewBase::cancelDelayedSelectionTimer()
  223. {
  224. delayedSelectionTimer = nullptr;
  225. }