diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp index bff4f835fe07b1362eecff3e9f1aac2a98bfb574..beb714b52d5d6a4418c2c6f3bbffde18d4f5acc9 100644 --- a/src/plugins/find/basetextfind.cpp +++ b/src/plugins/find/basetextfind.cpp @@ -215,7 +215,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, QRegExp regexp(before); regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString); regexp.setCaseSensitivity((findFlags & IFindSupport::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); - QTextCursor found = document()->find(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); + QTextCursor found = findOne(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); while (!found.isNull() && found.selectionStart() < found.selectionEnd() && inScope(found.selectionStart(), found.selectionEnd())) { ++count; @@ -224,7 +224,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, regexp.exactMatch(found.selectedText()); QString realAfter = usesRegExp ? expandRegExpReplacement(after, regexp) : after; editCursor.insertText(realAfter); - found = document()->find(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); } editCursor.endEditBlock(); return count; @@ -241,7 +241,7 @@ bool BaseTextFind::find(const QString &txt, QRegExp regexp(txt); regexp.setPatternSyntax((findFlags&IFindSupport::FindRegularExpression) ? QRegExp::RegExp : QRegExp::FixedString); regexp.setCaseSensitivity((findFlags&IFindSupport::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); - QTextCursor found = document()->find(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); + QTextCursor found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); if (!m_findScope.isNull()) { @@ -251,7 +251,7 @@ bool BaseTextFind::find(const QString &txt, start.setPosition(m_findScope.selectionStart()); else start.setPosition(m_findScope.selectionEnd()); - found = document()->find(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) return false; } @@ -263,7 +263,7 @@ bool BaseTextFind::find(const QString &txt, start.movePosition(QTextCursor::Start); else start.movePosition(QTextCursor::End); - found = document()->find(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags)); if (found.isNull()) { return false; } @@ -275,6 +275,28 @@ bool BaseTextFind::find(const QString &txt, return true; } + +// helper function. Works just like QTextDocument::find() but supports vertical block selection +QTextCursor BaseTextFind::findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const +{ + QTextCursor candidate = document()->find(expr, from, options); + if (!m_findScopeVerticalBlockSelection) + return candidate; + forever { + if (!inScope(candidate.selectionStart(), candidate.selectionEnd())) + return candidate; + QTextCursor b = candidate; + b.setPosition(candidate.selectionStart()); + QTextCursor e = candidate; + e.setPosition(candidate.selectionEnd()); + if (b.positionInBlock() >= m_findScopeFromColumn + && e.positionInBlock() <= m_findScopeToColumn) + return candidate; + candidate = document()->find(expr, candidate, options); + } + return candidate; +} + bool BaseTextFind::inScope(int startPosition, int endPosition) const { if (m_findScope.isNull()) @@ -288,8 +310,25 @@ void BaseTextFind::defineFindScope() QTextCursor cursor = textCursor(); if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) { m_findScope = cursor; - emit findScopeChanged(m_findScope); - cursor.setPosition(cursor.selectionStart()); + m_findScopeVerticalBlockSelection = false; + + int verticalBlockSelection = 0; + if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelection") >= 0) + verticalBlockSelection = m_plaineditor->property("verticalBlockSelection").toInt(); + + if (verticalBlockSelection) { + QTextDocument *doc = document(); + QTextCursor b(doc->docHandle(), cursor.selectionStart()); + QTextCursor e(doc->docHandle(), cursor.selectionEnd()); + m_findScopeFromColumn = qMin(b.positionInBlock(), e.positionInBlock()); + m_findScopeToColumn = m_findScopeFromColumn + verticalBlockSelection; + m_findScope.setPosition(b.block().position() + m_findScopeFromColumn); + m_findScope.setPosition(e.block().position() + qMin(e.block().length()-1, m_findScopeToColumn), + QTextCursor::KeepAnchor); + m_findScopeVerticalBlockSelection = verticalBlockSelection; + } + emit findScopeChanged(m_findScope, m_findScopeVerticalBlockSelection); + cursor.setPosition(m_findScope.selectionStart()); setTextCursor(cursor); } else { clearFindScope(); @@ -299,5 +338,6 @@ void BaseTextFind::defineFindScope() void BaseTextFind::clearFindScope() { m_findScope = QTextCursor(); - emit findScopeChanged(m_findScope); + m_findScopeVerticalBlockSelection = 0; + emit findScopeChanged(m_findScope, m_findScopeVerticalBlockSelection); } diff --git a/src/plugins/find/basetextfind.h b/src/plugins/find/basetextfind.h index 7ed4b0f32b34aaf34b50d3869157355840941970..0c5c777b8eaf3b9bcdfc07b2a78e5f32c904198f 100644 --- a/src/plugins/find/basetextfind.h +++ b/src/plugins/find/basetextfind.h @@ -70,7 +70,7 @@ public: signals: void highlightAll(const QString &txt, Find::IFindSupport::FindFlags findFlags); - void findScopeChanged(const QTextCursor &); + void findScopeChanged(const QTextCursor &, int = 0); private: bool find(const QString &txt, @@ -84,7 +84,11 @@ private: QPointer<QTextEdit> m_editor; QPointer<QPlainTextEdit> m_plaineditor; QTextCursor m_findScope; + int m_findScopeVerticalBlockSelection; + int m_findScopeFromColumn; + int m_findScopeToColumn; bool inScope(int startPosition, int endPosition) const; + QTextCursor findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const; int m_incrementalStartPos; }; diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index a83684b7cc1fd79bdaefcbd9cbd8793de5362d5d..239df6c1ad0ea5daf3628bcf248aa5adb1463aa1 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -2195,6 +2195,15 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block, text.replace(QChar::Nbsp, QLatin1Char(' ')); int idx = -1; int l = 1; + + int m_findScopeFirstColumn = 0; + if (!m_findScope.isNull() && m_findScopeVerticalBlockSelection) { + QTextCursor b = cursor; + cursor.setPosition(m_findScope.selectionStart()); + m_findScopeFirstColumn = cursor.positionInBlock(); + } + + while (idx < text.length()) { idx = m_searchExpr.indexIn(text, idx + l); if (idx < 0) @@ -2205,19 +2214,26 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block, || (idx + l < text.length() && text.at(idx + l).isLetterOrNumber()))) continue; - if (m_findScope.isNull() - || (blockPosition + idx >= m_findScope.selectionStart() - && blockPosition + idx + l <= m_findScope.selectionEnd())) { + if (!m_findScope.isNull()) { + if (blockPosition + idx < m_findScope.selectionStart() + || blockPosition + idx + l > m_findScope.selectionEnd()) + continue; + if (m_findScopeVerticalBlockSelection) { + if (idx < m_findScopeFirstColumn + || idx + l > m_findScopeFirstColumn + m_findScopeVerticalBlockSelection) + continue; + } + } + - overlay->addOverlaySelection(blockPosition + idx, - blockPosition + idx + l, - m_searchResultFormat.background().color().darker(120), - QColor(), - (idx == cursor.selectionStart() - blockPosition - && idx + l == cursor.selectionEnd() - blockPosition)? - TextEditorOverlay::DropShadow : 0); + overlay->addOverlaySelection(blockPosition + idx, + blockPosition + idx + l, + m_searchResultFormat.background().color().darker(120), + QColor(), + (idx == cursor.selectionStart() - blockPosition + && idx + l == cursor.selectionEnd() - blockPosition)? + TextEditorOverlay::DropShadow : 0); - } } } @@ -2475,7 +2491,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) TextEditorOverlay *overlay = new TextEditorOverlay(this); overlay->addOverlaySelection(d->m_findScope, d->m_searchScopeFormat.background().color().darker(120), - d->m_searchScopeFormat.background().color()); + d->m_searchScopeFormat.background().color(), + 0, + d->m_findScopeVerticalBlockSelection); overlay->setAlpha(false); overlay->paint(&painter, e->rect()); delete overlay; @@ -4674,10 +4692,23 @@ void BaseTextEditor::highlightSearchResults(const QString &txt, Find::IFindSuppo d->m_delayedUpdateTimer->start(10); } -void BaseTextEditor::setFindScope(const QTextCursor &scope) +int BaseTextEditor::verticalBlockSelection() const +{ + if (!d->m_inBlockSelectionMode) + return 0; + QTextCursor b = textCursor(); + QTextCursor e = b; + b.setPosition(b.selectionStart()); + e.setPosition(e.selectionEnd()); + + return qAbs(b.positionInBlock() - e.positionInBlock()) + d->m_blockSelectionExtraX; +} + +void BaseTextEditor::setFindScope(const QTextCursor &scope, int verticalBlockSelection) { if (scope.isNull() != d->m_findScope.isNull()) { d->m_findScope = scope; + d->m_findScopeVerticalBlockSelection = verticalBlockSelection; viewport()->update(); } } @@ -5628,7 +5659,7 @@ BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor) BaseTextFind *baseTextFind = new BaseTextFind(editor); connect(baseTextFind, SIGNAL(highlightAll(QString, Find::IFindSupport::FindFlags)), editor, SLOT(highlightSearchResults(QString, Find::IFindSupport::FindFlags))); - connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor)), editor, SLOT(setFindScope(QTextCursor))); + connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, int)), editor, SLOT(setFindScope(QTextCursor, int))); aggregate->add(baseTextFind); aggregate->add(editor); diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index e884b8c93d4d886735b6409f2d1ceb6dd7b09b2b..cb6bea2ba64cdc014f3266a7f6cd5e721f6f91f6 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -285,6 +285,7 @@ private: class TEXTEDITOR_EXPORT BaseTextEditor : public QPlainTextEdit { Q_OBJECT + Q_PROPERTY(int verticalBlockSelection READ verticalBlockSelection) public: BaseTextEditor(QWidget *parent); @@ -382,6 +383,9 @@ public: void insertCodeSnippet(const QTextCursor &cursor, const QString &snippet); + + int verticalBlockSelection() const; + public slots: void setDisplayName(const QString &title); @@ -457,7 +461,7 @@ private slots: void memorizeCursorPosition(); void restoreCursorPosition(); void highlightSearchResults(const QString &txt, Find::IFindSupport::FindFlags findFlags); - void setFindScope(const QTextCursor &); + void setFindScope(const QTextCursor &, int); void currentEditorChanged(Core::IEditor *editor); void maybeEmitContentsChangedBecauseOfUndo(); diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index 80eb0e929525a43808790d6536b03a99ebdfdeff..2dacf3470155e31fb028deb6d9596456f64d1389 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -250,6 +250,7 @@ public: bool m_moveLineUndoHack; QTextCursor m_findScope; + int m_findScopeVerticalBlockSelection; QTextCursor m_selectBlockAnchor; void moveCursorVisible(bool ensureVisible = true); diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp index 07227fc52f19fe2f446fbff8bb30e53219fd612f..044d018c6463b738fd253605eb1243c91b7853a3 100644 --- a/src/plugins/texteditor/texteditoroverlay.cpp +++ b/src/plugins/texteditor/texteditoroverlay.cpp @@ -71,7 +71,8 @@ void TextEditorOverlay::clear() void TextEditorOverlay::addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg, - uint overlaySelectionFlags) + uint overlaySelectionFlags, + int verticalBlockSelection) { if (end < begin) return; @@ -97,6 +98,8 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end, selection.m_dropShadow = (overlaySelectionFlags & DropShadow); + selection.m_verticalBlockSelection = verticalBlockSelection; + m_selections.append(selection); update(); } @@ -104,9 +107,11 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end, void TextEditorOverlay::addOverlaySelection(const QTextCursor &cursor, const QColor &fg, const QColor &bg, - uint overlaySelectionFlags) + uint overlaySelectionFlags, + int verticalBlockSelection) { - addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, overlaySelectionFlags); + addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, overlaySelectionFlags, + verticalBlockSelection); } QRect TextEditorOverlay::rect() const @@ -115,7 +120,7 @@ QRect TextEditorOverlay::rect() const } QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, const QTextCursor &end, - const QRect &clip) + const QRect &clip, int verticalBlockSelection) { if (begin.isNull() || end.isNull() || begin.position() > end.position()) return QPainterPath(); @@ -129,6 +134,7 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co ) return QPainterPath(); // nothing of the selection is visible + QTextBlock block = begin.block(); bool inSelection = false; @@ -159,10 +165,12 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co int beginChar = 0; if (!inSelection) { - beginChar = begin.position() - begin.block().position(); + beginChar = begin.positionInBlock(); line = blockLayout->lineForTextPosition(beginChar); inSelection = true; firstOrLastBlock = true; + } else if (verticalBlockSelection) { + beginChar = qMin(block.length()-1, begin.positionInBlock()); } else { while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace()) ++beginChar; @@ -173,10 +181,12 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co int lastLine = blockLayout->lineCount()-1; int endChar = -1; if (block == end.block()) { - endChar = end.position() - end.block().position(); + endChar = end.positionInBlock(); lastLine = blockLayout->lineForTextPosition(endChar).lineNumber(); inSelection = false; firstOrLastBlock = true; + } else if (verticalBlockSelection) { + endChar = qMin(block.length()-1, begin.positionInBlock() + verticalBlockSelection); } else { endChar = block.length(); while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace()) @@ -197,7 +207,7 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co lineRect.setRight(line.cursorToX(endChar)); selection += lineRect.translated(blockGeometry.topLeft()); } - } else { // empty lines + } else if (!verticalBlockSelection){ // empty lines const int emptyLineSelectionSize = 16; if (!firstOrLastBlock && !selection.isEmpty()) { // middle lineRect.setLeft(selection.last().left()); @@ -314,7 +324,8 @@ void TextEditorOverlay::paintSelection(QPainter *painter, || begin.position() > end.position()) return; - QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect()); + QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect(), + selection.m_verticalBlockSelection); painter->save(); QColor penColor = fg; @@ -374,7 +385,8 @@ void TextEditorOverlay::fillSelection(QPainter *painter, if (begin.isNull() || end.isNull() || begin.position() > end.position()) return; - QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect()); + QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect(), + selection.m_verticalBlockSelection); painter->save(); painter->translate(-.5, -.5); diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h index 2730a63d01e05252f2b5786184d8f1453c666b00..7cc9593c6d15a8776783c52a7fe04c9df6c5b6fa 100644 --- a/src/plugins/texteditor/texteditoroverlay.h +++ b/src/plugins/texteditor/texteditoroverlay.h @@ -37,7 +37,8 @@ namespace TextEditor { namespace Internal { struct TEXTEDITOR_EXPORT OverlaySelection { - OverlaySelection():m_fixedLength(-1), m_dropShadow(false), m_expandBegin(false){} + OverlaySelection():m_fixedLength(-1), m_dropShadow(false), + m_expandBegin(false), m_verticalBlockSelection(0){} QTextCursor m_cursor_begin; QTextCursor m_cursor_end; QColor m_fg; @@ -45,6 +46,7 @@ struct TEXTEDITOR_EXPORT OverlaySelection { int m_fixedLength; bool m_dropShadow; bool m_expandBegin; + int m_verticalBlockSelection; }; class TEXTEDITOR_EXPORT TextEditorOverlay : public QObject @@ -90,9 +92,9 @@ public: }; void addOverlaySelection(const QTextCursor &cursor, const QColor &fg, const QColor &bg, - uint overlaySelectionFlags = 0); + uint overlaySelectionFlags = 0, int verticalBlockSelection = 0); void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg, - uint overlaySelectionFlags = 0); + uint overlaySelectionFlags = 0, int verticalBlockSelection = 0); inline bool isEmpty() const { return m_selections.isEmpty(); } @@ -101,7 +103,8 @@ public: bool hasCursorInSelection(const QTextCursor &cursor) const; private: - QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip); + QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip, + int verticalBlockSelection); void paintSelection(QPainter *painter, const OverlaySelection &selection); void fillSelection(QPainter *painter, const OverlaySelection &selection, const QColor &color);