aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/multitextcursor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/multitextcursor.cpp')
-rw-r--r--src/libs/utils/multitextcursor.cpp271
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;