Fix no newlines warnings. Patch by Peter Oberndorfer
[kdevelopdvcssupport.git] / shell / plugincontroller.cpp
blob88be15826ee918e748dc05e92b9841bbf2cb40fa
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>
34 #include <kservice.h>
35 #include <kservicetypetrader.h>
36 #include <kmessagebox.h>
37 #include <kconfig.h>
38 #include <klocale.h>
39 #include <kxmlguiwindow.h>
40 #include <assert.h>
41 #include <kdebug.h>
42 #include <kdialog.h>
43 #include <kstandarddirs.h>
44 #include <kaction.h>
45 #include <kxmlguifactory.h>
46 #include <kmenu.h>
48 #include <interfaces/contextmenuextension.h>
49 #include <interfaces/iplugin.h>
51 #include "profileengine.h"
52 #include "mainwindow.h"
53 #include "core.h"
54 #include "shellextension.h"
56 namespace KDevelop
59 class PluginControllerPrivate
61 public:
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.
72 enum CleanupMode
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;
80 QString profile;
81 ProfileEngine engine;
82 Core *core;
83 QExtensionManager* m_manager;
86 PluginController::PluginController(Core *core)
87 : IPluginController(), d(new PluginControllerPrivate)
89 d->core = core;
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;
104 delete d->m_manager;
105 delete d;
108 QString PluginController::currentProfile() const
110 return d->profile;
113 ProfileEngine& PluginController::engine() const
115 return d->engine;
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 )
124 return it.key();
126 return KPluginInfo();
129 void PluginController::cleanup()
131 if(d->cleanupMode != PluginControllerPrivate::Running)
133 kDebug() << "called when not running. state =" << d->cleanupMode;
134 return;
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)
192 plugin->unload();
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 );
206 break;
210 if (deletion == Later)
211 plugin->deleteLater();
212 else
213 delete plugin;
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
230 QStringList unload;
231 KService::List coreLoad;
232 KService::List globalLoad;
233 d->engine.diffProfiles( ProfileEngine::Core,
234 currentProfile(),
235 newProfile,
236 unload,
237 coreLoad );
238 d->engine.diffProfiles( ProfileEngine::Global,
239 currentProfile(),
240 newProfile,
241 unload,
242 globalLoad );
244 QString oldProfile = d->profile;
245 d->profile = newProfile;
247 unloadPlugins( unload );
248 loadPlugins( coreLoad );
249 loadPlugins( globalLoad );
251 return oldProfile;
253 return QString();
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 )
262 return *it;
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 << "'!" ;
274 return 0L;
277 if ( d->loadedPlugins.contains( info ) )
278 return d->loadedPlugins[ info ];
280 kDebug() << "Attempting to load '" << pluginId << "'";
281 emit loadingPlugin( info.name() );
282 QString str_error;
283 IPlugin *plugin = 0;
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;
299 else
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 );
308 if ( plugin )
310 d->loadedPlugins.insert( info, plugin );
311 plugin->registerExtensions();
312 info.setPluginEnabled( true );
314 kDebug() << "Successfully loaded plugin '" << pluginId << "'";
316 emit pluginLoaded( plugin );
318 else
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;
326 else
328 kWarning() << "Loading plugin '" << pluginId
329 << "' failed, KPluginLoader reported error: '" << endl <<
330 str_error << "'";
334 return plugin;
338 IPlugin* PluginController::plugin( const QString& pluginId )
340 KPluginInfo info = infoForPluginId( pluginId );
341 if ( !info.isValid() )
342 return 0L;
344 if ( d->loadedPlugins.contains( info ) )
345 return d->loadedPlugins[ info ];
346 else
347 return 0L;
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" );
354 bool result = true;
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) );
361 if( l.isEmpty() )
363 result = false;
364 missing << iface;
368 return result;
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() )
418 return 0;
419 if( d->plugins.contains( infos.first() ) )
420 return d->loadedPlugins[ infos.first() ];
421 else
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)
432 IPlugin* plugin;
433 if( d->plugins.contains( info ) )
434 plugin = d->loadedPlugins[ info ];
435 else
436 plugin = loadPluginInternal( info.pluginName() );
438 if (plugin)
439 plugins << plugin;
440 else
441 kWarning(9501) << "Null plugin retrieved! Loading error?";
443 return plugins;
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()
455 return d->m_manager;
458 QStringList PluginController::allPluginNames()
460 QStringList names;
461 Q_FOREACH( const KPluginInfo& info , d->plugins )
463 names << info.pluginName();
465 return names;
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 );
476 return exts;
479 QStringList PluginController::projectPlugins()
481 QStringList names;
482 Q_FOREACH( const KPluginInfo& info , d->plugins )
484 if( info.property("X-KDevelop-Category").toString() == "Project" )
485 names << info.pluginName();
487 return names;
491 #include "plugincontroller.moc"