Plasma
pluginloader.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2010 Ryan Rix <ry@n.rix.si> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "pluginloader.h" 00021 00022 #include <kdebug.h> 00023 #include <kglobal.h> 00024 #include <kservice.h> 00025 #include <kservicetypetrader.h> 00026 #include <kstandarddirs.h> 00027 #include <kplugininfo.h> 00028 00029 #include "applet.h" 00030 #include "abstractrunner.h" 00031 #include "containment.h" 00032 #include "packagestructure.h" 00033 #include "popupapplet.h" 00034 #include "private/applet_p.h" 00035 #include "private/extenderapplet_p.h" 00036 #include "private/service_p.h" // for NullService 00037 #include "private/storage_p.h" 00038 00039 namespace Plasma { 00040 00041 static PluginLoader* s_pluginLoader = 0; 00042 00043 PluginLoader::PluginLoader() 00044 : d(0) 00045 { 00046 } 00047 00048 PluginLoader::~PluginLoader() 00049 { 00050 //delete d; 00051 } 00052 00053 void PluginLoader::setPluginLoader(PluginLoader* loader) 00054 { 00055 if (!s_pluginLoader) { 00056 s_pluginLoader = loader; 00057 } else { 00058 kDebug() << "Cannot set pluginLoader, already set!" << s_pluginLoader; 00059 } 00060 } 00061 00062 PluginLoader *PluginLoader::pluginLoader() 00063 { 00064 if (!s_pluginLoader) { 00065 // we have been called before any PluginLoader was set, so just use the default 00066 // implementation. this prevents plugins from nefariously injecting their own 00067 // plugin loader if the app doesn't 00068 s_pluginLoader = new PluginLoader; 00069 } 00070 00071 return s_pluginLoader; 00072 } 00073 00074 Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args) 00075 { 00076 // the application-specific appletLoader failed to create an applet, here we try with our own logic. 00077 if (name.isEmpty()) { 00078 return 0; 00079 } 00080 00081 Applet *applet = internalLoadApplet(name, appletId, args); 00082 if (applet) { 00083 return applet; 00084 } 00085 00086 const QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name); 00087 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 00088 00089 bool isContainment = false; 00090 if (offers.isEmpty()) { 00091 //TODO: what would be -really- cool is offer to try and download the applet 00092 // from the network at this point 00093 offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint); 00094 if (offers.count() > 0) { 00095 isContainment = true; 00096 } 00097 } 00098 00099 /* if (offers.count() > 1) { 00100 kDebug() << "hey! we got more than one! let's blindly take the first one"; 00101 } */ 00102 00103 AppletPrivate::filterOffers(offers); 00104 if (offers.isEmpty()) { 00105 kDebug() << "offers is empty for " << name; 00106 return 0; 00107 } 00108 00109 KService::Ptr offer = offers.first(); 00110 00111 if (appletId == 0) { 00112 appletId = ++AppletPrivate::s_maxAppletId; 00113 } 00114 00115 QVariantList allArgs; 00116 allArgs << offer->storageId() << appletId << args; 00117 00118 if (!offer->property("X-Plasma-API").toString().isEmpty()) { 00119 kDebug() << "we have a script using the" 00120 << offer->property("X-Plasma-API").toString() << "API"; 00121 if (isContainment) { 00122 return new Containment(0, allArgs); 00123 } else { 00124 if (offer->serviceTypes().contains("Plasma/PopupApplet")) { 00125 return new PopupApplet(0, allArgs); 00126 } else { 00127 return new Applet(0, allArgs); 00128 } 00129 } 00130 } 00131 00132 KPluginLoader plugin(*offer); 00133 00134 if (!Plasma::isPluginVersionCompatible(plugin.pluginVersion()) && 00135 (name != "internal:extender")) { 00136 return 0; 00137 } 00138 00139 00140 QString error; 00141 if (name == "internal:extender") { 00142 applet = new ExtenderApplet(0, allArgs); 00143 } else { 00144 applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error); 00145 } 00146 00147 if (!applet) { 00148 kDebug() << "Couldn't load applet \"" << name << "\"! reason given: " << error; 00149 } 00150 00151 return applet; 00152 } 00153 00154 DataEngine *PluginLoader::loadDataEngine(const QString &name) 00155 { 00156 DataEngine *engine = internalLoadDataEngine(name); 00157 if (engine) { 00158 return engine; 00159 } 00160 00161 // load the engine, add it to the engines 00162 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name); 00163 KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine", 00164 constraint); 00165 QString error; 00166 00167 if (offers.isEmpty()) { 00168 kDebug() << "offers are empty for " << name << " with constraint " << constraint; 00169 } else { 00170 QVariantList allArgs; 00171 allArgs << offers.first()->storageId(); 00172 QString api = offers.first()->property("X-Plasma-API").toString(); 00173 if (api.isEmpty()) { 00174 if (offers.first()) { 00175 KPluginLoader plugin(*offers.first()); 00176 if (Plasma::isPluginVersionCompatible(plugin.pluginVersion())) { 00177 engine = offers.first()->createInstance<Plasma::DataEngine>(0, allArgs, &error); 00178 } 00179 } 00180 } else { 00181 engine = new DataEngine(0, offers.first()); 00182 } 00183 } 00184 00185 if (!engine) { 00186 kDebug() << "Couldn't load engine \"" << name << "\". Error given: " << error; 00187 } 00188 00189 return engine; 00190 } 00191 00192 AbstractRunner *PluginLoader::loadRunner(const QString &name) 00193 { 00194 // FIXME: RunnerManager is all wrapped around runner loading; that should be sorted out 00195 // and the actual plugin loading added here 00196 return internalLoadRunner(name); 00197 } 00198 00199 Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent) 00200 { 00201 Service *service = internalLoadService(name, args, parent); 00202 if (service) { 00203 return service; 00204 } 00205 00206 //TODO: scripting API support 00207 if (name.isEmpty()) { 00208 return new NullService(QString(), parent); 00209 } else if (name == "org.kde.servicestorage") { 00210 return new Storage(parent); 00211 } 00212 00213 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name); 00214 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Service", constraint); 00215 00216 if (offers.isEmpty()) { 00217 kDebug() << "offers is empty for " << name; 00218 return new NullService(name, parent); 00219 } 00220 00221 KService::Ptr offer = offers.first(); 00222 QString error; 00223 00224 if (Plasma::isPluginVersionCompatible(KPluginLoader(*offer).pluginVersion())) { 00225 service = offer->createInstance<Plasma::Service>(parent, args, &error); 00226 } 00227 00228 if (!service) { 00229 kDebug() << "Couldn't load Service \"" << name << "\"! reason given: " << error; 00230 return new NullService(name, parent); 00231 } 00232 00233 if (service->name().isEmpty()) { 00234 service->setName(name); 00235 } 00236 00237 return service; 00238 } 00239 00240 KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp) 00241 { 00242 KPluginInfo::List list; 00243 00244 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00245 list = internalAppletInfo(category); 00246 } 00247 00248 QString constraint = AppletPrivate::parentAppConstraint(parentApp); 00249 00250 //note: constraint guaranteed non-empty from here down 00251 if (category.isEmpty()) { //use all but the excluded categories 00252 KConfigGroup group(KGlobal::config(), "General"); 00253 QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); 00254 foreach (const QString &category, excluded) { 00255 constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'"); 00256 } 00257 } else { //specific category (this could be an excluded one - is that bad?) 00258 constraint.append(" and ").append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); 00259 if (category == "Miscellaneous") { 00260 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); 00261 } 00262 } 00263 00264 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 00265 00266 //now we have to do some manual filtering because the constraint can't handle everything 00267 AppletPrivate::filterOffers(offers); 00268 00269 //kDebug() << "Applet::listAppletInfo constraint was '" << constraint 00270 // << "' which got us " << offers.count() << " matches"; 00271 return KPluginInfo::fromServices(offers); 00272 } 00273 00274 KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp) 00275 { 00276 KPluginInfo::List list; 00277 00278 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00279 list = internalDataEngineInfo(); 00280 } 00281 00282 QString constraint; 00283 if (parentApp.isEmpty()) { 00284 constraint.append("not exist [X-KDE-ParentApp]"); 00285 } else { 00286 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 00287 } 00288 00289 KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine", constraint); 00290 return list + KPluginInfo::fromServices(offers); 00291 } 00292 00293 KPluginInfo::List PluginLoader::listRunnerInfo(const QString &parentApp) 00294 { 00295 KPluginInfo::List list; 00296 00297 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00298 list = internalRunnerInfo(); 00299 } 00300 00301 QString constraint; 00302 if (parentApp.isEmpty()) { 00303 constraint.append("not exist [X-KDE-ParentApp]"); 00304 } else { 00305 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 00306 } 00307 00308 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner", constraint); 00309 return list + KPluginInfo::fromServices(offers); 00310 } 00311 00312 Applet* PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args) 00313 { 00314 Q_UNUSED(name) 00315 Q_UNUSED(appletId) 00316 Q_UNUSED(args) 00317 return 0; 00318 } 00319 00320 DataEngine* PluginLoader::internalLoadDataEngine(const QString &name) 00321 { 00322 Q_UNUSED(name) 00323 return 0; 00324 } 00325 00326 AbstractRunner* PluginLoader::internalLoadRunner(const QString &name) 00327 { 00328 Q_UNUSED(name) 00329 return 0; 00330 } 00331 00332 Service* PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent) 00333 { 00334 Q_UNUSED(name) 00335 Q_UNUSED(args) 00336 Q_UNUSED(parent) 00337 return 0; 00338 } 00339 00340 KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const 00341 { 00342 Q_UNUSED(category) 00343 return KPluginInfo::List(); 00344 } 00345 00346 KPluginInfo::List PluginLoader::internalDataEngineInfo() const 00347 { 00348 return KPluginInfo::List(); 00349 } 00350 00351 KPluginInfo::List PluginLoader::internalRunnerInfo() const 00352 { 00353 return KPluginInfo::List(); 00354 } 00355 00356 KPluginInfo::List PluginLoader::internalServiceInfo() const 00357 { 00358 return KPluginInfo::List(); 00359 } 00360 00361 static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString()) 00362 { 00363 QStringList files = KGlobal::dirs()->findAllResources("appdata", 00364 "plasma/internal/" + type + "/*.desktop", 00365 KStandardDirs::NoDuplicates); 00366 00367 KPluginInfo::List allInfo = KPluginInfo::fromFiles(files); 00368 00369 if (category.isEmpty() || allInfo.isEmpty()) { 00370 return allInfo; 00371 } 00372 00373 KPluginInfo::List matchingInfo; 00374 foreach (const KPluginInfo &info, allInfo) { 00375 if (info.category().compare(category, Qt::CaseInsensitive) == 0) { 00376 matchingInfo << info; 00377 } 00378 } 00379 00380 return matchingInfo; 00381 } 00382 00383 KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const 00384 { 00385 return standardInternalInfo("applets", category); 00386 } 00387 00388 KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const 00389 { 00390 return standardInternalInfo("dataengines"); 00391 } 00392 00393 KPluginInfo::List PluginLoader::standardInternalRunnerInfo() const 00394 { 00395 return standardInternalInfo("runners"); 00396 } 00397 00398 KPluginInfo::List PluginLoader::standardInternalServiceInfo() const 00399 { 00400 return standardInternalInfo("services"); 00401 } 00402 00403 } // Plasma Namespace 00404
KDE 4.6 API Reference