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"