diff --git a/src/core/config/LocalSettings.cpp b/src/core/config/LocalSettings.cpp index 095d6885..812ba704 100644 --- a/src/core/config/LocalSettings.cpp +++ b/src/core/config/LocalSettings.cpp @@ -163,6 +163,9 @@ bool LocalSettings::Load() if (j.contains("AddExtraHeaders")) m_bAddExtraHeaders = j["AddExtraHeaders"]; + + if (j.contains("Language")) + m_language = j["Language"]; return true; } @@ -203,6 +206,7 @@ bool LocalSettings::Save() j["Use12HourTime"] = m_bUse12HourTime; j["ShowBlockedMessages"] = m_bShowBlockedMessages; j["UseDoubleBuffering"] = m_bUseDoubleBuffering; + j["Language"] = m_language; if (m_bSaveWindowSize) { j["WindowWidth"] = m_width; diff --git a/src/core/config/LocalSettings.hpp b/src/core/config/LocalSettings.hpp index cd4364ea..df558ae8 100644 --- a/src/core/config/LocalSettings.hpp +++ b/src/core/config/LocalSettings.hpp @@ -226,6 +226,8 @@ class LocalSettings void SetUseDoubleBuffering(bool b) { m_bUseDoubleBuffering = b; } + int GetLanguage() const { return m_language; } + void SetLanguage(int lang) { m_language = lang; } private: std::string m_token; @@ -262,6 +264,7 @@ class LocalSettings int m_width = 1000; int m_height = 700; int m_userScale = 1000; + int m_language = 0; }; LocalSettings* GetLocalSettings(); diff --git a/src/resource.h b/src/resource.h index 73be3312..7f0c34ea 100644 --- a/src/resource.h +++ b/src/resource.h @@ -153,6 +153,7 @@ #define IDD_DIALOG_UPLOADING_ND 426 #define IDD_DIALOG_GUILD_CHOOSER_ND 427 #define IDD_DIALOG_PREFERENCES_ND 428 +#define IDD_DIALOG_LANGUAGE_ND 429 #define IDR_MAINMENU 501 #define IDR_MESSAGE_CONTEXT 502 #define IDR_GUILD_CONTEXT 503 @@ -337,6 +338,11 @@ #define IDS_CANT_LAUNCH_URL_UNS 775 #define IDS_CONFIRM_UNPIN 776 #define IDS_CONFIRM_UNPIN_TITLE 777 +#define IDS_LANGUAGE 778 +#define IDS_APPLY_LANG 779 +#define IDS_RESTART_REQUIRED 780 +#define IDS_LANG_DEFAULTS 781 +#define IDS_SYSTEM_LANGUAGE 782 #define IDC_OPTIONS_TABS 801 #define IDC_MY_ACCOUNT_BOX 802 #define IDC_MY_ACCOUNT_NAME 803 @@ -443,6 +449,12 @@ #define IDC_MINIMIZE_TO_NOTIF 904 #define IDC_HOW_GET_TOKEN 905 #define IDC_DOUBLE_BUFFERING 917 +#define IDC_LANGUAGE_LIST 918 +#define IDC_LANGUAGE_SYSTEM 919 +#define IDC_LANGUAGE_APPLY 920 +#define IDC_LANGUAGE_DEFAULTS 921 +#define IDC_PENDING_RESTART 922 +#define IDC_PENDING_RESTART_TEXT 923 #define ID_FILE_PREFERENCES 1001 #define ID_FILE_STOPALLSPEECH 1002 #define ID_FILE_EXIT 1003 diff --git a/src/resource.rc b/src/resource.rc index cbcfa7b9..893ae723 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -261,6 +261,7 @@ BEGIN END + ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -913,6 +914,23 @@ BEGIN END +IDD_DIALOG_LANGUAGE_ND DIALOG 0, 0, 264, 240 +STYLE WS_CHILD | DS_CONTROL +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Language Options", IDC_MDISPLAY_BOX, 6, 7, 248, 132 + LTEXT "Choose the language you want Discord Messenger to display", IDC_STATIC, 12, 18, 236, 8 + LISTBOX IDC_LANGUAGE_LIST, 12, 30, 236, 60, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + AUTOCHECKBOX "Detect and use my systems language by default", IDC_LANGUAGE_SYSTEM, 12, 93, 170, 10 + LTEXT "System Language: ", IDS_SYSTEM_LANGUAGE, 12, 106, 236, 8 + PUSHBUTTON "Apply Language", IDC_LANGUAGE_APPLY, 180, 120, 70, 14 + PUSHBUTTON "Revert to Defaults", IDC_LANGUAGE_DEFAULTS, 12, 120, 70, 14 + + ICON IDI_WARNING_IC,IDC_PENDING_RESTART,12,225,136,20,SS_REALSIZEIMAGE + LTEXT "Pending Restart. Some changes will not take effect until after a restart.", IDC_PENDING_RESTART_TEXT, 28, 226, 236, 8 + +END + ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO @@ -1718,11 +1736,19 @@ BEGIN IDS_CONFIRM_UNPIN_TITLE "Discord Messenger - Unpin Message" END +STRINGTABLE +BEGIN + IDS_APPLY_LANG "In order to apply the language, you need to restart Discord Messenger.\nDo you wish to restart now?" + IDS_RESTART_REQUIRED "Restart Required - Discord Messenger" + IDS_LANG_DEFAULTS "Default Settings have been loaded. They will take affect after a restart." + IDS_LANGUAGE "Language" + IDS_SYSTEM_LANGUAGE "System Language: %s" +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// - #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // @@ -1733,3 +1759,6 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + + + diff --git a/src/windows/InstanceMutex.hpp b/src/windows/InstanceMutex.hpp index 3e7e5fb4..bb88e468 100644 --- a/src/windows/InstanceMutex.hpp +++ b/src/windows/InstanceMutex.hpp @@ -12,4 +12,5 @@ class InstanceMutex public: HRESULT Init(); ~InstanceMutex(); + void Release() { Close(); } }; diff --git a/src/windows/Main.cpp b/src/windows/Main.cpp index 519c0da5..5075666d 100644 --- a/src/windows/Main.cpp +++ b/src/windows/Main.cpp @@ -59,6 +59,8 @@ IChannelView* g_pChannelView; MessageEditor* g_pMessageEditor; LoadingMessage* g_pLoadingMessage; +pSetThreadUILanguage g_fnSetThreadUILanguage = nullptr; + bool g_bBlockDoubleBufferingForThisInstance = false; constexpr int MIN_MEMORY_TO_BLOCK_DOUBLE_BUFFERING = 128 * 1024 * 1024; @@ -1921,6 +1923,31 @@ static bool ForceSingleInstance(LPCTSTR pClassName) return false; } +// Restarts the instance - for languages to apply +void RestartInstance() +{ + g_instanceMutex.Release(); // Close(); + + TCHAR szPath[MAX_PATH]; + GetModuleFileName(NULL, szPath, MAX_PATH); + + STARTUPINFO si = { 0 }; + si.cb = sizeof(si); + PROCESS_INFORMATION pi = { 0 }; + + if (!CreateProcess(szPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + + g_instanceMutex.Init(); + return; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + ExitProcess(0); + +} + // Blackwingcat's Extended Kernel workaround: Apparently, the custom user32.dll it uses tries // to write to the class name, which previously was part of .rodata, which is of course read-only. TCHAR g_className[64]; @@ -1966,6 +1993,19 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLin // Load the settings, and fix up some settings that are // actually bad to leave on on NT 3.x or NT 4 pSettings->Load(); + + // compatibility checking + HMODULE h = GetModuleHandle(TEXT("kernel32.dll")); + pSetThreadUILanguage fnSetUILanguage = (pSetThreadUILanguage)GetProcAddress(h, "SetThreadUILanguage"); + + LANGID langid = pSettings->GetLanguage(); + + if (fnSetUILanguage) { + fnSetUILanguage(langid); + } + else { + SetThreadLocale(MAKELCID(langid, SORT_DEFAULT)); + } if (LOBYTE(version) < 5 && pSettings->GetMessageStyle() == MS_GRADIENT) { diff --git a/src/windows/Main.hpp b/src/windows/Main.hpp index 11f2e6ea..ec2faa67 100644 --- a/src/windows/Main.hpp +++ b/src/windows/Main.hpp @@ -145,6 +145,8 @@ struct SendMessageAuxParams Snowflake m_snowflake; }; +typedef LANGID(WINAPI* pSetThreadUILanguage)(LANGID); + std::string FormatDiscrim(int discrim); std::string GetStringFromHResult(HRESULT hr); std::string GetDiscordToken(); @@ -155,3 +157,4 @@ void SetHeartbeatInterval(int timeMs); int GetProfilePictureSize(); HBITMAP GetDefaultBitmap(); bool ShouldBlockDoubleBuffering(); +void RestartInstance(); diff --git a/src/windows/OptionsDialog.cpp b/src/windows/OptionsDialog.cpp index 16556e2a..339a68ad 100644 --- a/src/windows/OptionsDialog.cpp +++ b/src/windows/OptionsDialog.cpp @@ -9,6 +9,8 @@ #endif #include +static bool g_bPendingRestart = false; + const eMessageStyle g_indexToMessageStyle[] = { MS_3DFACE, MS_GRADIENT, @@ -39,6 +41,7 @@ enum ePage PG_CHAT, PG_WINDOW, PG_CONNECTION, + PG_LANGUAGE, PG_PAGE_COUNT, PG_FIRST = PG_ACCOUNT_AND_PRIVACY }; @@ -288,6 +291,45 @@ void OptionsInitPage(HWND hwndDlg, int pageNum) free(tstrAPI); free(tstrCDN); + break; + } + case PG_LANGUAGE: + { + if (g_bPendingRestart) { + ShowWindow(GetDlgItem(hwndDlg, IDC_PENDING_RESTART), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_PENDING_RESTART_TEXT), SW_SHOW); + } + else { + ShowWindow(GetDlgItem(hwndDlg, IDC_PENDING_RESTART), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_PENDING_RESTART_TEXT), SW_HIDE); + } + + // TODO: Make a function that reads off available languages + // off resource file and adds them as listbox options + HWND hList = GetDlgItem(hwndDlg, IDC_LANGUAGE_LIST); + + // NOTE: must be in order!! + ListBox_AddString(hList, TEXT("English (US)")); + ListBox_AddString(hList, TEXT("Deutsch")); + ListBox_AddString(hList, TEXT("Espa\xf1ol")); + ListBox_AddString(hList, TEXT("Polski")); + + TCHAR szSysLang[128]; + TCHAR szResText[256]; + + // Check if currently loaded language is the name as the system. + LocalSettings* pSettings = GetLocalSettings(); + + if (GetSystemDefaultLangID() == pSettings->GetLanguage()) { + SendMessage(GetDlgItem(hwndDlg, IDC_LANGUAGE_SYSTEM), BM_SETCHECK, BST_CHECKED, 0); + } + + // Get Systems Language and display that + GetLocaleInfo(MAKELCID(GetSystemDefaultLangID(), SORT_DEFAULT), LOCALE_SLANGUAGE, szSysLang, 128); + + _stprintf(szResText, (LPCTSTR)TmGetTString(IDS_SYSTEM_LANGUAGE), szSysLang); + SetDlgItemText(hwndDlg, IDS_SYSTEM_LANGUAGE, szResText); + break; } } @@ -316,6 +358,7 @@ void WINAPI OnChildDialogInit(HWND hwndDlg) INT_PTR OptionsHandleCommand(HWND hwndParent, HWND hWnd, int pageNum, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (LOWORD(wParam) == IDOK) return EndDialog(hWnd, IDOK); @@ -648,6 +691,71 @@ INT_PTR OptionsHandleCommand(HWND hwndParent, HWND hWnd, int pageNum, UINT uMsg, } break; } + case PG_LANGUAGE: + { + + switch (LOWORD(wParam)) { + + case IDC_LANGUAGE_APPLY: + { + // NOTE: this must be in order as the ListBox + // temporary, once i'll figure out other ways + + LANGID langIds[] = { + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) + }; + + int sel = ListBox_GetCurSel(GetDlgItem(hWnd, IDC_LANGUAGE_LIST)); + + GetLocalSettings()->SetLanguage(langIds[sel]); + GetLocalSettings()->Save(); + + if (MessageBox(hWnd, TmGetTString(IDS_APPLY_LANG), TmGetTString(IDS_RESTART_REQUIRED), MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { + RestartInstance(); + } + else { + g_bPendingRestart = true; + + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART), SW_SHOW); + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART_TEXT), SW_SHOW); + } + break; + } + + case IDC_LANGUAGE_DEFAULTS: + { + MessageBox(hWnd, TmGetTString(IDS_LANG_DEFAULTS), TmGetTString(IDS_RESTART_REQUIRED), MB_OK); + g_bPendingRestart = true; + + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART), SW_SHOW); + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART_TEXT), SW_SHOW); + + // the default langID for English US + GetLocalSettings()->SetLanguage(1033); + GetLocalSettings()->Save(); + + break; + } + + case IDC_LANGUAGE_SYSTEM: + { + GetLocalSettings()->SetLanguage(GetSystemDefaultLangID()); + GetLocalSettings()->Save(); + + if (MessageBox(hWnd, TmGetTString(IDS_APPLY_LANG), TmGetTString(IDS_RESTART_REQUIRED), MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { + RestartInstance(); + } + else { + g_bPendingRestart = true; + + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART), SW_SHOW); + ShowWindow(GetDlgItem(hWnd, IDC_PENDING_RESTART_TEXT), SW_SHOW); + } + + break; + } + } + } } return 0; @@ -743,6 +851,7 @@ HRESULT OnPreferenceDialogInit(HWND hWnd) AddTab(hwndTab, tie, PG_CHAT, (LPTSTR)TmGetTString(IDS_CHAT)); AddTab(hwndTab, tie, PG_WINDOW, (LPTSTR)TmGetTString(IDS_WINDOW)); AddTab(hwndTab, tie, PG_CONNECTION, (LPTSTR)TmGetTString(IDS_CONNECTION)); + AddTab(hwndTab, tie, PG_LANGUAGE, (LPTSTR)TmGetTString(IDS_LANGUAGE)); // Lock the resources for the child dialog boxes. pHeader->apRes[PG_ACCOUNT_AND_PRIVACY] = LockDialogResource(MAKEINTRESOURCE(IDD_DIALOG_MY_ACCOUNT)); @@ -751,6 +860,8 @@ HRESULT OnPreferenceDialogInit(HWND hWnd) pHeader->apRes[ PG_CHAT ] = LockDialogResource(MAKEINTRESOURCE(IDD_DIALOG_CHATSETTINGS)); pHeader->apRes[ PG_WINDOW ] = LockDialogResource(MAKEINTRESOURCE(IDD_DIALOG_WINDOWSETTINGS)); pHeader->apRes[ PG_CONNECTION ] = LockDialogResource(MAKEINTRESOURCE(IDD_DIALOG_CONNECTION)); + pHeader->apRes[ PG_LANGUAGE ] = LockDialogResource(MAKEINTRESOURCE(IDD_DIALOG_LANGUAGE_ND)); + RECT rcTab; SetRectEmpty(&rcTab);