File indexing completed on 2024-04-21 12:26:42

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003     Some of below codes are got from Kopete source code.
0004 
0005     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #ifndef CHOQOKPLUGINMANAGER_H
0011 #define CHOQOKPLUGINMANAGER_H
0012 
0013 #include <QList>
0014 #include <QEventLoopLocker>
0015 
0016 #include <KPluginMetaData>
0017 
0018 #include "plugin.h"
0019 #include "choqok_export.h"
0020 
0021 namespace Choqok
0022 {
0023 
0024 class Protocol;
0025 typedef QList<Plugin *> PluginList;
0026 class PluginManagerPrivate;
0027 
0028 /**
0029  * @author Mehrdad Momeny \<mehrdad.momeny@gmail.com\>
0030  */
0031 class CHOQOK_EXPORT PluginManager : public QObject
0032 {
0033     friend class PluginManagerPrivate;
0034     Q_OBJECT
0035     Q_ENUMS(PluginLoadMode)
0036 
0037 public:
0038     /**
0039      * Retrieve the plugin loader instance.
0040      */
0041     static PluginManager *self();
0042 
0043     /**
0044      * Returns a list of all available plugins for the given category.
0045      * Currently there are two categories, "Plugins" and "Protocols", but
0046      * you can add your own categories if you want.
0047      *
0048      * If you pass an empty string you get the complete list of ALL plugins.
0049      *
0050      * You can query all information on the plugins through the KPluginMetaData
0051      * interface.
0052      */
0053     QVector<KPluginMetaData> availablePlugins(const QString &category = QString()) const;
0054 
0055     /**
0056      * Returns a list of all plugins that are actually loaded.
0057      * If you omit the category you get all, otherwise it's a filtered list.
0058      * See also @ref availablePlugins().
0059      */
0060     PluginList loadedPlugins(const QString &category = QString()) const;
0061 
0062     /**
0063      * @brief Search by plugin name. This is the key used as X-KDE-PluginInfo-Name in
0064      * the .desktop file.
0065      *
0066      * @return The @ref Choqok::Plugin object found by the search, or a null
0067      * pointer if the plugin is not loaded.
0068      *
0069      * If you want to also load the plugin you can better use @ref loadPlugin, which returns
0070      * the pointer to the plugin if it's already loaded.
0071      */
0072     Plugin *plugin(const QString &pluginName) const;
0073 
0074     /**
0075      * @return the KPluginMetaData for the specified plugin
0076      */
0077     KPluginMetaData pluginMetaData(const Choqok::Plugin *plugin) const;
0078 
0079     /**
0080      * Shuts down the plugin manager on Choqok shutdown, but first
0081      * unloads all plugins asynchronously.
0082      *
0083      * After 3 seconds all plugins should be removed; what's still left
0084      * by then is unloaded through a hard delete instead.
0085      *
0086      * Note that this call also derefs the plugin manager from the event
0087      * loop, so do NOT call this method when not terminating Choqok!
0088      */
0089     void shutdown();
0090 
0091     /**
0092      * Enable a plugin.
0093      *
0094      * This marks a plugin as enabled in the config file, so loadAll()
0095      * can pick it up later.
0096      *
0097      * This method does not actually load a plugin, it only edits the
0098      * config file.
0099      *
0100      * @param name is the name of the plugin as it is listed in the .desktop
0101      * file in the X-KDE-Library field.
0102      * @param enabled sets whether or not the plugin is enabled
0103      *
0104      * Returns false when no appropriate plugin can be found.
0105      */
0106     bool setPluginEnabled(const QString &name, bool enabled = true);
0107 
0108     /**
0109      * This method check if all the plugins are loaded.
0110      * @return true if all the plugins are loaded.
0111      */
0112     bool isAllPluginsLoaded() const;
0113 
0114     /**
0115      * Plugin loading mode. Used by @ref loadPlugin(). Code that doesn't want to block
0116      * the GUI and/or lot a lot of plugins at once should use asynchronous loading (@c LoadAsync).
0117      * The default is synchronous loading (@c LoadSync).
0118      */
0119     enum PluginLoadMode { LoadSync, LoadAsync };
0120 
0121 public Q_SLOTS:
0122     /**
0123      * @brief Load a single plugin by plugin name. Returns an existing plugin
0124      * if one is already loaded in memory.
0125      *
0126      * If mode is set to Async, the plugin will be queued and loaded in
0127      * the background. This method will return a null pointer. To get
0128      * the loaded plugin you can track the @ref pluginLoaded() signal.
0129      *
0130      * See also @ref plugin().
0131      */
0132     Plugin *loadPlugin(const QString &pluginId, PluginLoadMode mode = LoadSync);
0133 
0134     /**
0135      * @brief Unload the plugin specified by @p pluginName
0136      */
0137     bool unloadPlugin(const QString &pluginName);
0138 
0139     /**
0140      * @brief Loads all the enabled plugins. Also used to reread the
0141      * config file when the configuration has changed.
0142      */
0143     void loadAllPlugins();
0144 
0145 Q_SIGNALS:
0146     /**
0147      * @brief Signals a new plugin has just been loaded.
0148      */
0149     void pluginLoaded(Choqok::Plugin *plugin);
0150 
0151     /**
0152      * @brief Signals a plugin has just been unloaded.
0153      */
0154     void pluginUnloaded(const QString &pluginName);
0155 
0156     /**
0157      * @brief All plugins have been loaded by the plugin manager.
0158      *
0159      * This signal is emitted exactly ONCE, when the plugin manager has emptied
0160      * its plugin queue for the first time. This means that if you call an async
0161      * loadPlugin() before loadAllPlugins() this signal is probably emitted after
0162      * the initial call completes, unless you are quick enough to fill the queue
0163      * before it completes, which is a dangerous race you shouldn't count upon :)
0164      *
0165      * The signal is delayed one event loop iteration through a singleShot timer,
0166      * but that is not guaranteed to be enough for account instantiation. You may
0167      * need an additional timer for it in the code if you want to programmatically
0168      * act on it.
0169      *
0170      * If you use the signal for enabling/disabling GUI objects there is little
0171      * chance a user is able to activate them in the short while that's remaining,
0172      * the slow part of the code is over now and the remaining processing time
0173      * is neglectable for the user.
0174      */
0175     void allPluginsLoaded();
0176 
0177 private Q_SLOTS:
0178     /**
0179      * @brief Cleans up some references if the plugin is destroyed
0180      */
0181     void slotPluginDestroyed(QObject *plugin);
0182 
0183     /**
0184      * shutdown() starts a timer, when it fires we force all plugins
0185      * to be unloaded here by deref()-ing the event loop to trigger the plugin
0186      * manager's destruction
0187      */
0188     void slotShutdownTimeout();
0189 
0190     /**
0191      * Common entry point to deref() the KApplication. Used both by the clean
0192      * shutdown and the timeout condition of slotShutdownTimeout()
0193      */
0194     void slotShutdownDone();
0195 
0196     /**
0197      * Emitted by a Choqok::Plugin when it's ready for unload
0198      */
0199     void slotPluginReadyForUnload();
0200 
0201     /**
0202      * Load a plugin from our queue. Does nothing if the queue is empty.
0203      * Schedules itself again if more plugins are pending.
0204      */
0205     void slotLoadNextPlugin();
0206 
0207     /**
0208      * Called when the application is quitting
0209      */
0210     void slotAboutToQuit();
0211 
0212 private:
0213     /**
0214      * @internal
0215      *
0216      * The internal method for loading plugins.
0217      * Called by @ref loadPlugin directly or through the queue for async plugin
0218      * loading.
0219      */
0220     Plugin *loadPluginInternal(const QString &pluginId);
0221 
0222     /**
0223      * @internal
0224      *
0225      * Find the KPluginMetaData structure by key. Reduces some code duplication.
0226      *
0227      * Returns a null pointer when no plugin info is found.
0228      */
0229     KPluginMetaData metaDataForPluginId(const QString &pluginId) const;
0230 
0231     PluginManager();
0232     ~PluginManager();
0233 
0234     // We want to add a reference to the application's event loop so we
0235     // can remain in control when all windows are removed.
0236     // This way we can unload plugins asynchronously, which is more
0237     // robust if they are still doing processing.
0238     QEventLoopLocker lock;
0239 
0240 };
0241 
0242 }
0243 
0244 #endif // KOPETEPLUGINMANAGER_H
0245