diff --git a/Changes.txt b/Changes.txt
index 4d5776bcb..6ec962851 100644
--- a/Changes.txt
+++ b/Changes.txt
@@ -14,15 +14,17 @@
6.4 to 6.5 (December XX, 2020)
- * Enhanced cut/copy/paste for text editing (TODO: PromptWidget)
+ * Enhanced cut/copy/paste for text editing. (TODO: PromptWidget)
- * Added undo and redo to text editing (TODO: PromptWidget)
+ * Added undo and redo to text editing. (TODO: PromptWidget)
- * Added static tooltips to some UI items
+ * Added wildcard support to launcher dialog filter.
- * Added dynamic tooltips to most debugger items
+ * Added static tooltips to some UI items.
- * Increased sample size for CDFJ+
+ * Added dynamic tooltips to most debugger items.
+
+ * Increased sample size for CDFJ+.
-Have fun!
diff --git a/docs/index.html b/docs/index.html
index 0d2a8fd48..7e57b22e7 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -3695,9 +3695,10 @@
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.
+ 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.
diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx
index cbab575bd..11fa0fc06 100644
--- a/src/gui/LauncherDialog.cxx
+++ b/src/gui/LauncherDialog.cxx
@@ -348,6 +348,60 @@ void LauncherDialog::updateUI()
loadRomInfo();
}
+size_t LauncherDialog::matchWithJoker(const string& str, const string& pattern)
+{
+ if(str.length() >= pattern.length())
+ {
+ for(size_t pos = 0; pos < str.length() - pattern.length() + 1; ++pos)
+ {
+ bool found = true;
+
+ for(size_t i = 0; found && i < pattern.length(); ++i)
+ if(pattern[i] != str[pos + i] && pattern[i] != '?')
+ found = false;
+
+ if(found)
+ return pos;
+ }
+ }
+ return string::npos;
+}
+
+bool LauncherDialog::matchWithWildcards(const string& str, const string& pattern)
+{
+ string in = str;
+ string pat = pattern;
+ size_t pos = string::npos;
+
+ BSPF::toUpperCase(in);
+ BSPF::toUpperCase(pat);
+
+ for(size_t i = 0; i < pat.length(); ++i)
+ if(pat[i] == '*')
+ {
+ pos = i;
+ break;
+ }
+
+ if(pos != string::npos)
+ {
+ // '*' found, split pattern into left and right part, search recursively
+ const string leftPat = pat.substr(0, pos);
+ const string rightPat = pat.substr(pos + 1);
+ size_t posLeft = matchWithJoker(in, leftPat);
+
+ if(posLeft != string::npos)
+ return matchWithWildcards(in.substr(pos + posLeft), rightPat);
+ else
+ return false;
+ }
+ else
+ {
+ // no further '*' found
+ return matchWithJoker(in, pat) != string::npos;
+ }
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::applyFiltering()
{
@@ -361,7 +415,7 @@ void LauncherDialog::applyFiltering()
// Skip over files that don't match the pattern in the 'pattern' textbox
if(myPattern && myPattern->getText() != "" &&
- !BSPF::containsIgnoreCase(node.getName(), myPattern->getText()))
+ !matchWithWildcards(node.getName(), myPattern->getText()))
return false;
}
return true;
diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx
index cc265928b..6fafab663 100644
--- a/src/gui/LauncherDialog.hxx
+++ b/src/gui/LauncherDialog.hxx
@@ -112,6 +112,27 @@ class LauncherDialog : public Dialog
void loadConfig() override;
void saveConfig() override;
void updateUI();
+
+ /**
+ Search if string contains pattern including '?' as joker.
+
+ @param str The searched string
+ @param pattern The pattern to search for
+
+ @return Position of pattern in string.
+ */
+ size_t matchWithJoker(const string& str, const string& pattern);
+
+ /**
+ Search if string contains pattern including wildcard '*'
+ and '?' as joker, ignoring case.
+
+ @param str The searched string
+ @param pattern The pattern to search for
+
+ @return True if pattern was found.
+ */
+ bool matchWithWildcards(const string& str, const string& pattern);
void applyFiltering();
float getRomInfoZoom(int listHeight) const;
diff --git a/src/gui/WhatsNewDialog.cxx b/src/gui/WhatsNewDialog.cxx
index e94e432a2..5892bfcfd 100644
--- a/src/gui/WhatsNewDialog.cxx
+++ b/src/gui/WhatsNewDialog.cxx
@@ -48,6 +48,7 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const
#else
add(ypos, "enhanced cut/copy/paste for text editing");
add(ypos, "added undo and redo to text editing");
+ add(ypos, "added wildcard support to launcher dialog filter");
add(ypos, "added tooltips to many UI items");
add(ypos, "increased sample size for CDFJ+");
add(ypos, ELLIPSIS + " (for a complete list see 'docs/Changes.txt')");