Some cleanup of the BrowserDialog class, abstracting the file-specific

stuff into a new FileListWidget class (which will eventually be extended
to LauncherDialog as well).

The BrowserDialog now accepts different modes (file load and save, and
directories), and will be used in all places in the code where the
user currently has to type in filenames, etc.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2719 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2013-05-06 15:32:03 +00:00
parent f0ab412b33
commit 76294492b4
14 changed files with 324 additions and 178 deletions

View File

@ -25,11 +25,10 @@
#include "Dialog.hxx"
#include "DialogContainer.hxx"
#include "FSNode.hxx"
#include "GameList.hxx"
#include "GuiObject.hxx"
#include "OSystem.hxx"
#include "EditTextWidget.hxx"
#include "StringListWidget.hxx"
#include "FileListWidget.hxx"
#include "Widget.hxx"
#include "BrowserDialog.hxx"
@ -44,11 +43,7 @@
BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
int max_w, int max_h)
: Dialog(&boss->instance(), &boss->parent(), 0, 0, 0, 0),
CommandSender(boss),
_fileList(NULL),
_currentPath(NULL),
_nodeList(NULL),
_mode(FilesystemNode::kListAll)
CommandSender(boss)
{
// Set real dimensions
_w = max_w;
@ -74,19 +69,21 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
// Add file list
ypos += lineHeight + 4;
_fileList = new StringListWidget(this, font, xpos, ypos, _w - 2 * xpos,
_h - selectHeight - buttonHeight - ypos - 20);
_fileList = new FileListWidget(this, font, xpos, ypos, _w - 2 * xpos,
_h - selectHeight - buttonHeight - ypos - 20);
_fileList->setEditable(false);
addFocusWidget(_fileList);
// Add currently selected item
ypos += _fileList->getHeight() + 4;
_type = new StaticTextWidget(this, font, xpos, ypos,
font.getStringWidth("File: "), lineHeight,
"", kTextAlignCenter);
font.getStringWidth("Name: "), lineHeight,
"Name:", kTextAlignCenter);
_selected = new EditTextWidget(this, font, xpos + _type->getWidth(), ypos,
_w - _type->getWidth() - 2 * xpos, lineHeight, "");
_selected->setEditable(false);
addFocusWidget(_selected);
// Buttons
_goUpButton = new ButtonWidget(this, font, 10, _h - buttonHeight - 10,
@ -117,88 +114,74 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
addFocusWidget(b);
addOKWidget(b);
#endif
addFocusWidget(_fileList);
// Create a list of directory nodes
_nodeList = new GameList();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BrowserDialog::~BrowserDialog()
{
delete _nodeList;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::show(const string& title, const string& startpath,
FilesystemNode::ListMode mode, int cmd)
BrowserDialog::ListMode mode, int cmd)
{
_title->setLabel(title);
_cmd = cmd;
_mode = mode;
// If no node has been set, or the last used one is now invalid,
// go back to the users home dir.
_node = FilesystemNode(startpath);
if(!_node.exists())
_node = FilesystemNode("~");
switch(_mode)
{
case FileLoad:
_fileList->setFileListMode(FilesystemNode::kListAll);
_selected->setEditable(false);
break;
// Generally, we always want a directory listing
if(!_node.isDirectory() && _node.hasParent())
_node = _node.getParent();
case FileSave:
_fileList->setFileListMode(FilesystemNode::kListAll);
_selected->setEditable(false); // FIXME - disable user input for now
break;
// Alway refresh file list
updateListing();
case Directories:
_fileList->setFileListMode(FilesystemNode::kListDirectoriesOnly);
_selected->setEditable(false);
break;
}
// Set start path
_fileList->setLocation(FilesystemNode(startpath));
updateUI();
// Finally, open the dialog after it has been fully updated
open();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::updateListing()
const FilesystemNode& BrowserDialog::getResult() const
{
if(!_node.isDirectory())
return;
if(_mode == FileLoad || _mode == FileSave)
return _fileList->selected();
else
return _fileList->currentDir();
}
// Start with empty list
_nodeList->clear();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::updateUI()
{
// Only hilite the 'up' button if there's a parent directory
_goUpButton->setEnabled(_fileList->currentDir().hasParent());
// Update the path display
_currentPath->setLabel(_node.getShortPath());
_currentPath->setLabel(_fileList->currentDir().getShortPath());
// Read in the data from the file system
FSList content;
content.reserve(2048);
_node.getChildren(content, _mode);
// Enable/disable OK button based on current mode
bool enable = _mode == Directories || !_fileList->selected().isDirectory();
_okWidget->setEnabled(enable);
// Add '[..]' to indicate previous folder
if(_node.hasParent())
_nodeList->appendGame(" [..]", _node.getParent().getPath(), "", true);
// Now add the directory entries
for(unsigned int idx = 0; idx < content.size(); idx++)
{
string name = content[idx].getName();
bool isDir = content[idx].isDirectory();
if(isDir)
name = " [" + name + "]";
_nodeList->appendGame(name, content[idx].getPath(), "", isDir);
}
_nodeList->sortByName();
// Now fill the list widget with the contents of the GameList
StringList l;
for (int i = 0; i < (int) _nodeList->size(); ++i)
l.push_back(_nodeList->name(i));
_fileList->setList(l);
if(_fileList->getList().size() > 0)
_fileList->setSelected(0);
// Only hilite the 'up' button if there's a parent directory
_goUpButton->setEnabled(_node.hasParent());
if(!_fileList->selected().isDirectory())
_selected->setEditString(_fileList->getSelectedString());
else
_selected->setEditString("");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -215,37 +198,17 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
break;
case kGoUpCmd:
case ListWidget::kPrevDirCmd:
_node = _node.getParent();
updateListing();
_fileList->selectParent();
break;
case kBaseDirCmd:
_node = FilesystemNode(instance().baseDir());
updateListing();
_fileList->setLocation(FilesystemNode(instance().baseDir()));
break;
case ListWidget::kSelectionChangedCmd:
{
int item = _fileList->getSelected();
if(item >= 0)
{
_selected->setEditString(_fileList->getSelectedString());
}
case FileListWidget::ItemChanged:
case FileListWidget::ItemActivated:
updateUI();
break;
}
case ListWidget::kActivatedCmd:
case ListWidget::kDoubleClickedCmd:
{
int item = _fileList->getSelected();
if(item >= 0)
{
_node = FilesystemNode(_nodeList->path(item));
updateListing();
}
break;
}
default:
Dialog::handleCommand(sender, cmd, data, 0);

View File

@ -26,9 +26,8 @@
class GuiObject;
class ButtonWidget;
class EditTextWidget;
class FileListWidget;
class StaticTextWidget;
class StringListWidget;
class GameList;
#include "Dialog.hxx"
#include "Command.hxx"
@ -39,24 +38,25 @@ class BrowserDialog : public Dialog, public CommandSender
{
public:
enum ListMode {
FileLoad, // File selector, no input from user
FileSave, // File selector, filename changable by user
DirectoryOpen // Directories only, no input from user
FileLoad, // File selector, no input from user
FileSave, // File selector, filename changable by user
Directories // Directories only, no input from user
};
public:
BrowserDialog(GuiObject* boss, const GUI::Font& font, int max_w, int max_h);
virtual ~BrowserDialog();
const FilesystemNode& getResult() { return _node; }
/** Place the browser window onscreen, using the given attributes */
void show(const string& title, const string& startpath,
FilesystemNode::ListMode mode, int cmd);
BrowserDialog::ListMode mode, int cmd);
/** Get resulting file node (called after receiving kChooseCmd */
const FilesystemNode& getResult() const;
protected:
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
void updateListing();
void updateUI();
private:
enum {
@ -67,7 +67,7 @@ class BrowserDialog : public Dialog, public CommandSender
int _cmd;
StringListWidget* _fileList;
FileListWidget* _fileList;
StaticTextWidget* _currentPath;
StaticTextWidget* _title;
StaticTextWidget* _type;
@ -75,10 +75,7 @@ class BrowserDialog : public Dialog, public CommandSender
ButtonWidget* _goUpButton;
ButtonWidget* _basedirButton;
FilesystemNode _node;
GameList* _nodeList;
FilesystemNode::ListMode _mode;
BrowserDialog::ListMode _mode;
};
#endif

135
src/gui/FileListWidget.cxx Normal file
View File

@ -0,0 +1,135 @@
//============================================================================
//
// 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-2013 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#include "ScrollBarWidget.hxx"
#include "FileListWidget.hxx"
#include "bspf.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileListWidget::FileListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h)
: StringListWidget(boss, font, x, y, w, h),
_fsmode(FilesystemNode::kListAll)
{
// This widget is special, in that it catches signals and redirects them
setTarget(this);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileListWidget::~FileListWidget()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FileListWidget::setLocation(const FilesystemNode& node, string select)
{
_node = node;
if(!_node.exists())
_node = FilesystemNode("~");
// Generally, we always want a directory listing
if(!_node.isDirectory() && _node.hasParent())
{
select = _node.getName();
_node = _node.getParent();
}
// Start with empty list
_gameList.clear();
// Read in the data from the file system
FSList content;
content.reserve(512);
_node.getChildren(content, _fsmode);
// Add '[..]' to indicate previous folder
if(_node.hasParent())
_gameList.appendGame(" [..]", _node.getParent().getPath(), "", true);
// Now add the directory entries
for(unsigned int idx = 0; idx < content.size(); idx++)
{
string name = content[idx].getName();
bool isDir = content[idx].isDirectory();
if(isDir)
name = " [" + name + "]";
_gameList.appendGame(name, content[idx].getPath(), "", isDir);
}
_gameList.sortByName();
// Now fill the list widget with the contents of the GameList
StringList l;
for (int i = 0; i < (int) _gameList.size(); ++i)
l.push_back(_gameList.name(i));
setList(l);
setSelected(select);
ListWidget::recalc();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FileListWidget::selectParent()
{
if(_node.hasParent())
{
const string& curr = " [" + _node.getName() + "]";
setLocation(_node.getParent(), curr);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
switch (cmd)
{
case ListWidget::kPrevDirCmd:
selectParent();
break;
case ListWidget::kSelectionChangedCmd:
cmd = ItemChanged;
_selected = FilesystemNode(_gameList.path(data));
break;
case ListWidget::kActivatedCmd:
case ListWidget::kDoubleClickedCmd:
cmd = ItemActivated;
if(_gameList.isDir(data))
{
if(_gameList.name(data) == " [..]")
selectParent();
else
setLocation(FilesystemNode(_gameList.path(data)));
}
break;
default:
// If we don't know about the command, send it to the parent and exit
StringListWidget::handleCommand(sender, cmd, data, id);
return;
}
// Send command to boss, then revert to target 'this'
setTarget(_boss);
sendCommand(cmd, data, id);
setTarget(this);
}

View File

@ -0,0 +1,77 @@
//============================================================================
//
// 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-2013 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#ifndef FILE_LIST_WIDGET_HXX
#define FILE_LIST_WIDGET_HXX
#include "Command.hxx"
#include "FSNode.hxx"
#include "GameList.hxx"
#include "StringListWidget.hxx"
/**
Provides an encapsulation of a file listing, allowing to descend into
directories, and send signals based on whether an item is selected or
activated.
When the signals ItemChanged and ItemActivated are emitted, the caller
can query the selected() and/or currentDir() methods to determine the
current state.
Note that for the current implementation, the ItemActivated signal is
not sent when activating a directory (instead the code descends into
the directory). This may be changed in a future revision.
*/
class FileListWidget : public StringListWidget
{
public:
enum {
ItemChanged = 'FLic', // Entry in the list is changed (single-click, etc)
ItemActivated = 'FLac' // Entry in the list is activated (double-click, etc)
};
public:
FileListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h);
virtual ~FileListWidget();
/** Determines how to display files/folders */
void setFileListMode(FilesystemNode::ListMode mode) { _fsmode = mode; }
/** Set current location (file or directory) */
void setLocation(const FilesystemNode& node, string select = "");
/** Select parent directory (if applicable) */
void selectParent();
/** Gets current node(s) */
const FilesystemNode& selected() const { return _selected; }
const FilesystemNode& currentDir() const { return _node; }
protected:
void handleCommand(CommandSender* sender, int cmd, int data, int id);
private:
FilesystemNode::ListMode _fsmode;
FilesystemNode _node, _selected;
GameList _gameList;
};
#endif

View File

@ -286,42 +286,42 @@ void FileSnapDialog::handleCommand(CommandSender* sender, int cmd,
case kChooseRomDirCmd:
myBrowser->show("Select ROM directory:", myRomPath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kRomDirChosenCmd);
BrowserDialog::Directories, kRomDirChosenCmd);
break;
case kChooseSnapSaveDirCmd:
myBrowser->show("Select snapshot save directory:", mySnapSavePath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kSnapSaveDirChosenCmd);
BrowserDialog::Directories, kSnapSaveDirChosenCmd);
break;
case kChooseSnapLoadDirCmd:
myBrowser->show("Select snapshot load directory:", mySnapLoadPath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kSnapLoadDirChosenCmd);
BrowserDialog::Directories, kSnapLoadDirChosenCmd);
break;
case kChooseCheatFileCmd:
myBrowser->show("Select cheat file:", myCheatFile->getEditString(),
FilesystemNode::kListAll, kCheatFileChosenCmd);
BrowserDialog::FileLoad, kCheatFileChosenCmd);
break;
case kChoosePaletteFileCmd:
myBrowser->show("Select palette file:", myPaletteFile->getEditString(),
FilesystemNode::kListAll, kPaletteFileChosenCmd);
BrowserDialog::FileLoad, kPaletteFileChosenCmd);
break;
case kChoosePropsFileCmd:
myBrowser->show("Select properties file:", myPropsFile->getEditString(),
FilesystemNode::kListAll, kPropsFileChosenCmd);
BrowserDialog::FileLoad, kPropsFileChosenCmd);
break;
case kChooseNVRamDirCmd:
myBrowser->show("Select NVRAM directory:", myNVRamPath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kNVRamDirChosenCmd);
BrowserDialog::Directories, kNVRamDirChosenCmd);
break;
case kChooseStateDirCmd:
myBrowser->show("Select state directory:", myStatePath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kStateDirChosenCmd);
BrowserDialog::Directories, kStateDirChosenCmd);
break;
case kRomDirChosenCmd:

View File

@ -310,32 +310,9 @@ void LauncherDialog::updateListing(const string& nameToSelect)
myRomCount->setLabel(buf.str());
// Restore last selection
int selected = -1;
if(!myList->getList().isEmpty())
{
const string& find =
nameToSelect == "" ? instance().settings().getString("lastrom") : nameToSelect;
if(find == "")
selected = 0;
else
{
unsigned int itemToSelect = 0;
StringList::const_iterator iter;
for(iter = myList->getList().begin(); iter != myList->getList().end();
++iter, ++itemToSelect)
{
if(find == *iter)
{
selected = itemToSelect;
break;
}
}
if(itemToSelect > myList->getList().size())
selected = 0;
}
}
myList->setSelected(selected);
const string& find =
nameToSelect == "" ? instance().settings().getString("lastrom") : nameToSelect;
myList->setSelected(find);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -586,7 +563,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
myRomDir = new BrowserDialog(this, instance().font(), _w, _h);
myRomDir->show("Select ROM directory:", "~",
FilesystemNode::kListDirectoriesOnly, kStartupRomDirChosenCmd);
BrowserDialog::Directories, kStartupRomDirChosenCmd);
break;
case kStartupRomDirChosenCmd:

View File

@ -122,11 +122,9 @@ class LauncherDialog : public Dialog
BrowserDialog* myRomDir;
int mySelectedItem;
int myRomInfoSize;
FilesystemNode myCurrentNode;
Common::FixedStack<string> myNodeNames;
bool myShowDirs;
StringList myRomExts;
enum {

View File

@ -88,6 +88,33 @@ void ListWidget::setSelected(int item)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ListWidget::setSelected(const string& item)
{
int selected = -1;
if(!_list.isEmpty())
{
if(item == "")
selected = 0;
else
{
uInt32 itemToSelect = 0;
StringList::const_iterator iter;
for(iter = _list.begin(); iter != _list.end(); ++iter, ++itemToSelect)
{
if(item == *iter)
{
selected = itemToSelect;
break;
}
}
if(itemToSelect > _list.size() || selected == -1)
selected = 0;
}
}
setSelected(selected);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ListWidget::setHighlighted(int item)
{

View File

@ -54,8 +54,9 @@ class ListWidget : public EditableWidget
int rows() const { return _rows; }
int currentPos() const { return _currentPos; }
int getSelected() const { return _selectedItem; }
void setSelected(int item); // Use '-1' to indicate a redraw of an empty list
int getSelected() const { return _selectedItem; }
void setSelected(int item);
void setSelected(const string& item);
int getHighlighted() const { return _highlightedItem; }
void setHighlighted(int item);

View File

@ -241,7 +241,7 @@ void OptionsDialog::handleCommand(CommandSender* sender, int cmd,
break;
case kAboutCmd:
open();
myAboutDialog->open();
break;
case kExitCmd:

View File

@ -204,7 +204,7 @@ void RomAuditDialog::handleCommand(CommandSender* sender, int cmd,
case kChooseAuditDirCmd:
myBrowser->show("Select ROM directory to audit:", myRomPath->getEditString(),
FilesystemNode::kListDirectoriesOnly, kAuditDirChosenCmd);
BrowserDialog::Directories, kAuditDirChosenCmd);
break;
case kAuditDirChosenCmd:

View File

@ -27,12 +27,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StringListWidget::StringListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h, bool hilite,
NumberingMode mode)
int x, int y, int w, int h, bool hilite)
: ListWidget(boss, font, x, y, w, h),
_numberingMode(mode),
_hilite(hilite)
{
}
@ -66,8 +63,6 @@ void StringListWidget::drawWidget(bool hilite)
// Draw the list items
for (i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++)
{
const uInt32 textColor = (_selectedItem == pos && _editMode)
? kColor : kTextColor;
const int y = _y + 2 + _fontHeight * i;
// Draw the selected item inverted, on a highlighted background.
@ -79,15 +74,6 @@ void StringListWidget::drawWidget(bool hilite)
s.frameRect(_x + 1, _y + 1 + _fontHeight * i, _w - 1, _fontHeight, kTextColorHi);
}
// If in numbering mode, we first print a number prefix
if (_numberingMode != kListNumberingOff)
{
char temp[10];
BSPF_snprintf(temp, 9, "%2d. ", (pos + _numberingMode));
buffer = temp;
s.drawString(_font, buffer, _x + 2, y, _w - 4, textColor);
}
GUI::Rect r(getEditRect());
if (_selectedItem == pos && _editMode)
{
@ -115,18 +101,10 @@ void StringListWidget::drawWidget(bool hilite)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Rect StringListWidget::getEditRect() const
{
GUI::Rect r(2, 1, _w - 2 , _fontHeight);
GUI::Rect r(2, 1, _w - 2, _fontHeight);
const int offset = (_selectedItem - _currentPos) * _fontHeight;
r.top += offset;
r.bottom += offset;
if (_numberingMode != kListNumberingOff)
{
char temp[10];
// FIXME: Assumes that all digits have the same width.
BSPF_snprintf(temp, 9, "%2d. ", (_list.size() - 1 + _numberingMode));
r.left += _font.getStringWidth(temp);
}
return r;
}

View File

@ -25,19 +25,12 @@
#include "ListWidget.hxx"
enum NumberingMode {
kListNumberingOff = -1,
kListNumberingZero = 0,
kListNumberingOne = 1
};
/** StringListWidget */
class StringListWidget : public ListWidget
{
public:
StringListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h, bool hilite = true,
NumberingMode mode = kListNumberingOff);
int x, int y, int w, int h, bool hilite = true);
virtual ~StringListWidget();
void setList(const StringList& list);
@ -47,7 +40,6 @@ class StringListWidget : public ListWidget
GUI::Rect getEditRect() const;
protected:
NumberingMode _numberingMode;
bool _hilite;
};

View File

@ -13,6 +13,7 @@ MODULE_OBJS := \
src/gui/EditableWidget.o \
src/gui/EditTextWidget.o \
src/gui/EventMappingWidget.o \
src/gui/FileListWidget.o \
src/gui/FileSnapDialog.o \
src/gui/Font.o \
src/gui/GameInfoDialog.o \