From 73f3f42cf85d17e53f09e3daeced9d7c1b06b9e8 Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Fri, 15 Aug 2014 18:06:03 +0100 Subject: [PATCH] Started to implement plugin sorting. The C++ side sends a list of plugin names in sorted order back, and the JS reorders the cards and nav list items to match. Old and new load orders are recorded, and if sorting is cancelled the old order is used to reorder the UI again, otherwise if it is accepted the JS sends the plugin list back to C++, which applies the load order. The actual sorting doesn't yet happen, the C++ just sends back the current load order right now. If `loot.neverTellMeTheOdds` is true, then the received load order is randomly shuffled. Also fixed initialisation of the masterlist update checkbox state, and the type thrown by the filter application function. --- resources/report/js/script.js | 89 +++++++++++++++++++++++++++++++++-- src/backend/game.cpp | 6 +-- src/backend/game.h | 2 +- src/gui/handler.cpp | 21 +++++++++ 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/resources/report/js/script.js b/resources/report/js/script.js index 8286a633..f060df54 100644 --- a/resources/report/js/script.js +++ b/resources/report/js/script.js @@ -126,7 +126,7 @@ function togglePlugins(evt) { var hiddenPluginNo = 0; var hiddenMessageNo = 0; if (sections.length - 2 != entries.length) { - throw "Error: Number of plugins in sidebar doesn't match number of plugins in main area!"; + throw Error("Error: Number of plugins in sidebar doesn't match number of plugins in main area!"); } /* The conflict filter, if enabled, executes C++ code, so needs to be handled using a promise, so the rest of the function should wait until @@ -372,14 +372,93 @@ function updateMasterlist(evt) { loot.game.globalMessages = result.globalMessages; }).catch(processCefError); } +function sortUIElements(pluginNames) { + /* pluginNames is an array of plugin names in their sorted order. Rearrange + the plugin cards and nav entries to match it. */ + var main = document.getElementById('main'); + var pluginsNav = document.getElementById('pluginsNav'); + var entries = pluginsNav.children; + if (main.children.length - 2 != entries.length) { + throw Error("Error: Number of plugins in sidebar doesn't match number of plugins in main area!"); + } + pluginNames.forEach(function(name){ + var card = document.getElementById(name.replace(/\s+/g, '')); + var li; + for (var i = 0; i < entries.length; ++i) { + if (entries[i].getElementsByClassName('name')[0].textContent == name) { + li = entries[i]; + } + } + + /* Easiest just to remove them and add them on at the end. */ + main.removeChild(card); + main.appendChild(card); + pluginsNav.removeChild(li); + pluginsNav.appendChild(li); + }); +} function sortPlugins(evt) { - loot.query('sortPlugins').catch(processCefError); + if (loot.settings.updateMasterlist) { + updateMasterlist(evt); + } + loot.query('sortPlugins').then(JSON.parse).then(function(result){ + + if (loot.neverTellMeTheOdds) { + /* Array shuffler from */ + for(var j, x, i = result.length; i; j = Math.floor(Math.random() * i), x = result[--i], result[i] = result[j], result[j] = x); + } + + /* Record the previous order in case the user cancels sorting. */ + /* Start at 2 to skip summary and general messages. */ + var cards = document.getElementById('main').children; + loot.newLoadOrder = result; + loot.lastLoadOrder = []; + for (var i = 2; i < cards.length; ++i) { + loot.lastLoadOrder.push(cards[i].getElementsByTagName('h1')[0].textContent); + } + /* Now update the UI for the new order. */ + sortUIElements(result); + + /* Now hide the masterlist update buttons, and display the accept and + cancel sort buttons. */ + hideElement(document.getElementById('updateMasterlistButton')); + hideElement(document.getElementById('sortButton')); + showElement(document.getElementById('applySortButton')); + showElement(document.getElementById('cancelSortButton')); + }).catch(processCefError); } function applySort(evt) { - loot.query('applySort').catch(processCefError); + var request = JSON.stringify({ + name: 'applySort', + args: [ + loot.newLoadOrder + ] + }); + loot.query(request).then(function(result){ + /* Remove old load order storage. */ + delete loot.lastLoadOrder; + delete loot.newLoadOrder; + + /* Now show the masterlist update buttons, and hide the accept and + cancel sort buttons. */ + showElement(document.getElementById('updateMasterlistButton')); + showElement(document.getElementById('sortButton')); + hideElement(document.getElementById('applySortButton')); + hideElement(document.getElementById('cancelSortButton')); + }).catch(processCefError); } function cancelSort(evt) { - loot.query('cancelSort').catch(processCefError); + /* Sort UI elements again according to stored old load order. */ + sortUIElements(loot.lastLoadOrder); + delete loot.lastLoadOrder; + delete loot.newLoadOrder; + + /* Now show the masterlist update buttons, and hide the accept and + cancel sort buttons. */ + showElement(document.getElementById('updateMasterlistButton')); + showElement(document.getElementById('sortButton')); + hideElement(document.getElementById('applySortButton')); + hideElement(document.getElementById('cancelSortButton')); } function redatePlugins(evt) { if (evt.target.classList.contains('disabled')) { @@ -578,7 +657,7 @@ function closeSettingsDialog(evt) { games: document.getElementById('gameTable').getRowsData(false), language: document.getElementById('languageSelect').value, lastGame: loot.game.folder, - updateMasterlist: document.getElementById('updateMasterlist').value, + updateMasterlist: document.getElementById('updateMasterlist').checked, }; /* Send the settings back to the C++ side. */ diff --git a/src/backend/game.cpp b/src/backend/game.cpp index 0c5dad26..4e0fad1b 100644 --- a/src/backend/game.cpp +++ b/src/backend/game.cpp @@ -533,7 +533,7 @@ namespace loot { lo_destroy_handle(gh); } - void Game::SetLoadOrder(const std::list& loadOrder) const { + void Game::SetLoadOrder(const std::list& loadOrder) const { BOOST_LOG_TRIVIAL(debug) << "Setting load order for game: " << _name; lo_game_handle gh = nullptr; @@ -587,8 +587,8 @@ namespace loot { pluginArr = new char*[pluginArrSize]; int i = 0; for (const auto &plugin: loadOrder) { - pluginArr[i] = new char[plugin.Name().length() + 1]; - strcpy(pluginArr[i], plugin.Name().c_str()); + pluginArr[i] = new char[plugin.length() + 1]; + strcpy(pluginArr[i], plugin.c_str()); ++i; } diff --git a/src/backend/game.h b/src/backend/game.h index 5a903445..8ea1bc37 100644 --- a/src/backend/game.h +++ b/src/backend/game.h @@ -118,7 +118,7 @@ namespace loot { bool IsActive(const std::string& plugin) const; void GetLoadOrder(std::list& loadOrder) const; - void SetLoadOrder(const std::list& loadOrder) const; //Modifies game load order, even though const. + void SetLoadOrder(const std::list& loadOrder) const; //Modifies game load order, even though const. void RefreshActivePluginsList(); void RedatePlugins(); //Change timestamps to match load order (Skyrim only). diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index 0693be88..360d9894 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -171,6 +171,13 @@ namespace loot { callback->Success(UpdateMasterlist()); return true; } + else if (request == "sortPlugins") { + //Sort plugins into their load order. + list loadOrder; + g_app_state.CurrentGame().GetLoadOrder(loadOrder); + callback->Success(JSON::stringify(YAML::Node(loadOrder))); + return true; + } else { // May be a request with arguments. YAML::Node req; @@ -394,6 +401,20 @@ namespace loot { callback->Success(""); return true; } + else if (requestName == "applySort") { + BOOST_LOG_TRIVIAL(trace) << "User has accepted sorted load order, applying it."; + list loadOrder = req["args"][0].as>(); + try { + g_app_state.CurrentGame().SetLoadOrder(loadOrder); + } + catch (exception &e) { + callback->Failure(-1, e.what()); + return true; + } + + callback->Success(""); + return true; + } } return false;