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.

267 lines
8.0KB

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