File indexing completed on 2024-06-23 04:28:04
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "move_selection_stroke_strategy.h" 0008 0009 #include <klocalizedstring.h> 0010 #include <KoColorSpace.h> 0011 #include <KoCompositeOpRegistry.h> 0012 #include "kis_image.h" 0013 #include "kis_paint_layer.h" 0014 #include "kis_painter.h" 0015 #include "kis_paint_device.h" 0016 #include "kis_image_animation_interface.h" 0017 #include "kis_raster_keyframe_channel.h" 0018 #include "kis_transaction.h" 0019 #include "KisRunnableStrokeJobUtils.h" 0020 #include <commands_new/kis_selection_move_command2.h> 0021 #include "kis_lod_transform.h" 0022 #include "KisAnimAutoKey.h" 0023 0024 0025 MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer, 0026 KisSelectionSP selection, 0027 KisUpdatesFacade *updatesFacade, 0028 KisStrokeUndoFacade *undoFacade) 0029 : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move Selection"), false, undoFacade), 0030 m_paintLayer(paintLayer), 0031 m_selection(selection), 0032 m_updatesFacade(updatesFacade) 0033 { 0034 /** 0035 * Selection might have some update projection jobs pending, so we should ensure 0036 * all of them are completed before we start our stroke. 0037 */ 0038 enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER); 0039 enableJob(KisSimpleStrokeStrategy::JOB_FINISH); 0040 enableJob(KisSimpleStrokeStrategy::JOB_CANCEL); 0041 } 0042 0043 MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs) 0044 : QObject(), 0045 KisStrokeStrategyUndoCommandBased(rhs), 0046 m_paintLayer(rhs.m_paintLayer), 0047 m_selection(rhs.m_selection), 0048 m_updatesFacade(rhs.m_updatesFacade) 0049 { 0050 } 0051 0052 void MoveSelectionStrokeStrategy::initStrokeCallback() 0053 { 0054 KisStrokeStrategyUndoCommandBased::initStrokeCallback(); 0055 0056 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0057 0058 KisPaintDeviceSP movedDevice = new KisPaintDevice(m_paintLayer.data(), paintDevice->colorSpace()); 0059 0060 KUndo2Command *autoKeyframeCommand = 0061 KisAutoKey::tryAutoCreateDuplicatedFrame(m_paintLayer->paintDevice(), 0062 KisAutoKey::SupportsLod); 0063 if (autoKeyframeCommand) { 0064 runAndSaveCommand(toQShared(autoKeyframeCommand), KisStrokeJobData::BARRIER, KisStrokeJobData::NORMAL); 0065 } 0066 0067 QRect copyRect = m_selection->selectedRect(); 0068 KisPainter gc(movedDevice); 0069 gc.setSelection(m_selection); 0070 gc.bitBlt(copyRect.topLeft(), paintDevice, copyRect); 0071 gc.end(); 0072 0073 KisTransaction cutTransaction(name(), paintDevice); 0074 paintDevice->clearSelection(m_selection); 0075 runAndSaveCommand(KUndo2CommandSP(cutTransaction.endAndTake()), 0076 KisStrokeJobData::SEQUENTIAL, 0077 KisStrokeJobData::NORMAL); 0078 0079 KisIndirectPaintingSupport *indirect = 0080 static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); 0081 indirect->setTemporaryTarget(movedDevice); 0082 indirect->setTemporaryCompositeOp(COMPOSITE_OVER); 0083 indirect->setTemporaryOpacity(OPACITY_OPAQUE_U8); 0084 indirect->setTemporarySelection(0); 0085 indirect->setTemporaryChannelFlags(QBitArray()); 0086 0087 m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y()); 0088 m_initialSelectionOffset = QPoint(m_selection->x(), m_selection->y()); 0089 0090 { 0091 QRect handlesRect = movedDevice->exactBounds(); 0092 KisLodTransform t(paintDevice); 0093 handlesRect = t.mapInverted(handlesRect); 0094 0095 if (!handlesRect.isEmpty()) { 0096 emit this->sigHandlesRectCalculated(handlesRect); 0097 } else { 0098 emit this->sigStrokeStartedEmpty(); 0099 } 0100 0101 } 0102 } 0103 0104 void MoveSelectionStrokeStrategy::finishStrokeCallback() 0105 { 0106 KisIndirectPaintingSupport *indirect = 0107 static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); 0108 0109 KUndo2CommandSP parentCommand(new KUndo2Command()); 0110 0111 indirect->mergeToLayer(m_paintLayer, parentCommand.data(), name(), -1); 0112 0113 runAndSaveCommand(parentCommand, 0114 KisStrokeJobData::SEQUENTIAL, 0115 KisStrokeJobData::NORMAL); 0116 0117 indirect->setTemporaryTarget(0); 0118 0119 m_updatesFacade->blockUpdates(); 0120 0121 KUndo2CommandSP moveSelectionCommand( 0122 new KisSelectionMoveCommand2(m_selection, 0123 m_initialSelectionOffset, 0124 m_initialSelectionOffset + m_finalOffset)); 0125 0126 runAndSaveCommand( 0127 moveSelectionCommand, 0128 KisStrokeJobData::SEQUENTIAL, 0129 KisStrokeJobData::EXCLUSIVE); 0130 0131 m_updatesFacade->unblockUpdates(); 0132 0133 m_selection->setVisible(true); 0134 0135 KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); 0136 } 0137 0138 void MoveSelectionStrokeStrategy::cancelStrokeCallback() 0139 { 0140 KisIndirectPaintingSupport *indirect = 0141 static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); 0142 0143 if (indirect) { 0144 KisPaintDeviceSP t = indirect->temporaryTarget(); 0145 if (t) { 0146 KisRegion dirtyRegion = t->region(); 0147 0148 indirect->setTemporaryTarget(0); 0149 0150 m_paintLayer->setDirty(dirtyRegion); 0151 0152 m_selection->setX(m_initialSelectionOffset.x()); 0153 m_selection->setY(m_initialSelectionOffset.y()); 0154 m_selection->setVisible(true); 0155 m_selection->notifySelectionChanged(); 0156 } 0157 } 0158 KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); 0159 } 0160 0161 #include "tool/strokes/move_stroke_strategy.h" 0162 0163 void MoveSelectionStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) 0164 { 0165 MoveStrokeStrategy::Data *d = dynamic_cast<MoveStrokeStrategy::Data*>(data); 0166 ShowSelectionData *ssd = dynamic_cast<ShowSelectionData*>(data); 0167 0168 if (d) { 0169 KisIndirectPaintingSupport *indirect = 0170 static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); 0171 0172 KisPaintDeviceSP movedDevice = indirect->temporaryTarget(); 0173 0174 QRegion dirtyRegion = movedDevice->region().toQRegion(); 0175 0176 QPoint currentDeviceOffset(movedDevice->x(), movedDevice->y()); 0177 QPoint newDeviceOffset(m_initialDeviceOffset + d->offset); 0178 0179 dirtyRegion |= dirtyRegion.translated(newDeviceOffset - currentDeviceOffset); 0180 0181 movedDevice->setX(newDeviceOffset.x()); 0182 movedDevice->setY(newDeviceOffset.y()); 0183 m_finalOffset = d->offset; 0184 0185 m_paintLayer->setDirty(KisRegion::fromQRegion(dirtyRegion)); 0186 0187 m_selection->setX((m_initialSelectionOffset + d->offset).x()); 0188 m_selection->setY((m_initialSelectionOffset + d->offset).y()); 0189 0190 if (m_selection->isVisible()) { 0191 m_selection->notifySelectionChanged(); 0192 } 0193 0194 } else if (ssd) { 0195 m_selection->setVisible(ssd->showSelection); 0196 } else { 0197 KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); 0198 } 0199 } 0200 0201 KisStrokeStrategy* MoveSelectionStrokeStrategy::createLodClone(int levelOfDetail) 0202 { 0203 Q_UNUSED(levelOfDetail); 0204 0205 // Vector selections don't support lod-moves 0206 if (m_selection->hasShapeSelection()) return 0; 0207 0208 MoveSelectionStrokeStrategy *clone = new MoveSelectionStrokeStrategy(*this); 0209 connect(clone, SIGNAL(sigHandlesRectCalculated(QRect)), this, SIGNAL(sigHandlesRectCalculated(QRect))); 0210 return clone; 0211 } 0212 0213 KisStrokeJobData *MoveSelectionStrokeStrategy::ShowSelectionData::createLodClone(int levelOfDetail) 0214 { 0215 return new ShowSelectionData(*this, levelOfDetail); 0216 } 0217 0218 MoveSelectionStrokeStrategy::ShowSelectionData::ShowSelectionData(const MoveSelectionStrokeStrategy::ShowSelectionData &rhs, int /*levelOfDetail*/) 0219 : KisStrokeJobData(rhs), 0220 showSelection(rhs.showSelection) 0221 { 0222 }