summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Hunger <tobias.hunger@nokia.com>2011-09-20 09:42:10 +0000
committerTobias Hunger <tobias.hunger@nokia.com>2011-10-05 10:35:13 +0200
commit618788f127d1416675b0137050bdc7469fef98b9 (patch)
tree4dbac348246e81cdc79f3e560384812b85de465a
parent0132e23385b1b823e9d98fdb7277fa098f4767c0 (diff)
downloadqt-creator-618788f127d1416675b0137050bdc7469fef98b9.tar.gz
Taskwindow: Speed up the task window
Make the taskwindow semi-graciously handle my Qt Creator build with 91k build issues. Change-Id: I47275e2057d2ff9bf5229f0a367bb19c4f3141e4 Task-number: QTCREATORBUG-1551 Reviewed-on: http://codereview.qt-project.org/5263 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Daniel Teske <daniel.teske@nokia.com>
-rw-r--r--src/plugins/projectexplorer/projectexplorer.pro2
-rw-r--r--src/plugins/projectexplorer/taskmodel.cpp483
-rw-r--r--src/plugins/projectexplorer/taskmodel.h187
-rw-r--r--src/plugins/projectexplorer/taskwindow.cpp457
4 files changed, 702 insertions, 427 deletions
diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro
index 887eb67d63..dd5f9abf2b 100644
--- a/src/plugins/projectexplorer/projectexplorer.pro
+++ b/src/plugins/projectexplorer/projectexplorer.pro
@@ -30,6 +30,7 @@ HEADERS += projectexplorer.h \
showoutputtaskhandler.h \
vcsannotatetaskhandler.h \
taskwindow.h \
+ taskmodel.h \
projectfilewizardextension.h \
session.h \
dependenciespanel.h \
@@ -126,6 +127,7 @@ SOURCES += projectexplorer.cpp \
showoutputtaskhandler.cpp \
vcsannotatetaskhandler.cpp \
taskwindow.cpp \
+ taskmodel.cpp \
projectfilewizardextension.cpp \
session.cpp \
dependenciespanel.cpp \
diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp
new file mode 100644
index 0000000000..17292509bf
--- /dev/null
+++ b/src/plugins/projectexplorer/taskmodel.cpp
@@ -0,0 +1,483 @@
+/**************************************************************************
+**
+** 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 "taskmodel.h"
+
+#include "task.h"
+#include "taskhub.h"
+
+#include <QtCore/QDebug>
+
+#include <QtGui/QFontMetrics>
+
+namespace ProjectExplorer {
+namespace Internal {
+
+/////
+// TaskModel
+/////
+
+TaskModel::TaskModel(QObject *parent) :
+ QAbstractItemModel(parent),
+ m_maxSizeOfFileName(0),
+ m_lastMaxSizeIndex(0),
+ m_errorIcon(QLatin1String(":/projectexplorer/images/compile_error.png")),
+ m_warningIcon(QLatin1String(":/projectexplorer/images/compile_warning.png")),
+ m_sizeOfLineNumber(0)
+{
+ m_categories.insert(QString(), CategoryData());
+}
+
+int TaskModel::taskCount(const QString &category)
+{
+ return m_categories.value(category).count;
+}
+
+int TaskModel::errorTaskCount(const QString &category)
+{
+ return m_categories.value(category).errors;
+}
+
+int TaskModel::warningTaskCount(const QString &category)
+{
+ return m_categories.value(category).warnings;
+}
+
+bool TaskModel::hasFile(const QModelIndex &index) const
+{
+ int row = index.row();
+ if (!index.isValid() || row < 0 || row >= m_tasks.count())
+ return false;
+ return !m_tasks.at(row).file.isEmpty();
+}
+
+QIcon TaskModel::taskTypeIcon(Task::TaskType t) const
+{
+ switch (t) {
+ case Task::Warning:
+ return m_warningIcon;
+ case Task::Error:
+ return m_errorIcon;
+ case Task::Unknown:
+ break;
+ }
+ return QIcon();
+}
+
+void TaskModel::addCategory(const QString &categoryId, const QString &categoryName)
+{
+ Q_ASSERT(!categoryId.isEmpty());
+ CategoryData data;
+ data.displayName = categoryName;
+ m_categories.insert(categoryId, data);
+}
+
+QList<Task> TaskModel::tasks(const QString &categoryId) const
+{
+ if (categoryId.isEmpty())
+ return m_tasks;
+
+ QList<Task> taskList;
+ foreach (const Task &t, m_tasks) {
+ if (t.category == categoryId)
+ taskList.append(t);
+ }
+ return taskList;
+}
+
+void TaskModel::addTask(const Task &task)
+{
+ Q_ASSERT(m_categories.keys().contains(task.category));
+ CategoryData &data = m_categories[task.category];
+ CategoryData &global = m_categories[QString()];
+
+ beginInsertRows(QModelIndex(), m_tasks.count(), m_tasks.count());
+ m_tasks.append(task);
+ data.addTask(task);
+ global.addTask(task);
+ endInsertRows();
+}
+
+void TaskModel::removeTask(const Task &task)
+{
+ int index = m_tasks.indexOf(task);
+ if (index >= 0) {
+ const Task &t = m_tasks.at(index);
+
+ beginRemoveRows(QModelIndex(), index, index);
+ m_categories[task.category].removeTask(t);
+ m_categories[QString()].removeTask(t);
+ m_tasks.removeAt(index);
+ endRemoveRows();
+ }
+}
+
+void TaskModel::clearTasks(const QString &categoryId)
+{
+ if (categoryId.isEmpty()) {
+ if (m_tasks.count() == 0)
+ return;
+ beginRemoveRows(QModelIndex(), 0, m_tasks.count() -1);
+ m_tasks.clear();
+ foreach (const QString &key, m_categories.keys())
+ m_categories[key].clear();
+ endRemoveRows();
+ } else {
+ int index = 0;
+ int start = 0;
+ CategoryData &global = m_categories[QString()];
+ CategoryData &cat = m_categories[categoryId];
+
+ while (index < m_tasks.count()) {
+ while (index < m_tasks.count() && m_tasks.at(index).category != categoryId) {
+ ++start;
+ ++index;
+ }
+ if (index == m_tasks.count())
+ break;
+ while (index < m_tasks.count() && m_tasks.at(index).category == categoryId)
+ ++index;
+
+ // Index is now on the first non category
+ beginRemoveRows(QModelIndex(), start, index - 1);
+
+ for (int i = start; i < index; ++i) {
+ global.removeTask(m_tasks.at(i));
+ cat.removeTask(m_tasks.at(i));
+ }
+
+ m_tasks.erase(m_tasks.begin() + start, m_tasks.begin() + index);
+
+ endRemoveRows();
+ index = start;
+ }
+ }
+ m_maxSizeOfFileName = 0;
+ m_lastMaxSizeIndex = 0;
+}
+
+QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return QModelIndex();
+ return createIndex(row, column);
+}
+
+QModelIndex TaskModel::parent(const QModelIndex &child) const
+{
+ Q_UNUSED(child)
+ return QModelIndex();
+}
+
+int TaskModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : m_tasks.count();
+}
+
+int TaskModel::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 1;
+}
+
+QVariant TaskModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_tasks.count() || index.column() != 0)
+ return QVariant();
+
+ if (role == TaskModel::File) {
+ return m_tasks.at(index.row()).file;
+ } else if (role == TaskModel::Line) {
+ if (m_tasks.at(index.row()).line <= 0)
+ return QVariant();
+ else
+ return m_tasks.at(index.row()).line;
+ } else if (role == TaskModel::Description) {
+ return m_tasks.at(index.row()).description;
+ } else if (role == TaskModel::FileNotFound) {
+ return m_fileNotFound.value(m_tasks.at(index.row()).file);
+ } else if (role == TaskModel::Type) {
+ return (int)m_tasks.at(index.row()).type;
+ } else if (role == TaskModel::Category) {
+ return m_tasks.at(index.row()).category;
+ } else if (role == TaskModel::Icon) {
+ return taskTypeIcon(m_tasks.at(index.row()).type);
+ } else if (role == TaskModel::Task_t) {
+ return QVariant::fromValue(task(index));
+ }
+ return QVariant();
+}
+
+Task TaskModel::task(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Task();
+ return m_tasks.at(index.row());
+}
+
+QStringList TaskModel::categoryIds() const
+{
+ QStringList ids = m_categories.keys();
+ ids.removeAll(QString());
+ return ids;
+}
+
+QString TaskModel::categoryDisplayName(const QString &categoryId) const
+{
+ return m_categories.value(categoryId).displayName;
+}
+
+int TaskModel::sizeOfFile(const QFont &font)
+{
+ int count = m_tasks.count();
+ if (count == 0)
+ return 0;
+
+ if (m_maxSizeOfFileName > 0 && font == m_fileMeasurementFont && m_lastMaxSizeIndex == count - 1)
+ return m_maxSizeOfFileName;
+
+ QFontMetrics fm(font);
+ m_fileMeasurementFont = font;
+
+ for (int i = m_lastMaxSizeIndex; i < count; ++i) {
+ QString filename = m_tasks.at(i).file;
+ const int pos = filename.lastIndexOf(QLatin1Char('/'));
+ if (pos != -1)
+ filename = filename.mid(pos +1);
+
+ m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename));
+ }
+ m_lastMaxSizeIndex = count - 1;
+ return m_maxSizeOfFileName;
+}
+
+int TaskModel::sizeOfLineNumber(const QFont &font)
+{
+ if (m_sizeOfLineNumber == 0 || font != m_lineMeasurementFont) {
+ QFontMetrics fm(font);
+ m_lineMeasurementFont = font;
+ m_sizeOfLineNumber = fm.width("88888");
+ }
+ return m_sizeOfLineNumber;
+}
+
+void TaskModel::setFileNotFound(const QModelIndex &idx, bool b)
+{
+ if (idx.isValid() && idx.row() < m_tasks.count()) {
+ m_fileNotFound.insert(m_tasks[idx.row()].file, b);
+ emit dataChanged(idx, idx);
+ }
+}
+
+/////
+// TaskFilterModel
+/////
+
+TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent) : TaskModel(parent),
+ m_sourceModel(sourceModel)
+{
+ Q_ASSERT(m_sourceModel);
+ connect(m_sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(handleNewRows(QModelIndex,int,int)));
+ connect(m_sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(handleRemovedRows(QModelIndex,int,int)));
+ connect(m_sourceModel, SIGNAL(modelReset()),
+ this, SLOT(handleReset()));
+ connect(m_sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(handleDataChanged(QModelIndex,QModelIndex)));
+
+ m_includeUnknowns = m_includeWarnings = m_includeErrors = true;
+}
+
+QModelIndex TaskFilterModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return QModelIndex();
+ return createIndex(row, column, 0);
+}
+
+QModelIndex TaskFilterModel::parent(const QModelIndex &child) const
+{
+ Q_UNUSED(child)
+ return QModelIndex();
+}
+
+int TaskFilterModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ updateMapping();
+ return m_mapping.count();
+}
+
+int TaskFilterModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+ return m_sourceModel->columnCount(parent);
+}
+
+QVariant TaskFilterModel::data(const QModelIndex &index, int role) const
+{
+ return m_sourceModel->data(mapToSource(index), role);
+}
+
+static QPair<int, int> findFilteredRange(int first, int last, const QList<int> &list)
+{
+ QList<int>::const_iterator filteredFirst = qLowerBound(list, first);
+ QList<int>::const_iterator filteredLast = qUpperBound(filteredFirst, list.constEnd(), last);
+ return qMakePair(filteredFirst - list.constBegin(), filteredLast - list.constBegin() - 1);
+}
+
+void TaskFilterModel::handleNewRows(const QModelIndex &index, int first, int last)
+{
+ if (index.isValid())
+ return;
+
+ QList<int> newMapping;
+ for (int i = first; i <= last; ++i) {
+ const Task &task = m_sourceModel->task(m_sourceModel->index(i, 0));
+ if (filterAcceptsTask(task))
+ newMapping.append(i);
+ }
+
+ const int newItems = newMapping.count();
+ if (!newItems)
+ return;
+
+ int filteredFirst = -1;
+ if (last == m_sourceModel->rowCount() - 1)
+ filteredFirst = m_mapping.count();
+ else
+ filteredFirst = qLowerBound(m_mapping, first) - m_mapping.constBegin();
+
+ const int filteredLast = filteredFirst + newItems - 1;
+ beginInsertRows(QModelIndex(), filteredFirst, filteredLast);
+ if (filteredFirst == m_mapping.count()) {
+ m_mapping.append(newMapping);
+ } else {
+ QList<int> rest = m_mapping.mid(filteredFirst);
+
+ m_mapping.reserve(m_mapping.count() + newItems);
+ m_mapping.erase(m_mapping.begin() + filteredFirst, m_mapping.end());
+ m_mapping.append(newMapping);
+ foreach (int pos, rest)
+ m_mapping.append(pos + newItems);
+ }
+ endInsertRows();
+}
+
+void TaskFilterModel::handleRemovedRows(const QModelIndex &index, int first, int last)
+{
+ if (index.isValid())
+ return;
+
+ const QPair<int, int> range = findFilteredRange(first, last, m_mapping);
+ if (range.first > range.second)
+ return;
+
+ beginRemoveRows(QModelIndex(), range.first, range.second);
+ m_mapping.erase(m_mapping.begin() + range.first, m_mapping.begin() + range.second + 1);
+ for (int i = range.first; i < m_mapping.count(); ++i)
+ m_mapping[i] = m_mapping.at(i) - (last - first) - 1;
+ endRemoveRows();
+}
+
+void TaskFilterModel::handleDataChanged(QModelIndex top, QModelIndex bottom)
+{
+ const QPair<int, int> range = findFilteredRange(top.row(), bottom.row(), m_mapping);
+ if (range.first > range.second)
+ return;
+
+ emit dataChanged(index(range.first, top.column()), index(range.second, bottom.column()));
+}
+
+void TaskFilterModel::handleReset()
+{
+ invalidateFilter();
+}
+
+QModelIndex TaskFilterModel::mapToSource(const QModelIndex &index) const
+{
+ updateMapping();
+ int row = index.row();
+ if (row >= m_mapping.count())
+ return QModelIndex();
+ return m_sourceModel->index(m_mapping.at(row), index.column(), index.parent());
+}
+
+void TaskFilterModel::invalidateFilter()
+{
+ beginResetModel();
+ m_mappingUpToDate = false;
+ endResetModel();
+}
+
+void TaskFilterModel::updateMapping() const
+{
+ if (m_mappingUpToDate)
+ return;
+
+ m_mapping.clear();
+ for (int i = 0; i < m_sourceModel->rowCount(); ++i) {
+ QModelIndex index = m_sourceModel->index(i, 0);
+ const Task &task = m_sourceModel->task(index);
+ if (filterAcceptsTask(task))
+ m_mapping.append(i);
+ }
+
+ m_mappingUpToDate = true;
+}
+
+bool TaskFilterModel::filterAcceptsTask(const Task &task) const
+{
+ bool accept = true;
+ switch (task.type) {
+ case Task::Unknown:
+ accept = m_includeUnknowns;
+ break;
+ case Task::Warning:
+ accept = m_includeWarnings;
+ break;
+ case Task::Error:
+ accept = m_includeErrors;
+ break;
+ }
+
+ if (m_categoryIds.contains(task.category))
+ accept = false;
+
+ return accept;
+}
+
+} // namespace Internal
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h
new file mode 100644
index 0000000000..fa118caba4
--- /dev/null
+++ b/src/plugins/projectexplorer/taskmodel.h
@@ -0,0 +1,187 @@
+/**************************************************************************
+**
+** 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 <QtCore/QAbstractItemModel>
+
+#include <QtGui/QIcon>
+
+#include "task.h"
+
+namespace ProjectExplorer {
+namespace Internal {
+
+class TaskModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ // Model stuff
+ TaskModel(QObject *parent);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ Task task(const QModelIndex &index) const;
+
+ QStringList categoryIds() const;
+ QString categoryDisplayName(const QString &categoryId) const;
+ void addCategory(const QString &categoryId, const QString &categoryName);
+
+ QList<Task> tasks(const QString &categoryId = QString()) const;
+ void addTask(const Task &task);
+ void removeTask(const Task &task);
+ void clearTasks(const QString &categoryId = QString());
+
+ int sizeOfFile(const QFont &font);
+ int sizeOfLineNumber(const QFont &font);
+ void setFileNotFound(const QModelIndex &index, bool b);
+
+ enum Roles { File = Qt::UserRole, Line, Description, FileNotFound, Type, Category, Icon, Task_t };
+
+ QIcon taskTypeIcon(Task::TaskType t) const;
+
+ int taskCount(const QString &category);
+ int errorTaskCount(const QString &category);
+ int warningTaskCount(const QString &category);
+
+ bool hasFile(const QModelIndex &index) const;
+
+private:
+
+ class CategoryData
+ {
+ public:
+ CategoryData() : count(0), warnings(0), errors(0) { }
+
+ void addTask(const Task &task)
+ {
+ ++count;
+ if (task.type == Task::Warning)
+ ++warnings;
+ else if (task.type == Task::Error)
+ ++errors;
+ }
+
+ void removeTask(const Task &task)
+ {
+ --count;
+ if (task.type == Task::Warning)
+ --warnings;
+ else if (task.type == Task::Error)
+ --errors;
+ }
+
+ void clear() {
+ count = 0;
+ warnings = 0;
+ errors = 0;
+ }
+
+ QString displayName;
+ int count;
+ int warnings;
+ int errors;
+ };
+
+ QHash<QString,CategoryData> m_categories; // category id to data
+ QList<Task> m_tasks; // all tasks (in order of insertion)
+
+ QHash<QString,bool> m_fileNotFound;
+ int m_maxSizeOfFileName;
+ int m_lastMaxSizeIndex;
+ QFont m_fileMeasurementFont;
+ const QIcon m_errorIcon;
+ const QIcon m_warningIcon;
+ int m_sizeOfLineNumber;
+ QFont m_lineMeasurementFont;
+};
+
+class TaskFilterModel : public TaskModel
+{
+ Q_OBJECT
+
+public:
+ TaskFilterModel(TaskModel *sourceModel, QObject *parent = 0);
+
+ TaskModel *taskModel() { return m_sourceModel; }
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ bool filterIncludesUnknowns() const { return m_includeUnknowns; }
+ void setFilterIncludesUnknowns(bool b) { m_includeUnknowns = b; invalidateFilter(); }
+
+ bool filterIncludesWarnings() const { return m_includeWarnings; }
+ void setFilterIncludesWarnings(bool b) { m_includeWarnings = b; invalidateFilter(); }
+
+ bool filterIncludesErrors() const { return m_includeErrors; }
+ void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); }
+
+ QStringList filteredCategories() const { return m_categoryIds; }
+ void setFilteredCategories(const QStringList &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); }
+
+ Task task(const QModelIndex &index) const
+ { return m_sourceModel->task(mapToSource(index)); }
+
+ bool hasFile(const QModelIndex &index) const
+ { return m_sourceModel->hasFile(mapToSource(index)); }
+
+private slots:
+ void handleNewRows(const QModelIndex &index, int first, int last);
+ void handleRemovedRows(const QModelIndex &index, int first, int last);
+ void handleDataChanged(QModelIndex,QModelIndex bottom);
+ void handleReset();
+
+private:
+ QModelIndex mapToSource(const QModelIndex &index) const;
+ void invalidateFilter();
+ void updateMapping() const;
+ bool filterAcceptsTask(const Task &task) const;
+
+ bool m_includeUnknowns;
+ bool m_includeWarnings;
+ bool m_includeErrors;
+ QStringList m_categoryIds;
+
+ mutable QList<int> m_mapping;
+ mutable bool m_mappingUpToDate;
+
+ TaskModel *m_sourceModel;
+};
+
+} // namespace Internal
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp
index 9e1def4b3c..224a040bea 100644
--- a/src/plugins/projectexplorer/taskwindow.cpp
+++ b/src/plugins/projectexplorer/taskwindow.cpp
@@ -36,6 +36,7 @@
#include "projectexplorerconstants.h"
#include "task.h"
#include "taskhub.h"
+#include "taskmodel.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -78,93 +79,6 @@ public:
TaskWindowContext(QWidget *widget);
};
-class TaskModel : public QAbstractItemModel
-{
-public:
- // Model stuff
- TaskModel();
- QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
- QModelIndex parent(const QModelIndex &child) const;
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- int columnCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- Task task(const QModelIndex &index) const;
-
- QStringList categoryIds() const;
- QString categoryDisplayName(const QString &categoryId) const;
- void addCategory(const QString &categoryId, const QString &categoryName);
-
- QList<Task> tasks(const QString &categoryId = QString()) const;
- void addTask(const Task &task);
- void removeTask(const Task &task);
- void clearTasks(const QString &categoryId = QString());
-
- int sizeOfFile(const QFont &font);
- int sizeOfLineNumber(const QFont &font);
- void setFileNotFound(const QModelIndex &index, bool b);
-
- enum Roles { File = Qt::UserRole, Line, Description, FileNotFound, Type, Category, Icon, Task_t };
-
- QIcon taskTypeIcon(Task::TaskType t) const;
-
- int taskCount();
- int errorTaskCount();
- int warningTaskCount();
-
- bool hasFile(const QModelIndex &index) const;
-
-private:
- QHash<QString,QString> m_categories; // category id -> display name
- QList<Task> m_tasks; // all tasks (in order of insertion)
- QMap<QString,QList<Task> > m_tasksInCategory; // categoryId->tasks
-
- QHash<QString,bool> m_fileNotFound;
- int m_maxSizeOfFileName;
- QString m_fileMeasurementFont;
- const QIcon m_errorIcon;
- const QIcon m_warningIcon;
- int m_taskCount;
- int m_errorTaskCount;
- int m_warningTaskCount;
- int m_sizeOfLineNumber;
- QString m_lineMeasurementFont;
-};
-
-class TaskFilterModel : public QSortFilterProxyModel
-{
-public:
- TaskFilterModel(TaskModel *sourceModel, QObject *parent = 0);
-
- TaskModel *taskModel() const;
-
- bool filterIncludesUnknowns() const { return m_includeUnknowns; }
- void setFilterIncludesUnknowns(bool b) { m_includeUnknowns = b; invalidateFilter(); }
-
- bool filterIncludesWarnings() const { return m_includeWarnings; }
- void setFilterIncludesWarnings(bool b) { m_includeWarnings = b; invalidateFilter(); }
-
- bool filterIncludesErrors() const { return m_includeErrors; }
- void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); }
-
- QStringList filteredCategories() const { return m_categoryIds; }
- void setFilteredCategories(const QStringList &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); }
-
- Task task(const QModelIndex &index) const
- { return static_cast<TaskModel *>(sourceModel())->task(mapToSource(index)); }
-
- bool hasFile(const QModelIndex &index) const
- { return static_cast<TaskModel *>(sourceModel())->hasFile(mapToSource(index)); }
-
-protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
-
-private:
- bool m_includeUnknowns;
- bool m_includeWarnings;
- bool m_includeErrors;
- QStringList m_categoryIds;
-};
-
class TaskDelegate : public QStyledItemDelegate
{
Q_OBJECT
@@ -184,6 +98,9 @@ public slots:
private:
void generateGradientPixmap(int width, int height, QColor color, bool selected) const;
+ mutable int m_cachedHeight;
+ mutable QFont m_cachedFont;
+
/*
Collapsed:
+----------------------------------------------------------------------------------------------------+
@@ -285,313 +202,6 @@ void TaskView::keyPressEvent(QKeyEvent *e)
}
/////
-// TaskModel
-/////
-
-TaskModel::TaskModel() :
- m_maxSizeOfFileName(0),
- m_errorIcon(QLatin1String(":/projectexplorer/images/compile_error.png")),
- m_warningIcon(QLatin1String(":/projectexplorer/images/compile_warning.png")),
- m_taskCount(0),
- m_errorTaskCount(0),
- m_warningTaskCount(0),
- m_sizeOfLineNumber(0)
-{
-}
-
-int TaskModel::taskCount()
-{
- return m_taskCount;
-}
-
-int TaskModel::errorTaskCount()
-{
- return m_errorTaskCount;
-}
-
-int TaskModel::warningTaskCount()
-{
- return m_warningTaskCount;
-}
-
-bool TaskModel::hasFile(const QModelIndex &index) const
-{
- int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_tasks.count())
- return false;
- return !m_tasks.at(row).file.isEmpty();
-}
-
-QIcon TaskModel::taskTypeIcon(Task::TaskType t) const
-{
- switch (t) {
- case Task::Warning:
- return m_warningIcon;
- case Task::Error:
- return m_errorIcon;
- case Task::Unknown:
- break;
- }
- return QIcon();
-}
-
-void TaskModel::addCategory(const QString &categoryId, const QString &categoryName)
-{
- Q_ASSERT(!categoryId.isEmpty());
- m_categories.insert(categoryId, categoryName);
-}
-
-QList<Task> TaskModel::tasks(const QString &categoryId) const
-{
- if (categoryId.isEmpty()) {
- return m_tasks;
- } else {
- return m_tasksInCategory.value(categoryId);
- }
-}
-
-void TaskModel::addTask(const Task &task)
-{
- Q_ASSERT(m_categories.keys().contains(task.category));
-
- if (m_tasksInCategory.contains(task.category)) {
- m_tasksInCategory[task.category].append(task);
- } else {
- QList<Task> temp;
- temp.append(task);
- m_tasksInCategory.insert(task.category, temp);
- }
-
- beginInsertRows(QModelIndex(), m_tasks.size(), m_tasks.size());
- m_tasks.append(task);
- endInsertRows();
-
- m_maxSizeOfFileName = 0;
- ++m_taskCount;
- if (task.type == Task::Error)
- ++m_errorTaskCount;
- if (task.type == Task::Warning)
- ++m_warningTaskCount;
-}
-
-void TaskModel::removeTask(const Task &task)
-{
- if (m_tasks.contains(task)) {
- int index = m_tasks.indexOf(task);
- beginRemoveRows(QModelIndex(), index, index);
- m_tasks.removeAt(index);
- --m_taskCount;
- if (task.type == Task::Error)
- --m_errorTaskCount;
- if (task.type == Task::Warning)
- --m_warningTaskCount;
- endRemoveRows();
- }
-}
-
-void TaskModel::clearTasks(const QString &categoryId)
-{
- if (categoryId.isEmpty()) {
- if (m_tasks.size() == 0)
- return;
- beginRemoveRows(QModelIndex(), 0, m_tasks.size() -1);
- m_tasks.clear();
- m_tasksInCategory.clear();
- m_taskCount = 0;
- m_errorTaskCount = 0;
- m_warningTaskCount = 0;
- endRemoveRows();
- m_maxSizeOfFileName = 0;
- } else {
- int index = 0;
- int start = 0;
- int subErrorTaskCount = 0;
- int subWarningTaskCount = 0;
- while (index < m_tasks.size()) {
- while (index < m_tasks.size() && m_tasks.at(index).category != categoryId) {
- ++start;
- ++index;
- }
- if (index == m_tasks.size())
- break;
- while (index < m_tasks.size() && m_tasks.at(index).category == categoryId) {
- if (m_tasks.at(index).type == Task::Error)
- ++subErrorTaskCount;
- if (m_tasks.at(index).type == Task::Warning)
- ++subWarningTaskCount;
- ++index;
- }
- // Index is now on the first non category
- beginRemoveRows(QModelIndex(), start, index - 1);
-
- for (int i = start; i < index; ++i) {
- m_tasksInCategory[categoryId].removeOne(m_tasks.at(i));
- }
-
- m_tasks.erase(m_tasks.begin() + start, m_tasks.begin() + index);
-
- m_taskCount -= index - start;
- m_errorTaskCount -= subErrorTaskCount;
- m_warningTaskCount -= subWarningTaskCount;
-
- endRemoveRows();
- index = start;
- }
- // what to do with m_maxSizeOfFileName ?
- }
-}
-
-
-QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
-{
- if (parent.isValid())
- return QModelIndex();
- return createIndex(row, column, 0);
-}
-
-QModelIndex TaskModel::parent(const QModelIndex &child) const
-{
- Q_UNUSED(child)
- return QModelIndex();
-}
-
-int TaskModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : m_tasks.count();
-}
-
-int TaskModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 1;
-}
-
-QVariant TaskModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid() || index.row() >= m_tasks.size() || index.column() != 0)
- return QVariant();
-
- if (role == TaskModel::File) {
- return m_tasks.at(index.row()).file;
- } else if (role == TaskModel::Line) {
- if (m_tasks.at(index.row()).line <= 0)
- return QVariant();
- else
- return m_tasks.at(index.row()).line;
- } else if (role == TaskModel::Description) {
- return m_tasks.at(index.row()).description;
- } else if (role == TaskModel::FileNotFound) {
- return m_fileNotFound.value(m_tasks.at(index.row()).file);
- } else if (role == TaskModel::Type) {
- return (int)m_tasks.at(index.row()).type;
- } else if (role == TaskModel::Category) {
- return m_tasks.at(index.row()).category;
- } else if (role == TaskModel::Icon) {
- return taskTypeIcon(m_tasks.at(index.row()).type);
- } else if (role == TaskModel::Task_t) {
- return QVariant::fromValue(task(index));
- }
- return QVariant();
-}
-
-Task TaskModel::task(const QModelIndex &index) const
-{
- if (!index.isValid())
- return Task();
- return m_tasks.at(index.row());
-}
-
-QStringList TaskModel::categoryIds() const
-{
- return m_categories.keys();
-}
-
-QString TaskModel::categoryDisplayName(const QString &categoryId) const
-{
- return m_categories.value(categoryId);
-}
-
-int TaskModel::sizeOfFile(const QFont &font)
-{
- QString fontKey = font.key();
- if (m_maxSizeOfFileName > 0 && fontKey == m_fileMeasurementFont)
- return m_maxSizeOfFileName;
-
- QFontMetrics fm(font);
- m_fileMeasurementFont = fontKey;
-
- foreach (const Task & t, m_tasks) {
- QString filename = t.file;
- const int pos = filename.lastIndexOf(QLatin1Char('/'));
- if (pos != -1)
- filename = filename.mid(pos +1);
-
- m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename));
- }
- return m_maxSizeOfFileName;
-}
-
-int TaskModel::sizeOfLineNumber(const QFont &font)
-{
- QString fontKey = font.key();
- if (m_sizeOfLineNumber == 0 || fontKey != m_lineMeasurementFont) {
- QFontMetrics fm(font);
- m_lineMeasurementFont = fontKey;
- m_sizeOfLineNumber = fm.width("88888");
- }
- return m_sizeOfLineNumber;
-}
-
-void TaskModel::setFileNotFound(const QModelIndex &idx, bool b)
-{
- if (idx.isValid() && idx.row() < m_tasks.size()) {
- m_fileNotFound.insert(m_tasks[idx.row()].file, b);
- emit dataChanged(idx, idx);
- }
-}
-
-/////
-// TaskFilterModel
-/////
-
-TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent)
- : QSortFilterProxyModel(parent)
-{
- setSourceModel(sourceModel);
- setDynamicSortFilter(true);
- m_includeUnknowns = m_includeWarnings = m_includeErrors = true;
-}
-
-TaskModel *TaskFilterModel::taskModel() const
-{
- return static_cast<TaskModel*>(sourceModel());
-}
-
-bool TaskFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- bool accept = true;
-
- QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
- Task::TaskType type = Task::TaskType(index.data(TaskModel::Type).toInt());
- switch (type) {
- case Task::Unknown:
- accept = m_includeUnknowns;
- break;
- case Task::Warning:
- accept = m_includeWarnings;
- break;
- case Task::Error:
- accept = m_includeErrors;
- break;
- }
-
- const QString &categoryId = index.data(TaskModel::Category).toString();
- if (m_categoryIds.contains(categoryId))
- accept = false;
-
- return accept;
-}
-
-/////
// TaskWindow
/////
@@ -629,7 +239,7 @@ TaskWindow::TaskWindow(TaskHub *taskhub) : d(new TaskWindowPrivate)
{
d->m_defaultHandler = 0;
- d->m_model = new Internal::TaskModel;
+ d->m_model = new Internal::TaskModel(this);
d->m_filter = new Internal::TaskFilterModel(d->m_model);
d->m_listview = new Internal::TaskView;
@@ -745,7 +355,6 @@ void TaskWindow::visibilityChanged(bool /* b */)
void TaskWindow::addCategory(const QString &categoryId, const QString &displayName, bool visible)
{
- Q_ASSERT(!categoryId.isEmpty());
d->m_model->addCategory(categoryId, displayName);
if (!visible) {
QStringList filters = d->m_filter->filteredCategories();
@@ -878,36 +487,17 @@ void TaskWindow::filterCategoryTriggered(QAction *action)
int TaskWindow::taskCount(const QString &category) const
{
- if (category.isEmpty())
- return d->m_model->taskCount();
-
- return d->m_model->tasks(category).size();
+ return d->m_model->taskCount(category);
}
int TaskWindow::errorTaskCount(const QString &category) const
{
- if (category.isEmpty())
- return d->m_model->errorTaskCount();
-
- int count = 0;
- foreach (const Task &task, d->m_model->tasks(category)) {
- if (task.type == Task::Error)
- ++count;
- }
- return count;
+ return d->m_model->errorTaskCount(category);
}
int TaskWindow::warningTaskCount(const QString &category) const
{
- if (category.isEmpty())
- return d->m_model->warningTaskCount();
-
- int count = 0;
- foreach (const Task &task, d->m_model->tasks(category)) {
- if (task.type == Task::Warning)
- ++count;
- }
- return count;
+ return d->m_model->warningTaskCount(category);
}
int TaskWindow::priorityInStatusBar() const
@@ -1007,10 +597,10 @@ bool TaskWindow::canNavigate() const
// Delegate
/////
-TaskDelegate::TaskDelegate(QObject *parent)
- : QStyledItemDelegate(parent)
-{
-}
+TaskDelegate::TaskDelegate(QObject *parent) :
+ QStyledItemDelegate(parent),
+ m_cachedHeight(0)
+{ }
TaskDelegate::~TaskDelegate()
{
@@ -1021,17 +611,24 @@ QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
+ const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
+ const bool selected = (view->selectionModel()->currentIndex() == index);
+ QSize s;
+ s.setWidth(option.rect.width());
+
+ if (!selected && option.font == m_cachedFont && m_cachedHeight > 0) {
+ s.setHeight(m_cachedHeight);
+ return s;
+ }
+
QFontMetrics fm(option.font);
int fontHeight = fm.height();
int fontLeading = fm.leading();
- const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
Positions positions(option, model);
- QSize s;
- s.setWidth(option.rect.width());
- if (view->selectionModel()->currentIndex() == index) {
+ if (selected) {
QString description = index.data(TaskModel::Description).toString();
// Layout the description
int leading = fontLeading;
@@ -1056,6 +653,12 @@ QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd
}
if (s.height() < positions.minimumHeight())
s.setHeight(positions.minimumHeight());
+
+ if (!selected) {
+ m_cachedHeight = s.height();
+ m_cachedFont = option.font;
+ }
+
return s;
}
@@ -1101,7 +704,7 @@ void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
painter->setPen(textColor);
- TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
+ TaskModel *model = static_cast<TaskModel *>(view->model());
Positions positions(opt, model);
// Paint TaskIconArea: