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.

270 lines
8.2KB

  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. const std::unique_ptr<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. std::unique_ptr<XmlElement> opennessState (tree.getOpennessState (true));
  46. if (opennessState != nullptr)
  47. project->getStoredProperties().setValue (opennessStateKey, opennessState.get());
  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()) + 7;
  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. {
  109. public:
  110. RenameTreeItemCallback (JucerTreeViewBase& ti, Component& parent, const Rectangle<int>& bounds)
  111. : item (ti)
  112. {
  113. ed.setMultiLine (false, false);
  114. ed.setPopupMenuEnabled (false);
  115. ed.setSelectAllWhenFocused (true);
  116. ed.setFont (item.getFont());
  117. ed.onReturnKey = [this] { ed.exitModalState (1); };
  118. ed.onEscapeKey = [this] { ed.exitModalState (0); };
  119. ed.onFocusLost = [this] { ed.exitModalState (0); };
  120. ed.setText (item.getRenamingName());
  121. ed.setBounds (bounds);
  122. parent.addAndMakeVisible (ed);
  123. ed.enterModalState (true, this);
  124. }
  125. void modalStateFinished (int resultCode) override
  126. {
  127. if (resultCode != 0)
  128. item.setName (ed.getText());
  129. }
  130. private:
  131. struct RenameEditor : public TextEditor
  132. {
  133. void inputAttemptWhenModal() override { exitModalState (0); }
  134. };
  135. RenameEditor ed;
  136. JucerTreeViewBase& item;
  137. JUCE_DECLARE_NON_COPYABLE (RenameTreeItemCallback)
  138. };
  139. void JucerTreeViewBase::showRenameBox()
  140. {
  141. Rectangle<int> r (getItemPosition (true));
  142. r.setLeft (r.getX() + textX);
  143. r.setHeight (getItemHeight());
  144. new RenameTreeItemCallback (*this, *getOwnerView(), r);
  145. }
  146. void JucerTreeViewBase::itemClicked (const MouseEvent& e)
  147. {
  148. if (e.mods.isPopupMenu())
  149. {
  150. if (getOwnerView()->getNumSelectedItems() > 1)
  151. showMultiSelectionPopupMenu();
  152. else
  153. showPopupMenu();
  154. }
  155. else if (isSelected())
  156. {
  157. itemSelectionChanged (true);
  158. }
  159. }
  160. void JucerTreeViewBase::deleteItem() {}
  161. void JucerTreeViewBase::deleteAllSelectedItems() {}
  162. void JucerTreeViewBase::showDocument() {}
  163. void JucerTreeViewBase::showPopupMenu() {}
  164. void JucerTreeViewBase::showAddMenu() {}
  165. void JucerTreeViewBase::showMultiSelectionPopupMenu() {}
  166. static void treeViewMenuItemChosen (int resultCode, WeakReference<JucerTreeViewBase> item)
  167. {
  168. if (item != nullptr)
  169. item->handlePopupMenuResult (resultCode);
  170. }
  171. void JucerTreeViewBase::launchPopupMenu (PopupMenu& m)
  172. {
  173. m.showMenuAsync (PopupMenu::Options(),
  174. ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference<JucerTreeViewBase> (this)));
  175. }
  176. void JucerTreeViewBase::handlePopupMenuResult (int)
  177. {
  178. }
  179. ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const
  180. {
  181. for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent())
  182. if (ProjectContentComponent* pcc = dynamic_cast<ProjectContentComponent*> (c))
  183. return pcc;
  184. return nullptr;
  185. }
  186. //==============================================================================
  187. class JucerTreeViewBase::ItemSelectionTimer : public Timer
  188. {
  189. public:
  190. ItemSelectionTimer (JucerTreeViewBase& tvb) : owner (tvb) {}
  191. void timerCallback() override { owner.invokeShowDocument(); }
  192. private:
  193. JucerTreeViewBase& owner;
  194. JUCE_DECLARE_NON_COPYABLE (ItemSelectionTimer)
  195. };
  196. void JucerTreeViewBase::itemSelectionChanged (bool isNowSelected)
  197. {
  198. if (isNowSelected)
  199. {
  200. delayedSelectionTimer.reset (new ItemSelectionTimer (*this));
  201. delayedSelectionTimer->startTimer (getMillisecsAllowedForDragGesture());
  202. }
  203. else
  204. {
  205. cancelDelayedSelectionTimer();
  206. }
  207. }
  208. void JucerTreeViewBase::invokeShowDocument()
  209. {
  210. cancelDelayedSelectionTimer();
  211. showDocument();
  212. }
  213. void JucerTreeViewBase::itemDoubleClicked (const MouseEvent&)
  214. {
  215. invokeShowDocument();
  216. }
  217. void JucerTreeViewBase::cancelDelayedSelectionTimer()
  218. {
  219. delayedSelectionTimer.reset();
  220. }