summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-05-28 23:42:53 -0400
committerGitHub <noreply@github.com>2020-05-28 23:42:53 -0400
commitda35955d6c15c4abc83ae8c0a4ea8195c0c0ac85 (patch)
treebeab5513b64e93c32d50f628321654623b603129 /src
parent652d3ab054cd2bd94fe88dc42aa39798fc3b2c4a (diff)
parent0a5790b333883c7c4ef07bb44f0a411ce8ba79b5 (diff)
Merge pull request #1092 from Holt59/mod-data-content
Use ModDataContent feature from GamePlugin
Diffstat (limited to 'src')
-rw-r--r--src/categories.cpp116
-rw-r--r--src/filterlist.cpp14
-rw-r--r--src/filterlist.h4
-rw-r--r--src/mainwindow.cpp5
-rw-r--r--src/modinfo.cpp27
-rw-r--r--src/modinfo.h37
-rw-r--r--src/modinforegular.cpp58
-rw-r--r--src/modinforegular.h2
-rw-r--r--src/modinfowithconflictinfo.cpp7
-rw-r--r--src/modinfowithconflictinfo.h16
-rw-r--r--src/modlist.cpp89
-rw-r--r--src/modlist.h14
-rw-r--r--src/modlistsortproxy.cpp34
-rw-r--r--src/modlistsortproxy.h5
-rw-r--r--src/organizercore.cpp10
-rw-r--r--src/organizercore.h112
-rw-r--r--src/resources.qrc1
-rw-r--r--src/resources/contents/f4-nuclear.pngbin0 -> 1733 bytes
18 files changed, 291 insertions, 260 deletions
diff --git a/src/categories.cpp b/src/categories.cpp
index aea99532..b1e08a3a 100644
--- a/src/categories.cpp
+++ b/src/categories.cpp
@@ -113,7 +113,7 @@ void CategoryFactory::reset()
// 43 = Savegames (makes no sense to install them through MO)
// 45 = Videos and trailers
// 87 = Miscelanous
- addCategory(0, "None", MakeVector<int>(4, 28, 43, 45, 87), 0);
+ addCategory(0, "None", { 4, 28, 43, 45, 87 }, 0);
}
@@ -206,63 +206,63 @@ void CategoryFactory::loadDefaultCategories()
{
// the order here is relevant as it defines the order in which the
// mods appear in the combo box
- addCategory(1, "Animations", MakeVector<int>(2, 4, 51), 0);
- addCategory(52, "Poses", MakeVector<int>(1, 29), 1);
- addCategory(2, "Armour", MakeVector<int>(2, 5, 54), 0);
- addCategory(53, "Power Armor", MakeVector<int>(1, 53), 2);
- addCategory(3, "Audio", MakeVector<int>(3, 33, 35, 106), 0);
- addCategory(38, "Music", MakeVector<int>(2, 34, 61), 0);
- addCategory(39, "Voice", MakeVector<int>(2, 36, 107), 0);
- addCategory(5, "Clothing", MakeVector<int>(2, 9, 60), 0);
- addCategory(41, "Jewelry", MakeVector<int>(1, 102), 5);
- addCategory(42, "Backpacks", MakeVector<int>(1, 49), 5);
- addCategory(6, "Collectables", MakeVector<int>(2, 10, 92), 0);
- addCategory(28, "Companions", MakeVector<int>(3, 11, 66, 96), 0);
- addCategory(7, "Creatures, Mounts, & Vehicles", MakeVector<int>(4, 12, 65, 83, 101), 0);
- addCategory(8, "Factions", MakeVector<int>(2, 16, 25), 0);
- addCategory(9, "Gameplay", MakeVector<int>(2, 15, 24), 0);
- addCategory(27, "Combat", MakeVector<int>(1, 77), 9);
- addCategory(43, "Crafting", MakeVector<int>(2, 50, 100), 9);
- addCategory(48, "Overhauls", MakeVector<int>(2, 24, 79), 9);
- addCategory(49, "Perks", MakeVector<int>(1, 27), 9);
- addCategory(54, "Radio", MakeVector<int>(1, 31), 9);
- addCategory(55, "Shouts", MakeVector<int>(1, 104), 9);
- addCategory(22, "Skills & Levelling", MakeVector<int>(2, 46, 73), 9);
- addCategory(58, "Weather & Lighting", MakeVector<int>(1, 56), 9);
- addCategory(44, "Equipment", MakeVector<int>(1, 44), 43);
- addCategory(45, "Home/Settlement", MakeVector<int>(1, 45), 43);
- addCategory(10, "Body, Face, & Hair", MakeVector<int>(2, 17, 26), 0);
- addCategory(39, "Tattoos", MakeVector<int>(1, 57), 10);
- addCategory(40, "Character Presets", MakeVector<int>(1, 58), 0);
- addCategory(11, "Items", MakeVector<int>(2, 27, 85), 0);
- addCategory(32, "Mercantile", MakeVector<int>(2, 23, 69), 0);
- addCategory(37, "Ammo", MakeVector<int>(1, 3), 11);
- addCategory(19, "Weapons", MakeVector<int>(2, 41, 55), 11);
- addCategory(36, "Weapon & Armour Sets", MakeVector<int>(1, 42), 11);
- addCategory(23, "Player Homes", MakeVector<int>(2, 28, 67), 0);
- addCategory(25, "Castles & Mansions", MakeVector<int>(1, 68), 23);
- addCategory(51, "Settlements", MakeVector<int>(1, 48), 23);
- addCategory(12, "Locations", MakeVector<int>(10, 20, 21, 22, 30, 47, 70, 88, 89, 90, 91), 0);
- addCategory(4, "Cities", MakeVector<int>(1, 53), 12);
- addCategory(31, "Landscape Changes", MakeVector<int>(1, 58), 0);
- addCategory(29, "Environment", MakeVector<int>(2, 14, 74), 0);
- addCategory(30, "Immersion", MakeVector<int>(2, 51, 78), 0);
- addCategory(20, "Magic", MakeVector<int>(3, 75, 93, 94), 0);
- addCategory(21, "Models & Textures", MakeVector<int>(2, 19, 29), 0);
- addCategory(33, "Modders resources", MakeVector<int>(2, 18, 82), 0);
- addCategory(13, "NPCs", MakeVector<int>(3, 22, 33, 99), 0);
- addCategory(24, "Bugfixes", MakeVector<int>(2, 6, 95), 0);
- addCategory(14, "Patches", MakeVector<int>(2, 25, 84), 24);
- addCategory(35, "Utilities", MakeVector<int>(2, 38, 39), 0);
- addCategory(26, "Cheats", MakeVector<int>(1, 8), 0);
- addCategory(15, "Quests", MakeVector<int>(2, 30, 35), 0);
- addCategory(16, "Races & Classes", MakeVector<int>(1, 34), 0);
- addCategory(34, "Stealth", MakeVector<int>(1, 76), 0);
- addCategory(17, "UI", MakeVector<int>(2, 37, 42), 0);
- addCategory(18, "Visuals", MakeVector<int>(2, 40, 62), 0);
- addCategory(50, "Pip-Boy", MakeVector<int>(1, 52), 18);
- addCategory(46, "Shader Presets", MakeVector<int>(3, 13, 97, 105), 0);
- addCategory(47, "Miscellaneous", MakeVector<int>(2, 2, 28), 0);
+ addCategory(1, "Animations", { 2, 4, 51 }, 0);
+ addCategory(52, "Poses", { 1, 29 }, 1);
+ addCategory(2, "Armour", { 2, 5, 54 }, 0);
+ addCategory(53, "Power Armor", { 1, 53 }, 2);
+ addCategory(3, "Audio", { 3, 33, 35, 106 }, 0);
+ addCategory(38, "Music", { 2, 34, 61 }, 0);
+ addCategory(39, "Voice", { 2, 36, 107 }, 0);
+ addCategory(5, "Clothing", { 2, 9, 60 }, 0);
+ addCategory(41, "Jewelry", { 1, 102 }, 5);
+ addCategory(42, "Backpacks", { 1, 49 }, 5);
+ addCategory(6, "Collectables", { 2, 10, 92 }, 0);
+ addCategory(28, "Companions", { 3, 11, 66, 96 }, 0);
+ addCategory(7, "Creatures, Mounts, & Vehicles", { 4, 12, 65, 83, 101 }, 0);
+ addCategory(8, "Factions", { 2, 16, 25 }, 0);
+ addCategory(9, "Gameplay", { 2, 15, 24 }, 0);
+ addCategory(27, "Combat", { 1, 77 }, 9);
+ addCategory(43, "Crafting", { 2, 50, 100 }, 9);
+ addCategory(48, "Overhauls", { 2, 24, 79 }, 9);
+ addCategory(49, "Perks", { 1, 27 }, 9);
+ addCategory(54, "Radio", { 1, 31 }, 9);
+ addCategory(55, "Shouts", { 1, 104 }, 9);
+ addCategory(22, "Skills & Levelling", { 2, 46, 73 }, 9);
+ addCategory(58, "Weather & Lighting", { 1, 56 }, 9);
+ addCategory(44, "Equipment", { 1, 44 }, 43);
+ addCategory(45, "Home/Settlement", { 1, 45 }, 43);
+ addCategory(10, "Body, Face, & Hair", { 2, 17, 26 }, 0);
+ addCategory(39, "Tattoos", { 1, 57 }, 10);
+ addCategory(40, "Character Presets", { 1, 58 }, 0);
+ addCategory(11, "Items", { 2, 27, 85 }, 0);
+ addCategory(32, "Mercantile", { 2, 23, 69 }, 0);
+ addCategory(37, "Ammo", { 1, 3 }, 11);
+ addCategory(19, "Weapons", { 2, 41, 55 }, 11);
+ addCategory(36, "Weapon & Armour Sets", { 1, 42 }, 11);
+ addCategory(23, "Player Homes", { 2, 28, 67 }, 0);
+ addCategory(25, "Castles & Mansions", { 1, 68 }, 23);
+ addCategory(51, "Settlements", { 1, 48 }, 23);
+ addCategory(12, "Locations", { 10, 20, 21, 22, 30, 47, 70, 88, 89, 90, 91 }, 0);
+ addCategory(4, "Cities", { 1, 53 }, 12);
+ addCategory(31, "Landscape Changes", { 1, 58 }, 0);
+ addCategory(29, "Environment", { 2, 14, 74 }, 0);
+ addCategory(30, "Immersion", { 2, 51, 78 }, 0);
+ addCategory(20, "Magic", { 3, 75, 93, 94 }, 0);
+ addCategory(21, "Models & Textures", { 2, 19, 29 }, 0);
+ addCategory(33, "Modders resources", { 2, 18, 82 }, 0);
+ addCategory(13, "NPCs", { 3, 22, 33, 99 }, 0);
+ addCategory(24, "Bugfixes", { 2, 6, 95 }, 0);
+ addCategory(14, "Patches", { 2, 25, 84 }, 24);
+ addCategory(35, "Utilities", { 2, 38, 39 }, 0);
+ addCategory(26, "Cheats", { 1, 8 }, 0);
+ addCategory(15, "Quests", { 2, 30, 35 }, 0);
+ addCategory(16, "Races & Classes", { 1, 34 }, 0);
+ addCategory(34, "Stealth", { 1, 76 }, 0);
+ addCategory(17, "UI", { 2, 37, 42 }, 0);
+ addCategory(18, "Visuals", { 2, 40, 62 }, 0);
+ addCategory(50, "Pip-Boy", { 1, 52 }, 18);
+ addCategory(46, "Shader Presets", { 3, 13, 97, 105 }, 0);
+ addCategory(47, "Miscellaneous", { 2, 2, 28 }, 0);
}
diff --git a/src/filterlist.cpp b/src/filterlist.cpp
index 57bc740a..2b72c152 100644
--- a/src/filterlist.cpp
+++ b/src/filterlist.cpp
@@ -3,6 +3,7 @@
#include "categories.h"
#include "categoriesdialog.h"
#include "settings.h"
+#include "organizercore.h"
#include <utility.h>
using namespace MOBase;
@@ -180,8 +181,8 @@ private:
};
-FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory)
- : ui(ui), m_factory(factory)
+FilterList::FilterList(Ui::MainWindow* ui, OrganizerCore* organizer, CategoryFactory& factory)
+ : ui(ui), m_Organizer(organizer), m_factory(factory)
{
auto* eventFilter = new CriteriaItemFilter(
ui->filters, [&](auto* item, int dir){ return cycleItem(item, dir); });
@@ -233,12 +234,11 @@ QTreeWidgetItem* FilterList::addCriteriaItem(
void FilterList::addContentCriteria()
{
- for (unsigned i = 0; i < ModInfo::NUM_CONTENT_TYPES; ++i) {
- QString filterName = tr("Contains %1").arg(ModInfo::getContentTypeName(i));
+ m_Organizer->modDataContents().forEachContent([this](auto const& content) {
addCriteriaItem(
- nullptr, QString("<%1>").arg(filterName),
- i, ModListSortProxy::TypeContent);
- }
+ nullptr, QString("<%1>").arg(tr("Contains %1").arg(content.name())),
+ content.id(), ModListSortProxy::TypeContent);
+ }, true);
}
void FilterList::addCategoryCriteria(QTreeWidgetItem *root, const std::set<int> &categoriesUsed, int targetID)
diff --git a/src/filterlist.h b/src/filterlist.h
index b0ebc9a4..ba9dc71c 100644
--- a/src/filterlist.h
+++ b/src/filterlist.h
@@ -7,13 +7,14 @@
namespace Ui { class MainWindow; };
class CategoryFactory;
class Settings;
+class OrganizerCore;
class FilterList : public QObject
{
Q_OBJECT;
public:
- FilterList(Ui::MainWindow* ui, CategoryFactory& factory);
+ FilterList(Ui::MainWindow* ui, OrganizerCore *organizer, CategoryFactory& factory);
void restoreState(const Settings& s);
void saveState(Settings& s) const;
@@ -31,6 +32,7 @@ private:
class CriteriaItem;
Ui::MainWindow* ui;
+ OrganizerCore* m_Organizer;
CategoryFactory& m_factory;
bool onClick(QMouseEvent* e);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 971bb04d..477cbc70 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -311,7 +311,7 @@ MainWindow::MainWindow(Settings &settings
ui->statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount());
}
- m_Filters.reset(new FilterList(ui, m_CategoryFactory));
+ m_Filters.reset(new FilterList(ui, &m_OrganizerCore, m_CategoryFactory));
connect(
m_Filters.get(), &FilterList::criteriaChanged,
@@ -5951,7 +5951,8 @@ void MainWindow::onFiltersCriteria(const std::vector<ModListSortProxy::Criteria>
const auto& c = criteria[0];
if (c.type == ModListSortProxy::TypeContent) {
- label = ModInfo::getContentTypeName(c.id);
+ const auto *content = m_OrganizerCore.modDataContents().findById(c.id);
+ label = content ? content->name() : QString();
} else {
label = m_CategoryFactory.getCategoryNameByID(c.id);
}
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index 15fa71da..a8e6a55b 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -108,27 +108,6 @@ ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName,
return result;
}
-QString ModInfo::getContentTypeName(int contentType)
-{
- switch (contentType) {
- case CONTENT_PLUGIN: return tr("Plugins");
- case CONTENT_TEXTURE: return tr("Textures");
- case CONTENT_MESH: return tr("Meshes");
- case CONTENT_BSA: return tr("Bethesda Archive");
- case CONTENT_INTERFACE: return tr("UI Changes");
- case CONTENT_SOUND: return tr("Sound Effects");
- case CONTENT_SCRIPT: return tr("Scripts");
- case CONTENT_SKSE: return tr("Script Extender");
- case CONTENT_SKSEFILES: return tr("Script Extender Files");
- case CONTENT_SKYPROC: return tr("SkyProc Tools");
- case CONTENT_MCM: return tr("MCM Data");
- case CONTENT_INI: return tr("INI files");
- case CONTENT_MODGROUP: return tr("ModGroup files");
-
- default: throw MyException(tr("invalid content type: %1").arg(contentType));
- }
-}
-
void ModInfo::createFromOverwrite(PluginContainer *pluginContainer,
const MOBase::IPluginGame* game,
MOShared::DirectoryEntry **directoryStructure)
@@ -515,12 +494,6 @@ bool ModInfo::hasAnyOfTheseFlags(std::vector<ModInfo::EFlag> flags) const
return false;
}
-bool ModInfo::hasContent(ModInfo::EContent content) const
-{
- std::vector<EContent> contents = getContents();
- return std::find(contents.begin(), contents.end(), content) != contents.end();
-}
-
bool ModInfo::categorySet(int categoryID) const
{
for (std::set<int>::const_iterator iter = m_Categories.begin(); iter != m_Categories.end(); ++iter) {
diff --git a/src/modinfo.h b/src/modinfo.h
index 29e6124d..a6c8ea65 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -87,24 +87,6 @@ public:
FLAG_TRACKED,
};
- enum EContent {
- CONTENT_PLUGIN,
- CONTENT_TEXTURE,
- CONTENT_MESH,
- CONTENT_BSA,
- CONTENT_INTERFACE,
- CONTENT_SOUND,
- CONTENT_SCRIPT,
- CONTENT_SKSE,
- CONTENT_SKSEFILES,
- CONTENT_SKYPROC,
- CONTENT_MCM,
- CONTENT_INI,
- CONTENT_MODGROUP
- };
-
- static const int NUM_CONTENT_TYPES = CONTENT_MODGROUP + 1;
-
enum EHighlight {
HIGHLIGHT_NONE = 0,
HIGHLIGHT_INVALID = 1,
@@ -248,13 +230,6 @@ public:
//
static bool isRegularName(const QString& name);
- /**
- * @brief retieve a name for one of the CONTENT_ enums
- * @param contentType the content value
- * @return a display string
- */
- static QString getContentTypeName(int contentType);
-
virtual bool isRegular() const { return false; }
virtual bool isEmpty() const { return false; }
@@ -546,7 +521,7 @@ public:
/**
* @return a list of content types contained in a mod
*/
- virtual std::vector<EContent> getContents() const { return std::vector<EContent>(); }
+ virtual const std::set<int>& getContents() const = 0;
/**
* @brief test if the specified flag is set for this mod
@@ -563,11 +538,13 @@ public:
bool hasAnyOfTheseFlags(std::vector<ModInfo::EFlag> flags) const;
/**
- * @brief test if the mods contains the specified content
- * @param content the content to test
- * @return true if the content is there, false otherwise
+ * @brief Test if the mod contains the specified content.
+ *
+ * @param content ID of the content to test.
+ *
+ * @return true if the content is there, false otherwise.
*/
- bool hasContent(ModInfo::EContent content) const;
+ virtual bool hasContent(int content) const = 0;
/**
* @return an indicator if and how this mod should be highlighted by the UI
diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp
index a1f1a04d..8ef8e007 100644
--- a/src/modinforegular.cpp
+++ b/src/modinforegular.cpp
@@ -3,7 +3,7 @@
#include "categories.h"
#include "messagedialog.h"
#include "report.h"
-#include "scriptextender.h"
+#include "moddatacontent.h"
#include "settings.h"
#include <QApplication>
@@ -653,60 +653,16 @@ std::vector<ModInfo::EFlag> ModInfoRegular::getFlags() const
}
-std::vector<ModInfo::EContent> ModInfoRegular::doGetContents() const
+std::set<int> ModInfoRegular::doGetContents() const
{
- auto tree = contentFileTree();
- std::vector<ModInfo::EContent> contents;
+ ModDataContent* contentFeature = m_GamePlugin->feature<ModDataContent>();
- for (auto e : *tree) {
- if (e->isFile()) {
- auto suffix = e->suffix().toLower();
- if (suffix == "esp" || suffix == "esm" || suffix == "esl") {
- contents.push_back(CONTENT_PLUGIN);
- }
- else if (suffix == "bsa" || suffix == "ba2") {
- contents.push_back(CONTENT_BSA);
- }
- else if (suffix == "ini" && e->compare("meta.ini") != 0) {
- contents.push_back(CONTENT_INI);
- }
- else if (suffix == "modgroups") {
- contents.push_back(CONTENT_MODGROUP);
- }
- }
- else {
- if (e->compare("textures") == 0 || e->compare("icons") == 0 || e->compare("bookart") == 0)
- contents.push_back(CONTENT_TEXTURE);
- if (e->compare("meshes") == 0)
- contents.push_back(CONTENT_MESH);
- if (e->compare("interface") == 0 || e->compare("menus") == 0)
- contents.push_back(CONTENT_INTERFACE);
- if (e->compare("music") == 0 || e->compare("sound") == 0)
- contents.push_back(CONTENT_SOUND);
- if (e->compare("scripts") == 0)
- contents.push_back(CONTENT_SCRIPT);
- if (e->compare("SkyProc Patchers") == 0)
- contents.push_back(CONTENT_SKYPROC);
- if (e->compare("MCM") == 0)
- contents.push_back(CONTENT_MCM);
- }
- }
-
- ScriptExtender* extender = m_GamePlugin->feature<ScriptExtender>();
- if (extender != nullptr) {
- auto e = tree->findDirectory(extender->PluginPath());
- if (e) {
- contents.push_back(CONTENT_SKSEFILES);
- for (auto f : *e) {
- if (f->hasSuffix("dll")) {
- contents.push_back(CONTENT_SKSE);
- break;
- }
- }
- }
+ if (contentFeature) {
+ auto result = contentFeature->getContentsFor(contentFileTree());
+ return std::set<int>(std::begin(result), std::end(result));
}
- return contents;
+ return {};
}
diff --git a/src/modinforegular.h b/src/modinforegular.h
index e63e7570..7e70631e 100644
--- a/src/modinforegular.h
+++ b/src/modinforegular.h
@@ -416,7 +416,7 @@ private slots:
protected:
- virtual std::vector<EContent> doGetContents() const override;
+ virtual std::set<int> doGetContents() const override;
ModInfoRegular(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure);
diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp
index b0edf581..227ee12e 100644
--- a/src/modinfowithconflictinfo.cpp
+++ b/src/modinfowithconflictinfo.cpp
@@ -331,6 +331,11 @@ bool ModInfoWithConflictInfo::isValid() const {
return m_Valid.value();
}
-std::vector<ModInfo::EContent> ModInfoWithConflictInfo::getContents() const {
+const std::set<int>& ModInfoWithConflictInfo::getContents() const {
return m_Contents.value();
}
+
+bool ModInfoWithConflictInfo::hasContent(int content) const {
+ auto& contents = m_Contents.value();
+ return std::find(std::begin(contents), std::end(contents), content) != std::end(contents);
+}
diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h
index abc9f223..7109b83d 100644
--- a/src/modinfowithconflictinfo.h
+++ b/src/modinfowithconflictinfo.h
@@ -6,6 +6,7 @@
#include "thread_utils.h"
#include "modinfo.h"
+#include <set>
#include <QTime>
class ModInfoWithConflictInfo : public ModInfo
@@ -24,7 +25,16 @@ public:
/**
* @return a list of content types contained in a mod
*/
- virtual std::vector<EContent> getContents() const override;
+ virtual const std::set<int>& getContents() const override;
+
+ /**
+ * @brief Test if the mod contains the specified content.
+ *
+ * @param content ID of the content to test.
+ *
+ * @return true if the content is there, false otherwise.
+ */
+ virtual bool hasContent(int content) const override;
/**
* @brief clear all caches held for this mod
@@ -66,7 +76,7 @@ protected:
*
* @return the contents for this mod.
**/
- virtual std::vector<EContent> doGetContents() const { return {}; }
+ virtual std::set<int> doGetContents() const { return {}; }
/**
* @brief Retrieve a file tree corresponding to the underlying disk content
@@ -137,7 +147,7 @@ private:
MOShared::MemoizedLocked<std::shared_ptr<const MOBase::IFileTree>> m_FileTree;
MOShared::MemoizedLocked<bool> m_Valid;
- MOShared::MemoizedLocked<std::vector<EContent>> m_Contents;
+ MOShared::MemoizedLocked<std::set<int>> m_Contents;
MOShared::DirectoryEntry **m_DirectoryStructure;
diff --git a/src/modlist.cpp b/src/modlist.cpp
index e8c14434..06e5e9bf 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -19,12 +19,14 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modlist.h"
+#include "widgetutility.h"
#include "messagedialog.h"
#include "qtgroupingproxy.h"
#include "viewmarkingscrollbar.h"
#include "modlistsortproxy.h"
#include "pluginlist.h"
#include "settings.h"
+#include "organizercore.h"
#include "modinforegular.h"
#include "shared/directoryentry.h"
#include "shared/fileentry.h"
@@ -59,8 +61,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
-ModList::ModList(PluginContainer *pluginContainer, QObject *parent)
- : QAbstractItemModel(parent)
+ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer)
+ : QAbstractItemModel(organizer)
+ , m_Organizer(organizer)
, m_Profile(nullptr)
, m_NexusInterface(nullptr)
, m_Modified(false)
@@ -69,19 +72,6 @@ ModList::ModList(PluginContainer *pluginContainer, QObject *parent)
, m_DropOnItems(false)
, m_PluginContainer(pluginContainer)
{
- m_ContentIcons[ModInfo::CONTENT_PLUGIN] = std::make_tuple(":/MO/gui/content/plugin", QT_TR_NOOP("Game Plugins (ESP/ESM/ESL)"));
- m_ContentIcons[ModInfo::CONTENT_INTERFACE] = std::make_tuple(":/MO/gui/content/interface", QT_TR_NOOP("Interface"));
- m_ContentIcons[ModInfo::CONTENT_MESH] = std::make_tuple(":/MO/gui/content/mesh", QT_TR_NOOP("Meshes"));
- m_ContentIcons[ModInfo::CONTENT_BSA] = std::make_tuple(":/MO/gui/content/bsa", QT_TR_NOOP("Bethesda Archive"));
- m_ContentIcons[ModInfo::CONTENT_SCRIPT] = std::make_tuple(":/MO/gui/content/script", QT_TR_NOOP("Scripts (Papyrus)"));
- m_ContentIcons[ModInfo::CONTENT_SKSE] = std::make_tuple(":/MO/gui/content/skse", QT_TR_NOOP("Script Extender Plugin"));
- m_ContentIcons[ModInfo::CONTENT_SKYPROC] = std::make_tuple(":/MO/gui/content/skyproc", QT_TR_NOOP("SkyProc Patcher"));
- m_ContentIcons[ModInfo::CONTENT_SOUND] = std::make_tuple(":/MO/gui/content/sound", QT_TR_NOOP("Sound or Music"));
- m_ContentIcons[ModInfo::CONTENT_TEXTURE] = std::make_tuple(":/MO/gui/content/texture", QT_TR_NOOP("Textures"));
- m_ContentIcons[ModInfo::CONTENT_MCM] = std::make_tuple(":/MO/gui/content/menu", QT_TR_NOOP("MCM Configuration"));
- m_ContentIcons[ModInfo::CONTENT_INI] = std::make_tuple(":/MO/gui/content/inifile", QT_TR_NOOP("INI files"));
- m_ContentIcons[ModInfo::CONTENT_MODGROUP] = std::make_tuple(":/MO/gui/content/modgroup", QT_TR_NOOP("ModGroup files"));
-
m_LastCheck.start();
}
@@ -188,32 +178,24 @@ QString ModList::getConflictFlagText(ModInfo::EConflictFlag flag, ModInfo::Ptr m
}
-QVariantList ModList::contentsToIcons(const std::vector<ModInfo::EContent> &contents) const
+QVariantList ModList::contentsToIcons(const std::set<int> &contents) const
{
QVariantList result;
- std::set<ModInfo::EContent> contentsSet(contents.begin(), contents.end());
- for (auto iter = m_ContentIcons.begin(); iter != m_ContentIcons.end(); ++iter) {
- if (contentsSet.find(iter->first) != contentsSet.end()) {
- result.append(std::get<0>(iter->second));
- } else {
- result.append(QString());
- }
- }
+ m_Organizer->modDataContents().forEachContentInOrOut(
+ contents,
+ [&result](auto const& content) { result.append(content.icon()); },
+ [&result](auto const&) { result.append(QString()); });
return result;
}
-QString ModList::contentsToToolTip(const std::vector<ModInfo::EContent> &contents) const
+QString ModList::contentsToToolTip(const std::set<int> &contents) const
{
QString result("<table cellspacing=7>");
-
- std::set<ModInfo::EContent> contentsSet(contents.begin(), contents.end());
- for (auto iter = m_ContentIcons.begin(); iter != m_ContentIcons.end(); ++iter) {
- if (contentsSet.find(iter->first) != contentsSet.end()) {
- result.append(QString("<tr><td><img src=\"%1\" width=32/></td>"
- "<td valign=\"middle\">%2</td></tr>")
- .arg(std::get<0>(iter->second)).arg(tr(std::get<1>(iter->second).toStdString().c_str())));
- }
- }
+ m_Organizer->modDataContents().forEachContentIn(contents, [&result](auto const& content) {
+ result.append(QString("<tr><td><img src=\"%1\" width=32/></td>"
+ "<td valign=\"middle\">%2</td></tr>")
+ .arg(content.icon()).arg(content.name()));
+ });
result.append("</table>");
return result;
}
@@ -681,6 +663,13 @@ QVariant ModList::headerData(int section, Qt::Orientation orientation,
return getColumnToolTip(section);
} else if (role == Qt::TextAlignmentRole) {
return QVariant(Qt::AlignCenter);
+ } else if (role == MOBase::EnabledColumnRole) {
+ if (section == COL_CONTENT) {
+ return !m_Organizer->modDataContents().empty();
+ }
+ else {
+ return true;
+ }
}
}
return QAbstractItemModel::headerData(section, orientation, role);
@@ -1309,7 +1298,7 @@ QString ModList::getColumnName(int column)
}
-QString ModList::getColumnToolTip(int column)
+QString ModList::getColumnToolTip(int column) const
{
switch (column) {
case COL_NAME: return tr("Name of your mods");
@@ -1321,24 +1310,18 @@ QString ModList::getColumnToolTip(int column)
case COL_MODID: return tr("Id of the mod as used on Nexus.");
case COL_CONFLICTFLAGS: return tr("Indicators of file conflicts between mods.");
case COL_FLAGS: return tr("Emblems to highlight things that might require attention.");
- case COL_CONTENT: return tr("Depicts the content of the mod:<br>"
- "<table cellspacing=7>"
- "<tr><td><img src=\":/MO/gui/content/plugin\" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/interface\" width=32/></td><td>Interface</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/mesh\" width=32/></td><td>Meshes</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/bsa\" width=32/></td><td>BSA</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/texture\" width=32/></td><td>Textures</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/sound\" width=32/></td><td>Sounds</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/music\" width=32/></td><td>Music</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/string\" width=32/></td><td>Strings</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/script\" width=32/></td><td>Scripts (Papyrus)</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/skse\" width=32/></td><td>Script Extender plugins</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/skyproc\" width=32/></td><td>SkyProc Patcher</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/menu\" width=32/></td><td>Mod Configuration Menu</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/inifile\" width=32/></td><td>INI files</td></tr>"
- "<tr><td><img src=\":/MO/gui/content/modgroup\" width=32/></td><td>ModGroup files</td></tr>"
-
- "</table>");
+ case COL_CONTENT: {
+ auto& contents = m_Organizer->modDataContents();
+ if (m_Organizer->modDataContents().empty()) {
+ return QString();
+ }
+ QString result = tr("Depicts the content of the mod:") + "<br>" + "<table cellspacing=7>";
+ m_Organizer->modDataContents().forEachContent([&result](auto const& content) {
+ result += QString("<tr><td><img src=\"%1\" width=32/></td><td>%2</td></tr>")
+ .arg(content.icon()).arg(content.name());
+ });
+ return result + "</table>";
+ };
case COL_INSTALLTIME: return tr("Time this mod was installed");
case COL_NOTES: return tr("User notes about the mod");
default: return tr("unknown");
diff --git a/src/modlist.h b/src/modlist.h
index 4d456a9a..8c29300d 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -20,7 +20,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef MODLIST_H
#define MODLIST_H
-
+#include "moddatacontent.h"
#include "categories.h"
#include "nexusinterface.h"
#include "modinfo.h"
@@ -42,6 +42,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
class QSortFilterProxyModel;
class PluginContainer;
+class OrganizerCore;
/**
* Model presenting an overview of the installed mod
@@ -78,7 +79,7 @@ public:
* @brief constructor
* @todo ensure this view works without a profile set, otherwise there are intransparent dependencies on the initialisation order
**/
- ModList(PluginContainer *pluginContainer, QObject *parent = nullptr);
+ ModList(PluginContainer *pluginContainer, OrganizerCore *parent);
~ModList();
@@ -282,11 +283,11 @@ private:
QString getConflictFlagText(ModInfo::EConflictFlag flag, ModInfo::Ptr modInfo) const;
- static QString getColumnToolTip(int column);
+ QString getColumnToolTip(int column) const;
- QVariantList contentsToIcons(const std::vector<ModInfo::EContent> &content) const;
+ QVariantList contentsToIcons(const std::set<int> &contentIds) const;
- QString contentsToToolTip(const std::vector<ModInfo::EContent> &contents) const;
+ QString contentsToToolTip(const std::set<int> &contentsIds) const;
ModList::EColumn getEnabledColumn(int index) const;
@@ -328,6 +329,7 @@ private:
private:
+ OrganizerCore *m_Organizer;
Profile *m_Profile;
NexusInterface *m_NexusInterface;
@@ -352,8 +354,6 @@ private:
SignalModStateChanged m_ModStateChanged;
SignalModMoved m_ModMoved;
- std::map<ModInfo::EContent, std::tuple<QString, QString> > m_ContentIcons;
-
QElapsedTimer m_LastCheck;
PluginContainer *m_PluginContainer;
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 018b9760..ab6dd852 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -22,6 +22,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "profile.h"
#include "messagedialog.h"
#include "qtgroupingproxy.h"
+#include "organizercore.h"
+
#include <log.h>
#include <QMenu>
#include <QCheckBox>
@@ -33,8 +35,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
-ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent)
- : QSortFilterProxyModel(parent)
+ModListSortProxy::ModListSortProxy(Profile* profile, OrganizerCore * organizer)
+ : QSortFilterProxyModel(organizer)
+ , m_Organizer(organizer)
, m_Profile(profile)
, m_FilterActive(false)
, m_FilterMode(FilterAnd)
@@ -177,21 +180,16 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
}
} break;
case ModList::COL_CONTENT: {
- std::vector<ModInfo::EContent> lContent = leftMod->getContents();
- std::vector<ModInfo::EContent> rContent = rightMod->getContents();
- if (lContent.size() != rContent.size()) {
- lt = lContent.size() < rContent.size();
- }
-
- int lValue = 0;
- int rValue = 0;
- for (ModInfo::EContent content : lContent) {
- lValue += 2 << (unsigned int)content;
- }
- for (ModInfo::EContent content : rContent) {
- rValue += 2 << (unsigned int)content;
- }
-
+ const auto& lContents = leftMod->getContents();
+ const auto& rContents = rightMod->getContents();
+ unsigned int lValue = 0;
+ unsigned int rValue = 0;
+ m_Organizer->modDataContents().forEachContentIn(lContents, [&lValue](auto const& content) {
+ lValue += 2U << static_cast<unsigned int>(content.id());
+ });
+ m_Organizer->modDataContents().forEachContentIn(rContents, [&rValue](auto const& content) {
+ rValue += 2U << static_cast<unsigned int>(content.id());
+ });
lt = lValue < rValue;
} break;
case ModList::COL_NAME: {
@@ -449,7 +447,7 @@ bool ModListSortProxy::categoryMatchesMod(
bool ModListSortProxy::contentMatchesMod(ModInfo::Ptr info, bool enabled, int content) const
{
- return info->hasContent(static_cast<ModInfo::EContent>(content));
+ return info->hasContent(content);
}
bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h
index 3a29b7f7..90b6251e 100644
--- a/src/modlistsortproxy.h
+++ b/src/modlistsortproxy.h
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modlist.h"
class Profile;
+class OrganizerCore;
class ModListSortProxy : public QSortFilterProxyModel
{
@@ -72,7 +73,7 @@ public:
public:
- explicit ModListSortProxy(Profile *profile, QObject *parent = 0);
+ explicit ModListSortProxy(Profile *profile, OrganizerCore *organizer);
void setProfile(Profile *profile);
@@ -154,6 +155,8 @@ private slots:
void postDataChanged();
private:
+ OrganizerCore* m_Organizer;
+
Profile* m_Profile;
std::vector<Criteria> m_Criteria;
QString m_Filter;
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 6e4b3fbf..14f85ac0 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -135,6 +135,16 @@ OrganizerCore::OrganizerCore(Settings &settings)
connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)),
&m_PluginList, SLOT(managedGameChanged(MOBase::IPluginGame const *)));
+ connect(this, &OrganizerCore::managedGameChanged, [this](IPluginGame const* gamePlugin) {
+ ModDataContent* contentFeature = gamePlugin->feature<ModDataContent>();
+ if (contentFeature) {
+ m_Contents = ModDataContentHolder(contentFeature->getAllContents());
+ }
+ else {
+ m_Contents = ModDataContentHolder();
+ }
+ });
+
connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter,
&DelayedFileWriterBase::write);
diff --git a/src/organizercore.h b/src/organizercore.h
index a4d1a799..00765694 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -19,6 +19,7 @@
#include <delayedfilewriter.h>
#include <boost/signals2.hpp>
#include "executableinfo.h"
+#include "moddatacontent.h"
#include <log.h>
#include <QDir>
@@ -82,6 +83,110 @@ private:
typedef boost::signals2::signal<void (const QString&)> SignalModInstalled;
public:
+
+ /**
+ * Small holder for the game content returned by the ModDataContent feature (the
+ * list of all possible contents, not the per-mod content).
+ */
+ struct ModDataContentHolder {
+
+ using Content = ModDataContent::Content;
+
+ /**
+ * @return true if the hold list of contents is empty, false otherwise.
+ */
+ bool empty() const { return m_Contents.empty(); }
+
+ /**
+ * @param id ID of the content to retrieve.
+ *
+ * @return the content with the given ID, or a null pointer if it is not found.
+ */
+ const Content* findById(int id) const {
+ auto it = std::find_if(std::begin(m_Contents), std::end(m_Contents), [&id](auto const& content) { return content.id() == id; });
+ return it == std::end(m_Contents) ? nullptr : &(*it);
+ }
+
+ /**
+ * Apply the given function to each content whose ID is in the given set.
+ *
+ * @param ids The set of content IDs.
+ * @param fn The function to apply.
+ * @param includeFilter true to also apply the function to filter-only contents, false otherwise.
+ */
+ template <class Fn>
+ void forEachContentIn(std::set<int> const& ids, Fn const& fn, bool includeFilter = false) const {
+ for (const auto& content : m_Contents) {
+ if ((includeFilter || !content.isOnlyForFilter())
+ && ids.find(content.id()) != ids.end()) {
+ fn(content);
+ }
+ }
+ }
+
+ /**
+ * @brief Apply fnIn to each content whose ID is in the given set, and fnOut to each content not in the
+ * given set, excluding filter-only content (from both cases) unless includeFilter is true.
+ *
+ * @param ids The set of content IDs.
+ * @param fnIn Function to apply to content whose IDs are in ids.
+ * @param fnOut Function to apply to content whose IDs are not in ids.
+ * @param includeFilter true to also apply the function to filter-only contents, false otherwise.
+ */
+ template <class FnIn, class FnOut>
+ void forEachContentInOrOut(std::set<int> const& ids, FnIn const& fnIn, FnOut const& fnOut, bool includeFilter = false) const {
+ for (const auto& content : m_Contents) {
+ if ((includeFilter || !content.isOnlyForFilter())) {
+ if (ids.find(content.id()) != ids.end()) {
+ fnIn(content);
+ }
+ else {
+ fnOut(content);
+ }
+ }
+ }
+ }
+
+ /**
+ * Apply the given function to each content.
+ *
+ * @param fn The function to apply.
+ * @param includeFilter true to also apply the function to filter-only contents, false otherwise.
+ */
+ template <class Fn>
+ void forEachContent(Fn const& fn, bool includeFilter = false) const {
+ for (const auto& content : m_Contents) {
+ if (includeFilter || !content.isOnlyForFilter()) {
+ fn(content);
+ }
+ }
+ }
+
+
+ ModDataContentHolder& operator=(ModDataContentHolder const&) = delete;
+ ModDataContentHolder& operator=(ModDataContentHolder&&) = default;
+
+ private:
+
+ std::vector<Content> m_Contents;
+
+ /**
+ * @brief Construct a ModDataContentHolder without any contents (e.g., if the feature is
+ * missing).
+ */
+ ModDataContentHolder() { }
+
+ /**
+ * @brief Construct a ModDataContentHold holding the given list of contents.
+ */
+ ModDataContentHolder(std::vector<ModDataContent::Content> contents) :
+ m_Contents(std::move(contents)) { }
+
+ friend class OrganizerCore;
+ };
+
+public:
+
static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); }
OrganizerCore(Settings &settings);
@@ -122,6 +227,12 @@ public:
MOBase::IPluginGame const *managedGame() const;
+ /**
+ * @return the list of contents for the currently managed game, or an empty vector
+ * if the game plugin does not implement the ModDataContent feature.
+ */
+ const ModDataContentHolder& modDataContents() const { return m_Contents; }
+
bool isArchivesInit() const { return m_ArchivesInit; }
bool saveCurrentLists();
@@ -305,6 +416,7 @@ private:
PluginContainer *m_PluginContainer;
QString m_GameName;
MOBase::IPluginGame *m_GamePlugin;
+ ModDataContentHolder m_Contents;
Profile *m_CurrentProfile;
diff --git a/src/resources.qrc b/src/resources.qrc
index c54d5bfc..8e4d9156 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -106,6 +106,7 @@
<file alias="menu">resources/contents/config.png</file>
<file alias="inifile">resources/contents/feather-and-scroll.png</file>
<file alias="modgroup">resources/contents/xedit.png</file>
+ <file alias="material">resources/contents/f4-nuclear.png</file>
</qresource>
<qresource prefix="/qt/etc">
<file>qt.conf</file>
diff --git a/src/resources/contents/f4-nuclear.png b/src/resources/contents/f4-nuclear.png
new file mode 100644
index 00000000..c9d3c2e5
--- /dev/null
+++ b/src/resources/contents/f4-nuclear.png
Binary files differ