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.

268 lines
8.0KB

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