[-<--%--^-->-]
In the example in section
Example
we have seen that handler
objects may be bound to Tcl commands.
Handler objects may also be bound to 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, etcetera.
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 to decide 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 Eliens95.
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.
In the following, we will look at
how we may define a simple drawing editor
by declaring a handler defining the response
to pressing, moving and releasing a mouse button.
After that we will look more closely at the notion of events
and the definition of handlers and actions.
Before looking at the program, think of what
you would like a drawing editor to offer
you.
And, if you have any experience in programming
graphics applications,
how would you approach the implementation
of a drawing editor?
A drawing editor is a typical example of
an interactive program.
As a first
approximation, we will define a drawing editor
that allows the user
to paint a series of black dots by pressing
and moving the mouse button,
as pictured in figure Draw.
The program realizing our first attempt
is given below:
Again, the program may be broken up
in a number components.
First we must include
the hush.h header file.
Next we define
the class drawing_canvas.
The class drawing_canvas inherits
from the canvas
widget class and consequently allows for
drawing figures such as a circle.
See section
Canvas
and the appendix for further details
on the canvas class.
Before looking at the constructor of the
drawing_canvas, note that the
member functions press,
motion and release
expect a reference to an event.
These are precisely the member functions
corresponding to the event types
for which the canvas is sensitive.
The meaning of these member functions
becomes clear when looking at the role
of the instance variable dragging.
When
dragging
is non-zero and a motion event occurs,
a black dot is painted on the canvas.
Drawing starts when pressing a mouse button and
ends when releasing the button.
Turning back to the constructor,
we see that it expects a path string,
which is passed to the canvas
ancestor class to create an actual
canvas widget.
Further, the body of the constructor
sets the size of the widget to
200 by 100 and initializes
the variable dragging to zero.
Finally, the drawing_canvas widget
is declared to be its own handler.
The member function handler
is defined by the class widget.
Invoking the handler function
results in making the widget
sensitive to a number of predefined
events.
Discussion
A note on terminology is in place here.
The reader may be confused by the fact that
handlers can be bound to Tcl actions
as well as to events.
The situation may become even more confusing
when realizing that the widget
class itself is a descendant of the
handler class.
Schematically, we have
class widget : public handler {
public:
...
void bind(class handler* h) { ... }
...
};
The bind function declares
a handler object to be responsible
for dealing with the events
that are of interest to
the widget.
In other words,
a drawing_canvas fulfills
the dual role of being a widget and its
handler.
This must, however, be explicitly
indicated by the programmer,
which explains the occurrence
of the otherwise mysterious
expression bind(this).
The reason not to identify a widget
with a handler is simply that some widgets
may need separate handlers.
Before studying the
abstract handler class in more detail,
we will briefly look at the definition
of the event class.
Events always belong to a particular widget.
To which widget events are actually directed
depends on
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.
interface event {
int type(); // X event type
char* name(); // type as string
int x();
int y();
int button(int i = 0); // ButtonPress
int buttonup(int i = 0); // ButtonRelease
int motion(); // MotionNotify
int keyevent(); // KeyPress or KeyRelease
int buttonevent(int i = 0); // ButtonPress or ButtonRelease
int keycode();
void trace(); // prints event information
void* rawevent(); // 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, the routines check 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.
Handler objects provide a type secure
way to deal with local data and global
resources needed when responding to an event.
An actual handler class must be derived
from the (abstract) handler class defined below:
An instance of an actual handler class
may store the information
it needs in its instance variables when it is created.
A handler object can be activated in response
to an event or a Tcl command
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.
The definition of the dispatch function
is given below: