diff --git a/plugins/grepview/grepoutputdelegate.cpp b/plugins/grepview/grepoutputdelegate.cpp --- a/plugins/grepview/grepoutputdelegate.cpp +++ b/plugins/grepview/grepoutputdelegate.cpp @@ -78,28 +78,50 @@ 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) @@ -146,15 +168,25 @@ 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; diff --git a/plugins/grepview/grepoutputmodel.h b/plugins/grepview/grepoutputmodel.h --- a/plugins/grepview/grepoutputmodel.h +++ b/plugins/grepview/grepoutputmodel.h @@ -34,6 +34,7 @@ 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 ; diff --git a/plugins/grepview/grepoutputmodel.cpp b/plugins/grepview/grepoutputmodel.cpp --- a/plugins/grepview/grepoutputmodel.cpp +++ b/plugins/grepview/grepoutputmodel.cpp @@ -30,7 +30,7 @@ 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); @@ -53,6 +53,18 @@ 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 { @@ -144,13 +156,28 @@ 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 { @@ -260,29 +287,31 @@ 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; @@ -313,19 +342,27 @@ 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(); } } } @@ -388,6 +425,7 @@ 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); @@ -397,9 +435,13 @@ 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) @@ -430,19 +472,22 @@ 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); + } } } }