diff --git a/CMakeLists.txt b/CMakeLists.txt index 896b7cc5..25e686a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,36 @@ set (LOOT_GUI_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/editor_message.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_handler.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_app.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_scheme_handler_factory.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/apply_sort_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/cancel_find_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/cancel_sort_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/change_game_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/clear_all_metadata_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/clear_plugin_metadata_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/clipboard_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/close_settings_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/copy_content_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/copy_load_order_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/copy_metadata_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/discard_unapplied_changes_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/editor_opened_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/editor_closed_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_conflicting_plugins_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_game_data_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_game_types_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_init_errors_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_installed_games_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_languages_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_settings_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/get_version_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/metadata_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/open_log_location_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/open_readme_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/redate_plugins_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/save_filter_state_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/sort_plugins_query.h" + "${CMAKE_SOURCE_DIR}/src/gui/query/update_masterlist_query.h" "${CMAKE_SOURCE_DIR}/src/gui/query_handler.h" "${CMAKE_SOURCE_DIR}/src/gui/resource.h" "${CMAKE_SOURCE_DIR}/src/gui/yaml_simple_message_helpers.h") diff --git a/src/gui/query/apply_sort_query.h b/src/gui/query/apply_sort_query.h new file mode 100644 index 00000000..4123c57e --- /dev/null +++ b/src/gui/query/apply_sort_query.h @@ -0,0 +1,49 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_APPLY_SORT_QUERY +#define LOOT_GUI_QUERY_APPLY_SORT_QUERY + +#include "gui/query/query.h" + +namespace loot { +class ApplySortQuery : public Query { +public: + ApplySortQuery(LootState& state, const std::vector& plugins) : + state_(state), plugins_(plugins) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(trace) << "User has accepted sorted load order, applying it."; + state_.decrementUnappliedChangeCounter(); + state_.getCurrentGame().SetLoadOrder(plugins_); + callback->Success(""); + } + +private: + LootState& state_; + std::vector plugins_; +}; +} + +#endif diff --git a/src/gui/query/cancel_find_query.h b/src/gui/query/cancel_find_query.h new file mode 100644 index 00000000..f2b70450 --- /dev/null +++ b/src/gui/query/cancel_find_query.h @@ -0,0 +1,47 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CANCEL_FIND_QUERY +#define LOOT_GUI_QUERY_CANCEL_FIND_QUERY + +#include + +#include "gui/query/query.h" + +namespace loot { +class CancelFindQuery : public Query { +public: + CancelFindQuery(CefRefPtr browser) : browser_(browser) {} + + void execute(CefRefPtr callback) { + browser_->GetHost()->StopFinding(true); + callback->Success(""); + } + +private: + CefRefPtr browser_; +}; +} + +#endif diff --git a/src/gui/query/cancel_sort_query.h b/src/gui/query/cancel_sort_query.h new file mode 100644 index 00000000..ac32a4c8 --- /dev/null +++ b/src/gui/query/cancel_sort_query.h @@ -0,0 +1,52 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CANCEL_SORT_QUERY +#define LOOT_GUI_QUERY_CANCEL_SORT_QUERY + +#include "backend/app/loot_state.h" +#include "backend/helpers/json.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class CancelSortQuery : public MetadataQuery { +public: + CancelSortQuery(LootState& state) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + state_(state) {} + + void execute(CefRefPtr callback) { + state_.decrementUnappliedChangeCounter(); + state_.getCurrentGame().DecrementLoadOrderSortCount(); + + YAML::Node node(getGeneralMessages()); + callback->Success(JSON::stringify(node)); + } + +private: + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/change_game_query.h b/src/gui/query/change_game_query.h new file mode 100644 index 00000000..e618fc11 --- /dev/null +++ b/src/gui/query/change_game_query.h @@ -0,0 +1,53 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CHANGE_GAME_QUERY +#define LOOT_GUI_QUERY_CHANGE_GAME_QUERY + +#include + +#include "backend/helpers/json.h" +#include "gui/query/get_game_data_query.h" + +namespace loot { +class ChangeGameQuery : public GetGameDataQuery { +public: + ChangeGameQuery(LootState& state, CefRefPtr frame, const std::string& gameFolder) : + GetGameDataQuery(state, frame), + state_(state), + gameFolder_(gameFolder) {} + + void execute(CefRefPtr callback) { + state_.changeGame(gameFolder_); + + GetGameDataQuery::execute(callback); + } + +private: + LootState& state_; + const std::string gameFolder_; +}; +} + +#endif diff --git a/src/gui/query/clear_all_metadata_query.h b/src/gui/query/clear_all_metadata_query.h new file mode 100644 index 00000000..231b517d --- /dev/null +++ b/src/gui/query/clear_all_metadata_query.h @@ -0,0 +1,83 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY +#define LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY + +#include "backend/game/game.h" +#include "backend/helpers/json.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class ClearAllMetadataQuery : public MetadataQuery { +public: + ClearAllMetadataQuery(LootState& state) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + game_(state.getCurrentGame()) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(debug) << "Clearing all user metadata."; + + // Record which plugins have userlist entries. + auto userlistPluginNames = getUserlistPluginNames(); + + // Clear the user metadata. + game_.GetUserlist().Clear(); + game_.GetUserlist().Save(game_.UserlistPath()); + + BOOST_LOG_TRIVIAL(trace) << "Rederiving display metadata for " << userlistPluginNames.size() << " plugins that had user metadata."; + callback->Success(getDerivedMetadataJson(userlistPluginNames)); + } + +private: + std::vector getUserlistPluginNames() const { + auto userlistPlugins = game_.GetUserlist().Plugins(); + std::vector userlistPluginNames(userlistPlugins.size()); + std::transform(begin(userlistPlugins), + end(userlistPlugins), + begin(userlistPluginNames), + [](const PluginMetadata& plugin) { + return plugin.Name(); + }); + + return userlistPluginNames; + } + + std::string getDerivedMetadataJson(const std::vector& userlistPluginNames) { + YAML::Node pluginsNode; + for (const auto &pluginName : userlistPluginNames) { + pluginsNode.push_back(generateDerivedMetadata(pluginName)); + } + + if (pluginsNode.size() > 0) + return JSON::stringify(pluginsNode); + else + return "[]"; + } + + Game& game_; +}; +} + +#endif diff --git a/src/gui/query/clear_plugin_metadata_query.h b/src/gui/query/clear_plugin_metadata_query.h new file mode 100644 index 00000000..423ab51c --- /dev/null +++ b/src/gui/query/clear_plugin_metadata_query.h @@ -0,0 +1,60 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY +#define LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY + +#include "backend/game/game.h" +#include "backend/helpers/json.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class ClearPluginMetadataQuery : public MetadataQuery { +public: + ClearPluginMetadataQuery(LootState& state, const std::string& pluginName) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + game_(state.getCurrentGame()), + pluginName_(pluginName) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(debug) << "Clearing user metadata for plugin " << pluginName_; + + game_.GetUserlist().ErasePlugin(PluginMetadata(pluginName_)); + game_.GetUserlist().Save(game_.UserlistPath()); + + // Now rederive the displayed metadata from the masterlist. + YAML::Node derivedMetadata = generateDerivedMetadata(pluginName_); + if (derivedMetadata.size() > 0) + callback->Success(JSON::stringify(derivedMetadata)); + else + callback->Success("null"); + } + +private: + Game& game_; + std::string pluginName_; +}; +} + +#endif diff --git a/src/gui/query/clipboard_query.h b/src/gui/query/clipboard_query.h new file mode 100644 index 00000000..6eb0e076 --- /dev/null +++ b/src/gui/query/clipboard_query.h @@ -0,0 +1,64 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CLIPBOARD_QUERY +#define LOOT_GUI_QUERY_CLIPBOARD_QUERY + +#include "backend/helpers/helpers.h" +#include "gui/query/query.h" +#include "loot/error.h" + +namespace loot { +class ClipboardQuery : public Query { +protected: + void copyToClipboard(const std::string& text) { +#ifdef _WIN32 + if (!OpenClipboard(NULL)) { + throw Error(Error::Code::windows_error, "Failed to open the Windows clipboard."); + } + + if (!EmptyClipboard()) { + throw Error(Error::Code::windows_error, "Failed to empty the Windows clipboard."); + } + + // The clipboard takes a Unicode (ie. UTF-16) string that it then owns and must not + // be destroyed by LOOT. Convert the string, then copy it into a new block of + // memory for the clipboard. + std::wstring wtext = ToWinWide(text); + wchar_t * wcstr = new wchar_t[wtext.length() + 1]; + wcscpy(wcstr, wtext.c_str()); + + if (SetClipboardData(CF_UNICODETEXT, wcstr) == NULL) { + throw Error(Error::Code::windows_error, "Failed to copy metadata to the Windows clipboard."); + } + + if (!CloseClipboard()) { + throw Error(Error::Code::windows_error, "Failed to close the Windows clipboard."); + } +#endif + } +}; +} + +#endif diff --git a/src/gui/query/close_settings_query.h b/src/gui/query/close_settings_query.h new file mode 100644 index 00000000..d897a605 --- /dev/null +++ b/src/gui/query/close_settings_query.h @@ -0,0 +1,50 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_CLOSE_SETTINGS_QUERY +#define LOOT_GUI_QUERY_CLOSE_SETTINGS_QUERY + +#include "backend/app/loot_state.h" +#include "gui/query/get_installed_games_query.h" + +namespace loot { +class CloseSettingsQuery : public GetInstalledGamesQuery { +public: + CloseSettingsQuery(LootState& state, YAML::Node settings) : + GetInstalledGamesQuery(state), state_(state), settings_(settings) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object."; + state_.load(settings_); + + GetInstalledGamesQuery::execute(callback); + } + +private: + LootState& state_; + YAML::Node settings_; +}; +} + +#endif diff --git a/src/gui/query/copy_content_query.h b/src/gui/query/copy_content_query.h new file mode 100644 index 00000000..be7f95d6 --- /dev/null +++ b/src/gui/query/copy_content_query.h @@ -0,0 +1,60 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_COPY_CONTENT_QUERY +#define LOOT_GUI_QUERY_COPY_CONTENT_QUERY + +#include + +#include "gui/query/clipboard_query.h" + +namespace loot { +class CopyContentQuery : public ClipboardQuery { +public: + CopyContentQuery(const YAML::Node& content) : content_(content) {} + + void execute(CefRefPtr callback) { + std::string text = "[spoiler][code]" + getContentAsText() + "[/code][/spoiler]"; + + copyToClipboard(text); + callback->Success(""); + } + +private: + std::string getContentAsText() const { + YAML::Emitter out; + out.SetIndent(2); + out << content_; + + std::string text = out.c_str(); + boost::replace_all(text, "! ", ""); + + return text; + } + + YAML::Node content_; +}; +} + +#endif diff --git a/src/gui/query/copy_load_order_query.h b/src/gui/query/copy_load_order_query.h new file mode 100644 index 00000000..ab266d39 --- /dev/null +++ b/src/gui/query/copy_load_order_query.h @@ -0,0 +1,74 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_COPY_LOAD_ORDER_QUERY +#define LOOT_GUI_QUERY_COPY_LOAD_ORDER_QUERY + +#include "gui/query/clipboard_query.h" + +namespace loot { +class CopyLoadOrderQuery : public ClipboardQuery { +public: + CopyLoadOrderQuery(LootState& state, const std::vector& plugins) : + state_(state), plugins_(plugins) {} + + void execute(CefRefPtr callback) { + int numberOfIndexDigits = getNumberOfIndexDigits(); + + size_t activeIndex = 0; + std::stringstream stream; + for (const auto& pluginName : plugins_) { + activeIndex += writePluginLine(stream, pluginName, activeIndex); + } + + copyToClipboard(stream.str()); + callback->Success(""); + } + +private: + unsigned short getNumberOfIndexDigits() const { + if (plugins_.size() > 99) + return 3; + else if (plugins_.size() > 9) + return 2; + else + return 1; + } + + size_t writePluginLine(std::ostream& stream, const std::string& plugin, size_t activeIndex) { + if (state_.getCurrentGame().IsPluginActive(plugin)) { + stream << std::setw(getNumberOfIndexDigits()) << activeIndex << " " << std::hex << std::setw(2) << activeIndex << std::dec << " " << plugin << "\r\n"; + return 1; + } + + stream << std::setw(getNumberOfIndexDigits() + 4) << " " << plugin << "\r\n"; + return 0; + } + + LootState& state_; + std::vector plugins_; +}; +} + +#endif diff --git a/src/gui/query/copy_metadata_query.h b/src/gui/query/copy_metadata_query.h new file mode 100644 index 00000000..2d561034 --- /dev/null +++ b/src/gui/query/copy_metadata_query.h @@ -0,0 +1,70 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_COPY_METADATA_QUERY +#define LOOT_GUI_QUERY_COPY_METADATA_QUERY + +#include + +#include "gui/query/clipboard_query.h" + +namespace loot { +class CopyMetadataQuery : public ClipboardQuery { +public: + CopyMetadataQuery(LootState& state, const std::string& pluginName) : + state_(state), pluginName_(pluginName) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(debug) << "Copying metadata for plugin " << pluginName_; + + // Get metadata from masterlist and userlist. + PluginMetadata metadata = state_.getCurrentGame().GetMasterlist().FindPlugin(pluginName_); + metadata.MergeMetadata(state_.getCurrentGame().GetUserlist().FindPlugin(pluginName_)); + + // Generate text representation. + std::string text = "[spoiler][code]" + asText(metadata) + "[/code][/spoiler]"; + + copyToClipboard(text); + + BOOST_LOG_TRIVIAL(info) << "Exported userlist metadata text for \"" << pluginName_ << "\": " << text; + } + +private: + static std::string asText(const PluginMetadata& metadata) { + YAML::Emitter out; + out.SetIndent(2); + out << metadata; + + std::string text = out.c_str(); + boost::replace_all(text, "! ", ""); + + return text; + } + + LootState& state_; + std::string pluginName_; +}; +} + +#endif diff --git a/src/gui/query/discard_unapplied_changes_query.h b/src/gui/query/discard_unapplied_changes_query.h new file mode 100644 index 00000000..ed9b66b0 --- /dev/null +++ b/src/gui/query/discard_unapplied_changes_query.h @@ -0,0 +1,47 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_DISCARD_UNAPPLIED_CHANGES_QUERY +#define LOOT_GUI_QUERY_DISCARD_UNAPPLIED_CHANGES_QUERY + +#include "backend/app/loot_state.h" +#include "gui/query/query.h" + +namespace loot { +class DiscardUnappliedChangesQuery : public Query { +public: + DiscardUnappliedChangesQuery(LootState& state) : state_(state) {} + + void execute(CefRefPtr callback) { + while (state_.hasUnappliedChanges()) + state_.decrementUnappliedChangeCounter(); + callback->Success(""); + } + +private: + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/editor_closed_query.h b/src/gui/query/editor_closed_query.h new file mode 100644 index 00000000..9ee1bbe8 --- /dev/null +++ b/src/gui/query/editor_closed_query.h @@ -0,0 +1,176 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_EDITOR_CLOSED_QUERY +#define LOOT_GUI_QUERY_EDITOR_CLOSED_QUERY + +#include "backend/app/loot_state.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class EditorClosedQuery : public MetadataQuery { +public: + EditorClosedQuery(LootState& state, YAML::Node metadata) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + state_(state), metadata_(metadata) {} + + void execute(CefRefPtr callback) { + try { + callback->Success(applyUserEdits()); + } catch (Error&) { + throw; + } catch (std::exception& e) { + // If this was a YAML conversion error, cut off the line and column numbers, + // since the YAML wasn't written to a file. + std::string error = e.what(); + size_t pos = std::string::npos; + if ((pos = error.find("bad conversion")) != std::string::npos) { + error = error.substr(pos); + } + + throw std::runtime_error(error); + } + state_.decrementUnappliedChangeCounter(); + } + +private: + static PluginMetadata convertMetadata(YAML::Node& newMetadata, PluginMetadata& existingUserMetadata) { + PluginMetadata newUserlistEntry(existingUserMetadata.Name()); + + // First sort out the priority value. This is only given if it was changed. + BOOST_LOG_TRIVIAL(trace) << "Calculating userlist metadata local priority value from Javascript variables."; + if (newMetadata["priority"]) { + BOOST_LOG_TRIVIAL(trace) << "Local priority value was changed, recalculating..."; + // Priority value was changed, so add it to the userlist data. + newUserlistEntry.LocalPriority(Priority(newMetadata["priority"].as())); + } else { + // Priority value wasn't changed, use the existing userlist value. + BOOST_LOG_TRIVIAL(trace) << "Local priority value is unchanged, using existing userlist value (if it exists)."; + newUserlistEntry.LocalPriority(existingUserMetadata.LocalPriority()); + } + + if (newMetadata["globalPriority"]) { + BOOST_LOG_TRIVIAL(trace) << "Global priority value was changed, recalculating..."; + // Priority value was changed, so add it to the userlist data. + newUserlistEntry.GlobalPriority(Priority(newMetadata["globalPriority"].as())); + } else { + BOOST_LOG_TRIVIAL(trace) << "Global priority value is unchanged, using existing userlist value (if it exists)."; + newUserlistEntry.GlobalPriority(existingUserMetadata.GlobalPriority()); + } + + // Now the enabled flag. + newUserlistEntry.Enabled(newMetadata["userlist"]["enabled"].as()); + + // Now metadata lists. These are given in their entirety, so replace anything that + // currently exists. + BOOST_LOG_TRIVIAL(trace) << "Recording metadata lists from Javascript variables."; + if (newMetadata["userlist"]["after"]) + newUserlistEntry.LoadAfter(newMetadata["userlist"]["after"].as>()); + if (newMetadata["userlist"]["req"]) + newUserlistEntry.Reqs(newMetadata["userlist"]["req"].as>()); + if (newMetadata["userlist"]["inc"]) + newUserlistEntry.Incs(newMetadata["userlist"]["inc"].as>()); + + if (newMetadata["userlist"]["msg"]) + newUserlistEntry.Messages(toMessages(newMetadata["userlist"]["msg"].as>())); + if (newMetadata["userlist"]["tag"]) + newUserlistEntry.Tags(newMetadata["userlist"]["tag"].as>()); + if (newMetadata["userlist"]["dirty"]) + newUserlistEntry.DirtyInfo(newMetadata["userlist"]["dirty"].as>()); + if (newMetadata["userlist"]["clean"]) + newUserlistEntry.CleanInfo(newMetadata["userlist"]["clean"].as>()); + if (newMetadata["userlist"]["url"]) + newUserlistEntry.Locations(newMetadata["userlist"]["url"].as>()); + + return newUserlistEntry; + } + + PluginMetadata getUniqueMetadata(const PluginMetadata& metadata) { + BOOST_LOG_TRIVIAL(trace) << "Removing any user metadata that duplicates masterlist metadata."; + try { + Plugin tempPlugin(state_.getCurrentGame().GetPlugin(metadata.Name())); + tempPlugin.MergeMetadata(state_.getCurrentGame().GetMasterlist().FindPlugin(metadata)); + return metadata.NewMetadata(tempPlugin); + } catch (...) { + return metadata.NewMetadata(state_.getCurrentGame().GetMasterlist().FindPlugin(metadata)); + } + } + + std::string applyUserEdits() { + if (!metadata_.IsMap()) // No edits to apply. + return "null"; + + const std::string pluginName = metadata_["name"].as(); + + BOOST_LOG_TRIVIAL(trace) << "Applying user edits for: " << pluginName; + + // Find existing userlist entry. + PluginMetadata ulistPlugin = state_.getCurrentGame().GetUserlist().FindPlugin(pluginName); + + // Create new object for userlist entry. + PluginMetadata newMetadata = getUniqueMetadata(convertMetadata(metadata_, ulistPlugin)); + + // Now erase any existing userlist entry. + if (!ulistPlugin.HasNameOnly()) { + BOOST_LOG_TRIVIAL(trace) << "Erasing the existing userlist entry."; + state_.getCurrentGame().GetUserlist().ErasePlugin(ulistPlugin); + } + + // Add a new userlist entry if necessary. + if (!newMetadata.HasNameOnly()) { + BOOST_LOG_TRIVIAL(trace) << "Adding new metadata to new userlist entry."; + state_.getCurrentGame().GetUserlist().AddPlugin(newMetadata); + } + + // Save edited userlist. + state_.getCurrentGame().GetUserlist().Save(state_.getCurrentGame().UserlistPath()); + + // Now rederive the derived metadata. + BOOST_LOG_TRIVIAL(trace) << "Returning newly derived display metadata."; + YAML::Node derivedMetadata = generateDerivedMetadata(newMetadata.Name()); + if (derivedMetadata.size() > 0) + return JSON::stringify(derivedMetadata); + else + return "null"; + } + + static std::vector toMessages(std::vector messages) { + std::vector list; + + for (const auto& message : messages) { + list.push_back(Message( + message.type, + {{message.text, message.language}}, + message.condition)); + } + + return list; + } + + LootState& state_; + YAML::Node metadata_; +}; +} + +#endif diff --git a/src/gui/query/editor_opened_query.h b/src/gui/query/editor_opened_query.h new file mode 100644 index 00000000..dd7da973 --- /dev/null +++ b/src/gui/query/editor_opened_query.h @@ -0,0 +1,46 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_EDITOR_OPENED_QUERY +#define LOOT_GUI_QUERY_EDITOR_OPENED_QUERY + +#include "backend/app/loot_state.h" +#include "gui/query/query.h" + +namespace loot { +class EditorOpenedQuery : public Query { +public: + EditorOpenedQuery(LootState& state) : state_(state) {} + + void execute(CefRefPtr callback) { + state_.incrementUnappliedChangeCounter(); + callback->Success(""); + } + +private: + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/get_conflicting_plugins_query.h b/src/gui/query/get_conflicting_plugins_query.h new file mode 100644 index 00000000..945e62fa --- /dev/null +++ b/src/gui/query/get_conflicting_plugins_query.h @@ -0,0 +1,83 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY +#define LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY + +#include "backend/game/game.h" +#include "backend/helpers/json.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class GetConflictingPluginsQuery : public MetadataQuery { +public: + GetConflictingPluginsQuery(LootState& state, const std::string& pluginName) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + game_(state.getCurrentGame()), + pluginName_(pluginName) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(debug) << "Searching for plugins that conflict with " << pluginName_; + + // Checking for FormID overlap will only work if the plugins have been loaded, so check if + // the plugins have been fully loaded, and if not load all plugins. + if (!game_.ArePluginsFullyLoaded()) + game_.LoadAllInstalledPlugins(false); + + YAML::Node node; + auto plugin = game_.GetPlugin(pluginName_); + for (const auto& otherPlugin : game_.GetPlugins()) { + node.push_back(getConflictMetadata(plugin, otherPlugin)); + } + + if (node.size() > 0) + callback->Success(JSON::stringify(node)); + else + callback->Success("[]"); + } + +private: + YAML::Node getConflictMetadata(const Plugin& plugin, const Plugin& otherPlugin) { + YAML::Node pluginNode = generateDerivedMetadata(otherPlugin.Name()); + + pluginNode["name"] = otherPlugin.Name(); + pluginNode["crc"] = otherPlugin.Crc(); + pluginNode["isEmpty"] = otherPlugin.IsEmpty(); + + if (plugin.DoFormIDsOverlap(otherPlugin)) { + BOOST_LOG_TRIVIAL(debug) << "Found conflicting plugin: " << otherPlugin.Name(); + pluginNode["conflicts"] = true; + } else { + pluginNode["conflicts"] = false; + } + + return pluginNode; + } + + Game& game_; + std::string pluginName_; +}; +} + +#endif diff --git a/src/gui/query/get_game_data_query.h b/src/gui/query/get_game_data_query.h new file mode 100644 index 00000000..528fc014 --- /dev/null +++ b/src/gui/query/get_game_data_query.h @@ -0,0 +1,205 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_GAME_DATA_QUERY +#define LOOT_GUI_QUERY_GET_GAME_DATA_QUERY + +#include + +#include "loot/error.h" +#include "loot/loot_version.h" +#include "backend/helpers/json.h" +#include "backend/helpers/version.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class GetGameDataQuery : public MetadataQuery { +public: + GetGameDataQuery(LootState& state, CefRefPtr frame) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + state_(state), + frame_(frame) {} + + void execute(CefRefPtr callback) { + sendProgressUpdate(frame_, boost::locale::translate("Parsing, merging and evaluating metadata...")); + + // First clear CRC and condition caches, otherwise they could lead to incorrect evaluations. + state_.getCurrentGame().ClearCachedConditions(); + + /* If the game's plugins object is empty, this is the first time loading + the game data, so also load the metadata lists. */ + if (state_.getCurrentGame().GetPlugins().empty()) + loadMetadataLists(); + + state_.getCurrentGame().LoadAllInstalledPlugins(true); + + //Sort plugins into their load order. + std::vector installed; + std::vector loadOrder = state_.getCurrentGame().GetLoadOrder(); + for (const auto &pluginName : loadOrder) { + try { + const auto plugin = state_.getCurrentGame().GetPlugin(pluginName); + installed.push_back(plugin); + } catch (...) {} + } + + callback->Success(generateJsonResponse(installed)); + } + +private: + void loadMetadataLists() { + if (exists(state_.getCurrentGame().MasterlistPath())) { + BOOST_LOG_TRIVIAL(debug) << "Parsing masterlist."; + try { + state_.getCurrentGame().GetMasterlist().Load(state_.getCurrentGame().MasterlistPath()); + } catch (std::exception &e) { + state_.getCurrentGame().GetMasterlist().AppendMessage(Message(MessageType::error, (boost::format(boost::locale::translate( + "An error occurred while parsing the masterlist: %1%. " + "This probably happened because an update to LOOT changed " + "its metadata syntax support. Try updating your masterlist " + "to resolve the error." + )) % e.what()).str())); + } + } + + if (exists(state_.getCurrentGame().UserlistPath())) { + BOOST_LOG_TRIVIAL(debug) << "Parsing userlist."; + try { + state_.getCurrentGame().GetUserlist().Load(state_.getCurrentGame().UserlistPath()); + } catch (std::exception &e) { + state_.getCurrentGame().GetUserlist().AppendMessage(Message(MessageType::error, (boost::format(boost::locale::translate( + "An error occurred while parsing the userlist: %1%. " + "This probably happened because an update to LOOT changed " + "its metadata syntax support. Your user metadata will have " + "to be updated manually.\n\n" + "To do so, use the 'Open Debug Log Location' in LOOT's main " + "menu to open its data folder, then open your 'userlist.yaml' " + "file in the relevant game folder. You can then edit the " + "metadata it contains with reference to the " + "[syntax documentation](https://loot.github.io/docs/%2%.%3%.%4%/LOOT%%20Metadata%%20Syntax.html).\n\n" + "You can also seek support on LOOT's forum thread, which is " + "linked to on [LOOT's website](https://loot.github.io/)." + )) % e.what() % LootVersion::major % LootVersion::minor % LootVersion::patch).str())); + } + } + } + + YAML::Node convertMasterlistMetadata() { + YAML::Node masterlistNode; + try { + Masterlist::Info info = state_.getCurrentGame().GetMasterlist().GetInfo(state_.getCurrentGame().MasterlistPath(), true); + masterlistNode["revision"] = info.revision; + masterlistNode["date"] = info.date; + } catch (Error &e) { + masterlistNode["revision"] = e.what(); + masterlistNode["date"] = e.what(); + } + + return masterlistNode; + } + + static std::vector toEditorMessages(std::vector messages, const LanguageCode language) { + std::vector list; + + for (const auto& message : messages) { + list.push_back(EditorMessage(message, language)); + } + + return list; + } + + static YAML::Node convertPluginMetadata(const PluginMetadata& metadata, const LanguageCode language) { + YAML::Node node; + + node["enabled"] = metadata.Enabled(); + node["after"] = metadata.LoadAfter(); + node["req"] = metadata.Reqs(); + node["inc"] = metadata.Incs(); + node["msg"] = toEditorMessages(metadata.Messages(), language); + node["tag"] = metadata.Tags(); + node["dirty"] = metadata.DirtyInfo(); + node["clean"] = metadata.CleanInfo(); + node["url"] = metadata.Locations(); + + return node; + } + + YAML::Node generateDerivedMetadata(const Plugin& plugin) { + YAML::Node pluginNode; + + pluginNode["__type"] = "Plugin"; // For conversion back into a JS typed object. + pluginNode["name"] = plugin.Name(); + pluginNode["isActive"] = plugin.IsActive(); + pluginNode["isEmpty"] = plugin.IsEmpty(); + pluginNode["isMaster"] = plugin.isMasterFile(); + pluginNode["loadsArchive"] = plugin.LoadsArchive(); + pluginNode["crc"] = plugin.Crc(); + pluginNode["version"] = Version(plugin.getDescription()).AsString(); + + BOOST_LOG_TRIVIAL(trace) << "Getting masterlist metadata for: " << plugin.Name(); + Plugin mlistPlugin(plugin); + mlistPlugin.MergeMetadata(state_.getCurrentGame().GetMasterlist().FindPlugin(plugin)); + if (!mlistPlugin.HasNameOnly()) + pluginNode["masterlist"] = convertPluginMetadata(mlistPlugin, state_.getLanguage().GetCode()); + + BOOST_LOG_TRIVIAL(trace) << "Getting userlist metadata for: " << plugin.Name(); + PluginMetadata ulistPlugin(state_.getCurrentGame().GetUserlist().FindPlugin(plugin)); + if (!ulistPlugin.HasNameOnly()) + pluginNode["userlist"] = convertPluginMetadata(ulistPlugin, state_.getLanguage().GetCode()); + + // Now merge masterlist and userlist metadata and evaluate, + // putting any resulting metadata into the base of the pluginNode. + YAML::Node derivedNode = MetadataQuery::generateDerivedMetadata(plugin, mlistPlugin, ulistPlugin); + + for (auto it = derivedNode.begin(); it != derivedNode.end(); ++it) { + const std::string key = it->first.as(); + pluginNode[key] = it->second; + } + + return pluginNode; + } + + std::string generateJsonResponse(std::vector plugins) { + YAML::Node gameNode; + + // ID the game using its folder value. + gameNode["folder"] = state_.getCurrentGame().FolderName(); + gameNode["masterlist"] = convertMasterlistMetadata(); + gameNode["globalMessages"] = getGeneralMessages(); + gameNode["bashTags"] = state_.getCurrentGame().GetMasterlist().BashTags(); + + // Now store plugin data. + for (const auto& plugin : plugins) { + gameNode["plugins"].push_back(generateDerivedMetadata(plugin)); + } + + return JSON::stringify(gameNode); + } + + LootState& state_; + CefRefPtr frame_; +}; +} + +#endif diff --git a/src/gui/query/get_game_types_query.h b/src/gui/query/get_game_types_query.h new file mode 100644 index 00000000..37b2642a --- /dev/null +++ b/src/gui/query/get_game_types_query.h @@ -0,0 +1,55 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY +#define LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY + +#include "backend/game/game_settings.h" +#include "backend/helpers/json.h" +#include "gui/query/query.h" + +namespace loot { +class GetGameTypesQuery : public Query { +public: + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported languages."; + callback->Success(getGameTypesAsJson()); + } + +private: + static std::string getGameTypesAsJson() { + YAML::Node temp; + + temp.push_back(GameSettings(GameType::tes4).FolderName()); + temp.push_back(GameSettings(GameType::tes5).FolderName()); + temp.push_back(GameSettings(GameType::fo3).FolderName()); + temp.push_back(GameSettings(GameType::fonv).FolderName()); + temp.push_back(GameSettings(GameType::fo4).FolderName()); + + return JSON::stringify(temp); + } +}; +} + +#endif diff --git a/src/gui/query/get_init_errors_query.h b/src/gui/query/get_init_errors_query.h new file mode 100644 index 00000000..e79c20e8 --- /dev/null +++ b/src/gui/query/get_init_errors_query.h @@ -0,0 +1,50 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_INIT_ERRORS_QUERY +#define LOOT_GUI_QUERY_GET_INIT_ERRORS_QUERY + +#include "backend/app/loot_state.h" +#include "backend/helpers/json.h" +#include "gui/query/query.h" + +namespace loot { +class GetInitErrorsQuery : public Query { +public: + GetInitErrorsQuery(LootState& state) : state_(state) {} + + void execute(CefRefPtr callback) { + YAML::Node node(state_.getInitErrors()); + if (node.size() > 0) + callback->Success(JSON::stringify(node)); + else + callback->Success("null"); + } + +private: + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/get_installed_games_query.h b/src/gui/query/get_installed_games_query.h new file mode 100644 index 00000000..67085fe1 --- /dev/null +++ b/src/gui/query/get_installed_games_query.h @@ -0,0 +1,55 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_INSTALLED_GAMES_QUERY +#define LOOT_GUI_QUERY_GET_INSTALLED_GAMES_QUERY + +#include "backend/app/loot_state.h" +#include "backend/helpers/json.h" +#include "gui/query/query.h" + +namespace loot { +class GetInstalledGamesQuery : public Query { +public: + GetInstalledGamesQuery(LootState& state) : state_(state) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Getting LOOT's detected games."; + callback->Success(getInstalledGamesAsJson()); + } + +private: + std::string getInstalledGamesAsJson() const { + YAML::Node temp = YAML::Node(state_.getInstalledGames()); + if (temp.size() > 0) + return JSON::stringify(temp); + else + return "[]"; + } + + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/get_languages_query.h b/src/gui/query/get_languages_query.h new file mode 100644 index 00000000..5acb7f65 --- /dev/null +++ b/src/gui/query/get_languages_query.h @@ -0,0 +1,56 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_LANGUAGES_QUERY +#define LOOT_GUI_QUERY_GET_LANGUAGES_QUERY + +#include "backend/helpers/json.h" +#include "backend/helpers/language.h" +#include "gui/query/query.h" + +namespace loot { +class GetLanguagesQuery : public Query { +public: + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported languages."; + callback->Success(getLanguagesAsJson()); + } + +private: + static std::string getLanguagesAsJson() { + YAML::Node temp; + for (const auto& code : Language::codes) { + YAML::Node lang; + Language language(code); + lang["name"] = language.GetName(); + lang["locale"] = language.GetLocale(); + temp.push_back(lang); + } + + return JSON::stringify(temp); + } +}; +} + +#endif diff --git a/src/gui/query/get_settings_query.h b/src/gui/query/get_settings_query.h new file mode 100644 index 00000000..e5c0e068 --- /dev/null +++ b/src/gui/query/get_settings_query.h @@ -0,0 +1,47 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_SETTINGS_QUERY +#define LOOT_GUI_QUERY_GET_SETTINGS_QUERY + +#include "backend/app/loot_settings.h" +#include "backend/helpers/json.h" +#include "gui/query/query.h" + +namespace loot { +class GetSettingsQuery : public Query { +public: + GetSettingsQuery(LootSettings& settings) : settings_(settings) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Getting LOOT settings."; + callback->Success(JSON::stringify(settings_.toYaml())); + } + +private: + LootSettings& settings_; +}; +} + +#endif diff --git a/src/gui/query/get_version_query.h b/src/gui/query/get_version_query.h new file mode 100644 index 00000000..592806a3 --- /dev/null +++ b/src/gui/query/get_version_query.h @@ -0,0 +1,41 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_GET_VERSION_QUERY +#define LOOT_GUI_QUERY_GET_VERSION_QUERY + +#include "gui/query/query.h" +#include "loot/loot_version.h" + +namespace loot { +class GetVersionQuery : public Query { +public: + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Getting LOOT version."; + callback->Success("\"" + LootVersion::string() + "." + LootVersion::revision + "\""); + } +}; +} + +#endif diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h new file mode 100644 index 00000000..1476ee8c --- /dev/null +++ b/src/gui/query/metadata_query.h @@ -0,0 +1,155 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_METADATA_QUERY +#define LOOT_GUI_QUERY_METADATA_QUERY + +#include +#include + +#include "backend/game/game.h" +#include "backend/plugin/plugin.h" +#include "gui/query/query.h" + +namespace loot { +class MetadataQuery : public Query { +protected: + MetadataQuery(Game& game, const LanguageCode language) : + game_(game), language_(language) {} + + std::vector getGeneralMessages() { + std::vector messages; + appendMessages(messages, game_.GetMasterlist().Messages()); + appendMessages(messages, game_.GetUserlist().Messages()); + appendMessages(messages, game_.GetMessages()); + + evaluateMessageConditions(messages); + + return toSimpleMessages(messages, language_); + } + + YAML::Node generateDerivedMetadata(const Plugin& file, + const PluginMetadata& masterlistEntry, + const PluginMetadata& userlistEntry) { + Plugin plugin(file); + + plugin.MergeMetadata(masterlistEntry); + plugin.MergeMetadata(userlistEntry); + + evaluatePlugin(plugin); + + return toYaml(plugin); + } + + YAML::Node generateDerivedMetadata(const std::string& pluginName) { + // Now rederive the displayed metadata from the masterlist and userlist. + try { + auto plugin = game_.GetPlugin(pluginName); + PluginMetadata master(game_.GetMasterlist().FindPlugin(plugin)); + PluginMetadata user(game_.GetUserlist().FindPlugin(plugin)); + + return generateDerivedMetadata(plugin, master, user); + } catch (...) { + return YAML::Node(); + } + } + +private: + static void appendMessages(std::vector& destination, + const std::vector& source) { + destination.insert(end(destination), begin(source), end(source)); + } + + void evaluateMessageConditions(std::vector& messages) { + try { + auto it = begin(messages); + while (it != end(messages)) { + if (!it->EvalCondition(game_)) + it = messages.erase(it); + else + ++it; + } + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what(); + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str())); + } + } + + static std::vector toSimpleMessages(const std::vector& messages, + LanguageCode language) { + BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).GetName(); + std::vector simpleMessages(messages.size()); + std::transform(begin(messages), + end(messages), + begin(simpleMessages), + [&](const Message& message) { + return message.ToSimpleMessage(language); + }); + + return simpleMessages; + } + + void evaluatePlugin(Plugin& plugin) { + //Evaluate any conditions + BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; + try { + plugin.EvalAllConditions(game_); + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "\"" << plugin.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); + std::vector messages(plugin.Messages()); + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % plugin.Name() % e.what()).str())); + plugin.Messages(messages); + } + + //Also check install validity. + plugin.CheckInstallValidity(game_); + } + + YAML::Node toYaml(const Plugin& plugin) { + BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language_).GetName(); + + YAML::Node pluginNode; + pluginNode["name"] = plugin.Name(); + pluginNode["priority"] = plugin.LocalPriority().getValue(); + pluginNode["globalPriority"] = plugin.GlobalPriority().getValue(); + pluginNode["messages"] = plugin.SimpleMessages(language_); + pluginNode["tags"] = plugin.Tags(); + pluginNode["isDirty"] = !plugin.DirtyInfo().empty(); + pluginNode["loadOrderIndex"] = game_.GetActiveLoadOrderIndex(plugin.Name()); + + if (!plugin.CleanInfo().empty()) { + pluginNode["cleanedWith"] = plugin.CleanInfo().begin()->CleaningUtility(); + } else { + pluginNode["cleanedWith"] = ""; + } + + return pluginNode; + } + + Game& game_; + LanguageCode language_; +}; +} + +#endif diff --git a/src/gui/query/open_log_location_query.h b/src/gui/query/open_log_location_query.h new file mode 100644 index 00000000..bb2d9575 --- /dev/null +++ b/src/gui/query/open_log_location_query.h @@ -0,0 +1,44 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_OPEN_LOG_LOCATION_QUERY +#define LOOT_GUI_QUERY_OPEN_LOG_LOCATION_QUERY + +#include "backend/app/loot_paths.h" +#include "backend/helpers/helpers.h" +#include "gui/query/query.h" + +namespace loot { +class OpenLogLocationQuery : public Query { +public: + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Opening LOOT local appdata folder."; + OpenInDefaultApplication(LootPaths::getLogPath().parent_path()); + + callback->Success(""); + } +}; +} + +#endif diff --git a/src/gui/query/open_readme_query.h b/src/gui/query/open_readme_query.h new file mode 100644 index 00000000..2d247afd --- /dev/null +++ b/src/gui/query/open_readme_query.h @@ -0,0 +1,44 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_OPEN_README_QUERY +#define LOOT_GUI_QUERY_OPEN_README_QUERY + +#include "backend/app/loot_paths.h" +#include "backend/helpers/helpers.h" +#include "gui/query/query.h" + +namespace loot { +class OpenReadmeQuery : public Query { +public: + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Opening LOOT readme."; + OpenInDefaultApplication(LootPaths::getReadmePath()); + + callback->Success(""); + } +}; +} + +#endif diff --git a/src/gui/query/query.h b/src/gui/query/query.h new file mode 100644 index 00000000..a9ebd72f --- /dev/null +++ b/src/gui/query/query.h @@ -0,0 +1,47 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_QUERY +#define LOOT_GUI_QUERY_QUERY + +#include +#include + +namespace loot { +class Query : public CefBase { +public: + virtual void execute(CefRefPtr callback) = 0; + +protected: + void sendProgressUpdate(CefRefPtr frame, const std::string& message) { + BOOST_LOG_TRIVIAL(trace) << "Sending progress update: " << message; + frame->ExecuteJavaScript("loot.Dialog.showProgress('" + message + "');", frame->GetURL(), 0); + } + +private: + IMPLEMENT_REFCOUNTING(Query); +}; +} + +#endif diff --git a/src/gui/query/redate_plugins_query.h b/src/gui/query/redate_plugins_query.h new file mode 100644 index 00000000..9c11bbd0 --- /dev/null +++ b/src/gui/query/redate_plugins_query.h @@ -0,0 +1,46 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_REDATE_PLUGINS_QUERY +#define LOOT_GUI_QUERY_REDATE_PLUGINS_QUERY + +#include "backend/app/loot_state.h" +#include "gui/query/query.h" + +namespace loot { +class RedatePluginsQuery : public Query { +public: + RedatePluginsQuery(LootState& state) : state_(state) {} + + void execute(CefRefPtr callback) { + state_.getCurrentGame().RedatePlugins(); + callback->Success(""); + } + +private: + LootState& state_; +}; +} + +#endif diff --git a/src/gui/query/save_filter_state_query.h b/src/gui/query/save_filter_state_query.h new file mode 100644 index 00000000..84737e15 --- /dev/null +++ b/src/gui/query/save_filter_state_query.h @@ -0,0 +1,54 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_SAVE_FILTER_STATE_QUERY +#define LOOT_GUI_QUERY_SAVE_FILTER_STATE_QUERY + +#include + +#include "backend/app/loot_state.h" +#include "gui/query/query.h" + +namespace loot { +class SaveFilterStateQuery : public Query { +public: + SaveFilterStateQuery(LootState& state, + const std::string& filterId, + bool enabled) : + state_(state), filterId_(filterId), enabled_(enabled) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(trace) << "Saving filter states."; + state_.storeFilterState(filterId_, enabled_); + callback->Success(""); + } + +private: + LootState& state_; + std::string filterId_; + bool enabled_; +}; +} + +#endif diff --git a/src/gui/query/sort_plugins_query.h b/src/gui/query/sort_plugins_query.h new file mode 100644 index 00000000..8ecdb5d0 --- /dev/null +++ b/src/gui/query/sort_plugins_query.h @@ -0,0 +1,136 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_SORT_PLUGINS_QUERY +#define LOOT_GUI_QUERY_SORT_PLUGINS_QUERY + +#include + +#include "backend/helpers/json.h" +#include "backend/plugin/plugin_sorter.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class SortPluginsQuery : public MetadataQuery { +public: + SortPluginsQuery(LootState& state, CefRefPtr frame) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + state_(state), + frame_(frame) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(info) << "Beginning sorting operation."; + + // Always reload all the plugins. + sendProgressUpdate(frame_, boost::locale::translate("Loading plugin contents...")); + state_.getCurrentGame().LoadAllInstalledPlugins(false); + + //Sort plugins into their load order. + std::vector plugins = sortPlugins(); + + if ((state_.getCurrentGame().Type() == GameType::tes5 || state_.getCurrentGame().Type() == GameType::fo4)) + applyUnchangedLoadOrder(plugins); + + callback->Success(generateJsonResponse(plugins)); + + // plugins will be empty if there was a sorting error. + if (!plugins.empty()) + state_.incrementUnappliedChangeCounter(); + } + +private: + std::vector sortPlugins() { + sendProgressUpdate(frame_, boost::locale::translate("Sorting load order...")); + std::vector plugins; + try { + PluginSorter sorter; + plugins = sorter.Sort(state_.getCurrentGame(), state_.getLanguage().GetCode()); + } catch (Error& e) { + BOOST_LOG_TRIVIAL(error) << "Failed to sort plugins. Details: " << e.what(); + if (e.code() != Error::Code::sorting_error) + throw; + + state_.getCurrentGame().AppendMessage(Message(MessageType::error, e.what())); + } + + return plugins; + } + + void applyUnchangedLoadOrder(const std::vector& plugins) { + if (!equal(begin(plugins), end(plugins), begin(state_.getCurrentGame().GetLoadOrder()))) + return; + + // Load order has not been changed, set it without asking for user input + // because there are no changes to accept and some plugins' positions + // may only be inferred and not written to loadorder.txt/plugins.txt. + std::vector newLoadOrder(plugins.size()); + std::transform(begin(plugins), + end(plugins), + begin(newLoadOrder), + [](const Plugin& plugin) { + return plugin.Name(); + }); + state_.getCurrentGame().SetLoadOrder(newLoadOrder); + } + + YAML::Node generateDerivedMetadata(const Plugin& plugin) { + YAML::Node pluginNode; + + pluginNode["name"] = plugin.Name(); + pluginNode["crc"] = plugin.Crc(); + pluginNode["isEmpty"] = plugin.IsEmpty(); + + // Sorting may have produced a plugin loading error message, so rederive displayed data. + YAML::Node derivedNode = MetadataQuery::generateDerivedMetadata(plugin.Name()); + + for (const auto &pair : derivedNode) { + const std::string key = pair.first.as(); + pluginNode[key] = pair.second; + } + + return pluginNode; + } + + std::string generateJsonResponse(const std::vector& plugins) { + YAML::Node node; + + // Store global messages in case they have changed. + node["globalMessages"] = getGeneralMessages(); + + for (const auto &plugin : plugins) { + node["plugins"].push_back(generateDerivedMetadata(plugin)); + } + + if (node.size() > 0) + return JSON::stringify(node); + else + return "null"; + } + + LootState& state_; + CefRefPtr frame_; +}; +} + +#endif diff --git a/src/gui/query/update_masterlist_query.h b/src/gui/query/update_masterlist_query.h new file mode 100644 index 00000000..19bfbf28 --- /dev/null +++ b/src/gui/query/update_masterlist_query.h @@ -0,0 +1,133 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 WrinklyNinja + +This file is part of LOOT. + +LOOT 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. + +LOOT 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 LOOT. If not, see +. +*/ + +#ifndef LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY +#define LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY + +#include "backend/game/game.h" +#include "backend/helpers/json.h" +#include "gui/query/metadata_query.h" + +namespace loot { +class UpdateMasterlistQuery : public MetadataQuery { +public: + UpdateMasterlistQuery(LootState& state) : + MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + game_(state.getCurrentGame()) {} + + void execute(CefRefPtr callback) { + BOOST_LOG_TRIVIAL(debug) << "Updating and parsing masterlist."; + + if (!updateMasterlist()) { + callback->Success("null"); + return; + } + + // Now regenerate the JS-side masterlist data if the masterlist was changed. + callback->Success(generateJsonResponse()); + } + +private: + bool updateMasterlist() { + try { + return game_.GetMasterlist().Update(game_); + } catch (Error &e) { + if (e.code() == Error::Code::ok) { + // There was a parsing error, but roll-back was successful, so the + // process should still complete. + game_.GetMasterlist().AppendMessage(Message(MessageType::error, e.what())); + return true; + } else { + // Error wasn't a parsing error. Need to try parsing masterlist if it + // exists. + try { + game_.GetMasterlist().Load(game_.MasterlistPath()); + } catch (...) {} + } + throw; + } + } + + std::string generateJsonResponse() { + YAML::Node gameMetadata; + storeMasterlistMetadata(gameMetadata); + + // Store bash tags in case they have changed. + gameMetadata["bashTags"] = game_.GetMasterlist().BashTags(); + + // Store global messages in case they have changed. + gameMetadata["globalMessages"] = getGeneralMessages(); + + for (const auto& plugin : game_.GetPlugins()) { + gameMetadata["plugins"].push_back(generateDerivedMetadata(plugin)); + } + + return JSON::stringify(gameMetadata); + } + + void storeMasterlistMetadata(YAML::Node& gameMetadata) { + try { + Masterlist::Info info = game_.GetMasterlist().GetInfo(game_.MasterlistPath(), true); + gameMetadata["masterlist"]["revision"] = info.revision; + gameMetadata["masterlist"]["date"] = info.date; + } catch (Error &e) { + gameMetadata["masterlist"]["revision"] = e.what(); + gameMetadata["masterlist"]["date"] = e.what(); + } + } + + YAML::Node generateDerivedMetadata(const Plugin& plugin) { + YAML::Node pluginNode; + + Plugin mlistPlugin(plugin); + mlistPlugin.MergeMetadata(game_.GetMasterlist().FindPlugin(plugin)); + if (!mlistPlugin.HasNameOnly()) { + // Now add the masterlist metadata to the pluginNode. + pluginNode["masterlist"]["after"] = mlistPlugin.LoadAfter(); + pluginNode["masterlist"]["req"] = mlistPlugin.Reqs(); + pluginNode["masterlist"]["inc"] = mlistPlugin.Incs(); + pluginNode["masterlist"]["msg"] = mlistPlugin.Messages(); + pluginNode["masterlist"]["tag"] = mlistPlugin.Tags(); + pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo(); + pluginNode["masterlist"]["clean"] = mlistPlugin.CleanInfo(); + pluginNode["masterlist"]["url"] = mlistPlugin.Locations(); + } + + // Now merge masterlist and userlist metadata and evaluate, + // putting any resulting metadata into the base of the pluginNode. + YAML::Node derivedNode = MetadataQuery::generateDerivedMetadata(plugin.Name()); + + for (const auto &pair : derivedNode) { + const std::string key = pair.first.as(); + pluginNode[key] = pair.second; + } + + return pluginNode; + } + + Game& game_; +}; +} + +#endif diff --git a/src/gui/query_handler.cpp b/src/gui/query_handler.cpp index 20c6f1df..94a5ec3d 100644 --- a/src/gui/query_handler.cpp +++ b/src/gui/query_handler.cpp @@ -40,6 +40,33 @@ #include "gui/editor_message.h" #include "gui/loot_app.h" #include "gui/loot_handler.h" +#include "gui/query/apply_sort_query.h" +#include "gui/query/cancel_find_query.h" +#include "gui/query/cancel_sort_query.h" +#include "gui/query/change_game_query.h" +#include "gui/query/clear_all_metadata_query.h" +#include "gui/query/clear_plugin_metadata_query.h" +#include "gui/query/close_settings_query.h" +#include "gui/query/copy_content_query.h" +#include "gui/query/copy_load_order_query.h" +#include "gui/query/copy_metadata_query.h" +#include "gui/query/discard_unapplied_changes_query.h" +#include "gui/query/editor_opened_query.h" +#include "gui/query/editor_closed_query.h" +#include "gui/query/get_conflicting_plugins_query.h" +#include "gui/query/get_game_data_query.h" +#include "gui/query/get_game_types_query.h" +#include "gui/query/get_init_errors_query.h" +#include "gui/query/get_installed_games_query.h" +#include "gui/query/get_languages_query.h" +#include "gui/query/get_settings_query.h" +#include "gui/query/get_version_query.h" +#include "gui/query/open_log_location_query.h" +#include "gui/query/open_readme_query.h" +#include "gui/query/redate_plugins_query.h" +#include "gui/query/save_filter_state_query.h" +#include "gui/query/sort_plugins_query.h" +#include "gui/query/update_masterlist_query.h" #include "gui/resource.h" #include "gui/yaml_simple_message_helpers.h" @@ -70,987 +97,93 @@ bool QueryHandler::OnQuery(CefRefPtr browser, const CefString& request, bool persistent, CefRefPtr callback) { + YAML::Node parsedRequest; try { - YAML::Node parsedRequest = JSON::parse(request.ToString()); - - return HandleQuery(browser, frame, parsedRequest, callback); + parsedRequest = JSON::parse(request.ToString()); } catch (exception &e) { BOOST_LOG_TRIVIAL(error) << "Failed to parse CEF query request \"" << request.ToString() << "\": " << e.what(); callback->Failure(-1, e.what()); return true; } - return false; -} + auto query = createQuery(browser, frame, parsedRequest); -// Handle queries with input arguments. -bool QueryHandler::HandleQuery(CefRefPtr browser, - CefRefPtr frame, - YAML::Node& request, - CefRefPtr callback) { - const string requestName = request["name"].as(); + if (!query) + return false; - if (requestName == "openReadme") { - try { - OpenReadme(); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(e.codeAsUnsignedInt(), e.what()); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(-1, e.what()); - } - return true; - } else if (requestName == "openLogLocation") { - try { - OpenLogLocation(); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(e.codeAsUnsignedInt(), e.what()); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(-1, e.what()); - } - return true; - } else if (requestName == "getVersion") { - callback->Success(GetVersion()); - return true; - } else if (requestName == "getSettings") { - callback->Success(GetSettings()); - return true; - } else if (requestName == "getLanguages") { - callback->Success(GetLanguages()); - return true; - } else if (requestName == "getGameTypes") { - callback->Success(GetGameTypes()); - return true; - } else if (requestName == "getInstalledGames") { - callback->Success(GetInstalledGames()); - return true; - } else if (requestName == "getGameData") { - SendProgressUpdate(frame, translate("Parsing, merging and evaluating metadata...")); - return CefPostTask(TID_FILE, base::Bind(&QueryHandler::GetGameData, base::Unretained(this), frame, callback)); - } else if (requestName == "cancelFind") { - browser->GetHost()->StopFinding(true); - callback->Success(""); - return true; - } else if (requestName == "clearAllMetadata") { - callback->Success(ClearAllMetadata()); - return true; - } else if (requestName == "redatePlugins") { - BOOST_LOG_TRIVIAL(debug) << "Redating plugins."; - try { - lootState_.getCurrentGame().RedatePlugins(); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to redate plugins. " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), e.what()); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to redate plugins. " << e.what(); - callback->Failure(-1, e.what()); - } - - return true; - } else if (requestName == "updateMasterlist") { - return CefPostTask(TID_FILE, base::Bind(&QueryHandler::UpdateMasterlist, base::Unretained(this), callback)); - } else if (requestName == "sortPlugins") { - return CefPostTask(TID_FILE, base::Bind(&QueryHandler::SortPlugins, base::Unretained(this), frame, callback)); - } else if (requestName == "getInitErrors") { - YAML::Node node(lootState_.getInitErrors()); - if (node.size() > 0) - callback->Success(JSON::stringify(node)); - else - callback->Success("null"); - return true; - } else if (requestName == "cancelSort") { - lootState_.decrementUnappliedChangeCounter(); - lootState_.getCurrentGame().DecrementLoadOrderSortCount(); - - YAML::Node node(GetGeneralMessages(lootState_.getLanguage().GetCode())); - callback->Success(JSON::stringify(node)); - return true; - } else if (requestName == "editorOpened") { - lootState_.incrementUnappliedChangeCounter(); - callback->Success(""); - return true; - } else if (requestName == "discardUnappliedChanges") { - while (lootState_.hasUnappliedChanges()) - lootState_.decrementUnappliedChangeCounter(); - callback->Success(""); - return true; - } else if (requestName == "changeGame") { - try { - // Has one arg, which is the folder name of the new game. - lootState_.changeGame(request["args"][0].as()); - - CefPostTask(TID_FILE, base::Bind(&QueryHandler::GetGameData, base::Unretained(this), frame, callback)); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to change game. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to change game. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to change game. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to change game. Details: %1%")) % e.what()).str()); - } - return true; - } else if (requestName == "getConflictingPlugins") { - // Has one arg, which is the name of the plugin to get conflicts for. - CefPostTask(TID_FILE, base::Bind(&QueryHandler::GetConflictingPlugins, base::Unretained(this), request["args"][0].as(), callback)); - return true; - } else if (requestName == "copyMetadata") { - // Has one arg, which is the name of the plugin to copy metadata for. - try { - CopyMetadata(request["args"][0].as()); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } - return true; - } else if (requestName == "clearPluginMetadata") { - // Has one arg, which is the name of the plugin to copy metadata for. - callback->Success(ClearPluginMetadata(request["args"][0].as())); - return true; - } else if (requestName == "editorClosed") { - BOOST_LOG_TRIVIAL(debug) << "Editor for plugin closed."; - // One argument, which is the plugin metadata that has changed (+ its name). - try { - if (!request["args"][0].IsMap()) - callback->Success(""); - else - callback->Success(ApplyUserEdits(request["args"][0])); - lootState_.decrementUnappliedChangeCounter(); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to apply plugin metadata. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to apply plugin metadata. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - // If this was a YAML conversion error, cut off the line and column numbers, - // since the YAML wasn't written to a file. - string error = e.what(); - size_t pos = string::npos; - if ((pos = error.find("bad conversion")) != string::npos) { - error = error.substr(pos); - } - BOOST_LOG_TRIVIAL(error) << "Failed to apply plugin metadata. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to apply plugin metadata. Details: %1%")) % error).str()); - } - return true; - } else if (requestName == "closeSettings") { - BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object."; - - try { - // Update the settings. - // If the user has deleted a default game, we don't want to restore it now. - // It will be restored when LOOT is next loaded. - YAML::Node settings = request["args"][0]; - lootState_.load(settings); - - // Now send back the new list of installed games to the UI. - BOOST_LOG_TRIVIAL(trace) << "Getting new list of installed games."; - callback->Success(GetInstalledGames()); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(-1, e.what()); - } - return true; - } else if (requestName == "applySort") { - lootState_.decrementUnappliedChangeCounter(); - BOOST_LOG_TRIVIAL(trace) << "User has accepted sorted load order, applying it."; - try { - lootState_.getCurrentGame().SetLoadOrder(request["args"][0].as>()); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(e.codeAsUnsignedInt(), e.what()); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(-1, e.what()); - } - - return true; - } else if (requestName == "copyContent") { - // Has one arg, just convert it to a YAML output string. - try { - YAML::Emitter yout; - yout.SetIndent(2); - yout << request["args"][0]; - string text = yout.c_str(); - // Get rid of yaml-cpp weirdness. - boost::replace_all(text, "! ", ""); - text = "[spoiler][code]" + text + "[/code][/spoiler]"; - CopyToClipboard(text); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } - return true; - } else if (requestName == "copyLoadOrder") { - // Has one arg, an array of plugins in load order. Output them with indices in dec and hex. - try { - std::stringstream ss; - vector plugins = request["args"][0].as>(); - int decLength = 1; - if (plugins.size() > 99) { - decLength = 3; - } else if (plugins.size() > 9) { - decLength = 2; - } - size_t i = 0; - for (const auto& pluginName : plugins) { - if (lootState_.getCurrentGame().IsPluginActive(pluginName)) { - ss << std::setw(decLength) << i << " " << std::hex << std::setw(2) << i << std::dec << " "; - ++i; - } else { - ss << std::setw(decLength + 4) << " "; - } - ss << pluginName << "\r\n"; - } - CopyToClipboard(ss.str()); - callback->Success(""); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to copy plugin metadata. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to copy plugin metadata. Details: %1%")) % e.what()).str()); - } - return true; - } else if (requestName == "saveFilterState") { - // Has two args: the first is the filter ID, the second is the value. - BOOST_LOG_TRIVIAL(trace) << "Saving filter states."; - try { - lootState_.storeFilterState(request["args"][0].as(), request["args"][1].as()); - callback->Success(""); - } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - callback->Failure(-1, e.what()); - } - return true; - } - return false; -} - -void QueryHandler::GetConflictingPlugins(const std::string& pluginName, CefRefPtr callback) { - BOOST_LOG_TRIVIAL(debug) << "Searching for plugins that conflict with " << pluginName; - - // Checking for FormID overlap will only work if the plugins have been loaded, so check if - // the plugins have been fully loaded, and if not load all plugins. - if (!lootState_.getCurrentGame().ArePluginsFullyLoaded()) - lootState_.getCurrentGame().LoadAllInstalledPlugins(false); - - YAML::Node node; - auto plugin = lootState_.getCurrentGame().GetPlugin(pluginName); - for (const auto& otherPlugin : lootState_.getCurrentGame().GetPlugins()) { - // Plugin loading may have produced an error message, so rederive - // displayed data. - - YAML::Node pluginNode = GenerateDerivedMetadata(otherPlugin.Name()); - - pluginNode["name"] = otherPlugin.Name(); - pluginNode["crc"] = otherPlugin.Crc(); - pluginNode["isEmpty"] = otherPlugin.IsEmpty(); - if (plugin.DoFormIDsOverlap(otherPlugin)) { - BOOST_LOG_TRIVIAL(debug) << "Found conflicting plugin: " << otherPlugin.Name(); - pluginNode["conflicts"] = true; - } else { - pluginNode["conflicts"] = false; - } - - node.push_back(pluginNode); - } - - if (node.size() > 0) - callback->Success(JSON::stringify(node)); - else - callback->Success("[]"); -} - -void QueryHandler::CopyMetadata(const std::string& pluginName) { - BOOST_LOG_TRIVIAL(debug) << "Copying metadata for plugin " << pluginName; - - // Get metadata from masterlist and userlist. - PluginMetadata plugin = lootState_.getCurrentGame().GetMasterlist().FindPlugin(pluginName); - plugin.MergeMetadata(lootState_.getCurrentGame().GetUserlist().FindPlugin(pluginName)); - - // Generate text representation. - string text; - YAML::Emitter yout; - yout.SetIndent(2); - yout << plugin; - text = yout.c_str(); - // Get rid of yaml-cpp weirdness. - boost::replace_all(text, "! ", ""); - text = "[spoiler][code]" + text + "[/code][/spoiler]"; - - CopyToClipboard(text); - - BOOST_LOG_TRIVIAL(info) << "Exported userlist metadata text for \"" << pluginName << "\": " << text; -} - -std::string QueryHandler::ClearPluginMetadata(const std::string& pluginName) { - BOOST_LOG_TRIVIAL(debug) << "Clearing user metadata for plugin " << pluginName; - - lootState_.getCurrentGame().GetUserlist().ErasePlugin(PluginMetadata(pluginName)); - - // Save userlist edits. - lootState_.getCurrentGame().GetUserlist().Save(lootState_.getCurrentGame().UserlistPath()); - - // Now rederive the displayed metadata from the masterlist. - YAML::Node derivedMetadata = GenerateDerivedMetadata(pluginName); - if (derivedMetadata.size() > 0) - return JSON::stringify(derivedMetadata); - else - return "null"; -} - -std::string QueryHandler::ApplyUserEdits(const YAML::Node& pluginMetadata) { - BOOST_LOG_TRIVIAL(trace) << "Applying user edits for: " << pluginMetadata["name"].as(); - // Create new object for userlist entry. - PluginMetadata newUserlistEntry(pluginMetadata["name"].as()); - - // Find existing userlist entry. - PluginMetadata ulistPlugin = lootState_.getCurrentGame().GetUserlist().FindPlugin(newUserlistEntry); - - // First sort out the priority value. This is only given if it was changed. - BOOST_LOG_TRIVIAL(trace) << "Calculating userlist metadata local priority value from Javascript variables."; - if (pluginMetadata["priority"]) { - BOOST_LOG_TRIVIAL(trace) << "Local priority value was changed, recalculating..."; - // Priority value was changed, so add it to the userlist data. - newUserlistEntry.LocalPriority(Priority(pluginMetadata["priority"].as())); - } else { - // Priority value wasn't changed, use the existing userlist value. - BOOST_LOG_TRIVIAL(trace) << "Local priority value is unchanged, using existing userlist value (if it exists)."; - newUserlistEntry.LocalPriority(ulistPlugin.LocalPriority()); - } - - if (pluginMetadata["globalPriority"]) { - BOOST_LOG_TRIVIAL(trace) << "Global priority value was changed, recalculating..."; - // Priority value was changed, so add it to the userlist data. - newUserlistEntry.GlobalPriority(Priority(pluginMetadata["globalPriority"].as())); - } else { - BOOST_LOG_TRIVIAL(trace) << "Global priority value is unchanged, using existing userlist value (if it exists)."; - newUserlistEntry.GlobalPriority(ulistPlugin.GlobalPriority()); - } - - // Now the enabled flag. - newUserlistEntry.Enabled(pluginMetadata["userlist"]["enabled"].as()); - - // Now metadata lists. These are given in their entirety, so replace anything that - // currently exists. - BOOST_LOG_TRIVIAL(trace) << "Recording metadata lists from Javascript variables."; - if (pluginMetadata["userlist"]["after"]) - newUserlistEntry.LoadAfter(pluginMetadata["userlist"]["after"].as>()); - if (pluginMetadata["userlist"]["req"]) - newUserlistEntry.Reqs(pluginMetadata["userlist"]["req"].as>()); - if (pluginMetadata["userlist"]["inc"]) - newUserlistEntry.Incs(pluginMetadata["userlist"]["inc"].as>()); - - if (pluginMetadata["userlist"]["msg"]) - newUserlistEntry.Messages(ToMessages(pluginMetadata["userlist"]["msg"].as>())); - if (pluginMetadata["userlist"]["tag"]) - newUserlistEntry.Tags(pluginMetadata["userlist"]["tag"].as>()); - if (pluginMetadata["userlist"]["dirty"]) - newUserlistEntry.DirtyInfo(pluginMetadata["userlist"]["dirty"].as>()); - if (pluginMetadata["userlist"]["clean"]) - newUserlistEntry.CleanInfo(pluginMetadata["userlist"]["clean"].as>()); - if (pluginMetadata["userlist"]["url"]) - newUserlistEntry.Locations(pluginMetadata["userlist"]["url"].as>()); - -// For cleanliness, only data that does not duplicate masterlist and plugin data should be retained, so diff that. - BOOST_LOG_TRIVIAL(trace) << "Removing any user metadata that duplicates masterlist metadata."; try { - Plugin tempPlugin(lootState_.getCurrentGame().GetPlugin(newUserlistEntry.Name())); - tempPlugin.MergeMetadata(lootState_.getCurrentGame().GetMasterlist().FindPlugin(newUserlistEntry)); - newUserlistEntry = newUserlistEntry.NewMetadata(tempPlugin); - } catch (...) { - newUserlistEntry = newUserlistEntry.NewMetadata(lootState_.getCurrentGame().GetMasterlist().FindPlugin(newUserlistEntry)); - } - - // Now erase any existing userlist entry. - if (!ulistPlugin.HasNameOnly()) { - BOOST_LOG_TRIVIAL(trace) << "Erasing the existing userlist entry."; - lootState_.getCurrentGame().GetUserlist().ErasePlugin(ulistPlugin); - } - // Add a new userlist entry if necessary. - if (!newUserlistEntry.HasNameOnly()) { - BOOST_LOG_TRIVIAL(trace) << "Adding new metadata to new userlist entry."; - lootState_.getCurrentGame().GetUserlist().AddPlugin(newUserlistEntry); - } - - // Save edited userlist. - lootState_.getCurrentGame().GetUserlist().Save(lootState_.getCurrentGame().UserlistPath()); - - // Now rederive the derived metadata. - BOOST_LOG_TRIVIAL(trace) << "Returning newly derived display metadata."; - YAML::Node derivedMetadata = GenerateDerivedMetadata(newUserlistEntry.Name()); - if (derivedMetadata.size() > 0) - return JSON::stringify(derivedMetadata); - else - return "null"; -} - -void QueryHandler::OpenReadme() { - BOOST_LOG_TRIVIAL(info) << "Opening LOOT readme."; - // Open readme in default application. - OpenInDefaultApplication(LootPaths::getReadmePath()); -} - -void QueryHandler::OpenLogLocation() { - BOOST_LOG_TRIVIAL(info) << "Opening LOOT local appdata folder."; - //Open debug log folder. - OpenInDefaultApplication(LootPaths::getLogPath().parent_path()); -} - -std::string QueryHandler::GetVersion() { - BOOST_LOG_TRIVIAL(info) << "Getting LOOT version."; - YAML::Node version(LootVersion::string() + "." + LootVersion::revision); - return JSON::stringify(version); -} - -std::string QueryHandler::GetSettings() { - BOOST_LOG_TRIVIAL(info) << "Getting LOOT settings."; - return JSON::stringify(lootState_.toYaml()); -} - -std::string QueryHandler::GetLanguages() { - BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported languages."; - // Need to get an array of language names and their corresponding codes. - YAML::Node temp; - for (const auto& code : Language::codes) { - YAML::Node lang; - Language language(code); - lang["name"] = language.GetName(); - lang["locale"] = language.GetLocale(); - temp.push_back(lang); - } - return JSON::stringify(temp); -} - -std::string QueryHandler::GetGameTypes() { - BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported game types."; - YAML::Node temp; - temp.push_back(Game(GameType::tes4).FolderName()); - temp.push_back(Game(GameType::tes5).FolderName()); - temp.push_back(Game(GameType::fo3).FolderName()); - temp.push_back(Game(GameType::fonv).FolderName()); - temp.push_back(Game(GameType::fo4).FolderName()); - return JSON::stringify(temp); -} - -std::string QueryHandler::GetInstalledGames() { - BOOST_LOG_TRIVIAL(info) << "Getting LOOT's detected games."; - YAML::Node temp = YAML::Node(lootState_.getInstalledGames()); - if (temp.size() > 0) - return JSON::stringify(temp); - else - return "[]"; -} - -void QueryHandler::GetGameData(CefRefPtr frame, CefRefPtr callback) { - try { - /* GetGameData() can be called for initialising the UI for a game for the first time - in a session, or it can be called when changing to a game that has previously been - active. In the first case, all data should be loaded, but in the second, only load - order and plugin header info should be re-loaded. - Determine which case it is by checking to see if the game's plugins object is empty. - */ - BOOST_LOG_TRIVIAL(info) << "Getting data specific to LOOT's active game."; - // Get masterlist revision info and parse if it exists. Also get plugin headers info and parse userlist if it exists. - - // First clear CRC and condition caches, otherwise they could lead to incorrect evaluations. - lootState_.getCurrentGame().ClearCachedConditions(); - - bool isFirstLoad = lootState_.getCurrentGame().GetPlugins().empty(); - lootState_.getCurrentGame().LoadAllInstalledPlugins(true); - - //Sort plugins into their load order. - list installed; - vector loadOrder = lootState_.getCurrentGame().GetLoadOrder(); - for (const auto &pluginName : loadOrder) { - try { - const auto plugin = lootState_.getCurrentGame().GetPlugin(pluginName); - installed.push_back(plugin); - } catch (...) {} - } - - if (isFirstLoad) { - //Parse masterlist, don't update it. - if (exists(lootState_.getCurrentGame().MasterlistPath())) { - BOOST_LOG_TRIVIAL(debug) << "Parsing masterlist."; - try { - lootState_.getCurrentGame().GetMasterlist().Load(lootState_.getCurrentGame().MasterlistPath()); - } catch (exception &e) { - lootState_.getCurrentGame().GetMasterlist().AppendMessage(Message(MessageType::error, (boost::format(translate( - "An error occurred while parsing the masterlist: %1%. " - "This probably happened because an update to LOOT changed " - "its metadata syntax support. Try updating your masterlist " - "to resolve the error." - )) % e.what()).str())); - } - } - - //Parse userlist. - if (exists(lootState_.getCurrentGame().UserlistPath())) { - BOOST_LOG_TRIVIAL(debug) << "Parsing userlist."; - try { - lootState_.getCurrentGame().GetUserlist().Load(lootState_.getCurrentGame().UserlistPath()); - } catch (exception &e) { - lootState_.getCurrentGame().GetUserlist().AppendMessage(Message(MessageType::error, (boost::format(translate( - "An error occurred while parsing the userlist: %1%. " - "This probably happened because an update to LOOT changed " - "its metadata syntax support. Your user metadata will have " - "to be updated manually.\n\n" - "To do so, use the 'Open Debug Log Location' in LOOT's main " - "menu to open its data folder, then open your 'userlist.yaml' " - "file in the relevant game folder. You can then edit the " - "metadata it contains with reference to the " - "[syntax documentation](https://loot.github.io/docs/%2%.%3%.%4%/LOOT%%20Metadata%%20Syntax.html).\n\n" - "You can also seek support on LOOT's forum thread, which is " - "linked to on [LOOT's website](https://loot.github.io/)." - )) % e.what() % LootVersion::major % LootVersion::minor % LootVersion::patch).str())); - } - } - } - - // Now convert to a single object that can be turned into a JSON string - //--------------------------------------------------------------------- - - // The data structure is to be set as 'loot.game'. - YAML::Node gameNode; - - // ID the game using its folder value. - gameNode["folder"] = lootState_.getCurrentGame().FolderName(); - - // Store the masterlist revision and date. - try { - Masterlist::Info info = lootState_.getCurrentGame().GetMasterlist().GetInfo(lootState_.getCurrentGame().MasterlistPath(), true); - gameNode["masterlist"]["revision"] = info.revision; - gameNode["masterlist"]["date"] = info.date; - } catch (Error &e) { - gameNode["masterlist"]["revision"] = e.what(); - gameNode["masterlist"]["date"] = e.what(); - } - - // Now store global messages. - gameNode["globalMessages"] = GetGeneralMessages(lootState_.getLanguage().GetCode()); - - gameNode["bashTags"] = lootState_.getCurrentGame().GetMasterlist().BashTags(); - - // Now store plugin data. - for (const auto& plugin : installed) { - /* Each plugin has members while hold its raw masterlist and userlist data for - the editor, and also processed data for the main display. - */ - YAML::Node pluginNode; - // Find the masterlist metadata for this plugin. Treat Bash Tags from the plugin - // description as part of it. - BOOST_LOG_TRIVIAL(trace) << "Getting masterlist metadata for: " << plugin.Name(); - Plugin mlistPlugin(plugin); - mlistPlugin.MergeMetadata(lootState_.getCurrentGame().GetMasterlist().FindPlugin(plugin)); - - // Now do the same again for any userlist data. - BOOST_LOG_TRIVIAL(trace) << "Getting userlist metadata for: " << plugin.Name(); - PluginMetadata ulistPlugin(lootState_.getCurrentGame().GetUserlist().FindPlugin(plugin)); - - pluginNode["__type"] = "Plugin"; // For conversion back into a JS typed object. - pluginNode["name"] = plugin.Name(); - pluginNode["isActive"] = plugin.IsActive(); - pluginNode["isEmpty"] = plugin.IsEmpty(); - pluginNode["isMaster"] = plugin.isMasterFile(); - pluginNode["loadsArchive"] = plugin.LoadsArchive(); - pluginNode["crc"] = plugin.Crc(); - pluginNode["version"] = Version(plugin.getDescription()).AsString(); - - if (!mlistPlugin.HasNameOnly()) { - // Now add the masterlist metadata to the pluginNode. - pluginNode["masterlist"]["after"] = mlistPlugin.LoadAfter(); - pluginNode["masterlist"]["req"] = mlistPlugin.Reqs(); - pluginNode["masterlist"]["inc"] = mlistPlugin.Incs(); - pluginNode["masterlist"]["msg"] = ToEditorMessages(mlistPlugin.Messages(), lootState_.getLanguage().GetCode()); - pluginNode["masterlist"]["tag"] = mlistPlugin.Tags(); - pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo(); - pluginNode["masterlist"]["clean"] = mlistPlugin.CleanInfo(); - pluginNode["masterlist"]["url"] = mlistPlugin.Locations(); - } - - if (!ulistPlugin.HasNameOnly()) { - // Now add the userlist metadata to the pluginNode. - pluginNode["userlist"]["enabled"] = ulistPlugin.Enabled(); - pluginNode["userlist"]["after"] = ulistPlugin.LoadAfter(); - pluginNode["userlist"]["req"] = ulistPlugin.Reqs(); - pluginNode["userlist"]["inc"] = ulistPlugin.Incs(); - pluginNode["userlist"]["msg"] = ToEditorMessages(ulistPlugin.Messages(), lootState_.getLanguage().GetCode()); - pluginNode["userlist"]["tag"] = ulistPlugin.Tags(); - pluginNode["userlist"]["dirty"] = ulistPlugin.DirtyInfo(); - pluginNode["userlist"]["clean"] = ulistPlugin.CleanInfo(); - pluginNode["userlist"]["url"] = ulistPlugin.Locations(); - } - - // Now merge masterlist and userlist metadata and evaluate, - // putting any resulting metadata into the base of the pluginNode. - YAML::Node derivedNode = GenerateDerivedMetadata(plugin, mlistPlugin, ulistPlugin); - - for (auto it = derivedNode.begin(); it != derivedNode.end(); ++it) { - const string key = it->first.as(); - pluginNode[key] = it->second; - } - - gameNode["plugins"].push_back(pluginNode); - } - - callback->Success(JSON::stringify(gameNode)); + CefPostTask(TID_FILE, base::Bind(&Query::execute, query, callback)); } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to get game data. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to get game data. Details: %1%")) % e.what()).str()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to get game data. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to get game data. Details: %1%")) % e.what()).str()); - } -} - -void QueryHandler::UpdateMasterlist(CefRefPtr callback) { - try { - // Update / parse masterlist. - BOOST_LOG_TRIVIAL(debug) << "Updating and parsing masterlist."; - bool wasChanged = true; - try { - wasChanged = lootState_.getCurrentGame().GetMasterlist().Update(lootState_.getCurrentGame()); - } catch (Error &e) { - if (e.code() == Error::Code::ok) { - // There was a parsing error, but roll-back was successful, so the process - - // should still complete. - lootState_.getCurrentGame().GetMasterlist().AppendMessage(Message(MessageType::error, e.what())); - wasChanged = true; - } else { - // Error wasn't a parsing error. Need to try parsing masterlist if it exists. - try { - lootState_.getCurrentGame().GetMasterlist().Load(lootState_.getCurrentGame().MasterlistPath()); - } catch (...) {} - } - throw; - } - - // Now regenerate the JS-side masterlist data if the masterlist was changed. - if (wasChanged) { - // The data structure is to be set as 'loot.game'. - YAML::Node gameNode; - - // Store the masterlist revision and date. - try { - Masterlist::Info info = lootState_.getCurrentGame().GetMasterlist().GetInfo(lootState_.getCurrentGame().MasterlistPath(), true); - gameNode["masterlist"]["revision"] = info.revision; - gameNode["masterlist"]["date"] = info.date; - } catch (Error &e) { - gameNode["masterlist"]["revision"] = e.what(); - gameNode["masterlist"]["date"] = e.what(); - } - - // Store bash tags in case they have changed. - gameNode["bashTags"] = lootState_.getCurrentGame().GetMasterlist().BashTags(); - - // Store global messages in case they have changed. - gameNode["globalMessages"] = GetGeneralMessages(lootState_.getLanguage().GetCode()); - - for (const auto& plugin : lootState_.getCurrentGame().GetPlugins()) { - Plugin mlistPlugin(plugin); - mlistPlugin.MergeMetadata(lootState_.getCurrentGame().GetMasterlist().FindPlugin(plugin)); - - YAML::Node pluginNode; - if (!mlistPlugin.HasNameOnly()) { - // Now add the masterlist metadata to the pluginNode. - pluginNode["masterlist"]["after"] = mlistPlugin.LoadAfter(); - pluginNode["masterlist"]["req"] = mlistPlugin.Reqs(); - pluginNode["masterlist"]["inc"] = mlistPlugin.Incs(); - pluginNode["masterlist"]["msg"] = mlistPlugin.Messages(); - pluginNode["masterlist"]["tag"] = mlistPlugin.Tags(); - pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo(); - pluginNode["masterlist"]["clean"] = mlistPlugin.CleanInfo(); - pluginNode["masterlist"]["url"] = mlistPlugin.Locations(); - } - - // Now merge masterlist and userlist metadata and evaluate, - // putting any resulting metadata into the base of the pluginNode. - YAML::Node derivedNode = GenerateDerivedMetadata(plugin.Name()); - - for (const auto &pair : derivedNode) { - const string key = pair.first.as(); - pluginNode[key] = pair.second; - } - - gameNode["plugins"].push_back(pluginNode); - } - - callback->Success(JSON::stringify(gameNode)); - } else - callback->Success("null"); - } catch (Error &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to update the masterlist. Details: " << e.what(); - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to update the masterlist. Details: %1%")) % e.what()).str()); + BOOST_LOG_TRIVIAL(error) << e.what(); + callback->Failure(e.codeAsUnsignedInt(), e.what()); } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to update the masterlist. Details: " << e.what(); - callback->Failure(-1, (boost::format(translate("Failed to update the masterlist. Details: %1%")) % e.what()).str()); + BOOST_LOG_TRIVIAL(error) << e.what(); + callback->Failure(-1, e.what()); } + + return true; } -std::string QueryHandler::ClearAllMetadata() { - BOOST_LOG_TRIVIAL(debug) << "Clearing all user metadata."; - // Record which plugins have userlist entries. - vector userlistPlugins; - for (const auto &plugin : lootState_.getCurrentGame().GetUserlist().Plugins()) { - userlistPlugins.push_back(plugin.Name()); - } - BOOST_LOG_TRIVIAL(trace) << "User metadata exists for " << userlistPlugins.size() << " plugins."; +CefRefPtr QueryHandler::createQuery(CefRefPtr browser, + CefRefPtr frame, + const YAML::Node& request) { + const string name = request["name"].as(); - // Clear the user metadata. - lootState_.getCurrentGame().GetUserlist().Clear(); + if (name == "applySort") + return new ApplySortQuery(lootState_, request["args"][0].as>()); + else if (name == "cancelFind") + return new CancelFindQuery(browser); + else if (name == "cancelSort") + return new CancelSortQuery(lootState_); + else if (name == "changeGame") + return new ChangeGameQuery(lootState_, frame, request["args"][0].as()); + else if (name == "clearAllMetadata") + return new ClearAllMetadataQuery(lootState_); + else if (name == "clearPluginMetadata") + return new ClearPluginMetadataQuery(lootState_, request["args"][0].as()); + else if (name == "closeSettings") + return new CloseSettingsQuery(lootState_, request["args"][0]); + else if (name == "copyContent") + return new CopyContentQuery(request["args"][0]); + else if (name == "copyLoadOrder") + return new CopyLoadOrderQuery(lootState_, request["args"][0].as>()); + else if (name == "copyLoadOrder") + return new CopyMetadataQuery(lootState_, request["args"][0].as()); + else if (name == "discardUnappliedChanges") + return new DiscardUnappliedChangesQuery(lootState_); + else if (name == "editorClosed") + return new EditorClosedQuery(lootState_, request["args"][0]); + else if (name == "editorOpened") + return new EditorOpenedQuery(lootState_); + else if (name == "getConflictingPlugins") + return new GetConflictingPluginsQuery(lootState_, request["args"][0].as()); + else if (name == "getGameTypes") + return new GetGameTypesQuery(); + else if (name == "getGameData") + return new GetGameDataQuery(lootState_, frame); + else if (name == "getInitErrors") + return new GetInitErrorsQuery(lootState_); + else if (name == "getInstalledGames") + return new GetInstalledGamesQuery(lootState_); + else if (name == "getLanguages") + return new GetLanguagesQuery(); + else if (name == "getSettings") + return new GetSettingsQuery(lootState_); + else if (name == "getVersion") + return new GetVersionQuery(); + else if (name == "openLogLocation") + return new OpenLogLocationQuery(); + else if (name == "openReadme") + return new OpenReadmeQuery(); + else if (name == "redatePlugins") + return new RedatePluginsQuery(lootState_); + else if (name == "saveFilterState") + return new SaveFilterStateQuery(lootState_, request["args"][0].as(), request["args"][1].as()); + else if (name == "sortPlugins") + return new SortPluginsQuery(lootState_, frame); + else if (name == "updateMasterlist") + return new UpdateMasterlistQuery(lootState_); - // Save userlist edits. - lootState_.getCurrentGame().GetUserlist().Save(lootState_.getCurrentGame().UserlistPath()); - - // Regenerate the derived metadata (priority, messages, tags and dirty state) - // for any plugins with userlist entries. - YAML::Node pluginsNode; - for (const auto &plugin : userlistPlugins) { - pluginsNode.push_back(GenerateDerivedMetadata(plugin)); - } - BOOST_LOG_TRIVIAL(trace) << "Display metadata rederived for " << pluginsNode.size() << " plugins."; - - if (pluginsNode.size() > 0) - return JSON::stringify(pluginsNode); - else - return "[]"; -} - -void QueryHandler::SortPlugins(CefRefPtr frame, CefRefPtr callback) { - BOOST_LOG_TRIVIAL(info) << "Beginning sorting operation."; - BOOST_LOG_TRIVIAL(info) << "Using message language: " << lootState_.getLanguage().GetName(); - - try { - // Always reload all the plugins. - SendProgressUpdate(frame, translate("Loading plugin contents...")); - lootState_.getCurrentGame().LoadAllInstalledPlugins(false); - - //Sort plugins into their load order. - SendProgressUpdate(frame, translate("Sorting load order...")); - PluginSorter sorter; - vector plugins = sorter.Sort(lootState_.getCurrentGame(), lootState_.getLanguage().GetCode()); - - // If TESV or FO4, check if load order has been changed. - if ((lootState_.getCurrentGame().Type() == GameType::tes5 || lootState_.getCurrentGame().Type() == GameType::fo4) - && equal(begin(plugins), end(plugins), begin(lootState_.getCurrentGame().GetLoadOrder()))) { - // Load order has not been changed, set it without asking for - // user input because there are no changes to accept and some - // plugins' positions may only be inferred and not written to - // loadorder.txt/plugins.txt. - std::vector newLoadOrder; - std::transform(begin(plugins), - end(plugins), - back_inserter(newLoadOrder), - [](const Plugin& plugin) { - return plugin.Name(); - }); - lootState_.getCurrentGame().SetLoadOrder(newLoadOrder); - } - - YAML::Node node; - - // Store global messages in case they have changed. - node["globalMessages"] = GetGeneralMessages(lootState_.getLanguage().GetCode()); - - for (const auto &plugin : plugins) { - YAML::Node pluginNode; - - pluginNode["name"] = plugin.Name(); - pluginNode["crc"] = plugin.Crc(); - pluginNode["isEmpty"] = plugin.IsEmpty(); - - // Sorting may have produced a plugin loading error message, so rederive displayed data. - YAML::Node derivedNode = GenerateDerivedMetadata(plugin.Name()); - for (const auto &pair : derivedNode) { - const string key = pair.first.as(); - pluginNode[key] = pair.second; - } - - node["plugins"].push_back(pluginNode); - } - lootState_.incrementUnappliedChangeCounter(); - - if (node.size() > 0) - callback->Success(JSON::stringify(node)); - else - callback->Success("null"); - } catch (Error& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to sort plugins. Details: " << e.what(); - if (e.code() == Error::Code::sorting_error) { - lootState_.getCurrentGame().AppendMessage(Message(MessageType::error, e.what())); - - YAML::Node node; - node["globalMessages"] = GetGeneralMessages(lootState_.getLanguage().GetCode()); - callback->Success(JSON::stringify(node)); - } else - callback->Failure(e.codeAsUnsignedInt(), (boost::format(translate("Failed to sort plugins. Details: %1%")) % e.what()).str()); - } -} - -std::vector QueryHandler::GetGeneralMessages(const LanguageCode language) const { - vector messages; - auto metadataListMessages = lootState_.getCurrentGame().GetMasterlist().Messages(); - messages.insert(end(messages), - begin(metadataListMessages), - end(metadataListMessages)); - - metadataListMessages = lootState_.getCurrentGame().GetUserlist().Messages(); - messages.insert(end(messages), - begin(metadataListMessages), - end(metadataListMessages)); - - auto gameMessages = lootState_.getCurrentGame().GetMessages(); - messages.insert(end(messages), - begin(gameMessages), - end(gameMessages)); - - try { - auto it = begin(messages); - while (it != end(messages)) { - if (!it->EvalCondition(lootState_.getCurrentGame())) - it = messages.erase(it); - else - ++it; - } - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what(); - messages.push_back(Message(MessageType::error, (format(translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str())); - } - - vector simpleMessages; - - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).GetName(); - std::transform(begin(messages), - end(messages), - std::back_inserter>(simpleMessages), - [&](const Message& message) { - return message.ToSimpleMessage(language); - }); - - return simpleMessages; -} - -YAML::Node QueryHandler::GenerateDerivedMetadata(const Plugin& file, const PluginMetadata& masterlist, const PluginMetadata& userlist) { - BOOST_LOG_TRIVIAL(info) << "Using message language: " << lootState_.getLanguage().GetName(); - - // Now rederive the displayed metadata from the masterlist and userlist. - Plugin tempPlugin(file); - - tempPlugin.MergeMetadata(masterlist); - tempPlugin.MergeMetadata(userlist); - - //Evaluate any conditions - BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; - try { - tempPlugin.EvalAllConditions(lootState_.getCurrentGame()); - } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "\"" << tempPlugin.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); - vector messages(tempPlugin.Messages()); - messages.push_back(Message(MessageType::error, (format(translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % tempPlugin.Name() % e.what()).str())); - tempPlugin.Messages(messages); - } - - //Also check install validity. - tempPlugin.CheckInstallValidity(lootState_.getCurrentGame()); - - // Now add to pluginNode. - YAML::Node pluginNode; - pluginNode["name"] = tempPlugin.Name(); - pluginNode["priority"] = tempPlugin.LocalPriority().getValue(); - pluginNode["globalPriority"] = tempPlugin.GlobalPriority().getValue(); - pluginNode["messages"] = tempPlugin.SimpleMessages(lootState_.getLanguage().GetCode()); - pluginNode["tags"] = tempPlugin.Tags(); - pluginNode["isDirty"] = !tempPlugin.DirtyInfo().empty(); - pluginNode["loadOrderIndex"] = lootState_.getCurrentGame().GetActiveLoadOrderIndex(tempPlugin.Name()); - - if (!tempPlugin.CleanInfo().empty()) { - pluginNode["cleanedWith"] = tempPlugin.CleanInfo().begin()->CleaningUtility(); - } else { - pluginNode["cleanedWith"] = ""; - } - - return pluginNode; -} - -YAML::Node QueryHandler::GenerateDerivedMetadata(const std::string& pluginName) { - // Now rederive the displayed metadata from the masterlist and userlist. - try { - auto plugin = lootState_.getCurrentGame().GetPlugin(pluginName); - PluginMetadata master(lootState_.getCurrentGame().GetMasterlist().FindPlugin(plugin)); - PluginMetadata user(lootState_.getCurrentGame().GetUserlist().FindPlugin(plugin)); - - return this->GenerateDerivedMetadata(plugin, master, user); - } catch (...) { - return YAML::Node(); - } -} - -void QueryHandler::CopyToClipboard(const std::string& text) { -#ifdef _WIN32 - if (!OpenClipboard(NULL)) { - throw Error(Error::Code::windows_error, "Failed to open the Windows clipboard."); - } - - if (!EmptyClipboard()) { - throw Error(Error::Code::windows_error, "Failed to empty the Windows clipboard."); - } - - // The clipboard takes a Unicode (ie. UTF-16) string that it then owns and must not - // be destroyed by LOOT. Convert the string, then copy it into a new block of - // memory for the clipboard. - std::wstring wtext = ToWinWide(text); - wchar_t * wcstr = new wchar_t[wtext.length() + 1]; - wcscpy(wcstr, wtext.c_str()); - - if (SetClipboardData(CF_UNICODETEXT, wcstr) == NULL) { - throw Error(Error::Code::windows_error, "Failed to copy metadata to the Windows clipboard."); - } - - if (!CloseClipboard()) { - throw Error(Error::Code::windows_error, "Failed to close the Windows clipboard."); - } -#endif -} - -void QueryHandler::SendProgressUpdate(CefRefPtr frame, const std::string& message) { - BOOST_LOG_TRIVIAL(trace) << "Sending progress update: " << message; - frame->ExecuteJavaScript("loot.Dialog.showProgress('" + message + "');", frame->GetURL(), 0); -} - -std::vector QueryHandler::ToEditorMessages(std::vector messages, const LanguageCode language) { - std::vector list; - - for (const auto& message : messages) { - list.push_back(EditorMessage(message, language)); - } - - return list; -} - -std::vector QueryHandler::ToMessages(std::vector messages) { - std::vector list; - - for (const auto& message : messages) { - list.push_back(Message( - message.type, - {{message.text, message.language}}, - message.condition)); - } - - return list; + return nullptr; } } diff --git a/src/gui/query_handler.h b/src/gui/query_handler.h index de555c2e..7d17b2ea 100644 --- a/src/gui/query_handler.h +++ b/src/gui/query_handler.h @@ -32,6 +32,7 @@ #include "backend/plugin/plugin.h" #include "backend/metadata/plugin_metadata.h" #include "gui/editor_message.h" +#include "gui/query/query.h" namespace loot { class QueryHandler : public CefMessageRouterBrowserSide::Handler { @@ -46,38 +47,9 @@ public: bool persistent, CefRefPtr callback) OVERRIDE; private: - // Handle queries with input arguments. - bool HandleQuery(CefRefPtr browser, - CefRefPtr frame, - YAML::Node& request, - CefRefPtr callback); - - void OpenReadme(); - void OpenLogLocation(); - std::string GetVersion(); - std::string GetSettings(); - std::string GetLanguages(); - std::string GetGameTypes(); - std::string GetInstalledGames(); - void GetGameData(CefRefPtr frame, CefRefPtr callback); - void UpdateMasterlist(CefRefPtr callback); - std::string ClearAllMetadata(); - void SortPlugins(CefRefPtr frame, CefRefPtr callback); - - void GetConflictingPlugins(const std::string& pluginName, CefRefPtr callback); - void CopyMetadata(const std::string& pluginName); - std::string ClearPluginMetadata(const std::string& pluginName); - std::string ApplyUserEdits(const YAML::Node& pluginMetadata); - - std::vector GetGeneralMessages(const LanguageCode language) const; - YAML::Node GenerateDerivedMetadata(const std::string& pluginName); - YAML::Node GenerateDerivedMetadata(const Plugin& file, const PluginMetadata& masterlist, const PluginMetadata& userlist); - - void CopyToClipboard(const std::string& text); - void SendProgressUpdate(CefRefPtr frame, const std::string& message); - - std::vector ToEditorMessages(std::vector messages, const LanguageCode language); - std::vector ToMessages(std::vector messages); + CefRefPtr createQuery(CefRefPtr browser, + CefRefPtr frame, + const YAML::Node& request); LootState& lootState_; };