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"