Added pattern matching textbox to the ROM launcher. This works in
addition to the previously added FilterOptions (that code filtered by filename extension, while this further filters by an actual pattern). This is by request of several people in the AtariAge forums). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1658 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
|
@ -35,6 +35,10 @@
|
|||
* Made the ROM launcher filename filtering be case-insensitive. This fixes
|
||||
a bug whereby ROMs with uppercase extensions were marked as invalid.
|
||||
|
||||
* Added a pattern matching textbox to the ROM launcher, used to
|
||||
further filter the files shown in the listing. For now, this filters
|
||||
files only (directories are not filtered, and are always shown).
|
||||
|
||||
* The location of EEPROM files used for AtariVox/Savekey emulation can
|
||||
now be changed with the '-eepromdir' commandline argument as well as
|
||||
in the UI.
|
||||
|
|
|
@ -36,7 +36,8 @@ Stephen Anthony at stephena@users.sourceforge.net.
|
|||
directory named that way. Basically the same as is currently
|
||||
done for ROMs.
|
||||
|
||||
(2) Add popup text widget to dynamically select the entries shown.
|
||||
(2) Fix aliases not being followed correctly in the OSX port
|
||||
(although I suspect this isn't just an OSX issue).
|
||||
|
||||
* More support for copy and paste.
|
||||
|
||||
|
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
@ -2033,6 +2033,16 @@
|
|||
<p>ROM info viewer in 2x mode, UI sized 1400x900, large launcher font:</p>
|
||||
<img src="graphics/rominfo_2x_small.png">
|
||||
|
||||
<p>The text box in the upper right corner can be used to narrow down the
|
||||
results in the ROM listing. When this box is empty, all files are shown
|
||||
(subject to the restrictions from the filtering option, explained below).
|
||||
Typing characters here where show only those files that match that
|
||||
pattern. For example, typing 'Activision' will show only files that
|
||||
contain the word 'Activision' in their name. This is very useful for
|
||||
quickly finding a group of related ROMs. Note that the search is not
|
||||
case sensitive, so you don't need to worry about capital or lower-case
|
||||
letters.</p>
|
||||
|
||||
<p>The ROM launcher also contains a context menu, selected by clicking the
|
||||
right mouse button anywhere in the current window. This context menu
|
||||
contains the following items:</p>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id: EditableWidget.cxx,v 1.30 2009-01-03 15:44:13 stephena Exp $
|
||||
// $Id: EditableWidget.cxx,v 1.31 2009-01-24 21:44:49 stephena Exp $
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
|
@ -116,22 +116,24 @@ bool EditableWidget::handleKeyDown(int ascii, int keycode, int modifiers)
|
|||
|
||||
case 8: // backspace
|
||||
dirty = killChar(-1);
|
||||
if(dirty) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 127: // delete
|
||||
dirty = killChar(+1);
|
||||
if(dirty) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 256 + 20: // left arrow
|
||||
if(instance().eventHandler().kbdControl(modifiers))
|
||||
dirty = specialKeys(keycode);
|
||||
dirty = specialKeys(ascii, keycode);
|
||||
else if(_caretPos > 0)
|
||||
dirty = setCaretPos(_caretPos - 1);
|
||||
break;
|
||||
|
||||
case 256 + 19: // right arrow
|
||||
if(instance().eventHandler().kbdControl(modifiers))
|
||||
dirty = specialKeys(keycode);
|
||||
dirty = specialKeys(ascii, keycode);
|
||||
else if(_caretPos < (int)_editString.size())
|
||||
dirty = setCaretPos(_caretPos + 1);
|
||||
break;
|
||||
|
@ -147,7 +149,7 @@ bool EditableWidget::handleKeyDown(int ascii, int keycode, int modifiers)
|
|||
default:
|
||||
if (instance().eventHandler().kbdControl(modifiers))
|
||||
{
|
||||
dirty = specialKeys(keycode);
|
||||
dirty = specialKeys(ascii, keycode);
|
||||
}
|
||||
else if (tryInsertChar((char)ascii, _caretPos))
|
||||
{
|
||||
|
@ -247,7 +249,7 @@ bool EditableWidget::adjustOffset()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool EditableWidget::specialKeys(int keycode)
|
||||
bool EditableWidget::specialKeys(int ascii, int keycode)
|
||||
{
|
||||
bool handled = true;
|
||||
|
||||
|
@ -259,6 +261,7 @@ bool EditableWidget::specialKeys(int keycode)
|
|||
|
||||
case 'c':
|
||||
copySelectedText();
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
|
@ -267,22 +270,27 @@ bool EditableWidget::specialKeys(int keycode)
|
|||
|
||||
case 'd':
|
||||
handled = killChar(+1);
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
handled = killLine(+1);
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
handled = killLine(-1);
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
pasteSelectedText();
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
handled = killLastWord();
|
||||
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
||||
break;
|
||||
|
||||
case 256 + 20: // left arrow
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id: EditableWidget.hxx,v 1.16 2009-01-01 18:13:38 stephena Exp $
|
||||
// $Id: EditableWidget.hxx,v 1.17 2009-01-24 21:44:49 stephena Exp $
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
|
@ -68,7 +68,7 @@ class EditableWidget : public Widget, public CommandSender
|
|||
|
||||
private:
|
||||
// Line editing
|
||||
bool specialKeys(int keycode);
|
||||
bool specialKeys(int ascii, int keycode);
|
||||
bool killChar(int direction);
|
||||
bool killLine(int direction);
|
||||
bool killLastWord();
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id: LauncherDialog.cxx,v 1.103 2009-01-24 17:32:29 stephena Exp $
|
||||
// $Id: LauncherDialog.cxx,v 1.104 2009-01-24 21:44:49 stephena Exp $
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
|
@ -27,6 +27,7 @@
|
|||
#include "ContextMenu.hxx"
|
||||
#include "DialogContainer.hxx"
|
||||
#include "Dialog.hxx"
|
||||
#include "EditTextWidget.hxx"
|
||||
#include "FSNode.hxx"
|
||||
#include "GameList.hxx"
|
||||
#include "MD5.hxx"
|
||||
|
@ -63,10 +64,11 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
|
|||
{
|
||||
const GUI::Font& font = instance().launcherFont();
|
||||
|
||||
const int fontHeight = font.getFontHeight(),
|
||||
const int fontWidth = font.getMaxCharWidth(),
|
||||
fontHeight = font.getFontHeight(),
|
||||
bwidth = (_w - 2 * 10 - 8 * (4 - 1)) / 4,
|
||||
bheight = font.getLineHeight() + 4;
|
||||
int xpos = 0, ypos = 0, lwidth = 0;
|
||||
int xpos = 0, ypos = 0, lwidth = 0, lwidth2 = 0, fwidth = 0;
|
||||
WidgetArray wid;
|
||||
|
||||
// Show game name
|
||||
|
@ -75,16 +77,22 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
|
|||
new StaticTextWidget(this, font, xpos, ypos, lwidth, fontHeight,
|
||||
"Select an item from the list ...", kTextAlignLeft);
|
||||
|
||||
lwidth = font.getStringWidth("XXXX items found");
|
||||
xpos = _w - lwidth - 10;
|
||||
lwidth2 = font.getStringWidth("XXXX items found");
|
||||
xpos = _w - lwidth2 - 10;
|
||||
myRomCount = new StaticTextWidget(this, font, xpos, ypos,
|
||||
lwidth, fontHeight,
|
||||
lwidth2, fontHeight,
|
||||
"", kTextAlignRight);
|
||||
|
||||
// Add list with game titles
|
||||
xpos = 10; ypos += fontHeight + 5;
|
||||
// Add filter that can narrow the results shown in the listing
|
||||
// It has to fit between both labels
|
||||
fwidth = BSPF_min(15 * fontWidth, xpos - 20 - lwidth);
|
||||
xpos -= fwidth + 5;
|
||||
myPattern = new EditTextWidget(this, font, xpos, ypos,
|
||||
fwidth, fontHeight, "");
|
||||
|
||||
// Add list with game titles
|
||||
// Before we add the list, we need to know the size of the RomInfoWidget
|
||||
xpos = 10; ypos += fontHeight + 5;
|
||||
int romWidth = 0;
|
||||
int romSize = instance().settings().getInt("romviewer");
|
||||
if(romSize > 1 && w >= 1000 && h >= 760)
|
||||
|
@ -98,6 +106,7 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
|
|||
myList->setNumberingMode(kListNumberingOff);
|
||||
myList->setEditable(false);
|
||||
wid.push_back(myList);
|
||||
wid.push_back(myPattern); // Add after the list for tab order
|
||||
|
||||
// Add ROM info area (if enabled)
|
||||
if(romWidth > 0)
|
||||
|
@ -330,6 +339,11 @@ void LauncherDialog::loadDirListing()
|
|||
continue;
|
||||
}
|
||||
|
||||
// Skip over files that don't match the pattern in the 'pattern' textbox
|
||||
if(!isDir && myPattern->getEditString() != "" &&
|
||||
!matchPattern(name, myPattern->getEditString()))
|
||||
continue;
|
||||
|
||||
myGameList->appendGame(name, files[idx].getPath(), "", isDir);
|
||||
}
|
||||
|
||||
|
@ -389,6 +403,49 @@ void LauncherDialog::setListFilters()
|
|||
LauncherFilterDialog::parseExts(myRomExts, exts);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool LauncherDialog::matchPattern(const string& s, const string& pattern)
|
||||
{
|
||||
// This method is modelled after strcasestr, which we don't use
|
||||
// because it isn't guaranteed to be available everywhere
|
||||
// The strcasestr uses the KMP algorithm when the comparisons
|
||||
// reach a certain point, but since we'll be dealing with relatively
|
||||
// short strings, I think the overhead of building a KMP table
|
||||
// each time would be slower than the brute force method used here
|
||||
const char* haystack = s.c_str();
|
||||
const char* needle = pattern.c_str();
|
||||
|
||||
unsigned char b = tolower((unsigned char) *needle);
|
||||
|
||||
needle++;
|
||||
for (;; haystack++)
|
||||
{
|
||||
if (*haystack == '\0') /* No match */
|
||||
return false;
|
||||
|
||||
/* The first character matches */
|
||||
if (tolower ((unsigned char) *haystack) == b)
|
||||
{
|
||||
const char* rhaystack = haystack + 1;
|
||||
const char* rneedle = needle;
|
||||
|
||||
for (;; rhaystack++, rneedle++)
|
||||
{
|
||||
if (*rneedle == '\0') /* Found a match */
|
||||
return true;
|
||||
if (*rhaystack == '\0') /* No match */
|
||||
return false;
|
||||
|
||||
/* Nothing in this round */
|
||||
if (tolower ((unsigned char) *rhaystack)
|
||||
!= tolower ((unsigned char) *rneedle))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void LauncherDialog::handleKeyDown(int ascii, int keycode, int modifiers)
|
||||
{
|
||||
|
@ -502,6 +559,12 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
handleContextMenu();
|
||||
break;
|
||||
|
||||
case kEditAcceptCmd:
|
||||
case kEditChangedCmd:
|
||||
// The updateListing() method knows what to do when the text changes
|
||||
updateListing();
|
||||
break;
|
||||
|
||||
default:
|
||||
Dialog::handleCommand(sender, cmd, data, 0);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id: LauncherDialog.hxx,v 1.41 2009-01-24 17:32:29 stephena Exp $
|
||||
// $Id: LauncherDialog.hxx,v 1.42 2009-01-24 21:44:49 stephena Exp $
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
|
@ -79,6 +79,7 @@ class LauncherDialog : public Dialog
|
|||
void loadRomInfo();
|
||||
void handleContextMenu();
|
||||
void setListFilters();
|
||||
bool matchPattern(const string& s, const string& pattern);
|
||||
|
||||
private:
|
||||
ButtonWidget* myStartButton;
|
||||
|
@ -90,7 +91,7 @@ class LauncherDialog : public Dialog
|
|||
StaticTextWidget* myDirLabel;
|
||||
StaticTextWidget* myDir;
|
||||
StaticTextWidget* myRomCount;
|
||||
EditTextWidget* myFilename;
|
||||
EditTextWidget* myPattern;
|
||||
GameList* myGameList;
|
||||
|
||||
OptionsDialog* myOptions;
|
||||
|
|