git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
401
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h
vendored
Normal file
401
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h
vendored
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ExporterItem : public ProjectTreeItemBase,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
ExporterItem (Project& p, ProjectExporter* e, int index)
|
||||
: project (p), exporter (e), configListTree (exporter->getConfigurations()),
|
||||
exporterIndex (index)
|
||||
{
|
||||
exporter->initialiseDependencyPathValues();
|
||||
configListTree.addListener (this);
|
||||
targetLocationValue.referTo (exporter->getTargetLocationValue());
|
||||
targetLocationValue.addListener (this);
|
||||
}
|
||||
|
||||
int getItemHeight() const override { return 25; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return exporter->getNumConfigurations() > 0; }
|
||||
String getUniqueName() const override { return "exporter_" + String (exporterIndex); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return exporter->getUniqueName(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() const override { return false; }
|
||||
String getTooltip() override { return getDisplayName(); }
|
||||
|
||||
static Icon getIconForExporter (ProjectExporter* e)
|
||||
{
|
||||
if (e != nullptr)
|
||||
{
|
||||
if (e->isXcode()) return Icon (getIcons().xcode, Colours::transparentBlack);
|
||||
else if (e->isVisualStudio()) return Icon (getIcons().visualStudio, Colours::transparentBlack);
|
||||
else if (e->isAndroid()) return Icon (getIcons().android, Colours::transparentBlack);
|
||||
else if (e->isCodeBlocks()) return Icon (getIcons().codeBlocks, Colours::transparentBlack);
|
||||
else if (e->isMakefile()) return Icon (getIcons().linux, Colours::transparentBlack);
|
||||
else if (e->isCLion()) return Icon (getIcons().clion, Colours::transparentBlack);
|
||||
}
|
||||
|
||||
return Icon();
|
||||
}
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
return getIconForExporter (exporter.get()).withColour (getContentColour (true));
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new SettingsComp (*exporter));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
auto resultCallback = [safeThis = WeakReference<ExporterItem> { this }] (int result)
|
||||
{
|
||||
if (safeThis == nullptr || result == 0)
|
||||
return;
|
||||
|
||||
safeThis->closeSettingsPage();
|
||||
|
||||
auto parent = safeThis->exporter->settings.getParent();
|
||||
parent.removeChild (safeThis->exporter->settings,
|
||||
safeThis->project.getUndoManagerFor (parent));
|
||||
};
|
||||
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
"Delete Exporter",
|
||||
"Are you sure you want to delete this export target?",
|
||||
"",
|
||||
"",
|
||||
nullptr,
|
||||
ModalCallbackFunction::create (std::move (resultCallback)));
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
|
||||
addSubItem (new ConfigItem (config.config, *exporter));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
|
||||
menu.addItem (2, "Save this exporter");
|
||||
menu.addSeparator();
|
||||
menu.addItem (3, "Delete this exporter");
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
exporter->addNewConfiguration (false);
|
||||
else if (resultCode == 2)
|
||||
project.saveProject (Async::yes, exporter.get(), nullptr);
|
||||
else if (resultCode == 3)
|
||||
deleteAllSelectedItems();
|
||||
}
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
|
||||
}
|
||||
|
||||
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
||||
{
|
||||
return dragSourceDetails.description.toString().startsWith (getUniqueName());
|
||||
}
|
||||
|
||||
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
||||
{
|
||||
auto oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
|
||||
|
||||
if (oldIndex >= 0)
|
||||
configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
|
||||
}
|
||||
|
||||
int indexOfConfig (const String& configName)
|
||||
{
|
||||
int i = 0;
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
|
||||
if (config->getName() == configName)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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 == configListTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
std::unique_ptr<ProjectExporter> exporter;
|
||||
ValueTree configListTree;
|
||||
int exporterIndex;
|
||||
|
||||
Value targetLocationValue;
|
||||
|
||||
void valueChanged (Value& value) override
|
||||
{
|
||||
if (value == exporter->getTargetLocationValue())
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SettingsComp : public Component
|
||||
{
|
||||
SettingsComp (ProjectExporter& exp)
|
||||
: group (exp.getUniqueName(),
|
||||
ExporterItem::getIconForExporter (&exp),
|
||||
exp.getDescription())
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
exp.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ExporterItem)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ConfigItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
|
||||
: config (conf), exporter (e), configTree (config->config)
|
||||
{
|
||||
jassert (config != nullptr);
|
||||
configTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isMissing() const override { return false; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
String getUniqueName() const override { return "config_" + config->getName(); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return config->getName(); }
|
||||
void setName (const String&) override {}
|
||||
Icon getIcon() const override { return Icon (getIcons().config, getContentColour (true)); }
|
||||
void itemOpennessChanged (bool) override {}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new SettingsComp (*config));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
"Delete Configuration",
|
||||
"Are you sure you want to delete this configuration?",
|
||||
"",
|
||||
"",
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([parent = WeakReference<ConfigItem> { this }] (int result)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (result == 0)
|
||||
return;
|
||||
|
||||
parent->closeSettingsPage();
|
||||
parent->config->removeFromExporter();
|
||||
}));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
bool enabled = exporter.supportsUserDefinedConfigurations();
|
||||
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Create a copy of this configuration", enabled);
|
||||
menu.addSeparator();
|
||||
menu.addItem (2, "Delete this configuration", enabled);
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
exporter.addNewConfigurationFromExisting (*config);
|
||||
else if (resultCode == 2)
|
||||
deleteAllSelectedItems();
|
||||
}
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
return getParentItem()->getUniqueName() + "||" + config->getName();
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
|
||||
|
||||
private:
|
||||
ProjectExporter::BuildConfiguration::Ptr config;
|
||||
ProjectExporter& exporter;
|
||||
ValueTree configTree;
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComp : public Component
|
||||
{
|
||||
public:
|
||||
SettingsComp (ProjectExporter::BuildConfiguration& conf)
|
||||
: group (conf.exporter.getUniqueName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
conf.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
private:
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ConfigItem)
|
||||
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ExportersTreeRoot : public ProjectTreeItemBase
|
||||
{
|
||||
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 (Point<int>) override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->showNewExporterMenu();
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
int i = 0;
|
||||
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
|
||||
addSubItem (new TreeItemTypes::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<TreeItemTypes::ExporterItem*> (getSubItem (index)))
|
||||
exporter->deleteItem();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree exportersTree;
|
||||
|
||||
//==============================================================================
|
||||
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)
|
||||
};
|
882
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
vendored
Normal file
882
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
vendored
Normal file
@ -0,0 +1,882 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FileTreeItemBase : public JucerTreeViewBase,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
FileTreeItemBase (const Project::Item& projectItem)
|
||||
: item (projectItem), isFileMissing (false)
|
||||
{
|
||||
item.state.addListener (this);
|
||||
}
|
||||
|
||||
~FileTreeItemBase() override
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool acceptsFileDrop (const StringArray& files) const = 0;
|
||||
virtual bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) = 0;
|
||||
|
||||
//==============================================================================
|
||||
String getDisplayName() const override { return item.getName(); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
void setName (const String& newName) override { item.getNameValue() = newName; }
|
||||
bool isMissing() const override { return isFileMissing; }
|
||||
virtual File getFile() const { return item.getFile(); }
|
||||
|
||||
void deleteItem() override { item.removeItemFromProject(); }
|
||||
|
||||
void deleteAllSelectedItems() override
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
Array<File> filesToTrash;
|
||||
Array<Project::Item> itemsToRemove;
|
||||
|
||||
for (int i = 0; i < tree->getNumSelectedItems(); ++i)
|
||||
{
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
{
|
||||
itemsToRemove.add (p->item);
|
||||
|
||||
if (p->item.isGroup())
|
||||
{
|
||||
for (int j = 0; j < p->item.getNumChildren(); ++j)
|
||||
{
|
||||
auto associatedFile = p->item.getChild (j).getFile();
|
||||
|
||||
if (associatedFile.existsAsFile())
|
||||
filesToTrash.addIfNotAlreadyThere (associatedFile);
|
||||
}
|
||||
}
|
||||
else if (p->getFile().existsAsFile())
|
||||
{
|
||||
filesToTrash.addIfNotAlreadyThere (p->getFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WeakReference<FileTreeItemBase> treeRootItem { dynamic_cast<FileTreeItemBase*> (tree->getRootItem()) };
|
||||
|
||||
if (treeRootItem == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
auto doDelete = [treeRootItem, itemsToRemove] (const Array<File>& fsToTrash)
|
||||
{
|
||||
if (treeRootItem == nullptr)
|
||||
return;
|
||||
|
||||
auto& om = ProjucerApplication::getApp().openDocumentManager;
|
||||
|
||||
for (auto i = fsToTrash.size(); --i >= 0;)
|
||||
{
|
||||
auto f = fsToTrash.getUnchecked(i);
|
||||
|
||||
om.closeFileWithoutSaving (f);
|
||||
|
||||
if (! f.moveToTrash())
|
||||
{
|
||||
// xxx
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = itemsToRemove.size(); --i >= 0;)
|
||||
{
|
||||
if (auto itemToRemove = treeRootItem->findTreeViewItem (itemsToRemove.getUnchecked (i)))
|
||||
{
|
||||
if (auto* pcc = treeRootItem->getProjectContentComponent())
|
||||
{
|
||||
if (auto* fileInfoComp = dynamic_cast<FileGroupInformationComponent*> (pcc->getEditorComponent()))
|
||||
if (fileInfoComp->getGroupPath() == itemToRemove->getFile().getFullPathName())
|
||||
pcc->hideEditor();
|
||||
}
|
||||
|
||||
om.closeFileWithoutSaving (itemToRemove->getFile());
|
||||
itemToRemove->deleteItem();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (! filesToTrash.isEmpty())
|
||||
{
|
||||
String fileList;
|
||||
auto maxFilesToList = 10;
|
||||
for (auto i = jmin (maxFilesToList, filesToTrash.size()); --i >= 0;)
|
||||
fileList << filesToTrash.getUnchecked(i).getFullPathName() << "\n";
|
||||
|
||||
if (filesToTrash.size() > maxFilesToList)
|
||||
fileList << "\n...plus " << (filesToTrash.size() - maxFilesToList) << " more files...";
|
||||
|
||||
AlertWindow::showYesNoCancelBox (MessageBoxIconType::NoIcon,
|
||||
"Delete Project Items",
|
||||
"As well as removing the selected item(s) from the project, do you also want to move their files to the trash:\n\n"
|
||||
+ fileList,
|
||||
"Just remove references",
|
||||
"Also move files to Trash",
|
||||
"Cancel",
|
||||
tree->getTopLevelComponent(),
|
||||
ModalCallbackFunction::create ([treeRootItem, filesToTrash, doDelete] (int r) mutable
|
||||
{
|
||||
if (treeRootItem == nullptr)
|
||||
return;
|
||||
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
if (r != 2)
|
||||
filesToTrash.clear();
|
||||
|
||||
doDelete (filesToTrash);
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
doDelete (filesToTrash);
|
||||
}
|
||||
|
||||
virtual void revealInFinder() const
|
||||
{
|
||||
getFile().revealToUser();
|
||||
}
|
||||
|
||||
virtual void browseToAddExistingFiles()
|
||||
{
|
||||
auto location = item.isGroup() ? item.determineGroupFolder() : getFile();
|
||||
chooser = std::make_unique<FileChooser> ("Add Files to Jucer Project", location, "");
|
||||
auto flags = FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::canSelectDirectories
|
||||
| FileBrowserComponent::canSelectMultipleItems;
|
||||
|
||||
chooser->launchAsync (flags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResults().isEmpty())
|
||||
return;
|
||||
|
||||
StringArray files;
|
||||
|
||||
for (int i = 0; i < fc.getResults().size(); ++i)
|
||||
files.add (fc.getResults().getReference(i).getFullPathName());
|
||||
|
||||
addFilesRetainingSortOrder (files);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void checkFileStatus() // (recursive)
|
||||
{
|
||||
auto file = getFile();
|
||||
auto nowMissing = (file != File() && ! file.exists());
|
||||
|
||||
if (nowMissing != isFileMissing)
|
||||
{
|
||||
isFileMissing = nowMissing;
|
||||
repaintItem();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void addFilesAtIndex (const StringArray& files, int insertIndex)
|
||||
{
|
||||
if (auto* p = getParentProjectItem())
|
||||
p->addFilesAtIndex (files, insertIndex);
|
||||
}
|
||||
|
||||
virtual void addFilesRetainingSortOrder (const StringArray& files)
|
||||
{
|
||||
if (auto* p = getParentProjectItem())
|
||||
p->addFilesRetainingSortOrder (files);
|
||||
}
|
||||
|
||||
virtual void moveSelectedItemsTo (OwnedArray<Project::Item>&, int /*insertIndex*/)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void showMultiSelectionPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete");
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetScreenArea ({ p.x, p.y, 1, 1 }),
|
||||
ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this));
|
||||
}
|
||||
|
||||
static void treeViewMultiSelectItemChosen (int resultCode, FileTreeItemBase* item)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: item->deleteAllSelectedItems(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual FileTreeItemBase* findTreeViewItem (const Project::Item& itemToFind)
|
||||
{
|
||||
if (item == itemToFind)
|
||||
return this;
|
||||
|
||||
auto wasOpen = isOpen();
|
||||
setOpen (true);
|
||||
|
||||
for (auto i = getNumSubItems(); --i >= 0;)
|
||||
{
|
||||
if (auto* pg = dynamic_cast<FileTreeItemBase*> (getSubItem(i)))
|
||||
if (auto* found = pg->findTreeViewItem (itemToFind))
|
||||
return found;
|
||||
}
|
||||
|
||||
setOpen (wasOpen);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool mightContainSubItems() override { return item.getNumChildren() > 0; }
|
||||
String getUniqueName() const override { jassert (item.getID().isNotEmpty()); return item.getID(); }
|
||||
bool canBeSelected() const override { return true; }
|
||||
String getTooltip() override { return {}; }
|
||||
File getDraggableFile() const override { return getFile(); }
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
return projectItemDragType;
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < item.getNumChildren(); ++i)
|
||||
if (auto* p = createSubItem (item.getChild(i)))
|
||||
addSubItem (p);
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
return acceptsFileDrop (files);
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int insertIndex) override
|
||||
{
|
||||
if (files.size() == 1 && File (files[0]).hasFileExtension (Project::projectFileExtension))
|
||||
ProjucerApplication::getApp().openFile (files[0], [] (bool) {});
|
||||
else
|
||||
addFilesAtIndex (files, insertIndex);
|
||||
}
|
||||
|
||||
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
||||
{
|
||||
OwnedArray<Project::Item> selectedNodes;
|
||||
getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
|
||||
return selectedNodes.size() > 0 && acceptsDragItems (selectedNodes);
|
||||
}
|
||||
|
||||
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
||||
{
|
||||
OwnedArray<Project::Item> selectedNodes;
|
||||
getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
|
||||
if (selectedNodes.size() > 0)
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
std::unique_ptr<XmlElement> oldOpenness (tree->getOpennessState (false));
|
||||
|
||||
moveSelectedItemsTo (selectedNodes, insertIndex);
|
||||
|
||||
if (oldOpenness != nullptr)
|
||||
tree->restoreOpennessState (*oldOpenness, false);
|
||||
}
|
||||
}
|
||||
|
||||
int getMillisecsAllowedForDragGesture() override
|
||||
{
|
||||
// for images, give the user longer to start dragging before assuming they're
|
||||
// clicking to select it for previewing..
|
||||
return item.isImageFile() ? 250 : JucerTreeViewBase::getMillisecsAllowedForDragGesture();
|
||||
}
|
||||
|
||||
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
||||
OwnedArray<Project::Item>& selectedNodes)
|
||||
{
|
||||
if (dragSourceDetails.description == projectItemDragType)
|
||||
{
|
||||
auto* tree = dynamic_cast<TreeView*> (dragSourceDetails.sourceComponent.get());
|
||||
|
||||
if (tree == nullptr)
|
||||
tree = dragSourceDetails.sourceComponent->findParentComponentOfClass<TreeView>();
|
||||
|
||||
if (tree != nullptr)
|
||||
{
|
||||
auto numSelected = tree->getNumSelectedItems();
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
selectedNodes.add (new Project::Item (p->item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileTreeItemBase* getParentProjectItem() const
|
||||
{
|
||||
return dynamic_cast<FileTreeItemBase*> (getParentItem());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project::Item item;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree& tree, const Identifier&) override
|
||||
{
|
||||
if (tree == item.state)
|
||||
repaintItem();
|
||||
}
|
||||
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { treeChildrenChanged (parentTree); }
|
||||
|
||||
bool isFileMissing;
|
||||
|
||||
virtual FileTreeItemBase* createSubItem (const Project::Item& node) = 0;
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
auto colour = getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId);
|
||||
|
||||
return item.getIcon (isOpen()).withColour (colour);
|
||||
}
|
||||
|
||||
bool isIconCrossedOut() const override { return item.isIconCrossedOut(); }
|
||||
|
||||
void treeChildrenChanged (const ValueTree& parentTree)
|
||||
{
|
||||
if (parentTree == item.state)
|
||||
{
|
||||
refreshSubItems();
|
||||
treeHasChanged();
|
||||
setOpen (true);
|
||||
}
|
||||
}
|
||||
|
||||
void triggerAsyncRename (const Project::Item& itemToRename)
|
||||
{
|
||||
struct RenameMessage : public CallbackMessage
|
||||
{
|
||||
RenameMessage (TreeView* const t, const Project::Item& i)
|
||||
: tree (t), itemToRename (i) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (tree != nullptr)
|
||||
if (auto* root = dynamic_cast<FileTreeItemBase*> (tree->getRootItem()))
|
||||
if (auto* found = root->findTreeViewItem (itemToRename))
|
||||
found->showRenameBox();
|
||||
}
|
||||
|
||||
private:
|
||||
Component::SafePointer<TreeView> tree;
|
||||
Project::Item itemToRename;
|
||||
};
|
||||
|
||||
(new RenameMessage (getOwnerView(), itemToRename))->post();
|
||||
}
|
||||
|
||||
static void moveItems (OwnedArray<Project::Item>& selectedNodes, Project::Item destNode, int insertIndex)
|
||||
{
|
||||
for (auto i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
auto* n = selectedNodes.getUnchecked(i);
|
||||
|
||||
if (destNode == *n || destNode.state.isAChildOf (n->state)) // Check for recursion.
|
||||
return;
|
||||
|
||||
if (! destNode.canContain (*n))
|
||||
selectedNodes.remove (i);
|
||||
}
|
||||
|
||||
// Don't include any nodes that are children of other selected nodes..
|
||||
for (auto i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
auto* n = selectedNodes.getUnchecked(i);
|
||||
|
||||
for (auto j = selectedNodes.size(); --j >= 0;)
|
||||
{
|
||||
if (j != i && n->state.isAChildOf (selectedNodes.getUnchecked(j)->state))
|
||||
{
|
||||
selectedNodes.remove (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and re-insert them one at a time..
|
||||
for (int i = 0; i < selectedNodes.size(); ++i)
|
||||
{
|
||||
auto* selectedNode = selectedNodes.getUnchecked(i);
|
||||
|
||||
if (selectedNode->state.getParent() == destNode.state
|
||||
&& indexOfNode (destNode.state, selectedNode->state) < insertIndex)
|
||||
--insertIndex;
|
||||
|
||||
selectedNode->removeItemFromProject();
|
||||
destNode.addChild (*selectedNode, insertIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
static int indexOfNode (const ValueTree& parent, const ValueTree& child)
|
||||
{
|
||||
for (auto i = parent.getNumChildren(); --i >= 0;)
|
||||
if (parent.getChild (i) == child)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (FileTreeItemBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class SourceFileItem : public FileTreeItemBase
|
||||
{
|
||||
public:
|
||||
SourceFileItem (const Project::Item& projectItem)
|
||||
: FileTreeItemBase (projectItem)
|
||||
{
|
||||
}
|
||||
|
||||
bool acceptsFileDrop (const StringArray&) const override { return false; }
|
||||
bool acceptsDragItems (const OwnedArray<Project::Item>&) override { return false; }
|
||||
|
||||
String getDisplayName() const override
|
||||
{
|
||||
return getFile().getFileName();
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
JucerTreeViewBase::paintItem (g, width, height);
|
||||
|
||||
if (item.needsSaving())
|
||||
{
|
||||
auto bounds = g.getClipBounds().withY (0).withHeight (height);
|
||||
|
||||
g.setFont (getFont());
|
||||
g.setColour (getContentColour (false));
|
||||
|
||||
g.drawFittedText ("*", bounds.removeFromLeft (height), Justification::centred, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static File findCorrespondingHeaderOrCpp (const File& f)
|
||||
{
|
||||
if (f.hasFileExtension (sourceFileExtensions)) return f.withFileExtension (".h");
|
||||
if (f.hasFileExtension (headerFileExtensions)) return f.withFileExtension (".cpp");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void setName (const String& newName) override
|
||||
{
|
||||
if (newName != File::createLegalFileName (newName))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"That filename contained some illegal characters!");
|
||||
triggerAsyncRename (item);
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldFile = getFile();
|
||||
auto newFile = oldFile.getSiblingFile (newName);
|
||||
auto correspondingFile = findCorrespondingHeaderOrCpp (oldFile);
|
||||
|
||||
if (correspondingFile.exists() && newFile.hasFileExtension (oldFile.getFileExtension()))
|
||||
{
|
||||
auto correspondingItem = item.project.getMainGroup().findItemForFile (correspondingFile);
|
||||
|
||||
if (correspondingItem.isValid())
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::NoIcon,
|
||||
"File Rename",
|
||||
"Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?",
|
||||
{},
|
||||
{},
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([parent = WeakReference<SourceFileItem> { this },
|
||||
oldFile, newFile, correspondingFile, correspondingItem] (int result) mutable
|
||||
{
|
||||
if (parent == nullptr || result == 0)
|
||||
return;
|
||||
|
||||
if (! parent->item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension())))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (! item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename the file!\n\nCheck your file permissions!");
|
||||
}
|
||||
}
|
||||
|
||||
FileTreeItemBase* createSubItem (const Project::Item&) override
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
auto f = getFile();
|
||||
|
||||
if (f.exists())
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->showEditorForFile (f, false);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
m.addItem (1, "Open in external editor");
|
||||
m.addItem (2,
|
||||
#if JUCE_MAC
|
||||
"Reveal in Finder");
|
||||
#else
|
||||
"Reveal in Explorer");
|
||||
#endif
|
||||
|
||||
m.addItem (4, "Rename File...");
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (5, "Binary Resource", true, item.shouldBeAddedToBinaryResources());
|
||||
m.addItem (6, "Xcode Resource", true, item.shouldBeAddedToXcodeResources());
|
||||
m.addItem (7, "Compile", item.isSourceFile(), item.shouldBeCompiled());
|
||||
m.addItem (8, "Skip PCH", item.shouldBeCompiled(), item.shouldSkipPCH());
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (3, "Delete");
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
if (auto* group = dynamic_cast<GroupItem*> (getParentItem()))
|
||||
group->showAddMenu (p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: getFile().startAsProcess(); break;
|
||||
case 2: revealInFinder(); break;
|
||||
case 3: deleteAllSelectedItems(); break;
|
||||
case 4: triggerAsyncRename (item); break;
|
||||
case 5: item.getShouldAddToBinaryResourcesValue().setValue (! item.shouldBeAddedToBinaryResources()); break;
|
||||
case 6: item.getShouldAddToXcodeResourcesValue().setValue (! item.shouldBeAddedToXcodeResources()); break;
|
||||
case 7: item.getShouldCompileValue().setValue (! item.shouldBeCompiled()); break;
|
||||
case 8: item.getShouldSkipPCHValue().setValue (! item.shouldSkipPCH()); break;
|
||||
|
||||
default:
|
||||
if (auto* parentGroup = dynamic_cast<GroupItem*> (getParentProjectItem()))
|
||||
parentGroup->processCreateFileMenuItem (resultCode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (SourceFileItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GroupItem : public FileTreeItemBase
|
||||
{
|
||||
public:
|
||||
GroupItem (const Project::Item& projectItem, const String& filter = {})
|
||||
: FileTreeItemBase (projectItem),
|
||||
searchFilter (filter)
|
||||
{
|
||||
}
|
||||
|
||||
bool isRoot() const override { return item.isMainGroup(); }
|
||||
bool acceptsFileDrop (const StringArray&) const override { return true; }
|
||||
|
||||
void addNewGroup()
|
||||
{
|
||||
auto newGroup = item.addNewSubGroup ("New Group", 0);
|
||||
triggerAsyncRename (newGroup);
|
||||
}
|
||||
|
||||
bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) override
|
||||
{
|
||||
for (auto i = selectedNodes.size(); --i >= 0;)
|
||||
if (item.canContain (*selectedNodes.getUnchecked(i)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void addFilesAtIndex (const StringArray& files, int insertIndex) override
|
||||
{
|
||||
for (auto f : files)
|
||||
{
|
||||
if (item.addFileAtIndex (f, insertIndex, true))
|
||||
++insertIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void addFilesRetainingSortOrder (const StringArray& files) override
|
||||
{
|
||||
for (auto i = files.size(); --i >= 0;)
|
||||
item.addFileRetainingSortOrder (files[i], true);
|
||||
}
|
||||
|
||||
void moveSelectedItemsTo (OwnedArray<Project::Item>& selectedNodes, int insertIndex) override
|
||||
{
|
||||
moveItems (selectedNodes, item, insertIndex);
|
||||
}
|
||||
|
||||
void checkFileStatus() override
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (getSubItem(i)))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
|
||||
bool isGroupEmpty (const Project::Item& group) // recursive
|
||||
{
|
||||
for (int i = 0; i < group.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = group.getChild (i);
|
||||
|
||||
if ((child.isGroup() && ! isGroupEmpty (child))
|
||||
|| (child.isFile() && child.getName().containsIgnoreCase (searchFilter)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FileTreeItemBase* createSubItem (const Project::Item& child) override
|
||||
{
|
||||
if (child.isGroup())
|
||||
{
|
||||
if (searchFilter.isNotEmpty() && isGroupEmpty (child))
|
||||
return nullptr;
|
||||
|
||||
return new GroupItem (child, searchFilter);
|
||||
}
|
||||
|
||||
if (child.isFile())
|
||||
{
|
||||
if (child.getName().containsIgnoreCase (searchFilter))
|
||||
return new SourceFileItem (child);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::make_unique<FileGroupInformationComponent> (item));
|
||||
}
|
||||
|
||||
static void openAllGroups (TreeViewItem* root)
|
||||
{
|
||||
for (int i = 0; i < root->getNumSubItems(); ++i)
|
||||
if (auto* sub = root->getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, true);
|
||||
}
|
||||
|
||||
static void closeAllGroups (TreeViewItem* root)
|
||||
{
|
||||
for (int i = 0; i < root->getNumSubItems(); ++i)
|
||||
if (auto* sub = root->getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, false);
|
||||
}
|
||||
|
||||
static void openOrCloseAllSubGroups (TreeViewItem& treeItem, bool shouldOpen)
|
||||
{
|
||||
treeItem.setOpen (shouldOpen);
|
||||
|
||||
for (auto i = treeItem.getNumSubItems(); --i >= 0;)
|
||||
if (auto* sub = treeItem.getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, shouldOpen);
|
||||
}
|
||||
|
||||
static void setFilesToCompile (Project::Item projectItem, const bool shouldCompile)
|
||||
{
|
||||
if (projectItem.isFile() && (projectItem.getFile().hasFileExtension (fileTypesToCompileByDefault)))
|
||||
projectItem.getShouldCompileValue() = shouldCompile;
|
||||
|
||||
for (auto i = projectItem.getNumChildren(); --i >= 0;)
|
||||
setFilesToCompile (projectItem.getChild (i), shouldCompile);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
addCreateFileMenuItems (m);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (1, "Collapse all Groups");
|
||||
m.addItem (2, "Expand all Groups");
|
||||
|
||||
if (! isRoot())
|
||||
{
|
||||
if (isOpen())
|
||||
m.addItem (3, "Collapse all Sub-groups");
|
||||
else
|
||||
m.addItem (4, "Expand all Sub-groups");
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (5, "Enable compiling of all enclosed files");
|
||||
m.addItem (6, "Disable compiling of all enclosed files");
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (7, "Sort Items Alphabetically");
|
||||
m.addItem (8, "Sort Items Alphabetically (Groups first)");
|
||||
m.addSeparator();
|
||||
|
||||
if (! isRoot())
|
||||
{
|
||||
m.addItem (9, "Rename...");
|
||||
m.addItem (10, "Delete");
|
||||
}
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
addCreateFileMenuItems (m);
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: closeAllGroups (getOwnerView()->getRootItem()); break;
|
||||
case 2: openAllGroups (getOwnerView()->getRootItem()); break;
|
||||
case 3: openOrCloseAllSubGroups (*this, false); break;
|
||||
case 4: openOrCloseAllSubGroups (*this, true); break;
|
||||
case 5: setFilesToCompile (item, true); break;
|
||||
case 6: setFilesToCompile (item, false); break;
|
||||
case 7: item.sortAlphabetically (false, false); break;
|
||||
case 8: item.sortAlphabetically (true, false); break;
|
||||
case 9: triggerAsyncRename (item); break;
|
||||
case 10: deleteAllSelectedItems(); break;
|
||||
default: processCreateFileMenuItem (resultCode); break;
|
||||
}
|
||||
}
|
||||
|
||||
void addCreateFileMenuItems (PopupMenu& m)
|
||||
{
|
||||
m.addItem (1001, "Add New Group");
|
||||
m.addItem (1002, "Add Existing Files...");
|
||||
|
||||
m.addSeparator();
|
||||
wizard.addWizardsToMenu (m);
|
||||
}
|
||||
|
||||
void processCreateFileMenuItem (int menuID)
|
||||
{
|
||||
switch (menuID)
|
||||
{
|
||||
case 1001: addNewGroup(); break;
|
||||
case 1002: browseToAddExistingFiles(); break;
|
||||
|
||||
default:
|
||||
jassert (getProject() != nullptr);
|
||||
wizard.runWizardFromMenu (menuID, *getProject(), item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Project* getProject()
|
||||
{
|
||||
if (auto* tv = getOwnerView())
|
||||
if (auto* pcc = tv->findParentComponentOfClass<ProjectContentComponent>())
|
||||
return pcc->getProject();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setSearchFilter (const String& filter) override
|
||||
{
|
||||
searchFilter = filter;
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
String searchFilter;
|
||||
NewFileWizard wizard;
|
||||
};
|
694
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h
vendored
Normal file
694
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h
vendored
Normal file
@ -0,0 +1,694 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ModuleItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
ModuleItem (Project& p, const String& modID)
|
||||
: project (p), moduleID (modID)
|
||||
{
|
||||
missingDependencies = project.getEnabledModules().getExtraDependenciesNeeded (moduleID).size() > 0;
|
||||
cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID);
|
||||
|
||||
moduleInfo = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
}
|
||||
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
String getUniqueName() const override { return "module_" + moduleID; }
|
||||
String getDisplayName() const override { return moduleID; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() const override { return missingDependencies; }
|
||||
bool hasWarnings() const override { return cppStandardHigherThanProject; }
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new ModuleSettingsPanel (project, moduleID, getOwnerView()));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
closeSettingsPage();
|
||||
project.getEnabledModules().removeModule (moduleID);
|
||||
}
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
auto iconColour = getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId);
|
||||
|
||||
if (! isSelected())
|
||||
{
|
||||
if (moduleInfo.isValid() && moduleInfo.getVendor() == "juce")
|
||||
{
|
||||
if (moduleInfo.getLicense() == "ISC")
|
||||
iconColour = Colours::lightblue;
|
||||
else if (moduleInfo.getLicense() == "GPL/Commercial")
|
||||
iconColour = Colours::orange;
|
||||
}
|
||||
}
|
||||
|
||||
return Icon (getIcons().singleModule, iconColour);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
if (auto* parent = dynamic_cast<EnabledModulesItem*> (getParentItem()))
|
||||
parent->showPopupMenu (p);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Remove this module");
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
deleteItem();
|
||||
}
|
||||
|
||||
bool checkCppStandard()
|
||||
{
|
||||
auto oldVal = cppStandardHigherThanProject;
|
||||
cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID);
|
||||
|
||||
if (oldVal != cppStandardHigherThanProject)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Project& project;
|
||||
String moduleID;
|
||||
|
||||
private:
|
||||
ModuleDescription moduleInfo;
|
||||
bool missingDependencies = false;
|
||||
bool cppStandardHigherThanProject = false;
|
||||
|
||||
//==============================================================================
|
||||
class ModuleSettingsPanel : public Component,
|
||||
private Value::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ModuleSettingsPanel (Project& p, const String& modID, TreeView* tree)
|
||||
: group (p.getEnabledModules().getModuleInfo (modID).getID(),
|
||||
Icon (getIcons().singleModule, Colours::transparentBlack)),
|
||||
project (p),
|
||||
modulesTree (tree),
|
||||
moduleID (modID)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
setEnabled (modules.isModuleEnabled (moduleID));
|
||||
|
||||
PropertyListBuilder props;
|
||||
|
||||
props.add (new ModuleInfoComponent (project, moduleID));
|
||||
|
||||
if (modules.getExtraDependenciesNeeded (moduleID).size() > 0)
|
||||
props.add (new MissingDependenciesComponent (project, moduleID));
|
||||
|
||||
if (modules.doesModuleHaveHigherCppStandardThanProject (moduleID))
|
||||
props.add (new CppStandardWarningComponent());
|
||||
|
||||
group.clearProperties();
|
||||
exporterModulePathDefaultValues.clear();
|
||||
exporterModulePathValues.clear();
|
||||
globalPathValues.clear();
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (exporter->isCLion())
|
||||
continue;
|
||||
|
||||
exporterModulePathDefaultValues.add (exporter->getPathForModuleValue (moduleID));
|
||||
auto& defaultValue = exporterModulePathDefaultValues.getReference (exporterModulePathDefaultValues.size() - 1);
|
||||
|
||||
exporterModulePathValues.add (defaultValue.getPropertyAsValue());
|
||||
|
||||
auto pathComponent = std::make_unique<FilePathPropertyComponent> (defaultValue,
|
||||
"Path for " + exporter->getUniqueName().quoted(),
|
||||
true,
|
||||
exporter->getTargetOSForExporter() == TargetOS::getThisOS(),
|
||||
"*",
|
||||
project.getProjectFolder());
|
||||
|
||||
pathComponent->setEnabled (! modules.shouldUseGlobalPath (moduleID));
|
||||
|
||||
props.add (pathComponent.release(),
|
||||
"A path to the folder that contains the " + moduleID + " module when compiling the "
|
||||
+ exporter->getUniqueName().quoted() + " target. "
|
||||
"This can be an absolute path, or relative to the jucer project folder, but it "
|
||||
"must be valid on the filesystem of the target machine that will be performing this build. If this "
|
||||
"is empty then the global path will be used.");
|
||||
|
||||
globalPathValues.add (getAppSettings().getStoredPath (isJUCEModule (moduleID) ? Ids::defaultJuceModulePath : Ids::defaultUserModulePath,
|
||||
exporter->getTargetOSForExporter()).getPropertyAsValue());
|
||||
}
|
||||
|
||||
for (int i = 0; i < exporterModulePathDefaultValues.size(); ++i)
|
||||
{
|
||||
exporterModulePathDefaultValues.getReference (i).onDefaultChange = [this] { startTimer (50); };
|
||||
|
||||
exporterModulePathValues.getReference (i).addListener (this);
|
||||
globalPathValues.getReference (i).addListener (this);
|
||||
}
|
||||
|
||||
useGlobalPathValue.removeListener (this);
|
||||
useGlobalPathValue.referTo (modules.shouldUseGlobalPathValue (moduleID));
|
||||
useGlobalPathValue.addListener (this);
|
||||
|
||||
auto menuItemString = (TargetOS::getThisOS() == TargetOS::osx ? "\"Projucer->Global Paths...\""
|
||||
: "\"File->Global Paths...\"");
|
||||
|
||||
props.add (new BooleanPropertyComponent (useGlobalPathValue,
|
||||
"Use global path", "Use global path for this module"),
|
||||
String ("If this is enabled, then the locally-stored global path (set in the ") + menuItemString + " menu item) "
|
||||
"will be used as the path to this module. "
|
||||
"This means that if this Projucer project is opened on another machine it will use that machine's global path as the path to this module.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (modules.shouldCopyModuleFilesLocallyValue (moduleID),
|
||||
"Create local copy", "Copy the module into the project folder"),
|
||||
"If this is enabled, then a local copy of the entire module will be made inside your project (in the auto-generated JuceLibraryFiles folder), "
|
||||
"so that your project will be self-contained, and won't need to contain any references to files in other folders. "
|
||||
"This also means that you can check the module into your source-control system to make sure it is always in sync with your own code.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (modules.shouldShowAllModuleFilesInProjectValue (moduleID),
|
||||
"Add source to project", "Make module files browsable in projects"),
|
||||
"If this is enabled, then the entire source tree from this module will be shown inside your project, "
|
||||
"making it easy to browse/edit the module's classes. If disabled, then only the minimum number of files "
|
||||
"required to compile it will appear inside your project.");
|
||||
|
||||
auto info = modules.getModuleInfo (moduleID);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
configFlags.clear();
|
||||
LibraryModule (info).getConfigFlags (project, configFlags);
|
||||
|
||||
for (auto* flag : configFlags)
|
||||
{
|
||||
auto* c = new ChoicePropertyComponent (flag->value, flag->symbol);
|
||||
c->setTooltip (flag->description);
|
||||
props.add (c);
|
||||
}
|
||||
}
|
||||
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
String getModuleID() const noexcept { return moduleID; }
|
||||
|
||||
private:
|
||||
void valueChanged (Value& v) override
|
||||
{
|
||||
auto isExporterPathValue = [&]
|
||||
{
|
||||
for (auto& exporterValue : exporterModulePathValues)
|
||||
if (exporterValue.refersToSameSourceAs (v))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (isExporterPathValue)
|
||||
project.rescanExporterPathModules();
|
||||
|
||||
startTimer (50);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<ValueWithDefault> exporterModulePathDefaultValues;
|
||||
Array<Value> exporterModulePathValues, globalPathValues;
|
||||
Value useGlobalPathValue;
|
||||
|
||||
OwnedArray<Project::ConfigFlag> configFlags;
|
||||
|
||||
PropertyGroupComponent group;
|
||||
Project& project;
|
||||
SafePointer<TreeView> modulesTree;
|
||||
String moduleID;
|
||||
|
||||
//==============================================================================
|
||||
class ModuleInfoComponent : public PropertyComponent,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
ModuleInfoComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Module", 150), project (p), moduleID (modID)
|
||||
{
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
listeningValues.add (new Value (exporter->getPathForModuleValue (moduleID).getPropertyAsValue()))->addListener (this);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() override
|
||||
{
|
||||
info = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
repaint();
|
||||
}
|
||||
|
||||
private:
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
bounds.removeFromTop (5);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
auto topSlice = bounds.removeFromTop (bounds.getHeight() / 2);
|
||||
bounds.removeFromTop (bounds.getHeight() / 6);
|
||||
auto bottomSlice = bounds;
|
||||
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
|
||||
g.drawFittedText (info.getName(), topSlice.removeFromTop (topSlice.getHeight() / 4), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("Version: " + info.getVersion(), topSlice.removeFromTop (topSlice.getHeight() / 3), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("License: " + info.getLicense(), topSlice.removeFromTop (topSlice.getHeight() / 2), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("Location: " + info.getFolder().getParentDirectory().getFullPathName(),
|
||||
topSlice.removeFromTop (topSlice.getHeight()), Justification::centredLeft, 1);
|
||||
|
||||
g.drawFittedText (info.getDescription(), bottomSlice, Justification::topLeft, 3, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour (Colours::red);
|
||||
g.drawFittedText ("Cannot find this module at the specified path!", bounds, Justification::centred, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
Project& project;
|
||||
String moduleID;
|
||||
OwnedArray<Value> listeningValues;
|
||||
ModuleDescription info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleInfoComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MissingDependenciesComponent : public PropertyComponent
|
||||
{
|
||||
public:
|
||||
MissingDependenciesComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Dependencies", 100),
|
||||
project (p), moduleID (modID),
|
||||
missingDependencies (project.getEnabledModules().getExtraDependenciesNeeded (modID))
|
||||
{
|
||||
addAndMakeVisible (fixButton);
|
||||
fixButton.setColour (TextButton::buttonColourId, Colours::red);
|
||||
fixButton.setColour (TextButton::textColourOffId, Colours::white);
|
||||
fixButton.onClick = [this] { fixDependencies(); };
|
||||
}
|
||||
|
||||
void refresh() override {}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
String text ("This module has missing dependencies!\n\n"
|
||||
"To build correctly, it requires the following modules to be added:\n");
|
||||
text << missingDependencies.joinIntoString (", ");
|
||||
|
||||
g.setColour (Colours::red);
|
||||
g.drawFittedText (text, getLocalBounds().reduced (10), Justification::topLeft, 3);
|
||||
}
|
||||
|
||||
void fixDependencies()
|
||||
{
|
||||
auto& enabledModules = project.getEnabledModules();
|
||||
|
||||
if (enabledModules.tryToFixMissingDependencies (moduleID))
|
||||
{
|
||||
missingDependencies.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID);
|
||||
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"Adding Missing Dependencies",
|
||||
"Couldn't locate some of these modules - you'll need to find their "
|
||||
"folders manually and add them to the list.");
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
fixButton.setBounds (getWidth() - 168, getHeight() - 26, 160, 22);
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
|
||||
String moduleID;
|
||||
StringArray missingDependencies;
|
||||
TextButton fixButton { "Add Required Modules" };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct CppStandardWarningComponent : public PropertyComponent
|
||||
{
|
||||
CppStandardWarningComponent()
|
||||
: PropertyComponent ("CppStandard", 100)
|
||||
{
|
||||
}
|
||||
|
||||
void refresh() override {}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto text = String ("This module has a higher C++ language standard requirement than your project!\n\n"
|
||||
"To use this module you need to increase the C++ standard of the project.\n");
|
||||
|
||||
g.setColour (findColour (defaultHighlightColourId));
|
||||
g.drawFittedText (text, getLocalBounds().reduced (10), Justification::topLeft, 3);
|
||||
}
|
||||
|
||||
StringArray configsToWarnAbout;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppStandardWarningComponent)
|
||||
};
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EnabledModulesItem : public ProjectTreeItemBase,
|
||||
private Value::Listener,
|
||||
private AvailableModulesList::Listener
|
||||
{
|
||||
public:
|
||||
EnabledModulesItem (Project& p)
|
||||
: project (p),
|
||||
modulesListTree (project.getEnabledModules().getState())
|
||||
{
|
||||
modulesListTree.addListener (this);
|
||||
|
||||
projectCppStandardValue.referTo (project.getProjectValue (Ids::cppLanguageStandard));
|
||||
projectCppStandardValue.addListener (this);
|
||||
|
||||
ProjucerApplication::getApp().getJUCEPathModulesList().addListener (this);
|
||||
ProjucerApplication::getApp().getUserPathsModulesList().addListener (this);
|
||||
|
||||
project.getExporterPathsModulesList().addListener (this);
|
||||
}
|
||||
|
||||
~EnabledModulesItem() override
|
||||
{
|
||||
ProjucerApplication::getApp().getJUCEPathModulesList().removeListener (this);
|
||||
ProjucerApplication::getApp().getUserPathsModulesList().removeListener (this);
|
||||
|
||||
project.getExporterPathsModulesList().removeListener (this);
|
||||
}
|
||||
|
||||
int getItemHeight() const override { return 22; }
|
||||
bool isModulesList() const override { return true; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return true; }
|
||||
String getUniqueName() const override { return "modules"; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return "Modules"; }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() const override { return false; }
|
||||
Icon getIcon() const override { return Icon (getIcons().graph, getContentColour (true)); }
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::make_unique<ModulesInformationComponent> (project));
|
||||
}
|
||||
|
||||
static File getModuleFolder (const File& draggedFile)
|
||||
{
|
||||
if (draggedFile.hasFileExtension (headerFileExtensions))
|
||||
return draggedFile.getParentDirectory();
|
||||
|
||||
return draggedFile;
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
for (auto i = files.size(); --i >= 0;)
|
||||
if (ModuleDescription (getModuleFolder (files[i])).isValid())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int /*insertIndex*/) override
|
||||
{
|
||||
Array<ModuleDescription> modules;
|
||||
|
||||
for (auto f : files)
|
||||
{
|
||||
ModuleDescription m (getModuleFolder (f));
|
||||
|
||||
if (m.isValid())
|
||||
modules.add (m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
project.getEnabledModules().addModule (modules.getReference (i).getModuleFolder(),
|
||||
project.getEnabledModules().areMostModulesCopiedLocally(),
|
||||
project.getEnabledModules().areMostModulesUsingGlobalPath());
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < project.getEnabledModules().getNumModules(); ++i)
|
||||
addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i)));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
auto& enabledModules = project.getEnabledModules();
|
||||
PopupMenu allModules;
|
||||
|
||||
int index = 100;
|
||||
|
||||
// JUCE path
|
||||
PopupMenu jucePathModules;
|
||||
|
||||
for (auto& mod : ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules())
|
||||
jucePathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
jucePathModules.addSeparator();
|
||||
jucePathModules.addItem (-1, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Global JUCE modules path", jucePathModules);
|
||||
|
||||
// User path
|
||||
index = 200;
|
||||
PopupMenu userPathModules;
|
||||
|
||||
for (auto& mod : ProjucerApplication::getApp().getUserPathsModulesList().getAllModules())
|
||||
userPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
userPathModules.addSeparator();
|
||||
userPathModules.addItem (-2, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Global user modules path", userPathModules);
|
||||
|
||||
// Exporter path
|
||||
index = 300;
|
||||
PopupMenu exporterPathModules;
|
||||
|
||||
for (auto& mod : project.getExporterPathsModulesList().getAllModules())
|
||||
exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
exporterPathModules.addSeparator();
|
||||
exporterPathModules.addItem (-3, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Exporter paths", exporterPathModules);
|
||||
|
||||
PopupMenu menu;
|
||||
menu.addSubMenu ("Add a module", allModules);
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addItem (1001, "Add a module from a specified folder...");
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1001)
|
||||
{
|
||||
project.getEnabledModules().addModuleFromUserSelectedFile();
|
||||
}
|
||||
else if (resultCode < 0)
|
||||
{
|
||||
if (resultCode == -1) ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
else if (resultCode == -2) ProjucerApplication::getApp().rescanUserPathModules();
|
||||
else if (resultCode == -3) project.rescanExporterPathModules();
|
||||
}
|
||||
else if (resultCode > 0)
|
||||
{
|
||||
std::vector<AvailableModulesList::ModuleIDAndFolder> list;
|
||||
int offset = -1;
|
||||
|
||||
if (resultCode < 200)
|
||||
{
|
||||
list = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules();
|
||||
offset = 100;
|
||||
}
|
||||
else if (resultCode < 300)
|
||||
{
|
||||
list = ProjucerApplication::getApp().getUserPathsModulesList().getAllModules();
|
||||
offset = 200;
|
||||
}
|
||||
else if (resultCode < 400)
|
||||
{
|
||||
list = project.getExporterPathsModulesList().getAllModules();
|
||||
offset = 300;
|
||||
}
|
||||
|
||||
if (offset != -1)
|
||||
project.getEnabledModules().addModuleInteractive (list[(size_t) (resultCode - offset)].first);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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 == modulesListTree)
|
||||
{
|
||||
auto selectedID = getSelectedItemID();
|
||||
|
||||
refreshSubItems();
|
||||
|
||||
if (selectedID.isNotEmpty())
|
||||
setSelectedItem (selectedID);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree modulesListTree;
|
||||
Value projectCppStandardValue;
|
||||
|
||||
//==============================================================================
|
||||
void valueChanged (Value& v) override
|
||||
{
|
||||
if (v == projectCppStandardValue)
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
{
|
||||
if (auto* moduleItem = dynamic_cast<ModuleItem*> (getSubItem (i)))
|
||||
{
|
||||
if (moduleItem->checkCppStandard())
|
||||
{
|
||||
refreshSubItems();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeDuplicateModules()
|
||||
{
|
||||
auto jucePathModulesList = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules();
|
||||
|
||||
auto& userPathModules = ProjucerApplication::getApp().getUserPathsModulesList();
|
||||
userPathModules.removeDuplicates (jucePathModulesList);
|
||||
|
||||
auto& exporterPathModules = project.getExporterPathsModulesList();
|
||||
exporterPathModules.removeDuplicates (jucePathModulesList);
|
||||
exporterPathModules.removeDuplicates (userPathModules.getAllModules());
|
||||
}
|
||||
|
||||
void availableModulesChanged (AvailableModulesList*) override
|
||||
{
|
||||
removeDuplicateModules();
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
String getSelectedItemID() const
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (auto* item = getSubItem (i))
|
||||
if (item->isSelected())
|
||||
return item->getUniqueName();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void setSelectedItem (const String& itemID)
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
{
|
||||
if (auto* item = getSubItem (i))
|
||||
{
|
||||
if (item->getUniqueName() == itemID)
|
||||
{
|
||||
item->setSelected (true, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem)
|
||||
};
|
81
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ProjectTreeItemBase.h
vendored
Normal file
81
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ProjectTreeItemBase.h
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectTreeItemBase : public JucerTreeViewBase,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
ProjectTreeItemBase() {}
|
||||
|
||||
void showSettingsPage (Component* content)
|
||||
{
|
||||
content->setComponentID (getUniqueName());
|
||||
std::unique_ptr<Component> comp (content);
|
||||
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::move (comp));
|
||||
}
|
||||
|
||||
void closeSettingsPage()
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
if (auto* content = pcc->getEditorComponent())
|
||||
if (content->getComponentID() == getUniqueName())
|
||||
pcc->hideEditor();
|
||||
}
|
||||
|
||||
void deleteAllSelectedItems() override
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
|
||||
|
||||
if (auto* s = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (0)))
|
||||
s->deleteItem();
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
virtual bool isProjectSettings() const { return false; }
|
||||
virtual bool isModulesList() const { return false; }
|
||||
|
||||
static void updateSize (Component& comp, PropertyGroupComponent& group)
|
||||
{
|
||||
auto width = jmax (550, comp.getParentWidth() - 12);
|
||||
|
||||
auto y = 0;
|
||||
y += group.updateSize (12, y, width - 12);
|
||||
|
||||
y = jmax (comp.getParentHeight(), y);
|
||||
|
||||
comp.setSize (width, y);
|
||||
}
|
||||
};
|
560
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_Sidebar.h
vendored
Normal file
560
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_Sidebar.h
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class ConcertinaHeader : public Component,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
ConcertinaHeader (String n, Path p)
|
||||
: Component (n), name (n), iconPath (p)
|
||||
{
|
||||
setTitle (getName());
|
||||
|
||||
panelIcon = Icon (iconPath, Colours::white);
|
||||
|
||||
nameLabel.setText (name, dontSendNotification);
|
||||
nameLabel.setJustificationType (Justification::centredLeft);
|
||||
nameLabel.setInterceptsMouseClicks (false, false);
|
||||
nameLabel.setAccessible (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 (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 (! e.mouseWasDraggedSinceMouseDown())
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::button,
|
||||
AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[this] { sendChangeMessage(); }));
|
||||
}
|
||||
|
||||
int direction = 0;
|
||||
int yPosition = 0;
|
||||
|
||||
private:
|
||||
String name;
|
||||
Label nameLabel;
|
||||
|
||||
Path iconPath;
|
||||
Icon panelIcon;
|
||||
|
||||
Rectangle<float> arrowBounds, iconBounds;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FindPanel : public Component,
|
||||
private Timer,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
FindPanel (std::function<void (const String&)> cb)
|
||||
: callback (cb)
|
||||
{
|
||||
addAndMakeVisible (editor);
|
||||
editor.onTextChange = [this] { startTimer (250); };
|
||||
editor.onFocusLost = [this]
|
||||
{
|
||||
isFocused = false;
|
||||
repaint();
|
||||
};
|
||||
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
~FindPanel() override
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
}
|
||||
|
||||
void paintOverChildren (Graphics& g) override
|
||||
{
|
||||
if (! isFocused)
|
||||
return;
|
||||
|
||||
g.setColour (findColour (defaultHighlightColourId));
|
||||
|
||||
Path p;
|
||||
p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
|
||||
g.strokePath (p, PathStrokeType (2.0f));
|
||||
}
|
||||
|
||||
|
||||
void resized() override
|
||||
{
|
||||
editor.setBounds (getLocalBounds().reduced (2));
|
||||
}
|
||||
|
||||
private:
|
||||
TextEditor editor;
|
||||
bool isFocused = false;
|
||||
std::function<void (const String&)> callback;
|
||||
|
||||
//==============================================================================
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
|
||||
}
|
||||
|
||||
void globalFocusChanged (Component* focusedComponent) override
|
||||
{
|
||||
if (focusedComponent == &editor)
|
||||
{
|
||||
isFocused = true;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
callback (editor.getText());
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ConcertinaTreeComponent : public Component
|
||||
{
|
||||
public:
|
||||
class AdditionalComponents
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
addButton = (1 << 0),
|
||||
settingsButton = (1 << 1),
|
||||
findPanel = (1 << 2)
|
||||
};
|
||||
|
||||
AdditionalComponents with (Type t)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy.componentTypes |= t;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool has (Type t) const noexcept
|
||||
{
|
||||
return (componentTypes & t) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int componentTypes = 0;
|
||||
};
|
||||
|
||||
ConcertinaTreeComponent (const String& name,
|
||||
TreePanelBase* tree,
|
||||
AdditionalComponents additionalComponents)
|
||||
: Component (name),
|
||||
treeToDisplay (tree)
|
||||
{
|
||||
setTitle (getName());
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::addButton))
|
||||
{
|
||||
addButton = std::make_unique<IconButton> ("Add", getIcons().plus);
|
||||
addAndMakeVisible (addButton.get());
|
||||
addButton->onClick = [this] { showAddMenu(); };
|
||||
}
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::settingsButton))
|
||||
{
|
||||
settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings);
|
||||
addAndMakeVisible (settingsButton.get());
|
||||
settingsButton->onClick = [this] { showSettings(); };
|
||||
}
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::findPanel))
|
||||
{
|
||||
findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); });
|
||||
addAndMakeVisible (findPanel.get());
|
||||
}
|
||||
|
||||
addAndMakeVisible (treeToDisplay.get());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
|
||||
{
|
||||
auto bottomSlice = bounds.removeFromBottom (25);
|
||||
bottomSlice.removeFromRight (3);
|
||||
|
||||
if (addButton != nullptr)
|
||||
addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
|
||||
|
||||
if (settingsButton != nullptr)
|
||||
settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
|
||||
|
||||
if (findPanel != nullptr)
|
||||
findPanel->setBounds (bottomSlice.reduced (2));
|
||||
}
|
||||
|
||||
treeToDisplay->setBounds (bounds);
|
||||
}
|
||||
|
||||
TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<TreePanelBase> treeToDisplay;
|
||||
std::unique_ptr<IconButton> addButton, settingsButton;
|
||||
std::unique_ptr<FindPanel> findPanel;
|
||||
|
||||
void showAddMenu()
|
||||
{
|
||||
auto numSelected = treeToDisplay->tree.getNumSelectedItems();
|
||||
|
||||
if (numSelected > 1)
|
||||
return;
|
||||
|
||||
if (numSelected == 0)
|
||||
{
|
||||
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
||||
root->showPopupMenu (addButton->getScreenBounds().getCentre());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
|
||||
item->showAddMenu (addButton->getScreenBounds().getCentre());
|
||||
}
|
||||
}
|
||||
|
||||
void showSettings()
|
||||
{
|
||||
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
||||
{
|
||||
treeToDisplay->tree.clearSelectedItems();
|
||||
root->showDocument();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectSettingsComponent : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
ProjectSettingsComponent (Project& p)
|
||||
: project (p),
|
||||
group (project.getProjectFilenameRootString(),
|
||||
Icon (getIcons().settings, Colours::transparentBlack))
|
||||
{
|
||||
setTitle ("Project Settings");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (group);
|
||||
|
||||
updatePropertyList();
|
||||
project.addChangeListener (this);
|
||||
}
|
||||
|
||||
~ProjectSettingsComponent() override
|
||||
{
|
||||
project.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
group.updateSize (12, 0, getWidth() - 24);
|
||||
group.setBounds (getLocalBounds().reduced (12, 0));
|
||||
}
|
||||
|
||||
void updatePropertyList()
|
||||
{
|
||||
PropertyListBuilder props;
|
||||
project.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
group.setName ("Project Settings");
|
||||
|
||||
lastProjectType = project.getProjectTypeString();
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (lastProjectType != project.getProjectTypeString())
|
||||
updatePropertyList();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
auto width = jmax (550, getParentWidth());
|
||||
auto y = group.updateSize (12, 0, width - 12);
|
||||
|
||||
y = jmax (getParentHeight(), y);
|
||||
|
||||
setSize (width, y);
|
||||
}
|
||||
|
||||
Project& project;
|
||||
var lastProjectType;
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSettingsComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FileTreePanel : public TreePanelBase
|
||||
{
|
||||
FileTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "fileTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (true);
|
||||
setRoot (std::make_unique<TreeItemTypes::GroupItem> (p.getMainGroup()));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
|
||||
void updateMissingFileStatuses()
|
||||
{
|
||||
if (auto* p = dynamic_cast<TreeItemTypes::FileTreeItemBase*> (rootItem.get()))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModuleTreePanel : public TreePanelBase
|
||||
{
|
||||
ModuleTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "moduleTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (false);
|
||||
setRoot (std::make_unique<TreeItemTypes::EnabledModulesItem> (p));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExportersTreePanel : public TreePanelBase
|
||||
{
|
||||
ExportersTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "exportersTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (false);
|
||||
setRoot (std::make_unique<TreeItemTypes::ExportersTreeRoot> (p));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Sidebar : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
Sidebar (Project* p)
|
||||
: project (p)
|
||||
{
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
if (project != nullptr)
|
||||
buildConcertina();
|
||||
}
|
||||
|
||||
~Sidebar() override
|
||||
{
|
||||
TreePanelBase* panels[] = { getFileTreePanel(), getModuleTreePanel(), getExportersTreePanel() };
|
||||
|
||||
for (auto* panel : panels)
|
||||
if (panel != nullptr)
|
||||
panel->saveOpenness();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
concertinaPanel.setBounds (getLocalBounds().withTrimmedBottom (3));
|
||||
}
|
||||
|
||||
TreePanelBase* getTreeWithSelectedItems()
|
||||
{
|
||||
for (auto 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() { return getPanel<FileTreePanel> (0); }
|
||||
ModuleTreePanel* getModuleTreePanel() { return getPanel<ModuleTreePanel> (1); }
|
||||
ExportersTreePanel* getExportersTreePanel() { return getPanel<ExportersTreePanel> (2); }
|
||||
|
||||
void showPanel (int panelIndex)
|
||||
{
|
||||
jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
|
||||
|
||||
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (panelIndex), true);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename PanelType>
|
||||
PanelType* getPanel (int panelIndex)
|
||||
{
|
||||
if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (panelIndex)))
|
||||
return dynamic_cast<PanelType*> (panel->getTree());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
const auto pointerMatches = [source] (const std::unique_ptr<ConcertinaHeader>& header) { return header.get() == source; };
|
||||
const auto it = std::find_if (headers.begin(), headers.end(), pointerMatches);
|
||||
const auto index = (int) std::distance (headers.begin(), it);
|
||||
|
||||
if (index != (int) headers.size())
|
||||
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (index), true);
|
||||
}
|
||||
|
||||
void buildConcertina()
|
||||
{
|
||||
for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0 ; --i)
|
||||
concertinaPanel.removePanel (concertinaPanel.getPanel (i));
|
||||
|
||||
headers.clear();
|
||||
|
||||
auto addPanel = [this] (const String& name,
|
||||
TreePanelBase* tree,
|
||||
ConcertinaTreeComponent::AdditionalComponents components,
|
||||
const Path& icon)
|
||||
{
|
||||
if (project != nullptr)
|
||||
concertinaPanel.addPanel (-1, new ConcertinaTreeComponent (name, tree, components), true);
|
||||
|
||||
headers.push_back (std::make_unique<ConcertinaHeader> (name, icon));
|
||||
};
|
||||
|
||||
using AdditionalComponents = ConcertinaTreeComponent::AdditionalComponents;
|
||||
|
||||
addPanel ("File Explorer", new FileTreePanel (*project),
|
||||
AdditionalComponents{}
|
||||
.with (AdditionalComponents::addButton)
|
||||
.with (AdditionalComponents::findPanel),
|
||||
getIcons().fileExplorer);
|
||||
|
||||
addPanel ("Modules", new ModuleTreePanel (*project),
|
||||
AdditionalComponents{}
|
||||
.with (AdditionalComponents::addButton)
|
||||
.with (AdditionalComponents::settingsButton),
|
||||
getIcons().modules);
|
||||
|
||||
addPanel ("Exporters", new ExportersTreePanel (*project),
|
||||
AdditionalComponents{}.with (AdditionalComponents::addButton),
|
||||
getIcons().exporter);
|
||||
|
||||
for (int i = 0; i < concertinaPanel.getNumPanels(); ++i)
|
||||
{
|
||||
auto* p = concertinaPanel.getPanel (i);
|
||||
auto* h = headers[(size_t) i].get();
|
||||
p->addMouseListener (this, true);
|
||||
|
||||
h->addChangeListener (this);
|
||||
h->yPosition = i * 30;
|
||||
|
||||
concertinaPanel.setCustomPanelHeader (p, h, false);
|
||||
concertinaPanel.setPanelHeaderSize (p, 30);
|
||||
}
|
||||
|
||||
addAndMakeVisible (concertinaPanel);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
|
||||
{
|
||||
if (auto* p = concertinaPanel.getPanel (i))
|
||||
{
|
||||
if (! (p->isParentOf (e.eventComponent)))
|
||||
{
|
||||
auto* base = dynamic_cast<TreePanelBase*> (p);
|
||||
|
||||
if (base == nullptr)
|
||||
if (auto* concertina = dynamic_cast<ConcertinaTreeComponent*> (p))
|
||||
base = concertina->getTree();
|
||||
|
||||
if (base != nullptr)
|
||||
base->tree.clearSelectedItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ConcertinaPanel concertinaPanel;
|
||||
std::vector<std::unique_ptr<ConcertinaHeader>> headers;
|
||||
Project* project = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Sidebar)
|
||||
};
|
42
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_TreeItemTypes.h
vendored
Normal file
42
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_TreeItemTypes.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
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 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "../../../Application/Windows/jucer_TranslationToolWindowComponent.h"
|
||||
#include "../../../Utility/UI/jucer_JucerTreeViewBase.h"
|
||||
#include "../../../Utility/Helpers/jucer_NewFileWizard.h"
|
||||
#include "../jucer_ContentViewComponents.h"
|
||||
#include "../jucer_FileGroupInformationComponent.h"
|
||||
#include "../jucer_ModulesInformationComponent.h"
|
||||
|
||||
struct TreeItemTypes
|
||||
{
|
||||
#include "jucer_ProjectTreeItemBase.h"
|
||||
#include "jucer_ExporterTreeItems.h"
|
||||
#include "jucer_ModuleTreeItems.h"
|
||||
#include "jucer_FileTreeItems.h"
|
||||
};
|
Reference in New Issue
Block a user