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