Commit b02add24 authored by dt's avatar dt
Browse files

Add QML folder in project tree, show files there

Use the full path for folders to sort in the project tree.

For virtual folders ensure that the path ensures the sorting we want.

Reviewed-By: kkoehne
parent 376369d4
......@@ -69,7 +69,6 @@ bool CMakeProjectNode::addFiles(const ProjectExplorer::FileType fileType, const
return false;
}
// TODO: Maybe remove fileType, can be detected by project
bool CMakeProjectNode::removeFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths, QStringList *notRemoved)
{
Q_UNUSED(fileType)
......@@ -78,6 +77,13 @@ bool CMakeProjectNode::removeFiles(const ProjectExplorer::FileType fileType, con
return false;
}
bool CMakeProjectNode::deleteFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths)
{
Q_UNUSED(fileType)
Q_UNUSED(filePaths)
return false;
}
bool CMakeProjectNode::renameFile(const ProjectExplorer::FileType fileType, const QString &filePath, const QString &newFilePath)
{
Q_UNUSED(fileType)
......
......@@ -51,6 +51,8 @@ public:
virtual bool removeFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths,
QStringList *notRemoved = 0);
virtual bool deleteFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths);
virtual bool renameFile(const ProjectExplorer::FileType fileType,
const QString &filePath,
const QString &newFilePath);
......
......@@ -208,6 +208,14 @@ bool GenericProjectNode::removeFiles(const ProjectExplorer::FileType fileType,
return m_project->removeFiles(filePaths);
}
bool GenericProjectNode::deleteFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths)
{
Q_UNUSED(fileType)
Q_UNUSED(filePaths)
return false;
}
bool GenericProjectNode::renameFile(const ProjectExplorer::FileType fileType,
const QString &filePath, const QString &newFilePath)
{
......
......@@ -67,6 +67,8 @@ public:
virtual bool removeFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths,
QStringList *notRemoved = 0);
virtual bool deleteFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths);
virtual bool renameFile(const ProjectExplorer::FileType fileType,
const QString &filePath,
......
......@@ -166,6 +166,7 @@ struct ProjectExplorerPluginPrivate {
QAction *m_showInGraphicalShell;
QAction *m_openTerminalHere;
QAction *m_removeFileAction;
QAction *m_deleteFileAction;
QAction *m_renameFileAction;
QAction *m_projectSelectorAction;
QAction *m_projectSelectorActionMenu;
......@@ -723,6 +724,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
d->m_deleteFileAction = new QAction(tr("Delete File..."), this);
cmd = am->registerAction(d->m_deleteFileAction, ProjectExplorer::Constants::DELETEFILE,
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
// renamefile action
d->m_renameFileAction = new QAction(tr("Rename"), this);
cmd = am->registerAction(d->m_renameFileAction, ProjectExplorer::Constants::RENAMEFILE,
......@@ -822,6 +828,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(d->m_showInGraphicalShell, SIGNAL(triggered()), this, SLOT(showInGraphicalShell()));
connect(d->m_openTerminalHere, SIGNAL(triggered()), this, SLOT(openTerminalHere()));
connect(d->m_removeFileAction, SIGNAL(triggered()), this, SLOT(removeFile()));
connect(d->m_deleteFileAction, SIGNAL(triggered()), this, SLOT(deleteFile()));
connect(d->m_renameFileAction, SIGNAL(triggered()), this, SLOT(renameFile()));
updateActions();
......@@ -1934,6 +1941,10 @@ void ProjectExplorerPlugin::updateContextMenuActions(Node *node)
d->m_addExistingFilesAction->setEnabled(false);
d->m_addNewFileAction->setEnabled(false);
d->m_removeFileAction->setEnabled(false);
d->m_deleteFileAction->setEnabled(false);
d->m_removeFileAction->setVisible(true);
d->m_deleteFileAction->setVisible(true);
if (node->projectNode()) {
QList<ProjectNode::ProjectAction> actions =
......@@ -1945,8 +1956,18 @@ void ProjectExplorerPlugin::updateContextMenuActions(Node *node)
d->m_addNewFileAction->setEnabled(addFilesEnabled);
d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));
} else if (qobject_cast<FileNode*>(d->m_currentNode)) {
bool removeFileEnabled = actions.contains(ProjectNode::RemoveFile);
d->m_removeFileAction->setEnabled(removeFileEnabled);
// Enable and show remove / delete in magic ways:
// If both are disabled show Remove
// If both are enabled show both (can't happen atm)
// If only removeFile is enabled only show it
// If only deleteFile is enable only show it
bool enableRemove = actions.contains(ProjectNode::RemoveFile);
d->m_removeFileAction->setEnabled(enableRemove);
bool enableDelete = actions.contains(ProjectNode::DeleteFile);
d->m_deleteFileAction->setEnabled(enableDelete);
d->m_deleteFileAction->setVisible(enableDelete);
d->m_removeFileAction->setVisible(!enableDelete || enableRemove);
d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));
}
}
......@@ -2079,6 +2100,39 @@ void ProjectExplorerPlugin::removeFile()
}
}
void ProjectExplorerPlugin::deleteFile()
{
QTC_ASSERT(d->m_currentNode && d->m_currentNode->nodeType() == FileNodeType, return)
FileNode *fileNode = qobject_cast<FileNode*>(d->m_currentNode);
Core::ICore *core = Core::ICore::instance();
QString filePath = d->m_currentNode->path();
QMessageBox::StandardButton button =
QMessageBox::question(core->mainWindow(),
tr("Delete File"),
tr("Delete %1 from file system?").arg(filePath),
QMessageBox::Yes | QMessageBox::No);
if (button != QMessageBox::Yes)
return;
ProjectNode *projectNode = fileNode->projectNode();
Q_ASSERT(projectNode);
projectNode->deleteFiles(fileNode->fileType(), QStringList(filePath));
if (Core::IVersionControl *vc =
core->vcsManager()->findVersionControlForDirectory(QFileInfo(filePath).absolutePath())) {
vc->vcsDelete(filePath);
}
QFile file(filePath);
if (file.exists()) {
if (!file.remove())
QMessageBox::warning(core->mainWindow(), tr("Delete file failed"),
tr("Could not delete file %1.").arg(filePath));
}
}
void ProjectExplorerPlugin::renameFile()
{
QWidget *focusWidget = QApplication::focusWidget();
......
......@@ -172,6 +172,7 @@ private slots:
void openFile();
void showInGraphicalShell();
void removeFile();
void deleteFile();
void renameFile();
void updateRecentProjectMenu();
......
......@@ -78,6 +78,7 @@ const char * const OPENFILE = "ProjectExplorer.OpenFile";
const char * const SHOWINGRAPHICALSHELL = "ProjectExplorer.ShowInGraphicalShell";
const char * const OPENTERMIANLHERE = "ProjectExplorer.OpenTerminalHere";
const char * const REMOVEFILE = "ProjectExplorer.RemoveFile";
const char * const DELETEFILE = "ProjectExplorer.DeleteFile";
const char * const RENAMEFILE = "ProjectExplorer.RenameFile";
const char * const SHOW_TASK_IN_EDITOR = "ProjectExplorer.ShowTaskInEditor";
......
......@@ -43,7 +43,6 @@
#include <QtGui/QApplication>
#include <QtGui/QIcon>
#include <QtGui/QMessageBox>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QStyle>
using namespace ProjectExplorer;
......@@ -103,8 +102,8 @@ bool sortNodes(Node *n1, Node *n2)
FolderNode *folder1 = static_cast<FolderNode*>(n1);
FolderNode *folder2 = static_cast<FolderNode*>(n2);
if (folder1->displayName() != folder2->displayName())
return folder1->displayName().compare(folder2->displayName(), Qt::CaseInsensitive) < 0;
if (folder1->path() != folder2->path())
return folder1->path().compare(folder2->path(), Qt::CaseInsensitive) < 0;
else
return folder1 < folder2;
} else {
......
......@@ -558,11 +558,6 @@ bool ProjectNode::sortNodesByPath(Node *n1, Node *n2) {
return n1->path() < n2->path();
}
bool ProjectNode::sortFolderNodesByName(FolderNode *f1, FolderNode *f2)
{
return f1->displayName() < f2->displayName();
}
/*!
\class SessionNode
*/
......
......@@ -71,6 +71,7 @@ enum FileType {
SourceType,
FormType,
ResourceType,
QMLType,
ProjectFileType,
FileTypeSize
};
......@@ -160,6 +161,7 @@ public:
RemoveSubProject,
AddFile,
RemoveFile,
DeleteFile,
Rename
};
......@@ -183,10 +185,13 @@ public:
virtual bool removeFiles(const FileType fileType,
const QStringList &filePaths,
QStringList *notRemoved = 0) = 0;
virtual bool deleteFiles(const FileType fileType,
const QStringList &filePaths) = 0;
virtual bool renameFile(const FileType fileType,
const QString &filePath,
const QString &newFilePath) = 0;
QList<NodesWatcher*> watchers() const;
void registerWatcher(NodesWatcher *watcher);
void unregisterWatcher(NodesWatcher *watcher);
......@@ -194,7 +199,6 @@ public:
void accept(NodesVisitor *visitor);
static bool sortNodesByPath(Node *n1, Node *n2);
static bool sortFolderNodesByName(FolderNode *f1, FolderNode *f2);
protected:
// this is just the in-memory representation, a subclass
......
......@@ -204,6 +204,13 @@ bool QmlProjectNode::removeFiles(const ProjectExplorer::FileType /*fileType*/,
return false;
}
bool QmlProjectNode::deleteFiles(const ProjectExplorer::FileType /*fileType*/,
const QStringList & /*filePaths*/)
{
return false;
}
bool QmlProjectNode::renameFile(const ProjectExplorer::FileType /*fileType*/,
const QString & /*filePath*/, const QString & /*newFilePath*/)
{
......
......@@ -69,6 +69,9 @@ public:
const QStringList &filePaths,
QStringList *notRemoved = 0);
virtual bool deleteFiles(const ProjectExplorer::FileType fileType,
const QStringList &filePaths);
virtual bool renameFile(const ProjectExplorer::FileType fileType,
const QString &filePath,
const QString &newFilePath);
......
......@@ -53,6 +53,7 @@
#include <projectexplorer/buildmanager.h>
#include <utils/qtcassert.h>
#include <algorithm>
#include <QtCore/QDebug>
#include <QtCore/QDir>
......@@ -90,6 +91,9 @@ static const FileTypeDataStorage fileTypeDataStorage[] = {
{ ProjectExplorer::ResourceType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Resources"),
":/qt4projectmanager/images/qt_qrc.png" },
{ ProjectExplorer::QMLType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "QML"),
":/qt4projectmanager/images/qml.png" }, // TODO icon
{ ProjectExplorer::UnknownFileType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Other files"),
":/qt4projectmanager/images/unknown.png" }
......@@ -263,6 +267,7 @@ struct InternalNode
QMap<QString, InternalNode*> subnodes;
QStringList files;
ProjectExplorer::FileType type;
QString displayName;
QString fullPath;
QIcon icon;
......@@ -321,14 +326,15 @@ struct InternalNode
const QString &key = it.next();
if (it.hasNext()) { // key is directory
path += key;
if (!currentNode->subnodes.contains(key)) {
if (!currentNode->subnodes.contains(path)) {
InternalNode *val = new InternalNode;
val->type = type;
val->fullPath = path;
currentNode->subnodes.insert(key, val);
val->displayName = key;
currentNode->subnodes.insert(path, val);
currentNode = val;
} else {
currentNode = currentNode->subnodes.value(key);
currentNode = currentNode->subnodes.value(path);
}
path += separator;
} else { // key is filename
......@@ -342,15 +348,17 @@ struct InternalNode
// Removes folder nodes with only a single sub folder in it
void compress()
{
static const QChar separator = QDir::separator(); // it is used for the *keys* which will become display name
QMap<QString, InternalNode*> newSubnodes;
QMapIterator<QString, InternalNode*> i(subnodes);
while (i.hasNext()) {
i.next();
i.value()->compress();
if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) {
// replace i.value() by i.value()->subnodes.begin()
QString key = i.value()->subnodes.begin().key();
newSubnodes.insert(i.key()+separator+key, i.value()->subnodes.value(key));
InternalNode *keep = i.value()->subnodes.value(key);
keep->displayName = i.value()->displayName + "/" + keep->displayName;
newSubnodes.insert(key, keep);
i.value()->subnodes.clear();
delete i.value();
} else {
......@@ -372,24 +380,24 @@ struct InternalNode
existingFolderNodes << node;
}
qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortNodesByPath);
QList<FolderNode *> foldersToRemove;
QList<FolderNode *> foldersToAdd;
typedef QPair<InternalNode *, FolderNode *> NodePair;
QList<NodePair> nodesToUpdate;
// newFolders is already sorted
qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortFolderNodesByName);
// Both lists should be already sorted...
QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin();
QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();;
while (existingNodeIter != existingFolderNodes.constEnd()
&& newNodeIter != subnodes.constEnd()) {
if ((*existingNodeIter)->displayName() < newNodeIter.key()) {
if ((*existingNodeIter)->path() < newNodeIter.value()->fullPath) {
foldersToRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->displayName() > newNodeIter.key()) {
} else if ((*existingNodeIter)->path() > newNodeIter.value()->fullPath) {
FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
newNode->setDisplayName(newNodeIter.key());
newNode->setDisplayName(newNodeIter.value()->displayName);
if (!newNodeIter.value()->icon.isNull())
newNode->setIcon(newNodeIter.value()->icon);
foldersToAdd << newNode;
......@@ -408,7 +416,7 @@ struct InternalNode
}
while (newNodeIter != subnodes.constEnd()) {
FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
newNode->setDisplayName(newNodeIter.key());
newNode->setDisplayName(newNodeIter.value()->displayName);
if (!newNodeIter.value()->icon.isNull())
newNode->setIcon(newNodeIter.value()->icon);
foldersToAdd << newNode;
......@@ -498,6 +506,25 @@ QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, ProFileRea
return vPaths;
}
static QSet<QString> recursiveEnumerate(const QString &folder)
{
QSet<QString> result;
QFileInfo fi(folder);
if (fi.isDir()) {
QDir dir(folder);
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
foreach (const QFileInfo &file, dir.entryInfoList()) {
if (file.isDir())
result += recursiveEnumerate(file.absoluteFilePath());
else
result += file.absoluteFilePath();
}
} else if (fi.exists()) {
result << folder;
}
return result;
}
void Qt4PriFileNode::update(ProFile *includeFileExact, ProFileReader *readerExact, ProFile *includeFileCumlative, ProFileReader *readerCumulative)
{
......@@ -514,34 +541,143 @@ void Qt4PriFileNode::update(ProFile *includeFileExact, ProFileReader *readerExac
InternalNode contents;
// Figure out DEPLOYMENT and INSTALL folders
QStringList folders;
QStringList dynamicVariables = dynamicVarNames(readerExact, readerCumulative);
foreach (const QString &dynamicVar, dynamicVariables) {
folders += readerExact->values(dynamicVar);
if (readerCumulative)
folders += readerCumulative->values(dynamicVar);
}
for (int i=0; i < folders.size(); ++i) {
QFileInfo fi(folders.at(i));
if (fi.isRelative())
folders[i] = projectDir + "/" + folders.at(i);
}
folders.removeDuplicates();
watchFolders(folders.toSet());
m_recursiveEnumerateFiles.clear();
foreach (const QString &folder, folders) {
m_recursiveEnumerateFiles += recursiveEnumerate(folder);
}
// update files
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
const QStringList qmakeVariables = varNames(type);
QStringList qmakeVariables = varNames(type);
QStringList newFilePaths;
QSet<QString> newFilePaths;
foreach (const QString &qmakeVariable, qmakeVariables) {
QStringList vPathsExact = fullVPaths(baseVPathsExact, readerExact, type, qmakeVariable, projectDir);
QStringList vPathsCumulative = fullVPaths(baseVPathsCumulative, readerCumulative, type, qmakeVariable, projectDir);
newFilePaths += readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact);
newFilePaths += readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact).toSet();
if (readerCumulative)
newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative).toSet();
}
newFilePaths += filterFiles(type, m_recursiveEnumerateFiles);
// We only need to save this information if
// we are watching folders
if (!folders.isEmpty())
m_files[type] = newFilePaths;
else
m_files[type].clear();
if (!newFilePaths.isEmpty()) {
newFilePaths.removeDuplicates();
InternalNode *subfolder = new InternalNode;
subfolder->type = type;
subfolder->icon = fileTypes.at(i).icon;
subfolder->fullPath = m_projectDir + "/#" + fileTypes.at(i).typeName;
contents.subnodes.insert(fileTypes.at(i).typeName, subfolder);
subfolder->fullPath = m_projectDir + "/#" + QString::number(i) + fileTypes.at(i).typeName;
subfolder->displayName = fileTypes.at(i).typeName;
contents.subnodes.insert(subfolder->fullPath, subfolder);
// create the hierarchy with subdirectories
subfolder->create(m_projectDir, newFilePaths, type);
subfolder->create(m_projectDir, newFilePaths.toList(), type);
}
}
contents.updateSubFolders(this, this);
}
void Qt4PriFileNode::watchFolders(const QSet<QString> &folders)
{
QSet<QString> toUnwatch = m_watchedFolders;
toUnwatch.subtract(folders);
QSet<QString> toWatch = folders;
toWatch.subtract(m_watchedFolders);
if (!toUnwatch.isEmpty())
m_project->centralizedFolderWatcher()->unwatchFolders(toUnwatch.toList(), this);
if (!toWatch.isEmpty())
m_project->centralizedFolderWatcher()->watchFolders(toWatch.toList(), this);
m_watchedFolders = folders;
}
void Qt4PriFileNode::folderChanged(const QString &)
{
//qDebug()<<"########## Qt4PriFileNode::folderChanged";
// So, we need to figure out which files changed.
// Collect all the files
QSet<QString> newFiles;
foreach (const QString &folder, m_watchedFolders) {
newFiles += recursiveEnumerate(folder);
}
QSet<QString> addedFiles = newFiles;
addedFiles.subtract(m_recursiveEnumerateFiles);
QSet<QString> removedFiles = m_recursiveEnumerateFiles;
removedFiles.subtract(newFiles);
if (addedFiles.isEmpty() && removedFiles.isEmpty())
return;
m_recursiveEnumerateFiles = newFiles;
// Apply the differences
// per file type
const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
QSet<QString> add = filterFiles(type, addedFiles);
QSet<QString> remove = filterFiles(type, removedFiles);
if (!add.isEmpty() || !remove.isEmpty()) {
// Scream :)
// qDebug()<<"For type"<<fileTypes.at(i).typeName<<"\n"
// <<"added files"<<add<<"\n"
// <<"removed files"<<remove;
m_files[type].unite(add);
m_files[type].subtract(remove);
}
}
// Now apply stuff
InternalNode contents;
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
if (!m_files[type].isEmpty()) {
InternalNode *subfolder = new InternalNode;
subfolder->type = type;
subfolder->icon = fileTypes.at(i).icon;
subfolder->fullPath = m_projectDir + "/#" + QString::number(i) + fileTypes.at(i).typeName;
subfolder->displayName = fileTypes.at(i).typeName;
contents.subnodes.insert(subfolder->fullPath, subfolder);
// create the hierarchy with subdirectories
subfolder->create(m_projectDir, m_files[type].toList(), type);
}
}
contents.updateSubFolders(this, this);
}
......@@ -558,7 +694,11 @@ QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) c
switch (proFileNode->projectType()) {
case ApplicationTemplate:
case LibraryTemplate:
actions << AddFile << RemoveFile;
actions << AddFile;
if (m_recursiveEnumerateFiles.contains(node->path()))
actions << DeleteFile;
else
actions << RemoveFile;
break;
case SubDirsTemplate:
actions << AddSubProject << RemoveSubProject;
......@@ -640,6 +780,13 @@ bool Qt4PriFileNode::removeFiles(const FileType fileType, const QStringList &fil
return failedFiles.isEmpty();
}
bool Qt4PriFileNode::deleteFiles(const FileType fileType, const QStringList &filePaths)
{
QStringList failedFiles;
changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
return true;
}
bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
const QString &newFilePath)
{
......@@ -861,7 +1008,7 @@ void Qt4PriFileNode::clear()
removeFolderNodes(subFolderNodes(), this);
}
QStringList Qt4PriFileNode::varNames(FileType type)
QStringList Qt4PriFileNode::varNames(ProjectExplorer::FileType type)
{
QStringList vars;
switch (type) {
......@@ -881,6 +1028,8 @@ QStringList Qt4PriFileNode::varNames(FileType type)
case ProjectExplorer::FormType:
vars << QLatin1String("FORMS");