/* * 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. */ /*************************************************************************/ /* Stanford Portable Library */ /* Copyright (c) 2014 by Eric Roberts <eroberts@cs.stanford.edu> */ /* */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see <http://www.gnu.org/licenses/>. */ /*************************************************************************/ #ifndef _gevents_h #define _gevents_h #include <string> #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 * Adamss Hitchhikers 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