File indexing completed on 2024-05-12 15:58:43

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_stroke_strategy_undo_command_based.h"
0008 
0009 #include <QMutexLocker>
0010 #include "kis_image_interfaces.h"
0011 #include "kis_post_execution_undo_adapter.h"
0012 #include "commands_new/kis_saved_commands.h"
0013 
0014 
0015 KisStrokeStrategyUndoCommandBased::Data::~Data()
0016 {
0017     // acts as a key function
0018 }
0019 
0020 KisStrokeStrategyUndoCommandBased::
0021 KisStrokeStrategyUndoCommandBased(const KUndo2MagicString &name,
0022                                   bool undo,
0023                                   KisStrokeUndoFacade *undoFacade,
0024                                   KUndo2CommandSP initCommand,
0025                                   KUndo2CommandSP finishCommand)
0026   : KisRunnableBasedStrokeStrategy(QLatin1String("STROKE_UNDO_COMMAND_BASED"), name),
0027     m_undo(undo),
0028     m_initCommand(initCommand),
0029     m_finishCommand(finishCommand),
0030     m_undoFacade(undoFacade),
0031     m_macroId(-1),
0032     m_macroCommand(0)
0033 {
0034     enableJob(KisSimpleStrokeStrategy::JOB_INIT);
0035     enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
0036     enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
0037     enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
0038 }
0039 
0040 KisStrokeStrategyUndoCommandBased::
0041 KisStrokeStrategyUndoCommandBased(const KisStrokeStrategyUndoCommandBased &rhs)
0042   : KisRunnableBasedStrokeStrategy(rhs),
0043     m_undo(false),
0044     m_initCommand(rhs.m_initCommand),
0045     m_finishCommand(rhs.m_finishCommand),
0046     m_undoFacade(rhs.m_undoFacade),
0047     m_macroCommand(0)
0048 {
0049     KIS_ASSERT_RECOVER_NOOP(!rhs.m_macroCommand &&
0050                             !rhs.m_undo &&
0051                             "After the stroke has been started, no copying must happen");
0052 }
0053 
0054 void KisStrokeStrategyUndoCommandBased::setUsedWhileUndoRedo(bool value)
0055 {
0056     setClearsRedoOnStart(!value);
0057     setAsynchronouslyCancellable(!value);
0058 }
0059 
0060 void KisStrokeStrategyUndoCommandBased::executeCommand(KUndo2CommandSP command, bool undo)
0061 {
0062     if(!command) return;
0063 
0064     if (MutatedCommandInterface *mutatedCommand = dynamic_cast<MutatedCommandInterface*>(command.data())) {
0065         mutatedCommand->setRunnableJobsInterface(this->runnableJobsInterface());
0066     }
0067 
0068     if(undo) {
0069         command->undo();
0070     } else {
0071         command->redo();
0072     }
0073 }
0074 
0075 void KisStrokeStrategyUndoCommandBased::initStrokeCallback()
0076 {
0077     if(m_undoFacade) {
0078         m_macroCommand = m_undoFacade->postExecutionUndoAdapter()->createMacro(name());
0079     }
0080 
0081     executeCommand(m_initCommand, m_undo);
0082     notifyCommandDone(m_initCommand,
0083                       KisStrokeJobData::SEQUENTIAL,
0084                       KisStrokeJobData::NORMAL);
0085 }
0086 
0087 void KisStrokeStrategyUndoCommandBased::finishStrokeCallback()
0088 {
0089     executeCommand(m_finishCommand, m_undo);
0090     notifyCommandDone(m_finishCommand,
0091                       KisStrokeJobData::SEQUENTIAL,
0092                       KisStrokeJobData::NORMAL);
0093 
0094     QMutexLocker locker(&m_mutex);
0095     if(m_macroCommand) {
0096         Q_ASSERT(m_undoFacade);
0097         postProcessToplevelCommand(m_macroCommand);
0098         m_undoFacade->postExecutionUndoAdapter()->addMacro(m_macroCommand);
0099         m_macroCommand = 0;
0100     }
0101 }
0102 
0103 void KisStrokeStrategyUndoCommandBased::cancelStrokeCallback()
0104 {
0105     QVector<KisStrokeJobData *> jobs;
0106     cancelStrokeCallbackImpl(jobs);
0107     addMutatedJobs(jobs);
0108 }
0109 
0110 void KisStrokeStrategyUndoCommandBased::cancelStrokeCallbackImpl(QVector<KisStrokeJobData*> &mutatedJobs)
0111 {
0112     QMutexLocker locker(&m_mutex);
0113     if(m_macroCommand) {
0114         m_macroCommand->getCommandExecutionJobs(&mutatedJobs, !m_undo);
0115 
0116         delete m_macroCommand;
0117         m_macroCommand = 0;
0118     }
0119 }
0120 
0121 void KisStrokeStrategyUndoCommandBased::doStrokeCallback(KisStrokeJobData *data)
0122 {
0123     Data *d = dynamic_cast<Data*>(data);
0124 
0125     if (d) {
0126         executeCommand(d->command, d->undo);
0127         if (d->shouldGoToHistory) {
0128             notifyCommandDone(d->command,
0129                               d->sequentiality(),
0130                               d->exclusivity());
0131         }
0132     } else {
0133         KisRunnableBasedStrokeStrategy::doStrokeCallback(data);
0134     }
0135 
0136 }
0137 
0138 void KisStrokeStrategyUndoCommandBased::runAndSaveCommand(KUndo2CommandSP command,
0139                                                           KisStrokeJobData::Sequentiality sequentiality,
0140                                                           KisStrokeJobData::Exclusivity exclusivity)
0141 {
0142     if (!command) return;
0143 
0144     executeCommand(command, false);
0145     notifyCommandDone(command, sequentiality, exclusivity);
0146 }
0147 
0148 void KisStrokeStrategyUndoCommandBased::notifyCommandDone(KUndo2CommandSP command,
0149                                                           KisStrokeJobData::Sequentiality sequentiality,
0150                                                           KisStrokeJobData::Exclusivity exclusivity)
0151 {
0152     if(!command) return;
0153 
0154     QMutexLocker locker(&m_mutex);
0155     if(m_macroCommand) {
0156         m_macroCommand->addCommand(command, sequentiality, exclusivity);
0157     }
0158 }
0159 
0160 void KisStrokeStrategyUndoCommandBased::setCommandExtraData(KUndo2CommandExtraData *data)
0161 {
0162     if (m_undoFacade && m_macroCommand) {
0163         warnKrita << "WARNING: KisStrokeStrategyUndoCommandBased::setCommandExtraData():"
0164                    << "the extra data is set while the stroke has already been started!"
0165                    << "The result is undefined, continued actions may not work!";
0166     }
0167 
0168     m_commandExtraData.reset(data);
0169 }
0170 
0171 void KisStrokeStrategyUndoCommandBased::setMacroId(int value)
0172 {
0173     m_macroId = value;
0174 }
0175 
0176 void KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(KUndo2Command *command)
0177 {
0178     if (m_commandExtraData) {
0179         command->setExtraData(m_commandExtraData.take());
0180     }
0181 
0182     KisSavedMacroCommand *savedCommand = dynamic_cast<KisSavedMacroCommand*>(command);
0183     if (savedCommand) {
0184         savedCommand->setMacroId(m_macroId);
0185     }
0186 }
0187 
0188  KisStrokeUndoFacade* KisStrokeStrategyUndoCommandBased::undoFacade() const
0189  {
0190      return m_undoFacade;
0191  }