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 slotShutdownTimeout();
00093 #if 0
00094 kdWarning() << k_funcinfo
00095 << "Destructing plugin manager without going through "
00096 << "the shutdown process!"
00097 << endl
00098 << kdBacktrace(10) << endl;
00099 #endif
00100 }
00101
00102
00103 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00104 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00105 {
00106
00107 QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it );
00108 ++nextIt;
00109 kdWarning() << k_funcinfo << "Deleting stale plugin '"
00110 << it.data()->name() << "'" << endl;
00111 delete it.data();
00112 it = nextIt;
00113 }
00114
00115 delete d;
00116 }
00117
00118 QValueList<KPluginInfo*>
00119 PluginManager::availablePlugins( const QString &category ) const
00120 {
00121 if ( category.isEmpty() )
00122 return d->plugins;
00123
00124 QValueList<KPluginInfo*> result;
00125 QValueList<KPluginInfo*>::ConstIterator it;
00126 for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
00127 {
00128 if ( ( *it )->category() == category )
00129 result.append( *it );
00130 }
00131
00132 return result;
00133 }
00134
00135 QMap<KPluginInfo*, Plugin*>
00136 PluginManager::loadedPlugins( const QString &category ) const
00137 {
00138 QMap<KPluginInfo*, Plugin*> result;
00139 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00140 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00141 {
00142 if ( category.isEmpty() || it.key()->category() == category )
00143 result.insert( it.key(), it.data() );
00144 }
00145
00146 return result;
00147 }
00148
00149 void
00150 PluginManager::shutdown()
00151 {
00152 d->shutdownMode = Private::ShuttingDown;
00153
00154
00155 d->pluginsToLoad.clear();
00156
00157
00158 if ( d->loadedPlugins.empty() ) {
00159 d->shutdownMode = Private::DoneShutdown;
00160 } else {
00161 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00162 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00163 {
00164
00165 QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it );
00166 ++nextIt;
00167 it.data()->aboutToUnload();
00168 it = nextIt;
00169 }
00170 }
00171
00172 QTimer::singleShot( 3000, this, SLOT(slotShutdownTimeout()) );
00173 }
00174
00175 void
00176 PluginManager::slotPluginReadyForUnload()
00177 {
00178
00179
00180
00181 Plugin* plugin = dynamic_cast<Plugin*>( const_cast<QObject*>( sender() ) );
00182 if ( !plugin )
00183 {
00184 kdWarning() << k_funcinfo << "Calling object is not a plugin!" << endl;
00185 return;
00186
00187 }
00188 kdDebug()<<"manager unloading"<<endl;
00189 plugin->deleteLater();
00190 }
00191
00192 void
00193 PluginManager::slotShutdownTimeout()
00194 {
00195
00196
00197 if ( d->shutdownMode == Private::DoneShutdown )
00198 return;
00199
00200 #ifndef NDEBUG
00201 QStringList remaining;
00202 for ( QMap<KPluginInfo*, Plugin*>::ConstIterator it = d->loadedPlugins.begin();
00203 it != d->loadedPlugins.end(); ++it )
00204 remaining.append( it.key()->pluginName() );
00205
00206 kdWarning() << k_funcinfo << "Some plugins didn't shutdown in time!" << endl
00207 << "Remaining plugins: "
00208 << remaining.join( QString::fromLatin1( ", " ) ) << endl
00209 << "Forcing Komposer shutdown now." << endl;
00210 #endif
00211
00212 slotShutdownDone();
00213 }
00214
00215 void
00216 PluginManager::slotShutdownDone()
00217 {
00218 d->shutdownMode = Private::DoneShutdown;
00219
00220 kapp->deref();
00221 }
00222
00223 void
00224 PluginManager::loadAllPlugins()
00225 {
00226
00227
00228 if ( !d->config )
00229 d->config = KSharedConfig::openConfig( "komposerrc" );
00230
00231 QMap<QString, QString> entries = d->config->entryMap(
00232 QString::fromLatin1( "Plugins" ) );
00233
00234 QMap<QString, QString>::Iterator it;
00235 for ( it = entries.begin(); it != entries.end(); ++it )
00236 {
00237 QString key = it.key();
00238 if ( key.endsWith( QString::fromLatin1( "Enabled" ) ) )
00239 {
00240 key.setLength( key.length() - 7 );
00241
00242
00243 if ( it.data() == QString::fromLatin1( "true" ) )
00244 {
00245 if ( !plugin( key ) )
00246 d->pluginsToLoad.push( key );
00247 }
00248 else
00249 {
00250
00251
00252
00253 if ( plugin( key ) )
00254 unloadPlugin( key );
00255 }
00256 }
00257 }
00258
00259
00260 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00261 }
00262
00263 void PluginManager::slotLoadNextPlugin()
00264 {
00265 if ( d->pluginsToLoad.isEmpty() )
00266 {
00267 if ( d->shutdownMode == Private::StartingUp )
00268 {
00269 d->shutdownMode = Private::Running;
00270 emit allPluginsLoaded();
00271 }
00272 return;
00273 }
00274
00275 QString key = d->pluginsToLoad.pop();
00276 loadPluginInternal( key );
00277
00278
00279
00280
00281
00282 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00283 }
00284
00285 Plugin*
00286 PluginManager::loadPlugin( const QString &pluginId,
00287 PluginLoadMode mode )
00288 {
00289 if ( mode == LoadSync ) {
00290 return loadPluginInternal( pluginId );
00291 } else {
00292 d->pluginsToLoad.push( pluginId );
00293 QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
00294 return 0;
00295 }
00296 }
00297
00298 Plugin*
00299 PluginManager::loadPluginInternal( const QString &pluginId )
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<Komposer::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 "
00353 << "of the specified type."
00354 << endl;
00355 break;
00356 }
00357
00358 kdDebug() << k_funcinfo << "Loading plugin '" << pluginId
00359 << "' failed, KLibLoader reported error: '"
00360 << KLibLoader::self()->lastErrorMessage()
00361 << "'" << endl;
00362 }
00363
00364 return plugin;
00365 }
00366
00367 bool
00368 PluginManager::unloadPlugin( const QString &spec )
00369 {
00370 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00371 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00372 {
00373 if ( it.key()->pluginName() == spec )
00374 {
00375 it.data()->aboutToUnload();
00376 return true;
00377 }
00378 }
00379
00380 return false;
00381 }
00382
00383 void
00384 PluginManager::slotPluginDestroyed( QObject *plugin )
00385 {
00386 QMap<KPluginInfo*, Plugin*>::Iterator it;
00387 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00388 {
00389 if ( it.data() == plugin )
00390 {
00391 d->loadedPlugins.erase( it );
00392 break;
00393 }
00394 }
00395
00396 if ( d->shutdownMode == Private::ShuttingDown && d->loadedPlugins.isEmpty() )
00397 {
00398
00399
00400 QTimer::singleShot( 0, this, SLOT(slotShutdownDone()) );
00401 }
00402 }
00403
00404 Plugin*
00405 PluginManager::plugin( const QString &pluginId ) const
00406 {
00407 KPluginInfo *info = infoForPluginId( pluginId );
00408 if ( !info )
00409 return 0;
00410
00411 if ( d->loadedPlugins.contains( info ) )
00412 return d->loadedPlugins[ info ];
00413 else
00414 return 0;
00415 }
00416
00417 QString
00418 PluginManager::pluginName( const Plugin *plugin ) const
00419 {
00420 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00421 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00422 {
00423 if ( it.data() == plugin )
00424 return it.key()->name();
00425 }
00426
00427 return QString::fromLatin1( "Unknown" );
00428 }
00429
00430 QString
00431 PluginManager::pluginId( const Plugin *plugin ) const
00432 {
00433 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00434 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00435 {
00436 if ( it.data() == plugin )
00437 return it.key()->pluginName();
00438 }
00439
00440 return QString::fromLatin1( "unknown" );
00441 }
00442
00443 QString
00444 PluginManager::pluginIcon( const Plugin *plugin ) const
00445 {
00446 QMap<KPluginInfo*, Plugin*>::ConstIterator it;
00447 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
00448 {
00449 if ( it.data() == plugin )
00450 return it.key()->icon();
00451 }
00452
00453 return QString::fromLatin1( "Unknown" );
00454 }
00455
00456 KPluginInfo*
00457 PluginManager::infoForPluginId( const QString &pluginId ) const
00458 {
00459 QValueList<KPluginInfo*>::ConstIterator it;
00460 for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
00461 {
00462 if ( ( *it )->pluginName() == pluginId )
00463 return *it;
00464 }
00465
00466 return 0;
00467 }
00468
00469 bool
00470 PluginManager::setPluginEnabled( const QString &pluginId, bool enabled )
00471 {
00472 if ( !d->config )
00473 d->config = KSharedConfig::openConfig( "komposerrc" );
00474
00475 d->config->setGroup( "Plugins" );
00476
00477
00478 if ( !infoForPluginId( pluginId ) )
00479 return false;
00480
00481 d->config->writeEntry( pluginId + QString::fromLatin1( "Enabled" ), enabled );
00482 d->config->sync();
00483
00484 return true;
00485 }
00486
00487 }
00488
00489 #include "pluginmanager.moc"