diff --git a/src/emucore/FSNode.cxx b/src/emucore/FSNode.cxx index f2e5e477a..6be4078b0 100644 --- a/src/emucore/FSNode.cxx +++ b/src/emucore/FSNode.cxx @@ -76,9 +76,62 @@ bool FilesystemNode::exists() const return _realNode ? _realNode->exists() : false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode, + const NameFilter& filter, + bool includeParentDirectory) const +{ + if(getChildren(fslist, mode, filter, includeParentDirectory)) + { + // Sort only once at the end + #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 + for(auto& i : fslist) + { + if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip")) + { + FilesystemNodeZIP zipNode(i.getPath()); + + i.setName(zipNode.getName()); + } + } + #endif + + std::sort(fslist.begin(), fslist.end(), + [](const FilesystemNode& node1, const FilesystemNode& node2) + { + if(node1.isDirectory() != node2.isDirectory()) + return node1.isDirectory(); + else + return BSPF::compareIgnoreCase(node1.getName(), node2.getName()) < 0; + } + ); + + #if defined(ZIP_SUPPORT) + // After sorting replace zip files with zip nodes + for(auto& i : fslist) + { + if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip")) + { + // Force ZIP c'tor to be called + AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i.getPath(), + FilesystemNodeFactory::Type::ZIP); + FilesystemNode zipNode(ptr); + i = zipNode; + } + } + #endif + return true; + } + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, const NameFilter& filter, + bool includeChildDirectories, bool includeParentDirectory) const { if (!_realNode || !_realNode->isDirectory()) @@ -90,12 +143,15 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, if (!_realNode->getChildren(tmp, mode)) return false; + // when incuding child directories, everything must be sorted once at the end + if(!includeChildDirectories) + { #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 - for (auto& i : tmp) + for(auto& i : tmp) { - if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip")) + if(BSPF::endsWithIgnoreCase(i->getPath(), ".zip")) { FilesystemNodeZIP node(i->getPath()); @@ -104,15 +160,16 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, } #endif - std::sort(tmp.begin(), tmp.end(), - [](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2) + std::sort(tmp.begin(), tmp.end(), + [](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2) { - if (node1->isDirectory() != node2->isDirectory()) + if(node1->isDirectory() != node2->isDirectory()) return node1->isDirectory(); else return BSPF::compareIgnoreCase(node1->getName(), node2->getName()) < 0; } - ); + ); + } // Add parent node, if it is valid to do so if (includeParentDirectory && hasParent()) @@ -130,21 +187,44 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, { // Force ZIP c'tor to be called AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i->getPath(), - FilesystemNodeFactory::Type::ZIP); - FilesystemNode node(ptr); - if (filter(node)) - fslist.emplace_back(node); + FilesystemNodeFactory::Type::ZIP); + FilesystemNode zipNode(ptr); + + if(filter(zipNode)) + { + if(!includeChildDirectories) + fslist.emplace_back(zipNode); + else + { + // filter by zip node but add file node + FilesystemNode node(i); + fslist.emplace_back(node); + } + } } else #endif { // Make directories stand out - if (i->isDirectory()) + if(i->isDirectory()) i->setName(" [" + i->getName() + "]"); FilesystemNode node(i); - if (filter(node)) - fslist.emplace_back(node); + + if(includeChildDirectories) + { + if(i->isDirectory()) + node.getChildren(fslist, mode, filter, includeChildDirectories, false); + else + // do not add directories in this mode + if(filter(node)) + fslist.emplace_back(node); + } + else + { + if(filter(node)) + fslist.emplace_back(node); + } } } diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx index cf76f9ac1..b7b93bd5d 100644 --- a/src/emucore/FSNode.hxx +++ b/src/emucore/FSNode.hxx @@ -114,6 +114,17 @@ class FilesystemNode */ bool exists() const; + /** + * Return a list of child nodes of this and all sub-directories. If called on a node + * that does not represent a directory, false is returned. + * + * @return true if successful, false otherwise (e.g. when the directory + * does not exist). + */ + bool getAllChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly, + const NameFilter& filter = [](const FilesystemNode&) { return true; }, + bool includeParentDirectory = true) const; + /** * Return a list of child nodes of this directory node. If called on a node * that does not represent a directory, false is returned. @@ -123,6 +134,7 @@ class FilesystemNode */ bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly, const NameFilter& filter = [](const FilesystemNode&){ return true; }, + bool includeChildDirectories = false, bool includeParentDirectory = true) const; /** @@ -273,8 +285,8 @@ class FilesystemNode string getPathWithExt(const string& ext) const; private: - AbstractFSNodePtr _realNode; explicit FilesystemNode(const AbstractFSNodePtr& realNode); + AbstractFSNodePtr _realNode; void setPath(const string& path); }; diff --git a/src/gui/ContextMenu.hxx b/src/gui/ContextMenu.hxx index f1736e517..6807635ba 100644 --- a/src/gui/ContextMenu.hxx +++ b/src/gui/ContextMenu.hxx @@ -45,6 +45,8 @@ class ContextMenu : public Dialog, public CommandSender const VariantList& items, int cmd = 0, int width = 0); ~ContextMenu() override = default; + bool isShading() const override { return false; } + /** Set the parent widget's ID */ void setID(uInt32 id) { _id = id; } diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 9994bac45..50fcbd295 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -255,12 +255,11 @@ void Dialog::render() }); } - // Dialog is still on top if e.g a dialog without title is opened - // (e.g. ContextMenu) + // A dialog is still on top if a non-shading dialog (e.g. ContextMenu) + // is opended above it. bool onTop = parent().myDialogStack.top() == this || (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this - && !parent().myDialogStack.top()->hasTitle()); - //&& typeid(*parent().myDialogStack.top()) == typeid(ContextMenu)) + && !parent().myDialogStack.top()->isShading()); if(!onTop) { diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx index 7935f1c95..a6fd7259b 100644 --- a/src/gui/Dialog.hxx +++ b/src/gui/Dialog.hxx @@ -94,6 +94,8 @@ class Dialog : public GuiObject void setTitle(const string& title); bool hasTitle() { return !_title.empty(); } + virtual bool isShading() const { return true; } + /** Determine the maximum width/height of a dialog based on the minimum allowable bounds, also taking into account the current window size. diff --git a/src/gui/DialogContainer.cxx b/src/gui/DialogContainer.cxx index 273b713c8..16f32536a 100644 --- a/src/gui/DialogContainer.cxx +++ b/src/gui/DialogContainer.cxx @@ -171,7 +171,7 @@ void DialogContainer::removeDialog() { if(!myDialogStack.empty()) { - cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl; + //cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl; myDialogStack.pop(); // Inform the frame buffer that it has to render all surfaces diff --git a/src/gui/FileListWidget.cxx b/src/gui/FileListWidget.cxx index cb3b0f5c9..42d497d92 100644 --- a/src/gui/FileListWidget.cxx +++ b/src/gui/FileListWidget.cxx @@ -20,6 +20,7 @@ #include "ScrollBarWidget.hxx" #include "FileListWidget.hxx" #include "TimerManager.hxx" +#include "ProgressDialog.hxx" #include "bspf.hxx" @@ -72,22 +73,37 @@ void FileListWidget::setDirectory(const FilesystemNode& node, void FileListWidget::setLocation(const FilesystemNode& node, const string& select) { + progress().resetProgress(); + progress().open(); + _node = node; // Read in the data from the file system (start with an empty list) _fileList.clear(); - _fileList.reserve(512); - _node.getChildren(_fileList, _fsmode, _filter); + + if(_includeSubDirs) + { + // Actually this could become HUGE + _fileList.reserve(0x2000); + _node.getAllChildren(_fileList, _fsmode, _filter); + } + else + { + _fileList.reserve(0x200); + _node.getChildren(_fileList, _fsmode, _filter); + } // Now fill the list widget with the names from the file list StringList l; - for(const auto& file: _fileList) + for(const auto& file : _fileList) l.push_back(file.getName()); setList(l); setSelected(select); ListWidget::recalc(); + + progress().close(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -114,6 +130,21 @@ void FileListWidget::reload() } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ProgressDialog& FileListWidget::progress() +{ + if(myProgressDialog == nullptr) + myProgressDialog = make_unique(this, _font, "", false); + + return *myProgressDialog; +} + +void FileListWidget::incProgress() +{ + if(_includeSubDirs) + progress().incProgress(); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FileListWidget::handleText(char text) { @@ -202,3 +233,5 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt64 FileListWidget::_QUICK_SELECT_DELAY = 300; + +unique_ptr FileListWidget::myProgressDialog{nullptr}; diff --git a/src/gui/FileListWidget.hxx b/src/gui/FileListWidget.hxx index 8b60fac44..b5ba5bf7e 100644 --- a/src/gui/FileListWidget.hxx +++ b/src/gui/FileListWidget.hxx @@ -19,6 +19,7 @@ #define FILE_LIST_WIDGET_HXX class CommandSender; +class ProgressDialog; #include "FSNode.hxx" #include "Stack.hxx" @@ -59,12 +60,16 @@ class FileListWidget : public StringListWidget _filter = filter; } + // When enabled, all subdirectories will be searched too. + void setIncludeSubDirs(bool enable) { _includeSubDirs = enable; } + /** Set initial directory, and optionally select the given item. - @param node The directory to display. If this is a file, its parent - will instead be used, and the file will be selected - @param select An optional entry to select (if applicable) + @param node The directory to display. If this is a file, its parent + will instead be used, and the file will be selected + @param select An optional entry to select (if applicable) + @param recursive Recursively list sub-directories too */ void setDirectory(const FilesystemNode& node, const string& select = EmptyString); @@ -84,6 +89,12 @@ class FileListWidget : public StringListWidget static void setQuickSelectDelay(uInt64 time) { _QUICK_SELECT_DELAY = time; } + ProgressDialog& progress(); + void incProgress(); + + protected: + static unique_ptr myProgressDialog; + private: /** Very similar to setDirectory(), but also updates the history */ void setLocation(const FilesystemNode& node, const string& select); @@ -99,6 +110,7 @@ class FileListWidget : public StringListWidget FilesystemNode::NameFilter _filter; FilesystemNode _node; FSList _fileList; + bool _includeSubDirs{false}; Common::FixedStack _history; uInt32 _selected{0}; diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index f2b80c7d2..c1e7d1328 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -15,6 +15,9 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ +// TODO: +// - abort current file list reload when typing + #include "bspf.hxx" #include "Bankswitch.hxx" #include "BrowserDialog.hxx" @@ -29,6 +32,7 @@ #include "GlobalPropsDialog.hxx" #include "StellaSettingsDialog.hxx" #include "WhatsNewDialog.hxx" +#include "ProgressDialog.hxx" #include "MessageBox.hxx" #include "ToolTip.hxx" #include "OSystem.hxx" @@ -73,25 +77,71 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = myUseMinimalUI ? lineHeight - VGAP * 2: lineHeight * 1.25, buttonWidth = (_w - 2 * HBORDER - BUTTON_GAP * (4 - 1)); - int xpos = HBORDER, ypos = VBORDER, lwidth = 0, lwidth2 = 0; + int xpos = HBORDER, ypos = VBORDER; WidgetArray wid; - string lblRom = "Select a ROM from the list" + ELLIPSIS; + string lblSelect = "Select a ROM from the list" + ELLIPSIS; + string lblAllFiles = "Show all files"; const string& lblFilter = "Filter"; - const string& lblAllFiles = "Show all files"; - const string& lblFound = "XXXX items found"; + string lblSubDirs = "Incl. subdirectories"; + string lblFound = "12345 items found"; tooltip().setFont(font); - lwidth = font.getStringWidth(lblRom); - lwidth2 = font.getStringWidth(lblAllFiles) + CheckboxWidget::boxSize(font); - int lwidth3 = font.getStringWidth(lblFilter); - int lwidth4 = font.getStringWidth(lblFound); + int lwSelect = font.getStringWidth(lblSelect); + int cwAllFiles = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font); + int lwFilter = font.getStringWidth(lblFilter); + int cwSubDirs = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font); + int lwFound = font.getStringWidth(lblFound); + int wTotal = HBORDER * 2 + lwSelect + cwAllFiles + lwFilter + cwSubDirs + lwFound + + EditTextWidget::calcWidth(font, "123456") + LBL_GAP * 7; + bool noSelect = false; - if(w < HBORDER * 2 + lwidth + lwidth2 + lwidth3 + lwidth4 + fontWidth * 6 + LBL_GAP * 8) + if(w < wTotal) { // make sure there is space for at least 6 characters in the filter field - lblRom = "Select a ROM" + ELLIPSIS; - lwidth = font.getStringWidth(lblRom); + lblSelect = "Select a ROM" + ELLIPSIS; + int lwSelectShort = font.getStringWidth(lblSelect); + + wTotal -= lwSelect - lwSelectShort; + lwSelect = lwSelectShort; + } + if(w < wTotal) + { + // make sure there is space for at least 6 characters in the filter field + lblSubDirs = "Subdir."; + int cwSubDirsShort = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font); + + wTotal -= cwSubDirs - cwSubDirsShort; + cwSubDirs = cwSubDirsShort; + } + if(w < wTotal) + { + // make sure there is space for at least 6 characters in the filter field + lblAllFiles = "All files"; + int cwAllFilesShort = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font); + + wTotal -= cwAllFiles - cwAllFilesShort; + cwAllFiles = cwAllFilesShort; + } + if(w < wTotal) + { + // make sure there is space for at least 6 characters in the filter field + lblFound = "12345 found"; + int lwFoundShort = font.getStringWidth(lblFound); + + wTotal -= lwFound - lwFoundShort; + lwFound = lwFoundShort; + myShortCount = true; + } + if(w < wTotal) + { + // make sure there is space for at least 6 characters in the filter field + lblSelect = ""; + int lwSelectShort = font.getStringWidth(lblSelect); + + wTotal -= lwSelect - lwSelectShort; + lwSelect = lwSelectShort; + noSelect = true; } if(myUseMinimalUI) @@ -108,31 +158,51 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, } // Show the header - new StaticTextWidget(this, font, xpos, ypos, lblRom); + new StaticTextWidget(this, font, xpos, ypos, lblSelect); // Shop the files counter - xpos = _w - HBORDER - lwidth4; + xpos = _w - HBORDER - lwFound; myRomCount = new StaticTextWidget(this, font, xpos, ypos, - lwidth4, fontHeight, + lwFound, fontHeight, "", TextAlign::Right); // Add filter that can narrow the results shown in the listing // It has to fit between both labels if(!myUseMinimalUI && w >= 640) { - int fwidth = std::min(15 * fontWidth, xpos - lwidth3 - lwidth2 - lwidth - HBORDER - LBL_GAP * 8); + int fwFilter = std::min(EditTextWidget::calcWidth(font, "123456789012345"), + xpos - cwSubDirs - lwFilter - cwAllFiles + - lwSelect - HBORDER - LBL_GAP * (noSelect ? 5 : 7)); + + // 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."; + mySubDirs->setToolTip(tip.str()); + // Show the filter input field - xpos -= fwidth + LBL_GAP; - myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwidth, lineHeight, ""); - myPattern->setToolTip("Enter filter text to reduce file list."); + xpos -= fwFilter + LBL_GAP; + myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwFilter, lineHeight, ""); + myPattern->setToolTip("Enter filter text to reduce file list.\n" + "Use '*' and '?' as wildcards."); + // Show the "Filter" label - xpos -= lwidth3 + LBL_GAP; + xpos -= lwFilter + LBL_GAP; new StaticTextWidget(this, font, xpos, ypos, lblFilter); + // Show the checkbox for all files - xpos -= lwidth2 + LBL_GAP * 3; + if(noSelect) + xpos = HBORDER; + else + xpos -= cwAllFiles + LBL_GAP * 2; myAllFiles = new CheckboxWidget(this, font, xpos, ypos, lblAllFiles, kAllfilesCmd); myAllFiles->setToolTip("Uncheck to show ROM files only."); + wid.push_back(myAllFiles); wid.push_back(myPattern); + wid.push_back(mySubDirs); } // Add list with game titles @@ -167,11 +237,11 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, // Add textfield to show current directory xpos = HBORDER; - ypos += myList->getHeight() + VGAP * 2; - lwidth = font.getStringWidth("Path") + LBL_GAP; - myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwidth, fontHeight, + ypos += myList->getHeight() + VGAP; + lwSelect = font.getStringWidth("Path") + LBL_GAP; + myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwSelect, fontHeight, "Path", TextAlign::Left); - xpos += lwidth; + xpos += lwSelect; myDir = new EditTextWidget(this, font, xpos, ypos, _w - xpos - HBORDER, lineHeight, ""); myDir->setEditable(false, true); myDir->clearFlags(Widget::FLAG_RETAIN_FOCUS); @@ -236,9 +306,13 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, myGlobalProps = make_unique(this, myUseMinimalUI ? osystem.frameBuffer().launcherFont() : osystem.frameBuffer().font()); + // since we cannot know how many files there are, use are really high value here + myList->progress().setRange(0, 50000, 5); + myList->progress().setMessage(" Filtering files" + ELLIPSIS + " "); + // Do we show only ROMs or all files? bool onlyROMs = instance().settings().getBool("launcherroms"); - showOnlyROMs(onlyROMs); + if(myAllFiles) myAllFiles->setState(!onlyROMs); } @@ -341,7 +415,7 @@ void LauncherDialog::updateUI() // Indicate how many files were found ostringstream buf; - buf << (myList->getList().size() - 1) << " items found"; + buf << (myList->getList().size() - 1) << (myShortCount ? " found" : " items found"); myRomCount->setLabel(buf.str()); // Update ROM info UI item @@ -422,6 +496,7 @@ void LauncherDialog::applyFiltering() { myList->setNameFilter( [&](const FilesystemNode& node) { + myList->incProgress(); if(!node.isDirectory()) { // Do we want to show only ROMs or all files? @@ -664,6 +739,11 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd, reload(); break; + case kSubDirsCmd: + myList->setIncludeSubDirs(mySubDirs->getState()); + reload(); + break; + case kLoadROMCmd: case FileListWidget::ItemActivated: saveConfig(); @@ -689,9 +769,15 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd, break; case EditableWidget::kChangedCmd: + { + bool subAllowed = myPattern->getText().length() >= MIN_SUBDIRS_CHARS; + + mySubDirs->setEnabled(subAllowed); + myList->setIncludeSubDirs(mySubDirs->getState() && subAllowed); applyFiltering(); // pattern matching taken care of directly in this method reload(); break; + } case kQuitCmd: saveConfig(); diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index 205fd090d..44601d48d 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -100,6 +100,7 @@ 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; @@ -169,19 +170,22 @@ class LauncherDialog : public Dialog // automatically sized font for ROM info viewer unique_ptr myROMInfoFont; - ButtonWidget* myStartButton{nullptr}; - ButtonWidget* myPrevDirButton{nullptr}; - ButtonWidget* myOptionsButton{nullptr}; - ButtonWidget* myQuitButton{nullptr}; + CheckboxWidget* myAllFiles{nullptr}; + EditTextWidget* myPattern{nullptr}; + CheckboxWidget* mySubDirs{nullptr}; + StaticTextWidget* myRomCount{nullptr}; FileListWidget* myList{nullptr}; + StaticTextWidget* myDirLabel{nullptr}; EditTextWidget* myDir{nullptr}; - StaticTextWidget* myRomCount{nullptr}; - EditTextWidget* myPattern{nullptr}; - CheckboxWidget* myAllFiles{nullptr}; - RomInfoWidget* myRomInfoWidget{nullptr}; + ButtonWidget* myStartButton{nullptr}; + ButtonWidget* myPrevDirButton{nullptr}; + ButtonWidget* myOptionsButton{nullptr}; + ButtonWidget* myQuitButton{nullptr}; + + RomInfoWidget* myRomInfoWidget{nullptr}; std::unordered_map myMD5List; int mySelectedItem{0}; @@ -189,9 +193,11 @@ class LauncherDialog : public Dialog bool myShowOnlyROMs{false}; bool myUseMinimalUI{false}; bool myEventHandled{false}; + bool myShortCount{false}; enum { kAllfilesCmd = 'lalf', // show all files (or ROMs only) + kSubDirsCmd = 'lred', kPrevDirCmd = 'PRVD', kOptionsCmd = 'OPTI', kQuitCmd = 'QUIT' diff --git a/src/gui/ProgressDialog.cxx b/src/gui/ProgressDialog.cxx index 04bc2adae..026e0df82 100644 --- a/src/gui/ProgressDialog.cxx +++ b/src/gui/ProgressDialog.cxx @@ -26,8 +26,9 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font, - const string& message) - : Dialog(boss->instance(), boss->parent()) + const string& message, bool openDialog) + : Dialog(boss->instance(), boss->parent()), + myFont(font) { const int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), @@ -52,13 +53,23 @@ ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font, mySlider->setMinValue(1); mySlider->setMaxValue(100); - open(); + if(openDialog) + open(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ProgressDialog::setMessage(const string& message) { + const int fontWidth = myFont.getMaxCharWidth(), + HBORDER = fontWidth * 1.25; + const int lwidth = myFont.getStringWidth(message); + + // Recalculate real dimensions + _w = HBORDER * 2 + lwidth; + + myMessage->setWidth(lwidth); myMessage->setLabel(message); + mySlider->setWidth(lwidth); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -68,17 +79,25 @@ void ProgressDialog::setRange(int start, int finish, int step) myFinish = finish; myStep = int((step / 100.0) * (myFinish - myStart + 1)); - mySlider->setMinValue(myStart); + mySlider->setMinValue(myStart + myStep); mySlider->setMaxValue(myFinish); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ProgressDialog::resetProgress() +{ + myProgress = myStepProgress = 0; + mySlider->setValue(0); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ProgressDialog::setProgress(int progress) { // Only increase the progress bar if we have arrived at a new step - if(progress - mySlider->getValue() > myStep) + if(progress - myStepProgress >= myStep) { - mySlider->setValue(progress); + myStepProgress = 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 @@ -88,3 +107,9 @@ void ProgressDialog::setProgress(int progress) instance().frameBuffer().update(); } } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ProgressDialog::incProgress() +{ + setProgress(++myProgress); +} diff --git a/src/gui/ProgressDialog.hxx b/src/gui/ProgressDialog.hxx index 0ec8731b5..1b36b9cef 100644 --- a/src/gui/ProgressDialog.hxx +++ b/src/gui/ProgressDialog.hxx @@ -23,23 +23,29 @@ class StaticTextWidget; class SliderWidget; #include "bspf.hxx" +#include "Dialog.hxx" class ProgressDialog : public Dialog { public: ProgressDialog(GuiObject* boss, const GUI::Font& font, - const string& message); + const string& message, bool openDialog = true); ~ProgressDialog() override = default; void setMessage(const string& message); void setRange(int begin, int end, int step); + void resetProgress(); void setProgress(int progress); + void incProgress(); private: + const GUI::Font& myFont; StaticTextWidget* myMessage{nullptr}; SliderWidget* mySlider{nullptr}; int myStart{0}, myFinish{0}, myStep{0}; + int myProgress{0}; + int myStepProgress{0}; private: // Following constructors and assignment operators not supported diff --git a/src/gui/ToolTip.cxx b/src/gui/ToolTip.cxx index 6e6ef9fc9..a887e0096 100644 --- a/src/gui/ToolTip.cxx +++ b/src/gui/ToolTip.cxx @@ -179,5 +179,5 @@ void ToolTip::show(const string& tip) void ToolTip::render() { if(myTipShown) - mySurface->render(), cerr << " render tooltip" << endl; + mySurface->render(); // , cerr << " render tooltip" << endl; }