diff options
Diffstat (limited to 'src/libs/utils/multitextcursor.cpp')
-rw-r--r-- | src/libs/utils/multitextcursor.cpp | 271 |
1 files changed, 161 insertions, 110 deletions
diff --git a/src/libs/utils/multitextcursor.cpp b/src/libs/utils/multitextcursor.cpp index bb2e38f5eb..0c2e5b0792 100644 --- a/src/libs/utils/multitextcursor.cpp +++ b/src/libs/utils/multitextcursor.cpp @@ -16,33 +16,130 @@ namespace Utils { MultiTextCursor::MultiTextCursor() {} MultiTextCursor::MultiTextCursor(const QList<QTextCursor> &cursors) - : m_cursors(cursors) { - mergeCursors(); + setCursors(cursors); +} + +void MultiTextCursor::fillMapWithList() +{ + m_cursorMap.clear(); + for (auto it = m_cursorList.begin(); it != m_cursorList.end(); ++it) + m_cursorMap[it->selectionStart()] = it; +} + +MultiTextCursor& MultiTextCursor::operator=(const MultiTextCursor &multiCursor) +{ + m_cursorList = multiCursor.m_cursorList; + fillMapWithList(); + return *this; +} + +MultiTextCursor::MultiTextCursor(const MultiTextCursor &multiCursor) +{ + *this = multiCursor; +} + +MultiTextCursor& MultiTextCursor::operator=(const MultiTextCursor &&multiCursor) +{ + m_cursorList = std::move(multiCursor.m_cursorList); + fillMapWithList(); + return *this; +} + +MultiTextCursor::MultiTextCursor(const MultiTextCursor &&multiCursor) +{ + *this = std::move(multiCursor); +} + +MultiTextCursor::~MultiTextCursor() = default; + +static bool cursorsOverlap(const QTextCursor &c1, const QTextCursor &c2) +{ + if (c1.hasSelection()) { + if (c2.hasSelection()) { + return c2.selectionEnd() > c1.selectionStart() + && c2.selectionStart() < c1.selectionEnd(); + } + const int c2Pos = c2.position(); + return c2Pos > c1.selectionStart() && c2Pos < c1.selectionEnd(); + } + if (c2.hasSelection()) { + const int c1Pos = c1.position(); + return c1Pos > c2.selectionStart() && c1Pos < c2.selectionEnd(); + } + return c1 == c2; +}; + +static void mergeCursors(QTextCursor &c1, const QTextCursor &c2) +{ + if (c1.position() == c2.position() && c1.anchor() == c2.anchor()) + return; + if (c1.hasSelection()) { + if (!c2.hasSelection()) + return; + int pos = c1.position(); + int anchor = c1.anchor(); + if (c1.selectionStart() > c2.selectionStart()) { + if (pos < anchor) + pos = c2.selectionStart(); + else + anchor = c2.selectionStart(); + } + if (c1.selectionEnd() < c2.selectionEnd()) { + if (pos < anchor) + anchor = c2.selectionEnd(); + else + pos = c2.selectionEnd(); + } + c1.setPosition(anchor); + c1.setPosition(pos, QTextCursor::KeepAnchor); + } else { + c1 = c2; + } } void MultiTextCursor::addCursor(const QTextCursor &cursor) { QTC_ASSERT(!cursor.isNull(), return); - m_cursors.append(cursor); - mergeCursors(); + + QTextCursor c1 = cursor; + const int pos = c1.selectionStart(); + + auto found = m_cursorMap.lower_bound(pos); + if (found != m_cursorMap.begin()) + --found; + + for (; !m_cursorMap.empty() && found != m_cursorMap.end() + && found->second->selectionStart() <= cursor.selectionEnd();) { + const QTextCursor &c2 = *found->second; + if (cursorsOverlap(c1, c2)) { + Utils::mergeCursors(c1, c2); + m_cursorList.erase(found->second); + found = m_cursorMap.erase(found); + continue; + } + ++found; + } + + m_cursorMap[pos] = m_cursorList.insert(m_cursorList.end(), c1); } void MultiTextCursor::addCursors(const QList<QTextCursor> &cursors) { - m_cursors.append(cursors); - mergeCursors(); + for (const QTextCursor &c : cursors) + addCursor(c); } void MultiTextCursor::setCursors(const QList<QTextCursor> &cursors) { - m_cursors = cursors; - mergeCursors(); + m_cursorList.clear(); + m_cursorMap.clear(); + addCursors(cursors); } const QList<QTextCursor> MultiTextCursor::cursors() const { - return m_cursors; + return QList<QTextCursor>(m_cursorList.begin(), m_cursorList.end()); } void MultiTextCursor::replaceMainCursor(const QTextCursor &cursor) @@ -54,64 +151,72 @@ void MultiTextCursor::replaceMainCursor(const QTextCursor &cursor) QTextCursor MultiTextCursor::mainCursor() const { - if (m_cursors.isEmpty()) + if (m_cursorList.empty()) return {}; - return m_cursors.last(); + return m_cursorList.back(); } QTextCursor MultiTextCursor::takeMainCursor() { - if (m_cursors.isEmpty()) + if (m_cursorList.empty()) return {}; - return m_cursors.takeLast(); + + QTextCursor cursor = m_cursorList.back(); + auto it = m_cursorList.end(); + --it; + m_cursorMap.erase(it->selectionStart()); + m_cursorList.erase(it); + + return cursor; } void MultiTextCursor::beginEditBlock() { - QTC_ASSERT(!m_cursors.empty(), return); - m_cursors.last().beginEditBlock(); + QTC_ASSERT(!m_cursorList.empty(), return); + m_cursorList.back().beginEditBlock(); } void MultiTextCursor::endEditBlock() { - QTC_ASSERT(!m_cursors.empty(), return); - m_cursors.last().endEditBlock(); + QTC_ASSERT(!m_cursorList.empty(), return); + m_cursorList.back().endEditBlock(); } bool MultiTextCursor::isNull() const { - return m_cursors.isEmpty(); + return m_cursorList.empty(); } bool MultiTextCursor::hasMultipleCursors() const { - return m_cursors.size() > 1; + return m_cursorList.size() > 1; } int MultiTextCursor::cursorCount() const { - return static_cast<int>(m_cursors.size()); + return static_cast<int>(m_cursorList.size()); } void MultiTextCursor::movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode, int n) { - for (QTextCursor &cursor : m_cursors) + for (auto &cursor : m_cursorList) cursor.movePosition(operation, mode, n); + mergeCursors(); } bool MultiTextCursor::hasSelection() const { - return Utils::anyOf(m_cursors, &QTextCursor::hasSelection); + return Utils::anyOf(m_cursorList, &QTextCursor::hasSelection); } QString MultiTextCursor::selectedText() const { QString text; - const QList<QTextCursor> cursors = Utils::sorted(m_cursors); - for (const QTextCursor &cursor : cursors) { + for (const auto &element : std::as_const(m_cursorMap)) { + const QTextCursor &cursor = *element.second; const QString &cursorText = cursor.selectedText(); if (cursorText.isEmpty()) continue; @@ -128,8 +233,8 @@ QString MultiTextCursor::selectedText() const void MultiTextCursor::removeSelectedText() { beginEditBlock(); - for (QTextCursor &c : m_cursors) - c.removeSelectedText(); + for (auto cursor = m_cursorList.begin(); cursor != m_cursorList.end(); ++cursor) + cursor->removeSelectedText(); endEditBlock(); mergeCursors(); } @@ -149,25 +254,27 @@ static void insertAndSelect(QTextCursor &cursor, const QString &text, bool selec void MultiTextCursor::insertText(const QString &text, bool selectNewText) { - if (m_cursors.isEmpty()) + if (m_cursorList.empty()) return; - m_cursors.last().beginEditBlock(); + + m_cursorList.back().beginEditBlock(); if (hasMultipleCursors()) { QStringList lines = text.split('\n'); if (!lines.isEmpty() && lines.last().isEmpty()) lines.pop_back(); int index = 0; - if (lines.count() == m_cursors.count()) { - QList<QTextCursor> cursors = Utils::sorted(m_cursors); - for (QTextCursor &cursor : cursors) + if (static_cast<long unsigned int>(lines.count()) == m_cursorList.size()) { + for (const auto &element : std::as_const(m_cursorMap)) { + QTextCursor &cursor = *element.second; insertAndSelect(cursor, lines.at(index++), selectNewText); - m_cursors.last().endEditBlock(); + } + m_cursorList.back().endEditBlock(); return; } } - for (QTextCursor &cursor : m_cursors) - insertAndSelect(cursor, text, selectNewText); - m_cursors.last().endEditBlock(); + for (auto cursor = m_cursorList.begin(); cursor != m_cursorList.end(); ++cursor) + insertAndSelect(*cursor, text, selectNewText); + m_cursorList.back().endEditBlock(); } bool equalCursors(const QTextCursor &lhs, const QTextCursor &rhs) @@ -177,17 +284,21 @@ bool equalCursors(const QTextCursor &lhs, const QTextCursor &rhs) bool MultiTextCursor::operator==(const MultiTextCursor &other) const { - if (m_cursors.size() != other.m_cursors.size()) + if (m_cursorList.size() != other.m_cursorList.size()) return false; - if (m_cursors.isEmpty()) + if (m_cursorList.empty()) return true; - QList<QTextCursor> thisCursors = m_cursors; - QList<QTextCursor> otherCursors = other.m_cursors; - if (!equalCursors(thisCursors.takeLast(), otherCursors.takeLast())) + + if (!equalCursors(m_cursorList.back(), other.m_cursorList.back())) return false; - for (const QTextCursor &oc : otherCursors) { - auto compare = [oc](const QTextCursor &c) { return equalCursors(oc, c); }; - if (!Utils::contains(thisCursors, compare)) + + auto it = m_cursorMap.begin(); + auto otherIt = other.m_cursorMap.begin(); + for (;it != m_cursorMap.end() && otherIt != other.m_cursorMap.end(); ++it, ++otherIt) { + const QTextCursor &cursor = *it->second; + const QTextCursor &otherCursor = *otherIt->second; + if (it->first != otherIt->first || cursor != otherCursor + || cursor.anchor() != otherCursor.anchor()) return false; } return true; @@ -198,70 +309,10 @@ bool MultiTextCursor::operator!=(const MultiTextCursor &other) const return !operator==(other); } -static bool cursorsOverlap(const QTextCursor &c1, const QTextCursor &c2) -{ - if (c1.hasSelection()) { - if (c2.hasSelection()) { - return c2.selectionEnd() > c1.selectionStart() - && c2.selectionStart() < c1.selectionEnd(); - } - const int c2Pos = c2.position(); - return c2Pos > c1.selectionStart() && c2Pos < c1.selectionEnd(); - } - if (c2.hasSelection()) { - const int c1Pos = c1.position(); - return c1Pos > c2.selectionStart() && c1Pos < c2.selectionEnd(); - } - return c1 == c2; -}; - -static void mergeCursors(QTextCursor &c1, const QTextCursor &c2) -{ - if (c1.position() == c2.position() && c1.anchor() == c2.anchor()) - return; - if (c1.hasSelection()) { - if (!c2.hasSelection()) - return; - int pos = c1.position(); - int anchor = c1.anchor(); - if (c1.selectionStart() > c2.selectionStart()) { - if (pos < anchor) - pos = c2.selectionStart(); - else - anchor = c2.selectionStart(); - } - if (c1.selectionEnd() < c2.selectionEnd()) { - if (pos < anchor) - anchor = c2.selectionEnd(); - else - pos = c2.selectionEnd(); - } - c1.setPosition(anchor); - c1.setPosition(pos, QTextCursor::KeepAnchor); - } else { - c1 = c2; - } -} - void MultiTextCursor::mergeCursors() { - std::list<QTextCursor> cursors(m_cursors.begin(), m_cursors.end()); - cursors = Utils::filtered(cursors, [](const QTextCursor &c){ - return !c.isNull(); - }); - for (auto it = cursors.begin(); it != cursors.end(); ++it) { - QTextCursor &c1 = *it; - for (auto other = std::next(it); other != cursors.end();) { - const QTextCursor &c2 = *other; - if (cursorsOverlap(c1, c2)) { - Utils::mergeCursors(c1, c2); - other = cursors.erase(other); - continue; - } - ++other; - } - } - m_cursors = QList<QTextCursor>(cursors.begin(), cursors.end()); + QList<QTextCursor> cursors(m_cursorList.begin(), m_cursorList.end()); + setCursors(cursors); } // could go into QTextCursor... @@ -321,7 +372,7 @@ bool MultiTextCursor::handleMoveKeyEvent(QKeyEvent *e, return false; } - const QList<QTextCursor> cursors = m_cursors; + const std::list<QTextCursor> cursors = m_cursorList; for (QTextCursor cursor : cursors) { if (camelCaseNavigationEnabled && op == QTextCursor::WordRight) CamelCaseCursor::right(&cursor, edit, QTextCursor::MoveAnchor); @@ -329,14 +380,14 @@ bool MultiTextCursor::handleMoveKeyEvent(QKeyEvent *e, CamelCaseCursor::left(&cursor, edit, QTextCursor::MoveAnchor); else cursor.movePosition(op, QTextCursor::MoveAnchor); - m_cursors << cursor; - } - mergeCursors(); + addCursor(cursor); + } return true; } - for (QTextCursor &cursor : m_cursors) { + for (auto it = m_cursorList.begin(); it != m_cursorList.end(); ++it) { + QTextCursor &cursor = *it; QTextCursor::MoveMode mode = QTextCursor::MoveAnchor; QTextCursor::MoveOperation op = QTextCursor::NoMove; |