Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_memento_manager.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include <QtGlobal>
9#include "kis_memento.h"
10
11
12//#define DEBUG_MM
13
14#ifdef DEBUG_MM
15#define DEBUG_LOG_TILE_ACTION(action, tile, col, row) \
16 printf("### MementoManager (0x%X): %s " \
17 "\ttile:\t0x%X (%d, %d) ###\n", (quintptr)this, action, \
18 (quintptr)tile, col, row)
19
20#define DEBUG_LOG_SIMPLE_ACTION(action) \
21 printf("### MementoManager (0x%X): %s\n", (quintptr)this, action)
22
23#define DEBUG_DUMP_MESSAGE(action) do { \
24 printf("\n### MementoManager (0x%X): %s \t\t##########\n", \
25 (quintptr)this, action); \
26 debugPrintInfo(); \
27 printf("##################################################################\n\n"); \
28 } while(0)
29#else
30
31#define DEBUG_LOG_TILE_ACTION(action, tile, col, row)
32#define DEBUG_LOG_SIMPLE_ACTION(action)
33#define DEBUG_DUMP_MESSAGE(action)
34#endif
35
36
58#define blockRegistration() (m_registrationBlocked = true)
59#define unblockRegistration() (m_registrationBlocked = false)
60#define registrationBlocked() (m_registrationBlocked)
61
62#define namedTransactionInProgress() ((bool)m_currentMemento)
63
65 : m_index(0),
66 m_headsHashTable(0),
67 m_registrationBlocked(false)
68{
73}
74
76 : m_index(rhs.m_index, 0),
77 m_revisions(rhs.m_revisions),
78 m_cancelledRevisions(rhs.m_cancelledRevisions),
79 m_headsHashTable(rhs.m_headsHashTable, 0),
80 m_currentMemento(rhs.m_currentMemento),
81 m_registrationBlocked(rhs.m_registrationBlocked)
82{
83 Q_ASSERT_X(!m_registrationBlocked,
84 "KisMementoManager", "(impossible happened) "
85 "The device has been copied while registration was blocked");
86}
87
89{
90 // Nothing to be done here. Happily...
91 // Everything is done by QList and KisSharedPtr...
93}
94
114{
115 if (registrationBlocked()) return;
116
117 DEBUG_LOG_TILE_ACTION("reg. [C]", tile, tile->col(), tile->row());
118
119 KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row());
120
121 if(!mi) {
122 mi = new KisMementoItem();
123 mi->changeTile(tile);
124 m_index.addTile(mi);
125
128 }
129 }
130 else {
131 mi->reset();
132 mi->changeTile(tile);
133 }
134}
135
137{
138 if (registrationBlocked()) return;
139
140 DEBUG_LOG_TILE_ACTION("reg. [D]", tile, tile->col(), tile->row());
141
142 KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row());
143
144 if(!mi) {
145 mi = new KisMementoItem();
146
148 mi->deleteTile(tile, defaultTileData);
149 defaultTileData->deref();
150
151 m_index.addTile(mi);
152
155 }
156 }
157 else {
158 mi->reset();
159
161 mi->deleteTile(tile, defaultTileData);
162 defaultTileData->deref();
163 }
164}
165
167{
168 if (m_index.isEmpty()) {
170 //warnTiles << "Named Transaction is empty";
175 }
176 else {
178 return;
179 }
180 }
181
182 KisMementoItemList revisionList;
184 KisMementoItemSP parentMI;
185 bool newTile;
186
188 while ((mi = iter.tile())) {
189 parentMI = m_headsHashTable.getTileLazy(mi->col(), mi->row(), newTile);
190
191 mi->setParent(parentMI);
192 mi->commit();
193 revisionList.append(mi);
194
195 m_headsHashTable.deleteTile(mi->col(), mi->row());
196
198 //iter.next(); // previous line does this for us
199 }
200
201 KisHistoryItem hItem;
202 hItem.itemList = revisionList;
203 hItem.memento = m_currentMemento.data();
204 m_revisions.append(hItem);
205
208
209 DEBUG_DUMP_MESSAGE("COMMIT_DONE");
210
211 // Waking up pooler to prepare copies for us
213}
214
215KisTileSP KisMementoManager::getCommittedTile(qint32 col, qint32 row, bool &existingTile)
216{
223 return KisTileSP();
224
225 KisMementoItemSP mi = m_headsHashTable.getReadOnlyTileLazy(col, row, existingTile);
226 return mi->tile(0);
227}
228
230{
235
244 // KIS_SAFE_ASSERT_RECOVER_NOOP(m_index.isEmpty());
245
246 // Clear redo() information
247 m_cancelledRevisions.clear();
248
249 commit();
250 m_currentMemento = new KisMemento(this);
251
252 DEBUG_LOG_SIMPLE_ACTION("GET_MEMENTO_DONE");
253
254 return m_currentMemento;
255}
256
260
261#define forEachReversed(iter, list) \
262 for(iter=list.end(); iter-- != list.begin();)
263
264
266{
267 commit();
268
269 if (! m_revisions.size()) return;
270
271 KisHistoryItem changeList = m_revisions.takeLast();
272
273 // SANITY CHECK: the transaction's memento must be in sync with
274 // the revisions list we have locally
275 KIS_SAFE_ASSERT_RECOVER_NOOP(changeList.memento == memento);
276
278 KisMementoItemSP parentMI;
279 KisMementoItemList::iterator iter;
280
282 forEachReversed(iter, changeList.itemList) {
283 mi=*iter;
284 parentMI = mi->parent();
285
286 if (mi->type() == KisMementoItem::CHANGED)
287 ht->deleteTile(mi->col(), mi->row());
288 if (parentMI->type() == KisMementoItem::CHANGED)
289 ht->addTile(parentMI->tile(this));
290
291 m_headsHashTable.deleteTile(parentMI->col(), parentMI->row());
292 m_headsHashTable.addTile(parentMI);
293
294 // This is not necessary
295 //mi->setParent(0);
296 }
310
311 // We have just emulated a commit so:
314
315 m_cancelledRevisions.prepend(changeList);
316 DEBUG_DUMP_MESSAGE("UNDONE");
317
318 // Waking up pooler to prepare copies for us
320}
321
323{
325
326 if (!m_cancelledRevisions.size()) return;
327
328 KisHistoryItem changeList = m_cancelledRevisions.takeFirst();
329
330 // SANITY CHECK: the transaction's memento must be in sync with
331 // the revisions list we have locally
332 KIS_SAFE_ASSERT_RECOVER_NOOP(changeList.memento == memento);
333
335
337 Q_FOREACH (mi, changeList.itemList) {
338 if (mi->parent()->type() == KisMementoItem::CHANGED)
339 ht->deleteTile(mi->col(), mi->row());
340 if (mi->type() == KisMementoItem::CHANGED)
341 ht->addTile(mi->tile(this));
342
343 m_index.addTile(mi);
344 }
345 // see comment in rollback()
346
347 m_currentMemento = changeList.memento;
348 commit();
350 DEBUG_DUMP_MESSAGE("REDONE");
351}
352
354{
355 if (m_currentMemento == oldestMemento) {
356 commit();
357 }
358
359 qint32 revisionIndex = findRevisionByMemento(oldestMemento);
360 if (revisionIndex < 0) return;
361
362 for(; revisionIndex > 0; revisionIndex--) {
363 resetRevisionHistory(m_revisions.first().itemList);
364 m_revisions.removeFirst();
365 }
366
367 KIS_ASSERT(m_revisions.first().memento == oldestMemento);
368 resetRevisionHistory(m_revisions.first().itemList);
369
370 DEBUG_DUMP_MESSAGE("PURGE_HISTORY");
371}
372
374{
375 qint32 index = -1;
376 for(qint32 i = 0; i < m_revisions.size(); i++) {
377 if (m_revisions[i].memento == memento) {
378 index = i;
379 break;
380 }
381 }
382 return index;
383}
384
386{
387 KisMementoItemSP parentMI;
389
390 Q_FOREACH (mi, list) {
391 parentMI = mi->parent();
392 if(!parentMI) continue;
393
394 while (parentMI->parent()) {
395 parentMI = parentMI->parent();
396 }
397 mi->setParent(parentMI);
398 }
399}
400
402{
403 m_headsHashTable.setDefaultTileData(defaultTileData);
404 m_index.setDefaultTileData(defaultTileData);
405}
406
408{
409 printf("KisMementoManager stats:\n");
410 printf("Index list\n");
413
414 while ((mi = iter.tile())) {
415 mi->debugPrintInfo();
416 iter.next();
417 }
418
419 printf("Revisions list:\n");
420 qint32 i = 0;
421 Q_FOREACH (const KisHistoryItem &changeList, m_revisions) {
422 printf("--- revision #%d ---\n", i++);
423 Q_FOREACH (mi, changeList.itemList) {
424 mi->debugPrintInfo();
425 }
426 }
427
428 printf("\nCancelled revisions list:\n");
429 i = 0;
430 Q_FOREACH (const KisHistoryItem &changeList, m_cancelledRevisions) {
431 printf("--- revision #%d ---\n", m_revisions.size() + i++);
432 Q_FOREACH (mi, changeList.itemList) {
433 mi->debugPrintInfo();
434 }
435 }
436
437 printf("----------------\n");
439}
qint32 col() const
qint32 row() const
void changeTile(KisTile *tile)
KisTileSP tile(KisMementoManager *mm)
void deleteTile(KisTile *tile, KisTileData *defaultTileData)
void setParent(KisMementoItemSP parent)
KisMementoItemSP parent()
qint32 findRevisionByMemento(KisMementoSP memento) const
KisMementoSP currentMemento()
void registerTileChange(KisTile *tile)
KisMementoItemHashTable m_index
KisMementoSP m_currentMemento
KisHistoryList m_cancelledRevisions
void rollback(KisTileHashTable *ht, KisMementoSP memento)
KisMementoItemHashTable m_headsHashTable
KisHistoryList m_revisions
void rollforward(KisTileHashTable *ht, KisMementoSP memento)
void registerTileDeleted(KisTile *tile)
void setDefaultTileData(KisTileData *defaultTileData)
KisTileSP getCommittedTile(qint32 col, qint32 row, bool &existingTile)
void resetRevisionHistory(KisMementoItemList list)
void purgeHistory(KisMementoSP oldestMemento)
void updateExtent(qint32 col, qint32 row, QMutex *currentMementoExtentLock)
Definition kis_memento.h:88
static KisTileDataStore * instance()
std::enable_if< std::is_same< Helper, QWriteLocker >::value, void >::type moveCurrentToHashTable(KisTileHashTableTraits< T > *newHashTable)
KisTileData * refAndFetchDefaultTileData() const
TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile)
TileTypeSP getExistingTile(qint32 col, qint32 row)
void addTile(TileTypeSP tile)
bool deleteTile(TileTypeSP tile)
void setDefaultTileData(KisTileData *defaultTileData)
TileTypeSP getTileLazy(qint32 col, qint32 row, bool &newTile)
qint32 row() const
Definition kis_tile.h:92
qint32 col() const
Definition kis_tile.h:95
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define forEachReversed(iter, list)
#define blockRegistration()
#define unblockRegistration()
#define DEBUG_LOG_SIMPLE_ACTION(action)
#define DEBUG_DUMP_MESSAGE(action)
#define namedTransactionInProgress()
#define DEBUG_LOG_TILE_ACTION(action, tile, col, row)
#define registrationBlocked()
KisSharedPtr< KisTile > KisTileSP
Definition kis_tile.h:27
struct Tile * newTile(struct rect r)
Definition pixels.c:231
KisMementoItemList itemList