Commit edf0424b authored by Daniel Teske's avatar Daniel Teske
Browse files

Add a dialog for editing the file list of generic projects

Task-Nr: QTCREATORBUG-5112

Change-Id: Ic39c4346f2c64b05c314f3c03d963994e043dc45
Reviewed-on: http://codereview.qt.nokia.com/500

Reviewed-by: default avatarQt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarThorbjørn Lindeijer <thorbjorn@lindeijer.nl>
parent d411933f
......@@ -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) {
......
......@@ -96,6 +96,7 @@ public:
bool addFiles(const QStringList &filePaths);
bool removeFiles(const QStringList &filePaths);
bool setFiles(const QStringList &filePaths);
enum RefreshOptions {
Files = 0x01,
......
......@@ -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
......
......@@ -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
......@@ -37,15 +37,26 @@
#include "genericprojectfileseditor.h"
#include "genericmakestep.h"
#include "generictarget.h"
#include "genericproject.h"
#include "selectablefilesmodel.h"
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorer.h>
#include <texteditor/texteditoractionhandler.h>
#include <QtCore/QtPlugin>
#include <QtCore/QDebug>
#include <QtGui/QTreeView>
#include <QtGui/QMainWindow>
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<GenericProject *>(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)
......@@ -36,6 +36,12 @@
#include <extensionsystem/iplugin.h>
#include <QtCore/QObject>
#include <QtGui/QAction>
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
......
/**************************************************************************
**
** 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 <QtGui/QHBoxLayout>
#include <QtGui/QDialogButtonBox>
#include <QtGui/QTreeView>
using namespace GenericProjectManager;
using namespace GenericProjectManager::Internal;
SelectableFilesModel::SelectableFilesModel(const QString &baseDir, const QStringList &files, const QSet<QString> &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<Tree *>(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<Tree *>(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<Tree *>(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<Tree *>(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<Tree *>(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<Tree *>(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<Tree *>(index.internalPointer());
for (int i = 0; i<t->childDirectories.size(); ++i) {
t->childDirectories[i]->checked = t->checked;
propagateDown(index.child(i, 0));
}
for (int i = 0; i<t->files.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<QString> &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();
}
/**************************************************************************
**
** 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 <QtCore/QAbstractItemModel>
#include <QtCore/QSet>
#include <QtGui/QFileSystemModel>
#include <QtGui/QFileIconProvider>
#include <QtGui/QDialog>
#include <QtGui/QTreeView>
namespace GenericProjectManager {
namespace Internal {
struct Tree
{
QString name;
Qt::CheckState checked;
QList<Tree *> childDirectories;
QList<Tree *> 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<QString> &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<QString> m_files;
QSet<QString> m_suffixes;
Tree *m_root;
QFileIconProvider m_iconProvider;
};
class SelectableFilesDialog : public QDialog
{
Q_OBJECT
public:
SelectableFilesDialog(const QString &path, const QStringList files, const QSet<QString> &suffixes, QWidget *parent);
QStringList selectedFiles() const;
private:
void smartExpand(QTreeView *view, const QModelIndex &index);
SelectableFilesModel *m_selectableFilesModel;
};
}
}
#endif // SELECTABLEFILESMODEL_H
......@@ -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);