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
This commit is contained in:
stephena 2013-03-31 00:10:05 +00:00
parent fbf239798d
commit ac1bccf454
20 changed files with 395 additions and 194 deletions

View File

@ -33,8 +33,8 @@ template <class T>
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<T>& 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;
}

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -72,7 +72,7 @@ class DebuggerDialog : public Dialog
kDDExitFatalCmd = 'DDer'
};
TabWidget* myTab;
TabWidget *myTab, *myRomTab;
PromptWidget* myPrompt;
TiaInfoWidget* myTiaInfo;

View File

@ -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: ");

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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<int,FBSurface*>::const_iterator iter = mySurfaceList.find(id);
map<uInt32,FBSurface*>::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<int,FBSurface*>::iterator iter;
map<uInt32,FBSurface*>::iterator iter;
for(iter = mySurfaceList.begin(); iter != mySurfaceList.end(); ++iter)
iter->second->free();
if(tiasurface)

View File

@ -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<int,FBSurface*> mySurfaceList;
map<uInt32,FBSurface*> mySurfaceList;
// Holds static strings for the remap menu (emulation and menu events)
static GraphicsMode ourGraphicsModes[GFX_NumModes];

View File

@ -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

View File

@ -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;
}

View File

@ -45,12 +45,6 @@ class Dialog : public GuiObject
{
friend class DialogContainer;
struct Focus {
Widget* focusedWidget;
WidgetArray focusList;
};
typedef Common::Array<Focus> 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<Focus> 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<TabFocus> 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

View File

@ -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

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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;