Events in wxArt2D

Events are a major ingredient of WxArt2D. To get a loosely coupled library consisting of modules, it is very important not to use direct calls to functions in other modules. For instance using a pointer to an object in another module. This would directly result in a dependency of one module to another, since the calling module needs to know about the second module its class. One can prevent this type of dependencies often by notifying certain changes using events. The wxWidgets event system allows you to Connect events dynamically. This systems is used to connect to communication event. This way modules indirectly communicate, by lissening and reacting to certain event id's. One can compare this type of communication with a mailinglist on the internet. The event distributer is located in the base module, and other modules are layered on top of that. This approach of communicating via events is also mentioned Signaling.

When events are used within a class itself, it acts as a sort of advanced virtual function, the events will be processed directly by the class itself, and module dependencies are less important. This type of events with static event tables, is used more inside a module itself, when there is no need for other modules to know of the event.

Event handling starts already in the low levelmodule "general", with communication events like:

Next the DocviewModule module, which defines the document view framework, uses within all its classes the event based communicating mechanism. Here events are often distributed globally, in order to give application a way to react to changes in the document view framework. But also specialized events are sent from documents to makes views update them selfs after a change to a document. Many actions deal with opening/loading and closing files and documents, and how the GUI should react to that. For instance a new document that is loaded, should generate a new view or just reuse an existing view. Such actions are handled by a2dViewConnector, and when a connector is asked to connect a document into the GUI, the user code intercepts the right events in his derived connector to handle them the way he wants.

In the canvas module events are used to capture mouse events entering and leaving a2dCanvasObject's. This is eventually the key to editing those objects with tools. The tools are defined in the editor module, and use events again, in many ways like:

All in all there are loads of events, and all for a specific purpose. They are documented as much as possible at the emitting (sometimes receiving) class.

Command Id

The reason why commands are used to record and apply changes is explained in command processing. Each command generates an event, which can be intercepted by any class interested.

For commands submitted to the command processor, not a string is used to name the command, instead a unique a2dCommandId is associated with a command. This Id can easily be tested in other classes which do detect command events. Commands are derived from a2dCommand, and they are given a unique Id in each derived command class. When a command is submitted to a a2dCommandProcessor a a2dCommandProcessorEvent with id wxEVT_DO is transmitted. To update a dialog which is related to the command issued, one intercepts the wxEVT_DO event, and in there you test the command Id.

   1 BEGIN_EVENT_TABLE( a2dMeasureDlg, wxDialog )
   2     EVT_DO( a2dMeasureDlg::OnDoEvent )
   3 END_EVENT_TABLE()
   4 
   5 void a2dMeasureDlg::OnDoEvent( a2dCommandProcessorEvent& event )
   6 {
   7     if ( GetEvtHandlerEnabled() )
   8     {    
   9         if ( event.GetCommand()->GetCommandId() == &a2dCommand_SetRelativeStart::Id )
  10         {
  11             // the command we need to react to, is issued, now update this dialog.    
  12             a2dCommand_SetRelativeStart* command = wxDynamicCast( event.GetCommand(),a2dCommand_SetRelativeStart );
  13             
  14         }
  15         //code for other command Id's
  16     }        
  17 }

Menu id for calling commands and actions

a2dMenuIdItem is a class to hold a template to create a wxMenu. That menu will call the right commands in the appropriate command handler. a2dDocumentCommandProcessor::OnMenu() The a2dMenuIdItem It can be stored to a resource file (xrc). There are many predifined menu templates you can use in your own application to build menu's. To name a few: CmdMenu_FileOpen, CmdMenu_FileClose, CmdMenu_FileNew.

communicating information

Many actions in wxArt2D are done via sending events to a central class called a2dEventDistributer. One can have more then one, but the library itself uses a single instance, which is reachable via a2dGeneralGlobals->GetEventDistributer(). A class instance which thinks an action it performs, might be interesting for other objects in the application, creates an event with a unique Id, and tells the central distribution to send it to all classes instances which did register themself to the a2dEventDistributer. For some special actions, special event classes are created, like down here for the command Do action.

   1         a2dCommandProcessorEvent event( wxEVT_UNDO, (a2dCommand*) &cmd );
   2         event.SetEventObject(this);
   3         a2dGeneralGlobals->GetEventDistributer()->ProcessEvent( event );

With this you can intercept the event with e.g. a static event table.

   1 BEGIN_EVENT_TABLE( a2dMeasureDlg, wxDialog )
   2     EVT_DO( a2dMeasureDlg::OnDoEvent )
   3 END_EVENT_TABLE()

For most actions there is no need to create special event classes, because the information to communicate to the receivers, is already available as part of the sender event object. Creating event classes which can be used in event tables, is a bit cumbersome, therefore there is the swiss knife event class a2dComEvent, which can handle information on basic types, and/or a pointer to the sending class instance. A Unique ID for the event, makes it possible to cast the eventobject back to the actual class pointer. Also it possible to create an internal a2dProperty to hold extra information as part of the event. The draw back is that one needs to know what information can be retreived for a specific event ID. Therefore documentation is needed for each event ID to tell what information can be retrieved from the event.

   1 class a2dCanvasView: public a2dView
   2 
   3 //..............
   4         static const wxEventType sm_changedShowObject;
   5 //..............    
   6 }    
   7 
   8 void a2dCanvasView::SomeMember()
   9 {
  10     a2dComEvent changedShow( this, m_top, &sm_changedShowObject );
  11     a2dDocviewGlobals->GetEventDistributer()->ProcessEvent( changedShow );
  12 }

Which can be handled like this, in say a class a2dRecursiveEditTool, which is Registrated to a2dEventDistributer:

   1 A2D_BEGIN_EVENT_TABLE(a2dRecursiveEditTool,a2dObjectEditTool)
   2         A2D_EVT_COM_EVENT( a2dRecursiveEditTool::OnComEvent )
   3 A2D_END_EVENT_TABLE()
   4 
   5 void a2dRecursiveEditTool::OnComEvent( a2dComEvent& event )
   6 {
   7     if ( event.GetEventComId() == &a2dCanvasView::sm_changedShowObject )
   8     {
   9         if ( event.GetEventObject() == GetCanvasView() )
  10         {
  11             //do what needs to be done
  12             a2dCanvasObject* newtop = wxStaticCast( event.GetProperty()->GetRefObject(), a2dCanvasObject );
  13             if ( newtop )
  14             {
  15             }
  16         }
  17     }
  18     //handle all other com events
  19 }    

wxArt2dEvents (last edited 2013-02-26 14:21:04 by KlaasHolwerda)