diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index ff1fe41fdc7..273636cbf80 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -179,6 +179,16 @@ bool GenericProject::removeFiles(const QStringList &filePaths) return saveRawFileList(newList); } +bool GenericProject::setFiles(const QStringList &filePaths) +{ + QStringList newList; + QDir baseDir(QFileInfo(m_fileName).dir()); + foreach (const QString &filePath, filePaths) + newList.append(baseDir.relativeFilePath(filePath)); + + return saveRawFileList(newList); +} + void GenericProject::parseProject(RefreshOptions options) { if (options & Files) { diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index 31146ba16a3..7352ba8cf9d 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -96,6 +96,7 @@ public: bool addFiles(const QStringList &filePaths); bool removeFiles(const QStringList &filePaths); + bool setFiles(const QStringList &filePaths); enum RefreshOptions { Files = 0x01, diff --git a/src/plugins/genericprojectmanager/genericprojectconstants.h b/src/plugins/genericprojectmanager/genericprojectconstants.h index 2493ef9dfe6..eaea8b12e34 100644 --- a/src/plugins/genericprojectmanager/genericprojectconstants.h +++ b/src/plugins/genericprojectmanager/genericprojectconstants.h @@ -54,6 +54,8 @@ const char *const CONFIG_MIMETYPE = "application/vnd.nokia.qt.generic.config" // Project const char *const GENERICPROJECT_ID = "GenericProjectManager.GenericProject"; +const char *const EDITFILESACTION = "GenericProjectManager.EditFiles"; + } // namespace Constants } // namespace GenericProjectManager diff --git a/src/plugins/genericprojectmanager/genericprojectmanager.pro b/src/plugins/genericprojectmanager/genericprojectmanager.pro index 48a51d874e3..7c378f5a527 100644 --- a/src/plugins/genericprojectmanager/genericprojectmanager.pro +++ b/src/plugins/genericprojectmanager/genericprojectmanager.pro @@ -12,7 +12,8 @@ HEADERS = genericproject.h \ genericprojectfileseditor.h \ pkgconfigtool.h \ genericmakestep.h \ - genericbuildconfiguration.h + genericbuildconfiguration.h \ + selectablefilesmodel.h SOURCES = genericproject.cpp \ genericprojectplugin.cpp \ generictarget.cpp \ @@ -22,6 +23,7 @@ SOURCES = genericproject.cpp \ genericprojectfileseditor.cpp \ pkgconfigtool.cpp \ genericmakestep.cpp \ - genericbuildconfiguration.cpp + genericbuildconfiguration.cpp \ + selectablefilesmodel.cpp RESOURCES += genericproject.qrc FORMS += genericmakestep.ui diff --git a/src/plugins/genericprojectmanager/genericprojectplugin.cpp b/src/plugins/genericprojectmanager/genericprojectplugin.cpp index 0547a17f2c6..743bd418b31 100644 --- a/src/plugins/genericprojectmanager/genericprojectplugin.cpp +++ b/src/plugins/genericprojectmanager/genericprojectplugin.cpp @@ -37,15 +37,26 @@ #include "genericprojectfileseditor.h" #include "genericmakestep.h" #include "generictarget.h" +#include "genericproject.h" +#include "selectablefilesmodel.h" #include #include +#include +#include + +#include +#include + #include #include #include +#include +#include + using namespace GenericProjectManager; using namespace GenericProjectManager::Internal; @@ -84,10 +95,41 @@ bool GenericProjectPlugin::initialize(const QStringList &, QString *errorMessage addAutoReleasedObject(new GenericProjectWizard); addAutoReleasedObject(new GenericTargetFactory); + const Core::Context projectContext(Constants::PROJECTCONTEXT); + Core::ActionManager *am = core->actionManager(); + Core::ActionContainer *mproject = + am->actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT); + m_editFilesAction = new QAction(tr("Edit Files..."), this); + Core::Command *command = am->registerAction(m_editFilesAction, Constants::EDITFILESACTION, projectContext); + command->setAttribute(Core::Command::CA_Hide); + mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_FILES); + connect(m_editFilesAction, SIGNAL(triggered()), this, SLOT(editFiles())); + + connect(ProjectExplorer::ProjectExplorerPlugin::instance(), + SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)), + this, SLOT(updateContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*))); + return true; } void GenericProjectPlugin::extensionsInitialized() { } +void GenericProjectPlugin::updateContextMenu(ProjectExplorer::Project *project, ProjectExplorer::Node*) +{ + m_contextMenuProject = project; +} + +void GenericProjectPlugin::editFiles() +{ + GenericProject *genericProject = static_cast(m_contextMenuProject); + + Core::MimeDatabase *mimeDatabase = Core::ICore::instance()->mimeDatabase(); + SelectableFilesDialog sfd(QFileInfo(genericProject->file()->fileName()).path(), genericProject->files(), + mimeDatabase->suffixes().toSet(), Core::ICore::instance()->mainWindow()); + if (sfd.exec() == QDialog::Accepted) { + genericProject->setFiles(sfd.selectedFiles()); + } +} + Q_EXPORT_PLUGIN(GenericProjectPlugin) diff --git a/src/plugins/genericprojectmanager/genericprojectplugin.h b/src/plugins/genericprojectmanager/genericprojectplugin.h index 2c77722948f..d31fc0089be 100644 --- a/src/plugins/genericprojectmanager/genericprojectplugin.h +++ b/src/plugins/genericprojectmanager/genericprojectplugin.h @@ -36,6 +36,12 @@ #include #include +#include + +namespace ProjectExplorer { + class Project; + class Node; +} namespace GenericProjectManager { namespace Internal { @@ -52,9 +58,14 @@ public: virtual bool initialize(const QStringList &arguments, QString *errorString); virtual void extensionsInitialized(); +private slots: + void updateContextMenu(ProjectExplorer::Project *, ProjectExplorer::Node *); + void editFiles(); private: ProjectFilesFactory *m_projectFilesEditorFactory; + QAction *m_editFilesAction; + ProjectExplorer::Project *m_contextMenuProject; }; } // namespace Internal diff --git a/src/plugins/genericprojectmanager/selectablefilesmodel.cpp b/src/plugins/genericprojectmanager/selectablefilesmodel.cpp new file mode 100644 index 00000000000..07549611044 --- /dev/null +++ b/src/plugins/genericprojectmanager/selectablefilesmodel.cpp @@ -0,0 +1,289 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#include "selectablefilesmodel.h" + +#include +#include +#include + +using namespace GenericProjectManager; +using namespace GenericProjectManager::Internal; + +SelectableFilesModel::SelectableFilesModel(const QString &baseDir, const QStringList &files, const QSet &suffixes, QObject *parent) + : QAbstractItemModel(parent), m_baseDir(baseDir), m_suffixes(suffixes), m_root(0) +{ + m_files = files.toSet(); + + // Build a tree + m_root = new Tree; + m_root->name = "/"; + m_root->parent = 0; + m_root->isDir = true; + + buildTree(baseDir, m_root); +} + +void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree) +{ + const QFileInfoList fileInfoList = QDir(baseDir).entryInfoList(QDir::Files | + QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks); + bool allChecked = true; + bool allUnchecked = true; + foreach (const QFileInfo &fileInfo, fileInfoList) { + if (fileInfo.isDir()) { + Tree *t = new Tree; + t->parent = tree; + t->name = fileInfo.fileName(); + t->isDir = true; + buildTree(fileInfo.filePath(), t); + allChecked &= t->checked == Qt::Checked; + allUnchecked &= t->checked == Qt::Unchecked; + tree->childDirectories.append(t); + } else if (m_suffixes.contains(fileInfo.suffix())) { + Tree *t = new Tree; + t->parent = tree; + t->name = fileInfo.fileName(); + t->isDir = false; + t->icon = m_iconProvider.icon(fileInfo); + t->checked = m_files.contains(fileInfo.absoluteFilePath()) ? Qt::Checked : Qt::Unchecked; + t->fullPath = fileInfo.filePath(); + allChecked &= t->checked == Qt::Checked; + allUnchecked &= t->checked == Qt::Unchecked; + tree->files.append(t); + } + } + if (allChecked) + tree->checked = Qt::Checked; + else if (allUnchecked) + tree->checked = Qt::Unchecked; + else + tree->checked = Qt::PartiallyChecked; +} + +SelectableFilesModel::~SelectableFilesModel() +{ + deleteTree(m_root); +} + +void SelectableFilesModel::deleteTree(Tree *tree) +{ + foreach (Tree *t, tree->childDirectories) + deleteTree(t); + foreach (Tree *t, tree->files) + deleteTree(t); + delete tree; +} + +int SelectableFilesModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +int SelectableFilesModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return 1; + Tree *parentT = static_cast(parent.internalPointer()); + return parentT->childDirectories.size() + parentT->files.size(); +} + +QModelIndex SelectableFilesModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) + return createIndex(row, column, m_root); + Tree *parentT = static_cast(parent.internalPointer()); + if (row < parentT->childDirectories.size()) + return createIndex(row, column, parentT->childDirectories.at(row)); + else + return createIndex(row, column, parentT->files.at(row - parentT->childDirectories.size())); +} + +QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + Tree *parent = static_cast(child.internalPointer())->parent; + if (!parent) + return QModelIndex(); + if (!parent->parent) //then the parent is the root + return createIndex(0, 0, parent); + // figure out where the parent is + int pos = parent->parent->childDirectories.indexOf(parent); + if (pos == -1) + pos = parent->parent->childDirectories.size() + parent->parent->files.indexOf(parent); + return createIndex(pos, 0, parent); +} + +QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + Tree *t = static_cast(index.internalPointer()); + if (role == Qt::DisplayRole) + return t->name; + if (role == Qt::CheckStateRole) + return t->checked; + if (role == Qt::DecorationRole) { + if (t->isDir) + return m_iconProvider.icon(QFileIconProvider::Folder); + else + return t->icon; + } + return QVariant(); +} + +bool SelectableFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == Qt::CheckStateRole) { + // We can do that! + Tree *t = static_cast(index.internalPointer()); + t->checked = Qt::CheckState(value.toInt()); + propagateDown(index); + propagateUp(index); + emit dataChanged(index, index); + } + return false; +} + +void SelectableFilesModel::propagateUp(const QModelIndex &index) +{ + QModelIndex parent = index.parent(); + if (!parent.isValid()) + return; + Tree *parentT = static_cast(parent.internalPointer()); + if (!parentT) + return; + bool allChecked = true; + bool allUnchecked = true; + for (int i = 0; i < parentT->childDirectories.size(); ++i) { + allChecked &= parentT->childDirectories.at(i)->checked == Qt::Checked; + allUnchecked &= parentT->childDirectories.at(i)->checked == Qt::Unchecked; + } + for (int i = 0; i < parentT->files.size(); ++i) { + allChecked &= parentT->files.at(i)->checked == Qt::Checked; + allUnchecked &= parentT->files.at(i)->checked == Qt::Unchecked; + } + Qt::CheckState newState = Qt::PartiallyChecked; + if (allChecked) + newState = Qt::Checked; + if (allUnchecked) + newState = Qt::Unchecked; + if (parentT->checked != newState) { + parentT->checked = newState; + emit dataChanged(parent, parent); + propagateUp(parent); + } +} + +void SelectableFilesModel::propagateDown(const QModelIndex &index) +{ + Tree *t = static_cast(index.internalPointer()); + for (int i = 0; ichildDirectories.size(); ++i) { + t->childDirectories[i]->checked = t->checked; + propagateDown(index.child(i, 0)); + } + for (int i = 0; ifiles.size(); ++i) + t->files[i]->checked = t->checked; + + int rows = rowCount(index); + if (rows) + emit dataChanged(index.child(0, 0), index.child(rows-1, 0)); +} + +Qt::ItemFlags SelectableFilesModel::flags(const QModelIndex &index) const +{ + Q_UNUSED(index); + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; +} + +QStringList SelectableFilesModel::selectedFiles() const +{ + QStringList result; + collectFiles(m_root, &result); + return result; +} + +void SelectableFilesModel::collectFiles(Tree *root, QStringList *result) const +{ + if (root->checked == Qt::Unchecked) + return; + foreach (Tree *t, root->childDirectories) + collectFiles(t, result); + foreach (Tree *t, root->files) + if (t->checked == Qt::Checked) + result->append(t->fullPath); +} + + +SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringList files, const QSet &suffixes, QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + setLayout(layout); + setWindowTitle(tr("Edit Files")); + + QTreeView *view = new QTreeView(this); + layout->addWidget(view); + QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this); + buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), + this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), + this, SLOT(reject())); + layout->addWidget(buttonBox); + + m_selectableFilesModel = new SelectableFilesModel(path, files, suffixes, this); + view->setModel(m_selectableFilesModel); + view->setMinimumSize(400, 300); + view->setHeaderHidden(true); + view->expand(QModelIndex()); + smartExpand(view, m_selectableFilesModel->index(0,0, QModelIndex())); +} + +void SelectableFilesDialog::smartExpand(QTreeView *view, const QModelIndex &index) +{ + if (view->model()->data(index, Qt::CheckStateRole) == Qt::PartiallyChecked) { + view->expand(index); + int rows = view->model()->rowCount(index); + for (int i = 0; i < rows; ++i) + smartExpand(view, index.child(i, 0)); + } +} + +QStringList SelectableFilesDialog::selectedFiles() const +{ + return m_selectableFilesModel->selectedFiles(); +} diff --git a/src/plugins/genericprojectmanager/selectablefilesmodel.h b/src/plugins/genericprojectmanager/selectablefilesmodel.h new file mode 100644 index 00000000000..b87cc407684 --- /dev/null +++ b/src/plugins/genericprojectmanager/selectablefilesmodel.h @@ -0,0 +1,104 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef SELECTABLEFILESMODEL_H +#define SELECTABLEFILESMODEL_H + +#include +#include +#include +#include +#include +#include + +namespace GenericProjectManager { +namespace Internal { + + +struct Tree +{ + QString name; + Qt::CheckState checked; + QList childDirectories; + QList files; + bool isDir; + QIcon icon; + QString fullPath; + Tree *parent; +}; + +class SelectableFilesModel : public QAbstractItemModel +{ + Q_OBJECT +public: + SelectableFilesModel(const QString &baseDir, const QStringList &files, const QSet &suffixes, QObject *parent); + ~SelectableFilesModel(); + + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + QStringList selectedFiles() const; +private: + void collectFiles(Tree *root, QStringList *result) const; + void buildTree(const QString &baseDir, Tree *tree); + void deleteTree(Tree *tree); + void propagateUp(const QModelIndex &index); + void propagateDown(const QModelIndex &index); + QString m_baseDir; + QSet m_files; + QSet m_suffixes; + Tree *m_root; + QFileIconProvider m_iconProvider; +}; + +class SelectableFilesDialog : public QDialog +{ + Q_OBJECT +public: + SelectableFilesDialog(const QString &path, const QStringList files, const QSet &suffixes, QWidget *parent); + QStringList selectedFiles() const; +private: + void smartExpand(QTreeView *view, const QModelIndex &index); + SelectableFilesModel *m_selectableFilesModel; +}; + +} +} + + +#endif // SELECTABLEFILESMODEL_H diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 3acf25186a7..710c69d828b 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -475,15 +475,13 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er mprojectContextMenu->appendGroup(Constants::G_PROJECT_BUILD); mprojectContextMenu->appendGroup(Constants::G_PROJECT_RUN); mprojectContextMenu->appendGroup(Constants::G_PROJECT_FILES); - mprojectContextMenu->appendGroup(Constants::G_PROJECT_OTHER); - mprojectContextMenu->appendGroup(Constants::G_PROJECT_CONFIG); + mprojectContextMenu->appendGroup(Constants::G_PROJECT_LAST); msubProjectContextMenu->appendGroup(Constants::G_PROJECT_FIRST); msubProjectContextMenu->appendGroup(Constants::G_PROJECT_BUILD); msubProjectContextMenu->appendGroup(Constants::G_PROJECT_RUN); msubProjectContextMenu->appendGroup(Constants::G_PROJECT_FILES); - msubProjectContextMenu->appendGroup(Constants::G_PROJECT_OTHER); - msubProjectContextMenu->appendGroup(Constants::G_PROJECT_CONFIG); + msubProjectContextMenu->appendGroup(Constants::G_PROJECT_LAST); Core::ActionContainer *runMenu = Core::ICore::instance()->actionManager()->createMenu(Constants::RUNMENUCONTEXTMENU); runMenu->setOnAllDisabledBehavior(Core::ActionContainer::Hide); @@ -534,8 +532,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er sep->setSeparator(true); cmd = am->registerAction(sep, Core::Id("ProjectExplorer.Config.Sep"), projecTreeContext); msessionContextMenu->addAction(cmd, Constants::G_SESSION_CONFIG); - mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_CONFIG); - msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_CONFIG); sep = new QAction(this); sep->setSeparator(true); @@ -547,8 +543,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er cmd = am->registerAction(sep, Core::Id("ProjectExplorer.Other.Sep"), globalcontext); mbuild->addAction(cmd, Constants::G_BUILD_OTHER); msessionContextMenu->addAction(cmd, Constants::G_SESSION_OTHER); - mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_OTHER); - msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_OTHER); sep = new QAction(this); sep->setSeparator(true); @@ -808,7 +802,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); // unload project again, in right position - mprojectContextMenu->addAction(am->command(Constants::UNLOAD), Constants::G_PROJECT_FILES); + mprojectContextMenu->addAction(am->command(Constants::UNLOAD), Constants::G_PROJECT_LAST); // remove file action d->m_removeFileAction = new QAction(tr("Remove File..."), this); diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 44afd1ec1b4..78684b0c499 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -133,12 +133,11 @@ const char * const G_SESSION_FILES = "Session.Group.Files"; const char * const G_SESSION_OTHER = "Session.Group.Other"; const char * const G_SESSION_CONFIG = "Session.Group.Config"; -const char * const G_PROJECT_FILES = "Project.Group.Files"; const char * const G_PROJECT_FIRST = "Project.Group.Open"; const char * const G_PROJECT_BUILD = "Project.Group.Build"; -const char * const G_PROJECT_OTHER = "Project.Group.Other"; const char * const G_PROJECT_RUN = "Project.Group.Run"; -const char * const G_PROJECT_CONFIG = "Project.Group.Config"; +const char * const G_PROJECT_FILES = "Project.Group.Files"; +const char * const G_PROJECT_LAST = "Project.Group.Last"; const char * const G_FOLDER_FILES = "ProjectFolder.Group.Files"; const char * const G_FOLDER_OTHER = "ProjectFolder.Group.Other";