diff options
author | Tobias Hunger <tobias.hunger@nokia.com> | 2011-09-20 09:42:10 +0000 |
---|---|---|
committer | Tobias Hunger <tobias.hunger@nokia.com> | 2011-10-05 10:35:13 +0200 |
commit | 618788f127d1416675b0137050bdc7469fef98b9 (patch) | |
tree | 4dbac348246e81cdc79f3e560384812b85de465a | |
parent | 0132e23385b1b823e9d98fdb7277fa098f4767c0 (diff) | |
download | qt-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.pro | 2 | ||||
-rw-r--r-- | src/plugins/projectexplorer/taskmodel.cpp | 483 | ||||
-rw-r--r-- | src/plugins/projectexplorer/taskmodel.h | 187 | ||||
-rw-r--r-- | src/plugins/projectexplorer/taskwindow.cpp | 457 |
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: |