Binding actions to events

Handler objects may be bound to Tcl commands or events. Events are generated by the (X) window environment in response to actions of the user. These actions include pressing a mouse button, releasing a mouse button, moving the mouse, etc. Instead of explicitly dealing with all incoming events, applications delegate control to the environment by associating a callback function with each event that is relevant for a particular widget. This mechanism frees the programmer from the responsibility of deciding to which widget the event belongs and what action to take. Nevertheless, from the perspective of program design, the proper organization of the callback functions is not a trivial matter. Common practice is to write only a limited number of callback functions and perform explicit dispatching according to the type of event. An object-oriented approach may be advantageous as a means to organize a collection of callback functions as member functions of a single class. One way of doing this is to define an abstract event handler class which provides a virtual member function for each of the most commonly occurring events. In effect, such a handler class hides the dispatching according to the type of the event. A concrete handler class may then be defined simply by overriding the member functions corresponding to the events of interest. Before studying the abstract handler class in more detail, we will briefly look at the definition of the event class.

Events

Events always belong to a particular widget. To which widget events are actually directed depends upon whether the programmer has defined a binding for the event type. When such a binding exists for a widget and the (toolkit) environment decides that the event belongs to that widget, then the callback associated with that event is executed. Information concerning the event may be retrieved by asking the kit for the latest event. \slide{class-event}{The event class}{
  interface event  {  
\fbox{event}
int type();
\c{ X event type }
char* name();
\c{type as string}
int x(); int y(); ... void* rawevent();
\c{delivers} raw X event

};
}{
  interface event  {  
\fbox{event}
int type();
\c{// X event type }
char* name();
\c{// type as string}
int x(); int y(); int button(int i = 0);
\c{// ButtonPress}
int buttonup(int i = 0);
\c{// ButtonRelease}
int motion();
\c{// MotionNotify}
int keyevent();
\c{// KeyPress or KeyRelease}
int buttonevent(int i = 0);
\c{// ButtonPress or Release}
int keycode(); void trace();
\c{// prints event information}
void* rawevent();
// \c{delivers} raw X event
};
} } Event objects represent the events generated by the X-window system. Each event has a type. The type of the event can be inspected with type() which returns an integer value or name() which returns a string representation of the type. For some of the common event types, such as ButtonPress, ButtonRelease and MotionNotify, member functions are provided to facilitate testing. If an integer argument (1, 2 or 3) is given to button, buttonup or buttonevent, it is checked whether the event has occurred for the corresponding button. The functions x and y deliver the widget coordinates of the event, if appropriate. Calling trace for the event results in printing the type and coordinate information for the event. When setting the kit::trace level to 2 this information is automatically printed. Programmers not satisfied with this interface can check the type and access the underlying XEvent at their own risk. \c{

Handlers

Handler objects provide a type secure way in which to deal with local data and global resources needed when responding to an event. A handler object may store the information it needs in instance variables, when its constructor is called. See slide class-handler. } .so sli-handler \c{ Activating a handler object in response to an event or a Tcl command occurs by calling the dispatch function of the handler. The system takes care of this, provided that the user has bound the handler object to a Tcl command or event. } .so sli-dispatch \c{ The dispatch function, in its turn, calls the operator() function, after storing the kit, argc and argv arguments in the corresponding instance variables. See slide dispatching. The handler::operator() function fetches the latest event from the kit object and selects one of the predefined member functions (press, motion, release, etc.) according to the type of the event. The original handler class knows only virtual functions. Each of these function, including the dispatch and operator() function, may be redefined. The two-step indirection, via the dispatch and operator() functions, is introduced to facilitate derivation by inheritance, directly from the handler or from classes that are themselves derived from the handler class, such as the widget classes. }

Actions

Handler objects will be activated only when a binding has been defined, by using kit::action or implicitly by employing widget::bind or widget::handler. Such bindings may also be defined by kit::after or item::bind and item::handler. Implicit binding results in the creation of an anonymous action. Actions, as characterized by the class action, provide the actual means with which to bind C++ code to Tcl commands. Actions may be defined either trivially by a Tcl command, by a C/C++ command function or by handler objects. In effect, defining an action by means of a handler object amounts, implicitly, to defining a command function with a privileged client for which, by convention, the dispatch function is invoked. } \slide{class-client}{The client class}{

Client

client

  class client {  };  
empty class

Command

command

  typedef command(client*, kit*, int argc, char* argv[]);
  
} Data passed to a command function must be of the type client, which is defined by an empty class introduced only to please the compiler. In slide class-client the type definition of command is given. Apart from the client* parameter, a command function must also declare a kit* parameter and an argc and argv parameter, similar to that for main. The client* data of a command (and similarly for the clientdata parameter of a tclcommand) can be any kind of class. However, it is to be preferred that such classes are made a subclass of client. The client data pointer is declared when creating an action and passed to the command function when the actual call is made. The other parameters (argc and argv) depend on the actual call. The use of argc and argv comes from the original C interface of Tcl. It proves to be a very flexible way of communicating data, especially in string-oriented applications.
  interface action { 
\fbox{action}
action(char* name); action(char* name, handler* h); action(char* name, command f, client* data = 0); action(char* name, tcl_command f, ClientData data = 0); action( handler* h);
\ifsli{}{//} anonymous
action( command f, client* data = 0); action( tcl_command f, ClientData data = 0); char* name();
\c{// returns the name of an action}
};

slide: The action class

The class action offers no less than seven constructors. The first constructor, which takes a (char*) string as a parameter, is merely for convenience. It may be used to convert the name of a Tcl script command into an action. The following three constructors differ from the last three constructors only by their first string parameter, which serves to define the name under which the action will be known by the Tcl script interpreter. The last three constructors, in contrast, create anonymous actions, of which the user, however, can ask the name by invoking the function name. The preferred form of creating an action is by giving it (apart from a name) a handler as a parameter. As already noted, handler objects offer a type-secure way of dealing with client information. In contrast, the second constructor of this group, which takes a command function and possibly a pointer to client data as parameters, may make (type-insecure) conversions of the client data necessary. The constructor taking a {\em tcl_command} and ClientData as parameters is incorporated for compatibility reasons only. When using any of the last six constructors, as a side-effect an association is created between the name of the action and a Tcl command. If such a Tcl command already exists, the previous association will be overwritten. This is also the case if it has been defined as a Tcl script command.