From 90154e621af03bc0464a17821f56385e6b0f6ac5 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:03:10 +0100 Subject: Add Settings::unregisterPlugin(). --- src/settings.cpp | 10 ++++++++++ src/settings.h | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src') 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 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 // -- cgit v1.3.1 From facb4e7fe4bcc30539ea3fc79a23ab217735a5ab Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:04:18 +0100 Subject: Do not store installers in InstallationManager, use the ones from PluginContainer. --- src/installationmanager.cpp | 25 ++++++++++--------------- src/installationmanager.h | 15 --------------- src/plugincontainer.cpp | 5 ++++- 3 files changed, 14 insertions(+), 31 deletions(-) (limited to 'src') 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(); + + 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 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(); + 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(); + 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 @@ -128,12 +128,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. */ @@ -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 m_Installers; - // Archive management. std::unique_ptr m_ArchiveHandler; QString m_CurrentFile; diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 5a485dfe..1fb8638f 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -301,6 +301,9 @@ void PluginContainer::setUserInterface(IUserInterface *userInterface) for (IPluginTool* tool : bf::at_key(m_Plugins)) { tool->setParentWidget(userInterface->mainWindow()); } + for (IPluginInstaller* installer : bf::at_key(m_Plugins)) { + installer->setParentWidget(userInterface->mainWindow()); + } } m_UserInterface = userInterface; @@ -537,7 +540,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam if (initPlugin(installer, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(installer); if (m_Organizer) { - m_Organizer->installationManager()->registerInstaller(installer); + installer->setInstallationManager(m_Organizer->installationManager()); } return installer; } -- cgit v1.3.1 From 89190af4b210f6cddf1b0ab90ab0ac08f94c2257 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:05:54 +0100 Subject: Add signals (not used yet) for registering/unregistering plugins. --- src/mainwindow.cpp | 11 ++++++++++- src/plugincontainer.h | 8 +++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d0ed5029..d9cfcc82 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -474,7 +474,16 @@ MainWindow::MainWindow(Settings &settings if (m_PluginContainer.implementInterface(plugin)) { updateModPageMenu(); } }); connect(&m_PluginContainer, &PluginContainer::pluginDisabled, this, [this](IPlugin* plugin) { if (m_PluginContainer.implementInterface(plugin)) { updateModPageMenu(); } }); - + connect(&m_PluginContainer, &PluginContainer::pluginRegistered, this, [this](IPlugin* plugin) { + updateModPageMenu(); + scheduleCheckForProblems(); + updateDownloadView(); + }); + connect(&m_PluginContainer, &PluginContainer::pluginUnregistered, this, [this](IPlugin* plugin) { + updateModPageMenu(); + scheduleCheckForProblems(); + updateDownloadView(); + }); connect(&m_OrganizerCore, &OrganizerCore::modInstalled, this, &MainWindow::modInstalled); connect(&m_OrganizerCore, &OrganizerCore::close, this, &QMainWindow::close); diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 26e12659..ef5f9441 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -335,11 +335,17 @@ 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: -- cgit v1.3.1 From 4f7f378d8d6f9ec93d7e024ce73cda6b177fc490 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:07:04 +0100 Subject: Add methods to load/reload/unload plugins. --- src/plugincontainer.cpp | 346 +++++++++++++++++++++++++++++++++++++----------- src/plugincontainer.h | 53 +++++++- 2 files changed, 322 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 1fb8638f..3579a0d3 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -106,7 +106,7 @@ const std::set 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) @@ -431,6 +431,8 @@ bool PluginContainer::initPlugin(IPlugin *plugin, IPluginProxy *pluginProxy, boo return false; } + proxy->setParent(as_qobject(plugin)); + // Update requirements: it->second.fetchRequirements(); @@ -442,7 +444,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 @@ -462,7 +469,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; @@ -487,7 +494,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam // way to cast directly between IPlugin* and IPluginDiagnose* bf::at_key(m_Plugins).push_back(plugin); - plugin->setProperty("filepath", fileName); + plugin->setProperty("filepath", QDir::cleanPath(filepath)); if (m_Organizer) { m_Organizer->settings().plugins().registerPlugin(pluginObj); @@ -499,8 +506,8 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam bf::at_key(m_Plugins).push_back(diagnose); bf::at_key(m_AccessPlugins)[diagnose] = pluginObj; m_DiagnosisConnections.push_back( - diagnose->onInvalidated([&] () { emit diagnosisUpdate(); }) - ); + diagnose->onInvalidated([&] () { emit diagnosisUpdate(); }) + ); } } { // file mapper plugin @@ -514,6 +521,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam IPluginModPage *modPage = qobject_cast(plugin); if (initPlugin(modPage, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(modPage); + emit pluginRegistered(modPage); return modPage; } } @@ -524,6 +532,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam if (initPlugin(game, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(game); registerGame(game); + emit pluginRegistered(game); return game; } } @@ -532,6 +541,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam IPluginTool *tool = qobject_cast(plugin); if (initPlugin(tool, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(tool); + emit pluginRegistered(tool); return tool; } } @@ -542,6 +552,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam if (m_Organizer) { installer->setInstallationManager(m_Organizer->installationManager()); } + emit pluginRegistered(installer); return installer; } } @@ -557,55 +568,12 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam IPluginProxy *proxy = qobject_cast(plugin); if (initPlugin(proxy, pluginProxy, skipInit)) { bf::at_key(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 matchingPlugins = proxy->load(pluginName); - - // We are going to group plugin by names and "fix" them later: - std::map> 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(", ")); - - // 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); - } - } - } - } + emit pluginRegistered(proxy); - } 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; } @@ -616,6 +584,7 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam IPlugin *dummy = qobject_cast(plugin); if (initPlugin(dummy, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(dummy); + emit pluginRegistered(dummy); return dummy; } } @@ -623,34 +592,59 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString &fileNam return nullptr; } -void PluginContainer::unloadPlugins() +std::vector PluginContainer::loadProxied(const QString& filepath, IPluginProxy* proxy) { - if (m_UserInterface != nullptr) { - m_UserInterface->disconnectPlugins(); - } + std::vector proxiedPlugins; - // disconnect all slots before unloading plugins so plugins don't have to take care of that - if (m_Organizer) { - m_Organizer->disconnectPlugins(); - } + 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 matchingPlugins = proxy->load(filepath); - bf::for_each(m_Plugins, [](auto& t) { t.second.clear(); }); - bf::for_each(m_AccessPlugins, [](auto& t) { t.second.clear(); }); - m_Requirements.clear(); + // We are going to group plugin by names and "fix" them later: + std::map> proxiedByNames; - for (const boost::signals2::connection &connection : m_DiagnosisConnections) { - connection.disconnect(); - } - m_DiagnosisConnections.clear(); + for (QObject* proxiedPlugin : matchingPlugins) { + if (proxiedPlugin != nullptr) { - 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()); + 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(proxied); + 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); + } + } + } } - delete loader; } + catch (const std::exception& e) { + reportError(QObject::tr("failed to initialize plugin %1: %2").arg(filepath).arg(e.what())); + } + + return proxiedPlugins; } IPlugin* PluginContainer::managedGame() const @@ -757,6 +751,21 @@ const PluginRequirements& PluginContainer::requirements(IPlugin* plugin) const return m_Requirements.at(plugin); } +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::managedGame(const QString &name) const { auto iter = m_SupportedGames.find(name); @@ -772,6 +781,193 @@ const PreviewGenerator &PluginContainer::previewGenerator() const return m_PreviewGenerator; } +void PluginContainer::simulateStartup(const std::vector& plugins) const +{ + // setUserInterface() + if (m_UserInterface) { + for (auto* plugin : plugins) { + if (auto* proxy = dynamic_cast(plugin)) { + proxy->setParentWidget(m_UserInterface->mainWindow()); + } + if (auto* modPage = dynamic_cast(plugin)) { + modPage->setParentWidget(m_UserInterface->mainWindow()); + } + if (auto* tool = dynamic_cast(plugin)) { + tool->setParentWidget(m_UserInterface->mainWindow()); + } + if (auto* installer = dynamic_cast(plugin)) { + installer->setParentWidget(m_UserInterface->mainWindow()); + } + } + } + + // Simulate callbacks, e.g. onUserInterfaceInitialized and onProfileChanged. + for (auto* plugin : plugins) { + auto* oproxy = organizerProxy(plugin); + oproxy->m_UserInterfaceInitialized(m_UserInterface->mainWindow()); + oproxy->m_ProfileChanged(nullptr, m_Organizer->currentProfile()); + } +} + +void PluginContainer::loadPlugin(QString const& filepath) +{ + std::vector plugins; + if (QFileInfo(filepath).isFile() && QLibrary::isLibrary(filepath)) { + std::unique_ptr pluginLoader(new QPluginLoader(filepath, this)); + if (pluginLoader->instance() == nullptr) { + log::error("failed to load plugin {}: {}", filepath, pluginLoader->errorString()); + } + else { + if (IPlugin* plugin = registerPlugin(pluginLoader->instance(), filepath, nullptr)) { + log::debug("loaded plugin '{}' from '{}' - [{}]", + plugin->name(), QFileInfo(filepath).fileName(), implementedInterfaces(plugin).join(", ")); + m_PluginLoaders.push_back(pluginLoader.release()); + plugins.push_back(plugin); + } + else { + m_FailedPlugins.push_back(filepath); + log::warn("plugin \"{}\" failed to load (may be outdated)", filepath); + } + } + } + else { + // We need to check if this can be handled by a proxy. + for (auto* proxy : this->plugins()) { + auto filepaths = proxy->pluginList( + QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath())); + if (filepaths.contains(filepath)) { + plugins = loadProxied(filepath, proxy); + break; + } + } + } + + for (auto* plugin : plugins) { + emit pluginRegistered(plugin); + } + + simulateStartup(plugins); +} + +void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) +{ + if (auto* game = qobject_cast(object)) { + unregisterGame(game); + } + + // We need to do this BEFORE unloading from the proxy otherwise the + // qobject_cast won't work. + bf::for_each(m_Plugins, [object](auto& t) { + using type = typename std::decay_t::value_type; + + // We do not want to remove from QObject since we are iterating over them. + if constexpr (!std::is_same{}) { + auto itp = std::find(t.second.begin(), t.second.end(), qobject_cast(object)); + if (itp != t.second.end()) { + t.second.erase(itp); + } + } + }); + + emit pluginUnregistered(plugin); + + // Remove from the members. + if (auto* diagnose = qobject_cast(object)) { + bf::at_key(m_AccessPlugins).erase(diagnose); + } + if (auto* mapper = qobject_cast(object)) { + bf::at_key(m_AccessPlugins).erase(mapper); + } + + auto& mapNames = bf::at_key(m_AccessPlugins); + if (mapNames.contains(plugin->name())) { + mapNames.erase(plugin->name()); + } + + m_Organizer->settings().plugins().unregisterPlugin(plugin); + + // 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(m_Plugins); + for (auto it = objects.begin(); it != objects.end(); ) { + auto* plugin = qobject_cast(*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() +{ + 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; + } +} + void PluginContainer::loadPlugins() { TimeThis tt("PluginContainer::loadPlugins()"); diff --git a/src/plugincontainer.h b/src/plugincontainer.h index ef5f9441..213aaa70 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> m_Requirements; - MOBase::IOrganizer* m_Organizer; + OrganizerProxy* m_Organizer; std::vector m_RequiredFor; PluginRequirements( PluginContainer* pluginContainer, MOBase::IPlugin* plugin, - MOBase::IOrganizer* proxy, MOBase::IPluginProxy* pluginProxy); + OrganizerProxy* proxy, MOBase::IPluginProxy* pluginProxy); }; @@ -190,6 +191,10 @@ public: void setUserInterface(IUserInterface *userInterface); + void loadPlugin(QString const& filepath); + void unloadPlugin(QString const& filepath); + void reloadPlugin(QString const& filepath); + void loadPlugins(); void unloadPlugins(); @@ -352,6 +357,49 @@ private: friend class PluginRequirements; + /** + * @return the organizer proxy for the given plugin. + */ + OrganizerProxy* organizerProxy(MOBase::IPlugin* plugin) const; + + /** + * @return 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; + + /** + * @return the path to the file or folder corresponding to the plugin. + */ + QString filepath(MOBase::IPlugin* plugin) 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 Load plugins from the given filepath using the given proxy. + * + * @param filepath Path to a folder/file containing plugin(s) that can be load by the given proxy. + * @param proxy Proxy to use to load plugins. + * + * @return the list of created plugins. + */ + std::vector loadProxied(const QString& filepath, MOBase::IPluginProxy* proxy); + + /** + * @brief Simulate MO2 startup for the given plugins. + * + * @param plugins Plugins to simulate startup for. + */ + void simulateStartup(const std::vector& plugins) const; /** * @brief Retrieved the (localized) names of interfaces implemented by the given @@ -399,6 +447,7 @@ 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); -- cgit v1.3.1 From cb453a9ba70106832eec2cb0feecbd91d2e8b7d7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:08:22 +0100 Subject: Fix callbacks for IOrganizer when plugin are unloaded/loaded. --- src/organizercore.cpp | 42 ++++++++++++++++++------------------- src/organizercore.h | 24 +++++++++++---------- src/organizerproxy.cpp | 57 ++++++++++++++++++++++++++++++++++---------------- src/organizerproxy.h | 23 +++++++++++++++++++- src/proxyutils.h | 15 +++++++++++++ 5 files changed, 109 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index f14d3234..7016f9a1 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1184,58 +1184,56 @@ bool OrganizerCore::previewFile( return true; } -bool OrganizerCore::onAboutToRun( +boost::signals2::connection OrganizerCore::onAboutToRun( const std::function &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 &func) { - auto conn = m_FinishedRun.connect(func); - return conn.connected(); + return m_FinishedRun.connect(func); } -bool OrganizerCore::onUserInterfaceInitialized(std::function const& func) +boost::signals2::connection OrganizerCore::onUserInterfaceInitialized(std::function const& func) { - return m_UserInterfaceInitialized.connect(func).connected(); + return m_UserInterfaceInitialized.connect(func); } -bool OrganizerCore::onProfileCreated(std::function const& func) +boost::signals2::connection OrganizerCore::onProfileCreated(std::function const& func) { - return m_ProfileCreated.connect(func).connected(); + return m_ProfileCreated.connect(func); } -bool OrganizerCore::onProfileRenamed(std::function const& func) +boost::signals2::connection OrganizerCore::onProfileRenamed(std::function const& func) { - return m_ProfileRenamed.connect(func).connected(); + return m_ProfileRenamed.connect(func); } -bool OrganizerCore::onProfileRemoved(std::function const& func) +boost::signals2::connection OrganizerCore::onProfileRemoved(std::function const& func) { - return m_ProfileRemoved.connect(func).connected(); + return m_ProfileRemoved.connect(func); } -bool OrganizerCore::onProfileChanged(std::function const& func) +boost::signals2::connection OrganizerCore::onProfileChanged(std::function const& func) { - return m_ProfileChanged.connect(func).connected(); + return m_ProfileChanged.connect(func); } -bool OrganizerCore::onPluginSettingChanged(std::function const& func) +boost::signals2::connection OrganizerCore::onPluginSettingChanged(std::function const& func) { - return m_PluginSettingChanged.connect(func).connected(); + return m_PluginSettingChanged.connect(func); } -bool OrganizerCore::onPluginEnabled(std::function const& func) +boost::signals2::connection OrganizerCore::onPluginEnabled(std::function const& func) { - return m_PluginEnabled.connect(func).connected(); + return m_PluginEnabled.connect(func); } -bool OrganizerCore::onPluginDisabled(std::function const& func) +boost::signals2::connection OrganizerCore::onPluginDisabled(std::function const& func) { - return m_PluginDisabled.connect(func).connected(); + return m_PluginDisabled.connect(func); } void OrganizerCore::refresh(bool saveChanges) diff --git a/src/organizercore.h b/src/organizercore.h index 23b624e8..1b77b7f5 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; using SignalProfileChanged = boost::signals2::signal; using SignalPluginSettingChanged = boost::signals2::signal; - using SignalPluginEnabled = boost::signals2::signal; + using SignalPluginEnabled = boost::signals2::signal; public: @@ -328,16 +330,16 @@ public: ModList *modList(); void refresh(bool saveChanges = true); - bool onAboutToRun(const std::function& func); - bool onFinishedRun(const std::function& func); - bool onUserInterfaceInitialized(std::function const& func); - bool onProfileCreated(std::function const& func); - bool onProfileRenamed(std::function const& func); - bool onProfileRemoved(std::function const& func); - bool onProfileChanged(std::function const& func); - bool onPluginSettingChanged(std::function const& func); - bool onPluginEnabled(std::function const& func); - bool onPluginDisabled(std::function const& func); + boost::signals2::connection onAboutToRun(const std::function& func); + boost::signals2::connection onFinishedRun(const std::function& func); + boost::signals2::connection onUserInterfaceInitialized(std::function const& func); + boost::signals2::connection onProfileCreated(std::function const& func); + boost::signals2::connection onProfileRenamed(std::function const& func); + boost::signals2::connection onProfileRemoved(std::function const& func); + boost::signals2::connection onProfileChanged(std::function const& func); + boost::signals2::connection onPluginSettingChanged(std::function const& func); + boost::signals2::connection onPluginEnabled(std::function const& func); + boost::signals2::connection onPluginDisabled(std::function const& func); bool getArchiveParsing() const { diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 92f250ea..13b91ff4 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -22,10 +22,31 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin : m_Proxied(organizer) , m_PluginContainer(pluginContainer) , m_Plugin(plugin) + , m_PluginName(plugin->name()) , m_DownloadManagerProxy(std::make_unique(this, organizer->downloadManager())) , m_ModListProxy(std::make_unique(this, organizer->modList())) , m_PluginListProxy(std::make_unique(this, organizer->pluginList())) { + 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))); + +} + +OrganizerProxy::~OrganizerProxy() +{ + log::debug("~OrganizerProxy() for {}.", m_PluginName); + for (auto& conn : m_Connections) { + conn.disconnect(); + } } IModRepositoryBridge *OrganizerProxy::createNexusBridge() const @@ -265,68 +286,68 @@ MOBase::IPluginGame const *OrganizerProxy::managedGame() const bool OrganizerProxy::onAboutToRun(const std::function& 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& func) { - return m_Proxied->onFinishedRun(MOShared::callIfPluginActive(this, func)); -} - -bool OrganizerProxy::onUserInterfaceInitialized(std::function 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 const& func) { - return m_Proxied->onProfileCreated(MOShared::callIfPluginActive(this, func)); + return m_ProfileCreated.connect(func).connected(); } bool OrganizerProxy::onProfileRenamed(std::function const& func) { - return m_Proxied->onProfileRenamed(MOShared::callIfPluginActive(this, func)); + return m_ProfileRenamed.connect(func).connected(); } bool OrganizerProxy::onProfileRemoved(std::function const& func) { - return m_Proxied->onProfileRemoved(MOShared::callIfPluginActive(this, func)); + return m_ProfileRemoved.connect(func).connected(); } bool OrganizerProxy::onProfileChanged(std::function const& func) { - return m_Proxied->onProfileChanged(MOShared::callIfPluginActive(this, func)); + return m_ProfileChanged.connect(func).connected(); +} + +bool OrganizerProxy::onUserInterfaceInitialized(std::function 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 const& func) { - return m_Proxied->onPluginSettingChanged(func); + return m_PluginSettingChanged.connect(func).connected(); } bool OrganizerProxy::onPluginEnabled(std::function const& func) { - return m_Proxied->onPluginEnabled(func); + return m_PluginEnabled.connect(func).connected(); } bool OrganizerProxy::onPluginEnabled(const QString& pluginName, std::function 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 const& func) { - return m_Proxied->onPluginDisabled(func); + return m_PluginDisabled.connect(func).connected(); } bool OrganizerProxy::onPluginDisabled(const QString& pluginName, std::function 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..fb463822 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -6,7 +6,8 @@ #include #include -class OrganizerCore; +#include "organizercore.h" + class PluginContainer; class DownloadManagerProxy; class ModListProxy; @@ -18,6 +19,7 @@ class OrganizerProxy : public MOBase::IOrganizer public: OrganizerProxy(OrganizerCore *organizer, PluginContainer *pluginContainer, MOBase::IPlugin *plugin); + ~OrganizerProxy(); /** * @return the plugin corresponding to this proxy. @@ -76,12 +78,31 @@ public: virtual MOBase::IPluginGame const *managedGame() const; +protected: + + // The container needs access to some callbacks to simulate startup. + friend class PluginContainer; + private: OrganizerCore *m_Proxied; PluginContainer *m_PluginContainer; MOBase::IPlugin *m_Plugin; + QString m_PluginName; + + 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 m_Connections; std::unique_ptr m_DownloadManagerProxy; std::unique_ptr m_ModListProxy; 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 + auto callSignalIfPluginActive(OrganizerProxy* proxy, const Signal& signal, T defaultReturn = T{}) { + return callIfPluginActive(proxy, [&signal](auto&&... args) { + return signal(std::forward(args)...); + }, defaultReturn); + } + + template + auto callSignalAlways(const Signal& signal) { + return [&signal](auto&&... args) { + return signal(std::forward(args)...); + }; + } + } #endif -- cgit v1.3.1 From 4240f10260cbffbff52bf2d549100f4690a642b5 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 2 Dec 2020 21:09:42 +0100 Subject: Fix callbacks for pluginlist, modlist and download manager. --- src/downloadmanager.cpp | 18 ++++++++-------- src/downloadmanager.h | 18 +++++++++------- src/downloadmanagerproxy.cpp | 26 ++++++++++++++++------ src/downloadmanagerproxy.h | 14 +++++++++--- src/modlist.cpp | 19 ++++++++--------- src/modlist.h | 38 +++++++++++++++++---------------- src/modlistproxy.cpp | 27 +++++++++++++++++------ src/modlistproxy.h | 14 +++++++++--- src/organizercore.cpp | 8 ++++++- src/organizercore.h | 7 ++++++ src/pluginlist.cpp | 27 +++++++++++------------ src/pluginlist.h | 51 ++++++++++++++++++++++++++------------------ src/pluginlistproxy.cpp | 27 +++++++++++++++++------ src/pluginlistproxy.h | 13 ++++++++--- 14 files changed, 198 insertions(+), 109 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 648cfaa2..ff4e9458 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(parent); @@ -1787,24 +1787,24 @@ QString DownloadManager::downloadPath(int id) return getFilePath(id); } -bool DownloadManager::onDownloadComplete(const std::function& callback) +boost::signals2::connection DownloadManager::onDownloadComplete(const std::function& callback) { - return m_DownloadComplete.connect(callback).connected(); + return m_DownloadComplete.connect(callback); } -bool DownloadManager::onDownloadPaused(const std::function& callback) +boost::signals2::connection DownloadManager::onDownloadPaused(const std::function& callback) { - return m_DownloadPaused.connect(callback).connected(); + return m_DownloadPaused.connect(callback); } -bool DownloadManager::onDownloadFailed(const std::function& callback) +boost::signals2::connection DownloadManager::onDownloadFailed(const std::function& callback) { - return m_DownloadFailed.connect(callback).connected(); + return m_DownloadFailed.connect(callback); } -bool DownloadManager::onDownloadRemoved(const std::function& callback) +boost::signals2::connection DownloadManager::onDownloadRemoved(const std::function& 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(0,0,0,0,0)), m_HasData(false) {} }; + friend class DownloadManagerProxy; + using SignalDownloadCallback = boost::signals2::signal; 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& callback) override; - bool onDownloadPaused(const std::function& callback) override; - bool onDownloadFailed(const std::function& callback) override; - bool onDownloadRemoved(const std::function& callback) override; + boost::signals2::connection onDownloadComplete(const std::function& callback); + boost::signals2::connection onDownloadPaused(const std::function& callback); + boost::signals2::connection onDownloadFailed(const std::function& callback); + boost::signals2::connection onDownloadRemoved(const std::function& callback); /** * @brief retrieve a download index from the filename diff --git a/src/downloadmanagerproxy.cpp b/src/downloadmanagerproxy.cpp index 4a5c9b6b..06dad21f 100644 --- a/src/downloadmanagerproxy.cpp +++ b/src/downloadmanagerproxy.cpp @@ -4,9 +4,23 @@ #include "organizerproxy.h" using namespace MOBase; +using namespace MOShared; -DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, IDownloadManager* downloadManager) : - m_OrganizerProxy(oproxy), m_Proxied(downloadManager) { } +DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, DownloadManager* downloadManager) : + m_OrganizerProxy(oproxy), m_Proxied(downloadManager) +{ + 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))); +} + +DownloadManagerProxy::~DownloadManagerProxy() +{ + for (auto& conn : m_Connections) { + conn.disconnect(); + } +} int DownloadManagerProxy::startDownloadURLs(const QStringList& urls) { @@ -25,20 +39,20 @@ QString DownloadManagerProxy::downloadPath(int id) bool DownloadManagerProxy::onDownloadComplete(const std::function& callback) { - return m_Proxied->onDownloadComplete(MOShared::callIfPluginActive(m_OrganizerProxy, callback)); + return m_DownloadComplete.connect(callback).connected(); } bool DownloadManagerProxy::onDownloadPaused(const std::function& callback) { - return m_Proxied->onDownloadPaused(MOShared::callIfPluginActive(m_OrganizerProxy, callback)); + return m_DownloadPaused.connect(callback).connected(); } bool DownloadManagerProxy::onDownloadFailed(const std::function& callback) { - return m_Proxied->onDownloadFailed(MOShared::callIfPluginActive(m_OrganizerProxy, callback)); + return m_DownloadFailed.connect(callback).connected(); } bool DownloadManagerProxy::onDownloadRemoved(const std::function& 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..23692179 100644 --- a/src/downloadmanagerproxy.h +++ b/src/downloadmanagerproxy.h @@ -2,6 +2,7 @@ #define DOWNLOADMANAGERPROXY_H #include +#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; @@ -25,7 +26,14 @@ public: private: 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 m_Connections; }; #endif // ORGANIZERPROXY_H 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& func) +boost::signals2::connection ModList::onModInstalled(const std::function& func) { - return m_ModInstalled.connect(func).connected(); + return m_ModInstalled.connect(func); } -bool ModList::onModRemoved(const std::function& func) +boost::signals2::connection ModList::onModRemoved(const std::function& func) { - return m_ModRemoved.connect(func).connected(); + return m_ModRemoved.connect(func); } -bool ModList::onModStateChanged(const std::function&)>& func) +boost::signals2::connection ModList::onModStateChanged(const std::function&)>& 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 modIndices) const { - std::map mods; + std::map 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 modIndices) const m_ModStateChanged(mods); } -bool ModList::onModMoved(const std::function &func) +boost::signals2::connection ModList::onModMoved(const std::function &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; using SignalModRemoved = boost::signals2::signal; - using SignalModStateChanged = boost::signals2::signal&)>; + using SignalModStateChanged = boost::signals2::signal&)>; using SignalModMoved = boost::signals2::signal; 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& func) override; - bool onModRemoved(const std::function& func) override; - bool onModStateChanged(const std::function&)>& func) override; - bool onModMoved(const std::function &func) override; + boost::signals2::connection onModInstalled(const std::function& func); + boost::signals2::connection onModRemoved(const std::function& func); + boost::signals2::connection onModStateChanged(const std::function&)>& func); + boost::signals2::connection onModMoved(const std::function &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 state; + QFlags state; }; private: diff --git a/src/modlistproxy.cpp b/src/modlistproxy.cpp index 8fcbdbdf..991476da 100644 --- a/src/modlistproxy.cpp +++ b/src/modlistproxy.cpp @@ -1,11 +1,26 @@ #include "modlistproxy.h" #include "organizerproxy.h" #include "proxyutils.h" +#include "modlist.h" using namespace MOBase; +using namespace MOShared; -ModListProxy::ModListProxy(OrganizerProxy* oproxy, IModList* modlist) : - m_OrganizerProxy(oproxy), m_Proxied(modlist) { } +ModListProxy::ModListProxy(OrganizerProxy* oproxy, ModList* modlist) : + m_OrganizerProxy(oproxy), m_Proxied(modlist) +{ + 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))); +} + +ModListProxy::~ModListProxy() +{ + for (auto& conn : m_Connections) { + conn.disconnect(); + } +} QString ModListProxy::displayName(const QString& internalName) const { @@ -59,20 +74,20 @@ bool ModListProxy::setPriority(const QString& name, int newPriority) bool ModListProxy::onModInstalled(const std::function& func) { - return m_Proxied->onModInstalled(MOShared::callIfPluginActive(m_OrganizerProxy, func)); + return m_ModInstalled.connect(func).connected(); } bool ModListProxy::onModRemoved(const std::function& func) { - return m_Proxied->onModRemoved(MOShared::callIfPluginActive(m_OrganizerProxy, func)); + return m_ModRemoved.connect(func).connected(); } bool ModListProxy::onModStateChanged(const std::function&)>& func) { - return m_Proxied->onModStateChanged(MOShared::callIfPluginActive(m_OrganizerProxy, func)); + return m_ModStateChanged.connect(func).connected(); } bool ModListProxy::onModMoved(const std::function& 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..12d74e4a 100644 --- a/src/modlistproxy.h +++ b/src/modlistproxy.h @@ -2,6 +2,7 @@ #define MODLISTPROXY_H #include +#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; @@ -31,7 +32,14 @@ public: private: 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 m_Connections; }; #endif // ORGANIZERPROXY_H diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 7016f9a1..6a333a22 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -72,6 +72,7 @@ #include #include +#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) @@ -1529,6 +1530,11 @@ IPluginGame const *OrganizerCore::managedGame() const return m_GamePlugin; } +IOrganizer const* OrganizerCore::managedGameOrganizer() const +{ + return m_PluginContainer->requirements(m_GamePlugin).m_Organizer; +} + std::vector OrganizerCore::enabledArchives() { std::vector result; diff --git a/src/organizercore.h b/src/organizercore.h index 1b77b7f5..cb612eae 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -233,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. 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 . #include #include +#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(); 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&)>& func) +boost::signals2::connection PluginList::onPluginStateChanged(const std::function&)>& 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 &callback) +boost::signals2::connection PluginList::onRefreshed(const std::function &callback) { - auto conn = m_Refreshed.connect(callback); - return conn.connected(); + return m_Refreshed.connect(callback); } -bool PluginList::onPluginMoved(const std::function &func) +boost::signals2::connection PluginList::onPluginMoved(const std::function &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 #include +class OrganizerCore; + template 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; @@ -94,9 +96,13 @@ public: COL_LASTCOLUMN = COL_MODINDEX }; - typedef boost::signals2::signal SignalRefreshed; - typedef boost::signals2::signal SignalPluginMoved; - typedef boost::signals2::signal&)> SignalPluginStateChanged; + using PluginStates = MOBase::IPluginList::PluginStates; + + friend class PluginListProxy; + + using SignalRefreshed = boost::signals2::signal; + using SignalPluginMoved = boost::signals2::signal; + using SignalPluginStateChanged = boost::signals2::signal&)>; 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 &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 &func) override; - virtual bool onPluginStateChanged(const std::function&)> &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& callback); + boost::signals2::connection onPluginMoved(const std::function& func); + boost::signals2::connection onPluginStateChanged(const std::function&)> &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 m_ESPs; mutable std::map m_LastSaveHash; diff --git a/src/pluginlistproxy.cpp b/src/pluginlistproxy.cpp index 1f8dc2b4..b51d108b 100644 --- a/src/pluginlistproxy.cpp +++ b/src/pluginlistproxy.cpp @@ -3,11 +3,24 @@ #include "proxyutils.h" using namespace MOBase; +using namespace MOShared; -PluginListProxy::PluginListProxy(OrganizerProxy* oproxy, IPluginList* pluginlist) : - m_OrganizerProxy(oproxy), m_Proxied(pluginlist) { } +PluginListProxy::PluginListProxy(OrganizerProxy* oproxy, PluginList* pluginlist) : + m_OrganizerProxy(oproxy), m_Proxied(pluginlist) +{ + 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))); +} + +PluginListProxy::~PluginListProxy() +{ + for (auto& conn : m_Connections) { + conn.disconnect(); + } +} -QStringList PluginListProxy::pluginNames() const +QStringList PluginListProxy::pluginNames() const { return m_Proxied->pluginNames(); } @@ -57,17 +70,17 @@ QString PluginListProxy::origin(const QString& name) const return m_Proxied->origin(name); } -bool PluginListProxy::onRefreshed(const std::function& callback) +bool PluginListProxy::onRefreshed(const std::function& func) { - return m_Proxied->onRefreshed(MOShared::callIfPluginActive(m_OrganizerProxy, callback)); + return m_Refreshed.connect(func).connected(); } bool PluginListProxy::onPluginMoved(const std::function& func) { - return m_Proxied->onPluginMoved(MOShared::callIfPluginActive(m_OrganizerProxy, func)); + return m_PluginMoved.connect(func).connected(); } bool PluginListProxy::onPluginStateChanged(const std::function&)> &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..04a6ad55 100644 --- a/src/pluginlistproxy.h +++ b/src/pluginlistproxy.h @@ -2,6 +2,7 @@ #define PLUGINLISTPROXY_H #include +#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; @@ -30,7 +31,13 @@ public: private: OrganizerProxy* m_OrganizerProxy; - IPluginList* m_Proxied; + PluginList* m_Proxied; + + PluginList::SignalRefreshed m_Refreshed; + PluginList::SignalPluginMoved m_PluginMoved; + PluginList::SignalPluginStateChanged m_PluginStateChanged; + + std::vector m_Connections; }; #endif // ORGANIZERPROXY_H -- cgit v1.3.1 From 47626d654f2493cddf2126cd5e08e8be7efb67a8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 07:44:24 +0100 Subject: Clarify comment. --- src/organizerproxy.cpp | 2 +- src/plugincontainer.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 13b91ff4..1f73ee13 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -43,7 +43,7 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin OrganizerProxy::~OrganizerProxy() { - log::debug("~OrganizerProxy() for {}.", m_PluginName); + log::debug("Deleting organizer proxy for plugin '{}'.", m_PluginName); for (auto& conn : m_Connections) { conn.disconnect(); } diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 3579a0d3..9da467a4 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -855,8 +855,8 @@ void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) unregisterGame(game); } - // We need to do this BEFORE unloading from the proxy otherwise the - // qobject_cast won't work. + // 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::value_type; -- cgit v1.3.1 From 1a486a09428e30596bc47867917156bcffdb7632 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 07:52:24 +0100 Subject: Add MainWindow::onPluginRegistrationChanged. --- src/mainwindow.cpp | 19 +++++++++---------- src/mainwindow.h | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d9cfcc82..8e08103f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -474,16 +474,8 @@ MainWindow::MainWindow(Settings &settings if (m_PluginContainer.implementInterface(plugin)) { updateModPageMenu(); } }); connect(&m_PluginContainer, &PluginContainer::pluginDisabled, this, [this](IPlugin* plugin) { if (m_PluginContainer.implementInterface(plugin)) { updateModPageMenu(); } }); - connect(&m_PluginContainer, &PluginContainer::pluginRegistered, this, [this](IPlugin* plugin) { - updateModPageMenu(); - scheduleCheckForProblems(); - updateDownloadView(); - }); - connect(&m_PluginContainer, &PluginContainer::pluginUnregistered, this, [this](IPlugin* plugin) { - updateModPageMenu(); - scheduleCheckForProblems(); - updateDownloadView(); - }); + 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); @@ -5199,6 +5191,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..17c27b29 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -635,6 +635,8 @@ private slots: // ui slots void on_saveModsButton_clicked(); void on_managedArchiveLabel_linkHovered(const QString &link); + void onPluginRegistrationChanged(); + void storeSettings(); void readSettings(); void setupModList(); -- cgit v1.3.1 From a6654eaec8190d7f9297a35499f8271a8f28176c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 07:58:58 +0100 Subject: Centralize loading Qt plugin in loadQtPlugin. --- src/plugincontainer.cpp | 61 +++++++++++++++++++++++-------------------------- src/plugincontainer.h | 7 ++++++ 2 files changed, 35 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 9da467a4..1bcf7243 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -592,6 +592,28 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString& filepat return nullptr; } +MOBase::IPlugin* PluginContainer::loadQtPlugin(const QString& filepath) +{ + std::unique_ptr pluginLoader(new QPluginLoader(filepath, this)); + if (pluginLoader->instance() == nullptr) { + m_FailedPlugins.push_back(filepath); + log::error("failed to load plugin {}: {}",filepath, pluginLoader->errorString()); + } + else { + if (IPlugin* plugin = registerPlugin(pluginLoader->instance(), filepath, nullptr); plugin) { + log::debug("loaded plugin '{}' from '{}' - [{}]", + plugin->name(), QFileInfo(filepath).fileName(), implementedInterfaces(plugin).join(", ")); + m_PluginLoaders.push_back(pluginLoader.release()); + return plugin; + } + else { + m_FailedPlugins.push_back(filepath); + log::warn("plugin '{}' failed to load (may be outdated)", filepath); + } + } + return nullptr; +} + std::vector PluginContainer::loadProxied(const QString& filepath, IPluginProxy* proxy) { std::vector proxiedPlugins; @@ -813,21 +835,9 @@ void PluginContainer::loadPlugin(QString const& filepath) { std::vector plugins; if (QFileInfo(filepath).isFile() && QLibrary::isLibrary(filepath)) { - std::unique_ptr pluginLoader(new QPluginLoader(filepath, this)); - if (pluginLoader->instance() == nullptr) { - log::error("failed to load plugin {}: {}", filepath, pluginLoader->errorString()); - } - else { - if (IPlugin* plugin = registerPlugin(pluginLoader->instance(), filepath, nullptr)) { - log::debug("loaded plugin '{}' from '{}' - [{}]", - plugin->name(), QFileInfo(filepath).fileName(), implementedInterfaces(plugin).join(", ")); - m_PluginLoaders.push_back(pluginLoader.release()); - plugins.push_back(plugin); - } - else { - m_FailedPlugins.push_back(filepath); - log::warn("plugin \"{}\" failed to load (may be outdated)", filepath); - } + IPlugin* plugin = loadQtPlugin(filepath); + if (plugin) { + plugins.push_back(plugin); } } else { @@ -1062,24 +1072,9 @@ void PluginContainer::loadPlugins() loadCheck.flush(); } - QString pluginName = iter.filePath(); - if (QLibrary::isLibrary(pluginName)) { - std::unique_ptr 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); } } diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 213aaa70..43c7973f 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -394,6 +394,13 @@ private: */ std::vector loadProxied(const QString& filepath, MOBase::IPluginProxy* proxy); + /** + * @brief Load the Qt plugin from the given file. + * + * @param filepath Path to the DLL containing the Qt plugin. + */ + MOBase::IPlugin* loadQtPlugin(const QString& filepath); + /** * @brief Simulate MO2 startup for the given plugins. * -- cgit v1.3.1 From f229dbaaa7f516dc1958d0a0555b47cb17136377 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 08:03:40 +0100 Subject: Do not store the list of preview plugins in PreviewGenerator. --- src/plugincontainer.cpp | 1 - src/previewgenerator.cpp | 31 +++++++++++++++---------------- src/previewgenerator.h | 8 -------- 3 files changed, 15 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 1bcf7243..9b73a964 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -560,7 +560,6 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString& filepat IPluginPreview *preview = qobject_cast(plugin); if (initPlugin(preview, pluginProxy, skipInit)) { bf::at_key(m_Plugins).push_back(preview); - m_PreviewGenerator.registerPlugin(preview); return preview; } } diff --git a/src/previewgenerator.cpp b/src/previewgenerator.cpp index c7df5a25..904d679f 100644 --- a/src/previewgenerator.cpp +++ b/src/previewgenerator.cpp @@ -27,33 +27,32 @@ along with Mod Organizer. If not, see . #include "plugincontainer.h" +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(); + 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(); + 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..f70649b5 100644 --- a/src/previewgenerator.h +++ b/src/previewgenerator.h @@ -33,21 +33,13 @@ class PreviewGenerator public: PreviewGenerator(const PluginContainer* pluginContainer); - void registerPlugin(MOBase::IPluginPreview *plugin); - bool previewSupported(const QString &fileExtension) const; QWidget *genPreview(const QString &fileName) const; -private: - - QWidget *genImagePreview(const QString &fileName) const; - QWidget *genTxtPreview(const QString &fileName) const; - private: const PluginContainer* m_PluginContainer; - std::map m_PreviewPlugins; QSize m_MaxSize; }; -- cgit v1.3.1 From a30d49f4fc845164ef6bba4d2a834efce1dc01d7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 08:50:31 +0100 Subject: Avoid duplicating code to simulate startup. --- src/downloadmanagerproxy.cpp | 12 ++- src/downloadmanagerproxy.h | 6 ++ src/iuserinterface.h | 2 - src/mainwindow.cpp | 15 +--- src/mainwindow.h | 8 -- src/modlistproxy.cpp | 12 ++- src/modlistproxy.h | 6 ++ src/organizerproxy.cpp | 24 ++++- src/organizerproxy.h | 17 ++++ src/plugincontainer.cpp | 207 ++++++++++++++++++++----------------------- src/plugincontainer.h | 37 ++++---- src/pluginlistproxy.cpp | 12 ++- src/pluginlistproxy.h | 6 ++ 13 files changed, 208 insertions(+), 156 deletions(-) (limited to 'src') diff --git a/src/downloadmanagerproxy.cpp b/src/downloadmanagerproxy.cpp index 06dad21f..95099044 100644 --- a/src/downloadmanagerproxy.cpp +++ b/src/downloadmanagerproxy.cpp @@ -7,7 +7,14 @@ using namespace MOBase; using namespace MOShared; DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, DownloadManager* downloadManager) : - m_OrganizerProxy(oproxy), m_Proxied(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))); @@ -15,11 +22,12 @@ DownloadManagerProxy::DownloadManagerProxy(OrganizerProxy* oproxy, DownloadManag m_Connections.push_back(m_Proxied->onDownloadPaused(callSignalIfPluginActive(m_OrganizerProxy, m_DownloadPaused))); } -DownloadManagerProxy::~DownloadManagerProxy() +void DownloadManagerProxy::disconnectSignals() { for (auto& conn : m_Connections) { conn.disconnect(); } + m_Connections.clear(); } int DownloadManagerProxy::startDownloadURLs(const QStringList& urls) diff --git a/src/downloadmanagerproxy.h b/src/downloadmanagerproxy.h index 23692179..729fde91 100644 --- a/src/downloadmanagerproxy.h +++ b/src/downloadmanagerproxy.h @@ -25,6 +25,12 @@ public: private: + friend class OrganizerProxy; + + // See OrganizerProxy::connectSignals(). + void connectSignals(); + void disconnectSignals(); + OrganizerProxy* m_OrganizerProxy; DownloadManager* m_Proxied; 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 8e08103f..a270fd10 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -512,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()); } @@ -705,7 +703,6 @@ MainWindow::~MainWindow() try { cleanup(); - m_PluginContainer.setUserInterface(nullptr); m_OrganizerCore.setUserInterface(nullptr); if (m_IntegratedBrowser) { @@ -744,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 @@ -1413,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); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 17c27b29..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(); diff --git a/src/modlistproxy.cpp b/src/modlistproxy.cpp index 991476da..6165fc3f 100644 --- a/src/modlistproxy.cpp +++ b/src/modlistproxy.cpp @@ -7,7 +7,14 @@ using namespace MOBase; using namespace MOShared; ModListProxy::ModListProxy(OrganizerProxy* oproxy, ModList* modlist) : - m_OrganizerProxy(oproxy), m_Proxied(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))); @@ -15,11 +22,12 @@ ModListProxy::ModListProxy(OrganizerProxy* oproxy, ModList* modlist) : m_Connections.push_back(m_Proxied->onModStateChanged(callSignalIfPluginActive(m_OrganizerProxy, m_ModStateChanged))); } -ModListProxy::~ModListProxy() +void ModListProxy::disconnectSignals() { for (auto& conn : m_Connections) { conn.disconnect(); } + m_Connections.clear(); } QString ModListProxy::displayName(const QString& internalName) const diff --git a/src/modlistproxy.h b/src/modlistproxy.h index 12d74e4a..162b0b43 100644 --- a/src/modlistproxy.h +++ b/src/modlistproxy.h @@ -31,6 +31,12 @@ public: private: + friend class OrganizerProxy; + + // See OrganizerProxy::connectSignals(). + void connectSignals(); + void disconnectSignals(); + OrganizerProxy* m_OrganizerProxy; ModList* m_Proxied; diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 1f73ee13..c41045e1 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -25,7 +25,14 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin , m_PluginName(plugin->name()) , m_DownloadManagerProxy(std::make_unique(this, organizer->downloadManager())) , m_ModListProxy(std::make_unique(this, organizer->modList())) - , m_PluginListProxy(std::make_unique(this, organizer->pluginList())) + , m_PluginListProxy(std::make_unique(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))); @@ -39,14 +46,25 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin 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(); } -OrganizerProxy::~OrganizerProxy() +void OrganizerProxy::disconnectSignals() { - log::debug("Deleting organizer proxy for plugin '{}'.", m_PluginName); + log::debug("Disconnecting organizer proxy for plugin '{}'.", m_PluginName); + + // 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 diff --git a/src/organizerproxy.h b/src/organizerproxy.h index fb463822..c52f2730 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -21,11 +21,15 @@ 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; @@ -83,6 +87,19 @@ 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; diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 9b73a964..3c8050cf 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -289,24 +289,10 @@ PluginContainer::~PluginContainer() { unloadPlugins(); } -void PluginContainer::setUserInterface(IUserInterface *userInterface) +void PluginContainer::startPlugins(IUserInterface *userInterface) { - if (userInterface) { - for (IPluginProxy* proxy : bf::at_key(m_Plugins)) { - proxy->setParentWidget(userInterface->mainWindow()); - } - for (IPluginModPage* modPage : bf::at_key(m_Plugins)) { - modPage->setParentWidget(userInterface->mainWindow()); - } - for (IPluginTool* tool : bf::at_key(m_Plugins)) { - tool->setParentWidget(userInterface->mainWindow()); - } - for (IPluginInstaller* installer : bf::at_key(m_Plugins)) { - installer->setParentWidget(userInterface->mainWindow()); - } - } - m_UserInterface = userInterface; + startPluginsImpl(plugins()); } QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const @@ -591,83 +577,6 @@ IPlugin* PluginContainer::registerPlugin(QObject *plugin, const QString& filepat return nullptr; } -MOBase::IPlugin* PluginContainer::loadQtPlugin(const QString& filepath) -{ - std::unique_ptr pluginLoader(new QPluginLoader(filepath, this)); - if (pluginLoader->instance() == nullptr) { - m_FailedPlugins.push_back(filepath); - log::error("failed to load plugin {}: {}",filepath, pluginLoader->errorString()); - } - else { - if (IPlugin* plugin = registerPlugin(pluginLoader->instance(), filepath, nullptr); plugin) { - log::debug("loaded plugin '{}' from '{}' - [{}]", - plugin->name(), QFileInfo(filepath).fileName(), implementedInterfaces(plugin).join(", ")); - m_PluginLoaders.push_back(pluginLoader.release()); - return plugin; - } - else { - m_FailedPlugins.push_back(filepath); - log::warn("plugin '{}' failed to load (may be outdated)", filepath); - } - } - return nullptr; -} - -std::vector PluginContainer::loadProxied(const QString& filepath, IPluginProxy* proxy) -{ - std::vector 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 matchingPlugins = proxy->load(filepath); - - // We are going to group plugin by names and "fix" them later: - std::map> 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(proxied); - 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; -} - IPlugin* PluginContainer::managedGame() const { // TODO: This const_cast is safe but ugly. Most methods require a IPlugin*, so @@ -802,39 +711,119 @@ const PreviewGenerator &PluginContainer::previewGenerator() const return m_PreviewGenerator; } -void PluginContainer::simulateStartup(const std::vector& plugins) const +void PluginContainer::startPluginsImpl(const std::vector& plugins) const { // setUserInterface() if (m_UserInterface) { for (auto* plugin : plugins) { - if (auto* proxy = dynamic_cast(plugin)) { + if (auto* proxy = qobject_cast(plugin)) { proxy->setParentWidget(m_UserInterface->mainWindow()); } - if (auto* modPage = dynamic_cast(plugin)) { + if (auto* modPage = qobject_cast(plugin)) { modPage->setParentWidget(m_UserInterface->mainWindow()); } - if (auto* tool = dynamic_cast(plugin)) { + if (auto* tool = qobject_cast(plugin)) { tool->setParentWidget(m_UserInterface->mainWindow()); } - if (auto* installer = dynamic_cast(plugin)) { + if (auto* installer = qobject_cast(plugin)) { installer->setParentWidget(m_UserInterface->mainWindow()); } } } - // Simulate callbacks, e.g. onUserInterfaceInitialized and onProfileChanged. - for (auto* plugin : plugins) { + // Trigger initial callbacks, e.g. onUserInterfaceInitialized and onProfileChanged. + for (auto* object : plugins) { + auto* plugin = qobject_cast(object); auto* oproxy = organizerProxy(plugin); + oproxy->connectSignals(); oproxy->m_UserInterfaceInitialized(m_UserInterface->mainWindow()); oproxy->m_ProfileChanged(nullptr, m_Organizer->currentProfile()); } } +std::vector PluginContainer::loadProxied(const QString& filepath, IPluginProxy* proxy) +{ + std::vector 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 matchingPlugins = proxy->load(filepath); + + // We are going to group plugin by names and "fix" them later: + std::map> 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 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 plugins; + std::vector plugins; if (QFileInfo(filepath).isFile() && QLibrary::isLibrary(filepath)) { - IPlugin* plugin = loadQtPlugin(filepath); + QObject* plugin = loadQtPlugin(filepath); if (plugin) { plugins.push_back(plugin); } @@ -852,10 +841,10 @@ void PluginContainer::loadPlugin(QString const& filepath) } for (auto* plugin : plugins) { - emit pluginRegistered(plugin); + emit pluginRegistered(qobject_cast(plugin)); } - simulateStartup(plugins); + startPluginsImpl(plugins); } void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) @@ -895,6 +884,11 @@ void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) 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); @@ -949,10 +943,6 @@ void PluginContainer::reloadPlugin(QString const& filepath) 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(); @@ -1104,7 +1094,6 @@ void PluginContainer::loadPlugins() } } - std::vector PluginContainer::activeProblems() const { std::vector problems; diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 43c7973f..d6503a43 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -186,10 +186,23 @@ public: public: - PluginContainer(OrganizerCore *organizer); + PluginContainer(OrganizerCore* organizer); virtual ~PluginContainer(); - void setUserInterface(IUserInterface *userInterface); + /** + * @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); void loadPlugin(QString const& filepath); void unloadPlugin(QString const& filepath); @@ -392,21 +405,15 @@ private: * * @return the list of created plugins. */ - std::vector loadProxied(const QString& filepath, MOBase::IPluginProxy* proxy); + std::vector loadProxied(const QString& filepath, MOBase::IPluginProxy* proxy); - /** - * @brief Load the Qt plugin from the given file. - * - * @param filepath Path to the DLL containing the Qt plugin. - */ - MOBase::IPlugin* loadQtPlugin(const QString& filepath); + // Load the Qt plugin from the given file. + QObject* loadQtPlugin(const QString& filepath); - /** - * @brief Simulate MO2 startup for the given plugins. - * - * @param plugins Plugins to simulate startup for. - */ - void simulateStartup(const std::vector& plugins) const; + // 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& plugins) const; /** * @brief Retrieved the (localized) names of interfaces implemented by the given diff --git a/src/pluginlistproxy.cpp b/src/pluginlistproxy.cpp index b51d108b..b9ea9e36 100644 --- a/src/pluginlistproxy.cpp +++ b/src/pluginlistproxy.cpp @@ -6,18 +6,26 @@ using namespace MOBase; using namespace MOShared; PluginListProxy::PluginListProxy(OrganizerProxy* oproxy, PluginList* pluginlist) : - m_OrganizerProxy(oproxy), m_Proxied(pluginlist) + m_OrganizerProxy(oproxy), m_Proxied(pluginlist) { } + +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))); } -PluginListProxy::~PluginListProxy() +void PluginListProxy::disconnectSignals() { for (auto& conn : m_Connections) { conn.disconnect(); } + m_Connections.clear(); } QStringList PluginListProxy::pluginNames() const diff --git a/src/pluginlistproxy.h b/src/pluginlistproxy.h index 04a6ad55..e421b6ee 100644 --- a/src/pluginlistproxy.h +++ b/src/pluginlistproxy.h @@ -30,6 +30,12 @@ public: private: + friend class OrganizerProxy; + + // See OrganizerProxy::connectSignals(). + void connectSignals(); + void disconnectSignals(); + OrganizerProxy* m_OrganizerProxy; PluginList* m_Proxied; -- cgit v1.3.1 From 4118aef2a28964d1bffdb489bf2223fbd6f994db Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 13:32:47 +0100 Subject: Remove disconnectPlugins() from OrganizerCore. --- src/organizercore.cpp | 19 ------------------- src/organizercore.h | 1 - src/plugincontainer.cpp | 5 ----- src/plugincontainer.h | 47 ++++++++++++++++++----------------------------- 4 files changed, 18 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 6a333a22..256833a8 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -301,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(); diff --git a/src/organizercore.h b/src/organizercore.h index cb612eae..f2b904cd 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -201,7 +201,6 @@ public: void setUserInterface(IUserInterface* ui); void connectPlugins(PluginContainer *container); - void disconnectPlugins(); void setManagedGame(MOBase::IPluginGame *game); diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 3c8050cf..a2f0d5f0 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -943,11 +943,6 @@ void PluginContainer::reloadPlugin(QString const& filepath) void PluginContainer::unloadPlugins() { - // 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(); diff --git a/src/plugincontainer.h b/src/plugincontainer.h index d6503a43..bd19e7ef 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -209,7 +209,6 @@ public: void reloadPlugin(QString const& filepath); void loadPlugins(); - void unloadPlugins(); /** * @brief Find the game plugin corresponding to the given name. @@ -370,22 +369,30 @@ private: friend class PluginRequirements; - /** - * @return the organizer proxy for the given plugin. - */ + // Unload all the plugins. + void unloadPlugins(); + + // Retrieve the organizer proxy for the given plugin. OrganizerProxy* organizerProxy(MOBase::IPlugin* plugin) const; - /** - * @return the proxy plugin that instantiated the given plugin, or a null pointer - * if the plugin was not instantiated by a proxy. - */ + // 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; - /** - * @return the path to the file or folder corresponding to the plugin. - */ + // 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 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& plugins) const; + /** * @brief Unload the given plugin. * @@ -397,24 +404,6 @@ private: */ void unloadPlugin(MOBase::IPlugin* plugin, QObject* object); - /** - * @brief Load plugins from the given filepath using the given proxy. - * - * @param filepath Path to a folder/file containing plugin(s) that can be load by the given proxy. - * @param proxy Proxy to use to load plugins. - * - * @return the list of created plugins. - */ - std::vector 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& plugins) const; - /** * @brief Retrieved the (localized) names of interfaces implemented by the given * plugin. -- cgit v1.3.1 From 7a3bc0cd23e637d66a1f78809f7229e665391cdb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 13:33:14 +0100 Subject: Throw exception when trying to unload the current game plugin. --- src/plugincontainer.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index a2f0d5f0..c3d66913 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -850,6 +850,11 @@ void PluginContainer::loadPlugin(QString const& filepath) void PluginContainer::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) { if (auto* game = qobject_cast(object)) { + + if (game == managedGame()) { + throw Exception("cannot unload the plugin for the currently managed game"); + } + unregisterGame(game); } -- cgit v1.3.1 From a1ab717e9e4fd72071cd01d9ceb74e7ba10ffafd Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 13:41:06 +0100 Subject: Minor cleaning (comments, names). --- src/organizercore.cpp | 2 +- src/plugincontainer.cpp | 2 +- src/plugincontainer.h | 26 +++++++++++++++++--------- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 256833a8..631e49be 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -291,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); } diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index c3d66913..e4eb389f 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -696,7 +696,7 @@ QString PluginContainer::filepath(MOBase::IPlugin* plugin) const return as_qobject(plugin)->property("filepath").toString(); } -IPluginGame *PluginContainer::managedGame(const QString &name) const +IPluginGame *PluginContainer::game(const QString &name) const { auto iter = m_SupportedGames.find(name); if (iter != m_SupportedGames.end()) { diff --git a/src/plugincontainer.h b/src/plugincontainer.h index bd19e7ef..4eb30db6 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -204,22 +204,19 @@ public: */ void startPlugins(IUserInterface* userInterface); + /** + * @brief Load, unload or reload the plugin at the given path. + * + */ void loadPlugin(QString const& filepath); void unloadPlugin(QString const& filepath); void reloadPlugin(QString const& filepath); - void loadPlugins(); - /** - * @brief Find the game plugin corresponding to the given name. + * @brief Load all plugins. * - * @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 *managedGame(const QString &name) const; + void loadPlugins(); /** * @brief Retrieve the list of plugins of the given type. @@ -271,6 +268,17 @@ public: MOBase::IPlugin* plugin(MOBase::IPluginDiagnose* diagnose) const; 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. */ -- cgit v1.3.1 From 8b02a5a7965440b883e9601ece0e30bca1169a65 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 19:32:11 +0100 Subject: Fix crash when starting MO2 without instance. --- src/plugincontainer.cpp | 22 +++++++++++++--------- src/plugincontainer.h | 2 ++ src/previewgenerator.cpp | 8 ++++---- src/previewgenerator.h | 4 ++-- 4 files changed, 21 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index e4eb389f..c703a36a 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -280,7 +280,7 @@ void PluginRequirements::requiredFor(std::vector &required, st PluginContainer::PluginContainer(OrganizerCore *organizer) : m_Organizer(organizer) , m_UserInterface(nullptr) - , m_PreviewGenerator(this) + , m_PreviewGenerator(*this) { } @@ -397,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: @@ -417,8 +418,6 @@ bool PluginContainer::initPlugin(IPlugin *plugin, IPluginProxy *pluginProxy, boo return false; } - proxy->setParent(as_qobject(plugin)); - // Update requirements: it->second.fetchRequirements(); @@ -732,12 +731,17 @@ void PluginContainer::startPluginsImpl(const std::vector& plugins) con } // Trigger initial callbacks, e.g. onUserInterfaceInitialized and onProfileChanged. - for (auto* object : plugins) { - auto* plugin = qobject_cast(object); - auto* oproxy = organizerProxy(plugin); - oproxy->connectSignals(); - oproxy->m_UserInterfaceInitialized(m_UserInterface->mainWindow()); - oproxy->m_ProfileChanged(nullptr, m_Organizer->currentProfile()); + if (m_Organizer) { + for (auto* object : plugins) { + auto* plugin = qobject_cast(object); + auto* oproxy = organizerProxy(plugin); + oproxy->connectSignals(); + oproxy->m_ProfileChanged(nullptr, m_Organizer->currentProfile()); + + if (m_UserInterface) { + oproxy->m_UserInterfaceInitialized(m_UserInterface->mainWindow()); + } + } } } diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 4eb30db6..386b9bca 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -462,8 +462,10 @@ private: 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/previewgenerator.cpp b/src/previewgenerator.cpp index 904d679f..22332af3 100644 --- a/src/previewgenerator.cpp +++ b/src/previewgenerator.cpp @@ -29,14 +29,14 @@ along with Mod Organizer. If not, see . using namespace MOBase; -PreviewGenerator::PreviewGenerator(const PluginContainer* pluginContainer) : +PreviewGenerator::PreviewGenerator(const PluginContainer& pluginContainer) : m_PluginContainer(pluginContainer) { m_MaxSize = QGuiApplication::primaryScreen()->size() * 0.8; } bool PreviewGenerator::previewSupported(const QString &fileExtension) const { - auto& previews = m_PluginContainer->plugins(); + auto& previews = m_PluginContainer.plugins(); for (auto* preview : previews) { if (preview->supportedExtensions().contains(fileExtension)) { return true; @@ -48,9 +48,9 @@ bool PreviewGenerator::previewSupported(const QString &fileExtension) const QWidget *PreviewGenerator::genPreview(const QString &fileName) const { const QString ext = QFileInfo(fileName).suffix().toLower(); - auto& previews = m_PluginContainer->plugins(); + auto& previews = m_PluginContainer.plugins(); for (auto* preview : previews) { - if (m_PluginContainer->isEnabled(preview) && preview->supportedExtensions().contains(ext)) { + if (m_PluginContainer.isEnabled(preview) && preview->supportedExtensions().contains(ext)) { return preview->genFilePreview(fileName, m_MaxSize); } } diff --git a/src/previewgenerator.h b/src/previewgenerator.h index f70649b5..8e491116 100644 --- a/src/previewgenerator.h +++ b/src/previewgenerator.h @@ -31,7 +31,7 @@ class PluginContainer; class PreviewGenerator { public: - PreviewGenerator(const PluginContainer* pluginContainer); + PreviewGenerator(const PluginContainer& pluginContainer); bool previewSupported(const QString &fileExtension) const; @@ -39,7 +39,7 @@ public: private: - const PluginContainer* m_PluginContainer; + const PluginContainer& m_PluginContainer; QSize m_MaxSize; }; -- cgit v1.3.1 From d644eecbb7ce91705ad7913a743144716f3aa264 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 19:56:26 +0100 Subject: Remove log for disconnectSignals(). --- src/organizerproxy.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index c41045e1..0ca3468d 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -54,8 +54,6 @@ void OrganizerProxy::connectSignals() void OrganizerProxy::disconnectSignals() { - log::debug("Disconnecting organizer proxy for plugin '{}'.", m_PluginName); - // Disconnect the child proxies. m_DownloadManagerProxy->disconnectSignals(); m_ModListProxy->disconnectSignals(); -- cgit v1.3.1 From 3264d7899e2560bce4591ab2142ee9d51bc7890c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 4 Dec 2020 19:58:47 +0100 Subject: Remove unused member in proxy. --- src/organizerproxy.cpp | 1 - src/organizerproxy.h | 1 - 2 files changed, 2 deletions(-) (limited to 'src') diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 0ca3468d..acfb8404 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -22,7 +22,6 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* plugin : m_Proxied(organizer) , m_PluginContainer(pluginContainer) , m_Plugin(plugin) - , m_PluginName(plugin->name()) , m_DownloadManagerProxy(std::make_unique(this, organizer->downloadManager())) , m_ModListProxy(std::make_unique(this, organizer->modList())) , m_PluginListProxy(std::make_unique(this, organizer->pluginList())) { } diff --git a/src/organizerproxy.h b/src/organizerproxy.h index c52f2730..7a91a347 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -106,7 +106,6 @@ private: PluginContainer *m_PluginContainer; MOBase::IPlugin *m_Plugin; - QString m_PluginName; OrganizerCore::SignalAboutToRunApplication m_AboutToRun; OrganizerCore::SignalFinishedRunApplication m_FinishedRun; -- cgit v1.3.1