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 }