1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.cpp

528 lines
15 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "jucer_ProjectTreeViewBase.h"
#include "../Application/jucer_OpenDocumentManager.h"
//==============================================================================
ProjectTreeViewBase::ProjectTreeViewBase (const Project::Item& item_)
: item (item_), isFileMissing (false)
{
item.getNode().addListener (this);
}
ProjectTreeViewBase::~ProjectTreeViewBase()
{
item.getNode().removeListener (this);
}
//==============================================================================
const String ProjectTreeViewBase::getDisplayName() const
{
return item.getName().toString();
}
void ProjectTreeViewBase::setName (const String& newName)
{
if (item.isMainGroup())
item.getProject().setTitle (newName);
else
item.getName() = newName;
}
//==============================================================================
const File ProjectTreeViewBase::getFile() const
{
return item.getFile();
}
void ProjectTreeViewBase::browseToAddExistingFiles()
{
const File location (item.isGroup() ? item.determineGroupFolder() : getFile());
FileChooser fc ("Add Files to Jucer Project", location, String::empty, false);
if (fc.browseForMultipleFilesOrDirectories())
{
StringArray files;
for (int i = 0; i < fc.getResults().size(); ++i)
files.add (fc.getResults().getReference(i).getFullPathName());
addFiles (files, 0);
}
}
void ProjectTreeViewBase::addFiles (const StringArray& files, int insertIndex)
{
ProjectTreeViewBase* p = dynamic_cast <ProjectTreeViewBase*> (getParentItem());
if (p != nullptr)
p->addFiles (files, insertIndex);
}
void ProjectTreeViewBase::moveSelectedItemsTo (OwnedArray <Project::Item>& selectedNodes, int insertIndex)
{
jassertfalse;
}
//==============================================================================
ProjectTreeViewBase* ProjectTreeViewBase::findTreeViewItem (const Project::Item& itemToFind)
{
if (item == itemToFind)
{
return this;
}
else
{
const bool wasOpen = isOpen();
setOpen (true);
for (int i = getNumSubItems(); --i >= 0;)
{
ProjectTreeViewBase* pg = dynamic_cast <ProjectTreeViewBase*> (getSubItem(i));
if (pg != nullptr)
{
pg = pg->findTreeViewItem (itemToFind);
if (pg != nullptr)
return pg;
}
}
setOpen (wasOpen);
}
return 0;
}
//==============================================================================
void ProjectTreeViewBase::triggerAsyncRename (const Project::Item& itemToRename)
{
class RenameMessage : public CallbackMessage
{
public:
RenameMessage (TreeView* const tree_, const Project::Item& itemToRename_)
: tree (tree_), itemToRename (itemToRename_) {}
void messageCallback()
{
if (tree != nullptr)
{
ProjectTreeViewBase* pg = dynamic_cast <ProjectTreeViewBase*> (tree->getRootItem());
if (pg != nullptr)
{
pg = pg->findTreeViewItem (itemToRename);
if (pg != nullptr)
pg->showRenameBox();
}
}
}
private:
Component::SafePointer<TreeView> tree;
Project::Item itemToRename;
};
(new RenameMessage (getOwnerView(), itemToRename))->post();
}
//==============================================================================
void ProjectTreeViewBase::checkFileStatus()
{
const File file (getFile());
const bool nowMissing = file != File::nonexistent && ! file.exists();
if (nowMissing != isFileMissing)
{
isFileMissing = nowMissing;
repaintItem();
}
}
void ProjectTreeViewBase::revealInFinder() const
{
getFile().revealToUser();
}
void ProjectTreeViewBase::deleteItem()
{
item.removeItemFromProject();
}
void ProjectTreeViewBase::deleteAllSelectedItems()
{
TreeView* tree = getOwnerView();
const int numSelected = tree->getNumSelectedItems();
OwnedArray <File> filesToTrash;
OwnedArray <Project::Item> itemsToRemove;
int i;
for (i = 0; i < numSelected; ++i)
{
const ProjectTreeViewBase* const p = dynamic_cast <ProjectTreeViewBase*> (tree->getSelectedItem (i));
if (p != nullptr)
{
itemsToRemove.add (new Project::Item (p->item));
if (p->getFile().existsAsFile())
filesToTrash.add (new File (p->getFile()));
}
}
if (filesToTrash.size() > 0)
{
String fileList;
const int maxFilesToList = 10;
for (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...";
int r = AlertWindow::showYesNoCancelBox (AlertWindow::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());
if (r == 0)
return;
if (r != 2)
filesToTrash.clear();
}
ProjectTreeViewBase* treeRootItem = dynamic_cast <ProjectTreeViewBase*> (tree->getRootItem());
jassert (treeRootItem != nullptr);
if (treeRootItem != nullptr)
{
for (i = filesToTrash.size(); --i >= 0;)
{
const File f (*filesToTrash.getUnchecked(i));
OpenDocumentManager::getInstance()->closeFile (f, false);
if (! f.moveToTrash())
{
// xxx
}
}
for (i = itemsToRemove.size(); --i >= 0;)
{
ProjectTreeViewBase* itemToRemove = treeRootItem->findTreeViewItem (*itemsToRemove.getUnchecked(i));
if (itemToRemove != nullptr)
{
OpenDocumentManager::getInstance()->closeFile (itemToRemove->getFile(), false);
itemToRemove->deleteItem();
}
}
}
}
static int indexOfNode (const ValueTree& parent, const ValueTree& child)
{
for (int i = parent.getNumChildren(); --i >= 0;)
if (parent.getChild (i) == child)
return i;
return -1;
}
void ProjectTreeViewBase::moveItems (OwnedArray <Project::Item>& selectedNodes,
Project::Item destNode, int insertIndex)
{
int i;
for (i = selectedNodes.size(); --i >= 0;)
{
Project::Item* const n = selectedNodes.getUnchecked(i);
if (destNode == *n || destNode.getNode().isAChildOf (n->getNode())) // Check for recursion.
return;
if (! destNode.canContain (*n))
selectedNodes.remove (i);
}
// Don't include any nodes that are children of other selected nodes..
for (i = selectedNodes.size(); --i >= 0;)
{
Project::Item* const n = selectedNodes.getUnchecked(i);
for (int j = selectedNodes.size(); --j >= 0;)
{
if (j != i && n->getNode().isAChildOf (selectedNodes.getUnchecked(j)->getNode()))
{
selectedNodes.remove (i);
break;
}
}
}
// Remove and re-insert them one at a time..
for (i = 0; i < selectedNodes.size(); ++i)
{
Project::Item* selectedNode = selectedNodes.getUnchecked(i);
if (selectedNode->getNode().getParent() == destNode.getNode()
&& indexOfNode (destNode.getNode(), selectedNode->getNode()) < insertIndex)
--insertIndex;
selectedNode->removeItemFromProject();
destNode.addChild (*selectedNode, insertIndex++);
}
}
//==============================================================================
bool ProjectTreeViewBase::isInterestedInFileDrag (const StringArray& files)
{
return acceptsFileDrop (files);
}
void ProjectTreeViewBase::filesDropped (const StringArray& files, int insertIndex)
{
addFiles (files, insertIndex);
}
void ProjectTreeViewBase::getAllSelectedNodesInTree (Component* componentInTree, OwnedArray <Project::Item>& selectedNodes)
{
TreeView* tree = dynamic_cast <TreeView*> (componentInTree);
if (tree == nullptr)
tree = componentInTree->findParentComponentOfClass ((TreeView*) 0);
if (tree != nullptr)
{
const int numSelected = tree->getNumSelectedItems();
for (int i = 0; i < numSelected; ++i)
{
const ProjectTreeViewBase* const p = dynamic_cast <ProjectTreeViewBase*> (tree->getSelectedItem (i));
if (p != nullptr)
selectedNodes.add (new Project::Item (p->item));
}
}
}
bool ProjectTreeViewBase::isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails)
{
if (dragSourceDetails.description != projectItemDragType)
return false;
OwnedArray <Project::Item> selectedNodes;
getAllSelectedNodesInTree (dragSourceDetails.sourceComponent, selectedNodes);
return selectedNodes.size() > 0 && acceptsDragItems (selectedNodes);
}
void ProjectTreeViewBase::itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex)
{
OwnedArray <Project::Item> selectedNodes;
getAllSelectedNodesInTree (dragSourceDetails.sourceComponent, selectedNodes);
if (selectedNodes.size() > 0)
{
TreeView* tree = getOwnerView();
ScopedPointer <XmlElement> oldOpenness (tree->getOpennessState (false));
moveSelectedItemsTo (selectedNodes, insertIndex);
if (oldOpenness != nullptr)
tree->restoreOpennessState (*oldOpenness, false);
}
}
//==============================================================================
void ProjectTreeViewBase::treeChildrenChanged (const ValueTree& parentTree)
{
if (parentTree == item.getNode())
{
refreshSubItems();
treeHasChanged();
setOpen (true);
}
}
void ProjectTreeViewBase::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
{
if (tree == item.getNode())
repaintItem();
}
void ProjectTreeViewBase::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded)
{
treeChildrenChanged (parentTree);
}
void ProjectTreeViewBase::valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved)
{
treeChildrenChanged (parentTree);
}
void ProjectTreeViewBase::valueTreeChildOrderChanged (ValueTree& parentTree)
{
treeChildrenChanged (parentTree);
}
void ProjectTreeViewBase::valueTreeParentChanged (ValueTree& tree)
{
}
//==============================================================================
bool ProjectTreeViewBase::mightContainSubItems()
{
return item.getNumChildren() > 0;
}
const String ProjectTreeViewBase::getUniqueName() const
{
jassert (item.getID().isNotEmpty());
return item.getID();
}
void ProjectTreeViewBase::itemOpennessChanged (bool isNowOpen)
{
if (isNowOpen)
refreshSubItems();
}
void ProjectTreeViewBase::addSubItems()
{
for (int i = 0; i < item.getNumChildren(); ++i)
{
ProjectTreeViewBase* p = createSubItem (item.getChild(i));
if (p != nullptr)
addSubItem (p);
}
}
void ProjectTreeViewBase::refreshSubItems()
{
OpennessRestorer openness (*this);
clearSubItems();
addSubItems();
}
void ProjectTreeViewBase::showMultiSelectionPopupMenu()
{
PopupMenu m;
m.addItem (6, "Delete");
switch (m.show())
{
case 6: deleteAllSelectedItems(); break;
default: break;
}
}
void ProjectTreeViewBase::itemDoubleClicked (const MouseEvent& e)
{
invokeShowDocument();
}
class TreeviewItemSelectionTimer : public Timer
{
public:
TreeviewItemSelectionTimer (ProjectTreeViewBase& owner_)
: owner (owner_)
{}
void timerCallback()
{
owner.invokeShowDocument();
}
private:
ProjectTreeViewBase& owner;
JUCE_DECLARE_NON_COPYABLE (TreeviewItemSelectionTimer);
};
void ProjectTreeViewBase::itemSelectionChanged (bool isNowSelected)
{
if (isNowSelected)
{
delayedSelectionTimer = new TreeviewItemSelectionTimer (*this);
// for images, give the user longer to start dragging before assuming they're
// clicking to select it for previewing..
delayedSelectionTimer->startTimer (item.isImageFile() ? 250 : 120);
}
else
{
delayedSelectionTimer = nullptr;
}
}
const String ProjectTreeViewBase::getTooltip()
{
return String::empty;
}
const var ProjectTreeViewBase::getDragSourceDescription()
{
delayedSelectionTimer = nullptr;
return projectItemDragType;
}
void ProjectTreeViewBase::invokeShowDocument()
{
delayedSelectionTimer = nullptr;
showDocument();
}
//==============================================================================
ProjectTreeViewBase* ProjectTreeViewBase::getParentProjectItem() const
{
return dynamic_cast <ProjectTreeViewBase*> (getParentItem());
}
ProjectContentComponent* ProjectTreeViewBase::getProjectContentComponent() const
{
Component* c = getOwnerView();
while (c != nullptr)
{
ProjectContentComponent* pcc = dynamic_cast <ProjectContentComponent*> (c);
if (pcc != nullptr)
return pcc;
c = c->getParentComponent();
}
return 0;
}