1
2
3 #include "tundo.h"
4 #include <deque>
5
6 //-----------------------------------------------------------------------------
7
8 namespace {
9
deleteUndo(const TUndo * undo)10 void deleteUndo(const TUndo *undo) { delete undo; }
callUndo(const TUndo * undo)11 void callUndo(const TUndo *undo) { undo->undo(); }
callRedo(const TUndo * undo)12 void callRedo(const TUndo *undo) { undo->redo(); }
13 // void callRepeat(const TUndo* undo) {undo->repeat(); }
14
15 class TUndoBlock final : public TUndo {
16 std::vector<TUndo *> m_undos;
17 typedef std::vector<TUndo *>::const_iterator Iterator;
18 typedef std::vector<TUndo *>::const_reverse_iterator ReverseIterator;
19 mutable bool m_deleted, m_undoing;
20
21 public:
TUndoBlock()22 TUndoBlock() : m_deleted(false), m_undoing(false) {}
~TUndoBlock()23 ~TUndoBlock() {
24 assert(m_undoing == false);
25 assert(m_deleted == false);
26 m_deleted = true;
27 std::for_each(m_undos.begin(), m_undos.end(), deleteUndo);
28 m_undos.clear();
29 }
30
getSize() const31 int getSize() const override {
32 int size = sizeof(*this);
33 for (Iterator cit = m_undos.begin(); cit != m_undos.end(); ++cit)
34 size += (*cit)->getSize();
35 size += (m_undos.capacity() - m_undos.size()) * sizeof(TUndo *);
36 return size;
37 }
getUndoCount() const38 int getUndoCount() const { return (int)m_undos.size(); }
setLast()39 void setLast() {
40 for (UINT i = 0; i < m_undos.size(); i++) {
41 m_undos[i]->m_isLastInBlock = (i == 0);
42 m_undos[i]->m_isLastInRedoBlock = (i == m_undos.size() - 1);
43 }
44 }
45
undo() const46 void undo() const override {
47 assert(!m_deleted);
48 assert(!m_undoing);
49 m_undoing = true;
50 // VERSIONE CORRETTA
51 std::for_each(m_undos.rbegin(), m_undos.rend(), callUndo);
52 // VERSIONE SBAGLIATA
53 // for_each(m_undos.begin(), m_undos.end(), callUndo);
54 m_undoing = false;
55 }
redo() const56 void redo() const override {
57 assert(!m_deleted);
58 // VERSIONE CORRETTA
59 std::for_each(m_undos.begin(), m_undos.end(), callRedo);
60 // VERSIONE SBAGLIATA
61 // for_each(m_undos.rbegin(), m_undos.rend(), callRedo);
62 }
63
64 // void repeat() const {
65 // for_each(m_undos.begin(), m_undos.end(), callRepeat);
66 //}
onAdd()67 void onAdd() override {}
add(TUndo * undo)68 void add(TUndo *undo) {
69 undo->m_isLastInBlock = true;
70 undo->m_isLastInRedoBlock = true;
71 m_undos.push_back(undo);
72 }
73
popUndo(int n)74 void popUndo(int n) {
75 if (n == -1) n = m_undos.size();
76 if (m_undos.empty() || n <= 0) return;
77 while (n > 0 && !m_undos.empty()) {
78 TUndo *undo = m_undos.back();
79 m_undos.pop_back();
80 delete undo;
81 n--;
82 }
83 }
84
getHistoryString()85 QString getHistoryString() override {
86 if (m_undos.empty())
87 return TUndo::getHistoryString();
88 else if ((int)m_undos.size() == 1)
89 return m_undos.back()->getHistoryString();
90 else {
91 return QString("%1 etc..").arg(m_undos.back()->getHistoryString());
92 }
93 }
94
getHistoryType()95 int getHistoryType() override {
96 if (m_undos.empty())
97 return TUndo::getHistoryType();
98 else
99 return m_undos.back()->getHistoryType();
100 }
101 };
102 } // namespace
103
104 typedef std::deque<TUndo *> UndoList;
105 typedef UndoList::iterator UndoListIterator;
106 typedef UndoList::const_iterator UndoListConstIterator;
107
108 //-----------------------------------------------------------------------------
109
110 struct TUndoManager::TUndoManagerImp {
111 UndoList m_undoList;
112 UndoListIterator m_current;
113 bool m_skipped;
114 int m_undoMemorySize; // in bytes
115
116 std::vector<TUndoBlock *> m_blockStack;
117
118 public:
TUndoManagerImpTUndoManager::TUndoManagerImp119 TUndoManagerImp() : m_skipped(false), m_undoMemorySize(0) {
120 m_current = m_undoList.end();
121 }
~TUndoManagerImpTUndoManager::TUndoManagerImp122 ~TUndoManagerImp() {}
123
124 void add(TUndo *undo);
125
126 public:
127 static struct ManagerPtr {
128 TUndoManager *m_ptr;
129
130 public:
ManagerPtrTUndoManager::TUndoManagerImp::ManagerPtr131 ManagerPtr() : m_ptr(0) {}
~ManagerPtrTUndoManager::TUndoManagerImp::ManagerPtr132 ~ManagerPtr() {
133 if (m_ptr) delete m_ptr;
134 m_ptr = 0;
135 }
136
137 } theManager;
138
139 private:
140 void doAdd(TUndo *undo);
141 };
142
143 //=============================================================================
144
145 TUndoManager::TUndoManagerImp::ManagerPtr
146 TUndoManager::TUndoManagerImp::theManager;
147
148 //-----------------------------------------------------------------------------
149
manager()150 TUndoManager *TUndoManager::manager() {
151 if (!TUndoManagerImp::theManager.m_ptr)
152 TUndoManagerImp::theManager.m_ptr = new TUndoManager;
153 return TUndoManagerImp::theManager.m_ptr;
154 }
155
156 //=============================================================================
157
TUndoManager()158 TUndoManager::TUndoManager() : m_imp(new TUndoManagerImp) {}
159
160 //-----------------------------------------------------------------------------
161
~TUndoManager()162 TUndoManager::~TUndoManager() {
163 // cout << "Distrutto undo manager" << endl;
164 assert(m_imp->m_blockStack.empty());
165 reset();
166 }
167
168 //-----------------------------------------------------------------------------
169
add(TUndo * undo)170 void TUndoManager::TUndoManagerImp::add(TUndo *undo) {
171 assert(undo);
172
173 if (!m_blockStack.empty()) {
174 assert(m_current == m_undoList.end());
175 m_blockStack.back()->add(undo);
176 } else
177 doAdd(undo);
178 }
179
180 //-----------------------------------------------------------------------------
181
doAdd(TUndo * undo)182 void TUndoManager::TUndoManagerImp::doAdd(TUndo *undo) {
183 if (m_current != m_undoList.end()) {
184 std::for_each(m_current, m_undoList.end(), deleteUndo);
185 m_undoList.erase(m_current, m_undoList.end());
186 }
187
188 int i, memorySize = 0, count = m_undoList.size();
189 for (i = 0; i < count; i++) memorySize += m_undoList[i]->getSize();
190
191 while (count > 100 || (count != 0 && memorySize + undo->getSize() >
192 m_undoMemorySize)) // 20MB
193 {
194 --count;
195 TUndo *undo = m_undoList.front();
196 m_undoList.pop_front();
197 memorySize -= undo->getSize();
198 delete undo;
199 }
200
201 undo->m_isLastInBlock = true;
202 undo->m_isLastInRedoBlock = true;
203 m_undoList.push_back(undo);
204 m_current = m_undoList.end();
205 }
206
207 //-----------------------------------------------------------------------------
208
beginBlock()209 void TUndoManager::beginBlock() {
210 if (m_imp->m_current != m_imp->m_undoList.end()) {
211 std::for_each(m_imp->m_current, m_imp->m_undoList.end(), deleteUndo);
212 m_imp->m_undoList.erase(m_imp->m_current, m_imp->m_undoList.end());
213 }
214
215 TUndoBlock *undoBlock = new TUndoBlock;
216 m_imp->m_blockStack.push_back(undoBlock);
217 m_imp->m_current = m_imp->m_undoList.end();
218 }
219
220 //-----------------------------------------------------------------------------
221
endBlock()222 void TUndoManager::endBlock() {
223 // vogliamo fare anche resize del vector ???
224 assert(m_imp->m_blockStack.empty() == false);
225 TUndoBlock *undoBlock = m_imp->m_blockStack.back();
226 m_imp->m_blockStack.pop_back();
227 if (undoBlock->getUndoCount() > 0) {
228 undoBlock->setLast();
229 m_imp->add(undoBlock);
230 emit historyChanged();
231 } else {
232 delete undoBlock;
233 m_imp->m_current = m_imp->m_undoList.end();
234 }
235 }
236
237 //-----------------------------------------------------------------------------
238
undo()239 bool TUndoManager::undo() {
240 assert(m_imp->m_blockStack.empty());
241 UndoListIterator &it = m_imp->m_current;
242 if (it != m_imp->m_undoList.begin()) {
243 m_imp->m_skipped = false;
244 --it;
245 (*it)->undo();
246 emit historyChanged();
247 if (m_imp->m_skipped) {
248 m_imp->m_skipped = false;
249 return undo();
250 }
251 return true;
252 } else
253 return false;
254 }
255
256 //-----------------------------------------------------------------------------
257
redo()258 bool TUndoManager::redo() {
259 assert(m_imp->m_blockStack.empty());
260 UndoListIterator &it = m_imp->m_current;
261 if (it != m_imp->m_undoList.end()) {
262 m_imp->m_skipped = false;
263 (*it)->redo();
264 ++it;
265 emit historyChanged();
266 if (m_imp->m_skipped) {
267 m_imp->m_skipped = false;
268 return redo();
269 }
270 return true;
271 } else
272 return false;
273 }
274
275 //-----------------------------------------------------------------------------
276 // repeat e' come redo ma non sposta il puntatore al corrente
277 /*
278 void TUndoManager::repeat()
279 {
280 assert(m_imp->m_blockStack.empty());
281 UndoListIterator &it = m_imp->m_current;
282 if (it != m_imp->m_undoList.end())
283 {
284 (*it)->repeat();
285 }
286 }
287 */
288
289 //-----------------------------------------------------------------------------
290
skip()291 void TUndoManager::skip() { m_imp->m_skipped = true; }
292
293 //-----------------------------------------------------------------------------
294
setUndoMemorySize(int memorySyze)295 void TUndoManager::setUndoMemorySize(int memorySyze) {
296 m_imp->m_undoMemorySize = 1048576 * memorySyze;
297 }
298
299 //-----------------------------------------------------------------------------
300
add(TUndo * undo)301 void TUndoManager::add(TUndo *undo) {
302 assert(undo);
303 if (!undo) return;
304
305 m_imp->add(undo);
306 Q_EMIT historyChanged();
307
308 undo->onAdd();
309 Q_EMIT somethingChanged();
310 }
311
312 //-----------------------------------------------------------------------------
313
reset()314 void TUndoManager::reset() {
315 assert(m_imp->m_blockStack.empty());
316 m_imp->m_blockStack.clear();
317 UndoList &lst = m_imp->m_undoList;
318 std::for_each(lst.begin(), lst.end(), deleteUndo);
319 lst.clear();
320 m_imp->m_current = m_imp->m_undoList.end();
321 Q_EMIT historyChanged();
322 }
323
324 //-----------------------------------------------------------------------------
325
popUndo(int n,bool forward)326 void TUndoManager::popUndo(int n, bool forward) {
327 if (!forward) {
328 if (m_imp->m_blockStack.empty()) {
329 if (n == -1) {
330 UndoListIterator start = m_imp->m_undoList.begin();
331 n = 0;
332 while (start != m_imp->m_current) ++n;
333 }
334 if (m_imp->m_undoList.empty() || n <= 0) return;
335 if (m_imp->m_current == m_imp->m_undoList.end()) {
336 int i;
337 for (i = 0; i < n && m_imp->m_current != m_imp->m_undoList.begin();
338 i++) {
339 m_imp->m_current--;
340 delete (*m_imp->m_current);
341 m_imp->m_undoList.erase(m_imp->m_current);
342 m_imp->m_current = m_imp->m_undoList.end();
343 }
344 } else {
345 TUndo *undo = *m_imp->m_current;
346 UndoListIterator start, end = m_imp->m_current;
347 if (end == m_imp->m_undoList.begin()) return;
348
349 int i;
350 for (i = 0; i < n && m_imp->m_current != m_imp->m_undoList.begin(); i++)
351 m_imp->m_current--;
352
353 start = m_imp->m_current;
354 UndoListIterator _end = end;
355 while (_end != start) {
356 _end--;
357 delete (*_end);
358 }
359 m_imp->m_undoList.erase(start, end);
360
361 m_imp->m_current = m_imp->m_undoList.begin();
362 while (*m_imp->m_current != undo) m_imp->m_current++;
363 }
364 } else
365 m_imp->m_blockStack.back()->popUndo(n);
366 return;
367 }
368
369 if (m_imp->m_current == m_imp->m_undoList.end()) return;
370 if (m_imp->m_blockStack.empty()) {
371 UndoListIterator end, start = m_imp->m_current++;
372 if (n == -1)
373 end = m_imp->m_undoList.end();
374 else {
375 UndoListIterator it = start;
376 end = it;
377 int i = 0;
378 while (i != n && end != m_imp->m_undoList.end()) {
379 ++end;
380 i++;
381 }
382 }
383 std::for_each(start, end, deleteUndo);
384 m_imp->m_undoList.erase(start, end);
385 m_imp->m_current = m_imp->m_undoList.end();
386 } else
387 m_imp->m_blockStack.back()->popUndo(n);
388 }
389
390 //-----------------------------------------------------------------------------
391
getHistoryCount()392 int TUndoManager::getHistoryCount() { return (int)m_imp->m_undoList.size(); }
393
394 //-----------------------------------------------------------------------------
395
getCurrentHistoryIndex()396 int TUndoManager::getCurrentHistoryIndex() {
397 int index = 0;
398 UndoListIterator it = m_imp->m_undoList.begin();
399 while (1) {
400 if (it == m_imp->m_current) return index;
401
402 if (it == m_imp->m_undoList.end()) break;
403
404 index++;
405 it++;
406 }
407 return 0;
408 }
409
410 //-----------------------------------------------------------------------------
411
getUndoItem(int index)412 TUndo *TUndoManager::getUndoItem(int index) {
413 if (index > (int)m_imp->m_undoList.size() || index <= 0) return 0;
414
415 return m_imp->m_undoList.at(index - 1);
416 }
417