File indexing completed on 2024-12-22 04:10:03
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_saved_commands.h" 0008 0009 #include <QVector> 0010 0011 #include "kis_image_interfaces.h" 0012 #include "kis_stroke_strategy_undo_command_based.h" 0013 #include <KisAsynchronouslyMergeableCommandInterface.h> 0014 0015 0016 KisSavedCommandBase::KisSavedCommandBase(const KUndo2MagicString &name, 0017 KisStrokesFacade *strokesFacade) 0018 : KUndo2Command(name), 0019 m_strokesFacade(strokesFacade), 0020 m_skipOneRedo(true) 0021 { 0022 } 0023 0024 KisSavedCommandBase::~KisSavedCommandBase() 0025 { 0026 } 0027 0028 KisStrokesFacade* KisSavedCommandBase::strokesFacade() 0029 { 0030 return m_strokesFacade; 0031 } 0032 0033 void KisSavedCommandBase::runStroke(bool undo) 0034 { 0035 KisStrokeStrategyUndoCommandBased *strategy = 0036 new KisStrokeStrategyUndoCommandBased(text(), undo, 0); 0037 strategy->setUsedWhileUndoRedo(true); 0038 0039 KisStrokeId id = m_strokesFacade->startStroke(strategy); 0040 addCommands(id, undo); 0041 m_strokesFacade->endStroke(id); 0042 } 0043 0044 void KisSavedCommandBase::undo() 0045 { 0046 0047 runStroke(true); 0048 } 0049 0050 0051 0052 void KisSavedCommandBase::redo() 0053 { 0054 /** 0055 * All the commands are first executed in the stroke and then 0056 * added to the undo stack. It means that the first redo should be 0057 * skipped 0058 */ 0059 0060 if(m_skipOneRedo) { 0061 m_skipOneRedo = false; 0062 return; 0063 } 0064 0065 runStroke(false); 0066 } 0067 0068 0069 KisSavedCommand::KisSavedCommand(KUndo2CommandSP command, 0070 KisStrokesFacade *strokesFacade) 0071 : KisSavedCommandBase(command->text(), strokesFacade), 0072 m_command(command) 0073 { 0074 } 0075 0076 int KisSavedCommand::id() const 0077 { 0078 return m_command->id(); 0079 } 0080 0081 bool KisSavedCommand::mergeWith(const KUndo2Command* command) 0082 { 0083 return unwrap(command, [this] (const KUndo2Command *cmd) { 0084 return m_command->mergeWith(cmd); 0085 }); 0086 } 0087 0088 bool KisSavedCommand::canAnnihilateWith(const KUndo2Command *command) const 0089 { 0090 return unwrap(command, [this] (const KUndo2Command *cmd) { 0091 return m_command->canAnnihilateWith(cmd); 0092 }); 0093 } 0094 0095 void KisSavedCommand::addCommands(KisStrokeId id, bool undo) 0096 { 0097 strokesFacade()-> 0098 addJob(id, new KisStrokeStrategyUndoCommandBased::Data(m_command, undo)); 0099 } 0100 int KisSavedCommand::timedId() const 0101 { 0102 return m_command->timedId(); 0103 } 0104 void KisSavedCommand::setTimedID(int timedID) 0105 { 0106 m_command->setTimedID(timedID); 0107 } 0108 0109 bool KisSavedCommand::timedMergeWith(KUndo2Command *other) 0110 { 0111 /// Since we are saving the actual command inside another 0112 /// command, so we cannot unwrap the command here. Otherwise 0113 /// the shared pointer in `other` will destroy the merged 0114 /// command some time later. 0115 0116 return m_command->timedMergeWith(other); 0117 } 0118 QVector<KUndo2Command*> KisSavedCommand::mergeCommandsVector() const 0119 { 0120 return m_command->mergeCommandsVector(); 0121 } 0122 void KisSavedCommand::setTime(const QTime &time) 0123 { 0124 m_command->setTime(time); 0125 } 0126 0127 QTime KisSavedCommand::time() const 0128 { 0129 return m_command->time(); 0130 } 0131 void KisSavedCommand::setEndTime(const QTime &time) 0132 { 0133 m_command->setEndTime(time); 0134 } 0135 0136 QTime KisSavedCommand::endTime() const 0137 { 0138 return m_command->endTime(); 0139 } 0140 bool KisSavedCommand::isMerged() const 0141 { 0142 return m_command->isMerged(); 0143 } 0144 0145 0146 0147 struct KisSavedMacroCommand::Private 0148 { 0149 struct SavedCommand { 0150 KUndo2CommandSP command; 0151 KisStrokeJobData::Sequentiality sequentiality; 0152 KisStrokeJobData::Exclusivity exclusivity; 0153 }; 0154 0155 QVector<SavedCommand> commands; 0156 int macroId = -1; 0157 0158 const KisSavedMacroCommand *overriddenCommand = 0; 0159 QVector<const KUndo2Command*> skipWhenOverride; 0160 }; 0161 0162 KisSavedMacroCommand::KisSavedMacroCommand(const KUndo2MagicString &name, 0163 KisStrokesFacade *strokesFacade) 0164 : KisSavedCommandBase(name, strokesFacade), 0165 m_d(new Private()) 0166 { 0167 } 0168 0169 KisSavedMacroCommand::~KisSavedMacroCommand() 0170 { 0171 delete m_d; 0172 } 0173 0174 void KisSavedMacroCommand::setMacroId(int value) 0175 { 0176 m_d->macroId = value; 0177 } 0178 0179 int KisSavedMacroCommand::id() const 0180 { 0181 return m_d->macroId; 0182 } 0183 0184 bool KisSavedMacroCommand::mergeWith(const KUndo2Command* command) 0185 { 0186 const KisSavedMacroCommand *other = 0187 dynamic_cast<const KisSavedMacroCommand*>(command); 0188 0189 if (!other || other->id() != id() || id() < 0 || other->id() < 0) return false; 0190 0191 QVector<Private::SavedCommand> &otherCommands = other->m_d->commands; 0192 0193 if (other->m_d->overriddenCommand == this) { 0194 m_d->commands.clear(); 0195 0196 Q_FOREACH (Private::SavedCommand cmd, other->m_d->commands) { 0197 if (!other->m_d->skipWhenOverride.contains(cmd.command.data())) { 0198 m_d->commands.append(cmd); 0199 } 0200 } 0201 0202 if (other->extraData()) { 0203 setExtraData(other->extraData()->clone()); 0204 } else { 0205 setExtraData(0); 0206 } 0207 return true; 0208 } 0209 0210 if (m_d->commands.size() != otherCommands.size()) return false; 0211 0212 auto it = m_d->commands.constBegin(); 0213 auto end = m_d->commands.constEnd(); 0214 0215 auto otherIt = otherCommands.constBegin(); 0216 auto otherEnd = otherCommands.constEnd(); 0217 0218 bool sameCommands = true; 0219 while (it != end && otherIt != otherEnd) { 0220 KisAsynchronouslyMergeableCommandInterface *iface1 = 0221 dynamic_cast<KisAsynchronouslyMergeableCommandInterface*>(it->command.data()); 0222 0223 if (!iface1 || !iface1->canMergeWith(otherIt->command.data()) || 0224 it->command->id() < 0 || otherIt->command->id() < 0 || 0225 it->command->id() != otherIt->command->id() || 0226 it->sequentiality != otherIt->sequentiality || 0227 it->exclusivity != otherIt->exclusivity) { 0228 0229 sameCommands = false; 0230 break; 0231 } 0232 ++it; 0233 ++otherIt; 0234 } 0235 0236 if (!sameCommands) return false; 0237 0238 it = m_d->commands.constBegin(); 0239 otherIt = otherCommands.constBegin(); 0240 0241 while (it != end && otherIt != otherEnd) { 0242 if (it->command->id() != -1) { 0243 bool result = it->command->mergeWith(otherIt->command.data()); 0244 KIS_ASSERT_RECOVER(result) { return false; } 0245 } 0246 ++it; 0247 ++otherIt; 0248 } 0249 0250 if (other->extraData()) { 0251 setExtraData(other->extraData()->clone()); 0252 } else { 0253 setExtraData(0); 0254 } 0255 0256 return true; 0257 } 0258 0259 bool KisSavedMacroCommand::canAnnihilateWith(const KUndo2Command* command) const 0260 { 0261 const KisSavedMacroCommand *other = 0262 dynamic_cast<const KisSavedMacroCommand*>(command); 0263 0264 if (!other || other->id() != id() || id() < 0 || other->id() < 0) return false; 0265 0266 QVector<Private::SavedCommand> &otherCommands = other->m_d->commands; 0267 0268 if (other->m_d->overriddenCommand) return false; 0269 if (m_d->commands.size() != otherCommands.size()) return false; 0270 0271 auto it = m_d->commands.constBegin(); 0272 auto end = m_d->commands.constEnd(); 0273 0274 auto otherIt = otherCommands.constBegin(); 0275 auto otherEnd = otherCommands.constEnd(); 0276 0277 bool sameCommands = true; 0278 while (it != end && otherIt != otherEnd) { 0279 0280 if (!it->command->canAnnihilateWith(otherIt->command.data()) || 0281 it->command->id() < 0 || otherIt->command->id() < 0 || 0282 it->command->id() != otherIt->command->id() || 0283 it->sequentiality != otherIt->sequentiality || 0284 it->exclusivity != otherIt->exclusivity) { 0285 0286 sameCommands = false; 0287 break; 0288 } 0289 ++it; 0290 ++otherIt; 0291 } 0292 0293 if (!sameCommands) return false; 0294 0295 return true; 0296 } 0297 0298 void KisSavedMacroCommand::addCommand(KUndo2CommandSP command, 0299 KisStrokeJobData::Sequentiality sequentiality, 0300 KisStrokeJobData::Exclusivity exclusivity) 0301 { 0302 Private::SavedCommand item; 0303 item.command = command; 0304 item.sequentiality = sequentiality; 0305 item.exclusivity = exclusivity; 0306 0307 m_d->commands.append(item); 0308 } 0309 0310 void KisSavedMacroCommand::getCommandExecutionJobs(QVector<KisStrokeJobData *> *jobs, bool undo, bool shouldGoToHistory) const 0311 { 0312 QVector<Private::SavedCommand>::iterator it; 0313 0314 if(!undo) { 0315 for(it = m_d->commands.begin(); it != m_d->commands.end(); it++) { 0316 *jobs << new KisStrokeStrategyUndoCommandBased:: 0317 Data(it->command, 0318 undo, 0319 it->sequentiality, 0320 it->exclusivity, 0321 shouldGoToHistory); 0322 } 0323 } 0324 else { 0325 for(it = m_d->commands.end(); it != m_d->commands.begin();) { 0326 --it; 0327 0328 *jobs << new KisStrokeStrategyUndoCommandBased:: 0329 Data(it->command, 0330 undo, 0331 it->sequentiality, 0332 it->exclusivity, 0333 shouldGoToHistory); 0334 } 0335 } 0336 } 0337 0338 void KisSavedMacroCommand::setOverrideInfo(const KisSavedMacroCommand *overriddenCommand, const QVector<const KUndo2Command*> &skipWhileOverride) 0339 { 0340 m_d->overriddenCommand = overriddenCommand; 0341 m_d->skipWhenOverride = skipWhileOverride; 0342 } 0343 0344 void KisSavedMacroCommand::addCommands(KisStrokeId id, bool undo) 0345 { 0346 QVector<KisStrokeJobData *> jobs; 0347 getCommandExecutionJobs(&jobs, undo); 0348 0349 Q_FOREACH (KisStrokeJobData *job, jobs) { 0350 strokesFacade()->addJob(id, job); 0351 } 0352 }