namespace fs = std::filesystem;
-struct LanguageDll {
- std::wstring langName;
- std::wstring file;
+struct SettingsGeneralData {
+ WMSettings *settings;
+ std::vector<LanguageModule> langModules;
};
+static void FillLanguageList(HWND hLanguageList, const SettingsGeneralData& dlgData)
+{
+ SendMessage(hLanguageList,
+ CB_INITSTORAGE,
+ static_cast<WPARAM>(dlgData.langModules.size()), 200);
+ for (const auto &lang : dlgData.langModules) {
+ int itemId = ComboBox_AddString(hLanguageList, lang.langName.c_str());
+ if (itemId == CB_ERR || itemId == CB_ERRSPACE) {
+ WMLog::GetInstance().Write(L"Failed to add language %ls to language selector", lang.langName.c_str());
+ } else {
+ ComboBox_SetItemData(hLanguageList, itemId, lang.fileName.c_str());
+ }
+ }
+ ComboBox_SelectString(hLanguageList, 0, WMi18n::GetInstance().GetcurrentLanguageName().c_str());
+}
+
INT_PTR CALLBACK Settings_GeneralDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
EnableThemeDialogTexture(hDlg, ETDT_ENABLETAB);
}
- WMSettings* settings = reinterpret_cast<WMSettings*>(lParam);
- assert(settings != nullptr);
- SetWindowLongPtr(hDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(settings));
+ SettingsGeneralData *dlgData = new SettingsGeneralData;
+ memset(dlgData, 0, sizeof(*dlgData));
+ dlgData->langModules = WMi18n::GetInstance().GetAvailableLanguages();
+ dlgData->settings = reinterpret_cast<WMSettings *>(lParam);
+ assert(dlgData->settings != nullptr);
+
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(dlgData));
+
+ FillLanguageList(GetDlgItem(hDlg, IDC_LANGUAGE), *dlgData);
- DWORD enabled = settings->IsAutostartEnabled();
+ DWORD enabled = dlgData->settings->IsAutostartEnabled();
Button_SetCheck(hAutostart, enabled ? BST_CHECKED : BST_UNCHECKED);
- enabled = !!settings->QueryValue(SettingsKey::LOGGING_ENABLED);
+ enabled = !!dlgData->settings->QueryValue(SettingsKey::LOGGING_ENABLED);
Button_SetCheck(hLogging, enabled ? BST_CHECKED : BST_UNCHECKED);
Button_Enable(hOpenLog, enabled);
if (enabled) {
return TRUE;
}
+ case WM_DESTROY: {
+ SettingsGeneralData *dlgData = reinterpret_cast<SettingsGeneralData *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ if (dlgData != nullptr) {
+ delete dlgData;
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, 0);
+ }
+ return FALSE;
+ }
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_ENABLELOGGING) {
DWORD checked = Button_GetCheck(GetDlgItem(hDlg, IDC_ENABLELOGGING));
return 0;
}
case WM_SAVESETTINGS: {
- WMSettings* settings = reinterpret_cast<WMSettings*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ SettingsGeneralData *dlgData = reinterpret_cast<SettingsGeneralData *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ assert(dlgData != nullptr);
HWND hAutostart = GetDlgItem(hDlg, IDC_RUNONSTARTUP);
HWND hLogging = GetDlgItem(hDlg, IDC_ENABLELOGGING);
int enableLog = Button_GetCheck(hLogging) == BST_CHECKED;
- settings->SetValue(SettingsKey::LOGGING_ENABLED, enableLog);
+ dlgData->settings->SetValue(SettingsKey::LOGGING_ENABLED, enableLog);
WMLog::GetInstance().SetEnabled(enableLog);
if (Button_GetCheck(hAutostart) == BST_CHECKED) {
- settings->EnableAutostart(true);
+ dlgData->settings->EnableAutostart(true);
} else {
- settings->EnableAutostart(false);
+ dlgData->settings->EnableAutostart(false);
}
return 0;
--- /dev/null
+/*
+ WinMute
+ Copyright (c) 2023, Alexander Steinhoefer
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#include "common.h"
+
+extern HINSTANCE hglobInstance;
+
+WMi18n::WMi18n() noexcept
+{
+}
+
+WMi18n::~WMi18n() noexcept
+{
+ UnloadLanguage();
+}
+
+WMi18n& WMi18n::GetInstance()
+{
+ static WMi18n inst;
+ return inst;
+}
+
+std::optional<fs::path> WMi18n::GetLanguageModulesPath() const
+{
+ wchar_t wmPath[MAX_PATH + 1]{ 0 };
+ if (GetModuleFileNameW(nullptr, wmPath, MAX_PATH) <= 0) {
+ WMLog::GetInstance().WriteWindowsError(L"GetModuleFileNameW", GetLastError());
+ return std::nullopt;
+ }
+ fs::path langPath = wmPath;
+ return langPath.remove_filename() / L"lang";
+}
+
+std::vector<LanguageModule> WMi18n::GetAvailableLanguages() const
+{
+ std::vector<LanguageModule> langDlls{ { L"English", L"" } };
+ const auto langPath = GetLanguageModulesPath();
+ if (langPath.has_value()) {
+ const fs::path searchPath = *langPath / L"*.dll";
+ WIN32_FIND_DATAW wfd{ 0 };
+ HANDLE hFindFile = FindFirstFileExW(
+ searchPath.c_str(),
+ FindExInfoBasic,
+ &wfd,
+ FindExSearchNameMatch,
+ nullptr,
+ FIND_FIRST_EX_CASE_SENSITIVE);
+ if (hFindFile != INVALID_HANDLE_VALUE) {
+ do {
+ LanguageModule langMod;
+ const fs::path langFilePath = *langPath / wfd.cFileName;
+ langMod.fileName = wfd.cFileName;
+ HMODULE hLangFile = LoadLibraryExW(
+ langFilePath.c_str(),
+ nullptr,
+ LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
+ if (hLangFile == nullptr) {
+ continue;
+ }
+ wchar_t langName[255 + 1];
+ if (LoadStringW(hLangFile, IDI_LANG_NAME, langName, ARRAY_SIZE(langName)) <= 0) {
+ WMLog &log = WMLog::GetInstance();
+ log.WriteWindowsError(L"LoadStringW", GetLastError());
+ log.Write(L"Failed to load language %ls", wfd.cFileName);
+ } else {
+ langMod.langName = langName;
+ langDlls.push_back(langMod);
+ }
+ FreeLibrary(hLangFile);
+ } while (FindNextFileW(hFindFile, &wfd));
+ }
+ }
+ return langDlls;
+}
+
+std::wstring WMi18n::GetCurrentLanguageModule() const
+{
+ return curModuleName_;
+}
+
+std::wstring WMi18n::GetcurrentLanguageName() const
+{
+ return GetString(IDI_LANG_NAME);
+}
+
+// https://learn.microsoft.com/en-us/windows/win32/intl/creating-a-multilingual-user-interface-application
+
+bool WMi18n::LoadLanguage(const std::wstring &dllName)
+{
+ if (dllName.empty()) {
+ UnloadLanguage();
+ return true;
+ } else {
+ auto langModPath = GetLanguageModulesPath();
+ if (!langModPath.has_value()) {
+ return false;
+ }
+ fs::path pathDllName{ dllName };
+ *langModPath /= pathDllName.filename(); // Sanitize
+ if (!fs::exists(*langModPath)) {
+ // TODO: Error
+ } else {
+ auto newLangModule = LoadLibraryExW(
+ langModPath->c_str(), nullptr,
+ LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
+ if (newLangModule == nullptr) {
+ // TODO: Error
+ } else {
+ UnloadLanguage();
+ langModule_ = newLangModule;
+ curModuleName_ = dllName;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void WMi18n::UnloadLanguage() noexcept
+{
+ if (langModule_ != nullptr) {
+ FreeLibrary(langModule_);
+ langModule_ = nullptr;
+ curModuleName_.clear();
+ }
+}
+
+std::wstring WMi18n::GetString(UINT strId) const
+{
+ HMODULE hMod = (langModule_ == nullptr) ? hglobInstance : langModule_;
+ constexpr size_t strBufferSize = 2048 + 1;
+ wchar_t strBuffer[strBufferSize]{ 0 };
+ const int strLength = LoadStringW(hMod, strId, strBuffer, 0);
+ if (strLength == 0) {
+ return L"";
+ } else if (strLength < strBufferSize) {
+ LoadStringW(hMod, strId, strBuffer, ARRAY_SIZE(strBuffer));
+ return std::wstring(strBuffer, strBuffer + strLength);
+ } else if (strLength > 0) {
+ wchar_t *largeBuffer = new wchar_t[strLength + 1];
+ LoadStringW(hMod, strId, largeBuffer, strLength + 1);
+ std::wstring ret(strBuffer, largeBuffer + strLength);
+ delete[] largeBuffer;
+ return ret;
+ } else {
+ WMLog::GetInstance().WriteWindowsError(L"LoadStringW", GetLastError());
+ std::wstring err = L"String resource " + std::to_wstring(strId) + L" not found";
+ return err;
+ }
+}
--- /dev/null
+/*
+ WinMute
+ Copyright (c) 2023, Alexander Steinhoefer
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#pragma once
+
+#include "common.h"
+
+struct LanguageModule {
+ std::wstring langName;
+ std::wstring fileName;
+};
+
+class WMi18n {
+public:
+ static WMi18n& GetInstance();
+
+ bool LoadLanguage(const std::wstring &dllName);
+ std::vector<LanguageModule> GetAvailableLanguages() const;
+
+ std::optional<fs::path> GetLanguageModulesPath() const;
+ std::wstring GetCurrentLanguageModule() const;
+ std::wstring GetcurrentLanguageName() const;
+
+ std::wstring GetString(UINT id) const;
+
+private:
+ WMi18n() noexcept;
+ ~WMi18n() noexcept;
+ WMi18n(const WMi18n &) = delete;
+ WMi18n &operator=(const WMi18n &) = delete;
+
+ HMODULE langModule_ = nullptr;
+ std::wstring curModuleName_;
+
+ void UnloadLanguage() noexcept;
+};
+