| Requirements | This is a list of files that are required by the current plugin for it to function correctly. BOSS will display an error message if any of the listed files are missing.
Any file, not just plugins, can be listed here, and each file has three sub-fields:
@@ -370,6 +373,7 @@ To translate the BOSS application:
- Ysne58: For keeping the thread links on BOSS's Nexus site pages up to date.
- Ćukasz Niemczyk: For the Polish translation of v3.
- bluesky404: For the Chinese translation of v3.
+
- Kaos: For the French translation of v3.
- For beta testing: Surazal, myk002, Hickory, Ysne58, the Beta Testing & Analysis Guild at TES Alliance, AndalayBay, dAb, dj2005, Dubiousness, hyno111, Invader, matter2003, NightStar, olminator, saebel, Telyn, wiz0floyd, wormheart, Zanderat, zone22 and many more.
BOSS is written in C++ and makes use of the Alphanum, Boost, libespm, libgit2, libloadorder, PugiXML, wxWidgets and yaml-cpp libraries. BOSS's reports are written in HTML5/CSS3/Javascript and make use of Polyfill.js to provide Internet Explorer 8 compatibility. Copyright license information for all these may be found here.
diff --git a/docs/images/confirm-1.png b/docs/images/confirm-1.png
new file mode 100644
index 00000000..9ecb0385
Binary files /dev/null and b/docs/images/confirm-1.png differ
diff --git a/docs/images/confirm-2.png b/docs/images/confirm-2.png
new file mode 100644
index 00000000..88ecde28
Binary files /dev/null and b/docs/images/confirm-2.png differ
diff --git a/docs/images/confirm.png b/docs/images/confirm.png
deleted file mode 100644
index 83b16e4a..00000000
Binary files a/docs/images/confirm.png and /dev/null differ
diff --git a/src/backend/metadata.cpp b/src/backend/metadata.cpp
index 21f85253..41e9c3f4 100644
--- a/src/backend/metadata.cpp
+++ b/src/backend/metadata.cpp
@@ -741,7 +741,7 @@ namespace boss {
}
bool alpha_sort(const Plugin& lhs, const Plugin& rhs) {
- return boost::ilexicographical_compare(lhs.Name(), rhs.Name());
+ return boost::ilexicographical_compare(rhs.Name(), lhs.Name());
}
bool master_sort(const Plugin& lhs, const Plugin& rhs) {
diff --git a/src/gui/editor.cpp b/src/gui/editor.cpp
index 95ef6d27..f47d0c63 100644
--- a/src/gui/editor.cpp
+++ b/src/gui/editor.cpp
@@ -34,80 +34,420 @@
using namespace std;
-wxString Type[3] = {
- translate("Note"),
- translate("Warning"),
- translate("Error")
-};
+//////////////////////////////
+// TextDropTarget class
+//////////////////////////////
-wxString State[2] = {
- translate("Add"),
- translate("Remove")
-};
+TextDropTarget::TextDropTarget(wxListView * owner, wxStaticText * name) : targetOwner(owner), targetName(name) {}
-MessageList::MessageList(wxWindow * parent, wxWindowID id, const unsigned int language) : wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_VIRTUAL|wxLC_SINGLE_SEL), _language(language) {
- InsertColumn(0, translate("Type"));
- InsertColumn(1, translate("Content"));
- InsertColumn(2, translate("Condition"));
- InsertColumn(3, translate("Language"));
-
- Bind(wxEVT_LIST_DELETE_ITEM, &MessageList::OnDeleteItem, this);
-
- SetItemCount(0);
+bool TextDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString &data) {
+ if (data == targetName->GetLabelText() || targetOwner->FindItem(-1, data) != wxNOT_FOUND)
+ return false;
+ targetOwner->InsertItem(targetOwner->GetItemCount(), data);
+ return true;
}
-std::vector MessageList::GetItems() const {
- return _messages;
+///////////////////////////////////
+// Common Editor Class
+///////////////////////////////////
+
+CommonEditor::CommonEditor(const std::list& plugins, const boss::Game& game) : _basePlugins(plugins), _game(game) {}
+
+CommonEditor::CommonEditor(const std::list& plugins, const boss::Game& game, std::list& editedPlugins) : _basePlugins(plugins), _game(game), _editedPlugins(editedPlugins) {}
+
+boss::Plugin CommonEditor::GetMasterData(const wxString& plugin) const {
+ BOOST_LOG_TRIVIAL(debug) << "Getting hardcoded and masterlist metadata for plugin: " << plugin.ToUTF8();
+ boss::Plugin p;
+ boss::Plugin p_in(string(plugin.ToUTF8()));
+
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), p_in);
+
+ if (it != _basePlugins.end())
+ p = *it;
+
+ return p;
}
-void MessageList::SetItems(const std::vector& messages) {
- _messages = messages;
- SetItemCount(_messages.size());
- RefreshItems(0, _messages.size() - 1);
+boss::Plugin CommonEditor::GetUserData(const wxString& plugin) const {
+ BOOST_LOG_TRIVIAL(debug) << "Getting userlist metadata for plugin: " << plugin.ToUTF8();
+ boss::Plugin p(string(plugin.ToUTF8()));
+
+ list::const_iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), p);
+
+ if (it != _editedPlugins.end())
+ p = *it;
+
+ return p;
}
-boss::Message MessageList::GetItem(long item) const {
- return _messages[item];
-}
+void CommonEditor::ApplyEdits(const wxString& plugin, wxListView * wxList) {
+ BOOST_LOG_TRIVIAL(debug) << "Applying edits to plugin: " << plugin.ToUTF8();
-void MessageList::SetItem(long item, const boss::Message& message) {
- _messages[item] = message;
- RefreshItem(item);
-}
+ //Get recorded data.
+ boss::Plugin master(GetMasterData(plugin));
+ boss::Plugin edited(GetNewData(plugin));
-void MessageList::AppendItem(const boss::Message& message) {
- _messages.push_back(message);
- SetItemCount(_messages.size());
- RefreshItem(_messages.size() - 1);
-}
+ boss::Plugin diff = master.DiffMetadata(edited);
-void MessageList::OnDeleteItem(wxListEvent& event) {
- _messages.erase(_messages.begin() + event.GetIndex());
- SetItemCount(_messages.size());
- RefreshItems(event.GetIndex(), _messages.size() - 1);
-}
+ list::iterator pos = std::find(_editedPlugins.begin(), _editedPlugins.end(), diff);
+ long i = wxList->FindItem(-1, plugin);
-wxString MessageList::OnGetItemText(long item, long column) const {
- if (column < 0 || column > 3 || item < 0 || item > _messages.size() - 1)
- return wxString();
-
- if (column == 0) {
- if (_messages[item].Type() == boss::g_message_say)
- return Type[0];
- else if (_messages[item].Type() == boss::g_message_warn)
- return Type[1];
+ if (!diff.HasNameOnly()) {
+ wxList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
+ if (pos != _editedPlugins.end())
+ *pos = diff;
else
- return Type[2];
- } else if (column == 1) {
- return FromUTF8(_messages[item].ChooseContent(_language).Str());
- } else if (column == 2) {
- return FromUTF8(_messages[item].Condition());
- } else {
- return FromUTF8(boss::Language(_messages[item].ChooseContent(_language).Language()).Name());
+ _editedPlugins.push_back(diff);
+ }
+ else {
+ wxList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+ if (pos != _editedPlugins.end())
+ _editedPlugins.erase(pos); //Prevents unnecessary writes.
}
}
-Editor::Editor(wxWindow *parent, const wxString& title, const std::string userlistPath, const std::vector& basePlugins, std::vector& editedPlugins, const unsigned int language, const boss::Game& game) : wxFrame(parent, wxID_ANY, title), _userlistPath(userlistPath), _basePlugins(basePlugins), _editedPlugins(editedPlugins), _game(game) {
+boss::File CommonEditor::RowToFile(wxListView * list, long row) const {
+ return boss::File(
+ string(list->GetItemText(row, 0).ToUTF8()),
+ string(list->GetItemText(row, 1).ToUTF8()),
+ string(list->GetItemText(row, 2).ToUTF8())
+ );
+}
+
+boss::Tag CommonEditor::RowToTag(wxListView * list, long row) const {
+ string name = string(list->GetItemText(row, 1).ToUTF8());
+
+ if (list->GetItemText(row, 0) == State[1])
+ return boss::Tag(
+ name,
+ false,
+ string(list->GetItemText(row, 2).ToUTF8())
+ );
+ else
+ return boss::Tag(
+ name,
+ true,
+ string(list->GetItemText(row, 2).ToUTF8())
+ );
+}
+
+boss::PluginDirtyInfo CommonEditor::RowToPluginDirtyInfo(wxListView * list, long row) const {
+ string text(list->GetItemText(row, 0).ToUTF8());
+ uint32_t crc = strtoul(text.c_str(), NULL, 16);
+ return boss::PluginDirtyInfo(
+ crc,
+ atoi(string(list->GetItemText(row, 1).ToUTF8()).c_str()),
+ atoi(string(list->GetItemText(row, 2).ToUTF8()).c_str()),
+ atoi(string(list->GetItemText(row, 3).ToUTF8()).c_str()),
+ string(list->GetItemText(row, 4).ToUTF8()));
+}
+
+
+///////////////////////////////////
+// Mini Editor Class
+///////////////////////////////////
+
+
+MiniEditor::MiniEditor(wxWindow *parent, const wxString& title, const std::list& plugins, const boss::Game& game) : wxDialog(parent, wxID_ANY, title), CommonEditor(plugins, game) {
+ //Initialise editing panel.
+ editingPanel = new wxPanel(this, wxID_ANY);
+
+ //Initialise controls.
+ pluginText = new wxStaticText(editingPanel, wxID_ANY, "");
+ descText = new wxStaticText(this, wxID_ANY, translate("Please submit any edits made for reasons other than personal preference to the BOSS team so that they may be included in the masterlist."));
+ prioritySpin = new wxSpinCtrl(editingPanel, wxID_ANY, "0");
+ prioritySpin->SetRange(std::numeric_limits::min(), std::numeric_limits::max());
+ filterCheckbox = new wxCheckBox(editingPanel, wxID_ANY, translate("Show only conflicting plugins."));
+
+ pluginList = new wxListView(this, LIST_Plugins, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
+ loadAfterList = new wxListView(editingPanel, LIST_LoadAfter, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
+ loadAfterList->SetDropTarget(new TextDropTarget(loadAfterList, pluginText));
+
+ removeBtn = new wxButton(editingPanel, BUTTON_RemoveRow, translate("Remove Plugin"));
+
+ //Set up list columns.
+ pluginList->AppendColumn(translate("Plugin"));
+ pluginList->AppendColumn(translate("Priority"));
+
+ loadAfterList->AppendColumn(translate("Filename"));
+ loadAfterList->AppendColumn(translate("Display Name"));
+ loadAfterList->AppendColumn(translate("Condition"));
+ loadAfterList->SetColumnWidth(1, 0); //Hide this from the user.
+ loadAfterList->SetColumnWidth(2, 0); //Hide this from the user.
+
+ //Initialise control states.
+ removeBtn->Enable(false);
+ prioritySpin->Enable(false);
+ filterCheckbox->Enable(false);
+
+ //Make plugin name bold text.
+ pluginText->SetFont(pluginText->GetFont().Bold());
+
+ //Set up event handling.
+ Bind(wxEVT_LIST_ITEM_SELECTED, &MiniEditor::OnPluginSelect, this, LIST_Plugins);
+ Bind(wxEVT_LIST_BEGIN_DRAG, &MiniEditor::OnDragStart, this, LIST_Plugins);
+ Bind(wxEVT_LIST_ITEM_SELECTED, &MiniEditor::OnRowSelect, this, LIST_LoadAfter);
+ Bind(wxEVT_BUTTON, &MiniEditor::OnRemoveRow, this, BUTTON_RemoveRow);
+ Bind(wxEVT_CHECKBOX, &MiniEditor::OnFilterToggle, this);
+ Bind(wxEVT_BUTTON, &MiniEditor::OnApply, this, wxID_APPLY);
+
+ //Set up editing panel layout.
+ wxBoxSizer * mainBox = new wxBoxSizer(wxVERTICAL);
+ mainBox->Add(pluginText, 0, wxTOP | wxBOTTOM, 10);
+ mainBox->Add(filterCheckbox, 0, wxTOP | wxBOTTOM, 10);
+
+ wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL);
+ hbox2->Add(new wxStaticText(editingPanel, wxID_ANY, translate("Priority: ")), 0, wxRIGHT, 5);
+ hbox2->Add(prioritySpin);
+ mainBox->Add(hbox2, 0, wxEXPAND | wxBOTTOM, 10);
+
+ wxStaticBoxSizer * staticBox = new wxStaticBoxSizer(wxVERTICAL, editingPanel, translate("Load After"));
+ staticBox->Add(loadAfterList, 0, wxEXPAND);
+ staticBox->Add(removeBtn, 0, wxEXPAND | wxALIGN_RIGHT | wxTOP, 5);
+ mainBox->Add(staticBox, 0, wxEXPAND);
+
+ editingPanel->SetSizerAndFit(mainBox);
+ editingPanel->Layout();
+
+ //Set up rest of layout.
+ wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
+
+ wxBoxSizer * hBox = new wxBoxSizer(wxHORIZONTAL);
+ hBox->Add(pluginList, 0, wxEXPAND | wxALL, 10);
+ hBox->Add(editingPanel, 0, wxEXPAND | wxTOP | wxBOTTOM | wxRIGHT, 10);
+ bigBox->Add(hBox, 0, wxEXPAND | wxALL, 5);
+
+ bigBox->Add(descText, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 15);
+
+ //Need to add 'Yes' and 'No' buttons.
+ wxSizer * sizer = CreateSeparatedButtonSizer(wxAPPLY | wxCANCEL);
+
+ //Now add buttons to window sizer.
+ if (sizer != NULL)
+ bigBox->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 15);
+
+ //Fill pluginList with the contents of basePlugins.
+ int i = 0;
+ for (list::const_iterator it = _basePlugins.begin(); it != _basePlugins.end(); ++it) {
+ pluginList->InsertItem(i, FromUTF8(it->Name()));
+ pluginList->SetItem(i, 1, FromUTF8(boss::IntToString(it->Priority())));
+ if (it->FormIDs().empty()) {
+ pluginList->SetItemTextColour(i, wxColour(122, 122, 122));
+ }
+ else if (it->LoadsBSA(_game)) {
+ pluginList->SetItemTextColour(i, wxColour(0, 142, 219));
+ }
+ ++i;
+ }
+ pluginList->SetColumnWidth(0, wxLIST_AUTOSIZE);
+ pluginList->SetColumnWidth(1, 0);
+ descText->Wrap(pluginList->GetClientSize().GetWidth());
+
+ SetBackgroundColour(wxColour(255, 255, 255));
+ SetIcon(wxIconLocation("BOSS.exe"));
+
+ editingPanel->Hide();
+ SetSizerAndFit(bigBox);
+}
+
+void MiniEditor::OnPluginSelect(wxListEvent& event) {
+ //Create Plugin object for selected plugin.
+ wxString selectedPlugin = pluginList->GetItemText(event.GetIndex());
+ wxString currentPlugin = pluginText->GetLabelText();
+
+ //Check if the selected plugin is the same as the current plugin.
+ if (selectedPlugin != currentPlugin) {
+ BOOST_LOG_TRIVIAL(debug) << "User selected plugin: " << selectedPlugin.ToUTF8();
+
+ //Apply any current edits.
+ if (!currentPlugin.empty()) {
+ ApplyEdits(currentPlugin, pluginList);
+ //Also update the item's priority value in the plugins list in case it has changed.
+ pluginList->SetItem(pluginList->FindItem(-1, currentPlugin), 1, FromUTF8(boss::IntToString(prioritySpin->GetValue())));
+ }
+
+ //Merge metadata.
+ boss::Plugin plugin = GetMasterData(selectedPlugin);
+ plugin.MergeMetadata(GetUserData(selectedPlugin));
+
+ //Now fill editor fields with new plugin's info and update control states.
+ BOOST_LOG_TRIVIAL(debug) << "Filling editor fields with plugin info.";
+ pluginText->SetLabelText(FromUTF8(plugin.Name()));
+
+ prioritySpin->SetValue(plugin.Priority());
+
+ loadAfterList->DeleteAllItems();
+ set files = plugin.LoadAfter();
+ int i = 0;
+ for (set::const_iterator it = files.begin(), endit = files.end(); it != endit; ++it) {
+ loadAfterList->InsertItem(i, FromUTF8(it->Name()));
+ ++i;
+ }
+ if (loadAfterList->GetItemCount() == 0)
+ loadAfterList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ loadAfterList->SetColumnWidth(0, wxLIST_AUTOSIZE);
+
+ //Set control states.
+ prioritySpin->Enable(true);
+ filterCheckbox->Enable(true);
+ removeBtn->Enable(false);
+ }
+
+ //Change layout.
+ if (pluginList->GetColumnWidth(1) == 0)
+ pluginList->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER);
+ if (!editingPanel->IsShown())
+ editingPanel->Show();
+
+ //Do all the horrible fitting. Still haven't managed to set a min size for the editing controls.
+ //Layout() doesn't do anything extra, it automatically gets called by the windows.
+ //InvalidateBestSize() doesn't do anything useful for the panel or the window.
+ pluginList->InvalidateBestSize(); //Makes the priority column visible without scrolling.
+ editingPanel->Fit(); //Fits the editing panel to the pluginText length.
+ editingPanel->SetMinSize(editingPanel->GetSize()); //Makes sure that the editing panel is not cut off when Fit() is called below.
+ Fit();
+ int width = descText->GetSize().GetWidth();
+ descText->SetLabel(translate("Please submit any edits made for reasons other than personal preference to the BOSS team so that they may be included in the masterlist."));
+ descText->Wrap(width);
+}
+
+void MiniEditor::OnFilterToggle(wxCommandEvent& event) {
+ //First need to merge the base and edited plugin lists so that the right priority values get displayed.
+
+ list plugins(_basePlugins);
+ for (list::const_iterator it = _editedPlugins.begin(); it != _editedPlugins.end(); ++it) {
+ list::iterator pos = std::find(plugins.begin(), plugins.end(), *it);
+ pos->MergeMetadata(*it);
+ }
+
+ //Disable list selection.
+ if (event.IsChecked())
+ Unbind(wxEVT_LIST_ITEM_SELECTED, &MiniEditor::OnPluginSelect, this, LIST_Plugins);
+ else
+ Bind(wxEVT_LIST_ITEM_SELECTED, &MiniEditor::OnPluginSelect, this, LIST_Plugins);
+
+ Freeze();
+ if (event.IsChecked()) {
+ boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
+ list::const_iterator pos = std::find(plugins.begin(), plugins.end(), plugin);
+
+ if (pos != plugins.end()) {
+ pluginList->DeleteAllItems();
+
+ bool loadsBSA = pos->LoadsBSA(_game);
+
+ int i = 0;
+ for (list::const_iterator it = plugins.begin(); it != plugins.end(); ++it) {
+ //Want to filter to show only those the selected plugin can load after validly, and which also either conflict with it,
+ //or which load a BSA (if the selected plugin loads a BSA).
+ if (*it == *pos || !it->MustLoadAfter(*pos) && (pos->DoFormIDsOverlap(*it) || (loadsBSA && it->LoadsBSA(_game)))) {
+ pluginList->InsertItem(i, FromUTF8(it->Name()));
+ pluginList->SetItem(i, 1, FromUTF8(boss::IntToString(it->Priority())));
+ if (it->FormIDs().empty()) {
+ pluginList->SetItemTextColour(i, wxColour(122, 122, 122));
+ }
+ else if (it->LoadsBSA(_game)) {
+ pluginList->SetItemTextColour(i, wxColour(0, 142, 219));
+ }
+ if (std::find(_editedPlugins.begin(), _editedPlugins.end(), *it) != _editedPlugins.end()) {
+ pluginList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
+ }
+ ++i;
+ }
+ }
+ }
+ }
+ else {
+ pluginList->DeleteAllItems();
+ int i = 0;
+ for (list::const_iterator it = plugins.begin(); it != plugins.end(); ++it) {
+ pluginList->InsertItem(i, FromUTF8(it->Name()));
+ pluginList->SetItem(i, 1, FromUTF8(boss::IntToString(it->Priority())));
+ if (it->FormIDs().empty()) {
+ pluginList->SetItemTextColour(i, wxColour(122, 122, 122));
+ }
+ else if (it->LoadsBSA(_game)) {
+ pluginList->SetItemTextColour(i, wxColour(0, 142, 219));
+ }
+ if (std::find(_editedPlugins.begin(), _editedPlugins.end(), *it) != _editedPlugins.end()) {
+ pluginList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
+ }
+ ++i;
+ }
+ }
+
+ //Now re-select the current plugin in the list.
+ pluginList->Select(pluginList->FindItem(-1, pluginText->GetLabelText()));
+ Thaw();
+}
+
+void MiniEditor::OnRowSelect(wxListEvent& event) {
+ boss::File file(string(loadAfterList->GetItemText(event.GetIndex(), 0).ToUTF8()));
+ boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
+
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+
+ if (it != _basePlugins.end())
+ plugin = *it;
+ else
+ BOOST_LOG_TRIVIAL(warning) << "Could not find plugin in base list: " << plugin.Name();
+
+ set loadAfter = plugin.LoadAfter();
+
+ if (loadAfter.find(file) == loadAfter.end()) {
+ BOOST_LOG_TRIVIAL(trace) << "File \"" << file.Name() << "\" was not found in base plugin metadata. Removal enabled.";
+ removeBtn->Enable(true);
+ }
+ else {
+ BOOST_LOG_TRIVIAL(trace) << "File \"" << file.Name() << "\" was found in base plugin metadata. Removal disabled.";
+ removeBtn->Enable(false);
+ }
+}
+
+void MiniEditor::OnRemoveRow(wxCommandEvent& event) {
+ loadAfterList->DeleteItem(loadAfterList->GetFirstSelected());
+ removeBtn->Enable(false);
+}
+
+void MiniEditor::OnDragStart(wxListEvent& event) {
+ wxTextDataObject data(pluginList->GetItemText(event.GetItem()));
+ wxDropSource dropSource(pluginList);
+ dropSource.SetData(data);
+ wxDragResult result = dropSource.DoDragDrop();
+}
+
+void MiniEditor::OnApply(wxCommandEvent& event) {
+ //Apply any current edits.
+ wxString currentPlugin = pluginText->GetLabelText();
+ if (!currentPlugin.empty())
+ ApplyEdits(currentPlugin, pluginList);
+
+ EndModal(event.GetId());
+}
+
+const std::list& MiniEditor::GetEditedPlugins() const {
+ return _editedPlugins;
+}
+
+boss::Plugin MiniEditor::GetNewData(const wxString& plugin) const {
+ //Get new data. Because most of it is missing in the MiniEditor, start with the existing data.
+ boss::Plugin edited(GetMasterData(plugin));
+ edited.Priority(prioritySpin->GetValue());
+ set files;
+ for (int i = 0, max = loadAfterList->GetItemCount(); i < max; ++i) {
+ files.insert(RowToFile(loadAfterList, i));
+ }
+ edited.LoadAfter(files);
+
+ return edited;
+}
+
+///////////////////////////////////
+// Editor Class
+///////////////////////////////////
+
+Editor::Editor(wxWindow *parent, const wxString& title, const std::string userlistPath, const std::list& basePlugins, std::list& editedPlugins, const unsigned int language, const boss::Game& game) : wxFrame(parent, wxID_ANY, title), _userlistPath(userlistPath), CommonEditor(basePlugins, game, editedPlugins) {
//Initialise child windows.
listBook = new wxNotebook(this, BOOK_Lists);
@@ -270,12 +610,13 @@ Editor::Editor(wxWindow *parent, const wxString& title, const std::string userli
bigBox->Add(mainBox, 2, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 10);
//Fill pluginList with the contents of basePlugins.
- for (int i=0, max=_basePlugins.size(); i < max; ++i) {
- pluginList->InsertItem(i, FromUTF8(_basePlugins[i].Name()));
- if (_basePlugins[i].LoadsBSA(_game)) {
+ int i = 0;
+ for (list::const_iterator it = _basePlugins.begin(); it != _basePlugins.end(); ++it) {
+ pluginList->InsertItem(i, FromUTF8(it->Name()));
+ if (it->LoadsBSA(_game)) {
pluginList->SetItemTextColour(i, wxColour(0, 142, 219));
}
- if (std::find(_editedPlugins.begin(), _editedPlugins.end(), _basePlugins[i]) != _editedPlugins.end()) {
+ if (std::find(_editedPlugins.begin(), _editedPlugins.end(), *it) != _editedPlugins.end()) {
pluginList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
}
}
@@ -299,7 +640,7 @@ void Editor::OnPluginSelect(wxListEvent& event) {
//Apply any current edits.
if (!currentPlugin.empty())
- ApplyEdits(currentPlugin);
+ ApplyEdits(currentPlugin, pluginList);
//Merge metadata.
boss::Plugin plugin = GetMasterData(selectedPlugin);
@@ -328,6 +669,10 @@ void Editor::OnPluginSelect(wxListEvent& event) {
loadAfterList->SetItem(i, 2, FromUTF8(it->Condition()));
++i;
}
+ if (loadAfterList->GetItemCount() == 0)
+ loadAfterList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ loadAfterList->SetColumnWidth(0, wxLIST_AUTOSIZE);
files = plugin.Reqs();
i=0;
@@ -337,6 +682,10 @@ void Editor::OnPluginSelect(wxListEvent& event) {
reqsList->SetItem(i, 2, FromUTF8(it->Condition()));
++i;
}
+ if (reqsList->GetItemCount() == 0)
+ reqsList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ reqsList->SetColumnWidth(0, wxLIST_AUTOSIZE);
files = plugin.Incs();
i=0;
@@ -346,6 +695,10 @@ void Editor::OnPluginSelect(wxListEvent& event) {
incsList->SetItem(i, 2, FromUTF8(it->Condition()));
++i;
}
+ if (incsList->GetItemCount() == 0)
+ incsList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ incsList->SetColumnWidth(0, wxLIST_AUTOSIZE);
list messages = plugin.Messages();
vector vec(messages.begin(), messages.end());
@@ -362,6 +715,10 @@ void Editor::OnPluginSelect(wxListEvent& event) {
tagsList->SetItem(i, 2, FromUTF8(it->Condition()));
++i;
}
+ if (tagsList->GetItemCount() == 0)
+ tagsList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ tagsList->SetColumnWidth(0, wxLIST_AUTOSIZE);
set dirtyInfo = plugin.DirtyInfo();
i=0;
@@ -373,6 +730,10 @@ void Editor::OnPluginSelect(wxListEvent& event) {
dirtyList->SetItem(i, 4, FromUTF8(it->CleaningUtility()));
++i;
}
+ if (dirtyList->GetItemCount() == 0)
+ dirtyList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
+ else
+ dirtyList->SetColumnWidth(0, wxLIST_AUTOSIZE);
//Set control states.
prioritySpin->Enable(true);
@@ -381,6 +742,7 @@ void Editor::OnPluginSelect(wxListEvent& event) {
editBtn->Enable(false);
removeBtn->Enable(false);
}
+ Fit();
}
void Editor::OnPluginListRightClick(wxListEvent& event) {
@@ -429,7 +791,7 @@ void Editor::OnPluginClearMetadata(wxCommandEvent& event) {
//Need to clear what's currently in the editor and what's from the userlist.
- vector::const_iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), p);
+ list::const_iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), p);
//Delete existing userlist entry.
if (it != _editedPlugins.end())
@@ -678,7 +1040,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::File file = RowToFile(reqsList, event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -702,7 +1064,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::File file = RowToFile(incsList, event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -726,7 +1088,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::File file = RowToFile(loadAfterList, event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -750,7 +1112,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::Message message = messageList->GetItem(event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -774,7 +1136,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::Tag tag = RowToTag(tagsList, event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -797,7 +1159,7 @@ void Editor::OnRowSelect(wxListEvent& event) {
boss::PluginDirtyInfo dirtyData = RowToPluginDirtyInfo(dirtyList, event.GetIndex());
boss::Plugin plugin(string(pluginText->GetLabelText().ToUTF8()));
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
+ list::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin);
if (it != _basePlugins.end())
plugin = *it;
@@ -825,7 +1187,7 @@ void Editor::OnQuit(wxCommandEvent& event) {
//Apply any current edits.
wxString currentPlugin = pluginText->GetLabelText();
if (!currentPlugin.empty())
- ApplyEdits(currentPlugin);
+ ApplyEdits(currentPlugin, pluginList);
BOOST_LOG_TRIVIAL(debug) << "Saving metadata edits to userlist.";
@@ -844,54 +1206,6 @@ void Editor::OnQuit(wxCommandEvent& event) {
Close();
}
-void Editor::ApplyEdits(const wxString& plugin) {
- BOOST_LOG_TRIVIAL(debug) << "Applying edits to plugin: " << plugin.ToUTF8();
- boss::Plugin master = GetMasterData(plugin);
- boss::Plugin edited = GetNewData(plugin);
-
- boss::Plugin diff = master.DiffMetadata(edited);
-
- vector::iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), diff);
-
- if (it != _editedPlugins.end())
- *it = diff;
- else
- _editedPlugins.push_back(diff);
-
- //Also mark plugin as edited in list.
- long i = pluginList->FindItem(-1, plugin);
- if (!diff.HasNameOnly())
- pluginList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
- else
- pluginList->SetItemFont(i, wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
-
-}
-
-boss::Plugin Editor::GetMasterData(const wxString& plugin) const {
- BOOST_LOG_TRIVIAL(debug) << "Getting hardcoded and masterlist metadata for plugin: " << plugin.ToUTF8();
- boss::Plugin p;
- boss::Plugin p_in(string(plugin.ToUTF8()));
-
- vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), p_in);
-
- if (it != _basePlugins.end())
- p = *it;
-
- return p;
-}
-
-boss::Plugin Editor::GetUserData(const wxString& plugin) const {
- BOOST_LOG_TRIVIAL(debug) << "Getting userlist metadata for plugin: " << plugin.ToUTF8();
- boss::Plugin p(string(plugin.ToUTF8()));
-
- vector::const_iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), p);
-
- if (it != _editedPlugins.end())
- p = *it;
-
- return p;
-}
-
boss::Plugin Editor::GetNewData(const wxString& plugin) const {
BOOST_LOG_TRIVIAL(debug) << "Getting metadata from editor fields for plugin: " << plugin.ToUTF8();
boss::Plugin p(string(plugin.ToUTF8()));
@@ -935,422 +1249,3 @@ boss::Plugin Editor::GetNewData(const wxString& plugin) const {
return p;
}
-
-boss::File Editor::RowToFile(wxListView * list, long row) const {
- return boss::File(
- string(list->GetItemText(row, 0).ToUTF8()),
- string(list->GetItemText(row, 1).ToUTF8()),
- string(list->GetItemText(row, 2).ToUTF8())
- );
-}
-
-boss::Tag Editor::RowToTag(wxListView * list, long row) const {
- string name = string(list->GetItemText(row, 1).ToUTF8());
-
- if (list->GetItemText(row, 0) == State[1])
- return boss::Tag(
- name,
- false,
- string(list->GetItemText(row, 2).ToUTF8())
- );
- else
- return boss::Tag(
- name,
- true,
- string(list->GetItemText(row, 2).ToUTF8())
- );
-}
-
-boss::PluginDirtyInfo Editor::RowToPluginDirtyInfo(wxListView * list, long row) const {
- string text(list->GetItemText(row, 0).ToUTF8());
- uint32_t crc = strtoul(text.c_str(), NULL, 16);
- return boss::PluginDirtyInfo(
- crc,
- atoi(string(list->GetItemText(row, 1).ToUTF8()).c_str()),
- atoi(string(list->GetItemText(row, 2).ToUTF8()).c_str()),
- atoi(string(list->GetItemText(row, 3).ToUTF8()).c_str()),
- string(list->GetItemText(row, 4).ToUTF8()));
-}
-
-FileEditDialog::FileEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
-
- _name = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
- _display = new wxTextCtrl(this, wxID_ANY);
- _condition = new wxTextCtrl(this, wxID_ANY);
-
- wxSizerFlags leftItem(0);
- leftItem.Left();
-
- wxSizerFlags rightItem(1);
- rightItem.Right().Expand();
-
- wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
-
- wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
- GridSizer->AddGrowableCol(1,1);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Filename (required):")), leftItem);
- GridSizer->Add(_name, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Displayed Name:")), leftItem);
- GridSizer->Add(_display, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
- GridSizer->Add(_condition, rightItem);
-
- bigBox->Add(GridSizer, 0, wxEXPAND|wxALL, 15);
-
- bigBox->AddSpacer(10);
- bigBox->AddStretchSpacer(1);
-
- //Need to add 'OK' and 'Cancel' buttons.
- wxSizer * sizer = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
- if (sizer != NULL)
- bigBox->Add(sizer, 0, wxEXPAND|wxLEFT|wxBOTTOM|wxRIGHT, 15);
-
- SetBackgroundColour(wxColour(255,255,255));
- SetIcon(wxIconLocation("BOSS.exe"));
- SetSizerAndFit(bigBox);
-}
-
-void FileEditDialog::SetValues(const wxString& name, const wxString& display, const wxString& condition) {
-
- _name->SetValue(name);
- _display->SetValue(display);
- _condition->SetValue(condition);
-}
-
-wxString FileEditDialog::GetName() const {
- return _name->GetValue();
-}
-
-wxString FileEditDialog::GetDisplayName() const {
- return _display->GetValue();
-}
-
-wxString FileEditDialog::GetCondition() const {
- return _condition->GetValue();
-}
-
-MessageEditDialog::MessageEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
-
- wxArrayString languages;
- vector langs = boss::Language::Names();
- for (size_t i = 0; i < langs.size(); i++) {
- languages.Add(FromUTF8(langs[i]));
- }
-
- //Initialise controls.
- _type = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, Type);
- _language = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, languages);
-
- _condition = new wxTextCtrl(this, wxID_ANY);
- _str = new wxTextCtrl(this, wxID_ANY);
-
- _content = new wxListView(this, LIST_MessageContent, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL);
-
- addBtn = new wxButton(this, BUTTON_AddContent, translate("Add Content"));
- editBtn = new wxButton(this, BUTTON_EditContent, translate("Edit Content"));
- removeBtn = new wxButton(this, BUTTON_RemoveContent, translate("Remove Content"));
-
- _content->InsertColumn(0, translate("Language"));
- _content->InsertColumn(1, translate("String"));
-
- //Set up event handling.
- Bind(wxEVT_BUTTON, &MessageEditDialog::OnAdd, this, BUTTON_AddContent);
- Bind(wxEVT_BUTTON, &MessageEditDialog::OnEdit, this, BUTTON_EditContent);
- Bind(wxEVT_BUTTON, &MessageEditDialog::OnRemove, this, BUTTON_RemoveContent);
- Bind(wxEVT_LIST_ITEM_SELECTED, &MessageEditDialog::OnSelect, this, LIST_MessageContent);
-
- wxSizerFlags leftItem(0);
- leftItem.Left();
-
- wxSizerFlags rightItem(1);
- rightItem.Right();
-
- wxSizerFlags wholeItem(0);
- wholeItem.Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 15);
-
- wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
-
- wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
- GridSizer->AddGrowableCol(1,1);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Type:")), leftItem);
- GridSizer->Add(_type, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
- GridSizer->Add(_condition, rightItem);
-
- bigBox->AddSpacer(15);
-
- bigBox->Add(GridSizer, wholeItem);
-
- bigBox->Add(_content, wholeItem);
-
- wxFlexGridSizer * GridSizer2 = new wxFlexGridSizer(2, 5, 5);
- GridSizer2->AddGrowableCol(1,1);
-
- GridSizer2->Add(new wxStaticText(this, wxID_ANY, translate("Language:")), leftItem);
- GridSizer2->Add(_language, rightItem);
-
- GridSizer2->Add(new wxStaticText(this, wxID_ANY, translate("Content:")), leftItem);
- GridSizer2->Add(_str, rightItem);
-
- bigBox->Add(GridSizer2, wholeItem);
-
- wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);
- hbox->Add(addBtn, 0, wxRIGHT, 5);
- hbox->Add(editBtn, 0, wxLEFT|wxRIGHT, 5);
- hbox->Add(removeBtn, 0, wxLEFT, 5);
- bigBox->Add(hbox, 0, wxALIGN_RIGHT|wxLEFT|wxRIGHT, 15);
-
- bigBox->AddSpacer(10);
- bigBox->AddStretchSpacer(1);
-
- //Need to add 'OK' and 'Cancel' buttons.
- wxSizer * sizer = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
- if (sizer != NULL)
- bigBox->Add(sizer, 0, wxEXPAND|wxLEFT|wxBOTTOM|wxRIGHT, 15);
-
- //Set defaults.
- _type->SetSelection(0);
- _language->SetSelection(0);
- editBtn->Enable(false);
- removeBtn->Enable(false);
-
- SetBackgroundColour(wxColour(255,255,255));
- SetIcon(wxIconLocation("BOSS.exe"));
- SetSizerAndFit(bigBox);
-}
-
-void MessageEditDialog::SetMessage(const boss::Message& message) {
-
- if (message.Type() == boss::g_message_say)
- _type->SetSelection(0);
- else if (message.Type() == boss::g_message_warn)
- _type->SetSelection(1);
- else
- _type->SetSelection(2);
-
- _condition->SetValue(FromUTF8(message.Condition()));
-
- vector contents = message.Content();
- for (size_t i=0, max=contents.size(); i < max; ++i) {
- _content->InsertItem(i, FromUTF8(boss::Language(contents[i].Language()).Name()));
- _content->SetItem(i, 1, FromUTF8(contents[i].Str()));
- }
-}
-
-boss::Message MessageEditDialog::GetMessage() const {
-
- unsigned int type;
- string condition;
- if (_type->GetSelection() == 0)
- type = boss::g_message_say;
- else if (_type->GetSelection() == 1)
- type = boss::g_message_warn;
- else
- type = boss::g_message_error;
-
- condition = string(_condition->GetValue().ToUTF8());
-
- vector contents;
- for (size_t i=0, max=_content->GetItemCount(); i < max; ++i) {
-
- string str = string(_content->GetItemText(i, 1).ToUTF8());
- unsigned int lang = boss::Language(string(_content->GetItemText(i, 0).ToUTF8())).Code();
-
- contents.push_back(boss::MessageContent(str, lang));
- }
-
- return boss::Message(type, contents, condition);
-}
-
-void MessageEditDialog::OnSelect(wxListEvent& event) {
- _language->SetSelection(boss::Language(string(_content->GetItemText(event.GetIndex(), 0).ToUTF8())).Code());
- _str->SetValue(_content->GetItemText(event.GetIndex(), 1));
- editBtn->Enable(true);
- removeBtn->Enable(true);
-}
-
-void MessageEditDialog::OnAdd(wxCommandEvent& event) {
- long i = _content->GetItemCount();
- _content->InsertItem(i, FromUTF8(boss::Language(_language->GetSelection()).Name()));
- _content->SetItem(i, 1, _str->GetValue());
-}
-
-void MessageEditDialog::OnEdit(wxCommandEvent& event) {
- if (_content->GetFirstSelected() == -1) {
- BOOST_LOG_TRIVIAL(error) << "Attempting to edit message content, but no content row selected.";
- wxMessageBox(
- translate("Error: No content row selected."),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- NULL);
- return;
- }
- long i = _content->GetFirstSelected();
- _content->SetItem(i, 0, FromUTF8(boss::Language(_language->GetSelection()).Name()));
- _content->SetItem(i, 1, _str->GetValue());
-}
-
-void MessageEditDialog::OnRemove(wxCommandEvent& event) {
- if (_content->GetFirstSelected() == -1) {
- BOOST_LOG_TRIVIAL(error) << "Attempting to remove message content, but no content row selected.";
- wxMessageBox(
- translate("Error: No content row selected."),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- NULL);
- return;
- }
- _content->DeleteItem(_content->GetFirstSelected());
- editBtn->Enable(false);
- removeBtn->Enable(false);
-}
-
-TagEditDialog::TagEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
-
- //Initialise controls.
- _state = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 2, State);
-
- _name = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
- _condition = new wxTextCtrl(this, wxID_ANY);
-
- wxSizerFlags leftItem(0);
- leftItem.Left();
-
- wxSizerFlags rightItem(1);
- rightItem.Right().Expand();
-
- wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
-
- wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
- GridSizer->AddGrowableCol(1,1);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Add/Remove:")), leftItem);
- GridSizer->Add(_state, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Name (required):")), leftItem);
- GridSizer->Add(_name, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
- GridSizer->Add(_condition, rightItem);
-
- bigBox->Add(GridSizer, 0, wxEXPAND|wxALL, 15);
-
- bigBox->AddSpacer(10);
- bigBox->AddStretchSpacer(1);
-
- //Need to add 'OK' and 'Cancel' buttons.
- wxSizer * sizer = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
- if (sizer != NULL)
- bigBox->Add(sizer, 0, wxEXPAND|wxLEFT|wxBOTTOM|wxRIGHT, 15);
-
- //Set defaults.
- _state->SetSelection(0);
-
- SetBackgroundColour(wxColour(255,255,255));
- SetIcon(wxIconLocation("BOSS.exe"));
- SetSizerAndFit(bigBox);
-}
-
-void TagEditDialog::SetValues(int state, const wxString& name, const wxString& condition) {
-
- _state->SetSelection(state);
- _name->SetValue(name);
- _condition->SetValue(condition);
-}
-
-wxString TagEditDialog::GetState() const {
- return State[_state->GetSelection()];
-}
-
-wxString TagEditDialog::GetName() const {
- return _name->GetValue();
-}
-
-wxString TagEditDialog::GetCondition() const {
- return _condition->GetValue();
-}
-
-DirtInfoEditDialog::DirtInfoEditDialog(wxWindow * parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
- //Initialise controls.
- wxTextValidator val(wxFILTER_EMPTY | wxFILTER_INCLUDE_CHAR_LIST);
- val.SetCharIncludes("0123456789ABCDEFabcdef");
-
- _itm = new wxSpinCtrl(this, wxID_ANY, "0");
- _udr = new wxSpinCtrl(this, wxID_ANY, "0");
- _nav = new wxSpinCtrl(this, wxID_ANY, "0");
- _crc = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, val);
- _utility = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
-
- wxSizerFlags leftItem(0);
- leftItem.Left();
-
- wxSizerFlags rightItem(1);
- rightItem.Right().Expand();
-
- wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
-
- wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
- GridSizer->AddGrowableCol(1,1);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("CRC (required):")), leftItem);
- GridSizer->Add(_crc, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("ITM Count:")), leftItem);
- GridSizer->Add(_itm, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("UDR Count:")), leftItem);
- GridSizer->Add(_udr, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Deleted Navmesh Count:")), leftItem);
- GridSizer->Add(_nav, rightItem);
-
- GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Cleaning Utility (required):")), leftItem);
- GridSizer->Add(_utility, rightItem);
-
- bigBox->Add(GridSizer, 0, wxEXPAND|wxALL, 15);
-
- bigBox->AddSpacer(10);
- bigBox->AddStretchSpacer(1);
-
- //Need to add 'OK' and 'Cancel' buttons.
- wxSizer * sizer = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
- if (sizer != NULL)
- bigBox->Add(sizer, 0, wxEXPAND|wxLEFT|wxBOTTOM|wxRIGHT, 15);
-
- SetBackgroundColour(wxColour(255,255,255));
- SetIcon(wxIconLocation("BOSS.exe"));
- SetSizerAndFit(bigBox);
-}
-
-void DirtInfoEditDialog::SetValues(const wxString& crc, unsigned int itm, unsigned int udr, unsigned int nav, const wxString& utility) {
- _crc->SetValue(crc);
- _itm->SetValue(itm);
- _udr->SetValue(udr);
- _nav->SetValue(nav);
- _utility->SetValue(utility);
-}
-
-wxString DirtInfoEditDialog::GetCRC() const {
- return _crc->GetValue();
-}
-
-unsigned int DirtInfoEditDialog::GetITMs() const {
- return _itm->GetValue();
-}
-
-unsigned int DirtInfoEditDialog::GetUDRs() const {
- return _udr->GetValue();
-}
-
-unsigned int DirtInfoEditDialog::GetDeletedNavmeshes() const {
- return _nav->GetValue();
-}
-
-wxString DirtInfoEditDialog::GetUtility() const {
- return _utility->GetValue();
-}
diff --git a/src/gui/editor.h b/src/gui/editor.h
index 55f565b2..71fb1bf8 100644
--- a/src/gui/editor.h
+++ b/src/gui/editor.h
@@ -24,54 +24,103 @@
#define __BOSS_GUI_EDITOR__
#include "ids.h"
+#include "misc.h"
#include "../backend/metadata.h"
#include
-#include
+#include
#include
#include
#include
+#include
+#include
-class MessageList : public wxListView {
+/* Have two versions of the editor: one mini editor that only contains
+ controls for editing the load order related metadata, and a full editor
+ that contains controls for editing all the metadata.
+
+ Both editors need to convert between metadata and control data, and to
+ keep track of what edits have been made.
+
+ Have a immutable plugin list to keep track of non-user-edit metadata,
+ and a mutable plugin list to keep track of user-edit metadata. When
+ recording edits, diff the metadata obtained from the control values with
+ the metadata in the immutable plugin entry to get the new user-edit metadata.
+
+ If a metadata type's corresponding control is not present (ie. in the mini
+ editor), then use the immutable plugin entry's metadata for that type.
+*/
+
+class TextDropTarget : public wxTextDropTarget { //Class to override virtual functions.
public:
- MessageList(wxWindow * parent, wxWindowID id, const unsigned int language);
-
- void SetItems(const std::vector& messages);
- std::vector GetItems() const;
-
- boss::Message GetItem(long item) const;
- void SetItem(long item, const boss::Message& message);
- void AppendItem(const boss::Message& message);
-
- void OnDeleteItem(wxListEvent& event);
-protected:
- wxString OnGetItemText(long item, long column) const;
-
+ TextDropTarget(wxListView * owner, wxStaticText * name);
+ virtual bool OnDropText(wxCoord x, wxCoord y, const wxString &data);
private:
- std::vector _messages;
- const unsigned int _language;
+ wxListView * targetOwner;
+ wxStaticText * targetName;
};
-class Editor : public wxFrame {
+class CommonEditor {
public:
- Editor(wxWindow *parent, const wxString& title, const std::string userlistPath, const std::vector& basePlugins, std::vector& editedPlugins, const unsigned int language, const boss::Game& game);
+ CommonEditor(const std::list& plugins, const boss::Game& game);
+ CommonEditor(const std::list& plugins, const boss::Game& game, std::list& editedPlugins);
+protected:
+ const boss::Game& _game;
+ const std::list _basePlugins;
+ std::list _editedPlugins;
+
+ boss::Plugin GetMasterData(const wxString& plugin) const;
+ boss::Plugin GetUserData(const wxString& plugin) const;
+ virtual boss::Plugin GetNewData(const wxString& plugin) const = 0;
+
+ void ApplyEdits(const wxString& plugin, wxListView * list);
+
+ boss::File RowToFile(wxListView * list, long row) const;
+ boss::Tag RowToTag(wxListView * list, long row) const;
+ boss::PluginDirtyInfo RowToPluginDirtyInfo(wxListView * list, long row) const;
+};
+
+class MiniEditor : public wxDialog, public CommonEditor {
+public:
+ MiniEditor(wxWindow *parent, const wxString& title, const std::list& plugins, const boss::Game& game);
+
+ void OnPluginSelect(wxListEvent& event);
+ void OnRowSelect(wxListEvent& event);
+ void OnRemoveRow(wxCommandEvent& event);
+ void OnFilterToggle(wxCommandEvent& event);
+ void OnDragStart(wxListEvent& event);
+ void OnApply(wxCommandEvent& event);
+
+ const std::list& GetEditedPlugins() const;
+private:
+ wxListView * pluginList;
+ wxPanel * editingPanel;
+ wxButton * removeBtn;
+ wxListView * loadAfterList;
+ wxCheckBox * filterCheckbox;
+ wxSpinCtrl * prioritySpin;
+ wxStaticText * pluginText;
+ wxStaticText * descText;
+
+ boss::Plugin GetNewData(const wxString& plugin) const;
+};
+
+class Editor : public wxFrame, public CommonEditor {
+public:
+ Editor(wxWindow *parent, const wxString& title, const std::string userlistPath, const std::list& basePlugins, std::list& editedPlugins, const unsigned int language, const boss::Game& game);
void OnPluginSelect(wxListEvent& event);
void OnPluginListRightClick(wxListEvent& event);
void OnPluginCopyName(wxCommandEvent& event);
void OnPluginCopyMetadata(wxCommandEvent& event);
void OnPluginClearMetadata(wxCommandEvent& event);
- void OnEnabledToggle(wxCommandEvent& event);
- void OnPriorityChange(wxSpinEvent& event);
void OnListBookChange(wxBookCtrlEvent& event);
void OnAddRow(wxCommandEvent& event);
void OnEditRow(wxCommandEvent& event);
void OnRemoveRow(wxCommandEvent& event);
- void OnRecalc(wxCommandEvent& event);
void OnRowSelect(wxListEvent& event);
void OnQuit(wxCommandEvent& event);
private:
-
wxMenu * pluginMenu;
wxButton * addBtn;
wxButton * editBtn;
@@ -91,88 +140,7 @@ private:
wxStaticText * pluginText;
const std::string _userlistPath;
- const std::vector _basePlugins;
- const boss::Game& _game;
- std::vector _editedPlugins;
- std::vector currentMessages;
- void ApplyEdits(const wxString& plugin);
-
- boss::Plugin GetMasterData(const wxString& plugin) const;
- boss::Plugin GetUserData(const wxString& plugin) const;
boss::Plugin GetNewData(const wxString& plugin) const;
-
- boss::File RowToFile(wxListView * list, long row) const;
- boss::Tag RowToTag(wxListView * list, long row) const;
- boss::PluginDirtyInfo RowToPluginDirtyInfo(wxListView * list, long row) const;
};
-
-class FileEditDialog : public wxDialog {
-public:
- FileEditDialog(wxWindow *parent, const wxString& title);
-
- void SetValues(const wxString& name, const wxString& display, const wxString& condition);
- wxString GetName() const;
- wxString GetDisplayName() const;
- wxString GetCondition() const;
-private:
- wxTextCtrl * _name;
- wxTextCtrl * _display;
- wxTextCtrl * _condition;
-};
-
-class MessageEditDialog : public wxDialog {
-public:
- MessageEditDialog(wxWindow *parent, const wxString& title);
-
- void SetMessage(const boss::Message& message);
- boss::Message GetMessage() const;
-
- void OnSelect(wxListEvent& event);
- void OnAdd(wxCommandEvent& event);
- void OnEdit(wxCommandEvent& event);
- void OnRemove(wxCommandEvent& event);
-private:
- wxButton * addBtn;
- wxButton * editBtn;
- wxButton * removeBtn;
- wxChoice * _type;
- wxChoice * _language;
- wxListView * _content;
- wxTextCtrl * _condition;
- wxTextCtrl * _str;
-};
-
-class TagEditDialog : public wxDialog {
-public:
- TagEditDialog(wxWindow *parent, const wxString& title);
-
- void SetValues(int state, const wxString& name, const wxString& condition);
- wxString GetState() const;
- wxString GetName() const;
- wxString GetCondition() const;
-private:
- wxChoice * _state;
- wxTextCtrl * _name;
- wxTextCtrl * _condition;
-};
-
-class DirtInfoEditDialog : public wxDialog {
-public:
- DirtInfoEditDialog(wxWindow * parent, const wxString& title);
-
- void SetValues(const wxString& crc, unsigned int itm, unsigned int udr, unsigned int nav, const wxString& utility);
- wxString GetCRC() const;
- wxString GetUtility() const;
- unsigned int GetITMs() const;
- unsigned int GetUDRs() const;
- unsigned int GetDeletedNavmeshes() const;
-private:
- wxTextCtrl * _crc;
- wxSpinCtrl * _itm;
- wxSpinCtrl * _udr;
- wxSpinCtrl * _nav;
- wxTextCtrl * _utility;
-};
-
#endif
diff --git a/src/gui/main.cpp b/src/gui/main.cpp
index 1e957dd5..58091932 100644
--- a/src/gui/main.cpp
+++ b/src/gui/main.cpp
@@ -24,6 +24,7 @@
#include "settings.h"
#include "editor.h"
#include "viewer.h"
+#include "misc.h"
#include "../backend/globals.h"
#include "../backend/metadata.h"
@@ -487,6 +488,106 @@ void Launcher::OnClose(wxCloseEvent& event) {
Destroy();
}
+void Launcher::OnViewLastReport(wxCommandEvent& event) {
+ if (_settings["View Report Externally"] && _settings["View Report Externally"].as()) {
+ BOOST_LOG_TRIVIAL(debug) << "Opening report in external application...";
+ wxLaunchDefaultBrowser(FromUTF8(ToFileURL(_game.ReportPath().string())));
+ }
+ else {
+ //Create viewer window.
+ BOOST_LOG_TRIVIAL(debug) << "Opening viewer window...";
+ Viewer *viewer = new Viewer(this, translate("BOSS: Report Viewer"), FromUTF8(ToFileURL(_game.ReportPath().string())));
+ viewer->Show();
+ }
+ BOOST_LOG_TRIVIAL(debug) << "Report displayed.";
+}
+
+void Launcher::OnOpenSettings(wxCommandEvent& event) {
+ BOOST_LOG_TRIVIAL(debug) << "Opening settings window...";
+ SettingsFrame *settings = new SettingsFrame(this, translate("BOSS: Settings"), _settings, _games);
+ settings->ShowModal();
+ BOOST_LOG_TRIVIAL(debug) << "Settings window opened.";
+}
+
+void Launcher::OnGameChange(wxCommandEvent& event) {
+ BOOST_LOG_TRIVIAL(debug) << "Changing current game...";
+ _game = _games[event.GetId() - MENU_LowestDynamicGameID];
+ try {
+ _game.Init(); //In case it hasn't already been done.
+ *find(_games.begin(), _games.end(), _game) = _game; //Sync changes.
+ BOOST_LOG_TRIVIAL(debug) << "New game is " << _game.Name();
+ }
+ catch (boss::error& e) {
+ BOOST_LOG_TRIVIAL(error) << "Game-specific settings could not be initialised." << e.what();
+ wxMessageBox(
+ FromUTF8(format(loc::translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()),
+ translate("BOSS: Error"),
+ wxOK | wxICON_ERROR,
+ NULL);
+ }
+ SetTitle(FromUTF8("BOSS - " + _game.Name()));
+ if (_game.Id() == boss::g_game_tes5)
+ RedatePluginsItem->Enable(true);
+ else
+ RedatePluginsItem->Enable(false);
+}
+
+void Launcher::OnHelp(wxCommandEvent& event) {
+ //Look for file.
+ BOOST_LOG_TRIVIAL(debug) << "Opening readme at: " << g_path_readme;
+ if (fs::exists(g_path_readme)) {
+ wxLaunchDefaultBrowser(FromUTF8(ToFileURL(g_path_readme.string())));
+ }
+ else { //No readme exists, show a pop-up message saying so.
+ BOOST_LOG_TRIVIAL(error) << "File \"" << g_path_readme.string() << "\" could not be found.";
+ wxMessageBox(
+ FromUTF8(format(loc::translate("Error: \"%1%\" cannot be found.")) % g_path_readme.string()),
+ translate("BOSS: Error"),
+ wxOK | wxICON_ERROR,
+ this);
+ }
+}
+
+void Launcher::OnOpenDebugLog(wxCommandEvent& event) {
+ //Look for file.
+ BOOST_LOG_TRIVIAL(debug) << "Opening debug log at: " << g_path_log;
+ if (fs::exists(g_path_log)) {
+ wxLaunchDefaultApplication(FromUTF8(g_path_log.string()));
+ }
+ else { //No log exists, show a pop-up message saying so.
+ BOOST_LOG_TRIVIAL(error) << "File \"" << g_path_log.string() << "\" could not be found.";
+ wxMessageBox(
+ FromUTF8(format(loc::translate("Error: \"%1%\" cannot be found.")) % g_path_log.string()),
+ translate("BOSS: Error"),
+ wxOK | wxICON_ERROR,
+ this);
+ }
+}
+
+void Launcher::OnAbout(wxCommandEvent& event) {
+ BOOST_LOG_TRIVIAL(debug) << "Opening About dialog.";
+ wxAboutDialogInfo aboutInfo;
+ aboutInfo.SetName("BOSS");
+ aboutInfo.SetVersion(IntToString(g_version_major) + "." + IntToString(g_version_minor) + "." + IntToString(g_version_patch));
+ aboutInfo.SetDescription(translate("Load order optimisation for Oblivion, Skyrim, Fallout 3 and Fallout: New Vegas."));
+ aboutInfo.SetCopyright("Copyright (C) 2009-2014 BOSS Development Team.");
+ aboutInfo.SetWebSite("http://boss-developers.github.io");
+ aboutInfo.SetLicence("This program is free software: you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published by\n"
+ "the Free Software Foundation, either version 3 of the License, or\n"
+ "(at your option) any later version.\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program. If not, see .");
+ aboutInfo.SetIcon(wxIconLocation("BOSS.exe"));
+ wxAboutBox(aboutInfo);
+}
+
void Launcher::OnSortPlugins(wxCommandEvent& event) {
BOOST_LOG_TRIVIAL(debug) << "Beginning sorting process.";
@@ -501,6 +602,10 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
wxProgressDialog *progDia = new wxProgressDialog(translate("BOSS: Working..."),translate("BOSS working..."), 1000, this, wxPD_APP_MODAL|wxPD_AUTO_HIDE|wxPD_ELAPSED_TIME);
+ ///////////////////////////////////////////////////////
+ // Load Plugins & Lists
+ ///////////////////////////////////////////////////////
+
bool doUpdate = _settings["Update Masterlist"] && _settings["Update Masterlist"].as();
masterlist_updater_parser mup(doUpdate, _game, messages, mlist_plugins, mlist_messages, revision);
group.create_thread(mup);
@@ -560,6 +665,10 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
progDia->Pulse();
+ ///////////////////////////////////////////////////////
+ // Merge & Check Metadata
+ ///////////////////////////////////////////////////////
+
if (fs::exists(_game.MasterlistPath()) || fs::exists(_game.UserlistPath())) {
//Set language.
@@ -649,121 +758,134 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
progDia->Pulse();
- BOOST_LOG_TRIVIAL(debug) << "Building the plugin dependency graph...";
+ ///////////////////////////////////////////////////////
+ // Build Graph Edges & Sort
+ ///////////////////////////////////////////////////////
- //Now add the interactions between plugins to the graph as edges.
- BOOST_LOG_TRIVIAL(trace) << "Adding non-overlap edges.";
- AddSpecificEdges(graph);
+ /* Need to loop this section. There are 3 ways to exit the loop:
+
+ 1. Accept the load order at the preview with no changes.
+ 2. Cancel at the preview.
+ 3. Cancel making changes.
- BOOST_LOG_TRIVIAL(trace) << "Adding priority edges.";
- AddPriorityEdges(graph);
-
- BOOST_LOG_TRIVIAL(trace) << "Adding overlap edges.";
- AddOverlapEdges(graph);
+ Otherwise, the sorting must loop.
+
+ */
//Check for back-edges, then perform a topological sort.
try {
- BOOST_LOG_TRIVIAL(debug) << "Checking to see if the graph is cyclic.";
- boss::CheckForCycles(graph);
+ bool applyLoadOrder = false;
+ long ret;
+ do {
+ BOOST_LOG_TRIVIAL(debug) << "Building the plugin dependency graph...";
- progDia->Pulse();
+ //Now add the interactions between plugins to the graph as edges.
+ BOOST_LOG_TRIVIAL(trace) << "Adding non-overlap edges.";
+ AddSpecificEdges(graph);
- BOOST_LOG_TRIVIAL(debug) << "Performing a topological sort.";
- boss::Sort(graph, plugins);
+ BOOST_LOG_TRIVIAL(trace) << "Adding priority edges.";
+ AddPriorityEdges(graph);
- progDia->Destroy();
- progDia = NULL;
+ BOOST_LOG_TRIVIAL(trace) << "Adding overlap edges.";
+ AddOverlapEdges(graph);
- BOOST_LOG_TRIVIAL(debug) << "Displaying load order preview.";
- LoadOrderPreview preview(this, translate("BOSS: Calculated Load Order"), plugins, _game);
+ BOOST_LOG_TRIVIAL(debug) << "Checking to see if the graph is cyclic.";
+ boss::CheckForCycles(graph);
- if (preview.ShowModal() == wxID_OK) {
- BOOST_LOG_TRIVIAL(debug) << "Load order accepted.";
- list newPluginsList, editedPlugins;
- newPluginsList = preview.GetLoadOrder();
+ progDia->Pulse();
- BOOST_LOG_TRIVIAL(trace) << "Building list of edited plugins.";
- for (list::iterator it=newPluginsList.begin(),endit=newPluginsList.end(); it != endit; ++it) {
- list::const_iterator jt = find(plugins.begin(), plugins.end(), *it);
+ BOOST_LOG_TRIVIAL(debug) << "Performing a topological sort.";
+ boss::Sort(graph, plugins);
- if (jt == plugins.end())
- continue;
+ progDia->Destroy();
+ progDia = NULL;
- boss::Plugin diff = it->DiffMetadata(*jt);
+ BOOST_LOG_TRIVIAL(debug) << "Displaying load order preview.";
+ MiniEditor editor(this, translate("BOSS: Calculated Load Order"), plugins, _game);
- if (!diff.HasNameOnly())
- editedPlugins.push_back(diff);
+ long ret = editor.ShowModal();
+ const std::list edits = editor.GetEditedPlugins();
+
+ if (ret != wxID_APPLY) {
+ applyLoadOrder = false;
+ break;
+ }
+ else if (edits.empty()) {
+ applyLoadOrder = true;
+ break;
}
- plugins.swap(newPluginsList);
+ else {
+ //Recreate progress dialog.
+ progDia = new wxProgressDialog(translate("BOSS: Working..."), translate("BOSS working..."), 1000, this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_ELAPSED_TIME);
- if (!editedPlugins.empty()) {
+ //User accepted edits, now apply them, then loop.
+ //Apply edits to the graph vertices.
+ boss::vertex_it vit, vitend;
+ for (boost::tie(vit, vitend) = boost::vertices(graph); vit != vitend; ++vit) {
+ std::list::const_iterator pos = std::find(edits.begin(), edits.end(), graph[*vit]);
+ if (pos != edits.end()) {
+ BOOST_LOG_TRIVIAL(trace) << "Merging edits down to plugin list data.";
+ graph[*vit].MergeMetadata(*pos);
+ }
+ }
+
+ //Clear all existing edges from the graph.
+ std::pair edge_range = boost::edges(graph);
+ for (edge_it it = edge_range.first; it != edge_range.second; ++it) {
+ boost::remove_edge(*it, graph);
+ }
+
+ //Merge edits down to the userlist entries.
BOOST_LOG_TRIVIAL(trace) << "Merging down edits to the userlist.";
- for (list::const_iterator it=editedPlugins.begin(),endit=editedPlugins.end(); it != endit; ++it) {
+ for (list::const_iterator it = edits.begin(), endit = edits.end(); it != endit; ++it) {
list::iterator jt = find(ulist_plugins.begin(), ulist_plugins.end(), *it);
if (jt != ulist_plugins.end()) {
jt->MergeMetadata(*it);
- } else {
+ }
+ else {
ulist_plugins.push_back(*it);
}
}
- BOOST_LOG_TRIVIAL(trace) << "Checking that any pre-existing 'load after' entries in the userlist are still valid.";
- for (list::iterator it=ulist_plugins.begin(),endit=ulist_plugins.end(); it != endit; ++it) {
- //Double-check that the plugin is still loading after any other plugins already in its "load after" metadata.
- list::iterator mainPos = find(plugins.begin(), plugins.end(), *it);
-
- if (mainPos == plugins.end())
- continue;
-
- size_t mainDist = distance(plugins.begin(), mainPos);
-
- set loadAfter = it->LoadAfter();
- set::iterator jt = loadAfter.begin();
- while (jt != loadAfter.end()) {
- list::iterator filePos = find(plugins.begin(), plugins.end(), *jt);
-
- size_t fileDist = distance(plugins.begin(), filePos);
-
- if (fileDist > mainDist)
- loadAfter.erase(jt++);
- else
- ++jt;
- }
- it->LoadAfter(loadAfter);
- }
-
//Save edits to userlist.
BOOST_LOG_TRIVIAL(debug) << "Saving edited userlist.";
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
- << YAML::Key << "plugins" << YAML::Value << ulist_plugins
- << YAML::EndMap;
+ << YAML::Key << "plugins" << YAML::Value << ulist_plugins
+ << YAML::EndMap;
boss::ofstream uout(_game.UserlistPath());
uout << yout.c_str();
uout.close();
- }
- //Now set load order.
+ //Now loop.
+ }
+ } while (true);
+
+ if (applyLoadOrder) {
+ //User doesn't want to make any changes. Just go straight to applying the load order.
BOOST_LOG_TRIVIAL(debug) << "Setting load order.";
try {
_game.SetLoadOrder(plugins);
- } catch (boss::error& e) {
+ }
+ catch (boss::error& e) {
BOOST_LOG_TRIVIAL(error) << "Failed to set the load order. Details: " << e.what();
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Failed to set the load order. Details: %1%")) % e.what()).str()));
}
- } else {
+ BOOST_LOG_TRIVIAL(trace) << "Load order set:";
+ for (list::iterator it = plugins.begin(), endIt = plugins.end(); it != endIt; ++it) {
+ BOOST_LOG_TRIVIAL(trace) << '\t' << it->Name();
+ }
+ }
+ else {
+ //User decided to cancel sorting. Just go straight to displaying the report.
BOOST_LOG_TRIVIAL(info) << "The load order calculated was not applied as sorting was canceled.";
messages.push_back(boss::Message(boss::g_message_warn, loc::translate("The load order displayed in the Details tab was not applied as sorting was canceled.")));
}
- BOOST_LOG_TRIVIAL(trace) << "Load order set:";
- for (list::iterator it=plugins.begin(), endIt = plugins.end(); it != endIt; ++it) {
- BOOST_LOG_TRIVIAL(trace) << '\t' << it->Name();
- }
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Failed to calculate the load order. Details: " << e.what();
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Failed to calculate the load order. Details: %1%")) % e.what()).str()));
@@ -772,6 +894,10 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
progDia = NULL;
}
+ ///////////////////////////////////////////////////////
+ // Build & Display Report
+ ///////////////////////////////////////////////////////
+
//Read the details section of the previous report, if it exists.
string oldDetails;
if (fs::exists(_game.ReportPath().string())) {
@@ -835,7 +961,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
void Launcher::OnEditMetadata(wxCommandEvent& event) {
//Should probably check for masterlist updates before opening metadata editor.
- vector installed, mlist_plugins, ulist_plugins;
+ list installed, mlist_plugins, ulist_plugins;
wxProgressDialog *progDia = new wxProgressDialog(translate("BOSS: Working..."),translate("BOSS working..."), 1000, this, wxPD_APP_MODAL|wxPD_AUTO_HIDE|wxPD_ELAPSED_TIME);
@@ -866,7 +992,7 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
this);
}
if (mlist["plugins"])
- mlist_plugins = mlist["plugins"].as< vector >();
+ mlist_plugins = mlist["plugins"].as< list >();
}
progDia->Pulse();
@@ -888,26 +1014,25 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
this);
}
if (ulist["plugins"])
- ulist_plugins = ulist["plugins"].as< vector >();
+ ulist_plugins = ulist["plugins"].as< list >();
}
progDia->Pulse();
//Merge the masterlist down into the installed mods list.
BOOST_LOG_TRIVIAL(debug) << "Merging the masterlist down into the installed mods list.";
- for (vector::const_iterator it=mlist_plugins.begin(), endit=mlist_plugins.end(); it != endit; ++it) {
- vector::iterator pos = find(installed.begin(), installed.end(), *it);
+ for (list::const_iterator it = mlist_plugins.begin(), endit = mlist_plugins.end(); it != endit; ++it) {
+ list::iterator pos = find(installed.begin(), installed.end(), *it);
if (pos != installed.end())
pos->MergeMetadata(*it);
}
-
progDia->Pulse();
//Add empty entries for any userlist entries that aren't installed.
- BOOST_LOG_TRIVIAL(debug) << "Padding the userlist to match the plugins in the installed mods list.";
- for (vector::const_iterator it=ulist_plugins.begin(), endit=ulist_plugins.end(); it != endit; ++it) {
+ BOOST_LOG_TRIVIAL(debug) << "Padding the installed mods list to match the plugins in the userlist.";
+ for (list::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()));
}
@@ -916,7 +1041,7 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
//Sort into alphabetical order.
BOOST_LOG_TRIVIAL(debug) << "Sorting plugin list into alphabetical order.";
- std::sort(installed.begin(), installed.end(), boss::alpha_sort);
+ installed.sort(boss::alpha_sort);
progDia->Pulse();
@@ -937,19 +1062,6 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) {
BOOST_LOG_TRIVIAL(debug) << "Editor window opened.";
}
-void Launcher::OnViewLastReport(wxCommandEvent& event) {
- if (_settings["View Report Externally"] && _settings["View Report Externally"].as()) {
- BOOST_LOG_TRIVIAL(debug) << "Opening report in external application...";
- wxLaunchDefaultBrowser(FromUTF8(ToFileURL(_game.ReportPath().string())));
- } else {
- //Create viewer window.
- BOOST_LOG_TRIVIAL(debug) << "Opening viewer window...";
- Viewer *viewer = new Viewer(this, translate("BOSS: Report Viewer"), FromUTF8(ToFileURL(_game.ReportPath().string())));
- viewer->Show();
- }
- BOOST_LOG_TRIVIAL(debug) << "Report displayed.";
-}
-
void Launcher::OnRedatePlugins(wxCommandEvent& event) {
wxMessageDialog * dia = new wxMessageDialog(this, translate("This feature is provided so that modders using the Creation Kit may set the load order it uses. A side-effect is that any subscribed Steam Workshop mods will be re-downloaded by Steam. Do you wish to continue?"), translate("BOSS: Warning"), wxYES_NO|wxCANCEL|wxICON_EXCLAMATION);
@@ -1011,280 +1123,3 @@ void Launcher::OnRedatePlugins(wxCommandEvent& event) {
this);
}
}
-
-void Launcher::OnOpenSettings(wxCommandEvent& event) {
- BOOST_LOG_TRIVIAL(debug) << "Opening settings window...";
- SettingsFrame *settings = new SettingsFrame(this, translate("BOSS: Settings"), _settings, _games);
- settings->ShowModal();
- BOOST_LOG_TRIVIAL(debug) << "Settings window opened.";
-}
-
-void Launcher::OnGameChange(wxCommandEvent& event) {
- BOOST_LOG_TRIVIAL(debug) << "Changing current game...";
- _game = _games[event.GetId() - MENU_LowestDynamicGameID];
- try {
- _game.Init(); //In case it hasn't already been done.
- *find(_games.begin(), _games.end(), _game) = _game; //Sync changes.
- BOOST_LOG_TRIVIAL(debug) << "New game is " << _game.Name();
- } catch (boss::error& e) {
- BOOST_LOG_TRIVIAL(error) << "Game-specific settings could not be initialised." << e.what();
- wxMessageBox(
- FromUTF8(format(loc::translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- NULL);
- }
- SetTitle(FromUTF8("BOSS - " + _game.Name()));
- if (_game.Id() == boss::g_game_tes5)
- RedatePluginsItem->Enable(true);
- else
- RedatePluginsItem->Enable(false);
-}
-
-void Launcher::OnHelp(wxCommandEvent& event) {
- //Look for file.
- BOOST_LOG_TRIVIAL(debug) << "Opening readme at: " << g_path_readme;
- if (fs::exists(g_path_readme)) {
- wxLaunchDefaultBrowser(FromUTF8(ToFileURL(g_path_readme.string())));
- } else { //No readme exists, show a pop-up message saying so.
- BOOST_LOG_TRIVIAL(error) << "File \"" << g_path_readme.string() << "\" could not be found.";
- wxMessageBox(
- FromUTF8(format(loc::translate("Error: \"%1%\" cannot be found.")) % g_path_readme.string()),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- this);
- }
-}
-
-void Launcher::OnOpenDebugLog(wxCommandEvent& event) {
- //Look for file.
- BOOST_LOG_TRIVIAL(debug) << "Opening debug log at: " << g_path_log;
- if (fs::exists(g_path_log)) {
- wxLaunchDefaultApplication(FromUTF8(g_path_log.string()));
- } else { //No log exists, show a pop-up message saying so.
- BOOST_LOG_TRIVIAL(error) << "File \"" << g_path_log.string() << "\" could not be found.";
- wxMessageBox(
- FromUTF8(format(loc::translate("Error: \"%1%\" cannot be found.")) % g_path_log.string()),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- this);
- }
-}
-
-void Launcher::OnAbout(wxCommandEvent& event) {
- BOOST_LOG_TRIVIAL(debug) << "Opening About dialog.";
- wxAboutDialogInfo aboutInfo;
- aboutInfo.SetName("BOSS");
- aboutInfo.SetVersion(IntToString(g_version_major)+"."+IntToString(g_version_minor)+"."+IntToString(g_version_patch));
- aboutInfo.SetDescription(translate("Load order optimisation for Oblivion, Skyrim, Fallout 3 and Fallout: New Vegas."));
- aboutInfo.SetCopyright("Copyright (C) 2009-2014 BOSS Development Team.");
- aboutInfo.SetWebSite("http://boss-developers.github.io");
- aboutInfo.SetLicence("This program is free software: you can redistribute it and/or modify\n"
- "it under the terms of the GNU General Public License as published by\n"
- "the Free Software Foundation, either version 3 of the License, or\n"
- "(at your option) any later version.\n"
- "\n"
- "This program is distributed in the hope that it will be useful,\n"
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
- "GNU General Public License for more details.\n"
- "\n"
- "You should have received a copy of the GNU General Public License\n"
- "along with this program. If not, see .");
- aboutInfo.SetIcon(wxIconLocation("BOSS.exe"));
- wxAboutBox(aboutInfo);
-}
-
-LoadOrderPreview::LoadOrderPreview(wxWindow *parent, const wxString title, const std::list& plugins, const boss::Game& game) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), _plugins(plugins), _game(game) {
-
- //Init controls.
- _loadOrder = new wxListView(this, LIST_LoadOrder, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
-
- _moveUp = new wxButton(this, BUTTON_MoveUp, translate("Up"));
- _moveDown = new wxButton(this, BUTTON_MoveDown, translate("Down"));
-
- //Populate list.
- _loadOrder->AppendColumn(translate("Load Order"));
- size_t i=0;
- for (list::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; ++it, ++i) {
- _loadOrder->InsertItem(i, FromUTF8(it->Name()));
- if (it->FormIDs().empty()) {
- _loadOrder->SetItemTextColour(i, wxColour(122, 122, 122));
- }
- else if (it->LoadsBSA(_game)) {
- _loadOrder->SetItemTextColour(i, wxColour(0, 142, 219));
- }
- }
- _loadOrder->SetColumnWidth(0, wxLIST_AUTOSIZE);
-
- //Set up event handling.
- Bind(wxEVT_BUTTON, &LoadOrderPreview::OnMoveUp, this, BUTTON_MoveUp);
- Bind(wxEVT_BUTTON, &LoadOrderPreview::OnMoveDown, this, BUTTON_MoveDown);
- Bind(wxEVT_LIST_ITEM_SELECTED, &LoadOrderPreview::OnPluginSelect, this, LIST_LoadOrder);
-
- //Set up layout.
- wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
-
- bigBox->Add(_loadOrder, 1, wxEXPAND|wxALL, 15);
-
- wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);
- hbox->Add(_moveUp, 0, wxRIGHT, 5);
- hbox->Add(_moveDown, 0, wxLEFT, 5);
- bigBox->Add(hbox, 0, wxALIGN_RIGHT|wxBOTTOM|wxRIGHT, 15);
-
- bigBox->Add(new wxStaticText(this, wxID_ANY, translate("Please submit any alterations made for reasons other than user preference \nto the BOSS team so that they may include the changes in the masterlist.")), 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 15);
-
- //Need to add 'OK' and 'Cancel' buttons.
- wxSizer * sizer = CreateSeparatedButtonSizer(wxOK|wxCANCEL);
-
- //Now add TabHolder and OK button to window sizer.
- if (sizer != NULL)
- bigBox->Add(sizer, 0, wxEXPAND|wxLEFT|wxBOTTOM|wxRIGHT, 15);
-
- //Set initial up/down button states.
- _moveUp->Enable(false);
- _moveDown->Enable(false);
-
- //Now set the layout and sizes.
- SetBackgroundColour(wxColour(255,255,255));
- SetIcon(wxIconLocation("BOSS.exe"));
- SetSizerAndFit(bigBox);
-}
-
-void LoadOrderPreview::OnPluginSelect(wxListEvent& event) {
- _moveUp->Enable(true);
- _moveDown->Enable(true);
-}
-
-void LoadOrderPreview::OnMoveUp(wxCommandEvent& event) {
- BOOST_LOG_TRIVIAL(debug) << "Moving plugin(s) up the load order.";
- long selected = _loadOrder->GetFirstSelected();
-
- if (selected == 0)
- return;
-
- while (selected != -1) {
- wxString selectedText = _loadOrder->GetItemText(selected);
- wxString aboveText = _loadOrder->GetItemText(selected - 1);
-
- BOOST_LOG_TRIVIAL(trace) << "Moving plugin \"" << string(selectedText.ToUTF8());
-
- //Check that move is OK.
- list::const_iterator selectedPlugin = find(_plugins.begin(), _plugins.end(), boss::Plugin(string(selectedText.ToUTF8())));
- list::const_iterator abovePlugin = find(_plugins.begin(), _plugins.end(), boss::Plugin(string(aboveText.ToUTF8())));
-
- if (selectedPlugin != _plugins.end() && abovePlugin != _plugins.end()) {
- if (selectedPlugin->MustLoadAfter(*abovePlugin)) {
- BOOST_LOG_TRIVIAL(error) << "Cannot load \"" << selectedPlugin->Name() << "\" before \"" << abovePlugin->Name() << "\".";
- wxMessageBox(
- FromUTF8(format(loc::translate("Error: Cannot load \"%1%\" before \"%2%\".")) % selectedPlugin->Name() % abovePlugin->Name()),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- NULL);
- selected = _loadOrder->GetNextSelected(selected);
- continue;
- }
- }
-
- _loadOrder->SetItemText(selected, aboveText);
- _loadOrder->SetItemText(selected - 1, selectedText);
-
- _movedPlugins.insert(string(selectedText.ToUTF8()));
-
- _loadOrder->Select(selected, false);
- _loadOrder->Select(selected - 1, true);
-
- selected = _loadOrder->GetNextSelected(selected);
- }
- BOOST_LOG_TRIVIAL(debug) << "Plugin(s) moved up.";
-}
-
-void LoadOrderPreview::OnMoveDown(wxCommandEvent& event) {
- BOOST_LOG_TRIVIAL(debug) << "Moving plugin(s) down the load order.";
- long i=_loadOrder->GetItemCount() - 1;
-
- if (_loadOrder->IsSelected(i))
- return;
- --i;
-
- for (i; i > -1; --i) {
- if (_loadOrder->IsSelected(i)) {
- wxString selectedText = _loadOrder->GetItemText(i);
- wxString belowText = _loadOrder->GetItemText(i + 1);
-
- BOOST_LOG_TRIVIAL(trace) << "Moving plugin \"" << string(selectedText.ToUTF8());
-
- //Check that move is OK.
- list::const_iterator selectedPlugin = find(_plugins.begin(), _plugins.end(), boss::Plugin(string(selectedText.ToUTF8())));
- list::const_iterator belowPlugin = find(_plugins.begin(), _plugins.end(), boss::Plugin(string(belowText.ToUTF8())));
-
- if (selectedPlugin != _plugins.end() && belowPlugin != _plugins.end()) {
- if (belowPlugin->MustLoadAfter(*selectedPlugin)) {
- BOOST_LOG_TRIVIAL(error) << "Cannot load \"" << belowPlugin->Name() << "\" before \"" << selectedPlugin->Name() << "\".";
- wxMessageBox(
- FromUTF8(format(loc::translate("Error: Cannot load \"%1%\" before \"%2%\".")) % belowPlugin->Name() % selectedPlugin->Name()),
- translate("BOSS: Error"),
- wxOK | wxICON_ERROR,
- NULL);
- continue;
- }
- }
-
- _loadOrder->SetItemText(i, belowText);
- _loadOrder->SetItemText(i + 1, selectedText);
-
- _movedPlugins.insert(string(selectedText.ToUTF8()));
-
- _loadOrder->Select(i, false);
- _loadOrder->Select(i + 1, true);
- }
- }
- BOOST_LOG_TRIVIAL(debug) << "Plugin(s) moved down.";
-}
-
-std::list LoadOrderPreview::GetLoadOrder() const {
- BOOST_LOG_TRIVIAL(debug) << "Getting full load order from preview window.";
- list plugins;
- bool wasMovedUp = false;
- for (size_t i=0,max=_loadOrder->GetItemCount(); i < max; ++i) {
- string name = string(_loadOrder->GetItemText(i).ToUTF8());
-
- list::const_iterator it = find(_plugins.begin(), _plugins.end(), boss::Plugin(name));
-
- if (it == _plugins.end())
- continue;
-
- plugins.push_back(*it);
-
- if (wasMovedUp) {
- BOOST_LOG_TRIVIAL(trace) << "The previous plugin was moved up in the load order, so adding it to the 'load after' set for the current plugin.";
- list::const_iterator jt = ----plugins.end();
- set loadAfter = plugins.back().LoadAfter();
- loadAfter.insert(File(jt->Name()));
- plugins.back().LoadAfter(loadAfter);
- wasMovedUp = false;
- }
-
- if (_movedPlugins.find(name) != _movedPlugins.end()) {
- BOOST_LOG_TRIVIAL(trace) << "The current plugin was moved - checking if it was moved up or down.";
- //Check if this plugin has been moved earlier or later by comparing distances in the original list and the new one.
- size_t newDist = plugins.size() - 1;
- size_t oldDist = distance(_plugins.begin(), it);
-
- if (newDist > oldDist) {
- BOOST_LOG_TRIVIAL(trace) << "The current plugin was moved down, add the preceding plugin to the current plugin's 'load after' set.";
- //Record the preceding plugin in this plugin's "load after" set.
- list::const_iterator jt = ----plugins.end();
- set loadAfter = plugins.back().LoadAfter();
- loadAfter.insert(File(jt->Name()));
- plugins.back().LoadAfter(loadAfter);
- } else {
- BOOST_LOG_TRIVIAL(trace) << "The current plugin was moved up.";
- //Record this plugin in the following plugin's "load after" set.
- wasMovedUp = true;
- }
- }
- }
-
- return plugins;
-}
diff --git a/src/gui/main.h b/src/gui/main.h
index f682cdd3..ce6d59a7 100644
--- a/src/gui/main.h
+++ b/src/gui/main.h
@@ -70,23 +70,4 @@ private:
std::vector& _games;
};
-class LoadOrderPreview : public wxDialog {
-public:
- LoadOrderPreview(wxWindow *parent, const wxString title, const std::list& plugins, const boss::Game& game);
-
- void OnPluginSelect(wxListEvent& event);
- void OnMoveUp(wxCommandEvent& event);
- void OnMoveDown(wxCommandEvent& event);
-
- std::list GetLoadOrder() const;
-private:
- wxListView * _loadOrder;
- wxButton * _moveUp;
- wxButton * _moveDown;
-
- const std::list _plugins;
- std::set _movedPlugins;
- const boss::Game& _game;
-};
-
#endif
diff --git a/src/gui/misc.cpp b/src/gui/misc.cpp
new file mode 100644
index 00000000..850a0725
--- /dev/null
+++ b/src/gui/misc.cpp
@@ -0,0 +1,480 @@
+/* BOSS
+
+A plugin load order optimiser for games that use the esp/esm plugin system.
+
+Copyright (C) 2013-2014 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
+.
+*/
+
+#include "misc.h"
+#include "../backend/helpers.h"
+
+#include
+
+
+
+using namespace std;
+
+MessageList::MessageList(wxWindow * parent, wxWindowID id, const unsigned int language) : wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL | wxLC_SINGLE_SEL), _language(language) {
+ InsertColumn(0, translate("Type"));
+ InsertColumn(1, translate("Content"));
+ InsertColumn(2, translate("Condition"));
+ InsertColumn(3, translate("Language"));
+
+ Bind(wxEVT_LIST_DELETE_ITEM, &MessageList::OnDeleteItem, this);
+
+ SetItemCount(0);
+}
+
+std::vector MessageList::GetItems() const {
+ return _messages;
+}
+
+void MessageList::SetItems(const std::vector& messages) {
+ _messages = messages;
+ SetItemCount(_messages.size());
+ RefreshItems(0, _messages.size() - 1);
+}
+
+boss::Message MessageList::GetItem(long item) const {
+ return _messages[item];
+}
+
+void MessageList::SetItem(long item, const boss::Message& message) {
+ _messages[item] = message;
+ RefreshItem(item);
+}
+
+void MessageList::AppendItem(const boss::Message& message) {
+ _messages.push_back(message);
+ SetItemCount(_messages.size());
+ RefreshItem(_messages.size() - 1);
+}
+
+void MessageList::OnDeleteItem(wxListEvent& event) {
+ _messages.erase(_messages.begin() + event.GetIndex());
+ SetItemCount(_messages.size());
+ RefreshItems(event.GetIndex(), _messages.size() - 1);
+}
+
+wxString MessageList::OnGetItemText(long item, long column) const {
+ if (column < 0 || column > 3 || item < 0 || item > _messages.size() - 1)
+ return wxString();
+
+ if (column == 0) {
+ if (_messages[item].Type() == boss::g_message_say)
+ return Type[0];
+ else if (_messages[item].Type() == boss::g_message_warn)
+ return Type[1];
+ else
+ return Type[2];
+ }
+ else if (column == 1) {
+ return FromUTF8(_messages[item].ChooseContent(_language).Str());
+ }
+ else if (column == 2) {
+ return FromUTF8(_messages[item].Condition());
+ }
+ else {
+ return FromUTF8(boss::Language(_messages[item].ChooseContent(_language).Language()).Name());
+ }
+}
+
+
+FileEditDialog::FileEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
+
+ _name = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
+ _display = new wxTextCtrl(this, wxID_ANY);
+ _condition = new wxTextCtrl(this, wxID_ANY);
+
+ wxSizerFlags leftItem(0);
+ leftItem.Left();
+
+ wxSizerFlags rightItem(1);
+ rightItem.Right().Expand();
+
+ wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
+
+ wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
+ GridSizer->AddGrowableCol(1, 1);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Filename (required):")), leftItem);
+ GridSizer->Add(_name, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Displayed Name:")), leftItem);
+ GridSizer->Add(_display, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
+ GridSizer->Add(_condition, rightItem);
+
+ bigBox->Add(GridSizer, 0, wxEXPAND | wxALL, 15);
+
+ bigBox->AddSpacer(10);
+ bigBox->AddStretchSpacer(1);
+
+ //Need to add 'OK' and 'Cancel' buttons.
+ wxSizer * sizer = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
+ if (sizer != NULL)
+ bigBox->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 15);
+
+ SetBackgroundColour(wxColour(255, 255, 255));
+ SetIcon(wxIconLocation("BOSS.exe"));
+ SetSizerAndFit(bigBox);
+}
+
+void FileEditDialog::SetValues(const wxString& name, const wxString& display, const wxString& condition) {
+
+ _name->SetValue(name);
+ _display->SetValue(display);
+ _condition->SetValue(condition);
+}
+
+wxString FileEditDialog::GetName() const {
+ return _name->GetValue();
+}
+
+wxString FileEditDialog::GetDisplayName() const {
+ return _display->GetValue();
+}
+
+wxString FileEditDialog::GetCondition() const {
+ return _condition->GetValue();
+}
+
+MessageEditDialog::MessageEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
+
+ wxArrayString languages;
+ vector langs = boss::Language::Names();
+ for (size_t i = 0; i < langs.size(); i++) {
+ languages.Add(FromUTF8(langs[i]));
+ }
+
+ //Initialise controls.
+ _type = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, Type);
+ _language = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, languages);
+
+ _condition = new wxTextCtrl(this, wxID_ANY);
+ _str = new wxTextCtrl(this, wxID_ANY);
+
+ _content = new wxListView(this, LIST_MessageContent, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
+
+ addBtn = new wxButton(this, BUTTON_AddContent, translate("Add Content"));
+ editBtn = new wxButton(this, BUTTON_EditContent, translate("Edit Content"));
+ removeBtn = new wxButton(this, BUTTON_RemoveContent, translate("Remove Content"));
+
+ _content->InsertColumn(0, translate("Language"));
+ _content->InsertColumn(1, translate("String"));
+
+ //Set up event handling.
+ Bind(wxEVT_BUTTON, &MessageEditDialog::OnAdd, this, BUTTON_AddContent);
+ Bind(wxEVT_BUTTON, &MessageEditDialog::OnEdit, this, BUTTON_EditContent);
+ Bind(wxEVT_BUTTON, &MessageEditDialog::OnRemove, this, BUTTON_RemoveContent);
+ Bind(wxEVT_LIST_ITEM_SELECTED, &MessageEditDialog::OnSelect, this, LIST_MessageContent);
+
+ wxSizerFlags leftItem(0);
+ leftItem.Left();
+
+ wxSizerFlags rightItem(1);
+ rightItem.Right();
+
+ wxSizerFlags wholeItem(0);
+ wholeItem.Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM, 15);
+
+ wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
+
+ wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
+ GridSizer->AddGrowableCol(1, 1);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Type:")), leftItem);
+ GridSizer->Add(_type, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
+ GridSizer->Add(_condition, rightItem);
+
+ bigBox->AddSpacer(15);
+
+ bigBox->Add(GridSizer, wholeItem);
+
+ bigBox->Add(_content, wholeItem);
+
+ wxFlexGridSizer * GridSizer2 = new wxFlexGridSizer(2, 5, 5);
+ GridSizer2->AddGrowableCol(1, 1);
+
+ GridSizer2->Add(new wxStaticText(this, wxID_ANY, translate("Language:")), leftItem);
+ GridSizer2->Add(_language, rightItem);
+
+ GridSizer2->Add(new wxStaticText(this, wxID_ANY, translate("Content:")), leftItem);
+ GridSizer2->Add(_str, rightItem);
+
+ bigBox->Add(GridSizer2, wholeItem);
+
+ wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);
+ hbox->Add(addBtn, 0, wxRIGHT, 5);
+ hbox->Add(editBtn, 0, wxLEFT | wxRIGHT, 5);
+ hbox->Add(removeBtn, 0, wxLEFT, 5);
+ bigBox->Add(hbox, 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 15);
+
+ bigBox->AddSpacer(10);
+ bigBox->AddStretchSpacer(1);
+
+ //Need to add 'OK' and 'Cancel' buttons.
+ wxSizer * sizer = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
+ if (sizer != NULL)
+ bigBox->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 15);
+
+ //Set defaults.
+ _type->SetSelection(0);
+ _language->SetSelection(0);
+ editBtn->Enable(false);
+ removeBtn->Enable(false);
+
+ SetBackgroundColour(wxColour(255, 255, 255));
+ SetIcon(wxIconLocation("BOSS.exe"));
+ SetSizerAndFit(bigBox);
+}
+
+void MessageEditDialog::SetMessage(const boss::Message& message) {
+
+ if (message.Type() == boss::g_message_say)
+ _type->SetSelection(0);
+ else if (message.Type() == boss::g_message_warn)
+ _type->SetSelection(1);
+ else
+ _type->SetSelection(2);
+
+ _condition->SetValue(FromUTF8(message.Condition()));
+
+ vector contents = message.Content();
+ for (size_t i = 0, max = contents.size(); i < max; ++i) {
+ _content->InsertItem(i, FromUTF8(boss::Language(contents[i].Language()).Name()));
+ _content->SetItem(i, 1, FromUTF8(contents[i].Str()));
+ }
+}
+
+boss::Message MessageEditDialog::GetMessage() const {
+
+ unsigned int type;
+ string condition;
+ if (_type->GetSelection() == 0)
+ type = boss::g_message_say;
+ else if (_type->GetSelection() == 1)
+ type = boss::g_message_warn;
+ else
+ type = boss::g_message_error;
+
+ condition = string(_condition->GetValue().ToUTF8());
+
+ vector contents;
+ for (size_t i = 0, max = _content->GetItemCount(); i < max; ++i) {
+
+ string str = string(_content->GetItemText(i, 1).ToUTF8());
+ unsigned int lang = boss::Language(string(_content->GetItemText(i, 0).ToUTF8())).Code();
+
+ contents.push_back(boss::MessageContent(str, lang));
+ }
+
+ return boss::Message(type, contents, condition);
+}
+
+void MessageEditDialog::OnSelect(wxListEvent& event) {
+ _language->SetSelection(boss::Language(string(_content->GetItemText(event.GetIndex(), 0).ToUTF8())).Code());
+ _str->SetValue(_content->GetItemText(event.GetIndex(), 1));
+ editBtn->Enable(true);
+ removeBtn->Enable(true);
+}
+
+void MessageEditDialog::OnAdd(wxCommandEvent& event) {
+ long i = _content->GetItemCount();
+ _content->InsertItem(i, FromUTF8(boss::Language(_language->GetSelection()).Name()));
+ _content->SetItem(i, 1, _str->GetValue());
+}
+
+void MessageEditDialog::OnEdit(wxCommandEvent& event) {
+ if (_content->GetFirstSelected() == -1) {
+ BOOST_LOG_TRIVIAL(error) << "Attempting to edit message content, but no content row selected.";
+ wxMessageBox(
+ translate("Error: No content row selected."),
+ translate("BOSS: Error"),
+ wxOK | wxICON_ERROR,
+ NULL);
+ return;
+ }
+ long i = _content->GetFirstSelected();
+ _content->SetItem(i, 0, FromUTF8(boss::Language(_language->GetSelection()).Name()));
+ _content->SetItem(i, 1, _str->GetValue());
+}
+
+void MessageEditDialog::OnRemove(wxCommandEvent& event) {
+ if (_content->GetFirstSelected() == -1) {
+ BOOST_LOG_TRIVIAL(error) << "Attempting to remove message content, but no content row selected.";
+ wxMessageBox(
+ translate("Error: No content row selected."),
+ translate("BOSS: Error"),
+ wxOK | wxICON_ERROR,
+ NULL);
+ return;
+ }
+ _content->DeleteItem(_content->GetFirstSelected());
+ editBtn->Enable(false);
+ removeBtn->Enable(false);
+}
+
+TagEditDialog::TagEditDialog(wxWindow *parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
+
+ //Initialise controls.
+ _state = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 2, State);
+
+ _name = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
+ _condition = new wxTextCtrl(this, wxID_ANY);
+
+ wxSizerFlags leftItem(0);
+ leftItem.Left();
+
+ wxSizerFlags rightItem(1);
+ rightItem.Right().Expand();
+
+ wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
+
+ wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
+ GridSizer->AddGrowableCol(1, 1);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Add/Remove:")), leftItem);
+ GridSizer->Add(_state, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Name (required):")), leftItem);
+ GridSizer->Add(_name, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Condition:")), leftItem);
+ GridSizer->Add(_condition, rightItem);
+
+ bigBox->Add(GridSizer, 0, wxEXPAND | wxALL, 15);
+
+ bigBox->AddSpacer(10);
+ bigBox->AddStretchSpacer(1);
+
+ //Need to add 'OK' and 'Cancel' buttons.
+ wxSizer * sizer = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
+ if (sizer != NULL)
+ bigBox->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 15);
+
+ //Set defaults.
+ _state->SetSelection(0);
+
+ SetBackgroundColour(wxColour(255, 255, 255));
+ SetIcon(wxIconLocation("BOSS.exe"));
+ SetSizerAndFit(bigBox);
+}
+
+void TagEditDialog::SetValues(int state, const wxString& name, const wxString& condition) {
+
+ _state->SetSelection(state);
+ _name->SetValue(name);
+ _condition->SetValue(condition);
+}
+
+wxString TagEditDialog::GetState() const {
+ return State[_state->GetSelection()];
+}
+
+wxString TagEditDialog::GetName() const {
+ return _name->GetValue();
+}
+
+wxString TagEditDialog::GetCondition() const {
+ return _condition->GetValue();
+}
+
+DirtInfoEditDialog::DirtInfoEditDialog(wxWindow * parent, const wxString& title) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
+ //Initialise controls.
+ wxTextValidator val(wxFILTER_EMPTY | wxFILTER_INCLUDE_CHAR_LIST);
+ val.SetCharIncludes("0123456789ABCDEFabcdef");
+
+ _itm = new wxSpinCtrl(this, wxID_ANY, "0");
+ _udr = new wxSpinCtrl(this, wxID_ANY, "0");
+ _nav = new wxSpinCtrl(this, wxID_ANY, "0");
+ _crc = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, val);
+ _utility = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_EMPTY));
+
+ wxSizerFlags leftItem(0);
+ leftItem.Left();
+
+ wxSizerFlags rightItem(1);
+ rightItem.Right().Expand();
+
+ wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL);
+
+ wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5);
+ GridSizer->AddGrowableCol(1, 1);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("CRC (required):")), leftItem);
+ GridSizer->Add(_crc, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("ITM Count:")), leftItem);
+ GridSizer->Add(_itm, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("UDR Count:")), leftItem);
+ GridSizer->Add(_udr, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Deleted Navmesh Count:")), leftItem);
+ GridSizer->Add(_nav, rightItem);
+
+ GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Cleaning Utility (required):")), leftItem);
+ GridSizer->Add(_utility, rightItem);
+
+ bigBox->Add(GridSizer, 0, wxEXPAND | wxALL, 15);
+
+ bigBox->AddSpacer(10);
+ bigBox->AddStretchSpacer(1);
+
+ //Need to add 'OK' and 'Cancel' buttons.
+ wxSizer * sizer = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
+ if (sizer != NULL)
+ bigBox->Add(sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 15);
+
+ SetBackgroundColour(wxColour(255, 255, 255));
+ SetIcon(wxIconLocation("BOSS.exe"));
+ SetSizerAndFit(bigBox);
+}
+
+void DirtInfoEditDialog::SetValues(const wxString& crc, unsigned int itm, unsigned int udr, unsigned int nav, const wxString& utility) {
+ _crc->SetValue(crc);
+ _itm->SetValue(itm);
+ _udr->SetValue(udr);
+ _nav->SetValue(nav);
+ _utility->SetValue(utility);
+}
+
+wxString DirtInfoEditDialog::GetCRC() const {
+ return _crc->GetValue();
+}
+
+unsigned int DirtInfoEditDialog::GetITMs() const {
+ return _itm->GetValue();
+}
+
+unsigned int DirtInfoEditDialog::GetUDRs() const {
+ return _udr->GetValue();
+}
+
+unsigned int DirtInfoEditDialog::GetDeletedNavmeshes() const {
+ return _nav->GetValue();
+}
+
+wxString DirtInfoEditDialog::GetUtility() const {
+ return _utility->GetValue();
+}
\ No newline at end of file
diff --git a/src/gui/misc.h b/src/gui/misc.h
new file mode 100644
index 00000000..ad537d24
--- /dev/null
+++ b/src/gui/misc.h
@@ -0,0 +1,132 @@
+/* BOSS
+
+A plugin load order optimiser for games that use the esp/esm plugin system.
+
+Copyright (C) 2013-2014 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
+.
+*/
+#ifndef __BOSS_GUI_MISC__
+#define __BOSS_GUI_MISC__
+
+#include "ids.h"
+#include "../backend/metadata.h"
+
+#include
+#include
+#include
+#include
+
+const wxString Type[3] = {
+ translate("Note"),
+ translate("Warning"),
+ translate("Error")
+};
+
+const wxString State[2] = {
+ translate("Add"),
+ translate("Remove")
+};
+
+class MessageList : public wxListView {
+public:
+ MessageList(wxWindow * parent, wxWindowID id, const unsigned int language);
+
+ void SetItems(const std::vector& messages);
+ std::vector GetItems() const;
+
+ boss::Message GetItem(long item) const;
+ void SetItem(long item, const boss::Message& message);
+ void AppendItem(const boss::Message& message);
+
+ void OnDeleteItem(wxListEvent& event);
+protected:
+ wxString OnGetItemText(long item, long column) const;
+
+private:
+ std::vector _messages;
+ const unsigned int _language;
+};
+
+class FileEditDialog : public wxDialog {
+public:
+ FileEditDialog(wxWindow *parent, const wxString& title);
+
+ void SetValues(const wxString& name, const wxString& display, const wxString& condition);
+ wxString GetName() const;
+ wxString GetDisplayName() const;
+ wxString GetCondition() const;
+private:
+ wxTextCtrl * _name;
+ wxTextCtrl * _display;
+ wxTextCtrl * _condition;
+};
+
+class MessageEditDialog : public wxDialog {
+public:
+ MessageEditDialog(wxWindow *parent, const wxString& title);
+
+ void SetMessage(const boss::Message& message);
+ boss::Message GetMessage() const;
+
+ void OnSelect(wxListEvent& event);
+ void OnAdd(wxCommandEvent& event);
+ void OnEdit(wxCommandEvent& event);
+ void OnRemove(wxCommandEvent& event);
+private:
+ wxButton * addBtn;
+ wxButton * editBtn;
+ wxButton * removeBtn;
+ wxChoice * _type;
+ wxChoice * _language;
+ wxListView * _content;
+ wxTextCtrl * _condition;
+ wxTextCtrl * _str;
+};
+
+class TagEditDialog : public wxDialog {
+public:
+ TagEditDialog(wxWindow *parent, const wxString& title);
+
+ void SetValues(int state, const wxString& name, const wxString& condition);
+ wxString GetState() const;
+ wxString GetName() const;
+ wxString GetCondition() const;
+private:
+ wxChoice * _state;
+ wxTextCtrl * _name;
+ wxTextCtrl * _condition;
+};
+
+class DirtInfoEditDialog : public wxDialog {
+public:
+ DirtInfoEditDialog(wxWindow * parent, const wxString& title);
+
+ void SetValues(const wxString& crc, unsigned int itm, unsigned int udr, unsigned int nav, const wxString& utility);
+ wxString GetCRC() const;
+ wxString GetUtility() const;
+ unsigned int GetITMs() const;
+ unsigned int GetUDRs() const;
+ unsigned int GetDeletedNavmeshes() const;
+private:
+ wxTextCtrl * _crc;
+ wxSpinCtrl * _itm;
+ wxSpinCtrl * _udr;
+ wxSpinCtrl * _nav;
+ wxTextCtrl * _utility;
+};
+#endif
\ No newline at end of file
|