Overview of the Event Manager

Events are a major component of any application framework.

Events come in several types. There are user events (such as keyboard clicks), system events (such as periodic events), YAAF generated events (such as a button click event) and application generated events. Each of these events need to be passed to objects (such as windows, documents or forms) which are interested in the event.

YAAF provides three different event dispatching mechanisms. The first mechanism is a simple 'global broadcast' mechanism; events are broadcast to all receivers who are registered to receive global broadcasts. These messages are useful for telling all interested objects that some global state (such as a global preference or the color environment) has changed.

The second mechanism is related to the first: individual senders can send to all registered receivers. This mechanism is useful for setting up objects (such as databases) which need to notify only some receivers (such as the window into the database) about a change in the contents.

A third mechanism is the 'dispatch' mechanism; this is a hierarchical system of processing events which handle the concept of a 'focus' object, for handling things like an edit box within a dialog window.

This third mechanism works by first sending the event to the current "focus" in the "active" window. If the 'focus' object processes the event, the results are then returned to the code sending the event. If the 'focus' object does not process the event, the event is passed up to the object containing the focus object, eventually up to the containing Window object (XGWindow) and to the Application (XGAppCore) object, until it is eventually handled.

For example, consider the text editor XTextEdit (or xt on Unix). It consists of multiple windows, each one with a couple of scrollbars and a text editing area.

When a user presses a key, the YAAF libraries sends an event of type KXGKeyEvent (defined in <XMessages.h>) with the type of key and the modifier. This message is sent to the current "focus"; that is the current view which has asked to receive events in the current window; in this case, the text edit region of the window.

Each individual block represents the class and it's root classes. (Not all root classes in the hierarchy defining the class are shown.) The bold block is the view within the active window which has the "focus".

When the keyboard event is sent, it is sent to the focus of the currently active window. The TEditView has first crack at the event; if it accepts the event (and it does with keyboard events), it handles the processing and returns immediately.

Consider the case when the user selects "Quit" from the File menu. In this case, an event is also generated (an KXGDoMenuCommand event), and is also sent to the currently active window's focus. The event is not processed by TEditView, so the event manager checks with the XGView's class to determine if the event is handled.

As the event is not handled there, the event is then sent up to the view containing the TEditView (the XGScrollView class), and so forth. Eventually the event winds up with the XGAppCore class, where the quit menu command is eventually processed.

The hierarchy of event handlers generally reflects the visible structure of objects on the screen, but they don't have to: both XGDocument and XGFocus classes insert themselves into this processing chain in order to provide added functionality to the application. (XGDocuments insert themselves between the XGAppCore derived class and the windows that contain those documents; XGFocus events are called by the XGWindow event handler, and so are "beside" the XGWindow object.)

One primary difference between the YAAF library and other application frameworks is the introduction of the "XGFocus" object. This object defines who has the current "focus", or rather, which object in the hierarchy takes incoming events. As there can be several "XGFocus" objects, but only one "focus", the XGAppCore class contains methods for determining which "XGFocus" is active: ie, who really has the focus.

Currently only Windows and the Application class itself are descendants of the XGFocus class. This permits multiple windows to each have it's own focus, but to quickly allow switching from window to window. (The XGAppSingleWindow and XGAppMultiWindow classes basically return as the current focus the "active" window's XGFocus object, or the XGAppCore's "default" XGFocus object if there are no windows currently active or visible.)

Note: The mechanism described above, sending the events from the contained object up the hierarchy until a handler is found, only applies to events that were sent through the dispatch mechanism Events that were broadcast or sent are not sent up the XGDispatch hierarchy.

Also note that the same class can receive incoming events both from a hierarchy and from a broadcast message; simply register the constructed XGDispatch object with the appropriate classes or global dispatch routines.

Using Dispatch Tables

Even though there are several different ways an object can receive events, there is only one "event receiver" in the YAAF toolkit: the XGDispatch object. (This is radically different from earlier versions of YAAF, and could be a potential problem in porting applications.)

The XGDispatch object accepts events by it's "DoDispatch" method. In order to determine which class object receives the event sent, and to reduce the amount of code needed to be written, associated with each XGDispatch dervied object is a "dispatch table", built using macros similar to that used by the Microsoft MFC framework.

Declaring these tables is relatively straightforward. When declaring the definition of a XGDispatch derived class that handles events, you would write:

class MyClass : public XGDispatch {
    ... other declarations ...

    // required macro: DECLAREDISPATCH sets up internal
    // virtual functions used to get the dispatch table
    DECLAREDISPATCH

    // Methods used to handle each message. Notice
    // these are not declared as 'virtual'.
    long DoKeyEvent(long,void *);
    long DoMenuEvent(long,void *);
};

To declare the actual contents of the dispatch table (which is basically an array of events and pointers to methods to handle each event), you would write:

// Declare dispatch table. I handle two events.
BEGINDISPATCH(MyClass,XGDispatch)
    DISPATCHTABLE(KXGKeyEvent,MyClass::DoKeyEvent)
    DISPATCHTABLE(KXGDoMenuCommand,MyClass::DoMenuEvent)
ENDDISPATCH

// MyClass::DoKeyEvent
long MyClass::DoKeyEvent(long eventArg, void *eventPtr)
{
    ....
}

// MyClass::DoMenuEvent
long MyClass::DoMenuEvent(long eventArg, void *eventPtr)
{
    ....
}

The macros BEGINDISPATCH, DISPATCHTABLE and ENDDISPATCH build a simple array which is then returned to the event dispatch code in XGDispatch::DoDispatch().

Handling Received Events

Each method, such as "DoKeyEvent" above, takes as it's arguments the event argument (an integer) and a pointer to additional information. Both fields are optional; in the case of keyboard events, the pointer is NULL. Usually you would simply handle the event there, but what if you had a situation where you didn't want to process all keyboard events?

The return value is used to signal that. If the event handler routine returns '-1', this signals the XGDispatch::DoDispatch() code to continue searching for an event handler. If the routine returns a value other than -1, the event is marked as 'handled', and the return value you provided is propagated to the code which originally sent the event.

Classes

XGDispatch

The class which receives all events. To use, override the methods in this class with your own routines, and/or build your own dispatch table as shown above.

XGFocus

The class which tracks who within this logical unit (either an application or a window) has the focus.

XGSend

Declares a 'sender', a class which contains zero or more 'receivers' (XGDispatch classes which were appropriate registered with the sender) that can send message to those receivers.

XGBroadcast

A global class which declares a global 'sender' for global messages, messages that are sent system wide to all interested receivers.

Macros

DECLAREDISPATCH

This macro make the necessary declarations inside of an XGDispatch class to present the dispatch table to the XGDispatch::DoDispatch() routine.

BEGINDISPATCH

Declares the start of the dispatch table. This should be done in a 'global' scope aside the other members of the class which actually handle the events.

BEGINDISPATCHPROC

This macro is similar to the BEGINDISPATCH macro, but also has an argument for a method which will preprocess all events coming to this XGDispatch object.

DISPATCHTABLE

Declares one entry in a dispatch table.

ENDDISPATCH

The macro which ends the dispatch table declaration

Messages

Messages are grouped by the following logical divisions:

Periodic Events

These events are sent on a regular basis during different phases of the event loop.

Keyboard Events

Events generated by keyboard presses.

Application Events

Events that are generated during different phases of the event loop which allow an application to override how events are processed.

Menu Events

These events are sent in response to menu commands

Document Events

Events that are sent in response to state changes in a dialog object

Window Events

Events sent in response to state changes in windows.

Control Messages

Events that are sent in response to things done to controls.

Note: Some "events" (such as mouse events) are not sent as events through the event handling hierarchy, but are handled separately as part of the XGView hierarchy.