summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/downloadmanager.cpp18
-rw-r--r--src/downloadmanager.h18
-rw-r--r--src/downloadmanagerproxy.cpp32
-rw-r--r--src/downloadmanagerproxy.h20
-rw-r--r--src/installationmanager.cpp25
-rw-r--r--src/installationmanager.h15
-rw-r--r--src/iuserinterface.h2
-rw-r--r--src/mainwindow.cpp25
-rw-r--r--src/mainwindow.h10
-rw-r--r--src/modlist.cpp19
-rw-r--r--src/modlist.h38
-rw-r--r--src/modlistproxy.cpp33
-rw-r--r--src/modlistproxy.h20
-rw-r--r--src/organizercore.cpp71
-rw-r--r--src/organizercore.h32
-rw-r--r--src/organizerproxy.cpp74
-rw-r--r--src/organizerproxy.h39
-rw-r--r--src/plugincontainer.cpp426
-rw-r--r--src/plugincontainer.h96
-rw-r--r--src/pluginlist.cpp27
-rw-r--r--src/pluginlist.h51
-rw-r--r--src/pluginlistproxy.cpp33
-rw-r--r--src/pluginlistproxy.h19
-rw-r--r--src/previewgenerator.cpp33
-rw-r--r--src/previewgenerator.h12
-rw-r--r--src/proxyutils.h15
-rw-r--r--src/settings.cpp10
-rw-r--r--src/settings.h3
28 files changed, 820 insertions, 396 deletions
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index 6817e7e7..762e3cdb 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -201,7 +201,7 @@ QString DownloadManager::DownloadInfo::currentURL()
DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) :
- IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false),
+ m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false),
m_ParentWidget(nullptr)
{
m_OrganizerCore = dynamic_cast<OrganizerCore*>(parent);
@@ -1797,24 +1797,24 @@ QString DownloadManager::downloadPath(int id)
return getFilePath(id);
}
-bool DownloadManager::onDownloadComplete(const std::function<void(int)>& callback)
+boost::signals2::connection DownloadManager::onDownloadComplete(const std::function<void(int)>& callback)
{
- return m_DownloadComplete.connect(callback).connected();
+ return m_DownloadComplete.connect(callback);
}
-bool DownloadManager::onDownloadPaused(const std::function<void(int)>& callback)
+boost::signals2::connection DownloadManager::onDownloadPaused(const std::function<void(int)>& callback)
{
- return m_DownloadPaused.connect(callback).connected();
+ return m_DownloadPaused.connect(callback);
}
-bool DownloadManager::onDownloadFailed(const std::function<void(int)>& callback)
+boost::signals2::connection DownloadManager::onDownloadFailed(const std::function<void(int)>& callback)
{
- return m_DownloadFailed.connect(callback).connected();
+ return m_DownloadFailed.connect(callback);
}
-bool DownloadManager::onDownloadRemoved(const std::function<void(int)>& callback)
+boost::signals2::connection DownloadManager::onDownloadRemoved(const std::function<void(int)>& callback)
{
- return m_DownloadRemoved.connect(callback).connected();
+ return m_DownloadRemoved.connect(callback);
}
int DownloadManager::indexByName(const QString &fileName) const
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
index bd8e9799..953ab88b 100644
--- a/src/downloadmanager.h
+++ b/src/downloadmanager.h
@@ -48,7 +48,7 @@ class OrganizerCore;
/*!
* \brief manages downloading of files and provides progress information for gui elements
**/
-class DownloadManager : public MOBase::IDownloadManager
+class DownloadManager : public QObject
{
Q_OBJECT
@@ -127,6 +127,8 @@ private:
DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple<int,int,int,int,int>(0,0,0,0,0)), m_HasData(false) {}
};
+ friend class DownloadManagerProxy;
+
using SignalDownloadCallback = boost::signals2::signal<void(int)>;
public:
@@ -366,14 +368,14 @@ public:
public: // IDownloadManager interface:
- int startDownloadURLs(const QStringList &urls) override;
- int startDownloadNexusFile(int modID, int fileID) override;
- QString downloadPath(int id) override;
+ int startDownloadURLs(const QStringList &urls);
+ int startDownloadNexusFile(int modID, int fileID);
+ QString downloadPath(int id);
- bool onDownloadComplete(const std::function<void(int)>& callback) override;
- bool onDownloadPaused(const std::function<void(int)>& callback) override;
- bool onDownloadFailed(const std::function<void(int)>& callback) override;
- bool onDownloadRemoved(const std::function<void(int)>& callback) override;
+ boost::signals2::connection onDownloadComplete(const std::function<void(int)>& callback);
+ boost::signals2::connection onDownloadPaused(const std::function<void(int)>& callback);
+ boost::signals2::connection onDownloadFailed(const std::function<void(int)>& callback);
+ boost::signals2::connection onDownloadRemoved(const std::function<void(int)>& callback);
/**
* @brief retrieve a download index from the filename
diff --git a/src/downloadmanagerproxy.cpp b/src/downloadmanagerproxy.cpp
index 4a5c9b6b..95099044 100644
--- a/src/downloadmanagerproxy.cpp
+++ b/src/downloadmanagerproxy.cpp
@@ -4,10 +4,32 @@
#include "organizerproxy.h"
using namespace MOBase;
+using namespace MOShared;
-DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, IDownloadManager* downloadManager) :
+DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, DownloadManager* downloadManager) :
m_OrganizerProxy(oproxy), m_Proxied(downloadManager) { }
+DownloadManagerProxy::~DownloadManagerProxy()
+{
+ disconnectSignals();
+}
+
+void DownloadManagerProxy::connectSignals()
+{
+ m_Connections.push_back(m_Proxied->onDownloadComplete(callSignalIfPluginActive(m_OrganizerProxy, m_DownloadComplete)));
+ m_Connections.push_back(m_Proxied->onDownloadFailed(callSignalIfPluginActive(m_OrganizerProxy, m_DownloadFailed)));
+ m_Connections.push_back(m_Proxied->onDownloadRemoved(callSignalIfPluginActive(m_OrganizerProxy, m_DownloadRemoved)));
+ m_Connections.push_back(m_Proxied->onDownloadPaused(callSignalIfPluginActive(m_OrganizerProxy, m_DownloadPaused)));
+}
+
+void DownloadManagerProxy::disconnectSignals()
+{
+ for (auto& conn : m_Connections) {
+ conn.disconnect();
+ }
+ m_Connections.clear();
+}
+
int DownloadManagerProxy::startDownloadURLs(const QStringList& urls)
{
return m_Proxied->startDownloadURLs(urls);
@@ -25,20 +47,20 @@ QString DownloadManagerProxy::downloadPath(int id)
bool DownloadManagerProxy::onDownloadComplete(const std::function<void(int)>& callback)
{
- return m_Proxied->onDownloadComplete(MOShared::callIfPluginActive(m_OrganizerProxy, callback));
+ return m_DownloadComplete.connect(callback).connected();
}
bool DownloadManagerProxy::onDownloadPaused(const std::function<void(int)>& callback)
{
- return m_Proxied->onDownloadPaused(MOShared::callIfPluginActive(m_OrganizerProxy, callback));
+ return m_DownloadPaused.connect(callback).connected();
}
bool DownloadManagerProxy::onDownloadFailed(const std::function<void(int)>& callback)
{
- return m_Proxied->onDownloadFailed(MOShared::callIfPluginActive(m_OrganizerProxy, callback));
+ return m_DownloadFailed.connect(callback).connected();
}
bool DownloadManagerProxy::onDownloadRemoved(const std::function<void(int)>& callback)
{
- return m_Proxied->onDownloadRemoved(MOShared::callIfPluginActive(m_OrganizerProxy, callback));
+ return m_DownloadRemoved.connect(callback).connected();
}
diff --git a/src/downloadmanagerproxy.h b/src/downloadmanagerproxy.h
index f41b60c6..729fde91 100644
--- a/src/downloadmanagerproxy.h
+++ b/src/downloadmanagerproxy.h
@@ -2,6 +2,7 @@
#define DOWNLOADMANAGERPROXY_H
#include <idownloadmanager.h>
+#include "downloadmanager.h"
class OrganizerProxy;
@@ -10,8 +11,8 @@ class DownloadManagerProxy : public MOBase::IDownloadManager
public:
- DownloadManagerProxy(OrganizerProxy* oproxy, IDownloadManager* downloadManager);
- virtual ~DownloadManagerProxy() { }
+ DownloadManagerProxy(OrganizerProxy* oproxy, DownloadManager* downloadManager);
+ virtual ~DownloadManagerProxy();
int startDownloadURLs(const QStringList& urls) override;
int startDownloadNexusFile(int modID, int fileID) override;
@@ -24,8 +25,21 @@ public:
private:
+ friend class OrganizerProxy;
+
+ // See OrganizerProxy::connectSignals().
+ void connectSignals();
+ void disconnectSignals();
+
OrganizerProxy* m_OrganizerProxy;
- IDownloadManager* m_Proxied;
+ DownloadManager* m_Proxied;
+
+ DownloadManager::SignalDownloadCallback m_DownloadComplete;
+ DownloadManager::SignalDownloadCallback m_DownloadPaused;
+ DownloadManager::SignalDownloadCallback m_DownloadFailed;
+ DownloadManager::SignalDownloadCallback m_DownloadRemoved;
+
+ std::vector<boost::signals2::connection> m_Connections;
};
#endif // ORGANIZERPROXY_H
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index 078f7088..e9324b41 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -117,9 +117,6 @@ InstallationManager::~InstallationManager()
void InstallationManager::setParentWidget(QWidget *widget)
{
m_ParentWidget = widget;
- for (IPluginInstaller *installer : m_Installers) {
- installer->setParentWidget(widget);
- }
}
void InstallationManager::setPluginContainer(const PluginContainer* pluginContainer)
@@ -700,11 +697,13 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
archiveOpen ? ArchiveFileTree::makeTree(*m_ArchiveHandler) : nullptr;
IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED;
- std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) {
- return LHS->priority() > RHS->priority();
- });
+ auto installers = m_PluginContainer->plugins<IPluginInstaller>();
+
+ std::sort(installers.begin(), installers.end(), [] (IPluginInstaller* lhs, IPluginInstaller* rhs) {
+ return lhs->priority() > rhs->priority();
+ });
- for (IPluginInstaller *installer : m_Installers) {
+ for (IPluginInstaller *installer : installers) {
// don't use inactive installers (installer can't be null here but vc static code analysis thinks it could)
if ((installer == nullptr) || !m_PluginContainer->isEnabled(installer)) {
continue;
@@ -846,12 +845,6 @@ QString InstallationManager::getErrorString(Archive::Error errorCode)
}
}
-void InstallationManager::registerInstaller(IPluginInstaller *installer)
-{
- m_Installers.push_back(installer);
- installer->setInstallationManager(this);
-}
-
QStringList InstallationManager::getSupportedExtensions() const
{
std::set<QString, CaseInsensitive> supportedExtensions({ "zip", "rar", "7z", "fomod", "001" });
@@ -868,7 +861,8 @@ QStringList InstallationManager::getSupportedExtensions() const
void InstallationManager::notifyInstallationStart(QString const& archive, bool reinstallation, ModInfo::Ptr currentMod)
{
- for (auto* installer : m_Installers) {
+ auto& installers = m_PluginContainer->plugins<IPluginInstaller>();
+ for (auto* installer : installers) {
if (m_PluginContainer->isEnabled(installer)) {
installer->onInstallationStart(archive, reinstallation, currentMod.get());
}
@@ -879,7 +873,8 @@ void InstallationManager::notifyInstallationEnd(
MOBase::IPluginInstaller::EInstallResult result,
ModInfo::Ptr newMod)
{
- for (auto* installer : m_Installers) {
+ auto& installers = m_PluginContainer->plugins<IPluginInstaller>();
+ for (auto* installer : installers) {
if (m_PluginContainer->isEnabled(installer)) {
installer->onInstallationEnd(result, newMod.get());
}
diff --git a/src/installationmanager.h b/src/installationmanager.h
index 2378178a..277c276a 100644
--- a/src/installationmanager.h
+++ b/src/installationmanager.h
@@ -129,12 +129,6 @@ public:
static QString getErrorString(Archive::Error errorCode);
/**
- * @brief register an installer-plugin
- * @param the installer to register
- */
- void registerInstaller(MOBase::IPluginInstaller *installer);
-
- /**
* @return the extensions of archives supported by this installation manager.
*/
QStringList getSupportedExtensions() const override;
@@ -256,13 +250,6 @@ signals:
private:
- struct ByPriority {
- bool operator()(MOBase::IPluginInstaller *LHS, MOBase::IPluginInstaller *RHS) const
- {
- return LHS->priority() > RHS->priority();
- }
- };
-
struct CaseInsensitive {
bool operator() (const QString &LHS, const QString &RHS) const
{
@@ -295,8 +282,6 @@ private:
QString m_ModsDirectory;
QString m_DownloadsDirectory;
- std::vector<MOBase::IPluginInstaller*> m_Installers;
-
// Archive management.
std::unique_ptr<Archive> m_ArchiveHandler;
QString m_CurrentFile;
diff --git a/src/iuserinterface.h b/src/iuserinterface.h
index a2a91e62..277d5cf5 100644
--- a/src/iuserinterface.h
+++ b/src/iuserinterface.h
@@ -17,8 +17,6 @@ public:
virtual void installTranslator(const QString &name) = 0;
- virtual void disconnectPlugins() = 0;
-
virtual bool closeWindow() = 0;
virtual void setWindowEnabled(bool enabled) = 0;
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d0ed5029..a270fd10 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -474,7 +474,8 @@ MainWindow::MainWindow(Settings &settings
if (m_PluginContainer.implementInterface<IPluginModPage>(plugin)) { updateModPageMenu(); } });
connect(&m_PluginContainer, &PluginContainer::pluginDisabled, this, [this](IPlugin* plugin) {
if (m_PluginContainer.implementInterface<IPluginModPage>(plugin)) { updateModPageMenu(); } });
-
+ connect(&m_PluginContainer, &PluginContainer::pluginRegistered, this, &MainWindow::onPluginRegistrationChanged);
+ connect(&m_PluginContainer, &PluginContainer::pluginUnregistered, this, &MainWindow::onPluginRegistrationChanged);
connect(&m_OrganizerCore, &OrganizerCore::modInstalled, this, &MainWindow::modInstalled);
connect(&m_OrganizerCore, &OrganizerCore::close, this, &QMainWindow::close);
@@ -511,8 +512,6 @@ MainWindow::MainWindow(Settings &settings
m_Tutorial.expose("espList", m_OrganizerCore.pluginList());
m_OrganizerCore.setUserInterface(this);
- m_PluginContainer.setUserInterface(this);
- connect(this, &MainWindow::userInterfaceInitialized, &m_OrganizerCore, &OrganizerCore::userInterfaceInitialized);
for (const QString &fileName : m_PluginContainer.pluginFileNames()) {
installTranslator(QFileInfo(fileName).baseName());
}
@@ -704,7 +703,6 @@ MainWindow::~MainWindow()
try {
cleanup();
- m_PluginContainer.setUserInterface(nullptr);
m_OrganizerCore.setUserInterface(nullptr);
if (m_IntegratedBrowser) {
@@ -743,14 +741,6 @@ void MainWindow::onRequestsChanged(const APIStats& stats, const APIUserAccount&
}
-void MainWindow::disconnectPlugins()
-{
- if (ui->actionTool->menu() != nullptr) {
- ui->actionTool->menu()->clear();
- }
-}
-
-
void MainWindow::resizeLists(bool pluginListCustom)
{
// ensure the columns aren't so small you can't see them any more
@@ -1412,8 +1402,8 @@ void MainWindow::showEvent(QShowEvent *event)
m_WasVisible = true;
updateProblemsButton();
- // Notify plugin that the UI is initialized:
- emit userInterfaceInitialized();
+ // Notify plugin that the MO2 is ready:
+ m_PluginContainer.startPlugins(this);
}
}
@@ -5190,6 +5180,13 @@ void MainWindow::on_actionSettings_triggered()
}
}
+void MainWindow::onPluginRegistrationChanged()
+{
+ updateModPageMenu();
+ scheduleCheckForProblems();
+ updateDownloadView();
+}
+
void MainWindow::on_actionNexus_triggered()
{
const IPluginGame *game = m_OrganizerCore.managedGame();
diff --git a/src/mainwindow.h b/src/mainwindow.h
index da7bb6ee..7a1c683d 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -133,8 +133,6 @@ public:
void installTranslator(const QString &name);
- virtual void disconnectPlugins();
-
void displayModInformation(
ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) override;
@@ -168,12 +166,6 @@ signals:
*/
void styleChanged(const QString &styleFile);
- /**
- * @brief emitted when the user interface has been completely initialized
- */
- void userInterfaceInitialized();
-
-
void modListDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void checkForProblemsDone();
@@ -635,6 +627,8 @@ private slots: // ui slots
void on_saveModsButton_clicked();
void on_managedArchiveLabel_linkHovered(const QString &link);
+ void onPluginRegistrationChanged();
+
void storeSettings();
void readSettings();
void setupModList();
diff --git a/src/modlist.cpp b/src/modlist.cpp
index d7324e7f..237f65fa 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -1020,19 +1020,19 @@ bool ModList::setPriority(const QString &name, int newPriority)
}
}
-bool ModList::onModInstalled(const std::function<void(MOBase::IModInterface*)>& func)
+boost::signals2::connection ModList::onModInstalled(const std::function<void(MOBase::IModInterface*)>& func)
{
- return m_ModInstalled.connect(func).connected();
+ return m_ModInstalled.connect(func);
}
-bool ModList::onModRemoved(const std::function<void(QString const&)>& func)
+boost::signals2::connection ModList::onModRemoved(const std::function<void(QString const&)>& func)
{
- return m_ModRemoved.connect(func).connected();
+ return m_ModRemoved.connect(func);
}
-bool ModList::onModStateChanged(const std::function<void(const std::map<QString, ModStates>&)>& func)
+boost::signals2::connection ModList::onModStateChanged(const std::function<void(const std::map<QString, IModList::ModStates>&)>& func)
{
- return m_ModStateChanged.connect(func).connected();
+ return m_ModStateChanged.connect(func);
}
void ModList::notifyModInstalled(MOBase::IModInterface* mod) const
@@ -1047,7 +1047,7 @@ void ModList::notifyModRemoved(QString const& modName) const
void ModList::notifyModStateChanged(QList<unsigned int> modIndices) const
{
- std::map<QString, ModStates> mods;
+ std::map<QString, IModList::ModStates> mods;
for (auto modIndex : modIndices) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
mods.emplace(modInfo->name(), state(modIndex));
@@ -1055,10 +1055,9 @@ void ModList::notifyModStateChanged(QList<unsigned int> modIndices) const
m_ModStateChanged(mods);
}
-bool ModList::onModMoved(const std::function<void (const QString &, int, int)> &func)
+boost::signals2::connection ModList::onModMoved(const std::function<void (const QString &, int, int)> &func)
{
- auto conn = m_ModMoved.connect(func);
- return conn.connected();
+ return m_ModMoved.connect(func);
}
bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent)
diff --git a/src/modlist.h b/src/modlist.h
index 385ca04c..fd26bb91 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -49,7 +49,7 @@ class OrganizerCore;
* This is used in a view in the main window of MO. It combines general information about
* the mods from ModInfo with status information from the Profile
**/
-class ModList : public QAbstractItemModel, public MOBase::IModList
+class ModList : public QAbstractItemModel
{
Q_OBJECT
@@ -70,9 +70,11 @@ public:
COL_LASTCOLUMN = COL_NOTES,
};
+ friend class ModListProxy;
+
using SignalModInstalled = boost::signals2::signal<void(MOBase::IModInterface*)>;
using SignalModRemoved = boost::signals2::signal<void(QString const&)>;
- using SignalModStateChanged = boost::signals2::signal<void (const std::map<QString, ModStates>&)>;
+ using SignalModStateChanged = boost::signals2::signal<void (const std::map<QString, MOBase::IModList::ModStates>&)>;
using SignalModMoved = boost::signals2::signal<void (const QString &, int, int)>;
public:
@@ -153,37 +155,37 @@ public:
public:
/// \copydoc MOBase::IModList::displayName
- virtual QString displayName(const QString &internalName) const override;
+ QString displayName(const QString &internalName) const;
/// \copydoc MOBase::IModList::allMods
- virtual QStringList allMods() const override;
- virtual QStringList allModsByProfilePriority(MOBase::IProfile* profile = nullptr) const override;
+ QStringList allMods() const;
+ QStringList allModsByProfilePriority(MOBase::IProfile* profile = nullptr) const;
// \copydoc MOBase::IModList::getMod
- MOBase::IModInterface* getMod(const QString& name) const override;
+ MOBase::IModInterface* getMod(const QString& name) const;
// \copydoc MOBase::IModList::remove
- bool removeMod(MOBase::IModInterface* mod) override;
+ bool removeMod(MOBase::IModInterface* mod);
/// \copydoc MOBase::IModList::state
- virtual ModStates state(const QString &name) const override;
+ MOBase::IModList::ModStates state(const QString &name) const;
/// \copydoc MOBase::IModList::setActive
- virtual bool setActive(const QString &name, bool active) override;
+ bool setActive(const QString &name, bool active);
/// \copydoc MOBase::IModList::setActive
- int setActive(const QStringList& names, bool active) override;
+ int setActive(const QStringList& names, bool active);
/// \copydoc MOBase::IModList::priority
- virtual int priority(const QString &name) const override;
+ int priority(const QString &name) const;
/// \copydoc MOBase::IModList::setPriority
- virtual bool setPriority(const QString &name, int newPriority) override;
+ bool setPriority(const QString &name, int newPriority);
- bool onModInstalled(const std::function<void(MOBase::IModInterface*)>& func) override;
- bool onModRemoved(const std::function<void(QString const&)>& func) override;
- bool onModStateChanged(const std::function<void(const std::map<QString, ModStates>&)>& func) override;
- bool onModMoved(const std::function<void (const QString &, int, int)> &func) override;
+ boost::signals2::connection onModInstalled(const std::function<void(MOBase::IModInterface*)>& func);
+ boost::signals2::connection onModRemoved(const std::function<void(QString const&)>& func);
+ boost::signals2::connection onModStateChanged(const std::function<void(const std::map<QString, MOBase::IModList::ModStates>&)>& func);
+ boost::signals2::connection onModMoved(const std::function<void (const QString &, int, int)> &func);
public: // implementation of virtual functions of QAbstractItemModel
@@ -335,7 +337,7 @@ private:
bool dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent);
- ModStates state(unsigned int modIndex) const;
+ MOBase::IModList::ModStates state(unsigned int modIndex) const;
bool moveSelection(QAbstractItemView *itemView, int direction);
@@ -359,7 +361,7 @@ private:
struct TModInfoChange {
QString name;
- QFlags<IModList::ModState> state;
+ QFlags<MOBase::IModList::ModState> state;
};
private:
diff --git a/src/modlistproxy.cpp b/src/modlistproxy.cpp
index 8fcbdbdf..6165fc3f 100644
--- a/src/modlistproxy.cpp
+++ b/src/modlistproxy.cpp
@@ -1,12 +1,35 @@
#include "modlistproxy.h"
#include "organizerproxy.h"
#include "proxyutils.h"
+#include "modlist.h"
using namespace MOBase;
+using namespace MOShared;
-ModListProxy::ModListProxy(OrganizerProxy* oproxy, IModList* modlist) :
+ModListProxy::ModListProxy(OrganizerProxy* oproxy, ModList* modlist) :
m_OrganizerProxy(oproxy), m_Proxied(modlist) { }
+ModListProxy::~ModListProxy()
+{
+ disconnectSignals();
+}
+
+void ModListProxy::connectSignals()
+{
+ m_Connections.push_back(m_Proxied->onModInstalled(callSignalIfPluginActive(m_OrganizerProxy, m_ModInstalled)));
+ m_Connections.push_back(m_Proxied->onModMoved(callSignalIfPluginActive(m_OrganizerProxy, m_ModMoved)));
+ m_Connections.push_back(m_Proxied->onModRemoved(callSignalIfPluginActive(m_OrganizerProxy, m_ModRemoved)));
+ m_Connections.push_back(m_Proxied->onModStateChanged(callSignalIfPluginActive(m_OrganizerProxy, m_ModStateChanged)));
+}
+
+void ModListProxy::disconnectSignals()
+{
+ for (auto& conn : m_Connections) {
+ conn.disconnect();
+ }
+ m_Connections.clear();
+}
+
QString ModListProxy::displayName(const QString& internalName) const
{
return m_Proxied->displayName(internalName);
@@ -59,20 +82,20 @@ bool ModListProxy::setPriority(const QString& name, int newPriority)
bool ModListProxy::onModInstalled(const std::function<void(IModInterface*)>& func)
{
- return m_Proxied->onModInstalled(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_ModInstalled.connect(func).connected();
}
bool ModListProxy::onModRemoved(const std::function<void(QString const&)>& func)
{
- return m_Proxied->onModRemoved(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_ModRemoved.connect(func).connected();
}
bool ModListProxy::onModStateChanged(const std::function<void(const std::map<QString, ModStates>&)>& func)
{
- return m_Proxied->onModStateChanged(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_ModStateChanged.connect(func).connected();
}
bool ModListProxy::onModMoved(const std::function<void(const QString&, int, int)>& func)
{
- return m_Proxied->onModMoved(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_ModMoved.connect(func).connected();
}
diff --git a/src/modlistproxy.h b/src/modlistproxy.h
index c805cf4e..162b0b43 100644
--- a/src/modlistproxy.h
+++ b/src/modlistproxy.h
@@ -2,6 +2,7 @@
#define MODLISTPROXY_H
#include <imodlist.h>
+#include "modlist.h"
class OrganizerProxy;
@@ -10,8 +11,8 @@ class ModListProxy : public MOBase::IModList
public:
- ModListProxy(OrganizerProxy* oproxy, IModList* modlist);
- virtual ~ModListProxy() { }
+ ModListProxy(OrganizerProxy* oproxy, ModList* modlist);
+ virtual ~ModListProxy();
QString displayName(const QString& internalName) const override;
QStringList allMods() const override;
@@ -30,8 +31,21 @@ public:
private:
+ friend class OrganizerProxy;
+
+ // See OrganizerProxy::connectSignals().
+ void connectSignals();
+ void disconnectSignals();
+
OrganizerProxy* m_OrganizerProxy;
- IModList* m_Proxied;
+ ModList* m_Proxied;
+
+ ModList::SignalModInstalled m_ModInstalled;
+ ModList::SignalModMoved m_ModMoved;
+ ModList::SignalModRemoved m_ModRemoved;
+ ModList::SignalModStateChanged m_ModStateChanged;
+
+ std::vector<boost::signals2::connection> m_Connections;
};
#endif // ORGANIZERPROXY_H
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index f14d3234..631e49be 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -72,6 +72,7 @@
#include <tuple>
#include <utility>
+#include "organizerproxy.h"
using namespace MOShared;
using namespace MOBase;
@@ -96,7 +97,7 @@ OrganizerCore::OrganizerCore(Settings &settings)
, m_Settings(settings)
, m_Updater(&NexusInterface::instance())
, m_ModList(m_PluginContainer, this)
- , m_PluginList(this)
+ , m_PluginList(*this)
, m_DirectoryRefresher(new DirectoryRefresher(settings.refreshThreadCount()))
, m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0))
, m_DownloadManager(&NexusInterface::instance(), this)
@@ -290,7 +291,7 @@ void OrganizerCore::connectPlugins(PluginContainer *container)
m_ModList.setPluginContainer(m_PluginContainer);
if (!m_GameName.isEmpty()) {
- m_GamePlugin = m_PluginContainer->managedGame(m_GameName);
+ m_GamePlugin = m_PluginContainer->game(m_GameName);
emit managedGameChanged(m_GamePlugin);
}
@@ -300,25 +301,6 @@ void OrganizerCore::connectPlugins(PluginContainer *container)
[&](IPlugin* plugin) { m_PluginDisabled(plugin); });
}
-void OrganizerCore::disconnectPlugins()
-{
- m_AboutToRun.disconnect_all_slots();
- m_FinishedRun.disconnect_all_slots();
- m_UserInterfaceInitialized.disconnect_all_slots();
- m_ProfileChanged.disconnect_all_slots();
- m_PluginSettingChanged.disconnect_all_slots();
-
- m_ModList.disconnectSlots();
- m_PluginList.disconnectSlots();
- m_Updater.setPluginContainer(nullptr);
- m_DownloadManager.setPluginContainer(nullptr);
- m_ModList.setPluginContainer(nullptr);
-
- m_Settings.plugins().clearPlugins();
- m_GamePlugin = nullptr;
- m_PluginContainer = nullptr;
-}
-
void OrganizerCore::setManagedGame(MOBase::IPluginGame *game)
{
m_GameName = game->gameName();
@@ -1184,58 +1166,56 @@ bool OrganizerCore::previewFile(
return true;
}
-bool OrganizerCore::onAboutToRun(
+boost::signals2::connection OrganizerCore::onAboutToRun(
const std::function<bool(const QString &)> &func)
{
- auto conn = m_AboutToRun.connect(func);
- return conn.connected();
+ return m_AboutToRun.connect(func);
}
-bool OrganizerCore::onFinishedRun(
+boost::signals2::connection OrganizerCore::onFinishedRun(
const std::function<void(const QString &, unsigned int)> &func)
{
- auto conn = m_FinishedRun.connect(func);
- return conn.connected();
+ return m_FinishedRun.connect(func);
}
-bool OrganizerCore::onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func)
+boost::signals2::connection OrganizerCore::onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func)
{
- return m_UserInterfaceInitialized.connect(func).connected();
+ return m_UserInterfaceInitialized.connect(func);
}
-bool OrganizerCore::onProfileCreated(std::function<void(MOBase::IProfile*)> const& func)
+boost::signals2::connection OrganizerCore::onProfileCreated(std::function<void(MOBase::IProfile*)> const& func)
{
- return m_ProfileCreated.connect(func).connected();
+ return m_ProfileCreated.connect(func);
}
-bool OrganizerCore::onProfileRenamed(std::function<void(MOBase::IProfile*, QString const&, QString const&)> const& func)
+boost::signals2::connection OrganizerCore::onProfileRenamed(std::function<void(MOBase::IProfile*, QString const&, QString const&)> const& func)
{
- return m_ProfileRenamed.connect(func).connected();
+ return m_ProfileRenamed.connect(func);
}
-bool OrganizerCore::onProfileRemoved(std::function<void(QString const&)> const& func)
+boost::signals2::connection OrganizerCore::onProfileRemoved(std::function<void(QString const&)> const& func)
{
- return m_ProfileRemoved.connect(func).connected();
+ return m_ProfileRemoved.connect(func);
}
-bool OrganizerCore::onProfileChanged(std::function<void(IProfile*, IProfile*)> const& func)
+boost::signals2::connection OrganizerCore::onProfileChanged(std::function<void(IProfile*, IProfile*)> const& func)
{
- return m_ProfileChanged.connect(func).connected();
+ return m_ProfileChanged.connect(func);
}
-bool OrganizerCore::onPluginSettingChanged(std::function<void(QString const&, const QString& key, const QVariant&, const QVariant&)> const& func)
+boost::signals2::connection OrganizerCore::onPluginSettingChanged(std::function<void(QString const&, const QString& key, const QVariant&, const QVariant&)> const& func)
{
- return m_PluginSettingChanged.connect(func).connected();
+ return m_PluginSettingChanged.connect(func);
}
-bool OrganizerCore::onPluginEnabled(std::function<void(const IPlugin*)> const& func)
+boost::signals2::connection OrganizerCore::onPluginEnabled(std::function<void(const IPlugin*)> const& func)
{
- return m_PluginEnabled.connect(func).connected();
+ return m_PluginEnabled.connect(func);
}
-bool OrganizerCore::onPluginDisabled(std::function<void(const IPlugin*)> const& func)
+boost::signals2::connection OrganizerCore::onPluginDisabled(std::function<void(const IPlugin*)> const& func)
{
- return m_PluginDisabled.connect(func).connected();
+ return m_PluginDisabled.connect(func);
}
void OrganizerCore::refresh(bool saveChanges)
@@ -1531,6 +1511,11 @@ IPluginGame const *OrganizerCore::managedGame() const
return m_GamePlugin;
}
+IOrganizer const* OrganizerCore::managedGameOrganizer() const
+{
+ return m_PluginContainer->requirements(m_GamePlugin).m_Organizer;
+}
+
std::vector<QString> OrganizerCore::enabledArchives()
{
std::vector<QString> result;
diff --git a/src/organizercore.h b/src/organizercore.h
index 23b624e8..f2b904cd 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -61,6 +61,8 @@ class OrganizerCore : public QObject, public MOBase::IPluginDiagnose
private:
+ friend class OrganizerProxy;
+
struct SignalCombinerAnd
{
typedef bool result_type;
@@ -87,7 +89,7 @@ private:
using SignalProfileRemoved = boost::signals2::signal<void(QString const&)>;
using SignalProfileChanged = boost::signals2::signal<void (MOBase::IProfile *, MOBase::IProfile *)>;
using SignalPluginSettingChanged = boost::signals2::signal<void (QString const&, const QString& key, const QVariant&, const QVariant&)>;
- using SignalPluginEnabled = boost::signals2::signal<void(MOBase::IPlugin*)>;
+ using SignalPluginEnabled = boost::signals2::signal<void(const MOBase::IPlugin*)>;
public:
@@ -199,7 +201,6 @@ public:
void setUserInterface(IUserInterface* ui);
void connectPlugins(PluginContainer *container);
- void disconnectPlugins();
void setManagedGame(MOBase::IPluginGame *game);
@@ -232,6 +233,13 @@ public:
MOBase::IPluginGame const *managedGame() const;
/**
+ * @brief Retrieve the organizer proxy of the currently managed game.
+ *
+ */
+ MOBase::IOrganizer const* managedGameOrganizer() 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.
*/
@@ -328,16 +336,16 @@ public:
ModList *modList();
void refresh(bool saveChanges = true);
- bool onAboutToRun(const std::function<bool(const QString&)>& func);
- bool onFinishedRun(const std::function<void(const QString&, unsigned int)>& func);
- bool onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func);
- bool onProfileCreated(std::function<void(MOBase::IProfile*)> const& func);
- bool onProfileRenamed(std::function<void(MOBase::IProfile*, QString const&, QString const&)> const& func);
- bool onProfileRemoved(std::function<void(QString const&)> const& func);
- bool onProfileChanged(std::function<void(MOBase::IProfile*, MOBase::IProfile*)> const& func);
- bool onPluginSettingChanged(std::function<void(QString const&, const QString& key, const QVariant&, const QVariant&)> const& func);
- bool onPluginEnabled(std::function<void(const MOBase::IPlugin*)> const& func);
- bool onPluginDisabled(std::function<void(const MOBase::IPlugin*)> const& func);
+ boost::signals2::connection onAboutToRun(const std::function<bool(const QString&)>& func);
+ boost::signals2::connection onFinishedRun(const std::function<void(const QString&, unsigned int)>& func);
+ boost::signals2::connection onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func);
+ boost::signals2::connection onProfileCreated(std::function<void(MOBase::IProfile*)> const& func);
+ boost::signals2::connection onProfileRenamed(std::function<void(MOBase::IProfile*, QString const&, QString const&)> const& func);
+ boost::signals2::connection onProfileRemoved(std::function<void(QString const&)> const& func);
+ boost::signals2::connection onProfileChanged(std::function<void(MOBase::IProfile*, MOBase::IProfile*)> const& func);
+ boost::signals2::connection onPluginSettingChanged(std::function<void(QString const&, const QString& key, const QVariant&, const QVariant&)> const& func);
+ boost::signals2::connection onPluginEnabled(std::function<void(const MOBase::IPlugin*)> const& func);
+ boost::signals2::connection onPluginDisabled(std::function<void(const MOBase::IPlugin*)> const& func);
bool getArchiveParsing() const
{
diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp
index 92f250ea..acfb8404 100644
--- a/src/organizerproxy.cpp
+++ b/src/organizerproxy.cpp
@@ -24,8 +24,44 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin
, m_Plugin(plugin)
, m_DownloadManagerProxy(std::make_unique<DownloadManagerProxy>(this, organizer->downloadManager()))
, m_ModListProxy(std::make_unique<ModListProxy>(this, organizer->modList()))
- , m_PluginListProxy(std::make_unique<PluginListProxy>(this, organizer->pluginList()))
+ , m_PluginListProxy(std::make_unique<PluginListProxy>(this, organizer->pluginList())) { }
+
+OrganizerProxy::~OrganizerProxy()
+{
+ disconnectSignals();
+}
+
+void OrganizerProxy::connectSignals()
{
+ m_Connections.push_back(m_Proxied->onAboutToRun(callSignalIfPluginActive(this, m_AboutToRun, true)));
+ m_Connections.push_back(m_Proxied->onFinishedRun(callSignalIfPluginActive(this, m_FinishedRun)));
+ m_Connections.push_back(m_Proxied->onProfileCreated(callSignalIfPluginActive(this, m_ProfileCreated)));
+ m_Connections.push_back(m_Proxied->onProfileRenamed(callSignalIfPluginActive(this, m_ProfileRenamed)));
+ m_Connections.push_back(m_Proxied->onProfileRemoved(callSignalIfPluginActive(this, m_ProfileRemoved)));
+ m_Connections.push_back(m_Proxied->onProfileChanged(callSignalIfPluginActive(this, m_ProfileChanged)));
+
+ m_Connections.push_back(m_Proxied->onUserInterfaceInitialized(callSignalAlways(m_UserInterfaceInitialized)));
+ m_Connections.push_back(m_Proxied->onPluginSettingChanged(callSignalAlways(m_PluginSettingChanged)));
+ m_Connections.push_back(m_Proxied->onPluginEnabled(callSignalAlways(m_PluginEnabled)));
+ m_Connections.push_back(m_Proxied->onPluginDisabled(callSignalAlways(m_PluginDisabled)));
+
+ // Connect the child proxies.
+ m_DownloadManagerProxy->connectSignals();
+ m_ModListProxy->connectSignals();
+ m_PluginListProxy->connectSignals();
+}
+
+void OrganizerProxy::disconnectSignals()
+{
+ // Disconnect the child proxies.
+ m_DownloadManagerProxy->disconnectSignals();
+ m_ModListProxy->disconnectSignals();
+ m_PluginListProxy->disconnectSignals();
+
+ for (auto& conn : m_Connections) {
+ conn.disconnect();
+ }
+ m_Connections.clear();
}
IModRepositoryBridge *OrganizerProxy::createNexusBridge() const
@@ -265,68 +301,68 @@ MOBase::IPluginGame const *OrganizerProxy::managedGame() const
bool OrganizerProxy::onAboutToRun(const std::function<bool(const QString&)>& func)
{
- return m_Proxied->onAboutToRun(MOShared::callIfPluginActive(this, func, true));
+ return m_Proxied->onAboutToRun(MOShared::callIfPluginActive(this, func, true)).connected();
}
bool OrganizerProxy::onFinishedRun(const std::function<void(const QString&, unsigned int)>& func)
{
- return m_Proxied->onFinishedRun(MOShared::callIfPluginActive(this, func));
-}
-
-bool OrganizerProxy::onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func)
-{
- // Always call this one to allow plugin to initialize themselves even when not active:
- return m_Proxied->onUserInterfaceInitialized(func);
+ return m_Proxied->onFinishedRun(MOShared::callIfPluginActive(this, func)).connected();
}
bool OrganizerProxy::onProfileCreated(std::function<void(IProfile*)> const& func)
{
- return m_Proxied->onProfileCreated(MOShared::callIfPluginActive(this, func));
+ return m_ProfileCreated.connect(func).connected();
}
bool OrganizerProxy::onProfileRenamed(std::function<void(IProfile*, QString const&, QString const&)> const& func)
{
- return m_Proxied->onProfileRenamed(MOShared::callIfPluginActive(this, func));
+ return m_ProfileRenamed.connect(func).connected();
}
bool OrganizerProxy::onProfileRemoved(std::function<void(QString const&)> const& func)
{
- return m_Proxied->onProfileRemoved(MOShared::callIfPluginActive(this, func));
+ return m_ProfileRemoved.connect(func).connected();
}
bool OrganizerProxy::onProfileChanged(std::function<void(MOBase::IProfile*, MOBase::IProfile*)> const& func)
{
- return m_Proxied->onProfileChanged(MOShared::callIfPluginActive(this, func));
+ return m_ProfileChanged.connect(func).connected();
+}
+
+bool OrganizerProxy::onUserInterfaceInitialized(std::function<void(QMainWindow*)> const& func)
+{
+ // Always call this one to allow plugin to initialize themselves even when not active:
+ return m_UserInterfaceInitialized.connect(func).connected();
}
// Always call these one, otherwise plugin cannot detect they are being enabled / disabled:
bool OrganizerProxy::onPluginSettingChanged(std::function<void(QString const&, const QString& key, const QVariant&, const QVariant&)> const& func)
{
- return m_Proxied->onPluginSettingChanged(func);
+ return m_PluginSettingChanged.connect(func).connected();
}
bool OrganizerProxy::onPluginEnabled(std::function<void(const IPlugin*)> const& func)
{
- return m_Proxied->onPluginEnabled(func);
+ return m_PluginEnabled.connect(func).connected();
}
bool OrganizerProxy::onPluginEnabled(const QString& pluginName, std::function<void()> const& func)
{
- return m_Proxied->onPluginEnabled([=](const IPlugin* plugin) {
+ return onPluginEnabled([=](const IPlugin* plugin) {
if (plugin->name().compare(pluginName, Qt::CaseInsensitive) == 0) {
func();
}
- });
+ });
}
bool OrganizerProxy::onPluginDisabled(std::function<void(const IPlugin*)> const& func)
{
- return m_Proxied->onPluginDisabled(func);
+ return m_PluginDisabled.connect(func).connected();
}
bool OrganizerProxy::onPluginDisabled(const QString& pluginName, std::function<void()> const& func)
{
- return m_Proxied->onPluginDisabled([=](const IPlugin* plugin) {
+ return onPluginDisabled([=](const IPlugin* plugin) {
if (plugin->name().compare(pluginName, Qt::CaseInsensitive) == 0) {
func();
}
diff --git a/src/organizerproxy.h b/src/organizerproxy.h
index 3097adf0..7a91a347 100644
--- a/src/organizerproxy.h
+++ b/src/organizerproxy.h
@@ -6,7 +6,8 @@
#include <iplugin.h>
#include <imoinfo.h>
-class OrganizerCore;
+#include "organizercore.h"
+
class PluginContainer;
class DownloadManagerProxy;
class ModListProxy;
@@ -18,12 +19,17 @@ class OrganizerProxy : public MOBase::IOrganizer
public:
OrganizerProxy(OrganizerCore *organizer, PluginContainer *pluginContainer, MOBase::IPlugin *plugin);
+ ~OrganizerProxy();
+
+public:
/**
* @return the plugin corresponding to this proxy.
*/
MOBase::IPlugin* plugin() const { return m_Plugin; }
+public: // IOrganizer interface
+
virtual MOBase::IModRepositoryBridge *createNexusBridge() const;
virtual QString profileName() const;
virtual QString profilePath() const;
@@ -76,6 +82,24 @@ public:
virtual MOBase::IPluginGame const *managedGame() const;
+protected:
+
+ // The container needs access to some callbacks to simulate startup.
+ friend class PluginContainer;
+
+ /**
+ * @brief Connect the signals from this proxy and all the child proxies (plugin list, mod
+ * list, etc.) to the actual implementation. Before this call, plugins can register signals
+ * but they won't be triggered.
+ */
+ void connectSignals();
+
+ /**
+ * @brief Disconnect the signals from this proxy and all the child proxies (plugin list, mod
+ * list, etc.) from the actual implementation.
+ */
+ void disconnectSignals();
+
private:
OrganizerCore *m_Proxied;
@@ -83,6 +107,19 @@ private:
MOBase::IPlugin *m_Plugin;
+ OrganizerCore::SignalAboutToRunApplication m_AboutToRun;
+ OrganizerCore::SignalFinishedRunApplication m_FinishedRun;
+ OrganizerCore::SignalUserInterfaceInitialized m_UserInterfaceInitialized;
+ OrganizerCore::SignalProfileCreated m_ProfileCreated;
+ OrganizerCore::SignalProfileRenamed m_ProfileRenamed;
+ OrganizerCore::SignalProfileRemoved m_ProfileRemoved;
+ OrganizerCore::SignalProfileChanged m_ProfileChanged;
+ OrganizerCore::SignalPluginSettingChanged m_PluginSettingChanged;
+ OrganizerCore::SignalPluginEnabled m_PluginEnabled;
+ OrganizerCore::SignalPluginEnabled m_PluginDisabled;
+
+ std::vector<boost::signals2::connection> m_Connections;
+
std::unique_ptr<DownloadManagerProxy> m_DownloadManagerProxy;
std::unique_ptr<ModListProxy> m_ModListProxy;
std::unique_ptr<PluginListProxy> m_PluginListProxy;
diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp
index 5a485dfe..c703a36a 100644
--- a/src/plugincontainer.cpp
+++ b/src/plugincontainer.cpp
@@ -106,7 +106,7 @@ const std::set<QString> PluginRequirements::s_CorePlugins{
};
PluginRequirements::PluginRequirements(
- PluginContainer* pluginContainer, MOBase::IPlugin* plugin, IOrganizer* proxy,
+ PluginContainer* pluginContainer, MOBase::IPlugin* plugin, OrganizerProxy* proxy,
MOBase::IPluginProxy* pluginProxy)
: m_PluginContainer(pluginContainer)
, m_Plugin(plugin)
@@ -280,7 +280,7 @@ void PluginRequirements::requiredFor(std::vector<MOBase::IPlugin*> &required, st
PluginContainer::PluginContainer(OrganizerCore *organizer)
: m_Organizer(organizer)
, m_UserInterface(nullptr)
- , m_PreviewGenerator(this)
+ , m_PreviewGenerator(*this)
{
}
@@ -289,21 +289,10 @@ PluginContainer::~PluginContainer() {
unloadPlugins();
}
-void PluginContainer::setUserInterface(IUserInterface *userInterface)
+void PluginContainer::startPlugins(IUserInterface *userInterface)
{
- if (userInterface) {
- for (IPluginProxy* proxy : bf::at_key<IPluginProxy>(m_Plugins)) {
- proxy->setParentWidget(userInterface->mainWindow());
- }
- for (IPluginModPage* modPage : bf::at_key<IPluginModPage>(m_Plugins)) {
- modPage->setParentWidget(userInterface->mainWindow());
- }
- for (IPluginTool* tool : bf::at_key<IPluginTool>(m_Plugins)) {
- tool->setParentWidget(userInterface->mainWindow());
- }
- }
-
m_UserInterface = userInterface;
+ startPluginsImpl(plugins<QObject>());
}
QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const
@@ -408,6 +397,7 @@ bool PluginContainer::initPlugin(IPlugin *plugin, IPluginProxy *pluginProxy, boo
OrganizerProxy* proxy = nullptr;
if (m_Organizer) {
proxy = new OrganizerProxy(m_Organizer, this, plugin);
+ proxy->setParent(as_qobject(plugin));
}
// Check if it is a proxy plugin:
@@ -439,7 +429,12 @@ void PluginContainer::registerGame(IPluginGame *game)
m_SupportedGames.insert({ game->gameName(), game });
}
-IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileName, MOBase::IPluginProxy* pluginProxy)
+void PluginContainer::unregisterGame(MOBase::IPluginGame* game)
+{
+ m_SupportedGames.erase(game->gameName());
+}
+
+IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString& filepath, MOBase::IPluginProxy* pluginProxy)
{
// generic treatment for all plugins
@@ -459,7 +454,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
// If both plugins are from the same proxy and the same file, this is usually
// ok (in theory some one could write two different classes from the same Python file/module):
if (pluginProxy && m_Requirements.at(other).proxy() == pluginProxy
- && as_qobject(other)->property("filepath") == fileName) {
+ && this->filepath(other) == QDir::cleanPath(filepath)) {
// Plugin has already been initialized:
skipInit = true;
@@ -484,7 +479,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
// way to cast directly between IPlugin* and IPluginDiagnose*
bf::at_key<QObject>(m_Plugins).push_back(plugin);
- plugin->setProperty("filepath", fileName);
+ plugin->setProperty("filepath", QDir::cleanPath(filepath));
if (m_Organizer) {
m_Organizer->settings().plugins().registerPlugin(pluginObj);
@@ -496,8 +491,8 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
bf::at_key<IPluginDiagnose>(m_Plugins).push_back(diagnose);
bf::at_key<IPluginDiagnose>(m_AccessPlugins)[diagnose] = pluginObj;
m_DiagnosisConnections.push_back(
- diagnose->onInvalidated([&] () { emit diagnosisUpdate(); })
- );
+ diagnose->onInvalidated([&] () { emit diagnosisUpdate(); })
+ );
}
}
{ // file mapper plugin
@@ -511,6 +506,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
IPluginModPage *modPage = qobject_cast<IPluginModPage*>(plugin);
if (initPlugin(modPage, pluginProxy, skipInit)) {
bf::at_key<IPluginModPage>(m_Plugins).push_back(modPage);
+ emit pluginRegistered(modPage);
return modPage;
}
}
@@ -521,6 +517,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
if (initPlugin(game, pluginProxy, skipInit)) {
bf::at_key<IPluginGame>(m_Plugins).push_back(game);
registerGame(game);
+ emit pluginRegistered(game);
return game;
}
}
@@ -529,6 +526,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
IPluginTool *tool = qobject_cast<IPluginTool*>(plugin);
if (initPlugin(tool, pluginProxy, skipInit)) {
bf::at_key<IPluginTool>(m_Plugins).push_back(tool);
+ emit pluginRegistered(tool);
return tool;
}
}
@@ -537,8 +535,9 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
if (initPlugin(installer, pluginProxy, skipInit)) {
bf::at_key<IPluginInstaller>(m_Plugins).push_back(installer);
if (m_Organizer) {
- m_Organizer->installationManager()->registerInstaller(installer);
+ installer->setInstallationManager(m_Organizer->installationManager());
}
+ emit pluginRegistered(installer);
return installer;
}
}
@@ -546,7 +545,6 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
IPluginPreview *preview = qobject_cast<IPluginPreview*>(plugin);
if (initPlugin(preview, pluginProxy, skipInit)) {
bf::at_key<IPluginPreview>(m_Plugins).push_back(preview);
- m_PreviewGenerator.registerPlugin(preview);
return preview;
}
}
@@ -554,55 +552,12 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
IPluginProxy *proxy = qobject_cast<IPluginProxy*>(plugin);
if (initPlugin(proxy, pluginProxy, skipInit)) {
bf::at_key<IPluginProxy>(m_Plugins).push_back(proxy);
- QStringList pluginNames = proxy->pluginList(
- QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()));
- for (const QString &pluginName : pluginNames) {
- try {
- // We get a list of matching plugins as proxies can return multiple plugins
- // per file and do not have a good way of supporting multiple inheritance.
- QList<QObject*> matchingPlugins = proxy->load(pluginName);
-
- // We are going to group plugin by names and "fix" them later:
- std::map<QString, std::vector<IPlugin*>> proxiedByNames;
-
- for (QObject *proxiedPlugin : matchingPlugins) {
- if (proxiedPlugin != nullptr) {
-
- if (IPlugin* proxied = registerPlugin(proxiedPlugin, pluginName, proxy); proxied) {
- log::debug("loaded plugin '{}' from '{}' - [{}]",
- proxied->name(), QFileInfo(pluginName).fileName(), implementedInterfaces(proxied).join(", "));
+ emit pluginRegistered(proxy);
- // Store the plugin for later:
- proxiedByNames[proxied->name()].push_back(proxied);
- }
- else {
- log::warn(
- "plugin \"{}\" failed to load. If this plugin is for an older version of MO "
- "you have to update it or delete it if no update exists.",
- pluginName);
- }
- }
- }
-
- // Fake masters:
- for (auto& [name, proxiedPlugins] : proxiedByNames) {
- if (proxiedPlugins.size() > 1) {
- auto it = std::min_element(std::begin(proxiedPlugins), std::end(proxiedPlugins),
- [&](auto const& lhs, auto const& rhs) {
- return isBetterInterface(as_qobject(lhs), as_qobject(rhs));
- });
-
- for (auto& proxiedPlugin : proxiedPlugins) {
- if (proxiedPlugin != *it) {
- m_Requirements.at(proxiedPlugin).setMaster(*it);
- }
- }
- }
- }
-
- } catch (const std::exception &e) {
- reportError(QObject::tr("failed to initialize plugin %1: %2").arg(pluginName).arg(e.what()));
- }
+ QStringList filepaths = proxy->pluginList(
+ QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()));
+ for (const QString& filepath : filepaths) {
+ loadProxied(filepath, proxy);
}
return proxy;
}
@@ -613,6 +568,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
IPlugin *dummy = qobject_cast<IPlugin*>(plugin);
if (initPlugin(dummy, pluginProxy, skipInit)) {
bf::at_key<IPlugin>(m_Plugins).push_back(dummy);
+ emit pluginRegistered(dummy);
return dummy;
}
}
@@ -620,36 +576,6 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam
return nullptr;
}
-void PluginContainer::unloadPlugins()
-{
- if (m_UserInterface != nullptr) {
- m_UserInterface->disconnectPlugins();
- }
-
- // disconnect all slots before unloading plugins so plugins don't have to take care of that
- if (m_Organizer) {
- m_Organizer->disconnectPlugins();
- }
-
- bf::for_each(m_Plugins, [](auto& t) { t.second.clear(); });
- bf::for_each(m_AccessPlugins, [](auto& t) { t.second.clear(); });
- m_Requirements.clear();
-
- for (const boost::signals2::connection &connection : m_DiagnosisConnections) {
- connection.disconnect();
- }
- m_DiagnosisConnections.clear();
-
- while (!m_PluginLoaders.empty()) {
- QPluginLoader *loader = m_PluginLoaders.back();
- m_PluginLoaders.pop_back();
- if ((loader != nullptr) && !loader->unload()) {
- log::debug("failed to unload {}: {}", loader->fileName(), loader->errorString());
- }
- delete loader;
- }
-}
-
IPlugin* PluginContainer::managedGame() const
{
// TODO: This const_cast is safe but ugly. Most methods require a IPlugin*, so
@@ -754,7 +680,22 @@ const PluginRequirements& PluginContainer::requirements(IPlugin* plugin) const
return m_Requirements.at(plugin);
}
-IPluginGame *PluginContainer::managedGame(const QString &name) const
+OrganizerProxy* PluginContainer::organizerProxy(MOBase::IPlugin* plugin) const
+{
+ return requirements(plugin).m_Organizer;
+}
+
+MOBase::IPluginProxy* PluginContainer::pluginProxy(MOBase::IPlugin* plugin) const
+{
+ return requirements(plugin).proxy();
+}
+
+QString PluginContainer::filepath(MOBase::IPlugin* plugin) const
+{
+ return as_qobject(plugin)->property("filepath").toString();
+}
+
+IPluginGame *PluginContainer::game(const QString &name) const
{
auto iter = m_SupportedGames.find(name);
if (iter != m_SupportedGames.end()) {
@@ -769,6 +710,267 @@ const PreviewGenerator &PluginContainer::previewGenerator() const
return m_PreviewGenerator;
}
+void PluginContainer::startPluginsImpl(const std::vector<QObject*>& plugins) const
+{
+ // setUserInterface()
+ if (m_UserInterface) {
+ for (auto* plugin : plugins) {
+ if (auto* proxy = qobject_cast<IPluginProxy*>(plugin)) {
+ proxy->setParentWidget(m_UserInterface->mainWindow());
+ }
+ if (auto* modPage = qobject_cast<IPluginModPage*>(plugin)) {
+ modPage->setParentWidget(m_UserInterface->mainWindow());
+ }
+ if (auto* tool = qobject_cast<IPluginTool*>(plugin)) {
+ tool->setParentWidget(m_UserInterface->mainWindow());
+ }
+ if (auto* installer = qobject_cast<IPluginInstaller*>(plugin)) {
+ installer->setParentWidget(m_UserInterface->mainWindow());
+ }
+ }
+ }
+
+ // Trigger initial callbacks, e.g. onUserInterfaceInitialized and onProfileChanged.
+ if (m_Organizer) {
+ for (auto* object : plugins) {
+ auto* plugin = qobject_cast<IPlugin*>(object);
+ auto* oproxy = organizerProxy(plugin);
+ oproxy->connectSignals();
+ oproxy->m_ProfileChanged(nullptr, m_Organizer->currentProfile());
+
+ if (m_UserInterface) {
+ oproxy->m_UserInterfaceInitialized(m_UserInterface->mainWindow());
+ }
+ }
+ }
+}
+
+std::vector<QObject*> PluginContainer::loadProxied(const QString& filepath, IPluginProxy* proxy)
+{
+ std::vector<QObject*> proxiedPlugins;
+
+ try {
+ // We get a list of matching plugins as proxies can return multiple plugins
+ // per file and do not have a good way of supporting multiple inheritance.
+ QList<QObject*> matchingPlugins = proxy->load(filepath);
+
+ // We are going to group plugin by names and "fix" them later:
+ std::map<QString, std::vector<IPlugin*>> proxiedByNames;
+
+ for (QObject* proxiedPlugin : matchingPlugins) {
+ if (proxiedPlugin != nullptr) {
+
+ if (IPlugin* proxied = registerPlugin(proxiedPlugin, filepath, proxy); proxied) {
+ log::debug("loaded plugin '{}' from '{}' - [{}]",
+ proxied->name(), QFileInfo(filepath).fileName(), implementedInterfaces(proxied).join(", "));
+
+ // Store the plugin for later:
+ proxiedPlugins.push_back(proxiedPlugin);
+ proxiedByNames[proxied->name()].push_back(proxied);
+ }
+ else {
+ log::warn(
+ "plugin \"{}\" failed to load. If this plugin is for an older version of MO "
+ "you have to update it or delete it if no update exists.",
+ filepath);
+ }
+ }
+ }
+
+ // Fake masters:
+ for (auto& [name, proxiedPlugins] : proxiedByNames) {
+ if (proxiedPlugins.size() > 1) {
+ auto it = std::min_element(std::begin(proxiedPlugins), std::end(proxiedPlugins),
+ [&](auto const& lhs, auto const& rhs) {
+ return isBetterInterface(as_qobject(lhs), as_qobject(rhs));
+ });
+
+ for (auto& proxiedPlugin : proxiedPlugins) {
+ if (proxiedPlugin != *it) {
+ m_Requirements.at(proxiedPlugin).setMaster(*it);
+ }
+ }
+ }
+ }
+ }
+ catch (const std::exception& e) {
+ reportError(QObject::tr("failed to initialize plugin %1: %2").arg(filepath).arg(e.what()));
+ }
+
+ return proxiedPlugins;
+}
+
+QObject* PluginContainer::loadQtPlugin(const QString& filepath)
+{
+ std::unique_ptr<QPluginLoader> pluginLoader(new QPluginLoader(filepath, this));
+ if (pluginLoader->instance() == nullptr) {
+ m_FailedPlugins.push_back(filepath);
+ log::error("failed to load plugin {}: {}", filepath, pluginLoader->errorString());
+ }
+ else {
+ QObject* object = pluginLoader->instance();
+ if (IPlugin* plugin = registerPlugin(object, filepath, nullptr); plugin) {
+ log::debug("loaded plugin '{}' from '{}' - [{}]",
+ plugin->name(), QFileInfo(filepath).fileName(), implementedInterfaces(plugin).join(", "));
+ m_PluginLoaders.push_back(pluginLoader.release());
+ return object;
+ }
+ else {
+ m_FailedPlugins.push_back(filepath);
+ log::warn("plugin '{}' failed to load (may be outdated)", filepath);
+ }
+ }
+ return nullptr;
+}
+
+void PluginContainer::loadPlugin(QString const& filepath)
+{
+ std::vector<QObject*> plugins;
+ if (QFileInfo(filepath).isFile() && QLibrary::isLibrary(filepath)) {
+ QObject* plugin = loadQtPlugin(filepath);
+ if (plugin) {
+ plugins.push_back(plugin);
+ }
+ }
+ else {
+ // We need to check if this can be handled by a proxy.
+ for (auto* proxy : this->plugins<IPluginProxy>()) {
+ auto filepaths = proxy->pluginList(
+ QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()));
+ if (filepaths.contains(filepath)) {
+ plugins = loadProxied(filepath, proxy);
+ break;
+ }
+ }
+ }
+
+ for (auto* plugin : plugins) {
+ emit pluginRegistered(qobject_cast<IPlugin*>(plugin));
+ }
+
+ startPluginsImpl(plugins);
+}
+
+void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object)
+{
+ if (auto* game = qobject_cast<IPluginGame*>(object)) {
+
+ if (game == managedGame()) {
+ throw Exception("cannot unload the plugin for the currently managed game");
+ }
+
+ unregisterGame(game);
+ }
+
+ // We need to remove from the m_Plugins maps BEFORE unloading from the proxy
+ // otherwise the qobject_cast to check the plugin type will not work.
+ bf::for_each(m_Plugins, [object](auto& t) {
+ using type = typename std::decay_t<decltype(t.second)>::value_type;
+
+ // We do not want to remove from QObject since we are iterating over them.
+ if constexpr (!std::is_same<type, QObject*>{}) {
+ auto itp = std::find(t.second.begin(), t.second.end(), qobject_cast<type>(object));
+ if (itp != t.second.end()) {
+ t.second.erase(itp);
+ }
+ }
+ });
+
+ emit pluginUnregistered(plugin);
+
+ // Remove from the members.
+ if (auto* diagnose = qobject_cast<IPluginDiagnose*>(object)) {
+ bf::at_key<IPluginDiagnose>(m_AccessPlugins).erase(diagnose);
+ }
+ if (auto* mapper = qobject_cast<IPluginFileMapper*>(object)) {
+ bf::at_key<IPluginFileMapper>(m_AccessPlugins).erase(mapper);
+ }
+
+ auto& mapNames = bf::at_key<QString>(m_AccessPlugins);
+ if (mapNames.contains(plugin->name())) {
+ mapNames.erase(plugin->name());
+ }
+
+ m_Organizer->settings().plugins().unregisterPlugin(plugin);
+
+ // Force disconnection of the signals from the proxies. This is a safety
+ // operations since those signals should be disconnected when the proxies
+ // are destroyed anyway.
+ organizerProxy(plugin)->disconnectSignals();
+
+ // Is this a proxied plugin?
+ auto* proxy = pluginProxy(plugin);
+
+ if (proxy) {
+ proxy->unload(filepath(plugin));
+ }
+ else {
+ // We need to find the loader.
+ auto it = std::find_if(m_PluginLoaders.begin(), m_PluginLoaders.end(),
+ [object](auto* loader) { return loader->instance() == object; });
+
+ if (it != m_PluginLoaders.end()) {
+ if (!(*it)->unload()) {
+ log::error("failed to unload {}: {}", (*it)->fileName(), (*it)->errorString());
+ }
+ delete* it;
+ m_PluginLoaders.erase(it);
+ }
+ else {
+ log::error("loader for plugin {} does not exist, cannot unload", plugin->name());
+ }
+
+ }
+
+ // Do this at the end.
+ m_Requirements.erase(plugin);
+}
+
+void PluginContainer::unloadPlugin(QString const& filepath)
+{
+ // We need to find all the plugins from the given path and
+ // unload them:
+ QString cleanPath = QDir::cleanPath(filepath);
+ auto &objects = bf::at_key<QObject>(m_Plugins);
+ for (auto it = objects.begin(); it != objects.end(); ) {
+ auto* plugin = qobject_cast<IPlugin*>(*it);
+ if (this->filepath(plugin) == filepath) {
+ unloadPlugin(plugin, *it);
+ it = objects.erase(it);
+ }
+ else {
+ ++it;
+ }
+ }
+}
+
+void PluginContainer::reloadPlugin(QString const& filepath)
+{
+ unloadPlugin(filepath);
+ loadPlugin(filepath);
+}
+
+void PluginContainer::unloadPlugins()
+{
+ bf::for_each(m_Plugins, [](auto& t) { t.second.clear(); });
+ bf::for_each(m_AccessPlugins, [](auto& t) { t.second.clear(); });
+ m_Requirements.clear();
+
+ for (const boost::signals2::connection& connection : m_DiagnosisConnections) {
+ connection.disconnect();
+ }
+ m_DiagnosisConnections.clear();
+
+ while (!m_PluginLoaders.empty()) {
+ QPluginLoader* loader = m_PluginLoaders.back();
+ m_PluginLoaders.pop_back();
+ if ((loader != nullptr) && !loader->unload()) {
+ log::debug("failed to unload {}: {}", loader->fileName(), loader->errorString());
+ }
+ delete loader;
+ }
+}
+
void PluginContainer::loadPlugins()
{
TimeThis tt("PluginContainer::loadPlugins()");
@@ -863,24 +1065,9 @@ void PluginContainer::loadPlugins()
loadCheck.flush();
}
- QString pluginName = iter.filePath();
- if (QLibrary::isLibrary(pluginName)) {
- std::unique_ptr<QPluginLoader> pluginLoader(new QPluginLoader(pluginName, this));
- if (pluginLoader->instance() == nullptr) {
- m_FailedPlugins.push_back(pluginName);
- log::error(
- "failed to load plugin {}: {}",
- pluginName, pluginLoader->errorString());
- } else {
- if (IPlugin* plugin = registerPlugin(pluginLoader->instance(), pluginName, nullptr); plugin) {
- log::debug("loaded plugin '{}' from '{}' - [{}]",
- plugin->name(), QFileInfo(pluginName).fileName(), implementedInterfaces(plugin).join(", "));
- m_PluginLoaders.push_back(pluginLoader.release());
- } else {
- m_FailedPlugins.push_back(pluginName);
- log::warn("plugin \"{}\" failed to load (may be outdated)", pluginName);
- }
- }
+ QString filepath = iter.filePath();
+ if (QLibrary::isLibrary(filepath)) {
+ loadQtPlugin(filepath);
}
}
@@ -911,7 +1098,6 @@ void PluginContainer::loadPlugins()
}
}
-
std::vector<unsigned int> PluginContainer::activeProblems() const
{
std::vector<unsigned int> problems;
diff --git a/src/plugincontainer.h b/src/plugincontainer.h
index 26e12659..386b9bca 100644
--- a/src/plugincontainer.h
+++ b/src/plugincontainer.h
@@ -103,6 +103,7 @@ private:
// Set the master for this plugin. This is required to "fake" masters for proxied plugins.
void setMaster(MOBase::IPlugin* master);
+ friend class OrganizerCore;
friend class PluginContainer;
PluginContainer* m_PluginContainer;
@@ -110,12 +111,12 @@ private:
MOBase::IPluginProxy* m_PluginProxy;
MOBase::IPlugin* m_Master;
std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> m_Requirements;
- MOBase::IOrganizer* m_Organizer;
+ OrganizerProxy* m_Organizer;
std::vector<MOBase::IPlugin*> m_RequiredFor;
PluginRequirements(
PluginContainer* pluginContainer, MOBase::IPlugin* plugin,
- MOBase::IOrganizer* proxy, MOBase::IPluginProxy* pluginProxy);
+ OrganizerProxy* proxy, MOBase::IPluginProxy* pluginProxy);
};
@@ -185,24 +186,37 @@ public:
public:
- PluginContainer(OrganizerCore *organizer);
+ PluginContainer(OrganizerCore* organizer);
virtual ~PluginContainer();
- void setUserInterface(IUserInterface *userInterface);
-
- void loadPlugins();
- void unloadPlugins();
+ /**
+ * @brief Start the plugins.
+ *
+ * This function should not be called before MO2 is ready and plugins can be
+ * started, and will do the following:
+ * - connect the callbacks of the plugins,
+ * - set the parent widget for plugins that can have one,
+ * - notify plugins that MO2 has been started, including:
+ * - triggering a call to the "profile changed" callback for the initial profile,
+ * - triggering a call to the "user interface initialized" callback.
+ *
+ * @param userInterface The main user interface to use for the plugins.
+ */
+ void startPlugins(IUserInterface* userInterface);
/**
- * @brief Find the game plugin corresponding to the given name.
+ * @brief Load, unload or reload the plugin at the given path.
*
- * @param name The name of the game to find a plugin for (as returned by
- * IPluginGame::gameName()).
+ */
+ void loadPlugin(QString const& filepath);
+ void unloadPlugin(QString const& filepath);
+ void reloadPlugin(QString const& filepath);
+
+ /**
+ * @brief Load all plugins.
*
- * @return the game plugin for the given name, or a null pointer if no
- * plugin exists for this game.
*/
- MOBase::IPluginGame *managedGame(const QString &name) const;
+ void loadPlugins();
/**
* @brief Retrieve the list of plugins of the given type.
@@ -255,6 +269,17 @@ public:
MOBase::IPlugin* plugin(MOBase::IPluginFileMapper* mapper) const;
/**
+ * @brief Find the game plugin corresponding to the given name.
+ *
+ * @param name The name of the game to find a plugin for (as returned by
+ * IPluginGame::gameName()).
+ *
+ * @return the game plugin for the given name, or a null pointer if no
+ * plugin exists for this game.
+ */
+ MOBase::IPluginGame* game(const QString& name) const;
+
+ /**
* @return the IPlugin interface to the currently managed game.
*/
MOBase::IPlugin* managedGame() const;
@@ -335,17 +360,57 @@ public: // IPluginDiagnose interface
signals:
/**
- * @brief Emitted plugins are enabled or disabled.
+ * @brief Emitted when plugins are enabled or disabled.
*/
void pluginEnabled(MOBase::IPlugin*);
void pluginDisabled(MOBase::IPlugin*);
+ /**
+ * @brief Emitted when plugins are registered or unregistered.
+ */
+ void pluginRegistered(MOBase::IPlugin*);
+ void pluginUnregistered(MOBase::IPlugin*);
+
void diagnosisUpdate();
private:
friend class PluginRequirements;
+ // Unload all the plugins.
+ void unloadPlugins();
+
+ // Retrieve the organizer proxy for the given plugin.
+ OrganizerProxy* organizerProxy(MOBase::IPlugin* plugin) const;
+
+ // Retrieve the proxy plugin that instantiated the given plugin, or a null pointer
+ // if the plugin was not instantiated by a proxy.
+ MOBase::IPluginProxy* pluginProxy(MOBase::IPlugin* plugin) const;
+
+ // Retrieve the path to the file or folder corresponding to the plugin.
+ QString filepath(MOBase::IPlugin* plugin) const;
+
+ // Load plugins from the given filepath using the given proxy.
+ std::vector<QObject*> loadProxied(const QString& filepath, MOBase::IPluginProxy* proxy);
+
+ // Load the Qt plugin from the given file.
+ QObject* loadQtPlugin(const QString& filepath);
+
+ // See startPlugins for more details. This is simply an intermediate function
+ // that can be used when loading plugins after initialization. This uses the
+ // user interface in m_UserInterface.
+ void startPluginsImpl(const std::vector<QObject*>& plugins) const;
+
+ /**
+ * @brief Unload the given plugin.
+ *
+ * This function is not public because it's kind of dangerous trying to unload
+ * plugin directly since some plugins are linked together.
+ *
+ * @param plugin The plugin to unload/unregister.
+ * @param object The QObject corresponding to the plugin.
+ */
+ void unloadPlugin(MOBase::IPlugin* plugin, QObject* object);
/**
* @brief Retrieved the (localized) names of interfaces implemented by the given
@@ -393,11 +458,14 @@ private:
bool initPlugin(MOBase::IPlugin *plugin, MOBase::IPluginProxy* proxy, bool skipInit);
void registerGame(MOBase::IPluginGame *game);
+ void unregisterGame(MOBase::IPluginGame* game);
MOBase::IPlugin* registerPlugin(QObject *pluginObj, const QString &fileName, MOBase::IPluginProxy *proxy);
+ // Core organizer, can be null (e.g. on first MO2 startup).
OrganizerCore *m_Organizer;
+ // Main user interface, can be null until MW has been initialized.
IUserInterface *m_UserInterface;
PluginMap m_Plugins;
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 8d04b592..504b17c5 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -55,6 +55,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <algorithm>
#include <stdexcept>
+#include "organizercore.h"
using namespace MOBase;
using namespace MOShared;
@@ -94,8 +95,9 @@ static QString TruncateString(const QString& text)
}
-PluginList::PluginList(QObject *parent)
- : QAbstractItemModel(parent)
+PluginList::PluginList(OrganizerCore& organizer)
+ : QAbstractItemModel(&organizer)
+ , m_Organizer(organizer)
, m_FontMetrics(QFont())
{
connect(this, SIGNAL(writePluginsList()), this, SLOT(generatePluginIndexes()));
@@ -267,7 +269,7 @@ void PluginList::refresh(const QString &profileName
updateIndices();
if (gamePlugins) {
- gamePlugins->readPluginLists(this);
+ gamePlugins->readPluginLists(m_Organizer.managedGameOrganizer()->pluginList());
}
testMasters();
@@ -546,7 +548,7 @@ void PluginList::saveTo(const QString &lockedOrderFileName
{
GamePlugins *gamePlugins = m_GamePlugin->feature<GamePlugins>();
if (gamePlugins) {
- gamePlugins->writePluginLists(this);
+ gamePlugins->writePluginLists(m_Organizer.managedGameOrganizer()->pluginList());
}
writeLockedOrder(lockedOrderFileName);
@@ -875,13 +877,12 @@ QString PluginList::origin(const QString &name) const
}
}
-bool PluginList::onPluginStateChanged(const std::function<void(const std::map<QString, PluginStates>&)>& func)
+boost::signals2::connection PluginList::onPluginStateChanged(const std::function<void(const std::map<QString, PluginStates>&)>& func)
{
- auto conn = m_PluginStateChanged.connect(func);
- return conn.connected();
+ return m_PluginStateChanged.connect(func);
}
-void PluginList::pluginStatesChanged(QStringList const& pluginNames, IPluginList::PluginStates state) const {
+void PluginList::pluginStatesChanged(QStringList const& pluginNames, PluginStates state) const {
if (pluginNames.isEmpty()) {
return;
}
@@ -892,17 +893,15 @@ void PluginList::pluginStatesChanged(QStringList const& pluginNames, IPluginList
m_PluginStateChanged(infos);
}
-bool PluginList::onRefreshed(const std::function<void ()> &callback)
+boost::signals2::connection PluginList::onRefreshed(const std::function<void ()> &callback)
{
- auto conn = m_Refreshed.connect(callback);
- return conn.connected();
+ return m_Refreshed.connect(callback);
}
-bool PluginList::onPluginMoved(const std::function<void (const QString &, int, int)> &func)
+boost::signals2::connection PluginList::onPluginMoved(const std::function<void (const QString &, int, int)> &func)
{
- auto conn = m_PluginMoved.connect(func);
- return conn.connected();
+ return m_PluginMoved.connect(func);
}
diff --git a/src/pluginlist.h b/src/pluginlist.h
index 0b49b86f..5f0cef3d 100644
--- a/src/pluginlist.h
+++ b/src/pluginlist.h
@@ -43,6 +43,8 @@ namespace MOBase { class IPluginGame; }
#include <vector>
#include <map>
+class OrganizerCore;
+
template <class C>
class ChangeBracket {
@@ -79,7 +81,7 @@ private:
/**
* @brief model representing the plugins (.esp/.esm) in the current virtual data folder
**/
-class PluginList : public QAbstractItemModel, public MOBase::IPluginList
+class PluginList : public QAbstractItemModel
{
Q_OBJECT
friend class ChangeBracket<PluginList>;
@@ -94,9 +96,13 @@ public:
COL_LASTCOLUMN = COL_MODINDEX
};
- typedef boost::signals2::signal<void ()> SignalRefreshed;
- typedef boost::signals2::signal<void (const QString &, int, int)> SignalPluginMoved;
- typedef boost::signals2::signal<void (const std::map<QString, PluginStates>&)> SignalPluginStateChanged;
+ using PluginStates = MOBase::IPluginList::PluginStates;
+
+ friend class PluginListProxy;
+
+ using SignalRefreshed = boost::signals2::signal<void ()>;
+ using SignalPluginMoved = boost::signals2::signal<void (const QString &, int, int)>;
+ using SignalPluginStateChanged = boost::signals2::signal<void (const std::map<QString, PluginStates>&)>;
public:
@@ -105,7 +111,7 @@ public:
*
* @param parent parent object
**/
- PluginList(QObject *parent = nullptr);
+ PluginList(OrganizerCore &organizer);
~PluginList();
@@ -221,21 +227,22 @@ public:
public:
- virtual QStringList pluginNames() const override;
- virtual PluginStates state(const QString &name) const override;
- virtual void setState(const QString &name, PluginStates state) override;
- virtual int priority(const QString &name) const override;
- virtual int loadOrder(const QString &name) const override;
- virtual bool setPriority(const QString& name, int newPriority) override;
- virtual bool onRefreshed(const std::function<void()> &callback);
- virtual bool isMaster(const QString &name) const override;
- virtual bool isLight(const QString &name) const;
- virtual bool isLightFlagged(const QString &name) const;
- virtual QStringList masters(const QString &name) const override;
- virtual QString origin(const QString &name) const override;
- virtual void setLoadOrder(const QStringList &pluginList) override;
- virtual bool onPluginMoved(const std::function<void (const QString &, int, int)> &func) override;
- virtual bool onPluginStateChanged(const std::function<void (const std::map<QString, PluginStates>&)> &func) override;
+ QStringList pluginNames() const;
+ PluginStates state(const QString &name) const;
+ void setState(const QString &name, PluginStates state);
+ int priority(const QString &name) const;
+ int loadOrder(const QString &name) const;
+ bool setPriority(const QString& name, int newPriority);
+ bool isMaster(const QString &name) const;
+ bool isLight(const QString &name) const;
+ bool isLightFlagged(const QString &name) const;
+ QStringList masters(const QString &name) const;
+ QString origin(const QString &name) const;
+ void setLoadOrder(const QStringList& pluginList);
+
+ boost::signals2::connection onRefreshed(const std::function<void()>& callback);
+ boost::signals2::connection onPluginMoved(const std::function<void(const QString&, int, int)>& func);
+ boost::signals2::connection onPluginStateChanged(const std::function<void (const std::map<QString, PluginStates>&)> &func);
public: // implementation of the QAbstractTableModel interface
@@ -368,10 +375,12 @@ private:
* @param state New state of the plugin.
*
*/
- void pluginStatesChanged(QStringList const& pluginNames, IPluginList::PluginStates state) const;
+ void pluginStatesChanged(QStringList const& pluginNames, PluginStates state) const;
private:
+ OrganizerCore& m_Organizer;
+
std::vector<ESPInfo> m_ESPs;
mutable std::map<QString, QByteArray> m_LastSaveHash;
diff --git a/src/pluginlistproxy.cpp b/src/pluginlistproxy.cpp
index 1f8dc2b4..b9ea9e36 100644
--- a/src/pluginlistproxy.cpp
+++ b/src/pluginlistproxy.cpp
@@ -3,11 +3,32 @@
#include "proxyutils.h"
using namespace MOBase;
+using namespace MOShared;
-PluginListProxy::PluginListProxy(OrganizerProxy* oproxy, IPluginList* pluginlist) :
+PluginListProxy::PluginListProxy(OrganizerProxy* oproxy, PluginList* pluginlist) :
m_OrganizerProxy(oproxy), m_Proxied(pluginlist) { }
-QStringList PluginListProxy::pluginNames() const
+PluginListProxy::~PluginListProxy()
+{
+ disconnectSignals();
+}
+
+void PluginListProxy::connectSignals()
+{
+ m_Connections.push_back(m_Proxied->onRefreshed(callSignalIfPluginActive(m_OrganizerProxy, m_Refreshed)));
+ m_Connections.push_back(m_Proxied->onPluginMoved(callSignalIfPluginActive(m_OrganizerProxy, m_PluginMoved)));
+ m_Connections.push_back(m_Proxied->onPluginStateChanged(callSignalIfPluginActive(m_OrganizerProxy, m_PluginStateChanged)));
+}
+
+void PluginListProxy::disconnectSignals()
+{
+ for (auto& conn : m_Connections) {
+ conn.disconnect();
+ }
+ m_Connections.clear();
+}
+
+QStringList PluginListProxy::pluginNames() const
{
return m_Proxied->pluginNames();
}
@@ -57,17 +78,17 @@ QString PluginListProxy::origin(const QString& name) const
return m_Proxied->origin(name);
}
-bool PluginListProxy::onRefreshed(const std::function<void()>& callback)
+bool PluginListProxy::onRefreshed(const std::function<void()>& func)
{
- return m_Proxied->onRefreshed(MOShared::callIfPluginActive(m_OrganizerProxy, callback));
+ return m_Refreshed.connect(func).connected();
}
bool PluginListProxy::onPluginMoved(const std::function<void(const QString&, int, int)>& func)
{
- return m_Proxied->onPluginMoved(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_PluginMoved.connect(func).connected();
}
bool PluginListProxy::onPluginStateChanged(const std::function<void(const std::map<QString, PluginStates>&)> &func)
{
- return m_Proxied->onPluginStateChanged(MOShared::callIfPluginActive(m_OrganizerProxy, func));
+ return m_PluginStateChanged.connect(func).connected();
}
diff --git a/src/pluginlistproxy.h b/src/pluginlistproxy.h
index e08406e3..e421b6ee 100644
--- a/src/pluginlistproxy.h
+++ b/src/pluginlistproxy.h
@@ -2,6 +2,7 @@
#define PLUGINLISTPROXY_H
#include <ipluginlist.h>
+#include "pluginlist.h"
class OrganizerProxy;
@@ -10,8 +11,8 @@ class PluginListProxy : public MOBase::IPluginList
public:
- PluginListProxy(OrganizerProxy* oproxy, IPluginList* pluginlist);
- virtual ~PluginListProxy() { }
+ PluginListProxy(OrganizerProxy* oproxy, PluginList* pluginlist);
+ virtual ~PluginListProxy();
QStringList pluginNames() const override;
PluginStates state(const QString& name) const override;
@@ -29,8 +30,20 @@ public:
private:
+ friend class OrganizerProxy;
+
+ // See OrganizerProxy::connectSignals().
+ void connectSignals();
+ void disconnectSignals();
+
OrganizerProxy* m_OrganizerProxy;
- IPluginList* m_Proxied;
+ PluginList* m_Proxied;
+
+ PluginList::SignalRefreshed m_Refreshed;
+ PluginList::SignalPluginMoved m_PluginMoved;
+ PluginList::SignalPluginStateChanged m_PluginStateChanged;
+
+ std::vector<boost::signals2::connection> m_Connections;
};
#endif // ORGANIZERPROXY_H
diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp
index c7df5a25..22332af3 100644
--- a/src/previewgenerator.cpp
+++ b/src/previewgenerator.cpp
@@ -27,33 +27,32 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "plugincontainer.h"
-PreviewGenerator::PreviewGenerator(const PluginContainer* pluginContainer) :
+using namespace MOBase;
+
+PreviewGenerator::PreviewGenerator(const PluginContainer& pluginContainer) :
m_PluginContainer(pluginContainer) {
m_MaxSize = QGuiApplication::primaryScreen()->size() * 0.8;
}
-void PreviewGenerator::registerPlugin(MOBase::IPluginPreview *plugin)
-{
- for (const QString &extension : plugin->supportedExtensions()) {
- m_PreviewPlugins.insert(std::make_pair(extension, plugin));
- }
-}
-
bool PreviewGenerator::previewSupported(const QString &fileExtension) const
{
- auto it = m_PreviewPlugins.find(fileExtension.toLower());
- if (it == m_PreviewPlugins.end()) {
- return false;
+ auto& previews = m_PluginContainer.plugins<IPluginPreview>();
+ for (auto* preview : previews) {
+ if (preview->supportedExtensions().contains(fileExtension)) {
+ return true;
+ }
}
- return m_PluginContainer->isEnabled(it->second);
+ return false;
}
QWidget *PreviewGenerator::genPreview(const QString &fileName) const
{
- auto iter = m_PreviewPlugins.find(QFileInfo(fileName).suffix().toLower());
- if (iter != m_PreviewPlugins.end() && m_PluginContainer->isEnabled(iter->second)) {
- return iter->second->genFilePreview(fileName, m_MaxSize);
- } else {
- return nullptr;
+ const QString ext = QFileInfo(fileName).suffix().toLower();
+ auto& previews = m_PluginContainer.plugins<IPluginPreview>();
+ for (auto* preview : previews) {
+ if (m_PluginContainer.isEnabled(preview) && preview->supportedExtensions().contains(ext)) {
+ return preview->genFilePreview(fileName, m_MaxSize);
+ }
}
+ return nullptr;
}
diff --git a/src/previewgenerator.h b/src/previewgenerator.h
index 0d8f0781..8e491116 100644
--- a/src/previewgenerator.h
+++ b/src/previewgenerator.h
@@ -31,9 +31,7 @@ class PluginContainer;
class PreviewGenerator
{
public:
- PreviewGenerator(const PluginContainer* pluginContainer);
-
- void registerPlugin(MOBase::IPluginPreview *plugin);
+ PreviewGenerator(const PluginContainer& pluginContainer);
bool previewSupported(const QString &fileExtension) const;
@@ -41,13 +39,7 @@ public:
private:
- QWidget *genImagePreview(const QString &fileName) const;
- QWidget *genTxtPreview(const QString &fileName) const;
-
-private:
-
- const PluginContainer* m_PluginContainer;
- std::map<QString, MOBase::IPluginPreview*> m_PreviewPlugins;
+ const PluginContainer& m_PluginContainer;
QSize m_MaxSize;
};
diff --git a/src/proxyutils.h b/src/proxyutils.h
index 4f26c070..6c85ba14 100644
--- a/src/proxyutils.h
+++ b/src/proxyutils.h
@@ -21,6 +21,21 @@ namespace MOShared {
};
}
+ // We need to connect to the organizer.
+ template <class Signal, class T = int>
+ auto callSignalIfPluginActive(OrganizerProxy* proxy, const Signal& signal, T defaultReturn = T{}) {
+ return callIfPluginActive(proxy, [&signal](auto&&... args) {
+ return signal(std::forward<decltype(args)>(args)...);
+ }, defaultReturn);
+ }
+
+ template <class Signal, class T = int>
+ auto callSignalAlways(const Signal& signal) {
+ return [&signal](auto&&... args) {
+ return signal(std::forward<decltype(args)>(args)...);
+ };
+ }
+
}
#endif
diff --git a/src/settings.cpp b/src/settings.cpp
index d325c02f..85aa5f2f 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1317,6 +1317,16 @@ void PluginSettings::registerPlugin(IPlugin *plugin)
}
}
+void PluginSettings::unregisterPlugin(IPlugin* plugin)
+{
+ auto it = std::find(m_Plugins.begin(), m_Plugins.end(), plugin);
+ if (it != m_Plugins.end()) {
+ m_Plugins.erase(it);
+ }
+ m_PluginSettings.remove(plugin->name());
+ m_PluginDescriptions.remove(plugin->name());
+}
+
std::vector<MOBase::IPlugin*> PluginSettings::plugins() const
{
return m_Plugins;
diff --git a/src/settings.h b/src/settings.h
index 122eb729..48aefc27 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -304,9 +304,10 @@ public:
//
void clearPlugins();
- // adds the given plugin to the list and loads all of its settings
+ // adds/removes the given plugin to the list and loads all of its settings
//
void registerPlugin(MOBase::IPlugin *plugin);
+ void unregisterPlugin(MOBase::IPlugin* plugin);
// returns all the registered plugins
//