Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_transaction_data.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
10#include "kis_paint_device.h"
12#include "kis_datamanager.h"
13#include "kis_image.h"
14#include "KoColor.h"
18#include "kis_image_config.h"
19#include <boost/optional.hpp>
20
21//#define DEBUG_TRANSACTIONS
22
23#ifdef DEBUG_TRANSACTIONS
24# define DEBUG_ACTION(action) dbgKrita << action << "for" << m_d->device->dataManager()
25#else
26# define DEBUG_ACTION(action)
27#endif
28
29
31{
32 QScopedPointer<KisTransactionWrapperFactory> factory;
33 QScopedPointer<KUndo2Command> beginTransactionCommand;
34 QScopedPointer<KUndo2Command> endTransactionCommand;
35};
36
37class Q_DECL_HIDDEN KisTransactionData::Private
38{
39public:
44 QPoint oldOffset;
45 QPoint newOffset;
46
48 bool defaultPixelChanged = false;
49
51 QPainterPath savedOutlineCache;
52 QScopedPointer<KUndo2Command> flattenUndoCommand;
54
58
59 QScopedPointer<OptionalInterstrokeInfo> interstrokeInfo;
60 bool suppressUpdates = false;
61
64 void moveDevice(const QPoint newOffset);
65};
66
67KisTransactionData::KisTransactionData(const KUndo2MagicString& name, KisPaintDeviceSP device, bool resetSelectionOutlineCache, KisTransactionWrapperFactory *interstrokeDataFactory, KUndo2Command* parent, bool suppressUpdates)
68 : KUndo2Command(name, parent)
69 , m_d(new Private())
70{
71 m_d->resetSelectionOutlineCache = resetSelectionOutlineCache;
72 m_d->suppressUpdates = suppressUpdates;
73 setTimedID(-1);
74
75 if (!interstrokeDataFactory && device->interstrokeData()) {
76 interstrokeDataFactory = new KisInterstrokeDataTransactionWrapperFactory(0);
77 }
78
79 if (interstrokeDataFactory) {
80 m_d->interstrokeInfo.reset(new OptionalInterstrokeInfo());
81 m_d->interstrokeInfo->factory.reset(interstrokeDataFactory);
82 }
83
85 init(device);
87}
88
90{
91 m_d->device = device;
92 DEBUG_ACTION("Transaction started");
93
94 m_d->oldOffset = QPoint(device->x(), device->y());
95 m_d->oldDefaultPixel = device->defaultPixel();
96 m_d->firstRedo = true;
97 m_d->transactionFinished = false;
98
99 m_d->transactionTime = device->defaultBounds()->currentTime();
100
101 if (m_d->interstrokeInfo) {
102 m_d->interstrokeInfo->beginTransactionCommand.reset(m_d->interstrokeInfo->factory->createBeginTransactionCommand(m_d->device));
103 if (m_d->interstrokeInfo->beginTransactionCommand) {
104 m_d->interstrokeInfo->beginTransactionCommand->redo();
105 }
106 }
107
108 m_d->transactionFrameId = device->framesInterface() ? device->framesInterface()->currentFrameId() : -1;
109 m_d->savedDataManager = m_d->transactionFrameId >= 0 ?
110 m_d->device->framesInterface()->frameDataManager(m_d->transactionFrameId) :
111 m_d->device->dataManager();
112 m_d->memento = m_d->savedDataManager->getMemento();
113}
114
116{
117 Q_ASSERT(m_d->memento);
118 m_d->savedDataManager->purgeHistory(m_d->memento);
119
120 delete m_d;
121}
122
123void KisTransactionData::Private::moveDevice(const QPoint newOffset)
124{
125 if (transactionFrameId >= 0) {
126 device->framesInterface()->setFrameOffset(transactionFrameId, newOffset);
127 } else {
128 device->moveTo(newOffset);
129 }
130}
131
133{
134 if(!m_d->transactionFinished) {
135 // make sure the time didn't change during the transaction
137 m_d->transactionTime == m_d->device->defaultBounds()->currentTime());
138
139 DEBUG_ACTION("Transaction ended");
140 m_d->transactionFinished = true;
141 m_d->savedDataManager->commit();
142 m_d->newOffset = QPoint(m_d->device->x(), m_d->device->y());
143 m_d->defaultPixelChanged = m_d->oldDefaultPixel != m_d->device->defaultPixel();
144
145 if (m_d->interstrokeInfo) {
146 m_d->interstrokeInfo->endTransactionCommand.reset(m_d->interstrokeInfo->factory->createEndTransactionCommand());
147 if (m_d->interstrokeInfo->endTransactionCommand) {
148 m_d->interstrokeInfo->endTransactionCommand->redo();
149 }
150 m_d->interstrokeInfo->factory.reset();
151 }
152 }
153}
154
156{
157 if (m_d->suppressUpdates) return;
158
159 if (m_d->transactionFrameId == -1 ||
160 m_d->transactionFrameId ==
161 m_d->device->framesInterface()->currentFrameId()) {
162
163 QRect rc;
164 QRect mementoExtent = m_d->memento->extent();
165
166 if (m_d->newOffset == m_d->oldOffset) {
167 rc = mementoExtent.translated(m_d->device->x(), m_d->device->y());
168 } else {
169 QRect totalExtent =
170 m_d->savedDataManager->extent() | mementoExtent;
171
172 rc = totalExtent.translated(m_d->oldOffset) |
173 totalExtent.translated(m_d->newOffset);
174 }
175
176 if (m_d->defaultPixelChanged) {
177 rc |= m_d->device->defaultBounds()->bounds();
178 }
179
180 m_d->device->setDirty(rc);
181 } else {
182 m_d->device->framesInterface()->invalidateFrameCache(m_d->transactionFrameId);
183 }
184}
185
187{
188 KisPixelSelectionSP pixelSelection =
189 dynamic_cast<KisPixelSelection*>(m_d->device.data());
190
191 KisSelectionSP selection;
192 if (pixelSelection && (selection = pixelSelection->parentSelection())) {
193 selection->notifySelectionChanged();
194 }
195}
196
198{
199 KisPixelSelectionSP pixelSelection;
200
201 if (m_d->resetSelectionOutlineCache &&
202 (pixelSelection =
203 dynamic_cast<KisPixelSelection*>(m_d->device.data()))) {
204
205 pixelSelection->invalidateOutlineCache();
206 }
207}
208
210{
211 KisPixelSelectionSP pixelSelection =
212 dynamic_cast<KisPixelSelection*>(device.data());
213
214 if (pixelSelection) {
215 KisSelection *selection = pixelSelection->parentSelection().data();
216 if (selection) {
217 m_d->flattenUndoCommand.reset(selection->flatten());
218
219 if (m_d->flattenUndoCommand) {
220 m_d->flattenUndoCommand->redo();
221 }
222 }
223 }
224}
225
227{
228 KisPixelSelectionSP pixelSelection =
229 dynamic_cast<KisPixelSelection*>(m_d->device.data());
230
231 if (pixelSelection) {
232 if (m_d->flattenUndoCommand) {
233 if (undo) {
234 m_d->flattenUndoCommand->undo();
235 } else {
236 m_d->flattenUndoCommand->redo();
237 }
238 }
239 }
240}
241
242void KisTransactionData::Private::possiblySwitchCurrentTime()
243{
244 if (device->defaultBounds()->currentTime() == transactionTime) return;
245
246 device->requestTimeSwitch(transactionTime);
247}
248
250{
251 //KUndo2QStack calls redo(), so the first call needs to be blocked
252 if (m_d->firstRedo) {
253 m_d->firstRedo = false;
254
255
258 return;
259 }
260
261
262
263 doFlattenUndoRedo(false);
265
266 DEBUG_ACTION("Redo()");
267
268 if (m_d->interstrokeInfo && m_d->interstrokeInfo->beginTransactionCommand) {
269 m_d->interstrokeInfo->beginTransactionCommand->redo();
270 }
271
272 Q_ASSERT(m_d->memento);
273 m_d->savedDataManager->rollforward(m_d->memento);
274
275 if (m_d->newOffset != m_d->oldOffset) {
276 m_d->moveDevice(m_d->newOffset);
277 }
278
279 if (m_d->interstrokeInfo && m_d->interstrokeInfo->endTransactionCommand) {
280 m_d->interstrokeInfo->endTransactionCommand->redo();
281 }
282
283 m_d->possiblySwitchCurrentTime();
284 startUpdates();
286}
287
289{
290 DEBUG_ACTION("Undo()");
291
292 if (m_d->interstrokeInfo && m_d->interstrokeInfo->endTransactionCommand) {
293 m_d->interstrokeInfo->endTransactionCommand->undo();
294 }
295
296 Q_ASSERT(m_d->memento);
297 m_d->savedDataManager->rollback(m_d->memento);
298
299 if (m_d->newOffset != m_d->oldOffset) {
300 m_d->moveDevice(m_d->oldOffset);
301 }
302
303 if (m_d->interstrokeInfo && m_d->interstrokeInfo->beginTransactionCommand) {
304 m_d->interstrokeInfo->beginTransactionCommand->undo();
305 }
306
308 doFlattenUndoRedo(true);
309
310 m_d->possiblySwitchCurrentTime();
311 startUpdates();
313}
314
316{
317 m_d->savedOutlineCacheValid = false;
318
319 KisPixelSelectionSP pixelSelection =
320 dynamic_cast<KisPixelSelection*>(m_d->device.data());
321
322 if (pixelSelection) {
323 m_d->savedOutlineCacheValid = pixelSelection->outlineCacheValid();
324 if (m_d->savedOutlineCacheValid) {
325 m_d->savedOutlineCache = pixelSelection->outlineCache();
326
328 }
329 }
330}
331
333{
334 Q_UNUSED(undo);
335 KisPixelSelectionSP pixelSelection =
336 dynamic_cast<KisPixelSelection*>(m_d->device.data());
337
338 if (pixelSelection) {
340 QPainterPath savedOutlineCache;
341
342 savedOutlineCacheValid = pixelSelection->outlineCacheValid();
344 savedOutlineCache = pixelSelection->outlineCache();
345 }
346
347 if (m_d->savedOutlineCacheValid) {
348 pixelSelection->setOutlineCache(m_d->savedOutlineCache);
349 } else {
350 pixelSelection->invalidateOutlineCache();
351 }
352
353 m_d->savedOutlineCacheValid = savedOutlineCacheValid;
354 if (m_d->savedOutlineCacheValid) {
355 m_d->savedOutlineCache = savedOutlineCache;
356 }
357 }
358}
virtual void setTimedID(int timedID)
virtual int currentTime() const =0
KisInterstrokeDataSP interstrokeData() const
KisPaintDeviceFramesInterface * framesInterface()
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
void doFlattenUndoRedo(bool undo)
void possiblySwitchCurrentTime()
QScopedPointer< KUndo2Command > flattenUndoCommand
void init(KisPaintDeviceSP device)
virtual void saveSelectionOutlineCache()
KisDataManagerSP savedDataManager
void possiblyFlattenSelection(KisPaintDeviceSP device)
KisDataManagerSP dataManager()
void moveDevice(const QPoint newOffset)
virtual void restoreSelectionOutlineCache(bool undo)
QScopedPointer< OptionalInterstrokeInfo > interstrokeInfo
KisTransactionData(const KUndo2MagicString &name, KisPaintDeviceSP device, bool resetSelectionOutlineCache, KisTransactionWrapperFactory *interstrokeDataFactory, KUndo2Command *parent, bool suppressUpdates)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define DEBUG_ACTION(action)
KisSelectionWSP parentSelection
void setOutlineCache(const QPainterPath &cache)
KUndo2Command * flatten()
flatten creates a new pixel selection component from the shape selection and throws away the shape se...
void notifySelectionChanged()
QScopedPointer< KUndo2Command > beginTransactionCommand
QScopedPointer< KisTransactionWrapperFactory > factory
QScopedPointer< KUndo2Command > endTransactionCommand