Command management
Command record
A command is represented in memory by a record of variable size, starting
with an encoded data field and followed by zero, one or more 32-bit tokens.
VP::Cmd::Record is the base class which
implements access helper methods (vp/CmdManager.h).
The encoded data field stores the following information :
- Command category.
- Command flags.
- Command undoability.
- Command class ID.
Command category
- CAT_REGULAR, regular command.
- CAT_GROUP_ENTER, start of command group.
- CAT_GROUP_LEAVE, end of command group.
Command flags
- FLAG_VALIDATE, if set when the command
category is CAT_GROUP_LEAVE, this bit
specifies that the command group has been validated; if cleared, the command
group has been invalidated. This information is used by the group start
command to handle both cases (validation and cancellation), so that a single
group end command can be used.
Command undoability
- UNDO_NOT_NEEDED, the command cannot (and
does not need to) be undone.
- UNDO_WITH_PREV, the command will be undone
automatically with the previous one.
- UNDO_WITH_NEXT, the command will be undone
automatically with the next one.
- UNDO_INFO_ONLY,
the command has no effect on the document but is used as a time stamp or
other sort of mark in the command history (such as document saved).
- UNDO_REGULAR, the command can be
undone/redone. This is the case of most commands which are not directly tied
to the next or previous one.
Command class ID
- CID_FENCE, special class ID used to
specify a fence in the command history (either the beginning of the command
history, or the end of it).
- CID_NO_OP, no-op command.
- CID_FORK, command history forking point.
At this point, the command histroy can branch to one or more different
paths. The command record stores the indexes of the beginning of every extra
branch.
- CID_BACK_TO_FORK, command history link to
forking point, used to specify a fence at the beginning of a forking branch.
Every CID_FORK index points to a CID_BACK_TO_FORK
command, and the CID_BACK_TO_FORK points
back to the CID_FORK command.
- CID_TRANSACTION, command history
transaction, which specifies a block of indivisible operations.
- CID_... maps to a specific command. Every
command has a unique command class ID.
Command class
The command classes all derive from class VP::Cmd::Base
and implement the interface VP::Cmd::ProcessOp.
Thus, every command class implements the following members :
- A constructor taking a pointer to the document, which simply forwards the
construction to VP::Cmd::Base. When you
create a command, you use this constructor.
- A special constructor, which initialises VP::Cmd::Base
with the command class ID and the associated command name. This constructor
is solely used to register the command class, so never use it directly.
- A virtual method ProcessUndo, which
implements the undo behaviour of the command.
- A virtual method ProcessRedo, which
implements the redo behaviour of the command.
- A virtual method Purge, which is used to purge
the command from the command history, when it is no longer needed.
- A virtual method Optimise, which is used
to optimise the command, usually collapsing the current and the
previous commands into a single one.
- An operator (), which implements the do
behaviour.
- A virtual method Execute, which is used to
execute the do behaviour based on textual arguments, when the command
is invoked from command scripts. This is based on operator ().
Methods ProcessUndo and ProcessRedo
Methods ProcessUndo and ProcessRedo
are very similar. They must operate on the provided command record. The
operation being performed is specified by the parameter op
and is one of the VP::Result::ACTION_xxx values.
VP::Result::Code
ProcessUndo (VP::Result::Code op,
VP::Cmd::RecordPtr record)
The parameter op
must be interpreted as following :
- VP::Result::ACTION_VALIDATE : undoing a validated sequence, which
means that we must undo from the current command down to the command
following the leading command,
start the sequence again and finally redo from the command following the
leading command up to the
current command.
This is needed for an object creation sequence, when undoing the validate or
cancel creation command, in order to put the document manager back into
creation mode. An object creation sequence can be
represented by following commands :
- ObjectCreation, puts the document
manager into creation mode.
- PointCreation, PointCreation
... PointCreation, defines the points
needed by the graphical object.
- ValidateCreation / CancelCreation,
either validate the creation or cancel it.
- VP::Result::ACTION_CANCEL : undoing a
cancelled sequence; see description about undoing a validated sequence. This
should make no difference to the leading command.
- VP::Result::ACTION_EXECUTE : undo
the command. For a command block, this means that the block was left and
fully undone.
VP::Result::Code
ProcessRedo (VP::Result::Code op,
VP::Cmd::RecordPtr record)
The parameter op
must be interpreted as following :
- VP::Result::ACTION_VALIDATE : redoing a validated
sequence. This means that the end of the sequence has just been redone (with
a sequence validation command). The leading command usually restores the
object(s) resulting from the command sequence execution, since it usually
also implements the undo.
- VP::Result::ACTION_VALIDATE_AUTO :
automatic validation (e.g. for a segment, the validation is always
automatic, since no more than 2 points may be defined; for another shape, a
manual validation might be required).
- VP::Result::ACTION_VALIDATE_CLOSE :
close validation (e.g. when closing a shape).
- VP::Result::ACTION_CANCEL : redoing a
cancelled sequence. Same logic as for the validation, however the object(s)
resulting from the command sequence execution will not be retained.
- VP::Result::ACTION_EXECUTE : redo
the command. For a command block, this means that the block was entered and
just started to be redone.
Method Purge
void
Purge (VP::Result::Code, VP::Cmd::RecordPtr)
Purge may be called either to purge the undo
or the redo history, depending on the value of the code parameter :
- VP::Result::PURGE_UNDO : free objects
maintained in order to undo the command. This can only be called if the
command is in the done or redone state.
The Delete command, for instance, will
delete the object it maintained to undo the deletion.
- VP::Result::PURGE_REDO : free objects
maintained in order to redo the command. This can only be called if the
command is in the undone state.
The CreatePoint command, for instance, will
delete the point it maintained to redo the creation.
- VP::Result::PURGE_OPTIMISE : free
objects because of an optimisation. The object is in the undoable state, so
this should usually be implemented just like PURGE_UNDO.
Method Optimise
void
Optimise (VP::Cmd::RecordPtr)
Optimise may be called to collapse the
current command with the previous one. Usually, this method :
- Calls VP::Cmd::Manager::OptimiseCheck,
- compares the contents of the previous record with the current one, and if
both can be reduced to a single one :
- updates the previous record and
- removes the current record by calling VP::Cmd::Manager::OptimiseRemove.
Operator ()
void
operator () (T1 const & arg1, T2 const & arg2)
The command class' operator () implements the do behaviour and has
zero, one or more arguments (const passed by
reference). The types of the arguments can be one of the following :
- OPStringRef, a text passed as an OPaC
string object.
- VP::Unit::Val, a generic value with a
specified unit (e.g. 10.5 mm).
- VP::ObjectIndex, an object index (the
index refers to the object store).
- bool, a boolean.
- VP::Result::Code, an action code (VP::Result::ACTION_xxx).
Other types can be used, but an implementation for the templatized function VP::Cmd::Parse<T>
must be provided (see method Execute).
Method Execute
void
Execute (VP::CharCPtr script)
This method decodes the script arguments and forwards them to the command's operator
(), based on the templatized decoding function VP::Cmd::Parse<T>.
Command base class
class VPCoreDLL Base : public VP::Cmd::ProcessOp
{
protected:
...
Base (VP::Cmd::ClassID cid, const char* name);
Base (VP::DocumentPtr document);
};
Command definition
A command is best defined by using the CMD_DEFINITION
macro :
CMD_DEFINITION (Name, (T1 arg1, T2 arg2));
which defines a command class named Cmd_Name,
tagged with the CID_Name unique class ID. The
command 'do' operation (in this case) takes two arguments of type T1
and T2.
The implementation of the command is simplified by the following
macros :
- CMD_IMPLEMENT_EXECUTE_n, where n
specifies the number of parameters (from 0 to n) which are taken by
the 'do' operation, implements the core 'do' operation of the command.
- CMD_IMPLEMENT_UNDO, implements the undo
part of the command.
- CMD_IMPLEMENT_REDO, implements the redo
part of the command.
- CMD_IMPLEMENT_PURGE, implements the purge
part of the command; the purge is needed when a command (which is always in
the undone state) is removed from the command history.
- CMD_IMPLEMENT_OPTIMISE, implements the optimise
part of the command.
Here is a
partial example of an implementation for command Cmd_Name
defined previously :
CMD_IMPLEMENT_EXECUTE_2 (Name, T1, arg1, T2, arg2)
{
VP::Cmd::RecordPtr record = 0;
record = cmd_manager->Insert (VP::Cmd::CAT_REGULAR,
VP::Cmd::CID_Name,
VP::Cmd::UNDO_REGULAR, 3);
...
}
CMD_IMPLEMENT_UNDO (Name)
{
...
return VP::Result::STATUS_OK;
}
CMD_IMPLEMENT_REDO (Name)
{
...
return VP::Result::STATUS_OK;
}
CMD_IMPLEMENT_PURGE (Name)
{
...
}
CMD_IMPLEMENT_OPTIMISE (Name)
{
...
}
There are default "no-op" macros for the undo, redo, purge and
optimise methods. There are also default "not implemented" macros for
the undo and the redo. And finally, there is a macro to specify that undo and
redo are symmetric and need only one implementation. Pick the one you
need :
CMD_NOP_UNDO (Name);
CMD_NOP_REDO (Name);
CMD_NOP_PURGE (Name);
CMD_NOP_OPTIMISE (Name);
CMD_UNIMPLEMENTED_UNDO (Name);
CMD_UNIMPLEMENTED_REDO (Name);
CMD_SYMMETRIC_REDO (Name);
To avoid having to modify/extend every command source file the day a new
command method is added, it is highly recommended to use one of the following default
macros :
- CMD_BASE_DEFAULT, includes CMD_NOP_UNDO,
CMD_NOP_REDO, CMD_NOP_PURGE
and CMD_OPTX_DEFAULT.
- CMD_OPTX_DEFAULT, includes CMD_NOP_OPTIMISE
and CMD_X_DEFAULT.
- CMD_X_DEFAULT, for the future extensions.
Defining a parsable enum
It is possible to define enums and make them parsable automatically by the
command manager :
ENUM_PARSER_BEGIN (EnumTypeName)
{ "label 1", ENUM_LABEL_1 },
{ "label 2", ENUM_LABEL_2 },
{ 0, INVALID_ENUM_LABEL }
ENUM_PARSER_END ()
This defines an implementation for the Parse<EnumTypeName>
template which converts a textual argument into an enumeration value.
It is also possible to define the parsing table programmatically, using the
following macro :
const char*
RetEnumLabelName (EnumTypeName value)
{
...
}
ENUM_PARSER (EnumTypeName, RetEnumLabelName,
INVALID_ENUM_LABEL, ENUM_LABEL_END)
This defines an implementation for the Parse<EnumTypeName>
template which converts a textual argument into an enumeration value. The
textual representations of the enum values are provided by a function; INVALID_ENUM_LABEL
should define an invalid label and ENUM_LABEL_END
the last valid label. Label values should not start below 0 (but for the invalid
value).
Command implementation
...
Exec command
The generic command Exec takes the contents of
either the named variable or the !action
variable and executes it as if it were a command itself.
If the variable
begins with 'Dialog.', then this substring will
be stripped and replaced by the name of the dialog; this allows the reuse of the
same GUI description in different dialogs, with different command bindings.
The elements embedded within {} are substituted by the values of their corresponding variables. If the
variables are, for example :
!action = Dialog.Test {a1} {a2}
a1 = foo
a2 = bar
and the dialog is named 'Settings', then the
following command will get executed :
Settings_Test foo bar
Adding a {*} in the !action
variable will force an additional substitution pass, which allows the use of
meta variables (if a variable contains another variable reference, the final
variable value will be used, in the end).
List of available commands
The following commands are currently implemented by VP :
- DeleteObject (VP::ObjectIndex object)
- ReplaceAlias (VP::ObjectIndex a1, VP::ObjectIndex a2)
- CloneStyle (VP::ObjectIndex style)
- SetFamilyStyle (VP::ObjectIndex style)
- ObjectCreation (VP::ObjectIndex after)
- ValidateCreation (VP::Result::Code action)
- CancelCreation ()
- PointCreation (VP::Unit::Val x, VP::Unit::Val y)
- UpdatePointCreation (VP::Unit::Val x, VP::Unit::Val y)
- ValidatePointCreation ()
- ObjectSelection (VP::ObjectIndexStack objects)
- ObjectDeselection ()
- StartObjectSelection (VP::ObjectIndex view)
- ValidateObjectSelection ()
- CancelObjectSelection ()
- BeginRectInObjectSelection (VP::Unit::Val x, VP::Unit::Val y)
- UpdateRectInObjectSelection (VP::Unit::Val x, VP::Unit::Val y)
- EndRectInObjectSelection ()
- GripNotification (Card32 index, VP::Path::Event event)
- SetDefObject (VP::ObjectIndex def_object)
- NewDocument ()
- OpenDocument (OPStringRef name)
- SaveDocument (OPStringRef name)
- SaveAsDocument (OPStringRef name)
- SaveAllDocument ()
- SaveCopyDocument (OPStringRef name)
- PrintDocument ()
- SpecialQuit ()
- AttrSetValue (VP::ObjectIndex attr, VP::DocAttr::PropIndex prop,
VP::Unit::Val v)
- Exec ()
- ViewLeftPress (Card32 dialog_id)
- ViewLeftRelease (Card32 dialog_id)
- ViewRightPress (Card32 dialog_id)
- ViewRightRelease (Card32 dialog_id)
- DialogOpenDocument ()
- DialogSaveDocument ()
- DebugDumpDocument ()
- DebugDumpDialogs ()
- DebugDumpDialogVars (Card32 id)
- DebugDumpObjTypes ()
- DebugDumpUIControllers ()
- DebugDumpCommands ()
- DebugBreak ()
- FullSample_SelectStyle (Card32 sel)
- FullSample_SwapPaint ()
- MoveObject (-)
- MoveGrip (-)