00001
00023 #include "pluginmanager.h"
00024
00025 #include "plugin.h"
00026
00027 #include <tqapplication.h>
00028 #include <tqfile.h>
00029 #include <tqregexp.h>
00030 #include <tqtimer.h>
00031 #include <tqvaluestack.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 TQValueList<KPluginInfo*> plugins;
00052
00053
00054
00055 TQMap<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 TQValueStack<TQString> pluginsToLoad;
00067 };
00068
00069 PluginManager::PluginManager( TQObject *parent )
00070 : TQObject( 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, TQT_SLOT( loadAllPlugins() ) );
00083
00084 d->plugins = KPluginInfo::fromServices(
00085 KTrader::self()->query( TQString::fromLatin1( "Komposer/Plugin" ),
00086 TQString::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 TQMap<KPluginInfo*, Plugin*>::ConstIterator it;
00104 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00105 {
00106
00107 TQMap<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 TQValueList<KPluginInfo*>
00119 PluginManager::availablePlugins( const TQString &category ) const
00120 {
00121 if ( category.isEmpty() )
00122 return d->plugins;
00123
00124 TQValueList<KPluginInfo*> result;
00125 TQValueList<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 TQMap<KPluginInfo*, Plugin*>
00136 PluginManager::loadedPlugins( const TQString &category ) const
00137 {
00138 TQMap<KPluginInfo*, Plugin*> result;
00139 TQMap<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 TQMap<KPluginInfo*, Plugin*>::ConstIterator it;
00162 for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); )
00163 {
00164
00165 TQMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it );
00166 ++nextIt;
00167 it.data()->aboutToUnload();
00168 it = nextIt;
00169 }
00170 }
00171
00172 TQTimer::singleShot( 3000, this, TQT_SLOT(slotShutdownTimeout()) );
00173 }
00174
00175 void
00176 PluginManager::slotPluginReadyForUnload()
00177 {
00178
00179
00180
00181 Plugin* plugin = dynamic_cast<Plugin*>( const_cast<TQObject*>( 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 TQStringList remaining;
00202 for ( TQMap<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( TQString::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 TQMap<TQString, TQString> entries = d->config->entryMap(
00232 TQString::fromLatin1( "Plugins" ) );
00233
00234 TQMap<TQString, TQString>::Iterator it;
00235 for ( it = entries.begin(); it != entries.end(); ++it )
00236 {
00237 TQString key = it.key();
00238 if ( key.endsWith( TQString::fromLatin1( "Enabled" ) ) )
00239 {
00240 key.setLength( key.length() - 7 );
00241
00242
00243 if ( it.data() == TQString::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 TQTimer::singleShot( 0, this, TQT_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 TQString key = d->pluginsToLoad.pop();
00276 loadPluginInternal( key );
00277
00278
00279
00280
00281
00282 TQTimer::singleShot( 0, this, TQT_SLOT( slotLoadNextPlugin() ) );
00283 }
00284
00285 Plugin*
00286 PluginManager::loadPlugin( const TQString &pluginId,
00287 PluginLoadMode mode )
00288 {
00289 if ( mode == LoadSync ) {
00290 return loadPluginInternal( pluginId );
00291 } else {
00292 d->pluginsToLoad.push( pluginId );
00293 TQTimer::singleShot( 0, this, TQT_SLOT( slotLoadNextPlugin() ) );
00294 return 0;
00295 }
00296 }
00297
00298 Plugin*
00299 PluginManager::loadPluginInternal( const TQString &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 TQString::fromLatin1( "Komposer/Plugin" ),
00314 TQString::fromLatin1( "[X-KDE-PluginInfo-Name]=='%1'" ).arg( pluginId ),
00315 this, 0, TQStringList(), &error );
00316
00317 if ( plugin ) {
00318 d->loadedPlugins.insert( info, plugin );
00319 info->setPluginEnabled( true );
00320
00321 connect( plugin, TQT_SIGNAL(destroyed(TQObject*)),
00322 this, TQT_SLOT(slotPluginDestroyed(TQObject*)) );
00323 connect( plugin, TQT_SIGNAL(readyForUnload()),
00324 this, TQT_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 TQString &spec )
00369 {
00370 TQMap<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( TQObject *plugin )
00385 {
00386 TQMap<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 TQTimer::singleShot( 0, this, TQT_SLOT(slotShutdownDone()) );
00401 }
00402 }
00403
00404 Plugin*
00405 PluginManager::plugin( const TQString &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 TQString
00418 PluginManager::pluginName( const Plugin *plugin ) const
00419 {
00420 TQMap<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 TQString::fromLatin1( "Unknown" );
00428 }
00429
00430 TQString
00431 PluginManager::pluginId( const Plugin *plugin ) const
00432 {
00433 TQMap<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 TQString::fromLatin1( "unknown" );
00441 }
00442
00443 TQString
00444 PluginManager::pluginIcon( const Plugin *plugin ) const
00445 {
00446 TQMap<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 TQString::fromLatin1( "Unknown" );
00454 }
00455
00456 KPluginInfo*
00457 PluginManager::infoForPluginId( const TQString &pluginId ) const
00458 {
00459 TQValueList<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 TQString &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 + TQString::fromLatin1( "Enabled" ), enabled );
00482 d->config->sync();
00483
00484 return true;
00485 }
00486
00487 }
00488
00489 #include "pluginmanager.moc"