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.

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