1/* This file is part of the KDE project
2 Copyright (C) 1999 Simon Hausmann <hausmann@kde.org>
3 (C) 1999 David Faure <faure@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include <kparts/plugin.h>
22#include <kparts/part.h>
23#include <kparts/componentfactory.h>
24
25#include <assert.h>
26
27#include <QtCore/QFile>
28#include <QtCore/QObject>
29#include <QtCore/QFileInfo>
30
31#include <kcomponentdata.h>
32#include <kstandarddirs.h>
33#include <kdebug.h>
34#include <kxmlguifactory.h>
35#include <klocale.h>
36#include <kdesktopfile.h>
37#include <kconfiggroup.h>
38
39using namespace KParts;
40
41class Plugin::PluginPrivate
42{
43public:
44 KComponentData m_parentInstance;
45 QString m_library; // filename of the library
46};
47
48Plugin::Plugin( QObject* parent )
49 : QObject( parent ),d(new PluginPrivate())
50{
51 //kDebug() << className();
52}
53
54Plugin::~Plugin()
55{
56 delete d;
57}
58
59QString Plugin::xmlFile() const
60{
61 QString path = KXMLGUIClient::xmlFile();
62
63 if ( !d->m_parentInstance.isValid() || ( path.length() > 0 && path[ 0 ] == '/' ) )
64 return path;
65
66 QString absPath = KStandardDirs::locate( "data", d->m_parentInstance.componentName() + '/' + path );
67 assert( !absPath.isEmpty() );
68 return absPath;
69}
70
71QString Plugin::localXMLFile() const
72{
73 QString path = KXMLGUIClient::xmlFile();
74
75 if ( !d->m_parentInstance.isValid() || ( path.length() > 0 && path[ 0 ] == '/' ) )
76 return path;
77
78 QString absPath = KStandardDirs::locateLocal( "data", d->m_parentInstance.componentName() + '/' + path );
79 assert( !absPath.isEmpty() );
80 return absPath;
81}
82
83//static
84QList<Plugin::PluginInfo> Plugin::pluginInfos(const KComponentData &componentData)
85{
86 if (!componentData.isValid())
87 kError(1000) << "No componentData ???" << endl;
88
89 QList<PluginInfo> plugins;
90
91 // TODO KDE5: change * into *.rc and remove test for .desktop from the for loop below.
92 const QStringList pluginDocs = componentData.dirs()->findAllResources(
93 "data", componentData.componentName()+"/kpartplugins/*", KStandardDirs::Recursive );
94
95 QMap<QString,QStringList> sortedPlugins;
96
97 QStringList::ConstIterator pIt = pluginDocs.begin();
98 QStringList::ConstIterator pEnd = pluginDocs.end();
99 for (; pIt != pEnd; ++pIt )
100 {
101 QFileInfo fInfo( *pIt );
102 if ( fInfo.completeSuffix() == QLatin1String( "desktop" ) )
103 continue;
104
105 QMap<QString,QStringList>::Iterator mapIt = sortedPlugins.find( fInfo.fileName() );
106 if ( mapIt == sortedPlugins.end() )
107 mapIt = sortedPlugins.insert( fInfo.fileName(), QStringList() );
108
109 mapIt.value().append( *pIt );
110 }
111
112 QMap<QString,QStringList>::ConstIterator mapIt = sortedPlugins.constBegin();
113 QMap<QString,QStringList>::ConstIterator mapEnd = sortedPlugins.constEnd();
114 for (; mapIt != mapEnd; ++mapIt )
115 {
116 PluginInfo info;
117 QString doc;
118 info.m_absXMLFileName = KXMLGUIClient::findMostRecentXMLFile( mapIt.value(), doc );
119 if ( info.m_absXMLFileName.isEmpty() )
120 continue;
121
122 kDebug( 1000 ) << "found KParts Plugin : " << info.m_absXMLFileName;
123 info.m_relXMLFileName = "kpartplugins/";
124 info.m_relXMLFileName += mapIt.key();
125
126 info.m_document.setContent( doc );
127 if ( info.m_document.documentElement().isNull() )
128 continue;
129
130 plugins.append( info );
131 }
132
133 return plugins;
134}
135
136void Plugin::loadPlugins(QObject *parent, const KComponentData &componentData)
137{
138 loadPlugins( parent, pluginInfos( componentData ), componentData );
139}
140
141void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos, const KComponentData &componentData)
142{
143 QList<PluginInfo>::ConstIterator pIt = pluginInfos.begin();
144 QList<PluginInfo>::ConstIterator pEnd = pluginInfos.end();
145 for (; pIt != pEnd; ++pIt )
146 {
147 QString library = (*pIt).m_document.documentElement().attribute( "library" );
148
149 if ( library.isEmpty() || hasPlugin( parent, library ) )
150 continue;
151
152 Plugin *plugin = loadPlugin( parent, library, (*pIt).m_document.documentElement().attribute( "X-KDE-PluginKeyword" ) );
153
154 if ( plugin )
155 {
156 plugin->d->m_parentInstance = componentData;
157 plugin->setXMLFile( (*pIt).m_relXMLFileName, false, false );
158 plugin->setDOMDocument( (*pIt).m_document );
159
160 }
161 }
162
163}
164
165void Plugin::loadPlugins( QObject *parent, const QList<PluginInfo> &pluginInfos )
166{
167 loadPlugins(parent, pluginInfos, KComponentData());
168}
169
170// static, deprecated
171#ifndef KDE_NO_DEPRECATED
172Plugin* Plugin::loadPlugin( QObject * parent, const char* libname )
173{
174 Plugin* plugin = KLibLoader::createInstance<Plugin>( libname, parent );
175 if ( !plugin )
176 return 0;
177 plugin->d->m_library = libname;
178 return plugin;
179}
180#endif
181
182// static, deprecated
183#ifndef KDE_NO_DEPRECATED
184Plugin* Plugin::loadPlugin( QObject * parent, const QByteArray &libname )
185{
186 return loadPlugin( parent, libname.data() );
187}
188#endif
189
190Plugin* Plugin::loadPlugin( QObject * parent, const QString &libname )
191{
192 return loadPlugin( parent, libname, "" );
193}
194
195// static
196Plugin* Plugin::loadPlugin( QObject * parent, const QString &libname, const QString &keyword )
197{
198 KPluginLoader loader( libname );
199 KPluginFactory* factory = loader.factory();
200
201 if (!factory) {
202 return 0;
203 }
204
205 Plugin* plugin = factory->create<Plugin>( keyword, parent );
206 if ( !plugin )
207 return 0;
208 plugin->d->m_library = libname;
209 return plugin;
210}
211
212QList<KParts::Plugin *> Plugin::pluginObjects( QObject *parent )
213{
214 QList<KParts::Plugin *> objects;
215
216 if (!parent )
217 return objects;
218
219 // TODO: move to a new method KGlobal::findDirectChildren, if there is more than one use of this?
220 const QObjectList plugins = parent->children();
221
222 QObjectList::ConstIterator it = plugins.begin();
223 for ( ; it != plugins.end() ; ++it )
224 {
225 Plugin * plugin = qobject_cast<Plugin *>( *it );
226 if ( plugin )
227 objects.append( plugin );
228 }
229
230 return objects;
231}
232
233bool Plugin::hasPlugin( QObject* parent, const QString& library )
234{
235 const QObjectList plugins = parent->children();
236
237 QObjectList::ConstIterator it = plugins.begin();
238 for ( ; it != plugins.end() ; ++it )
239 {
240 Plugin * plugin = qobject_cast<Plugin *>( *it );
241 if ( plugin && plugin->d->m_library == library )
242 {
243 return true;
244 }
245 }
246 return false;
247}
248
249void Plugin::setComponentData(const KComponentData &componentData)
250{
251 KGlobal::locale()->insertCatalog(componentData.catalogName());
252 KXMLGUIClient::setComponentData(componentData);
253}
254
255void Plugin::loadPlugins(QObject *parent, KXMLGUIClient* parentGUIClient,
256 const KComponentData &componentData, bool enableNewPluginsByDefault,
257 int interfaceVersionRequired)
258{
259 KConfigGroup cfgGroup( componentData.config(), "KParts Plugins" );
260 const QList<PluginInfo> plugins = pluginInfos( componentData );
261 QList<PluginInfo>::ConstIterator pIt = plugins.begin();
262 const QList<PluginInfo>::ConstIterator pEnd = plugins.end();
263 for (; pIt != pEnd; ++pIt )
264 {
265 QDomElement docElem = (*pIt).m_document.documentElement();
266 QString library = docElem.attribute( "library" );
267 QString keyword;
268
269 if ( library.isEmpty() )
270 continue;
271
272 // Check configuration
273 const QString name = docElem.attribute( "name" );
274
275 bool pluginEnabled = enableNewPluginsByDefault;
276 if ( cfgGroup.hasKey( name + "Enabled" ) )
277 {
278 pluginEnabled = cfgGroup.readEntry( name + "Enabled" , false );
279 }
280 else
281 { // no user-setting, load plugin default setting
282 QString relPath = QString( componentData.componentName() ) + '/' + (*pIt).m_relXMLFileName;
283 relPath.truncate( relPath.lastIndexOf( '.' ) ); // remove extension
284 relPath += ".desktop";
285 //kDebug(1000) << "looking for " << relPath;
286 const QString desktopfile = componentData.dirs()->findResource( "data", relPath );
287 if( !desktopfile.isEmpty() )
288 {
289 //kDebug(1000) << "loadPlugins found desktop file for " << name << ": " << desktopfile;
290 KDesktopFile _desktop( desktopfile );
291 const KConfigGroup desktop = _desktop.desktopGroup();
292 keyword = desktop.readEntry("X-KDE-PluginKeyword", "");
293 pluginEnabled = desktop.readEntry( "X-KDE-PluginInfo-EnabledByDefault",
294 enableNewPluginsByDefault );
295 if ( interfaceVersionRequired != 0 )
296 {
297 const int version = desktop.readEntry( "X-KDE-InterfaceVersion", 1 );
298 if ( version != interfaceVersionRequired )
299 {
300 kDebug(1000) << "Discarding plugin " << name << ", interface version " << version << ", expected " << interfaceVersionRequired;
301 pluginEnabled = false;
302 }
303 }
304 }
305 else
306 {
307 //kDebug(1000) << "loadPlugins no desktop file found in " << relPath;
308 }
309 }
310
311 // search through already present plugins
312 const QObjectList pluginList = parent->children();
313
314 bool pluginFound = false;
315 for ( QObjectList::ConstIterator it = pluginList.begin(); it != pluginList.end() ; ++it )
316 {
317 Plugin * plugin = qobject_cast<Plugin *>( *it );
318 if( plugin && plugin->d->m_library == library )
319 {
320 // delete and unload disabled plugins
321 if( !pluginEnabled )
322 {
323 kDebug( 1000 ) << "remove plugin " << name;
324 KXMLGUIFactory * factory = plugin->factory();
325 if( factory )
326 factory->removeClient( plugin );
327 delete plugin;
328 }
329
330 pluginFound = true;
331 break;
332 }
333 }
334
335 // if the plugin is already loaded or if it's disabled in the
336 // configuration do nothing
337 if( pluginFound || !pluginEnabled )
338 continue;
339
340 kDebug( 1000 ) << "load plugin " << name << " " << library << " " << keyword;
341 Plugin *plugin = loadPlugin( parent, library, keyword );
342
343 if ( plugin )
344 {
345 plugin->d->m_parentInstance = componentData;
346 plugin->setXMLFile( (*pIt).m_relXMLFileName, false, false );
347 plugin->setDOMDocument( (*pIt).m_document );
348 parentGUIClient->insertChildClient( plugin );
349 }
350 }
351}
352
353// vim:sw=4:et:sts=4
354
355#include "plugin.moc"
356