00001
00023 #include "pluginmanager.h"
00024
00025 #include "plugin.h"
00026
00027 #include <qapplication.h>
00028 #include <qfile.h>
00029 #include <qregexp.h>
00030 #include <qtimer.h>
00031 #include <qvaluestack.h>
00032
00033 #include <kapplication.h>
00034 #include <kdebug.h>
00035 #include <kparts/componentfactory.h>
00036 #include <kplugininfo.h>
00037 #include <ksettings/dispatcher.h>
00038 #include <ksimpleconfig.h>
00039 #include <kstandarddirs.h>
00040 #include <kstaticdeleter.h>
00041 #include <kurl.h>
00042
00043
00044 namespace Komposer
00045 {
00046
00047 class PluginManager::Private
00048 {
00049 public:
00050
00051 QValueList<KPluginInfo*> plugins;
00052
00053
00054
00055 QMap<KPluginInfo*, Plugin*> loadedPlugins;
00056
00057
00058
00059
00060
00061 enum ShutdownMode { StartingUp, Running, ShuttingDown, DoneShutdown };
00062 ShutdownMode shutdownMode;
00063
00064 KSharedConfig::Ptr config;
00065
00066 QValueStack<QString> pluginsToLoad;
00067 };
00068
00069 PluginManager::PluginManager( QObject* parent )
00070 : QObject( parent )
00071 {
00072 d = new Private;
00073
00074
00075
00076
00077
00078 kapp->ref();
00079 d->shutdownMode = Private::StartingUp;
00080
00081 KSettings::Dispatcher::self()->registerInstance( KGlobal::instance(),
00082 this, SLOT( loadAllPlugins() ) );
00083
00084 d->plugins = KPluginInfo::fromServices(
00085 KTrader::self()->query( QString::fromLatin1( "Komposer/Plugin" ),
00086 QString::fromLatin1( "[X-Komposer-Version] == 1" ) ) );
00087 }
00088
00089 PluginManager::~PluginManager()
00090 {
00091 if ( d->shutdownMode != Private::DoneShutdown )
00092 kdWarning() << k_funcinfo
00093 << "Destructing plugin manager without going through the shutdown process!"
00094 << endl
00095 << kdBacktrace() << endl;
00096
00097
00098 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00099 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00100 {
00101
00102 QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it );
00103 ++nextIt;
00104 kdWarning() << k_funcinfo << "Deleting stale plugin '"
00105 << it.data()->name() << "'" << endl;
00106 delete it.data();
00107 it = nextIt;
00108 }
00109
00110 delete d;
00111 }
00112
00113 QValueList<KPluginInfo*>
00114 PluginManager::availablePlugins( const QString& category ) const
00115 {
00116 if ( category.isEmpty() )
00117 return d->plugins;
00118
00119 QValueList<KPluginInfo*> result;
00120 QValueList<KPluginInfo*>::ConstIterator it;
00121 for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
00122 {
00123 if ( ( *it )->category() == category )
00124 result.append( *it );
00125 }
00126
00127 return result;
00128 }
00129
00130 QMap<KPluginInfo*, Plugin*>
00131 PluginManager::loadedPlugins( const QString& category ) const
00132 {
00133 QMap<KPluginInfo*, Plugin*> result;
00134 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00135 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00136 {
00137 if ( category.isEmpty() || it.key()->category() == category )
00138 result.insert( it.key(), it.data() );
00139 }
00140
00141 return result;
00142 }
00143
00144 void
00145 PluginManager::shutdown()
00146 {
00147 kdDebug() << k_funcinfo << endl;
00148
00149 d->shutdownMode = Private::ShuttingDown;
00150
00151
00152 d->pluginsToLoad.clear();
00153
00154
00155 if ( d->loadedPlugins.empty() ) {
00156 d->shutdownMode = Private::DoneShutdown;
00157 } else {
00158 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00159 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00160 {
00161
00162 QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it );
00163 ++nextIt;
00164 it.data()->aboutToUnload();
00165 it = nextIt;
00166 }
00167 }
00168
00169 QTimer::singleShot( 3000, this, SLOT(slotShutdownTimeout()) );
00170 }
00171
00172 void
00173 PluginManager::slotPluginReadyForUnload()
00174 {
00175
00176
00177
00178 Plugin* plugin = dynamic_cast<Plugin*>( const_cast<QObject*>( sender() ) );
00179 if ( !plugin )
00180 {
00181 kdWarning() << k_funcinfo << "Calling object is not a plugin!" << endl;
00182 return;
00183
00184 }
00185
00186 plugin->deleteLater();
00187 }
00188
00189 void
00190 PluginManager::slotShutdownTimeout()
00191 {
00192
00193
00194 if ( d->shutdownMode == Private::DoneShutdown )
00195 return;
00196
00197 #ifndef NDEBUG
00198 QStringList remaining;
00199 for ( QMap<KPluginInfo*, Plugin*>::ConstIterator it = d->loadedPlugins.begin();
00200 it != d->loadedPlugins.end(); ++it )
00201 remaining.append( it.key()->pluginName() );
00202
00203 kdWarning() << k_funcinfo << "Some plugins didn't shutdown in time!" << endl
00204 << "Remaining plugins: "
00205 << remaining.join( QString::fromLatin1( ", " ) ) << endl
00206 << "Forcing Komposer shutdown now." << endl;
00207 #endif
00208
00209 slotShutdownDone();
00210 }
00211
00212 void
00213 PluginManager::slotShutdownDone()
00214 {
00215 kdDebug() << k_funcinfo << endl;
00216
00217 d->shutdownMode = Private::DoneShutdown;
00218
00219 kapp->deref();
00220 }
00221
00222 void
00223 PluginManager::loadAllPlugins()
00224 {
00225
00226
00227 if ( !d->config )
00228 d->config = KSharedConfig::openConfig( "komposerrc" );
00229
00230 QMap<QString, QString> entries = d->config->entryMap(
00231 QString::fromLatin1( "Plugins" ) );
00232
00233 QMap<QString, QString>::Iterator it;
00234 for ( it = entries.begin(); it != entries.end(); ++it )
00235 {
00236 QString key = it.key();
00237 if ( key.endsWith( QString::fromLatin1( "Enabled" ) ) )
00238 {
00239 key.setLength( key.length() - 7 );
00240
00241
00242 if ( it.data() == QString::fromLatin1( "true" ) )
00243 {
00244 if ( !plugin( key ) )
00245 d->pluginsToLoad.push( key );
00246 }
00247 else
00248 {
00249
00250
00251 if ( plugin( key ) )
00252 unloadPlugin( key );
00253 }
00254 }
00255 }
00256
00257
00258 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00259 }
00260
00261 void PluginManager::slotLoadNextPlugin()
00262 {
00263 if ( d->pluginsToLoad.isEmpty() )
00264 {
00265 if ( d->shutdownMode == Private::StartingUp )
00266 {
00267 d->shutdownMode = Private::Running;
00268 emit allPluginsLoaded();
00269 }
00270 return;
00271 }
00272
00273 QString key = d->pluginsToLoad.pop();
00274 loadPluginInternal( key );
00275
00276
00277
00278
00279
00280 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00281 }
00282
00283 Plugin*
00284 PluginManager::loadPlugin( const QString& pluginId,
00285 PluginLoadMode mode )
00286 {
00287 if ( mode == LoadSync ) {
00288 return loadPluginInternal( pluginId );
00289 } else {
00290 d->pluginsToLoad.push( pluginId );
00291 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00292 return 0;
00293 }
00294 }
00295
00296 Plugin*
00297 PluginManager::loadPluginInternal( const QString& pluginId )
00298 {
00299 kdDebug() << k_funcinfo << pluginId << endl;
00300
00301 KPluginInfo* info = infoForPluginId( pluginId );
00302 if ( !info ) {
00303 kdWarning() << k_funcinfo << "Unable to find a plugin named '"
00304 << pluginId << "'!" << endl;
00305 return 0;
00306 }
00307
00308 if ( d->loadedPlugins.contains( info ) )
00309 return d->loadedPlugins[ info ];
00310
00311 int error = 0;
00312 Plugin* plugin = KParts::ComponentFactory::createInstanceFromQuery<Plugin>(
00313 QString::fromLatin1( "Komposer/Plugin" ),
00314 QString::fromLatin1( "[X-KDE-PluginInfo-Name]=='%1'" ).arg( pluginId ),
00315 this, 0, QStringList(), &error );
00316
00317 if ( plugin ) {
00318 d->loadedPlugins.insert( info, plugin );
00319 info->setPluginEnabled( true );
00320
00321 connect( plugin, SIGNAL(destroyed(QObject*)),
00322 this, SLOT(slotPluginDestroyed(QObject*)) );
00323 connect( plugin, SIGNAL( readyForUnload() ),
00324 this, SLOT(slotPluginReadyForUnload()) );
00325
00326 kdDebug() << k_funcinfo << "Successfully loaded plugin '"
00327 << pluginId << "'" << endl;
00328
00329 emit pluginLoaded( plugin );
00330 } else {
00331 switch ( error ) {
00332 case KParts::ComponentFactory::ErrNoServiceFound:
00333 kdDebug() << k_funcinfo << "No service implementing the given mimetype "
00334 << "and fullfilling the given constraint expression can be found."
00335 << endl;
00336 break;
00337
00338 case KParts::ComponentFactory::ErrServiceProvidesNoLibrary:
00339 kdDebug() << "the specified service provides no shared library." << endl;
00340 break;
00341
00342 case KParts::ComponentFactory::ErrNoLibrary:
00343 kdDebug() << "the specified library could not be loaded." << endl;
00344 break;
00345
00346 case KParts::ComponentFactory::ErrNoFactory:
00347 kdDebug() << "the library does not export a factory for creating components."
00348 << endl;
00349 break;
00350
00351 case KParts::ComponentFactory::ErrNoComponent:
00352 kdDebug() << "the factory does not support creating components of the specified type."
00353 << endl;
00354 break;
00355 }
00356
00357 kdDebug() << k_funcinfo << "Loading plugin '" << pluginId
00358 << "' failed, KLibLoader reported error: '" << endl
00359 << KLibLoader::self()->lastErrorMessage()
00360 << "'" << endl;
00361 }
00362
00363 return plugin;
00364 }
00365
00366 bool
00367 PluginManager::unloadPlugin( const QString& spec )
00368 {
00369 kdDebug() << k_funcinfo << spec << endl;
00370
00371 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00372 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00373 {
00374 if ( it.key()->pluginName() == spec )
00375 {
00376 it.data()->aboutToUnload();
00377 return true;
00378 }
00379 }
00380
00381 return false;
00382 }
00383
00384 void
00385 PluginManager::slotPluginDestroyed( QObject* plugin )
00386 {
00387 QMap<KPluginInfo*, Plugin*>::Iterator it;
00388 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00389 {
00390 if ( it.data() == plugin )
00391 {
00392 d->loadedPlugins.erase( it );
00393 break;
00394 }
00395 }
00396
00397 if ( d->shutdownMode == Private::ShuttingDown && d->loadedPlugins.isEmpty() )
00398 {
00399
00400
00401 QTimer::singleShot( 0, this, SLOT(slotShutdownDone()) );
00402 }
00403 }
00404
00405 Plugin*
00406 PluginManager::plugin( const QString& pluginId ) const
00407 {
00408 KPluginInfo* info = infoForPluginId( pluginId );
00409 if ( !info )
00410 return 0;
00411
00412 if ( d->loadedPlugins.contains( info ) )
00413 return d->loadedPlugins[ info ];
00414 else
00415 return 0;
00416 }
00417
00418 QString
00419 PluginManager::pluginName( const Plugin* plugin ) const
00420 {
00421 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00422 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00423 {
00424 if ( it.data() == plugin )
00425 return it.key()->name();
00426 }
00427
00428 return QString::fromLatin1( "Unknown" );
00429 }
00430
00431 QString
00432 PluginManager::pluginId( const Plugin* plugin ) const
00433 {
00434 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00435 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00436 {
00437 if ( it.data() == plugin )
00438 return it.key()->pluginName();
00439 }
00440
00441 return QString::fromLatin1( "unknown" );
00442 }
00443
00444 QString
00445 PluginManager::pluginIcon( const Plugin* plugin ) const
00446 {
00447 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00448 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00449 {
00450 if ( it.data() == plugin )
00451 return it.key()->icon();
00452 }
00453
00454 return QString::fromLatin1( "Unknown" );
00455 }
00456
00457 KPluginInfo*
00458 PluginManager::infoForPluginId( const QString& pluginId ) const
00459 {
00460 QValueList<KPluginInfo*>::ConstIterator it;
00461 for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
00462 {
00463 if ( ( *it )->pluginName() == pluginId )
00464 return *it;
00465 }
00466
00467 return 0;
00468 }
00469
00470 bool
00471 PluginManager::setPluginEnabled( const QString& pluginId, bool enabled )
00472 {
00473 if ( !d->config )
00474 d->config = KSharedConfig::openConfig( "komposerrc" );
00475
00476 d->config->setGroup( "Plugins" );
00477
00478
00479 if ( !infoForPluginId( pluginId ) )
00480 return false;
00481
00482 d->config->writeEntry( pluginId + QString::fromLatin1( "Enabled" ), enabled );
00483 d->config->sync();
00484
00485 return true;
00486 }
00487
00488 }
00489
00490 #include "pluginmanager.moc"