From edb9a28eb17394ca510d8fa0f3d26edcfefb59e0 Mon Sep 17 00:00:00 2001 From: stephena Date: Mon, 19 Jul 2010 16:36:37 +0000 Subject: [PATCH] Added MessageBox class, used to present a small message to the user from the UI. Modified the ROM launcher to display a messagebox when running Stella for the first time (which currently means when the 'romdir' hasn't been set). This messagebox suggests setting the default ROM directory, and if the user clicks on 'OK', only then is the ROM directory file browser displayed. Reworked some of the event handling methods in EventHandler to save function calls on every event. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2071 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- src/emucore/EventHandler.cxx | 242 ++++++++++++++++------------------- src/emucore/EventHandler.hxx | 32 ----- src/gui/InputTextDialog.hxx | 4 +- src/gui/LauncherDialog.cxx | 29 ++++- src/gui/LauncherDialog.hxx | 10 +- src/gui/MessageBox.cxx | 83 ++++++++++++ src/gui/MessageBox.hxx | 52 ++++++++ src/gui/module.mk | 1 + 8 files changed, 279 insertions(+), 174 deletions(-) create mode 100644 src/gui/MessageBox.cxx create mode 100644 src/gui/MessageBox.hxx diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 19f9dd6ed..df1246034 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -618,14 +618,61 @@ void EventHandler::poll(uInt64 time) } case SDL_MOUSEMOTION: - handleMouseMotionEvent(event); + // Determine which mode we're in, then send the event to the appropriate place + if(myState == S_EMULATE) + { + if(myMouseEnabled) + { + int x = event.motion.xrel, y = event.motion.yrel; + myEvent->set(Event::MouseAxisXValue, x); + myEvent->set(Event::MouseAxisYValue, y); + } + } + else if(myOverlay) + { + int x = event.motion.x, y = event.motion.y; + myOverlay->handleMouseMotionEvent(x, y, 0); + } break; // SDL_MOUSEMOTION case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: { uInt8 state = event.button.type == SDL_MOUSEBUTTONDOWN ? 1 : 0; - handleMouseButtonEvent(event, state); + + // Determine which mode we're in, then send the event to the appropriate place + if(myState == S_EMULATE) + { + if(myMouseEnabled) + myEvent->set(Event::MouseButtonValue, state); + } + else if(myOverlay) + { + // Take window zooming into account + Int32 x = event.button.x, y = event.button.y; + + switch(event.button.button) + { + case SDL_BUTTON_LEFT: + myOverlay->handleMouseButtonEvent( + state ? EVENT_LBUTTONDOWN : EVENT_LBUTTONUP, x, y, state); + break; + case SDL_BUTTON_RIGHT: + myOverlay->handleMouseButtonEvent( + state ? EVENT_RBUTTONDOWN : EVENT_RBUTTONUP, x, y, state); + break; + case SDL_BUTTON_WHEELDOWN: + if(state) + myOverlay->handleMouseButtonEvent(EVENT_WHEELDOWN, x, y, 1); + break; + case SDL_BUTTON_WHEELUP: + if(state) + myOverlay->handleMouseButtonEvent(EVENT_WHEELUP, x, y, 1); + break; + default: + break; + } + } break; // SDL_MOUSEBUTTONUP, SDL_MOUSEBUTTONDOWN } @@ -663,8 +710,17 @@ void EventHandler::poll(uInt64 time) // Filter out buttons handled by OSystem if(!myOSystem->joyButtonHandled(button)) - handleJoyEvent(stick, button, state); + { + // Handle buttons which switch eventhandler state + if(state && eventStateChange(myJoyTable[stick][button][kEmulationMode])) + return; + // Determine which mode we're in, then send the event to the appropriate place + if(myState == S_EMULATE) + handleEvent(myJoyTable[stick][button][kEmulationMode], state); + else if(myOverlay != NULL) + myOverlay->handleJoyEvent(stick, button, state); + } break; // Regular button case JT_STELLADAPTOR_LEFT: @@ -696,7 +752,46 @@ void EventHandler::poll(uInt64 time) { case JT_REGULAR: if(myState == S_EMULATE) - handleJoyAxisEvent(stick, axis, value); + { + // Every axis event has two associated values, negative and positive + Event::Type eventAxisNeg = myJoyAxisTable[stick][axis][0][kEmulationMode]; + Event::Type eventAxisPos = myJoyAxisTable[stick][axis][1][kEmulationMode]; + + // Check for analog events, which are handled differently + // We'll pass them off as Stelladaptor events, and let the controllers + // handle it + switch((int)eventAxisNeg) + { + case Event::PaddleZeroAnalog: + myEvent->set(Event::SALeftAxis0Value, value); + break; + case Event::PaddleOneAnalog: + myEvent->set(Event::SALeftAxis1Value, value); + break; + case Event::PaddleTwoAnalog: + myEvent->set(Event::SARightAxis0Value, value); + break; + case Event::PaddleThreeAnalog: + myEvent->set(Event::SARightAxis1Value, value); + break; + default: + { + // Otherwise, we know the event is digital + if(value > Joystick::deadzone()) + handleEvent(eventAxisPos, 1); + else if(value < -Joystick::deadzone()) + handleEvent(eventAxisNeg, 1); + else + { + // Turn off both events, since we don't know exactly which one + // was previously activated. + handleEvent(eventAxisNeg, 0); + handleEvent(eventAxisPos, 0); + } + break; + } + } + } else if(myOverlay != NULL) { // First, clamp the values to simulate digital input @@ -745,7 +840,7 @@ void EventHandler::poll(uInt64 time) break; // Preprocess all hat events, converting to Stella JoyHat type - // Generate two equivalent hat events representing combined direction + // Generate multiple equivalent hat events representing combined direction // when we get a diagonal hat event if(value == SDL_HAT_CENTERED) handleJoyHatEvent(stick, hat, EVENT_HATCENTER); @@ -808,123 +903,6 @@ void EventHandler::poll(uInt64 time) myEvent->set(Event::MouseAxisYValue, 0); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleMouseMotionEvent(SDL_Event& event) -{ - // Determine which mode we're in, then send the event to the appropriate place - if(myState == S_EMULATE) - { - if(myMouseEnabled) - { - int x = event.motion.xrel, y = event.motion.yrel; - myEvent->set(Event::MouseAxisXValue, x); - myEvent->set(Event::MouseAxisYValue, y); - } - } - else if(myOverlay) - { - int x = event.motion.x, y = event.motion.y; - myOverlay->handleMouseMotionEvent(x, y, 0); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleMouseButtonEvent(SDL_Event& event, int state) -{ - // Determine which mode we're in, then send the event to the appropriate place - if(myState == S_EMULATE) - { - if(myMouseEnabled) - myEvent->set(Event::MouseButtonValue, state); - } - else if(myOverlay) - { - // Take window zooming into account - Int32 x = event.button.x, y = event.button.y; - MouseButton button; - - switch(event.button.button) - { - case SDL_BUTTON_LEFT: - button = state ? EVENT_LBUTTONDOWN : EVENT_LBUTTONUP; - break; - case SDL_BUTTON_RIGHT: - button = state ? EVENT_RBUTTONDOWN : EVENT_RBUTTONUP; - break; - case SDL_BUTTON_WHEELDOWN: - if(state) button = EVENT_WHEELDOWN; - else return; - break; - case SDL_BUTTON_WHEELUP: - if(state) button = EVENT_WHEELUP; - else return; - break; - default: - return; - } - myOverlay->handleMouseButtonEvent(button, x, y, state); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleJoyEvent(int stick, int button, int state) -{ -#ifdef JOYSTICK_SUPPORT - if(state && eventStateChange(myJoyTable[stick][button][kEmulationMode])) - return; - - // Determine which mode we're in, then send the event to the appropriate place - if(myState == S_EMULATE) - handleEvent(myJoyTable[stick][button][kEmulationMode], state); - else if(myOverlay != NULL) - myOverlay->handleJoyEvent(stick, button, state); -#endif -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleJoyAxisEvent(int stick, int axis, int value) -{ -#ifdef JOYSTICK_SUPPORT - // Every axis event has two associated values, negative and positive - Event::Type eventAxisNeg = myJoyAxisTable[stick][axis][0][kEmulationMode]; - Event::Type eventAxisPos = myJoyAxisTable[stick][axis][1][kEmulationMode]; - - // Check for analog events, which are handled differently - // We'll pass them off as Stelladaptor events, and let the controllers - // handle it - switch((int)eventAxisNeg) - { - case Event::PaddleZeroAnalog: - myEvent->set(Event::SALeftAxis0Value, value); - break; - case Event::PaddleOneAnalog: - myEvent->set(Event::SALeftAxis1Value, value); - break; - case Event::PaddleTwoAnalog: - myEvent->set(Event::SARightAxis0Value, value); - break; - case Event::PaddleThreeAnalog: - myEvent->set(Event::SARightAxis1Value, value); - break; - default: - { - // Otherwise, we know the event is digital - int deadzone = Joystick::deadzone(); - if(value > -deadzone && value < deadzone) - { - // Turn off both events, since we don't know exactly which one - // was previously activated. - handleEvent(eventAxisNeg, 0); - handleEvent(eventAxisPos, 0); - } - else - handleEvent(value < 0 ? eventAxisNeg : eventAxisPos, 1); - break; - } - } -#endif -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::handleJoyHatEvent(int stick, int hat, JoyHat value) { @@ -1163,7 +1141,7 @@ void EventHandler::setActionMappings(EventMode mode) if(myJoyTable[stick][button][mode] == event) { buf.str(""); - buf << "J" << stick << " B" << button; + buf << "J" << stick << "/B" << button; if(key == "") key = key + buf.str(); else @@ -1181,16 +1159,16 @@ void EventHandler::setActionMappings(EventMode mode) if(myJoyAxisTable[stick][axis][dir][mode] == event) { buf.str(""); - buf << "J" << stick << " axis " << axis; + buf << "J" << stick << "/A" << axis; if(eventIsAnalog(event)) { dir = 2; // Immediately exit the inner loop after this iteration - buf << " abs"; + buf << "/+|-"; } else if(dir == 0) - buf << " neg"; + buf << "/-"; else - buf << " pos"; + buf << "/+"; if(key == "") key = key + buf.str(); @@ -1210,13 +1188,13 @@ void EventHandler::setActionMappings(EventMode mode) if(myJoyHatTable[stick][hat][dir][mode] == event) { buf.str(""); - buf << "J" << stick << " hat " << hat; + buf << "J" << stick << "/H" << hat; switch(dir) { - case EVENT_HATUP: buf << " up"; break; - case EVENT_HATDOWN: buf << " down"; break; - case EVENT_HATLEFT: buf << " left"; break; - case EVENT_HATRIGHT: buf << " right"; break; + case EVENT_HATUP: buf << "/up"; break; + case EVENT_HATDOWN: buf << "/down"; break; + case EVENT_HATLEFT: buf << "/left"; break; + case EVENT_HATRIGHT: buf << "/right"; break; } if(key == "") key = key + buf.str(); diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index 2d7e2c33c..c6d8d0c51 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -325,38 +325,6 @@ class EventHandler void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; } private: - /** - Send a mouse motion event to the handler. - - @param event The mouse motion event generated by SDL - */ - void handleMouseMotionEvent(SDL_Event& event); - - /** - Send a mouse button event to the handler. - - @param event The mouse button event generated by SDL - */ - void handleMouseButtonEvent(SDL_Event& event, int state); - - /** - Send a joystick button event to the handler - - @param stick The joystick number - @param button The joystick button - @param state The state of the button (pressed or released) - */ - void handleJoyEvent(int stick, int button, int state); - - /** - Send a joystick axis event to the handler - - @param stick The joystick number - @param axis The joystick axis - @param value The value on the given axis - */ - void handleJoyAxisEvent(int stick, int axis, int value); - /** Send a joystick hat event to the handler diff --git a/src/gui/InputTextDialog.hxx b/src/gui/InputTextDialog.hxx index 01f19db76..9b588f3e0 100644 --- a/src/gui/InputTextDialog.hxx +++ b/src/gui/InputTextDialog.hxx @@ -30,8 +30,6 @@ class EditTextWidget; #include "Dialog.hxx" #include "Command.hxx" -typedef Common::Array InputWidget; - class InputTextDialog : public Dialog, public CommandSender { public: @@ -60,6 +58,8 @@ class InputTextDialog : public Dialog, public CommandSender virtual void handleCommand(CommandSender* sender, int cmd, int data, int id); private: + typedef Common::Array InputWidget; + InputWidget myInput; StaticTextWidget* myTitle; diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 15a65a9e9..1beccfbe8 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -35,6 +35,7 @@ #include "OptionsDialog.hxx" #include "GlobalPropsDialog.hxx" #include "LauncherFilterDialog.hxx" +#include "MessageBox.hxx" #include "OSystem.hxx" #include "Props.hxx" #include "PropsSet.hxx" @@ -61,6 +62,7 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent, myMenu(NULL), myGlobalProps(NULL), myFilters(NULL), + myFirstRunMsg(NULL), myRomDir(NULL), mySelectedItem(0) { @@ -237,11 +239,19 @@ void LauncherDialog::loadConfig() // time running Stella; in this case, we should prompt the user if(romdir == "") { - if(!myRomDir) - myRomDir = new BrowserDialog(this, instance().font(), _w, _h); - - myRomDir->show("First startup -> Select ROM directory:", romdir, - FilesystemNode::kListDirectoriesOnly, kStartupRomDirChosenCmd); + if(!myFirstRunMsg) + { + StringList msg; + msg.push_back("This seems to be your first time running Stella."); + msg.push_back("Before you can start a game, you need to"); + msg.push_back("specify where your ROMs are located."); + msg.push_back(""); + msg.push_back("Click 'OK' to select a default ROM directory,"); + msg.push_back("or 'Cancel' to browse the filesystem manually."); + myFirstRunMsg = new MessageBox(this, instance().font(), msg, + kFirstRunMsgChosenCmd); + } + myFirstRunMsg->show(); } // Assume that if the list is empty, this is the first time that loadConfig() @@ -565,6 +575,15 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd, instance().eventHandler().quit(); break; + case kFirstRunMsgChosenCmd: + // Show a file browser, starting from the users' home directory + if(!myRomDir) + myRomDir = new BrowserDialog(this, instance().font(), _w, _h); + + myRomDir->show("Select ROM directory:", "~", + FilesystemNode::kListDirectoriesOnly, kStartupRomDirChosenCmd); + break; + case kStartupRomDirChosenCmd: { FilesystemNode dir(myRomDir->getResult()); diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index bdb35eb08..a20366abf 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -34,6 +34,7 @@ class BrowserDialog; class OptionsDialog; class GlobalPropsDialog; class LauncherFilterDialog; +class MessageBox; class OSystem; class Properties; class EditTextWidget; @@ -97,13 +98,15 @@ class LauncherDialog : public Dialog EditTextWidget* myPattern; GameList* myGameList; - OptionsDialog* myOptions; - RomInfoWidget* myRomInfoWidget; + OptionsDialog* myOptions; + RomInfoWidget* myRomInfoWidget; ContextMenu* myMenu; GlobalPropsDialog* myGlobalProps; LauncherFilterDialog* myFilters; - BrowserDialog* myRomDir; + + MessageBox* myFirstRunMsg; + BrowserDialog* myRomDir; int mySelectedItem; int myRomInfoSize; @@ -119,6 +122,7 @@ class LauncherDialog : public Dialog kOptionsCmd = 'OPTI', kQuitCmd = 'QUIT', + kFirstRunMsgChosenCmd = 'frmc', kStartupRomDirChosenCmd = 'rmsc' }; }; diff --git a/src/gui/MessageBox.cxx b/src/gui/MessageBox.cxx new file mode 100644 index 000000000..4fc7c91b3 --- /dev/null +++ b/src/gui/MessageBox.cxx @@ -0,0 +1,83 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id$ +//============================================================================ + +#include "Dialog.hxx" +#include "OSystem.hxx" +#include "Version.hxx" +#include "Widget.hxx" + +#include "MessageBox.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MessageBox::MessageBox(GuiObject* boss, const GUI::Font& font, + const StringList& text, int cmd) + : Dialog(&boss->instance(), &boss->parent(), 0, 0, 16, 16), + CommandSender(boss), + myCmd(cmd) +{ + const int lineHeight = font.getLineHeight(), + fontWidth = font.getMaxCharWidth(), + fontHeight = font.getFontHeight(); + int xpos, ypos; + WidgetArray wid; + + // Set real dimensions + _w = 50 * fontWidth + 8; + _h = (text.size() + 2) * lineHeight + 20; + + xpos = 10; ypos = 10; + for(uInt32 i = 0; i < text.size(); ++i) + { + new StaticTextWidget(this, font, xpos, ypos, _w - 20, + fontHeight, text[i], kTextAlignLeft); + ypos += fontHeight; + } + + addOKCancelBGroup(wid, font); + + addToFocusList(wid); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MessageBox::~MessageBox() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void MessageBox::handleCommand(CommandSender* sender, int cmd, int data, int id) +{ + switch(cmd) + { + case kOKCmd: + { + close(); + + // Send a signal to the calling class that 'OK' has been selected + // Since we aren't derived from a widget, we don't have a 'data' or 'id' + if(myCmd) + sendCommand(myCmd, 0, 0); + + break; + } + + default: + Dialog::handleCommand(sender, cmd, data, id); + break; + } +} diff --git a/src/gui/MessageBox.hxx b/src/gui/MessageBox.hxx new file mode 100644 index 000000000..2b13b2f35 --- /dev/null +++ b/src/gui/MessageBox.hxx @@ -0,0 +1,52 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id$ +//============================================================================ + +#ifndef MESSAGE_BOX_HXX +#define MESSAGE_BOX_HXX + +class GuiObject; +class StaticTextWidget; + +#include "Dialog.hxx" +#include "Command.hxx" +#include "DialogContainer.hxx" + +/** + * Show a simple message box containing the given text, with buttons + * prompting the user to accept or reject. If the user selects 'OK', + * the value of 'cmd' is returned. + */ +class MessageBox : public Dialog, public CommandSender +{ + public: + MessageBox(GuiObject* boss, const GUI::Font& font, const StringList& text, + int cmd = 0); + virtual ~MessageBox(); + + /** Place the input dialog onscreen and center it */ + void show() { parent().addDialog(this); } + + private: + virtual void handleCommand(CommandSender* sender, int cmd, int data, int id); + + private: + int myCmd; +}; + +#endif diff --git a/src/gui/module.mk b/src/gui/module.mk index bb3bb6f42..9f3461150 100644 --- a/src/gui/module.mk +++ b/src/gui/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ src/gui/LauncherFilterDialog.o \ src/gui/ListWidget.o \ src/gui/Menu.o \ + src/gui/MessageBox.o \ src/gui/OptionsDialog.o \ src/gui/PopUpWidget.o \ src/gui/ProgressDialog.o \