- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class ProjectTreeItemBase : public JucerTreeViewBase,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
ProjectTreeItemBase (const Project::Item& projectItem)
|
||||
: item (projectItem), isFileMissing (false)
|
||||
{
|
||||
item.state.addListener (this);
|
||||
}
|
||||
|
||||
~ProjectTreeItemBase()
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool isRoot() const { return false; }
|
||||
|
||||
virtual bool acceptsFileDrop (const StringArray& files) const = 0;
|
||||
virtual bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) = 0;
|
||||
|
||||
//==============================================================================
|
||||
virtual String getDisplayName() const { return item.getName(); }
|
||||
virtual String getRenamingName() const { return getDisplayName(); }
|
||||
|
||||
virtual void setName (const String& newName)
|
||||
{
|
||||
if (item.isMainGroup())
|
||||
item.project.setTitle (newName);
|
||||
else
|
||||
item.getNameValue() = newName;
|
||||
}
|
||||
|
||||
virtual bool isMissing() { return isFileMissing; }
|
||||
virtual File getFile() const { return item.getFile(); }
|
||||
|
||||
virtual void deleteItem() { item.removeItemFromProject(); }
|
||||
|
||||
virtual void deleteAllSelectedItems()
|
||||
{
|
||||
TreeView* tree = getOwnerView();
|
||||
const int numSelected = tree->getNumSelectedItems();
|
||||
OwnedArray <File> filesToTrash;
|
||||
OwnedArray <Project::Item> itemsToRemove;
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
{
|
||||
if (const ProjectTreeItemBase* const p = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
{
|
||||
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 (int 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();
|
||||
}
|
||||
|
||||
if (ProjectTreeItemBase* treeRootItem = dynamic_cast<ProjectTreeItemBase*> (tree->getRootItem()))
|
||||
{
|
||||
OpenDocumentManager& om = IntrojucerApp::getApp().openDocumentManager;
|
||||
|
||||
for (int i = filesToTrash.size(); --i >= 0;)
|
||||
{
|
||||
const File f (*filesToTrash.getUnchecked(i));
|
||||
|
||||
om.closeFile (f, false);
|
||||
|
||||
if (! f.moveToTrash())
|
||||
{
|
||||
// xxx
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = itemsToRemove.size(); --i >= 0;)
|
||||
{
|
||||
if (ProjectTreeItemBase* itemToRemove = treeRootItem->findTreeViewItem (*itemsToRemove.getUnchecked(i)))
|
||||
{
|
||||
om.closeFile (itemToRemove->getFile(), false);
|
||||
itemToRemove->deleteItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void revealInFinder() const
|
||||
{
|
||||
getFile().revealToUser();
|
||||
}
|
||||
|
||||
virtual void 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);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void checkFileStatus() // (recursive)
|
||||
{
|
||||
const File file (getFile());
|
||||
const bool nowMissing = file != File::nonexistent && ! file.exists();
|
||||
|
||||
if (nowMissing != isFileMissing)
|
||||
{
|
||||
isFileMissing = nowMissing;
|
||||
repaintItem();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void addFiles (const StringArray& files, int insertIndex)
|
||||
{
|
||||
if (ProjectTreeItemBase* p = getParentProjectItem())
|
||||
p->addFiles (files, insertIndex);
|
||||
}
|
||||
|
||||
virtual void moveSelectedItemsTo (OwnedArray <Project::Item>&, int /*insertIndex*/)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
virtual void showMultiSelectionPopupMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete");
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this));
|
||||
}
|
||||
|
||||
static void treeViewMultiSelectItemChosen (int resultCode, ProjectTreeItemBase* item)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: item->deleteAllSelectedItems(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ProjectTreeItemBase* findTreeViewItem (const Project::Item& itemToFind)
|
||||
{
|
||||
if (item == itemToFind)
|
||||
return this;
|
||||
|
||||
const bool wasOpen = isOpen();
|
||||
setOpen (true);
|
||||
|
||||
for (int i = getNumSubItems(); --i >= 0;)
|
||||
{
|
||||
if (ProjectTreeItemBase* pg = dynamic_cast<ProjectTreeItemBase*> (getSubItem(i)))
|
||||
if (ProjectTreeItemBase* found = pg->findTreeViewItem (itemToFind))
|
||||
return found;
|
||||
}
|
||||
|
||||
setOpen (wasOpen);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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&) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeParentChanged (ValueTree&) override {}
|
||||
|
||||
//==============================================================================
|
||||
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 String::empty; }
|
||||
File getDraggableFile() const override { return getFile(); }
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
return projectItemDragType;
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < item.getNumChildren(); ++i)
|
||||
if (ProjectTreeItemBase* 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))
|
||||
IntrojucerApp::getApp().openFile (files[0]);
|
||||
else
|
||||
addFiles (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)
|
||||
{
|
||||
TreeView* tree = getOwnerView();
|
||||
ScopedPointer<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)
|
||||
{
|
||||
TreeView* tree = dynamic_cast<TreeView*> (dragSourceDetails.sourceComponent.get());
|
||||
|
||||
if (tree == nullptr)
|
||||
tree = dragSourceDetails.sourceComponent->findParentComponentOfClass<TreeView>();
|
||||
|
||||
if (tree != nullptr)
|
||||
{
|
||||
const int numSelected = tree->getNumSelectedItems();
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
if (const ProjectTreeItemBase* const p = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
selectedNodes.add (new Project::Item (p->item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProjectTreeItemBase* getParentProjectItem() const
|
||||
{
|
||||
return dynamic_cast<ProjectTreeItemBase*> (getParentItem());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project::Item item;
|
||||
|
||||
protected:
|
||||
bool isFileMissing;
|
||||
|
||||
virtual ProjectTreeItemBase* createSubItem (const Project::Item& node) = 0;
|
||||
|
||||
Icon getIcon() const override { return item.getIcon().withContrastingColourTo (getBackgroundColour()); }
|
||||
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)
|
||||
{
|
||||
class RenameMessage : public CallbackMessage
|
||||
{
|
||||
public:
|
||||
RenameMessage (TreeView* const t, const Project::Item& i)
|
||||
: tree (t), itemToRename (i) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (tree != nullptr)
|
||||
if (ProjectTreeItemBase* root = dynamic_cast<ProjectTreeItemBase*> (tree->getRootItem()))
|
||||
if (ProjectTreeItemBase* 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 (int i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
Project::Item* const 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 (int i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
Project::Item* const n = selectedNodes.getUnchecked(i);
|
||||
|
||||
for (int 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)
|
||||
{
|
||||
Project::Item* 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 (int i = parent.getNumChildren(); --i >= 0;)
|
||||
if (parent.getChild (i) == child)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user