File indexing completed on 2024-05-05 04:33:16
0001 /* 0002 SPDX-FileCopyrightText: 2004-2018 Gilles Caulier <caulier dot gilles at gmail dot com> 0003 SPDX-FileCopyrightText: 2004-2005 Renchi Raju <renchi dot raju at gmail dot com> 0004 SPDX-FileCopyrightText: 2009 Andi Clemens <andi dot clemens at googlemail dot com> 0005 SPDX-FileCopyrightText: 2009 Aleix Pol Gonzalez <aleixpol at kde dot org> 0006 SPDX-FileCopyrightText: 2012-2013 Victor Dodon <dodonvictor at gmail dot com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "pluginloader.h" 0012 0013 // Qt includes 0014 0015 #include <QStringList> 0016 #include <QVariantList> 0017 #include <QVariant> 0018 #include <QAction> 0019 #include <QStandardPaths> 0020 0021 // KF includes 0022 0023 #include <KServiceTypeTrader> 0024 #include <KSharedConfig> 0025 #include <KDesktopFile> 0026 #include <KConfigGroup> 0027 #include <KXMLGUIFactory> 0028 #include <KToolBar> 0029 #include <KActionCollection> 0030 0031 // Local includes 0032 0033 #include "plugin.h" 0034 #include "interface.h" 0035 #include "configwidget.h" 0036 #include "libkipi_version.h" 0037 #include "libkipi_config.h" 0038 #include "libkipi_debug.h" 0039 0040 namespace KIPI 0041 { 0042 0043 class Q_DECL_HIDDEN PluginLoader::Info::Private 0044 { 0045 public: 0046 0047 Private() 0048 { 0049 shouldLoad = false; 0050 plugin = nullptr; 0051 parent = nullptr; 0052 } 0053 0054 bool shouldLoad; 0055 KService::Ptr service; 0056 Plugin* plugin; 0057 KXmlGuiWindow* parent; 0058 }; 0059 0060 PluginLoader::Info::Info(KXmlGuiWindow* const parent, const KService::Ptr& service, bool shouldLoad) 0061 : d(new Private) 0062 { 0063 d->service = service; 0064 d->shouldLoad = shouldLoad; 0065 d->parent = parent; 0066 } 0067 0068 PluginLoader::Info::~Info() 0069 { 0070 if (d->parent && d->plugin) 0071 { 0072 d->parent->guiFactory()->removeClient(d->plugin); 0073 0074 const auto toolBars = d->parent->toolBars(); 0075 for (KToolBar* const toolbar : toolBars) 0076 { 0077 toolbar->removeXMLGUIClient(d->plugin); 0078 } 0079 } 0080 0081 delete d->plugin; 0082 } 0083 0084 KService::Ptr PluginLoader::Info::service() const 0085 { 0086 return d->service; 0087 } 0088 0089 QString PluginLoader::Info::name() const 0090 { 0091 return d->service->name(); 0092 } 0093 0094 QString PluginLoader::Info::uname() const 0095 { 0096 return d->service->untranslatedGenericName(); 0097 } 0098 0099 QString PluginLoader::Info::author() const 0100 { 0101 return d->service->property(QString::fromLatin1("author"), QVariant::String).toString(); 0102 } 0103 0104 QString PluginLoader::Info::comment() const 0105 { 0106 return d->service->comment(); 0107 } 0108 0109 QString PluginLoader::Info::library() const 0110 { 0111 return d->service->library(); 0112 } 0113 0114 QIcon PluginLoader::Info::icon() const 0115 { 0116 if (d->service->icon().isEmpty() && d->plugin) 0117 { 0118 if (!d->plugin->actions().isEmpty() && d->plugin->actions().first()) 0119 { 0120 return d->plugin->actions().first()->icon(); 0121 } 0122 else 0123 { 0124 return QIcon(); 0125 } 0126 } 0127 else 0128 { 0129 return QIcon::fromTheme(d->service->icon()); 0130 } 0131 } 0132 0133 Plugin* PluginLoader::Info::plugin() const 0134 { 0135 if (!d->plugin && shouldLoad()) 0136 { 0137 QString error; 0138 0139 d->plugin = d->service->createInstance<Plugin>(PluginLoader::instance()->interface(), QVariantList(), &error); 0140 0141 if (d->plugin) 0142 { 0143 qCDebug(LIBKIPI_LOG) << "Loaded plugin " << d->plugin->objectName(); 0144 0145 Q_EMIT (PluginLoader::instance()->plug(const_cast<Info*>(this))); 0146 } 0147 else 0148 { 0149 qCWarning(LIBKIPI_LOG) << "Cannot create instance for plugin " 0150 << name() 0151 << " (" << library() << ")" 0152 << " with error: " 0153 << error; 0154 } 0155 } 0156 0157 return d->plugin; 0158 } 0159 0160 QStringList PluginLoader::Info::pluginCategories() const 0161 { 0162 return d->service->property(QString::fromLatin1("X-KIPI-PluginCategories")).toStringList(); 0163 } 0164 0165 void PluginLoader::Info::reload() 0166 { 0167 if (d->parent) 0168 { 0169 d->parent->guiFactory()->removeClient(d->plugin); 0170 0171 const auto toolBars = d->parent->toolBars(); 0172 for (KToolBar* const toolbar : toolBars) 0173 { 0174 toolbar->removeXMLGUIClient(d->plugin); 0175 } 0176 } 0177 0178 delete d->plugin; 0179 d->plugin = nullptr; 0180 } 0181 0182 bool PluginLoader::Info::shouldLoad() const 0183 { 0184 return d->shouldLoad; 0185 } 0186 0187 void PluginLoader::Info::setShouldLoad(bool value) 0188 { 0189 d->shouldLoad = value; 0190 } 0191 0192 //--------------------------------------------------------------------- 0193 0194 static PluginLoader* s_instance = nullptr; 0195 static bool s_loaded = false; 0196 0197 class Q_DECL_HIDDEN PluginLoader::Private 0198 { 0199 public: 0200 0201 Private() 0202 { 0203 interface = nullptr; 0204 parent = nullptr; 0205 } 0206 0207 QStringList ignoredPlugins; 0208 QStringList disabledActions; 0209 0210 KXmlGuiWindow* parent; 0211 0212 PluginLoader::PluginList pluginList; 0213 Interface* interface; 0214 }; 0215 0216 PluginLoader::PluginLoader() 0217 : d(new Private) 0218 { 0219 Q_ASSERT((s_instance == nullptr) && (!s_loaded)); 0220 s_instance = this; 0221 } 0222 0223 PluginLoader::PluginLoader(KXmlGuiWindow* const parent) 0224 : d(new Private) 0225 { 0226 Q_ASSERT((s_instance == nullptr) && (!s_loaded)); 0227 s_instance = this; 0228 0229 if (!parent) 0230 { 0231 qWarning(LIBKIPI_LOG) << "KDE XML application instance is null..."; 0232 } 0233 0234 d->parent = parent; 0235 } 0236 0237 void PluginLoader::setInterface(Interface* const interface) 0238 { 0239 d->interface = interface; 0240 setParent(interface); 0241 } 0242 0243 void PluginLoader::setIgnoredPluginsList(const QStringList& ignores) 0244 { 0245 d->ignoredPlugins = ignores; 0246 } 0247 0248 void PluginLoader::setDisabledPluginActions(const QStringList& disabledActions) 0249 { 0250 d->disabledActions = disabledActions; 0251 } 0252 0253 QStringList PluginLoader::disabledPluginActions() const 0254 { 0255 return d->disabledActions; 0256 } 0257 0258 void PluginLoader::init() 0259 { 0260 Q_ASSERT((s_instance != nullptr) && (!s_loaded)); 0261 0262 if (!d->interface) 0263 { 0264 qWarning(LIBKIPI_LOG) << "KIPI host interface instance is null. No plugin will be loaded..."; 0265 return; 0266 } 0267 0268 s_loaded = true; 0269 const KService::List offers = KServiceTypeTrader::self()->query(QString::fromLatin1("KIPI/Plugin")); 0270 KSharedConfigPtr config = KSharedConfig::openConfig(); 0271 KConfigGroup group = config->group(QString::fromLatin1("KIPI/EnabledPlugin")); 0272 0273 for (KService::List::ConstIterator iter = offers.begin(); iter != offers.end(); ++iter) 0274 { 0275 KService::Ptr service = *iter; 0276 QString name = service->name(); 0277 QString uname = service->untranslatedGenericName(); 0278 QString library = service->library(); 0279 QStringList reqFeatures = service->property(QString::fromLatin1("X-KIPI-ReqFeatures")).toStringList(); 0280 int binVersion = service->property(QString::fromLatin1("X-KIPI-BinaryVersion")).toInt(); 0281 0282 if (library.isEmpty() || uname.isEmpty()) 0283 { 0284 qCWarning(LIBKIPI_LOG) << "Plugin had an empty name or library file - this should not happen."; 0285 continue; 0286 } 0287 0288 if (d->ignoredPlugins.contains(uname)) 0289 { 0290 qCDebug(LIBKIPI_LOG) << "Plugin " << name << " (generic name: " << uname << ") is in the ignore list from host application"; 0291 continue; 0292 } 0293 0294 if (binVersion != kipi_binary_version) 0295 { 0296 qCDebug(LIBKIPI_LOG) << "Plugin " << name 0297 << "has a SO version (" << binVersion 0298 << ") which is different than libkipi ABI version (" << kipi_binary_version << "). " 0299 << "Refusing to load."; 0300 continue; 0301 } 0302 0303 bool appHasAllReqFeatures = true; 0304 0305 for (QStringList::const_iterator featureIt = reqFeatures.constBegin(); 0306 featureIt != reqFeatures.constEnd(); ++featureIt) 0307 { 0308 if (!d->interface->hasFeature(*featureIt)) 0309 { 0310 qCDebug(LIBKIPI_LOG) << "Plugin " << name << " was not loaded because the host application is missing\n" 0311 << "the feature " << *featureIt; 0312 appHasAllReqFeatures = false; 0313 break; 0314 } 0315 } 0316 0317 bool load = group.readEntry(uname, true); 0318 0319 if (!appHasAllReqFeatures) 0320 { 0321 continue; 0322 } 0323 0324 Info* const info = new Info(d->parent, service, load); 0325 d->pluginList.append(info); 0326 } 0327 } 0328 0329 PluginLoader::~PluginLoader() = default; 0330 0331 void PluginLoader::loadPlugins() 0332 { 0333 Q_EMIT replug(); // added for convenience, now they can be loaded on demand 0334 } 0335 0336 const PluginLoader::PluginList& PluginLoader::pluginList() 0337 { 0338 return d->pluginList; 0339 } 0340 0341 PluginLoader* PluginLoader::instance() 0342 { 0343 if (!s_instance) 0344 { 0345 qCDebug(LIBKIPI_LOG) << "Instance is null..."; 0346 } 0347 0348 return s_instance; 0349 } 0350 0351 Interface* PluginLoader::interface() const 0352 { 0353 return d->interface; 0354 } 0355 0356 ConfigWidget* PluginLoader::configWidget(QWidget* const parent) const 0357 { 0358 return new ConfigWidget(parent); 0359 } 0360 0361 QString PluginLoader::kipiPluginsVersion() const 0362 { 0363 QString ver; 0364 QString path = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString::fromLatin1("kipiplugins.desktop")); 0365 0366 KDesktopFile desk(path); 0367 QMap<QString, QString> map = desk.entryMap(QString::fromLatin1("X-KipiPlugins Entry")); 0368 0369 if (!map.isEmpty()) 0370 { 0371 QString val = map[QString::fromLatin1("Version")]; 0372 0373 if (!val.isEmpty()) 0374 ver = val; 0375 } 0376 0377 return ver; 0378 } 0379 0380 } // namespace KIPI 0381 0382 #include "moc_pluginloader.cpp"