diff --git a/plugins/problemreporter/problemreportermodel.cpp b/plugins/problemreporter/problemreportermodel.cpp index 0afcc3e6c9..1de4f5aab8 100644 --- a/plugins/problemreporter/problemreportermodel.cpp +++ b/plugins/problemreporter/problemreportermodel.cpp @@ -1,168 +1,168 @@ /* * 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. */ #include "problemreportermodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; const int ProblemReporterModel::MinTimeout = 1000; const int ProblemReporterModel::MaxTimeout = 5000; ProblemReporterModel::ProblemReporterModel(QObject* parent) : ProblemModel(parent, new FilteredProblemStore()) { - setFeatures(CanDoFullUpdate | CanShowImports | ScopeFilter | SeverityFilter); + setFeatures(CanDoFullUpdate | CanShowImports | ScopeFilter | SeverityFilter | ShowSource); m_minTimer = new QTimer(this); m_minTimer->setInterval(MinTimeout); m_minTimer->setSingleShot(true); connect(m_minTimer, &QTimer::timeout, this, &ProblemReporterModel::timerExpired); m_maxTimer = new QTimer(this); m_maxTimer->setInterval(MaxTimeout); m_maxTimer->setSingleShot(true); connect(m_maxTimer, &QTimer::timeout, this, &ProblemReporterModel::timerExpired); connect(store(), &FilteredProblemStore::changed, this, &ProblemReporterModel::onProblemsChanged); connect(ICore::self()->languageController()->staticAssistantsManager(), &StaticAssistantsManager::problemsChanged, this, &ProblemReporterModel::onProblemsChanged); } ProblemReporterModel::~ProblemReporterModel() { } QVector ProblemReporterModel::problems(const QSet& docs) const { QVector result; DUChainReadLocker lock; foreach (const IndexedString& doc, docs) { if (doc.isEmpty()) continue; TopDUContext* ctx = DUChain::self()->chainForDocument(doc); if (!ctx) continue; foreach (ProblemPointer p, DUChainUtils::allProblemsForContext(ctx)) { result.append(p); } } return result; } void ProblemReporterModel::forceFullUpdate() { Q_ASSERT(thread() == QThread::currentThread()); QSet documents = store()->documents()->get(); if (showImports()) documents += store()->documents()->getImports(); DUChainReadLocker lock(DUChain::lock()); foreach (const IndexedString& document, documents) { if (document.isEmpty()) continue; TopDUContext::Features updateType = TopDUContext::ForceUpdate; if (documents.size() == 1) updateType = TopDUContext::ForceUpdateRecursive; DUChain::self()->updateContextForUrl( document, (TopDUContext::Features)(updateType | TopDUContext::VisibleDeclarationsAndContexts)); } } void ProblemReporterModel::onProblemsChanged() { rebuildProblemList(); } void ProblemReporterModel::timerExpired() { m_minTimer->stop(); m_maxTimer->stop(); rebuildProblemList(); } void ProblemReporterModel::setCurrentDocument(KDevelop::IDocument* doc) { Q_ASSERT(thread() == QThread::currentThread()); beginResetModel(); /// Will trigger signal changed() if problems change store()->setCurrentDocument(IndexedString(doc->url())); endResetModel(); } void ProblemReporterModel::problemsUpdated(const KDevelop::IndexedString& url) { Q_ASSERT(thread() == QThread::currentThread()); // skip update for urls outside current scope if (!store()->documents()->get().contains(url) && !(showImports() && store()->documents()->getImports().contains(url))) return; /// m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period. m_minTimer->start(); /// m_maxTimer will expire unconditionally in MaxTimeout if (!m_maxTimer->isActive()) { m_maxTimer->start(); } } void ProblemReporterModel::rebuildProblemList() { /// No locking here, because it may be called from an already locked context beginResetModel(); QVector allProblems = problems(store()->documents()->get()); if (showImports()) allProblems += problems(store()->documents()->getImports()); store()->setProblems(allProblems); endResetModel(); } diff --git a/plugins/problemreporter/problemtreeview.cpp b/plugins/problemreporter/problemtreeview.cpp index 59fe0817bf..9eb50f36a2 100644 --- a/plugins/problemreporter/problemtreeview.cpp +++ b/plugins/problemreporter/problemtreeview.cpp @@ -1,220 +1,223 @@ /* * 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 "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)) { setObjectName(QStringLiteral("Problem Reporter Tree")); setWhatsThis(i18n("Problems")); setItemDelegate(new ProblemTreeViewItemDelegate(this)); 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::ShowSource)) { + hideColumn(ProblemModel::Source); + } 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); m_proxy->setFilterKeyColumn(-1); m_proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); } 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()); const auto problem = index.data(ProblemModel::ProblemRole).value(); if (!problem) return; url = problem->finalLocation().document.toUrl(); start = problem->finalLocation().start(); } if (QFile::exists(url.toLocalFile())) { 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(); } int ProblemTreeView::setFilter(const QString& filterText) { m_proxy->setFilterFixedString(filterText); return m_proxy->rowCount(); } ProblemModel* ProblemTreeView::model() const { return static_cast(m_proxy->sourceModel()); } void ProblemTreeView::setModel(QAbstractItemModel* model) { Q_ASSERT(qobject_cast(model)); m_proxy->setSourceModel(model); QTreeView::setModel(m_proxy); } void ProblemTreeView::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) return; 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()) { return; } QString title = solution->title(); title = KDevelop::htmlToPlainText(title); title.replace(QLatin1String("'"), QLatin1String("\'")); 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/shell/problemmodel.h b/shell/problemmodel.h index 0ed5227992..287c858935 100644 --- a/shell/problemmodel.h +++ b/shell/problemmodel.h @@ -1,210 +1,211 @@ /* * 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 + CanByPassScopeFilter = 32, /// Can bypass scope filter + ShowSource = 64 /// Show problem's source. Set if problems can have different sources. }; Q_DECLARE_FLAGS(Features, FeatureCode) explicit ProblemModel(QObject *parent, ProblemStore *store = nullptr); ~ProblemModel() override; enum Columns { Error, Source, File, Line, Column, LastColumn }; enum Roles { ProblemRole = Qt::UserRole + 1, SeverityRole }; int columnCount(const QModelIndex & parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex & index) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; 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 problems for selected document QVector problems(const KDevelop::IndexedString& document); /// Retrieve the supported features Features features() const; /// Retrieve 'show imports' filter setting bool showImports(); /// Set the supported features void setFeatures(Features features); /// Tooltip for "Force Full Update" action in the Problems View when the model /// is active (correspondent tab is selected) QString fullUpdateTooltip() const; /// Set the "Force Full Update" action tooltip void setFullUpdateTooltip(const QString& tooltip); signals: /// Emitted when the stored problems are changed with addProblem(), setProblems() and /// clearProblems() methods. This signal emitted only when internal problems storage is /// really changed: for example, it is not emitted when we call clearProblems() method /// for empty model. void problemsChanged(); /// Emitted when the "Force Full Update" action tooltip is changed with setFullUpdateTooltip(). /// This signal emitted only when tooltip is really changed. void fullUpdateTooltipChanged(); public slots: /// Show imports void setShowImports(bool showImports); /// Sets the scope filter. Uses int to be able to use QSignalMapper void setScope(int scope); /// Sets the severity filter. Uses int to be able to use QSignalMapper void setSeverity(int severity);///old-style severity filtering void setSeverities(KDevelop::IProblem::Severities severities);///new-style severity filtering 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); virtual void closedDocument(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