From 2daf0a3ef07e8601dd2c5b4bba83bd6a5abe250a Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Thu, 1 Sep 2016 17:46:04 +0100 Subject: [PATCH 1/8] Update CEF to 3.2785.1466.g80e473e The Linux build uses 3.2785.1465.g87ce149. They're only one commit apart, with no significant differences. --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b19fdb42..896b7cc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,13 +124,13 @@ set(YAML_CPP_INCLUDE_DIRS "${SOURCE_DIR}/include") set(YAML_CPP_LIBRARIES "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/libyaml-cpp${CMAKE_STATIC_LIBRARY_SUFFIX}") IF (CMAKE_SYSTEM_NAME MATCHES "Windows") - set(CEF_URL "http://opensource.spotify.com/cefbuilds/cef_binary_3.2743.1442.ge29124d_windows32_minimal.tar.bz2") - set(CEF_URL_HASH "8d0bb1ba2abccebef1e0d45fa2bb9c7ed7ec60de") + set(CEF_URL "http://opensource.spotify.com/cefbuilds/cef_binary_3.2785.1466.g80e473e_windows32_minimal.tar.bz2") + set(CEF_URL_HASH "31086a64fb226cdb4b4ce5a93d5eb32ea0308c17") set(CEF_PRECOMPILED_BINARIES "libcef.dll" "natives_blob.bin" "snapshot_blob.bin" "d3dcompiler_47.dll" "libEGL.dll" "libGLESv2.dll") set(CEF_LIB libcef${CMAKE_STATIC_LIBRARY_SUFFIX}) ELSE () - set(CEF_URL "http://opensource.spotify.com/cefbuilds/cef_binary_3.2743.1442.ge29124d_linux64_minimal.tar.bz2") - set(CEF_URL_HASH "9a8f227d08d9c4edb6636041375b900d6b864e12") + set(CEF_URL "http://opensource.spotify.com/cefbuilds/cef_binary_3.2785.1465.g87ce149_linux64_minimal.tar.bz2") + set(CEF_URL_HASH "78352474cc6db47aa517596fd44a99940a408c2e") set(CEF_PRECOMPILED_BINARIES "libcef.so" "natives_blob.bin" "snapshot_blob.bin" "chrome-sandbox") set(CEF_LIB libcef${CMAKE_SHARED_LIBRARY_SUFFIX}) ENDIF() From fd076b7a39174007ae35d68c10db54f11d8cb646 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Tue, 30 Aug 2016 20:48:03 +0100 Subject: [PATCH 2/8] Fixed silent JS test error --- src/tests/gui/html/js/test_plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/gui/html/js/test_plugin.js b/src/tests/gui/html/js/test_plugin.js index 06d23744..3cdfae8c 100644 --- a/src/tests/gui/html/js/test_plugin.js +++ b/src/tests/gui/html/js/test_plugin.js @@ -1276,7 +1276,7 @@ describe('PluginCardContent', () => { it('should return an array missing the "do not clean" message when the "do not clean" messages filter is enabled', () => { filters.hideDoNotCleanMessages = true; plugin.getCardContent(filters).messages.should.deepEqual([ - type: plugin.messages[0], + plugin.messages[0], ]); }); From 6e976923fb7381080bbed52fa0acea5a6f9911c1 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Tue, 30 Aug 2016 20:20:25 +0100 Subject: [PATCH 3/8] Use the same request structure for all queries This simplifies the parsing logic and further refactoring. --- resources/l10n/template.pot | 38 ++++++------ src/backend/helpers/json.h | 2 +- src/gui/html/js/events.js | 2 +- src/gui/html/js/query.js | 13 ++-- src/gui/query_handler.cpp | 95 +++++++++++++---------------- src/gui/query_handler.h | 12 ++-- src/tests/gui/html/js/test_query.js | 6 +- 7 files changed, 76 insertions(+), 92 deletions(-) diff --git a/resources/l10n/template.pot b/resources/l10n/template.pot index c37835ae..1f3f707d 100644 --- a/resources/l10n/template.pot +++ b/resources/l10n/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: LOOT 0.9.2\n" "Report-Msgid-Bugs-To: https://github.com/loot/loot/issues\n" -"POT-Creation-Date: 2016-08-17 18:15+0100\n" +"POT-Creation-Date: 2016-09-10 10:52+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -584,32 +584,32 @@ msgid "" "can be accessed through the main menu." msgstr "" -#: src/gui/query_handler.cpp:111 +#: src/gui/query_handler.cpp:133 msgid "Parsing, merging and evaluating metadata..." msgstr "" -#: src/gui/query_handler.cpp:203 src/gui/query_handler.cpp:206 +#: src/gui/query_handler.cpp:191 src/gui/query_handler.cpp:194 msgid "Failed to change game. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:220 src/gui/query_handler.cpp:223 -#: src/gui/query_handler.cpp:298 src/gui/query_handler.cpp:301 -#: src/gui/query_handler.cpp:329 src/gui/query_handler.cpp:332 +#: src/gui/query_handler.cpp:208 src/gui/query_handler.cpp:211 +#: src/gui/query_handler.cpp:289 src/gui/query_handler.cpp:292 +#: src/gui/query_handler.cpp:320 src/gui/query_handler.cpp:323 msgid "Failed to copy plugin metadata. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:238 src/gui/query_handler.cpp:248 +#: src/gui/query_handler.cpp:229 src/gui/query_handler.cpp:239 msgid "Failed to apply plugin metadata. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:601 +#: src/gui/query_handler.cpp:592 msgid "" "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." msgstr "" -#: src/gui/query_handler.cpp:616 +#: src/gui/query_handler.cpp:607 msgid "" "An error occurred while parsing the userlist: %1%. This probably happened " "because an update to LOOT changed its metadata syntax support. Your user " @@ -625,33 +625,33 @@ msgid "" "[LOOT's website](https://loot.github.io/)." msgstr "" -#: src/gui/query_handler.cpp:721 src/gui/query_handler.cpp:724 +#: src/gui/query_handler.cpp:712 src/gui/query_handler.cpp:715 msgid "Failed to get game data. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:806 src/gui/query_handler.cpp:809 +#: src/gui/query_handler.cpp:797 src/gui/query_handler.cpp:800 msgid "Failed to update the masterlist. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:848 +#: src/gui/query_handler.cpp:839 msgid "Loading plugin contents..." msgstr "" -#: src/gui/query_handler.cpp:852 +#: src/gui/query_handler.cpp:843 msgid "Sorting load order..." msgstr "" -#: src/gui/query_handler.cpp:909 +#: src/gui/query_handler.cpp:900 msgid "Failed to sort plugins. Details: %1%" msgstr "" -#: src/gui/query_handler.cpp:939 +#: src/gui/query_handler.cpp:931 msgid "" "A global message contains a condition that could not be evaluated. Details: " "%1%" msgstr "" -#: src/gui/query_handler.cpp:961 src/backend/plugin/plugin_sorter.cpp:217 +#: src/gui/query_handler.cpp:963 src/backend/plugin/plugin_sorter.cpp:217 msgid "\"%1%\" contains a condition that could not be evaluated. Details: %2%" msgstr "" @@ -759,12 +759,12 @@ msgstr "" msgid "Failed to open file in its default application." msgstr "" -#: src/backend/metadata/condition_evaluator.cpp:173 +#: src/backend/metadata/condition_evaluator.cpp:171 msgid "Invalid file path:" msgstr "" -#: src/backend/metadata/condition_evaluator.cpp:183 -#: src/backend/metadata/condition_evaluator.cpp:223 +#: src/backend/metadata/condition_evaluator.cpp:181 +#: src/backend/metadata/condition_evaluator.cpp:221 msgid "Invalid regex string \"%1%\": %2%" msgstr "" diff --git a/src/backend/helpers/json.h b/src/backend/helpers/json.h index 863fc14c..8f1033b3 100644 --- a/src/backend/helpers/json.h +++ b/src/backend/helpers/json.h @@ -32,7 +32,7 @@ along with LOOT. If not, see namespace loot { namespace JSON { -YAML::Node parse(std::string& json) { +YAML::Node parse(const std::string& json) { return YAML::Load(json); } diff --git a/src/gui/html/js/events.js b/src/gui/html/js/events.js index b36ea361..8a1f08a2 100644 --- a/src/gui/html/js/events.js +++ b/src/gui/html/js/events.js @@ -369,7 +369,7 @@ function onEditorClose(evt) { } else { /* Don't need to record changes, but still need to notify C++ side that the editor has been closed. */ - promise = loot.query('editorClosed'); + promise = loot.query('editorClosed', 'null'); } promise.catch(loot.handlePromiseError).then(() => { /* Remove body attribute so that sidebar items are styled correctly. */ diff --git a/src/gui/html/js/query.js b/src/gui/html/js/query.js index 8a07730c..8197c75d 100644 --- a/src/gui/html/js/query.js +++ b/src/gui/html/js/query.js @@ -12,15 +12,10 @@ if (!requestName) { throw new Error('No request name passed'); } - let request; - if (args.length === 0) { - request = requestName; - } else { - request = JSON.stringify({ - name: requestName, - args, - }); - } + const request = JSON.stringify({ + name: requestName, + args, + }); return new Promise((resolve, reject) => { window.cefQuery({ request, diff --git a/src/gui/query_handler.cpp b/src/gui/query_handler.cpp index 3dd6eda2..80c9e4d8 100644 --- a/src/gui/query_handler.cpp +++ b/src/gui/query_handler.cpp @@ -70,7 +70,27 @@ bool QueryHandler::OnQuery(CefRefPtr browser, const CefString& request, bool persistent, CefRefPtr callback) { - if (request == "openReadme") { + try { + YAML::Node parsedRequest = JSON::parse(request.ToString()); + + return HandleQuery(browser, frame, parsedRequest, callback); + } 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; +} + +// Handle queries with input arguments. +bool QueryHandler::HandleQuery(CefRefPtr browser, + CefRefPtr frame, + YAML::Node& request, + CefRefPtr callback) { + const string requestName = request["name"].as(); + + if (requestName == "openReadme") { try { OpenReadme(); callback->Success(""); @@ -82,7 +102,7 @@ bool QueryHandler::OnQuery(CefRefPtr browser, callback->Failure(-1, e.what()); } return true; - } else if (request == "openLogLocation") { + } else if (requestName == "openLogLocation") { try { OpenLogLocation(); callback->Success(""); @@ -94,32 +114,32 @@ bool QueryHandler::OnQuery(CefRefPtr browser, callback->Failure(-1, e.what()); } return true; - } else if (request == "getVersion") { + } else if (requestName == "getVersion") { callback->Success(GetVersion()); return true; - } else if (request == "getSettings") { + } else if (requestName == "getSettings") { callback->Success(GetSettings()); return true; - } else if (request == "getLanguages") { + } else if (requestName == "getLanguages") { callback->Success(GetLanguages()); return true; - } else if (request == "getGameTypes") { + } else if (requestName == "getGameTypes") { callback->Success(GetGameTypes()); return true; - } else if (request == "getInstalledGames") { + } else if (requestName == "getInstalledGames") { callback->Success(GetInstalledGames()); return true; - } else if (request == "getGameData") { + } 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 (request == "cancelFind") { + } else if (requestName == "cancelFind") { browser->GetHost()->StopFinding(true); callback->Success(""); return true; - } else if (request == "clearAllMetadata") { + } else if (requestName == "clearAllMetadata") { callback->Success(ClearAllMetadata()); return true; - } else if (request == "redatePlugins") { + } else if (requestName == "redatePlugins") { BOOST_LOG_TRIVIAL(debug) << "Redating plugins."; try { lootState_.getCurrentGame().RedatePlugins(); @@ -133,68 +153,34 @@ bool QueryHandler::OnQuery(CefRefPtr browser, } return true; - } else if (request == "updateMasterlist") { + } else if (requestName == "updateMasterlist") { return CefPostTask(TID_FILE, base::Bind(&QueryHandler::UpdateMasterlist, base::Unretained(this), callback)); - } else if (request == "sortPlugins") { + } else if (requestName == "sortPlugins") { return CefPostTask(TID_FILE, base::Bind(&QueryHandler::SortPlugins, base::Unretained(this), frame, callback)); - } else if (request == "getInitErrors") { + } 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 (request == "cancelSort") { + } 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 (request == "editorOpened") { + } else if (requestName == "editorOpened") { lootState_.incrementUnappliedChangeCounter(); callback->Success(""); return true; - } else if (request == "editorClosed") { - // This version of the editorClosed query has no arguments as it is - // sent when editing is cancelled. Just update the unapplied changes - // counter. - lootState_.decrementUnappliedChangeCounter(); - callback->Success(""); - return true; - } else if (request == "discardUnappliedChanges") { + } else if (requestName == "discardUnappliedChanges") { while (lootState_.hasUnappliedChanges()) lootState_.decrementUnappliedChangeCounter(); callback->Success(""); return true; - } else { - // May be a request with arguments. - YAML::Node req; - try { - // Can't pass this as a reference directly as GCC - // complains about it. - std::string requestString = request.ToString(); - req = JSON::parse(requestString); - } 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 HandleComplexQuery(browser, frame, req, callback); - } - - return false; -} - -// Handle queries with input arguments. -bool QueryHandler::HandleComplexQuery(CefRefPtr browser, - CefRefPtr frame, - YAML::Node& request, - CefRefPtr callback) { - const string requestName = request["name"].as(); - - if (requestName == "changeGame") { + } else if (requestName == "changeGame") { try { // Has one arg, which is the folder name of the new game. lootState_.changeGame(request["args"][0].as()); @@ -233,7 +219,10 @@ bool QueryHandler::HandleComplexQuery(CefRefPtr browser, BOOST_LOG_TRIVIAL(debug) << "Editor for plugin closed."; // One argument, which is the plugin metadata that has changed (+ its name). try { - callback->Success(ApplyUserEdits(request["args"][0])); + 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(); diff --git a/src/gui/query_handler.h b/src/gui/query_handler.h index cc5e391f..61e8338d 100644 --- a/src/gui/query_handler.h +++ b/src/gui/query_handler.h @@ -46,6 +46,12 @@ 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(); @@ -58,12 +64,6 @@ private: std::string ClearAllMetadata(); void SortPlugins(CefRefPtr frame, CefRefPtr callback); - // Handle queries with input arguments. - bool HandleComplexQuery(CefRefPtr browser, - CefRefPtr frame, - YAML::Node& request, - CefRefPtr callback); - void GetConflictingPlugins(const std::string& pluginName, CefRefPtr callback); void CopyMetadata(const std::string& pluginName); std::string ClearPluginMetadata(const std::string& pluginName); diff --git a/src/tests/gui/html/js/test_query.js b/src/tests/gui/html/js/test_query.js index 7c4d0193..3859100b 100644 --- a/src/tests/gui/html/js/test_query.js +++ b/src/tests/gui/html/js/test_query.js @@ -2,7 +2,7 @@ /* Mock the window.cefQuery method */ window.cefQuery = (obj) => { - if (obj.request === 'fail') { + if (obj.request === '{"name":"fail","args":[]}') { obj.onFailure(-1, obj.request); } else { obj.onSuccess(obj.request); @@ -19,7 +19,7 @@ describe('query()', () => { }); it('should succeed if a request name is passed', () => { - return loot.query('test').should.be.fulfilledWith('test'); + return loot.query('test').should.be.fulfilledWith('{"name":"test","args":[]}'); }); it('should succeed if a request name and arguments are passed', () => { @@ -34,6 +34,6 @@ describe('query()', () => { }); it('should fail with an Error object when an error occurs', () => { - return loot.query('fail').should.be.rejectedWith(Error, { message: 'Error code: -1; fail' }); + return loot.query('fail').should.be.rejectedWith(Error, { message: 'Error code: -1; {"name":"fail","args":[]}' }); }); }); From f136fbf74f42cb5a2a109bce48ad0daa6501cd23 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Thu, 8 Sep 2016 18:59:27 +0100 Subject: [PATCH 4/8] Store messages in vectors Using std::list doesn't provide any benefits, and means a few differences compared to the more standard vector container. --- src/api/api_database.cpp | 2 +- src/backend/metadata/plugin_metadata.cpp | 30 +++++++++---------- src/backend/metadata/plugin_metadata.h | 10 +++---- src/backend/metadata_list.cpp | 8 ++--- src/backend/metadata_list.h | 4 +-- src/backend/plugin/plugin_sorter.cpp | 4 +-- src/gui/query_handler.cpp | 12 ++++---- src/gui/query_handler.h | 4 +-- .../backend/metadata/plugin_metadata_test.h | 12 ++++---- src/tests/backend/metadata_list_test.h | 8 ++--- src/tests/backend/plugin/plugin_test.h | 8 ++--- 11 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/api/api_database.cpp b/src/api/api_database.cpp index b15529cc..06c87a75 100644 --- a/src/api/api_database.cpp +++ b/src/api/api_database.cpp @@ -191,7 +191,7 @@ PluginCleanliness ApiDatabase::GetPluginCleanliness(const std::string& plugin) { // This isn't a very reliable system, because if the lists have been evaluated in some language // other than English, the strings will be in different languages (and the API can't tell what they'd be) // and the strings may be non-standard and begin with something other than "Do not clean." anyway. - std::list messages(game_.GetMasterlist().FindPlugin(PluginMetadata(plugin)).Messages()); + std::vector messages(game_.GetMasterlist().FindPlugin(PluginMetadata(plugin)).Messages()); for (const auto& message : messages) { if (boost::starts_with(message.GetContent(LanguageCode::english).GetText(), "Do not clean")) { diff --git a/src/backend/metadata/plugin_metadata.cpp b/src/backend/metadata/plugin_metadata.cpp index 3cd11c07..cb160f67 100644 --- a/src/backend/metadata/plugin_metadata.cpp +++ b/src/backend/metadata/plugin_metadata.cpp @@ -37,10 +37,10 @@ #include "backend/helpers/helpers.h" using std::inserter; -using std::list; using std::regex; using std::regex_match; using std::set; +using std::vector; namespace loot { PluginMetadata::PluginMetadata() : enabled_(true) {} @@ -128,11 +128,11 @@ PluginMetadata PluginMetadata::DiffMetadata(const PluginMetadata& plugin) const inserter(filesDiff, begin(filesDiff))); p.Incs(filesDiff); - list msgs1 = plugin.Messages(); - list msgs2 = messages_; - msgs1.sort(); - msgs2.sort(); - list mDiff; + vector msgs1 = plugin.Messages(); + vector msgs2 = messages_; + std::sort(begin(msgs1), end(msgs1)); + std::sort(begin(msgs2), end(msgs2)); + vector mDiff; set_symmetric_difference(begin(msgs2), end(msgs2), begin(msgs1), @@ -206,11 +206,11 @@ PluginMetadata PluginMetadata::NewMetadata(const PluginMetadata& plugin) const { inserter(filesDiff, begin(filesDiff))); p.Incs(filesDiff); - list msgs1 = plugin.Messages(); - list msgs2 = messages_; - msgs1.sort(); - msgs2.sort(); - list mDiff; + vector msgs1 = plugin.Messages(); + vector msgs2 = messages_; + std::sort(begin(msgs1), end(msgs1)); + std::sort(begin(msgs2), end(msgs2)); + vector mDiff; set_difference(begin(msgs2), end(msgs2), begin(msgs1), @@ -281,7 +281,7 @@ std::set PluginMetadata::Incs() const { return incompatibilities_; } -std::list PluginMetadata::Messages() const { +std::vector PluginMetadata::Messages() const { return messages_; } @@ -301,8 +301,8 @@ std::set PluginMetadata::Locations() const { return locations_; } -std::list PluginMetadata::SimpleMessages(const LanguageCode language) const { - std::list simpleMessages(messages_.size()); +std::vector PluginMetadata::SimpleMessages(const LanguageCode language) const { + std::vector simpleMessages(messages_.size()); std::transform(begin(messages_), end(messages_), begin(simpleMessages), [&](const Message& message) { return message.ToSimpleMessage(language); }); @@ -334,7 +334,7 @@ void PluginMetadata::Incs(const std::set& i) { incompatibilities_ = i; } -void PluginMetadata::Messages(const std::list& m) { +void PluginMetadata::Messages(const std::vector& m) { messages_ = m; } diff --git a/src/backend/metadata/plugin_metadata.h b/src/backend/metadata/plugin_metadata.h index 9f7afa60..cd674ae4 100644 --- a/src/backend/metadata/plugin_metadata.h +++ b/src/backend/metadata/plugin_metadata.h @@ -71,13 +71,13 @@ public: std::set LoadAfter() const; std::set Reqs() const; std::set Incs() const; - std::list Messages() const; + std::vector Messages() const; std::set Tags() const; std::set DirtyInfo() const; std::set CleanInfo() const; std::set Locations() const; - std::list SimpleMessages(const LanguageCode language) const; + std::vector SimpleMessages(const LanguageCode language) const; void Enabled(const bool enabled); void LocalPriority(const Priority& priority); @@ -85,7 +85,7 @@ public: void LoadAfter(const std::set& after); void Reqs(const std::set& reqs); void Incs(const std::set& incs); - void Messages(const std::list& messages); + void Messages(const std::vector& messages); void Tags(const std::set& tags); void DirtyInfo(const std::set& info); void CleanInfo(const std::set& info); @@ -103,7 +103,7 @@ public: bool operator == (const std::string& rhs) const; bool operator != (const std::string& rhs) const; protected: - std::list messages_; + std::vector messages_; std::set tags_; private: std::string name_; @@ -201,7 +201,7 @@ struct convert { if (node["inc"]) rhs.Incs(node["inc"].as>()); if (node["msg"]) - rhs.Messages(node["msg"].as>()); + rhs.Messages(node["msg"].as>()); if (node["tag"]) rhs.Tags(node["tag"].as>()); if (node["dirty"]) { diff --git a/src/backend/metadata_list.cpp b/src/backend/metadata_list.cpp index 1551fb88..af26a546 100644 --- a/src/backend/metadata_list.cpp +++ b/src/backend/metadata_list.cpp @@ -31,8 +31,6 @@ #include "loot/error.h" #include "backend/game/game.h" -using std::list; - namespace loot { void MetadataList::Load(const boost::filesystem::path& filepath) { Clear(); @@ -58,7 +56,7 @@ void MetadataList::Load(const boost::filesystem::path& filepath) { } } if (metadataList["globals"]) - messages_ = metadataList["globals"].as>(); + messages_ = metadataList["globals"].as>(); if (metadataList["bash_tags"]) bashTags_ = metadataList["bash_tags"].as>(); @@ -89,14 +87,14 @@ void MetadataList::Clear() { } std::list MetadataList::Plugins() const { - list pluginList(plugins_.begin(), plugins_.end()); + std::list pluginList(plugins_.begin(), plugins_.end()); pluginList.insert(pluginList.end(), regexPlugins_.begin(), regexPlugins_.end()); return pluginList; } -std::list MetadataList::Messages() const { +std::vector MetadataList::Messages() const { return messages_; } diff --git a/src/backend/metadata_list.h b/src/backend/metadata_list.h index ef6a33f0..cba3d175 100644 --- a/src/backend/metadata_list.h +++ b/src/backend/metadata_list.h @@ -43,7 +43,7 @@ public: void Clear(); std::list Plugins() const; - std::list Messages() const; + std::vector Messages() const; std::set BashTags() const; // Merges multiple matching regex entries if any are found. @@ -63,7 +63,7 @@ protected: std::set bashTags_; std::unordered_set plugins_; std::list regexPlugins_; - std::list messages_; + std::vector messages_; }; } diff --git a/src/backend/plugin/plugin_sorter.cpp b/src/backend/plugin/plugin_sorter.cpp index be40abda..b58f162a 100644 --- a/src/backend/plugin/plugin_sorter.cpp +++ b/src/backend/plugin/plugin_sorter.cpp @@ -40,6 +40,7 @@ using std::list; using std::string; +using std::vector; namespace loot { typedef boost::graph_traits::vertex_iterator vertex_it; @@ -99,7 +100,6 @@ private: }; std::vector PluginSorter::Sort(Game& game, const LanguageCode language) { - using std::vector; // Clear existing data. graph_.clear(); indexMap_.clear(); @@ -213,7 +213,7 @@ void PluginSorter::AddPluginVertices(Game& game, const LanguageCode language) { graph_[v].EvalAllConditions(game); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << "\"" << graph_[v].Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); - list messages(graph_[v].Messages()); + vector messages(graph_[v].Messages()); messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % graph_[v].Name() % e.what()).str())); graph_[v].Messages(messages); } diff --git a/src/gui/query_handler.cpp b/src/gui/query_handler.cpp index 80c9e4d8..20c6f1df 100644 --- a/src/gui/query_handler.cpp +++ b/src/gui/query_handler.cpp @@ -454,7 +454,7 @@ std::string QueryHandler::ApplyUserEdits(const YAML::Node& pluginMetadata) { newUserlistEntry.Incs(pluginMetadata["userlist"]["inc"].as>()); if (pluginMetadata["userlist"]["msg"]) - newUserlistEntry.Messages(ToMessages(pluginMetadata["userlist"]["msg"].as>())); + newUserlistEntry.Messages(ToMessages(pluginMetadata["userlist"]["msg"].as>())); if (pluginMetadata["userlist"]["tag"]) newUserlistEntry.Tags(pluginMetadata["userlist"]["tag"].as>()); if (pluginMetadata["userlist"]["dirty"]) @@ -959,7 +959,7 @@ YAML::Node QueryHandler::GenerateDerivedMetadata(const Plugin& file, const Plugi 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(); - list messages(tempPlugin.Messages()); + 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); } @@ -1031,8 +1031,8 @@ void QueryHandler::SendProgressUpdate(CefRefPtr frame, const std::stri frame->ExecuteJavaScript("loot.Dialog.showProgress('" + message + "');", frame->GetURL(), 0); } -std::list QueryHandler::ToEditorMessages(std::list messages, const LanguageCode language) { - std::list list; +std::vector QueryHandler::ToEditorMessages(std::vector messages, const LanguageCode language) { + std::vector list; for (const auto& message : messages) { list.push_back(EditorMessage(message, language)); @@ -1041,8 +1041,8 @@ std::list QueryHandler::ToEditorMessages(std::list messa return list; } -std::list QueryHandler::ToMessages(std::list messages) { - std::list list; +std::vector QueryHandler::ToMessages(std::vector messages) { + std::vector list; for (const auto& message : messages) { list.push_back(Message( diff --git a/src/gui/query_handler.h b/src/gui/query_handler.h index 61e8338d..de555c2e 100644 --- a/src/gui/query_handler.h +++ b/src/gui/query_handler.h @@ -76,8 +76,8 @@ private: void CopyToClipboard(const std::string& text); void SendProgressUpdate(CefRefPtr frame, const std::string& message); - std::list ToEditorMessages(std::list messages, const LanguageCode language); - std::list ToMessages(std::list messages); + std::vector ToEditorMessages(std::vector messages, const LanguageCode language); + std::vector ToMessages(std::vector messages); LootState& lootState_; }; diff --git a/src/tests/backend/metadata/plugin_metadata_test.h b/src/tests/backend/metadata/plugin_metadata_test.h index 9067e574..522f5582 100644 --- a/src/tests/backend/metadata/plugin_metadata_test.h +++ b/src/tests/backend/metadata/plugin_metadata_test.h @@ -241,7 +241,7 @@ TEST_P(PluginMetadataTest, mergeMetadataShouldMergeMessages) { plugin2.Messages({message}); plugin1.MergeMetadata(plugin2); - EXPECT_EQ(std::list({message, message}), plugin1.Messages()); + EXPECT_EQ(std::vector({message, message}), plugin1.Messages()); } TEST_P(PluginMetadataTest, mergeMetadataShouldMergeTags) { @@ -419,7 +419,7 @@ TEST_P(PluginMetadataTest, diffMetadataShouldOutputMessagesThatAreNotCommonToBot plugin2.Messages({message1, message3}); PluginMetadata diff = plugin1.DiffMetadata(plugin2); - EXPECT_EQ(std::list({message2, message3}), diff.Messages()); + EXPECT_EQ(std::vector({message2, message3}), diff.Messages()); } TEST_P(PluginMetadataTest, diffMetadataShouldOutputTagsThatAreNotCommonToBothInputPlugins) { @@ -575,7 +575,7 @@ TEST_P(PluginMetadataTest, newMetadataShouldOutputMessagesThatAreNotCommonToBoth plugin2.Messages({message1, message3}); PluginMetadata newMetadata = plugin1.NewMetadata(plugin2); - EXPECT_EQ(std::list({message2}), newMetadata.Messages()); + EXPECT_EQ(std::vector({message2}), newMetadata.Messages()); } TEST_P(PluginMetadataTest, newMetadataShouldOutputTagsThatAreNotCommonToBothInputPlugins) { @@ -687,7 +687,7 @@ TEST_P(PluginMetadataTest, evalAllConditionsShouldEvaluateAllMetadataConditions) EXPECT_EQ(expectedFiles, plugin.LoadAfter()); EXPECT_EQ(expectedFiles, plugin.Reqs()); EXPECT_EQ(expectedFiles, plugin.Incs()); - EXPECT_EQ(std::list({message1}), plugin.Messages()); + EXPECT_EQ(std::vector({message1}), plugin.Messages()); EXPECT_EQ(std::set({tag1}), plugin.Tags()); EXPECT_EQ(std::set({info1}), plugin.DirtyInfo()); EXPECT_EQ(std::set({info1}), plugin.CleanInfo()); @@ -1073,7 +1073,7 @@ TEST_P(PluginMetadataTest, encodingAsYamlShouldSetMsgFieldIfMessagesExist) { YAML::Node node; node = plugin; - EXPECT_EQ(plugin.Messages(), node["msg"].as>()); + EXPECT_EQ(plugin.Messages(), node["msg"].as>()); } TEST_P(PluginMetadataTest, encodingAsYamlShouldSetTagFieldIfTagsExist) { @@ -1163,7 +1163,7 @@ TEST_P(PluginMetadataTest, decodingFromYamlShouldStoreAllGivenData) { EXPECT_EQ(std::set({ File("Blank.esm") }), plugin.Incs()); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::say, "content") }), plugin.Messages()); EXPECT_EQ(std::set({ diff --git a/src/tests/backend/metadata_list_test.h b/src/tests/backend/metadata_list_test.h index 040fb764..b0dc0a76 100644 --- a/src/tests/backend/metadata_list_test.h +++ b/src/tests/backend/metadata_list_test.h @@ -82,7 +82,7 @@ TEST_P(MetadataListTest, loadShouldLoadGlobalMessages) { MetadataList metadataList; EXPECT_NO_THROW(metadataList.Load(metadataPath)); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::say, "A global message."), }), metadataList.Messages()); } @@ -170,7 +170,7 @@ TEST_P(MetadataListTest, saveShouldWriteTheLoadedMetadataToTheGivenFilePath) { "Relev" }), metadataList.BashTags()); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::say, "A global message."), }), metadataList.Messages()); @@ -290,7 +290,7 @@ TEST_P(MetadataListTest, evalAllConditionsShouldEvaluateTheConditionsForThePlugi ASSERT_NO_THROW(metadataList.Load(metadataPath)); PluginMetadata plugin = metadataList.FindPlugin(PluginMetadata(blankEsm)); - ASSERT_EQ(std::list({ + ASSERT_EQ(std::vector({ Message(MessageType::warn, "This is a warning."), Message(MessageType::say, "This message should be removed when evaluating conditions."), }), plugin.Messages()); @@ -302,7 +302,7 @@ TEST_P(MetadataListTest, evalAllConditionsShouldEvaluateTheConditionsForThePlugi EXPECT_NO_THROW(metadataList.EvalAllConditions(game)); plugin = metadataList.FindPlugin(PluginMetadata(blankEsm)); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::warn, "This is a warning."), }), plugin.Messages()); diff --git a/src/tests/backend/plugin/plugin_test.h b/src/tests/backend/plugin/plugin_test.h index 51a23f71..8d313492 100644 --- a/src/tests/backend/plugin/plugin_test.h +++ b/src/tests/backend/plugin/plugin_test.h @@ -281,7 +281,7 @@ TEST_P(PluginTest, checkInstallValidityShouldCheckThatRequirementsArePresent) { }); plugin.CheckInstallValidity(game_); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::error, "This plugin requires \"" + missingEsp + "\" to be installed, but it is missing."), }), plugin.Messages()); } @@ -294,7 +294,7 @@ TEST_P(PluginTest, checkInstallValidityShouldCheckThatIncompatibilitiesAreAbsent }); plugin.CheckInstallValidity(game_); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::error, "This plugin is incompatible with \"" + masterFile + "\", but both are present."), }), plugin.Messages()); } @@ -311,7 +311,7 @@ TEST_P(PluginTest, checkInstallValidityShouldGenerateMessagesFromDirtyInfo) { }); plugin.CheckInstallValidity(game_); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ PluginCleaningData(blankEsmCrc, "utility1", info, 0, 1, 2).AsMessage(), PluginCleaningData(0xDEADBEEF, "utility2", info, 0, 5, 10).AsMessage(), }), plugin.Messages()); @@ -321,7 +321,7 @@ TEST_P(PluginTest, checkInstallValidityShouldCheckIfAPluginsMastersAreAllPresent Plugin plugin(game_, blankDifferentMasterDependentEsp, false); plugin.CheckInstallValidity(game_); - EXPECT_EQ(std::list({ + EXPECT_EQ(std::vector({ Message(MessageType::error, "This plugin requires \"" + blankDifferentEsm + "\" to be active, but it is inactive."), }), plugin.Messages()); } From e0907a33cef65d9756cc5806542468fe1990b462 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Fri, 2 Sep 2016 21:19:32 +0100 Subject: [PATCH 5/8] Refactor JS<->C++ query execution Use Query-based classes to contain the actual execution logic. --- CMakeLists.txt | 30 + src/gui/query/apply_sort_query.h | 49 + src/gui/query/cancel_find_query.h | 47 + src/gui/query/cancel_sort_query.h | 52 + src/gui/query/change_game_query.h | 53 + src/gui/query/clear_all_metadata_query.h | 83 ++ src/gui/query/clear_plugin_metadata_query.h | 60 + src/gui/query/clipboard_query.h | 64 + src/gui/query/close_settings_query.h | 50 + src/gui/query/copy_content_query.h | 60 + src/gui/query/copy_load_order_query.h | 74 ++ src/gui/query/copy_metadata_query.h | 70 ++ .../query/discard_unapplied_changes_query.h | 47 + src/gui/query/editor_closed_query.h | 176 +++ src/gui/query/editor_opened_query.h | 46 + src/gui/query/get_conflicting_plugins_query.h | 83 ++ src/gui/query/get_game_data_query.h | 205 ++++ src/gui/query/get_game_types_query.h | 55 + src/gui/query/get_init_errors_query.h | 50 + src/gui/query/get_installed_games_query.h | 55 + src/gui/query/get_languages_query.h | 56 + src/gui/query/get_settings_query.h | 47 + src/gui/query/get_version_query.h | 41 + src/gui/query/metadata_query.h | 155 +++ src/gui/query/open_log_location_query.h | 44 + src/gui/query/open_readme_query.h | 44 + src/gui/query/query.h | 47 + src/gui/query/redate_plugins_query.h | 46 + src/gui/query/save_filter_state_query.h | 54 + src/gui/query/sort_plugins_query.h | 136 +++ src/gui/query/update_masterlist_query.h | 133 +++ src/gui/query_handler.cpp | 1063 ++--------------- src/gui/query_handler.h | 36 +- 33 files changed, 2314 insertions(+), 997 deletions(-) create mode 100644 src/gui/query/apply_sort_query.h create mode 100644 src/gui/query/cancel_find_query.h create mode 100644 src/gui/query/cancel_sort_query.h create mode 100644 src/gui/query/change_game_query.h create mode 100644 src/gui/query/clear_all_metadata_query.h create mode 100644 src/gui/query/clear_plugin_metadata_query.h create mode 100644 src/gui/query/clipboard_query.h create mode 100644 src/gui/query/close_settings_query.h create mode 100644 src/gui/query/copy_content_query.h create mode 100644 src/gui/query/copy_load_order_query.h create mode 100644 src/gui/query/copy_metadata_query.h create mode 100644 src/gui/query/discard_unapplied_changes_query.h create mode 100644 src/gui/query/editor_closed_query.h create mode 100644 src/gui/query/editor_opened_query.h create mode 100644 src/gui/query/get_conflicting_plugins_query.h create mode 100644 src/gui/query/get_game_data_query.h create mode 100644 src/gui/query/get_game_types_query.h create mode 100644 src/gui/query/get_init_errors_query.h create mode 100644 src/gui/query/get_installed_games_query.h create mode 100644 src/gui/query/get_languages_query.h create mode 100644 src/gui/query/get_settings_query.h create mode 100644 src/gui/query/get_version_query.h create mode 100644 src/gui/query/metadata_query.h create mode 100644 src/gui/query/open_log_location_query.h create mode 100644 src/gui/query/open_readme_query.h create mode 100644 src/gui/query/query.h create mode 100644 src/gui/query/redate_plugins_query.h create mode 100644 src/gui/query/save_filter_state_query.h create mode 100644 src/gui/query/sort_plugins_query.h create mode 100644 src/gui/query/update_masterlist_query.h 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_; }; From a557767443906dd9707efdc9842c0e6ad2f006ae Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sat, 10 Sep 2016 15:54:38 +0100 Subject: [PATCH 6/8] Fix query error handling Queries are executed in a separate thread from the query handler, and exception handling needs to be done in that thread, so refactor exception handling into Query::execute() and move query-specific logic to Query::executeLogic(). Also simplify Query::executeLogic() to return the success string, since they all throw on failure. --- src/gui/query/apply_sort_query.h | 7 ++++--- src/gui/query/cancel_find_query.h | 6 +++--- src/gui/query/cancel_sort_query.h | 5 ++--- src/gui/query/change_game_query.h | 4 ++-- src/gui/query/clear_all_metadata_query.h | 5 +++-- src/gui/query/clear_plugin_metadata_query.h | 10 +++++----- src/gui/query/close_settings_query.h | 4 ++-- src/gui/query/copy_content_query.h | 8 ++++---- src/gui/query/copy_load_order_query.h | 6 +++--- src/gui/query/copy_metadata_query.h | 4 +++- src/gui/query/discard_unapplied_changes_query.h | 5 +++-- src/gui/query/editor_closed_query.h | 8 +++++--- src/gui/query/editor_opened_query.h | 4 ++-- src/gui/query/get_conflicting_plugins_query.h | 10 +++++----- src/gui/query/get_game_data_query.h | 4 ++-- src/gui/query/get_game_types_query.h | 4 ++-- src/gui/query/get_init_errors_query.h | 8 ++++---- src/gui/query/get_installed_games_query.h | 4 ++-- src/gui/query/get_languages_query.h | 4 ++-- src/gui/query/get_settings_query.h | 6 +++--- src/gui/query/get_version_query.h | 4 ++-- src/gui/query/metadata_query.h | 2 +- src/gui/query/open_log_location_query.h | 4 ++-- src/gui/query/open_readme_query.h | 4 ++-- src/gui/query/query.h | 16 +++++++++++++++- src/gui/query/redate_plugins_query.h | 4 ++-- src/gui/query/save_filter_state_query.h | 4 ++-- src/gui/query/sort_plugins_query.h | 8 +++++--- src/gui/query/update_masterlist_query.h | 10 ++++------ src/gui/query_handler.cpp | 10 +--------- 30 files changed, 97 insertions(+), 85 deletions(-) diff --git a/src/gui/query/apply_sort_query.h b/src/gui/query/apply_sort_query.h index 4123c57e..e3eafda5 100644 --- a/src/gui/query/apply_sort_query.h +++ b/src/gui/query/apply_sort_query.h @@ -33,16 +33,17 @@ public: ApplySortQuery(LootState& state, const std::vector& plugins) : state_(state), plugins_(plugins) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(trace) << "User has accepted sorted load order, applying it."; state_.decrementUnappliedChangeCounter(); state_.getCurrentGame().SetLoadOrder(plugins_); - callback->Success(""); + + return ""; } private: LootState& state_; - std::vector plugins_; + const std::vector plugins_; }; } diff --git a/src/gui/query/cancel_find_query.h b/src/gui/query/cancel_find_query.h index f2b70450..70fbe1b4 100644 --- a/src/gui/query/cancel_find_query.h +++ b/src/gui/query/cancel_find_query.h @@ -34,13 +34,13 @@ class CancelFindQuery : public Query { public: CancelFindQuery(CefRefPtr browser) : browser_(browser) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { browser_->GetHost()->StopFinding(true); - callback->Success(""); + return ""; } private: - CefRefPtr browser_; + const CefRefPtr browser_; }; } diff --git a/src/gui/query/cancel_sort_query.h b/src/gui/query/cancel_sort_query.h index ac32a4c8..b75dc7c7 100644 --- a/src/gui/query/cancel_sort_query.h +++ b/src/gui/query/cancel_sort_query.h @@ -36,12 +36,11 @@ public: MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { state_.decrementUnappliedChangeCounter(); state_.getCurrentGame().DecrementLoadOrderSortCount(); - YAML::Node node(getGeneralMessages()); - callback->Success(JSON::stringify(node)); + return JSON::stringify(YAML::Node(getGeneralMessages())); } private: diff --git a/src/gui/query/change_game_query.h b/src/gui/query/change_game_query.h index e618fc11..dea40451 100644 --- a/src/gui/query/change_game_query.h +++ b/src/gui/query/change_game_query.h @@ -38,10 +38,10 @@ public: state_(state), gameFolder_(gameFolder) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { state_.changeGame(gameFolder_); - GetGameDataQuery::execute(callback); + return GetGameDataQuery::executeLogic(); } private: diff --git a/src/gui/query/clear_all_metadata_query.h b/src/gui/query/clear_all_metadata_query.h index 231b517d..0eb8ff8e 100644 --- a/src/gui/query/clear_all_metadata_query.h +++ b/src/gui/query/clear_all_metadata_query.h @@ -36,7 +36,7 @@ public: MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), game_(state.getCurrentGame()) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(debug) << "Clearing all user metadata."; // Record which plugins have userlist entries. @@ -47,7 +47,8 @@ public: game_.GetUserlist().Save(game_.UserlistPath()); BOOST_LOG_TRIVIAL(trace) << "Rederiving display metadata for " << userlistPluginNames.size() << " plugins that had user metadata."; - callback->Success(getDerivedMetadataJson(userlistPluginNames)); + + return getDerivedMetadataJson(userlistPluginNames); } private: diff --git a/src/gui/query/clear_plugin_metadata_query.h b/src/gui/query/clear_plugin_metadata_query.h index 423ab51c..3aa16252 100644 --- a/src/gui/query/clear_plugin_metadata_query.h +++ b/src/gui/query/clear_plugin_metadata_query.h @@ -37,7 +37,7 @@ public: game_(state.getCurrentGame()), pluginName_(pluginName) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(debug) << "Clearing user metadata for plugin " << pluginName_; game_.GetUserlist().ErasePlugin(PluginMetadata(pluginName_)); @@ -46,14 +46,14 @@ public: // 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"); + return JSON::stringify(derivedMetadata); + + return "null"; } private: Game& game_; - std::string pluginName_; + const std::string pluginName_; }; } diff --git a/src/gui/query/close_settings_query.h b/src/gui/query/close_settings_query.h index d897a605..f6738d79 100644 --- a/src/gui/query/close_settings_query.h +++ b/src/gui/query/close_settings_query.h @@ -34,11 +34,11 @@ public: CloseSettingsQuery(LootState& state, YAML::Node settings) : GetInstalledGamesQuery(state), state_(state), settings_(settings) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object."; state_.load(settings_); - GetInstalledGamesQuery::execute(callback); + return GetInstalledGamesQuery::executeLogic(); } private: diff --git a/src/gui/query/copy_content_query.h b/src/gui/query/copy_content_query.h index be7f95d6..ef8bb16a 100644 --- a/src/gui/query/copy_content_query.h +++ b/src/gui/query/copy_content_query.h @@ -34,11 +34,11 @@ class CopyContentQuery : public ClipboardQuery { public: CopyContentQuery(const YAML::Node& content) : content_(content) {} - void execute(CefRefPtr callback) { - std::string text = "[spoiler][code]" + getContentAsText() + "[/code][/spoiler]"; + std::string executeLogic() { + const std::string text = "[spoiler][code]" + getContentAsText() + "[/code][/spoiler]"; copyToClipboard(text); - callback->Success(""); + return ""; } private: @@ -53,7 +53,7 @@ private: return text; } - YAML::Node content_; + const YAML::Node content_; }; } diff --git a/src/gui/query/copy_load_order_query.h b/src/gui/query/copy_load_order_query.h index ab266d39..6ea42af8 100644 --- a/src/gui/query/copy_load_order_query.h +++ b/src/gui/query/copy_load_order_query.h @@ -33,7 +33,7 @@ public: CopyLoadOrderQuery(LootState& state, const std::vector& plugins) : state_(state), plugins_(plugins) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { int numberOfIndexDigits = getNumberOfIndexDigits(); size_t activeIndex = 0; @@ -43,7 +43,7 @@ public: } copyToClipboard(stream.str()); - callback->Success(""); + return ""; } private: @@ -67,7 +67,7 @@ private: } LootState& state_; - std::vector plugins_; + const std::vector plugins_; }; } diff --git a/src/gui/query/copy_metadata_query.h b/src/gui/query/copy_metadata_query.h index 2d561034..a8f3c8ee 100644 --- a/src/gui/query/copy_metadata_query.h +++ b/src/gui/query/copy_metadata_query.h @@ -35,7 +35,7 @@ public: CopyMetadataQuery(LootState& state, const std::string& pluginName) : state_(state), pluginName_(pluginName) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(debug) << "Copying metadata for plugin " << pluginName_; // Get metadata from masterlist and userlist. @@ -48,6 +48,8 @@ public: copyToClipboard(text); BOOST_LOG_TRIVIAL(info) << "Exported userlist metadata text for \"" << pluginName_ << "\": " << text; + + return ""; } private: diff --git a/src/gui/query/discard_unapplied_changes_query.h b/src/gui/query/discard_unapplied_changes_query.h index ed9b66b0..94ae7106 100644 --- a/src/gui/query/discard_unapplied_changes_query.h +++ b/src/gui/query/discard_unapplied_changes_query.h @@ -33,10 +33,11 @@ class DiscardUnappliedChangesQuery : public Query { public: DiscardUnappliedChangesQuery(LootState& state) : state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { while (state_.hasUnappliedChanges()) state_.decrementUnappliedChangeCounter(); - callback->Success(""); + + return ""; } private: diff --git a/src/gui/query/editor_closed_query.h b/src/gui/query/editor_closed_query.h index 9ee1bbe8..67610d73 100644 --- a/src/gui/query/editor_closed_query.h +++ b/src/gui/query/editor_closed_query.h @@ -35,9 +35,12 @@ public: MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), state_(state), metadata_(metadata) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { try { - callback->Success(applyUserEdits()); + const std::string json = applyUserEdits(); + state_.decrementUnappliedChangeCounter(); + + return json; } catch (Error&) { throw; } catch (std::exception& e) { @@ -51,7 +54,6 @@ public: throw std::runtime_error(error); } - state_.decrementUnappliedChangeCounter(); } private: diff --git a/src/gui/query/editor_opened_query.h b/src/gui/query/editor_opened_query.h index dd7da973..d2fd4037 100644 --- a/src/gui/query/editor_opened_query.h +++ b/src/gui/query/editor_opened_query.h @@ -33,9 +33,9 @@ class EditorOpenedQuery : public Query { public: EditorOpenedQuery(LootState& state) : state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { state_.incrementUnappliedChangeCounter(); - callback->Success(""); + return ""; } private: diff --git a/src/gui/query/get_conflicting_plugins_query.h b/src/gui/query/get_conflicting_plugins_query.h index 945e62fa..526e1997 100644 --- a/src/gui/query/get_conflicting_plugins_query.h +++ b/src/gui/query/get_conflicting_plugins_query.h @@ -37,7 +37,7 @@ public: game_(state.getCurrentGame()), pluginName_(pluginName) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { 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 @@ -52,9 +52,9 @@ public: } if (node.size() > 0) - callback->Success(JSON::stringify(node)); - else - callback->Success("[]"); + return JSON::stringify(node); + + return "[]"; } private: @@ -76,7 +76,7 @@ private: } Game& game_; - std::string pluginName_; + const std::string pluginName_; }; } diff --git a/src/gui/query/get_game_data_query.h b/src/gui/query/get_game_data_query.h index 528fc014..120ea023 100644 --- a/src/gui/query/get_game_data_query.h +++ b/src/gui/query/get_game_data_query.h @@ -41,7 +41,7 @@ public: state_(state), frame_(frame) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { sendProgressUpdate(frame_, boost::locale::translate("Parsing, merging and evaluating metadata...")); // First clear CRC and condition caches, otherwise they could lead to incorrect evaluations. @@ -64,7 +64,7 @@ public: } catch (...) {} } - callback->Success(generateJsonResponse(installed)); + return generateJsonResponse(installed); } private: diff --git a/src/gui/query/get_game_types_query.h b/src/gui/query/get_game_types_query.h index 37b2642a..1e466047 100644 --- a/src/gui/query/get_game_types_query.h +++ b/src/gui/query/get_game_types_query.h @@ -32,9 +32,9 @@ along with LOOT. If not, see namespace loot { class GetGameTypesQuery : public Query { public: - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported languages."; - callback->Success(getGameTypesAsJson()); + return getGameTypesAsJson(); } private: diff --git a/src/gui/query/get_init_errors_query.h b/src/gui/query/get_init_errors_query.h index e79c20e8..d2d36b75 100644 --- a/src/gui/query/get_init_errors_query.h +++ b/src/gui/query/get_init_errors_query.h @@ -34,12 +34,12 @@ class GetInitErrorsQuery : public Query { public: GetInitErrorsQuery(LootState& state) : state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { YAML::Node node(state_.getInitErrors()); if (node.size() > 0) - callback->Success(JSON::stringify(node)); - else - callback->Success("null"); + return JSON::stringify(node); + + return "null"; } private: diff --git a/src/gui/query/get_installed_games_query.h b/src/gui/query/get_installed_games_query.h index 67085fe1..bb83084d 100644 --- a/src/gui/query/get_installed_games_query.h +++ b/src/gui/query/get_installed_games_query.h @@ -34,9 +34,9 @@ class GetInstalledGamesQuery : public Query { public: GetInstalledGamesQuery(LootState& state) : state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT's detected games."; - callback->Success(getInstalledGamesAsJson()); + return getInstalledGamesAsJson(); } private: diff --git a/src/gui/query/get_languages_query.h b/src/gui/query/get_languages_query.h index 5acb7f65..dc259364 100644 --- a/src/gui/query/get_languages_query.h +++ b/src/gui/query/get_languages_query.h @@ -32,9 +32,9 @@ along with LOOT. If not, see namespace loot { class GetLanguagesQuery : public Query { public: - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT's supported languages."; - callback->Success(getLanguagesAsJson()); + return getLanguagesAsJson(); } private: diff --git a/src/gui/query/get_settings_query.h b/src/gui/query/get_settings_query.h index e5c0e068..0aac9fe7 100644 --- a/src/gui/query/get_settings_query.h +++ b/src/gui/query/get_settings_query.h @@ -34,13 +34,13 @@ class GetSettingsQuery : public Query { public: GetSettingsQuery(LootSettings& settings) : settings_(settings) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT settings."; - callback->Success(JSON::stringify(settings_.toYaml())); + return JSON::stringify(settings_.toYaml()); } private: - LootSettings& settings_; + const LootSettings& settings_; }; } diff --git a/src/gui/query/get_version_query.h b/src/gui/query/get_version_query.h index 592806a3..a477b07c 100644 --- a/src/gui/query/get_version_query.h +++ b/src/gui/query/get_version_query.h @@ -31,9 +31,9 @@ along with LOOT. If not, see namespace loot { class GetVersionQuery : public Query { public: - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT version."; - callback->Success("\"" + LootVersion::string() + "." + LootVersion::revision + "\""); + return "\"" + LootVersion::string() + "." + LootVersion::revision + "\""; } }; } diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h index 1476ee8c..07175510 100644 --- a/src/gui/query/metadata_query.h +++ b/src/gui/query/metadata_query.h @@ -148,7 +148,7 @@ private: } Game& game_; - LanguageCode language_; + const LanguageCode language_; }; } diff --git a/src/gui/query/open_log_location_query.h b/src/gui/query/open_log_location_query.h index bb2d9575..978996ff 100644 --- a/src/gui/query/open_log_location_query.h +++ b/src/gui/query/open_log_location_query.h @@ -32,11 +32,11 @@ along with LOOT. If not, see namespace loot { class OpenLogLocationQuery : public Query { public: - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Opening LOOT local appdata folder."; OpenInDefaultApplication(LootPaths::getLogPath().parent_path()); - callback->Success(""); + return ""; } }; } diff --git a/src/gui/query/open_readme_query.h b/src/gui/query/open_readme_query.h index 2d247afd..542e6cd0 100644 --- a/src/gui/query/open_readme_query.h +++ b/src/gui/query/open_readme_query.h @@ -32,11 +32,11 @@ along with LOOT. If not, see namespace loot { class OpenReadmeQuery : public Query { public: - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Opening LOOT readme."; OpenInDefaultApplication(LootPaths::getReadmePath()); - callback->Success(""); + return ""; } }; } diff --git a/src/gui/query/query.h b/src/gui/query/query.h index a9ebd72f..b10861bd 100644 --- a/src/gui/query/query.h +++ b/src/gui/query/query.h @@ -28,12 +28,26 @@ along with LOOT. If not, see #include #include +#include "loot/error.h" + namespace loot { class Query : public CefBase { public: - virtual void execute(CefRefPtr callback) = 0; + void execute(CefRefPtr callback) { + try { + callback->Success(executeLogic()); + } catch (Error &e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + callback->Failure(e.codeAsUnsignedInt(), e.what()); + } catch (std::exception &e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + callback->Failure(-1, e.what()); + } + } protected: + virtual std::string executeLogic() = 0; + 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); diff --git a/src/gui/query/redate_plugins_query.h b/src/gui/query/redate_plugins_query.h index 9c11bbd0..5afe886f 100644 --- a/src/gui/query/redate_plugins_query.h +++ b/src/gui/query/redate_plugins_query.h @@ -33,9 +33,9 @@ class RedatePluginsQuery : public Query { public: RedatePluginsQuery(LootState& state) : state_(state) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { state_.getCurrentGame().RedatePlugins(); - callback->Success(""); + return ""; } private: diff --git a/src/gui/query/save_filter_state_query.h b/src/gui/query/save_filter_state_query.h index 84737e15..fb2f8b20 100644 --- a/src/gui/query/save_filter_state_query.h +++ b/src/gui/query/save_filter_state_query.h @@ -38,10 +38,10 @@ public: bool enabled) : state_(state), filterId_(filterId), enabled_(enabled) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(trace) << "Saving filter states."; state_.storeFilterState(filterId_, enabled_); - callback->Success(""); + return ""; } private: diff --git a/src/gui/query/sort_plugins_query.h b/src/gui/query/sort_plugins_query.h index 8ecdb5d0..d3c17f56 100644 --- a/src/gui/query/sort_plugins_query.h +++ b/src/gui/query/sort_plugins_query.h @@ -39,7 +39,7 @@ public: state_(state), frame_(frame) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(info) << "Beginning sorting operation."; // Always reload all the plugins. @@ -52,11 +52,13 @@ public: if ((state_.getCurrentGame().Type() == GameType::tes5 || state_.getCurrentGame().Type() == GameType::fo4)) applyUnchangedLoadOrder(plugins); - callback->Success(generateJsonResponse(plugins)); + std::string json = generateJsonResponse(plugins); // plugins will be empty if there was a sorting error. if (!plugins.empty()) state_.incrementUnappliedChangeCounter(); + + return json; } private: @@ -78,7 +80,7 @@ private: } void applyUnchangedLoadOrder(const std::vector& plugins) { - if (!equal(begin(plugins), end(plugins), begin(state_.getCurrentGame().GetLoadOrder()))) + if (plugins.empty() || !equal(begin(plugins), end(plugins), begin(state_.getCurrentGame().GetLoadOrder()))) return; // Load order has not been changed, set it without asking for user input diff --git a/src/gui/query/update_masterlist_query.h b/src/gui/query/update_masterlist_query.h index 19bfbf28..6d786a27 100644 --- a/src/gui/query/update_masterlist_query.h +++ b/src/gui/query/update_masterlist_query.h @@ -36,16 +36,14 @@ public: MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), game_(state.getCurrentGame()) {} - void execute(CefRefPtr callback) { + std::string executeLogic() { BOOST_LOG_TRIVIAL(debug) << "Updating and parsing masterlist."; - if (!updateMasterlist()) { - callback->Success("null"); - return; - } + if (!updateMasterlist()) + return "null"; // Now regenerate the JS-side masterlist data if the masterlist was changed. - callback->Success(generateJsonResponse()); + return generateJsonResponse(); } private: diff --git a/src/gui/query_handler.cpp b/src/gui/query_handler.cpp index 94a5ec3d..bee78c5c 100644 --- a/src/gui/query_handler.cpp +++ b/src/gui/query_handler.cpp @@ -111,15 +111,7 @@ bool QueryHandler::OnQuery(CefRefPtr browser, if (!query) return false; - try { - CefPostTask(TID_FILE, base::Bind(&Query::execute, query, callback)); - } 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()); - } + CefPostTask(TID_FILE, base::Bind(&Query::execute, query, callback)); return true; } From f977592c660b06ad8245beed00070a77990f6076 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sat, 10 Sep 2016 16:39:28 +0100 Subject: [PATCH 7/8] Fix changing game deriving metadata incorrectly Metadata was derived against the old game, as it was stored as a reference in MetadataQuery. --- src/gui/query/cancel_sort_query.h | 2 +- src/gui/query/clear_all_metadata_query.h | 2 +- src/gui/query/clear_plugin_metadata_query.h | 2 +- src/gui/query/editor_closed_query.h | 2 +- src/gui/query/get_conflicting_plugins_query.h | 2 +- src/gui/query/get_game_data_query.h | 8 +++-- src/gui/query/metadata_query.h | 32 +++++++++---------- src/gui/query/sort_plugins_query.h | 2 +- src/gui/query/update_masterlist_query.h | 2 +- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/gui/query/cancel_sort_query.h b/src/gui/query/cancel_sort_query.h index b75dc7c7..307861f4 100644 --- a/src/gui/query/cancel_sort_query.h +++ b/src/gui/query/cancel_sort_query.h @@ -33,7 +33,7 @@ namespace loot { class CancelSortQuery : public MetadataQuery { public: CancelSortQuery(LootState& state) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), state_(state) {} std::string executeLogic() { diff --git a/src/gui/query/clear_all_metadata_query.h b/src/gui/query/clear_all_metadata_query.h index 0eb8ff8e..cd056128 100644 --- a/src/gui/query/clear_all_metadata_query.h +++ b/src/gui/query/clear_all_metadata_query.h @@ -33,7 +33,7 @@ namespace loot { class ClearAllMetadataQuery : public MetadataQuery { public: ClearAllMetadataQuery(LootState& state) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), game_(state.getCurrentGame()) {} std::string executeLogic() { diff --git a/src/gui/query/clear_plugin_metadata_query.h b/src/gui/query/clear_plugin_metadata_query.h index 3aa16252..c8d03a44 100644 --- a/src/gui/query/clear_plugin_metadata_query.h +++ b/src/gui/query/clear_plugin_metadata_query.h @@ -33,7 +33,7 @@ namespace loot { class ClearPluginMetadataQuery : public MetadataQuery { public: ClearPluginMetadataQuery(LootState& state, const std::string& pluginName) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), game_(state.getCurrentGame()), pluginName_(pluginName) {} diff --git a/src/gui/query/editor_closed_query.h b/src/gui/query/editor_closed_query.h index 67610d73..5e1a3db9 100644 --- a/src/gui/query/editor_closed_query.h +++ b/src/gui/query/editor_closed_query.h @@ -32,7 +32,7 @@ namespace loot { class EditorClosedQuery : public MetadataQuery { public: EditorClosedQuery(LootState& state, YAML::Node metadata) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), state_(state), metadata_(metadata) {} std::string executeLogic() { diff --git a/src/gui/query/get_conflicting_plugins_query.h b/src/gui/query/get_conflicting_plugins_query.h index 526e1997..8a67a021 100644 --- a/src/gui/query/get_conflicting_plugins_query.h +++ b/src/gui/query/get_conflicting_plugins_query.h @@ -33,7 +33,7 @@ namespace loot { class GetConflictingPluginsQuery : public MetadataQuery { public: GetConflictingPluginsQuery(LootState& state, const std::string& pluginName) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), game_(state.getCurrentGame()), pluginName_(pluginName) {} diff --git a/src/gui/query/get_game_data_query.h b/src/gui/query/get_game_data_query.h index 120ea023..512a2638 100644 --- a/src/gui/query/get_game_data_query.h +++ b/src/gui/query/get_game_data_query.h @@ -37,7 +37,7 @@ namespace loot { class GetGameDataQuery : public MetadataQuery { public: GetGameDataQuery(LootState& state, CefRefPtr frame) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), state_(state), frame_(frame) {} @@ -49,11 +49,13 @@ public: /* 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(); + bool isFirstLoad = state_.getCurrentGame().GetPlugins().empty(); state_.getCurrentGame().LoadAllInstalledPlugins(true); + if (isFirstLoad) + loadMetadataLists(); + //Sort plugins into their load order. std::vector installed; std::vector loadOrder = state_.getCurrentGame().GetLoadOrder(); diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h index 07175510..26a80291 100644 --- a/src/gui/query/metadata_query.h +++ b/src/gui/query/metadata_query.h @@ -35,18 +35,17 @@ along with LOOT. If not, see namespace loot { class MetadataQuery : public Query { protected: - MetadataQuery(Game& game, const LanguageCode language) : - game_(game), language_(language) {} + MetadataQuery(LootState& state) : state_(state) {} std::vector getGeneralMessages() { std::vector messages; - appendMessages(messages, game_.GetMasterlist().Messages()); - appendMessages(messages, game_.GetUserlist().Messages()); - appendMessages(messages, game_.GetMessages()); + appendMessages(messages, state_.getCurrentGame().GetMasterlist().Messages()); + appendMessages(messages, state_.getCurrentGame().GetUserlist().Messages()); + appendMessages(messages, state_.getCurrentGame().GetMessages()); evaluateMessageConditions(messages); - return toSimpleMessages(messages, language_); + return toSimpleMessages(messages, state_.getLanguage().GetCode()); } YAML::Node generateDerivedMetadata(const Plugin& file, @@ -65,9 +64,9 @@ protected: 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)); + auto plugin = state_.getCurrentGame().GetPlugin(pluginName); + PluginMetadata master(state_.getCurrentGame().GetMasterlist().FindPlugin(plugin)); + PluginMetadata user(state_.getCurrentGame().GetUserlist().FindPlugin(plugin)); return generateDerivedMetadata(plugin, master, user); } catch (...) { @@ -85,7 +84,7 @@ private: try { auto it = begin(messages); while (it != end(messages)) { - if (!it->EvalCondition(game_)) + if (!it->EvalCondition(state_.getCurrentGame())) it = messages.erase(it); else ++it; @@ -114,7 +113,7 @@ private: //Evaluate any conditions BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; try { - plugin.EvalAllConditions(game_); + plugin.EvalAllConditions(state_.getCurrentGame()); } 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()); @@ -123,20 +122,20 @@ private: } //Also check install validity. - plugin.CheckInstallValidity(game_); + plugin.CheckInstallValidity(state_.getCurrentGame()); } YAML::Node toYaml(const Plugin& plugin) { - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language_).GetName(); + BOOST_LOG_TRIVIAL(info) << "Using message language: " << state_.getLanguage().GetName(); YAML::Node pluginNode; pluginNode["name"] = plugin.Name(); pluginNode["priority"] = plugin.LocalPriority().getValue(); pluginNode["globalPriority"] = plugin.GlobalPriority().getValue(); - pluginNode["messages"] = plugin.SimpleMessages(language_); + pluginNode["messages"] = plugin.SimpleMessages(state_.getLanguage().GetCode()); pluginNode["tags"] = plugin.Tags(); pluginNode["isDirty"] = !plugin.DirtyInfo().empty(); - pluginNode["loadOrderIndex"] = game_.GetActiveLoadOrderIndex(plugin.Name()); + pluginNode["loadOrderIndex"] = state_.getCurrentGame().GetActiveLoadOrderIndex(plugin.Name()); if (!plugin.CleanInfo().empty()) { pluginNode["cleanedWith"] = plugin.CleanInfo().begin()->CleaningUtility(); @@ -147,8 +146,7 @@ private: return pluginNode; } - Game& game_; - const LanguageCode language_; + LootState& state_; }; } diff --git a/src/gui/query/sort_plugins_query.h b/src/gui/query/sort_plugins_query.h index d3c17f56..0bc13e88 100644 --- a/src/gui/query/sort_plugins_query.h +++ b/src/gui/query/sort_plugins_query.h @@ -35,7 +35,7 @@ namespace loot { class SortPluginsQuery : public MetadataQuery { public: SortPluginsQuery(LootState& state, CefRefPtr frame) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), state_(state), frame_(frame) {} diff --git a/src/gui/query/update_masterlist_query.h b/src/gui/query/update_masterlist_query.h index 6d786a27..e3d19517 100644 --- a/src/gui/query/update_masterlist_query.h +++ b/src/gui/query/update_masterlist_query.h @@ -33,7 +33,7 @@ namespace loot { class UpdateMasterlistQuery : public MetadataQuery { public: UpdateMasterlistQuery(LootState& state) : - MetadataQuery(state.getCurrentGame(), state.getLanguage().GetCode()), + MetadataQuery(state), game_(state.getCurrentGame()) {} std::string executeLogic() { From 624a7585b98fdcba14e97379b29463ebfc048692 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sat, 10 Sep 2016 17:00:15 +0100 Subject: [PATCH 8/8] Don't act on the changeGame event during init The event is fired when setting the initial game menu selection, before the game data is loaded, so there's no point processing the event. --- src/gui/html/js/events.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/html/js/events.js b/src/gui/html/js/events.js index 8a1f08a2..47f6a92f 100644 --- a/src/gui/html/js/events.js +++ b/src/gui/html/js/events.js @@ -37,7 +37,9 @@ function onConflictsFilter(evt) { } function onChangeGame(evt) { - if (evt.detail.item.getAttribute('value') === loot.game.folder) { + if (evt.detail.item.getAttribute('value') === loot.game.folder + || loot.game.folder.length === 0) { + // Game folder length is zero if LOOT is being initalised. return; } /* Send off a CEF query with the folder name of the new game. */