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_;
};