summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Teske <daniel.teske@digia.com>2014-05-13 13:06:30 +0200
committerDaniel Teske <daniel.teske@digia.com>2014-05-13 15:35:36 +0200
commit5969c01fa625fa41f0b45907c31c411792c4ffee (patch)
tree9db7484d394e7030aaa160dd20198ffe7a58e623
parent4743217eac61bb7af7f04787d2558493ac390823 (diff)
downloadqt-creator-5969c01fa625fa41f0b45907c31c411792c4ffee.tar.gz
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>
-rw-r--r--src/libs/utils/treeviewcombobox.cpp67
-rw-r--r--src/libs/utils/treeviewcombobox.h5
-rw-r--r--src/plugins/projectexplorer/addnewmodel.cpp213
-rw-r--r--src/plugins/projectexplorer/addnewmodel.h88
-rw-r--r--src/plugins/projectexplorer/projectexplorer.pro6
-rw-r--r--src/plugins/projectexplorer/projectfilewizardextension.cpp400
-rw-r--r--src/plugins/projectexplorer/projectfilewizardextension.h4
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.cpp70
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.h17
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.ui9
10 files changed, 593 insertions, 286 deletions
diff --git a/src/libs/utils/treeviewcombobox.cpp b/src/libs/utils/treeviewcombobox.cpp
index 804b0e6eb8..dc6e5c5fbd 100644
--- a/src/libs/utils/treeviewcombobox.cpp
+++ b/src/libs/utils/treeviewcombobox.cpp
@@ -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());
diff --git a/src/libs/utils/treeviewcombobox.h b/src/libs/utils/treeviewcombobox.h
index f2dabb5792..543a237d92 100644
--- a/src/libs/utils/treeviewcombobox.h
+++ b/src/libs/utils/treeviewcombobox.h
@@ -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;
};
diff --git a/src/plugins/projectexplorer/addnewmodel.cpp b/src/plugins/projectexplorer/addnewmodel.cpp
new file mode 100644
index 0000000000..ff07bdb93e
--- /dev/null
+++ b/src/plugins/projectexplorer/addnewmodel.cpp
@@ -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();
+}
diff --git a/src/plugins/projectexplorer/addnewmodel.h b/src/plugins/projectexplorer/addnewmodel.h
new file mode 100644
index 0000000000..9d4bdb7d06
--- /dev/null
+++ b/src/plugins/projectexplorer/addnewmodel.h
@@ -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
diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro
index 02a2d121f7..3e70b00e06 100644
--- a/src/plugins/projectexplorer/projectexplorer.pro
+++ b/src/plugins/projectexplorer/projectexplorer.pro
@@ -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 \
diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp
index 124e00eda0..a9fd4a1881 100644
--- a/src/plugins/projectexplorer/projectfilewizardextension.cpp
+++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp
@@ -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)
-{
- visitFolderNode(node);
}
-void AddNewFileNodesVisitor::visitFolderNode(FolderNode *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)
{
- const QList<ProjectExplorer::ProjectAction> &list = node->supportedActions(node);
- if (list.contains(ProjectExplorer::AddNewFile) && !list.contains(ProjectExplorer::InheritedFromParent))
- m_folderNodes.push_back(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;
+ }
}
-// AddNewProjectNodesVisitor: Retrieve all folders which support AddNewProject
-// also checks the path of the added subproject
-class AddNewProjectNodesVisitor : public NodesVisitor
+AddNewTree *BestNodeSelector::bestChoice() 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)
-{}
+ if (m_deploys)
+ return 0;
+ return m_bestChoice;
+}
-ProjectNodeList AddNewProjectNodesVisitor::projectNodes(const QString &subProjectPath)
+QString BestNodeSelector::deployingProjects() const
{
- 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);
+static inline AddNewTree *buildAddProjectTree(ProjectNode *root, const QString &projectPath, Node *contextNode, BestNodeSelector *selector)
+{
+ QList<AddNewTree *> children;
+ foreach (ProjectNode *pn, root->subProjectNodes()) {
+ AddNewTree *child = buildAddProjectTree(pn, projectPath, contextNode, selector);
+ if (child)
+ children.append(child);
+ }
- int compare(const FolderEntry &rhs) const;
+ 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;
+ }
+ }
- FolderNode *node;
- QString directory; // For matching against wizards' files, which are native.
- QString displayName;
- QString baseName;
- int priority;
-};
+ if (children.isEmpty())
+ return 0;
+ return new AddNewTree(root, children, root->displayName());
+}
-FolderEntry::FolderEntry(FolderNode *n, const QStringList &generatedFiles, Node *contextNode) :
- node(n)
+static inline AddNewTree *buildAddProjectTree(SessionNode *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->projectNodes()) {
+ AddNewTree *child = buildAddProjectTree(pn, projectPath, contextNode, selector);
+ if (child)
+ children.append(child);
+ }
+ children.prepend(createNoneNode(selector));
+ 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 *buildAddFilesTree(FolderNode *root, const QStringList &files, 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 (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());
}
-inline bool operator<(const FolderEntry &pe1, const FolderEntry &pe2)
+static inline AddNewTree *buildAddFilesTree(SessionNode *root, const QStringList &files, Node *contextNode, BestNodeSelector *selector)
{
- return pe1.compare(pe2) < 0;
+ 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());
}
-QDebug operator<<(QDebug d, const FolderEntry &e)
+static inline AddNewTree *getChoices(const QStringList &generatedFiles,
+ IWizard::WizardKind wizardKind,
+ Node *contextNode,
+ BestNodeSelector *selector)
{
- d.nospace() << e.directory << ' ' << e.displayName << ' ' << e.priority;
- return d;
+ 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
diff --git a/src/plugins/projectexplorer/projectfilewizardextension.h b/src/plugins/projectexplorer/projectfilewizardextension.h
index daad9cd5ee..bd40f22e2c 100644
--- a/src/plugins/projectexplorer/projectfilewizardextension.h
+++ b/src/plugins/projectexplorer/projectfilewizardextension.h
@@ -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);
diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp
index e2f1ecc867..1313b45365 100644
--- a/src/plugins/projectexplorer/projectwizardpage.cpp
+++ b/src/plugins/projectexplorer/projectwizardpage.cpp
@@ -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,36 +67,71 @@ 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::setAddingSubProject(bool addingSubProject)
+void ProjectWizardPage::setBestNode(AddNewTree *tree)
{
- m_ui->projectLabel->setText(addingSubProject ?
- tr("Add as a subproject to project:")
- : tr("Add to &project:"));
+ QModelIndex index = m_model->indexForTree(tree);
+ m_ui->projectComboBox->setCurrentIndex(index);
+
+ while (index.isValid()) {
+ m_ui->projectComboBox->view()->expand(index);
+ index = index.parent();
+ }
}
-int ProjectWizardPage::currentProjectIndex() const
+FolderNode *ProjectWizardPage::currentNode() const
{
- return m_ui->projectComboBox->currentIndex();
+ QModelIndex index = m_ui->projectComboBox->view()->currentIndex();
+ return m_model->nodeForIndex(index);
}
-void ProjectWizardPage::setCurrentProjectIndex(int idx)
+void ProjectWizardPage::setAddingSubProject(bool addingSubProject)
{
- m_ui->projectComboBox->setCurrentIndex(idx);
+ m_ui->projectLabel->setText(addingSubProject ?
+ tr("Add as a subproject to project:")
+ : tr("Add to &project:"));
}
void ProjectWizardPage::setNoneLabel(const QString &label)
diff --git a/src/plugins/projectexplorer/projectwizardpage.h b/src/plugins/projectexplorer/projectwizardpage.h
index 6a6b86def1..6a92594bc2 100644
--- a/src/plugins/projectexplorer/projectwizardpage.h
+++ b/src/plugins/projectexplorer/projectwizardpage.h
@@ -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
diff --git a/src/plugins/projectexplorer/projectwizardpage.ui b/src/plugins/projectexplorer/projectwizardpage.ui
index a337f8fc45..e169128bc2 100644
--- a/src/plugins/projectexplorer/projectwizardpage.ui
+++ b/src/plugins/projectexplorer/projectwizardpage.ui
@@ -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>