Failed commands on QUndoStack
-
Qt Undo/Redo Framework is pretty neat but I have encountered a rather annoying problem with it. When a command fails to redo there is no way for it to be removed from the stack. Since it is the most recent command I fail to see why there is this limitation.
Essentially it would mean removing it upon failure or after calling QUndoCommand::undo if failure was indicated (same as going one step back and recording new command). Currently this could be partially implemented for stand-alone commands but not for macros or child commands as one has no control over it once it is finished.
I have already tried to implement it using the merging mechanism but it is not perfect (for instance if first command fails it cannot be merged with anything or it does not work outside macro's boundaries when macro is recording) and it prevents the merge to be used in a regular way.
The result is that the stack might contain bunch of invalid commands that do nothing (or undos changes that were not actually done) that causes problems ranging from mere inconvenience to potential corruption of data/state (if undo expects redo to be successful).
Are there any solutions to this problem?
-
This isn't the complete solution but work-around that will work for some cases and does not create too much overhead:
Have a base class of all your command sub-classes, such as
Note: Using C++11
@
class UndoBase : public QUndoCommand
{
public:
bool isFailed() const { return m_bFailed; }
void setDisabled(const bool &disabled) { m_bDisabled = disabled; }protected:
bool m_bFailed = false;
bool m_bDisabled = false;
};
@and then your commands like this
@
class MyUndo : public UndoBase
{
public:
virtual void undo() override
{
if(m_bDisabled) return;
//your undo code
}
virtual void redo() override
{
if(m_bDisabled) return;
//your redo code
m_bFailed = true; //if failed
}
};
@and then create a processing function for your commands either in subclass of QUndoStack or wherever like this (example assumes this function is part of QUndoStack subclass):
@
bool processCommand(UndoBase *cmd)
{
if(!cmd) return false;cmd->redo(); if(cmd->isFailed()) { delete cmd; return false; } else { cmd->setDisabled(true); push(cmd); cmd->setDisabled(false); return true; }
}
@This will ensure that any failed command gets discarded. However if you are recording in the macro the macro command will still be placed on the stack...
-
Very old topic, but anyway here is the answer, since 5.9.
QUndoCommand::setObsolete(bool)
Returns whether the command is obsolete.
The boolean is used for the automatic removal of commands that are not necessary in the
stack anymore. Will be checked in QUndoStack::push(),
QUndoStack::undo(), QUndoStack::redo(), and QUndoStack::setIndex().Example:
void YourUndoCommand::redo() { // Your code that failed for some reason... const bool redoFailed = true; if (redoFailed) { // Mark this command as absolute, which means // it will be removed automatically from the stack just after we leave redo. this->setObsolete(true); } }