From dfea33df8440bb98486596116cad31e72660c177 Mon Sep 17 00:00:00 2001 From: codereader Date: Thu, 9 Nov 2017 08:19:40 +0100 Subject: [PATCH] WIP Path handling. Added code to derive some defaults, fs_game / fs_game_base, etc. --- include/igame.h | 2 + radiant/settings/GameManager.cpp | 88 ++++++++++++++----- radiant/ui/prefdialog/GameSetupDialog.cpp | 52 ++++++++++- radiant/ui/prefdialog/GameSetupDialog.h | 6 +- radiant/ui/prefdialog/GameSetupPage.h | 3 + radiant/ui/prefdialog/GameSetupPageIdTech.cpp | 61 +++++++++++-- radiant/ui/prefdialog/GameSetupPageIdTech.h | 4 +- 7 files changed, 182 insertions(+), 34 deletions(-) diff --git a/include/igame.h b/include/igame.h index d4d7bb5a8f..9d5c177d33 100644 --- a/include/igame.h +++ b/include/igame.h @@ -11,6 +11,8 @@ const char* const RKEY_GAME_TYPE = "user/game/type"; const char* const RKEY_FS_GAME = "user/game/fs_game"; const char* const RKEY_FS_GAME_BASE = "user/game/fs_game_base"; const char* const RKEY_ENGINE_PATH = "user/paths/enginePath"; +const char* const RKEY_MOD_PATH = "user/paths/modPath"; +const char* const RKEY_MOD_BASE_PATH = "user/paths/modBasePath"; namespace game { diff --git a/radiant/settings/GameManager.cpp b/radiant/settings/GameManager.cpp index b66e32a07f..16bfc27ba0 100644 --- a/radiant/settings/GameManager.cpp +++ b/radiant/settings/GameManager.cpp @@ -13,6 +13,7 @@ #include "os/dir.h" #include "os/path.h" #include "os/fs.h" +#include "string/trim.h" #include "string/convert.h" #include "string/replace.h" #include "string/predicate.h" @@ -212,6 +213,7 @@ std::string Manager::getUserEnginePath() void Manager::constructPaths() { +#if 0 _enginePath = GlobalRegistry().get(RKEY_ENGINE_PATH); // Make sure it's a well formatted path @@ -248,10 +250,12 @@ void Manager::constructPaths() // No fs_game, no modpath _modPath = ""; } +#endif } bool Manager::userWantsToCorrectSettings() const { +#if 0 std::stringstream msg("Warning:\n"); if (!os::fileOrDirExists(_enginePath)) @@ -273,69 +277,106 @@ bool Manager::userWantsToCorrectSettings() const return wxutil::Messagebox::Show(_("Invalid Settings"), msg.str(), ui::IDialog::MESSAGE_ASK, NULL) == ui::IDialog::RESULT_YES; +#endif + return false; } void Manager::initEnginePath() { // Try to retrieve a saved value for the engine path - std::string enginePath = GlobalRegistry().get(RKEY_ENGINE_PATH); + _enginePath = registry::getValue(RKEY_ENGINE_PATH); + _modPath = registry::getValue(RKEY_MOD_PATH); + _modBasePath = registry::getValue(RKEY_MOD_BASE_PATH); + xml::NodeList gameNodeList = GlobalRegistry().findXPath("game"); - if (enginePath.empty() && gameNodeList.size() > 0) { + if (_enginePath.empty() && !gameNodeList.empty()) + { // No engine path known, but we have a valid game description // Try to deduce the engine path from the Registry settings (Win32 only) std::string regKey = gameNodeList[0].getAttributeValue("registryKey"); std::string regValue = gameNodeList[0].getAttributeValue("registryValue"); rMessage() << "GameManager: Querying Windows Registry for game path: " - << "HKEY_LOCAL_MACHINE\\" - << regKey << "\\" << regValue << std::endl; + << "HKEY_LOCAL_MACHINE\\" + << regKey << "\\" << regValue << std::endl; // Query the Windows Registry for a default installation path // This will return "" for non-Windows environments - enginePath = Win32Registry::getKeyValue(regKey, regValue); + _enginePath = Win32Registry::getKeyValue(regKey, regValue); rMessage() << "GameManager: Windows Registry returned result: " - << enginePath << std::endl; + << _enginePath << std::endl; } // If the engine path is still empty, consult the .game file for a fallback value - if (enginePath.empty()) { + if (_enginePath.empty()) + { // No engine path set so far, search the game file for default values const std::string ENGINEPATH_ATTRIBUTE = #if defined(WIN32) - "enginepath_win32" + "enginepath_win32" #elif defined(__linux__) || defined (__FreeBSD__) - "enginepath_linux" + "enginepath_linux" #elif defined(__APPLE__) - "enginepath_macos" + "enginepath_macos" #else #error "unknown platform" #endif - ; + ; - enginePath = os::standardPathWithSlash( + _enginePath = os::standardPathWithSlash( currentGame()->getKeyValue(ENGINEPATH_ATTRIBUTE) ); } // Normalise the path in any case - enginePath = os::standardPathWithSlash(enginePath); + _enginePath = os::standardPathWithSlash(_enginePath); // Take this path and store it into the Registry, it's expected to be there - GlobalRegistry().set(RKEY_ENGINE_PATH, enginePath); + registry::setValue(RKEY_ENGINE_PATH, _enginePath); +#if 0 // Try to do something with the information currently in the Registry // It should be enough to know the engine path and the fs_game. constructPaths(); - +#endif +#if 0 // Check loop, continue, till the user specifies a valid setting while (!settingsValid()) { - // Engine path doesn't exist, ask the user - ui::GameSetupDialog::Show(cmd::ArgumentList()); - //ui::PrefDialog::ShowDialog(_("Game")); +#endif + if (!settingsValid()) + { + // Paths not valid, ask the user to select something + ui::GameSetupDialog::Result result = ui::GameSetupDialog::Show(cmd::ArgumentList()); + + if (!result.enginePath.empty()) + { + _currentGameName = result.gameName; + _enginePath = result.enginePath; + _modBasePath = result.modBasePath; + _modPath = result.modPath; + + // Persist the settings to the registry + registry::setValue(RKEY_GAME_TYPE, _currentGameName); + registry::setValue(RKEY_ENGINE_PATH, _enginePath); + registry::setValue(RKEY_MOD_PATH, _modPath); + registry::setValue(RKEY_MOD_BASE_PATH, _modBasePath); + + // Extract the fs_game / fs_game_base settings from the mod path + std::string fsGame = os::getRelativePath(_modPath, _enginePath); + string::trim_right(fsGame, "/"); + + std::string fsGameBase = os::getRelativePath(_modBasePath, _enginePath); + string::trim_right(fsGameBase, "/"); + + registry::setValue(RKEY_FS_GAME, fsGame); + registry::setValue(RKEY_FS_GAME_BASE, fsGameBase); + } + } +#if 0 // After the dialog, the settings are located in the registry. // Construct the paths with the settings found there constructPaths(); @@ -345,6 +386,7 @@ void Manager::initEnginePath() break; } } +#endif // Register as observer, to get notified about future engine path changes observeKey(RKEY_ENGINE_PATH); @@ -385,16 +427,18 @@ void Manager::addVFSSearchPath(const std::string &path) bool Manager::settingsValid() const { - if (os::fileOrDirExists(_enginePath)) { - + if (os::fileOrDirExists(_enginePath)) + { // Check the mod base path, if appropriate - if (!_fsGameBase.empty() && !os::fileOrDirExists(_modBasePath)) { + if (!_modBasePath.empty() && !os::fileOrDirExists(_modBasePath)) + { // Mod base name is not empty, but folder doesnt' exist return false; } // Check the mod path, if appropriate - if (!_fsGame.empty() && !os::fileOrDirExists(_modPath)) { + if (!_modPath.empty() && !os::fileOrDirExists(_modPath)) + { // Mod name is not empty, but mod folder doesnt' exist return false; } diff --git a/radiant/ui/prefdialog/GameSetupDialog.cpp b/radiant/ui/prefdialog/GameSetupDialog.cpp index 2b07abf19a..b5c94da649 100644 --- a/radiant/ui/prefdialog/GameSetupDialog.cpp +++ b/radiant/ui/prefdialog/GameSetupDialog.cpp @@ -27,6 +27,8 @@ GameSetupDialog::GameSetupDialog(wxWindow* parent) : GetSizer()->Add(mainVbox, 1, wxEXPAND | wxALL, 12); _book = new wxChoicebook(this, wxID_ANY); + _book->Connect(wxEVT_CHOICEBOOK_PAGE_CHANGED, wxBookCtrlEventHandler(GameSetupDialog::onPageChanged), nullptr, this); + wxStaticText* label = new wxStaticText(this, wxID_ANY, _("Please select a Game Type:")); mainVbox->Add(label); @@ -40,7 +42,7 @@ GameSetupDialog::GameSetupDialog(wxWindow* parent) : // Create the assign shortcut button wxButton* cancelButton = new wxButton(this, wxID_CANCEL); - cancelButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(GameSetupDialog::onCancel), NULL, this); + cancelButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(GameSetupDialog::onCancel), nullptr, this); buttonHBox->Add(saveButton, 0, wxRIGHT, 6); buttonHBox->Add(cancelButton, 0, wxRIGHT, 6); @@ -88,11 +90,40 @@ void GameSetupDialog::initialiseControls() _book->AddPage(container, game->getKeyValue("name")); } + + // Select the currently active game type + setSelectedPage(registry::getValue(RKEY_GAME_TYPE)); +} + +void GameSetupDialog::setSelectedPage(const std::string& name) +{ + for (std::size_t i = 0; i < _book->GetPageCount(); ++i) + { + wxWindow* container = _book->GetPage(i); + + GameSetupPage* page = dynamic_cast(wxWindow::FindWindowByName("GameSetupPage", container)); + + assert(page != nullptr); + + wxStringClientData* data = static_cast(page->GetClientData()); + + if (data->GetData() == name) + { + _book->SetSelection(i); + page->onPageShown(); + return; + } + } } GameSetupPage* GameSetupDialog::getSelectedPage() { - // Extract the game type value from the current page and save it to the registry + return getPage(_book->GetSelection()); +} + +GameSetupPage* GameSetupDialog::getPage(int num) +{ + // Find the actual GameSetupPage in the window hierarchy wxWindow* container = _book->GetPage(_book->GetSelection()); return dynamic_cast(wxWindow::FindWindowByName("GameSetupPage", container)); } @@ -146,6 +177,19 @@ void GameSetupDialog::onCancel(wxCommandEvent& ev) EndModal(wxID_CANCEL); } +void GameSetupDialog::onPageChanged(wxBookCtrlEvent& ev) +{ + if (ev.GetSelection() != wxNOT_FOUND) + { + GameSetupPage* page = getPage(ev.GetSelection()); + + if (page != nullptr) + { + page->onPageShown(); + } + } +} + GameSetupDialog::Result GameSetupDialog::Show(const cmd::ArgumentList& args) { GameSetupDialog::Result result; @@ -159,9 +203,9 @@ GameSetupDialog::Result GameSetupDialog::Show(const cmd::ArgumentList& args) if (dialog->ShowModal() == wxID_OK) { - result.gameType = dialog->getSelectedGameType(); + result.gameName = dialog->getSelectedGameType(); - if (result.gameType.empty()) + if (result.gameName.empty()) { rError() << "Cannot save game paths, nothing selected" << std::endl; return result; diff --git a/radiant/ui/prefdialog/GameSetupDialog.h b/radiant/ui/prefdialog/GameSetupDialog.h index 8e972d0ce8..a7ed8f6463 100644 --- a/radiant/ui/prefdialog/GameSetupDialog.h +++ b/radiant/ui/prefdialog/GameSetupDialog.h @@ -5,6 +5,7 @@ #include "GameSetupPage.h" class wxChoicebook; +class wxBookCtrlEvent; namespace ui { @@ -35,7 +36,7 @@ class GameSetupDialog : // The result after the user is done with the dialog struct Result { - std::string gameType; // Display name of the selected game + std::string gameName; // Display name of the selected game std::string enginePath; // selected engine path std::string modPath; // selected mod path std::string modBasePath; // selected mod base path @@ -47,11 +48,14 @@ class GameSetupDialog : private: GameSetupPage* getSelectedPage(); + GameSetupPage* getPage(int num); + void setSelectedPage(const std::string& name); void initialiseControls(); void onSave(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); + void onPageChanged(wxBookCtrlEvent& ev); }; } diff --git a/radiant/ui/prefdialog/GameSetupPage.h b/radiant/ui/prefdialog/GameSetupPage.h index 778e96e5a2..ac2dd8f6c4 100644 --- a/radiant/ui/prefdialog/GameSetupPage.h +++ b/radiant/ui/prefdialog/GameSetupPage.h @@ -39,6 +39,9 @@ class GameSetupPage : // GameSettingsInvalidException in case something is not correct. virtual void validateSettings() = 0; + // Called by the owning GameSetupDialog when this page is selected + virtual void onPageShown() = 0; + // The following three path accessors are needed by the owning GameManager // to continue setting up the VFS search order, map paths, etc. diff --git a/radiant/ui/prefdialog/GameSetupPageIdTech.cpp b/radiant/ui/prefdialog/GameSetupPageIdTech.cpp index a497bf39a0..96fd799d64 100644 --- a/radiant/ui/prefdialog/GameSetupPageIdTech.cpp +++ b/radiant/ui/prefdialog/GameSetupPageIdTech.cpp @@ -12,7 +12,9 @@ #include #include +#include "string/trim.h" #include "os/file.h" +#include "os/path.h" #include "registry/Widgets.h" namespace ui @@ -27,19 +29,22 @@ GameSetupPageIdTech::GameSetupPageIdTech(wxWindow* parent) : wxFlexGridSizer* table = new wxFlexGridSizer(3, 2, wxSize(6, 6)); this->SetSizer(table); - _enginePathEntry = createPathEntry(RKEY_ENGINE_PATH); + _enginePathEntry = new wxutil::PathEntry(this, true); + _enginePathEntry->getEntryWidget()->SetMinClientSize( + wxSize(_enginePathEntry->getEntryWidget()->GetCharWidth() * 30, -1)); + table->Add(new wxStaticText(this, wxID_ANY, _("Engine Path")), 0, wxALIGN_CENTRE_VERTICAL); table->Add(_enginePathEntry, 0); - _fsGameEntry = createEntry(RKEY_FS_GAME); + _fsGameEntry = new wxTextCtrl(this, wxID_ANY); + _fsGameEntry->SetMinClientSize(wxSize(_fsGameEntry->GetCharWidth() * 30, -1)); table->Add(new wxStaticText(this, wxID_ANY, _("Mod (fs_game)")), 0, wxALIGN_CENTRE_VERTICAL); table->Add(_fsGameEntry, 0); - _fsGameBaseEntry = createEntry(RKEY_FS_GAME_BASE); + _fsGameBaseEntry = new wxTextCtrl(this, wxID_ANY); + _fsGameBaseEntry->SetMinClientSize(wxSize(_fsGameEntry->GetCharWidth() * 30, -1)); table->Add(new wxStaticText(this, wxID_ANY, _("Mod Base (fs_game_base, optional)")), 0, wxALIGN_CENTRE_VERTICAL); table->Add(_fsGameBaseEntry, 0); - - // TODO: Derive some default values for the paths unless they're already set } const char* GameSetupPageIdTech::TYPE() @@ -99,6 +104,49 @@ std::string GameSetupPageIdTech::getModPath() return _modPath; } +void GameSetupPageIdTech::onPageShown() +{ + // Load the values from the registry if the controls are still empty + if (_enginePathEntry->getValue().empty()) + { + _enginePath = registry::getValue(RKEY_ENGINE_PATH); + + // TODO: If engine path still empty, try to deduce it from defaults/registry + + _enginePathEntry->setValue(_enginePath); + } + + if (_fsGameEntry->GetValue().empty()) + { + // Check if we have a valid mod path + _modPath = registry::getValue(RKEY_MOD_PATH); + + if (!_modPath.empty()) + { + // Extract the fs_game part from the absolute mod path, if possible + std::string fsGame = os::getRelativePath(_modPath, _enginePath); + string::trim_right(fsGame, "/"); + + _fsGameEntry->SetValue(fsGame); + } + } + + if (_fsGameBaseEntry->GetValue().empty()) + { + // Check if we have a valid mod base path + _modBasePath = registry::getValue(RKEY_MOD_BASE_PATH); + + if (!_modBasePath.empty()) + { + std::string fsGameBase = os::getRelativePath(_modBasePath, _enginePath); + string::trim_right(fsGameBase, "/"); + + // Extract the fs_game part from the absolute mod base path, if possible + _fsGameBaseEntry->SetValue(fsGameBase); + } + } +} + void GameSetupPageIdTech::constructPaths() { _enginePath = _enginePathEntry->getEntryWidget()->GetValue().ToStdString(); @@ -139,6 +187,7 @@ void GameSetupPageIdTech::constructPaths() } } +#if 0 wxTextCtrl* GameSetupPageIdTech::createEntry(const std::string& registryKey) { wxTextCtrl* entryWidget = new wxTextCtrl(this, wxID_ANY); @@ -169,5 +218,5 @@ wxutil::PathEntry* GameSetupPageIdTech::createPathEntry(const std::string& regis return entry; } - +#endif } diff --git a/radiant/ui/prefdialog/GameSetupPageIdTech.h b/radiant/ui/prefdialog/GameSetupPageIdTech.h index 1be93ae0a0..775f245427 100644 --- a/radiant/ui/prefdialog/GameSetupPageIdTech.h +++ b/radiant/ui/prefdialog/GameSetupPageIdTech.h @@ -39,6 +39,7 @@ class GameSetupPageIdTech : const char* getType() override; void validateSettings() override; + void onPageShown() override; std::string getEnginePath() override; std::string getModBasePath() override; @@ -46,9 +47,10 @@ class GameSetupPageIdTech : private: void constructPaths(); - +#if 0 wxTextCtrl* createEntry(const std::string& registryKey); wxutil::PathEntry* createPathEntry(const std::string& registryKey); +#endif }; }