Fixed startup initialisation.

There are a few rough edges still, eg. CEF's debug logging is always
enabled, and I'm currently using a global object for the LOOT-specific
data, but these can be changed.
This commit is contained in:
WrinklyNinja
2014-07-15 20:08:47 +01:00
parent d6e1ecb393
commit 8482a8625f
6 changed files with 218 additions and 210 deletions
+30 -5
View File
@@ -277,7 +277,7 @@ function addNewTableRow(evt) {
/* Add deletion listener. */
row.getElementsByClassName('fa-trash-o')[0].addEventListener('click', removeTableRow, false);
}
function addTableRow(tableBody, data) {
function addTableRow(tableBody, tableData) {
var rowTemplateId = tableBody.getAttribute('data-template');
var content = document.getElementById(rowTemplateId).content;
var row = document.importNode(content, true);
@@ -285,8 +285,8 @@ function addTableRow(tableBody, data) {
row = tableBody.lastElementChild.previousElementSibling;
/* Data is an object with keys that match element class names. */
for (var key in data) {
row.getElementsByClassName(key)[0].value = data[key];
for (var key in tableData) {
row.getElementsByClassName(key)[0].value = tableData[key];
}
/* Enable row editing. */
@@ -624,6 +624,12 @@ function setupEventHandlers() {
document.body.addEventListener('mouseover', toggleHoverText, false);
}
function initUI() {
if (typeof loot == 'undefined') {
console.log('loot is not defined.');
return;
}
document.getElementById('LOOTVersion').textContent = loot.version;
/* Fill in languages, games, settings values. */
/* Fill in game row template's game type options. */
@@ -677,6 +683,26 @@ function initUI() {
settingsLangSelect.value = loot.settings.language;
debugVerbositySelect.value = loot.settings.debugVerbosity;
}
var loot;
function initJavascriptVars() {
// Create and send a new query.
var request_id = window.cefQuery({
request: 'initJavascriptVars',
persistent: false,
onSuccess: function(response) {
try {
loot = JSON.parse(response);
} catch (e) {
console.log(e);
console.log('Response: ' + response);
}
initUI();
},
onFailure: function(error_code, error_message) {
showMessageBox('error', "Error", "Error code: " + error_code + "; " + error_message);
}
});
}
function processURLParams() {
/* Get the data path from the URL and load it. */
/*var pos = document.URL.indexOf("?data=");*/
@@ -691,7 +717,6 @@ function processURLParams() {
var activePluginNo = 0;
var dirtyPluginNo = 0;
/* Fill report with data. */
document.getElementById('LOOTVersion').textContent = loot.version;
document.getElementById('masterlistRevision').textContent = data.masterlist.revision.substr(0, 9);
document.getElementById('masterlistDate').textContent = data.masterlist.date;
var generalMessagesList = document.getElementById('generalMessages').getElementsByTagName('ul')[0];
@@ -829,7 +854,7 @@ function processURLParams() {
}
/* Now initialise the rest of the report. */
initUI();
initJavascriptVars();
setupEventHandlers();
if (isStorageSupported()) {
loadSettings();
+3 -3
View File
@@ -55,11 +55,11 @@
<!-- Game <option> elements go here. -->
</select>
<td><input class="folder" type="text" readonly required>
<td><input class="masterFile" type="text" readonly>
<td><input class="url" type="text" readonly>
<td><input class="master" type="text" readonly>
<td><input class="repo" type="text" readonly>
<td><input class="branch" type="text" readonly>
<td><input class="path" type="text" readonly>
<td><input class="registryKey" type="text" readonly>
<td><input class="registry" type="text" readonly>
<td class="fa fa-trash-o fa-fw">
</template>
<template id="pluginNav">
+90 -190
View File
@@ -51,11 +51,90 @@ namespace fs = boost::filesystem;
namespace loot {
LootState g_app_state = LootState();
LootApp::LootApp() {}
void LootApp::Init(std::string& cmdLineGame) {
string initError;
CefRefPtr<CefBrowserProcessHandler> LootApp::GetBrowserProcessHandler() {
return this;
}
CefRefPtr<CefRenderProcessHandler> LootApp::GetRenderProcessHandler() {
return this;
}
void LootApp::OnContextInitialized() {
//Make sure this is running in the UI thread.
assert(CefCurrentlyOn(TID_UI));
// Information used when creating the native window.
CefWindowInfo window_info;
#if _WIN32 || _WIN64
// On Windows we need to specify certain flags that will be passed to CreateWindowEx().
window_info.SetAsPopup(NULL, "LOOT");
#endif
// Set the handler for browser-level callbacks.
CefRefPtr<LootHandler> handler(new LootHandler());
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
// Set URL to load. Ignore any command line values.
std::string url = ToFileURL(g_path_report);
// Create the first browser window.
CefBrowserHost::CreateBrowser(window_info, handler.get(), url, browser_settings, NULL);
}
void LootApp::OnWebKitInitialized() {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
message_router_ = CefMessageRouterRendererSide::Create(config);
}
bool LootApp::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
// Handle IPC messages from the browser process...
return message_router_->OnProcessMessageReceived(browser, source_process, message);
}
void LootApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
_context = context;
// Register javascript functions.
message_router_->OnContextCreated(browser, frame, context);
}
void LootApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
_context = NULL;
}
void LootApp::InitJSVars() {
BOOST_LOG_TRIVIAL(debug) << "Populating JS object variable initial values.";
if (_context == NULL)
return;
}
// LootState member functions
//---------------------------
void LootState::Init(const std::string& cmdLineGame) {
string initError;
//Load settings.
if (!fs::exists(g_path_settings)) {
try {
@@ -126,10 +205,8 @@ namespace loot {
cout.imbue(locale());
boost::filesystem::path::imbue(locale());
// Detect Games
//-------------
int gameIndex = -1;
// Detect games & select startup game
//-----------------------------------
//Detect installed games.
BOOST_LOG_TRIVIAL(debug) << "Detecting installed games.";
@@ -149,200 +226,23 @@ namespace loot {
BOOST_LOG_TRIVIAL(debug) << "Selecting game.";
if (cmdLineGame.empty()) {
if (_settings["Game"] && _settings["Game"].as<string>() != "auto")
cmdLineGame = _settings["Game"].as<string>();
else if (_settings["Last Game"] && _settings["Last Game"].as<string>() != "auto")
cmdLineGame = _settings["Last Game"].as<string>();
size_t gameIndex(0);
try {
gameIndex = SelectGame(_settings, _games, cmdLineGame);
}
if (!cmdLineGame.empty()) {
for (size_t i = 0, max = _games.size(); i < max; ++i) {
if (cmdLineGame == _games[i].FolderName() && _games[i].IsInstalled())
gameIndex = i;
}
catch (exception &) {
BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected.";
}
if (gameIndex < 0) {
//Set gameIndex to the first installed game.
for (size_t i = 0, max = _games.size(); i < max; ++i) {
if (_games[i].IsInstalled()) {
gameIndex = i;
break;
}
}
if (gameIndex < 0) {
BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected.";
initError = translate("Error: None of the supported games were detected.").str();
return;
}
}
loot::Game& game(_games[gameIndex]);
BOOST_LOG_TRIVIAL(debug) << "Game selected is " << game.Name();
BOOST_LOG_TRIVIAL(debug) << "Game selected is " << _games[gameIndex].Name();
//Now that game is selected, initialise it.
BOOST_LOG_TRIVIAL(debug) << "Initialising game-specific settings.";
try {
game.Init();
*find(_games.begin(), _games.end(), game) = game; //Sync changes.
_games[gameIndex].Init();
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Game-specific settings could not be initialised. " << e.what();
initError = (format(translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()).str();
return;
}
}
CefRefPtr<CefBrowserProcessHandler> LootApp::GetBrowserProcessHandler() {
return this;
}
CefRefPtr<CefRenderProcessHandler> LootApp::GetRenderProcessHandler() {
return this;
}
void LootApp::OnContextInitialized() {
//Make sure this is running in the UI thread.
assert(CefCurrentlyOn(TID_UI));
// Information used when creating the native window.
CefWindowInfo window_info;
#if _WIN32 || _WIN64
// On Windows we need to specify certain flags that will be passed to CreateWindowEx().
window_info.SetAsPopup(NULL, "LOOT");
#endif
// Set the handler for browser-level callbacks.
CefRefPtr<LootHandler> handler(new LootHandler());
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
// Set URL to load. Ignore any command line values.
std::string url = ToFileURL(g_path_report);
// Create the first browser window.
CefBrowserHost::CreateBrowser(window_info, handler.get(), url, browser_settings, NULL);
}
void LootApp::OnWebKitInitialized() {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
message_router_ = CefMessageRouterRendererSide::Create(config);
}
bool LootApp::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
// Handle IPC messages from the browser process...
return message_router_->OnProcessMessageReceived(browser, source_process, message);
}
void LootApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Register javascript functions.
message_router_->OnContextCreated(browser, frame, context);
_context = context;
}
void LootApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
_context = NULL;
}
void LootApp::InitJSVars() {
BOOST_LOG_TRIVIAL(debug) << "Populating JS object variable initial values.";
if (_context == NULL)
return;
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = _context->GetGlobal();
// Here the initialisation values should be set.
CefRefPtr<CefV8Value> lootObj = CefV8Value::CreateObject(NULL);
// LOOT Version
//-------------
lootObj->SetValue("version", CefV8Value::CreateString(to_string(g_version_major) + "." + to_string(g_version_minor) + "." + to_string(g_version_patch)), V8_PROPERTY_ATTRIBUTE_NONE);
// LOOT Settings
//--------------
BOOST_LOG_TRIVIAL(debug) << "Setting GUI values for LOOT's settings.";
CefRefPtr<CefV8Value> settingsObj = CefV8Value::CreateObject(NULL);
if (_settings["Language"])
settingsObj->SetValue("language", CefV8Value::CreateString(Language(_settings["Language"].as<string>()).Name()), V8_PROPERTY_ATTRIBUTE_NONE);
if (_settings["Update Masterlist"])
settingsObj->SetValue("updateMasterlist", CefV8Value::CreateBool(_settings["Update Masterlist"].as<bool>()), V8_PROPERTY_ATTRIBUTE_NONE);
if (_settings["Debug Verbosity"])
settingsObj->SetValue("debugVerbosity", CefV8Value::CreateInt(_settings["Debug Verbosity"].as<int>()), V8_PROPERTY_ATTRIBUTE_NONE);
if (_settings["Game"])
settingsObj->SetValue("game", CefV8Value::CreateString(_settings["Game"].as<string>()), V8_PROPERTY_ATTRIBUTE_NONE);
vector<Game> games = GetGames(_settings);
CefRefPtr<CefV8Value> gamesArr = CefV8Value::CreateArray(games.size());
for (int i = 0; i < games.size(); ++i) {
CefRefPtr<CefV8Value> gameObj = CefV8Value::CreateObject(NULL);
gameObj->SetValue("type", CefV8Value::CreateString(loot::Game(games[i].Id()).FolderName()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("name", CefV8Value::CreateString(games[i].Name()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("folder", CefV8Value::CreateString(games[i].FolderName()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("masterFile", CefV8Value::CreateString(games[i].Master()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("url", CefV8Value::CreateString(games[i].RepoURL()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("branch", CefV8Value::CreateString(games[i].RepoBranch()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("path", CefV8Value::CreateString(games[i].GamePath().string()), V8_PROPERTY_ATTRIBUTE_NONE);
gameObj->SetValue("registryKey", CefV8Value::CreateString(games[i].RegistryKey()), V8_PROPERTY_ATTRIBUTE_NONE);
gamesArr->SetValue(i, gameObj);
}
settingsObj->SetValue("games", gamesArr, V8_PROPERTY_ATTRIBUTE_NONE);
lootObj->SetValue("settings", settingsObj, V8_PROPERTY_ATTRIBUTE_NONE);
// LOOT Game Types
//----------------
BOOST_LOG_TRIVIAL(debug) << "Setting GUI values for LOOT's game types.";
CefRefPtr<CefV8Value> gameTypesArr = CefV8Value::CreateArray(4);
gameTypesArr->SetValue(0, CefV8Value::CreateString(Game(Game::tes4).FolderName()));
gameTypesArr->SetValue(1, CefV8Value::CreateString(Game(Game::tes5).FolderName()));
gameTypesArr->SetValue(2, CefV8Value::CreateString(Game(Game::fo3).FolderName()));
gameTypesArr->SetValue(3, CefV8Value::CreateString(Game(Game::fonv).FolderName()));
lootObj->SetValue("gameTypes", gameTypesArr, V8_PROPERTY_ATTRIBUTE_NONE);
// LOOT Languages
//---------------
BOOST_LOG_TRIVIAL(debug) << "Setting GUI values for LOOT's languages.";
vector<string> langs = Language::Names();
CefRefPtr<CefV8Value> langsArr = CefV8Value::CreateArray(langs.size());
for (int i = 0; i < langs.size(); ++i) {
langsArr->SetValue(i, CefV8Value::CreateString(langs[i]));
}
lootObj->SetValue("languages", langsArr, V8_PROPERTY_ATTRIBUTE_NONE);
// Load Order
//-----------
object->SetValue("loot", lootObj, V8_PROPERTY_ATTRIBUTE_NONE);
}
}
+10 -5
View File
@@ -40,8 +40,6 @@ namespace loot {
public:
LootApp();
void Init(std::string& cmdLineGame);
// Override CefApp methods.
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE;
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE;
@@ -69,12 +67,19 @@ namespace loot {
CefRefPtr<CefV8Context> _context;
YAML::Node _settings;
std::vector<loot::Game> _games;
IMPLEMENT_REFCOUNTING(LootApp);
};
class LootState {
public:
void Init(const std::string& cmdLineGame);
YAML::Node _settings;
std::vector<loot::Game> _games;
};
extern LootState g_app_state;
}
#endif
+83 -5
View File
@@ -24,23 +24,28 @@
#include "handler.h"
#include "resource.h"
#include "app.h"
#include <include/cef_app.h>
#include <include/cef_runnable.h>
#include <include/cef_task.h>
#include <boost/log/trivial.hpp>
#include <sstream>
#include <string>
#include <cassert>
using namespace std;
namespace loot {
namespace {
LootHandler * g_instance = NULL;
}
// Handler methods
//----------------
// WinHandler methods
//-------------------
Handler::Handler() {}
@@ -52,8 +57,7 @@ namespace loot {
bool persistent,
CefRefPtr<Callback> callback) {
const std::string& message_name = request;
if (message_name == "openReadme") {
if (request == "openReadme") {
// Open readme in default application.
HINSTANCE ret = ShellExecute(0, NULL, ToWinWide(ToFileURL(g_path_readme)).c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret > 32)
@@ -62,7 +66,7 @@ namespace loot {
callback->Failure((int)ret, "Shell execute failed.");
return true;
}
else if (message_name == "openLogLocation") {
else if (request == "openLogLocation") {
//Open debug log folder.
HINSTANCE ret = ShellExecute(NULL, L"open", ToWinWide(g_path_log.parent_path().string()).c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret > 32)
@@ -71,10 +75,84 @@ namespace loot {
callback->Failure((int)ret, "Shell execute failed.");
return true;
}
else if (request == "initJavascriptVars") {
// Convert the _settings YAML object to a JSON string, and also add members for the LOOT version, game types and languages.
if (g_app_state._games.empty()) {
BOOST_LOG_TRIVIAL(warning) << "Application state not yet initialised, initialising now...";
g_app_state.Init("");
BOOST_LOG_TRIVIAL(info) << "Application state initialised.";
}
YAML::Node temp;
// LOOT Version
//-------------
temp["version"] = to_string(g_version_major) + "." + to_string(g_version_minor) + "." + to_string(g_version_patch);
// LOOT Settings
//--------------
//temp["settings"] = g_app_state._settings;
// Do a bit of translation of key names into more javascript-friendly names.
// Consider instead making the settings file use the more friendly names itself.
temp["settings"]["debugVerbosity"] = g_app_state._settings["Debug Verbosity"];
temp["settings"]["game"] = g_app_state._settings["Game"];
temp["settings"]["games"] = g_app_state._settings["Games"];
temp["settings"]["language"] = Language(g_app_state._settings["Language"].as<string>()).Name();
temp["settings"]["lastGame"] = g_app_state._settings["Last Game"];
// LOOT Game Types
//-----------------
vector<string> gameTypes;
gameTypes.push_back(Game(Game::tes4).FolderName());
gameTypes.push_back(Game(Game::tes5).FolderName());
gameTypes.push_back(Game(Game::fo3).FolderName());
gameTypes.push_back(Game(Game::fonv).FolderName());
temp["gameTypes"] = gameTypes;
// LOOT Languages
//---------------
BOOST_LOG_TRIVIAL(debug) << "Setting GUI values for LOOT's languages.";
temp["languages"] = Language::Names();
// Now output as JSON.
YAML::Emitter tempout;
tempout.SetOutputCharset(YAML::EscapeNonAscii);
tempout.SetStringFormat(YAML::DoubleQuoted);
tempout.SetBoolFormat(YAML::TrueFalseBool);
tempout.SetSeqFormat(YAML::Flow);
tempout.SetMapFormat(YAML::Flow);
tempout << temp;
// yaml-cpp produces `!<!>` artifacts in its output, so remove them.
std::string json = tempout.c_str();
boost::replace_all(json, "!<!>", "");
// There's also a bit of weirdness where there are some \x escapes and some \u escapes.
// They should all be \u, so transform them.
boost::replace_all(json, "\\x", "\\u00");
callback->Success(json);
return true;
}
return false;
}
// LootHandler methods
//--------------------
LootHandler::LootHandler() : is_closing_(false) {
assert(!g_instance);
g_instance = this;
+2 -2
View File
@@ -121,6 +121,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
gameStr = command_line->GetSwitchValue("game");
}
loot::g_app_state.Init(gameStr);
// Back to CEF
//------------
@@ -133,8 +135,6 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
// Do application init
//--------------------
app.get()->Init(gameStr);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
CefRunMessageLoop();