1 /*
2     This file is part of Choqok, the KDE micro-blogging client
3     Some of below codes are got from Kopete source code.
4 
5     Copyright (C) 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
6 
7     This program is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License as
9     published by the Free Software Foundation; either version 2 of
10     the License or (at your option) version 3 or any later version
11     accepted by the membership of KDE e.V. (or its successor approved
12     by the membership of KDE e.V.), which shall act as a proxy
13     defined in Section 14 of version 3 of the license.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, see http://www.gnu.org/licenses/
22 */
23 
24 #ifndef CHOQOKPLUGINMANAGER_H
25 #define CHOQOKPLUGINMANAGER_H
26 
27 #include <QList>
28 
29 #include <KPluginInfo>
30 
31 #include "plugin.h"
32 #include "choqok_export.h"
33 
34 class QEventLoopLocker;
35 
36 namespace Choqok
37 {
38 
39 class Protocol;
40 typedef QList<Plugin *> PluginList;
41 class PluginManagerPrivate;
42 
43 /**
44  * @author Mehrdad Momeny \<mehrdad.momeny@gmail.com\>
45  */
46 class CHOQOK_EXPORT PluginManager : public QObject
47 {
48     friend class PluginManagerPrivate;
49     Q_OBJECT
50     Q_ENUMS(PluginLoadMode)
51 
52 public:
53     /**
54      * Retrieve the plugin loader instance.
55      */
56     static PluginManager *self();
57 
58     /**
59      * Returns a list of all available plugins for the given category.
60      * Currently there are two categories, "Plugins" and "Protocols", but
61      * you can add your own categories if you want.
62      *
63      * If you pass an empty string you get the complete list of ALL plugins.
64      *
65      * You can query all information on the plugins through the KPluginInfo
66      * interface.
67      */
68     QList<KPluginInfo> availablePlugins(const QString &category = QString()) const;
69 
70     /**
71      * Returns a list of all plugins that are actually loaded.
72      * If you omit the category you get all, otherwise it's a filtered list.
73      * See also @ref availablePlugins().
74      */
75     PluginList loadedPlugins(const QString &category = QString()) const;
76 
77     /**
78      * @brief Search by plugin name. This is the key used as X-KDE-PluginInfo-Name in
79      * the .desktop file.
80      *
81      * @return The @ref Choqok::Plugin object found by the search, or a null
82      * pointer if the plugin is not loaded.
83      *
84      * If you want to also load the plugin you can better use @ref loadPlugin, which returns
85      * the pointer to the plugin if it's already loaded.
86      */
87     Plugin *plugin(const QString &pluginName) const;
88 
89     /**
90      * @return the KPluginInfo for the specified plugin
91      */
92     KPluginInfo pluginInfo(const Choqok::Plugin *plugin) const;
93 
94     /**
95      * Shuts down the plugin manager on Choqok shutdown, but first
96      * unloads all plugins asynchronously.
97      *
98      * After 3 seconds all plugins should be removed; what's still left
99      * by then is unloaded through a hard delete instead.
100      *
101      * Note that this call also derefs the plugin manager from the event
102      * loop, so do NOT call this method when not terminating Choqok!
103      */
104     void shutdown();
105 
106     /**
107      * Enable a plugin.
108      *
109      * This marks a plugin as enabled in the config file, so loadAll()
110      * can pick it up later.
111      *
112      * This method does not actually load a plugin, it only edits the
113      * config file.
114      *
115      * @param name is the name of the plugin as it is listed in the .desktop
116      * file in the X-KDE-Library field.
117      * @param enabled sets whether or not the plugin is enabled
118      *
119      * Returns false when no appropriate plugin can be found.
120      */
121     bool setPluginEnabled(const QString &name, bool enabled = true);
122 
123     /**
124      * This method check if all the plugins are loaded.
125      * @return true if all the plugins are loaded.
126      */
127     bool isAllPluginsLoaded() const;
128 
129     /**
130      * Plugin loading mode. Used by @ref loadPlugin(). Code that doesn't want to block
131      * the GUI and/or lot a lot of plugins at once should use asynchronous loading (@c LoadAsync).
132      * The default is synchronous loading (@c LoadSync).
133      */
134     enum PluginLoadMode { LoadSync, LoadAsync };
135 
136 public Q_SLOTS:
137     /**
138      * @brief Load a single plugin by plugin name. Returns an existing plugin
139      * if one is already loaded in memory.
140      *
141      * If mode is set to Async, the plugin will be queued and loaded in
142      * the background. This method will return a null pointer. To get
143      * the loaded plugin you can track the @ref pluginLoaded() signal.
144      *
145      * See also @ref plugin().
146      */
147     Plugin *loadPlugin(const QString &pluginId, PluginLoadMode mode = LoadSync);
148 
149     /**
150      * @brief Unload the plugin specified by @p pluginName
151      */
152     bool unloadPlugin(const QString &pluginName);
153 
154     /**
155      * @brief Loads all the enabled plugins. Also used to reread the
156      * config file when the configuration has changed.
157      */
158     void loadAllPlugins();
159 
160 Q_SIGNALS:
161     /**
162      * @brief Signals a new plugin has just been loaded.
163      */
164     void pluginLoaded(Choqok::Plugin *plugin);
165 
166     /**
167      * @brief Signals a plugin has just been unloaded.
168      */
169     void pluginUnloaded(const QString &pluginName);
170 
171     /**
172      * @brief All plugins have been loaded by the plugin manager.
173      *
174      * This signal is emitted exactly ONCE, when the plugin manager has emptied
175      * its plugin queue for the first time. This means that if you call an async
176      * loadPlugin() before loadAllPlugins() this signal is probably emitted after
177      * the initial call completes, unless you are quick enough to fill the queue
178      * before it completes, which is a dangerous race you shouldn't count upon :)
179      *
180      * The signal is delayed one event loop iteration through a singleShot timer,
181      * but that is not guaranteed to be enough for account instantiation. You may
182      * need an additional timer for it in the code if you want to programmatically
183      * act on it.
184      *
185      * If you use the signal for enabling/disabling GUI objects there is little
186      * chance a user is able to activate them in the short while that's remaining,
187      * the slow part of the code is over now and the remaining processing time
188      * is neglectable for the user.
189      */
190     void allPluginsLoaded();
191 
192 private Q_SLOTS:
193     /**
194      * @brief Cleans up some references if the plugin is destroyed
195      */
196     void slotPluginDestroyed(QObject *plugin);
197 
198     /**
199      * shutdown() starts a timer, when it fires we force all plugins
200      * to be unloaded here by deref()-ing the event loop to trigger the plugin
201      * manager's destruction
202      */
203     void slotShutdownTimeout();
204 
205     /**
206      * Common entry point to deref() the KApplication. Used both by the clean
207      * shutdown and the timeout condition of slotShutdownTimeout()
208      */
209     void slotShutdownDone();
210 
211     /**
212      * Emitted by a Choqok::Plugin when it's ready for unload
213      */
214     void slotPluginReadyForUnload();
215 
216     /**
217      * Load a plugin from our queue. Does nothing if the queue is empty.
218      * Schedules itself again if more plugins are pending.
219      */
220     void slotLoadNextPlugin();
221 
222     /**
223      * Called when the application is quitting
224      */
225     void slotAboutToQuit();
226 
227 private:
228     /**
229      * @internal
230      *
231      * The internal method for loading plugins.
232      * Called by @ref loadPlugin directly or through the queue for async plugin
233      * loading.
234      */
235     Plugin *loadPluginInternal(const QString &pluginId);
236 
237     /**
238      * @internal
239      *
240      * Find the KPluginInfo structure by key. Reduces some code duplication.
241      *
242      * Returns a null pointer when no plugin info is found.
243      */
244     KPluginInfo infoForPluginId(const QString &pluginId) const;
245 
246     PluginManager();
247     ~PluginManager();
248 
249     // We want to add a reference to the application's event loop so we
250     // can remain in control when all windows are removed.
251     // This way we can unload plugins asynchronously, which is more
252     // robust if they are still doing processing.
253     QEventLoopLocker lock;
254 
255 };
256 
257 }
258 
259 #endif // KOPETEPLUGINMANAGER_H
260 
261