diff --git a/docs/index.html b/docs/index.html
index d9080cae9..f1a9ecb25 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -3689,19 +3689,29 @@
ROM Info Viewer width at 50% , UI sized 1280x900, large launcher font:
- The 'Show all files' checkbox allows displaying files which do not
- have a valid ROM extension. The 'Filter' text box can be used to narrow down
- the results in the ROM listing. When this box is empty, all files are shown.
- Typing characters here will 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. Also you can use '*' and '?' as wildcards. E.g.
- for '(198?)*atari' only games from the 1980s made by Atari will be listed.
- When there are least three characters in the filter, the checkbox 'Incl.
- subdirectories' gets enabled. When checked, Stella will search files in all
- subdirectories too.
+ The dialog items at the top can be used to define the listed files:
+
+
+ -
+ The 'Show all files' checkbox allows displaying files which do not
+ have a valid ROM extension.
+
-
+ The 'Filter' text box can be used to narrow down the results in the
+ ROM listing. When this box is empty, all files are shown. Typing
+ characters here will 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. You also can use '*' and '?' as
+ wildcards. E.g. for '(198?)*atari' only ROMs from the 1980s made by
+ Atari will be listed.
+
-
+ If 'Incl. subdirectories' is checked, Stella will list matching files
+ from all subdirectories too.
+
+
+
diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx
index 73f09268f..cd631bc7e 100644
--- a/src/debugger/DebuggerParser.cxx
+++ b/src/debugger/DebuggerParser.cxx
@@ -1747,9 +1747,13 @@ void DebuggerParser::executeRunTo()
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "RunTo searching through " << max_iterations << " disassembled instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << "RunTo searching through " << max_iterations << " disassembled instructions"
+ << progress.ELLIPSIS;
+ progress.setMessage(buf.str());
progress.setRange(0, max_iterations, 5);
+ progress.open();
bool done = false;
do {
@@ -1763,8 +1767,8 @@ void DebuggerParser::executeRunTo()
done = (BSPF::findIgnoreCase(next, argStrings[0]) != string::npos);
}
// Update the progress bar
- progress.setProgress(count);
- } while(!done && ++count < max_iterations);
+ progress.incProgress();
+ } while(!done && ++count < max_iterations && !progress.isCancelled());
progress.close();
@@ -1789,13 +1793,15 @@ void DebuggerParser::executeRunToPc()
uInt32 count = 0;
bool done = false;
- constexpr uInt32 max_iterations = 1000000;
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "RunTo PC searching through " << max_iterations << " instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
- progress.setRange(0, max_iterations, 5);
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << " RunTo PC running" << progress.ELLIPSIS << " ";
+ progress.setMessage(buf.str());
+ progress.setRange(0, 100000, 5);
+ progress.open();
do {
debugger.step(false);
@@ -1803,8 +1809,9 @@ void DebuggerParser::executeRunToPc()
// Update romlist to point to current PC
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
done = (pcline >= 0) && (list[pcline].address == args[0]);
- progress.setProgress(count);
- } while(!done && ++count < max_iterations/*list.size()*/);
+ progress.incProgress();
+ ++count;
+ } while(!done && !progress.isCancelled());
progress.close();
if(done)
@@ -1953,20 +1960,24 @@ void DebuggerParser::executeStepwhile()
Expression* expr = YaccParser::getResult();
int ncycles = 0;
uInt32 count = 0;
- constexpr uInt32 max_iterations = 1000000;
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "stepwhile running through " << max_iterations << " disassembled instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
- progress.setRange(0, max_iterations, 5);
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << "stepwhile running through disassembled instructions"
+ << progress.ELLIPSIS;
+ progress.setMessage(buf.str());
+ progress.setRange(0, 100000, 5);
+ progress.open();
do {
ncycles += debugger.step(false);
- progress.setProgress(count);
- } while (expr->evaluate() && ++count < max_iterations);
+ progress.incProgress();
+ ++count;
+ } while (expr->evaluate() && !progress.isCancelled());
progress.close();
commandResult << "executed " << ncycles << " cycles";
diff --git a/src/emucore/FSNode.cxx b/src/emucore/FSNode.cxx
index 6be4078b0..6ff0b6482 100644
--- a/src/emucore/FSNode.cxx
+++ b/src/emucore/FSNode.cxx
@@ -79,9 +79,10 @@ bool FilesystemNode::exists() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode,
const NameFilter& filter,
- bool includeParentDirectory) const
+ bool includeParentDirectory,
+ const CancelCheck& isCancelled) const
{
- if(getChildren(fslist, mode, filter, includeParentDirectory))
+ if(getChildren(fslist, mode, filter, includeParentDirectory, true, isCancelled))
{
// Sort only once at the end
#if defined(ZIP_SUPPORT)
@@ -124,7 +125,6 @@ bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode,
#endif
return true;
}
-
return false;
}
@@ -132,7 +132,8 @@ bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode,
bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
const NameFilter& filter,
bool includeChildDirectories,
- bool includeParentDirectory) const
+ bool includeParentDirectory,
+ const CancelCheck& isCancelled) const
{
if (!_realNode || !_realNode->isDirectory())
return false;
@@ -146,6 +147,9 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
// when incuding child directories, everything must be sorted once at the end
if(!includeChildDirectories)
{
+ if(isCancelled())
+ return false;
+
#if defined(ZIP_SUPPORT)
// before sorting, replace single file ZIP archive names with contained file names
// because they are displayed using their contained file names
@@ -182,6 +186,9 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
// And now add the rest of the entries
for (const auto& i: tmp)
{
+ if(isCancelled())
+ return false;
+
#if defined(ZIP_SUPPORT)
if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
{
@@ -214,7 +221,7 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
if(includeChildDirectories)
{
if(i->isDirectory())
- node.getChildren(fslist, mode, filter, includeChildDirectories, false);
+ node.getChildren(fslist, mode, filter, includeChildDirectories, false, isCancelled);
else
// do not add directories in this mode
if(filter(node))
@@ -227,7 +234,6 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
}
}
}
-
return true;
}
diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx
index b7b93bd5d..05378d185 100644
--- a/src/emucore/FSNode.hxx
+++ b/src/emucore/FSNode.hxx
@@ -57,6 +57,7 @@ class FilesystemNode
/** Function used to filter the file listing. Returns true if the filename
should be included, else false.*/
using NameFilter = std::function;
+ using CancelCheck = std::function const;
/**
* Create a new pathless FilesystemNode. Since there's no path associated
@@ -123,7 +124,8 @@ class FilesystemNode
*/
bool getAllChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
const NameFilter& filter = [](const FilesystemNode&) { return true; },
- bool includeParentDirectory = true) const;
+ bool includeParentDirectory = true,
+ const CancelCheck& isCancelled = []() { return false; }) const;
/**
* Return a list of child nodes of this directory node. If called on a node
@@ -135,7 +137,8 @@ class FilesystemNode
bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
const NameFilter& filter = [](const FilesystemNode&){ return true; },
bool includeChildDirectories = false,
- bool includeParentDirectory = true) const;
+ bool includeParentDirectory = true,
+ const CancelCheck& isCancelled = []() { return false; }) const;
/**
* Set/get a string representation of the name of the file. This is can be
diff --git a/src/gui/FileListWidget.cxx b/src/gui/FileListWidget.cxx
index 42d497d92..be68d632f 100644
--- a/src/gui/FileListWidget.cxx
+++ b/src/gui/FileListWidget.cxx
@@ -75,6 +75,9 @@ void FileListWidget::setLocation(const FilesystemNode& node,
{
progress().resetProgress();
progress().open();
+ class FilesystemNode::CancelCheck isCancelled = []() {
+ return myProgressDialog->isCancelled();
+ };
_node = node;
@@ -85,21 +88,24 @@ void FileListWidget::setLocation(const FilesystemNode& node,
{
// Actually this could become HUGE
_fileList.reserve(0x2000);
- _node.getAllChildren(_fileList, _fsmode, _filter);
+ _node.getAllChildren(_fileList, _fsmode, _filter, true, isCancelled);
}
else
{
_fileList.reserve(0x200);
- _node.getChildren(_fileList, _fsmode, _filter);
+ _node.getChildren(_fileList, _fsmode, _filter, false, true, isCancelled);
}
- // Now fill the list widget with the names from the file list
- StringList l;
- for(const auto& file : _fileList)
- l.push_back(file.getName());
+ if(!isCancelled())
+ {
+ // Now fill the list widget with the names from the file list
+ StringList l;
+ for(const auto& file : _fileList)
+ l.push_back(file.getName());
- setList(l);
- setSelected(select);
+ setList(l);
+ setSelected(select);
+ }
ListWidget::recalc();
@@ -134,7 +140,7 @@ void FileListWidget::reload()
ProgressDialog& FileListWidget::progress()
{
if(myProgressDialog == nullptr)
- myProgressDialog = make_unique(this, _font, "", false);
+ myProgressDialog = make_unique(this, _font, "");
return *myProgressDialog;
}
diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx
index 74f08328a..87d0938b4 100644
--- a/src/gui/LauncherDialog.cxx
+++ b/src/gui/LauncherDialog.cxx
@@ -177,10 +177,8 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
// Show the subdirectories checkbox
xpos -= cwSubDirs + LBL_GAP;
mySubDirs = new CheckboxWidget(this, font, xpos, ypos, lblSubDirs, kSubDirsCmd);
- mySubDirs->setEnabled(false);
ostringstream tip;
- tip << "Search files in subdirectories too.\n"
- << "Filter must have at least " << MIN_SUBDIRS_CHARS << " chars.";
+ tip << "Search files in subdirectories too.";
mySubDirs->setToolTip(tip.str());
// Show the filter input field
@@ -780,15 +778,14 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
break;
case EditableWidget::kChangedCmd:
+ case EditableWidget::kAcceptCmd:
{
- bool subAllowed = myPattern->getText().length() >= MIN_SUBDIRS_CHARS;
- bool subDirs = subAllowed && mySubDirs->getState();
+ bool subDirs = mySubDirs->getState();
- mySubDirs->setEnabled(subAllowed);
myList->setIncludeSubDirs(subDirs);
applyFiltering(); // pattern matching taken care of directly in this method
- if(subDirs)
+ if(subDirs && cmd == EditableWidget::kChangedCmd)
{
// delay (potentially slow) subdirectories reloads until user stops typing
myReloadTime = TimerManager::getTicks() / 1000 + myList->getQuickSelectDelay();
diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx
index a415f1403..a5acc262f 100644
--- a/src/gui/LauncherDialog.hxx
+++ b/src/gui/LauncherDialog.hxx
@@ -102,7 +102,6 @@ class LauncherDialog : public Dialog
static constexpr int MIN_ROMINFO_CHARS = 30;
static constexpr int MIN_ROMINFO_ROWS = 7; // full lines
static constexpr int MIN_ROMINFO_LINES = 4; // extra lines
- static constexpr int MIN_SUBDIRS_CHARS = 3; // minimum filter chars for subdirectory search
void setPosition() override { positionAt(0); }
void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override;
diff --git a/src/gui/ProgressDialog.cxx b/src/gui/ProgressDialog.cxx
index 026e0df82..e36cf6008 100644
--- a/src/gui/ProgressDialog.cxx
+++ b/src/gui/ProgressDialog.cxx
@@ -18,6 +18,8 @@
#include "bspf.hxx"
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
+#include "EventHandler.hxx"
+#include "TimerManager.hxx"
#include "Widget.hxx"
#include "Dialog.hxx"
#include "Font.hxx"
@@ -26,7 +28,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font,
- const string& message, bool openDialog)
+ const string& message)
: Dialog(boss->instance(), boss->parent()),
myFont(font)
{
@@ -35,41 +37,56 @@ ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font,
lineHeight = font.getLineHeight(),
VBORDER = fontHeight / 2,
HBORDER = fontWidth * 1.25,
- VGAP = fontHeight / 4;
- int xpos, ypos, lwidth;
+ VGAP = fontHeight / 4,
+ buttonHeight = font.getLineHeight() * 1.25,
+ BTN_BORDER = fontWidth * 2.5,
+ buttonWidth = font.getStringWidth("Cancel") + BTN_BORDER,
+ lwidth = font.getStringWidth(message);
+
+ int xpos, ypos;
+ WidgetArray wid;
// Calculate real dimensions
- lwidth = font.getStringWidth(message);
- _w = HBORDER * 2 + lwidth;
- _h = VBORDER * 2 + lineHeight * 2 + VGAP * 2;
+ _w = HBORDER * 2 + std::max(lwidth, buttonWidth);
+ _h = VBORDER * 2 + lineHeight * 2 + buttonHeight + VGAP * 6;
xpos = HBORDER; ypos = VBORDER;
myMessage = new StaticTextWidget(this, font, xpos, ypos, lwidth, fontHeight,
message, TextAlign::Center);
myMessage->setTextColor(kTextColorEm);
- xpos = HBORDER; ypos += lineHeight + VGAP * 2;
- mySlider = new SliderWidget(this, font, xpos, ypos, lwidth, lineHeight, "", 0, 0);
+ ypos += lineHeight + VGAP * 2;
+ mySlider = new SliderWidget(this, font, xpos, ypos, lwidth, lineHeight,
+ "", 0, 0);
mySlider->setMinValue(1);
mySlider->setMaxValue(100);
- if(openDialog)
- open();
+ ypos += lineHeight + VGAP * 4;
+ ButtonWidget* b = new ButtonWidget(this, font, (_w - buttonWidth) / 2, ypos,
+ buttonWidth, buttonHeight, "Cancel",
+ Event::UICancel);
+ wid.push_back(b);
+ addCancelWidget(b);
+ addToFocusList(wid);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ProgressDialog::setMessage(const string& message)
{
const int fontWidth = myFont.getMaxCharWidth(),
- HBORDER = fontWidth * 1.25;
- const int lwidth = myFont.getStringWidth(message);
+ HBORDER = fontWidth * 1.25,
+ lwidth = myFont.getStringWidth(message),
+ BTN_BORDER = fontWidth * 2.5,
+ buttonWidth = myFont.getStringWidth("Cancel") + BTN_BORDER;
// Recalculate real dimensions
- _w = HBORDER * 2 + lwidth;
+ _w = HBORDER * 2 + std::max(lwidth, buttonWidth);
myMessage->setWidth(lwidth);
myMessage->setLabel(message);
mySlider->setWidth(lwidth);
+
+ _cancelWidget->setPos((_w - buttonWidth) / 2, _cancelWidget->getTop());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -88,6 +105,7 @@ void ProgressDialog::resetProgress()
{
myProgress = myStepProgress = 0;
mySlider->setValue(0);
+ myIsCancelled = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -100,11 +118,13 @@ void ProgressDialog::setProgress(int progress)
mySlider->setValue(progress % (myFinish - myStart + 1));
// Since this dialog is usually called in a tight loop that doesn't
- // yield, we need to manually tell the framebuffer that a redraw is
- // necessary
+ // yield, we need to manually:
+ // - tell the framebuffer that a redraw is necessary
+ // - poll the events
// This isn't really an ideal solution, since all redrawing and
// event handling is suspended until the dialog is closed
instance().frameBuffer().update();
+ instance().eventHandler().poll(TimerManager::getTicks());
}
}
@@ -113,3 +133,20 @@ void ProgressDialog::incProgress()
{
setProgress(++myProgress);
}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void ProgressDialog::handleCommand(CommandSender* sender, int cmd,
+ int data, int id)
+{
+ switch(cmd)
+ {
+ case Event::UICancel:
+ myIsCancelled = true;
+ break;
+
+ default:
+ Dialog::handleCommand(sender, cmd, data, 0);
+ break;
+ }
+}
+
diff --git a/src/gui/ProgressDialog.hxx b/src/gui/ProgressDialog.hxx
index 1b36b9cef..a62fb8cbf 100644
--- a/src/gui/ProgressDialog.hxx
+++ b/src/gui/ProgressDialog.hxx
@@ -21,6 +21,7 @@
class GuiObject;
class StaticTextWidget;
class SliderWidget;
+class ButtonWidget;
#include "bspf.hxx"
#include "Dialog.hxx"
@@ -29,7 +30,7 @@ class ProgressDialog : public Dialog
{
public:
ProgressDialog(GuiObject* boss, const GUI::Font& font,
- const string& message, bool openDialog = true);
+ const string& message = "");
~ProgressDialog() override = default;
void setMessage(const string& message);
@@ -37,6 +38,7 @@ class ProgressDialog : public Dialog
void resetProgress();
void setProgress(int progress);
void incProgress();
+ bool isCancelled() const { return myIsCancelled; }
private:
const GUI::Font& myFont;
@@ -46,6 +48,10 @@ class ProgressDialog : public Dialog
int myStart{0}, myFinish{0}, myStep{0};
int myProgress{0};
int myStepProgress{0};
+ bool myIsCancelled{false};
+
+ private:
+ void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
private:
// Following constructors and assignment operators not supported
diff --git a/src/gui/RomAuditDialog.cxx b/src/gui/RomAuditDialog.cxx
index 09c264025..a95e4fce5 100644
--- a/src/gui/RomAuditDialog.cxx
+++ b/src/gui/RomAuditDialog.cxx
@@ -119,13 +119,17 @@ void RomAuditDialog::auditRoms()
// Create a progress dialog box to show the progress of processing
// the ROMs, since this is usually a time-consuming operation
- ProgressDialog progress(this, instance().frameBuffer().font(),
- "Auditing ROM files ...");
+ ostringstream buf;
+ ProgressDialog progress(this, instance().frameBuffer().font());
+
+ buf << "Auditing ROM files" << ELLIPSIS;
+ progress.setMessage(buf.str());
progress.setRange(0, int(files.size()) - 1, 5);
+ progress.open();
Properties props;
uInt32 renamed = 0, notfound = 0;
- for(uInt32 idx = 0; idx < files.size(); ++idx)
+ for(uInt32 idx = 0; idx < files.size() && !progress.isCancelled(); ++idx)
{
string extension;
if(files[idx].isFile() &&
@@ -156,7 +160,7 @@ void RomAuditDialog::auditRoms()
}
// Update the progress bar, indicating one more ROM has been processed
- progress.setProgress(idx);
+ progress.incProgress();
}
progress.close();
diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx
index e0b9a2389..61b9296cb 100644
--- a/src/gui/Widget.cxx
+++ b/src/gui/Widget.cxx
@@ -173,6 +173,36 @@ void Widget::drawChain()
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPosX(int x)
+{
+ setPos(x, _y);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPosY(int y)
+{
+ setPos(_x, y);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPos(int x, int y)
+{
+ setPos(Common::Point(x, y));
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPos(const Common::Point& pos)
+{
+ if(pos != Common::Point(_x, _y))
+ {
+ _x = pos.x;
+ _y = pos.y;
+ // we have to redraw the whole dialog!
+ dialog().setDirty();
+ }
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::handleMouseEntered()
{
diff --git a/src/gui/Widget.hxx b/src/gui/Widget.hxx
index 64685813a..cf4637ea1 100644
--- a/src/gui/Widget.hxx
+++ b/src/gui/Widget.hxx
@@ -53,6 +53,10 @@ class Widget : public GuiObject
virtual int getTop() const { return _y; }
virtual int getRight() const { return _x + getWidth(); }
virtual int getBottom() const { return _y + getHeight(); }
+ virtual void setPosX(int x);
+ virtual void setPosY(int y);
+ virtual void setPos(int x, int y);
+ virtual void setPos(const Common::Point& pos);
virtual bool handleText(char text) { return false; }
virtual bool handleKeyDown(StellaKey key, StellaMod mod) { return false; }