Started to implement XHTML report generator. Metadata.cpp edit is to speed up sorting for more rapid testing, will be reverted later. Dummy data is being fed in generators.h until the interface is implemented. Uses PugiXML for writing and (EventShim)[https://github.com/jwmcpeak/EventShim] for getting W3C events in IE8 (for now, at least).

This commit is contained in:
WrinklyNinja
2013-05-12 19:57:34 +01:00
parent 660bce95c0
commit b49e486840
11 changed files with 856 additions and 37 deletions
+4 -4
View File
@@ -11,10 +11,10 @@
cmake_minimum_required (VERSION 2.8.9)
project (boss)
set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/game.cpp" "${CMAKE_SOURCE_DIR}/src/helpers.cpp")
set (BOSS_SRC "${CMAKE_SOURCE_DIR}/src/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/game.cpp" "${CMAKE_SOURCE_DIR}/src/helpers.cpp" "${PROJECT_LIBS_DIR}/pugixml/src/pugixml.cpp")
# Include source and library directories.
include_directories ("${PROJECT_LIBS_DIR}/alphanum" "${PROJECT_LIBS_DIR}/boost" "${PROJECT_LIBS_DIR}/yaml-cpp/include" "${CMAKE_SOURCE_DIR}/src" "${PROJECT_LIBS_DIR}/libloadorder/src" "${PROJECT_LIBS_DIR}/libespm" "${PROJECT_LIBS_DIR}/zlib")
include_directories ("${PROJECT_LIBS_DIR}/alphanum" "${PROJECT_LIBS_DIR}/boost" "${PROJECT_LIBS_DIR}/yaml-cpp/include" "${CMAKE_SOURCE_DIR}/src" "${PROJECT_LIBS_DIR}/libloadorder/src" "${PROJECT_LIBS_DIR}/libespm" "${PROJECT_LIBS_DIR}/zlib" "${PROJECT_LIBS_DIR}/pugixml/src")
##############################
@@ -54,7 +54,7 @@ IF (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
IF (CMAKE_SYSTEM_NAME MATCHES "Windows")
link_directories ("${PROJECT_LIBS_DIR}/boost/stage-mingw-${PROJECT_ARCH}/lib" "${PROJECT_LIBS_DIR}/wxWidgets/build-msw/lib")
include_directories ("${PROJECT_LIBS_DIR}/wxWidgets/build-msw/lib/wx/include/i586-mingw32msvc-msw-unicode-static-2.9" "${PROJECT_LIBS_DIR}/wxWidgets/include")
set (BOSS_LIBS version wx_mswu_core-2.9-i586-mingw32msvc wx_baseu-2.9-i586-mingw32msvc wx_mswu_adv-2.9-i586-mingw32msvc wxpng-2.9-i586-mingw32msvc wxzlib-2.9-i586-mingw32msvc comctl32)
set (BOSS_LIBS version wx_mswu_webview-2.9-i586-mingw32msvc wx_mswu_core-2.9-i586-mingw32msvc wx_baseu-2.9-i586-mingw32msvc wx_mswu_adv-2.9-i586-mingw32msvc wxpng-2.9-i586-mingw32msvc wxzlib-2.9-i586-mingw32msvc comctl32)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--subsystem,windows")
ENDIF ()
@@ -71,7 +71,7 @@ ENDIF ()
# More General Settings
##############################
set (BOSS_GUI_SRC ${BOSS_SRC} "${CMAKE_SOURCE_DIR}/src/gui/ids.cpp" "${CMAKE_SOURCE_DIR}/src/gui/main.cpp" "${CMAKE_SOURCE_DIR}/src/gui/settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/editor.cpp")
set (BOSS_GUI_SRC ${BOSS_SRC} "${CMAKE_SOURCE_DIR}/src/gui/ids.cpp" "${CMAKE_SOURCE_DIR}/src/gui/main.cpp" "${CMAKE_SOURCE_DIR}/src/gui/settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/editor.cpp" "${CMAKE_SOURCE_DIR}/src/gui/viewer.cpp")
set (BOSS_API_SRC ${BOSS_SRC} "${CMAKE_SOURCE_DIR}/src/api.cpp")
+30 -16
View File
@@ -36,19 +36,19 @@ BOSSv3 won't have a command line interface, to simplify things. It also allows B
## Roadmap/To Do
- [x] Write new masterlist, userlist, settings file parsers. DONE
- [x] Implement per-game handling. DONE
- [x] Write API. DONE
- [x] Tie together automatic 'simple' sorting with masterlist, userlist data parsing and usage. DONE
- [x] Initialisation and finishing routines (read settings file, write log file, etc.). DONE
- [x] Remove game detection override. DONE
- [x] Include libespm initialisation into Game constructor. DONE
- [x] Implement reading of plugin versions from their description field. DONE
- [x] Develop UI - main window. DONE
- [x] Develop UI - settings window. DONE
- [x] Write new masterlist, userlist, settings file parsers.
- [x] Implement per-game handling.
- [x] Write API.
- [x] Tie together automatic 'simple' sorting with masterlist, userlist data parsing and usage.
- [x] Initialisation and finishing routines (read settings file, write log file, etc.).
- [x] Remove game detection override.
- [x] Include libespm initialisation into Game constructor.
- [x] Implement reading of plugin versions from their description field.
- [x] Develop UI - main window.
- [x] Develop UI - settings window.
- [ ] Develop sorting algorithm.
- [ ] Work out how to implement masterlist updating and write the code.
- [x] Develop UI - metadata editor window. DONE
- [x] Develop UI - metadata editor window.
- [ ] Develop UI - report viewer.
- [ ] Error handling.
- [ ] Checks for cyclic dependencies and incompatibilities.
@@ -60,14 +60,17 @@ BOSSv3 won't have a command line interface, to simplify things. It also allows B
- [ ] Make validity checks non-fatal.
- [ ] Add a massive "RUN THE GAME LAUNCHER IF YOUR GAME IS NOT DETECTED" message somewhere.
- [ ] Generalise Total Conversion support, so that any TC for any of the supported 'base' games can be used with BOSS.
- [ ] Write XHTML report generator.
- [ ] Write report CSS.
- [ ] Write report Javascript.
## BOSS Report
Once BOSS has applied a load order, it will display a report, similar to the BOSS Log, that covers the following:
* The number of messagesm, broken down into the total, the number of warnings and the number of errors.
* The masterlist version used and whether the masterlist was updated or not.
* The number of messages, broken down into the total, the number of warnings and the number of errors.
* The masterlist version used.
* Any parser or validity checker errors encountered.
* If there were any changes since BOSS was last run.
* The new load order.
@@ -90,7 +93,7 @@ The report data will be stored in a YAML file, which could use an extended form
* Plugins get 'version' (string) and 'crc' (int) nodes added to them.
* A new top-level node that holds a 'masterlist version' (string), a 'masterlist updated' (boolean) and a 'report changed' (boolean) node.
That should cover all the data necessary. In terms of UI, much of the BOSS Log UI is unnecessary in v3, all that's needed really is the summary page and the 'recognised' plugin list. They should be kept separate, maybe using notebook tabs, since that works quite well for the current BOSS Log. I would like to use HTML/CSS for the contents, but wxWidgets' HTML module makes that a bit involved.
There are a whole slew of options when it comes to how the report UI will be implemented. Using a native interface isn't a realistic option because it's so difficult to get anything that isn't very limited implemented, and the report is mostly static so wouldn't take advantage of native controls anyway. A HTML/CSS/JS approach is therefore preferable. The neatest way to do this would be to write the HTML as XHTML using PugiXML, then include external CSS & JS that gets distributed with BOSS.
## Optimisation
@@ -104,8 +107,7 @@ Some ideas that have yet to be tested or implemented:
* Masterlist updating could also just use a straightforward HTTP GET request, and if this method is chosen then the Accept-Encoding header should be sent with appropriate values so that the server can use compression when serving the file. Unfortunately, Google Code doesn't seem to use it, though other sites do.
Data Files Format
=================
## Data Files Format
BOSSv3's settings file, masterlist, userlist and previous run logs will all be written in YAML. This allows me to take advantage of existing parsing libraries and the format's flexibility. It's also not that much more verbose than v2's masterlist format, once data structure changes are taken into account.
@@ -123,6 +125,18 @@ LOOT will be a self-contained installation that can be dropped anywhere. It will
The directory structure will be identical to that which BOSS currently has, with the exception that the text and ini files will be replaced by YAML files.
## Required Libraries
BOSS uses the following libraries:
* Alphanum
* Boost
* Libespm
* Libloadorder
* wxWidgets
* yaml-cpp
## Misc
Here is the full list of BOSS members at the end of 2012. Any of these who lose
+192
View File
@@ -0,0 +1,192 @@
'use strict';
// shim for older browsers:
if (!document.getElementsByClassName) {
document.getElementsByClassName = (function(){
// Utility function to traverse the DOM:
function traverse (node, callback) {
callback(node);
for (var i=0;i < node.childNodes.length; i++) {
traverse(node.childNodes[i],callback);
}
}
// Actual definition of getElementsByClassName
return function (name) {
var result = [];
traverse(document.body,function(node){
if (node.className && node.className.indexOf(name) != -1) {
result.push(node);
}
});
return result;
}
})()
}
function showElement(element) {
if (element != null) {
if (element.className.indexOf('hidden') != -1) {
element.className = element.className.replace(' hidden', '');
} else if (element.className.indexOf('visible') == -1) {
element.className += ' visible';
}
}
}
function hideElement(element) {
if (element != null) {
if (element.className.indexOf('visible') != -1) {
element.className = element.className.replace('visible', '');
} else if (element.className.indexOf('hidden') == -1) {
element.className += ' hidden';
}
}
}
function stepUnhideElement(element) {
if (element != null && element.className.indexOf('hidden') != -1) {
element.className = element.className.replace('hidden', '');
}
}
function stepHideElement(element) {
if (element != null) {
element.className += ' hidden';
}
}
function toggleDisplayCSS(evt) {
var e = document.getElementsByClassName(evt.target.getAttribute('data-class'));
if (evt.target.checked) {
for (var i = 0, z = e.length; i < z; i++) {
e[i].className += ' hidden';
}
} else {
for (var i = 0, z = e.length; i < z; i++) {
e[i].className = e[i].className.replace(' hidden', '');
}
}
}
function toggleFilters(evt) {
if (document.getElementById('filtersToggle').className.indexOf('current') == -1) {
showElement(filters);
evt.target.className += ' current';
} else {
hideElement(filters);
evt.target.className = evt.target.className.replace(' current', '');
}
}
function showSection(evt) {
hideElement(document.querySelector('#summary,#plugins'));
showElement(document.getElementById(evt.target.getAttribute('data-section')));
var elem = document.querySelector('#nav div.current[data-section]');
if (elem != null) {
elem.className = elem.className.replace(' current', '');
}
if (evt.target.className.indexOf('current') == -1) {
evt.target.className += ' current';
} /*Also enable/disable filters based on current page.*/
var elemArr = document.getElementById('filters').getElementsByTagName('input');
for (var i = 0, z = elemArr.length; i < z; i++) {
if (evt.target.getAttribute('data-section') == 'plugins') {
elemArr[i].disabled = false;
} else {
elemArr[i].disabled = true;
}
}
}
function toggleMessages(evt) {
var listItems = document.getElementById('plugins').getElementsByTagName('li');
var i = listItems.length - 1;
var hiddenNo = parseInt(document.getElementById('hiddenMessageNo').textContent);
while (i > -1) {
var spans = listItems[i].getElementsByTagName('span');
if (spans.length == 0 || spans[0].className.indexOf('mod') == -1) {
var filterMatch = false;
if (evt.target.id == 'hideAllPluginMessages') {
filterMatch = true;
} else if (evt.target.id == 'hideNotes' && listItems[i].className.indexOf('note') != -1) {
filterMatch = true;
} else if (evt.target.id == 'hideBashTags' && listItems[i].className.indexOf('tag') != -1) {
filterMatch = true;
} else if (evt.target.id == 'hideDoNotCleanMessages' && listItems[i].className.indexOf('dirty') != -1 && (listItems[i].textContent.indexOf('Do not clean.') != -1 || listItems[i].textContent.indexOf('Do not clean.') != -1)) {
filterMatch = true;
}
if (filterMatch) {
if (evt.target.checked) {
if (listItems[i].className.indexOf('hidden') == -1) {
hiddenNo++;
}
stepHideElement(listItems[i]);
} else {
stepUnhideElement(listItems[i]);
if (listItems[i].className.indexOf('hidden') == -1) {
hiddenNo--;
}
}
}
}
i--;
}
document.getElementById('hiddenMessageNo').textContent = hiddenNo;
var event = document.createEvent('Event');
event.initEvent('click', true, true);
document.getElementById('hideMessagelessPlugins').dispatchEvent(event);
}
function togglePlugins(evt) {
var plugins = document.getElementById('plugins').getElementsByTagName('ul')[0].childNodes;
var i = plugins.length - 1;
var hiddenNo = parseInt(document.getElementById('hiddenPluginNo').textContent);
while (i > -1) {
if (plugins[i].nodeType == Node.ELEMENT_NODE) {
var isMessageless = true, isInactive = true;
var messages = plugins[i].getElementsByTagName('li');
var j = messages.length - 1;
while (j > -1) {
if (messages[j].className.indexOf('hidden') == -1) {
isMessageless = false;
break;
}
j--;
}
if (plugins[i].getElementsByClassName('active').length != 0) {
isInactive = false;
}
if ((document.getElementById('hideMessagelessPlugins').checked && isMessageless) || (document.getElementById('hideInactivePlugins').checked && isInactive)) {
if (plugins[i].className.indexOf('hidden') == -1) {
hiddenNo++;
hideElement(plugins[i]);
}
} else if (plugins[i].className.indexOf('hidden') != -1) {
hiddenNo--;
showElement(plugins[i]);
}
}
i--;
}
document.getElementById('hiddenPluginNo').textContent = hiddenNo;
}
function setupEventHandlers() {
var i, elemArr;
document.getElementById('filtersToggle').addEventListener('click', toggleFilters, false);
/*Set up handlers for section display.*/
elemArr = document.getElementById('nav').querySelectorAll('.button[data-section]');
var i = elemArr.length - 1;
while (i > -1) {
elemArr[i].addEventListener('click', showSection, false);
i--;
}
document.getElementById('hideVersionNumbers').addEventListener('click', toggleDisplayCSS, false);
document.getElementById('hideActiveLabel').addEventListener('click', toggleDisplayCSS, false);
document.getElementById('hideChecksums').addEventListener('click', toggleDisplayCSS, false);
document.getElementById('hideNotes').addEventListener('click', toggleMessages, false);
document.getElementById('hideBashTags').addEventListener('click', toggleMessages, false);
document.getElementById('hideDoNotCleanMessages').addEventListener('click', toggleMessages, false);
document.getElementById('hideAllPluginMessages').addEventListener('click', toggleMessages, false);
document.getElementById('hideInactivePlugins').addEventListener('click', togglePlugins, false);
document.getElementById('hideMessagelessPlugins').addEventListener('click', togglePlugins, false);
}
function init() {
setupEventHandlers();
var elemArr = document.getElementById('filters').getElementsByTagName('input');
for (var i = 0, z = elemArr.length; i < z; i++) {
elemArr[i].disabled = true;
}
showElement(document.getElementsByTagName('section')[0])
}
init();
+124
View File
@@ -0,0 +1,124 @@
body {
font:10pt/1.5 Helvetica,sans-serif;
margin:0;
background:#f8f8f8;
}
noscript div {
background:#c00;
display:block;
padding:10px 5px;
color:white;
}
#nav {
background:#eee;
position:fixed;
top:0;
left:0;
z-index:2;
box-shadow: 0 0 3px 1px rgba(0,0,0,0.5);
width:100%;
}
.button {
display:inline-table;
cursor:pointer;
padding:0.5em;
text-decoration:none;
transition:background 0.2s;
}
.button.current {
background: #ccc;
}
.button[id] {
position:absolute;
right:0;
}
#main {
position:absolute;
top:32px;
width:100%;
}
#summary, #plugins {
padding:1em;
}
table {
margin:0 auto;
border-radius:10px;
border-collapse:separate;
border-spacing:0;
}
td {
padding:0.5em;
line-height:1.5;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
text-align:right;
padding-left:3em;
}
table tr:first-child td {
border-top: 1px solid #ccc;
}
table td:first-child {
text-align:left;
padding-left:0.5em;
border-left: 1px solid #ccc;
border-right: none;
}
table tr:first-child td:first-child {
border-top-left-radius: 5px;
}
table tr:first-child td:last-child {
border-top-right-radius: 5px;
}
table tr:last-child td:first-child {
border-bottom-left-radius: 5px;
}
table tr:last-child td:last-child {
border-bottom-right-radius: 5px;
}
#noChanges {
font-weight:bold;
font-size:12pt;
text-align:center;
margin:1em;
}
#filters {
background:#e8e8e8;
z-index:1;
overflow:hidden;
position:fixed;
right:0;
top:32px;
height:0;
}
#filters.visible {
height:auto;
box-shadow:0 0 3px 1px rgba(0,0,0,0.5);
}
#filters label {
display:block;
padding:0.2em 0.5em;
white-space:nowrap;
cursor:pointer;
}
#filters div {
padding:0.4em;
font-style:italic;
}
input[type='checkbox'] {
position:relative;
top:0.15em;
margin-right:0.5em;
}
ul {margin-top:0.5em; margin-bottom:1em;}
li {margin:0.75em 0;}
.active {
display:block;
color:green;
margin-left:1.5em;
}
#plugins > ul {list-style:none;}
.hidden, #summary.hidden, #plugins.hidden {
display:none;
}
+2 -2
View File
@@ -154,8 +154,8 @@ namespace boss {
return fs::path(bossFolderName) / "userlist.yaml";
}
fs::path Game::ResultsPath() const {
return fs::path(bossFolderName) / "results.yaml";
fs::path Game::ReportPath() const {
return fs::path(bossFolderName) / "report.html";
}
void Game::RefreshActivePluginsList() {
+1 -1
View File
@@ -57,7 +57,7 @@ namespace boss {
boost::filesystem::path DataPath() const;
boost::filesystem::path MasterlistPath() const;
boost::filesystem::path UserlistPath() const;
boost::filesystem::path ResultsPath() const;
boost::filesystem::path ReportPath() const;
//Creates directory in BOSS folder for BOSS's game-specific files.
void CreateBOSSGameFolder();
+415
View File
@@ -0,0 +1,415 @@
/* BOSS
A plugin load order optimiser for games that use the esp/esm plugin system.
Copyright (C) 2013 WrinklyNinja
This file is part of BOSS.
BOSS 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.
BOSS 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 BOSS. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef __BOSS_GENERATORS__
#define __BOSS_GENERATORS__
#include "helpers.h"
#include "metadata.h"
#include <pugixml.hpp>
#include <stdexcept>
#include <string>
#include <list>
namespace boss {
void WriteMessage(pugi::xml_node& listItem, std::string content) {
size_t pos1f = content.find("\"file:");
size_t pos1h = content.find("\"http");
size_t pos1;
if (pos1f < pos1h)
pos1 = pos1f;
else
pos1 = pos1h;
size_t pos2 = content.find(' ', pos1+1);
size_t pos3 = content.find('"', pos1+1);
while (pos1 != std::string::npos && pos2 != std::string::npos && pos3 != std::string::npos) {
std::string url = content.substr(pos1+1, pos2-pos1-1);
std::string display = content.substr(pos2+1, pos3-pos2-1);
//Insert normal text preceding hyperlink.
listItem.append_child(pugi::node_pcdata).set_value(content.substr(0, pos1).c_str());
//Insert hyperlink.
pugi::xml_node a = listItem.append_child();
a.set_name("a");
a.append_attribute("href").set_value(url.c_str());
a.text().set(display.c_str());
//Remove all the processed text from the string.
content = content.substr(pos3+1);
//Look for the next hyperlink.
pos1f = content.find("\"file:");
pos1h = content.find("\"http");
if (pos1f < pos1h)
pos1 = pos1f;
else
pos1 = pos1h;
pos2 = content.find(' ', pos1+1);
pos3 = content.find('"', pos1+1);
}
//Insert any leftover text.
listItem.append_child(pugi::node_pcdata).set_value(content.c_str());
}
void GenerateHead(pugi::xml_document& doc) {
//Add DOCTYPE node.
doc.append_child(pugi::node_doctype).set_value("html");
//Create 'head' node.
pugi::xml_node head = doc.append_child();
head.set_name("head");
//Now add head elements.
pugi::xml_node node;
node = head.append_child();
node.set_name("meta");
node.append_attribute("http-equiv").set_value("X-UA-Compatible");
node.append_attribute("content").set_value("IE=8");
node = head.append_child();
node.set_name("meta");
node.append_attribute("charset").set_value("utf-8");
node = head.append_child();
node.set_name("title");
node.text().set("BOSS Report");
node = head.append_child();
node.set_name("link");
node.append_attribute("rel").set_value("stylesheet");
node.append_attribute("href").set_value("../../examples/style.css");
}
void AppendNav(pugi::xml_node& body) {
pugi::xml_node nav, div;
nav = body.append_child();
nav.set_name("div");
nav.append_attribute("id").set_value("nav");
div = nav.append_child();
div.set_name("div");
div.append_attribute("class").set_value("button current");
div.append_attribute("data-section").set_value("summary");
div.text().set("Summary");
div = nav.append_child();
div.set_name("div");
div.append_attribute("class").set_value("button");
div.append_attribute("data-section").set_value("plugins");
div.text().set("Details");
div = nav.append_child();
div.set_name("div");
div.append_attribute("class").set_value("button");
div.append_attribute("id").set_value("filtersToggle");
div.text().set("Filters");
}
void AppendSummary(pugi::xml_node& main,
bool hasChanged,
const std::string& masterlistVersion,
bool masterlistUpdateEnabled,
int messageNo,
int warnNo,
int errorNo,
const std::list<Message>& messages,
const std::string& language) {
pugi::xml_node summary = main.append_child();
summary.set_name("div");
summary.append_attribute("id").set_value("summary");
pugi::xml_node table = summary.append_child();
table.set_name("table");
table = table.append_child();
table.set_name("tbody");
pugi::xml_node row, cell;
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("BOSS Version");
cell = row.append_child();
cell.set_name("td");
cell.text().set((IntToString(VERSION_MAJOR)+"."+IntToString(VERSION_MINOR)+"."+IntToString(VERSION_PATCH)).c_str());
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("Masterlist Version");
cell = row.append_child();
cell.set_name("td");
cell.text().set(masterlistVersion.c_str());
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("Masterlist Updating");
cell = row.append_child();
cell.set_name("td");
if (masterlistUpdateEnabled)
cell.text().set("Enabled");
else
cell.text().set("Disabled");
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("Total Number of Messages");
cell = row.append_child();
cell.set_name("td");
cell.text().set(IntToString(messageNo).c_str());
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("Number of Warnings");
cell = row.append_child();
cell.set_name("td");
cell.text().set(IntToString(warnNo).c_str());
row = table.append_child();
row.set_name("tr");
cell = row.append_child();
cell.set_name("td");
cell.text().set("Number of Errors");
cell = row.append_child();
cell.set_name("td");
cell.text().set(IntToString(errorNo).c_str());
if (hasChanged) {
pugi::xml_node note = summary.append_child();
note.set_name("div");
note.append_attribute("id").set_value("noChanges");
note.text().set("No changes since last run.");
}
if (!messages.empty()) {
pugi::xml_node list = summary.append_child();
list.set_name("ul");
for (std::list<Message>::const_iterator it=messages.begin(), endit=messages.end(); it != endit; ++it) {
//Need to weed out different language messages.
if (it->Language().empty() || boost::iequals(it->Language(), language)) {
pugi::xml_node li = list.append_child();
li.set_name("li");
li.append_attribute("class").set_value(it->Type().c_str());
//Turn any urls into hyperlinks.
WriteMessage(li, it->Content());
}
}
}
}
void AppendMain(pugi::xml_node& body,
bool hasChanged,
const std::string& masterlistVersion,
bool masterlistUpdateEnabled,
int messageNo,
int warnNo,
int errorNo,
const std::list<Message>& messages,
const std::string& language) {
pugi::xml_node main = body.append_child();
main.set_name("div");
main.append_attribute("id").set_value("main");
pugi::xml_node noscript, div;
noscript = main.append_child();
noscript.set_name("noscript");
div = noscript.append_child();
div.set_name("div");
div.text().set("The BOSS Report requires Javascript to be enabled in order to function.");
AppendSummary(main, hasChanged, masterlistVersion, masterlistUpdateEnabled, messageNo, warnNo, errorNo, messages, language);
}
void AppendFilters(pugi::xml_node& body, int messageNo, int pluginNo) {
pugi::xml_node filters = body.append_child();
filters.set_name("div");
filters.append_attribute("id").set_value("filters");
pugi::xml_node label, input;
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideVersionNumbers");
input.append_attribute("data-class").set_value("version");
input.text().set("Hide Version Numbers");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideActiveLabel");
input.append_attribute("data-class").set_value("active");
input.text().set("Hide 'Active' Label");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideChecksums");
input.append_attribute("data-class").set_value("crc");
input.text().set("Hide Checksums");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideNotes");
input.text().set("Hide Notes");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideBashTags");
input.text().set("Hide Bash Tag Suggestions");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideDoNotCleanMessages");
input.text().set("Hide 'Do Not Clean' Messages");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideAllPluginMessages");
input.text().set("Hide All Plugin Messages");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideInactivePlugins");
input.text().set("Hide Inactive Plugins");
label = filters.append_child();
label.set_name("label");
input = label.append_child();
input.set_name("input");
input.append_attribute("type").set_value("checkbox");
input.append_attribute("id").set_value("hideMessagelessPlugins");
input.text().set("Hide Messageless Plugins");
pugi::xml_node span;
label = filters.append_child();
label.set_name("div");
span = label.append_child();
span.set_name("span");
span.append_attribute("id").set_value("hiddenPluginNo");
span.text().set("0");
label.append_child(pugi::node_pcdata).set_value((" of " + IntToString(pluginNo) + " plugins hidden.").c_str());
label = filters.append_child();
label.set_name("div");
span = label.append_child();
span.set_name("span");
span.append_attribute("id").set_value("hiddenMessageNo");
span.text().set("0");
label.append_child(pugi::node_pcdata).set_value((" of " + IntToString(messageNo) + " messages hidden.").c_str());
}
void AppendScripts(pugi::xml_node& body) {
pugi::xml_node node;
node = body.append_child();
node.set_name("script");
node.append_attribute("src").set_value("../../examples/eventShim.js");
node.text().set(" ");
node = body.append_child();
node.set_name("script");
node.append_attribute("src").set_value("../../examples/script.js");
node.text().set(" ");
}
void GenerateReport(const std::string& file) {
pugi::xml_document doc;
GenerateHead(doc);
pugi::xml_node body = doc.append_child();
body.set_name("body");
AppendNav(body);
std::list<Message> messages;
messages.push_back(Message("say", "This is a test message with a link to \"http://www.google.com Google\" in it."));
AppendMain(body, true, "4308 (2013-06-06)", true, 500, 4, 2, messages, "eng");
AppendFilters(body, 5, 5);
AppendScripts(body);
if (!doc.save_file(file.c_str(), "\t", pugi::format_default | pugi::format_no_declaration))
throw std::runtime_error("Could not write XML file.");
}
}
#endif
+8 -12
View File
@@ -23,12 +23,14 @@
#include "main.h"
#include "settings.h"
#include "editor.h"
#include "viewer.h"
#include "../globals.h"
#include "../metadata.h"
#include "../parsers.h"
#include "../error.h"
#include "../helpers.h"
#include "../generators.h"
#include <ostream>
#include <algorithm>
@@ -272,7 +274,7 @@ Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, const
else if (_game.Id() == GAME_FONV)
GameMenu->FindItem(MENU_FalloutNewVegas)->Check();
if (!fs::exists(_game.ResultsPath()))
if (!fs::exists(_game.ReportPath()))
ViewButton->Enable(false);
//Disable the menu items for the undetected games.
@@ -513,16 +515,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
out << "Writing results file..." << endl;
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
<< YAML::Key << "globals" << YAML::Value << messages
<< YAML::Key << "plugins" << YAML::Value << plugins
<< YAML::EndMap;
ofstream results_out(_game.ResultsPath().string().c_str());
results_out << yout.c_str();
results_out.close();
GenerateReport(_game.ReportPath().string());
progDia->Pulse();
@@ -605,7 +598,10 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
}
void Launcher::OnViewLastReport(wxCommandEvent& event) {
//Create viewer window.
Viewer *viewer = new Viewer(this, translate("BOSS: Report Viewer"), _game);
viewer->Show();
}
void Launcher::OnOpenSettings(wxCommandEvent& event) {
+43
View File
@@ -0,0 +1,43 @@
/* BOSS
A plugin load order optimiser for games that use the esp/esm plugin system.
Copyright (C) 2013 WrinklyNinja
This file is part of BOSS.
BOSS 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.
BOSS 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 BOSS. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "viewer.h"
#include <wx/webview.h>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
Viewer::Viewer(wxWindow *parent, const wxString& title, const boss::Game& game) : wxFrame(parent, wxID_ANY, title) {
if (!fs::exists(fs::absolute(game.ReportPath())))
throw boss::error(ERROR_PATH_NOT_FOUND, "The path \"" + game.ReportPath().string() + "\" could not be found.");
wxWebView * web = wxWebView::New(this, wxID_ANY, "file://" + fs::absolute(game.ReportPath()).string());
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
topsizer->Add(web, 1, wxEXPAND);
SetSizer(topsizer);
SetSize(800,600);
}
+35
View File
@@ -0,0 +1,35 @@
/* BOSS
A plugin load order optimiser for games that use the esp/esm plugin system.
Copyright (C) 2013 WrinklyNinja
This file is part of BOSS.
BOSS 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.
BOSS 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 BOSS. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef __BOSS_GUI_VIEWER__
#define __BOSS_GUI_VIEWER__
#include "ids.h"
#include "../game.h"
class Viewer : public wxFrame {
public:
Viewer(wxWindow *parent, const wxString& title, const boss::Game& game);
};
#endif
+2 -2
View File
@@ -241,7 +241,7 @@ namespace boss {
Plugin::Plugin(const boss::Game& game, const std::string& n)
: name(n), enabled(true), priority(0) {
/*
// Get data from file contents using libespm. Assumes libespm has already been initialised.
boost::filesystem::path filepath = game.DataPath() / n;
espm::File file(filepath.string(), game.espm_settings, false, false);
@@ -298,7 +298,7 @@ namespace boss {
break;
}
}
}*/
}
void Plugin::Merge(const Plugin& plugin, bool ifDisabled) {