File indexing completed on 2024-04-28 15:29:23
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org> 0004 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "plugin.h" 0010 0011 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 90) 0012 0013 QT_WARNING_PUSH 0014 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0015 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0016 0017 #include "part.h" 0018 0019 #include <KConfigGroup> 0020 #include <KDesktopFile> 0021 #include <KLocalizedString> 0022 #include <KPluginFactory> 0023 #include <KPluginLoader> 0024 #include <KPluginMetaData> 0025 #include <KSharedConfig> 0026 #include <KXMLGUIFactory> 0027 #if !KPARTS_BUILD_DEPRECATED_SINCE(5, 77) || KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76) 0028 #include <KAboutData> 0029 #endif 0030 0031 #include <QDir> 0032 #include <QFile> 0033 #include <QFileInfo> 0034 #include <QStandardPaths> 0035 0036 using namespace KParts; 0037 0038 class KParts::PluginPrivate 0039 { 0040 public: 0041 QString m_parentInstance; 0042 QString m_library; // filename of the library 0043 }; 0044 0045 Plugin::Plugin(QObject *parent) 0046 : QObject(parent) 0047 , d(new PluginPrivate()) 0048 { 0049 // qDebug() << className(); 0050 } 0051 0052 Plugin::~Plugin() = default; 0053 0054 QString Plugin::xmlFile() const 0055 { 0056 QString path = KXMLGUIClient::xmlFile(); 0057 0058 if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) { 0059 return path; 0060 } 0061 0062 QString absPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->m_parentInstance + QLatin1Char('/') + path); 0063 Q_ASSERT(!absPath.isEmpty()); 0064 return absPath; 0065 } 0066 0067 QString Plugin::localXMLFile() const 0068 { 0069 QString path = KXMLGUIClient::xmlFile(); 0070 0071 if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) { 0072 return path; 0073 } 0074 0075 QString absPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->m_parentInstance + QLatin1Char('/') + path; 0076 return absPath; 0077 } 0078 0079 // static 0080 QList<Plugin::PluginInfo> Plugin::pluginInfos(const QString &componentName) 0081 { 0082 QList<PluginInfo> plugins; 0083 0084 QMap<QString, QStringList> sortedPlugins; 0085 0086 const QStringList dirs = 0087 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, componentName + QLatin1String("/kpartplugins"), QStandardPaths::LocateDirectory); 0088 for (const QString &dir : dirs) { 0089 const auto rcfiles = QDir(dir).entryList(QStringList(QStringLiteral("*.rc"))); 0090 for (const QString &file : rcfiles) { 0091 const QFileInfo fInfo(dir + QLatin1Char('/') + file); 0092 QMap<QString, QStringList>::Iterator mapIt = sortedPlugins.find(fInfo.fileName()); 0093 if (mapIt == sortedPlugins.end()) { 0094 mapIt = sortedPlugins.insert(fInfo.fileName(), QStringList()); 0095 } 0096 mapIt.value().append(fInfo.absoluteFilePath()); 0097 } 0098 } 0099 0100 QMap<QString, QStringList>::ConstIterator mapIt = sortedPlugins.constBegin(); 0101 QMap<QString, QStringList>::ConstIterator mapEnd = sortedPlugins.constEnd(); 0102 for (; mapIt != mapEnd; ++mapIt) { 0103 PluginInfo info; 0104 QString doc; 0105 info.m_absXMLFileName = KXMLGUIClient::findMostRecentXMLFile(mapIt.value(), doc); 0106 if (info.m_absXMLFileName.isEmpty()) { 0107 continue; 0108 } 0109 0110 // qDebug() << "found KParts Plugin : " << info.m_absXMLFileName; 0111 info.m_relXMLFileName = QLatin1String("kpartplugins/") + mapIt.key(); 0112 0113 info.m_document.setContent(doc); 0114 if (info.m_document.documentElement().isNull()) { 0115 continue; 0116 } 0117 0118 plugins.append(info); 0119 } 0120 0121 return plugins; 0122 } 0123 0124 void Plugin::loadPlugins(QObject *parent, const QString &componentName) 0125 { 0126 loadPlugins(parent, pluginInfos(componentName), componentName); 0127 } 0128 0129 void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos, const QString &componentName) 0130 { 0131 for (const auto &pluginInfo : pluginInfos) { 0132 const QString library = pluginInfo.m_document.documentElement().attribute(QStringLiteral("library")); 0133 0134 if (library.isEmpty() || hasPlugin(parent, library)) { 0135 continue; 0136 } 0137 0138 Plugin *plugin = loadPlugin(parent, library, pluginInfo.m_document.documentElement().attribute(QStringLiteral("X-KDE-PluginKeyword"))); 0139 0140 if (plugin) { 0141 plugin->d->m_parentInstance = componentName; 0142 plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false); 0143 plugin->setDOMDocument(pluginInfo.m_document); 0144 } 0145 } 0146 } 0147 0148 void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos) 0149 { 0150 loadPlugins(parent, pluginInfos, QString()); 0151 } 0152 0153 // static 0154 Plugin *Plugin::loadPlugin(QObject *parent, const QString &libname, const QString &keyword) 0155 { 0156 KPluginLoader loader(libname); 0157 KPluginFactory *factory = loader.factory(); 0158 0159 if (!factory) { 0160 return nullptr; 0161 } 0162 0163 Plugin *plugin = factory->create<Plugin>(keyword, parent); 0164 if (!plugin) { 0165 return nullptr; 0166 } 0167 plugin->d->m_library = libname; 0168 return plugin; 0169 } 0170 0171 QList<KParts::Plugin *> Plugin::pluginObjects(QObject *parent) 0172 { 0173 QList<KParts::Plugin *> objects; 0174 0175 if (!parent) { 0176 return objects; 0177 } 0178 0179 objects = parent->findChildren<Plugin *>(QString(), Qt::FindDirectChildrenOnly); 0180 return objects; 0181 } 0182 0183 bool Plugin::hasPlugin(QObject *parent, const QString &library) 0184 { 0185 const QObjectList plugins = parent->children(); 0186 0187 return std::any_of(plugins.begin(), plugins.end(), [&library](QObject *p) { 0188 Plugin *plugin = qobject_cast<Plugin *>(p); 0189 return (plugin && plugin->d->m_library == library); 0190 }); 0191 } 0192 0193 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 77) 0194 void Plugin::setComponentData(const KAboutData &pluginData) 0195 { 0196 // backward-compatible registration, usage deprecated 0197 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76) 0198 QT_WARNING_PUSH 0199 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0200 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0201 KAboutData::registerPluginData(pluginData); 0202 QT_WARNING_POP 0203 #endif 0204 0205 KXMLGUIClient::setComponentName(pluginData.componentName(), pluginData.displayName()); 0206 } 0207 #endif 0208 0209 void Plugin::setMetaData(const KPluginMetaData &metaData) 0210 { 0211 // backward-compatible registration, usage deprecated 0212 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76) 0213 QT_WARNING_PUSH 0214 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0215 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0216 KAboutData::registerPluginData(KAboutData::fromPluginMetaData(metaData)); 0217 QT_WARNING_POP 0218 #endif 0219 0220 KXMLGUIClient::setComponentName(metaData.pluginId(), metaData.name()); 0221 } 0222 0223 void Plugin::loadPlugins(QObject *parent, 0224 KXMLGUIClient *parentGUIClient, 0225 const QString &componentName, 0226 bool enableNewPluginsByDefault, 0227 int interfaceVersionRequired) 0228 { 0229 KConfigGroup cfgGroup(KSharedConfig::openConfig(componentName + QLatin1String("rc")), "KParts Plugins"); 0230 const QList<PluginInfo> plugins = pluginInfos(componentName); 0231 for (const auto &pluginInfo : plugins) { 0232 QDomElement docElem = pluginInfo.m_document.documentElement(); 0233 QString library = docElem.attribute(QStringLiteral("library")); 0234 QString keyword; 0235 0236 if (library.isEmpty()) { 0237 continue; 0238 } 0239 0240 // Check configuration 0241 const QString name = docElem.attribute(QStringLiteral("name")); 0242 0243 bool pluginEnabled = enableNewPluginsByDefault; 0244 if (cfgGroup.hasKey(name + QLatin1String("Enabled"))) { 0245 pluginEnabled = cfgGroup.readEntry(name + QLatin1String("Enabled"), false); 0246 } else { // no user-setting, load plugin default setting 0247 QString relPath = componentName + QLatin1Char('/') + pluginInfo.m_relXMLFileName; 0248 relPath.truncate(relPath.lastIndexOf(QLatin1Char('.'))); // remove extension 0249 relPath += QLatin1String(".desktop"); 0250 // qDebug() << "looking for " << relPath; 0251 const QString desktopfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relPath); 0252 if (!desktopfile.isEmpty()) { 0253 // qDebug() << "loadPlugins found desktop file for " << name << ": " << desktopfile; 0254 KDesktopFile _desktop(desktopfile); 0255 const KConfigGroup desktop = _desktop.desktopGroup(); 0256 keyword = desktop.readEntry("X-KDE-PluginKeyword", ""); 0257 pluginEnabled = desktop.readEntry("X-KDE-PluginInfo-EnabledByDefault", enableNewPluginsByDefault); 0258 if (interfaceVersionRequired != 0) { 0259 const int version = desktop.readEntry("X-KDE-InterfaceVersion", 1); 0260 if (version != interfaceVersionRequired) { 0261 // qDebug() << "Discarding plugin " << name << ", interface version " << version << ", expected " << interfaceVersionRequired; 0262 pluginEnabled = false; 0263 } 0264 } 0265 } else { 0266 // qDebug() << "loadPlugins no desktop file found in " << relPath; 0267 } 0268 } 0269 0270 // search through already present plugins 0271 const QObjectList pluginList = parent->children(); 0272 0273 bool pluginFound = false; 0274 for (auto *p : pluginList) { 0275 Plugin *plugin = qobject_cast<Plugin *>(p); 0276 if (plugin && plugin->d->m_library == library) { 0277 // delete and unload disabled plugins 0278 if (!pluginEnabled) { 0279 // qDebug() << "remove plugin " << name; 0280 KXMLGUIFactory *factory = plugin->factory(); 0281 if (factory) { 0282 factory->removeClient(plugin); 0283 } 0284 delete plugin; 0285 } 0286 0287 pluginFound = true; 0288 break; 0289 } 0290 } 0291 0292 // if the plugin is already loaded or if it's disabled in the 0293 // configuration do nothing 0294 if (pluginFound || !pluginEnabled) { 0295 continue; 0296 } 0297 0298 // qDebug() << "load plugin " << name << " " << library << " " << keyword; 0299 Plugin *plugin = loadPlugin(parent, library, keyword); 0300 0301 if (plugin) { 0302 plugin->d->m_parentInstance = componentName; 0303 plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false); 0304 plugin->setDOMDocument(pluginInfo.m_document); 0305 parentGUIClient->insertChildClient(plugin); 0306 } 0307 } 0308 } 0309 0310 #include "moc_plugin.cpp" 0311 0312 #endif