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 }