From 7ed3b77fdd6a0909cc7bba274f4fbd6b0a85f2b2 Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Sun, 20 Jul 2025 21:32:51 -0400 Subject: [PATCH 1/5] feat: Add `App::CancellationToken` --- CHANGELOG.md | 10 +++++++ CMakeLists.txt | 4 ++- Doxyfile | 2 +- include/app/cancellationtoken.h | 52 +++++++++++++++++++++++++++++++++ manual/README.md | 8 ++--- src/app/cancellationtoken.cpp | 50 +++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 include/app/cancellationtoken.h create mode 100644 src/app/cancellationtoken.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index f242b2c..3b2c129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 2025.7.4 +### Breaking Changes +None +### New APIs +#### App +- Added `CancellationToken` class +### Fixes +#### Localization +- Fixed included headers + ## 2025.7.3 ### Breaking Changes None diff --git a/CMakeLists.txt b/CMakeLists.txt index 06e185d..5b27659 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") #libnick Definition -project ("libnick" LANGUAGES C CXX VERSION 2025.7.3 DESCRIPTION "A cross-platform base for native Nickvision applications.") +project ("libnick" LANGUAGES C CXX VERSION 2025.7.4 DESCRIPTION "A cross-platform base for native Nickvision applications.") include(CMakePackageConfigHelpers) include(GNUInstallDirs) include(CTest) @@ -36,6 +36,7 @@ if(NOT WIN32) endif() add_library (${PROJECT_NAME} "include/app/appinfo.h" + "include/app/cancellationtoken.h" "include/app/datafilebase.h" "include/app/datafilemanager.h" "include/app/windowgeometry.h" @@ -95,6 +96,7 @@ add_library (${PROJECT_NAME} "include/update/version.h" "include/update/versiontype.h" "src/app/appinfo.cpp" + "src/app/cancellationtoken.cpp" "src/app/datafilebase.cpp" "src/app/datafilemanager.cpp" "src/app/windowgeometry.cpp" diff --git a/Doxyfile b/Doxyfile index 9b0d8e6..a7edc3c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "libnick" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "2025.7.3" +PROJECT_NUMBER = "2025.7.4" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/include/app/cancellationtoken.h b/include/app/cancellationtoken.h new file mode 100644 index 0000000..bec84d2 --- /dev/null +++ b/include/app/cancellationtoken.h @@ -0,0 +1,52 @@ +#ifndef CANCELLATIONTOKEN_H +#define CANCELLATIONTOKEN_H + +#include +#include + +namespace Nickvision::App +{ + /** + * @brief A token that can be used to cancel an operation. + */ + class CancellationToken + { + public: + /** + * @brief Constructs a CancellationToken. + * @param cancelFunction A callback function to call when the token is cancelled + */ + CancellationToken(const std::function& cancelFunction = {}); + /** + * @brief Gets whether or not the token is cancelled. + * @return True if token is cancelled, else false + */ + bool isCancelled() const; + /** + * @brief Gets the cancel function to be called when the token is cancelled. + * @return The cancel function + */ + const std::function& getCancelFunction() const; + /** + * @brief Sets the cancel function to be called when the token is cancelled. + * @param cancelFunction The cancel function + */ + void setCancelFunction(const std::function& cancelFunction); + /** + * @brief Cancels the token. + */ + void cancel(); + /** + * @brief Converts the token to a boolean. + * @return True if token is cancelled, else false + */ + operator bool() const; + + private: + mutable std::mutex m_mutex; + bool m_cancelled; + std::function m_cancelFunction; + }; +} + +#endif //CANCELLATIONTOKEN_H \ No newline at end of file diff --git a/manual/README.md b/manual/README.md index c756c4f..58579fa 100644 --- a/manual/README.md +++ b/manual/README.md @@ -6,15 +6,15 @@ libnick provides Nickvision apps with a common set of cross-platform APIs for managing system and desktop app functionality such as network management, taskbar icons, translations, app updates, and more. -## 2025.7.3 +## 2025.7.4 ### Breaking Changes None ### New APIs -#### Localization -- You can now specify "C" in the `Gettext::changeLanguage()` function to turn off translations. +#### App +- Added `CancellationToken` class ### Fixes #### Localization -- Improved `Gettext::getAvailableLanguages()`'s search for languages +- Fixed included headers ## Dependencies The following are a list of dependencies used by libnick. diff --git a/src/app/cancellationtoken.cpp b/src/app/cancellationtoken.cpp new file mode 100644 index 0000000..78fc274 --- /dev/null +++ b/src/app/cancellationtoken.cpp @@ -0,0 +1,50 @@ +#include "app/cancellationtoken.h" + +namespace Nickvision::App +{ + CancellationToken::CancellationToken(const std::function& cancelFunction) + : m_cancelled{ false }, + m_cancelFunction{ cancelFunction } + { + + } + + bool CancellationToken::isCancelled() const + { + std::lock_guard lock{ m_mutex }; + return m_cancelled; + } + + const std::function& CancellationToken::getCancelFunction() const + { + std::lock_guard lock{ m_mutex }; + return m_cancelFunction; + } + + void CancellationToken::setCancelFunction(const std::function& cancelFunction) + { + std::lock_guard lock{ m_mutex }; + m_cancelFunction = cancelFunction; + } + + void CancellationToken::cancel() + { + std::unique_lock lock{ m_mutex }; + if(m_cancelled) + { + return; + } + m_cancelled = true; + if(m_cancelFunction) + { + lock.unlock(); + m_cancelFunction(); + } + } + + CancellationToken::operator bool() const + { + std::lock_guard lock{ m_mutex }; + return m_cancelled; + } +} \ No newline at end of file From c736b408c236342624e01dc108a3db2cf97cbabc Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Mon, 21 Jul 2025 20:48:48 -0400 Subject: [PATCH 2/5] fix: Calling empty callbacks --- include/events/event.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/events/event.h b/include/events/event.h index e7ed0a7..7cdc0f5 100644 --- a/include/events/event.h +++ b/include/events/event.h @@ -112,7 +112,10 @@ namespace Nickvision::Events std::lock_guard lock{ m_mutex }; for (const std::function& handler : m_handlers) { - handler(param); + if(handler) + { + handler(param); + } } } /** From be7b97f945c685b70a96e0df54bdc0436635bbd2 Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Fri, 25 Jul 2025 18:43:28 -0400 Subject: [PATCH 3/5] feat: Fix vcpkg cache --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 4 ++-- .github/workflows/windows.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 629d3e8..b567412 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -51,9 +51,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl glib gtest libsecret maddy triplet: ${{ matrix.variant.triplet }} - revision: 3c81ed09705008f13bf76f39853507bef51f71a1 + revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace token: ${{ github.token }} - cache-key: ${{ matrix.variant.triplet }} + cache-key: ${{ matrix.variant.triplet }}-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace - name: "Build" working-directory: ${{github.workspace}}/build run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b64ba64..756cf7c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -31,9 +31,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl glib gtest maddy triplet: arm64-osx - revision: 3c81ed09705008f13bf76f39853507bef51f71a1 + revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace token: ${{ github.token }} - cache-key: "arm64-osx" + cache-key: "arm64-osx-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace" - name: "Build" working-directory: ${{github.workspace}}/build run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b0aa801..9ed1a93 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,9 +38,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl gtest maddy sqlcipher triplet: ${{ matrix.variant.triplet }} - revision: 3c81ed09705008f13bf76f39853507bef51f71a1 + revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace token: ${{ github.token }} - cache-key: ${{ matrix.variant.triplet }} + cache-key: ${{ matrix.variant.triplet }}-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace - name: "Build" working-directory: ${{github.workspace}}/build run: | From cb81b80300a701dc5c3beefd3a3fa2e45f2ff924 Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Sat, 26 Jul 2025 18:46:49 -0400 Subject: [PATCH 4/5] feat: Maddy 1.6.0 provides cmake now --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 4 ++-- .github/workflows/windows.yml | 4 ++-- CMakeLists.txt | 7 ++----- cmake/config.cmake.in | 4 +--- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b567412..daa2a96 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -51,9 +51,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl glib gtest libsecret maddy triplet: ${{ matrix.variant.triplet }} - revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace + revision: 4103f46cb1ebb69ef5511cab840ecea0f441fbd7 token: ${{ github.token }} - cache-key: ${{ matrix.variant.triplet }}-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace + cache-key: ${{ matrix.variant.triplet }}-4103f46cb1ebb69ef5511cab840ecea0f441fbd7 - name: "Build" working-directory: ${{github.workspace}}/build run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 756cf7c..277eba8 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -31,9 +31,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl glib gtest maddy triplet: arm64-osx - revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace + revision: 4103f46cb1ebb69ef5511cab840ecea0f441fbd7 token: ${{ github.token }} - cache-key: "arm64-osx-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace" + cache-key: "arm64-osx-4103f46cb1ebb69ef5511cab840ecea0f441fbd7" - name: "Build" working-directory: ${{github.workspace}}/build run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9ed1a93..ef3cd0f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,9 +38,9 @@ jobs: with: pkgs: boost-json cpr gettext-libintl gtest maddy sqlcipher triplet: ${{ matrix.variant.triplet }} - revision: 4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace + revision: 4103f46cb1ebb69ef5511cab840ecea0f441fbd7 token: ${{ github.token }} - cache-key: ${{ matrix.variant.triplet }}-4887ad6d1414f74cb7cb8d1e527fb46adb4e9ace + cache-key: ${{ matrix.variant.triplet }}-4103f46cb1ebb69ef5511cab840ecea0f441fbd7 - name: "Build" working-directory: ${{github.workspace}}/build run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b27659..bcd3830 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,11 +148,8 @@ endif() find_package(Boost REQUIRED COMPONENTS json) find_package(cpr CONFIG REQUIRED) find_package(Intl REQUIRED) -target_link_libraries(${PROJECT_NAME} PUBLIC Boost::json cpr::cpr Intl::Intl) -if(USING_VCPKG) - find_package(unofficial-maddy CONFIG REQUIRED) - target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::maddy::maddy) -endif() +find_package(maddy CONFIG REQUIRED) +target_link_libraries(${PROJECT_NAME} PUBLIC Boost::json cpr::cpr Intl::Intl maddy::maddy) if(WIN32) find_package(sqlcipher CONFIG REQUIRED) target_link_libraries(${PROJECT_NAME} PUBLIC sqlcipher::sqlcipher Advapi32 Dnsapi Dwmapi Gdiplus Kernel32 Shell32 UxTheme Ws2_32) diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in index 4d98257..38d2784 100644 --- a/cmake/config.cmake.in +++ b/cmake/config.cmake.in @@ -17,9 +17,7 @@ endif() find_dependency(Boost REQUIRED COMPONENTS json) find_dependency(cpr CONFIG REQUIRED) find_dependency(Intl REQUIRED) -if(USING_VCPKG) - find_dependency(unofficial-maddy REQUIRED) -endif() +find_dependency(maddy CONFIG REQUIRED) if(WIN32) find_dependency(sqlcipher CONFIG REQUIRED) elseif(APPLE) From e15226756ccb7dfa7fa850fc5da5b4504e03f444 Mon Sep 17 00:00:00 2001 From: Nick Logozzo Date: Sat, 26 Jul 2025 19:04:28 -0400 Subject: [PATCH 5/5] fix: Gettext::changeLanguage("C") --- CHANGELOG.md | 3 ++- include/localization/gettext.h | 26 ++++++++++++++---- manual/README.md | 3 ++- src/localization/gettext.cpp | 48 +++++++++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2c129..201e991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ## 2025.7.4 ### Breaking Changes -None +- `maddy` dependency requires >= 1.6.0 ### New APIs #### App - Added `CancellationToken` class ### Fixes #### Localization - Fixed included headers +- Fixed Gettext::changeLanguage("C") not turning off translations ## 2025.7.3 ### Breaking Changes diff --git a/include/localization/gettext.h b/include/localization/gettext.h index 7219f18..a7238f1 100644 --- a/include/localization/gettext.h +++ b/include/localization/gettext.h @@ -29,8 +29,8 @@ #include #define GETTEXT_CONTEXT_SEPARATOR "\004" -#define _(String) dgettext(::Nickvision::Localization::Gettext::getDomainName().c_str(), String) -#define _n(String, StringPlural, N) dngettext(::Nickvision::Localization::Gettext::getDomainName().c_str(), String, StringPlural, static_cast(N)) +#define _(String) ::Nickvision::Localization::Gettext::dgettext(String) +#define _n(String, StringPlural, N) ::Nickvision::Localization::Gettext::dngettext(String, StringPlural, static_cast(N)) #define _f(String, ...) ::Nickvision::Localization::Gettext::fgettext(String, __VA_ARGS__) #define _fn(String, StringPlural, N, ...) ::Nickvision::Localization::Gettext::fngettext(String, StringPlural, static_cast(N), __VA_ARGS__) #define _p(Context, String) ::Nickvision::Localization::Gettext::pgettext(Context GETTEXT_CONTEXT_SEPARATOR String, String) @@ -57,19 +57,34 @@ namespace Nickvision::Localization::Gettext const std::vector& getAvailableLanguages(); /** * @brief Changes the current language for gettext translations. - * @param language The language code to change translations to (use "C" to turn off translations and use the default language) + * @param language The language code to change translations to (use "C" to turn off translations; use "" to use the system default language) * @return True if the language was changed successfully, else false */ bool changeLanguage(const std::string& language); + /** + * @brief Translates a message. + * @param msgid The message to translate + * @return The translated message + */ + const char* dgettext(const char* msgid); + /** + * @brief Translates a plural message. + * @param msg The message to translate + * @param msgPlural The plural version of the message to translate + * @param n The number of objects (used to determine whether or not to use the plural version of the message) + * @return The translated message for the given number of objects + */ + const char* dngettext(const char* msg, const char* msgPlural, unsigned long n); /** * @brief Translates a message and formats it with the given arguments. * @param msg The message to translate * @param args The arguments to format the translated message with + * @return The formatted translated message */ template std::string fgettext(const char* msg, Args&&... args) { - return std::vformat(_(msg), std::make_format_args(args...)); + return std::vformat(Nickvision::Localization::Gettext::dgettext(msg), std::make_format_args(args...)); } /** * @brief Translates a plural message and formats it with the given arguments. @@ -77,11 +92,12 @@ namespace Nickvision::Localization::Gettext * @param msgPlural The plural version of the message to translate * @param n The number of objects (used to determine whether or not to use the plural version of the message) * @param args The arguments to format the translated message with + * @return The formatted translated message for the given number of objects */ template std::string fngettext(const char* msg, const char* msgPlural, unsigned long n, Args&&... args) { - return std::vformat(_n(msg, msgPlural, n), std::make_format_args(args...)); + return std::vformat(Nickvision::Localization::Gettext::dngettext(msg, msgPlural, n), std::make_format_args(args...)); } /** * @brief Translates a message for a given context. diff --git a/manual/README.md b/manual/README.md index 58579fa..66ad4b9 100644 --- a/manual/README.md +++ b/manual/README.md @@ -8,13 +8,14 @@ libnick provides Nickvision apps with a common set of cross-platform APIs for ma ## 2025.7.4 ### Breaking Changes -None +- `maddy` dependency requires >= 1.6.0 ### New APIs #### App - Added `CancellationToken` class ### Fixes #### Localization - Fixed included headers +- Fixed Gettext::changeLanguage("C") not turning off translations ## Dependencies The following are a list of dependencies used by libnick. diff --git a/src/localization/gettext.cpp b/src/localization/gettext.cpp index 2e1c163..ef2bebe 100644 --- a/src/localization/gettext.cpp +++ b/src/localization/gettext.cpp @@ -10,7 +10,8 @@ using namespace Nickvision::System; namespace Nickvision::Localization { - static std::string s_domainName; + static std::string s_domainName{}; + static bool s_translationsOff{ false }; bool Gettext::init(const std::string& domainName) { @@ -57,10 +58,18 @@ namespace Nickvision::Localization bool Gettext::changeLanguage(const std::string& language) { - if(language == "C") + if(language.empty()) { - Environment::setVariable("LANGUAGE", ""); - setlocale(LC_ALL, "C"); + if(Environment::hasVariable("LANGUAGE")) + { + Environment::clearVariable("LANGUAGE"); + } + s_translationsOff = false; + return true; + } + else if(language == "C") + { + s_translationsOff = true; return true; } const std::vector& langs{ Gettext::getAvailableLanguages() }; @@ -69,12 +78,35 @@ namespace Nickvision::Localization return false; } Environment::setVariable("LANGUAGE", language); + s_translationsOff = false; return true; } + const char* Gettext::dgettext(const char* msgid) + { + if(s_translationsOff) + { + return msgid; + } + return ::dgettext(s_domainName.c_str(), msgid); + } + + const char* Gettext::dngettext(const char* msg, const char* msgPlural, unsigned long n) + { + if(s_translationsOff) + { + return n == 1 ? msg : msgPlural; + } + return ::dngettext(s_domainName.c_str(), msg, msgPlural, n); + } + const char* Gettext::pgettext(const char* context, const char* msg) { - const char* translation{ dcgettext(s_domainName.c_str(), context, LC_MESSAGES) }; + if(s_translationsOff) + { + return msg; + } + const char* translation{ ::dcgettext(s_domainName.c_str(), context, LC_MESSAGES) }; if (translation == context) { return msg; @@ -84,7 +116,11 @@ namespace Nickvision::Localization const char* Gettext::pngettext(const char* context, const char* msg, const char* msgPlural, unsigned long n) { - const char* translation{ dcngettext(s_domainName.c_str(), context, msgPlural, n, LC_MESSAGES) }; + if(s_translationsOff) + { + return n == 1 ? msg : msgPlural; + } + const char* translation{ ::dcngettext(s_domainName.c_str(), context, msgPlural, n, LC_MESSAGES) }; if (translation == context || translation == msgPlural) { return n == 1 ? msg : msgPlural;