From ac1bccf4546925a31fcd954d67b8d772125bb1be Mon Sep 17 00:00:00 2001 From: stephena Date: Sun, 31 Mar 2013 00:10:05 +0000 Subject: [PATCH] First pass at adding a cart-specific bankswitch/info tab to the debugger. In the process, I had to spend several days extending the UI/dialog class to actually contain multiple tabs. This was harder than expected, and it still isn't quite finished. In many ways, we're beginning to reach the limits of the current code; it was never designed for a full-fledged, graphically rich UI. For now the tab is empty, but eventually it will contain general info about the ROM bankswitch type (size, virtual layout, etc), but also cart-specific info, including the ability to change banks, which can be vary greatly among the different schemes. Eventually, it may even allow to see/modify very cart-specific info (like display RAM in DPC, etc). Better handle errors in opening the serial port (AtariVox support) for Windows and OSX. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2678 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- src/common/Array.hxx | 32 +-- src/debugger/gui/DebuggerDialog.cxx | 58 ++++- src/debugger/gui/DebuggerDialog.hxx | 2 +- src/debugger/gui/RomWidget.cxx | 13 +- src/debugger/gui/RomWidget.hxx | 2 +- src/emucore/Console.cxx | 14 +- src/emucore/Console.hxx | 5 + src/emucore/FrameBuffer.cxx | 23 +- src/emucore/FrameBuffer.hxx | 7 +- src/emucore/OSystem.cxx | 3 + src/gui/Dialog.cxx | 336 +++++++++++++++++++--------- src/gui/Dialog.hxx | 53 +++-- src/gui/GameInfoDialog.cxx | 8 +- src/gui/InputDialog.cxx | 6 +- src/gui/Rect.hxx | 6 + src/gui/TabWidget.cxx | 4 +- src/gui/UIDialog.cxx | 6 +- src/gui/VideoDialog.cxx | 4 +- src/macosx/SerialPortMACOSX.cxx | 4 + src/win32/SerialPortWin32.cxx | 3 + 20 files changed, 395 insertions(+), 194 deletions(-) diff --git a/src/common/Array.hxx b/src/common/Array.hxx index 313bd861b..bff81fa54 100644 --- a/src/common/Array.hxx +++ b/src/common/Array.hxx @@ -33,8 +33,8 @@ template class Array { protected: - int _capacity; - int _size; + uInt32 _capacity; + uInt32 _size; T *_data; public: @@ -48,7 +48,7 @@ class Array _size = array._size; _capacity = _size + 128; _data = new T[_capacity]; - for(int i = 0; i < _size; i++) + for(uInt32 i = 0; i < _size; i++) _data[i] = array._data[i]; } @@ -58,7 +58,7 @@ class Array delete [] _data; } - void reserve(int capacity) + void reserve(uInt32 capacity) { if(capacity <= _capacity) return; @@ -74,11 +74,11 @@ class Array void push_back(const Array& array) { ensureCapacity(_size + array._size); - for(int i = 0; i < array._size; i++) + for(uInt32 i = 0; i < array._size; i++) _data[_size++] = array._data[i]; } - void insert_at(int idx, const T& element) + void insert_at(uInt32 idx, const T& element) { assert(idx >= 0 && idx <= _size); ensureCapacity(_size + 1); @@ -87,30 +87,30 @@ class Array // usually isn't correct (specifically, for any class which has a non-default // copy behaviour. E.g. the String class uses a refCounter which has to be // updated whenever a String is copied. - for(int i = _size; i > idx; i--) + for(uInt32 i = _size; i > idx; i--) _data[i] = _data[i-1]; _data[idx] = element; _size++; } - T remove_at(int idx) + T remove_at(uInt32 idx) { assert(idx >= 0 && idx < _size); T tmp = _data[idx]; - for(int i = idx; i < _size - 1; i++) + for(uInt32 i = idx; i < _size - 1; i++) _data[i] = _data[i+1]; _size--; return tmp; } - T& operator [](int idx) + T& operator [](uInt32 idx) { assert(idx >= 0 && idx < _size); return _data[idx]; } - const T& operator [](int idx) const + const T& operator [](uInt32 idx) const { assert(idx >= 0 && idx < _size); return _data[idx]; @@ -123,14 +123,14 @@ class Array _size = array._size; _capacity = _size + 128; _data = new T[_capacity]; - for(int i = 0; i < _size; i++) + for(uInt32 i = 0; i < _size; i++) _data[i] = array._data[i]; return *this; } - unsigned int size() const { return _size; } - unsigned int capacity() const { return _capacity; } + uInt32 size() const { return _size; } + uInt32 capacity() const { return _capacity; } void clear(bool fullerase = true) { @@ -172,7 +172,7 @@ class Array } protected: - void ensureCapacity(int new_len) + void ensureCapacity(uInt32 new_len) { if (new_len <= _capacity) return; @@ -184,7 +184,7 @@ class Array if (old_data) { // Copy old data - for (int i = 0; i < _size; i++) + for (uInt32 i = 0; i < _size; i++) _data[i] = old_data[i]; delete [] old_data; } diff --git a/src/debugger/gui/DebuggerDialog.cxx b/src/debugger/gui/DebuggerDialog.cxx index f7772e83a..680b4643f 100644 --- a/src/debugger/gui/DebuggerDialog.cxx +++ b/src/debugger/gui/DebuggerDialog.cxx @@ -47,6 +47,7 @@ DebuggerDialog::DebuggerDialog(OSystem* osystem, DialogContainer* parent, int x, int y, int w, int h) : Dialog(osystem, parent, x, y, w, h, true), // use base surface myTab(NULL), + myRomTab(NULL), myFatalError(NULL) { addTiaArea(); @@ -72,7 +73,9 @@ void DebuggerDialog::loadConfig() myTiaZoom->loadConfig(); myCpu->loadConfig(); myRam->loadConfig(); - myRom->loadConfig(); + + myRomTab->loadConfig(); +// myRom->loadConfig(); myMessageBox->setEditString(""); } @@ -176,12 +179,14 @@ void DebuggerDialog::addTiaArea() void DebuggerDialog::addTabArea() { const GUI::Rect& r = instance().debugger().getTabBounds(); - const int vBorder = 4; // The tab widget + // Since there are two tab widgets in this dialog, we specifically + // assign an ID of 0 myTab = new TabWidget(this, instance().consoleFont(), r.left, r.top + vBorder, r.width(), r.height() - vBorder); + myTab->setID(0); addTabWidget(myTab); const int widWidth = r.width() - vBorder; @@ -193,28 +198,28 @@ void DebuggerDialog::addTabArea() myPrompt = new PromptWidget(myTab, instance().consoleFont(), 2, 2, widWidth, widHeight); myTab->setParentWidget(tabID, myPrompt); - addToFocusList(myPrompt->getFocusList(), tabID); + addToFocusList(myPrompt->getFocusList(), myTab, tabID); // The TIA tab tabID = myTab->addTab("TIA"); TiaWidget* tia = new TiaWidget(myTab, instance().consoleFont(), 2, 2, widWidth, widHeight); myTab->setParentWidget(tabID, tia); - addToFocusList(tia->getFocusList(), tabID); + addToFocusList(tia->getFocusList(), myTab, tabID); // The input/output tab (includes RIOT and INPTx from TIA) tabID = myTab->addTab("I/O"); RiotWidget* riot = new RiotWidget(myTab, instance().consoleFont(), 2, 2, widWidth, widHeight); myTab->setParentWidget(tabID, riot); - addToFocusList(riot->getFocusList(), tabID); + addToFocusList(riot->getFocusList(), myTab, tabID); // The Audio tab tabID = myTab->addTab("Audio"); AudioWidget* aud = new AudioWidget(myTab, instance().consoleFont(), 2, 2, widWidth, widHeight); myTab->setParentWidget(tabID, aud); - addToFocusList(aud->getFocusList(), tabID); + addToFocusList(aud->getFocusList(), myTab, tabID); myTab->setActiveTab(0); } @@ -248,6 +253,8 @@ void DebuggerDialog::addStatusArea() void DebuggerDialog::addRomArea() { const GUI::Rect& r = instance().debugger().getRomBounds(); + const int vBorder = 4; + int xpos, ypos; xpos = r.left + 10; ypos = 10; @@ -289,14 +296,45 @@ void DebuggerDialog::addRomArea() bwidth, bheight, "<", kDDRewindCmd); myRewindButton->clearFlags(WIDGET_ENABLED); - xpos = r.left + 10; ypos += myRam->getHeight() + 5; - myRom = new RomWidget(this, instance().consoleFont(), xpos, ypos); - addToFocusList(myRom->getFocusList()); - // Add the DataGridOpsWidget to any widgets which contain a // DataGridWidget which we want controlled myCpu->setOpsWidget(ops); myRam->setOpsWidget(ops); + + //////////////////////////////////////////////////////////////////// + // Disassembly area + + xpos = r.left + vBorder; ypos += myRam->getHeight() + 5; + const int tabWidth = r.width() - vBorder; + const int tabHeight = r.height() - ypos; + int tabID; + + // Since there are two tab widgets in this dialog, we specifically + // assign an ID of 1 + myRomTab = new TabWidget( + this, instance().consoleFont(), xpos, ypos, tabWidth, tabHeight); + myRomTab->setID(1); + addTabWidget(myRomTab); + + // The main disassembly tab + tabID = myRomTab->addTab(" Disassembly "); + myRom = new RomWidget(myRomTab, instance().consoleFont(), + 2, 2, tabWidth - 1, + tabHeight - myRomTab->getTabHeight() - 2); + myRomTab->setParentWidget(tabID, myRom); + addToFocusList(myRom->getFocusList(), myRomTab, tabID); + + // The 'cart-specific' information tab + tabID = myRomTab->addTab(instance().console().cartridge().name()); +#if 0 + myRom = new RomWidget(myRomTab, instance().consoleFont(), + 2, 2, tabWidth - 1, + tabHeight - myRomTab->getTabHeight() - 2); + myRomTab->setParentWidget(tabID, myRom); + addToFocusList(myRom->getFocusList(), myRomTab, tabID); +#endif + + myRomTab->setActiveTab(0); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/gui/DebuggerDialog.hxx b/src/debugger/gui/DebuggerDialog.hxx index c3a536a98..29c277c6a 100644 --- a/src/debugger/gui/DebuggerDialog.hxx +++ b/src/debugger/gui/DebuggerDialog.hxx @@ -72,7 +72,7 @@ class DebuggerDialog : public Dialog kDDExitFatalCmd = 'DDer' }; - TabWidget* myTab; + TabWidget *myTab, *myRomTab; PromptWidget* myPrompt; TiaInfoWidget* myTiaInfo; diff --git a/src/debugger/gui/RomWidget.cxx b/src/debugger/gui/RomWidget.cxx index 7e808857f..f1a537025 100644 --- a/src/debugger/gui/RomWidget.cxx +++ b/src/debugger/gui/RomWidget.cxx @@ -38,8 +38,9 @@ #include "RomWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y) - : Widget(boss, font, x, y, 16, 16), +RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h) + : Widget(boss, font, x, y, w, h), CommandSender(boss), myListIsDirty(true), myCurrentBank(-1) @@ -85,18 +86,12 @@ RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y) // Create rom listing xpos = x; ypos += myBank->getHeight() + 4; - const GUI::Rect& dialog = instance().debugger().getDialogBounds(); - int w = dialog.width() - x - 5, h = dialog.height() - ypos - 3; - myRomList = new RomListWidget(boss, font, xpos, ypos, w, h); + myRomList = new RomListWidget(boss, font, xpos, ypos, _w - 4, _h - ypos - 2); myRomList->setTarget(this); myRomList->myMenu->setTarget(this); addFocusWidget(myRomList); - // Calculate real dimensions - _w = myRomList->getWidth(); - _h = myRomList->getHeight(); - // Create dialog box for save ROM (get name) StringList label; label.push_back("Filename: "); diff --git a/src/debugger/gui/RomWidget.hxx b/src/debugger/gui/RomWidget.hxx index 64d22a609..4de51a043 100644 --- a/src/debugger/gui/RomWidget.hxx +++ b/src/debugger/gui/RomWidget.hxx @@ -39,7 +39,7 @@ class StringList; class RomWidget : public Widget, public CommandSender { public: - RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y); + RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h); virtual ~RomWidget(); void invalidate(bool forcereload = true) diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 44a2c3ea8..8bebe1cdc 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -163,11 +163,6 @@ Console::Console(OSystem* osystem, Cartridge* cart, const Properties& props) // Reset the system to its power-on state mySystem->reset(); -#ifdef DEBUGGER_SUPPORT - myOSystem->createDebugger(*this); - m6502->attach(myOSystem->debugger()); -#endif - // Finally, add remaining info about the console myConsoleInfo.CartName = myProperties.get(Cartridge_Name); myConsoleInfo.CartMD5 = myProperties.get(Cartridge_MD5); @@ -920,6 +915,15 @@ void Console::toggleFixedColors() const myOSystem->frameBuffer().showMessage("Fixed debug colors disabled"); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Console::addDebugger() +{ +#ifdef DEBUGGER_SUPPORT + myOSystem->createDebugger(*this); + mySystem->m6502().attach(myOSystem->debugger()); +#endif +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 Console::ourNTSCPalette[256] = { 0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0, diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index bc8652ab6..6ad707677 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -170,6 +170,11 @@ class Console : public Serializable */ const ConsoleInfo& about() const { return myConsoleInfo; } + /** + Set up the console to use the debugger. + */ + void addDebugger(); + public: /** Overloaded assignment operator diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 122caf064..e9e8a2975 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -50,9 +50,8 @@ FrameBuffer::FrameBuffer(OSystem* osystem) myInitializedCount(0), myPausedCount(0) { - myMsg.surface = myStatsMsg.surface = NULL; - myMsg.surfaceID = myStatsMsg.surfaceID = -1; - myMsg.enabled = myStatsMsg.enabled = false; + myMsg.surface = myStatsMsg.surface = NULL; + myMsg.enabled = myStatsMsg.enabled = false; // Load NTSC filter settings myNTSCFilter.loadConfig(myOSystem->settings()); @@ -175,13 +174,13 @@ FBInitStatus FrameBuffer::initialize(const string& title, if(myStatsMsg.surface == NULL) { - myStatsMsg.surfaceID = allocateSurface(myStatsMsg.w, myStatsMsg.h); - myStatsMsg.surface = surface(myStatsMsg.surfaceID); + uInt32 surfaceID = allocateSurface(myStatsMsg.w, myStatsMsg.h); + myStatsMsg.surface = surface(surfaceID); } if(myMsg.surface == NULL) { - myMsg.surfaceID = allocateSurface(640, myOSystem->font().getFontHeight()+10); - myMsg.surface = surface(myMsg.surfaceID); + uInt32 surfaceID = allocateSurface(640, myOSystem->font().getFontHeight()+10); + myMsg.surface = surface(surfaceID); } // Finally, show some information about the framebuffer, @@ -586,22 +585,22 @@ void FrameBuffer::toggleScanlineInterpolation() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int FrameBuffer::allocateSurface(int w, int h, bool useBase) +uInt32 FrameBuffer::allocateSurface(int w, int h, bool useBase) { // Create a new surface FBSurface* surface = createSurface(w, h, useBase); // Add it to the list - mySurfaceList.insert(make_pair(int(mySurfaceList.size()), surface)); + mySurfaceList.insert(make_pair(mySurfaceList.size(), surface)); // Return a reference to it return mySurfaceList.size() - 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FBSurface* FrameBuffer::surface(int id) const +FBSurface* FrameBuffer::surface(uInt32 id) const { - map::const_iterator iter = mySurfaceList.find(id); + map::const_iterator iter = mySurfaceList.find(id); return iter != mySurfaceList.end() ? iter->second : NULL; } @@ -614,7 +613,7 @@ void FrameBuffer::resetSurfaces(FBSurface* tiasurface) // Any derived FrameBuffer classes that call this method should be // aware of these restrictions, and act accordingly - map::iterator iter; + map::iterator iter; for(iter = mySurfaceList.begin(); iter != mySurfaceList.end(); ++iter) iter->second->free(); if(tiasurface) diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index d6382e28f..b1484ac81 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -173,7 +173,7 @@ class FrameBuffer @return A unique ID used to identify this surface */ - int allocateSurface(int w, int h, bool useBase = false); + uInt32 allocateSurface(int w, int h, bool useBase = false); /** Retrieve the surface associated with the given ID. @@ -181,7 +181,7 @@ class FrameBuffer @param id The ID for the surface to retrieve. @return A pointer to a valid surface object, or NULL. */ - FBSurface* surface(int id) const; + FBSurface* surface(uInt32 id) const; /** Returns the current dimensions of the framebuffer image. @@ -633,7 +633,6 @@ class FrameBuffer MessagePosition position; uInt32 color; FBSurface* surface; - int surfaceID; bool enabled; }; Message myMsg; @@ -645,7 +644,7 @@ class FrameBuffer VideoModeList* myCurrentModeList; // Holds a reference to all the surfaces that have been created - map mySurfaceList; + map mySurfaceList; // Holds static strings for the remap menu (emulation and menu events) static GraphicsMode ourGraphicsModes[GFX_NumModes]; diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 6b979db11..2e7850b4f 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -531,6 +531,9 @@ bool OSystem::createConsole(const FilesystemNode& rom, const string& md5sum, myConsole = openConsole(myRomFile, myRomMD5, type, id); if(myConsole) { + #ifdef DEBUGGER_SUPPORT + myConsole->addDebugger(); + #endif #ifdef CHEATCODE_SUPPORT myCheatManager->loadCheats(myRomMD5); #endif diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 2b8b20c75..c89f9d6a5 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -46,31 +46,26 @@ Dialog::Dialog(OSystem* instance, DialogContainer* parent, _cancelWidget(0), _visible(false), _isBase(isBase), - _ourTab(NULL), - _surface(NULL), - _focusID(0), - _surfaceID(-1) + _surface(0), + _tabID(0) { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dialog::~Dialog() { - for(unsigned int i = 0; i < _ourFocusList.size(); ++i) - _ourFocusList[i].focusList.clear(); + _myFocus.list.clear(); + _myTabList.clear(); delete _firstWidget; _firstWidget = NULL; - _ourButtonGroup.clear(); + _buttonGroup.clear(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::open() { - _result = 0; - _visible = true; - // Make sure we have a valid surface to draw into // Technically, this shouldn't be needed until drawDialog(), but some // dialogs cause drawing to occur within loadConfig() @@ -80,11 +75,10 @@ void Dialog::open() // However, this policy is left entirely to the framebuffer // We suggest the hint here, but specific framebuffers are free to // ignore it - _surface = instance().frameBuffer().surface(_surfaceID); if(_surface == NULL) { - _surfaceID = instance().frameBuffer().allocateSurface(_w, _h, _isBase); - _surface = instance().frameBuffer().surface(_surfaceID); + uInt32 surfaceID = instance().frameBuffer().allocateSurface(_w, _h, _isBase); + _surface = instance().frameBuffer().surface(surfaceID); } center(); @@ -92,7 +86,9 @@ void Dialog::open() // (Re)-build the focus list to use for the widgets which are currently // onscreen - buildFocusWidgetList(_focusID); + buildCurrentFocusList(); + + _visible = true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -135,43 +131,75 @@ void Dialog::releaseFocus() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::addFocusWidget(Widget* w) { - // All focusable widgets should retain focus - if(w) - w->setFlags(WIDGET_RETAIN_FOCUS); + if(!w) + return; - if(_ourFocusList.size() == 0) - { - Focus f; - f.focusedWidget = 0; - _ourFocusList.push_back(f); - } - _ourFocusList[0].focusedWidget = w; - _ourFocusList[0].focusList.push_back(w); + // All focusable widgets should retain focus + w->setFlags(WIDGET_RETAIN_FOCUS); + + _myFocus.widget = w; + _myFocus.list.push_back(w); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Dialog::addToFocusList(WidgetArray& list, int id) +void Dialog::addToFocusList(WidgetArray& list) { // All focusable widgets should retain focus - for(unsigned int i = 0; i < list.size(); ++i) + for(uInt32 i = 0; i < list.size(); ++i) list[i]->setFlags(WIDGET_RETAIN_FOCUS); - id++; // Arrays start at 0, not -1. - - // Make sure the array is large enough - while((int)_ourFocusList.size() <= id) - { - Focus f; - f.focusedWidget = NULL; - _ourFocusList.push_back(f); - } - - _ourFocusList[id].focusList.push_back(list); - if(id == 0 && _ourFocusList.size() > 0) - _focusList = _ourFocusList[0].focusList; + _myFocus.list.push_back(list); + _focusList = _myFocus.list; if(list.size() > 0) - _ourFocusList[id].focusedWidget = list[0]; + _myFocus.widget = list[0]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::addToFocusList(WidgetArray& list, TabWidget* w, int tabId) +{ + // Only add the list if the tab actually exists + if(!w || w->getID() < 0 || (uInt32)w->getID() >= _myTabList.size()) + return; + + assert(w == _myTabList[w->getID()].widget); + + // All focusable widgets should retain focus + for(uInt32 i = 0; i < list.size(); ++i) + list[i]->setFlags(WIDGET_RETAIN_FOCUS); + + // First get the appropriate focus list + FocusList& focus = _myTabList[w->getID()].focus; + + // Now insert in the correct place in that focus list + uInt32 id = tabId; + if(id < focus.size()) + focus[id].list.push_back(list); + else + { + // Make sure the array is large enough + while(focus.size() <= id) + focus.push_back(Focus()); + + focus[id].list.push_back(list); + } + + if(list.size() > 0) + focus[id].widget = list[0]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::addTabWidget(TabWidget* w) +{ + if(!w || w->getID() < 0) + return; + + // Make sure the array is large enough + uInt32 id = w->getID(); + while(_myTabList.size() < id) + _myTabList.push_back(TabFocus()); + + _myTabList.push_back(TabFocus(w)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -183,51 +211,66 @@ void Dialog::setFocus(Widget* w) { // Redraw widgets for new focus _focusedWidget = Widget::setFocusForChain(this, getFocusList(), w, 0); + +cerr << "set focus for " << _focusedWidget << endl; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Dialog::buildFocusWidgetList(int id) +void Dialog::buildCurrentFocusList(int tabID) { // Yes, this is hideously complex. That's the price we pay for // tab navigation ... + _focusList.clear(); - // Remember which item previously had focus, but only if it belongs - // to this focus list - if(_focusID < (int)_ourFocusList.size() && - Widget::isWidgetInChain(_ourFocusList[_focusID].focusList, _focusedWidget)) - _ourFocusList[_focusID].focusedWidget = _focusedWidget; - - _focusID = id; - - // Create a focuslist for items currently onscreen - // We do this by starting with any dialog focus list (at index 0 in the - // focus lists, then appending the list indicated by 'id'. - if(_focusID < (int)_ourFocusList.size()) + // Remember which tab item previously had focus, if applicable + // This only applies if this method was called for a tab change + Widget* tabFocusWidget = 0; + if(tabID >= 0 && tabID < (int)_myTabList.size()) { - _focusList.clear(); - _focusList.push_back(_ourFocusList[0].focusList); +cerr << "save tab, move to next\n"; + // Save focus in previously selected tab column, + // and get focus for new tab column + TabFocus& tabfocus = _myTabList[tabID]; + tabfocus.saveCurrentFocus(_focusedWidget); + tabFocusWidget = tabfocus.getNewFocus(); - // Append extra focus list - if(_focusID > 0) - _focusList.push_back(_ourFocusList[_focusID].focusList); - - // Add button group at end of current focus list - // We do it this way for TabWidget, so that buttons are scanned - // *after* the widgets in the current tab - if(_ourButtonGroup.size() > 0) - _focusList.push_back(_ourButtonGroup); - - // Only update _focusedWidget if it doesn't belong to the main focus list - // HACK - figure out how to properly deal with only one focus-able widget - // in a tab -- TabWidget is the spawn of the devil - if(_focusList.size() == 1) - _focusedWidget = _focusList[0]; - else if(!Widget::isWidgetInChain(_ourFocusList[0].focusList, _focusedWidget)) - _focusedWidget = _ourFocusList[_focusID].focusedWidget; + _tabID = tabID; } - else - _focusedWidget = 0; + + // Special case for dialogs containing only one tab widget, with all items + // arranged in separate tabs + bool containsSingleTab = _myFocus.list.size() == 1 && _myTabList.size() == 1; + + // A dialog containing only one tabwidget should be added first + if(containsSingleTab) + { + _focusList.push_back(_myFocus.list); + _focusedWidget = _focusList[0]; + } + + // Now add appropriate items from tablist (if present) + for(uInt32 id = 0; id < _myTabList.size(); ++id) + _myTabList[id].appendFocusList(_focusList); + + // Add remaining items from main focus list + if(!containsSingleTab) + _focusList.push_back(_myFocus.list); + + // Add button group at end of current focus list + // We do it this way for TabWidget, so that buttons are scanned + // *after* the widgets in the current tab + if(_buttonGroup.size() > 0) + _focusList.push_back(_buttonGroup); + + // Finally, the moment we've all been waiting for :) + // Set the actual focus widget + if(tabFocusWidget) +{cerr << "tab focus changed\n"; + _focusedWidget = tabFocusWidget; +} + else if(!_focusedWidget && _focusList.size() > 0) + _focusedWidget = _focusList[0]; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -295,20 +338,14 @@ void Dialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii) // not ascii?? if(instance().eventHandler().kbdShift(mod)) { - if(key == KBDK_LEFT && _ourTab) // left arrow - { - _ourTab->cycleTab(-1); + if(key == KBDK_LEFT && cycleTab(-1)) return; - } - else if(key == KBDK_RIGHT && _ourTab) // right arrow - { - _ourTab->cycleTab(+1); + else if(key == KBDK_RIGHT && cycleTab(+1)) return; - } - else if(key == KBDK_TAB) // tab + else if(key == KBDK_TAB) e = Event::UINavPrev; } - else if(key == KBDK_TAB) // tab + else if(key == KBDK_TAB) e = Event::UINavNext; // Check the keytable now, since we might get one of the above events, @@ -338,22 +375,19 @@ void Dialog::handleKeyUp(StellaKey key, StellaMod mod, char ascii) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::handleMouseDown(int x, int y, int button, int clickCount) { - Widget* w; - w = findWidget(x, y); + Widget* w = findWidget(x, y); _dragWidget = w; - setFocus(w); if(w) - w->handleMouseDown(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount); + w->handleMouseDown(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), + button, clickCount); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::handleMouseUp(int x, int y, int button, int clickCount) { - Widget* w; - if(_focusedWidget) { // Lose focus on mouseup unless the widget requested to retain the focus @@ -361,10 +395,10 @@ void Dialog::handleMouseUp(int x, int y, int button, int clickCount) releaseFocus(); } - w = _dragWidget; - + Widget* w = _dragWidget; if(w) - w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount); + w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), + button, clickCount); _dragWidget = 0; } @@ -372,16 +406,14 @@ void Dialog::handleMouseUp(int x, int y, int button, int 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); + Widget* w = findWidget(x, y); if(!w) w = _focusedWidget; - if (w) + if(w) w->handleMouseWheel(x, y, direction); } @@ -389,13 +421,13 @@ void Dialog::handleMouseWheel(int x, int y, int direction) void Dialog::handleMouseMoved(int x, int y, int button) { Widget* w; - + if(_focusedWidget && !_dragWidget) { 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); @@ -441,7 +473,8 @@ bool Dialog::handleMouseClicks(int x, int y, int button) Widget* w = findWidget(x, y); if(w) - return w->handleMouseClicks(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button); + return w->handleMouseClicks(x - (w->getAbsX() - _x), + y - (w->getAbsY() - _y), button); else return false; } @@ -554,14 +587,40 @@ bool Dialog::handleNavEvent(Event::Type e) return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Dialog::cycleTab(int direction) +{ +cerr << "cycle " << (direction < 0 ? "left" : "right") << ", tabID = " << _tabID << endl; + + if(_tabID >= 0 && _tabID < (int)_myTabList.size()) + { + _myTabList[_tabID].widget->cycleTab(direction); + return true; + } + return false; + +#if 0 + if(key == KBDK_LEFT && _ourTab) // left arrow + { + _ourTab->cycleTab(-1); + return; + } + else if(key == KBDK_RIGHT && _ourTab) // right arrow + { + _ourTab->cycleTab(+1); + return; + } +#endif +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::handleCommand(CommandSender* sender, int cmd, int data, int id) { switch(cmd) { case kTabChangedCmd: - // Add this focus list for the given tab to the global focus list - buildFocusWidgetList(++data); + if(_visible) + buildCurrentFocusList(id); break; case kCloseCmd: @@ -580,7 +639,7 @@ Widget* Dialog::findWidget(int x, int y) return Widget::findWidgetInChain(_firstWidget, x, y); } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::addOKCancelBGroup(WidgetArray& wid, const GUI::Font& font, const string& okText, const string& cancelText) { @@ -614,3 +673,72 @@ void Dialog::addOKCancelBGroup(WidgetArray& wid, const GUI::Font& font, addOKWidget(b); #endif } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Dialog::Focus::Focus(Widget* w) + : widget(w) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Dialog::Focus::~Focus() +{ + list.clear(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Dialog::TabFocus::TabFocus(TabWidget* w) + : widget(w), + currentTab(0) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Dialog::TabFocus::~TabFocus() +{ + focus.clear(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::TabFocus::appendFocusList(WidgetArray& list) +{ + uInt32 active = widget->getActiveTab(); + + if(active >= 0 && active < focus.size()) + list.push_back(focus[active].list); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::TabFocus::saveCurrentFocus(Widget* w) +{ +#if 1 + if(currentTab >= 0 && currentTab < focus.size()) + { + cerr << "chain len = " << focus[currentTab].list.size() << endl; + + if(Widget::isWidgetInChain(focus[currentTab].list, w)) + { + cerr << "saving widget\n"; + focus[currentTab].widget = w; + } + else + cerr << "not in chain\n"; +} + +#else + if(currentTab >= 0 && currentTab < focus.size() && + Widget::isWidgetInChain(focus[currentTab].list, w)) +{cerr << "saving widget\n"; + focus[currentTab].widget = w; +} +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Widget* Dialog::TabFocus::getNewFocus() +{ + currentTab = widget->getActiveTab(); + + return (currentTab >= 0 && currentTab < focus.size()) ? + focus[currentTab].widget : 0; +} diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx index 710722c9a..9540ab4c8 100644 --- a/src/gui/Dialog.hxx +++ b/src/gui/Dialog.hxx @@ -45,12 +45,6 @@ class Dialog : public GuiObject { friend class DialogContainer; - struct Focus { - Widget* focusedWidget; - WidgetArray focusList; - }; - typedef Common::Array FocusList; - public: Dialog(OSystem* instance, DialogContainer* parent, int x, int y, int w, int h, bool isBase = false); @@ -69,10 +63,11 @@ class Dialog : public GuiObject virtual void setDefaults() {} void addFocusWidget(Widget* w); - void addToFocusList(WidgetArray& list, int id = -1); - void addBGroupToFocusList(WidgetArray& list) { _ourButtonGroup = list; } + void addToFocusList(WidgetArray& list); + void addToFocusList(WidgetArray& list, TabWidget* w, int tabId); + void addBGroupToFocusList(WidgetArray& list) { _buttonGroup = list; } void redrawFocus(); - void addTabWidget(TabWidget* w) { _ourTab = w; } + void addTabWidget(TabWidget* w); void addOKWidget(Widget* w) { _okWidget = w; } void addCancelWidget(Widget* w) { _cancelWidget = w; } void setFocus(Widget* w); @@ -102,12 +97,10 @@ class Dialog : public GuiObject const string& okText = "", const string& cancelText = ""); - void setResult(int result) { _result = result; } - int getResult() const { return _result; } - private: - void buildFocusWidgetList(int id); + void buildCurrentFocusList(int tabID = -1); bool handleNavEvent(Event::Type e); + bool cycleTab(int direction); protected: Widget* _mouseWidget; @@ -119,14 +112,36 @@ class Dialog : public GuiObject bool _isBase; private: - FocusList _ourFocusList; - TabWidget* _ourTab; - WidgetArray _ourButtonGroup; + struct Focus { + Widget* widget; + WidgetArray list; + + Focus(Widget* w = 0); + virtual ~Focus(); + }; + typedef Common::Array FocusList; + + struct TabFocus { + TabWidget* widget; + FocusList focus; + uInt32 currentTab; + + TabFocus(TabWidget* w = 0); + virtual ~TabFocus(); + + void appendFocusList(WidgetArray& list); + void saveCurrentFocus(Widget* w); + Widget* getNewFocus(); + }; + typedef Common::Array TabFocusList; + + Focus _myFocus; // focus for base dialog + TabFocusList _myTabList; // focus for each tab (if any) + + WidgetArray _buttonGroup; FBSurface* _surface; - int _result; - int _focusID; - int _surfaceID; + int _tabID; }; #endif diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 6e3821e08..ac4822882 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -178,7 +178,7 @@ GameInfoDialog::GameInfoDialog( wid.push_back(myType); // Add items for tab 0 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // 2) Console properties @@ -216,7 +216,7 @@ GameInfoDialog::GameInfoDialog( wid.push_back(myTVType); // Add items for tab 1 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // 3) Controller properties @@ -326,7 +326,7 @@ GameInfoDialog::GameInfoDialog( wid.push_back(myMouseY); // Add items for tab 2 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // 4) Display properties @@ -389,7 +389,7 @@ GameInfoDialog::GameInfoDialog( myPPBlendLabel->setFlags(WIDGET_CLEARBG); // Add items for tab 3 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // Activate the first tab diff --git a/src/gui/InputDialog.cxx b/src/gui/InputDialog.cxx index 8fcbc12b9..ea219f2f5 100644 --- a/src/gui/InputDialog.cxx +++ b/src/gui/InputDialog.cxx @@ -67,7 +67,7 @@ InputDialog::InputDialog(OSystem* osystem, DialogContainer* parent, myTab->getHeight() - ypos, actions, kEmulationMode); myTab->setParentWidget(tabID, myEmulEventMapper); - addToFocusList(myEmulEventMapper->getFocusList(), tabID); + addToFocusList(myEmulEventMapper->getFocusList(), myTab, tabID); // 2) Event mapper for UI actions tabID = myTab->addTab("UI Events"); @@ -78,7 +78,7 @@ InputDialog::InputDialog(OSystem* osystem, DialogContainer* parent, myTab->getHeight() - ypos, actions, kMenuMode); myTab->setParentWidget(tabID, myMenuEventMapper); - addToFocusList(myMenuEventMapper->getFocusList(), tabID); + addToFocusList(myMenuEventMapper->getFocusList(), myTab, tabID); // 3) Devices & ports addDevicePortTab(font); @@ -197,7 +197,7 @@ void InputDialog::addDevicePortTab(const GUI::Font& font) wid.push_back(myMouseControl); // Add items for virtual device ports - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/Rect.hxx b/src/gui/Rect.hxx index 7b4ce3771..0befcef84 100644 --- a/src/gui/Rect.hxx +++ b/src/gui/Rect.hxx @@ -164,6 +164,12 @@ struct Rect void moveTo(const Point & p) { moveTo(p.x, p.y); } + + friend ostream& operator<<(ostream& os, const Rect& r) { + os << "x=" << r.x() << ", y=" << r.y() + << ", w=" << r.width() << ", h=" << r.height(); + return os; + } }; } // End of namespace GUI diff --git a/src/gui/TabWidget.cxx b/src/gui/TabWidget.cxx index daac02d5a..12e5c1699 100644 --- a/src/gui/TabWidget.cxx +++ b/src/gui/TabWidget.cxx @@ -39,6 +39,8 @@ TabWidget::TabWidget(GuiObject* boss, const GUI::Font& font, _activeTab(-1), _firstTime(true) { + _id = 0; // For dialogs with multiple tab widgets, they should specifically + // call ::setID to differentiate among them _flags = WIDGET_ENABLED | WIDGET_CLEARBG; _type = kTabWidget; _bgcolor = kDlgColor; @@ -112,7 +114,7 @@ void TabWidget::setActiveTab(int tabID, bool show) // Let parent know about the tab change if(show) - sendCommand(kTabChangedCmd, _activeTab, -1); + sendCommand(kTabChangedCmd, _activeTab, _id); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/UIDialog.cxx b/src/gui/UIDialog.cxx index 939e1a105..7ebc99cf4 100644 --- a/src/gui/UIDialog.cxx +++ b/src/gui/UIDialog.cxx @@ -156,7 +156,7 @@ UIDialog::UIDialog(OSystem* osystem, DialogContainer* parent, kTextAlignLeft); // Add items for tab 0 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); ////////////////////////////////////////////////////////// // 2) Debugger options @@ -211,7 +211,7 @@ UIDialog::UIDialog(OSystem* osystem, DialogContainer* parent, } // Add items for tab 1 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); ////////////////////////////////////////////////////////// // 3) Misc. options @@ -263,7 +263,7 @@ UIDialog::UIDialog(OSystem* osystem, DialogContainer* parent, ypos += lineHeight + 4; // Add items for tab 2 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // Activate the first tab myTab->setActiveTab(0); diff --git a/src/gui/VideoDialog.cxx b/src/gui/VideoDialog.cxx index 9cfa0d548..141a2c3be 100644 --- a/src/gui/VideoDialog.cxx +++ b/src/gui/VideoDialog.cxx @@ -236,7 +236,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, ypos += lineHeight + 4; // Add items for tab 0 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); ////////////////////////////////////////////////////////// // 2) TV effects options @@ -326,7 +326,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, CREATE_CLONE_BUTTON(Custom, "Revert"); // Add items for tab 2 - addToFocusList(wid, tabID); + addToFocusList(wid, myTab, tabID); // Activate the first tab myTab->setActiveTab(0); diff --git a/src/macosx/SerialPortMACOSX.cxx b/src/macosx/SerialPortMACOSX.cxx index b14ad3d26..e44b196b7 100644 --- a/src/macosx/SerialPortMACOSX.cxx +++ b/src/macosx/SerialPortMACOSX.cxx @@ -39,6 +39,7 @@ SerialPortMACOSX::SerialPortMACOSX() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SerialPortMACOSX::~SerialPortMACOSX() { + closePort(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -65,7 +66,10 @@ bool SerialPortMACOSX::openPort(const string& device) void SerialPortMACOSX::closePort() { if(myHandle) + { close(myHandle); + myHandle = 0; + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/win32/SerialPortWin32.cxx b/src/win32/SerialPortWin32.cxx index 848ad3180..43f72b680 100644 --- a/src/win32/SerialPortWin32.cxx +++ b/src/win32/SerialPortWin32.cxx @@ -49,7 +49,10 @@ bool SerialPortWin32::openPort(const string& device) FillMemory(&dcb, sizeof(dcb), 0); dcb.DCBlength = sizeof(dcb); if(!BuildCommDCB("19200,n,8,1", &dcb)) + { + closePort() return false; + } memset(&dcb, 0, sizeof(DCB)); dcb.BaudRate = CBR_19200;