diff --git a/interfaces/iproblem.h b/interfaces/iproblem.h index 0fea92465..b97aeda6d 100644 --- a/interfaces/iproblem.h +++ b/interfaces/iproblem.h @@ -1,118 +1,120 @@ /* * Copyright 2015 Laszlo Kis-Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IPROBLEM_H #define IPROBLEM_H #include #include #include #include namespace KDevelop { class IAssistant; /// Interface for the Problem classes class IProblem : public QSharedData { public: typedef QExplicitlySharedDataPointer Ptr; /// The source of the problem. That is which tool / which part found this problem. enum Source { Unknown, Disk, Preprocessor, Lexer, Parser, DUChainBuilder, SemanticAnalysis, ToDo, Plugin /// The source is a problem checker plugin }; /// Severity of the problem. That is, how serious is the found problem. enum Severity { Error, Warning, Hint }; IProblem(){} virtual ~IProblem(){} /// Returns the source of the problem virtual Source source() const = 0; /// Sets the source of the problem virtual void setSource(Source source) = 0; /// Returns a string containing the source of the problem virtual QString sourceString() const = 0; /// Returns the location of the problem (path, line, column) virtual KDevelop::DocumentRange finalLocation() const = 0; /// Sets the location of the problem (path, line, column) virtual void setFinalLocation(const KDevelop::DocumentRange& location) = 0; /// Returns the short description of the problem. virtual QString description() const = 0; /// Sets the short description of the problem virtual void setDescription(const QString& description) = 0; /// Returns the detailed explanation of the problem. virtual QString explanation() const = 0; /// Sets the detailed explanation of the problem virtual void setExplanation(const QString& explanation) = 0; /// Returns the severity of the problem virtual Severity severity() const = 0; /// Sets the severity of the problem virtual void setSeverity(Severity severity) = 0; /// Returns a string containing the severity of the problem virtual QString severityString() const = 0; /// Returns the diagnostics of the problem. virtual QVector diagnostics() const = 0; /// Sets the diagnostics of the problem virtual void setDiagnostics(const QVector &diagnostics) = 0; /// Adds a diagnostic line to the problem virtual void addDiagnostic(const Ptr &diagnostic) = 0; /// Clears all diagnostics virtual void clearDiagnostics() = 0; /// Returns a solution assistant for the problem, if applicable that is. virtual QExplicitlySharedDataPointer solutionAssistant() const = 0; }; } +Q_DECLARE_METATYPE(KDevelop::IProblem::Ptr); + #endif diff --git a/plugins/problemreporter/problemtreeview.cpp b/plugins/problemreporter/problemtreeview.cpp index 9497778f4..af7a2e82c 100644 --- a/plugins/problemreporter/problemtreeview.cpp +++ b/plugins/problemreporter/problemtreeview.cpp @@ -1,371 +1,381 @@ /* * KDevelop Problem Reporter * * Copyright (c) 2006-2007 Hamish Rodda * Copyright 2006 Adam Treat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemtreeview.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include "problemreporterplugin.h" #include #include #include //#include "modeltest.h" using namespace KDevelop; namespace KDevelop { class ProblemTreeViewItemDelegate : public QItemDelegate { Q_OBJECT public: explicit ProblemTreeViewItemDelegate(QObject* parent = nullptr); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; }; } ProblemTreeViewItemDelegate::ProblemTreeViewItemDelegate(QObject* parent) : QItemDelegate(parent) { } void ProblemTreeViewItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem newOption(option); newOption.textElideMode = index.column() == ProblemModel::File ? Qt::ElideMiddle : Qt::ElideRight; QItemDelegate::paint(painter, newOption, index); } ProblemTreeView::ProblemTreeView(QWidget* parent, QAbstractItemModel* itemModel) : QTreeView(parent) + , m_proxy(new QSortFilterProxyModel(this)) { - ProblemModel* problemModel = dynamic_cast(itemModel); - Q_ASSERT(problemModel); setObjectName("Problem Reporter Tree"); setWhatsThis(i18n("Problems")); setItemDelegate(new ProblemTreeViewItemDelegate); setSelectionBehavior(QAbstractItemView::SelectRows); + m_proxy->setSortRole(ProblemModel::SeverityRole); + m_proxy->setDynamicSortFilter(true); + m_proxy->sort(0, Qt::AscendingOrder); + + ProblemModel* problemModel = dynamic_cast(itemModel); + Q_ASSERT(problemModel); setModel(problemModel); + header()->setStretchLastSection(false); if (problemModel->features().testFlag(ProblemModel::CanDoFullUpdate)) { QAction* fullUpdateAction = new QAction(this); fullUpdateAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); fullUpdateAction->setText(i18n("Force Full Update")); fullUpdateAction->setToolTip(i18nc("@info:tooltip", "Re-parse all watched documents")); fullUpdateAction->setIcon(QIcon::fromTheme("view-refresh")); connect(fullUpdateAction, &QAction::triggered, model(), &ProblemModel::forceFullUpdate); addAction(fullUpdateAction); } if (problemModel->features().testFlag(ProblemModel::CanShowImports)) { QAction* showImportsAction = new QAction(this); addAction(showImportsAction); showImportsAction->setCheckable(true); showImportsAction->setChecked(false); showImportsAction->setText(i18n("Show Imports")); showImportsAction->setToolTip(i18nc("@info:tooltip", "Display problems in imported files")); this->model()->setShowImports(false); connect(showImportsAction, &QAction::triggered, model(), &ProblemModel::setShowImports); } if (problemModel->features().testFlag(ProblemModel::ScopeFilter)) { KActionMenu* scopeMenu = new KActionMenu(this); scopeMenu->setDelayed(false); scopeMenu->setText(i18n("Scope")); scopeMenu->setToolTip(i18nc("@info:tooltip", "Which files to display the problems for")); QActionGroup* scopeActions = new QActionGroup(this); QAction* currentDocumentAction = new QAction(this); currentDocumentAction->setText(i18n("Current Document")); currentDocumentAction->setToolTip(i18nc("@info:tooltip", "Display problems in current document")); QAction* openDocumentsAction = new QAction(this); openDocumentsAction->setText(i18n("Open Documents")); openDocumentsAction->setToolTip(i18nc("@info:tooltip", "Display problems in all open documents")); QAction* currentProjectAction = new QAction(this); currentProjectAction->setText(i18n("Current Project")); currentProjectAction->setToolTip(i18nc("@info:tooltip", "Display problems in current project")); QAction* allProjectAction = new QAction(this); allProjectAction->setText(i18n("All Projects")); allProjectAction->setToolTip(i18nc("@info:tooltip", "Display problems in all projects")); QVector actions; actions.push_back(currentDocumentAction); actions.push_back(openDocumentsAction); actions.push_back(currentProjectAction); actions.push_back(allProjectAction); if (problemModel->features().testFlag(ProblemModel::CanByPassScopeFilter)) { QAction* showAllAction = new QAction(this); showAllAction->setText(i18n("Show All")); showAllAction->setToolTip(i18nc("@info:tooltip", "Display ALL problems")); actions.push_back(showAllAction); } foreach (QAction* action, actions) { action->setCheckable(true); scopeActions->addAction(action); scopeMenu->addAction(action); } addAction(scopeMenu); // Show All should be default if it's supported. It helps with error messages that are otherwise invisible if (problemModel->features().testFlag(ProblemModel::CanByPassScopeFilter)) { actions.last()->setChecked(true); model()->setScope(BypassScopeFilter); } else { currentDocumentAction->setChecked(true); model()->setScope(CurrentDocument); } QSignalMapper* scopeMapper = new QSignalMapper(this); scopeMapper->setMapping(currentDocumentAction, CurrentDocument); scopeMapper->setMapping(openDocumentsAction, OpenDocuments); scopeMapper->setMapping(currentProjectAction, CurrentProject); scopeMapper->setMapping(allProjectAction, AllProjects); connect(currentDocumentAction, &QAction::triggered, scopeMapper, static_cast(&QSignalMapper::map)); connect(openDocumentsAction, &QAction::triggered, scopeMapper, static_cast(&QSignalMapper::map)); connect(currentProjectAction, &QAction::triggered, scopeMapper, static_cast(&QSignalMapper::map)); connect(allProjectAction, &QAction::triggered, scopeMapper, static_cast(&QSignalMapper::map)); if (problemModel->features().testFlag(ProblemModel::CanByPassScopeFilter)) { scopeMapper->setMapping(actions.last(), BypassScopeFilter); connect(actions.last(), &QAction::triggered, scopeMapper, static_cast(&QSignalMapper::map)); } connect(scopeMapper, static_cast(&QSignalMapper::mapped), model(), &ProblemModel::setScope); } if (problemModel->features().testFlag(ProblemModel::SeverityFilter)) { KActionMenu* severityMenu = new KActionMenu(i18n("Severity"), this); severityMenu->setDelayed(false); severityMenu->setToolTip(i18nc("@info:tooltip", "Select the lowest level of problem severity to be displayed")); QActionGroup* severityActions = new QActionGroup(this); auto errorSeverityAction = new QAction(i18n("Error"), this); errorSeverityAction->setToolTip(i18nc("@info:tooltip", "Display only errors")); errorSeverityAction->setIcon(QIcon::fromTheme("dialog-error")); auto warningSeverityAction = new QAction(i18n("Warning"), this); warningSeverityAction->setToolTip(i18nc("@info:tooltip", "Display errors and warnings")); warningSeverityAction->setIcon(QIcon::fromTheme("dialog-warning")); auto hintSeverityAction = new QAction(i18n("Hint"), this); hintSeverityAction->setToolTip(i18nc("@info:tooltip", "Display errors, warnings and hints")); hintSeverityAction->setIcon(QIcon::fromTheme("dialog-information")); QAction* severityActionArray[] = { errorSeverityAction, warningSeverityAction, hintSeverityAction }; for (int i = 0; i < 3; ++i) { severityActionArray[i]->setCheckable(true); severityActions->addAction(severityActionArray[i]); severityMenu->addAction(severityActionArray[i]); } addAction(severityMenu); hintSeverityAction->setChecked(true); model()->setSeverity(IProblem::Hint); QSignalMapper* severityMapper = new QSignalMapper(this); severityMapper->setMapping(errorSeverityAction, IProblem::Error); severityMapper->setMapping(warningSeverityAction, IProblem::Warning); severityMapper->setMapping(hintSeverityAction, IProblem::Hint); connect(errorSeverityAction, &QAction::triggered, severityMapper, static_cast(&QSignalMapper::map)); connect(warningSeverityAction, &QAction::triggered, severityMapper, static_cast(&QSignalMapper::map)); connect(hintSeverityAction, &QAction::triggered, severityMapper, static_cast(&QSignalMapper::map)); connect(severityMapper, static_cast(&QSignalMapper::mapped), model(), &ProblemModel::setSeverity); } if (problemModel->features().testFlag(ProblemModel::Grouping)) { KActionMenu* groupingMenu = new KActionMenu(i18n("Grouping"), this); groupingMenu->setDelayed(false); QActionGroup* groupingActions = new QActionGroup(this); QAction* noGroupingAction = new QAction(i18n("None"), this); QAction* pathGroupingAction = new QAction(i18n("Path"), this); QAction* severityGroupingAction = new QAction(i18n("Severity"), this); QAction* groupingActionArray[] = { noGroupingAction, pathGroupingAction, severityGroupingAction }; for (unsigned i = 0; i < sizeof(groupingActionArray) / sizeof(QAction*); ++i) { QAction* action = groupingActionArray[i]; action->setCheckable(true); groupingActions->addAction(action); groupingMenu->addAction(action); } addAction(groupingMenu); noGroupingAction->setChecked(true); QSignalMapper* groupingMapper = new QSignalMapper(this); groupingMapper->setMapping(noGroupingAction, NoGrouping); groupingMapper->setMapping(pathGroupingAction, PathGrouping); groupingMapper->setMapping(severityGroupingAction, SeverityGrouping); connect(noGroupingAction, &QAction::triggered, groupingMapper, static_cast(&QSignalMapper::map)); connect(pathGroupingAction, &QAction::triggered, groupingMapper, static_cast(&QSignalMapper::map)); connect(severityGroupingAction, &QAction::triggered, groupingMapper, static_cast(&QSignalMapper::map)); connect(groupingMapper, static_cast(&QSignalMapper::mapped), model(), &ProblemModel::setGrouping); } connect(this, &ProblemTreeView::clicked, this, &ProblemTreeView::itemActivated); connect(model(), &QAbstractItemModel::rowsInserted, this, &ProblemTreeView::changed); connect(model(), &QAbstractItemModel::rowsRemoved, this, &ProblemTreeView::changed); connect(model(), &QAbstractItemModel::modelReset, this, &ProblemTreeView::changed); } ProblemTreeView::~ProblemTreeView() { } void ProblemTreeView::openDocumentForCurrentProblem() { itemActivated(currentIndex()); } void ProblemTreeView::itemActivated(const QModelIndex& index) { if (!index.isValid()) return; KTextEditor::Cursor start; QUrl url; { // TODO: is this really necessary? DUChainReadLocker lock(DUChain::lock()); - IProblem::Ptr problem = model()->problemForIndex(index); + const auto problem = index.data(ProblemModel::ProblemRole).value(); if (!problem) return; url = problem->finalLocation().document.toUrl(); start = problem->finalLocation().start(); } ICore::self()->documentController()->openDocument(url, start); } void ProblemTreeView::resizeColumns() { for (int i = 0; i < model()->columnCount(); ++i) resizeColumnToContents(i); } void ProblemTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { QTreeView::dataChanged(topLeft, bottomRight, roles); resizeColumns(); } void ProblemTreeView::reset() { QTreeView::reset(); resizeColumns(); } ProblemModel* ProblemTreeView::model() const { - return static_cast(QTreeView::model()); + return static_cast(m_proxy->sourceModel()); } void ProblemTreeView::setModel(QAbstractItemModel* model) { Q_ASSERT(qobject_cast(model)); - QTreeView::setModel(model); + m_proxy->setSourceModel(model); + QTreeView::setModel(m_proxy); } void ProblemTreeView::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { - IProblem::Ptr problem = model()->problemForIndex(index); - if (problem) { - QExplicitlySharedDataPointer solution = problem->solutionAssistant(); - if (solution) { - QList actions; - foreach (KDevelop::IAssistantAction::Ptr action, solution->actions()) { - actions << action->toKAction(); - } - if (!actions.isEmpty()) { - QString title = solution->title(); - title = KDevelop::htmlToPlainText(title); - title.replace("'", "\'"); - - QPointer m = new QMenu(this); - m->addSection(title); - m->addActions(actions); - m->exec(event->globalPos()); - delete m; - } - } + const auto problem = index.data(ProblemModel::ProblemRole).value(); + if (!problem) { + return; + } + QExplicitlySharedDataPointer solution = problem->solutionAssistant(); + if (!solution) { + return; + } + QList actions; + foreach (KDevelop::IAssistantAction::Ptr action, solution->actions()) { + actions << action->toKAction(); + } + if (!actions.isEmpty()) { + QString title = solution->title(); + title = KDevelop::htmlToPlainText(title); + title.replace("'", "\'"); + + QPointer m = new QMenu(this); + m->addSection(title); + m->addActions(actions); + m->exec(event->globalPos()); + delete m; } } } void ProblemTreeView::showEvent(QShowEvent* event) { Q_UNUSED(event) resizeColumns(); } #include "problemtreeview.moc" diff --git a/plugins/problemreporter/problemtreeview.h b/plugins/problemreporter/problemtreeview.h index 39c26f554..5ecaf8063 100644 --- a/plugins/problemreporter/problemtreeview.h +++ b/plugins/problemreporter/problemtreeview.h @@ -1,74 +1,75 @@ /* * KDevelop Problem Reporter * * Copyright (c) 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_PLUGIN_PROBLEMWIDGET_H #define KDEVPLATFORM_PLUGIN_PROBLEMWIDGET_H #include namespace KDevelop { class ParseJob; class TopDUContext; class IDocument; class ProblemModel; } class ProblemReporterPlugin; +class QSortFilterProxyModel; class ProblemTreeView : public QTreeView { Q_OBJECT public: ProblemTreeView(QWidget* parent, QAbstractItemModel* itemModel); virtual ~ProblemTreeView(); KDevelop::ProblemModel* model() const; virtual void setModel(QAbstractItemModel* model) override; virtual void contextMenuEvent(QContextMenuEvent*) override; virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles = QVector()) override; virtual void reset() override; public slots: void openDocumentForCurrentProblem(); signals: // Emitted when the model's rows change (added/removed/reset) void changed(); protected: virtual void showEvent(QShowEvent* event) override; private slots: void itemActivated(const QModelIndex& index); private: void resizeColumns(); ProblemReporterPlugin* m_plugin; - ; + QSortFilterProxyModel* m_proxy; }; #endif // kate: space-indent on; indent-width 2; tab-width: 4; replace-tabs on; auto-insert-doxygen on diff --git a/shell/problemmodel.cpp b/shell/problemmodel.cpp index 20b682183..5c785cfa7 100644 --- a/shell/problemmodel.cpp +++ b/shell/problemmodel.cpp @@ -1,319 +1,325 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { QIcon iconForSeverity(KDevelop::IProblem::Severity severity) { switch (severity) { case KDevelop::IProblem::Hint: return QIcon::fromTheme(QStringLiteral("dialog-information")); case KDevelop::IProblem::Warning: return QIcon::fromTheme(QStringLiteral("dialog-warning")); case KDevelop::IProblem::Error: return QIcon::fromTheme(QStringLiteral("dialog-error")); } return {}; } } struct ProblemModelPrivate { ProblemModelPrivate(KDevelop::ProblemStore *store) : m_problems(store) , m_features(KDevelop::ProblemModel::NoFeatures) { } QScopedPointer m_problems; KDevelop::ProblemModel::Features m_features; }; namespace KDevelop { ProblemModel::ProblemModel(QObject * parent, ProblemStore *store) : QAbstractItemModel(parent) , d(new ProblemModelPrivate(store)) { if (!d->m_problems) { d->m_problems.reset(new FilteredProblemStore()); d->m_features = ScopeFilter | SeverityFilter | Grouping | CanByPassScopeFilter; } setScope(CurrentDocument); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ProblemModel::setCurrentDocument); /// CompletionSettings include a list of todo markers we care for, so need to update connect(ICore::self()->languageController()->completionSettings(), &ICompletionSettings::settingsChanged, this, &ProblemModel::forceFullUpdate); if (ICore::self()->documentController()->activeDocument()) { setCurrentDocument(ICore::self()->documentController()->activeDocument()); } connect(d->m_problems.data(), &ProblemStore::beginRebuild, this, &ProblemModel::onBeginRebuild); connect(d->m_problems.data(), &ProblemStore::endRebuild, this, &ProblemModel::onEndRebuild); } ProblemModel::~ ProblemModel() { } int ProblemModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return d->m_problems->count(); } else { return d->m_problems->count(reinterpret_cast(parent.internalPointer())); } } static QString displayUrl(const QUrl &url, const QUrl &base) { if (base.isParentOf(url)) { return url.toDisplayString(QUrl::PreferLocalFile).mid(base.toDisplayString(QUrl::PreferLocalFile).length()); } else { return ICore::self()->projectController()->prettyFileName(url, IProjectController::FormatPlain); } } QVariant ProblemModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); QUrl baseDirectory = d->m_problems->currentDocument().toUrl().adjusted(QUrl::RemoveFilename); IProblem::Ptr p = problemForIndex(index); if (!p.constData()) { if (role == Qt::DisplayRole && index.column() == Error) { ProblemStoreNode *node = reinterpret_cast(index.internalPointer()); if (node) { return node->label(); } } return {}; } + if (role == SeverityRole) { + return p->severity(); + } else if (role == ProblemRole) { + return QVariant::fromValue(p); + } + switch (role) { case Qt::DisplayRole: switch (index.column()) { case Source: return p->sourceString(); case Error: return p->description(); case File: return displayUrl(p->finalLocation().document.toUrl(), baseDirectory); case Line: if (p->finalLocation().isValid()) { return QString::number(p->finalLocation().start().line() + 1); } break; case Column: if (p->finalLocation().isValid()) { return QString::number(p->finalLocation().start().column() + 1); } break; } break; case Qt::DecorationRole: if (index.column() == Error) { return iconForSeverity(p->severity()); } break; case Qt::ToolTipRole: return p->explanation(); default: break; } return {}; } QModelIndex ProblemModel::parent(const QModelIndex& index) const { ProblemStoreNode *node = reinterpret_cast(index.internalPointer()); if (!node) { return {}; } ProblemStoreNode *parent = node->parent(); if (!parent || parent->isRoot()) { return {}; } int idx = parent->index(); return createIndex(idx, 0, parent); } QModelIndex ProblemModel::index(int row, int column, const QModelIndex& parent) const { if (row < 0 || row >= rowCount(parent) || column < 0 || column >= LastColumn) { return QModelIndex(); } ProblemStoreNode *parentNode = reinterpret_cast(parent.internalPointer()); const ProblemStoreNode *node = d->m_problems->findNode(row, parentNode); return createIndex(row, column, (void*)node); } int ProblemModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent) return LastColumn; } IProblem::Ptr ProblemModel::problemForIndex(const QModelIndex& index) const { ProblemStoreNode *node = reinterpret_cast(index.internalPointer()); if (!node) { return {}; } else { return node->problem(); } } ProblemModel::Features ProblemModel::features() const { return d->m_features; } void ProblemModel::setFeatures(Features features) { d->m_features = features; } void ProblemModel::addProblem(const IProblem::Ptr &problem) { int c = d->m_problems->count(); beginInsertRows(QModelIndex(), c, c + 1); d->m_problems->addProblem(problem); endInsertRows(); } void ProblemModel::setProblems(const QVector &problems) { beginResetModel(); d->m_problems->setProblems(problems); endResetModel(); } void ProblemModel::clearProblems() { beginResetModel(); d->m_problems->clear(); endResetModel(); } QVariant ProblemModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); if (role != Qt::DisplayRole) return {}; switch (section) { case Source: return i18nc("@title:column source of problem", "Source"); case Error: return i18nc("@title:column problem description", "Problem"); case File: return i18nc("@title:column file where problem was found", "File"); case Line: return i18nc("@title:column line number with problem", "Line"); case Column: return i18nc("@title:column column number with problem", "Column"); } return {}; } void ProblemModel::setCurrentDocument(IDocument* document) { Q_ASSERT(thread() == QThread::currentThread()); QUrl currentDocument = document->url(); /// Will trigger signals beginRebuild(), endRebuild() if problems change and are rebuilt d->m_problems->setCurrentDocument(IndexedString(currentDocument)); } void ProblemModel::onBeginRebuild() { beginResetModel(); } void ProblemModel::onEndRebuild() { endResetModel(); } void ProblemModel::setScope(int scope) { Q_ASSERT(thread() == QThread::currentThread()); /// Will trigger signals beginRebuild(), endRebuild() if problems change and are rebuilt d->m_problems->setScope(scope); } void ProblemModel::setSeverity(int severity) { Q_ASSERT(thread() == QThread::currentThread()); /// Will trigger signals beginRebuild(), endRebuild() if problems change and are rebuilt d->m_problems->setSeverity(severity); } void ProblemModel::setGrouping(int grouping) { /// Will trigger signals beginRebuild(), endRebuild() if problems change and are rebuilt d->m_problems->setGrouping(grouping); } ProblemStore* ProblemModel::store() const { return d->m_problems.data(); } } diff --git a/shell/problemmodel.h b/shell/problemmodel.h index ff6898c1a..0933a8049 100644 --- a/shell/problemmodel.h +++ b/shell/problemmodel.h @@ -1,177 +1,182 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * Copyright 2015 Laszlo Kis-Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROBLEMMODEL_H #define PROBLEMMODEL_H #include #include #include #include struct ProblemModelPrivate; namespace KDevelop { class IDocument; class ProblemStore; /** * @brief Wraps a ProblemStore and adds the QAbstractItemModel interface, so the it can be used in a model/view architecture. * * By default ProblemModel instantiates a FilteredProblemStore, with the following features on: * \li ScopeFilter * \li SeverityFilter * \li Grouping * \li CanByPassScopeFilter * * Has to following columns: * \li Error * \li Source * \li File * \li Line * \li Column * \li LastColumn * * Possible ProblemModel features * \li NoFeatures * \li CanDoFullUpdate * \li CanShowImports * \li ScopeFilter * \li SeverityFilter * \li Grouping * \li CanByPassScopeFilter * * Scope, severity, grouping, imports can be set using the slots named after these features. * * Usage example: * @code * IProblem::Ptr problem(new DetectedProblem); * problem->setDescription(QStringLiteral("Problem")); * ProblemModel *model = new ProblemModel(nullptr); * model->addProblem(problem); * model->rowCount(); // returns 1 * QModelIndex idx = model->index(0, 0); * model->data(index); // "Problem" * @endcode * */ class KDEVPLATFORMSHELL_EXPORT ProblemModel : public QAbstractItemModel { Q_OBJECT public: /// List of supportable features enum FeatureCode { NoFeatures = 0, /// No features :( CanDoFullUpdate = 1, /// Reload/Reparse problems CanShowImports = 2, /// Show problems from imported files. E.g.: Header files in C/C++ ScopeFilter = 4, /// Filter problems by scope. E.g.: current document, open documents, etc SeverityFilter = 8, /// Filter problems by severity. E.g.: hint, warning, error, etc Grouping = 16, /// Can group problems CanByPassScopeFilter = 32 /// Can bypass scope filter }; Q_DECLARE_FLAGS(Features, FeatureCode) explicit ProblemModel(QObject *parent, ProblemStore *store = NULL); virtual ~ProblemModel(); enum Columns { Error, Source, File, Line, Column, LastColumn }; + enum Roles { + ProblemRole = Qt::UserRole + 1, + SeverityRole + }; + virtual int columnCount(const QModelIndex & parent = QModelIndex()) const override; virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex & index) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override; virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; IProblem::Ptr problemForIndex(const QModelIndex& index) const; /// Adds a new problem to the model void addProblem(const IProblem::Ptr &problem); /// Clears the problems, then adds a new set of them void setProblems(const QVector &problems); /// Clears the problems void clearProblems(); /// Retrieve the supported features Features features() const; /// Set the supported features void setFeatures(Features features); public slots: /// Show imports virtual void setShowImports(bool){} /// Sets the scope filter. Uses int to be able to use QSignalMapper virtual void setScope(int scope); /// Sets the severity filter. Uses int to be able to use QSignalMapper virtual void setSeverity(int severity); void setGrouping(int grouping); /** * Force a full problem update. * E.g.: Reparse the source code. * Obviously it doesn't make sense for run-time problem checkers. */ virtual void forceFullUpdate(){} protected slots: /// Triggered when problems change virtual void onProblemsChanged(){} private slots: /// Triggered when the current document changes virtual void setCurrentDocument(IDocument* doc); /// Triggered before the problems are rebuilt void onBeginRebuild(); /// Triggered once the problems have been rebuilt void onEndRebuild(); protected: ProblemStore *store() const; private: QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(ProblemModel::Features) } #endif // PROBLEMMODEL_H