From 40352be5cc36f598fde0f8d22b15ba9495c80bfd Mon Sep 17 00:00:00 2001 From: krzys-h Date: Sun, 24 Jan 2016 16:03:24 +0100 Subject: [PATCH] Add language switcher to the settings UI, closes #506 --- src/CMakeLists.txt | 1 + src/app/app.cpp | 148 +++++++++------------------- src/app/app.h | 1 - src/app/controller.cpp | 12 +-- src/app/controller.h | 4 +- src/common/event.cpp | 1 + src/common/event.h | 1 + src/common/language.cpp | 54 ++++++++++ src/common/language.h | 7 +- src/common/settings.cpp | 29 ++++++ src/common/settings.h | 6 ++ src/level/robotmain.cpp | 19 +--- src/level/robotmain.h | 3 - src/ui/screen/screen_setup_game.cpp | 36 ++++++- 14 files changed, 181 insertions(+), 141 deletions(-) create mode 100644 src/common/language.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 53865091..35809a47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,6 +87,7 @@ set(BASE_SOURCES common/event.cpp common/image.cpp common/key.cpp + common/language.cpp common/logger.cpp common/misc.cpp common/pathman.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 0a82f32f..0b47cd32 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -245,7 +245,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) OPT_RUNSCENE, OPT_SCENETEST, OPT_LOGLEVEL, - OPT_LANGUAGE, OPT_LANGDIR, OPT_DATADIR, OPT_SAVEDIR, @@ -262,7 +261,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) { "runscene", required_argument, nullptr, OPT_RUNSCENE }, { "scenetest", no_argument, nullptr, OPT_SCENETEST }, { "loglevel", required_argument, nullptr, OPT_LOGLEVEL }, - { "language", required_argument, nullptr, OPT_LANGUAGE }, { "langdir", required_argument, nullptr, OPT_LANGDIR }, { "datadir", required_argument, nullptr, OPT_DATADIR }, { "savedir", required_argument, nullptr, OPT_SAVEDIR }, @@ -306,7 +304,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->Message(" -runscene sceneNNN run given scene on start\n"); GetLogger()->Message(" -scenetest win every mission right after it's loaded\n"); GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n"); - GetLogger()->Message(" -language lang set language (one of: en, de, fr, pl, ru)\n"); GetLogger()->Message(" -langdir path set custom language directory path\n"); GetLogger()->Message(" -datadir path set custom data directory path\n"); GetLogger()->Message(" -savedir path set custom save directory path (must be writable)\n"); @@ -371,19 +368,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->SetLogLevel(logLevel); break; } - case OPT_LANGUAGE: - { - Language language; - if (! ParseLanguage(optarg, language)) - { - GetLogger()->Error("Invalid language: '%s'\n", optarg); - return PARSE_ARGS_FAIL; - } - - GetLogger()->Info("Using language %s\n", optarg); - m_language = language; - break; - } case OPT_DATADIR: { m_pathManager->SetDataPath(optarg); @@ -440,7 +424,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) bool CApplication::Create() { std::string path; - bool defaultValues = false; GetLogger()->Info("Creating CApplication\n"); @@ -454,27 +437,10 @@ bool CApplication::Create() if (!GetConfigFile().Init()) { - GetLogger()->Warn("Config not found. Default values will be used!\n"); - defaultValues = true; + GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - if (GetConfigFile().GetStringProperty("Language", "Lang", path)) - { - Language language; - if (ParseLanguage(path, language)) - { - m_language = language; - GetLogger()->Info("Setting language '%s' from ini file\n", path.c_str()); - } - else - { - GetLogger()->Error("Invalid language '%s' in ini file\n", path.c_str()); - } - } - - SetLanguage(m_language); - - //Create the sound instance. + // Create the sound instance. #ifdef OPENAL_SOUND if (!m_headless) { @@ -647,7 +613,7 @@ bool CApplication::Create() m_eventQueue = MakeUnique(); // Create the robot application. - m_controller = MakeUnique(this, !defaultValues); + m_controller = MakeUnique(); if (m_runSceneCategory == LevelCategory::Max) m_controller->StartApp(); @@ -1599,76 +1565,26 @@ char CApplication::GetLanguageChar() const return langChar; } -bool CApplication::ParseLanguage(const std::string& str, Language& language) -{ - if (str == "en") - { - language = LANGUAGE_ENGLISH; - return true; - } - else if (str == "de") - { - language = LANGUAGE_GERMAN; - return true; - } - else if (str == "fr") - { - language = LANGUAGE_FRENCH; - return true; - } - else if (str == "pl") - { - language = LANGUAGE_POLISH; - return true; - } - else if (str == "ru") - { - language = LANGUAGE_RUSSIAN; - return true; - } - - return false; -} - void CApplication::SetLanguage(Language language) { m_language = language; /* Gettext initialization */ - std::string locale = ""; - switch (m_language) + static char envLang[50] = { 0 }; + if (envLang[0] == 0) { - default: - case LANGUAGE_ENV: - locale = ""; - break; - - case LANGUAGE_ENGLISH: - locale = "en_US.utf8"; - break; - - case LANGUAGE_GERMAN: - locale = "de_DE.utf8"; - break; - - case LANGUAGE_FRENCH: - locale = "fr_FR.utf8"; - break; - - case LANGUAGE_POLISH: - locale = "pl_PL.utf8"; - break; - - case LANGUAGE_RUSSIAN: - locale = "ru_RU.utf8"; - break; + // Get this only at the first call, since this code modifies it + const char* currentEnvLang = gl_locale_name(LC_MESSAGES, "LC_MESSAGES"); + if (currentEnvLang != nullptr) + { + strcpy(envLang, currentEnvLang); + } } - if (locale.empty()) + if (language == LANGUAGE_ENV) { - const char* envLang = gl_locale_name(LC_MESSAGES, "LC_MESSAGES"); - if (envLang == nullptr) + if (envLang[0] == 0) { GetLogger()->Error("Failed to get language from environment, setting default language\n"); m_language = LANGUAGE_ENGLISH; @@ -1704,15 +1620,41 @@ void CApplication::SetLanguage(Language language) } } } - else + + std::string locale = ""; + switch (m_language) { - std::string langStr = "LANGUAGE="; - langStr += locale; - strcpy(m_languageLocale, langStr.c_str()); - putenv(m_languageLocale); - GetLogger()->Trace("SetLanguage: Set LANGUAGE=%s in environment\n", locale.c_str()); + default: + locale = ""; + break; + + case LANGUAGE_ENGLISH: + locale = "en_US.utf8"; + break; + + case LANGUAGE_GERMAN: + locale = "de_DE.utf8"; + break; + + case LANGUAGE_FRENCH: + locale = "fr_FR.utf8"; + break; + + case LANGUAGE_POLISH: + locale = "pl_PL.utf8"; + break; + + case LANGUAGE_RUSSIAN: + locale = "ru_RU.utf8"; + break; } + std::string langStr = "LANGUAGE="; + langStr += locale; + strcpy(m_languageLocale, langStr.c_str()); + putenv(m_languageLocale); + GetLogger()->Trace("SetLanguage: Set LANGUAGE=%s in environment\n", locale.c_str()); + char* defaultLocale = setlocale(LC_ALL, ""); // Load system locale GetLogger()->Debug("Default system locale: %s\n", defaultLocale); setlocale(LC_NUMERIC, "C"); // Force numeric locale to "C" (fixes decimal point problems) diff --git a/src/app/app.h b/src/app/app.h index 962bf30c..d8f985e6 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -290,7 +290,6 @@ public: Language GetLanguage() const; char GetLanguageChar() const; void SetLanguage(Language language); - static bool ParseLanguage(const std::string& str, Language& language); //@} //! Management of sleep in main loop (lowers CPU usage) diff --git a/src/app/controller.cpp b/src/app/controller.cpp index 520e925d..0e2f8f07 100644 --- a/src/app/controller.cpp +++ b/src/app/controller.cpp @@ -26,25 +26,15 @@ -CController::CController(CApplication* app, bool configLoaded) - : m_app(app) +CController::CController() { m_main = MakeUnique(); - if (configLoaded) - m_main->LoadConfigFile(); - else - m_main->CreateConfigFile(); } CController::~CController() { } -CApplication* CController::GetApplication() -{ - return m_app; -} - CRobotMain* CController::GetRobotMain() { return m_main.get(); diff --git a/src/app/controller.h b/src/app/controller.h index c061d220..985bae3c 100644 --- a/src/app/controller.h +++ b/src/app/controller.h @@ -40,11 +40,9 @@ struct Event; class CController { public: - CController(CApplication* app, bool configLoaded = true); + CController(); ~CController(); - //! Return CApplication instance - CApplication* GetApplication(); //! Return CRobotMain instance CRobotMain* GetRobotMain(); diff --git a/src/common/event.cpp b/src/common/event.cpp index 1684ad5e..6e5398d7 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -227,6 +227,7 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING] = "EVENT_INTERFACE_SHADOW_MAPPING"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING_QUALITY] = "EVENT_INTERFACE_SHADOW_MAPPING_QUALITY"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING_BUFFER] = "EVENT_INTERFACE_SHADOW_MAPPING_BUFFER"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_LANGUAGE] = "EVENT_INTERFACE_LANGUAGE"; EVENT_TYPE_TEXT[EVENT_INTERFACE_KINFO1] = "EVENT_INTERFACE_KINFO1"; EVENT_TYPE_TEXT[EVENT_INTERFACE_KINFO2] = "EVENT_INTERFACE_KINFO2"; diff --git a/src/common/event.h b/src/common/event.h index 88fd2821..e458dfda 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -255,6 +255,7 @@ enum EventType EVENT_INTERFACE_SHADOW_MAPPING = 787, EVENT_INTERFACE_SHADOW_MAPPING_QUALITY = 788, EVENT_INTERFACE_SHADOW_MAPPING_BUFFER = 789, + EVENT_INTERFACE_LANGUAGE = 790, EVENT_INTERFACE_KINFO1 = 500, EVENT_INTERFACE_KINFO2 = 501, diff --git a/src/common/language.cpp b/src/common/language.cpp new file mode 100644 index 00000000..94a8e484 --- /dev/null +++ b/src/common/language.cpp @@ -0,0 +1,54 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "common/language.h" + +#include + +const std::map LANGUAGE_MAP = { + { LANGUAGE_ENGLISH, "en" }, + { LANGUAGE_GERMAN, "de" }, + { LANGUAGE_FRENCH, "fr" }, + { LANGUAGE_POLISH, "pl" }, + { LANGUAGE_RUSSIAN, "ru" } +}; + +bool ParseLanguage(const std::string& str, Language& language) +{ + for (auto it = LANGUAGE_MAP.begin(); it != LANGUAGE_MAP.end(); ++it) + { + if (it->second == str) + { + language = it->first; + return true; + } + } + + return false; +} + +bool LanguageToString(const Language& language, std::string& str) +{ + if (LANGUAGE_MAP.count(language) > 0) + { + str = LANGUAGE_MAP.at(language); + return true; + } + return false; +} \ No newline at end of file diff --git a/src/common/language.h b/src/common/language.h index 42f213a9..b0988c54 100644 --- a/src/common/language.h +++ b/src/common/language.h @@ -1,6 +1,6 @@ /* * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam + * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify @@ -19,6 +19,8 @@ #pragma once +#include + /** * \enum Language * \brief Application language @@ -32,3 +34,6 @@ enum Language LANGUAGE_POLISH = 3, LANGUAGE_RUSSIAN = 4 }; + +bool ParseLanguage(const std::string& str, Language& language); +bool LanguageToString(const Language& language, std::string& str); \ No newline at end of file diff --git a/src/common/settings.cpp b/src/common/settings.cpp index ed2677c9..e845c338 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -23,6 +23,7 @@ #include "app/input.h" #include "common/config_file.h" +#include "common/logger.h" #include "graphics/engine/camera.h" #include "graphics/engine/engine.h" @@ -49,6 +50,8 @@ CSettings::CSettings() m_IODim = Math::Point(320.0f/640.0f, (121.0f+18.0f*8)/480.0f); m_IOPos.x = (1.0f-m_IODim.x)/2.0f; // in the middle m_IOPos.y = (1.0f-m_IODim.y)/2.0f; + + m_language = LANGUAGE_ENV; } void CSettings::SaveResolutionSettings(const Gfx::DeviceConfig& config) @@ -120,6 +123,9 @@ void CSettings::SaveSettings() GetConfigFile().SetFloatProperty("Edit", "IODimX", m_IODim.x); GetConfigFile().SetFloatProperty("Edit", "IODimY", m_IODim.y); + std::string lang = ""; + LanguageToString(m_language, lang); + GetConfigFile().SetStringProperty("Language", "Lang", lang); GetConfigFile().Save(); } @@ -135,6 +141,7 @@ void CSettings::LoadSettings() int iValue = 0; float fValue = 0.0f; bool bValue = false; + std::string sValue = ""; GetConfigFile().GetBoolProperty("Setup", "Tooltips", m_tooltips); GetConfigFile().GetBoolProperty("Setup", "InterfaceGlint", m_interfaceGlint); @@ -269,6 +276,17 @@ void CSettings::LoadSettings() GetConfigFile().GetFloatProperty("Edit", "IOPosY", m_IOPos.y); GetConfigFile().GetFloatProperty("Edit", "IODimX", m_IODim.x); GetConfigFile().GetFloatProperty("Edit", "IODimY", m_IODim.y); + + m_language = LANGUAGE_ENV; + if (GetConfigFile().GetStringProperty("Language", "Lang", sValue)) + { + if (!sValue.empty() && !ParseLanguage(sValue, m_language)) + { + GetLogger()->Error("Failed to parse language '%s' from config file. Default language will be used.\n", + sValue.c_str()); + } + } + app->SetLanguage(m_language); } void CSettings::SetTooltips(bool tooltips) @@ -392,3 +410,14 @@ Math::Point CSettings::GetIODim() { return m_IODim; } + +void CSettings::SetLanguage(Language language) +{ + m_language = language; + CApplication::GetInstancePointer()->SetLanguage(m_language); +} + +Language CSettings::GetLanguage() +{ + return m_language; +} diff --git a/src/common/settings.h b/src/common/settings.h index a6006e41..018f9af2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -19,6 +19,7 @@ #pragma once +#include "common/language.h" #include "common/singleton.h" #include "math/point.h" @@ -80,6 +81,9 @@ public: Math::Point GetIODim(); //@} + void SetLanguage(Language language); + Language GetLanguage(); + protected: bool m_tooltips; bool m_interfaceGlint; @@ -94,4 +98,6 @@ protected: bool m_IOPublic; Math::Point m_IOPos; Math::Point m_IODim; + + Language m_language; }; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 09c6c973..2bedfaf9 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -262,6 +262,10 @@ CRobotMain::CRobotMain() m_engine->SetTracePrecision(1.0f); + m_settings->LoadSettings(); + m_settings->SaveSettings(); + m_settings->SaveResolutionSettings(m_app->GetVideoConfig()); + SelectPlayer(CPlayerProfile::GetLastName()); CScriptFunctions::Init(); @@ -321,21 +325,6 @@ void CRobotMain::ReloadAllTextures() } } - -//! Creates the file colobot.ini at the first time -void CRobotMain::CreateConfigFile() -{ - m_settings->SaveSettings(); - m_settings->SaveResolutionSettings(m_app->GetVideoConfig()); - - GetConfigFile().Save(); -} - -void CRobotMain::LoadConfigFile() -{ - m_settings->LoadSettings(); -} - std::string PhaseToString(Phase phase) { if (phase == PHASE_WELCOME1) return "PHASE_WELCOME1"; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 7fce8142..fe7e40ec 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -160,9 +160,6 @@ public: Ui::CDisplayText* GetDisplayText(); CPauseManager* GetPauseManager(); - void CreateConfigFile(); - void LoadConfigFile(); - void ResetAfterVideoConfigChanged(); void ReloadAllTextures(); diff --git a/src/ui/screen/screen_setup_game.cpp b/src/ui/screen/screen_setup_game.cpp index db5e1547..5e3994a5 100644 --- a/src/ui/screen/screen_setup_game.cpp +++ b/src/ui/screen/screen_setup_game.cpp @@ -31,6 +31,7 @@ #include "ui/controls/check.h" #include "ui/controls/interface.h" #include "ui/controls/label.h" +#include "ui/controls/list.h" #include "ui/controls/slider.h" #include "ui/controls/window.h" @@ -52,6 +53,7 @@ void CScreenSetupGame::CreateInterface() CLabel* pl; CCheck* pc; CSlider* psl; + CList* pli; Math::Point pos, ddim; std::string name; @@ -131,6 +133,19 @@ void CScreenSetupGame::CreateInterface() pos.y -= 0.048f; pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_EDITVALUE); pc->SetState(STATE_SHADOW); + pos.y -= 0.048f; + + ddim.y = dim.y*3.0f; + pos.y -= ddim.y; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LANGUAGE); + pli->SetState(STATE_SHADOW); + // TODO: Add something like GetSupportedLanguages() and GetLanguageFriendlyName() for this + pli->SetItemName(1+LANGUAGE_ENV, "[System default]"); + pli->SetItemName(1+LANGUAGE_ENGLISH, "English"); + pli->SetItemName(1+LANGUAGE_FRENCH, "French"); + pli->SetItemName(1+LANGUAGE_GERMAN, "German"); + pli->SetItemName(1+LANGUAGE_POLISH, "Polish"); + pli->SetItemName(1+LANGUAGE_RUSSIAN, "Russian"); UpdateSetupButtons(); } @@ -227,11 +242,8 @@ bool CScreenSetupGame::EventProcess(const Event &event) break; case EVENT_INTERFACE_AUTOSAVE_INTERVAL: - ChangeSetupButtons(); - UpdateSetupButtons(); - break; - case EVENT_INTERFACE_AUTOSAVE_SLOTS: + case EVENT_INTERFACE_LANGUAGE: ChangeSetupButtons(); UpdateSetupButtons(); break; @@ -249,6 +261,7 @@ void CScreenSetupGame::UpdateSetupButtons() CWindow* pw; CCheck* pc; CSlider* ps; + CList* pli; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -345,6 +358,12 @@ void CScreenSetupGame::UpdateSetupButtons() ps->SetState(STATE_ENABLE, m_main->GetAutosave()); ps->SetVisibleValue(m_main->GetAutosaveSlots()); } + + pli = static_cast(pw->SearchControl(EVENT_INTERFACE_LANGUAGE)); + if ( pli != nullptr ) + { + pli->SetSelect(1+m_settings->GetLanguage()); + } } // Updates the engine function of the buttons after the setup phase. @@ -353,6 +372,7 @@ void CScreenSetupGame::ChangeSetupButtons() { CWindow* pw; CSlider* ps; + CList* pli; float value; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -371,6 +391,14 @@ void CScreenSetupGame::ChangeSetupButtons() value = ps->GetVisibleValue(); m_main->SetAutosaveSlots(static_cast(round(value))); } + + pli = static_cast(pw->SearchControl(EVENT_INTERFACE_LANGUAGE)); + if ( pli != nullptr ) + { + m_settings->SetLanguage(static_cast(pli->GetSelect()-1)); + // TODO: A really ugly way to apply the change immediately + m_main->ChangePhase(m_main->GetPhase()); + } } } // namespace Ui