From 91d4ded3fdca061c784e3f076a0442e6d112a8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 23 Apr 2023 14:36:34 +0200 Subject: [PATCH] Add function UnescapeMenuString Turns E&dit into Edit and 'd'. Double ampersands are translated to just a single one, hence the unescaping part of this. Useful in the Mac port to deal with desktop UI translation strings with included Windows hotkeys. --- Common/StringUtils.cpp | 24 ++++++++++++++++++++++++ Common/StringUtils.h | 4 ++++ unittest/UnitTest.cpp | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index 3115e1f03c..faa4c78399 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -342,3 +342,27 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st } return result; } + +std::string UnescapeMenuString(const char *input, char *shortcutChar) { + size_t len = strlen(input); + std::string output; + output.reserve(len); + bool escaping = false; + for (size_t i = 0; i < len; i++) { + if (input[i] == '&') { + if (escaping) { + output.push_back(input[i]); + escaping = false; + } else { + escaping = true; + } + } else { + output.push_back(input[i]); + if (escaping && shortcutChar) { + *shortcutChar = input[i]; + } + escaping = false; + } + } + return output; +} \ No newline at end of file diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 16bfaa18e5..ea73b7b2f6 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -76,6 +76,10 @@ void GetQuotedStrings(const std::string& str, std::vector& output); std::string ReplaceAll(std::string input, const std::string& src, const std::string& dest); +// Takes something like R&eplace and returns Replace, plus writes 'e' to *shortcutChar +// if not nullptr. Useful for Windows menu strings. +std::string UnescapeMenuString(const char *input, char *shortcutChar); + void SkipSpace(const char **ptr); size_t truncate_cpy(char *dest, size_t destSize, const char *src); diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index bcbb7ad4e0..b038bdb878 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -26,6 +26,11 @@ // // To use, set command line parameter to one or more of the tests below, or "all". // Search for "availableTests". +// +// Example of how to run with CMake: +// +// ./b.sh --unittest +// build/unittest EscapeMenuString #include "ppsspp_config.h" @@ -57,6 +62,7 @@ #include "Common/BitScan.h" #include "Common/CPUDetect.h" #include "Common/Log.h" +#include "Common/StringUtils.h" #include "Core/Config.h" #include "Common/File/VFS/VFS.h" #include "Common/File/VFS/DirectoryReader.h" @@ -890,6 +896,20 @@ bool TestInputMapping() { return true; } +bool TestEscapeMenuString() { + char c; + std::string temp = UnescapeMenuString("&File", &c); + EXPECT_EQ_INT((int)c, (int)'F'); + EXPECT_EQ_STR(temp, std::string("File")); + temp = UnescapeMenuString("U&til", &c); + EXPECT_EQ_INT((int)c, (int)'t'); + EXPECT_EQ_STR(temp, std::string("Util")); + temp = UnescapeMenuString("Ed&it", nullptr); + EXPECT_EQ_STR(temp, std::string("Edit")); + temp = UnescapeMenuString("Cut && Paste", nullptr); + EXPECT_EQ_STR(temp, std::string("Cut & Paste")); + return true; +} typedef bool (*TestFunc)(); struct TestItem { @@ -944,6 +964,7 @@ TestItem availableTests[] = { TEST_ITEM(SmallDataConvert), TEST_ITEM(DepthMath), TEST_ITEM(InputMapping), + TEST_ITEM(EscapeMenuString), }; int main(int argc, const char *argv[]) {