Metadata editor now saves userlist on 'OK' exit, fixed linker errors.

This commit is contained in:
WrinklyNinja
2013-05-10 17:52:54 +01:00
parent 6b8b160842
commit 1e39f4a5c8
9 changed files with 150 additions and 147 deletions
+12 -23
View File
@@ -22,8 +22,8 @@
*/
#include "editor.h"
#include "../parsers.h"
#include <wx/statline.h>
#include <algorithm>
#include <boost/algorithm/string.hpp>
@@ -65,7 +65,7 @@ wxString State[2] = {
translate("Remove")
};
Editor::Editor(wxWindow *parent, const wxString& title) : wxFrame(parent, wxID_ANY, title) {
Editor::Editor(wxWindow *parent, const wxString& title, const boss::Game& game, const std::vector<boss::Plugin>& basePlugins, std::vector<boss::Plugin>& editedPlugins, bool showRecalcButton) : wxFrame(parent, wxID_ANY, title), _game(game), _basePlugins(basePlugins), _editedPlugins(editedPlugins) {
//Initialise child windows.
listBook = new wxNotebook(this, BOOK_Lists);
@@ -189,33 +189,22 @@ Editor::Editor(wxWindow *parent, const wxString& title) : wxFrame(parent, wxID_A
mainBox->Add(hbox6, 0, wxALIGN_RIGHT);
bigBox->Add(mainBox, 2, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 10);
SetBackgroundColour(wxColour(255,255,255));
SetIcon(wxIconLocation("BOSS.exe"));
SetSizerAndFit(bigBox);
}
void Editor::SetList(const std::vector<boss::Plugin>& basePlugins, const std::vector<boss::Plugin>& editedPlugins) {
_basePlugins = basePlugins;
_editedPlugins = editedPlugins;
//Fill pluginList with the contents of basePlugins.
for (int i=0, max=_basePlugins.size(); i < max; ++i) {
pluginList->InsertItem(i, FromUTF8(_basePlugins[i].Name()));
}
pluginList->SetColumnWidth(0, wxLIST_AUTOSIZE);
Layout();
}
void Editor::IsSorted(bool sorted) {
if (sorted) {
applyBtn->SetLabel(translate("Apply Load Order"));
} else {
if (!showRecalcButton) {
recalcBtn->Show(false);
applyBtn->SetLabel(translate("Save Changes"));
}
Layout();
SetBackgroundColour(wxColour(255,255,255));
SetIcon(wxIconLocation("BOSS.exe"));
SetSizerAndFit(bigBox);
}
void Editor::OnPluginSelect(wxListEvent& event) {
@@ -644,18 +633,18 @@ void Editor::OnRowSelect(wxListEvent& event) {
void Editor::OnQuit(wxCommandEvent& event) {
if (event.GetId() == BUTTON_Apply) {
//Save edits to userlist.
/* YAML::Emitter yout;
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
<< YAML::Key << "plugins" << YAML::Value << _editedPlugins
<< YAML::EndMap;
ofstream out(game.UserlistPath().string().c_str());
ofstream out(_game.UserlistPath().string().c_str());
out << yout.c_str();
out.close();
*/
if (recalcBtn->IsShown()) {
//Signal that the load order should be written.
//Signal that the load order should be written and the log opened.
}
}
Close();
+4 -5
View File
@@ -34,10 +34,7 @@
class Editor : public wxFrame {
public:
Editor(wxWindow *parent, const wxString& title);
void SetList(const std::vector<boss::Plugin>& basePlugins, const std::vector<boss::Plugin>& editedPlugins);
void IsSorted(bool sorted);
Editor(wxWindow *parent, const wxString& title, const boss::Game& game, const std::vector<boss::Plugin>& basePlugins, std::vector<boss::Plugin>& editedPlugins, bool showRecalcButton);
void OnPluginSelect(wxListEvent& event);
void OnEnabledToggle(wxCommandEvent& event);
@@ -69,7 +66,9 @@ private:
wxSpinCtrl * prioritySpin;
wxStaticText * pluginText;
std::vector<boss::Plugin> _basePlugins, _editedPlugins;
const boss::Game& _game;
const std::vector<boss::Plugin> _basePlugins;
std::vector<boss::Plugin> _editedPlugins;
void ApplyEdits(const wxString& plugin);
+2
View File
@@ -28,6 +28,8 @@
#include <boost/format.hpp>
#include "wx/wxprec.h"
#include "error.h" //Something in wxWidgets has a #define that conflicts with the error codes.
#ifndef WX_PRECOMP
# include "wx/wx.h"
#endif
+56 -81
View File
@@ -20,13 +20,15 @@
along with BOSS. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "main.h"
#include "settings.h"
#include "editor.h"
#include "../globals.h"
#include "../metadata.h"
#include "../parsers.h"
#include "main.h"
#include "settings.h"
#include "editor.h"
#include "../error.h"
#include "../helpers.h"
#include <ostream>
#include <algorithm>
@@ -256,8 +258,28 @@ Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, const
if (!fs::exists(_game.ResultsPath()))
ViewButton->Enable(false);
DisableUndetectedGames();
//Disable the menu items for the undetected games.
GameMenu->FindItem(MENU_Oblivion)->Enable(false);
GameMenu->FindItem(MENU_Nehrim)->Enable(false);
GameMenu->FindItem(MENU_Skyrim)->Enable(false);
GameMenu->FindItem(MENU_Fallout3)->Enable(false);
GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(false);
for (size_t i=0; i < _detectedGames.size(); i++) {
if (_detectedGames[i] == GAME_TES4)
GameMenu->FindItem(MENU_Oblivion)->Enable();
else if (_detectedGames[i] == GAME_NEHRIM)
GameMenu->FindItem(MENU_Nehrim)->Enable();
else if (_detectedGames[i] == GAME_TES5)
GameMenu->FindItem(MENU_Skyrim)->Enable();
else if (_detectedGames[i] == GAME_FO3)
GameMenu->FindItem(MENU_Fallout3)->Enable();
else if (_detectedGames[i] == GAME_FONV)
GameMenu->FindItem(MENU_FalloutNewVegas)->Enable();
}
//Set title bar text.
SetTitle(FromUTF8("BOSS - " + _game.Name()));
//Now set the layout and sizes.
SetMenuBar(MenuBar);
@@ -330,6 +352,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
translate("BOSS: Error"),
wxOK | wxICON_ERROR,
this);
return;
}
if (mlist["globals"])
mlist_messages = mlist["globals"].as< list<boss::Message> >();
@@ -353,6 +376,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
translate("BOSS: Error"),
wxOK | wxICON_ERROR,
this);
return;
}
if (ulist["globals"])
ulist_messages = ulist["globals"].as< list<boss::Message> >();
@@ -398,7 +422,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
for (list<boss::Plugin>::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) {
try {
it->EvalAllConditions(game);
it->EvalAllConditions(game);
} catch (boss::error& e) {
//LOG_ERROR("Error: %s", e.what());
wxMessageBox(
@@ -406,6 +430,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
translate("BOSS: Error"),
wxOK | wxICON_ERROR,
this);
return;
}
}
@@ -491,64 +516,42 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
progDia->Destroy();
//Create editor window.
Editor *editor = new Editor(this, translate("BOSS: Metadata Editor"));
//The 'plugins' list contains the merged plugin info, but we want to pass a vector of plugins with only masterlist info in its place, so have to construct that.
vector<boss::Plugin> pluginVec;
for (list<boss::Plugin>::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; ++it) {
boss::Plugin p = *it;
list<boss::Plugin>::iterator pos = std::find(mlist_plugins.begin(), mlist_plugins.end(), *it);
if (pos != mlist_plugins.end())
p.Merge(*pos);
pluginVec.push_back(p);
}
vector<boss::Plugin> ulistPluginVec(ulist_plugins.begin(), ulist_plugins.end());
//Pass plugin lists to editor window.
editor->SetList(pluginVec, ulistPluginVec);
//Tell the editor that it's not being shown as part of the load order sort process.
editor->IsSorted(true);
editor->Show();
//Now a results report definitely exists.
ViewButton->Enable(true);
}
void Launcher::OnEditMetadata(wxCommandEvent& event) {
//Parse the userlist and masterlist and get a list of installed plugins.
vector<boss::Plugin> plugins;
YAML::Node mlist, ulist;
vector<boss::Plugin> mlist_plugins, ulist_plugins;
//Should probably check for masterlist updates before opening metadata editor.
vector<boss::Plugin> installed, mlist_plugins, ulist_plugins;
//Scan for installed plugins.
for (fs::directory_iterator it(_game.DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && (it->path().extension().string() == ".esp" || it->path().extension().string() == ".esm")) {
boss::Plugin plugin(it->path().filename().string());
plugins.push_back(plugin);
installed.push_back(boss::Plugin(it->path().filename().string()));
}
}
//Parse masterlist.
if (fs::exists(_game.MasterlistPath())) {
YAML::Node mlist;
try {
mlist = YAML::LoadFile(_game.MasterlistPath().string());
} catch (YAML::ParserException& e) {
//LOG_ERROR("Error: %s", e.getString().c_str());
wxMessageBox(
FromUTF8(format(loc::translate("Error: Masterlist parsing failed. %1%")) % e.what()),
translate("BOSS: Error"),
wxOK | wxICON_ERROR,
this);
FromUTF8(format(loc::translate("Error: Masterlist parsing failed. %1%")) % e.what()),
translate("BOSS: Error"),
wxOK | wxICON_ERROR,
this);
}
if (mlist["plugins"])
mlist_plugins = mlist["plugins"].as< vector<boss::Plugin> >();
}
//Parse userlist.
if (fs::exists(_game.UserlistPath())) {
YAML::Node ulist;
try {
ulist = YAML::LoadFile(_game.UserlistPath().string());
} catch (YAML::ParserException& e) {
@@ -563,31 +566,25 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
ulist_plugins = ulist["plugins"].as< vector<boss::Plugin> >();
}
//Cut out any plugins in the masterlist that aren't installed or in the userlist.
//Merge down to the plugin list so that it holds all uneditable metadata.
list<boss::Plugin> slim_mlist;
//Merge the masterlist down into the installed mods list.
for (vector<boss::Plugin>::const_iterator it=mlist_plugins.begin(), endit=mlist_plugins.end(); it != endit; ++it) {
vector<boss::Plugin>::iterator pos = std::find(plugins.begin(), plugins.end(), *it);
vector<boss::Plugin>::iterator pos = find(installed.begin(), installed.end(), *it);
if (pos != plugins.end())
if (pos != installed.end())
pos->Merge(*it);
else if (std::find(ulist_plugins.begin(), ulist_plugins.end(), *it) != ulist_plugins.end())
plugins.push_back(*it);
}
//Add empty entries for any userlist entries that aren't installed.
for (vector<boss::Plugin>::const_iterator it=ulist_plugins.begin(), endit=ulist_plugins.end(); it != endit; ++it) {
if (find(installed.begin(), installed.end(), *it) == installed.end())
installed.push_back(boss::Plugin(it->Name()));
}
//Sort into alphabetical order.
std::sort(plugins.begin(), plugins.end(), AlphaSortPlugins);
std::sort(ulist_plugins.begin(), ulist_plugins.end(), AlphaSortPlugins);
std::sort(installed.begin(), installed.end(), AlphaSortPlugins);
//Create editor window.
Editor *editor = new Editor(this, translate("BOSS: Metadata Editor"));
//Pass plugin lists to editor window.
editor->SetList(plugins, ulist_plugins);
//Tell the editor that it's not being shown as part of the load order sort process.
editor->IsSorted(false);
Editor *editor = new Editor(this, translate("BOSS: Metadata Editor"), _game, installed, ulist_plugins, false);
editor->Show();
}
@@ -671,28 +668,6 @@ void Launcher::OnAbout(wxCommandEvent& event) {
wxAboutBox(aboutInfo);
}
void Launcher::DisableUndetectedGames() {
GameMenu->FindItem(MENU_Oblivion)->Enable(false);
GameMenu->FindItem(MENU_Nehrim)->Enable(false);
GameMenu->FindItem(MENU_Skyrim)->Enable(false);
GameMenu->FindItem(MENU_Fallout3)->Enable(false);
GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(false);
for (size_t i=0; i < _detectedGames.size(); i++) {
if (_detectedGames[i] == GAME_TES4)
GameMenu->FindItem(MENU_Oblivion)->Enable();
else if (_detectedGames[i] == GAME_NEHRIM)
GameMenu->FindItem(MENU_Nehrim)->Enable();
else if (_detectedGames[i] == GAME_TES5)
GameMenu->FindItem(MENU_Skyrim)->Enable();
else if (_detectedGames[i] == GAME_FO3)
GameMenu->FindItem(MENU_Fallout3)->Enable();
else if (_detectedGames[i] == GAME_FONV)
GameMenu->FindItem(MENU_FalloutNewVegas)->Enable();
}
SetTitle(FromUTF8("BOSS - " + _game.Name()));
}
bool Launcher::AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs) {
return boost::to_lower_copy(lhs.Name()) < boost::to_lower_copy(rhs.Name());
}
-2
View File
@@ -64,8 +64,6 @@ private:
boss::Game& _game;
YAML::Node& _settings; //BOSS Settings.
const std::vector<unsigned int>& _detectedGames;
void DisableUndetectedGames();
static bool AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs);
};
+33
View File
@@ -23,6 +23,7 @@
#include "helpers.h"
#include "metadata.h"
#include "parsers.h"
#include <src/playground.h>
@@ -106,6 +107,38 @@ namespace boss {
data = d;
}
bool ConditionalData::EvalCondition(boss::Game& game) const {
if (condition.empty())
return true;
boost::unordered_map<std::string, bool>::const_iterator it = game.conditionCache.find(boost::to_lower_copy(condition));
if (it != game.conditionCache.end())
return it->second;
condition_grammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar;
boost::spirit::qi::space_type skipper;
std::string::const_iterator begin, end;
bool eval;
grammar.SetGame(game);
begin = condition.begin();
end = condition.end();
bool r;
try {
r = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper, eval);
} catch (boss::error& e) {
throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed: " + e.what());
}
if (!r || begin != end)
throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed!");
game.conditionCache.emplace(boost::to_lower_copy(condition), eval);
return eval;
}
Message::Message() {}
Message::Message(const std::string& t, const std::string& cont)
View File
+39
View File
@@ -0,0 +1,39 @@
/* BOSS
A plugin load order optimiser for games that use the esp/esm plugin system.
Copyright (C) 2012-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_NETWORK__
#define __BOSS_NETWORK__
namespace boss {
/* Need to be able to:
- Check if there's an internet connection.
- Get the revision number for the local masterlist, if present.
- Get the revision number for the remote masterlist.
- Compare the two revision numbers.
- Download the remote masterlist if it has a higher revision number.
- Test that the downloaded masterlist is valid by parsing it to see if any errors come up.
- If the downloaded masterlist is parsed OK, replace the local masterlist with it.
*/
}
+4 -36
View File
@@ -211,7 +211,7 @@ namespace YAML {
out << EndSeq;
}
Emitter& operator << (Emitter& out, const boss::Message& rhs) {
inline Emitter& operator << (Emitter& out, const boss::Message& rhs) {
out << BeginMap
<< Key << "type" << Value << rhs.Type()
<< Key << "content" << Value << rhs.Content();
@@ -225,7 +225,7 @@ namespace YAML {
out << EndMap;
}
Emitter& operator << (Emitter& out, const boss::File& rhs) {
inline Emitter& operator << (Emitter& out, const boss::File& rhs) {
if (!rhs.IsConditional() && rhs.DisplayName().empty())
out << rhs.Name();
else {
@@ -242,7 +242,7 @@ namespace YAML {
}
}
Emitter& operator << (Emitter& out, const boss::Tag& rhs) {
inline Emitter& operator << (Emitter& out, const boss::Tag& rhs) {
if (!rhs.IsConditional())
out << rhs.PrefixedName();
else {
@@ -253,7 +253,7 @@ namespace YAML {
}
}
Emitter& operator << (Emitter& out, const boss::Plugin& rhs) {
inline Emitter& operator << (Emitter& out, const boss::Plugin& rhs) {
//if (!rhs.HasNameOnly()) {
out << BeginMap
@@ -521,37 +521,5 @@ namespace boss {
return !boost::contains(parent_path, "../../");
}
};
bool ConditionalData::EvalCondition(boss::Game& game) const {
if (condition.empty())
return true;
boost::unordered_map<std::string, bool>::const_iterator it = game.conditionCache.find(boost::to_lower_copy(condition));
if (it != game.conditionCache.end())
return it->second;
condition_grammar<std::string::const_iterator, qi::space_type> grammar;
qi::space_type skipper;
std::string::const_iterator begin, end;
bool eval;
grammar.SetGame(game);
begin = condition.begin();
end = condition.end();
bool r;
try {
r = qi::phrase_parse(begin, end, grammar, skipper, eval);
} catch (boss::error& e) {
throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed: " + e.what());
}
if (!r || begin != end)
throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed!");
game.conditionCache.emplace(boost::to_lower_copy(condition), eval);
return eval;
}
}
#endif