From 0c1dea158b298b9ab5eadf30abe28bfd88068604 Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Thu, 9 May 2013 08:11:09 +0100 Subject: [PATCH] Forgot to add UI code to last commit, so all UI changes mentioned in its log are actually in this. Clicking on a plugin in the metadata editor now loads its values into the editor fields, with checks for edits and saving of edits when a new plugin is selected. Terminology is a bit confusing though, since edits aren't really 'saved' until the 'Apply Load Order' button is pressed. Maybe I should change that... --- src/gui/editor.cpp | 223 +++++++++++++++++++++++++++++++++++-------- src/gui/editor.h | 32 +++++-- src/gui/ids.h | 3 - src/gui/main.cpp | 120 ++++++++++++++++------- src/gui/main.h | 12 +-- src/gui/settings.cpp | 58 ++++++----- src/metadata.cpp | 77 ++++++++++++--- src/metadata.h | 8 ++ 8 files changed, 401 insertions(+), 132 deletions(-) diff --git a/src/gui/editor.cpp b/src/gui/editor.cpp index 4b0d45a9..391b210c 100644 --- a/src/gui/editor.cpp +++ b/src/gui/editor.cpp @@ -24,12 +24,20 @@ #include "editor.h" #include +#include + +BEGIN_EVENT_TABLE( Editor, wxFrame ) + EVT_LIST_ITEM_SELECTED( LIST_Plugins, Editor::OnPluginSelect ) + EVT_SPINCTRL( SPIN_Priority, Editor::OnPriorityChange ) +END_EVENT_TABLE() + +using namespace std; Editor::Editor(const wxString title, wxFrame *parent) : wxFrame(parent, wxID_ANY, title) { //Initialise child windows. filesBook = new wxNotebook(this, wxID_ANY); - wxNotebook * messagesBook = new wxNotebook(this, wxID_ANY); + messagesBook = new wxNotebook(this, wxID_ANY); wxPanel * reqsTab = new wxPanel(filesBook); wxPanel * incsTab = new wxPanel(filesBook); @@ -43,36 +51,36 @@ Editor::Editor(const wxString title, wxFrame *parent) : wxFrame(parent, wxID_ANY filesBook->AddPage(loadAfterTab, translate("Load After")); messagesBook->AddPage(messagesTab, translate("Messages"), true); - messagesBook->AddPage(tagsTab, translate("Bash Tags")); + messagesBook->AddPage(tagsTab, translate("Bash Tag Suggestions")); //Initialise controls. - pluginList = new wxListBox(this, LIST_Plugins); pluginText = new wxStaticText(this, wxID_ANY, ""); prioritySpin = new wxSpinCtrl(this, SPIN_Priority, "0"); + prioritySpin->SetRange(-10,10); enableUserEditsBox = new wxCheckBox(this, wxID_ANY, translate("Enable User Changes")); addFileBtn = new wxButton(this, BUTTON_AddFile, translate("Add File")); editFileBtn = new wxButton(this, BUTTON_EditFile, translate("Edit File")); removeFileBtn = new wxButton(this, BUTTON_RemoveFile, translate("Remove File")); - addMsgBtn = new wxButton(messagesTab, BUTTON_AddMessage, translate("Add Message")); - editMsgBtn = new wxButton(messagesTab, BUTTON_EditMessage, translate("Edit Message")); - removeMsgBtn = new wxButton(messagesTab, BUTTON_RemoveMessage, translate("Remove Message")); - addTagBtn = new wxButton(tagsTab, BUTTON_AddTag, translate("Add Tag")); - editTagBtn = new wxButton(tagsTab, BUTTON_EditTag, translate("Edit Tag")); - removeTagBtn = new wxButton(tagsTab, BUTTON_RemoveTag, translate("Remove Tag")); + addMsgBtn = new wxButton(this, BUTTON_AddMessage, translate("Add Message")); + editMsgBtn = new wxButton(this, BUTTON_EditMessage, translate("Edit Message")); + removeMsgBtn = new wxButton(this, BUTTON_RemoveMessage, translate("Remove Message")); saveEditsBtn = new wxButton(this, BUTTON_SaveEdits, translate("Save Edits")); undoEditsBtn = new wxButton(this, BUTTON_UndoEdits, translate("Undo Edits")); recalcBtn = new wxButton(this, BUTTON_Recalc, translate("Recalculate Load Order")); applyBtn = new wxButton(this, BUTTON_Apply, translate("Apply Load Order")); cancelBtn = new wxButton(this, BUTTON_Cancel, translate("Cancel")); - reqsList = new wxListCtrl(reqsTab, LIST_Reqs, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); - incsList = new wxListCtrl(incsTab, LIST_Incs, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); - loadAfterList = new wxListCtrl(loadAfterTab, LIST_LoadAfter, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); - messageList = new wxListCtrl(messagesTab, LIST_Messages, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); - tagsList = new wxListCtrl(tagsTab, LIST_BashTags, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); + pluginList = new wxListCtrl(this, LIST_Plugins, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); + reqsList = new wxListCtrl(reqsTab, LIST_Reqs, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); + incsList = new wxListCtrl(incsTab, LIST_Incs, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); + loadAfterList = new wxListCtrl(loadAfterTab, LIST_LoadAfter, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); + messageList = new wxListCtrl(messagesTab, LIST_Messages, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); + tagsList = new wxListCtrl(tagsTab, LIST_BashTags, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL); //Set up list columns. + pluginList->AppendColumn(translate("Plugins")); + reqsList->AppendColumn(translate("Filename")); reqsList->AppendColumn(translate("Display Name")); reqsList->AppendColumn(translate("Condition")); @@ -101,7 +109,7 @@ Editor::Editor(const wxString title, wxFrame *parent) : wxFrame(parent, wxID_ANY vbox1->Add(new wxStaticText(this, wxID_ANY, translate("Plugins")), 0, wxALL, 5); vbox1->Add(pluginList, 1, wxEXPAND|wxALL, 5); - bigBox->Add(vbox1, 0, wxEXPAND|wxALL, 5); + bigBox->Add(vbox1, 1, wxEXPAND|wxALL, 5); wxBoxSizer * mainBox = new wxBoxSizer(wxVERTICAL); @@ -135,48 +143,183 @@ Editor::Editor(const wxString title, wxFrame *parent) : wxFrame(parent, wxID_ANY mainBox->Add(hbox2); wxBoxSizer * tabBox4 = new wxBoxSizer(wxVERTICAL); - tabBox4->Add(messageList, 0, wxEXPAND|wxALL, 5); - - wxBoxSizer * hbox3 = new wxBoxSizer(wxHORIZONTAL); - hbox3->Add(addMsgBtn, 0, wxALL, 10); - hbox3->Add(editMsgBtn, 0, wxALL, 10); - hbox3->Add(removeMsgBtn, 0, wxALL, 10); - tabBox4->Add(hbox3); - + tabBox4->Add(messageList, 0, wxEXPAND); messagesTab->SetSizer(tabBox4); wxBoxSizer * tabBox5 = new wxBoxSizer(wxVERTICAL); - tabBox5->Add(tagsList, 0, wxEXPAND|wxALL, 5); - - wxBoxSizer * hbox4 = new wxBoxSizer(wxHORIZONTAL); - hbox4->Add(addTagBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); - hbox4->Add(editTagBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); - hbox4->Add(removeTagBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); - tabBox5->Add(hbox4); - + tabBox5->Add(tagsList, 0, wxEXPAND); tagsTab->SetSizer(tabBox5); mainBox->Add(messagesBook, 1, wxEXPAND); + wxBoxSizer * hbox3 = new wxBoxSizer(wxHORIZONTAL); + hbox3->Add(addMsgBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); + hbox3->Add(editMsgBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); + hbox3->Add(removeMsgBtn, 0, wxTOP|wxLEFT|wxRIGHT, 10); + mainBox->Add(hbox3); + mainBox->AddSpacer(20); wxBoxSizer * hbox5 = new wxBoxSizer(wxHORIZONTAL); hbox5->Add(enableUserEditsBox, 0, wxALL, 5); hbox5->Add(saveEditsBtn, 0, wxALL, 5); hbox5->Add(undoEditsBtn, 0, wxALL, 5); - mainBox->Add(hbox5); + mainBox->Add(hbox5, 0, wxBOTTOM, 20); - mainBox->AddSpacer(20); + sortingButtons = new wxBoxSizer(wxHORIZONTAL); + sortingButtons->Add(recalcBtn, 0, wxALL, 5); + sortingButtons->Add(applyBtn, 0, wxALL, 5); + sortingButtons->Add(cancelBtn, 0, wxALL, 5); + mainBox->Add(sortingButtons); - wxBoxSizer * hbox6 = new wxBoxSizer(wxHORIZONTAL); - hbox6->Add(recalcBtn, 0, wxALL, 5); - hbox6->Add(applyBtn, 0, wxALL, 5); - hbox6->Add(cancelBtn, 0, wxALL, 5); - mainBox->Add(hbox6); - - bigBox->Add(mainBox, 1, wxEXPAND|wxALL, 5); + bigBox->Add(mainBox, 2, wxEXPAND|wxALL, 5); SetBackgroundColour(wxColour(255,255,255)); SetSizerAndFit(bigBox); } + +void Editor::SetList(const std::vector& basePlugins, const std::vector& editedPlugins) { + _basePlugins = basePlugins; + _editedPlugins = editedPlugins; + + //Fill pluginList with the contents of basePlugins. + for (int i=0, max=_basePlugins.size(); i < max; ++i) { + pluginList->InsertItem(i, _basePlugins[i].Name()); + } + pluginList->SetColumnWidth(0, wxLIST_AUTOSIZE); + Layout(); +} + +void Editor::IsSorted(bool sorted) { + sortingButtons->ShowItems(sorted); + Layout(); +} + +void Editor::OnPluginSelect(wxListEvent& event) { + //Create Plugin object for selected plugin. + boss::Plugin plugin = boss::Plugin(string(pluginList->GetItemText(event.GetIndex()).ToUTF8())); + + //Check if the selected plugin is the same as the current plugin. + if (currentPlugin != plugin) { + //Check if there are edits made to the current plugin compared to its original. + if (IsCurrentPluginEdited()) { + //Ask if changes should be saved. + wxMessageDialog *dlg = new wxMessageDialog(this, + translate("The current edits are not saved. Do you want to save them?"), + translate("BOSS: Metadata Editor"), wxYES_NO); + + if (dlg->ShowModal() == wxID_YES) { + //Save changes. + boss::Plugin diff = currentPlugin.DiffMetadata(GetOriginal(currentPlugin, false)); + + vector::iterator it = std::find(_editedPlugins.begin(), _editedPlugins.end(), diff); + + if (it != _editedPlugins.end()) + *it = diff; + else + _editedPlugins.push_back(diff); + } + } + + plugin = GetOriginal(plugin, true); + + //Now fill editor fields with new plugin's info. + pluginText->SetLabelText(wxString(plugin.Name().c_str(), wxConvUTF8)); + + prioritySpin->SetValue(plugin.Priority()); + + enableUserEditsBox->SetValue(plugin.Enabled()); + + loadAfterList->DeleteAllItems(); + reqsList->DeleteAllItems(); + incsList->DeleteAllItems(); + messageList->DeleteAllItems(); + tagsList->DeleteAllItems(); + + set files = plugin.LoadAfter(); + int i=0; + for (set::const_iterator it=files.begin(), endit=files.end(); it != endit; ++it) { + loadAfterList->InsertItem(i, wxString(it->Name().c_str(), wxConvUTF8)); + loadAfterList->SetItem(i, 1, wxString(it->DisplayName().c_str(), wxConvUTF8)); + loadAfterList->SetItem(i, 2, wxString(it->Condition().c_str(), wxConvUTF8)); + ++i; + } + + files = plugin.Reqs(); + i=0; + for (set::const_iterator it=files.begin(), endit=files.end(); it != endit; ++it) { + reqsList->InsertItem(i, wxString(it->Name().c_str(), wxConvUTF8)); + reqsList->SetItem(i, 1, wxString(it->DisplayName().c_str(), wxConvUTF8)); + reqsList->SetItem(i, 2, wxString(it->Condition().c_str(), wxConvUTF8)); + ++i; + } + + files = plugin.Incs(); + i=0; + for (set::const_iterator it=files.begin(), endit=files.end(); it != endit; ++it) { + incsList->InsertItem(i, wxString(it->Name().c_str(), wxConvUTF8)); + incsList->SetItem(i, 1, wxString(it->DisplayName().c_str(), wxConvUTF8)); + incsList->SetItem(i, 2, wxString(it->Condition().c_str(), wxConvUTF8)); + ++i; + } + + list messages = plugin.Messages(); + i=0; + for (list::const_iterator it=messages.begin(), endit=messages.end(); it != endit; ++it) { + messageList->InsertItem(i, wxString(it->Type().c_str(), wxConvUTF8)); + messageList->SetItem(i, 1, wxString(it->Content().c_str(), wxConvUTF8)); + messageList->SetItem(i, 2, wxString(it->Condition().c_str(), wxConvUTF8)); + messageList->SetItem(i, 2, wxString(it->Language().c_str(), wxConvUTF8)); + ++i; + } + + set tags = plugin.Tags(); + i=0; + for (set::const_iterator it=tags.begin(), endit=tags.end(); it != endit; ++it) { + if (it->IsAddition()) + tagsList->InsertItem(i, translate("Add")); + else + tagsList->InsertItem(i, translate("Remove")); + tagsList->SetItem(i, 1, wxString(it->Name().c_str(), wxConvUTF8)); + tagsList->SetItem(i, 2, wxString(it->Condition().c_str(), wxConvUTF8)); + ++i; + } + + currentPlugin = plugin; + } +} + +void Editor::OnPriorityChange(wxSpinEvent& event) { + currentPlugin.Priority(event.GetPosition()); +} + +bool Editor::IsCurrentPluginEdited() const { + boss::Plugin original = GetOriginal(currentPlugin, true); + + return (original.Priority() != currentPlugin.Priority() + || original.Enabled() != currentPlugin.Enabled() + || original.LoadAfter() != currentPlugin.LoadAfter() + || original.Reqs() != currentPlugin.Reqs() + || original.Incs() != currentPlugin.Incs() + || original.Messages() != currentPlugin.Messages() + || original.Tags() != currentPlugin.Tags()); +} + +boss::Plugin Editor::GetOriginal(const boss::Plugin& plugin, bool withEdits) const { + //Merges together the base and (saved) edited plugin info. + boss::Plugin p; + vector::const_iterator it = std::find(_basePlugins.begin(), _basePlugins.end(), plugin); + + if (it != _basePlugins.end()) + p = *it; + + if (withEdits) { + it = std::find(_editedPlugins.begin(), _editedPlugins.end(), plugin); + + if (it != _editedPlugins.end()) + p.Merge(*it, true); + } + + return p; +} diff --git a/src/gui/editor.h b/src/gui/editor.h index a2d28094..89981fe6 100644 --- a/src/gui/editor.h +++ b/src/gui/editor.h @@ -35,8 +35,16 @@ class Editor : public wxFrame { public: Editor(const wxString title, wxFrame *parent); + void SetList(const std::vector& basePlugins, const std::vector& editedPlugins); + void IsSorted(bool sorted); + + void OnPluginSelect(wxListEvent& event); + void OnPriorityChange(wxSpinEvent& event); + DECLARE_EVENT_TABLE() private: - wxListBox * pluginList; + + bool IsCurrentPluginEdited() const; + boss::Plugin GetOriginal(const boss::Plugin& plugin, bool withEdits) const; wxButton * addFileBtn; wxButton * editFileBtn; @@ -46,27 +54,31 @@ private: wxButton * editMsgBtn; wxButton * removeMsgBtn; - wxButton * addTagBtn; - wxButton * editTagBtn; - wxButton * removeTagBtn; - wxButton * saveEditsBtn; wxButton * undoEditsBtn; wxButton * recalcBtn; wxButton * applyBtn; wxButton * cancelBtn; - wxCheckBox * enableUserEditsBox; - wxSpinCtrl * prioritySpin; - wxStaticText * pluginText; - wxNotebook * filesBook; + wxListCtrl * pluginList; wxListCtrl * reqsList; wxListCtrl * incsList; wxListCtrl * loadAfterList; wxListCtrl * messageList; wxListCtrl * tagsList; + + wxNotebook * filesBook; + wxNotebook * messagesBook; + + wxCheckBox * enableUserEditsBox; + wxSpinCtrl * prioritySpin; + wxStaticText * pluginText; - std::vector plugins; + wxBoxSizer * sortingButtons; + + std::vector _basePlugins, _editedPlugins; + + boss::Plugin currentPlugin; }; #endif diff --git a/src/gui/ids.h b/src/gui/ids.h index 8e1ed1c9..f28ab9a6 100644 --- a/src/gui/ids.h +++ b/src/gui/ids.h @@ -68,9 +68,6 @@ enum { BUTTON_AddMessage, BUTTON_EditMessage, BUTTON_RemoveMessage, - BUTTON_AddTag, - BUTTON_EditTag, - BUTTON_RemoveTag, BUTTON_SaveEdits, BUTTON_UndoEdits, BUTTON_Recalc, diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 19ad44ef..a3347284 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -181,20 +181,8 @@ bool BossGUI::OnInit() { break; } } - } else { - if (Game(GAME_NEHRIM, false).IsInstalledLocally()) //Before Oblivion because Nehrim installs can have Oblivion.esm for porting mods. - targetGame = GAME_NEHRIM; - else if (Game(GAME_TES4, false).IsInstalledLocally()) - targetGame = GAME_TES4; - else if (Game(GAME_TES5, false).IsInstalledLocally()) - targetGame = GAME_TES5; - else if (Game(GAME_FONV, false).IsInstalledLocally()) //Before Fallout 3 because some mods for New Vegas require Fallout3.esm. - targetGame = GAME_FONV; - else if (Game(GAME_FO3, false).IsInstalledLocally()) - targetGame = GAME_FO3; - else - targetGame = detected[0]; - } + } else + targetGame = detected[0]; targetGame = GAME_TES5; Game game(targetGame); @@ -210,30 +198,37 @@ bool BossGUI::OnInit() { Launcher::Launcher(const wxChar *title, const YAML::Node& settings, const Game& game, const vector& detectedGames) : wxFrame(NULL, wxID_ANY, title), _game(game), _detectedGames(detectedGames), _settings(settings) { - //Set up menu bar first. - MenuBar = new wxMenuBar(); - // File Menu - FileMenu = new wxMenu(); + //Initialise menu items. + wxMenuBar * MenuBar = new wxMenuBar(); + wxMenu * FileMenu = new wxMenu(); + wxMenu * EditMenu = new wxMenu(); + GameMenu = new wxMenu(); + wxMenu * HelpMenu = new wxMenu(); + + //Initialise controls. + wxButton * EditButton = new wxButton(this,OPTION_EditMetadata, translate("Edit Metadata")); + wxButton * SortButton = new wxButton(this,OPTION_SortPlugins, translate("Sort Plugins")); + ViewButton = new wxButton(this,OPTION_ViewLastReport, translate("View Last Report")); + + //Construct menus. + //File Menu FileMenu->Append(OPTION_ViewLastReport, translate("&View Last Report"), translate("Opens your last report.")); FileMenu->Append(OPTION_SortPlugins, translate("&Sort Plugins"), translate("Sorts your installed plugins.")); FileMenu->AppendSeparator(); FileMenu->Append(MENU_Quit, translate("&Quit"), translate("Quit BOSS.")); MenuBar->Append(FileMenu, translate("&File")); //Edit Menu - EditMenu = new wxMenu(); EditMenu->Append(OPTION_EditMetadata, translate("&Metadata..."), translate("Opens a window where you can edit plugin metadata.")); EditMenu->Append(MENU_ShowSettings, translate("&Settings..."), translate("Opens the Settings window.")); MenuBar->Append(EditMenu, translate("&Edit")); //Game menu - GameMenu = new wxMenu(); GameMenu->AppendRadioItem(MENU_Oblivion, wxT("&Oblivion"), translate("Switch to running BOSS for Oblivion.")); GameMenu->AppendRadioItem(MENU_Nehrim, wxT("&Nehrim"), translate("Switch to running BOSS for Nehrim.")); GameMenu->AppendRadioItem(MENU_Skyrim, wxT("&Skyrim"), translate("Switch to running BOSS for Skyrim.")); GameMenu->AppendRadioItem(MENU_Fallout3, wxT("&Fallout 3"), translate("Switch to running BOSS for Fallout 3.")); GameMenu->AppendRadioItem(MENU_FalloutNewVegas, wxT("&Fallout: New Vegas"), translate("Switch to running BOSS for Fallout: New Vegas.")); MenuBar->Append(GameMenu, translate("&Active Game")); - // About menu - HelpMenu = new wxMenu(); + //About menu HelpMenu->Append(MENU_OpenMainReadMe, translate("Open &Main Readme"), translate("Opens the main BOSS readme in your default web browser.")); HelpMenu->Append(MENU_OpenSyntaxReadMe, translate("Open &Metadata File Syntax Doc"), translate("Opens the BOSS metadata file syntax documentation in your default web browser.")); HelpMenu->Append(MENU_OpenAPIReadMe, translate("&Open API Readme"), translate("Opens the BOSS API readme in your default web browser.")); @@ -242,17 +237,14 @@ Launcher::Launcher(const wxChar *title, const YAML::Node& settings, const Game& HelpMenu->AppendSeparator(); HelpMenu->Append(MENU_ShowAbout, translate("&About BOSS..."), translate("Shows information about BOSS.")); MenuBar->Append(HelpMenu, translate("&Help")); - SetMenuBar(MenuBar); - //Set up stuff in the frame. - SetBackgroundColour(wxColour(255,255,255)); - - //Add the three buttons. + //Set up layout. wxBoxSizer *buttonBox = new wxBoxSizer(wxVERTICAL); - buttonBox->Add(EditButton = new wxButton(this,OPTION_EditMetadata, translate("Edit Metadata")), 1, wxEXPAND|wxALIGN_CENTRE|wxALL, 10); - buttonBox->Add(SortButton = new wxButton(this,OPTION_SortPlugins, translate("Sort Plugins")), 1, wxEXPAND|wxALIGN_CENTRE|wxLEFT|wxRIGHT, 10); - buttonBox->Add(ViewButton = new wxButton(this,OPTION_ViewLastReport, translate("View Last Report")), 1, wxEXPAND|wxALIGN_CENTRE|wxALL, 10); + buttonBox->Add(EditButton, 1, wxEXPAND|wxALIGN_CENTRE|wxALL, 10); + buttonBox->Add(SortButton, 1, wxEXPAND|wxALIGN_CENTRE|wxLEFT|wxRIGHT, 10); + buttonBox->Add(ViewButton, 1, wxEXPAND|wxALIGN_CENTRE|wxALL, 10); + //Set up initial state. SortButton->SetDefault(); if (_game.Id() == GAME_TES4) @@ -265,10 +257,15 @@ Launcher::Launcher(const wxChar *title, const YAML::Node& settings, const Game& GameMenu->FindItem(MENU_Fallout3)->Check(); else if (_game.Id() == GAME_FONV) GameMenu->FindItem(MENU_FalloutNewVegas)->Check(); + + if (!fs::exists(_game.ResultsPath())) + ViewButton->Enable(false); DisableUndetectedGames(); //Now set the layout and sizes. + SetMenuBar(MenuBar); + SetBackgroundColour(wxColour(255,255,255)); SetSizerAndFit(buttonBox); SetSize(wxSize(250, 200)); } @@ -488,14 +485,41 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { out.close(); progDia->Destroy(); + + //Create editor window. + Editor *editor = new Editor(translate("BOSS: Metadata Editor"), this); + editor->SetIcon(wxIconLocation("BOSS.exe")); + + //The 'plugins' list contains the merged plugin info, but we want to pass a vector of plugins with only masterlist info in its place, so have to construct that. + vector pluginVec; + for (list::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; ++it) { + boss::Plugin p = *it; + list::iterator pos = std::find(mlist_plugins.begin(), mlist_plugins.end(), *it); + + if (pos != mlist_plugins.end()) + p.Merge(*pos); + + pluginVec.push_back(p); + } + vector ulistPluginVec(ulist_plugins.begin(), ulist_plugins.end()); + + //Pass plugin lists to editor window. + editor->SetList(pluginVec, ulistPluginVec); + + //Tell the editor that it's not being shown as part of the load order sort process. + editor->IsSorted(true); + + editor->Show(); + + ViewButton->Enable(true); } void Launcher::OnEditMetadata(wxCommandEvent& event) { //Parse the userlist and masterlist and get a list of installed plugins. - list plugins; + vector plugins; YAML::Node mlist, ulist; - list mlist_plugins, ulist_plugins; + vector mlist_plugins, ulist_plugins; for (fs::directory_iterator it(_game.DataPath()); it != fs::directory_iterator(); ++it) { if (fs::is_regular_file(it->status()) && (it->path().extension().string() == ".esp" || it->path().extension().string() == ".esm")) { @@ -517,7 +541,7 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) { NULL); } if (mlist["plugins"]) - mlist_plugins = mlist["plugins"].as< list >(); + mlist_plugins = mlist["plugins"].as< vector >(); } if (fs::exists(_game.UserlistPath())) { @@ -532,12 +556,36 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) { NULL); } if (ulist["plugins"]) - ulist_plugins = ulist["plugins"].as< list >(); + ulist_plugins = ulist["plugins"].as< vector >(); } + //Cut out any plugins in the masterlist that aren't installed or in the userlist. + //Merge down to the plugin list so that it holds all uneditable metadata. + list slim_mlist; + for (vector::const_iterator it=mlist_plugins.begin(), endit=mlist_plugins.end(); it != endit; ++it) { + vector::iterator pos = std::find(plugins.begin(), plugins.end(), *it); + + if (pos != plugins.end()) + pos->Merge(*it); + else if (std::find(ulist_plugins.begin(), ulist_plugins.end(), *it) != ulist_plugins.end()) + plugins.push_back(*it); + } + + //Sort into alphabetical order. + std::sort(plugins.begin(), plugins.end(), AlphaSortPlugins); + std::sort(ulist_plugins.begin(), ulist_plugins.end(), AlphaSortPlugins); + + //Create editor window. Editor *editor = new Editor(translate("BOSS: Metadata Editor"), this); editor->SetIcon(wxIconLocation("BOSS.exe")); + + //Pass plugin lists to editor window. + editor->SetList(plugins, ulist_plugins); + + //Tell the editor that it's not being shown as part of the load order sort process. + editor->IsSorted(false); + editor->Show(); } @@ -648,3 +696,7 @@ void Launcher::DisableUndetectedGames() { SetTitle(wxT("BOSS - " + _game.Name())); //Don't need to convert name, known to be only ASCII chars. } + +bool Launcher::AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs) { + return boost::to_lower_copy(lhs.Name()) < boost::to_lower_copy(rhs.Name()); +} diff --git a/src/gui/main.h b/src/gui/main.h index 5a8a9ed5..ffd2dfa4 100644 --- a/src/gui/main.h +++ b/src/gui/main.h @@ -54,20 +54,16 @@ public: DECLARE_EVENT_TABLE() private: - wxMenuBar *MenuBar; - wxMenu *FileMenu; - wxMenu *EditMenu; - wxMenu *GameMenu; - wxMenu *HelpMenu; - wxButton *SortButton; - wxButton *ViewButton; - wxButton *EditButton; + wxMenu * GameMenu; + wxButton * ViewButton; std::vector _detectedGames; boss::Game _game; YAML::Node _settings; //BOSS Settings. void DisableUndetectedGames(); + + static bool AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs); }; #endif diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2f63ac98..f4525999 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -34,6 +34,7 @@ using namespace std; SettingsFrame::SettingsFrame(const wxString title, wxFrame *parent, YAML::Node& settings) : wxFrame(parent, wxID_ANY, title), _settings(settings) { + //Initialise drop-down list contents. wxString DebugVerbosity[] = { translate("None"), translate("Low"), @@ -58,9 +59,24 @@ SettingsFrame::SettingsFrame(const wxString title, wxFrame *parent, YAML::Node& wxString("简体中文", wxConvUTF8)*/ }; - //Set up stuff in the frame. - SetBackgroundColour(wxColour(255,255,255)); + //Initialise controls. + GameChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6, Game); + LanguageChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, Language); + LanguageChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, Language); + DebugVerbosityChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, DebugVerbosity); + OblivionURL = new wxTextCtrl(this, wxID_ANY); + NehrimURL = new wxTextCtrl(this, wxID_ANY); + SkyrimURL = new wxTextCtrl(this, wxID_ANY); + FO3URL = new wxTextCtrl(this, wxID_ANY); + FONVURL = new wxTextCtrl(this, wxID_ANY); + + UpdateMasterlistBox = new wxCheckBox(this, wxID_ANY, translate("Update masterlist before sorting.")); + + wxButton * okBtn = new wxButton(this, OPTION_OKExitSettings, translate("OK"), wxDefaultPosition, wxSize(70, 30)); + wxButton * cancelBtn = new wxButton(this, OPTION_CancelExitSettings, translate("Cancel"), wxDefaultPosition, wxSize(70, 30)); + + //Set up layout. wxSizerFlags leftItem(1); leftItem.Border(wxALL, 10).Expand().Left(); @@ -70,45 +86,44 @@ SettingsFrame::SettingsFrame(const wxString title, wxFrame *parent, YAML::Node& wxSizerFlags wholeItem(1); wholeItem.Border(wxALL, 10).Expand(); - wxBoxSizer *bigBox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer * bigBox = new wxBoxSizer(wxVERTICAL); - wxFlexGridSizer *GridSizer = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer * GridSizer = new wxFlexGridSizer(2, 5, 5); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Default Game:")), leftItem); - GridSizer->Add(GameChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6, Game), rightItem); + GridSizer->Add(GameChoice, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Language:")), leftItem); - GridSizer->Add(LanguageChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, Language), rightItem); + GridSizer->Add(LanguageChoice, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Debug Verbosity:")), leftItem); - GridSizer->Add(DebugVerbosityChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, DebugVerbosity), rightItem); + GridSizer->Add(DebugVerbosityChoice, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Oblivion Masterlist URL:")), leftItem); - GridSizer->Add(OblivionURL = new wxTextCtrl(this, wxID_ANY), rightItem); + GridSizer->Add(OblivionURL, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Nehrim Masterlist URL:")), leftItem); - GridSizer->Add(NehrimURL = new wxTextCtrl(this, wxID_ANY), rightItem); + GridSizer->Add(NehrimURL, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Skyrim Masterlist URL:")), leftItem); - GridSizer->Add(SkyrimURL = new wxTextCtrl(this, wxID_ANY), rightItem); + GridSizer->Add(SkyrimURL, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Fallout 3 Masterlist URL:")), leftItem); - GridSizer->Add(FO3URL = new wxTextCtrl(this, wxID_ANY), rightItem); + GridSizer->Add(FO3URL, rightItem); GridSizer->Add(new wxStaticText(this, wxID_ANY, translate("Fallout: New Vegas Masterlist URL:")), leftItem); - GridSizer->Add(FONVURL = new wxTextCtrl(this, wxID_ANY), rightItem); + GridSizer->Add(FONVURL, rightItem); bigBox->Add(GridSizer); - bigBox->Add(UpdateMasterlistBox = new wxCheckBox(this, wxID_ANY, translate("Update masterlist before sorting.")), wholeItem); + bigBox->Add(UpdateMasterlistBox, wholeItem); - wxString text = translate("Language settings will be applied after the BOSS GUI is restarted."); - bigBox->Add(new wxStaticText(this, wxID_ANY, text), wholeItem); + bigBox->Add(new wxStaticText(this, wxID_ANY, translate("Settings are applied after BOSS is restarted.")), wholeItem); //Need to add 'OK' and 'Cancel' buttons. - wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); - hbox->Add(new wxButton(this, OPTION_OKExitSettings, translate("OK"), wxDefaultPosition, wxSize(70, 30))); - hbox->Add(new wxButton(this, OPTION_CancelExitSettings, translate("Cancel"), wxDefaultPosition, wxSize(70, 30)), 0, wxLEFT, 20); + wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL); + hbox->Add(okBtn); + hbox->Add(cancelBtn, 0, wxLEFT, 20); //Now add TabHolder and OK button to window sizer. bigBox->Add(hbox, 0, wxCENTER|wxALL, 10); @@ -120,6 +135,7 @@ SettingsFrame::SettingsFrame(const wxString title, wxFrame *parent, YAML::Node& DebugVerbosityChoice->SetToolTip(translate("The output is logged to the BOSSDebugLog.txt file")); //Now set the layout and sizes. + SetBackgroundColour(wxColour(255,255,255)); SetSizerAndFit(bigBox); } @@ -221,12 +237,6 @@ void SettingsFrame::OnQuit(wxCommandEvent& event) { _settings["Masterlist URLs"]["Fallout New Vegas"] = string(FONVURL->GetValue().ToUTF8()); - //Also set the logger settings now. - /* g_logger.setOriginTracking(gl_debug_with_source); - g_logger.setVerbosity(static_cast(LV_WARN + gl_debug_verbosity)); - if (gl_log_debug_output) - g_logger.setStream(debug_log_path.string().c_str());*/ - YAML::Emitter yout; yout.SetIndent(2); yout << _settings; diff --git a/src/metadata.cpp b/src/metadata.cpp index 2bddb4c1..988bea13 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -52,7 +52,7 @@ namespace boss { } bool FormID::operator == (const FormID& rhs) const { - return (plugin == rhs.Plugin() && id == rhs.Id()); + return (boost::iequals(plugin, rhs.Plugin()) && id == rhs.Id()); } bool FormID::operator != (const FormID& rhs) const { @@ -114,6 +114,14 @@ namespace boss { Message::Message(const std::string& t, const std::string& cont, const std::string& cond, const std::string& l) : type(t), language(l), ConditionalData(cond, cont) {} + + bool Message::operator < (const Message& rhs) const { + return (Content() < rhs.Content()); + } + + bool Message::operator == (const Message& rhs) const { + return (boost::iequals(Type(), rhs.Type()) && boost::iequals(Content(), rhs.Content())); + } std::string Message::Type() const { return type; @@ -136,6 +144,10 @@ namespace boss { return (Name() < rhs.Name()); } + bool File::operator == (const File& rhs) const { + return boost::iequals(Name(), rhs.Name()); + } + std::string File::Name() const { return Data(); } @@ -174,6 +186,10 @@ namespace boss { return (PrefixedName() < rhs.PrefixedName()); } + bool Tag::operator == (const Tag& rhs) const { + return boost::iequals(PrefixedName(), rhs.PrefixedName()); + } + bool Tag::IsAddition() const { return addTag; } @@ -260,6 +276,7 @@ namespace boss { return; //The following should be replaced. + enabled = plugin.Enabled(); priority = plugin.Priority(); masters = plugin.Masters(); formIDs = plugin.FormIDs(); @@ -285,6 +302,42 @@ namespace boss { return; } + + Plugin Plugin::DiffMetadata(const Plugin& plugin) const { + Plugin p(name); + p.Priority(priority); + + //Compare this plugin against the given plugin. + set files = plugin.LoadAfter(); + set filesDiff; + set_difference(files.begin(), files.end(), loadAfter.begin(), loadAfter.end(), inserter(filesDiff, filesDiff.begin())); + p.LoadAfter(filesDiff); + + filesDiff.clear(); + files = plugin.Reqs(); + set_difference(files.begin(), files.end(), requirements.begin(), requirements.end(), inserter(filesDiff, filesDiff.begin())); + p.Reqs(filesDiff); + + filesDiff.clear(); + files = plugin.Incs(); + set_difference(files.begin(), files.end(), incompatibilities.begin(), incompatibilities.end(), inserter(filesDiff, filesDiff.begin())); + p.Incs(filesDiff); + + list msgs1 = plugin.Messages(); + list msgs2 = messages; + msgs1.sort(); + msgs2.sort(); + list mDiff; + set_difference(msgs1.begin(), msgs1.end(), msgs2.begin(), msgs2.end(), inserter(mDiff, mDiff.begin())); + p.Messages(mDiff); + + set bashTags = plugin.Tags(); + set tagDiff; + set_difference(bashTags.begin(), bashTags.end(), tags.begin(), tags.end(), inserter(tagDiff, tagDiff.begin())); + p.Tags(tagDiff); + + return p; + } std::string Plugin::Name() const { return name; @@ -454,10 +507,8 @@ namespace boss { boost::unordered_set Plugin::OverlapFormIDs(const Plugin& plugin) const { boost::unordered_set otherFormIDs = plugin.FormIDs(); boost::unordered_set overlap; - for (boost::unordered_set::const_iterator it=formIDs.begin(), endIt=formIDs.end(); it != endIt; ++it) { - if (otherFormIDs.find(*it) != otherFormIDs.end()) - overlap.insert(*it); - } + + set_intersection(formIDs.begin(), formIDs.end(), otherFormIDs.begin(), otherFormIDs.end(), inserter(overlap, overlap.begin())); return overlap; } @@ -479,14 +530,10 @@ namespace boss { if (boost::iequals(*it, plugin.Name())) return true; } - for (set::const_iterator it=requirements.begin(), endIt=requirements.end(); it != endIt; ++it) { - if (boost::iequals(it->Name(), plugin.Name())) - return true; - } - for (set::const_iterator it=loadAfter.begin(), endIt=loadAfter.end(); it != endIt; ++it) { - if (boost::iequals(it->Name(), plugin.Name())) - return true; - } + if (find(requirements.begin(), requirements.end(), plugin) != requirements.end()) + return true; + if (find(loadAfter.begin(), loadAfter.end(), plugin) != requirements.end()) + return true; return false; } @@ -508,4 +555,8 @@ namespace boss { } return issues; } + + bool operator == (const File& lhs, const Plugin& rhs) { + return boost::iequals(lhs.Name(), rhs.Name()); + } } diff --git a/src/metadata.h b/src/metadata.h index 3fa2a7a4..be401221 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -96,6 +96,9 @@ namespace boss { Message(const std::string& type, const std::string& content); Message(const std::string& type, const std::string& content, const std::string& condition, const std::string& language); + + bool operator < (const Message& rhs) const; + bool operator == (const Message& rhs) const; std::string Type() const; std::string Language() const; @@ -113,6 +116,7 @@ namespace boss { const std::string& condition); bool operator < (const File& rhs) const; + bool operator == (const File& rhs) const; std::string Name() const; std::string DisplayName() const; @@ -127,6 +131,7 @@ namespace boss { Tag(const std::string& tag, const std::string& condition); bool operator < (const Tag& rhs) const; + bool operator == (const Tag& rhs) const; bool IsAddition() const; std::string Name() const; @@ -142,6 +147,7 @@ namespace boss { Plugin(const boss::Game& game, const std::string& name); void Merge(const Plugin& plugin, bool ifdDisabled = false); + Plugin DiffMetadata(const Plugin& plugin) const; std::string Name() const; bool Enabled() const; @@ -201,6 +207,8 @@ namespace boss { std::string version; //Obtained from description field. bool isMaster; }; + + bool operator == (const File& lhs, const Plugin& rhs); } #endif