1 /* This file is part of the KDE project
2 Copyright 2004, 2007 Alexander Dymo <adymo@kdevelop.org>
3 Copyright 2006 Matt Rogers <mattr@kde.org
4 Copyright 2007 Andreas Pakulat <apaku@gmx.de>
6 Based on code from Kopete
7 Copyright (c) 2002-2003 Martijn Klingens <klingens@kde.org>
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
24 #include "plugincontroller.h"
26 #include <QtCore/QFile>
27 #include <QtCore/QTimer>
28 #include <QtGui/QApplication>
29 #include <QtGui/QAction>
30 #include <QtDesigner/QExtensionManager>
32 #include <kcmdlineargs.h>
33 #include <klibloader.h>
35 #include <kservicetypetrader.h>
36 #include <kmessagebox.h>
39 #include <kxmlguiwindow.h>
43 #include <kstandarddirs.h>
45 #include <kxmlguifactory.h>
48 #include <interfaces/contextmenuextension.h>
49 #include <interfaces/iplugin.h>
51 #include "profileengine.h"
52 #include "mainwindow.h"
54 #include "shellextension.h"
59 class PluginControllerPrivate
62 QList
<KPluginInfo
> plugins
;
64 //map plugin infos to currently loaded plugins
65 typedef QMap
<KPluginInfo
, IPlugin
*> InfoToPluginMap
;
66 InfoToPluginMap loadedPlugins
;
68 // The plugin manager's mode. The mode is StartingUp until loadAllPlugins()
69 // has finished loading the plugins, after which it is set to Running.
70 // ShuttingDown and DoneShutdown are used during shutdown by the
71 // async unloading of plugins.
74 Running
/**< the plugin manager is running */,
75 CleaningUp
/**< the plugin manager is cleaning up for shutdown */,
76 CleanupDone
/**< the plugin manager has finished cleaning up */
78 CleanupMode cleanupMode
;
83 QExtensionManager
* m_manager
;
86 PluginController::PluginController(Core
*core
)
87 : IPluginController(), d(new PluginControllerPrivate
)
90 d
->profile
= ShellExtension::getInstance() ->defaultProfile();
91 kDebug() << "Loading plugins which match:" << QString( "[X-KDevelop-Version] == %1" ).arg(KDEVELOP_PLUGIN_VERSION
);
92 d
->plugins
= KPluginInfo::fromServices( KServiceTypeTrader::self()->query( QLatin1String( "KDevelop/Plugin" ),
93 QString( "[X-KDevelop-Version] == %1" ).arg(KDEVELOP_PLUGIN_VERSION
) ) );
94 d
->cleanupMode
= PluginControllerPrivate::Running
;
95 d
->m_manager
= new QExtensionManager();
98 PluginController::~PluginController()
100 if ( d
->cleanupMode
!= PluginControllerPrivate::CleanupDone
)
101 kWarning(9501) << "Destructing plugin controller without going through the shutdown process! Backtrace is: "
102 << endl
<< kBacktrace() << endl
;
108 QString
PluginController::currentProfile() const
113 ProfileEngine
& PluginController::engine() const
118 KPluginInfo
PluginController::pluginInfo( const IPlugin
* plugin
) const
120 for ( PluginControllerPrivate::InfoToPluginMap::ConstIterator it
= d
->loadedPlugins
.begin();
121 it
!= d
->loadedPlugins
.end(); ++it
)
123 if ( it
.value() == plugin
)
126 return KPluginInfo();
129 void PluginController::cleanup()
131 if(d
->cleanupMode
!= PluginControllerPrivate::Running
)
133 kDebug() << "called when not running. state =" << d
->cleanupMode
;
137 d
->cleanupMode
= PluginControllerPrivate::CleaningUp
;
139 // Ask all plugins to unload
140 while ( !d
->loadedPlugins
.isEmpty() )
142 //Let the plugin do some stuff before unloading
143 unloadPlugin(d
->loadedPlugins
.begin().value(), Now
);
146 d
->cleanupMode
= PluginControllerPrivate::CleanupDone
;
149 IPlugin
* PluginController::loadPlugin( const QString
& pluginName
)
151 return loadPluginInternal( pluginName
);
154 void PluginController::loadPlugins( PluginType type
)
156 KPluginInfo::List offers
= d
->engine
.offers( d
->profile
, type
);
157 foreach( const KPluginInfo
& pi
, offers
)
159 loadPluginInternal( pi
.pluginName() );
163 void PluginController::unloadPlugins( PluginType type
)
165 //TODO see if this can be optimized so it's not something like O(n^2)
166 KPluginInfo::List offers
= d
->engine
.offers( d
->profile
, type
);
167 foreach( const KPluginInfo
& pi
, offers
)
169 foreach ( const KPluginInfo
& lpi
, d
->loadedPlugins
.keys() )
171 if ( pi
.pluginName() == lpi
.pluginName() )
172 unloadPlugin( pi
.pluginName() );
177 QList
<IPlugin
*> PluginController::loadedPlugins() const
179 return d
->loadedPlugins
.values();
182 void PluginController::unloadPlugin( const QString
& pluginId
)
184 if( IPlugin
*thePlugin
= plugin( pluginId
) )
186 unloadPlugin(thePlugin
, Later
);
190 void PluginController::unloadPlugin(IPlugin
* plugin
, PluginDeletion deletion
)
194 //Remove the plugin from our list of plugins so we create a new
195 //instance when we're asked for it again.
196 //This is important to do right here, not later when the plugin really
197 //vanishes. For example project re-opening might try to reload the plugin
198 //and then would get the "old" pointer which will be deleted in the next
199 //event loop run and thus causing crashes.
200 for ( PluginControllerPrivate::InfoToPluginMap::Iterator it
= d
->loadedPlugins
.begin();
201 it
!= d
->loadedPlugins
.end(); ++it
)
203 if ( it
.value() == plugin
)
205 d
->loadedPlugins
.erase( it
);
210 if (deletion
== Later
)
211 plugin
->deleteLater();
216 KUrl::List
PluginController::profileResources( const QString
&nameFilter
)
218 return d
->engine
.resources( currentProfile(), nameFilter
);
221 KUrl::List
PluginController::profileResourcesRecursive( const QString
&nameFilter
)
223 return d
->engine
.resourcesRecursive( currentProfile(), nameFilter
);
226 QString
PluginController::changeProfile( const QString
&newProfile
)
228 Q_UNUSED( newProfile
);
229 /* FIXME disabled for now
231 KService::List coreLoad;
232 KService::List globalLoad;
233 d->engine.diffProfiles( ProfileEngine::Core,
238 d->engine.diffProfiles( ProfileEngine::Global,
244 QString oldProfile = d->profile;
245 d->profile = newProfile;
247 unloadPlugins( unload );
248 loadPlugins( coreLoad );
249 loadPlugins( globalLoad );
256 KPluginInfo
PluginController::infoForPluginId( const QString
&pluginId
) const
258 QList
<KPluginInfo
>::ConstIterator it
;
259 for ( it
= d
->plugins
.begin(); it
!= d
->plugins
.end(); ++it
)
261 if ( it
->pluginName() == pluginId
)
265 return KPluginInfo();
268 IPlugin
*PluginController::loadPluginInternal( const QString
&pluginId
)
270 KPluginInfo info
= infoForPluginId( pluginId
);
271 if ( !info
.isValid() )
273 kWarning(9501) << "Unable to find a plugin named '" << pluginId
<< "'!" ;
277 if ( d
->loadedPlugins
.contains( info
) )
278 return d
->loadedPlugins
[ info
];
280 kDebug() << "Attempting to load '" << pluginId
<< "'";
281 emit
loadingPlugin( info
.name() );
284 QStringList missingInterfaces
;
285 kDebug() << "Checking... " << info
.name();
286 if ( checkForDependencies( info
, missingInterfaces
) )
288 QVariant prop
= info
.property( "X-KDevelop-PluginType" );
289 kDebug() << "Checked... starting to load:" << info
.name() << "type:" << prop
;
290 if(prop
.toString()=="Kross")
292 kDebug() << "it is a kross plugin!!";
293 QStringList interfaces
=info
.property( "X-KDevelop-Interfaces" ).toStringList();
294 plugin
= KServiceTypeTrader::createInstanceFromQuery
<IPlugin
>( QLatin1String( "KDevelop/Plugin" ),
295 QString::fromLatin1( "[X-KDE-PluginInfo-Name]=='KDevKrossManager'" ),
296 d
->core
, QVariantList() << interfaces
<< info
.pluginName(), &str_error
);
297 kDebug() << "kross plugin:" << plugin
;
301 plugin
= KServiceTypeTrader::createInstanceFromQuery
<IPlugin
>( QLatin1String( "KDevelop/Plugin" ),
302 QString::fromLatin1( "[X-KDE-PluginInfo-Name]=='%1'" ).arg( pluginId
), d
->core
, QVariantList(), &str_error
);
304 loadDependencies( info
);
305 loadOptionalDependencies( info
);
310 d
->loadedPlugins
.insert( info
, plugin
);
311 plugin
->registerExtensions();
312 info
.setPluginEnabled( true );
314 kDebug() << "Successfully loaded plugin '" << pluginId
<< "'";
316 emit
pluginLoaded( plugin
);
320 if( str_error
.isEmpty() && !missingInterfaces
.isEmpty() )
322 kWarning() << "Can't load plugin '" << pluginId
323 << "' some of its required dependecies could not be fulfilled:" << endl
324 << missingInterfaces
.join(",") << endl
;
328 kWarning() << "Loading plugin '" << pluginId
329 << "' failed, KPluginLoader reported error: '" << endl
<<
338 IPlugin
* PluginController::plugin( const QString
& pluginId
)
340 KPluginInfo info
= infoForPluginId( pluginId
);
341 if ( !info
.isValid() )
344 if ( d
->loadedPlugins
.contains( info
) )
345 return d
->loadedPlugins
[ info
];
350 ///@todo plugin load operation should be O(n)
351 bool PluginController::checkForDependencies( const KPluginInfo
& info
, QStringList
& missing
) const
353 QVariant prop
= info
.property( "X-KDevelop-IRequired" );
355 if( prop
.canConvert
<QStringList
>() )
357 QStringList deps
= prop
.toStringList();
358 foreach( QString iface
, deps
)
360 KPluginInfo::List l
= queryPlugins( QString("'%1' in [X-KDevelop-Interfaces]").arg(iface
) );
371 void PluginController::loadOptionalDependencies( const KPluginInfo
& info
)
373 QVariant prop
= info
.property( "X-KDevelop-IOptional" );
374 if( prop
.canConvert
<QStringList
>() )
376 QStringList deps
= prop
.toStringList();
377 foreach( QString iface
, deps
)
379 KPluginInfo info
= queryPlugins( QString("'%1' in [X-KDevelop-Interfaces]").arg(iface
) ).first();
380 if( !loadPluginInternal( info
.pluginName() ) )
382 kDebug() << "Couldn't load optional dependecy:" << iface
<< info
.pluginName();
388 void PluginController::loadDependencies( const KPluginInfo
& info
)
390 QVariant prop
= info
.property( "X-KDevelop-IRequired" );
391 if( prop
.canConvert
<QStringList
>() )
393 QStringList deps
= prop
.toStringList();
394 foreach( QString iface
, deps
)
396 KPluginInfo info
= queryPlugins( QString("'%1' in [X-KDevelop-Interfaces]").arg(iface
) ).first();
397 loadPluginInternal( info
.pluginName() );
402 IPlugin
* PluginController::pluginForExtension( const QString
& extension
, const QString
& pluginname
)
404 kDebug() << "Loading Plugin ("<< pluginname
<< ") for Extension:" << extension
;
405 QStringList constraints
;
406 if (!pluginname
.isEmpty())
407 constraints
<< QString("[X-KDE-PluginInfo-Name]=='%1'").arg( pluginname
);
409 return pluginForExtension(extension
, constraints
);
412 IPlugin
*PluginController::pluginForExtension(const QString
&extension
, const QStringList
&constraints
)
414 kDebug() << "Finding Plugin for Extension:" << extension
<< "|" << constraints
;
415 KPluginInfo::List infos
= queryExtensionPlugins(extension
, constraints
);
417 if( infos
.isEmpty() )
419 if( d
->plugins
.contains( infos
.first() ) )
420 return d
->loadedPlugins
[ infos
.first() ];
422 return loadPluginInternal( infos
.first().pluginName() );
425 QList
<IPlugin
*> PluginController::allPluginsForExtension(const QString
&extension
, const QStringList
&constraints
)
427 kDebug() << "Finding all Plugins for Extension:" << extension
<< "|" << constraints
;
428 KPluginInfo::List infos
= queryExtensionPlugins(extension
, constraints
);
429 QList
<IPlugin
*> plugins
;
430 foreach (const KPluginInfo
&info
, infos
)
433 if( d
->plugins
.contains( info
) )
434 plugin
= d
->loadedPlugins
[ info
];
436 plugin
= loadPluginInternal( info
.pluginName() );
441 kWarning(9501) << "Null plugin retrieved! Loading error?";
446 KPluginInfo::List
PluginController::queryExtensionPlugins(const QString
&extension
, const QStringList
&constraints
)
448 QStringList c
= constraints
;
449 c
<< QString("'%1' in [X-KDevelop-Interfaces]").arg( extension
);
450 return queryPlugins( c
.join(" and ") );
453 QExtensionManager
* PluginController::extensionManager()
458 QStringList
PluginController::allPluginNames()
461 Q_FOREACH( const KPluginInfo
& info
, d
->plugins
)
463 names
<< info
.pluginName();
468 QList
<ContextMenuExtension
> PluginController::queryPluginsForContextMenuExtensions( KDevelop::Context
* context
) const
470 QList
<ContextMenuExtension
> exts
;
471 Q_FOREACH( const KPluginInfo
& info
, d
->loadedPlugins
.keys() )
473 IPlugin
* plug
= d
->loadedPlugins
[info
];
474 exts
<< plug
->contextMenuExtension( context
);
479 QStringList
PluginController::projectPlugins()
482 Q_FOREACH( const KPluginInfo
& info
, d
->plugins
)
484 if( info
.property("X-KDevelop-Category").toString() == "Project" )
485 names
<< info
.pluginName();
491 #include "plugincontroller.moc"