Differences between revisions 4 and 5
Revision 4 as of 2008-08-04 06:46:43
Size: 6584
Editor: bcproxy
Comment:
Revision 5 as of 2008-08-19 18:50:40
Size: 7694
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
<<TableOfContents>>
Line 8: Line 10:
   * implementing command wrappers for script languages like wxLua is much easier.    * implementing command wrappers for script languages like [[wxLua]] is much easier.
Line 11: Line 13:
   * instead of dialogs checking in idle time for changes somewhere else, it is more effective to notify dialogs when a change did happen. Using commands to make all changes, garantees that no changes will be missed.    * instead of dialogs checking in idle time for changes somewhere else, it is more effective to notify dialogs when a change did happen. Using commands to make all changes, guarantees that no changes will be missed.
Line 15: Line 17:
A command is a class, and each command issued to make a change to documents result in a new instance being stored on the undo stack. The command stores enough information to reverse the change it did when submitted. So the command brings the document from one state into another, and it can be submitted reverse to get the previous state. All handling of commands is via <<Dox(a2dCommandProcessor), it stores commands submitted to it, and also implements undo and redoing those commands. In general at least every document has its own command processor. The reason for this is simple. Imagine drawing into one document, and next click on a view of a second document and start drawing there, a bit later going back to draw in the first document. One wants to be able to undo all changes in the first document, without undoing changes to the second document. To be able to do this one needs to have two separate undo stack, since commands can only be undone in reverse sequence. A command is a class, and each command issued to make a change to documents result in a new instance being stored on the undo stack. The command stores enough information to reverse the change it did when submitted. So the command brings the document from one state into another, and it can be submitted reverse to get the previous state. All handling of commands is via <<Dox(a2dCommandProcessor)>>, it stores commands submitted to it, and also implements undo and redoing those commands. In general at least every document has its own command processor. The reason for this is simple. Imagine drawing into one document, and next click on a view of a second document and start drawing there, a bit later going back to draw in the first document. One wants to be able to undo all changes in the first document, without undoing changes to the second document. To be able to do this one needs to have two separate undo stack, since commands can only be undone in reverse sequence.
Line 27: Line 29:
As explained above a change in a document can result in more changes to other objects. And those changes are triggered by, but unknown to the first change. And in most cases they can not even be issued as a range of commands in one part of the code. In fact the commands for the changes are distributed in various part of the library, and the can only be combined by grouping the up front. Grouping such distributed submitted commands, is simply a matter of opening a new command group in the command processor. As explained above a change in a document can result in more changes to other objects. And those changes are triggered by, but unknown to the first change. And in most cases they can not even be issued as a range of commands in a single part of the code. In fact the commands for the changes are distributed in various parts of the library, and they can only be combined by grouping them up front. Grouping such distributed submitted commands, is simply a matter of opening a new command group in the command processor.
Line 53: Line 55:
As you you see, the period separates the arguments for the command. Although this does look like standard C++, it still is. Not all commands use this way to set the arguments to the command, and writing the command class to use this system is a bit more complicated, the advantages become clear when there are many options to a command. Study the code of <<Dox(a2dCommand__AskFile)>> to see how its done.
As you you see, the period separates the arguments for the command. Although this does not look like standard C++, it still is. Not all commands use this way to set the arguments to the command, and writing the command class to use this system is a bit more complicated, the advantages become clear when there are many options to a command. Study the code of <<Dox(a2dCommand__AskFile)>> to see how its done.

= communication of command changes =

When a command is issued, in general some class instance its data is changed. For example the central strored current fill style is changed, using a command line command. New objects added to the document via commands will get this new fill style. But assume at the moment the fill style is changed, the canvas object edit tool <<Dox(a2dObjectEditTool)>> is active, editing a selected canvas object. Very likely you want that object to take on that new fill style. And if at the same time the <<Dox(a2dStyleDialog)>> is shown, you also want that dialog to show the new fill style. To achieve that, each submitted command generates some events, <<Dox(a2dCommand)>> has DistributeEvent( wxEventType eventType ) member, which is called with wxEVT_DO.
The default implementation is calling a2dGeneralGlobals->GetEventDistributer()->ProcessEvent() to distribute the event to all object that registrated to receive that event. See <<Dox(a2dEventDistributer)>> to learn more about that. The same with Undo and Redo is happening.

command processor

Most actions from the users which result in changes to a document, are done via commands. For this there are several reasons.

  • commands can be undone and redone
  • commands can be grouped, which can be undone at once.
  • a strict split between GUI,command line and on the other side the data storage layer.
  • implementing command wrappers for script languages like wxLua is much easier.

  • a central place to handle commands, which can be submitted from a command line as well as from dialogs or menu's.
  • each command generates an event, which can be intercepted by any class interested. Like if the users changes the current fill style, tools can decide to take over this style, and for example apply it directly to the object which is drawn.
  • instead of dialogs checking in idle time for changes somewhere else, it is more effective to notify dialogs when a change did happen. Using commands to make all changes, guarantees that no changes will be missed.

Undo and Redo

A command is a class, and each command issued to make a change to documents result in a new instance being stored on the undo stack. The command stores enough information to reverse the change it did when submitted. So the command brings the document from one state into another, and it can be submitted reverse to get the previous state. All handling of commands is via a2dCommandProcessor, it stores commands submitted to it, and also implements undo and redoing those commands. In general at least every document has its own command processor. The reason for this is simple. Imagine drawing into one document, and next click on a view of a second document and start drawing there, a bit later going back to draw in the first document. One wants to be able to undo all changes in the first document, without undoing changes to the second document. To be able to do this one needs to have two separate undo stack, since commands can only be undone in reverse sequence. At the same time one likes to be able to submit commands to open files, close files etc. For this a central command processor is used. Commands issued to that, in general can not be undone, but still are very useful for scripting and recording, and communicating changes in an application.

tree of grouped commands

Commands in a command processor are stored as a sequence, but at the same time a tree. Each group of commands, can become a new branch in the tree. The leafs on the tree are the actual commands. Why is it important to be able to group commands? There are situations where one action needs to be build up using several commands. As an example: drag an object which is connected by wires to other objects. Changing the position of an object is one command, but the changing wires are rewired, and all those changes are submitted from several locations inside the code. So a command can result in extra commands being submitted, and the command itself does not know this. Now by grouping such commands, makes it possible to undo the whole set of commands at once. The commands which did place the dragged object at a new position, and the commands which resulted indirectly from wires being replaced/rerouted, can be undone as if it was just one command. If we did not have such a grouping mechanism, the user could only undo the rewiring one wire at the time, and at last undo the actual drag of the object. This is not what he expects. One the other hand when drawing a polygon, one segment at a time, the user wants to be able to undo/redo the drawing one segment at a time. As soon as the polygon is finished and some other objects are drawn, undo should result in removing the polygons at once and not segment per segment. Again the segments drawing commands are placed inside a group, and as long as drawing the polygon is in action, the commands inside the group can be undone/redone. But when the polygon is finished, the group is closed, and a next polygon draw is placed in its own group. Undoing commands at the group level here, result in undoing the whole groups at once. Undoing a group commands is nothing more the automatically take all commands in the group and undoing them one by one.

In the end we have a tree structure, a grouped command contains several commands, which them selfs can be groups again.

where and when

As explained above a change in a document can result in more changes to other objects. And those changes are triggered by, but unknown to the first change. And in most cases they can not even be issued as a range of commands in a single part of the code. In fact the commands for the changes are distributed in various parts of the library, and they can only be combined by grouping them up front. Grouping such distributed submitted commands, is simply a matter of opening a new command group in the command processor.

groups of commands in a menu

A second use of a group is, to combine several commands in a group command first, without submitting it right away. Such a combined command can be attached to a menu. And when submitting the group command, all individual commands in the group will be submitted.

command classes with arguments

For commands a special way of adding arguments to the command are used. This makes it appear as a hash list of arguments. The advantage is that not all arguments need to be given, and the order is also not important. Here two examples of two commands which are prepared to be submitted:

   1 a2dCommand_GroupAB* command = new a2dCommand_GroupAB(     a2dCommand_GroupAB::Args().
   2         what( a2dCommand_GroupAB::BoolOperation_GroupAB ).
   3         operation( BOOL_A_SUB_B ).
   4         selectedA( true ).
   5         selectedB( true ) );

   1 a2dCommand_AskFile* command = new a2dCommand_AskFile( a2dCommand_AskFile::Args().
   2         message( _("Give Name of input Layer file") ).
   3         defaultDir( _T("%{layerFileSavePath}") ).
   4         extension( _T("*.cvg") ).
   5         fileFilter( _T("*.cvg") ).
   6         flags( wxOPEN | wxFILE_MUST_EXIST ).
   7         storeInVariable( _T("ask_file_result") ) ); 

As you you see, the period separates the arguments for the command. Although this does not look like standard C++, it still is. Not all commands use this way to set the arguments to the command, and writing the command class to use this system is a bit more complicated, the advantages become clear when there are many options to a command. Study the code of a2dCommand__AskFile to see how its done.

communication of command changes

When a command is issued, in general some class instance its data is changed. For example the central strored current fill style is changed, using a command line command. New objects added to the document via commands will get this new fill style. But assume at the moment the fill style is changed, the canvas object edit tool a2dObjectEditTool is active, editing a selected canvas object. Very likely you want that object to take on that new fill style. And if at the same time the a2dStyleDialog is shown, you also want that dialog to show the new fill style. To achieve that, each submitted command generates some events, a2dCommand has DistributeEvent( wxEventType eventType ) member, which is called with wxEVT_DO. The default implementation is calling a2dGeneralGlobals->GetEventDistributer()->ProcessEvent() to distribute the event to all object that registrated to receive that event. See a2dEventDistributer to learn more about that. The same with Undo and Redo is happening.

wxArt2D: wxArt2dCommandProcessing (last edited 2009-09-27 19:09:03 by KlaasHolwerda)