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