From 7b21f7af2e62da9f745812383fc697e840bf4f6a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 9 Nov 2020 07:50:35 -0500 Subject: [PATCH] Common/MsgHandler: Add fmt-capable variants of the alert macros Adds an interface that uses fmt under the hood, which is much more flexible than printf, particularly for localization purposes, given fmt supports positional formatters in a cross-platform manner out of the box with no configuration necessary. --- Languages/update-source-strings.sh | 21 ++++++++-- Source/Core/Common/MsgHandler.cpp | 62 +++++++++++++++++++----------- Source/Core/Common/MsgHandler.h | 46 ++++++++++++++++++++++ 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/Languages/update-source-strings.sh b/Languages/update-source-strings.sh index 2bf3bdcd28..6f7faf44a7 100755 --- a/Languages/update-source-strings.sh +++ b/Languages/update-source-strings.sh @@ -9,9 +9,24 @@ cd "$(dirname "$0")/.." SRCDIR=Source find $SRCDIR -name '*.cpp' -o -name '*.h' -o -name '*.c' | \ xgettext -s -p ./Languages/po -o dolphin-emu.pot --package-name="Dolphin Emulator" \ - --keyword=_ --keyword=wxTRANSLATE --keyword=SuccessAlertT --keyword=PanicAlertT \ - --keyword=PanicYesNoT --keyword=AskYesNoT --keyword=CriticalAlertT --keyword=GetStringT \ - --keyword=_trans --keyword=tr:1,1t --keyword=tr:1,2c --keyword=QT_TR_NOOP --keyword=FmtFormatT \ + --keyword=_ \ + --keyword=wxTRANSLATE \ + --keyword=AskYesNoT \ + --keyword=AskYesNoFmtT \ + --keyword=CriticalAlertT \ + --keyword=CriticalAlertFmtT \ + --keyword=PanicAlertT \ + --keyword=PanicAlertFmtT \ + --keyword=PanicYesNoT \ + --keyword=PanicYesNoFmtT \ + --keyword=SuccessAlertT \ + --keyword=SuccessAlertFmtT \ + --keyword=GetStringT \ + --keyword=_trans \ + --keyword=tr:1,1t \ + --keyword=tr:1,2c \ + --keyword=QT_TR_NOOP \ + --keyword=FmtFormatT \ --add-comments=i18n --from-code=utf-8 -f - # Copy strings from qt-strings.pot to dolphin-emu.pot diff --git a/Source/Core/Common/MsgHandler.cpp b/Source/Core/Common/MsgHandler.cpp index ce745ebc0a..e548554d7e 100644 --- a/Source/Core/Common/MsgHandler.cpp +++ b/Source/Core/Common/MsgHandler.cpp @@ -52,6 +52,28 @@ std::string DefaultStringTranslator(const char* text) MsgAlertHandler s_msg_handler = DefaultMsgHandler; StringTranslator s_str_translator = DefaultStringTranslator; bool s_alert_enabled = true; + +const char* GetCaption(MsgType style) +{ + static const std::string info_caption = s_str_translator(_trans("Information")); + static const std::string warn_caption = s_str_translator(_trans("Question")); + static const std::string ques_caption = s_str_translator(_trans("Warning")); + static const std::string crit_caption = s_str_translator(_trans("Critical")); + + switch (style) + { + case MsgType::Information: + return info_caption.c_str(); + case MsgType::Question: + return ques_caption.c_str(); + case MsgType::Warning: + return warn_caption.c_str(); + case MsgType::Critical: + return crit_caption.c_str(); + default: + return "Unhandled caption"; + } +} } // Anonymous namespace // Select which of these functions that are used for message boxes. If @@ -83,30 +105,9 @@ std::string GetStringT(const char* string) bool MsgAlert(bool yes_no, MsgType style, const char* format, ...) { // Read message and write it to the log - const char* caption = ""; + const char* caption = GetCaption(style); char buffer[2048]; - static const std::string info_caption = s_str_translator(_trans("Information")); - static const std::string warn_caption = s_str_translator(_trans("Question")); - static const std::string ques_caption = s_str_translator(_trans("Warning")); - static const std::string crit_caption = s_str_translator(_trans("Critical")); - - switch (style) - { - case MsgType::Information: - caption = info_caption.c_str(); - break; - case MsgType::Question: - caption = ques_caption.c_str(); - break; - case MsgType::Warning: - caption = warn_caption.c_str(); - break; - case MsgType::Critical: - caption = crit_caption.c_str(); - break; - } - va_list args; va_start(args, format); CharArrayFromFormatV(buffer, sizeof(buffer) - 1, s_str_translator(format).c_str(), args); @@ -123,4 +124,21 @@ bool MsgAlert(bool yes_no, MsgType style, const char* format, ...) return true; } + +bool MsgAlertFmtImpl(bool yes_no, MsgType style, fmt::string_view format, + const fmt::format_args& args) +{ + const char* caption = GetCaption(style); + const auto message = fmt::vformat(format, args); + ERROR_LOG_FMT(MASTER_LOG, "{}: {}", caption, message); + + // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored + if (s_msg_handler != nullptr && + (s_alert_enabled || style == MsgType::Question || style == MsgType::Critical)) + { + return s_msg_handler(caption, message.c_str(), yes_no, style); + } + + return true; +} } // namespace Common diff --git a/Source/Core/Common/MsgHandler.h b/Source/Core/Common/MsgHandler.h index 737640361c..0cf13e0856 100644 --- a/Source/Core/Common/MsgHandler.h +++ b/Source/Core/Common/MsgHandler.h @@ -27,11 +27,22 @@ void RegisterMsgAlertHandler(MsgAlertHandler handler); void RegisterStringTranslator(StringTranslator translator); std::string GetStringT(const char* string); + bool MsgAlert(bool yes_no, MsgType style, const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; + +bool MsgAlertFmtImpl(bool yes_no, MsgType style, fmt::string_view format, + const fmt::format_args& args); + +template +bool MsgAlertFmt(bool yes_no, MsgType style, fmt::string_view format, const Args&... args) +{ + return MsgAlertFmtImpl(yes_no, style, format, fmt::make_args_checked(format, args...)); +} + void SetEnableAlert(bool enable); // Like fmt::format, except the string becomes translatable @@ -42,6 +53,8 @@ std::string FmtFormatT(const char* string, Args&&... args) } } // namespace Common +// Deprecated variants of the alert macros. See the fmt variants down below. + #define SuccessAlert(format, ...) \ Common::MsgAlert(false, Common::MsgType::Information, format, ##__VA_ARGS__) @@ -72,3 +85,36 @@ std::string FmtFormatT(const char* string, Args&&... args) #define CriticalAlertT(format, ...) \ Common::MsgAlert(false, Common::MsgType::Critical, format, ##__VA_ARGS__) + +// Fmt-capable variants of the macros + +#define SuccessAlertFmt(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Information, FMT_STRING(format), ##__VA_ARGS__) + +#define PanicAlertFmt(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Warning, FMT_STRING(format), ##__VA_ARGS__) + +#define PanicYesNoFmt(format, ...) \ + Common::MsgAlertFmt(true, Common::MsgType::Warning, FMT_STRING(format), ##__VA_ARGS__) + +#define AskYesNoFmt(format, ...) \ + Common::MsgAlertFmt(true, Common::MsgType::Question, FMT_STRING(format), ##__VA_ARGS__) + +#define CriticalAlertFmt(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Critical, FMT_STRING(format), ##__VA_ARGS__) + +// Use these macros (that do the same thing) if the message should be translated. +#define SuccessAlertFmtT(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Information, FMT_STRING(format), ##__VA_ARGS__) + +#define PanicAlertFmtT(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Warning, FMT_STRING(format), ##__VA_ARGS__) + +#define PanicYesNoFmtT(format, ...) \ + Common::MsgAlertFmt(true, Common::MsgType::Warning, FMT_STRING(format), ##__VA_ARGS__) + +#define AskYesNoFmtT(format, ...) \ + Common::MsgAlertFmt(true, Common::MsgType::Question, FMT_STRING(format), ##__VA_ARGS__) + +#define CriticalAlertFmtT(format, ...) \ + Common::MsgAlertFmt(false, Common::MsgType::Critical, FMT_STRING(format), ##__VA_ARGS__)