diff --git a/plugins/grepview/grepoutputdelegate.cpp b/plugins/grepview/grepoutputdelegate.cpp index f81a289804..0e127ab1bf 100644 --- a/plugins/grepview/grepoutputdelegate.cpp +++ b/plugins/grepview/grepoutputdelegate.cpp @@ -1,174 +1,206 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright (C) 2007 Andreas Pakulat * * Copyright 2010 Julien Desgats * * * * 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 Library 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 "grepoutputdelegate.h" #include "grepoutputmodel.h" #include #include #include #include #include #include #include #include #include #include GrepOutputDelegate* GrepOutputDelegate::m_self = nullptr; GrepOutputDelegate* GrepOutputDelegate::self() { Q_ASSERT(m_self); return m_self; } GrepOutputDelegate::GrepOutputDelegate( QObject* parent ) : QStyledItemDelegate(parent) { Q_ASSERT(!m_self); m_self = this; } GrepOutputDelegate::~GrepOutputDelegate() { m_self = nullptr; } void GrepOutputDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { // there is no function in QString to left-trim. A call to remove this regexp does the job static const QRegExp leftspaces(QStringLiteral("^\\s*"), Qt::CaseSensitive, QRegExp::RegExp); // rich text component const auto *model = dynamic_cast(index.model()); const auto *item = dynamic_cast(model->itemFromIndex(index)); QStyleOptionViewItem options = option; initStyleOption(&options, index); // building item representation QTextDocument doc; QTextCursor cur(&doc); QPalette::ColorGroup cg = (options.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; QPalette::ColorRole cr = (options.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text; QTextCharFormat fmt = cur.charFormat(); fmt.setFont(options.font); if(item && item->isText()) { - // Use custom manual highlighting - - const KTextEditor::Range rng = item->change()->m_range; - - // the line number appears grayed - fmt.setForeground(options.palette.brush(QPalette::Disabled, cr)); - cur.insertText(i18n("Line %1: ",item->lineNumber()), fmt); - - // switch to normal color - fmt.setForeground(options.palette.brush(cg, cr)); - cur.insertText(item->text().left(rng.start().column()).remove(leftspaces), fmt); - - fmt.setFontWeight(QFont::Bold); - if ( !(options.state & QStyle::State_Selected) ) { - QColor bgHighlight = option.palette.color(QPalette::AlternateBase); - fmt.setBackground(bgHighlight); + if (item->hasChildren()) { + // the line number appears grayed + fmt.setForeground(options.palette.brush(QPalette::Disabled, cr)); + cur.insertText(i18n("Line %1: ",item->lineNumber()), fmt); + fmt.setForeground(options.palette.brush(cg, cr)); + + int firstStart = static_cast(item->child(0))->change()->m_range.start().column(); + cur.insertText(item->text().left(firstStart).remove(leftspaces), fmt); + KTextEditor::Range previousRange(item->lineNumber(), 0, item->lineNumber(), firstStart); + for (int i = 0; i < item->rowCount(); ++i) { + const KTextEditor::Range range = static_cast(item->child(i))->change()->m_range; + fmt.setForeground(options.palette.brush(cg, cr)); + cur.insertText(item->text().mid(previousRange.end().column(), range.start().column() - previousRange.end().column()), fmt); + fmt.setFontWeight(QFont::Bold); + if ( !(options.state & QStyle::State_Selected) ) { + QColor bgHighlight = option.palette.color(QPalette::AlternateBase); + fmt.setBackground(bgHighlight); + } + cur.insertText(item->text().mid(range.start().column(), range.columnWidth()), fmt); + fmt.clearBackground(); + previousRange = range; + } + fmt.setFontWeight(QFont::Normal); + cur.insertText(item->text().mid(previousRange.end().column()), fmt); + } else { + // Use custom manual highlighting + + const KTextEditor::Range rng = item->change()->m_range; + + // switch to normal color + fmt.setForeground(options.palette.brush(cg, cr)); + cur.insertText(item->text().left(rng.start().column()).remove(leftspaces), fmt); + + fmt.setFontWeight(QFont::Bold); + if ( !(options.state & QStyle::State_Selected) ) { + QColor bgHighlight = option.palette.color(QPalette::AlternateBase); + fmt.setBackground(bgHighlight); + } + cur.insertText(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column()), fmt); + fmt.clearBackground(); + + fmt.setFontWeight(QFont::Normal); + cur.insertText(item->text().mid(rng.end().column()), fmt); } - cur.insertText(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column()), fmt); - fmt.clearBackground(); - - fmt.setFontWeight(QFont::Normal); - cur.insertText(item->text().mid(rng.end().column()), fmt); }else{ QString text; if(item) text = item->text(); else text = index.data().toString(); // Simply insert the text as html. We use this for the titles. doc.setHtml(text); } painter->save(); options.text = QString(); // text will be drawn separately options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget); // set correct draw area QRect clip = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options); QFontMetrics metrics(options.font); painter->translate(clip.topLeft() - QPoint(0, metrics.descent())); // We disable the clipping for now, as it leads to strange clipping errors // clip.setTopLeft(QPoint(0,0)); // painter->setClipRect(clip); QAbstractTextDocumentLayout::PaintContext ctx; // ctx.clip = clip; painter->setBackground(Qt::transparent); doc.documentLayout()->draw(painter, ctx); painter->restore(); } QSize GrepOutputDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { const auto *model = dynamic_cast(index.model()); const GrepOutputItem *item = model ? dynamic_cast(model->itemFromIndex(index)) : nullptr; QSize ret = QStyledItemDelegate::sizeHint(option, index); //take account of additional width required for highlighting (bold text) //and line numbers. These are not included in the default Qt size calculation. if(item && item->isText()) { QFont font = option.font; QFontMetrics metrics(font); font.setBold(true); QFontMetrics bMetrics(font); - - const KTextEditor::Range rng = item->change()->m_range; - - int width = metrics.width(item->text().left(rng.start().column())) + - metrics.width(item->text().mid(rng.end().column())) + - bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) + - option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) + - std::max(option.decorationSize.width(), 0); - ret.setWidth(width); + if (item->hasChildren()) { + int bWidth = 0; + for (int i = 0; i < item->rowCount(); ++i) { + bWidth += static_cast(item->child(i))->change()->m_range.columnWidth(); + } + int width = option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) + + metrics.width(item->text().length() - bWidth) + bMetrics.width(bWidth) + + std::max(option.decorationSize.width(), 0); + + ret.setWidth(width); + } else { + const KTextEditor::Range rng = item->change()->m_range; + + int width = metrics.width(item->text().left(rng.start().column())) + + metrics.width(item->text().mid(rng.end().column())) + + bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) + + std::max(option.decorationSize.width(), 0); + ret.setWidth(width); + } }else{ // This is only used for titles, so not very performance critical QString text; if(item) text = item->text(); else text = index.data().toString(); QTextDocument doc; doc.setDocumentMargin(0); doc.setHtml(text); QSize newSize = doc.size().toSize(); if(newSize.height() > ret.height()) ret.setHeight(newSize.height()); } return ret; } diff --git a/plugins/grepview/grepoutputmodel.cpp b/plugins/grepview/grepoutputmodel.cpp index a3a4b409b3..b17609f384 100644 --- a/plugins/grepview/grepoutputmodel.cpp +++ b/plugins/grepview/grepoutputmodel.cpp @@ -1,481 +1,526 @@ /*************************************************************************** * Copyright 1999-2001 Bernd Gehrmann and the KDevelop Team * * bernd@kdevelop.org * * Copyright 2007 Dukju Ahn * * Copyright 2010 Silvère Lestang * * Copyright 2010 Julien Desgats * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "grepoutputmodel.h" #include "grepviewplugin.h" #include "greputil.h" #include #include #include #include #include #include #include #include using namespace KDevelop; -GrepOutputItem::GrepOutputItem(const DocumentChangePointer& change, const QString &text, bool checkable) +GrepOutputItem::GrepOutputItem(const DocumentChangePointer &change, const QString &text, bool checkable) : QStandardItem(), m_change(change) { setText(text); setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); setCheckable(checkable); if(checkable) setCheckState(Qt::Checked); } GrepOutputItem::GrepOutputItem(const QString& filename, const QString& text, bool checkable) : QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range::invalid(), QString(), QString())) { setText(text); setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); setCheckable(checkable); if(checkable) { setAutoTristate(true); setCheckState(Qt::Checked); } } +GrepOutputItem::GrepOutputItem(const QString &filename, const QString &text, int line, bool checkable) + : QStandardItem(), m_change(new DocumentChange(IndexedString(filename), KTextEditor::Range(line, 0, line, text.length() - 1), QString(), QString())) +{ + setText(text); + setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + setCheckable(checkable); + if(checkable) + { + setAutoTristate(true); + setCheckState(Qt::Checked); + } +} int GrepOutputItem::lineNumber() const { // line starts at 0 for cursor but we want to start at 1 return m_change->m_range.start().line() + 1; } QString GrepOutputItem::filename() const { return m_change->m_document.str(); } DocumentChangePointer GrepOutputItem::change() const { return m_change; } bool GrepOutputItem::isText() const { return m_change->m_range.isValid(); } void GrepOutputItem::propagateState() { for(int i = 0; i < rowCount(); i++) { auto *item = static_cast(child(i)); if(item->isEnabled()) { item->setCheckState(checkState()); item->propagateState(); } } } void GrepOutputItem::refreshState() { if(rowCount() > 0) { int checked = 0; int unchecked = 0; int enabled = 0; //only enabled items are relevants for(int i = 0; i < rowCount(); i++) { QStandardItem *item = child(i); if(item->isEnabled()) { enabled += 1; switch(child(i)->checkState()) { case Qt::Checked: checked += 1; break; case Qt::Unchecked: unchecked += 1; break; default: break; } } } if(enabled == 0) { setCheckState(Qt::Unchecked); setEnabled(false); } else if(checked == enabled) { setCheckState(Qt::Checked); } else if (unchecked == enabled) { setCheckState(Qt::Unchecked); } else { setCheckState(Qt::PartiallyChecked); } } if(auto *p = static_cast(parent())) { p->refreshState(); } } QVariant GrepOutputItem::data ( int role ) const { auto *grepModel = static_cast(model()); if(role == Qt::ToolTipRole && grepModel && isText()) { - QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped(); - // show replaced version in tooltip if we are in replace mode - const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText; - const QString repl = QLatin1String("") + match.toHtmlEscaped() + QLatin1String(""); - QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped(); - const QString toolTip = QLatin1String("") + QString(start + repl + end).trimmed() + QLatin1String(""); - return toolTip; + if (hasChildren()) { + QString toolTip; + KTextEditor::Range previousRange(lineNumber(), 0, lineNumber(), 0); + for (int i = 0; i < rowCount(); ++i) { + const GrepOutputItem *item = static_cast(child(i)); + const KTextEditor::Range range = item->change()->m_range; + toolTip += text().mid(previousRange.end().column(), range.start().column() - previousRange.end().column()).toHtmlEscaped(); + const QString match = isCheckable() ? grepModel->replacementFor(item->change()->m_oldText) : item->change()->m_oldText; + toolTip += QLatin1String("") + match.toHtmlEscaped() + QLatin1String(""); + previousRange = range; + } + toolTip += text().mid(previousRange.end().column()); + return QString(QLatin1String("") + toolTip.trimmed() + QLatin1String("")); + } else { + QString start = text().left(m_change->m_range.start().column()).toHtmlEscaped(); + // show replaced version in tooltip if we are in replace mode + const QString match = isCheckable() ? grepModel->replacementFor(m_change->m_oldText) : m_change->m_oldText; + const QString repl = QLatin1String("") + match.toHtmlEscaped() + QLatin1String(""); + QString end = text().mid(m_change->m_range.end().column()).toHtmlEscaped(); + const QString toolTip = QLatin1String("") + QString(start + repl + end).trimmed() + QLatin1String(""); + return toolTip; + } } else if (role == Qt::FontRole) { return QFontDatabase::systemFont(QFontDatabase::FixedFont); } else { return QStandardItem::data(role); } } GrepOutputItem::~GrepOutputItem() {} /////////////////////////////////////////////////////////////// GrepOutputModel::GrepOutputModel( QObject *parent ) : QStandardItemModel( parent ) { connect(this, &GrepOutputModel::itemChanged, this, &GrepOutputModel::updateCheckState); } GrepOutputModel::~GrepOutputModel() {} void GrepOutputModel::clear() { QStandardItemModel::clear(); // the above clear() also destroys the root item, so invalidate the pointer m_rootItem = nullptr; m_fileCount = 0; m_matchCount = 0; } void GrepOutputModel::setRegExp(const QRegExp& re) { m_regExp = re; m_finalUpToDate = false; } void GrepOutputModel::setReplacement(const QString& repl) { m_replacement = repl; m_finalUpToDate = false; } void GrepOutputModel::setReplacementTemplate(const QString& tmpl) { m_replacementTemplate = tmpl; m_finalUpToDate = false; } QString GrepOutputModel::replacementFor(const QString &text) { if(!m_finalUpToDate) { m_finalReplacement = substitudePattern(m_replacementTemplate, m_replacement); m_finalUpToDate = true; } return QString(text).replace(m_regExp, m_finalReplacement); } void GrepOutputModel::activate( const QModelIndex &idx ) { QStandardItem *stditem = itemFromIndex(idx); auto *grepitem = dynamic_cast(stditem); if( !grepitem || !grepitem->isText() ) return; QUrl url = QUrl::fromLocalFile(grepitem->filename()); int line = grepitem->lineNumber() - 1; KTextEditor::Range range( line, 0, line+1, 0); // Try to find the actual text range we found during the grep IDocument* doc = ICore::self()->documentController()->documentForUrl( url ); if(!doc) doc = ICore::self()->documentController()->openDocument( url, range ); if(!doc) return; if (KTextEditor::Document* tdoc = doc->textDocument()) { KTextEditor::Range matchRange = grepitem->change()->m_range; QString actualText = tdoc->text(matchRange); QString expectedText = grepitem->change()->m_oldText; if (actualText == expectedText) { range = matchRange; } } ICore::self()->documentController()->activateDocument( doc, range ); } QModelIndex GrepOutputModel::previousItemIndex(const QModelIndex ¤tIdx) const { GrepOutputItem* current_item = nullptr; if (!currentIdx.isValid()) { // no item selected, search recursively for the last item in search results QStandardItem *it = item(0,0); while (it) { QStandardItem *child = it->child( it->rowCount() - 1 ); if (!child) return it->index(); it = child; } return QModelIndex(); } else current_item = static_cast(itemFromIndex(currentIdx)); if (current_item->parent() != nullptr) { int row = currentIdx.row(); - if(!current_item->isText()) // the item is a file { int item_row = current_item->row(); - if(item_row > 0) + if(item_row > 0) { + int last_line = current_item->parent()->child(item_row-1)->rowCount() - 1; + int last_item = current_item->parent()->child(item_row-1)->child(last_line)->rowCount() - 1; + return current_item->parent()->child(item_row-1)->child(last_line)->child(last_item)->index(); + } + } + else if (current_item->hasChildren())//the item is a line + { + int idx_line = current_item->row(); + if (idx_line > 0) { - int idx_last_item = current_item->parent()->child(item_row - 1)->rowCount() - 1; - return current_item->parent()->child(item_row - 1)->child(idx_last_item)->index(); + int last_item = current_item->parent()->child(idx_line - 1)->rowCount() - 1; + return current_item->parent()->child(idx_line - 1)->child(last_item)->index(); } + return previousItemIndex(current_item->parent()->index()); } else // the item is a match { - if(row > 0) + if(row > 0) { return current_item->parent()->child(row - 1)->index(); - else // we return the index of the last item of the previous file - { - int parrent_row = current_item->parent()->row(); - if(parrent_row > 0) - { - int idx_last_item = current_item->parent()->parent()->child(parrent_row - 1)->rowCount() - 1; - return current_item->parent()->parent()->child(parrent_row - 1)->child(idx_last_item)->index(); - } } + return previousItemIndex(current_item->parent()->index()); } } return currentIdx; } QModelIndex GrepOutputModel::nextItemIndex(const QModelIndex ¤tIdx) const { GrepOutputItem* current_item = nullptr; if (!currentIdx.isValid()) { QStandardItem *it = item(0,0); if (!it) return QModelIndex(); current_item = static_cast(it); } else current_item = static_cast(itemFromIndex(currentIdx)); if (current_item->parent() == nullptr) { // root item with overview of search results if (current_item->rowCount() > 0) return nextItemIndex(current_item->child(0)->index()); else return QModelIndex(); } else { int row = currentIdx.row(); if(!current_item->isText()) // the item is a file { int item_row = current_item->row(); if(item_row < current_item->parent()->rowCount()) { - return current_item->parent()->child(item_row)->child(0)->index(); + return current_item->parent()->child(item_row)->child(0)->child(0)->index(); } } + else if(current_item->hasChildren()) //the item is a line + { + return current_item->child(0)->index(); + } else // the item is a match { if(row < current_item->parent()->rowCount() - 1) return current_item->parent()->child(row + 1)->index(); + else if(current_item->parent()->row() < current_item->parent()->parent()->rowCount() - 1) //next line + { + return current_item->parent()->parent()->child(current_item->parent()->row() + 1)->child(0)->index(); + } else // we return the index of the first item of the next file { - int parrent_row = current_item->parent()->row(); + int parrent_row = current_item->parent()->parent()->row(); if(parrent_row < current_item->parent()->parent()->rowCount() - 1) { - return current_item->parent()->parent()->child(parrent_row + 1)->child(0)->index(); + return current_item->parent()->parent()->parent()->child(parrent_row + 1)->child(0)->child(0)->index(); } } } } return currentIdx; } const GrepOutputItem *GrepOutputModel::getRootItem() const { return m_rootItem; } bool GrepOutputModel::itemsCheckable() const { return m_itemsCheckable; } void GrepOutputModel::makeItemsCheckable(bool checkable) { if(m_itemsCheckable == checkable) return; if(m_rootItem) makeItemsCheckable(checkable, m_rootItem); m_itemsCheckable = checkable; } void GrepOutputModel::makeItemsCheckable(bool checkable, GrepOutputItem* item) { item->setCheckable(checkable); if(checkable) { item->setCheckState(Qt::Checked); if(item->rowCount() && checkable) item->setAutoTristate(true); } for(int row = 0; row < item->rowCount(); ++row) makeItemsCheckable(checkable, static_cast(item->child(row, 0))); } void GrepOutputModel::appendOutputs( const QString &filename, const GrepOutputItem::List &items ) { if(items.isEmpty()) return; if(rowCount() == 0) { m_rootItem = new GrepOutputItem(QString(), QString(), m_itemsCheckable); appendRow(m_rootItem); } m_fileCount += 1; m_matchCount += items.length(); const QString matchText = i18np("1 match", "%1 matches", m_matchCount); const QString fileText = i18np("1 file", "%1 files", m_fileCount); m_rootItem->setText(i18nc("%1 is e.g. '4 matches', %2 is e.g. '1 file'", "%1 in %2", matchText, fileText)); QString fnString = i18np("%2: 1 match", "%2: %1 matches", items.length(), ICore::self()->projectController()->prettyFileName(QUrl::fromLocalFile(filename))); auto *fileItem = new GrepOutputItem(filename, fnString, m_itemsCheckable); m_rootItem->appendRow(fileItem); + GrepOutputItem *lineItem = new GrepOutputItem(filename, items[0].text(), items[0].change()->m_range.start().line(), m_itemsCheckable); for (const GrepOutputItem& item : items) { auto* copy = new GrepOutputItem(item); copy->setCheckable(m_itemsCheckable); if(m_itemsCheckable) { copy->setCheckState(Qt::Checked); if(copy->rowCount()) copy->setAutoTristate(true); } - - fileItem->appendRow(copy); + if (copy->change()->m_range.start().line() != lineItem->change()->m_range.start().line()) { + fileItem->appendRow(lineItem); + lineItem = new GrepOutputItem(filename, copy->text(), copy->change()->m_range.start().line(), m_itemsCheckable); + } + lineItem->appendRow(copy); } + fileItem->appendRow(lineItem); } void GrepOutputModel::updateCheckState(QStandardItem* item) { // if we don't disconnect the SIGNAL, the setCheckState will call it in loop disconnect(this, &GrepOutputModel::itemChanged, nullptr, nullptr); // try to update checkstate on non checkable items would make a checkbox appear if(item->isCheckable()) { auto *it = static_cast(item); it->propagateState(); it->refreshState(); } connect(this, &GrepOutputModel::itemChanged, this, &GrepOutputModel::updateCheckState); } void GrepOutputModel::doReplacements() { Q_ASSERT(m_rootItem); if (!m_rootItem) return; // nothing to do, abort DocumentChangeSet changeSet; changeSet.setFormatPolicy(DocumentChangeSet::NoAutoFormat); for(int fileRow = 0; fileRow < m_rootItem->rowCount(); fileRow++) { auto *file = static_cast(m_rootItem->child(fileRow)); - - for(int matchRow = 0; matchRow < file->rowCount(); matchRow++) + for(int lineRow = 0; lineRow < file->rowCount(); lineRow++) { - auto *match = static_cast(file->child(matchRow)); - if(match->checkState() == Qt::Checked) + auto *line = static_cast(file->child(lineRow)); + for(int matchRow = 0; matchRow < line->rowCount(); matchRow++) { - DocumentChangePointer change = match->change(); - // setting replacement text based on current replace value - change->m_newText = replacementFor(change->m_oldText); - changeSet.addChange(change); - // this item cannot be checked anymore - match->setCheckState(Qt::Unchecked); - match->setEnabled(false); + auto *match = static_cast(line->child(matchRow)); + if(match->checkState() == Qt::Checked) + { + DocumentChangePointer change = match->change(); + // setting replacement text based on current replace value + change->m_newText = replacementFor(change->m_oldText); + changeSet.addChange(change); + // this item cannot be checked anymore + match->setCheckState(Qt::Unchecked); + match->setEnabled(false); + } } } } DocumentChangeSet::ChangeResult result = changeSet.applyAllChanges(); if(!result.m_success) { DocumentChangePointer ch = result.m_reasonChange; if(ch) emit showErrorMessage(i18nc("%1 is the old text, %2 is the new text, %3 is the file path, %4 and %5 are its row and column", "Failed to replace %1 by %2 in %3:%4:%5", ch->m_oldText.toHtmlEscaped(), ch->m_newText.toHtmlEscaped(), ch->m_document.toUrl().toLocalFile(), ch->m_range.start().line() + 1, ch->m_range.start().column() + 1)); } } void GrepOutputModel::showMessageSlot(IStatus* status, const QString& message) { m_savedMessage = message; m_savedIStatus = status; showMessageEmit(); } void GrepOutputModel::showMessageEmit() { emit showMessage(m_savedIStatus, m_savedMessage); } bool GrepOutputModel::hasResults() { return(m_matchCount > 0); } diff --git a/plugins/grepview/grepoutputmodel.h b/plugins/grepview/grepoutputmodel.h index 6c0d6a8b78..2fc909a419 100644 --- a/plugins/grepview/grepoutputmodel.h +++ b/plugins/grepview/grepoutputmodel.h @@ -1,112 +1,113 @@ /*************************************************************************** * Copyright 1999-2001 Bernd Gehrmann and the KDevelop Team * * bernd@kdevelop.org * * Copyright 2007 Dukju Ahn * * Copyright 2010 Silvère Lestang * * Copyright 2010 Julien Desgats * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_GREPOUTPUTMODEL_H #define KDEVPLATFORM_PLUGIN_GREPOUTPUTMODEL_H #include #include #include class QModelIndex; class QRegExp; namespace KDevelop { class IStatus; } class GrepOutputItem : public QStandardItem { public: using List = QList; GrepOutputItem(const KDevelop::DocumentChangePointer& change, const QString& text, bool checkable); GrepOutputItem(const QString &filename, const QString &text, bool checkable); + GrepOutputItem(const QString &filename, const QString &text, int line, bool checkable); ~GrepOutputItem() override; QString filename() const ; int lineNumber() const ; KDevelop::DocumentChangePointer change() const ; bool isText() const ; /// Recursively apply check state to children void propagateState() ; /// Check children to determine current state void refreshState() ; QVariant data ( int role = Qt::UserRole + 1 ) const override; private: KDevelop::DocumentChangePointer m_change; }; Q_DECLARE_METATYPE(GrepOutputItem::List) class GrepOutputModel : public QStandardItemModel { Q_OBJECT public: explicit GrepOutputModel( QObject *parent = nullptr ); ~GrepOutputModel() override; void setRegExp(const QRegExp& re); void setReplacementTemplate(const QString &tmpl); /// applies replacement on given text QString replacementFor(const QString &text); void clear(); // resets file & match counts bool hasResults(); QModelIndex previousItemIndex(const QModelIndex ¤tIdx) const; QModelIndex nextItemIndex(const QModelIndex ¤tIdx) const; const GrepOutputItem *getRootItem() const; void makeItemsCheckable(bool checkable); bool itemsCheckable() const; public Q_SLOTS: void appendOutputs( const QString &filename, const GrepOutputItem::List &lines ); void activate( const QModelIndex &idx ); void doReplacements(); void setReplacement(const QString &repl); //receive status message from GrepJob, and store it void showMessageSlot( KDevelop::IStatus*, const QString& message ); //emit stored message as signal 'showMessage' to GrepOutputView. //called when user selects a search with the combobox void showMessageEmit(); Q_SIGNALS: void showMessage( KDevelop::IStatus*, const QString& message ); void showErrorMessage(const QString & message, int timeout = 0); private: void makeItemsCheckable(bool checkable, GrepOutputItem* item); QRegExp m_regExp; QString m_replacement; QString m_replacementTemplate; QString m_finalReplacement; bool m_finalUpToDate = false; /// says if m_finalReplacement is up to date or must be regenerated GrepOutputItem *m_rootItem = nullptr; int m_fileCount = 0; int m_matchCount = 0; QString m_savedMessage; KDevelop::IStatus *m_savedIStatus; bool m_itemsCheckable = false; private Q_SLOTS: void updateCheckState(QStandardItem*); }; #endif