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/Containment")) { 00125 return new Containment(0, allArgs); 00126 } else if (offer->serviceTypes().contains("Plasma/PopupApplet")) { 00127 return new PopupApplet(0, allArgs); 00128 } else { 00129 return new Applet(0, allArgs); 00130 } 00131 } 00132 } 00133 00134 KPluginLoader plugin(*offer); 00135 00136 if (!Plasma::isPluginVersionCompatible(plugin.pluginVersion()) && 00137 (name != "internal:extender")) { 00138 return 0; 00139 } 00140 00141 00142 QString error; 00143 if (name == "internal:extender") { 00144 applet = new ExtenderApplet(0, allArgs); 00145 } else { 00146 applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error); 00147 } 00148 00149 if (!applet) { 00150 kDebug() << "Couldn't load applet \"" << name << "\"! reason given: " << error; 00151 } 00152 00153 return applet; 00154 } 00155 00156 DataEngine *PluginLoader::loadDataEngine(const QString &name) 00157 { 00158 DataEngine *engine = internalLoadDataEngine(name); 00159 if (engine) { 00160 return engine; 00161 } 00162 00163 // load the engine, add it to the engines 00164 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name); 00165 KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine", 00166 constraint); 00167 QString error; 00168 00169 if (offers.isEmpty()) { 00170 kDebug() << "offers are empty for " << name << " with constraint " << constraint; 00171 } else { 00172 QVariantList allArgs; 00173 allArgs << offers.first()->storageId(); 00174 QString api = offers.first()->property("X-Plasma-API").toString(); 00175 if (api.isEmpty()) { 00176 if (offers.first()) { 00177 KPluginLoader plugin(*offers.first()); 00178 if (Plasma::isPluginVersionCompatible(plugin.pluginVersion())) { 00179 engine = offers.first()->createInstance<Plasma::DataEngine>(0, allArgs, &error); 00180 } 00181 } 00182 } else { 00183 engine = new DataEngine(0, offers.first()); 00184 } 00185 } 00186 00187 if (!engine) { 00188 kDebug() << "Couldn't load engine \"" << name << "\". Error given: " << error; 00189 } 00190 00191 return engine; 00192 } 00193 00194 AbstractRunner *PluginLoader::loadRunner(const QString &name) 00195 { 00196 // FIXME: RunnerManager is all wrapped around runner loading; that should be sorted out 00197 // and the actual plugin loading added here 00198 return internalLoadRunner(name); 00199 } 00200 00201 Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent) 00202 { 00203 Service *service = internalLoadService(name, args, parent); 00204 if (service) { 00205 return service; 00206 } 00207 00208 //TODO: scripting API support 00209 if (name.isEmpty()) { 00210 return new NullService(QString(), parent); 00211 } else if (name == "org.kde.servicestorage") { 00212 return new Storage(parent); 00213 } 00214 00215 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name); 00216 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Service", constraint); 00217 00218 if (offers.isEmpty()) { 00219 kDebug() << "offers is empty for " << name; 00220 return new NullService(name, parent); 00221 } 00222 00223 KService::Ptr offer = offers.first(); 00224 QString error; 00225 00226 if (Plasma::isPluginVersionCompatible(KPluginLoader(*offer).pluginVersion())) { 00227 service = offer->createInstance<Plasma::Service>(parent, args, &error); 00228 } 00229 00230 if (!service) { 00231 kDebug() << "Couldn't load Service \"" << name << "\"! reason given: " << error; 00232 return new NullService(name, parent); 00233 } 00234 00235 if (service->name().isEmpty()) { 00236 service->setName(name); 00237 } 00238 00239 return service; 00240 } 00241 00242 KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp) 00243 { 00244 KPluginInfo::List list; 00245 00246 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00247 list = internalAppletInfo(category); 00248 } 00249 00250 QString constraint = AppletPrivate::parentAppConstraint(parentApp); 00251 00252 //note: constraint guaranteed non-empty from here down 00253 if (category.isEmpty()) { //use all but the excluded categories 00254 KConfigGroup group(KGlobal::config(), "General"); 00255 QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); 00256 foreach (const QString &category, excluded) { 00257 constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'"); 00258 } 00259 } else { //specific category (this could be an excluded one - is that bad?) 00260 constraint.append(" and ").append("[X-KDE-PluginInfo-Category] == '").append(category).append("'"); 00261 if (category == "Miscellaneous") { 00262 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')"); 00263 } 00264 } 00265 00266 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint); 00267 00268 //now we have to do some manual filtering because the constraint can't handle everything 00269 AppletPrivate::filterOffers(offers); 00270 00271 //kDebug() << "Applet::listAppletInfo constraint was '" << constraint 00272 // << "' which got us " << offers.count() << " matches"; 00273 return KPluginInfo::fromServices(offers); 00274 } 00275 00276 KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp) 00277 { 00278 KPluginInfo::List list; 00279 00280 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00281 list = internalDataEngineInfo(); 00282 } 00283 00284 QString constraint; 00285 if (parentApp.isEmpty()) { 00286 constraint.append("not exist [X-KDE-ParentApp]"); 00287 } else { 00288 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 00289 } 00290 00291 KService::List offers = KServiceTypeTrader::self()->query("Plasma/DataEngine", constraint); 00292 return list + KPluginInfo::fromServices(offers); 00293 } 00294 00295 KPluginInfo::List PluginLoader::listRunnerInfo(const QString &parentApp) 00296 { 00297 KPluginInfo::List list; 00298 00299 if (parentApp.isEmpty() || parentApp == KGlobal::mainComponent().componentName()) { 00300 list = internalRunnerInfo(); 00301 } 00302 00303 QString constraint; 00304 if (parentApp.isEmpty()) { 00305 constraint.append("not exist [X-KDE-ParentApp]"); 00306 } else { 00307 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'"); 00308 } 00309 00310 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner", constraint); 00311 return list + KPluginInfo::fromServices(offers); 00312 } 00313 00314 Applet* PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args) 00315 { 00316 Q_UNUSED(name) 00317 Q_UNUSED(appletId) 00318 Q_UNUSED(args) 00319 return 0; 00320 } 00321 00322 DataEngine* PluginLoader::internalLoadDataEngine(const QString &name) 00323 { 00324 Q_UNUSED(name) 00325 return 0; 00326 } 00327 00328 AbstractRunner* PluginLoader::internalLoadRunner(const QString &name) 00329 { 00330 Q_UNUSED(name) 00331 return 0; 00332 } 00333 00334 Service* PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent) 00335 { 00336 Q_UNUSED(name) 00337 Q_UNUSED(args) 00338 Q_UNUSED(parent) 00339 return 0; 00340 } 00341 00342 KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const 00343 { 00344 Q_UNUSED(category) 00345 return KPluginInfo::List(); 00346 } 00347 00348 KPluginInfo::List PluginLoader::internalDataEngineInfo() const 00349 { 00350 return KPluginInfo::List(); 00351 } 00352 00353 KPluginInfo::List PluginLoader::internalRunnerInfo() const 00354 { 00355 return KPluginInfo::List(); 00356 } 00357 00358 KPluginInfo::List PluginLoader::internalServiceInfo() const 00359 { 00360 return KPluginInfo::List(); 00361 } 00362 00363 static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString()) 00364 { 00365 QStringList files = KGlobal::dirs()->findAllResources("appdata", 00366 "plasma/internal/" + type + "/*.desktop", 00367 KStandardDirs::NoDuplicates); 00368 00369 KPluginInfo::List allInfo = KPluginInfo::fromFiles(files); 00370 00371 if (category.isEmpty() || allInfo.isEmpty()) { 00372 return allInfo; 00373 } 00374 00375 KPluginInfo::List matchingInfo; 00376 foreach (const KPluginInfo &info, allInfo) { 00377 if (info.category().compare(category, Qt::CaseInsensitive) == 0) { 00378 matchingInfo << info; 00379 } 00380 } 00381 00382 return matchingInfo; 00383 } 00384 00385 KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const 00386 { 00387 return standardInternalInfo("applets", category); 00388 } 00389 00390 KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const 00391 { 00392 return standardInternalInfo("dataengines"); 00393 } 00394 00395 KPluginInfo::List PluginLoader::standardInternalRunnerInfo() const 00396 { 00397 return standardInternalInfo("runners"); 00398 } 00399 00400 KPluginInfo::List PluginLoader::standardInternalServiceInfo() const 00401 { 00402 return standardInternalInfo("services"); 00403 } 00404 00405 } // Plasma Namespace 00406
KDE 4.6 API Reference