File indexing completed on 2024-04-28 11:45:28

0001 /*
0002     SPDX-FileCopyrightText: 2011 Dominik Haumann <dhaumann@kde.org>
0003     SPDX-FileCopyrightText: 2009-2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0004     SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org>
0005     SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0006     SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "kateundo.h"
0012 
0013 #include "katedocument.h"
0014 #include "kateundomanager.h"
0015 #include "kateview.h"
0016 
0017 #include <ktexteditor/cursor.h>
0018 #include <ktexteditor/view.h>
0019 
0020 KateUndo::KateUndo(KTextEditor::DocumentPrivate *document)
0021     : m_document(document)
0022 {
0023 }
0024 
0025 KateEditInsertTextUndo::KateEditInsertTextUndo(KTextEditor::DocumentPrivate *document, int line, int col, const QString &text)
0026     : KateUndo(document)
0027     , m_line(line)
0028     , m_col(col)
0029     , m_text(text)
0030 {
0031 }
0032 
0033 KateEditRemoveTextUndo::KateEditRemoveTextUndo(KTextEditor::DocumentPrivate *document, int line, int col, const QString &text)
0034     : KateUndo(document)
0035     , m_line(line)
0036     , m_col(col)
0037     , m_text(text)
0038 {
0039 }
0040 
0041 KateEditWrapLineUndo::KateEditWrapLineUndo(KTextEditor::DocumentPrivate *document, int line, int col, int len, bool newLine)
0042     : KateUndo(document)
0043     , m_line(line)
0044     , m_col(col)
0045     , m_len(len)
0046     , m_newLine(newLine)
0047 {
0048 }
0049 
0050 KateEditUnWrapLineUndo::KateEditUnWrapLineUndo(KTextEditor::DocumentPrivate *document, int line, int col, int len, bool removeLine)
0051     : KateUndo(document)
0052     , m_line(line)
0053     , m_col(col)
0054     , m_len(len)
0055     , m_removeLine(removeLine)
0056 {
0057 }
0058 
0059 KateEditInsertLineUndo::KateEditInsertLineUndo(KTextEditor::DocumentPrivate *document, int line, const QString &text)
0060     : KateUndo(document)
0061     , m_line(line)
0062     , m_text(text)
0063 {
0064 }
0065 
0066 KateEditRemoveLineUndo::KateEditRemoveLineUndo(KTextEditor::DocumentPrivate *document, int line, const QString &text)
0067     : KateUndo(document)
0068     , m_line(line)
0069     , m_text(text)
0070 {
0071 }
0072 
0073 bool KateUndo::isEmpty() const
0074 {
0075     return false;
0076 }
0077 
0078 bool KateEditInsertTextUndo::isEmpty() const
0079 {
0080     return len() == 0;
0081 }
0082 
0083 bool KateEditRemoveTextUndo::isEmpty() const
0084 {
0085     return len() == 0;
0086 }
0087 
0088 bool KateUndo::mergeWith(const KateUndo * /*undo*/)
0089 {
0090     return false;
0091 }
0092 
0093 bool KateEditInsertTextUndo::mergeWith(const KateUndo *undo)
0094 {
0095     // we can do a hard cast, we ensure we are only called with the same types on the outside
0096     Q_ASSERT(type() == undo->type());
0097     const KateEditInsertTextUndo *u = static_cast<const KateEditInsertTextUndo *>(undo);
0098     if (m_line == u->m_line && (m_col + len()) == u->m_col) {
0099         m_text += u->m_text;
0100         return true;
0101     }
0102 
0103     return false;
0104 }
0105 
0106 bool KateEditRemoveTextUndo::mergeWith(const KateUndo *undo)
0107 {
0108     // we can do a hard cast, we ensure we are only called with the same types on the outside
0109     Q_ASSERT(type() == undo->type());
0110     const KateEditRemoveTextUndo *u = static_cast<const KateEditRemoveTextUndo *>(undo);
0111     if (m_line == u->m_line && m_col == (u->m_col + u->len())) {
0112         m_text.prepend(u->m_text);
0113         m_col = u->m_col;
0114         return true;
0115     }
0116 
0117     return false;
0118 }
0119 
0120 void KateEditInsertTextUndo::undo()
0121 {
0122     KTextEditor::DocumentPrivate *doc = document();
0123 
0124     doc->editRemoveText(m_line, m_col, len());
0125 }
0126 
0127 void KateEditRemoveTextUndo::undo()
0128 {
0129     KTextEditor::DocumentPrivate *doc = document();
0130 
0131     doc->editInsertText(m_line, m_col, m_text);
0132 }
0133 
0134 void KateEditWrapLineUndo::undo()
0135 {
0136     KTextEditor::DocumentPrivate *doc = document();
0137 
0138     doc->editUnWrapLine(m_line, m_newLine, m_len);
0139 }
0140 
0141 void KateEditUnWrapLineUndo::undo()
0142 {
0143     KTextEditor::DocumentPrivate *doc = document();
0144 
0145     doc->editWrapLine(m_line, m_col, m_removeLine);
0146 }
0147 
0148 void KateEditInsertLineUndo::undo()
0149 {
0150     KTextEditor::DocumentPrivate *doc = document();
0151 
0152     doc->editRemoveLine(m_line);
0153 }
0154 
0155 void KateEditRemoveLineUndo::undo()
0156 {
0157     KTextEditor::DocumentPrivate *doc = document();
0158 
0159     doc->editInsertLine(m_line, m_text);
0160 }
0161 
0162 void KateEditMarkLineAutoWrappedUndo::undo()
0163 {
0164     KTextEditor::DocumentPrivate *doc = document();
0165 
0166     doc->editMarkLineAutoWrapped(m_line, m_autowrapped);
0167 }
0168 
0169 void KateEditRemoveTextUndo::redo()
0170 {
0171     KTextEditor::DocumentPrivate *doc = document();
0172 
0173     doc->editRemoveText(m_line, m_col, len());
0174 }
0175 
0176 void KateEditInsertTextUndo::redo()
0177 {
0178     KTextEditor::DocumentPrivate *doc = document();
0179 
0180     doc->editInsertText(m_line, m_col, m_text);
0181 }
0182 
0183 void KateEditUnWrapLineUndo::redo()
0184 {
0185     KTextEditor::DocumentPrivate *doc = document();
0186 
0187     doc->editUnWrapLine(m_line, m_removeLine, m_len);
0188 }
0189 
0190 void KateEditWrapLineUndo::redo()
0191 {
0192     KTextEditor::DocumentPrivate *doc = document();
0193 
0194     doc->editWrapLine(m_line, m_col, m_newLine);
0195 }
0196 
0197 void KateEditRemoveLineUndo::redo()
0198 {
0199     KTextEditor::DocumentPrivate *doc = document();
0200 
0201     doc->editRemoveLine(m_line);
0202 }
0203 
0204 void KateEditInsertLineUndo::redo()
0205 {
0206     KTextEditor::DocumentPrivate *doc = document();
0207 
0208     doc->editInsertLine(m_line, m_text);
0209 }
0210 
0211 void KateEditMarkLineAutoWrappedUndo::redo()
0212 {
0213     KTextEditor::DocumentPrivate *doc = document();
0214 
0215     doc->editMarkLineAutoWrapped(m_line, m_autowrapped);
0216 }
0217 
0218 KateUndoGroup::KateUndoGroup(KateUndoManager *manager,
0219                              const KTextEditor::Cursor cursorPosition,
0220                              KTextEditor::Range selection,
0221                              const QVector<KTextEditor::ViewPrivate::PlainSecondaryCursor> &secondary)
0222     : m_manager(manager)
0223     , m_undoSelection(selection)
0224     , m_redoSelection(-1, -1, -1, -1)
0225     , m_undoCursor(cursorPosition)
0226     , m_undoSecondaryCursors(secondary)
0227     , m_redoCursor(-1, -1)
0228 {
0229 }
0230 
0231 KateUndoGroup::~KateUndoGroup()
0232 {
0233     qDeleteAll(m_items);
0234 }
0235 
0236 void KateUndoGroup::undo(KTextEditor::ViewPrivate *view)
0237 {
0238     if (m_items.isEmpty()) {
0239         return;
0240     }
0241 
0242     m_manager->startUndo();
0243 
0244     for (int i = m_items.size() - 1; i >= 0; --i) {
0245         m_items[i]->undo();
0246     }
0247 
0248     if (view != nullptr) {
0249         if (m_undoSelection.isValid()) {
0250             view->setSelection(m_undoSelection);
0251         } else {
0252             view->removeSelection();
0253         }
0254         view->clearSecondaryCursors();
0255         view->addSecondaryCursorsWithSelection(m_undoSecondaryCursors);
0256 
0257         if (m_undoCursor.isValid()) {
0258             view->setCursorPosition(m_undoCursor);
0259         }
0260     }
0261 
0262     m_manager->endUndo();
0263 }
0264 
0265 void KateUndoGroup::redo(KTextEditor::ViewPrivate *view)
0266 {
0267     if (m_items.isEmpty()) {
0268         return;
0269     }
0270 
0271     m_manager->startUndo();
0272 
0273     for (int i = 0; i < m_items.size(); ++i) {
0274         m_items[i]->redo();
0275     }
0276 
0277     if (view != nullptr) {
0278         if (m_redoSelection.isValid()) {
0279             view->setSelection(m_redoSelection);
0280         } else {
0281             view->removeSelection();
0282         }
0283         view->clearSecondaryCursors();
0284         view->addSecondaryCursorsWithSelection(m_redoSecondaryCursors);
0285 
0286         if (m_redoCursor.isValid()) {
0287             view->setCursorPosition(m_redoCursor);
0288         }
0289     }
0290 
0291     m_manager->endUndo();
0292 }
0293 
0294 void KateUndoGroup::editEnd(const KTextEditor::Cursor cursorPosition,
0295                             KTextEditor::Range selectionRange,
0296                             const QVector<KTextEditor::ViewPrivate::PlainSecondaryCursor> &secondaryCursors)
0297 {
0298     m_redoCursor = cursorPosition;
0299     m_redoSecondaryCursors = secondaryCursors;
0300     m_redoSelection = selectionRange;
0301 }
0302 
0303 void KateUndoGroup::addItem(KateUndo *u)
0304 {
0305     // kill empty items
0306     if (u->isEmpty()) {
0307         delete u;
0308         return;
0309     }
0310 
0311     // try to merge, do that only for equal types, inside mergeWith we do hard casts
0312     if (!m_items.isEmpty() && m_items.last()->type() == u->type() && m_items.last()->mergeWith(u)) {
0313         delete u;
0314         return;
0315     }
0316 
0317     // default: just add new item unchanged
0318     m_items.append(u);
0319 }
0320 
0321 bool KateUndoGroup::merge(KateUndoGroup *newGroup, bool complex)
0322 {
0323     if (m_safePoint) {
0324         return false;
0325     }
0326 
0327     if (newGroup->isOnlyType(singleType()) || complex) {
0328         // Take all of its items first -> last
0329         KateUndo *u = newGroup->m_items.isEmpty() ? nullptr : newGroup->m_items.takeFirst();
0330         while (u) {
0331             addItem(u);
0332             u = newGroup->m_items.isEmpty() ? nullptr : newGroup->m_items.takeFirst();
0333         }
0334 
0335         if (newGroup->m_safePoint) {
0336             safePoint();
0337         }
0338 
0339         m_redoCursor = newGroup->m_redoCursor;
0340         m_redoSecondaryCursors = newGroup->m_redoSecondaryCursors;
0341         m_redoSelection = newGroup->m_redoSelection;
0342         //         m_redoSelections = newGroup->m_redoSelections;
0343 
0344         return true;
0345     }
0346 
0347     return false;
0348 }
0349 
0350 void KateUndoGroup::safePoint(bool safePoint)
0351 {
0352     m_safePoint = safePoint;
0353 }
0354 
0355 void KateUndoGroup::flagSavedAsModified()
0356 {
0357     for (KateUndo *item : std::as_const(m_items)) {
0358         if (item->isFlagSet(KateUndo::UndoLine1Saved)) {
0359             item->unsetFlag(KateUndo::UndoLine1Saved);
0360             item->setFlag(KateUndo::UndoLine1Modified);
0361         }
0362 
0363         if (item->isFlagSet(KateUndo::UndoLine2Saved)) {
0364             item->unsetFlag(KateUndo::UndoLine2Saved);
0365             item->setFlag(KateUndo::UndoLine2Modified);
0366         }
0367 
0368         if (item->isFlagSet(KateUndo::RedoLine1Saved)) {
0369             item->unsetFlag(KateUndo::RedoLine1Saved);
0370             item->setFlag(KateUndo::RedoLine1Modified);
0371         }
0372 
0373         if (item->isFlagSet(KateUndo::RedoLine2Saved)) {
0374             item->unsetFlag(KateUndo::RedoLine2Saved);
0375             item->setFlag(KateUndo::RedoLine2Modified);
0376         }
0377     }
0378 }
0379 
0380 void KateUndoGroup::markUndoAsSaved(QBitArray &lines)
0381 {
0382     for (int i = m_items.size() - 1; i >= 0; --i) {
0383         KateUndo *item = m_items[i];
0384         item->updateUndoSavedOnDiskFlag(lines);
0385     }
0386 }
0387 
0388 void KateUndoGroup::markRedoAsSaved(QBitArray &lines)
0389 {
0390     for (int i = m_items.size() - 1; i >= 0; --i) {
0391         KateUndo *item = m_items[i];
0392         item->updateRedoSavedOnDiskFlag(lines);
0393     }
0394 }
0395 
0396 KTextEditor::Document *KateUndoGroup::document()
0397 {
0398     return m_manager->document();
0399 }
0400 
0401 KateUndo::UndoType KateUndoGroup::singleType() const
0402 {
0403     KateUndo::UndoType ret = KateUndo::editInvalid;
0404 
0405     for (const KateUndo *item : m_items) {
0406         if (ret == KateUndo::editInvalid) {
0407             ret = item->type();
0408         } else if (ret != item->type()) {
0409             return KateUndo::editInvalid;
0410         }
0411     }
0412 
0413     return ret;
0414 }
0415 
0416 bool KateUndoGroup::isOnlyType(KateUndo::UndoType type) const
0417 {
0418     if (type == KateUndo::editInvalid) {
0419         return false;
0420     }
0421 
0422     return std::all_of(m_items.begin(), m_items.end(), [type](const KateUndo *item) {
0423         return (item->type() == type);
0424     });
0425 }