/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #include "../../Application/jucer_Headers.h" #include "jucer_JucerTreeViewBase.h" #include "../../Project/UI/jucer_ProjectContentComponent.h" //============================================================================== void TreePanelBase::setRoot (JucerTreeViewBase* root) { rootItem.reset (root); tree.setRootItem (root); tree.getRootItem()->setOpen (true); if (project != nullptr) { const std::unique_ptr treeOpenness (project->getStoredProperties() .getXmlValue (opennessStateKey)); if (treeOpenness != nullptr) { tree.restoreOpennessState (*treeOpenness, true); for (int i = tree.getNumSelectedItems(); --i >= 0;) if (JucerTreeViewBase* item = dynamic_cast (tree.getSelectedItem (i))) item->cancelDelayedSelectionTimer(); } } } void TreePanelBase::saveOpenness() { if (project != nullptr) { std::unique_ptr opennessState (tree.getOpennessState (true)); if (opennessState != nullptr) project->getStoredProperties().setValue (opennessStateKey, opennessState.get()); else project->getStoredProperties().removeValue (opennessStateKey); } } //============================================================================== JucerTreeViewBase::JucerTreeViewBase() : textX (0) { setLinesDrawnForSubItems (false); setDrawsInLeftMargin (true); } JucerTreeViewBase::~JucerTreeViewBase() { } void JucerTreeViewBase::refreshSubItems() { WholeTreeOpennessRestorer wtor (*this); clearSubItems(); addSubItems(); } Font JucerTreeViewBase::getFont() const { return Font (getItemHeight() * 0.6f); } void JucerTreeViewBase::paintOpenCloseButton (Graphics& g, const Rectangle& area, Colour /*backgroundColour*/, bool isMouseOver) { g.setColour (getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId : treeIconColourId)); TreeViewItem::paintOpenCloseButton (g, area, getOwnerView()->findColour (defaultIconColourId), isMouseOver); } void JucerTreeViewBase::paintIcon (Graphics& g, Rectangle area) { g.setColour (getContentColour (true)); getIcon().draw (g, area, isIconCrossedOut()); textX = roundToInt (area.getRight()) + 7; } void JucerTreeViewBase::paintItem (Graphics& g, int width, int height) { ignoreUnused (width, height); auto bounds = g.getClipBounds().withY (0).withHeight (height).toFloat(); g.setColour (getOwnerView()->findColour (treeIconColourId).withMultipliedAlpha (0.4f)); g.fillRect (bounds.removeFromBottom (0.5f).reduced (5, 0)); } Colour JucerTreeViewBase::getContentColour (bool isIcon) const { if (isMissing()) return Colours::red; if (isSelected()) return getOwnerView()->findColour (defaultHighlightedTextColourId); if (hasWarnings()) return getOwnerView()->findColour (defaultHighlightColourId); return getOwnerView()->findColour (isIcon ? treeIconColourId : defaultTextColourId); } void JucerTreeViewBase::paintContent (Graphics& g, const Rectangle& area) { g.setFont (getFont()); g.setColour (getContentColour (false)); g.drawFittedText (getDisplayName(), area, Justification::centredLeft, 1, 1.0f); } Component* JucerTreeViewBase::createItemComponent() { return new TreeItemComponent (*this); } //============================================================================== class RenameTreeItemCallback : public ModalComponentManager::Callback { public: RenameTreeItemCallback (JucerTreeViewBase& ti, Component& parent, const Rectangle& bounds) : item (ti) { ed.setMultiLine (false, false); ed.setPopupMenuEnabled (false); ed.setSelectAllWhenFocused (true); ed.setFont (item.getFont()); ed.onReturnKey = [this] { ed.exitModalState (1); }; ed.onEscapeKey = [this] { ed.exitModalState (0); }; ed.onFocusLost = [this] { ed.exitModalState (0); }; ed.setText (item.getRenamingName()); ed.setBounds (bounds); parent.addAndMakeVisible (ed); ed.enterModalState (true, this); } void modalStateFinished (int resultCode) override { if (resultCode != 0) item.setName (ed.getText()); } private: struct RenameEditor : public TextEditor { void inputAttemptWhenModal() override { exitModalState (0); } }; RenameEditor ed; JucerTreeViewBase& item; JUCE_DECLARE_NON_COPYABLE (RenameTreeItemCallback) }; void JucerTreeViewBase::showRenameBox() { Rectangle r (getItemPosition (true)); r.setLeft (r.getX() + textX); r.setHeight (getItemHeight()); new RenameTreeItemCallback (*this, *getOwnerView(), r); } void JucerTreeViewBase::itemClicked (const MouseEvent& e) { if (e.mods.isPopupMenu()) { if (getOwnerView()->getNumSelectedItems() > 1) showMultiSelectionPopupMenu(); else showPopupMenu(); } else if (isSelected()) { itemSelectionChanged (true); } } void JucerTreeViewBase::deleteItem() {} void JucerTreeViewBase::deleteAllSelectedItems() {} void JucerTreeViewBase::showDocument() {} void JucerTreeViewBase::showPopupMenu() {} void JucerTreeViewBase::showAddMenu() {} void JucerTreeViewBase::showMultiSelectionPopupMenu() {} static void treeViewMenuItemChosen (int resultCode, WeakReference item) { if (item != nullptr) item->handlePopupMenuResult (resultCode); } void JucerTreeViewBase::launchPopupMenu (PopupMenu& m) { m.showMenuAsync (PopupMenu::Options(), ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference (this))); } void JucerTreeViewBase::handlePopupMenuResult (int) { } ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const { for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent()) if (ProjectContentComponent* pcc = dynamic_cast (c)) return pcc; return nullptr; } //============================================================================== class JucerTreeViewBase::ItemSelectionTimer : public Timer { public: ItemSelectionTimer (JucerTreeViewBase& tvb) : owner (tvb) {} void timerCallback() override { owner.invokeShowDocument(); } private: JucerTreeViewBase& owner; JUCE_DECLARE_NON_COPYABLE (ItemSelectionTimer) }; void JucerTreeViewBase::itemSelectionChanged (bool isNowSelected) { if (isNowSelected) { delayedSelectionTimer.reset (new ItemSelectionTimer (*this)); delayedSelectionTimer->startTimer (getMillisecsAllowedForDragGesture()); } else { cancelDelayedSelectionTimer(); } } void JucerTreeViewBase::invokeShowDocument() { cancelDelayedSelectionTimer(); showDocument(); } void JucerTreeViewBase::itemDoubleClicked (const MouseEvent&) { invokeShowDocument(); } void JucerTreeViewBase::cancelDelayedSelectionTimer() { delayedSelectionTimer.reset(); }