diff --git a/stella/Changes.txt b/stella/Changes.txt index e1a6b6640..06996207f 100644 --- a/stella/Changes.txt +++ b/stella/Changes.txt @@ -12,12 +12,15 @@ Release History =============================================================================== 1.4.2a to 1.4.2b: MacOSX version only (February 27, 2005) + * Added fix to mute sound while user is loading a new cartridge, or using the Preferences window. 1.4.2 to 1.4.2a: MacOSX version only (February 21, 2005) + * Fixed problem in timing loop which was causing crashes after 15-30 minutes. + * Turned optimization on in compiler settings, which had somehow gotten turned off. diff --git a/stella/README-SDL.txt b/stella/README-SDL.txt index 1669736cb..664cd4ed5 100644 --- a/stella/README-SDL.txt +++ b/stella/README-SDL.txt @@ -1,13 +1,11 @@ - -Please distribute this file with the SDL runtime environment: - -The Simple DirectMedia Layer (SDL for short) is a cross-platfrom library -designed to make it easy to write multi-media software, such as games and -emulators. - -The Simple DirectMedia Layer library source code is available from: -http://www.libsdl.org/ - -This library is distributed under the terms of the GNU LGPL license: -http://www.gnu.org/copyleft/lesser.html - +Please distribute this file with the SDL runtime environment: + +The Simple DirectMedia Layer (SDL for short) is a cross-platfrom library +designed to make it easy to write multi-media software, such as games and +emulators. + +The Simple DirectMedia Layer library source code is available from: +http://www.libsdl.org/ + +This library is distributed under the terms of the GNU LGPL license: +http://www.gnu.org/copyleft/lesser.html diff --git a/stella/src/build/makefile b/stella/src/build/makefile index 41c2b44ef..01090a0fd 100644 --- a/stella/src/build/makefile +++ b/stella/src/build/makefile @@ -13,7 +13,7 @@ ## See the file "license" for information on usage and redistribution of ## this file, and for a DISCLAIMER OF ALL WARRANTIES. ## -## $Id: makefile,v 1.61 2005-02-22 02:59:52 stephena Exp $ +## $Id: makefile,v 1.62 2005-02-27 23:41:17 stephena Exp $ ##============================================================================ ##============================================================================ @@ -32,7 +32,7 @@ OPTIMIZATIONS = ### to include support for saving snapshots in png format ### (requires PNG library) - SNAPSHOT_SUPPORT = 1 +# SNAPSHOT_SUPPORT = 1 ### to include support for game developers ### enables some extra commandline options that allow the user @@ -41,7 +41,7 @@ OPTIMIZATIONS = ### to build on SMP (or distcc-based) machines ### change to number of CPU's you have - NUMBER_CPU = 1 + NUMBER_CPU = 3 ##============================================================================ ## All done, type make to get a list of frontends diff --git a/stella/src/common/mainSDL.cxx b/stella/src/common/mainSDL.cxx index 599fc68ad..408692ce5 100644 --- a/stella/src/common/mainSDL.cxx +++ b/stella/src/common/mainSDL.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: mainSDL.cxx,v 1.26 2005-02-25 02:29:37 stephena Exp $ +// $Id: mainSDL.cxx,v 1.27 2005-02-27 23:41:17 stephena Exp $ //============================================================================ #include @@ -876,7 +876,7 @@ int main(int argc, char* argv[]) theShowInfoFlag = theSettings->getBool("showinfo"); theGrabMouseIndicator = theSettings->getBool("grabmouse"); theHideCursorIndicator = theSettings->getBool("hidecursor"); - bool theRomLauncherFlag = true;//theSettings->getBool("romlauncher"); + bool theRomLauncherFlag = false;//true;//FIXMEtheSettings->getBool("romlauncher"); // Create a properties set for us to use and set it up PropertiesSet propertiesSet; @@ -971,9 +971,10 @@ int main(int argc, char* argv[]) break; else { - cerr << "Attempting to open " << romfile << endl; - theConsole = CreateConsole(romfile); - mainGameLoop(); + if((theConsole = CreateConsole(romfile)) != NULL) + mainGameLoop(); + else + break; } } } diff --git a/stella/src/emucore/EventHandler.cxx b/stella/src/emucore/EventHandler.cxx index 54d65710f..1fff86249 100644 --- a/stella/src/emucore/EventHandler.cxx +++ b/stella/src/emucore/EventHandler.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: EventHandler.cxx,v 1.34 2005-02-25 02:29:38 stephena Exp $ +// $Id: EventHandler.cxx,v 1.35 2005-02-27 23:41:18 stephena Exp $ //============================================================================ #include @@ -127,10 +127,6 @@ void EventHandler::handleKeyEvent(SDLKey key, SDLMod mod, uInt8 state) // Determine which mode we're in, then send the event to the appropriate place switch(myState) { - case S_NONE: - return; - break; - case S_EMULATE: // if(mod & KMOD_ALT && state) // handleEvent(myAltKeyTable[key], state); @@ -140,17 +136,21 @@ void EventHandler::handleKeyEvent(SDLKey key, SDLMod mod, uInt8 state) handleEvent(myKeyTable[key], state); break; - case S_BROWSER: -//FIXME myOSystem->gui().browser().handleKeyEvent(key, mod, state); - break; - case S_MENU: //FIXME myOSystem->gui().menu().handleKeyEvent(key, mod, state); break; + case S_BROWSER: +//FIXME myOSystem->gui().browser().handleKeyEvent(key, mod, state); + break; + case S_DEBUGGER: // Not yet implemented break; + + case S_NONE: + return; + break; } } diff --git a/stella/src/emucore/EventHandler.hxx b/stella/src/emucore/EventHandler.hxx index 2654f0ef1..614f43912 100644 --- a/stella/src/emucore/EventHandler.hxx +++ b/stella/src/emucore/EventHandler.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: EventHandler.hxx,v 1.17 2005-02-25 02:29:38 stephena Exp $ +// $Id: EventHandler.hxx,v 1.18 2005-02-27 23:41:19 stephena Exp $ //============================================================================ #ifndef EVENTHANDLER_HXX @@ -32,17 +32,16 @@ class OSystem; This class takes care of event remapping and dispatching for the Stella core, as well as keeping track of the current 'mode'. - The frontends will send translated events here, and the handler will - check to see what the current 'mode' is. For now, the modes can be - normal and menu mode. + The frontend will send translated events here, and the handler will + check to see what the current 'mode' is. - If in normal mode, events received from the frontends are remapped and + If in emulation mode, events received from the frontend are remapped and sent to the emulation core. If in menu mode, the events are sent - unchanged to the user interface, where (among other things) changing key + unchanged to the menu class, where (among other things) changing key mapping can take place. @author Stephen Anthony - @version $Id: EventHandler.hxx,v 1.17 2005-02-25 02:29:38 stephena Exp $ + @version $Id: EventHandler.hxx,v 1.18 2005-02-27 23:41:19 stephena Exp $ */ class EventHandler { diff --git a/stella/src/emucore/FrameBuffer.cxx b/stella/src/emucore/FrameBuffer.cxx index 741fb5b7a..af98d1c2e 100644 --- a/stella/src/emucore/FrameBuffer.cxx +++ b/stella/src/emucore/FrameBuffer.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBuffer.cxx,v 1.16 2005-02-22 18:40:59 stephena Exp $ +// $Id: FrameBuffer.cxx,v 1.17 2005-02-27 23:41:19 stephena Exp $ //============================================================================ #include @@ -192,82 +192,96 @@ void FrameBuffer::update() // Do any pre-frame stuff preFrameUpdate(); - // Determine which mode we are in (normal or menu mode) - // In normal mode, only the mediasource or messages are shown, - // and they are shown per-frame - // In menu mode, any of the menus are shown, but the mediasource - // is not updated, and all updates depend on whether the screen is dirty - if(!myMenuMode) + // Determine which mode we are in (from the EventHandler) + // Take care of S_EMULATE mode here, otherwise let the GUI + // figure out what to draw + switch(myOSystem->eventHandler().state()) { - // Draw changes to the mediasource - if(!myPauseStatus) - myOSystem->console().mediaSource().update(); - - // We always draw the screen, even if the core is paused - drawMediaSource(); - - if(!myPauseStatus) + case EventHandler::S_EMULATE: { - // Draw any pending messages - if(myMessageTime > 0) - { - uInt32 width = myMessageText.length()*FONTWIDTH + FONTWIDTH; - uInt32 height = LINEOFFSET + FONTHEIGHT; - uInt32 x = (myWidth >> 1) - (width >> 1); - uInt32 y = myHeight - height - LINEOFFSET/2; + // Draw changes to the mediasource + if(!myPauseStatus) + myOSystem->console().mediaSource().update(); - // Draw the bounded box and text - drawBoundedBox(x, y+1, width, height-2); - drawText(x + XBOXOFFSET/2, LINEOFFSET/2 + y, myMessageText); - myMessageTime--; - - // Erase this message on next update - if(myMessageTime == 0) - theRedrawEntireFrameIndicator = true; - } - } - } - else // we are in MENU_MODE - { - // Only update the screen if it's been invalidated - // or the menus have changed - if(theMenuChangedIndicator || theRedrawEntireFrameIndicator) - { + // We always draw the screen, even if the core is paused drawMediaSource(); - // Then overlay any menu items - switch(myCurrentWidget) + if(!myPauseStatus) { - case W_NONE: - break; + // Draw any pending messages + if(myMessageTime > 0) + { + uInt32 width = myMessageText.length()*FONTWIDTH + FONTWIDTH; + uInt32 height = LINEOFFSET + FONTHEIGHT; + uInt32 x = (myWidth >> 1) - (width >> 1); + uInt32 y = myHeight - height - LINEOFFSET/2; - case MAIN_MENU: - drawMainMenu(); - break; + // Draw the bounded box and text + drawBoundedBox(x, y+1, width, height-2); + drawText(x + XBOXOFFSET/2, LINEOFFSET/2 + y, myMessageText); + myMessageTime--; - case REMAP_MENU: - drawRemapMenu(); - break; - - case INFO_MENU: - drawInfoMenu(); - break; - - default: - break; + // Erase this message on next update + if(myMessageTime == 0) + theRedrawEntireFrameIndicator = true; + } } - - // Now the screen is up to date - theRedrawEntireFrameIndicator = false; - - // This is a performance hack to only draw the menus when necessary - // Software mode is single-buffered, so we don't have to worry - // However, OpenGL mode is double-buffered, so we need to draw the - // menus at least twice (so they'll be in both buffers) - // Otherwise, we get horrible flickering - myMenuRedraws--; - theMenuChangedIndicator = (myMenuRedraws != 0); + break; // S_EMULATE } + + case EventHandler::S_MENU: // FIXME - this whole thing will disappear into the gui().menu class + { + // Only update the screen if it's been invalidated + // or the menus have changed + if(theMenuChangedIndicator || theRedrawEntireFrameIndicator) + { + drawMediaSource(); + + // Then overlay any menu items + // FIXME myOSystem->gui().menu().draw(); + + switch(myCurrentWidget) + { + case W_NONE: + break; + case MAIN_MENU: + drawMainMenu(); + break; + case REMAP_MENU: + drawRemapMenu(); + break; + case INFO_MENU: + drawInfoMenu(); + break; + default: + break; + } + + // Now the screen is up to date + theRedrawEntireFrameIndicator = false; + + // This is a performance hack to only draw the menus when necessary + // Software mode is single-buffered, so we don't have to worry + // However, OpenGL mode is double-buffered, so we need to draw the + // menus at least twice (so they'll be in both buffers) + // Otherwise, we get horrible flickering + myMenuRedraws--; + theMenuChangedIndicator = (myMenuRedraws != 0); + } + break; + } + + case EventHandler::S_BROWSER: + // FIXME myOSystem->gui().browser().draw(); + break; + + case EventHandler::S_DEBUGGER: + // Not yet implemented + break; + + case EventHandler::S_NONE: + return; + break; } // Do any post-frame stuff diff --git a/stella/src/emucore/FrameBuffer.hxx b/stella/src/emucore/FrameBuffer.hxx index 8b4b393ca..7d4e58123 100644 --- a/stella/src/emucore/FrameBuffer.hxx +++ b/stella/src/emucore/FrameBuffer.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBuffer.hxx,v 1.16 2005-02-22 18:41:11 stephena Exp $ +// $Id: FrameBuffer.hxx,v 1.17 2005-02-27 23:41:19 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_HXX @@ -39,7 +39,7 @@ FIXME This class also implements a MAME-like user interface where Stella settin can be changed. @author Stephen Anthony - @version $Id: FrameBuffer.hxx,v 1.16 2005-02-22 18:41:11 stephena Exp $ + @version $Id: FrameBuffer.hxx,v 1.17 2005-02-27 23:41:19 stephena Exp $ */ class FrameBuffer { @@ -160,8 +160,8 @@ FIXME void refresh(bool now = false) { theRedrawEntireFrameIndicator = true; - if(now) - drawMediaSource(); + myMenuRedraws = 2; + if(now) drawMediaSource(); } /** diff --git a/stella/src/gui/Dialog.cxx b/stella/src/gui/Dialog.cxx new file mode 100644 index 000000000..b3b2f3d9c --- /dev/null +++ b/stella/src/gui/Dialog.cxx @@ -0,0 +1,277 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2002-2004 The ScummVM project + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header: /home/stephena/STELLA_CVS-to-SVN/stella/src/gui/Dialog.cxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ + */ + +#include + +#include "stdafx.h" +#include "newgui.h" +#include "dialog.h" +#include "widget.h" + +namespace GUI { + +/* + * TODO list + * - add some sense of the window being "active" (i.e. in front) or not. If it + * was inactive and just became active, reset certain vars (like who is focused). + * Maybe we should just add lostFocus and receivedFocus methods to Dialog, just + * like we have for class Widget? + * ... + */ + +Dialog::~Dialog() { + delete _firstWidget; + _firstWidget = 0; +} + +int Dialog::runModal() { + // Open up + open(); + + // Start processing events + g_gui.runLoop(); + + // Return the result code + return _result; +} + +void Dialog::open() { + Widget *w = _firstWidget; + + _result = 0; + _visible = true; + g_gui.openDialog(this); + + // Search for the first objects that wantsFocus() (if any) and give it the focus + while (w && !w->wantsFocus()) { + w = w->_next; + } + + if (w) { + w->receivedFocus(); + _focusedWidget = w; + } +} + +void Dialog::close() { + _visible = false; + g_gui.closeTopDialog(); + + if (_mouseWidget) { + _mouseWidget->handleMouseLeft(0); + _mouseWidget = 0; + } + releaseFocus(); +} + +void Dialog::releaseFocus() { + if (_focusedWidget) { + _focusedWidget->lostFocus(); + _focusedWidget = 0; + } +} + +void Dialog::draw() { + g_gui._needRedraw = true; +} + +void Dialog::drawDialog() { + + if (!isVisible()) + return; + + g_gui.blendRect(_x, _y, _w, _h, g_gui._bgcolor); + g_gui.box(_x, _y, _w, _h, g_gui._color, g_gui._shadowcolor); + + // Draw all children + Widget *w = _firstWidget; + while (w) { + w->draw(); + w = w->_next; + } + + // Flag the draw area as dirty + g_gui.addDirtyRect(_x, _y, _w, _h); +} + +void Dialog::handleMouseDown(int x, int y, int button, int clickCount) { + Widget *w; + w = findWidget(x, y); + + // If the click occured inside a widget which is not the currently + // focused one, change the focus to that widget. + // TODO: use the wantsFocus() method to objects, so that only fields + // that want it get the focus (like edit fields, list field...) + // However, right now we "abuse" the focus also for the click&drag + // behaviour of buttons. This should probably be changed by adding + // a nother field, e.g. _clickedWidget or _dragWidget. + if (w && w != _focusedWidget) { + // The focus will change. Tell the old focused widget (if any) + // that it lost the focus. + releaseFocus(); + + // Tell the new focused widget (if any) that it just gained the focus. + if (w) + w->receivedFocus(); + + _focusedWidget = w; + } + + if (w && w == _focusedWidget) + _focusedWidget->handleMouseDown(x - (_focusedWidget->getAbsX() - _x), y - (_focusedWidget->getAbsY() - _y), button, clickCount); +} + +void Dialog::handleMouseUp(int x, int y, int button, int clickCount) { + Widget *w; + + if (_focusedWidget) { + w = _focusedWidget; + + // Lose focus on mouseup unless the widget requested to retain the focus + if (! (_focusedWidget->getFlags() & WIDGET_RETAIN_FOCUS )) { + releaseFocus(); + } + + } else { + w = findWidget(x, y); + } + + if (w) + w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount); +} + +void Dialog::handleMouseWheel(int x, int y, int direction) { + Widget *w; + + // This may look a bit backwards, but I think it makes more sense for + // the mouse wheel to primarily affect the widget the mouse is at than + // the widget that happens to be focused. + + w = findWidget(x, y); + if (!w) + w = _focusedWidget; + if (w) + w->handleMouseWheel(x, y, direction); +} + +void Dialog::handleKeyDown(uint16 ascii, int keycode, int modifiers) { + if (_focusedWidget) { + if (_focusedWidget->handleKeyDown(ascii, keycode, modifiers)) + return; + } + + // Hotkey handling + if (ascii != 0) { + Widget *w = _firstWidget; + ascii = toupper(ascii); + while (w) { + if (w->_type == kButtonWidget && ascii == toupper(((ButtonWidget *)w)->_hotkey)) { + // The hotkey for widget w was pressed. We fake a mouse click into the + // button by invoking the appropriate methods. + w->handleMouseDown(0, 0, 1, 1); + w->handleMouseUp(0, 0, 1, 1); + return; + } + w = w->_next; + } + } + + // ESC closes all dialogs by default + if (keycode == 27) { + setResult(-1); + close(); + } + + // TODO: tab/shift-tab should focus the next/previous focusable widget +} + +void Dialog::handleKeyUp(uint16 ascii, int keycode, int modifiers) { + // Focused widget receives keyup events + if (_focusedWidget) + _focusedWidget->handleKeyUp(ascii, keycode, modifiers); +} + +void Dialog::handleMouseMoved(int x, int y, int button) { + Widget *w; + + if (_focusedWidget) { + w = _focusedWidget; + int wx = w->getAbsX() - _x; + int wy = w->getAbsY() - _y; + + // We still send mouseEntered/Left messages to the focused item + // (but to no other items). + bool mouseInFocusedWidget = (x >= wx && x < wx + w->_w && y >= wy && y < wy + w->_h); + if (mouseInFocusedWidget && _mouseWidget != w) { + _mouseWidget = w; + w->handleMouseEntered(button); + } else if (!mouseInFocusedWidget && _mouseWidget == w) { + _mouseWidget = 0; + w->handleMouseLeft(button); + } + + w->handleMouseMoved(x - wx, y - wy, button); + } + + w = findWidget(x, y); + + if (_mouseWidget != w) { + if (_mouseWidget) + _mouseWidget->handleMouseLeft(button); + if (w) + w->handleMouseEntered(button); + _mouseWidget = w; + } + + if (!w || !(w->getFlags() & WIDGET_TRACK_MOUSE)) { + return; + } + + w->handleMouseMoved(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button); +} + +void Dialog::handleTickle() { + // Focused widget receives tickle notifications + if (_focusedWidget && _focusedWidget->getFlags() & WIDGET_WANT_TICKLE) { + _focusedWidget->handleTickle(); + } +} + +void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kCloseCmd: + close(); + break; + } +} + +/* + * Determine the widget at location (x,y) if any. Assumes the coordinates are + * in the local coordinate system, i.e. relative to the top left of the dialog. + */ +Widget *Dialog::findWidget(int x, int y) { + return Widget::findWidgetInChain(_firstWidget, x, y); +} + +ButtonWidget *Dialog::addButton(int x, int y, const Common::String &label, uint32 cmd, char hotkey) { + return new ButtonWidget(this, x, y, kButtonWidth, 16, label, cmd, hotkey); +} + +} // End of namespace GUI diff --git a/stella/src/gui/Dialog.hxx b/stella/src/gui/Dialog.hxx new file mode 100644 index 000000000..a7278f966 --- /dev/null +++ b/stella/src/gui/Dialog.hxx @@ -0,0 +1,87 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Dialog.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#ifndef DIALOG_HXX +#define DIALOG_HXX + +#include "bspf.hxx" + +#include "common/scummsys.h" +#include "common/str.h" + +#include "gui/object.h" + +/** + This is the base class for all dialog boxes. + + @author Stephen Anthony + @version $Id: Dialog.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +*/ +class Dialog +{ + public: + Dialog(uInt16 x, uInt16 y, uInt16 w, uInt16 h) + : GuiObject(x, y, w, h), + _mouseWidget(0), _focusedWidget(0), _visible(false) { + } + virtual ~Dialog(); + + virtual int runModal(); + + bool isVisible() const { return _visible; } + + void releaseFocus(); + +protected: + virtual void open(); + virtual void close(); + + virtual void draw(); + virtual void drawDialog(); + + virtual void handleTickle(); // Called periodically (in every guiloop() ) + virtual void handleMouseDown(int x, int y, int button, int clickCount); + virtual void handleMouseUp(int x, int y, int button, int clickCount); + virtual void handleMouseWheel(int x, int y, int direction); + virtual void handleKeyDown(uint16 ascii, int keycode, int modifiers); + virtual void handleKeyUp(uint16 ascii, int keycode, int modifiers); + virtual void handleMouseMoved(int x, int y, int button); + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + virtual void handleScreenChanged() {} + + Widget *findWidget(int x, int y); // Find the widget at pos x,y if any + + ButtonWidget *addButton(int x, int y, const Common::String &label, uint32 cmd, char hotkey); + + void setResult(int result) { _result = result; } + int getResult() const { return _result; } + + +protected: + Widget *_mouseWidget; + Widget *_focusedWidget; + bool _visible; + +private: + int _result; + +}; + +} // End of namespace GUI + +#endif diff --git a/stella/src/gui/GuiObject.hxx b/stella/src/gui/GuiObject.hxx new file mode 100644 index 000000000..804536918 --- /dev/null +++ b/stella/src/gui/GuiObject.hxx @@ -0,0 +1,60 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: GuiObject.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#ifndef GUI_OBJECT_HXX +#define GUI_OBJECT_HXX + +class Widget; + +#include "bspf.hxx" + +/** + This is the base class for all GUI objects/widgets. + + @author Stephen Anthony + @version $Id: GuiObject.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +*/ +class GuiObject +{ + //friend class Widget; + + public: + GuiObject(int x, int y, int w, int h) : _x(x), _y(y), _w(w), _h(h), _firstWidget(0) { } + + virtual Int16 getAbsX() const { return _x; } + virtual Int16 getAbsY() const { return _y; } + virtual Int16 getChildX() const { return getAbsX(); } + virtual Int16 getChildY() const { return getAbsY(); } + virtual uInt16 getWidth() const { return _w; } + virtual uInt16 getHeight() const { return _h; } + + virtual bool isVisible() const = 0; + + virtual void draw() = 0; + + protected: + Int16 _x, _y; + uInt16 _w, _h; + + Widget* _firstWidget; + + protected: + virtual void releaseFocus() = 0; +}; + +#endif diff --git a/stella/src/gui/Menu.cxx b/stella/src/gui/Menu.cxx new file mode 100644 index 000000000..0ee9f48ed --- /dev/null +++ b/stella/src/gui/Menu.cxx @@ -0,0 +1,47 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Menu.cxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#include + +#include "bspf.hxx" + +#include "Menu.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Menu::Menu() +{ + cerr << "Menu::Menu()\n"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Menu::~Menu() +{ + cerr << "Menu::~Menu()\n"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Menu::handleKeyEvent(SDLKey key, SDLMod mod, uInt8 state) +{ + cerr << "Menu::handleKeyEvent()\n"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Menu::draw() +{ + cerr << "Menu::draw()\n"; +} diff --git a/stella/src/gui/Menu.hxx b/stella/src/gui/Menu.hxx new file mode 100644 index 000000000..04c21570f --- /dev/null +++ b/stella/src/gui/Menu.hxx @@ -0,0 +1,67 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Menu.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#ifndef MENU_HXX +#define MENU_HXX + +#include "bspf.hxx" + +/** + The base class for all menus in Stella. + + This class keeps track of all configuration menus. organizes them into + a stack, and handles their events. + + @author Stephen Anthony + @version $Id: Menu.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +*/ +class Menu +{ + public: + /** + Create a new menu stack + */ + Menu(); + + /** + Destructor + */ + virtual ~Menu(); + + public: + /** + Handle a keyboard event. + + @param key keysym + @param mod modifiers + @param state state of key + */ + void handleKeyEvent(SDLKey key, SDLMod mod, uInt8 state); + +// FIXME - add mouse and joystick handlers + + /** + Draw the stack of menus. + */ + void draw(); + + private: + +}; + +#endif diff --git a/stella/src/gui/Widget.cxx b/stella/src/gui/Widget.cxx new file mode 100644 index 000000000..747098c95 --- /dev/null +++ b/stella/src/gui/Widget.cxx @@ -0,0 +1,267 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Widget.cxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#include "Dialog.hxx" +#include "GuiObject.hxx" +#include "bspf.hxx" + +#include "Widget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Widget::Widget(GuiObject* boss, uInt32 x, uInt32 y, uInt32 w, uInt32 h) + : GuiObject(x, y, w, h), _type(0), _boss(boss), + _id(0), _flags(0), _hasFocus(false) +{ + // Insert into the widget list of the boss + _next = _boss->_firstWidget; + _boss->_firstWidget = this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Widget::~Widget() +{ + delete _next; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::draw() +{ + NewGui *gui = &g_gui; + + if (!isVisible() || !_boss->isVisible()) + return; + + int oldX = _x, oldY = _y; + + // Account for our relative position in the dialog + _x = getAbsX(); + _y = getAbsY(); + + // Clear background (unless alpha blending is enabled) + if (_flags & WIDGET_CLEARBG) + gui->fillRect(_x, _y, _w, _h, gui->_bgcolor); + + // Draw border + if (_flags & WIDGET_BORDER) { + OverlayColor colorA = gui->_color; + OverlayColor colorB = gui->_shadowcolor; + if ((_flags & WIDGET_INV_BORDER) == WIDGET_INV_BORDER) + SWAP(colorA, colorB); + gui->box(_x, _y, _w, _h, colorA, colorB); + _x += 4; + _y += 4; + _w -= 8; + _h -= 8; + } + + // Now perform the actual widget draw + drawWidget((_flags & WIDGET_HILITED) ? true : false); + + // Restore x/y + if (_flags & WIDGET_BORDER) { + _x -= 4; + _y -= 4; + _w += 8; + _h += 8; + } + + // Flag the draw area as dirty + gui->addDirtyRect(_x, _y, _w, _h); + + _x = oldX; + _y = oldY; + + // Draw all children + Widget *w = _firstWidget; + while (w) { + w->draw(); + w = w->_next; + } +} + +Widget *Widget::findWidgetInChain(Widget *w, int x, int y) { + while (w) { + // Stop as soon as we find a widget that contains the point (x,y) + if (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->_h) + break; + w = w->_next; + } + if (w) + w = w->findWidget(x - w->_x, y - w->_y); + return w; +} + +#pragma mark - + +StaticTextWidget::StaticTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text, TextAlignment align) + : Widget(boss, x, y, w, h), _align(align) { + _flags = WIDGET_ENABLED; + _type = kStaticTextWidget; + setLabel(text); +} + +void StaticTextWidget::setValue(int value) { + char buf[256]; + sprintf(buf, "%d", value); + _label = buf; +} + +void StaticTextWidget::drawWidget(bool hilite) { + NewGui *gui = &g_gui; + gui->drawString(_label, _x, _y, _w, isEnabled() ? gui->_textcolor : gui->_color, _align); +} + +#pragma mark - + +ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const String &label, uint32 cmd, uint8 hotkey) + : StaticTextWidget(boss, x, y, w, h, label, kTextAlignCenter), CommandSender(boss), + _cmd(cmd), _hotkey(hotkey) { + _flags = WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG; + _type = kButtonWidget; +} + +void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) { + if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) + sendCommand(_cmd, 0); +} + +void ButtonWidget::drawWidget(bool hilite) { + NewGui *gui = &g_gui; + gui->drawString(_label, _x, _y + (_h - kLineHeight)/2 + 1, _w, + !isEnabled() ? gui->_color : + hilite ? gui->_textcolorhi : gui->_textcolor, _align); +} + +#pragma mark - + +/* 8x8 checkbox bitmap */ +static uint32 checked_img[8] = { + 0x00000000, + 0x01000010, + 0x00100100, + 0x00011000, + 0x00011000, + 0x00100100, + 0x01000010, + 0x00000000, +}; + +CheckboxWidget::CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const String &label, uint32 cmd, uint8 hotkey) + : ButtonWidget(boss, x, y, w, h, label, cmd, hotkey), _state(false) { + _flags = WIDGET_ENABLED; + _type = kCheckboxWidget; +} + +void CheckboxWidget::handleMouseUp(int x, int y, int button, int clickCount) { + if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) { + toggleState(); + } +} + +void CheckboxWidget::setState(bool state) { + if (_state != state) { + _state = state; + _flags ^= WIDGET_INV_BORDER; + draw(); + } + sendCommand(_cmd, _state); +} + +void CheckboxWidget::drawWidget(bool hilite) { + NewGui *gui = &g_gui; + + // Draw the box + gui->box(_x, _y, 14, 14, gui->_color, gui->_shadowcolor); + + // If checked, draw cross inside the box + if (_state) + gui->drawBitmap(checked_img, _x + 3, _y + 3, isEnabled() ? gui->_textcolor : gui->_color); + else + gui->fillRect(_x + 2, _y + 2, 10, 10, gui->_bgcolor); + + // Finally draw the label + gui->drawString(_label, _x + 20, _y + 3, _w, isEnabled() ? gui->_textcolor : gui->_color); +} + +#pragma mark - + +SliderWidget::SliderWidget(GuiObject *boss, int x, int y, int w, int h, const String &label, uint labelWidth, uint32 cmd, uint8 hotkey) + : ButtonWidget(boss, x, y, w, h, label, cmd, hotkey), + _value(0), _oldValue(0),_valueMin(0), _valueMax(100), _isDragging(false), + _labelWidth(labelWidth) { + _flags = WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG; + _type = kSliderWidget; +} + +void SliderWidget::handleMouseMoved(int x, int y, int button) { + // TODO: when the mouse is dragged outside the widget, the slider should + // snap back to the old value. + if (isEnabled() && _isDragging && x >= (int)_labelWidth) { + int newValue = posToValue(x - _labelWidth); + if (newValue < _valueMin) + newValue = _valueMin; + else if (newValue > _valueMax) + newValue = _valueMax; + + if (newValue != _value) { + _value = newValue; + draw(); + sendCommand(_cmd, _value); // FIXME - hack to allow for "live update" in sound dialog + } + } +} + +void SliderWidget::handleMouseDown(int x, int y, int button, int clickCount) { + if (isEnabled()) { + _isDragging = true; + handleMouseMoved(x, y, button); + } +} + +void SliderWidget::handleMouseUp(int x, int y, int button, int clickCount) { + if (isEnabled() && _isDragging) { + sendCommand(_cmd, _value); + } + _isDragging = false; +} + +void SliderWidget::drawWidget(bool hilite) { + NewGui *gui = &g_gui; + + // Draw the label, if any + if (_labelWidth > 0) + gui->drawString(_label, _x, _y + 2, _labelWidth, isEnabled() ? gui->_textcolor : gui->_color, kTextAlignRight); + + // Draw the box + gui->box(_x + _labelWidth, _y, _w - _labelWidth, _h, gui->_color, gui->_shadowcolor); + + // Draw the 'bar' + gui->fillRect(_x + _labelWidth + 2, _y + 2, valueToPos(_value), _h - 4, + !isEnabled() ? gui->_color : + hilite ? gui->_textcolorhi : gui->_textcolor); +} + +int SliderWidget::valueToPos(int value) { + return ((_w - _labelWidth - 4) * (value - _valueMin) / (_valueMax - _valueMin)); +} + +int SliderWidget::posToValue(int pos) { + return (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - 4) + _valueMin; +} + +} // End of namespace GUI diff --git a/stella/src/gui/Widget.hxx b/stella/src/gui/Widget.hxx new file mode 100644 index 000000000..ffa76a122 --- /dev/null +++ b/stella/src/gui/Widget.hxx @@ -0,0 +1,196 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Widget.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +//============================================================================ + +#ifndef WIDGET_HXX +#define WIDGET_HXX + +class Dialog; + +#include "GuiObject.hxx" +#include "bspf.hxx" + + +/** + This is the base class for all widgets. + + @author Stephen Anthony + @version $Id: Widget.hxx,v 1.1 2005-02-27 23:41:19 stephena Exp $ +*/ +class Widget : public GuiObject +{ + friend class Dialog; + + public: + Widget(GuiObject* boss, uInt32 x, uInt32 y, uInt32 w, uInt32 h); + virtual ~Widget(); + + virtual Int16 getAbsX() const { return _x + _boss->getChildX(); } + virtual Int16 getAbsY() const { return _y + _boss->getChildY(); } + + virtual void handleMouseDown(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount) {} + virtual void handleMouseUp(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount) {} + virtual void handleMouseEntered(uInt32 button) {} + virtual void handleMouseLeft(uInt32 button) {} + virtual void handleMouseMoved(uInt32 x, uInt32 y, uInt32 button) {} + virtual void handleMouseWheel(uInt32 x, uInt32 y, uInt32 direction) {} +//FIXME virtual bool handleKeyDown(uint16 ascii, int keycode, int modifiers) { return false; } // Return true if the event was handled +//FIXME virtual bool handleKeyUp(uint16 ascii, int keycode, int modifiers) { return false; } // Return true if the event was handled + virtual void handleTickle() {} + + void draw(); + void receivedFocus() { _hasFocus = true; receivedFocusWidget(); } + void lostFocus() { _hasFocus = false; lostFocusWidget(); } + virtual bool wantsFocus() { return false; }; + + void setFlags(int flags) { _flags |= flags; } + void clearFlags(int flags) { _flags &= ~flags; } + uInt32 getFlags() const { return _flags; } + + void setEnabled(bool e) { if (e) setFlags(WIDGET_ENABLED); else clearFlags(WIDGET_ENABLED); } + bool isEnabled() const { return _flags & WIDGET_ENABLED; } + bool isVisible() const { return !(_flags & WIDGET_INVISIBLE); } + + protected: + virtual void drawWidget(bool hilite) {} + + virtual void receivedFocusWidget() {} + virtual void lostFocusWidget() {} + + virtual Widget *findWidget(uInt32 x, uInt32 y) { return this; } + + void releaseFocus() { assert(_boss); _boss->releaseFocus(); } + + // By default, delegate unhandled commands to the boss + void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) + { assert(_boss); _boss->handleCommand(sender, cmd, data); } + + protected: + uInt32 _type; + GuiObject* _boss; + Widget* _next; + uInt16 _id; + uInt16 _flags; + bool _hasFocus; + + public: + static Widget *findWidgetInChain(Widget *start, uInt32 x, uInt32 y); +}; + + +/* StaticTextWidget */ +class StaticTextWidget : public Widget +{ + public: + StaticTextWidget(GuiObject* boss, uInt32 x, uInt32 y, uInt32 w, uInt32 h, + const String &text, TextAlignment align); + void setValue(uInt32 value); + void setLabel(const string& label) { _label = label; } + const string& getLabel() const { return _label; } + void setAlign(TextAlignment align) { _align = align; } + TextAlignment getAlign() const { return _align; } + + protected: + void drawWidget(bool hilite); + + protected: +// typedef Graphics::TextAlignment TextAlignment; + + string _label; +// TextAlignment _align; +}; + + +/* ButtonWidget */ +class ButtonWidget : public StaticTextWidget +{ + public: + ButtonWidget(GuiObject* boss, uInt32 x, uInt32 y, uInt32 w, uInt32 h, + const string& label, uInt32 cmd = 0, uInt8 hotkey = 0); + + void setCmd(uint32 cmd) { _cmd = cmd; } + uint32 getCmd() const { return _cmd; } + + void handleMouseUp(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount); + void handleMouseEntered(uInt32 button) { setFlags(WIDGET_HILITED); draw(); } + void handleMouseLeft(uInt32 button) { clearFlags(WIDGET_HILITED); draw(); } + + protected: + void drawWidget(bool hilite); + + protected: + uInt32 _cmd; + uInt8 _hotkey; +}; + + +/* CheckboxWidget */ +class CheckboxWidget : public ButtonWidget +{ + public: + CheckboxWidget(GuiObject* boss, uInt32 x, uInt32 y, uInt32 w, uInt32 h, + const string& label, uInt32 cmd = 0, uInt8 hotkey = 0); + + void handleMouseUp(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount); + virtual void handleMouseEntered(uInt32 button) {} + virtual void handleMouseLeft(uInt32 button) {} + + void setState(bool state); + void toggleState() { setState(!_state); } + bool getState() const { return _state; } + + protected: + void drawWidget(bool hilite); + + protected: + bool _state; +}; + + +/* SliderWidget */ +class SliderWidget : public ButtonWidget +{ + public: + SliderWidget(GuiObject *boss, int x, int y, int w, int h, const string& label = "", + uInt32 labelWidth = 0, uInt32 cmd = 0, uInt8 hotkey = 0); + + void setValue(uInt32 value) { _value = value; } + uInt32 getValue() const { return _value; } + + void setMinValue(uInt32 value) { _valueMin = value; } + uInt32 getMinValue() const { return _valueMin; } + void setMaxValue(uInt32 value) { _valueMax = value; } + uInt32 getMaxValue() const { return _valueMax; } + + void handleMouseMoved(uInt32 x, uInt32 y, uInt32 button); + void handleMouseDown(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount); + void handleMouseUp(uInt32 x, uInt32 y, uInt32 button, uInt32 clickCount); + + protected: + void drawWidget(bool hilite); + + uInt32 valueToPos(uInt32 value); + uInt32 posToValue(uInt32 pos); + + protected: + uInt32 _value, _oldValue; + uInt32 _valueMin, _valueMax; + bool _isDragging; + uInt32 _labelWidth; +}; + +#endif