diff --git a/libs/image/kis_paint_layer.cc b/libs/image/kis_paint_layer.cc index 7d534f384e..ba645a7c05 100644 --- a/libs/image/kis_paint_layer.cc +++ b/libs/image/kis_paint_layer.cc @@ -1,353 +1,355 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2006 Bart Coppens * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paint_layer.h" #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_default_bounds.h" #include "kis_onion_skin_compositor.h" #include "kis_raster_keyframe_channel.h" #include "kis_signal_auto_connection.h" #include "kis_layer_properties_icons.h" #include "kis_onion_skin_cache.h" struct Q_DECL_HIDDEN KisPaintLayer::Private { public: Private() : contentChannel(0) {} KisPaintDeviceSP paintDevice; QBitArray paintChannelFlags; // the real pointer is owned by the paint device KisRasterKeyframeChannel *contentChannel; KisSignalAutoConnectionsStore onionSkinConnection; KisOnionSkinCache onionSkinCache; }; KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev) : KisLayer(image, name, opacity) , m_d(new Private()) { Q_ASSERT(dev); init(dev); m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); } KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity) : KisLayer(image, name, opacity) , m_d(new Private()) { Q_ASSERT(image); init(new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image))); } KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace) : KisLayer(image, name, opacity) , m_d(new Private()) { if (!colorSpace) { Q_ASSERT(image); colorSpace = image->colorSpace(); } Q_ASSERT(colorSpace); init(new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image))); } KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs) : KisLayer(rhs) , KisIndirectPaintingSupport() , m_d(new Private) { const bool copyAnimation = rhs.isAnimated(); if (!copyAnimation) { init(new KisPaintDevice(*rhs.m_d->paintDevice.data()), rhs.m_d->paintChannelFlags); } else { init(new KisPaintDevice(*rhs.m_d->paintDevice.data(), true, this), rhs.m_d->paintChannelFlags); m_d->contentChannel = m_d->paintDevice->keyframeChannel(); addKeyframeChannel(m_d->contentChannel); m_d->contentChannel->setOnionSkinsEnabled(rhs.onionSkinEnabled()); KisLayer::enableAnimation(); } } void KisPaintLayer::init(KisPaintDeviceSP paintDevice, const QBitArray &paintChannelFlags) { m_d->paintDevice = paintDevice; m_d->paintDevice->setParentNode(this); m_d->paintChannelFlags = paintChannelFlags; } KisPaintLayer::~KisPaintLayer() { delete m_d; } bool KisPaintLayer::allowAsChild(KisNodeSP node) const { return node->inherits("KisMask"); } KisPaintDeviceSP KisPaintLayer::original() const { return m_d->paintDevice; } KisPaintDeviceSP KisPaintLayer::paintDevice() const { return m_d->paintDevice; } bool KisPaintLayer::needProjection() const { return hasTemporaryTarget() || (isAnimated() && onionSkinEnabled()); } void KisPaintLayer::copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const { lockTemporaryTarget(); KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect); if (hasTemporaryTarget()) { KisPainter gc(projection); setupTemporaryPainter(&gc); gc.bitBlt(rect.topLeft(), temporaryTarget(), rect); } if (m_d->contentChannel && m_d->contentChannel->keyframeCount() > 1 && onionSkinEnabled() && !m_d->paintDevice->defaultBounds()->externalFrameActive()) { KisPaintDeviceSP skins = m_d->onionSkinCache.projection(m_d->paintDevice); KisPainter gcDest(projection); gcDest.setCompositeOp(m_d->paintDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND)); gcDest.bitBlt(rect.topLeft(), skins, rect); gcDest.end(); } if (!m_d->contentChannel || (m_d->contentChannel && m_d->contentChannel->keyframeCount() <= 1) || !onionSkinEnabled()) { m_d->onionSkinCache.reset(); } unlockTemporaryTarget(); } void KisPaintLayer::setDirty(const QRect & rect) { KisLayer::setDirty(rect); } QIcon KisPaintLayer::icon() const { return KisIconUtils::loadIcon("paintLayer"); } void KisPaintLayer::setImage(KisImageWSP image) { m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); KisLayer::setImage(image); } KisBaseNode::PropertyList KisPaintLayer::sectionModelProperties() const { KisBaseNode::PropertyList l = KisLayer::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::alphaLocked, alphaLocked()); if (isAnimated()) { l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::onionSkins, onionSkinEnabled()); } return l; } void KisPaintLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { Q_FOREACH (const KisBaseNode::Property &property, properties) { if (property.name == i18n("Alpha Locked")) { setAlphaLocked(property.state.toBool()); } else if (property.name == i18n("Onion Skins")) { setOnionSkinEnabled(property.state.toBool()); } } KisLayer::setSectionModelProperties(properties); } const KoColorSpace * KisPaintLayer::colorSpace() const { return m_d->paintDevice->colorSpace(); } bool KisPaintLayer::accept(KisNodeVisitor &v) { return v.visit(this); } void KisPaintLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } void KisPaintLayer::setChannelLockFlags(const QBitArray& channelFlags) { Q_ASSERT(((quint32)channelFlags.count() == colorSpace()->channelCount() || channelFlags.isEmpty())); m_d->paintChannelFlags = channelFlags; } const QBitArray& KisPaintLayer::channelLockFlags() const { return m_d->paintChannelFlags; } QRect KisPaintLayer::extent() const { - QRect rect = temporaryTarget() ? temporaryTarget()->extent() : QRect(); + KisPaintDeviceSP t = temporaryTarget(); + QRect rect = t ? t->extent() : QRect(); if (onionSkinEnabled()) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); return rect | KisLayer::extent(); } QRect KisPaintLayer::exactBounds() const { - QRect rect = temporaryTarget() ? temporaryTarget()->exactBounds() : QRect(); + KisPaintDeviceSP t = temporaryTarget(); + QRect rect = t ? t->extent() : QRect(); if (onionSkinEnabled()) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); return rect | KisLayer::exactBounds(); } bool KisPaintLayer::alphaLocked() const { QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->paintChannelFlags; return flags.count(true) == 0 && !m_d->paintChannelFlags.isEmpty(); } void KisPaintLayer::setAlphaLocked(bool lock) { if(m_d->paintChannelFlags.isEmpty()) m_d->paintChannelFlags = colorSpace()->channelFlags(true, true); - + if(lock) m_d->paintChannelFlags &= colorSpace()->channelFlags(true, false); else m_d->paintChannelFlags |= colorSpace()->channelFlags(false, true); baseNodeChangedCallback(); } bool KisPaintLayer::onionSkinEnabled() const { return nodeProperties().boolProperty("onionskin", false); } void KisPaintLayer::setOnionSkinEnabled(bool state) { int oldState = onionSkinEnabled(); if (oldState == state) return; if (state == false && oldState) { // FIXME: change ordering! race condition possible! // Turning off onionskins shrinks our extent. Let's clean up the onion skins first setDirty(KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice)); } if (state) { m_d->onionSkinConnection.addConnection(KisOnionSkinCompositor::instance(), SIGNAL(sigOnionSkinChanged()), this, SLOT(slotExternalUpdateOnionSkins())); } else { m_d->onionSkinConnection.clear(); } nodeProperties().setProperty("onionskin", state); if (m_d->contentChannel) { m_d->contentChannel->setOnionSkinsEnabled(state); } baseNodeChangedCallback(); } void KisPaintLayer::slotExternalUpdateOnionSkins() { if (!onionSkinEnabled()) return; const QRect dirtyRect = KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice); setDirty(dirtyRect); } void KisPaintLayer::enableAnimation() { m_d->contentChannel = m_d->paintDevice->createKeyframeChannel(KisKeyframeChannel::Content, this); addKeyframeChannel(m_d->contentChannel); m_d->contentChannel->setOnionSkinsEnabled(onionSkinEnabled()); KisLayer::enableAnimation(); } KisPaintDeviceList KisPaintLayer::getLodCapableDevices() const { KisPaintDeviceList list = KisLayer::getLodCapableDevices(); KisPaintDeviceSP onionSkinsDevice = m_d->onionSkinCache.lodCapableDevice(); if (onionSkinsDevice) { list << onionSkinsDevice; } return list; } diff --git a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp index 600b3f3ec9..d939dc5f51 100644 --- a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp +++ b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp @@ -1,314 +1,318 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painter_based_stroke_strategy.h" #include #include #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_transaction.h" #include "kis_image.h" #include #include "kis_undo_stores.h" KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo() : painter(new KisPainter()), dragDistance(new KisDistanceInformation()), m_parentPainterInfo(0), m_childPainterInfo(0) { } KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo(const QPointF &lastPosition, int lastTime) : painter(new KisPainter()), dragDistance(new KisDistanceInformation(lastPosition, lastTime)), m_parentPainterInfo(0), m_childPainterInfo(0) { } KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo(PainterInfo *rhs, int levelOfDetail) : painter(new KisPainter()), dragDistance(new KisDistanceInformation(*rhs->dragDistance, levelOfDetail)), m_parentPainterInfo(rhs) { rhs->m_childPainterInfo = this; } KisPainterBasedStrokeStrategy::PainterInfo::~PainterInfo() { if (m_parentPainterInfo) { m_parentPainterInfo->m_childPainterInfo = 0; } delete(painter); delete(dragDistance); } KisDistanceInformation* KisPainterBasedStrokeStrategy::PainterInfo::buddyDragDistance() { return m_childPainterInfo ? m_childPainterInfo->dragDistance : 0; } KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id, const KUndo2MagicString &name, KisResourcesSnapshotSP resources, QVector painterInfos,bool useMergeID) : KisSimpleStrokeStrategy(id, name), m_resources(resources), m_painterInfos(painterInfos), m_transaction(0), m_undoEnabled(true), m_useMergeID(useMergeID) { init(); } KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id, const KUndo2MagicString &name, KisResourcesSnapshotSP resources, PainterInfo *painterInfo,bool useMergeID) : KisSimpleStrokeStrategy(id, name), m_resources(resources), m_painterInfos(QVector() << painterInfo), m_transaction(0), m_undoEnabled(true), m_useMergeID(useMergeID) { init(); } void KisPainterBasedStrokeStrategy::init() { enableJob(KisSimpleStrokeStrategy::JOB_INIT); enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); enableJob(KisSimpleStrokeStrategy::JOB_SUSPEND); enableJob(KisSimpleStrokeStrategy::JOB_RESUME); } KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const KisPainterBasedStrokeStrategy &rhs, int levelOfDetail) : KisSimpleStrokeStrategy(rhs), m_resources(rhs.m_resources), m_transaction(rhs.m_transaction), m_undoEnabled(true), m_useMergeID(rhs.m_useMergeID) { Q_FOREACH (PainterInfo *info, rhs.m_painterInfos) { m_painterInfos.append(new PainterInfo(info, levelOfDetail)); } KIS_ASSERT_RECOVER_NOOP( !rhs.m_transaction && !rhs.m_targetDevice && !rhs.m_activeSelection && "After the stroke has been started, no copying must happen"); } KisPaintDeviceSP KisPainterBasedStrokeStrategy::targetDevice() const { return m_targetDevice; } KisSelectionSP KisPainterBasedStrokeStrategy::activeSelection() const { return m_activeSelection; } const QVector KisPainterBasedStrokeStrategy::painterInfos() const { return m_painterInfos; } void KisPainterBasedStrokeStrategy::setUndoEnabled(bool value) { m_undoEnabled = value; } void KisPainterBasedStrokeStrategy::initPainters(KisPaintDeviceSP targetDevice, KisSelectionSP selection, bool hasIndirectPainting, const QString &indirectPaintingCompositeOp) { Q_FOREACH (PainterInfo *info, m_painterInfos) { KisPainter *painter = info->painter; painter->begin(targetDevice, !hasIndirectPainting ? selection : 0); m_resources->setupPainter(painter); if(hasIndirectPainting) { painter->setCompositeOp(targetDevice->colorSpace()->compositeOp(indirectPaintingCompositeOp)); painter->setOpacity(OPACITY_OPAQUE_U8); painter->setChannelFlags(QBitArray()); } } } void KisPainterBasedStrokeStrategy::deletePainters() { Q_FOREACH (PainterInfo *info, m_painterInfos) { delete info; } m_painterInfos.clear(); } void KisPainterBasedStrokeStrategy::initStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisPaintDeviceSP paintDevice = node->paintDevice(); KisPaintDeviceSP targetDevice = paintDevice; bool hasIndirectPainting = needsIndirectPainting(); KisSelectionSP selection = m_resources->activeSelection(); if (hasIndirectPainting) { KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if (indirect) { targetDevice = paintDevice->createCompositionSourceDevice(); targetDevice->setParentNode(node); indirect->setTemporaryTarget(targetDevice); indirect->setTemporaryCompositeOp(m_resources->compositeOp()); indirect->setTemporaryOpacity(m_resources->opacity()); indirect->setTemporarySelection(selection); QBitArray channelLockFlags = m_resources->channelLockFlags(); indirect->setTemporaryChannelFlags(channelLockFlags); } else { hasIndirectPainting = false; } } if(m_useMergeID){ m_transaction = new KisTransaction(name(), targetDevice,0,timedID(this->id())); } else{ m_transaction = new KisTransaction(name(), targetDevice); } initPainters(targetDevice, selection, hasIndirectPainting, indirectPaintingCompositeOp()); m_targetDevice = targetDevice; m_activeSelection = selection; // sanity check: selection should be applied only once if (selection && !m_painterInfos.isEmpty()) { KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); KIS_ASSERT_RECOVER_RETURN(hasIndirectPainting || m_painterInfos.first()->painter->selection()); KIS_ASSERT_RECOVER_RETURN(!hasIndirectPainting || !indirect->temporarySelection() || !m_painterInfos.first()->painter->selection()); } } void KisPainterBasedStrokeStrategy::finishStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisLayerSP layer = dynamic_cast(node.data()); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); KisPostExecutionUndoAdapter *undoAdapter = m_resources->postExecutionUndoAdapter(); QScopedPointer dumbUndoAdapter; QScopedPointer dumbUndoStore; if (!m_undoEnabled) { dumbUndoStore.reset(new KisDumbUndoStore()); dumbUndoAdapter.reset(new KisPostExecutionUndoAdapter(dumbUndoStore.data(), 0)); undoAdapter = dumbUndoAdapter.data(); } if(layer && indirect && indirect->hasTemporaryTarget()) { KUndo2MagicString transactionText = m_transaction->text(); m_transaction->end(); if(m_useMergeID){ indirect->mergeToLayer(layer, undoAdapter, transactionText,timedID(this->id())); } else{ indirect->mergeToLayer(layer, undoAdapter, transactionText); } } else { m_transaction->commit(undoAdapter); } delete m_transaction; deletePainters(); } void KisPainterBasedStrokeStrategy::cancelStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); - if(indirect && indirect->hasTemporaryTarget()) { - delete m_transaction; - deletePainters(); - QRegion region = indirect->temporaryTarget()->region(); - indirect->setTemporaryTarget(0); - node->setDirty(region); + if (indirect) { + KisPaintDeviceSP t = indirect->temporaryTarget(); + if (t) { + delete m_transaction; + deletePainters(); + + QRegion region = t->region(); + indirect->setTemporaryTarget(0); + node->setDirty(region); + } } else { m_transaction->revert(); delete m_transaction; deletePainters(); } } void KisPainterBasedStrokeStrategy::suspendStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if(indirect && indirect->hasTemporaryTarget()) { indirect->setTemporaryTarget(0); } } void KisPainterBasedStrokeStrategy::resumeStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if(indirect) { if (node->paintDevice() != m_targetDevice) { indirect->setTemporaryTarget(m_targetDevice); indirect->setTemporaryCompositeOp(m_resources->compositeOp()); indirect->setTemporaryOpacity(m_resources->opacity()); indirect->setTemporarySelection(m_activeSelection); } } } diff --git a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp index 612bbf7436..32d5999df7 100644 --- a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp +++ b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp @@ -1,185 +1,189 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "move_selection_stroke_strategy.h" #include #include #include #include "kis_image.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer, KisSelectionSP selection, KisUpdatesFacade *updatesFacade, KisPostExecutionUndoAdapter *undoAdapter) : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move Selection"), false, undoAdapter), m_paintLayer(paintLayer), m_selection(selection), m_updatesFacade(updatesFacade), m_undoEnabled(true) { enableJob(KisSimpleStrokeStrategy::JOB_INIT); enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL); } MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs, bool suppressUndo) : KisStrokeStrategyUndoCommandBased(rhs, suppressUndo), m_paintLayer(rhs.m_paintLayer), m_selection(rhs.m_selection), m_updatesFacade(rhs.m_updatesFacade), m_undoEnabled(rhs.m_undoEnabled) { } void MoveSelectionStrokeStrategy::initStrokeCallback() { KisStrokeStrategyUndoCommandBased::initStrokeCallback(); KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); KisPaintDeviceSP movedDevice = new KisPaintDevice(m_paintLayer.data(), paintDevice->colorSpace()); QRect copyRect = m_selection->selectedRect(); KisPainter gc(movedDevice); gc.setSelection(m_selection); gc.bitBlt(copyRect.topLeft(), paintDevice, copyRect); gc.end(); KisTransaction cutTransaction(name(), paintDevice); paintDevice->clearSelection(m_selection); runAndSaveCommand(KUndo2CommandSP(cutTransaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); indirect->setTemporaryTarget(movedDevice); indirect->setTemporaryCompositeOp(paintDevice->colorSpace()->compositeOp(COMPOSITE_OVER)); indirect->setTemporaryOpacity(OPACITY_OPAQUE_U8); m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y()); m_selection->setVisible(false); } void MoveSelectionStrokeStrategy::finishStrokeCallback() { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); KisTransaction transaction(name(), m_paintLayer->paintDevice()); indirect->mergeToLayer(m_paintLayer, (KisUndoAdapter*)0, KUndo2MagicString()); if (m_undoEnabled) { runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } else { transaction.end(); } indirect->setTemporaryTarget(0); QPoint selectionOffset(m_selection->x(), m_selection->y()); m_updatesFacade->blockUpdates(); if (m_undoEnabled) { KUndo2CommandSP moveSelectionCommand( new KisSelectionMoveCommand2(m_selection, selectionOffset, selectionOffset + m_finalOffset)); runAndSaveCommand( moveSelectionCommand, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } else { QPoint offset = selectionOffset + m_finalOffset; m_selection->setX(offset.x()); m_selection->setY(offset.y()); } m_updatesFacade->unblockUpdates(); m_selection->setVisible(true); KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); } void MoveSelectionStrokeStrategy::cancelStrokeCallback() { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); - QRegion dirtyRegion = indirect->temporaryTarget()->region(); + if (indirect) { + KisPaintDeviceSP t = indirect->temporaryTarget(); + if (t) { + QRegion dirtyRegion = t->region(); - indirect->setTemporaryTarget(0); - - m_selection->setVisible(true); + indirect->setTemporaryTarget(0); - m_paintLayer->setDirty(dirtyRegion); + m_selection->setVisible(true); + m_paintLayer->setDirty(dirtyRegion); + } + } KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); } #include "move_stroke_strategy.h" void MoveSelectionStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { MoveStrokeStrategy::Data *d = dynamic_cast(data); if (d) { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); KisPaintDeviceSP movedDevice = indirect->temporaryTarget(); QRegion dirtyRegion = movedDevice->region(); QPoint currentDeviceOffset(movedDevice->x(), movedDevice->y()); QPoint newDeviceOffset(m_initialDeviceOffset + d->offset); dirtyRegion |= dirtyRegion.translated(newDeviceOffset - currentDeviceOffset); movedDevice->setX(newDeviceOffset.x()); movedDevice->setY(newDeviceOffset.y()); m_finalOffset = d->offset; m_paintLayer->setDirty(dirtyRegion); } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } } void MoveSelectionStrokeStrategy::setUndoEnabled(bool value) { m_undoEnabled = value; } KisStrokeStrategy* MoveSelectionStrokeStrategy::createLodClone(int levelOfDetail) { MoveSelectionStrokeStrategy *clone = new MoveSelectionStrokeStrategy(*this, levelOfDetail > 0); clone->setUndoEnabled(false); return clone; }