This section describes creating a new event and offers background information on translating an event. For information on creating a new device, see Section 11.3, “Creating a Device”. The device's main responsibility is translating events, which is described in more detail in Section 11.2, “Dispatching Events”.
The file SoSubEvent.h contains the macros for defining new event classes. The SO_EVENT_HEADER() macro declares type identifier and naming variables and methods that all event classes must support. The SO_EVENT_SOURCE() macro defines the static variables and methods declared in the SO_EVENT_HEADER() macro.
Creating a new event requires these steps:
Select a name for the new event class and determine what class it is derived from.
Define an initClass() method to initialize the type information. Use the SO_EVENT_INIT_CLASS() macro. The application needs to call the event's initClass() method immediately after SoXt::init.
Define an exitClass() method to clean up the type information. In the exitClass() routine of your class, use the macro SO_EVENT_EXIT_CLASS.
Define a constructor.
Define a destructor.
Implement set() and get() methods for the additional information your event provides. For example, the dial event needs to include information on which dial was turned, and what its value is.
Write convenience routines for the event to perform common queries and tasks (optional step). For the dial box, the convenience routine is isDialEvent() .
Write convenience macros for the event (optional step). These are static functions that are used in event callback functions. For the dial, the macro is DIAL_EVENT(). Note that it uses the convenience routines defined in step 6.
When an event is dispatched, the event translator creates an Inventor event from an X event and sets its values (see Section 11.2, “Dispatching Events”). It provides all the information about the event, including the following:
Time the event occurred
Position of the locator when the event occurred
State of the modifier keys (Shift, Control, Alt) when the event occurred
Any additional information required by the event (for example, if a keyboard key is pressed, which key is it?)
Inventor includes three subclasses of SoEvent. SoButtonEvent includes additional information about the button state (is it up or down?). Subclasses of SoButtonEvent( C++ | Java | .NET ) provide information about which button was pressed. SoMotion3Event( C++ | Java | .NET ) includes information on translation and rotation values generated by an input device such as the spaceball. SoLocation2Event( C++ | Java | .NET ) includes information on the absolute location of the cursor in window coordinates.
A value such as the event's time or position is read-only during event traversal because the event is passed as a const pointer. Only the creator of an event can set its values.
The dial and button input device generates two X events that need to be translated into Inventor events and handled by the database:
provides value changes of the eight dials | |
provides information about the state of the device's 32 buttons |
The information provided by XDeviceMotionEvent is translated into a DialEvent . The XDeviceButtonEvent is translated into a ButtonBoxEvent , which is subclassed from SoButtonEvent( C++ | Java | .NET ) and has button information specific to the button box.
This section discusses the code for the DialEvent, which describes the state of any of the eight dials. Note, however, that you could instead choose to create a more generic event that could be used for other devices in addition to the dial box. For example, you could create a ResetToHomePositionEvent that would be used when the user presses a button box button, clicks on a Home button on the screen, or performs some other designated action.
Be sure to call initClass() on the event after initializing Inventor. |
Example 11.1, “ DialEvent.h ” shows the code for the dial event include file.
Example 11.1. DialEvent.h
#include <Inventor/SbBasic.h> #include <Inventor/events/SoEvent.h> #include <Inventor/events/SoSubEvent.h> // Convenience macro for determining if an event matches #define DIAL_EVENT(EVENT, WHICH) \ (DialEvent::isDialEvent(EVENT, WHICH)) class DialEvent : public SoEvent { SO_EVENT_HEADER(); public: // Constructor DialEvent(); // Which dial generated the event, 1-8 void setDial(int d) { dial = d; } int getDial() const { return dial; } // Value of the dial turned void setValue(int v) { value = v; } int getValue() const { return value; } // Convenience routines to see if an SoEvent is a turn of // the passed dial. Passing -1 matches any button. static SbBool isDialEvent(const SoEvent *e, int which = -1); static void initClass(); private: int dial; // Which dial int value; // Value of dial };
Example 11.2, “ DialEvent.c++ ” shows the complete source code for the dial event.
Example 11.2. DialEvent.c++
#include "DialEvent.h" SO_EVENT_SOURCE(DialEvent); // Class initialization void DialEvent::initClass() { SO_EVENT_INIT_CLASS(DialEvent, SoEvent); } // Constructor DialEvent::DialEvent() { dial = 0; value = 0; } // Convenience routine - this returns TRUE if the event is a // dial turn event matching the passed dial. SbBool DialEvent::isDialEvent(const SoEvent *e, int whichDial) { SbBool isMatch = FALSE; // is it a dial event? if (e->isOfType(DialEvent::getClassTypeId())) { const DialEvent *de = (const DialEvent *) e; // did the caller want any dial turn? or do they match? if ((whichDial == -1) || (de->getDial() == whichDial)) isMatch = TRUE; } return isMatch; }