File indexing completed on 2024-06-16 04:38:14

0001 /*
0002     SPDX-FileCopyrightText: 2020-2022 Mladen Milinkovic <max@smoothware.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "undostack.h"
0008 
0009 #include "appglobal.h"
0010 #include "application.h"
0011 #include "actions/useraction.h"
0012 #include "actions/useractionnames.h"
0013 #include "core/undo/undoaction.h"
0014 #include "gui/treeview/lineswidget.h"
0015 #include "gui/treeview/linesmodel.h"
0016 
0017 using namespace SubtitleComposer;
0018 
0019 UndoStack::UndoStack(QObject *parent)
0020     : QUndoStack(parent),
0021       m_level(0),
0022       m_undoAction(QUndoStack::createUndoAction(UserActionManager::instance())),
0023       m_redoAction(QUndoStack::createRedoAction(UserActionManager::instance()))
0024 {
0025     m_selectionStack.push(Selection(app()->linesWidget()->selectionModel()));
0026 
0027     connect(this, &UndoStack::undoTextChanged, undoAction(), &QAction::setToolTip);
0028     connect(this, &UndoStack::redoTextChanged, redoAction(), &QAction::setToolTip);
0029     connect(this, &UndoStack::indexChanged, parent, [](){ if(Subtitle *s = appSubtitle()) s->updateState(); });
0030     connect(this, &UndoStack::cleanChanged, parent, [](){ if(Subtitle *s = appSubtitle()) s->updateState(); });
0031 }
0032 
0033 UndoStack::~UndoStack()
0034 {
0035 
0036 }
0037 
0038 void
0039 UndoStack::clear()
0040 {
0041     m_selectionStack.clear();
0042     m_selectionStack.push(Selection(app()->linesWidget()->selectionModel()));
0043     QUndoStack::clear();
0044 }
0045 
0046 
0047 inline static void
0048 restoreSelection(int current, const QList<std::pair<int, int>> &selection)
0049 {
0050     LinesWidget *lw = app()->linesWidget();
0051     LinesModel *lm = lw->model();
0052     QItemSelectionModel *sm = lw->selectionModel();
0053 
0054     // make sure that selection has valid iterators
0055     lm->processSelectionUpdate();
0056 
0057     // restore selected ranges
0058     QItemSelection itemSel;
0059     const int lastCol = lm->columnCount() - 1;
0060     for(const std::pair<int, int> &r : selection)
0061         itemSel.push_back(QItemSelectionRange(lm->index(r.first), lm->index(r.second, lastCol)));
0062     if(sm->selection() != itemSel)
0063         sm->select(itemSel, QItemSelectionModel::ClearAndSelect);
0064 
0065     // restore current item
0066     const QModelIndex idx = lm->index(current);
0067     if(sm->currentIndex().row() != idx.row()) {
0068         sm->setCurrentIndex(idx, QItemSelectionModel::Rows);
0069         if(lw->scrollFollowsModel())
0070             lw->scrollTo(idx, QAbstractItemView::EnsureVisible);
0071     }
0072 }
0073 
0074 inline static void
0075 saveSelection(int *current, QList<std::pair<int, int>> *selection)
0076 {
0077     LinesWidget *lw = app()->linesWidget();
0078     const QItemSelectionModel *sm = lw->selectionModel();
0079 
0080     // make sure that selection has valid iterators
0081     lw->model()->processSelectionUpdate();
0082 
0083     *current = sm->currentIndex().row();
0084 
0085     selection->clear();
0086     const QItemSelection &is = sm->selection();
0087     for(const QItemSelectionRange &r : is)
0088         selection->push_back(std::pair<int, int>(r.top(), r.bottom()));
0089 }
0090 
0091 void
0092 UndoStack::levelIncrease(int idx)
0093 {
0094     if(m_level++ == 0) {
0095         while(m_dirtyStack.size() < idx)
0096             m_dirtyStack.push(DirtyMode::None);
0097         while(m_selectionStack.size() <= idx)
0098             m_selectionStack.push(Selection());
0099         Selection &sel = m_selectionStack[idx];
0100         saveSelection(&sel.preCurrentRow, &sel.preSelection);
0101     }
0102 }
0103 
0104 void
0105 UndoStack::levelDecrease(int idx)
0106 {
0107     if(--m_level == 0) {
0108         Selection &sel = m_selectionStack[idx];
0109         saveSelection(&sel.postCurrentRow, &sel.postSelection);
0110     }
0111 }
0112 
0113 void
0114 UndoStack::push(UndoAction *cmd)
0115 {
0116     const int idx = index();
0117     const int idx1 = idx + 1;
0118     levelIncrease(idx1);
0119     m_dirtyStack[idx] = static_cast<DirtyMode>(m_dirtyStack.at(idx) | cmd->m_dirtyMode);
0120     QUndoStack::push(cmd); // NOTE: cmd can/will be deleted after push()
0121     levelDecrease(idx1);
0122 }
0123 
0124 void
0125 UndoStack::beginMacro(const QString &text)
0126 {
0127     QUndoStack::beginMacro(text);
0128     levelIncrease(index() + 1);
0129 }
0130 
0131 void
0132 UndoStack::endMacro(DirtyMode dirtyOverride)
0133 {
0134     const int idx = index();
0135     levelDecrease(idx + 1);
0136     if(dirtyOverride != Invalid)
0137         m_dirtyStack[idx] = dirtyOverride;
0138     QUndoStack::endMacro();
0139 }
0140 
0141 void
0142 UndoStack::undo()
0143 {
0144     QUndoStack::undo();
0145 
0146     const Selection &sel = m_selectionStack.at(index() + 1);
0147     restoreSelection(sel.preCurrentRow, sel.preSelection);
0148 }
0149 
0150 void
0151 UndoStack::redo()
0152 {
0153     QUndoStack::redo();
0154 
0155     const Selection &sel = m_selectionStack.at(index());
0156     restoreSelection(sel.postCurrentRow, sel.postSelection);
0157 }
0158 
0159 
0160 UndoStack::Selection::Selection()
0161     : preCurrentRow(-1)
0162 {
0163 }
0164 
0165 UndoStack::Selection::Selection(QItemSelectionModel *sel)
0166     : preCurrentRow(sel->currentIndex().row()),
0167       postCurrentRow(sel->currentIndex().row())
0168 {
0169     const QItemSelection &is = sel->selection();
0170     for(const QItemSelectionRange &r : is) {
0171         preSelection.push_back(std::pair<int, int>(r.top(), r.bottom()));
0172         postSelection.push_back(std::pair<int, int>(r.top(), r.bottom()));
0173     }
0174 }