ProjectExtensionsPage: Rework project combo box

Show a actual tree in the combobox.

Task-number: QTCREATORBUG-12002
Change-Id: I22b62f444923193972109a096bc6eef26a31bf9f
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
This commit is contained in:
Daniel Teske
2014-05-13 13:06:30 +02:00
parent 4743217eac
commit 5969c01fa6
10 changed files with 598 additions and 291 deletions

View File

@@ -57,18 +57,41 @@ TreeViewComboBox::TreeViewComboBox(QWidget *parent)
m_view->viewport()->installEventFilter(this);
}
QModelIndex TreeViewComboBox::indexAbove(QModelIndex index)
{
do
index = m_view->indexAbove(index);
while (index.isValid() && !(model()->flags(index) & Qt::ItemIsSelectable));
return index;
}
QModelIndex TreeViewComboBox::indexBelow(QModelIndex index)
{
do
index = m_view->indexBelow(index);
while (index.isValid() && !(model()->flags(index) & Qt::ItemIsSelectable));
return index;
}
QModelIndex TreeViewComboBox::lastIndex(const QModelIndex index)
{
if (index.isValid() && !m_view->isExpanded(index))
return index;
int rows = m_view->model()->rowCount(index);
if (rows == 0)
return index;
return lastIndex(m_view->model()->index(rows - 1, 0, index));
}
void TreeViewComboBox::wheelEvent(QWheelEvent *e)
{
QModelIndex index = m_view->currentIndex();
if (e->delta() > 0) {
do
index = m_view->indexAbove(index);
while (index.isValid() && !(model()->flags(index) & Qt::ItemIsSelectable));
} else if (e->delta() < 0) {
do
index = m_view->indexBelow(index);
while (index.isValid() && !(model()->flags(index) & Qt::ItemIsSelectable));
}
if (e->delta() > 0)
index = indexAbove(index);
else if (e->delta() < 0)
index = indexBelow(index);
e->accept();
if (!index.isValid())
return;
@@ -79,8 +102,34 @@ void TreeViewComboBox::wheelEvent(QWheelEvent *e)
emit activated(index.row());
}
void TreeViewComboBox::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Up || e->key() == Qt::Key_PageUp) {
setCurrentIndex(indexAbove(m_view->currentIndex()));
} else if (e->key() == Qt::Key_Down || e->key() == Qt::Key_PageDown) {
setCurrentIndex(indexBelow(m_view->currentIndex()));
} else if (e->key() == Qt::Key_Home) {
QModelIndex index = m_view->model()->index(0, 0);
if (index.isValid() && !model()->flags(index) & Qt::ItemIsSelectable)
index = indexBelow(index);
setCurrentIndex(index);
} else if (e->key() == Qt::Key_End) {
QModelIndex index = lastIndex(m_view->rootIndex());
if (index.isValid() && !model()->flags(index) & Qt::ItemIsSelectable)
index = indexAbove(index);
setCurrentIndex(index);
} else {
QComboBox::keyPressEvent(e);
return;
}
e->accept();
}
void TreeViewComboBox::setCurrentIndex(const QModelIndex &index)
{
if (!index.isValid())
return;
setRootModelIndex(model()->parent(index));
QComboBox::setCurrentIndex(index.row());
setRootModelIndex(QModelIndex());

View File

@@ -52,6 +52,7 @@ public:
TreeViewComboBox(QWidget *parent = 0);
void wheelEvent(QWheelEvent *e);
void keyPressEvent(QKeyEvent *e);
void setCurrentIndex(const QModelIndex &index);
bool eventFilter(QObject* object, QEvent* event);
void showPopup();
@@ -60,6 +61,10 @@ public:
TreeViewComboBoxView *view() const;
private:
QModelIndex indexBelow(QModelIndex index);
QModelIndex indexAbove(QModelIndex index);
QModelIndex lastIndex(const QModelIndex index);
TreeViewComboBoxView *m_view;
bool m_skipNextHide;
};

View File

@@ -0,0 +1,213 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "addnewmodel.h"
#include "projectexplorer.h"
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
AddNewTree::AddNewTree(const QString &displayName)
: m_parent(0),
m_children(QList<AddNewTree *>()),
m_displayName(displayName),
m_node(0),
m_canAdd(true),
m_priority(-1)
{
}
AddNewTree::AddNewTree(FolderNode *node, QList<AddNewTree *> children, const QString &displayName)
: m_parent(0),
m_children(children),
m_displayName(displayName),
m_node(0),
m_canAdd(false),
m_priority(-1)
{
if (node)
m_toolTip = ProjectExplorerPlugin::directoryFor(node);
foreach (AddNewTree *child, m_children)
child->m_parent = this;
}
AddNewTree::AddNewTree(FolderNode *node, QList<AddNewTree *> children, const FolderNode::AddNewInformation &info)
: m_parent(0),
m_children(children),
m_displayName(info.displayName),
m_node(node),
m_canAdd(true),
m_priority(info.priority)
{
if (node)
m_toolTip = ProjectExplorerPlugin::directoryFor(node);
foreach (AddNewTree *child, m_children)
child->m_parent = this;
}
AddNewTree::~AddNewTree()
{
qDeleteAll(m_children);
}
AddNewTree *AddNewTree::parent() const
{
return m_parent;
}
QList<AddNewTree *> AddNewTree::children() const
{
return m_children;
}
bool AddNewTree::canAdd() const
{
return m_canAdd;
}
QString AddNewTree::displayName() const
{
return m_displayName;
}
QString AddNewTree::toolTip() const
{
return m_toolTip;
}
FolderNode *AddNewTree::node() const
{
return m_node;
}
int AddNewTree::priority() const
{
return m_priority;
}
AddNewModel::AddNewModel(AddNewTree *root)
: m_root(root)
{
}
AddNewModel::~AddNewModel()
{
delete m_root;
}
int AddNewModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return m_root->children().size();
AddNewTree *tree = static_cast<AddNewTree *>(parent.internalPointer());
return tree->children().size();
}
int AddNewModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QModelIndex AddNewModel::index(int row, int column, const QModelIndex &parent) const
{
if (column != 0)
return QModelIndex();
if (!parent.isValid()) {
if (row >= 0 && row < m_root->children().size())
return createIndex(row, column, m_root->children().at(row));
return QModelIndex();
}
AddNewTree *tree = static_cast<AddNewTree *>(parent.internalPointer());
if (row >= 0 && row < tree->children().size())
return createIndex(row, column, tree->children().at(row));
return QModelIndex();
}
QModelIndex AddNewModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
AddNewTree *childTree = static_cast<AddNewTree *>(child.internalPointer());
if (childTree == m_root)
return QModelIndex();
AddNewTree *parent = childTree->parent();
if (parent == m_root)
return QModelIndex();
AddNewTree *grandparent = parent->parent();
for (int i = 0; i < grandparent->children().size(); ++i) {
if (grandparent->children().at(i) == parent)
return createIndex(i, 0, parent);
}
return QModelIndex();
}
QVariant AddNewModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
AddNewTree *tree = static_cast<AddNewTree *>(index.internalPointer());
if (role == Qt::DisplayRole)
return tree->displayName();
else if (role == Qt::ToolTipRole)
return tree->toolTip();
return QVariant();
}
Qt::ItemFlags AddNewModel::flags(const QModelIndex &index) const
{
AddNewTree *tree = static_cast<AddNewTree *>(index.internalPointer());
if (tree && tree->canAdd())
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return Qt::NoItemFlags;
}
FolderNode *AddNewModel::nodeForIndex(const QModelIndex &index) const
{
if (!index.isValid())
return m_root->node();
AddNewTree *tree = static_cast<AddNewTree *>(index.internalPointer());
return tree->node();
}
QModelIndex AddNewModel::indexForTree(AddNewTree *tree) const
{
if (!tree)
return index(0, 0, QModelIndex());
AddNewTree *parent = tree->parent();
if (!parent)
return QModelIndex();
for (int i = 0; i < parent->children().size(); ++i)
if (parent->children().at(i) == tree)
return createIndex(i, 0, tree);
return QModelIndex();
}

View File

@@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef ADDNEWMODEL_H
#define ADDNEWMODEL_H
#include "projectnodes.h"
#include <QAbstractItemModel>
namespace ProjectExplorer {
class FolderNode;
namespace Internal {
class AddNewTree
{
public:
AddNewTree(const QString &displayName);
AddNewTree(FolderNode *node, QList<AddNewTree *> children, const QString &displayName);
AddNewTree(FolderNode *node, QList<AddNewTree *> children, const FolderNode::AddNewInformation &info);
~AddNewTree();
AddNewTree *parent() const;
QList<AddNewTree *> children() const;
bool canAdd() const;
QString displayName() const;
QString toolTip() const;
FolderNode *node() const;
int priority() const;
private:
AddNewTree *m_parent;
QList<AddNewTree *> m_children;
QString m_displayName;
QString m_toolTip;
FolderNode *m_node;
bool m_canAdd;
int m_priority;
};
class AddNewModel : public QAbstractItemModel
{
public:
AddNewModel(AddNewTree *root);
~AddNewModel();
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
FolderNode *nodeForIndex(const QModelIndex &index) const;
QModelIndex indexForTree(AddNewTree *tree) const;
private:
AddNewTree *m_root;
};
}
}
#endif // ADDNEWMODEL_H

View File

@@ -145,7 +145,8 @@ HEADERS += projectexplorer.h \
customparser.h \
customparserconfigdialog.h \
ipotentialkit.h \
selectablefilesmodel.h
selectablefilesmodel.h \
addnewmodel.h
SOURCES += projectexplorer.cpp \
abi.cpp \
@@ -277,7 +278,8 @@ SOURCES += projectexplorer.cpp \
customparser.cpp \
customparserconfigdialog.cpp \
ipotentialkit.cpp \
selectablefilesmodel.cpp
selectablefilesmodel.cpp \
addnewmodel.cpp
FORMS += processstep.ui \
editorsettingspropertiespage.ui \

View File

@@ -33,6 +33,7 @@
#include "projectnodes.h"
#include "nodesvisitor.h"
#include "projectwizardpage.h"
#include "addnewmodel.h"
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -85,134 +86,168 @@ enum { debugExtension = 0 };
namespace ProjectExplorer {
typedef QList<FolderNode *> FolderNodeList;
typedef QList<ProjectNode *> ProjectNodeList;
namespace Internal {
// AddNewFileNodesVisitor: Retrieve all folders which support AddNew
class AddNewFileNodesVisitor : public NodesVisitor
class BestNodeSelector
{
public:
AddNewFileNodesVisitor();
static FolderNodeList allFolders();
virtual void visitProjectNode(ProjectNode *node);
virtual void visitFolderNode(FolderNode *node);
BestNodeSelector(const QString &commonDirectory, const QStringList &files, Node *contextNode);
void inspect(AddNewTree *tree);
AddNewTree *bestChoice() const;
QString deployingProjects() const;
private:
FolderNodeList m_folderNodes;
QString m_commonDirectory;
QStringList m_files;
Node *m_contextNode;
bool m_deploys;
QString m_deployText;
AddNewTree *m_bestChoice;
int m_bestMatchLength;
int m_bestMatchPriority;
};
AddNewFileNodesVisitor::AddNewFileNodesVisitor()
{}
FolderNodeList AddNewFileNodesVisitor::allFolders()
BestNodeSelector::BestNodeSelector(const QString &commonDirectory, const QStringList &files, Node *contextNode)
: m_commonDirectory(commonDirectory),
m_files(files),
m_contextNode(contextNode),
m_deploys(false),
m_deployText(QCoreApplication::translate("ProjectWizard", "The files are implicitly added to the projects:") + QLatin1Char('\n')),
m_bestChoice(0),
m_bestMatchLength(-1),
m_bestMatchPriority(-1)
{
AddNewFileNodesVisitor visitor;
SessionManager::sessionNode()->accept(&visitor);
return visitor.m_folderNodes;
}
void AddNewFileNodesVisitor::visitProjectNode(ProjectNode *node)
// Find the project the new files should be added
// If any node deploys the files, then we don't want to add the files.
// Otherwise consider their common path. Either a direct match on the directory
// or the directory with the longest matching path (list containing"/project/subproject1"
// matching common path "/project/subproject1/newuserpath").
void BestNodeSelector::inspect(AddNewTree *tree)
{
visitFolderNode(node);
FolderNode *node = tree->node();
if (node->nodeType() == ProjectNodeType) {
if (static_cast<ProjectNode *>(node)->deploysFolder(m_commonDirectory)) {
m_deploys = true;
m_deployText += tree->displayName() + QLatin1Char('\n');
}
}
if (m_deploys)
return;
const QString projectDirectory = ProjectExplorerPlugin::directoryFor(node);
const int projectDirectorySize = projectDirectory.size();
if (!m_commonDirectory.startsWith(projectDirectory))
return;
bool betterMatch = projectDirectorySize > m_bestMatchLength
|| (projectDirectorySize == m_bestMatchLength && tree->priority() > m_bestMatchPriority);
if (betterMatch) {
m_bestMatchPriority = tree->priority();
m_bestMatchLength = projectDirectorySize;
m_bestChoice = tree;
}
}
void AddNewFileNodesVisitor::visitFolderNode(FolderNode *node)
AddNewTree *BestNodeSelector::bestChoice() const
{
const QList<ProjectExplorer::ProjectAction> &list = node->supportedActions(node);
if (list.contains(ProjectExplorer::AddNewFile) && !list.contains(ProjectExplorer::InheritedFromParent))
m_folderNodes.push_back(node);
if (m_deploys)
return 0;
return m_bestChoice;
}
// AddNewProjectNodesVisitor: Retrieve all folders which support AddNewProject
// also checks the path of the added subproject
class AddNewProjectNodesVisitor : public NodesVisitor
QString BestNodeSelector::deployingProjects() const
{
public:
AddNewProjectNodesVisitor(const QString &subProjectPath);
static ProjectNodeList projectNodes(const QString &subProjectPath);
virtual void visitProjectNode(ProjectNode *node);
private:
ProjectNodeList m_projectNodes;
QString m_subProjectPath;
};
AddNewProjectNodesVisitor::AddNewProjectNodesVisitor(const QString &subProjectPath)
: m_subProjectPath(subProjectPath)
{}
ProjectNodeList AddNewProjectNodesVisitor::projectNodes(const QString &subProjectPath)
{
AddNewProjectNodesVisitor visitor(subProjectPath);
SessionManager::sessionNode()->accept(&visitor);
return visitor.m_projectNodes;
if (m_deploys)
return m_deployText;
return QString();
}
void AddNewProjectNodesVisitor::visitProjectNode(ProjectNode *node)
static inline AddNewTree *createNoneNode(BestNodeSelector *selector)
{
const QList<ProjectExplorer::ProjectAction> &list = node->supportedActions(node);
if (list.contains(ProjectExplorer::AddSubProject) && !list.contains(ProjectExplorer::InheritedFromParent))
if (m_subProjectPath.isEmpty() || node->canAddSubProject(m_subProjectPath))
m_projectNodes.push_back(node);
QString displayName = QCoreApplication::translate("ProjectWizard", "<Implicitly Add>");
if (selector->bestChoice())
displayName = QCoreApplication::translate("ProjectWizard", "<None>");
return new AddNewTree(displayName);
}
// ProjectEntry: Context entry for a *.pri/*.pro file. Stores name and path
// for quick sort and path search, provides operator<() for maps.
struct FolderEntry {
FolderEntry() : node(0), priority(-1) {}
explicit FolderEntry(FolderNode *node, const QStringList &generatedFiles, Node *contextNode);
int compare(const FolderEntry &rhs) const;
FolderNode *node;
QString directory; // For matching against wizards' files, which are native.
QString displayName;
QString baseName;
int priority;
};
FolderEntry::FolderEntry(FolderNode *n, const QStringList &generatedFiles, Node *contextNode) :
node(n)
static inline AddNewTree *buildAddProjectTree(ProjectNode *root, const QString &projectPath, Node *contextNode, BestNodeSelector *selector)
{
FolderNode::AddNewInformation info = node->addNewInformation(generatedFiles, contextNode);
displayName = info.displayName;
const QFileInfo fi(ProjectExplorerPlugin::pathFor(node));
baseName = fi.baseName();
priority = info.priority;
directory = ProjectExplorerPlugin::directoryFor(node);
QList<AddNewTree *> children;
foreach (ProjectNode *pn, root->subProjectNodes()) {
AddNewTree *child = buildAddProjectTree(pn, projectPath, contextNode, selector);
if (child)
children.append(child);
}
const QList<ProjectExplorer::ProjectAction> &list = root->supportedActions(root);
if (list.contains(ProjectExplorer::AddSubProject) && !list.contains(ProjectExplorer::InheritedFromParent)) {
if (projectPath.isEmpty() || root->canAddSubProject(projectPath)) {
FolderNode::AddNewInformation info = root->addNewInformation(QStringList() << projectPath, contextNode);
AddNewTree *item = new AddNewTree(root, children, info);
selector->inspect(item);
return item;
}
}
if (children.isEmpty())
return 0;
return new AddNewTree(root, children, root->displayName());
}
// Sort helper that sorts by base name and puts '*.pro' before '*.pri'
int FolderEntry::compare(const FolderEntry &rhs) const
static inline AddNewTree *buildAddProjectTree(SessionNode *root, const QString &projectPath, Node *contextNode, BestNodeSelector *selector)
{
if (const int drc = displayName.compare(rhs.displayName))
return drc;
if (const int drc = directory.compare(rhs.directory))
return drc;
if (const int brc = baseName.compare(rhs.baseName))
return brc;
if (priority > rhs.priority)
return -1;
if (priority < rhs.priority)
return 1;
return 0;
QList<AddNewTree *> children;
foreach (ProjectNode *pn, root->projectNodes()) {
AddNewTree *child = buildAddProjectTree(pn, projectPath, contextNode, selector);
if (child)
children.append(child);
}
children.prepend(createNoneNode(selector));
return new AddNewTree(root, children, root->displayName());
}
inline bool operator<(const FolderEntry &pe1, const FolderEntry &pe2)
static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList &files, Node *contextNode, BestNodeSelector *selector)
{
return pe1.compare(pe2) < 0;
QList<AddNewTree *> children;
foreach (FolderNode *fn, root->subFolderNodes()) {
AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector);
if (child)
children.append(child);
}
const QList<ProjectExplorer::ProjectAction> &list = root->supportedActions(root);
if (list.contains(ProjectExplorer::AddNewFile) && !list.contains(ProjectExplorer::InheritedFromParent)) {
FolderNode::AddNewInformation info = root->addNewInformation(files, contextNode);
AddNewTree *item = new AddNewTree(root, children, info);
selector->inspect(item);
return item;
}
if (children.isEmpty())
return 0;
return new AddNewTree(root, children, root->displayName());
}
QDebug operator<<(QDebug d, const FolderEntry &e)
static inline AddNewTree *buildAddFilesTree(SessionNode *root, const QStringList &files, Node *contextNode, BestNodeSelector *selector)
{
d.nospace() << e.directory << ' ' << e.displayName << ' ' << e.priority;
return d;
QList<AddNewTree *> children;
foreach (ProjectNode *pn, root->projectNodes()) {
AddNewTree *child = buildAddFilesTree(pn, files, contextNode, selector);
if (child)
children.append(child);
}
children.prepend(createNoneNode(selector));
return new AddNewTree(root, children, root->displayName());
}
static inline AddNewTree *getChoices(const QStringList &generatedFiles,
IWizard::WizardKind wizardKind,
Node *contextNode,
BestNodeSelector *selector)
{
if (wizardKind == IWizard::ProjectWizard)
return buildAddProjectTree(SessionManager::sessionNode(), generatedFiles.first(), contextNode, selector);
else
return buildAddFilesTree(SessionManager::sessionNode(), generatedFiles, contextNode, selector);
}
// --------- ProjectWizardContext
@@ -223,7 +258,6 @@ struct ProjectWizardContext
QList<IVersionControl*> versionControls;
QList<IVersionControl*> activeVersionControls;
QList<FolderEntry> projects;
QPointer<ProjectWizardPage> page; // this is managed by the wizard!
bool repositoryExists; // Is VCS 'add' sufficient, or should a repository be created?
QString commonDirectory;
@@ -240,7 +274,6 @@ ProjectWizardContext::ProjectWizardContext() :
void ProjectWizardContext::clear()
{
activeVersionControls.clear();
projects.clear();
commonDirectory.clear();
page = 0;
repositoryExists = false;
@@ -258,51 +291,6 @@ ProjectFileWizardExtension::~ProjectFileWizardExtension()
delete m_context;
}
static QList<FolderEntry> findDeployProject(const QList<FolderEntry> &folders,
QString &commonPath)
{
QList<FolderEntry> filtered;
foreach (const FolderEntry &folder, folders)
if (folder.node->nodeType() == ProjectNodeType)
if (static_cast<ProjectNode *>(folder.node)->deploysFolder(commonPath))
filtered << folder;
return filtered;
}
// Find the project the new files should be added to given their common
// path. Either a direct match on the directory or the directory with
// the longest matching path (list containing"/project/subproject1" matching
// common path "/project/subproject1/newuserpath").
static int findMatchingProject(const QList<FolderEntry> &projects,
const QString &commonPath)
{
if (projects.isEmpty() || commonPath.isEmpty())
return -1;
const int count = projects.size();
int bestMatch = -1;
int bestMatchLength = 0;
int bestMatchPriority = -1;
for (int p = 0; p < count; p++) {
// Direct match or better match? (note that the wizards' files are native).
const FolderEntry &entry = projects.at(p);
const QString &projectDirectory = entry.directory;
const int projectDirectorySize = projectDirectory.size();
if (!commonPath.startsWith(projectDirectory))
continue;
bool betterMatch = projectDirectorySize > bestMatchLength
|| (projectDirectorySize == bestMatchLength && entry.priority > bestMatchPriority);
if (betterMatch) {
bestMatchPriority = entry.priority;
bestMatchLength = projectDirectory.size();
bestMatch = p;
}
}
return bestMatch;
}
static QString generatedProjectFilePath(const QList<GeneratedFile> &files)
{
foreach (const GeneratedFile &file, files)
@@ -315,48 +303,37 @@ void ProjectFileWizardExtension::firstExtensionPageShown(
const QList<GeneratedFile> &files,
const QVariantMap &extraValues)
{
initProjectChoices(files, extraValues);
if (debugExtension)
qDebug() << Q_FUNC_INFO << files.size();
// Parametrize wizard page: find best project to add to, set up files display and
// version control depending on path
QStringList fileNames;
foreach (const GeneratedFile &f, files)
fileNames.push_back(f.path());
m_context->commonDirectory = Utils::commonPath(fileNames);
m_context->page->setFilesDisplay(m_context->commonDirectory, fileNames);
// Find best project (Entry at 0 is 'None').
int bestProjectIndex = -1;
QList<FolderEntry> deployingProjects = findDeployProject(m_context->projects, m_context->commonDirectory);
if (!deployingProjects.isEmpty()) {
// Oh we do have someone that deploys it
// then the best match is NONE
// We display a label explaining that and rename <None> to
// <Implicitly Add>
m_context->page->setNoneLabel(tr("<Implicitly Add>"));
QString text = tr("The files are implicitly added to the projects:");
text += QLatin1Char('\n');
foreach (const FolderEntry &project, deployingProjects) {
text += project.displayName;
text += QLatin1Char('\n');
}
m_context->page->setAdditionalInfo(text);
bestProjectIndex = -1;
QStringList filePaths;
ProjectExplorer::ProjectAction projectAction;
if (m_context->wizard->kind()== IWizard::ProjectWizard) {
projectAction = ProjectExplorer::AddSubProject;
filePaths << generatedProjectFilePath(files);
} else {
bestProjectIndex = findMatchingProject(m_context->projects, m_context->commonDirectory);
m_context->page->setNoneLabel(tr("<None>"));
projectAction = ProjectExplorer::AddNewFile;
foreach (const GeneratedFile &gf, files)
filePaths << gf.path();
}
if (bestProjectIndex == -1)
m_context->page->setCurrentProjectIndex(0);
else
m_context->page->setCurrentProjectIndex(bestProjectIndex + 1);
Node *contextNode = extraValues.value(QLatin1String(Constants::PREFERRED_PROJECT_NODE)).value<Node *>();
BestNodeSelector selector(m_context->commonDirectory, filePaths, contextNode);
AddNewTree *tree = getChoices(filePaths, m_context->wizard->kind(), contextNode, &selector);
m_context->page->setAdditionalInfo(selector.deployingProjects());
AddNewModel *model = new AddNewModel(tree);
m_context->page->setModel(model);
m_context->page->setBestNode(selector.bestChoice());
m_context->page->setAddingSubProject(projectAction == ProjectExplorer::AddSubProject);
// Store all version controls for later use:
if (m_context->versionControls.isEmpty()) {
@@ -429,72 +406,6 @@ QList<QWizardPage *> ProjectFileWizardExtension::extensionPages(const IWizard *w
return QList<QWizardPage *>() << m_context->page;
}
static inline void getProjectChoicesAndToolTips(QStringList *projectChoicesParam,
QStringList *projectToolTipsParam,
ProjectExplorer::ProjectAction *projectActionParam,
const QList<GeneratedFile> &generatedFiles,
ProjectWizardContext *context,
Node *contextNode)
{
// Set up project list which remains the same over duration of wizard execution
// As tooltip, set the directory for disambiguation (should someone have
// duplicate base names in differing directories).
//: No project selected
QStringList projectChoices(ProjectFileWizardExtension::tr("<None>"));
QStringList projectToolTips((QString()));
typedef QMap<FolderEntry, bool> FolderEntryMap;
// Sort by base name and purge duplicated entries (resulting from dependencies)
// via Map.
FolderEntryMap entryMap;
ProjectExplorer::ProjectAction projectAction;
if (context->wizard->kind()== IWizard::ProjectWizard) {
const QString projectFilePath = generatedProjectFilePath(generatedFiles);
projectAction = ProjectExplorer::AddSubProject;
foreach (ProjectNode *pn, AddNewProjectNodesVisitor::projectNodes(projectFilePath))
entryMap.insert(FolderEntry(pn, QStringList() << projectFilePath, contextNode), true);
} else {
QStringList filePaths;
foreach (const GeneratedFile &gf, generatedFiles)
filePaths << gf.path();
projectAction = ProjectExplorer::AddNewFile;
foreach (FolderNode *fn, AddNewFileNodesVisitor::allFolders())
entryMap.insert(FolderEntry(fn, filePaths, contextNode), true);
}
context->projects.clear();
// Collect names
const FolderEntryMap::const_iterator cend = entryMap.constEnd();
for (FolderEntryMap::const_iterator it = entryMap.constBegin(); it != cend; ++it) {
context->projects.push_back(it.key());
projectChoices.push_back(it.key().displayName);
projectToolTips.push_back(QDir::toNativeSeparators(it.key().directory));
}
*projectChoicesParam = projectChoices;
*projectToolTipsParam = projectToolTips;
*projectActionParam = projectAction;
}
void ProjectFileWizardExtension::initProjectChoices(const QList<GeneratedFile> &generatedFiles, const QVariantMap &extraValues)
{
QStringList projectChoices;
QStringList projectToolTips;
ProjectExplorer::ProjectAction projectAction;
getProjectChoicesAndToolTips(&projectChoices, &projectToolTips, &projectAction,
generatedFiles, m_context,
extraValues.value(QLatin1String(Constants::PREFERRED_PROJECT_NODE)).value<Node *>());
m_context->page->setProjects(projectChoices);
m_context->page->setProjectToolTips(projectToolTips);
m_context->page->setAddingSubProject(projectAction == ProjectExplorer::AddSubProject);
}
bool ProjectFileWizardExtension::processFiles(
const QList<GeneratedFile> &files,
bool *removeOpenProjectAttribute, QString *errorMessage)
@@ -525,11 +436,9 @@ bool ProjectFileWizardExtension::processProject(
QString generatedProject = generatedProjectFilePath(files);
// Add files to folder (Entry at 0 is 'None').
const int folderIndex = m_context->page->currentProjectIndex() - 1;
if (folderIndex < 0 || folderIndex >= m_context->projects.size())
FolderNode *folder = m_context->page->currentNode();
if (!folder)
return true;
FolderNode *folder = m_context->projects.at(folderIndex).node;
if (m_context->wizard->kind() == IWizard::ProjectWizard) {
if (!static_cast<ProjectNode *>(folder)->addSubProjects(QStringList(generatedProject))) {
*errorMessage = tr("Failed to add subproject \"%1\"\nto project \"%2\".")
@@ -600,11 +509,7 @@ void ProjectFileWizardExtension::applyCodeStyle(GeneratedFile *file) const
if (!languageId.isValid())
return; // don't modify files like *.ui *.pro
FolderNode *folder = 0;
const int projectIndex = m_context->page->currentProjectIndex() - 1;
if (projectIndex >= 0 && projectIndex < m_context->projects.size())
folder = m_context->projects.at(projectIndex).node;
FolderNode *folder = m_context->page->currentNode();
Project *baseProject = SessionManager::projectForNode(folder);
ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(languageId);
@@ -632,10 +537,5 @@ void ProjectFileWizardExtension::applyCodeStyle(GeneratedFile *file) const
file->setContents(doc.toPlainText());
}
void ProjectFileWizardExtension::setProjectIndex(int i)
{
m_context->page->setCurrentProjectIndex(i);
}
} // namespace Internal
} // namespace ProjectExplorer

View File

@@ -35,6 +35,7 @@
#include <coreplugin/ifilewizardextension.h>
namespace ProjectExplorer {
class FolderNode;
namespace Internal {
@@ -52,14 +53,11 @@ public:
bool *removeOpenProjectAttribute, QString *errorMessage);
void applyCodeStyle(Core::GeneratedFile *file) const;
void setProjectIndex(int i);
public slots:
void firstExtensionPageShown(const QList<Core::GeneratedFile> &files, const QVariantMap &extraValues);
void initializeVersionControlChoices();
private:
void initProjectChoices(const QList<Core::GeneratedFile> &generatedFiles, const QVariantMap &extraValues);
bool processProject(const QList<Core::GeneratedFile> &files,
bool *removeOpenProjectAttribute, QString *errorMessage);
bool processVersionControl(const QList<Core::GeneratedFile> &files, QString *errorMessage);

View File

@@ -27,6 +27,7 @@
**
****************************************************************************/
#include "addnewmodel.h"
#include "projectwizardpage.h"
#include "ui_projectwizardpage.h"
@@ -36,6 +37,7 @@
#include <QDir>
#include <QTextStream>
#include <QTreeView>
/*!
\class ProjectExplorer::Internal::ProjectWizardPage
@@ -51,7 +53,8 @@ using namespace Internal;
ProjectWizardPage::ProjectWizardPage(QWidget *parent) :
QWizardPage(parent),
m_ui(new Ui::WizardPage)
m_ui(new Ui::WizardPage),
m_model(0)
{
m_ui->setupUi(this);
m_ui->vcsManageButton->setText(Core::ICore::msgShowOptionsDialog());
@@ -64,19 +67,64 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent) :
ProjectWizardPage::~ProjectWizardPage()
{
delete m_ui;
delete m_model;
}
void ProjectWizardPage::setProjects(const QStringList &p)
void ProjectWizardPage::setModel(AddNewModel *model)
{
m_ui->projectComboBox->clear();
m_ui->projectComboBox->addItems(p);
m_ui->projectComboBox->setEnabled(p.size() > 1);
m_ui->projectLabel->setEnabled(p.size() > 1);
delete m_model;
m_model = model;
// TODO see OverViewCombo and OverView for click event filter
m_ui->projectComboBox->setModel(model);
bool enabled = m_model->rowCount(QModelIndex()) > 1;
m_ui->projectComboBox->setEnabled(enabled);
expandTree(QModelIndex());
}
void ProjectWizardPage::setProjectToolTips(const QStringList &t)
bool ProjectWizardPage::expandTree(const QModelIndex &root)
{
m_projectToolTips = t;
bool expand = false;
if (!root.isValid()) // always expand root
expand = true;
// Check children
int count = m_model->rowCount(root);
for (int i = 0; i < count; ++i) {
if (expandTree(m_model->index(i, 0, root)))
expand = true;
}
// Apply to self
if (expand)
m_ui->projectComboBox->view()->expand(root);
else
m_ui->projectComboBox->view()->collapse(root);
// if we are a high priority node, our *parent* needs to be expanded
AddNewTree *tree = static_cast<AddNewTree *>(root.internalPointer());
if (tree && tree->priority() >= 100)
expand = true;
return expand;
}
void ProjectWizardPage::setBestNode(AddNewTree *tree)
{
QModelIndex index = m_model->indexForTree(tree);
m_ui->projectComboBox->setCurrentIndex(index);
while (index.isValid()) {
m_ui->projectComboBox->view()->expand(index);
index = index.parent();
}
}
FolderNode *ProjectWizardPage::currentNode() const
{
QModelIndex index = m_ui->projectComboBox->view()->currentIndex();
return m_model->nodeForIndex(index);
}
void ProjectWizardPage::setAddingSubProject(bool addingSubProject)
@@ -86,16 +134,6 @@ void ProjectWizardPage::setAddingSubProject(bool addingSubProject)
: tr("Add to &project:"));
}
int ProjectWizardPage::currentProjectIndex() const
{
return m_ui->projectComboBox->currentIndex();
}
void ProjectWizardPage::setCurrentProjectIndex(int idx)
{
m_ui->projectComboBox->setCurrentIndex(idx);
}
void ProjectWizardPage::setNoneLabel(const QString &label)
{
m_ui->projectComboBox->setItemText(0, label);

View File

@@ -32,8 +32,15 @@
#include <QWizardPage>
QT_BEGIN_NAMESPACE
class QTreeView;
QT_END_NAMESPACE
namespace ProjectExplorer {
class FolderNode;
namespace Internal {
class AddNewModel;
class AddNewTree;
namespace Ui { class WizardPage; }
@@ -46,11 +53,9 @@ public:
explicit ProjectWizardPage(QWidget *parent = 0);
virtual ~ProjectWizardPage();
void setProjects(const QStringList &);
void setProjectToolTips(const QStringList &);
int currentProjectIndex() const;
void setCurrentProjectIndex(int);
void setModel(AddNewModel *model);
void setBestNode(ProjectExplorer::Internal::AddNewTree *tree);
FolderNode *currentNode() const;
void setNoneLabel(const QString &label);
void setAdditionalInfo(const QString &text);
@@ -71,9 +76,11 @@ private slots:
private:
void setProjectToolTip(const QString &);
bool expandTree(const QModelIndex &root);
Ui::WizardPage *m_ui;
QStringList m_projectToolTips;
AddNewModel *m_model;
};
} // namespace Internal

View File

@@ -67,7 +67,7 @@
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="projectComboBox">
<widget class="Utils::TreeViewComboBox" name="projectComboBox">
<property name="enabled">
<bool>false</bool>
</property>
@@ -144,6 +144,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::TreeViewComboBox</class>
<extends>QComboBox</extends>
<header location="global">utils/treeviewcombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>