File indexing completed on 2024-06-16 03:42:36
0001 /* 0002 File : PluginManager.cpp 0003 Project : LabPlot/SciDAVis 0004 Description : This class manages all plugins. 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2009 Tilman Benkert <thzs*gmx.net (use @ for *)> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "backend/core/plugin/PluginManager.h" 0011 #include "backend/core/plugin/PluginLoader.h" 0012 #include <QPluginLoader> 0013 #include <QSet> 0014 #include <QSettings> 0015 #include <QtDebug> 0016 0017 /** 0018 * \class PluginManager 0019 * \brief This class manages all plugins. 0020 * 0021 * This class is used to persistently (using QSettings) enable/disable dynamic plugins and 0022 * to make the difference between static and dynamic plugins transparent 0023 * to the application. You typically do the following: 0024 \code 0025 // let the user select a plugin from a file dialog 0026 PluginManager::enablePlugin(absoluteFilePath); 0027 ... 0028 // somewhere else in the application 0029 foreach(QObject *plugin, PluginManager::plugins()) { 0030 MyInterface *myObject = qobject_cast<MyInterface *>(plugin); 0031 if (myObject) 0032 // found a suitable plugin 0033 } 0034 \endcode 0035 * 0036 */ 0037 0038 #ifdef Q_OS_MAC // Mac 0039 #define SETTINGS_FORMAT QSettings::IniFormat 0040 #else 0041 #define SETTINGS_FORMAT QSettings::NativeFormat 0042 #endif 0043 0044 PluginManager::PluginManager() = default; 0045 0046 QList<PluginLoader*> PluginManager::m_loadedPlugins; 0047 QList<PluginLoader*> PluginManager::m_pluginsWithErrors; 0048 QObjectList PluginManager::m_staticPlugins; 0049 bool PluginManager::m_loaded = false; 0050 QObjectList PluginManager::m_allPlugins; 0051 0052 /** 0053 * \brief Load a plugin and append it to the plugin list in the settings. 0054 * 0055 * \param absolutePath Absolute path to the plugin file. 0056 * \return True, if the plugin was successfully loaded. Even if false is returned, 0057 * the path is still appended to the settings. 0058 */ 0059 bool PluginManager::enablePlugin(const QString& absolutePath) { 0060 loadAll(); 0061 0062 // save the plugin in the settings 0063 // TODO 0064 // QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName); 0065 // settings.beginGroup("PluginManager"); 0066 // QStringList pluginPaths = settings.value("enabledPlugins", QStringList()).toStringList(); 0067 // pluginPaths.removeDuplicates(); 0068 // if (!pluginPaths.contains(absolutePath)) { 0069 // pluginPaths << absolutePath; 0070 // settings.setValue("enabledPlugins", pluginPaths); 0071 // } 0072 // settings.endGroup(); 0073 0074 // check whether it's already loaded 0075 bool result = true; 0076 bool alreadyLoaded = false; 0077 for (auto* loader : m_loadedPlugins) { 0078 if (loader->fileName() == absolutePath) { 0079 alreadyLoaded = true; 0080 break; 0081 } 0082 } 0083 0084 if (!alreadyLoaded) { 0085 PluginLoader* pluginLoader = nullptr; 0086 // check whether a loader for this plugin already exists 0087 for (auto* loader : m_pluginsWithErrors) { 0088 if (loader->fileName() == absolutePath) { 0089 pluginLoader = loader; 0090 m_pluginsWithErrors.removeAll(loader); 0091 break; 0092 } 0093 } 0094 0095 if (!pluginLoader) 0096 pluginLoader = new PluginLoader(absolutePath); 0097 0098 // try to load the plugin 0099 if (!pluginLoader->load()) { 0100 m_pluginsWithErrors << pluginLoader; 0101 result = false; 0102 } else { 0103 m_loadedPlugins << pluginLoader; 0104 m_allPlugins << pluginLoader->instance(); 0105 } 0106 } 0107 0108 return result; 0109 } 0110 0111 /** 0112 * \brief Remove the plugin from the plugin list in the settings (and unload it if rightNow == true). 0113 * 0114 * \param rightNow Set this to true if you want to unload the plugin immediately (which is not 0115 * always a good idea). The default behavior is to disable the plugin for the next program startup. 0116 */ 0117 void PluginManager::disablePlugin(const QString& absolutePath, bool rightNow) { 0118 loadAll(); 0119 0120 // remove the plugin from the settings 0121 // TODO 0122 // QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName); 0123 // settings.beginGroup("PluginManager"); 0124 // QStringList pluginPaths = settings.value("enabledPlugins", QStringList()).toStringList(); 0125 // pluginPaths.removeDuplicates(); 0126 // int index = pluginPaths.indexOf(absolutePath); 0127 // if (index > -1) { 0128 // pluginPaths.removeAt(index); 0129 // settings.setValue("enabledPlugins", pluginPaths); 0130 // } 0131 // settings.endGroup(); 0132 0133 // remove the related loader if it is in the list of failed loaders 0134 for (auto* loader : m_pluginsWithErrors) { 0135 if (loader->fileName() == absolutePath) { 0136 m_pluginsWithErrors.removeAll(loader); 0137 delete loader; 0138 break; 0139 } 0140 } 0141 0142 if (rightNow) { 0143 // unload the plugin and remove the loader 0144 for (auto* loader : m_loadedPlugins) { 0145 if (loader->fileName() == absolutePath) { 0146 m_allPlugins.removeAll(loader->instance()); 0147 m_loadedPlugins.removeAll(loader); 0148 loader->unload(); 0149 delete loader; 0150 break; 0151 } 0152 } 0153 } 0154 } 0155 0156 /** 0157 * \brief Get all plugin root instances. 0158 * 0159 * This function will return a list of all static plugin root instances 0160 * as well as those from all successfully loaded dynamic plugins. 0161 */ 0162 QObjectList PluginManager::plugins() { 0163 loadAll(); 0164 return m_allPlugins; 0165 } 0166 0167 /** 0168 * \brief Load all plugins contained in the "enabledPlugins" setting. 0169 * 0170 * This method also fetches a list of all static plugins. 0171 */ 0172 void PluginManager::loadAll() { 0173 if (!m_loaded) { 0174 m_staticPlugins = QPluginLoader::staticInstances(); 0175 m_allPlugins << m_staticPlugins; 0176 0177 // TODO: 0178 // QSettings settings(SETTINGS_FORMAT, QSettings::UserScope, SciDAVis::appName, SciDAVis::appName); 0179 // settings.beginGroup("PluginManager"); 0180 // QStringList plugins = settings.value("enabledPlugins", QStringList()).toStringList(); 0181 // plugins.removeDuplicates(); 0182 // foreach (const QString& plugin, plugins) { 0183 // PluginLoader *pluginLoader = new PluginLoader(plugin); 0184 // if (!pluginLoader->load()) { 0185 // m_pluginsWithErrors << pluginLoader; 0186 // } else { 0187 // m_loadedPlugins << pluginLoader; 0188 // m_allPlugins << pluginLoader->instance(); 0189 // } 0190 // } 0191 // settings.endGroup(); 0192 0193 m_loaded = true; 0194 } 0195 } 0196 0197 /** 0198 * \brief Get the file names of all loaded plugins. 0199 */ 0200 QStringList PluginManager::loadedPluginFileNames() { 0201 QStringList result; 0202 for (auto* loader : m_loadedPlugins) 0203 result << loader->fileName(); 0204 return result; 0205 } 0206 0207 /** 0208 * \brief Get the file names of all plugins that failed to load. 0209 */ 0210 QStringList PluginManager::failedPluginFileNames() { 0211 QStringList result; 0212 for (auto* loader : m_pluginsWithErrors) 0213 result << loader->fileName(); 0214 return result; 0215 } 0216 0217 /** 0218 * \brief Get the error messages of a plugin that failed to load. 0219 */ 0220 QString PluginManager::errorOfPlugin(const QString& fileName) { 0221 QString result; 0222 for (auto* loader : m_pluginsWithErrors) 0223 if (loader->fileName() == fileName) 0224 result = loader->statusString(); 0225 return result; 0226 } 0227 0228 /** 0229 * \brief Get the plugin root instance for a given file name. 0230 */ 0231 QObject* PluginManager::instanceOfPlugin(const QString& fileName) { 0232 QObject* result = nullptr; 0233 for (auto* loader : m_loadedPlugins) 0234 if (loader->fileName() == fileName) 0235 result = loader->instance(); 0236 return result; 0237 } 0238 0239 #ifdef QT_DEBUG 0240 void PluginManager::printAll() { 0241 loadAll(); 0242 for (auto* loader : m_loadedPlugins) { 0243 qDebug() << "Plugin" << loader->fileName() << "loaded"; 0244 } 0245 0246 for (auto* loader : m_pluginsWithErrors) { 0247 qDebug() << loader->statusString(); 0248 } 0249 } 0250 #endif