mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-11 23:54:18 +00:00
460 lines
16 KiB
C++
460 lines
16 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
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 "../jucer_Headers.h"
|
|
#include "jucer_TreeItemTypes.h"
|
|
|
|
//==============================================================================
|
|
class ConcertinaHeader : public Component,
|
|
public ChangeBroadcaster
|
|
{
|
|
public:
|
|
ConcertinaHeader (String n, Path p)
|
|
: Component (n), name (n), iconPath (p)
|
|
{
|
|
panelIcon = Icon (iconPath, Colours::white);
|
|
|
|
nameLabel.setText (name, dontSendNotification);
|
|
nameLabel.setJustificationType (Justification::centredLeft);
|
|
nameLabel.setInterceptsMouseClicks (false, false);
|
|
nameLabel.setColour (Label::textColourId, Colours::white);
|
|
|
|
addAndMakeVisible (nameLabel);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
auto b = getLocalBounds().toFloat();
|
|
|
|
iconBounds = b.removeFromLeft (b.getHeight()).reduced (7, 7);
|
|
arrowBounds = b.removeFromRight (b.getHeight());
|
|
nameLabel.setBounds (b.toNearestInt());
|
|
}
|
|
|
|
void paint (Graphics& g) override
|
|
{
|
|
g.setColour (findColour (defaultButtonBackgroundColourId));
|
|
g.fillRoundedRectangle (getLocalBounds().reduced (2, 3).toFloat(), 2.0f);
|
|
|
|
g.setColour (Colours::white);
|
|
g.fillPath (arrowPath = ProjucerLookAndFeel::getArrowPath (arrowBounds,
|
|
getParentComponent()->getBoundsInParent().getY() == yPosition ? 2 : 0,
|
|
true, Justification::centred));
|
|
|
|
panelIcon.draw (g, iconBounds.toFloat(), false);
|
|
}
|
|
|
|
void mouseUp (const MouseEvent& e) override
|
|
{
|
|
if (arrowPath.getBounds().expanded (3).contains (e.getPosition().toFloat()))
|
|
sendChangeMessage();
|
|
}
|
|
|
|
int direction = 0;
|
|
int yPosition = 0;
|
|
|
|
private:
|
|
String name;
|
|
Label nameLabel;
|
|
|
|
Path iconPath;
|
|
Icon panelIcon;
|
|
|
|
Rectangle<float> arrowBounds, iconBounds;
|
|
Path arrowPath;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
|
|
};
|
|
|
|
//==============================================================================
|
|
class ExportersTreeRoot : public JucerTreeViewBase,
|
|
private ValueTree::Listener
|
|
{
|
|
public:
|
|
ExportersTreeRoot (Project& p)
|
|
: project (p),
|
|
exportersTree (project.getExporters())
|
|
{
|
|
exportersTree.addListener (this);
|
|
}
|
|
|
|
bool isRoot() const override { return true; }
|
|
bool canBeSelected() const override { return true; }
|
|
bool isMissing() const override { return false; }
|
|
bool mightContainSubItems() override { return project.getNumExporters() > 0; }
|
|
String getUniqueName() const override { return "exporters"; }
|
|
String getRenamingName() const override { return getDisplayName(); }
|
|
String getDisplayName() const override { return "Exporters"; }
|
|
void setName (const String&) override {}
|
|
Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
|
|
|
|
void showPopupMenu() override
|
|
{
|
|
if (auto* pcc = getProjectContentComponent())
|
|
pcc->showNewExporterMenu();
|
|
}
|
|
|
|
void addSubItems() override
|
|
{
|
|
int i = 0;
|
|
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
|
|
addSubItem (new ConfigTreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
|
|
}
|
|
|
|
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
|
{
|
|
return dragSourceDetails.description.toString().startsWith (getUniqueName());
|
|
}
|
|
|
|
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
|
{
|
|
int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
|
|
exportersTree.moveChild (oldIndex, jmax (0, insertIndex), project.getUndoManagerFor (exportersTree));
|
|
}
|
|
|
|
void itemOpennessChanged (bool isNowOpen) override
|
|
{
|
|
if (isNowOpen)
|
|
refreshSubItems();
|
|
}
|
|
|
|
void removeExporter (int index)
|
|
{
|
|
if (auto* exporter = dynamic_cast<ConfigTreeItemTypes::ExporterItem*> (getSubItem (index)))
|
|
exporter->deleteItem();
|
|
}
|
|
|
|
private:
|
|
Project& project;
|
|
ValueTree exportersTree;
|
|
|
|
//==========================================================================
|
|
void valueTreePropertyChanged (ValueTree&, const Identifier&) override {}
|
|
void valueTreeParentChanged (ValueTree&) override {}
|
|
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
|
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
|
|
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
|
|
|
|
void refreshIfNeeded (ValueTree& changedTree)
|
|
{
|
|
if (changedTree == exportersTree)
|
|
refreshSubItems();
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
|
|
};
|
|
|
|
struct FileTreePanel : public TreePanelBase
|
|
{
|
|
FileTreePanel (Project& p)
|
|
: TreePanelBase (&p, "fileTreeState")
|
|
{
|
|
tree.setMultiSelectEnabled (true);
|
|
setRoot (new FileTreeItemTypes::GroupItem (p.getMainGroup()));
|
|
tree.setRootItemVisible (false);
|
|
}
|
|
|
|
void updateMissingFileStatuses()
|
|
{
|
|
if (auto* p = dynamic_cast<FileTreeItemTypes::ProjectTreeItemBase*> (rootItem.get()))
|
|
p->checkFileStatus();
|
|
}
|
|
};
|
|
|
|
struct ModuleTreePanel : public TreePanelBase
|
|
{
|
|
ModuleTreePanel (Project& p)
|
|
: TreePanelBase (&p, "moduleTreeState")
|
|
{
|
|
tree.setMultiSelectEnabled (false);
|
|
setRoot (new ConfigTreeItemTypes::EnabledModulesItem (p));
|
|
tree.setRootItemVisible (false);
|
|
}
|
|
};
|
|
|
|
struct ExportersTreePanel : public TreePanelBase
|
|
{
|
|
ExportersTreePanel (Project& p)
|
|
: TreePanelBase (&p, "exportersTreeState")
|
|
{
|
|
tree.setMultiSelectEnabled (false);
|
|
setRoot (new ExportersTreeRoot (p));
|
|
tree.setRootItemVisible (false);
|
|
}
|
|
|
|
void deleteSelectedItems() override
|
|
{
|
|
for (int i = rootItem->getNumSubItems() - 1; i >= 0; --i)
|
|
if (rootItem->getSubItem (i)->isSelected())
|
|
if (auto* root = dynamic_cast<ExportersTreeRoot*> (rootItem.get()))
|
|
root->removeExporter (i);
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
class ConcertinaTreeComponent : public Component,
|
|
private Button::Listener
|
|
{
|
|
public:
|
|
ConcertinaTreeComponent (TreePanelBase* tree, bool showSettings = false)
|
|
: treeToDisplay (tree),
|
|
showSettingsButton (showSettings)
|
|
{
|
|
addAndMakeVisible (popupMenuButton = new IconButton ("Add", &getIcons().plus));
|
|
popupMenuButton->addListener (this);
|
|
|
|
if (showSettingsButton)
|
|
{
|
|
addAndMakeVisible (settingsButton = new IconButton ("Settings", &getIcons().settings));
|
|
settingsButton->addListener (this);
|
|
}
|
|
|
|
addAndMakeVisible (treeToDisplay);
|
|
}
|
|
|
|
~ConcertinaTreeComponent()
|
|
{
|
|
treeToDisplay = nullptr;
|
|
popupMenuButton = nullptr;
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
auto bounds = getLocalBounds();
|
|
|
|
auto bottomSlice = bounds.removeFromBottom (25);
|
|
bottomSlice.removeFromRight (5);
|
|
popupMenuButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
|
|
|
|
if (showSettingsButton)
|
|
settingsButton->setBounds (bottomSlice.removeFromRight(25).reduced (2));
|
|
|
|
treeToDisplay->setBounds (bounds);
|
|
}
|
|
|
|
TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
|
|
|
|
private:
|
|
ScopedPointer<TreePanelBase> treeToDisplay;
|
|
ScopedPointer<IconButton> popupMenuButton, settingsButton;
|
|
bool showSettingsButton;
|
|
|
|
void buttonClicked (Button* b) override
|
|
{
|
|
if (b == popupMenuButton)
|
|
{
|
|
auto numSelected = treeToDisplay->tree.getNumSelectedItems();
|
|
|
|
if (numSelected > 1)
|
|
return;
|
|
|
|
if (numSelected == 0)
|
|
{
|
|
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
|
root->showPopupMenu();
|
|
}
|
|
else
|
|
{
|
|
auto* selectedItem = treeToDisplay->tree.getSelectedItem (0);
|
|
|
|
if (auto* fileItem = dynamic_cast<FileTreeItemTypes::ProjectTreeItemBase*> (selectedItem))
|
|
fileItem->showPlusMenu();
|
|
else if (auto* exporterItem = dynamic_cast<ConfigTreeItemTypes::ExporterItem*> (selectedItem))
|
|
exporterItem->showPlusMenu();
|
|
}
|
|
}
|
|
else if (b == settingsButton)
|
|
{
|
|
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
|
{
|
|
treeToDisplay->tree.clearSelectedItems();
|
|
root->showDocument();
|
|
}
|
|
}
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
|
|
};
|
|
|
|
//==============================================================================
|
|
class ProjectTab : public Component,
|
|
private ChangeListener
|
|
{
|
|
public:
|
|
ProjectTab (Project* p)
|
|
: project (p)
|
|
{
|
|
addAndMakeVisible (concertinaPanel);
|
|
buildConcertina();
|
|
}
|
|
|
|
~ProjectTab()
|
|
{
|
|
getFileTreePanel()->saveOpenness();
|
|
getModuleTreePanel()->saveOpenness();
|
|
getExportersTreePanel()->saveOpenness();
|
|
}
|
|
|
|
void paint (Graphics& g) override
|
|
{
|
|
g.fillAll (findColour (secondaryBackgroundColourId));
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
concertinaPanel.setBounds (getLocalBounds());
|
|
}
|
|
|
|
TreePanelBase* getTreeWithSelectedItems()
|
|
{
|
|
for (int i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
|
|
{
|
|
if (auto* treeComponent = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (i)))
|
|
{
|
|
if (auto* base = treeComponent->getTree())
|
|
if (base->tree.getNumSelectedItems() != 0)
|
|
return base;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FileTreePanel* getFileTreePanel()
|
|
{
|
|
if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (0)))
|
|
return dynamic_cast<FileTreePanel*> (panel->getTree());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ModuleTreePanel* getModuleTreePanel()
|
|
{
|
|
if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (1)))
|
|
return dynamic_cast<ModuleTreePanel*> (panel->getTree());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ExportersTreePanel* getExportersTreePanel()
|
|
{
|
|
if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (2)))
|
|
return dynamic_cast<ExportersTreePanel*> (panel->getTree());
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void showPanel (int panelIndex)
|
|
{
|
|
jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
|
|
|
|
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (panelIndex), true);
|
|
}
|
|
|
|
void setPanelHeightProportion (int panelIndex, float prop)
|
|
{
|
|
jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
|
|
|
|
concertinaPanel.setPanelSize (concertinaPanel.getPanel (panelIndex),
|
|
roundToInt (prop * (concertinaPanel.getHeight() - 90)), false);
|
|
}
|
|
|
|
float getPanelHeightProportion (int panelIndex)
|
|
{
|
|
jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
|
|
|
|
return ((float) (concertinaPanel.getPanel (panelIndex)->getHeight()) / (concertinaPanel.getHeight() - 90));
|
|
}
|
|
|
|
|
|
private:
|
|
ConcertinaPanel concertinaPanel;
|
|
OwnedArray<ConcertinaHeader> headers;
|
|
Project* project = nullptr;
|
|
|
|
//==============================================================================
|
|
void changeListenerCallback (ChangeBroadcaster* source) override
|
|
{
|
|
if (auto* header = dynamic_cast<ConcertinaHeader*> (source))
|
|
{
|
|
auto index = headers.indexOf (header);
|
|
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (index), true);
|
|
}
|
|
}
|
|
|
|
void buildConcertina()
|
|
{
|
|
for (int i = concertinaPanel.getNumPanels() - 1; i >= 0 ; --i)
|
|
concertinaPanel.removePanel (concertinaPanel.getPanel (i));
|
|
|
|
headers.clear();
|
|
|
|
if (project != nullptr)
|
|
{
|
|
concertinaPanel.addPanel (0, new ConcertinaTreeComponent (new FileTreePanel (*project)), true);
|
|
concertinaPanel.addPanel (1, new ConcertinaTreeComponent (new ModuleTreePanel (*project), true), true);
|
|
concertinaPanel.addPanel (2, new ConcertinaTreeComponent (new ExportersTreePanel (*project)), true);
|
|
}
|
|
|
|
headers.add (new ConcertinaHeader ("File explorer", getIcons().fileExplorer));
|
|
headers.add (new ConcertinaHeader ("Modules", getIcons().modules));
|
|
headers.add (new ConcertinaHeader ("Exporters", getIcons().exporter));
|
|
|
|
for (int i = 0; i < concertinaPanel.getNumPanels(); ++i)
|
|
{
|
|
auto* p = concertinaPanel.getPanel (i);
|
|
auto* h = headers.getUnchecked (i);
|
|
p->addMouseListener (this, true);
|
|
|
|
h->addChangeListener (this);
|
|
h->yPosition = i * 30;
|
|
|
|
concertinaPanel.setCustomPanelHeader (p, h, false);
|
|
concertinaPanel.setPanelHeaderSize (p, 30);
|
|
}
|
|
}
|
|
|
|
void mouseDown (const MouseEvent& e) override
|
|
{
|
|
for (int i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
|
|
{
|
|
auto* p = concertinaPanel.getPanel (i);
|
|
|
|
if (! (p->isParentOf (e.eventComponent)))
|
|
{
|
|
auto* base = dynamic_cast<TreePanelBase*> (p);
|
|
|
|
if (base == nullptr)
|
|
base = dynamic_cast<ConcertinaTreeComponent*> (p)->getTree();
|
|
|
|
if (base != nullptr)
|
|
base->tree.clearSelectedItems();
|
|
}
|
|
}
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectTab)
|
|
};
|