1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
2023-10-02 15:42:20 +01:00

884 lines
31 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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...";
auto options = MessageBoxOptions::makeOptionsYesNoCancel (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());
messageBox = AlertWindow::showScopedAsync (options, [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 final : 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;
}
ScopedMessageBox messageBox;
private:
std::unique_ptr<FileChooser> chooser;
JUCE_DECLARE_WEAK_REFERENCEABLE (FileTreeItemBase)
};
//==============================================================================
class SourceFileItem final : 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))
{
auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
"File Rename",
"That filename contained some illegal characters!");
messageBox = AlertWindow::showScopedAsync (options, [this, item = item] (int)
{
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())
{
auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::NoIcon,
"File Rename",
"Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?");
messageBox = AlertWindow::showScopedAsync (options, [parent = WeakReference { this }, oldFile, newFile, correspondingFile, correspondingItem] (int result) mutable
{
if (parent == nullptr || result == 0)
return;
if (! parent->item.renameFile (newFile))
{
auto opts = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
"File Rename",
"Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
parent->messageBox = AlertWindow::showScopedAsync (opts, nullptr);
return;
}
if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension())))
{
auto opts = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
"File Rename",
"Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
parent->messageBox = AlertWindow::showScopedAsync (opts, nullptr);
}
});
}
}
if (! item.renameFile (newFile))
{
auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon,
"File Rename",
"Failed to rename the file!\n\nCheck your file permissions!");
messageBox = AlertWindow::showScopedAsync (options, nullptr);
}
}
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 final : 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;
};