Refactor block selection

Block selection was "broken" when using tabs, or rather
incomplete: It treated tabs as normal characters, which
has shown to be unexpected by people using tabs in code.

The new implementation has a vastly improved find scope
as well. In addition, creating a blog selection with
mouse or keyboard feels a lot more solid now, as the
actual selection is detached from possible valid cursor
positions.

Task-number: QTCREATORBUG-1541
This commit is contained in:
mae
2010-08-05 15:01:20 +02:00
parent 9b338fbbe4
commit 29b073e92e
10 changed files with 510 additions and 225 deletions

View File

@@ -710,12 +710,12 @@ void BaseTextEditor::editorContentsChange(int position, int charsRemoved, int ch
void BaseTextEditor::slotSelectionChanged()
{
bool changed = (d->m_inBlockSelectionMode != d->m_lastEventWasBlockSelectionEvent);
d->m_inBlockSelectionMode = d->m_lastEventWasBlockSelectionEvent;
if (changed || d->m_inBlockSelectionMode)
if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) {
d->m_inBlockSelectionMode = false;
d->m_blockSelection.clear();
viewport()->update();
if (!d->m_inBlockSelectionMode)
d->m_blockSelectionExtraX = 0;
}
if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection())
d->m_selectBlockAnchor = QTextCursor();
@@ -1092,9 +1092,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
d->m_moveLineUndoHack = false;
d->clearVisibleFoldedBlock();
QKeyEvent *original_e = e;
d->m_lastEventWasBlockSelectionEvent = false;
if (e->key() == Qt::Key_Escape) {
if (d->m_snippetOverlay->isVisible()) {
e->accept();
@@ -1182,16 +1179,20 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
} else if (!ro
&& (e == QKeySequence::MoveToStartOfBlock
|| e == QKeySequence::SelectStartOfBlock)){
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
d->m_lastEventWasBlockSelectionEvent = true;
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
e->accept();
return;
}
handleHomeKey(e == QKeySequence::SelectStartOfBlock);
e->accept();
return;
} else if (!ro
&& (e == QKeySequence::MoveToStartOfLine
|| e == QKeySequence::SelectStartOfLine)){
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
d->m_lastEventWasBlockSelectionEvent = true;
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
e->accept();
return;
}
QTextCursor cursor = textCursor();
if (QTextLayout *layout = cursor.block().layout()) {
if (layout->lineForTextPosition(cursor.position() - cursor.block().position()).lineNumber() == 0) {
@@ -1273,36 +1274,23 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
return;
}
// fall through
case Qt::Key_End:
case Qt::Key_Right:
case Qt::Key_Left:
#ifndef Q_WS_MAC
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
d->m_lastEventWasBlockSelectionEvent = true;
if (d->m_inBlockSelectionMode) {
if (e->key() == Qt::Key_Right && textCursor().atBlockEnd()) {
d->m_blockSelectionExtraX++;
viewport()->update();
e->accept();
return;
} else if (e->key() == Qt::Key_Left && d->m_blockSelectionExtraX > 0) {
d->m_blockSelectionExtraX--;
e->accept();
viewport()->update();
return;
}
}
e = new QKeyEvent(
e->type(),
e->key(),
e->modifiers() & ~Qt::AltModifier,
e->text(),
e->isAutoRepeat(),
e->count()
);
int diff_row = 0;
int diff_col = 0;
if (e->key() == Qt::Key_Up)
diff_row = -1;
else if (e->key() == Qt::Key_Down)
diff_row = 1;
else if (e->key() == Qt::Key_Left)
diff_col = -1;
else if (e->key() == Qt::Key_Right)
diff_col = 1;
handleBlockSelection(diff_row, diff_col);
e->accept();
return;
}
#endif
break;
@@ -1395,8 +1383,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
maybeRequestAutoCompletion(e->text().at(0));
}
if (e != original_e)
delete e;
}
void BaseTextEditor::maybeRequestAutoCompletion(const QChar &ch)
@@ -1934,10 +1920,9 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
m_editable(0),
m_actionHack(0),
m_inBlockSelectionMode(false),
m_lastEventWasBlockSelectionEvent(false),
m_blockSelectionExtraX(-1),
m_moveLineUndoHack(false),
m_findScopeVerticalBlockSelection(0),
m_findScopeVerticalBlockSelectionFirstColumn(-1),
m_findScopeVerticalBlockSelectionLastColumn(-1),
m_highlightBlocksTimer(0),
m_requestAutoCompletionRevision(0),
m_requestAutoCompletionPosition(0),
@@ -2158,12 +2143,6 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
int idx = -1;
int l = 1;
int m_findScopeFirstColumn = 0;
if (!m_findScopeStart.isNull() && m_findScopeVerticalBlockSelection) {
m_findScopeFirstColumn = m_findScopeStart.positionInBlock() + 1;
}
while (idx < text.length()) {
idx = m_searchExpr.indexIn(text, idx + l);
if (idx < 0)
@@ -2176,17 +2155,8 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
|| (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
continue;
if (!m_findScopeStart.isNull()) {
if (blockPosition + idx < m_findScopeStart.position()
|| blockPosition + idx + l > m_findScopeEnd.position())
continue;
if (m_findScopeVerticalBlockSelection) {
if (idx < m_findScopeFirstColumn
|| idx + l > m_findScopeFirstColumn + m_findScopeVerticalBlockSelection)
continue;
}
}
if (!q->inFindScope(blockPosition + idx, blockPosition + idx + l))
continue;
overlay->addOverlaySelection(blockPosition + idx,
blockPosition + idx + l,
@@ -2216,6 +2186,7 @@ void BaseTextEditorPrivate::clearBlockSelection()
{
if (m_inBlockSelectionMode) {
m_inBlockSelectionMode = false;
m_blockSelection.clear();
QTextCursor cursor = q->textCursor();
cursor.clearSelection();
q->setTextCursor(cursor);
@@ -2224,68 +2195,77 @@ void BaseTextEditorPrivate::clearBlockSelection()
QString BaseTextEditorPrivate::copyBlockSelection()
{
QString text;
QString selection;
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
return text;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
QTextBlock block = startBlock;
if (!m_inBlockSelectionMode)
return selection;
const TabSettings &ts = q->tabSettings();
QTextBlock block = m_blockSelection.firstBlock.block();
QTextBlock lastBlock = m_blockSelection.lastBlock.block();
for (;;) {
QString text = block.text();
int startOffset = 0;
int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
int endOffset = 0;
int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
text += cursor.selectedText();
if (block == endBlock)
if (startPos == endPos) {
selection += QString(endOffset - startOffset, QLatin1Char(' '));
} else {
if (startOffset < 0)
selection += QString(-startOffset, QLatin1Char(' '));
if (endOffset < 0)
--endPos;
selection += text.mid(startPos, endPos - startPos);
if (endOffset < 0) {
selection += QString(ts.m_tabSize + endOffset, QLatin1Char(' '));
} else if (endOffset > 0) {
selection += QString(endOffset, QLatin1Char(' '));
}
}
if (block == lastBlock)
break;
text += QLatin1Char('\n');
selection += QLatin1Char('\n');
block = block.next();
}
return text;
return selection;
}
void BaseTextEditorPrivate::removeBlockSelection(const QString &text)
{
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
if (!cursor.hasSelection() || !m_inBlockSelectionMode)
return;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
int cursorPosition = cursor.selectionStart();
cursor.clearSelection();
cursor.beginEditBlock();
QTextBlock block = startBlock;
const TabSettings &ts = q->tabSettings();
QTextBlock block = m_blockSelection.firstBlock.block();
QTextBlock lastBlock = m_blockSelection.lastBlock.block();
for (;;) {
QString text = block.text();
int startOffset = 0;
int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
int endOffset = 0;
int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
cursor.setPosition(block.position() + startPos);
cursor.setPosition(block.position() + endPos, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
if (block == endBlock)
if (startOffset < 0)
cursor.insertText(QString(ts.m_tabSize + startOffset, QLatin1Char(' ')));
if (endOffset < 0)
cursor.insertText(QString(-endOffset, QLatin1Char(' ')));
if (block == lastBlock)
break;
block = block.next();
}
cursor.setPosition(start);
cursor.setPosition(cursorPosition);
if (!text.isEmpty())
cursor.insertText(text);
cursor.endEditBlock();
@@ -2448,37 +2428,25 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
}
}
if (!d->m_findScopeStart.isNull()) {
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn < 0) {
TextEditorOverlay *overlay = new TextEditorOverlay(this);
overlay->addOverlaySelection(d->m_findScopeStart.position(),
d->m_findScopeEnd.position(),
d->m_searchScopeFormat.background().color().darker(120),
d->m_searchScopeFormat.background().color(),
TextEditorOverlay::ExpandBegin,
d->m_findScopeVerticalBlockSelection);
TextEditorOverlay::ExpandBegin);
overlay->setAlpha(false);
overlay->paint(&painter, e->rect());
delete overlay;
}
BlockSelectionData *blockSelection = 0;
int blockSelectionIndex = -1;
if (d->m_inBlockSelectionMode
&& context.selections.count() && context.selections.last().cursor == textCursor()) {
blockSelection = new BlockSelectionData;
blockSelection->selectionIndex = context.selections.size()-1;
const QAbstractTextDocumentLayout::Selection &selection = context.selections[blockSelection->selectionIndex];
int start = blockSelection->selectionStart = selection.cursor.selectionStart();
int end = blockSelection->selectionEnd = selection.cursor.selectionEnd();
QTextBlock block = doc->findBlock(start);
int columnA = start - block.position();
block = doc->findBlock(end);
int columnB = end - block.position();
blockSelection->firstColumn = qMin(columnA, columnB);
blockSelection->lastColumn = qMax(columnA, columnB) + d->m_blockSelectionExtraX;
blockSelectionIndex = context.selections.size()-1;
context.selections[blockSelectionIndex].format.clearBackground();
}
QTextBlock visibleCollapsedBlock;
@@ -2517,6 +2485,74 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
} // end first pass
// possible extra pass for the block selection find scope
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn >= 0) {
QTextBlock blockFS = block;
QPointF offsetFS = offset;
while (blockFS.isValid()) {
QRectF r = blockBoundingRect(blockFS).translated(offsetFS);
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
if (blockFS.position() >= d->m_findScopeStart.block().position()
&& blockFS.position() <= d->m_findScopeEnd.block().position()) {
QTextLayout *layout = blockFS.layout();
QString text = blockFS.text();
const TabSettings &ts = tabSettings();
int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
int offset = 0;
int relativePos = ts.positionAtColumn(text,
d->m_findScopeVerticalBlockSelectionFirstColumn,
&offset);
QTextLine line = layout->lineForTextPosition(relativePos);
qreal x = line.cursorToX(relativePos) + offset * spacew;
int eoffset = 0;
int erelativePos = ts.positionAtColumn(text,
d->m_findScopeVerticalBlockSelectionLastColumn,
&eoffset);
QTextLine eline = layout->lineForTextPosition(erelativePos);
qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
QRectF rr = line.naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
if (line.lineNumber() == eline.lineNumber()) {
rr.setRight(r.left() + ex);
}
painter.fillRect(rr, d->m_searchScopeFormat.background());
QColor lineCol = d->m_searchScopeFormat.background().color().darker(120);
QPen pen = painter.pen();
painter.setPen(lineCol);
if (blockFS == d->m_findScopeStart.block())
painter.drawLine(rr.topLeft(), rr.topRight());
if (blockFS == d->m_findScopeEnd.block())
painter.drawLine(rr.bottomLeft(), rr.bottomRight());
painter.drawLine(rr.topLeft(), rr.bottomLeft());
painter.drawLine(rr.topRight(), rr.bottomRight());
painter.setPen(pen);
}
}
offsetFS.ry() += r.height();
if (offsetFS.y() > viewportRect.height())
break;
blockFS = blockFS.next();
if (!blockFS.isVisible()) {
// invisible blocks do have zero line count
blockFS = doc->findBlockByLineNumber(blockFS.firstLineNumber());
}
}
}
d->m_searchResultOverlay->fill(&painter,
d->m_searchResultFormat.background().color(),
e->rect());
@@ -2565,9 +2601,11 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
o.start = selStart;
o.length = selEnd - selStart;
o.format = range.format;
if (blockSelection && blockSelection->selectionIndex == i) {
o.start = qMin(blockSelection->firstColumn, bllen-1);
o.length = qMin(blockSelection->lastColumn, bllen-1) - o.start;
if (i == blockSelectionIndex) {
QString text = block.text();
const TabSettings &ts = tabSettings();
o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn);
o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn) - o.start;
}
if ((hasMainSelection && i == context.selections.size()-1)
|| (o.format.foreground().style() == Qt::NoBrush
@@ -2609,6 +2647,62 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
painter.fillRect(rr, color);
}
QRectF blockSelectionCursorRect;
if (d->m_inBlockSelectionMode
&& block.position() >= d->m_blockSelection.firstBlock.block().position()
&& block.position() <= d->m_blockSelection.lastBlock.block().position()) {
QString text = block.text();
const TabSettings &ts = tabSettings();
int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
int offset = 0;
int relativePos = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn, &offset);
QTextLine line = layout->lineForTextPosition(relativePos);
qreal x = line.cursorToX(relativePos) + offset * spacew;
int eoffset = 0;
int erelativePos = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn, &eoffset);
QTextLine eline = layout->lineForTextPosition(erelativePos);
qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
QRectF rr = line.naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
if (line.lineNumber() == eline.lineNumber()) {
rr.setRight(r.left() + ex);
}
painter.fillRect(rr, palette().highlight());
if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopLeft
&& block == d->m_blockSelection.firstBlock.block())
|| (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomLeft
&& block == d->m_blockSelection.lastBlock.block())
) {
rr.setRight(rr.left()+2);
blockSelectionCursorRect = rr;
}
for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) {
rr = layout->lineAt(i).naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
painter.fillRect(rr, palette().highlight());
}
rr = eline.naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setRight(r.left() + ex);
if (line.lineNumber() != eline.lineNumber())
painter.fillRect(rr, palette().highlight());
if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopRight
&& block == d->m_blockSelection.firstBlock.block())
|| (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomRight
&& block == d->m_blockSelection.lastBlock.block())) {
rr.setLeft(rr.right()-2);
blockSelectionCursorRect = rr;
}
}
bool drawCursor = ((editable || true) // we want the cursor in read-only mode
&& context.cursorPosition >= blpos
&& context.cursorPosition < blpos + bllen);
@@ -2665,6 +2759,12 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
cursor_cpos = cpos;
cursor_pen = painter.pen();
}
#ifndef Q_WS_MAC // no visible cursor on mac
if (blockSelectionCursorRect.isValid())
painter.fillRect(blockSelectionCursorRect, palette().text());
#endif
}
offset.ry() += r.height();
@@ -2693,8 +2793,6 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
//end QPlainTextEdit::paintEvent()
delete blockSelection;
offset = contentOffset();
block = firstVisibleBlock();
@@ -2822,22 +2920,27 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
}
if (d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
// draw the overlays, but only if we do not have a find scope, otherwise the
// view becomes too noisy.
if (d->m_findScopeStart.isNull()) {
if (d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
if (d->m_snippetOverlay->isVisible())
d->m_snippetOverlay->paint(&painter, e->rect());
if (d->m_snippetOverlay->isVisible())
d->m_snippetOverlay->paint(&painter, e->rect());
if (!d->m_refactorOverlay->isEmpty())
d->m_refactorOverlay->paint(&painter, e->rect());
}
if (!d->m_searchResultOverlay->isEmpty()) {
d->m_searchResultOverlay->paint(&painter, e->rect());
d->m_searchResultOverlay->clear();
}
if (!d->m_refactorOverlay->isEmpty())
d->m_refactorOverlay->paint(&painter, e->rect());
// draw the cursor last, on top of everything
if (cursor_layout) {
if (cursor_layout && !d->m_inBlockSelectionMode) {
painter.setPen(cursor_pen);
cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
}
@@ -2897,6 +3000,7 @@ void BaseTextEditor::drawCollapsedBlockPopup(QPainter &painter,
layout->draw(&painter, offset, selections, clip);
b.setVisible(false); // restore previous state
b.setLineCount(0); // restore 0 line count for invisible block
offset.ry() += r.height();
b = b.next();
}
@@ -3421,8 +3525,6 @@ void BaseTextEditorPrivate::clearVisibleFoldedBlock()
void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
{
d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier);
updateLink(e);
if (e->buttons() == Qt::NoButton) {
@@ -3447,12 +3549,23 @@ void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
}
} else {
QPlainTextEdit::mouseMoveEvent(e);
}
if (d->m_lastEventWasBlockSelectionEvent && d->m_inBlockSelectionMode) {
if (textCursor().atBlockEnd()) {
d->m_blockSelectionExtraX = qMax(0, e->pos().x() - cursorRect().center().x()) / fontMetrics().averageCharWidth();
} else {
d->m_blockSelectionExtraX = 0;
if (e->modifiers() & Qt::AltModifier) {
if (!d->m_inBlockSelectionMode) {
d->m_blockSelection.fromSelection(tabSettings(), textCursor());
d->m_inBlockSelectionMode = true;
} else {
QTextCursor cursor = textCursor();
// get visual column
int column = tabSettings().columnAt(cursor.block().text(), cursor.positionInBlock());
if (cursor.positionInBlock() == cursor.block().length()-1) {
column += (e->pos().x() - cursorRect().center().x())/fontMetrics().width(QChar(QLatin1Char(' ')));
}
d->m_blockSelection.moveAnchor(cursor.blockNumber(), column);
setTextCursor(d->m_blockSelection.selection(tabSettings()));
viewport()->update();
}
}
}
if (viewport()->cursor().shape() == Qt::BlankCursor)
@@ -4306,16 +4419,18 @@ void BaseTextEditor::highlightSearchResults(const QString &txt, Find::FindFlags
d->m_delayedUpdateTimer->start(10);
}
int BaseTextEditor::verticalBlockSelection() const
int BaseTextEditor::verticalBlockSelectionFirstColumn() const
{
if (!d->m_inBlockSelectionMode)
return 0;
QTextCursor b = textCursor();
QTextCursor e = b;
b.setPosition(b.selectionStart());
e.setPosition(e.selectionEnd());
if (d->m_inBlockSelectionMode)
return d->m_blockSelection.firstVisualColumn;
return -1;
}
return qAbs(b.positionInBlock() - e.positionInBlock()) + d->m_blockSelectionExtraX;
int BaseTextEditor::verticalBlockSelectionLastColumn() const
{
if (d->m_inBlockSelectionMode)
return d->m_blockSelection.lastVisualColumn;
return -1;
}
QRegion BaseTextEditor::translatedLineRegion(int lineStart, int lineEnd) const
@@ -4337,12 +4452,18 @@ QRegion BaseTextEditor::translatedLineRegion(int lineStart, int lineEnd) const
return region;
}
void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection)
void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end,
int verticalBlockSelectionFirstColumn,
int verticalBlockSelectionLastColumn)
{
if (start != d->m_findScopeStart || end != d->m_findScopeEnd) {
if (start != d->m_findScopeStart
|| end != d->m_findScopeEnd
|| verticalBlockSelectionFirstColumn != d->m_findScopeVerticalBlockSelectionFirstColumn
|| verticalBlockSelectionLastColumn != d->m_findScopeVerticalBlockSelectionLastColumn) {
d->m_findScopeStart = start;
d->m_findScopeEnd = end;
d->m_findScopeVerticalBlockSelection = verticalBlockSelection;
d->m_findScopeVerticalBlockSelectionFirstColumn = verticalBlockSelectionFirstColumn;
d->m_findScopeVerticalBlockSelectionLastColumn = verticalBlockSelectionLastColumn;
viewport()->update();
}
}
@@ -5247,21 +5368,27 @@ void BaseTextEditor::insertFromMimeData(const QMimeData *source)
QStringList lines = text.split(QLatin1Char('\n'));
QTextCursor cursor = textCursor();
cursor.beginEditBlock();
const TabSettings &ts = d->m_document->tabSettings();
int initialCursorPosition = cursor.position();
int column = cursor.position() - cursor.block().position();
int column = ts.columnAt(cursor.block().text(), cursor.positionInBlock());
cursor.insertText(lines.first());
for (int i = 1; i < lines.count(); ++i) {
QTextBlock next = cursor.block().next();
if (next.isValid()) {
cursor.setPosition(next.position() + qMin(column, next.length()-1));
cursor.setPosition(next.position());
} else {
cursor.movePosition(QTextCursor::EndOfBlock);
cursor.insertBlock();
}
int actualColumn = cursor.position() - cursor.block().position();
if (actualColumn < column)
cursor.insertText(QString(column - actualColumn, QLatin1Char(' ')));
int offset = 0;
int position = ts.positionAtColumn(cursor.block().text(), column, &offset);
cursor.setPosition(cursor.block().position() + position);
if (offset < 0) {
cursor.deleteChar();
cursor.insertText(QString(-offset, QLatin1Char(' ')));
} else {
cursor.insertText(QString(offset, QLatin1Char(' ')));
}
cursor.insertText(lines.at(i));
}
cursor.setPosition(initialCursorPosition);
@@ -5359,8 +5486,8 @@ BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor)
BaseTextFind *baseTextFind = new BaseTextFind(editor);
connect(baseTextFind, SIGNAL(highlightAll(QString, Find::FindFlags)),
editor, SLOT(highlightSearchResults(QString, Find::FindFlags)));
connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, QTextCursor, int)),
editor, SLOT(setFindScope(QTextCursor, QTextCursor, int)));
connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, QTextCursor, int, int)),
editor, SLOT(setFindScope(QTextCursor, QTextCursor, int, int)));
aggregate->add(baseTextFind);
aggregate->add(editor);
@@ -5524,3 +5651,125 @@ void BaseTextEditor::doFoo() {
setRefactorMarkers(markers);
#endif
}
void Internal::BaseTextBlockSelection::moveAnchor(int blockNumber, int visualColumn)
{
if (visualColumn >= 0) {
if (anchor % 2) {
lastVisualColumn = visualColumn;
if (lastVisualColumn < firstVisualColumn) {
qSwap(firstVisualColumn, lastVisualColumn);
anchor = (Anchor) (anchor - 1);
}
} else {
firstVisualColumn = visualColumn;
if (firstVisualColumn > lastVisualColumn) {
qSwap(firstVisualColumn, lastVisualColumn);
anchor = (Anchor) (anchor + 1);
}
}
}
if (blockNumber >= 0 && blockNumber < firstBlock.document()->blockCount()) {
if (anchor <= TopRight) {
firstBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
if (firstBlock.blockNumber() > lastBlock.blockNumber()) {
qSwap(firstBlock, lastBlock);
anchor = (Anchor) (anchor + 2);
}
} else {
lastBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
if (lastBlock.blockNumber() < firstBlock.blockNumber()) {
qSwap(firstBlock, lastBlock);
anchor = (Anchor) (anchor - 2);
}
}
}
firstBlock.movePosition(QTextCursor::StartOfBlock);
lastBlock.movePosition(QTextCursor::EndOfBlock);
}
QTextCursor Internal::BaseTextBlockSelection::selection(const TabSettings &ts) const
{
QTextCursor cursor = firstBlock;
if (anchor <= TopRight) {
cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn));
cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn),
QTextCursor::KeepAnchor);
} else {
cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn));
cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn),
QTextCursor::KeepAnchor);
}
return cursor;
}
void Internal::BaseTextBlockSelection::fromSelection(const TabSettings &ts, const QTextCursor &selection)
{
firstBlock = selection;
firstBlock.setPosition(selection.selectionStart());
firstVisualColumn = ts.columnAt(firstBlock.block().text(), firstBlock.positionInBlock());
lastBlock = selection;
lastBlock.setPosition(selection.selectionEnd());
lastVisualColumn = ts.columnAt(lastBlock.block().text(), lastBlock.positionInBlock());
if (selection.anchor() > selection.position())
anchor = TopLeft;
else
anchor = BottomRight;
firstBlock.movePosition(QTextCursor::StartOfBlock);
lastBlock.movePosition(QTextCursor::EndOfBlock);
}
bool BaseTextEditor::inFindScope(const QTextCursor &cursor)
{
if (cursor.isNull())
return false;
return inFindScope(cursor.selectionStart(), cursor.selectionEnd());
}
bool BaseTextEditor::inFindScope(int selectionStart, int selectionEnd)
{
if (d->m_findScopeStart.isNull())
return true; // no scope, everything is included
if (selectionStart < d->m_findScopeStart.position())
return false;
if (selectionEnd > d->m_findScopeEnd.position())
return false;
if (d->m_findScopeVerticalBlockSelectionFirstColumn < 0)
return true;
QTextBlock block = document()->findBlock(selectionStart);
if (block != document()->findBlock(selectionEnd))
return false;
QString text = block.text();
const TabSettings &ts = tabSettings();
int startPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionFirstColumn);
int endPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionLastColumn);
if (selectionStart - block.position() < startPosition)
return false;
if (selectionEnd - block.position() > endPosition)
return false;
return true;
}
void BaseTextEditor::handleBlockSelection(int diff_row, int diff_col)
{
if (!d->m_inBlockSelectionMode) {
d->m_blockSelection.fromSelection(tabSettings(), textCursor());
d->m_inBlockSelectionMode = true;
}
d->m_blockSelection.moveAnchor(d->m_blockSelection.anchorBlockNumber() + diff_row,
d->m_blockSelection.anchorColumnNumber() + diff_col);
setTextCursor(d->m_blockSelection.selection(tabSettings()));
viewport()->update();
// ### TODO ensure horizontal visibility
// const bool rtl = q->isRightToLeft();
// if (cr.left() < visible.left() || cr.right() > visible.right()) {
// int x = cr.center().x() + horizontalOffset() - visible.width()/2;
// hbar->setValue(rtl ? hbar->maximum() - x : x);
// }
}