/* * File: gevents.h * --------------- * This file defines the event types used in the StanfordCPPLib * graphics libraries. The structure of this package is adapted from * the Java event model. * */ #ifndef _gevents_h #define _gevents_h #include #include "gtimer.h" #include "gwindow.h" /* * Type: EventClassType * -------------------- * This enumeration type defines the event classes. The element values * are each a single bit and can be added or ORed together to generate * an event mask. The CLICK_EVENT class responds only to * the MOUSE_CLICKED event type. The ANY_EVENT class * selects any event. */ enum EventClassType { NULL_EVENT = 0x000, ACTION_EVENT = 0x010, KEY_EVENT = 0x020, TIMER_EVENT = 0x040, WINDOW_EVENT = 0x080, MOUSE_EVENT = 0x100, CLICK_EVENT = 0x200, ANY_EVENT = 0x3F0 }; /* * Type: EventType * --------------- * This enumeration type defines the event types for all events. */ typedef enum { WINDOW_CLOSED = WINDOW_EVENT + 1, WINDOW_RESIZED = WINDOW_EVENT + 2, ACTION_PERFORMED = ACTION_EVENT + 1, MOUSE_CLICKED = MOUSE_EVENT + 1, MOUSE_PRESSED = MOUSE_EVENT + 2, MOUSE_RELEASED = MOUSE_EVENT + 3, MOUSE_MOVED = MOUSE_EVENT + 4, MOUSE_DRAGGED = MOUSE_EVENT + 5, KEY_PRESSED = KEY_EVENT + 1, KEY_RELEASED = KEY_EVENT + 2, KEY_TYPED = KEY_EVENT + 3, TIMER_TICKED = TIMER_EVENT + 1, } EventType; /* * Type: ModifierCodes * ------------------- * This enumeration type defines a set of constants used to check whether * modifiers are in effect. */ enum ModifierCodes { SHIFT_DOWN = 1 << 0, CTRL_DOWN = 1 << 1, META_DOWN = 1 << 2, ALT_DOWN = 1 << 3, ALT_GRAPH_DOWN = 1 << 4, BUTTON1_DOWN = 1 << 5, BUTTON2_DOWN = 1 << 6, BUTTON3_DOWN = 1 << 7 }; /* * Type: KeyCodes * -------------- * This type defines the names of the key codes returned in a key event. */ enum KeyCodes { BACKSPACE_KEY = 8, TAB_KEY = 9, ENTER_KEY = 10, CLEAR_KEY = 12, ESCAPE_KEY = 27, PAGE_UP_KEY = 33, PAGE_DOWN_KEY = 34, END_KEY = 35, HOME_KEY = 36, LEFT_ARROW_KEY = 37, UP_ARROW_KEY = 38, RIGHT_ARROW_KEY = 39, DOWN_ARROW_KEY = 40, F1_KEY = 112, F2_KEY = 113, F3_KEY = 114, F4_KEY = 115, F5_KEY = 116, F6_KEY = 117, F7_KEY = 118, F8_KEY = 119, F9_KEY = 120, F10_KEY = 121, F11_KEY = 122, F12_KEY = 123, DELETE_KEY = 127, HELP_KEY = 156 }; /* Forward definitions */ class GWindowEvent; class GActionEvent; class GMouseEvent; class GKeyEvent; class GTimerEvent; class GObject; /* * Class: GEvent * ------------- * This class is the root of the hierarchy for all events. * * *

The standard paradigm for using GEvent is illustrated * by the following program, which allows the user to draw lines on the * graphics window: * *

 *    int main() {
 *       GWindow gw;
 *       GLine *line;
 *       cout << "This program lets the user draw lines by dragging." << endl;
 *       while (true) {
 *          GMouseEvent e = waitForEvent(MOUSE_EVENT);
 *          if (e.getEventType() == MOUSE_PRESSED) {
 *             line = new GLine(e.getX(), e.getY(), e.getX(), e.getY());
 *             gw.add(line);
 *          } else if (e.getEventType() == MOUSE_DRAGGED) {
 *             line->setEndPoint(e.getX(), e.getY());
 *          }
 *       }
 *    }
 *
*/ class GEvent { public: /* * Friend constructor: GEvent * Usage: GEvent event; * -------------------- * Ensures that an event is properly initialized to a NULL event. */ GEvent(); /* * Method: getEventClass * Usage: EventClassType eventClass = e.getEventClass(); * ----------------------------------------------------- * Returns the enumerated type constant indicating the class of the * event. */ EventClassType getEventClass() const; /* * Method: getEventType * Usage: EventType type = e.getEventType(); * ----------------------------------------- * Returns the enumerated type constant corresponding to the specific * event type. */ EventType getEventType() const; /* * Method: getEventTime * Usage: double time = e.getEventTime(); * -------------------------------------- * Returns the system time in milliseconds at which the event occurred. * To ensure portability among systems that represent time in different * ways, the StanfordCPPLib packages use type double to * represent time, which is always encoded as the number of milliseconds * that have elapsed since 00:00:00 UTC on January 1, 1970, which is * the conventional zero point for computer-based time systems. */ double getEventTime() const; /* * Method: getModifiers * Usage: int modifiers = e.getModifiers(); * ---------------------------------------- * Returns an integer whose bits indicate what modifiers are in effect. * To check whether the shift key is down, for example, one could use * the following code: * *
 *    if (e.getModifiers() & SHIFT_DOWN) ...
 *
*/ int getModifiers() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ virtual std::string toString() const; /* * Method: isValid * Usage: if (e.isValid()) ... * --------------------------- * Returns true if the event is valid. */ bool isValid(); /* Private section */ /**********************************************************************/ /* Note: Everything below this point in the file is logically part */ /* of the implementation and should not be of interest to clients. */ /**********************************************************************/ /* * Method: setEventTime * Usage: e.setEventTime(time); * ---------------------------- * Sets the event time field for this event. The event system needs * access to this method, but conventional clients don't. */ void setEventTime(double time); /* * Method: setModifiers * Usage: e.setModifiers(modifiers); * --------------------------------- * Sets the modifiers field for this event. The event system needs * access to this method, but conventional clients don't. */ void setModifiers(int modifiers); private: /* * Instance variables * ------------------ * Implementation note: All the variables from the subclasses are included * in the outer class to make it possible to convert between general events * and the various subclasses. By keeping all event classes the same size, * this design avoids any issues of slicing off parts of the data during * such conversions. */ /* General events */ EventClassType eventClass; int eventType; int modifiers; double eventTime; bool valid; std::string sourceKey; /* Window, mouse, and key events */ GWindowData *gwd; /* Action events */ GObject *source; std::string actionCommand; /* Mouse events */ double x; double y; /* Key events */ int keyChar; int keyCode; /* Timer events */ GTimerData *gtd; /* Friend specifications */ friend class GWindowEvent; friend class GActionEvent; friend class GMouseEvent; friend class GKeyEvent; friend class GTimerEvent; }; /* * Function: waitForClick * Usage: waitForClick(); * ---------------------- * Waits for a mouse click in any window, discarding any other events. */ void waitForClick(); /* * Function: waitForEvent * Usage: GEvent e = waitForEvent(mask); * ------------------------------------- * Dismisses the process until an event occurs whose type is covered by * the event mask. The mask parameter is a combination of the events of * interest. For example, to wait for a mouse event or an action event, * clients can use the following call: * *
 *    e = waitForEvent(MOUSE_EVENT + ACTION_EVENT);
 *
* * The mask parameter is optional. If it is missing, * waitForEvent accepts any event. * *

As a more sophisticated example, the following code is the canonical * event loop for an animated application that needs to respond to mouse, * key, and timer events: * *

 *    GTimer timer(ANIMATION_DELAY_IN_MILLISECONDS);
 *    timer.start();
 *    while (true) {
 *       GEvent e = waitForEvent(TIMER_EVENT + MOUSE_EVENT + KEY_EVENT);
 *       switch (e.getEventClass()) {
 *        case TIMER_EVENT:
 *          takeAnimationStep();
 *          break;
 *        case MOUSE_EVENT:
 *          handleMouseEvent(GMouseEvent(e));
 *          break;
 *        case KEY_EVENT:
 *          handleKeyEvent(GKeyEvent(e));
 *          break;
 *       }
 *    }
 *
*/ GEvent waitForEvent(int mask = ANY_EVENT); /* * Function: getNextEvent * Usage: GEvent e = getNextEvent(mask); * ------------------------------------- * Checks to see if there are any events of the desired type waiting on the * event queue. If so, this function returns the event in exactly the same * fashion as waitForEvent; if not, getNextEvent * returns an invalid event. The mask parameter is optional. * If it is missing, getNextEvent accepts any event. */ GEvent getNextEvent(int mask = ANY_EVENT); /* * Class: GWindowEvent * ------------------- * This event subclass represents a window event. * Each GWindowEvent keeps track of the event type * (WINDOW_CLOSED, WINDOW_RESIZED) along * with the identity of the window. */ class GWindowEvent : public GEvent { public: /* * Constructor: GWindowEvent * Usage: GWindowEvent windowEvent(type, gw); * ------------------------------------------ * Creates a GWindowEvent using the specified parameters. */ GWindowEvent(EventType type, const GWindow & gw); /* * Method: getGWindow * Usage: GWindow gw = e.getGWindow(); * ----------------------------------- * Returns the graphics window in which this event occurred. */ GWindow getGWindow() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ std::string toString() const; /* Private section */ GWindowEvent(); GWindowEvent(GEvent e); }; /* * Class: GActionEvent * ------------------- * This event subclass represents an action event. * Action events are generated by the classes in the * GInteractor * hierarchy. As an example, the following program displays * a button that, when pushed, generates the message * “Please do not press this button again” * (with thanks to Douglas Adams’s Hitchhiker’s * Guide to the Galaxy): * *
 *    int main() {
 *       GWindow gw;
 *       GButton *button = new GButton("RED");
 *       gw.addToRegion(button, "SOUTH");
 *       while (true) {
 *          GEvent e = waitForEvent(ACTION_EVENT | CLICK_EVENT);
 *          if (e.getEventType() == MOUSE_CLICKED) break;
 *          cout << "Please do not press this button again." << endl;
 *       }
 *       return 0;
 *    }
 *
*/ class GActionEvent : public GEvent { public: /* * Constructor: GActionEvent * Usage: GActionEvent actionEvent(type, source, actionCommand); * ------------------------------------------------------------- * Creates a GActionEvent using the specified parameters. */ GActionEvent(EventType type, GObject *source, std::string actionCommand); /* * Method: getSource * Usage: GObject *gobj = e.getSource(); * ------------------------------------- * Returns a pointer to the GObject that generated this event. */ GObject *getSource() const; /* * Method: getActionCommand * Usage: string cmd = e.getActionCommand(); * ----------------------------------------- * Returns the action command associated with this event. */ std::string getActionCommand() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ std::string toString() const; /* Private section */ GActionEvent(); GActionEvent(GEvent e); }; /* * Class: GMouseEvent * ------------------ * This event subclass represents a mouse event. Each mouse event * records the event type (MOUSE_PRESSED, * MOUSE_RELEASED, MOUSE_CLICKED, * MOUSE_MOVED, MOUSE_DRAGGED) along * with the coordinates of the event. Clicking the mouse generates * three events in the following order: MOUSE_PRESSED, * MOUSE_RELEASED, MOUSE_CLICKED. * *

As an example, the following program uses mouse events to let * the user draw rectangles on the graphics window. The only * complexity in this code is the use of the library functions * min and abs to ensure that the * dimensions of the rectangle are positive. * *

 *    int main() {
 *       GWindow gw;
 *       cout << "This program lets the user draw rectangles." << endl;
 *       GRect *rect;
 *       double startX;
 *       double startY;
 *       while (true) {
 *          GMouseEvent e = waitForEvent();
 *          if (e.getEventType() == MOUSE_PRESSED) {
 *             startX = e.getX();
 *             startY = e.getY();
 *             rect = new GRect(startX, startY, 0, 0);
 *             rect->setFilled(true);
 *             gw.add(rect);
 *          } else if (e.getEventType() == MOUSE_DRAGGED) {
 *             double x = min(e.getX(), startX);
 *             double y = min(e.getY(), startY);
 *             double width = abs(e.getX() - startX);
 *             double height = abs(e.getY() - startY);
 *             rect->setBounds(x, y, width, height);
 *          }
 *       }
 *    }
 *
*/ class GMouseEvent : public GEvent { public: /* * Constructor: GMouseEvent * Usage: GMouseEvent mouseEvent(type, gw, x, y); * ---------------------------------------------- * Creates a GMouseEvent using the specified parameters. */ GMouseEvent(EventType type, const GWindow & gw, double x, double y); /* * Method: getGWindow * Usage: GWindow gw = e.getGWindow(); * ----------------------------------- * Returns the graphics window in which this event occurred. */ GWindow getGWindow() const; /* * Method: getX * Usage: double x = getX(); * ------------------------- * Returns the x coordinate at which the event occurred relative * to the window origin at the upper left corner of the window. */ double getX() const; /* * Method: getY * Usage: double y = getY(); * ------------------------- * Returns the y coordinate at which the event occurred relative * to the window origin at the upper left corner of the window. */ double getY() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ std::string toString() const; /* Private section */ GMouseEvent(); GMouseEvent(GEvent e); }; /* * Class: GKeyEvent * ---------------- * This event subclass represents a key event. Each key event records * the event type along with two representations of the key. The * getKeyChar function is more generally useful and * returns the character after taking into account modifier keys. * The getKeyCode function returns an integer identifying * the key, which can be a function key as well as a standard key. * The codes return by getKeyCode are listed in the * KeyCodes enumeration. */ class GKeyEvent : public GEvent { public: /* * Constructor: GKeyEvent * Usage: GKeyEvent keyEvent(type, gw, keyChar, keyCode); * ------------------------------------------------------ * Creates a GKeyEvent using the specified parameters. */ GKeyEvent(EventType type, const GWindow & gw, int keyChar, int keyCode); /* * Method: getGWindow * Usage: GWindow gw = e.getGWindow(); * ----------------------------------- * Returns the graphics window in which this event occurred. */ GWindow getGWindow() const; /* * Method: getKeyChar * Usage: char ch = e.getKeyChar(); * -------------------------------- * Returns the character represented by the keystroke, taking the modifier * keys into account. For example, if the user types the 'a' * key with the shift key down, getKeyChar will return * 'A'. If the key code in the event does not correspond * to a character, getKeyChar returns the null character. */ char getKeyChar() const; /* * Method: getKeyCode * Usage: int key = getKeyCode(); * ------------------------------ * Returns the integer code associated with the key in the event. */ int getKeyCode() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ std::string toString() const; /* Private section */ GKeyEvent(); GKeyEvent(GEvent e); }; /* * Class: GTimerEvent * ------------------ * This event subclass represents a timer event. Timer events are * generated by a GTimer * object, which produces a new event at a fixed interval measured in * milliseconds. As an example, the following program generates a * timer event every two seconds, stopping when the user clicks * somewhere in the window: * *
 *    int main() {
 *       cout << "This program generates timer events." << endl;
 *       GTimer timer(2000);
 *       timer.start();
 *       while (true) {
 *          GEvent e = waitForEvent(CLICK_EVENT | TIMER_EVENT);
 *          if (e.getEventType() == MOUSE_CLICKED) break;
 *          cout << "Timer ticked" << endl;
 *       }
 *       return 0;
 *    }
 *
*/ class GTimerEvent : public GEvent { public: /* * Constructor: GTimerEvent * Usage: GTimerEvent timerEvent(type, timer); * ------------------------------------------- * Creates a GTimerEvent for the specified timer. */ GTimerEvent(EventType type, const GTimer & timer); /* * Method: getGTimer * Usage: GTimer timer = e.getGTimer(); * ------------------------------------ * Returns the timer that generated this event. */ GTimer getGTimer() const; /* * Method: toString * Usage: string str = e.toString(); * --------------------------------- * Converts the event to a human-readable representation of the event. */ std::string toString() const; /* Private section */ GTimerEvent(); GTimerEvent(GEvent e); }; #endif