From 0ed9043c343e537c9606fc298e8fddbfbe849899 Mon Sep 17 00:00:00 2001 From: mae Date: Tue, 29 Sep 2009 12:44:00 +0200 Subject: [PATCH] tune auto-indentation of pasted text auto-indentation of pasted text now only happens when complete lines including a trailing paragraph separator are copied and pasted. In addition, the reindent() functions ensures that the relative indentation within the pasted block is preserved. This mechanism is now also used for moving lines up/down. Done with thorbjorn --- src/plugins/texteditor/basetexteditor.cpp | 99 ++++++++++++++++++----- src/plugins/texteditor/basetexteditor.h | 2 + src/plugins/texteditor/tabsettings.cpp | 34 ++++++++ src/plugins/texteditor/tabsettings.h | 3 + 4 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index a12e56e3a48..13461fe7c7c 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -827,7 +827,7 @@ void BaseTextEditor::moveLineUpDown(bool up) move.setPosition(end, QTextCursor::KeepAnchor); } - indent(document(), move, QChar::Null); + reindent(document(), move); move.endEditBlock(); setTextCursor(move); @@ -3356,8 +3356,8 @@ void BaseTextEditor::indentBlock(QTextDocument *, QTextBlock, QChar) void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar) { if (cursor.hasSelection()) { - QTextBlock block = doc->findBlock(qMin(cursor.selectionStart(), cursor.selectionEnd())); - const QTextBlock end = doc->findBlock(qMax(cursor.selectionStart(), cursor.selectionEnd())).next(); + QTextBlock block = doc->findBlock(cursor.selectionStart()); + const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next(); do { indentBlock(doc, block, typedChar); block = block.next(); @@ -3367,6 +3367,29 @@ void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar } } +void BaseTextEditor::reindent(QTextDocument *doc, const QTextCursor &cursor) +{ + if (cursor.hasSelection()) { + QTextBlock block = doc->findBlock(cursor.selectionStart()); + const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next(); + + const TabSettings &ts = d->m_document->tabSettings(); + int previousIndentation = ts.indentationColumn(block.text()); + indentBlock(doc, block, QChar::Null); + int currentIndentation = ts.indentationColumn(block.text()); + int delta = currentIndentation - previousIndentation; + + block = block.next(); + while (block.isValid() && block != end) { + ts.reindentLine(block, delta); + block = block.next(); + } + } else { + indentBlock(doc, cursor.block(), QChar::Null); + } +} + + BaseTextEditor::Link BaseTextEditor::findLinkAt(const QTextCursor &, bool) { return Link(); @@ -4466,11 +4489,39 @@ QMimeData *BaseTextEditor::createMimeDataFromSelection() const if (d->m_inBlockSelectionMode) { QMimeData *mimeData = new QMimeData; QString text = d->copyBlockSelection(); - mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.blocktext"), text.toUtf8()); + mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"), text.toUtf8()); mimeData->setText(text); // for exchangeability return mimeData; + } else if (textCursor().hasSelection()){ + QTextCursor cursor = textCursor(); + QMimeData *mimeData = new QMimeData; + QString text = cursor.selectedText(); + mimeData->setText(text); + + /* + Try to figure out whether we are copying an entire block, and store the complete block + including indentation in the qtcreator.blocktext mimetype. + */ + QTextCursor selstart = cursor; + selstart.setPosition(cursor.selectionStart()); + QTextCursor selend = cursor; + selend.setPosition(cursor.selectionEnd()); + const TabSettings &ts = d->m_document->tabSettings(); + + bool startOk = ts.cursorIsAtBeginningOfLine(selstart); + bool endOk = (selend.block() != selstart.block() && ts.cursorIsAtBeginningOfLine(selend)); + + if (startOk && endOk) { + selstart.movePosition(QTextCursor::StartOfBlock); + selend.movePosition(QTextCursor::StartOfBlock); + cursor.setPosition(selstart.position()); + cursor.setPosition(selend.position(), QTextCursor::KeepAnchor); + text = cursor.selectedText(); + mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.blocktext"), text.toUtf8()); + } + return mimeData; } - return QPlainTextEdit::createMimeDataFromSelection(); + return 0; } bool BaseTextEditor::canInsertFromMimeData(const QMimeData *source) const @@ -4480,8 +4531,11 @@ bool BaseTextEditor::canInsertFromMimeData(const QMimeData *source) const void BaseTextEditor::insertFromMimeData(const QMimeData *source) { - if (!isReadOnly() && source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))) { - QString text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))); + if (isReadOnly()) + return; + + if (source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"))) { + QString text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"))); if (text.isEmpty()) return; QStringList lines = text.split(QLatin1Char('\n')); @@ -4511,13 +4565,15 @@ void BaseTextEditor::insertFromMimeData(const QMimeData *source) return; } + + QString text = source->text(); if (text.isEmpty()) return; const TabSettings &ts = d->m_document->tabSettings(); QTextCursor cursor = textCursor(); - if (cursor.hasSelection() || !ts.m_autoIndent) { + if (!ts.m_autoIndent) { cursor.beginEditBlock(); cursor.insertText(text); cursor.endEditBlock(); @@ -4526,29 +4582,28 @@ void BaseTextEditor::insertFromMimeData(const QMimeData *source) } cursor.beginEditBlock(); - int bpos = cursor.block().position(); - int pos = cursor.position() - bpos; - int fns = ts.firstNonSpace(cursor.block().text()); - if (pos <= fns) { // cursor is at beginning of line, we will reindent + if (ts.cursorIsAtBeginningOfLine(cursor) + && source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))) { + text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))); + if (text.isEmpty()) + return; - bool hasEndingNewline = (text.endsWith(QLatin1Char('\n')) - || text.endsWith(QChar::ParagraphSeparator) - || text.endsWith(QLatin1Char('\r'))); + cursor.removeSelectedText(); - if (hasEndingNewline) - cursor.setPosition(bpos); // we'll add a final newline, preserve current line's indentation + int bpos = cursor.block().position(); + int pos = cursor.position() - bpos; + + cursor.setPosition(bpos); // since we'll add a final newline, preserve current line's indentation cursor.insertText(text); pos = cursor.position(); cursor.setPosition(bpos); cursor.setPosition(pos, QTextCursor::KeepAnchor); - if (hasEndingNewline) - cursor.setPosition(cursor.position()-1, QTextCursor::KeepAnchor); - indent(document(), cursor, QChar::Null); + cursor.setPosition(cursor.position()-1, QTextCursor::KeepAnchor); + reindent(document(), cursor); cursor.clearSelection(); - if (hasEndingNewline) - cursor.setPosition(cursor.position()+1); + cursor.setPosition(cursor.position()+1); // skip newline } else { cursor.insertText(text); } diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index ab2a6ec2763..462706b5bb4 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -521,6 +521,8 @@ protected: virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); // Indent at cursor. Calls indentBlock for selection or current line. virtual void indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar); + // Reindent at cursor. Selection will be adjusted according to the indentation change of the first block + virtual void reindent(QTextDocument *doc, const QTextCursor &cursor); struct Link { diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp index 393434d9085..daa84307667 100644 --- a/src/plugins/texteditor/tabsettings.cpp +++ b/src/plugins/texteditor/tabsettings.cpp @@ -87,6 +87,14 @@ void TabSettings::fromSettings(const QString &category, const QSettings *s) m_tabKeyBehavior = (TabKeyBehavior)s->value(group + QLatin1String(tabKeyBehaviorKey), m_tabKeyBehavior).toInt(); } + +bool TabSettings::cursorIsAtBeginningOfLine(const QTextCursor &cursor) const +{ + QString text = cursor.block().text(); + int fns = firstNonSpace(text); + return (cursor.position() - cursor.block().position() <= fns); +} + int TabSettings::lineIndentPosition(const QString &text) const { int i = 0; @@ -255,6 +263,32 @@ void TabSettings::indentLine(QTextBlock block, int newIndent) const cursor.endEditBlock(); } +void TabSettings::reindentLine(QTextBlock block, int delta) const +{ + const QString text = block.text(); + const int oldBlockLength = text.size(); + + int oldIndent = indentationColumn(text); + int newIndent = qMax(oldIndent + delta, 0); + + if (oldIndent == newIndent) + return; + + const QString indentString = indentationString(0, newIndent); + newIndent = indentString.length(); + + if (oldBlockLength == indentString.length() && text == indentString) + return; + + QTextCursor cursor(block); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::StartOfBlock); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text)); + cursor.removeSelectedText(); + cursor.insertText(indentString); + cursor.endEditBlock(); +} + bool TabSettings::equals(const TabSettings &ts) const { return m_spacesForTabs == ts.m_spacesForTabs diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h index f26e0bb5ad9..ad6c1cd6d85 100644 --- a/src/plugins/texteditor/tabsettings.h +++ b/src/plugins/texteditor/tabsettings.h @@ -66,7 +66,10 @@ struct TEXTEDITOR_EXPORT TabSettings QString indentationString(const QString &text) const; int indentationColumn(const QString &text) const; + bool cursorIsAtBeginningOfLine(const QTextCursor &cursor) const; + void indentLine(QTextBlock block, int newIndent) const; + void reindentLine(QTextBlock block, int delta) const; int trailingWhitespaces(const QString &text) const; bool isIndentationClean(const QString &text) const;