diff options
author | David Schulz <david.schulz@digia.com> | 2014-06-16 14:20:36 +0200 |
---|---|---|
committer | hjk <hjk121@nokiamail.com> | 2014-06-20 13:16:54 +0200 |
commit | 6e9b7240066e3ec9f5798136bee0dce3a472716e (patch) | |
tree | da76d2994163fd6f768c168008ad375f3e94ecc2 | |
parent | ce324b8d28c51188672dc48d70d6e21a0adc8610 (diff) | |
download | qt-creator-6e9b7240066e3ec9f5798136bee0dce3a472716e.tar.gz |
Editor: Blockselection rewrite.
Also adding the possibility to insert text into the blockselection.
Task-number: QTCREATORBUG-7773
Change-Id: I7a47a1d630f769a8253ee1a2f21057820ea170d5
Reviewed-by: Lukas Holecek <hluk@email.cz>
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
-rw-r--r-- | src/plugins/fakevim/fakevimhandler.cpp | 29 | ||||
-rw-r--r-- | src/plugins/fakevim/fakevimhandler.h | 4 | ||||
-rw-r--r-- | src/plugins/fakevim/fakevimplugin.cpp | 35 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor.cpp | 835 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor.h | 13 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor_p.h | 56 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor_test.cpp | 462 | ||||
-rw-r--r-- | src/plugins/texteditor/codeassist/codeassistant.cpp | 3 | ||||
-rw-r--r-- | src/plugins/texteditor/tabsettings.cpp | 12 | ||||
-rw-r--r-- | src/plugins/texteditor/tabsettings.h | 1 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditorplugin.h | 25 |
11 files changed, 1063 insertions, 412 deletions
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 650564da4c..628c29fd37 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -1800,12 +1800,19 @@ public: } // Set cursor in text editor widget. void commitCursor() { - if (editor()) - EDITOR(setTextCursor(m_cursor)); + if (isVisualBlockMode()) { + emit q->requestSetBlockSelection(m_cursor); + } else { + emit q->requestDisableBlockSelection(); + if (editor()) + EDITOR(setTextCursor(m_cursor)); + } } // Restore cursor from editor widget. void pullCursor() { - if (editor()) + if (isVisualBlockMode()) + q->requestBlockSelection(&m_cursor); + else if (editor()) m_cursor = EDITOR(textCursor()); } @@ -2500,14 +2507,12 @@ void FakeVimHandler::Private::exportSelection() const int col2 = pos - document()->findBlock(pos).position(); if (col1 > col2) ++anc; - else if (!atEndOfLine()) + else if (!atBlockEnd()) ++pos; // FIXME: After '$' command (i.e. m_visualTargetColumn == -1), end of selected lines // should be selected. setAnchorAndPosition(anc, pos); commitCursor(); - emit q->requestSetBlockSelection(false); - emit q->requestSetBlockSelection(true); } else if (g.visualMode == VisualLineMode) { const int posLine = lineForPosition(pos); const int ancLine = lineForPosition(anc); @@ -2642,9 +2647,6 @@ void FakeVimHandler::Private::ensureCursorVisible() void FakeVimHandler::Private::importSelection() { - bool hasBlock = false; - emit q->requestHasBlockSelection(&hasBlock); - if (position() == m_oldExternalPosition && anchor() == m_oldExternalAnchor) { // Undo drawing correction. @@ -6867,8 +6869,6 @@ void FakeVimHandler::Private::scrollToLine(int line) EDITOR(ensureCursorVisible()); EDITOR(setTextCursor(tc)); - if (isVisualBlockMode()) - emit q->requestSetBlockSelection(true); m_firstVisibleLine = line; } @@ -7388,15 +7388,11 @@ bool FakeVimHandler::Private::passEventToEditor(QEvent &event) { removeEventFilter(); - commitCursor(); - EDITOR(setOverwriteMode(false)); - emit q->requestSetBlockSelection(false); + commitCursor(); bool accepted = QApplication::sendEvent(editor(), &event); if (!m_textedit && !m_plaintextedit) return false; - if (isVisualBlockMode()) - emit q->requestSetBlockSelection(true); updateCursorShape(); if (accepted) @@ -7567,7 +7563,6 @@ void FakeVimHandler::Private::toggleVisualMode(VisualMode visualMode) if (visualMode == g.visualMode) { leaveVisualMode(); } else { - emit q->requestSetBlockSelection(false); m_positionPastEnd = false; m_anchorPastEnd = false; g.visualMode = visualMode; diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 408b1c3cc3..e2631778ea 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -148,7 +148,9 @@ signals: void findRequested(bool reverse); void findNextRequested(bool reverse); void handleExCommandRequested(bool *handled, const ExCommand &cmd); - void requestSetBlockSelection(bool on); + void requestDisableBlockSelection(); + void requestSetBlockSelection(const QTextCursor&); + void requestBlockSelection(QTextCursor*); void requestHasBlockSelection(bool *on); void foldToggle(int depth); void foldAll(bool fold); diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 851a8eabbf..1440f17fc5 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -1018,7 +1018,9 @@ private slots: void jumpToGlobalMark(QChar mark, bool backTickMode, const QString &fileName); void showSettingsDialog(); void maybeReadVimRc(); - void setBlockSelection(bool); + void disableBlockSelection(); + void setBlockSelection(const QTextCursor&); + void blockSelection(QTextCursor *); void hasBlockSelection(bool*); void setShowRelativeLineNumbers(const QVariant &value); @@ -1767,8 +1769,12 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) SLOT(indentRegion(int,int,QChar))); connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)), SLOT(checkForElectricCharacter(bool*,QChar)), Qt::DirectConnection); - connect(handler, SIGNAL(requestSetBlockSelection(bool)), - SLOT(setBlockSelection(bool))); + connect(handler, SIGNAL(requestDisableBlockSelection()), + SLOT(disableBlockSelection())); + connect(handler, SIGNAL(requestSetBlockSelection(QTextCursor)), + SLOT(setBlockSelection(QTextCursor))); + connect(handler, SIGNAL(requestBlockSelection(QTextCursor*)), + SLOT(blockSelection(QTextCursor*)), Qt::DirectConnection); connect(handler, SIGNAL(requestHasBlockSelection(bool*)), SLOT(hasBlockSelection(bool*)), Qt::DirectConnection); connect(handler, SIGNAL(completionRequested()), @@ -1864,13 +1870,32 @@ void FakeVimPluginPrivate::triggerSimpleCompletions(const QString &needle, bool m_wordProvider->setActive(needle, forward, qobject_cast<FakeVimHandler *>(sender())); } -void FakeVimPluginPrivate::setBlockSelection(bool on) +void FakeVimPluginPrivate::disableBlockSelection() { FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()); if (!handler) return; if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget())) - bt->setBlockSelection(on); + bt->setBlockSelection(false); +} + +void FakeVimPluginPrivate::setBlockSelection(const QTextCursor &cursor) +{ + FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()); + if (!handler) + return; + if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget())) + bt->setBlockSelection(cursor); +} + +void FakeVimPluginPrivate::blockSelection(QTextCursor *cursor) +{ + FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()); + if (!handler) + return; + if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget())) + if (cursor) + *cursor = bt->blockSelection(); } void FakeVimPluginPrivate::hasBlockSelection(bool *on) diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 37b90868e2..6c6b70b972 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -69,6 +69,7 @@ #include <QCoreApplication> #include <QTextCodec> #include <QDebug> +#include <QTime> #include <QTimer> #include <QTimeLine> #include <QMimeData> @@ -195,7 +196,6 @@ QString BaseTextEditorWidget::convertToPlainText(const QString &txt) } static const char kTextBlockMimeType[] = "application/vnd.qtcreator.blocktext"; -static const char kVerticalTextBlockMimeType[] = "application/vnd.qtcreator.vblocktext"; BaseTextEditorWidget::BaseTextEditorWidget(QWidget *parent) : QPlainTextEdit(parent) @@ -540,6 +540,22 @@ QString BaseTextEditorWidget::msgTextTooLarge(quint64 size) arg(size >> 20); } +void BaseTextEditorWidget::insertPlainText(const QString &text) +{ + if (d->m_inBlockSelectionMode) + d->insertIntoBlockSelection(text); + else + QPlainTextEdit::insertPlainText(text); +} + +QString BaseTextEditorWidget::selectedText() const +{ + if (d->m_inBlockSelectionMode) + return d->copyBlockSelection(); + else + return textCursor().selectedText(); +} + void BaseTextEditorWidget::updateCannotDecodeInfo() { setReadOnly(d->m_document->hasDecodingError()); @@ -651,15 +667,8 @@ void BaseTextEditorWidget::editorContentsChange(int position, int charsRemoved, void BaseTextEditorWidget::slotSelectionChanged() { - if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) { - d->m_inBlockSelectionMode = false; - d->m_blockSelection.clear(); - viewport()->update(); - } - - if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection()) + if (!textCursor().hasSelection() && !d->m_selectBlockAnchor.isNull()) d->m_selectBlockAnchor = QTextCursor(); - // Clear any link which might be showing when the selection changes clearLink(); } @@ -976,6 +985,8 @@ void BaseTextEditorWidget::insertLineAbove() void BaseTextEditorWidget::insertLineBelow() { + if (d->m_inBlockSelectionMode) + d->disableBlockSelection(false); QTextCursor cursor = textCursor(); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); @@ -1015,6 +1026,20 @@ void BaseTextEditorWidget::unindent() setTextCursor(baseTextDocument()->unindent(textCursor())); } +void BaseTextEditorWidget::undo() +{ + if (d->m_inBlockSelectionMode) + d->disableBlockSelection(false); + QPlainTextEdit::undo(); +} + +void BaseTextEditorWidget::redo() +{ + if (d->m_inBlockSelectionMode) + d->disableBlockSelection(false); + QPlainTextEdit::redo(); +} + void BaseTextEditorWidget::openLinkUnderCursor() { const bool openInNextSplit = alwaysOpenLinksInNextSplit(); @@ -1049,6 +1074,8 @@ void BaseTextEditorWidget::moveLineUpDown(bool up) bool hasSelection = cursor.hasSelection(); if (hasSelection) { + if (d->m_inBlockSelectionMode) + d->disableBlockSelection(true); move.setPosition(cursor.selectionStart()); move.movePosition(QTextCursor::StartOfBlock); move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor); @@ -1565,24 +1592,24 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) bool ro = isReadOnly(); const bool inOverwriteMode = overwriteMode(); - if (d->m_inBlockSelectionMode) { + if (!ro && d->m_inBlockSelectionMode) { if (e == QKeySequence::Cut) { - if (!ro) { - cut(); - e->accept(); - return; - } + cut(); + e->accept(); + return; } else if (e == QKeySequence::Delete || e->key() == Qt::Key_Backspace) { - if (!ro) { - d->removeBlockSelection(); - e->accept(); - return; + if (d->m_blockSelection.positionColumn == d->m_blockSelection.anchorColumn) { + if (e == QKeySequence::Delete) + ++d->m_blockSelection.positionColumn; + else if (d->m_blockSelection.positionColumn > 0) + --d->m_blockSelection.positionColumn; } + d->removeBlockSelection(); + e->accept(); + return; } else if (e == QKeySequence::Paste) { - if (!ro) { - d->removeBlockSelection(); - // continue - } + d->removeBlockSelection(); + // continue } } @@ -1590,6 +1617,11 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) if (!ro && (e == QKeySequence::InsertParagraphSeparator || (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator))) { + if (d->m_inBlockSelectionMode) { + d->disableBlockSelection(false); + e->accept(); + return; + } if (d->m_snippetOverlay->isVisible()) { e->accept(); d->m_snippetOverlay->hide(); @@ -1602,8 +1634,6 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) } QTextCursor cursor = textCursor(); - if (d->m_inBlockSelectionMode) - cursor.clearSelection(); const TabSettings &ts = d->m_document->tabSettings(); const TypingSettings &tps = d->m_document->typingSettings(); cursor.beginEditBlock(); @@ -1706,6 +1736,19 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); c.removeSelectedText(); return; + } else if (!ro && (e == QKeySequence::MoveToNextPage || e == QKeySequence::MoveToPreviousPage) + && d->m_inBlockSelectionMode) { + d->disableBlockSelection(false); + QPlainTextEdit::keyPressEvent(e); + return; + } else if (!ro && (e == QKeySequence::SelectNextPage || e == QKeySequence::SelectPreviousPage) + && d->m_inBlockSelectionMode) { + QPlainTextEdit::keyPressEvent(e); + d->m_blockSelection.positionBlock = QPlainTextEdit::textCursor().blockNumber(); + setTextCursor(d->m_blockSelection.selection(d->m_document.data()), true); + viewport()->update(); + e->accept(); + return; } else switch (e->key()) { @@ -1763,26 +1806,34 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) break; if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) { - 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); + if (!d->m_inBlockSelectionMode) + d->enableBlockSelection(textCursor()); + switch (e->key()) { + case Qt::Key_Up: + if (d->m_blockSelection.positionBlock > 0) + --d->m_blockSelection.positionBlock; + break; + case Qt::Key_Down: + if (d->m_blockSelection.positionBlock < document()->blockCount() - 1) + ++d->m_blockSelection.positionBlock; + break; + case Qt::Key_Left: + if (d->m_blockSelection.positionColumn > 0) + --d->m_blockSelection.positionColumn; + break; + case Qt::Key_Right: + ++d->m_blockSelection.positionColumn; + break; + default: + break; + } + d->resetCursorFlashTimer(); + setTextCursor(d->m_blockSelection.selection(d->m_document.data()), true); + viewport()->update(); e->accept(); return; - } else { - // leave block selection mode - if (d->m_inBlockSelectionMode) { - d->m_inBlockSelectionMode = false; - d->m_blockSelection.clear(); - viewport()->update(); - } + } else if (d->m_inBlockSelectionMode) { // leave block selection mode + d->disableBlockSelection(); } break; case Qt::Key_Insert: @@ -1811,8 +1862,8 @@ void BaseTextEditorWidget::keyPressEvent(QKeyEvent *e) const QString eventText = e->text(); if (!ro && d->m_inBlockSelectionMode) { - if (!isPrintableText(eventText)) { - d->removeBlockSelection(eventText); + if (isPrintableText(eventText)) { + d->insertIntoBlockSelection(eventText); goto skip_event; } } @@ -1964,10 +2015,12 @@ void BaseTextEditorWidget::universalHelper() // give it proper scrutiny before pushing it onto others. } -void BaseTextEditorWidget::setTextCursor(const QTextCursor &cursor) +void BaseTextEditorWidget::setTextCursor(const QTextCursor &cursor, bool keepBlockSelection) { // workaround for QTextControl bug bool selectionChange = cursor.hasSelection() || textCursor().hasSelection(); + if (!keepBlockSelection && d->m_inBlockSelectionMode) + d->disableBlockSelection(false); QTextCursor c = cursor; c.setVisualNavigation(true); QPlainTextEdit::setTextCursor(c); @@ -1975,6 +2028,11 @@ void BaseTextEditorWidget::setTextCursor(const QTextCursor &cursor) slotSelectionChanged(); } +void BaseTextEditorWidget::setTextCursor(const QTextCursor &cursor) +{ + setTextCursor(cursor, false); +} + void BaseTextEditorWidget::gotoLine(int line, int column, bool centerLine) { d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history @@ -2062,6 +2120,16 @@ bool BaseTextEditorWidget::event(QEvent *e) return QPlainTextEdit::event(e); } +void BaseTextEditorWidget::inputMethodEvent(QInputMethodEvent *e) +{ + if (d->m_inBlockSelectionMode) { + if (!e->commitString().isEmpty()) + d->insertIntoBlockSelection(e->commitString()); + return; + } + QPlainTextEdit::inputMethodEvent(e); +} + void BaseTextEditorWidget::documentAboutToBeReloaded() { //memorize cursor position @@ -2660,26 +2728,16 @@ void BaseTextEditorWidgetPrivate::highlightSearchResults(const QTextBlock &block } } -void BaseTextEditorWidgetPrivate::clearBlockSelection() -{ - if (m_inBlockSelectionMode) { - m_inBlockSelectionMode = false; - m_blockSelection.clear(); - QTextCursor cursor = q->textCursor(); - cursor.clearSelection(); - q->setTextCursor(cursor); - } -} - QString BaseTextEditorWidgetPrivate::copyBlockSelection() { - QString selection; - QTextCursor cursor = q->textCursor(); if (!m_inBlockSelectionMode) - return selection; + return QString(); + QString selection; const TabSettings &ts = m_document->tabSettings(); - QTextBlock block = m_blockSelection.firstBlock.block(); - QTextBlock lastBlock = m_blockSelection.lastBlock.block(); + QTextBlock block = + m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber()); + const QTextBlock &lastBlock = + m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber()); bool textInserted = false; for (;;) { if (q->selectionVisible(block.blockNumber())) { @@ -2689,9 +2747,9 @@ QString BaseTextEditorWidgetPrivate::copyBlockSelection() QString text = block.text(); int startOffset = 0; - int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset); + int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn(), &startOffset); int endOffset = 0; - int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset); + int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn(), &endOffset); if (startPos == endPos) { selection += QString(endOffset - startOffset, QLatin1Char(' ')); @@ -2715,45 +2773,202 @@ QString BaseTextEditorWidgetPrivate::copyBlockSelection() return selection; } -void BaseTextEditorWidgetPrivate::removeBlockSelection(const QString &text) +void BaseTextEditorWidgetPrivate::setCursorToColumn(QTextCursor &cursor, int column, QTextCursor::MoveMode moveMode) +{ + const TabSettings &ts = m_document->tabSettings(); + int offset = 0; + const int cursorPosition = cursor.position(); + const int pos = ts.positionAtColumn(cursor.block().text(), column, &offset); + cursor.setPosition(cursor.block().position() + pos, offset == 0 ? moveMode : QTextCursor::MoveAnchor); + if (offset == 0) + return; + if (offset < 0) { + // the column is inside a tab so it is replaced with spaces + cursor.setPosition(cursor.block().position() + pos - 1, QTextCursor::KeepAnchor); + cursor.insertText(ts.indentationString( + ts.columnAt(cursor.block().text(), pos - 1), + ts.columnAt(cursor.block().text(), pos), cursor.block())); + } else { + // column is behind the last position + cursor.insertText(ts.indentationString(ts.columnAt(cursor.block().text(), pos), + column, cursor.block())); + } + if (moveMode == QTextCursor::KeepAnchor) + cursor.setPosition(cursorPosition); + cursor.setPosition(cursor.block().position() + ts.positionAtColumn( + cursor.block().text(), column), moveMode); +} + +void BaseTextEditorWidgetPrivate::insertIntoBlockSelection(const QString &text) +{ + // TODO: add autocompleter support + QTextCursor cursor = q->textCursor(); + cursor.beginEditBlock(); + + if (q->overwriteMode() && m_blockSelection.lastVisualColumn() == m_blockSelection.positionColumn) + ++m_blockSelection.positionColumn; + + if (m_blockSelection.positionColumn != m_blockSelection.anchorColumn) { + removeBlockSelection(); + if (!m_inBlockSelectionMode) { + q->insertPlainText(text); + cursor.endEditBlock(); + return; + } + } + + if (text.isEmpty()) { + cursor.endEditBlock(); + return; + } + + int positionBlock = m_blockSelection.positionBlock; + int anchorBlock = m_blockSelection.anchorBlock; + int column = m_blockSelection.positionColumn; + + const QTextBlock &firstBlock = + m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber()); + QTextBlock block = + m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber()); + + // unify the length of all lines in a multiline text + const int selectionLineCount = m_blockSelection.lastBlockNumber() + - m_blockSelection.firstBlockNumber(); + const int textNewLineCount = text.count(QLatin1Char('\n')) ; + QStringList textLines = text.split(QLatin1Char('\n')); + const TabSettings &ts = m_document->tabSettings(); + int textLength = 0; + const QStringList::const_iterator endLine = textLines.constEnd(); + for (QStringList::const_iterator textLine = textLines.constBegin(); textLine != endLine; ++textLine) + textLength += qMax(0, ts.columnCountForText(*textLine, column) - textLength); + for (QStringList::iterator textLine = textLines.begin(); textLine != endLine; ++textLine) + textLine->append(QString(qMax(0, textLength - ts.columnCountForText(*textLine, column)), QLatin1Char(' '))); + + // insert Text + for (;;) { + // If the number of lines to be inserted equals the number of the selected lines the + // lines of the copy paste buffer are inserted in the corresponding lines of the selection. + // Otherwise the complete buffer is inserted in each of the selected lines. + cursor.setPosition(block.position()); + if (selectionLineCount == textNewLineCount) { + setCursorToColumn(cursor, column); + cursor.insertText(textLines.at(block.blockNumber() + - m_blockSelection.firstBlockNumber())); + } else { + QStringList::const_iterator textLine = textLines.constBegin(); + while (true) { + setCursorToColumn(cursor, column); + cursor.insertText(*textLine); + ++textLine; + if (textLine == endLine) + break; + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.insertText(QLatin1String("\n")); + if (qMax(anchorBlock, positionBlock) == anchorBlock) + ++anchorBlock; + else + ++positionBlock; + } + } + if (block == firstBlock) + break; + block = block.previous(); + } + cursor.endEditBlock(); + + column += textLength; + m_blockSelection.fromPostition(positionBlock, column, anchorBlock, column); + q->setTextCursor(m_blockSelection.selection(m_document.data()), true); +} + +void BaseTextEditorWidgetPrivate::removeBlockSelection() { QTextCursor cursor = q->textCursor(); if (!cursor.hasSelection() || !m_inBlockSelectionMode) return; + const int firstColumn = m_blockSelection.firstVisualColumn(); + const int lastColumn = m_blockSelection.lastVisualColumn(); + if (firstColumn == lastColumn) + return; + const int positionBlock = m_blockSelection.positionBlock; + const int anchorBlock = m_blockSelection.anchorBlock; + int cursorPosition = cursor.selectionStart(); cursor.clearSelection(); cursor.beginEditBlock(); const TabSettings &ts = m_document->tabSettings(); - QTextBlock block = m_blockSelection.firstBlock.block(); - QTextBlock lastBlock = m_blockSelection.lastBlock.block(); + QTextBlock block = m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber()); + const QTextBlock &lastBlock = m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber()); 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() + startPos); - cursor.setPosition(block.position() + endPos, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - - if (startOffset < 0) - cursor.insertText(QString(ts.m_tabSize + startOffset, QLatin1Char(' '))); - if (endOffset < 0) - cursor.insertText(QString(-endOffset, QLatin1Char(' '))); - + const int startPos = ts.positionAtColumn(block.text(), firstColumn, &startOffset); + // removing stuff doesn't make sense if the cursor is behind the code + if (startPos < block.length() - 1 || startOffset < 0) { + cursor.setPosition(block.position()); + setCursorToColumn(cursor, firstColumn); + setCursorToColumn(cursor, lastColumn, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + } if (block == lastBlock) break; block = block.next(); } cursor.setPosition(cursorPosition); - if (!text.isEmpty()) - cursor.insertText(text); cursor.endEditBlock(); + m_blockSelection.fromPostition(positionBlock, firstColumn, anchorBlock, firstColumn); + cursor = m_blockSelection.selection(m_document.data()); + q->setTextCursor(cursor, m_blockSelection.hasSelection()); +} + +void BaseTextEditorWidgetPrivate::enableBlockSelection(const QTextCursor &cursor) +{ + const TabSettings &ts = m_document->tabSettings(); + const QTextBlock &positionTextBlock = cursor.block(); + int positionBlock = positionTextBlock.blockNumber(); + int positionColumn = ts.columnAt(positionTextBlock.text(), + cursor.position() - positionTextBlock.position()); + + const QTextDocument *document = cursor.document(); + const QTextBlock &anchorTextBlock = document->findBlock(cursor.anchor()); + int anchorBlock = anchorTextBlock.blockNumber(); + int anchorColumn = ts.columnAt(anchorTextBlock.text(), + cursor.anchor() - anchorTextBlock.position()); + enableBlockSelection(positionBlock, anchorColumn, anchorBlock, positionColumn); +} + +void BaseTextEditorWidgetPrivate::enableBlockSelection(int positionBlock, int positionColumn, + int anchorBlock, int anchorColumn) +{ + m_blockSelection.fromPostition(positionBlock, anchorColumn, anchorBlock, positionColumn); + resetCursorFlashTimer(); + m_inBlockSelectionMode = true; + q->setTextCursor(m_blockSelection.selection(m_document.data()), true); + q->viewport()->update(); +} + +void BaseTextEditorWidgetPrivate::disableBlockSelection(bool keepSelection) +{ + m_inBlockSelectionMode = false; + m_cursorFlashTimer.stop(); + QTextCursor cursor = m_blockSelection.selection(m_document.data()); + m_blockSelection.clear(); + if (!keepSelection) + cursor.clearSelection(); q->setTextCursor(cursor); + q->viewport()->update(); +} + +void BaseTextEditorWidgetPrivate::resetCursorFlashTimer() +{ + m_cursorVisible = true; + const int flashTime = qApp->cursorFlashTime(); + if (flashTime > 0) { + m_cursorFlashTimer.stop(); + m_cursorFlashTimer.start(flashTime / 2, q); + } } void BaseTextEditorWidgetPrivate::moveCursorVisible(bool ensureVisible) @@ -2812,6 +3027,16 @@ static QColor calcBlendColor(const QColor &baseColor, int level, int count) return blendColors(color80, color90, blendFactor); } +static QTextLayout::FormatRange createBlockCursorCharFormatRange(int pos, const QPalette &palette) +{ + QTextLayout::FormatRange o; + o.start = pos; + o.length = 1; + o.format.setForeground(palette.base()); + o.format.setBackground(palette.text()); + return o; +} + void BaseTextEditorWidget::paintEvent(QPaintEvent *e) { /* @@ -2929,8 +3154,8 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) int blockSelectionIndex = -1; - if (d->m_inBlockSelectionMode - && context.selections.count() && context.selections.last().cursor == textCursor()) { + if (d->m_inBlockSelectionMode && context.selections.count() + && context.selections.last().cursor == textCursor()) { blockSelectionIndex = context.selections.size()-1; context.selections[blockSelectionIndex].format.clearBackground(); } @@ -3123,8 +3348,8 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) if (i == blockSelectionIndex) { QString text = block.text(); const TabSettings &ts = d->m_document->tabSettings(); - o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn); - o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn) - o.start; + 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 @@ -3171,21 +3396,25 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) QRectF blockSelectionCursorRect; if (d->m_inBlockSelectionMode - && block.position() >= d->m_blockSelection.firstBlock.block().position() - && block.position() <= d->m_blockSelection.lastBlock.block().position()) { + && block.blockNumber() >= d->m_blockSelection.firstBlockNumber() + && block.blockNumber() <= d->m_blockSelection.lastBlockNumber()) { QString text = block.text(); const TabSettings &ts = d->m_document->tabSettings(); - qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' ')); + const qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' ')); + const int cursorw = overwriteMode() ? QFontMetrics(font()).width(QLatin1Char(' ')) + : cursorWidth(); 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 relativePos = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn(), + &offset); + const QTextLine line = layout->lineForTextPosition(relativePos); + const 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; + int erelativePos = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn(), + &eoffset); + const QTextLine eline = layout->lineForTextPosition(erelativePos); + const qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew; QRectF rr = line.naturalTextRect(); rr.moveTop(rr.top() + r.top()); @@ -3193,13 +3422,18 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) 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; + if (d->m_cursorVisible + && d->m_blockSelection.firstVisualColumn() + == d->m_blockSelection.positionColumn) { + if (overwriteMode() && offset == 0 + && relativePos < text.length() + && text.at(relativePos) != QLatin1Char('\t') + && text.at(relativePos) != QLatin1Char('\n')) { + selections.append(createBlockCursorCharFormatRange(relativePos, palette())); + } else { + blockSelectionCursorRect = rr; + blockSelectionCursorRect.setRight(rr.left() + cursorw); + } } for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) { rr = layout->lineAt(i).naturalTextRect(); @@ -3213,12 +3447,19 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) 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; + if (d->m_cursorVisible + && d->m_blockSelection.lastVisualColumn() + == d->m_blockSelection.positionColumn) { + if (overwriteMode() && eoffset == 0 + && erelativePos < text.length() + && text.at(erelativePos) != QLatin1Char('\t') + && text.at(erelativePos) != QLatin1Char('\n')) { + selections.append(createBlockCursorCharFormatRange(erelativePos, palette())); + } else { + blockSelectionCursorRect = rr; + blockSelectionCursorRect.setLeft(rr.right()); + blockSelectionCursorRect.setRight(rr.right() + cursorw); + } } } @@ -3227,7 +3468,7 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) && context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen); - bool drawCursorAsBlock = drawCursor && overwriteMode() ; + bool drawCursorAsBlock = drawCursor && overwriteMode() && !d->m_inBlockSelectionMode; if (drawCursorAsBlock) { int relativePos = context.cursorPosition - blpos; @@ -3253,14 +3494,8 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) rr.moveLeft(r.left() + x); rr.setWidth(w); painter.fillRect(rr, palette().text()); - if (doSelection) { - QTextLayout::FormatRange o; - o.start = relativePos; - o.length = 1; - o.format.setForeground(palette().base()); - o.format.setBackground(palette().text()); - selections.append(o); - } + if (doSelection) + selections.append(createBlockCursorCharFormatRange(relativePos, palette())); } @@ -3268,8 +3503,7 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) layout->draw(&painter, offset, selections, er); if ((drawCursor && !drawCursorAsBlock) - || (editable && context.cursorPosition < -1 - && !layout->preeditAreaText().isEmpty())) { + || (editable && context.cursorPosition < -1 && !layout->preeditAreaText().isEmpty())) { int cpos = context.cursorPosition; if (cpos < -1) cpos = layout->preeditAreaPosition() - (cpos + 2); @@ -3281,7 +3515,9 @@ void BaseTextEditorWidget::paintEvent(QPaintEvent *e) cursor_pen = painter.pen(); } - if (!HostOsInfo::isMacHost() && blockSelectionCursorRect.isValid()) + if ((!HostOsInfo::isMacHost() + || d->m_blockSelection.positionColumn == d->m_blockSelection.anchorColumn) + && blockSelectionCursorRect.isValid()) painter.fillRect(blockSelectionCursorRect, palette().text()); } @@ -4021,6 +4257,9 @@ void BaseTextEditorWidget::timerEvent(QTimerEvent *e) d->suggestedVisibleFoldedBlockNumber = -1; d->foldedBlockTimer.stop(); viewport()->update(); + } else if (e->timerId() == d->m_cursorFlashTimer.timerId()) { + d->m_cursorVisible = !d->m_cursorVisible; + viewport()->update(); } QPlainTextEdit::timerEvent(e); } @@ -4067,20 +4306,36 @@ void BaseTextEditorWidget::mouseMoveEvent(QMouseEvent *e) if (e->modifiers() & Qt::AltModifier) { if (!d->m_inBlockSelectionMode) { - d->m_blockSelection.fromSelection(d->m_document->tabSettings(), textCursor()); - d->m_inBlockSelectionMode = true; + if (textCursor().hasSelection()) { + d->enableBlockSelection(textCursor()); + } else { + const QTextCursor &cursor = cursorForPosition(e->pos()); + int column = d->m_document->tabSettings().columnAt( + cursor.block().text(), cursor.positionInBlock()); + if (cursor.positionInBlock() == cursor.block().length()-1) + column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' ')); + int block = cursor.blockNumber(); + if (block == blockCount() - 1) + block += (e->pos().y() - cursorRect().center().y()) / QFontMetricsF(font()).lineSpacing(); + d->enableBlockSelection(block, column, block, column); + } } else { - QTextCursor cursor = textCursor(); + const QTextCursor &cursor = textCursor(); // get visual column int column = d->m_document->tabSettings().columnAt( cursor.block().text(), cursor.positionInBlock()); if (cursor.positionInBlock() == cursor.block().length()-1) - column += (e->pos().x() - cursorRect().center().x())/QFontMetricsF(font()).width(QLatin1Char(' ')); - d->m_blockSelection.moveAnchor(cursor.blockNumber(), column); - setTextCursor(d->m_blockSelection.selection(d->m_document->tabSettings())); + column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' ')); + + d->m_blockSelection.positionBlock = cursor.blockNumber(); + d->m_blockSelection.positionColumn = column; + + setTextCursor(d->m_blockSelection.selection(d->m_document.data()), true); viewport()->update(); } + } else if (d->m_inBlockSelectionMode) { + d->disableBlockSelection(); } } if (viewport()->cursor().shape() == Qt::BlankCursor) @@ -4104,22 +4359,43 @@ static bool handleForwardBackwardMouseButtons(QMouseEvent *e) void BaseTextEditorWidget::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { - d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop - - QTextBlock foldedBlock = foldedBlockAt(e->pos()); - if (foldedBlock.isValid()) { - toggleBlockVisible(foldedBlock); - viewport()->setCursor(Qt::IBeamCursor); - } + if (e->modifiers() == Qt::AltModifier) { + const QTextCursor &cursor = cursorForPosition(e->pos()); + int column = d->m_document->tabSettings().columnAt( + cursor.block().text(), cursor.positionInBlock()); + if (cursor.positionInBlock() == cursor.block().length()-1) + column += (e->pos().x() - cursorRect(cursor).center().x()) / QFontMetricsF(font()).width(QLatin1Char(' ')); + int block = cursor.blockNumber(); + if (block == blockCount() - 1) + block += (e->pos().y() - cursorRect(cursor).center().y()) / QFontMetricsF(font()).lineSpacing(); + if (d->m_inBlockSelectionMode) { + d->m_blockSelection.positionBlock = block; + d->m_blockSelection.positionColumn = column; - RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos()); - if (refactorMarker.isValid()) { - emit refactorMarkerClicked(refactorMarker); + setTextCursor(d->m_blockSelection.selection(d->m_document.data()), true); + viewport()->update(); + } else { + d->enableBlockSelection(block, column, block, column); + } } else { - updateLink(e); + if (d->m_inBlockSelectionMode) + d->disableBlockSelection(false); // just in case, otherwise we might get strange drag and drop + + QTextBlock foldedBlock = foldedBlockAt(e->pos()); + if (foldedBlock.isValid()) { + toggleBlockVisible(foldedBlock); + viewport()->setCursor(Qt::IBeamCursor); + } + + RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos()); + if (refactorMarker.isValid()) { + emit refactorMarkerClicked(refactorMarker); + } else { + updateLink(e); - if (d->m_currentLink.hasValidLinkText()) - d->m_linkPressed = true; + if (d->m_currentLink.hasValidLinkText()) + d->m_linkPressed = true; + } } } else if (e->button() == Qt::RightButton) { int eventCursorPosition = cursorForPosition(e->pos()).position(); @@ -4738,16 +5014,12 @@ void BaseTextEditorWidget::highlightSearchResults(const QString &txt, Core::Find int BaseTextEditorWidget::verticalBlockSelectionFirstColumn() const { - if (d->m_inBlockSelectionMode) - return d->m_blockSelection.firstVisualColumn; - return -1; + return d->m_inBlockSelectionMode ? d->m_blockSelection.firstVisualColumn() : -1; } int BaseTextEditorWidget::verticalBlockSelectionLastColumn() const { - if (d->m_inBlockSelectionMode) - return d->m_blockSelection.lastVisualColumn; - return -1; + return d->m_inBlockSelectionMode ? d->m_blockSelection.lastVisualColumn() : -1; } QRegion BaseTextEditorWidget::translatedLineRegion(int lineStart, int lineEnd) const @@ -5081,7 +5353,7 @@ void BaseTextEditorWidget::copyLine() copy(); if (!prevCursor.hasSelection()) prevCursor.movePosition(QTextCursor::StartOfBlock); - setTextCursor(prevCursor); + setTextCursor(prevCursor, d->m_inBlockSelectionMode); } void BaseTextEditorWidget::deleteLine() @@ -5579,19 +5851,26 @@ void BaseTextEditorWidget::cut() void BaseTextEditorWidget::selectAll() { - d->clearBlockSelection(); + d->disableBlockSelection(); QPlainTextEdit::selectAll(); } void BaseTextEditorWidget::copy() { - if (!textCursor().hasSelection()) + if (!textCursor().hasSelection() || (d->m_inBlockSelectionMode + && d->m_blockSelection.anchorColumn == d->m_blockSelection.positionColumn)) { return; + } QPlainTextEdit::copy(); collectToCircularClipboard(); } +void BaseTextEditorWidget::paste() +{ + QPlainTextEdit::paste(); +} + void BaseTextEditorWidget::collectToCircularClipboard() { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); @@ -5603,13 +5882,6 @@ void BaseTextEditorWidget::collectToCircularClipboard() circularClipBoard->toLastCollect(); } -void BaseTextEditorWidget::paste() -{ - if (d->m_inBlockSelectionMode) - d->removeBlockSelection(); - QPlainTextEdit::paste(); -} - void BaseTextEditorWidget::circularPaste() { CircularClipboard *circularClipBoard = CircularClipboard::instance(); @@ -5636,9 +5908,7 @@ QMimeData *BaseTextEditorWidget::createMimeDataFromSelection() const { if (d->m_inBlockSelectionMode) { QMimeData *mimeData = new QMimeData; - QString text = d->copyBlockSelection(); - mimeData->setData(QLatin1String(kVerticalTextBlockMimeType), text.toUtf8()); - mimeData->setText(text); // for exchangeability + mimeData->setText(d->copyBlockSelection()); return mimeData; } else if (textCursor().hasSelection()) { QTextCursor cursor = textCursor(); @@ -5737,54 +6007,6 @@ void BaseTextEditorWidget::insertFromMimeData(const QMimeData *source) if (isReadOnly()) return; - if (source->hasFormat(QLatin1String(kVerticalTextBlockMimeType))) { - QString text = QString::fromUtf8(source->data(QLatin1String(kVerticalTextBlockMimeType))); - if (text.isEmpty()) - return; - - if (d->m_codeAssistant->hasContext()) - d->m_codeAssistant->destroyContext(); - - QStringList lines = text.split(QLatin1Char('\n')); - QTextCursor cursor = textCursor(); - cursor.beginEditBlock(); - const TabSettings &ts = d->m_document->tabSettings(); - int initialCursorPosition = cursor.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()); - } else { - cursor.movePosition(QTextCursor::EndOfBlock); - cursor.insertBlock(); - } - 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); - cursor.endEditBlock(); - setTextCursor(cursor); - ensureCursorVisible(); - - if (d->m_snippetOverlay->isVisible() && lines.count() > 1) { - d->m_snippetOverlay->hide(); - d->m_snippetOverlay->mangle(); - d->m_snippetOverlay->clear(); - } - - return; - } - QString text = source->text(); if (text.isEmpty()) return; @@ -5792,6 +6014,11 @@ void BaseTextEditorWidget::insertFromMimeData(const QMimeData *source) if (d->m_codeAssistant->hasContext()) d->m_codeAssistant->destroyContext(); + if (d->m_inBlockSelectionMode) { + d->insertIntoBlockSelection(text); + return; + } + if (d->m_snippetOverlay->isVisible() && (text.contains(QLatin1Char('\n')) || text.contains(QLatin1Char('\t')))) { d->m_snippetOverlay->hide(); @@ -5863,10 +6090,7 @@ QMimeData *BaseTextEditorWidget::duplicateMimeData(const QMimeData *source) QMimeData *mimeData = new QMimeData; mimeData->setText(source->text()); mimeData->setHtml(source->html()); - if (source->hasFormat(QLatin1String(kVerticalTextBlockMimeType))) { - mimeData->setData(QLatin1String(kVerticalTextBlockMimeType), - source->data(QLatin1String(kVerticalTextBlockMimeType))); - } else if (source->hasFormat(QLatin1String(kTextBlockMimeType))) { + if (source->hasFormat(QLatin1String(kTextBlockMimeType))) { mimeData->setData(QLatin1String(kTextBlockMimeType), source->data(QLatin1String(kTextBlockMimeType))); } @@ -6031,9 +6255,7 @@ QRect BaseTextEditor::cursorRect(int pos) const QString BaseTextEditor::selectedText() const { - if (m_editorWidget->textCursor().hasSelection()) - return m_editorWidget->textCursor().selectedText(); - return QString(); + return m_editorWidget->selectedText(); } void BaseTextEditor::remove(int length) @@ -6045,8 +6267,7 @@ void BaseTextEditor::remove(int length) void BaseTextEditor::insert(const QString &string) { - QTextCursor tc = m_editorWidget->textCursor(); - tc.insertText(string); + m_editorWidget->insertPlainText(string); } void BaseTextEditor::replace(int length, const QString &string) @@ -6058,6 +6279,7 @@ void BaseTextEditor::replace(int length, const QString &string) void BaseTextEditor::setCursorPosition(int pos) { + m_editorWidget->setBlockSelection(false); QTextCursor tc = m_editorWidget->textCursor(); tc.setPosition(pos); m_editorWidget->setTextCursor(tc); @@ -6065,6 +6287,7 @@ void BaseTextEditor::setCursorPosition(int pos) void BaseTextEditor::select(int toPos) { + m_editorWidget->setBlockSelection(false); QTextCursor tc = m_editorWidget->textCursor(); tc.setPosition(toPos, QTextCursor::KeepAnchor); m_editorWidget->setTextCursor(tc); @@ -6150,81 +6373,76 @@ void BaseTextEditorWidget::doFoo() { #endif } -void BaseTextBlockSelection::moveAnchor(int blockNumber, int visualColumn) +BaseTextBlockSelection::BaseTextBlockSelection(const BaseTextBlockSelection &other) { - 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); - } - } - } + positionBlock = other.positionBlock; + positionColumn = other.positionColumn; + anchorBlock = other.anchorBlock; + anchorColumn = other.anchorColumn; +} - 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); +void BaseTextBlockSelection::clear() +{ + positionColumn = positionBlock = anchorColumn = anchorBlock = 0; } -int BaseTextBlockSelection::position(const TabSettings &ts) const +// returns a cursor which always has the complete selection +QTextCursor BaseTextBlockSelection::selection(const BaseTextDocument *baseTextDocument) const { - const QTextBlock &block = anchor <= TopRight ? lastBlock.block() : firstBlock.block(); - const int column = anchor % 2 ? firstVisualColumn : lastVisualColumn; - return block.position() + ts.positionAtColumn(block.text(), column); + return cursor(baseTextDocument, true); } -QTextCursor BaseTextBlockSelection::selection(const TabSettings &ts) const +// returns a cursor which always has the correct position and anchor +QTextCursor BaseTextBlockSelection::cursor(const BaseTextDocument *baseTextDocument) 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); + return cursor(baseTextDocument, false); +} + +QTextCursor BaseTextBlockSelection::cursor(const BaseTextDocument *baseTextDocument, + bool fullSelection) const +{ + if (!baseTextDocument) + return QTextCursor(); + QTextDocument *document = baseTextDocument->document(); + const TabSettings &ts = baseTextDocument->tabSettings(); + + int selectionAnchorColumn; + int selectionPositionColumn; + if (anchorBlock == positionBlock || !fullSelection) { + selectionAnchorColumn = anchorColumn; + selectionPositionColumn = positionColumn; + } else if (anchorBlock == firstBlockNumber()){ + selectionAnchorColumn = firstVisualColumn(); + selectionPositionColumn = lastVisualColumn(); } 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); + selectionAnchorColumn = lastVisualColumn(); + selectionPositionColumn = firstVisualColumn(); } + + QTextCursor cursor(document); + + const QTextBlock &anchorTextBlock = document->findBlockByNumber(anchorBlock); + const int anchorPosition = anchorTextBlock.position() + + ts.positionAtColumn(anchorTextBlock.text(), selectionAnchorColumn); + + const QTextBlock &positionTextBlock = document->findBlockByNumber(positionBlock); + const int cursorPosition = positionTextBlock.position() + + ts.positionAtColumn(positionTextBlock.text(), selectionPositionColumn); + + cursor.setPosition(anchorPosition); + cursor.setPosition(cursorPosition, QTextCursor::KeepAnchor); return cursor; } -void BaseTextBlockSelection::fromSelection(const TabSettings &ts, const QTextCursor &selection) +void BaseTextBlockSelection::fromPostition(int positionBlock, int positionColumn, + int anchorBlock, int anchorColumn) { - 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); + this->positionBlock = positionBlock; + this->positionColumn = positionColumn; + this->anchorBlock = anchorBlock; + this->anchorColumn = anchorColumn; } + bool BaseTextEditorWidget::inFindScope(const QTextCursor &cursor) { if (cursor.isNull()) @@ -6261,38 +6479,31 @@ void BaseTextEditorWidget::setBlockSelection(bool on) if (d->m_inBlockSelectionMode == on) return; - d->m_inBlockSelectionMode = on; if (on) - d->m_blockSelection.fromSelection(d->m_document->tabSettings(), textCursor()); - viewport()->update(); + d->enableBlockSelection(textCursor()); + else + d->disableBlockSelection(false); } -bool BaseTextEditorWidget::hasBlockSelection() const +void BaseTextEditorWidget::setBlockSelection(int positionBlock, int positionColumn, + int anchhorBlock, int anchorColumn) { - return d->m_inBlockSelectionMode; + d->enableBlockSelection(positionBlock, positionColumn, anchhorBlock, anchorColumn); } -void BaseTextEditorWidget::handleBlockSelection(int diff_row, int diff_col) +void BaseTextEditorWidget::setBlockSelection(const QTextCursor &cursor) { + d->enableBlockSelection(cursor); +} - if (!d->m_inBlockSelectionMode) { - d->m_blockSelection.fromSelection(d->m_document->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(d->m_document->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); -// } +QTextCursor BaseTextEditorWidget::blockSelection() const +{ + return d->m_blockSelection.cursor(d->m_document.data()); +} +bool BaseTextEditorWidget::hasBlockSelection() const +{ + return d->m_inBlockSelectionMode; } void BaseTextEditorWidget::updateTabStops() @@ -6365,22 +6576,24 @@ void BaseTextEditorWidget::transformBlockSelection(TransformationMethod method) const TabSettings &ts = d->m_document->tabSettings(); // saved to restore the blockselection - const int selectionPosition = d->m_blockSelection.position(ts); - const int anchorColumn = d->m_blockSelection.anchorColumnNumber(); - const int anchorBlock = d->m_blockSelection.anchorBlockNumber(); - const BaseTextBlockSelection::Anchor anchor = d->m_blockSelection.anchor; + const int positionColumn = d->m_blockSelection.positionColumn; + const int positionBlock = d->m_blockSelection.positionBlock; + const int anchorColumn = d->m_blockSelection.anchorColumn; + const int anchorBlock = d->m_blockSelection.anchorBlock; - QTextBlock block = d->m_blockSelection.firstBlock.block(); - const QTextBlock &lastBlock = d->m_blockSelection.lastBlock.block(); + QTextBlock block = d->m_document->document()->findBlockByNumber( + d->m_blockSelection.firstBlockNumber()); + const QTextBlock &lastBlock = d->m_document->document()->findBlockByNumber( + d->m_blockSelection.lastBlockNumber()); cursor.beginEditBlock(); for (;;) { // get position of the selection const QString &blockText = block.text(); const int startPos = block.position() - + ts.positionAtColumn(blockText, d->m_blockSelection.firstVisualColumn); + + ts.positionAtColumn(blockText, d->m_blockSelection.firstVisualColumn()); const int endPos = block.position() - + ts.positionAtColumn(blockText, d->m_blockSelection.lastVisualColumn); + + ts.positionAtColumn(blockText, d->m_blockSelection.lastVisualColumn()); // check if the selection is inside the text block if (startPos < endPos) { @@ -6397,13 +6610,7 @@ void BaseTextEditorWidget::transformBlockSelection(TransformationMethod method) cursor.endEditBlock(); // restore former block selection - cursor.setPosition(selectionPosition, QTextCursor::MoveAnchor); - d->m_inBlockSelectionMode = true; - d->m_blockSelection.fromSelection(ts, cursor); - d->m_blockSelection.anchor = anchor; - d->m_blockSelection.moveAnchor(anchorBlock, anchorColumn); - setTextCursor(d->m_blockSelection.selection(d->m_document->tabSettings())); - viewport()->update(); + d->enableBlockSelection(positionBlock, anchorColumn, anchorBlock, positionColumn); } void BaseTextEditorWidget::inSnippetMode(bool *active) diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 5d37e0c3b8..18b8bfb250 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -206,11 +206,16 @@ public: void setReadOnly(bool b); + void setTextCursor(const QTextCursor &cursor, bool keepBlockSelection); void setTextCursor(const QTextCursor &cursor); void insertCodeSnippet(const QTextCursor &cursor, const QString &snippet); void setBlockSelection(bool on); + void setBlockSelection(int positionBlock, int positionColumn, int anchhorBlock, + int anchorColumn); + void setBlockSelection(const QTextCursor &cursor); + QTextCursor blockSelection() const; bool hasBlockSelection() const; int verticalBlockSelectionFirstColumn() const; @@ -231,6 +236,9 @@ public: static QString msgTextTooLarge(quint64 size); + void insertPlainText(const QString &text); + QString selectedText() const; + public slots: virtual void copy(); virtual void paste(); @@ -310,6 +318,9 @@ public slots: void indent(); void unindent(); + void undo(); + void redo(); + void openLinkUnderCursor(); void openLinkUnderCursorInNextSplit(); @@ -322,6 +333,7 @@ signals: protected: bool event(QEvent *e); + void inputMethodEvent(QInputMethodEvent *e); void keyPressEvent(QKeyEvent *e); void wheelEvent(QWheelEvent *e); void changeEvent(QEvent *e); @@ -549,7 +561,6 @@ private: void transformBlockSelection(Internal::TransformationMethod method); private slots: - void handleBlockSelection(int diff_row, int diff_col); void updateTabStops(); void applyFontSettingsDelayed(); diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index 0748e0141d..1e69b3525e 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -59,23 +59,34 @@ class ClipboardAssistProvider; class TEXTEDITOR_EXPORT BaseTextBlockSelection { public: + BaseTextBlockSelection() + : positionBlock(0), positionColumn(0) + , anchorBlock(0) , anchorColumn(0){} + BaseTextBlockSelection(const BaseTextBlockSelection &other); + + void clear(); + QTextCursor selection(const BaseTextDocument *baseTextDocument) const; + QTextCursor cursor(const BaseTextDocument *baseTextDocument) const; + void fromPostition(int positionBlock, int positionColumn, int anchorBlock, int anchorColumn); + bool hasSelection() { return !(positionBlock == anchorBlock && positionColumn == anchorColumn); } + + // defines the first block + inline int firstBlockNumber() const { return qMin(positionBlock, anchorBlock); } + // defines the last block + inline int lastBlockNumber() const { return qMax(positionBlock, anchorBlock); } + // defines the first visual column of the selection + inline int firstVisualColumn() const { return qMin(positionColumn, anchorColumn); } + // defines the last visual column of the selection + inline int lastVisualColumn() const { return qMax(positionColumn, anchorColumn); } - bool isValid() const{ return !firstBlock.isNull() && !lastBlock.isNull(); } - void clear() { firstBlock = lastBlock = QTextCursor(); } - - QTextCursor firstBlock; // defines the first block - QTextCursor lastBlock; // defines the last block - int firstVisualColumn; // defines the first visual column of the selection - int lastVisualColumn; // defines the last visual column of the selection - enum Anchor {TopLeft = 0, TopRight, BottomLeft, BottomRight} anchor; - BaseTextBlockSelection():firstVisualColumn(0), lastVisualColumn(0), anchor(BottomRight){} - void moveAnchor(int blockNumber, int visualColumn); - int position(const TabSettings &ts) const; - inline int anchorColumnNumber() const { return (anchor % 2) ? lastVisualColumn : firstVisualColumn; } - inline int anchorBlockNumber() const { - return (anchor <= TopRight ? firstBlock.blockNumber() : lastBlock.blockNumber()); } - QTextCursor selection(const TabSettings &ts) const; - void fromSelection(const TabSettings &ts, const QTextCursor &selection); +public: + int positionBlock; + int positionColumn; + int anchorBlock; + int anchorColumn; + +private: + QTextCursor cursor(const BaseTextDocument *baseTextDocument, bool fullSelection) const; }; //================BaseTextEditorPrivate============== @@ -180,9 +191,18 @@ public: // block selection mode bool m_inBlockSelectionMode; - void clearBlockSelection(); QString copyBlockSelection(); - void removeBlockSelection(const QString &text = QString()); + void insertIntoBlockSelection(const QString &text = QString()); + void setCursorToColumn(QTextCursor &cursor, int column, + QTextCursor::MoveMode moveMode = QTextCursor::MoveAnchor); + void removeBlockSelection(); + void enableBlockSelection(const QTextCursor &cursor); + void enableBlockSelection(int positionBlock, int positionColumn, + int anchorBlock, int anchorColumn); + void disableBlockSelection(bool keepSelection = true); + void resetCursorFlashTimer(); + QBasicTimer m_cursorFlashTimer; + bool m_cursorVisible; bool m_moveLineUndoHack; QTextCursor m_findScopeStart; diff --git a/src/plugins/texteditor/basetexteditor_test.cpp b/src/plugins/texteditor/basetexteditor_test.cpp index de296bab66..077a97d095 100644 --- a/src/plugins/texteditor/basetexteditor_test.cpp +++ b/src/plugins/texteditor/basetexteditor_test.cpp @@ -29,6 +29,7 @@ #ifdef WITH_TESTS +#include <QClipboard> #include <QString> #include <QtTest/QtTest> @@ -42,80 +43,99 @@ using namespace TextEditor; enum TransFormationType { Uppercase, Lowercase }; +struct TestBlockSelection +{ + int positionBlock; + int positionColumn; + int anchorBlock; + int anchorColumn; + TestBlockSelection(int positionBlock, int positionColumn, int anchorBlock, int anchorColumn) + : positionBlock(positionBlock), positionColumn(positionColumn) + , anchorBlock(anchorBlock), anchorColumn(anchorColumn) {} + TestBlockSelection() {} +}; + Q_DECLARE_METATYPE(TransFormationType) +Q_DECLARE_METATYPE(TestBlockSelection) void Internal::TextEditorPlugin::testBlockSelectionTransformation_data() { - QTest::addColumn<QByteArray>("input"); - QTest::addColumn<QByteArray>("transformedText"); - QTest::addColumn<int>("position"); - QTest::addColumn<int>("anchor"); + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("transformedText"); + QTest::addColumn<TestBlockSelection>("selection"); QTest::addColumn<TransFormationType>("type"); QTest::newRow("uppercase") - << QByteArray("aaa\nbbb\nccc\n") - << QByteArray("aAa\nbBb\ncCc\n") - << 10 << 1 << Uppercase; + << QString::fromLatin1("aaa\nbbb\nccc\n") + << QString::fromLatin1("aAa\nbBb\ncCc\n") + << TestBlockSelection(0, 1, 2, 2) + << Uppercase; QTest::newRow("lowercase") - << QByteArray("AAA\nBBB\nCCC\n") - << QByteArray("AaA\nBbB\nCcC\n") - << 10 << 1 << Lowercase; + << QString::fromLatin1("AAA\nBBB\nCCC\n") + << QString::fromLatin1("AaA\nBbB\nCcC\n") + << TestBlockSelection(0, 1, 2, 2) + << Lowercase; QTest::newRow("uppercase_leading_tabs") - << QByteArray("\taaa\n\tbbb\n\tccc\n") - << QByteArray("\taAa\n\tbBb\n\tcCc\n") - << 13 << 2 << Uppercase; + << QString::fromLatin1("\taaa\n" "\tbbb\n" "\tccc\n") + << QString::fromLatin1("\taAa\n" "\tbBb\n" "\tcCc\n") + << TestBlockSelection(0, 9, 2, 10) + << Uppercase; QTest::newRow("lowercase_leading_tabs") - << QByteArray("\tAAA\n\tBBB\n\tCCC\n") - << QByteArray("\tAaA\n\tBbB\n\tCcC\n") - << 13 << 2 << Lowercase; + << QString::fromLatin1("\tAAA\n\tBBB\n\tCCC\n") + << QString::fromLatin1("\tAaA\n\tBbB\n\tCcC\n") + << TestBlockSelection(0, 9, 2, 10) + << Lowercase; QTest::newRow("uppercase_mixed_leading_whitespace") - << QByteArray("\taaa\n bbbbb\n ccccc\n") - << QByteArray("\tAaa\n bbbbB\n ccccC\n") - << 24 << 1 << Uppercase; + << QString::fromLatin1("\taaa\n bbbbb\n ccccc\n") + << QString::fromLatin1("\tAaa\n bbbbB\n ccccC\n") + << TestBlockSelection(0, 8, 2, 9) + << Uppercase; QTest::newRow("lowercase_mixed_leading_whitespace") - << QByteArray("\tAAA\n BBBBB\n CCCCC\n") - << QByteArray("\taAA\n BBBBb\n CCCCc\n") - << 24 << 1 << Lowercase; + << QString::fromLatin1("\tAAA\n BBBBB\n CCCCC\n") + << QString::fromLatin1("\taAA\n BBBBb\n CCCCc\n") + << TestBlockSelection(0, 8, 2, 9) + << Lowercase; QTest::newRow("uppercase_mid_tabs1") - << QByteArray("a\ta\nbbbbbbbbb\nccccccccc\n") - << QByteArray("a\ta\nbBBBBBbbb\ncCCCCCccc\n") - << 20 << 1 << Uppercase; + << QString::fromLatin1("a\ta\nbbbbbbbbb\nccccccccc\n") + << QString::fromLatin1("a\ta\nbBBBBBbbb\ncCCCCCccc\n") + << TestBlockSelection(0, 1, 2, 6) + << Uppercase; QTest::newRow("lowercase_mid_tabs2") - << QByteArray("AA\taa\n\t\t\nccccCCCCCC\n") - << QByteArray("Aa\taa\n\t\t\ncccccccccC\n") - << 18 << 1 << Lowercase; + << QString::fromLatin1("AA\taa\n\t\t\nccccCCCCCC\n") + << QString::fromLatin1("Aa\taa\n\t\t\ncccccccccC\n") + << TestBlockSelection(0, 1, 2, 9) + << Lowercase; QTest::newRow("uppercase_over_line_ending") - << QByteArray("aaaaa\nbbb\nccccc\n") - << QByteArray("aAAAa\nbBB\ncCCCc\n") - << 14 << 1 << Uppercase; + << QString::fromLatin1("aaaaa\nbbb\nccccc\n") + << QString::fromLatin1("aAAAa\nbBB\ncCCCc\n") + << TestBlockSelection(0, 1, 2, 4) + << Uppercase; QTest::newRow("lowercase_over_line_ending") - << QByteArray("AAAAA\nBBB\nCCCCC\n") - << QByteArray("AaaaA\nBbb\nCcccC\n") - << 14 << 1 << Lowercase; + << QString::fromLatin1("AAAAA\nBBB\nCCCCC\n") + << QString::fromLatin1("AaaaA\nBbb\nCcccC\n") + << TestBlockSelection(0, 1, 2, 4) + << Lowercase; } void Internal::TextEditorPlugin::testBlockSelectionTransformation() { // fetch test data - QFETCH(QByteArray, input); - QFETCH(QByteArray, transformedText); - QFETCH(int, position); - QFETCH(int, anchor); + QFETCH(QString, input); + QFETCH(QString, transformedText); + QFETCH(TestBlockSelection, selection); QFETCH(TransFormationType, type); // open editor Core::IEditor *editor = Core::EditorManager::openEditorWithContents( - Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, 0, input); + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, 0, input.toLatin1()); QVERIFY(editor); if (BaseTextEditor *textEditor = qobject_cast<BaseTextEditor*>(editor)) { - - // place cursor and enable block selection BaseTextEditorWidget *editorWidget = textEditor->editorWidget(); - QTextCursor cursor = editorWidget->textCursor(); - cursor.setPosition(anchor); - cursor.setPosition(position, QTextCursor::KeepAnchor); - editorWidget->setTextCursor(cursor); - editorWidget->setBlockSelection(true); + editorWidget->setBlockSelection(selection.positionBlock, + selection.positionColumn, + selection.anchorBlock, + selection.anchorColumn); + editorWidget->update(); // transform blockselection switch (type) { @@ -126,9 +146,355 @@ void Internal::TextEditorPlugin::testBlockSelectionTransformation() editorWidget->lowercaseSelection(); break; } - QCOMPARE(textEditor->baseTextDocument()->plainText().toLatin1(), transformedText); - } else { + QCOMPARE(textEditor->baseTextDocument()->plainText(), transformedText); + } + Core::EditorManager::closeEditor(editor, false); +} + +static const char text[] = + "first line\n" + "second line\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n"; + +void Internal::TextEditorPlugin::testBlockSelectionInsert_data() +{ + QTest::addColumn<QString>("transformedText"); + QTest::addColumn<TestBlockSelection>("selection"); + QTest::addColumn<QString>("insertText"); + + QTest::newRow("insert") + << QString::fromLatin1(".first line\n" + ".second line\n" + ".third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1("."); + QTest::newRow("insert after line end 1") + << QString::fromLatin1("first line\n" + "second line\n" + "third. line\n" + " .\n" + "longe.st line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(2, 5, 4, 5) + << QString::fromLatin1("."); + QTest::newRow("insert after line end 2") + << QString::fromLatin1("first line .\n" + "second line .\n" + "third line .\n" + " .\n" + "longest line in this text .\n" + " leading space .\n" + "" "\tleading tab .\n" + "mid\t" "tab .\n" + "endtab\t .\n") + << TestBlockSelection(0, 32, 8, 32) + << QString::fromLatin1("."); + QTest::newRow("insert in leading tab") + << QString::fromLatin1("first line\n" + "second line\n" + "third line\n" + "\n" + "lo.ngest line in this text\n" + " . leading space\n" + " . leading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(4, 2, 6, 2) + << QString::fromLatin1("."); + QTest::newRow("insert in middle tab") + << QString::fromLatin1("first line\n" + "second line\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + " . leading tab\n" + "mid . tab\n" + "endta.b\t\n") + + << TestBlockSelection(6, 5, 8, 5) + << QString::fromLatin1("."); + QTest::newRow("insert same block count with all blocks same length") + << QString::fromLatin1(".first line\n" + ".second line\n" + ".third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1(".\n.\n."); + QTest::newRow("insert same block count with all blocks different length") + << QString::fromLatin1(". first line\n" + ".. second line\n" + "...third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1(".\n..\n..."); + QTest::newRow("insert same block count with some blocks containing tabs") + << QString::fromLatin1(". first line\n" + ".\t second line\n" + ".\t.third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1(".\n.\t\n.\t."); + QTest::newRow("insert same block count with some blocks containing tabs in mid line") + << QString::fromLatin1("fi. rst line\n" + "se.\t cond line\n" + "th.\t.ird line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 2, 2, 2) + << QString::fromLatin1(".\n.\t\n.\t."); + QTest::newRow("insert different block count with all blocks same length") + << QString::fromLatin1(".first line\n" + ".\n" + ".second line\n" + ".\n" + ".third line\n" + ".\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1(".\n."); + QTest::newRow("insert different block count with all blocks different length") + << QString::fromLatin1(". first line\n" + "..\n" + ". second line\n" + "..\n" + ". third line\n" + "..\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 2, 0) + << QString::fromLatin1(".\n.."); + QTest::newRow("insert different block count with some blocks containing tabs") + << QString::fromLatin1(". first line\n" + ".\t \n" + ".\t.\n" + ". second line\n" + ".\t \n" + ".\t.\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 0, 1, 0) + << QString::fromLatin1(".\n.\t\n.\t."); + QTest::newRow("insert different block count with some blocks containing tabs in mid line") + << QString::fromLatin1("fi. rst line\n" + " .\t \n" + " .\t.\n" + "se. cond line\n" + " .\t \n" + " .\t.\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 2, 1, 2) + << QString::fromLatin1(".\n.\t\n.\t."); +} + +void Internal::TextEditorPlugin::testBlockSelectionInsert() +{ + // fetch test data + QFETCH(QString, transformedText); + QFETCH(TestBlockSelection, selection); + QFETCH(QString, insertText); + + // open editor + Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, 0, text); + QVERIFY(editor); + if (BaseTextEditor *textEditor = qobject_cast<BaseTextEditor*>(editor)) { + BaseTextEditorWidget *editorWidget = textEditor->editorWidget(); + editorWidget->setBlockSelection(selection.positionBlock, + selection.positionColumn, + selection.anchorBlock, + selection.anchorColumn); + editorWidget->update(); + editorWidget->insertPlainText(insertText); + + QCOMPARE(textEditor->baseTextDocument()->plainText(), transformedText); + } + Core::EditorManager::closeEditor(editor, false); +} + + +void Internal::TextEditorPlugin::testBlockSelectionRemove_data() +{ + QTest::addColumn<QString>("transformedText"); + QTest::addColumn<TestBlockSelection>("selection"); + QTest::newRow("Delete") + << QString::fromLatin1("first ine\n" + "second ine\n" + "third ine\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 7, 2, 8); + QTest::newRow("Delete All") + << QString::fromLatin1("\n\n\n\n\n\n\n\n\n") + << TestBlockSelection(0, 0, 8, 30); + QTest::newRow("Delete Inside Tab") + << QString::fromLatin1("first line\n" + "second line\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + " leading tab\n" + "mi\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(5, 2, 7, 3); + QTest::newRow("Delete around Tab") + << QString::fromLatin1("first line\n" + "second line\n" + "third line\n" + "\n" + "longest line in this text\n" + " ng space\n" + " eading tab\n" + "miab\n" + "endtab\t\n") + << TestBlockSelection(5, 2, 7, 9); + QTest::newRow("Delete behind text") + << QString::fromLatin1("first line\n" + "second line\n" + "third line\n" + "\n" + "longest line in this text\n" + " leading space\n" + "" "\tleading tab\n" + "mid\t" "tab\n" + "endtab\t\n") + << TestBlockSelection(0, 30, 8, 35); +} + +void Internal::TextEditorPlugin::testBlockSelectionRemove() +{ + // fetch test data + QFETCH(QString, transformedText); + QFETCH(TestBlockSelection, selection); + + // open editor + Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, 0, text); + QVERIFY(editor); + if (BaseTextEditor *textEditor = qobject_cast<BaseTextEditor*>(editor)) { + BaseTextEditorWidget *editorWidget = textEditor->editorWidget(); + editorWidget->setBlockSelection(selection.positionBlock, + selection.positionColumn, + selection.anchorBlock, + selection.anchorColumn); + editorWidget->update(); + editorWidget->insertPlainText(QString()); + + QCOMPARE(textEditor->baseTextDocument()->plainText(), transformedText); + } + Core::EditorManager::closeEditor(editor, false); +} + +void Internal::TextEditorPlugin::testBlockSelectionCopy_data() +{ + QTest::addColumn<QString>("copiedText"); + QTest::addColumn<TestBlockSelection>("selection"); + QTest::newRow("copy") + << QString::fromLatin1("lin\n" + "lin\n" + "lin") + << TestBlockSelection(0, 7, 2, 10); + QTest::newRow("copy over line end") + << QString::fromLatin1("ond line \n" + "rd line \n" + " ") + << TestBlockSelection(1, 3, 3, 15); + QTest::newRow("copy inside tab") + << QString::fromLatin1("ond line \n" + "rd line \n" + " ") + << TestBlockSelection(1, 3, 3, 15); + QTest::newRow("copy start in tab") + << QString::fromLatin1("gest lin\n" + " leading\n" + " lea") + << TestBlockSelection(4, 3, 6, 11); + QTest::newRow("copy around tab") + << QString::fromLatin1(" leadin\n" + " le\n" + "d\tta") + << TestBlockSelection(5, 2, 7, 10); +} + +void Internal::TextEditorPlugin::testBlockSelectionCopy() +{ + // fetch test data + QFETCH(QString, copiedText); + QFETCH(TestBlockSelection, selection); + + // open editor + Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, 0, text); + QVERIFY(editor); + if (BaseTextEditor *textEditor = qobject_cast<BaseTextEditor*>(editor)) { + BaseTextEditorWidget *editorWidget = textEditor->editorWidget(); + editorWidget->setBlockSelection(selection.positionBlock, + selection.positionColumn, + selection.anchorBlock, + selection.anchorColumn); + editorWidget->update(); + editorWidget->copy(); + QCOMPARE(qApp->clipboard()->text(), copiedText); } Core::EditorManager::closeEditor(editor, false); } diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index 58caa1e4ab..c4b8ac0d07 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -248,6 +248,9 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, { QTC_ASSERT(!isWaitingForProposal(), return); + if (m_textEditor->editorWidget()->hasBlockSelection()) + return; // TODO + if (!provider) { if (kind == Completion) provider = m_completionProvider; diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp index be09d8cbde..bb3ce677b1 100644 --- a/src/plugins/texteditor/tabsettings.cpp +++ b/src/plugins/texteditor/tabsettings.cpp @@ -213,6 +213,18 @@ int TabSettings::positionAtColumn(const QString &text, int column, int *offset) return i; } +int TabSettings::columnCountForText(const QString &text, int startColumn) const +{ + int column = startColumn; + for (int i = 0; i < text.size(); ++i) { + if (text.at(i) == QLatin1Char('\t')) + column = column - (column % m_tabSize) + m_tabSize; + else + ++column; + } + return column - startColumn; +} + int TabSettings::spacesLeftFromPosition(const QString &text, int position) { int i = position; diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h index c8836a25ef..0dfdb7cd26 100644 --- a/src/plugins/texteditor/tabsettings.h +++ b/src/plugins/texteditor/tabsettings.h @@ -70,6 +70,7 @@ public: int lineIndentPosition(const QString &text) const; int columnAt(const QString &text, int position) const; int positionAtColumn(const QString &text, int column, int *offset = 0) const; + int columnCountForText(const QString &text, int startColumn = 0) const; int indentedColumn(int column, bool doIndent = true) const; QString indentationString(int startColumn, int targetColumn, const QTextBlock ¤tBlock = QTextBlock()) const; QString indentationString(const QString &text) const; diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h index 895a08c628..db057c7c84 100644 --- a/src/plugins/texteditor/texteditorplugin.h +++ b/src/plugins/texteditor/texteditorplugin.h @@ -73,14 +73,6 @@ private slots: void updateVariable(const QByteArray &variable); void updateCurrentSelection(const QString &text); -#ifdef WITH_TESTS - void testSnippetParsing_data(); - void testSnippetParsing(); - - void testBlockSelectionTransformation_data(); - void testBlockSelectionTransformation(); -#endif - private: static TextEditorPlugin *m_instance; TextEditorSettings *m_settings; @@ -89,6 +81,23 @@ private: Core::SearchResultWindow *m_searchResultWindow; OutlineFactory *m_outlineFactory; BaseTextMarkRegistry *m_baseTextMarkRegistry; + + +#ifdef WITH_TESTS +private slots: + void testSnippetParsing_data(); + void testSnippetParsing(); + + void testBlockSelectionTransformation_data(); + void testBlockSelectionTransformation(); + void testBlockSelectionInsert_data(); + void testBlockSelectionInsert(); + void testBlockSelectionRemove_data(); + void testBlockSelectionRemove(); + void testBlockSelectionCopy_data(); + void testBlockSelectionCopy(); +#endif + }; } // namespace Internal |