diff --git a/src/view/katemulticlipboard.cpp b/src/view/katemulticlipboard.cpp index 3b9fdd0a..aa0ef5d8 100644 --- a/src/view/katemulticlipboard.cpp +++ b/src/view/katemulticlipboard.cpp @@ -1,139 +1,142 @@ /* This file is part of the KDE and the Kate project * * Copyright (C) 2016 Sven Brauch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "katemulticlipboard.h" #include "katemulticursor.h" #include "katedocument.h" #include "kateview.h" #include "kateglobal.h" #include #include #include static QString TEXT_LIST_MIME_KEY = QStringLiteral("application/x-ktexteditor-text-list"); KateMultiClipboard::KateMultiClipboard(KateMultiCursor* cursors) : m_cursors(cursors) { } void KateMultiClipboard::copyToClipboard() const { qApp->clipboard()->setMimeData(createMimeData()); } void KateMultiClipboard::pasteFromClipboard(QClipboard::Mode clipboardMode) { KTextEditor::Document::EditingTransaction t(m_cursors->doc()); auto mime = qApp->clipboard()->mimeData(clipboardMode); - if ( mime->hasFormat(TEXT_LIST_MIME_KEY) ) { + if (mime->hasFormat(TEXT_LIST_MIME_KEY)) { auto data = mime->data(TEXT_LIST_MIME_KEY); int32_t size; QDataStream s(data); s >> size; - if ( size == m_cursors->cursorsCount() ) { + if (size == m_cursors->cursorsCount()) { QVector texts; // can paste as a vector - for ( int j = 0; j < size; j++ ) { - if ( s.atEnd() ) { - qWarning() << "invalid data in clipboard (expected size" << size << "got" << j-1 << ")"; + for (int j = 0; j < size; j++) { + if (s.atEnd()) { + qWarning() << "invalid data in clipboard (expected size" << size << "got" << j - 1 << ")"; return; } QString text; s >> text; texts << text; } pasteVector(texts); // ok, we're done return; } } - if ( mime->hasText() ) { + if (mime->hasText()) { // otherwise, paste the same text everywhere // this happens if no vector data is available _or_ the length mismatches. pasteText(mime->text()); // done return; } qDebug() << "no text or text list in clipboard, no action taken"; } -void KateMultiClipboard::pasteVector(const QVector& texts) { - if ( texts.isEmpty() ) { +void KateMultiClipboard::pasteVector(const QVector& texts) +{ + if (texts.isEmpty()) { return; } m_cursors->view()->removeSelectedText(); - if ( texts.size() == m_cursors->cursorsCount() ) { + if (texts.size() == m_cursors->cursorsCount()) { auto cursors = m_cursors->cursors(); - std::sort(cursors.begin(), cursors.end(), [](const Cursor& a, const Cursor& b) { return a>b; }); + std::sort(cursors.begin(), cursors.end(), [](const Cursor & a, const Cursor & b) { + return a > b; + }); int j = 0; - Q_FOREACH ( const auto& cursor, cursors ) { + Q_FOREACH (const auto& cursor, cursors) { m_cursors->doc()->insertText(cursor, texts.at(j++)); } - } - else { + } else { // join vector QString res; - Q_FOREACH ( const auto& t, texts ) { - if ( !res.isEmpty() ) { + Q_FOREACH (const auto& t, texts) { + if (!res.isEmpty()) { res.prepend(QLatin1Char('\n')); } res.append(t); } pasteText(res); } } -void KateMultiClipboard::pasteText(const QString& text) { +void KateMultiClipboard::pasteText(const QString& text) +{ m_cursors->view()->removeSelectedText(); - Q_FOREACH ( auto c, m_cursors->movingCursors() ) { + Q_FOREACH (auto c, m_cursors->movingCursors()) { m_cursors->doc()->insertText(c->toCursor(), text); } } QMimeData* KateMultiClipboard::createMimeData() const { auto mime = new QMimeData; QByteArray data; QString textData; QVector textList; QDataStream s(&data, QIODevice::WriteOnly); auto sels = m_cursors->selections()->selections(); s << (int32_t) sels.size(); - Q_FOREACH ( const auto& sel, sels ) { + Q_FOREACH (const auto& sel, sels) { textData += textData.isEmpty() ? QString() : QLatin1String("\n"); auto text = m_cursors->doc()->text(sel, false); s << text; textData += text; textList.append(text); } KTextEditor::EditorPrivate::self()->updateClipboardHistory(textList); mime->setData(TEXT_LIST_MIME_KEY, data); mime->setText(textData); return mime; } diff --git a/src/view/katemulticlipboard.h b/src/view/katemulticlipboard.h index 6e84e685..66fe68d7 100644 --- a/src/view/katemulticlipboard.h +++ b/src/view/katemulticlipboard.h @@ -1,42 +1,43 @@ /* This file is part of the KDE and the Kate project * * Copyright (C) 2016 Sven Brauch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATEMULTICLIPBOARD_H #define KATEMULTICLIPBOARD_H #include class KateMultiCursor; class QMimeData; -class KateMultiClipboard { +class KateMultiClipboard +{ public: KateMultiClipboard(KateMultiCursor* cursors); void copyToClipboard() const; void pasteFromClipboard(QClipboard::Mode clipboardMode); void pasteVector(const QVector& texts); void pasteText(const QString& text); QMimeData* createMimeData() const; private: KateMultiCursor* m_cursors; }; #endif // KATEMULTICLIPBOARD_H diff --git a/src/view/katemulticursor.cpp b/src/view/katemulticursor.cpp index 7aa11072..b1eb0b54 100644 --- a/src/view/katemulticursor.cpp +++ b/src/view/katemulticursor.cpp @@ -1,1241 +1,1264 @@ /* This file is part of the KDE and the Kate project * * Copyright (C) 2016 Sven Brauch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "katemulticursor.h" #include "kateview.h" #include "kateviewinternal.h" #include "katehighlight.h" #include "katedocument.h" #include "kateconfig.h" #include "katepartdebug.h" #include "katelayoutcache.h" #include "cursor.h" #include class CalculatingCursor { public: // These constructors constrain their arguments to valid positions // before only the third one did, but that leads to crashs // see bug 227449 CalculatingCursor(KateViewInternal *vi) : m_vi(vi) { makeValid(); } CalculatingCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : m_cursor(c) , m_vi(vi) { makeValid(); } CalculatingCursor(KateViewInternal *vi, int line, int col) : m_cursor(line, col) , m_vi(vi) { makeValid(); } virtual ~CalculatingCursor() { } int line() const { return m_cursor.line(); } int column() const { return m_cursor.column(); } operator KTextEditor::Cursor() const { return m_cursor; } virtual CalculatingCursor &operator+=(int n) = 0; virtual CalculatingCursor &operator-=(int n) = 0; CalculatingCursor &operator++() { return operator+=(1); } CalculatingCursor &operator--() { return operator-=(1); } void makeValid() { m_cursor.setLine(qBound(0, line(), int(doc()->lines() - 1))); if (view()->wrapCursor()) { m_cursor.setColumn(qBound(0, column(), doc()->lineLength(line()))); } else { m_cursor.setColumn(qMax(0, column())); } Q_ASSERT(valid()); } void toEdge(KateViewInternal::Bias bias) { if (bias == KateViewInternal::left) { m_cursor.setColumn(0); } else if (bias == KateViewInternal::right) { m_cursor.setColumn(doc()->lineLength(line())); } } bool atEdge() const { return atEdge(KateViewInternal::left) || atEdge(KateViewInternal::right); } bool atEdge(KateViewInternal::Bias bias) const { switch (bias) { case KateViewInternal::left: return column() == 0; case KateViewInternal::none: return atEdge(); case KateViewInternal::right: return column() >= doc()->lineLength(line()); default: Q_ASSERT(false); return false; } } protected: bool valid() const { return line() >= 0 && line() < doc()->lines() && column() >= 0 && (!view()->wrapCursor() || column() <= doc()->lineLength(line())); } KTextEditor::ViewPrivate *view() { return m_vi->m_view; } const KTextEditor::ViewPrivate *view() const { return m_vi->m_view; } KTextEditor::DocumentPrivate *doc() { return view()->doc(); } const KTextEditor::DocumentPrivate *doc() const { return view()->doc(); } KTextEditor::Cursor m_cursor; KateViewInternal *m_vi; }; class BoundedCursor : public CalculatingCursor { public: BoundedCursor(KateViewInternal *vi) : CalculatingCursor(vi) {} BoundedCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : CalculatingCursor(vi, c) {} BoundedCursor(KateViewInternal *vi, int line, int col) : CalculatingCursor(vi, line, col) {} CalculatingCursor &operator+=(int n) Q_DECL_OVERRIDE { KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve valid layout for line " << line(); return *this; } const bool wrapCursor = view()->wrapCursor(); int maxColumn = -1; if (n >= 0) { for (int i = 0; i < n; i++) { if (column() >= thisLine->length()) { if (wrapCursor) { break; } else if (view()->dynWordWrap()) { // Don't go past the edge of the screen in dynamic wrapping mode if (maxColumn == -1) { maxColumn = thisLine->length() + ((m_vi->width() - thisLine->widthOfLastLine()) / m_vi->renderer()->spaceWidth()) - 1; } if (column() >= maxColumn) { m_cursor.setColumn(maxColumn); break; } m_cursor.setColumn(column() + 1); } else { m_cursor.setColumn(column() + 1); } } else { m_cursor.setColumn(thisLine->layout()->nextCursorPosition(column())); } } } else { for (int i = 0; i > n; i--) { if (column() >= thisLine->length()) { m_cursor.setColumn(column() - 1); } else if (column() == 0) { break; } else { m_cursor.setColumn(thisLine->layout()->previousCursorPosition(column())); } } } Q_ASSERT(valid()); return *this; } CalculatingCursor &operator-=(int n) Q_DECL_OVERRIDE { return operator+=(-n); } }; class WrappingCursor : public CalculatingCursor { public: WrappingCursor(KateViewInternal *vi) : CalculatingCursor(vi) {} WrappingCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : CalculatingCursor(vi, c) {} WrappingCursor(KateViewInternal *vi, int line, int col) : CalculatingCursor(vi, line, col) {} CalculatingCursor &operator+=(int n) Q_DECL_OVERRIDE { KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } if (n >= 0) { for (int i = 0; i < n; i++) { if (column() >= thisLine->length()) { // Have come to the end of a line if (line() >= doc()->lines() - 1) // Have come to the end of the document { break; } // Advance to the beginning of the next line m_cursor.setColumn(0); m_cursor.setLine(line() + 1); // Retrieve the next text range thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } continue; } m_cursor.setColumn(thisLine->layout()->nextCursorPosition(column())); } } else { for (int i = 0; i > n; i--) { if (column() == 0) { // Have come to the start of the document if (line() == 0) { break; } // Start going back to the end of the last line m_cursor.setLine(line() - 1); // Retrieve the next text range thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } // Finish going back to the end of the last line m_cursor.setColumn(thisLine->length()); continue; } if (column() > thisLine->length()) { m_cursor.setColumn(column() - 1); } else { m_cursor.setColumn(thisLine->layout()->previousCursorPosition(column())); } } } Q_ASSERT(valid()); return *this; } CalculatingCursor &operator-=(int n) Q_DECL_OVERRIDE { return operator+=(-n); } }; KateMultiCursor::KateMultiCursor(KateViewInternal* view) : m_viewInternal(view) { qDebug() << "creating new multicursor engine for view" << view; appendCursorInternal({0, 0}); Q_ASSERT(view); } -KTextEditor::ViewPrivate* KateMultiCursor::view() const { +KTextEditor::ViewPrivate* KateMultiCursor::view() const +{ return m_viewInternal->view(); } -KTextEditor::DocumentPrivate* KateMultiCursor::doc() const { +KTextEditor::DocumentPrivate* KateMultiCursor::doc() const +{ return view()->doc(); } KateViewInternal* KateMultiCursor::viewInternal() const { return m_viewInternal; } KateViewInternal* KateMultiSelection::viewInternal() const { return m_viewInternal; } -const KateMultiSelection* KateMultiCursor::selections() const { +const KateMultiSelection* KateMultiCursor::selections() const +{ return view()->selections(); } KateMultiSelection* KateMultiCursor::selections() { return view()->selections(); } -void KateMultiCursor::setPrimaryCursor(const KTextEditor::Cursor& cursor, bool repaint, bool select) { +void KateMultiCursor::setPrimaryCursor(const KTextEditor::Cursor& cursor, bool repaint, bool select) +{ qDebug() << "called" << cursor; Q_ASSERT(cursor.isValid()); - if ( cursor == primaryCursor() ) { + if (cursor == primaryCursor()) { return; } CursorRepainter rep(this, repaint); KateMultiSelection::SelectingCursorMovement sel(selections(), select); m_cursors.first()->setPosition(cursor); } -Cursors KateMultiCursor::cursors() const { +Cursors KateMultiCursor::cursors() const +{ auto cursors = secondaryCursors(); cursors.prepend(primaryCursor()); std::sort(cursors.begin(), cursors.end(), [](const KTextEditor::Cursor c1, const KTextEditor::Cursor c2) { return c1 > c2; }); return cursors; } -const QVector KateMultiCursor::movingCursors() const { +const QVector KateMultiCursor::movingCursors() const +{ QVector ret; - Q_FOREACH ( const auto& c, m_cursors ) { + Q_FOREACH (const auto& c, m_cursors) { ret.append(c); } return ret; } size_t KateMultiCursor::cursorsCount() const { Q_ASSERT(!m_cursors.isEmpty()); return m_cursors.size(); } -Cursor KateMultiCursor::primaryCursor() const { +Cursor KateMultiCursor::primaryCursor() const +{ return m_cursors.first()->toCursor(); } -Cursors KateMultiCursor::secondaryCursors() const { +Cursors KateMultiCursor::secondaryCursors() const +{ QVector cursors; cursors.reserve(m_cursors.size() - 1); - foreach ( const auto moving, m_cursors.mid(1) ) { + foreach (const auto moving, m_cursors.mid(1)) { auto cursor = moving->toCursor(); cursors.append(cursor); } return cursors; } -bool KateMultiCursor::hasSecondaryCursors() const { +bool KateMultiCursor::hasSecondaryCursors() const +{ return m_cursors.size() > 1; } void KateMultiCursor::appendCursorInternal(const KTextEditor::Cursor& cursor) { m_cursors.append(MovingCursor::Ptr(doc()->newMovingCursor(cursor, KTextEditor::MovingCursor::MoveOnInsert))); m_selections.append(MovingRange::Ptr(doc()->newMovingRange({cursor, cursor}, Kate::TextRange::ExpandLeft, Kate::TextRange::AllowEmpty))); m_selections.last()->setView(view()); m_selections.last()->setZDepth(-100000.0); Q_ASSERT(m_cursors.size() == m_selections.size()); } -bool KateMultiCursor::toggleSecondaryCursorAt(const KTextEditor::Cursor& cursor, bool ensureExists) { +bool KateMultiCursor::toggleSecondaryCursorAt(const KTextEditor::Cursor& cursor, bool ensureExists) +{ Q_ASSERT(cursor.isValid()); qDebug() << "called" << cursor << m_cursors; - if ( selections()->positionSelected(cursor) ) { + if (selections()->positionSelected(cursor)) { // cannot place secondary cursors inside a selection qDebug() << "will not place cursor inside a selection"; return false; } CursorRepainter rep(this); - Q_FOREACH ( const auto moving, m_cursors.mid(1) ) { - if ( moving->toCursor() == cursor ) { + Q_FOREACH (const auto moving, m_cursors.mid(1)) { + if (moving->toCursor() == cursor) { removeCursorInternal(moving); Q_ASSERT(!m_cursors.isEmpty()); - if ( ! ensureExists ) { + if (! ensureExists) { qDebug() << "removed secondary cursor" << cursor; return false; } } } appendCursorInternal(cursor); qDebug() << "new list of cursors:" << m_cursors; return true; } -void KateMultiCursor::clearSecondaryCursors() { +void KateMultiCursor::clearSecondaryCursors() +{ qDebug() << "clearing secondary cursors"; CursorRepainter rep(this); m_cursors.resize(1); m_selections.resize(1); qDebug() << "cursors:" << m_cursors; } -QVector KateMultiCursor::allCursors() const { +QVector KateMultiCursor::allCursors() const +{ Q_ASSERT(m_cursors.size() >= 1); Q_ASSERT(m_selections.size() == m_cursors.size()); return m_cursors; } -void KateMultiCursor::moveCursorsLeft(bool sel, int32_t chars) { +void KateMultiCursor::moveCursorsLeft(bool sel, int32_t chars) +{ qDebug() << "called" << sel << chars; CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); m_savedHorizontalPositions.clear(); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { if (! view()->wrapCursor() && cursor->column() == 0) { return; } cursor->setPosition(moveLeftRight(*cursor, -chars)); - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { break; } } } -void KateMultiCursor::moveCursorsRight(bool sel, int32_t chars) { +void KateMultiCursor::moveCursorsRight(bool sel, int32_t chars) +{ qDebug() << "called" << sel << chars; moveCursorsLeft(sel, -chars); } -void KateMultiCursor::moveCursorsUp(bool sel, int32_t chars) { +void KateMultiCursor::moveCursorsUp(bool sel, int32_t chars) +{ qDebug() << "called" << sel << chars; CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { auto x = m_savedHorizontalPositions.value(cursor.data(), -1); cursor->setPosition(moveUpDown(*cursor, -chars, x)); m_savedHorizontalPositions.insert(cursor.data(), x); qDebug() << "add cached x:" << *cursor.data() << x; - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { break; } } } -void KateMultiCursor::moveCursorsDown(bool sel, int32_t chars) { +void KateMultiCursor::moveCursorsDown(bool sel, int32_t chars) +{ qDebug() << "called" << sel << chars; moveCursorsUp(sel, -chars); } -void KateMultiCursor::moveCursorsEndOfLine(bool sel) { +void KateMultiCursor::moveCursorsEndOfLine(bool sel) +{ qDebug() << "called" << sel; m_savedHorizontalPositions.clear(); CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { cursor->setPosition(moveEnd(*cursor)); - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { return; } } } -void KateMultiCursor::moveCursorsStartOfLine(bool sel) { +void KateMultiCursor::moveCursorsStartOfLine(bool sel) +{ qDebug() << "called" << sel; m_savedHorizontalPositions.clear(); CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { cursor->setPosition(moveHome(*cursor)); - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { return; } } } -void KateMultiCursor::moveCursorsWordNext(bool sel) { +void KateMultiCursor::moveCursorsWordNext(bool sel) +{ qDebug() << "called" << sel; m_savedHorizontalPositions.clear(); CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { cursor->setPosition(moveWord(*cursor, Right)); - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { return; } } } -void KateMultiCursor::moveCursorsWordPrevious(bool sel) { +void KateMultiCursor::moveCursorsWordPrevious(bool sel) +{ qDebug() << "called" << sel; m_savedHorizontalPositions.clear(); CursorRepainter rep(this); KateMultiSelection::SelectingCursorMovement mov(selections(), sel); - Q_FOREACH ( const auto& cursor, allCursors() ) { + Q_FOREACH (const auto& cursor, allCursors()) { cursor->setPosition(moveWord(*cursor, Left)); - if ( secondaryFrozen() ) { + if (secondaryFrozen()) { return; } } } -Cursor KateMultiCursor::moveHome(const KTextEditor::Cursor& cursor) const { +Cursor KateMultiCursor::moveHome(const KTextEditor::Cursor& cursor) const +{ auto currentLayout = viewInternal()->currentLayout(cursor); if (view()->dynWordWrap() && currentLayout.startCol()) { // Allow us to go to the real start if we're already at the start of the view line if (cursor.column() != currentLayout.startCol()) { return currentLayout.start(); } } if (!doc()->config()->smartHome()) { BoundedCursor c(viewInternal(), cursor); c.toEdge(KateViewInternal::left); return static_cast(c); } Kate::TextLine l = doc()->kateTextLine(cursor.line()); if (!l) { return KTextEditor::Cursor::invalid(); } KTextEditor::Cursor c = cursor; int lc = l->firstChar(); if (lc < 0 || c.column() == lc) { c.setColumn(0); } else { c.setColumn(lc); } return c; } -Cursor KateMultiCursor::moveEnd(const KTextEditor::Cursor& cursor) const { +Cursor KateMultiCursor::moveEnd(const KTextEditor::Cursor& cursor) const +{ auto layout = viewInternal()->currentLayout(cursor); if (view()->dynWordWrap() && layout.wrap()) { // Allow us to go to the real end if we're already at the end of the view line if (cursor.column() < layout.endCol() - 1) { KTextEditor::Cursor c(cursor.line(), layout.endCol() - 1); return c; } } if (!doc()->config()->smartHome()) { BoundedCursor c(viewInternal(), cursor); c.toEdge(KateViewInternal::right); return static_cast(c); } Kate::TextLine l = doc()->kateTextLine(cursor.line()); if (!l) { return KTextEditor::Cursor::invalid(); } // "Smart End", as requested in bugs #78258 and #106970 if (cursor.column() == doc()->lineLength(cursor.line())) { KTextEditor::Cursor c = cursor; c.setColumn(l->lastChar() + 1); return c; } else { BoundedCursor bounded(viewInternal(), cursor); bounded.toEdge(KateViewInternal::right); return static_cast(bounded); } Q_UNREACHABLE(); } -Cursor KateMultiCursor::moveLeftRight(const Cursor& start, int32_t chars) const { +Cursor KateMultiCursor::moveLeftRight(const Cursor& start, int32_t chars) const +{ KTextEditor::Cursor c; if (view()->wrapCursor()) { c = WrappingCursor(viewInternal(), start) += chars; } else { c = BoundedCursor(viewInternal(), start) += chars; } return c; } -Cursor KateMultiCursor::moveUpDown(const Cursor& start, int32_t direction, int32_t& x) const { +Cursor KateMultiCursor::moveUpDown(const Cursor& start, int32_t direction, int32_t& x) const +{ qDebug() << "called" << start << direction; /** * move cursor to start/end of line, if we are at first/last line! */ auto visLine = viewInternal()->toVirtualCursor(start).line(); auto cache = viewInternal()->cache(); - if ( direction < 0 ) { - if ( visLine == 0 && (!view()->dynWordWrap() || cache->viewLine(start) == 0)) { + if (direction < 0) { + if (visLine == 0 && (!view()->dynWordWrap() || cache->viewLine(start) == 0)) { return moveHome(start); } - } - else { + } else { if ((visLine >= view()->textFolding().visibleLines() - 1) && (!view()->dynWordWrap() - || cache->viewLine(start) == cache->lastViewLine(start.line()))) - { + || cache->viewLine(start) == cache->lastViewLine(start.line()))) { return moveEnd(start); } } // This is not the first/last line because that is already simplified out above KateTextLayout thisLine = viewInternal()->currentLayout(start); KateTextLayout pRange = direction > 0 ? viewInternal()->nextLayout(start) : viewInternal()->previousLayout(start); // Ensure we're in the right spot Q_ASSERT(start.line() == thisLine.line()); Q_ASSERT(start.column() >= thisLine.startCol()); Q_ASSERT(!thisLine.wrap() || start.column() < thisLine.endCol()); auto prev_x = x == -1 ? viewInternal()->renderer()->cursorToX(thisLine, viewInternal()->toVirtualCursor(start).column()) : x; qDebug() << "use x:" << x << prev_x; auto res = viewInternal()->renderer()->xToCursor(pRange, prev_x, !view()->wrapCursor()); x = prev_x; return res; } KTextEditor::Cursor KateMultiCursor::moveWord(const KTextEditor::Cursor& cursor, KateMultiCursor::Direction dir) const { // We look up into which category the current position falls: // 1. a "word" character // 2. a "non-word" character (except space) // 3. the end of the line // and skip all following characters that fall into this class. // If the skipped characters are followed by space, we skip that too. // The code assumes that space is never part of the word character class. WrappingCursor c(viewInternal(), cursor); KateHighlighting *h = doc()->highlight(); - if ( dir == Right ) { + if (dir == Right) { if (c.atEdge(KateViewInternal::right)) { ++c; } else if (h->isInWord(doc()->line(c.line())[ c.column() ])) { while (!c.atEdge(KateViewInternal::right) && h->isInWord(doc()->line(c.line())[ c.column() ])) { ++c; } } else { while (!c.atEdge(KateViewInternal::right) - && !h->isInWord(doc()->line(c.line())[ c.column() ]) - // we must not skip space, because if that space is followed - // by more non-word characters, we would skip them, too - && !doc()->line(c.line())[ c.column() ].isSpace()) { + && !h->isInWord(doc()->line(c.line())[ c.column() ]) + // we must not skip space, because if that space is followed + // by more non-word characters, we would skip them, too + && !doc()->line(c.line())[ c.column() ].isSpace()) { ++c; } } while (!c.atEdge(KateViewInternal::right) && doc()->line(c.line())[ c.column() ].isSpace()) { ++c; } - } - else if ( dir == Left ) { + } else if (dir == Left) { if (!c.atEdge(KateViewInternal::left)) { while (!c.atEdge(KateViewInternal::left) && doc()->line(c.line())[ c.column() - 1 ].isSpace()) { --c; } } if (c.atEdge(KateViewInternal::left)) { --c; } else if (h->isInWord(doc()->line(c.line())[ c.column() - 1 ])) { while (!c.atEdge(KateViewInternal::left) && h->isInWord(doc()->line(c.line())[ c.column() - 1 ])) { --c; } } else { while (!c.atEdge(KateViewInternal::left) - && !h->isInWord(doc()->line(c.line())[ c.column() - 1 ]) - // in order to stay symmetric to wordLeft() - // we must not skip space preceding a non-word sequence - && !doc()->line(c.line())[ c.column() - 1 ].isSpace()) { + && !h->isInWord(doc()->line(c.line())[ c.column() - 1 ]) + // in order to stay symmetric to wordLeft() + // we must not skip space preceding a non-word sequence + && !doc()->line(c.line())[ c.column() - 1 ].isSpace()) { --c; } } } return c; } bool KateMultiCursor::cursorAtWordBoundary(const KTextEditor::Cursor& c) const { KateHighlighting *h = doc()->highlight(); auto line = doc()->line(c.line()); - if ( line.length() < c.column() ) { + if (line.length() < c.column()) { auto character = line.at(c.column()); return !h->isInWord(character); } return true; } -const KateMultiCursor* KateMultiSelection::cursors() const { +const KateMultiCursor* KateMultiSelection::cursors() const +{ return view()->cursors(); } -KTextEditor::ViewPrivate* KateMultiSelection::view() const { +KTextEditor::ViewPrivate* KateMultiSelection::view() const +{ return m_viewInternal->view(); } -KTextEditor::DocumentPrivate* KateMultiSelection::doc() const { +KTextEditor::DocumentPrivate* KateMultiSelection::doc() const +{ return view()->doc(); } KateMultiSelection::KateMultiSelection(KateViewInternal* view) : m_viewInternal(view) { } void KateMultiCursor::removeEncompassedSecondaryCursors(CursorSelectionFlags flags) { // join adjacent or partially-overlapping ranges bool did_remove = false; do { did_remove = false; - for ( int32_t i = 0; i < m_selections.size(); i++ ) { + for (int32_t i = 0; i < m_selections.size(); i++) { auto sel = m_selections.at(i)->toRange(); - if ( sel.isEmpty() ) { + if (sel.isEmpty()) { continue; } - for ( int32_t j = i+1; j < m_selections.size(); j++ ) { + for (int32_t j = i + 1; j < m_selections.size(); j++) { auto next = m_selections.at(j)->toRange(); KTextEditor::Range intersect; - if ( ! (intersect = sel.intersect(next)).isEmpty() ) { + if (!(intersect = sel.intersect(next)).isEmpty()) { did_remove = true; // update first to encompass both, then remove the second qDebug() << "joining ranges:" << sel << next << i << j; auto curPos = m_cursors.at(i)->toCursor(); auto newCurPos = m_cursors.at(j)->toCursor(); m_selections[i]->setRange({qMin(sel.start(), next.start()), qMax(sel.end(), next.end())}); if ( ! (flags & UseMostRecentCursorFlag) ) { // decide which cursor to keep: the one at the edge - if ( m_selections.at(i)->toRange().boundaryAtCursor(newCurPos) ) { + if (m_selections.at(i)->toRange().boundaryAtCursor(newCurPos)) { m_cursors.at(i)->setPosition(newCurPos); } - } - else { + } else { auto resultingRange = m_selections.at(i)->toRange(); qDebug() << "cursor not at boundary, adjusting" << resultingRange << curPos << newCurPos; auto newPos = KTextEditor::Cursor::invalid(); - if ( next.end() > sel.end() ) { + if (next.end() > sel.end()) { // from the right newPos = newCurPos == next.end() ? resultingRange.end() : resultingRange.start(); - } - else { + } else { // from the left newPos = newCurPos == next.start() ? resultingRange.start() : resultingRange.end(); } m_cursors.at(i)->setPosition(newPos); } removeCursorInternal(m_cursors.at(j)); j--; } } } - } while ( did_remove ); + } while (did_remove); } void KateMultiCursor::removeDuplicateCursors() { qDebug() << "called"; // do not consider primary cursors in frozen mode auto start = secondaryFrozen() ? 1 : 0; - for ( size_t i = start; i < m_cursors.size(); i++ ) { - for ( size_t j = start; j < i; j++ ) { - if ( m_cursors.at(i)->toCursor() == m_cursors.at(j)->toCursor() ) { + for (size_t i = start; i < m_cursors.size(); i++) { + for (size_t j = start; j < i; j++) { + if (m_cursors.at(i)->toCursor() == m_cursors.at(j)->toCursor()) { qDebug() << "removing duplicate cursor" << *m_cursors.at(j), - removeCursorInternal(m_cursors.at(j)); + removeCursorInternal(m_cursors.at(j)); j--; i--; } } } Q_ASSERT(!m_cursors.isEmpty()); Q_ASSERT(m_cursors.size() == m_selections.size()); } void KateMultiCursor::removeCursorInternal(const MovingCursor::Ptr& cursor) { qDebug() << "removing cursor" << *cursor; Q_ASSERT(m_cursors.contains(cursor)); auto index = m_cursors.indexOf(cursor); m_cursors.remove(index); m_selections.remove(index); Q_ASSERT(m_cursors.size() == m_selections.size()); Q_ASSERT(m_cursors.size() >= 1); } KTextEditor::Cursor KateMultiCursor::toVirtualCursor(const KTextEditor::Cursor& c) const { return viewInternal()->toVirtualCursor(c); } void KateMultiSelection::clearSelectionIfNotPersistent() { - if ( ! view()->config()->persistentSelection() ) { + if (! view()->config()->persistentSelection()) { clearSelection(); } } void KateMultiSelection::clearCursorsInternal() { cursors()->m_cursors.clear(); cursors()->m_selections.clear(); } KTextEditor::MovingRange::Ptr KateMultiSelection::addSelectionInternal(const KTextEditor::Range& newSelection, const Cursor& newCursor) { qDebug() << "called" << newSelection << newCursor; Q_ASSERT(newCursor.isValid()); cursors()->appendCursorInternal(newCursor); auto sel = cursors()->m_selections.last(); sel->setRange(newSelection); return sel; } void KateMultiSelection::setSelection(const KTextEditor::Range& selection, const KTextEditor::Cursor& cursor) { if ( selection.isEmpty() ) { clearSelectionInternal(); return; } auto newCursor = cursor.isValid() ? cursor : selection.end(); - setSelection(QVector{selection}, QVector{newCursor}); + setSelection(QVector {selection}, QVector {newCursor}); }; -void KateMultiSelection::setSelection(const QVector& selection, const QVector& newCursors) { +void KateMultiSelection::setSelection(const QVector& selection, const QVector& newCursors) +{ Q_ASSERT(selection.size() == newCursors.size()); Q_ASSERT(!selection.isEmpty()); KateMultiCursor::CursorRepainter rep(cursors()); clearCursorsInternal(); - for ( size_t i = 0; i < selection.size(); i++ ) { + for (size_t i = 0; i < selection.size(); i++) { auto cursor = newCursors.at(i).isValid() ? newCursors.at(i) : selection.at(i).end(); addSelectionInternal(selection.at(i), cursor); } qDebug() << "new selections:" << selections(); }; -KateMultiCursor* KateMultiSelection::cursors() { +KateMultiCursor* KateMultiSelection::cursors() +{ return m_viewInternal->cursors(); } size_t KateMultiCursor::indexOfCursor(const KTextEditor::Cursor& cursor) const { - for ( size_t i = 0; i < m_cursors.size(); i++ ) { - if ( m_cursors.at(i)->toCursor() == cursor ) { + for (size_t i = 0; i < m_cursors.size(); i++) { + if (m_cursors.at(i)->toCursor() == cursor) { return i; } } return -1; } void KateMultiSelection::doSelectWithCursorInternal(const KTextEditor::Range& select, size_t cursorIndex) { auto adjacentRange = cursors()->m_selections.at(cursorIndex); auto adjacent = adjacentRange->toRange(); KTextEditor::Range intersect; - if ( !adjacentRange->isEmpty() && !(intersect = adjacentRange->toRange().intersect(select)).isEmpty() ) { + if (!adjacentRange->isEmpty() && !(intersect = adjacentRange->toRange().intersect(select)).isEmpty()) { // there is an ajdacent range, toggle or shrink it - if ( adjacent.contains(select) ) { + if (adjacent.contains(select)) { // case 1: only shrink the adjacent range - if ( adjacent.start() == select.start() ) { + if (adjacent.start() == select.start()) { adjacentRange->setRange({select.end(), adjacent.end()}); - } - else { + } else { adjacentRange->setRange({adjacent.start(), select.start()}); } - } - else { + } else { // case 2: toggle overlapped region - if ( adjacent.start() == select.start() ) { + if (adjacent.start() == select.start()) { adjacentRange->setRange({adjacent.end(), select.end()}); - } - else { + } else { adjacentRange->setRange({select.start(), adjacent.start()}); } } - } - else if ( adjacentRange->isEmpty() ) { + } else if (adjacentRange->isEmpty()) { // new selection adjacentRange->setRange(select); - } - else { + } else { // grow selection adjacentRange->setRange({qMin(adjacentRange->start().toCursor(), select.start()), qMax(adjacentRange->end().toCursor(), select.end())}); } } KTextEditor::MovingRange::Ptr KateMultiSelection::selectionForCursor(const KTextEditor::Cursor& cursor) const { auto index = cursors()->indexOfCursor(cursor); Q_ASSERT(index != -1); return cursors()->m_selections.at(index); } -KTextEditor::Range KateMultiSelection::primarySelection() const { +KTextEditor::Range KateMultiSelection::primarySelection() const +{ return *cursors()->m_selections.first(); } -Selections KateMultiSelection::selections() const { +Selections KateMultiSelection::selections() const +{ Q_ASSERT(cursors()->m_selections.size() == cursors()->cursorsCount()); Selections ret; ret.reserve(cursors()->m_selections.size()); - Q_FOREACH ( const auto& r, cursors()->m_selections ) { + Q_FOREACH (const auto& r, cursors()->m_selections) { ret.append(r->toRange()); } return ret; } -bool KateMultiSelection::hasMultipleSelections() const { +bool KateMultiSelection::hasMultipleSelections() const +{ auto s = cursors()->m_selections; - return std::count_if(s.begin(), s.end(), [](const KTextEditor::MovingRange::Ptr& r) { + return std::count_if(s.begin(), s.end(), [](const KTextEditor::MovingRange::Ptr & r) { return !r->isEmpty(); }) > 1; } -bool KateMultiSelection::hasSelections() const { +bool KateMultiSelection::hasSelections() const +{ auto s = cursors()->m_selections; - return std::find_if(s.begin(), s.end(), [](const KTextEditor::MovingRange::Ptr& r) { + return std::find_if(s.begin(), s.end(), [](const KTextEditor::MovingRange::Ptr & r) { return !r->isEmpty(); }) != s.end(); } bool KateMultiSelection::positionSelected(const KTextEditor::Cursor &cursor) const { KTextEditor::Cursor ret = cursor; if ((!view()->blockSelection()) && (ret.column() < 0)) { ret.setColumn(0); } auto s = cursors()->m_selections; return std::any_of(s.begin(), s.end(), [&cursor](const KTextEditor::MovingRange::Ptr r) { return r->toRange().contains(cursor); }); } bool KateMultiSelection::lineSelected(int line) const { auto s = cursors()->m_selections; return !view()->blockSelection() && std::any_of(s.begin(), s.end(), [line](const KTextEditor::MovingRange::Ptr r) { return r->toRange().containsLine(line); } ); } -void KateMultiSelection::clearSelection() { +void KateMultiSelection::clearSelection() +{ KateMultiCursor::CursorRepainter rep(cursors()); clearSelectionInternal(); } void KateMultiSelection::clearSelectionInternal() { qDebug() << " *** clearing selections"; - Q_FOREACH ( auto& s, cursors()->m_selections ) { + Q_FOREACH (auto& s, cursors()->m_selections) { s->setRange(KTextEditor::Range::invalid()); } } bool KateMultiSelection::lineEndSelected(const KTextEditor::Cursor &lineEndPos) const { auto s = cursors()->m_selections; return !view()->blockSelection() && std::any_of(s.begin(), s.end(), [&lineEndPos](const KTextEditor::MovingRange::Ptr r) { return (lineEndPos.line() > r->start().line() || (lineEndPos.line() == r->start().line() && (r->start().column() < lineEndPos.column() || lineEndPos.column() == -1))) && (lineEndPos.line() < r->end().line() || (lineEndPos.line() == r->end().line() && (lineEndPos.column() <= r->end().column() && lineEndPos.column() != -1))); } ); } bool KateMultiSelection::lineHasSelection(int line) const { auto s = cursors()->m_selections; return std::any_of(s.begin(), s.end(), [line](const KTextEditor::MovingRange::Ptr r) { return r->toRange().containsLine(line); }); } -bool KateMultiSelection::overlapsLine(int line) const { +bool KateMultiSelection::overlapsLine(int line) const +{ auto s = cursors()->m_selections; return std::any_of(s.begin(), s.end(), [line](const KTextEditor::MovingRange::Ptr r) { return r->toRange().overlapsLine(line); }); } void KateMultiSelection::selectEntityAt(const KTextEditor::Cursor& cursor, KTextEditor::MovingRange::Ptr update, KateMultiSelection::SelectionMode kind) { - if ( kind == Mouse ) { + if (kind == Mouse) { return; } - if ( kind == Word ) { + if (kind == Word) { update->setRange(view()->document()->wordRangeAt(cursor)); } - if ( kind == Line ) { - update->setRange({cursor.line(), 0, cursor.line()+1, 0}); + if (kind == Line) { + update->setRange({cursor.line(), 0, cursor.line() + 1, 0}); } } void KateMultiSelection::beginNewSelection(const KTextEditor::Cursor& fromCursor, KateMultiSelection::SelectionMode mode, KateMultiSelection::SelectionFlags flags) { qDebug() << "called" << fromCursor << mode << flags; KateMultiCursor::CursorRepainter rep(cursors()); m_activeSelectionMode = mode; - if ( flags & AddNewCursor ) { + if (flags & AddNewCursor) { cursors()->appendCursorInternal(fromCursor); - } - else { + } else { cursors()->clearSecondaryCursors(); cursors()->m_cursors.last()->setPosition(fromCursor); - if ( ! (flags & KeepSelectionRange) ) { + if (!(flags & KeepSelectionRange)) { cursors()->m_selections.last()->setRange({fromCursor, fromCursor}); } } m_activeSelectingCursor = cursors()->m_cursors.last(); selectEntityAt(fromCursor, cursors()->m_selections.last(), m_activeSelectionMode); m_activeSelectingCursor->setPosition(cursors()->m_selections.last()->end()); auto newSelection = cursors()->m_selections.last()->toRange(); - Q_FOREACH ( const auto& c, cursors()->m_cursors ) { + Q_FOREACH (const auto& c, cursors()->m_cursors) { auto cur = c->toCursor(); - if ( newSelection.contains(cur) && !newSelection.boundaryAtCursor(cur) ) { + if (newSelection.contains(cur) && !newSelection.boundaryAtCursor(cur)) { // The new selection contains a cursor which existed previously. // Remove that. cursors()->removeCursorInternal(c); } } } void KateMultiSelection::updateNewSelection(const KTextEditor::Cursor& cursor) { qDebug() << "called" << cursor; auto selection = cursors()->m_selections.last(); Q_ASSERT(m_activeSelectionMode != None); Q_ASSERT(!m_activeSelectingCursor.isNull()); Q_ASSERT(m_activeSelectingCursor->isValid()); Q_ASSERT(selection->isEmpty() || selection->toRange().boundaryAtCursor(*m_activeSelectingCursor)); auto oldPos = m_activeSelectingCursor->toCursor(); - if ( oldPos == cursor ) { + if (oldPos == cursor) { return; } KateMultiCursor::CursorRepainter rep(cursors()); SelectingCursorMovement sel(this, true, true); m_activeSelectingCursor->setPosition(cursor); - if ( m_activeSelectionMode == Word && !cursors()->cursorAtWordBoundary(cursor) ) { + if (m_activeSelectionMode == Word && !cursors()->cursorAtWordBoundary(cursor)) { auto moved = cursors()->moveWord(cursor, oldPos < cursor ? KateMultiCursor::Left : KateMultiCursor::Right); m_activeSelectingCursor->setPosition(moved); - } - else if ( m_activeSelectionMode == Line ) { + } else if (m_activeSelectionMode == Line) { m_activeSelectingCursor->setColumn(oldPos < cursor ? 0 : doc()->lineLength(cursor.line())); } } bool KateMultiSelection::currentlySelecting() const { return m_activeSelectionMode != None; } KateMultiSelection::SelectionMode KateMultiSelection::activeSelectionMode() const { return m_activeSelectionMode; } void KateMultiSelection::finishNewSelection() { qDebug() << "called"; m_activeSelectionMode = None; m_activeSelectingCursor.clear(); KateMultiCursor::CursorRepainter rep(cursors()); cursors()->removeEncompassedSecondaryCursors(KateMultiCursor::UseMostRecentCursorFlag); } KateMultiSelection::SelectingCursorMovement::SelectingCursorMovement(KateMultiSelection* selections, bool isSelecting, bool allowDuplicates) : m_selections(selections) , m_isSelecting(isSelecting) , m_allowDuplicates(allowDuplicates) { Q_ASSERT(selections); - if ( m_isSelecting ) { + if (m_isSelecting) { m_oldPositions = currentPositions(); // always unfreeze when selecting m_selections->cursors()->setSecondaryFrozen(false); - } - else { + } else { // if moving without selecting, clear the selection m_selections->clearSelectionIfNotPersistent(); } } -KateMultiSelection::SelectingCursorMovement::PositionMap KateMultiSelection::SelectingCursorMovement::currentPositions() const { +KateMultiSelection::SelectingCursorMovement::PositionMap KateMultiSelection::SelectingCursorMovement::currentPositions() const +{ PositionMap ret; - Q_FOREACH ( const auto c, m_selections->cursors()->movingCursors() ) { + Q_FOREACH (const auto c, m_selections->cursors()->movingCursors()) { ret.insert(c, c->toCursor()); } return ret; } KateMultiSelection::SelectingCursorMovement::~SelectingCursorMovement() { - if ( ! m_isSelecting ) { + if (! m_isSelecting) { m_selections->cursors()->removeDuplicateCursors(); return; } auto newPositions = currentPositions(); Q_ASSERT(newPositions.size() == m_oldPositions.size()); - if ( newPositions.size() != m_oldPositions.size() ) { + if (newPositions.size() != m_oldPositions.size()) { qWarning() << "cursor count changed across movement, not modifying selection"; return; } - Q_FOREACH ( const auto& cursor, m_oldPositions.keys() ) { + Q_FOREACH (const auto& cursor, m_oldPositions.keys()) { auto old = m_oldPositions.value(cursor); auto current = newPositions.value(cursor); qDebug() << "cursor moved:" << old << " -> " << current; auto range = KTextEditor::Range(qMin(old, current), qMax(old, current)); m_selections->doSelectWithCursorInternal(range, m_selections->cursors()->m_cursors.indexOf(cursor)); } - if ( ! m_allowDuplicates ) { + if (! m_allowDuplicates) { m_selections->cursors()->removeEncompassedSecondaryCursors(); } qDebug() << "** selections after cursor movement:" << m_selections->selections(); } KateMultiCursor::CursorRepainter::CursorRepainter(KateMultiCursor* cursors, bool repaint) : m_initialAffectedLines() , m_cursors(cursors) , m_repaint(repaint) , m_primary(cursors->primaryCursor()) { - if ( ! m_repaint ) { + if (! m_repaint) { return; } - Q_FOREACH ( const auto& c, cursors->cursors() ) { + Q_FOREACH (const auto& c, cursors->cursors()) { m_initialAffectedLines.append(cursors->toVirtualCursor(c)); } - Q_FOREACH ( auto range, cursors->selections()->selections() ) { - if ( ! range.isValid() ) { + Q_FOREACH (auto range, cursors->selections()->selections()) { + if (! range.isValid()) { continue; } // adding superfluous items here is cheap; repainting // is done only once for each affected line in the end - for ( int32_t line = range.start().line(); line <= range.end().line(); line++ ) { + for (int32_t line = range.start().line(); line <= range.end().line(); line++) { m_initialAffectedLines.append({line, 0}); } } } KateMultiCursor::CursorRepainter::~CursorRepainter() { - if ( m_cursors->primaryCursor() != m_primary ) { + if (m_cursors->primaryCursor() != m_primary) { m_cursors->viewInternal()->notifyPrimaryCursorChanged(m_cursors->primaryCursor()); } - if ( ! m_repaint ) { + if (! m_repaint) { return; } QVector resulting = m_initialAffectedLines; - Q_FOREACH ( const auto& cursor, m_cursors->cursors() ) { + Q_FOREACH (const auto& cursor, m_cursors->cursors()) { Q_ASSERT(cursor.isValid()); auto viewCursor = m_cursors->toVirtualCursor(cursor); - if ( !resulting.contains(viewCursor) ) { + if (!resulting.contains(viewCursor)) { resulting.append(viewCursor); } } - Q_FOREACH ( auto range, m_cursors->selections()->selections() ) { - if ( ! range.isValid() ) { + Q_FOREACH (auto range, m_cursors->selections()->selections()) { + if (! range.isValid()) { continue; } // TODO: only repaint changed selections - for ( int32_t line = range.start().line(); line <= range.end().line(); line++ ) { + for (int32_t line = range.start().line(); line <= range.end().line(); line++) { auto pos = Cursor{line, 0}; - if ( pos.isValid() && ! resulting.contains(pos) ) { + if (pos.isValid() && ! resulting.contains(pos)) { resulting.append(pos); } } } qDebug() << "repaint:" << resulting; m_cursors->viewInternal()->notifyLinesUpdated(resulting); } diff --git a/src/view/katemulticursor.h b/src/view/katemulticursor.h index 76cedd10..9efc5e29 100644 --- a/src/view/katemulticursor.h +++ b/src/view/katemulticursor.h @@ -1,242 +1,250 @@ /* This file is part of the KDE and the Kate project * * Copyright (C) 2016 Sven Brauch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATEMULTICURSOR_H #define KATEMULTICURSOR_H #include "ktexteditor/movingrange.h" -namespace KTextEditor { - class DocumentPrivate; - class ViewPrivate; +namespace KTextEditor +{ +class DocumentPrivate; +class ViewPrivate; } class KateMultiSelection; class KateViewInternal; using KTextEditor::MovingCursor; using KTextEditor::MovingRange; using KTextEditor::Cursor; using Cursors = QVector; using Selections = QVector; -class KTEXTEDITOR_EXPORT KateMultiCursor { +class KTEXTEDITOR_EXPORT KateMultiCursor +{ public: friend class KateMultiSelection; KateMultiCursor(KateViewInternal* view); Cursors cursors() const; const QVector movingCursors() const; Cursor primaryCursor() const; Cursors secondaryCursors() const; bool hasSecondaryCursors() const; size_t cursorsCount() const; - void setPrimaryCursor(const Cursor& cursor, bool repaint=true, bool select=false); + void setPrimaryCursor(const Cursor& cursor, bool repaint = true, bool select = false); - bool toggleSecondaryCursorAt(const Cursor& cursor, bool ensureExists=false); + bool toggleSecondaryCursorAt(const Cursor& cursor, bool ensureExists = false); void clearSecondaryCursors(); - void moveCursorsLeft(bool select=false, int32_t chars=1); - void moveCursorsRight(bool select=false, int32_t chars=1); - void moveCursorsUp(bool select=false, int32_t chars=1); - void moveCursorsDown(bool select=false, int32_t chars=1); - void moveCursorsEndOfLine(bool select=false); - void moveCursorsStartOfLine(bool select=false); - void moveCursorsWordPrevious(bool select=false); - void moveCursorsWordNext(bool select=false); - - bool secondaryFrozen() const { + void moveCursorsLeft(bool select = false, int32_t chars = 1); + void moveCursorsRight(bool select = false, int32_t chars = 1); + void moveCursorsUp(bool select = false, int32_t chars = 1); + void moveCursorsDown(bool select = false, int32_t chars = 1); + void moveCursorsEndOfLine(bool select = false); + void moveCursorsStartOfLine(bool select = false); + void moveCursorsWordPrevious(bool select = false); + void moveCursorsWordNext(bool select = false); + + bool secondaryFrozen() const + { return m_secondaryFrozen; } - void toggleSecondaryFrozen() { + void toggleSecondaryFrozen() + { return setSecondaryFrozen(!m_secondaryFrozen); } /** * @brief Freeze secondary cursors. * * This means they will not move when the user navigates the * primary cursor. Typing or removing chars automatically unfreezes * cursors. * * @param frozen true to freeze, false to unfreeze. */ - void setSecondaryFrozen(bool frozen) { + void setSecondaryFrozen(bool frozen) + { m_secondaryFrozen = frozen; }; const KateMultiSelection* selections() const; KateMultiSelection* selections(); protected: enum Direction { Left = -1, None = 0, Right = +1 }; /// Cursor transformations. Functions to calculate where /// a given cursor moves under a certain operation. Cursor moveLeftRight(const Cursor& c, int32_t chars) const; Cursor moveUpDown(const Cursor& c, int32_t direction, int32_t& xpos) const; Cursor moveWord(const Cursor& c, Direction dir) const; bool cursorAtWordBoundary(const Cursor& c) const; Cursor moveHome(const Cursor& c) const; Cursor moveEnd(const Cursor& c) const; public: KTextEditor::ViewPrivate* view() const; KateViewInternal* viewInternal() const; KTextEditor::DocumentPrivate* doc() const; private: size_t indexOfCursor(const KTextEditor::Cursor& cursor) const; private: KateViewInternal* m_viewInternal = nullptr; QVector m_cursors; // It is guaranteed that this always contains exactly one selection // for each cursor, and in the same order. QVector m_selections; QMap m_savedHorizontalPositions; bool m_secondaryFrozen = false; private: QVector allCursors() const; void appendCursorInternal(const Cursor& cursor); void removeCursorInternal(const MovingCursor::Ptr& cursor); enum CursorSelectionFlags { NoFlags = 0x0, UseMostRecentCursorFlag = 0x1 }; - void removeEncompassedSecondaryCursors(CursorSelectionFlags flags=NoFlags); + void removeEncompassedSecondaryCursors(CursorSelectionFlags flags = NoFlags); void removeDuplicateCursors(); private: KTextEditor::Cursor toVirtualCursor(const KTextEditor::Cursor& c) const; public: - class CursorRepainter { + class CursorRepainter + { public: - CursorRepainter(KateMultiCursor* cursors, bool repaint=true); + CursorRepainter(KateMultiCursor* cursors, bool repaint = true); ~CursorRepainter(); private: QVector m_initialAffectedLines; KateMultiCursor* m_cursors; const bool m_repaint; Cursor m_primary; }; friend class CursorRepainter; }; -class KTEXTEDITOR_EXPORT KateMultiSelection { +class KTEXTEDITOR_EXPORT KateMultiSelection +{ public: KateMultiSelection(KateViewInternal* view); KTextEditor::Range primarySelection() const; bool hasMultipleSelections() const; bool hasSelections() const; Selections selections() const; - void setSelection(const KTextEditor::Range& selection, const Cursor& cursor=Cursor::invalid()); + void setSelection(const KTextEditor::Range& selection, const Cursor& cursor = Cursor::invalid()); void setSelection(const QVector& selection, const QVector& cursors); void clearSelection(); void clearSelectionIfNotPersistent(); // Mouse selection enum SelectionMode { None, Mouse, Word, Line }; enum SelectionFlags { UsePrimaryCursor = 0x1, AddNewCursor = 0x2, KeepSelectionRange = 0x4 }; - void beginNewSelection(const Cursor& fromCursor, SelectionMode mode=Mouse, SelectionFlags flags=UsePrimaryCursor); + void beginNewSelection(const Cursor& fromCursor, SelectionMode mode = Mouse, SelectionFlags flags = UsePrimaryCursor); void updateNewSelection(const Cursor& cursor); void finishNewSelection(); bool currentlySelecting() const; SelectionMode activeSelectionMode() const; public: bool positionSelected(const Cursor& cursor) const; bool lineSelected(int line) const; bool lineEndSelected(const Cursor& lineEnd) const; bool lineHasSelection(int line) const; bool overlapsLine(int line) const; private: const KateMultiCursor* cursors() const; KateMultiCursor* cursors(); KTextEditor::ViewPrivate* view() const; KateViewInternal* viewInternal() const; KTextEditor::DocumentPrivate* doc() const; KTextEditor::MovingRange::Ptr selectionForCursor(const KTextEditor::Cursor& cursor) const; KTextEditor::MovingRange::Ptr addSelectionInternal(const KTextEditor::Range& range, const Cursor& cursor); void doSelectWithCursorInternal(const KTextEditor::Range& range, size_t cursorIndex); void selectEntityAt(const Cursor& cursor, KTextEditor::MovingRange::Ptr update, SelectionMode kind); /** * @brief Clear the selection, i.e. set all selection ranges to empty. */ void clearSelectionInternal(); /** * @brief Removes *all* cursors and selections, including the primary cursor. * Make sure to add at least one new cursor after calling this. */ void clearCursorsInternal(); private: KateViewInternal* m_viewInternal = nullptr; private: // members for mouse selection SelectionMode m_activeSelectionMode = None; KTextEditor::MovingCursor::Ptr m_activeSelectingCursor; public: - class SelectingCursorMovement { + class SelectingCursorMovement + { public: - SelectingCursorMovement(KateMultiSelection* selections, bool isSelecting=true, bool allowDuplicates=false); + SelectingCursorMovement(KateMultiSelection* selections, bool isSelecting = true, bool allowDuplicates = false); ~SelectingCursorMovement(); private: using PositionMap = QMap; KateMultiSelection* m_selections; bool m_isSelecting; PositionMap m_oldPositions; PositionMap currentPositions() const; bool m_allowDuplicates; }; friend class SelectingCursorMovement; friend class CursorRepainter; }; #endif // KATEMULTICURSOR_H