From 0b0e278459d74235c46f576d7b803f4b3467408d Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Sun, 17 Aug 2014 10:05:21 +0200 Subject: [PATCH] Deal gracefully with the selector being started on a non-existent dir If directory browsing is enabled, fall back to the closest existing parent directory. If directory browsing is disabled, show empty list. --- src/filelister.cpp | 6 ++++-- src/filelister.h | 9 ++++++++- src/selector.cpp | 19 ++++++++++++------- src/selector.h | 2 +- src/utilities.cpp | 7 +++++++ src/utilities.h | 8 ++++++++ 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/filelister.cpp b/src/filelister.cpp index bcf3987..4a1eed3 100644 --- a/src/filelister.cpp +++ b/src/filelister.cpp @@ -63,7 +63,7 @@ static void moveNames(set&& from, vector& to) to.shrink_to_fit(); } -void FileLister::browse(const string& path, bool clean) +bool FileLister::browse(const string& path, bool clean) { if (clean) { directories.clear(); @@ -79,7 +79,7 @@ void FileLister::browse(const string& path, bool clean) if (errno != ENOENT) { ERROR("Unable to open directory: %s\n", slashedPath.c_str()); } - return; + return false; } set directorySet; @@ -157,6 +157,8 @@ void FileLister::browse(const string& path, bool clean) files.clear(); moveNames(move(fileSet), files); } + + return true; } string FileLister::operator[](uint x) diff --git a/src/filelister.h b/src/filelister.h index dcf34a6..dbf2363 100644 --- a/src/filelister.h +++ b/src/filelister.h @@ -33,7 +33,14 @@ private: public: FileLister(); - void browse(const std::string& path, bool clean = true); + + /** + * Scans the given directory. + * @param clean If true, start a new result set, if false add to the + * results from the previous scan. + * @return True iff the given directory could be opened. + */ + bool browse(const std::string& path, bool clean = true); unsigned int size() { return files.size() + directories.size(); } unsigned int dirCount() { return directories.size(); } diff --git a/src/selector.cpp b/src/selector.cpp index 13adcfc..008c306 100644 --- a/src/selector.cpp +++ b/src/selector.cpp @@ -53,7 +53,10 @@ int Selector::exec(int startSelection) { FileLister fl; fl.setShowDirectories(showDirectories); fl.setFilter(link.getSelectorFilter()); - prepare(fl); + while (!prepare(fl) && showDirectories && dir != "/") { + // The given directory could not be opened; try parent. + dir = parentDir(dir); + } OffscreenSurface bg(*gmenu2x->bg); drawTitleIcon(bg, link.getIconPath(), true); @@ -185,15 +188,15 @@ int Selector::exec(int startSelection) { result = false; break; } - + // ...fall through... case InputManager::LEFT: if (showDirectories) { - string::size_type p = dir.rfind("/", dir.size()-2); - if (p==string::npos || dir.length() < 2 || dir[0] != '/') { + string oldDir = dir; + dir = parentDir(dir); + if (dir == "/" && oldDir == "/") { close = true; result = false; } else { - dir = dir.substr(0,p+1); selected = 0; firstElement = 0; prepare(fl); @@ -227,12 +230,14 @@ int Selector::exec(int startSelection) { return result ? (int)selected : -1; } -void Selector::prepare(FileLister& fl) { - fl.browse(dir); +bool Selector::prepare(FileLister& fl) { + bool opened = fl.browse(dir); screendir = dir; if (!screendir.empty() && screendir[screendir.length() - 1] != '/') { screendir += "/"; } screendir += "previews/"; + + return opened; } diff --git a/src/selector.h b/src/selector.h index 8c5f667..2ed9cb1 100644 --- a/src/selector.h +++ b/src/selector.h @@ -35,7 +35,7 @@ private: LinkApp& link; std::string file, dir, screendir; - void prepare(FileLister& fl); + bool prepare(FileLister& fl); public: Selector(GMenu2X *gmenu2x, LinkApp& link, diff --git a/src/utilities.cpp b/src/utilities.cpp index 4ff4075..66e2f1d 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -81,6 +81,13 @@ string readFileAsString(const char *filename) { } } +string parentDir(string const& dir) { + // Note that size() is unsigned, so for short strings the '- 2' wraps + // around and as a result the entire string is searched, which is fine. + auto p = dir.rfind('/', dir.size() - 2); + return p == string::npos ? "/" : dir.substr(0, p + 1); +} + bool fileExists(const string &file) { fstream fin; fin.open(file.c_str() ,ios::in); diff --git a/src/utilities.h b/src/utilities.h index acbef73..2d84e39 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -52,6 +52,14 @@ std::string readFileAsString(const char *filename); std::string strreplace(std::string orig, const std::string &search, const std::string &replace); std::string cmdclean(std::string cmdline); +/** + * Returns the parent directory of the given directory path, or "/" if there is + * no parent. + * This function does not check the file system: it is only string manipulation. + * @return The parent directory path, including a trailing '/'. + */ +std::string parentDir(std::string const& dir); + inline std::string trimExtension(std::string const& filename) { return filename.substr(0, filename.rfind('.')); }