KDECore
kservice.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 * Copyright (C) 1999 - 2001 Waldo Bastian <bastian@kde.org> 00003 * Copyright (C) 1999 - 2005 David Faure <faure@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License version 2 as published by the Free Software Foundation; 00008 * 00009 * This library 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 GNU 00012 * Library General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU Library General Public License 00015 * along with this library; see the file COPYING.LIB. If not, write to 00016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 * Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kservice.h" 00021 #include "kservice_p.h" 00022 #include "kmimetypefactory.h" 00023 #include "kmimetyperepository_p.h" 00024 00025 #include <sys/types.h> 00026 #include <sys/stat.h> 00027 00028 #include <stddef.h> 00029 #include <unistd.h> 00030 #include <stdlib.h> 00031 00032 #include <QtCore/QCharRef> 00033 #include <QtCore/QFile> 00034 #include <QtCore/QDir> 00035 #include <QtCore/QMap> 00036 00037 #include <kauthorized.h> 00038 #include <kdebug.h> 00039 #include <kdesktopfile.h> 00040 #include <kglobal.h> 00041 #include <kconfiggroup.h> 00042 #include <kstandarddirs.h> 00043 00044 #include "kservicefactory.h" 00045 #include "kservicetypefactory.h" 00046 00047 int servicesDebugArea() { 00048 static int s_area = KDebug::registerArea("kdecore (services)"); 00049 return s_area; 00050 } 00051 00052 QDataStream &operator<<(QDataStream &s, const KService::ServiceTypeAndPreference &st) 00053 { 00054 s << st.preference << st.serviceType; 00055 return s; 00056 } 00057 QDataStream &operator>>(QDataStream &s, KService::ServiceTypeAndPreference &st) 00058 { 00059 s >> st.preference >> st.serviceType; 00060 return s; 00061 } 00062 00063 void KServicePrivate::init( const KDesktopFile *config, KService* q ) 00064 { 00065 const QString entryPath = q->entryPath(); 00066 bool absPath = !QDir::isRelativePath(entryPath); 00067 00068 // TODO: it makes sense to have a KConstConfigGroup I guess 00069 const KConfigGroup desktopGroup = const_cast<KDesktopFile*>(config)->desktopGroup(); 00070 QMap<QString, QString> entryMap = desktopGroup.entryMap(); 00071 00072 entryMap.remove(QLatin1String("Encoding")); // reserved as part of Desktop Entry Standard 00073 entryMap.remove(QLatin1String("Version")); // reserved as part of Desktop Entry Standard 00074 00075 q->setDeleted( desktopGroup.readEntry("Hidden", false) ); 00076 entryMap.remove(QLatin1String("Hidden")); 00077 if ( q->isDeleted() ) { 00078 m_bValid = false; 00079 return; 00080 } 00081 00082 m_strName = config->readName(); 00083 entryMap.remove(QLatin1String("Name")); 00084 if ( m_strName.isEmpty() ) 00085 { 00086 // Try to make up a name. 00087 m_strName = entryPath; 00088 int i = m_strName.lastIndexOf(QLatin1Char('/')); 00089 m_strName = m_strName.mid(i+1); 00090 i = m_strName.lastIndexOf(QLatin1Char('.')); 00091 if (i != -1) 00092 m_strName = m_strName.left(i); 00093 } 00094 00095 m_strType = config->readType(); 00096 entryMap.remove(QLatin1String("Type")); 00097 if ( m_strType.isEmpty() ) 00098 { 00099 /*kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath 00100 << " has no Type=... entry." 00101 << " It should be \"Application\" or \"Service\"" << endl; 00102 m_bValid = false; 00103 return;*/ 00104 m_strType = QString::fromLatin1("Application"); 00105 } else if (m_strType != QLatin1String("Application") && m_strType != QLatin1String("Service")) { 00106 kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath 00107 << " has Type=" << m_strType 00108 << " instead of \"Application\" or \"Service\"" << endl; 00109 m_bValid = false; 00110 return; 00111 } 00112 00113 // NOT readPathEntry, it is not XDG-compliant. Path entries written by 00114 // KDE4 will be still treated as such, though. 00115 m_strExec = desktopGroup.readEntry( "Exec", QString() ); 00116 entryMap.remove(QLatin1String("Exec")); 00117 00118 if (m_strType == QLatin1String("Application")) { 00119 // It's an application? Should have an Exec line then, otherwise we can't run it 00120 if (m_strExec.isEmpty()) { 00121 kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath 00122 << " has Type=" << m_strType 00123 << " but no Exec line" << endl; 00124 m_bValid = false; 00125 return; 00126 } 00127 } 00128 00129 // In case Try Exec is set, check if the application is available 00130 if (!config->tryExec()) { 00131 q->setDeleted( true ); 00132 m_bValid = false; 00133 return; 00134 } 00135 00136 const QByteArray resource = config->resource(); 00137 00138 if ( (m_strType == QLatin1String("Application")) && 00139 (!resource.isEmpty()) && 00140 (resource != "apps") && 00141 !absPath) 00142 { 00143 kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath 00144 << " has Type=" << m_strType << " but is located under \"" << resource 00145 << "\" instead of \"apps\"" << endl; 00146 m_bValid = false; 00147 return; 00148 } 00149 00150 if ( (m_strType == QLatin1String("Service")) && 00151 (!resource.isEmpty()) && 00152 (resource != "services") && 00153 !absPath) 00154 { 00155 kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath 00156 << " has Type=" << m_strType << " but is located under \"" << resource 00157 << "\" instead of \"services\""; 00158 m_bValid = false; 00159 return; 00160 } 00161 00162 QString _name = entryPath; 00163 int pos = _name.lastIndexOf(QLatin1Char('/')); 00164 if (pos != -1) 00165 _name = _name.mid(pos+1); 00166 pos = _name.indexOf(QLatin1Char('.')); 00167 if (pos != -1) 00168 _name = _name.left(pos); 00169 00170 m_strIcon = config->readIcon(); 00171 entryMap.remove(QLatin1String("Icon")); 00172 m_bTerminal = desktopGroup.readEntry( "Terminal", false); // should be a property IMHO 00173 entryMap.remove(QLatin1String("Terminal")); 00174 m_strTerminalOptions = desktopGroup.readEntry( "TerminalOptions" ); // should be a property IMHO 00175 entryMap.remove(QLatin1String("TerminalOptions")); 00176 m_strPath = config->readPath(); 00177 entryMap.remove(QLatin1String("Path")); 00178 m_strComment = config->readComment(); 00179 entryMap.remove(QLatin1String("Comment")); 00180 m_strGenName = config->readGenericName(); 00181 entryMap.remove(QLatin1String("GenericName")); 00182 QString _untranslatedGenericName = desktopGroup.readEntryUntranslated( "GenericName" ); 00183 if (!_untranslatedGenericName.isEmpty()) 00184 entryMap.insert(QLatin1String("UntranslatedGenericName"), _untranslatedGenericName); 00185 00186 m_lstKeywords = desktopGroup.readEntry("Keywords", QStringList()); 00187 entryMap.remove(QLatin1String("Keywords")); 00188 m_lstKeywords += desktopGroup.readEntry("X-KDE-Keywords", QStringList()); 00189 entryMap.remove(QLatin1String("X-KDE-Keywords")); 00190 categories = desktopGroup.readXdgListEntry("Categories"); 00191 entryMap.remove(QLatin1String("Categories")); 00192 // TODO KDE5: only care for X-KDE-Library in Type=Service desktop files 00193 // This will prevent people defining a part and an app in the same desktop file 00194 // which makes user-preference handling difficult. 00195 m_strLibrary = desktopGroup.readEntry( "X-KDE-Library" ); 00196 entryMap.remove(QLatin1String("X-KDE-Library")); 00197 if (!m_strLibrary.isEmpty() && m_strType == QLatin1String("Application")) { 00198 kWarning(servicesDebugArea()) << "The desktop entry file" << entryPath 00199 << "has Type=" << m_strType 00200 << "but also has a X-KDE-Library key. This works for now," 00201 " but makes user-preference handling difficult, so support for this might" 00202 " be removed at some point. Consider splitting it into two desktop files."; 00203 } 00204 00205 QStringList lstServiceTypes = desktopGroup.readEntry( "ServiceTypes", QStringList() ); 00206 entryMap.remove(QLatin1String("ServiceTypes")); 00207 lstServiceTypes += desktopGroup.readEntry( "X-KDE-ServiceTypes", QStringList() ); 00208 entryMap.remove(QLatin1String("X-KDE-ServiceTypes")); 00209 lstServiceTypes += desktopGroup.readXdgListEntry( "MimeType" ); 00210 entryMap.remove(QLatin1String("MimeType")); 00211 00212 if ( m_strType == QLatin1String("Application") && !lstServiceTypes.contains(QLatin1String("Application")) ) 00213 // Applications implement the service type "Application" ;-) 00214 lstServiceTypes += QString::fromLatin1("Application"); 00215 00216 m_initialPreference = desktopGroup.readEntry( "InitialPreference", 1 ); 00217 entryMap.remove(QLatin1String("InitialPreference")); 00218 00219 // Assign the "initial preference" to each mimetype/servicetype 00220 // (and to set such preferences in memory from kbuildsycoca) 00221 m_serviceTypes.reserve(lstServiceTypes.size()); 00222 QListIterator<QString> st_it(lstServiceTypes); 00223 while ( st_it.hasNext() ) { 00224 const QString st = st_it.next(); 00225 if (st.isEmpty()) { 00226 kWarning(servicesDebugArea()) << "The desktop entry file" << entryPath 00227 << "has an empty mimetype!"; 00228 continue; 00229 } 00230 int initialPreference = m_initialPreference; 00231 if ( st_it.hasNext() ) { 00232 // TODO better syntax - separate group with mimetype=number entries? 00233 bool isNumber; 00234 const int val = st_it.peekNext().toInt(&isNumber); 00235 if (isNumber) { 00236 initialPreference = val; 00237 st_it.next(); 00238 } 00239 } 00240 m_serviceTypes.push_back(KService::ServiceTypeAndPreference(initialPreference, st)); 00241 } 00242 00243 if (entryMap.contains(QLatin1String("Actions"))) { 00244 parseActions(config, q); 00245 } 00246 00247 QString dbusStartupType = desktopGroup.readEntry("X-DBUS-StartupType").toLower(); 00248 entryMap.remove(QLatin1String("X-DBUS-StartupType")); 00249 if (dbusStartupType == QLatin1String("unique")) 00250 m_DBUSStartusType = KService::DBusUnique; 00251 else if (dbusStartupType == QLatin1String("multi")) 00252 m_DBUSStartusType = KService::DBusMulti; 00253 else if (dbusStartupType == QLatin1String("wait")) 00254 m_DBUSStartusType = KService::DBusWait; 00255 else 00256 m_DBUSStartusType = KService::DBusNone; 00257 00258 m_strDesktopEntryName = _name.toLower(); 00259 00260 m_bAllowAsDefault = desktopGroup.readEntry("AllowDefault", true); 00261 entryMap.remove(QLatin1String("AllowDefault")); 00262 00263 // allow plugin users to translate categories without needing a separate key 00264 QMap<QString,QString>::Iterator entry = entryMap.find(QString::fromLatin1("X-KDE-PluginInfo-Category")); 00265 if (entry != entryMap.end()) { 00266 const QString& key = entry.key(); 00267 m_mapProps.insert(key, QVariant(desktopGroup.readEntryUntranslated(key))); 00268 m_mapProps.insert(key + QLatin1String("-Translated"), QVariant(*entry)); 00269 entryMap.erase(entry); 00270 } 00271 00272 // Store all additional entries in the property map. 00273 // A QMap<QString,QString> would be easier for this but we can't 00274 // break BC, so we have to store it in m_mapProps. 00275 // qDebug("Path = %s", entryPath.toLatin1().constData()); 00276 QMap<QString,QString>::ConstIterator it = entryMap.constBegin(); 00277 for( ; it != entryMap.constEnd();++it) { 00278 const QString key = it.key(); 00279 // do not store other translations like Name[fr]; kbuildsycoca will rerun if we change languages anyway 00280 if (!key.contains(QLatin1Char('['))) { 00281 //kDebug(servicesDebugArea()) << " Key =" << key << " Data =" << *it; 00282 m_mapProps.insert(key, QVariant(*it)); 00283 } 00284 } 00285 } 00286 00287 void KServicePrivate::parseActions(const KDesktopFile *config, KService* q) 00288 { 00289 const QStringList keys = config->readActions(); 00290 if (keys.isEmpty()) 00291 return; 00292 00293 QStringList::ConstIterator it = keys.begin(); 00294 const QStringList::ConstIterator end = keys.end(); 00295 for ( ; it != end; ++it ) { 00296 const QString group = *it; 00297 if (group == QLatin1String("_SEPARATOR_")) { 00298 m_actions.append(KServiceAction(group, QString(), QString(), QString(), false)); 00299 continue; 00300 } 00301 00302 if (config->hasActionGroup(group)) { 00303 const KConfigGroup cg = config->actionGroup(group); 00304 if ( !cg.hasKey( "Name" ) || !cg.hasKey( "Exec" ) ) { 00305 kWarning(servicesDebugArea()) << "The action" << group << "in the desktop file" << q->entryPath() 00306 << "has no Name or no Exec key"; 00307 } else { 00308 m_actions.append(KServiceAction(group, 00309 cg.readEntry("Name"), 00310 cg.readEntry("Icon"), 00311 cg.readEntry("Exec"), 00312 cg.readEntry("NoDisplay", false))); 00313 } 00314 } else { 00315 kWarning(servicesDebugArea()) << "The desktop file" << q->entryPath() 00316 << "references the action" << group << "but doesn't define it"; 00317 } 00318 } 00319 } 00320 00321 void KServicePrivate::load(QDataStream& s) 00322 { 00323 qint8 def, term; 00324 qint8 dst, initpref; 00325 QStringList dummyList; // KDE4: you can reuse this for another QStringList. KDE5: remove 00326 00327 // WARNING: THIS NEEDS TO REMAIN COMPATIBLE WITH PREVIOUS KDE 4.x VERSIONS! 00328 // !! This data structure should remain binary compatible at all times !! 00329 // You may add new fields at the end. Make sure to update the version 00330 // number in ksycoca.h 00331 s >> m_strType >> m_strName >> m_strExec >> m_strIcon 00332 >> term >> m_strTerminalOptions 00333 >> m_strPath >> m_strComment >> dummyList >> def >> m_mapProps 00334 >> m_strLibrary 00335 >> dst 00336 >> m_strDesktopEntryName 00337 >> initpref 00338 >> m_lstKeywords >> m_strGenName 00339 >> categories >> menuId >> m_actions >> m_serviceTypes; 00340 00341 m_bAllowAsDefault = (bool)def; 00342 m_bTerminal = (bool)term; 00343 m_DBUSStartusType = (KService::DBusStartupType) dst; 00344 m_initialPreference = initpref; 00345 00346 m_bValid = true; 00347 } 00348 00349 void KServicePrivate::save(QDataStream& s) 00350 { 00351 KSycocaEntryPrivate::save( s ); 00352 qint8 def = m_bAllowAsDefault, initpref = m_initialPreference; 00353 qint8 term = m_bTerminal; 00354 qint8 dst = (qint8) m_DBUSStartusType; 00355 00356 // WARNING: THIS NEEDS TO REMAIN COMPATIBLE WITH PREVIOUS KDE 4.x VERSIONS! 00357 // !! This data structure should remain binary compatible at all times !! 00358 // You may add new fields at the end. Make sure to update the version 00359 // number in ksycoca.h 00360 s << m_strType << m_strName << m_strExec << m_strIcon 00361 << term << m_strTerminalOptions 00362 << m_strPath << m_strComment << QStringList() << def << m_mapProps 00363 << m_strLibrary 00364 << dst 00365 << m_strDesktopEntryName 00366 << initpref 00367 << m_lstKeywords << m_strGenName 00368 << categories << menuId << m_actions << m_serviceTypes; 00369 } 00370 00372 00373 KService::KService( const QString & _name, const QString &_exec, const QString &_icon) 00374 : KSycocaEntry(*new KServicePrivate(QString())) 00375 { 00376 Q_D(KService); 00377 d->m_strType = QString::fromLatin1("Application"); 00378 d->m_strName = _name; 00379 d->m_strExec = _exec; 00380 d->m_strIcon = _icon; 00381 d->m_bTerminal = false; 00382 d->m_bAllowAsDefault = true; 00383 d->m_initialPreference = 10; 00384 } 00385 00386 00387 KService::KService( const QString & _fullpath ) 00388 : KSycocaEntry(*new KServicePrivate(_fullpath)) 00389 { 00390 Q_D(KService); 00391 00392 KDesktopFile config( _fullpath ); 00393 d->init(&config, this); 00394 } 00395 00396 KService::KService( const KDesktopFile *config ) 00397 : KSycocaEntry(*new KServicePrivate(config->fileName())) 00398 { 00399 Q_D(KService); 00400 00401 d->init(config, this); 00402 } 00403 00404 KService::KService( QDataStream& _str, int _offset ) 00405 : KSycocaEntry(*new KServicePrivate(_str, _offset)) 00406 { 00407 } 00408 00409 KService::~KService() 00410 { 00411 } 00412 00413 bool KService::hasServiceType( const QString& serviceType ) const 00414 { 00415 Q_D(const KService); 00416 00417 if (!d->m_bValid) return false; // (useless) safety test 00418 const KServiceType::Ptr ptr = KServiceType::serviceType( serviceType ); 00419 const int serviceOffset = offset(); 00420 // doesn't seem to work: 00421 //if ( serviceOffset == 0 ) 00422 // serviceOffset = serviceByStorageId( storageId() ); 00423 if ( serviceOffset ) 00424 return KServiceFactory::self()->hasOffer( ptr->offset(), ptr->serviceOffersOffset(), serviceOffset ); 00425 00426 // fall-back code for services that are NOT from ksycoca 00427 // For each service type we are associated with, if it doesn't 00428 // match then we try its parent service types. 00429 QVector<ServiceTypeAndPreference>::ConstIterator it = d->m_serviceTypes.begin(); 00430 for( ; it != d->m_serviceTypes.end(); ++it ) { 00431 const QString& st = (*it).serviceType; 00432 //kDebug(servicesDebugArea()) << " has " << (*it); 00433 if ( st == ptr->name() ) 00434 return true; 00435 // also the case of parent servicetypes 00436 KServiceType::Ptr p = KServiceType::serviceType( st ); 00437 if ( p && p->inherits( ptr->name() ) ) 00438 return true; 00439 } 00440 return false; 00441 } 00442 00443 #ifndef KDE_NO_DEPRECATED 00444 bool KService::hasMimeType( const KServiceType* ptr ) const 00445 { 00446 if (!ptr) return false; 00447 00448 return hasMimeType(ptr->name()); 00449 } 00450 #endif 00451 00452 bool KService::hasMimeType(const QString& mimeType) const 00453 { 00454 Q_D(const KService); 00455 const QString mime = KMimeTypeRepository::self()->canonicalName(mimeType); 00456 int serviceOffset = offset(); 00457 if ( serviceOffset ) { 00458 KMimeTypeFactory *factory = KMimeTypeFactory::self(); 00459 const int mimeOffset = factory->entryOffset(mime); 00460 const int serviceOffersOffset = factory->serviceOffersOffset(mime); 00461 if (serviceOffersOffset == -1) 00462 return false; 00463 return KServiceFactory::self()->hasOffer(mimeOffset, serviceOffersOffset, serviceOffset); 00464 } 00465 00466 // fall-back code for services that are NOT from ksycoca 00467 QVector<ServiceTypeAndPreference>::ConstIterator it = d->m_serviceTypes.begin(); 00468 for( ; it != d->m_serviceTypes.end(); ++it ) { 00469 const QString& st = (*it).serviceType; 00470 //kDebug(servicesDebugArea()) << " has " << (*it); 00471 if ( st == mime ) 00472 return true; 00473 // TODO: should we handle inherited mimetypes here? 00474 // KMimeType was in kio when this code was written, this is the only reason it's not done. 00475 // But this should matter only in a very rare case, since most code gets KServices from ksycoca. 00476 // Warning, change hasServiceType if you implement this here (and check kbuildservicefactory). 00477 } 00478 return false; 00479 } 00480 00481 QVariant KServicePrivate::property( const QString& _name) const 00482 { 00483 return property( _name, QVariant::Invalid); 00484 } 00485 00486 // Return a string QVariant if string isn't null, and invalid variant otherwise 00487 // (the variant must be invalid if the field isn't in the .desktop file) 00488 // This allows trader queries like "exist Library" to work. 00489 static QVariant makeStringVariant( const QString& string ) 00490 { 00491 // Using isEmpty here would be wrong. 00492 // Empty is "specified but empty", null is "not specified" (in the .desktop file) 00493 return string.isNull() ? QVariant() : QVariant( string ); 00494 } 00495 00496 QVariant KService::property( const QString& _name, QVariant::Type t ) const 00497 { 00498 Q_D(const KService); 00499 return d->property(_name, t); 00500 } 00501 00502 QVariant KServicePrivate::property( const QString& _name, QVariant::Type t ) const 00503 { 00504 if ( _name == QLatin1String("Type") ) 00505 return QVariant( m_strType ); // can't be null 00506 else if ( _name == QLatin1String("Name") ) 00507 return QVariant( m_strName ); // can't be null 00508 else if ( _name == QLatin1String("Exec") ) 00509 return makeStringVariant( m_strExec ); 00510 else if ( _name == QLatin1String("Icon") ) 00511 return makeStringVariant( m_strIcon ); 00512 else if ( _name == QLatin1String("Terminal") ) 00513 return QVariant( m_bTerminal ); 00514 else if ( _name == QLatin1String("TerminalOptions") ) 00515 return makeStringVariant( m_strTerminalOptions ); 00516 else if ( _name == QLatin1String("Path") ) 00517 return makeStringVariant( m_strPath ); 00518 else if ( _name == QLatin1String("Comment") ) 00519 return makeStringVariant( m_strComment ); 00520 else if ( _name == QLatin1String("GenericName") ) 00521 return makeStringVariant( m_strGenName ); 00522 else if ( _name == QLatin1String("ServiceTypes") ) 00523 return QVariant( serviceTypes() ); 00524 else if ( _name == QLatin1String("AllowAsDefault") ) 00525 return QVariant( m_bAllowAsDefault ); 00526 else if ( _name == QLatin1String("InitialPreference") ) 00527 return QVariant( m_initialPreference ); 00528 else if ( _name == QLatin1String("Library") ) 00529 return makeStringVariant( m_strLibrary ); 00530 else if ( _name == QLatin1String("DesktopEntryPath") ) // can't be null 00531 return QVariant( path ); 00532 else if ( _name == QLatin1String("DesktopEntryName")) 00533 return QVariant( m_strDesktopEntryName ); // can't be null 00534 else if ( _name == QLatin1String("Categories")) 00535 return QVariant( categories ); 00536 else if ( _name == QLatin1String("Keywords")) 00537 return QVariant( m_lstKeywords ); 00538 00539 // Ok we need to convert the property from a QString to its real type. 00540 // Maybe the caller helped us. 00541 if (t == QVariant::Invalid) 00542 { 00543 // No luck, let's ask KServiceTypeFactory what the type of this property 00544 // is supposed to be. 00545 t = KServiceTypeFactory::self()->findPropertyTypeByName(_name); 00546 if (t == QVariant::Invalid) 00547 { 00548 kDebug(servicesDebugArea()) << "Request for unknown property '" << _name << "'\n"; 00549 return QVariant(); // Unknown property: Invalid variant. 00550 } 00551 } 00552 00553 QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( _name ); 00554 if ( (it == m_mapProps.end()) || (!it->isValid())) 00555 { 00556 //kDebug(servicesDebugArea()) << "Property not found " << _name; 00557 return QVariant(); // No property set. 00558 } 00559 00560 switch(t) 00561 { 00562 case QVariant::String: 00563 return *it; // no conversion necessary 00564 default: 00565 // All others 00566 // For instance properties defined as StringList, like MimeTypes. 00567 // XXX This API is accessible only through a friend declaration. 00568 return KConfigGroup::convertToQVariant(_name.toUtf8().constData(), it->toString().toUtf8(), t); 00569 } 00570 } 00571 00572 QStringList KServicePrivate::propertyNames() const 00573 { 00574 QStringList res; 00575 00576 QMap<QString,QVariant>::ConstIterator it = m_mapProps.begin(); 00577 for( ; it != m_mapProps.end(); ++it ) 00578 res.append( it.key() ); 00579 00580 res.append( QString::fromLatin1("Type") ); 00581 res.append( QString::fromLatin1("Name") ); 00582 res.append( QString::fromLatin1("Comment") ); 00583 res.append( QString::fromLatin1("GenericName") ); 00584 res.append( QString::fromLatin1("Icon") ); 00585 res.append( QString::fromLatin1("Exec") ); 00586 res.append( QString::fromLatin1("Terminal") ); 00587 res.append( QString::fromLatin1("TerminalOptions") ); 00588 res.append( QString::fromLatin1("Path") ); 00589 res.append( QString::fromLatin1("ServiceTypes") ); 00590 res.append( QString::fromLatin1("AllowAsDefault") ); 00591 res.append( QString::fromLatin1("InitialPreference") ); 00592 res.append( QString::fromLatin1("Library") ); 00593 res.append( QString::fromLatin1("DesktopEntryPath") ); 00594 res.append( QString::fromLatin1("DesktopEntryName") ); 00595 res.append( QString::fromLatin1("Keywords") ); 00596 res.append( QString::fromLatin1("Categories") ); 00597 00598 return res; 00599 } 00600 00601 KService::List KService::allServices() 00602 { 00603 return KServiceFactory::self()->allServices(); 00604 } 00605 00606 #ifndef KDE_NO_DEPRECATED 00607 KService::Ptr KService::serviceByName( const QString& _name ) 00608 { 00609 return KServiceFactory::self()->findServiceByName( _name ); 00610 } 00611 #endif 00612 00613 KService::Ptr KService::serviceByDesktopPath( const QString& _name ) 00614 { 00615 return KServiceFactory::self()->findServiceByDesktopPath( _name ); 00616 } 00617 00618 KService::Ptr KService::serviceByDesktopName( const QString& _name ) 00619 { 00620 // Prefer kde4-konsole over kde-konsole, if both are available 00621 QString name = _name.toLower(); 00622 KService::Ptr s; 00623 if (!_name.startsWith(QLatin1String("kde4-"))) 00624 s = KServiceFactory::self()->findServiceByDesktopName(QString::fromLatin1("kde4-") + name); 00625 if (!s) 00626 s = KServiceFactory::self()->findServiceByDesktopName( name ); 00627 00628 return s; 00629 } 00630 00631 KService::Ptr KService::serviceByMenuId( const QString& _name ) 00632 { 00633 return KServiceFactory::self()->findServiceByMenuId( _name ); 00634 } 00635 00636 KService::Ptr KService::serviceByStorageId( const QString& _storageId ) 00637 { 00638 KService::Ptr service = KService::serviceByMenuId( _storageId ); 00639 if (service) 00640 return service; 00641 00642 service = KService::serviceByDesktopPath(_storageId); 00643 if (service) 00644 return service; 00645 00646 if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId)) 00647 return KService::Ptr(new KService(_storageId)); 00648 00649 QString tmp = _storageId; 00650 tmp = tmp.mid(tmp.lastIndexOf(QLatin1Char('/'))+1); // Strip dir 00651 00652 if (tmp.endsWith(QLatin1String(".desktop"))) 00653 tmp.truncate(tmp.length()-8); 00654 00655 if (tmp.endsWith(QLatin1String(".kdelnk"))) 00656 tmp.truncate(tmp.length()-7); 00657 00658 service = KService::serviceByDesktopName(tmp); 00659 00660 return service; 00661 } 00662 00663 bool KService::substituteUid() const { 00664 QVariant v = property(QLatin1String("X-KDE-SubstituteUID"), QVariant::Bool); 00665 return v.isValid() && v.toBool(); 00666 } 00667 00668 QString KService::username() const { 00669 // See also KDesktopFile::tryExec() 00670 QString user; 00671 QVariant v = property(QLatin1String("X-KDE-Username"), QVariant::String); 00672 user = v.isValid() ? v.toString() : QString(); 00673 if (user.isEmpty()) 00674 user = QString::fromLocal8Bit(qgetenv("ADMIN_ACCOUNT")); 00675 if (user.isEmpty()) 00676 user = QString::fromLatin1("root"); 00677 return user; 00678 } 00679 00680 bool KService::showInKDE() const 00681 { 00682 Q_D(const KService); 00683 00684 QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find( QString::fromLatin1("OnlyShowIn") ); 00685 if ( (it != d->m_mapProps.end()) && (it->isValid())) 00686 { 00687 const QStringList aList = it->toString().split(QLatin1Char(';')); 00688 if (!aList.contains(QString::fromLatin1("KDE"))) 00689 return false; 00690 } 00691 00692 it = d->m_mapProps.find( QString::fromLatin1("NotShowIn") ); 00693 if ( (it != d->m_mapProps.end()) && (it->isValid())) 00694 { 00695 const QStringList aList = it->toString().split(QLatin1Char(';')); 00696 if (aList.contains(QString::fromLatin1("KDE"))) 00697 return false; 00698 } 00699 return true; 00700 } 00701 00702 bool KService::noDisplay() const { 00703 if ( qvariant_cast<bool>(property(QString::fromLatin1("NoDisplay"), QVariant::Bool)) ) 00704 return true; 00705 00706 if (!showInKDE()) 00707 return true; 00708 00709 if (!KAuthorized::authorizeControlModule( storageId() ) ) 00710 return true; 00711 00712 return false; 00713 } 00714 00715 QString KService::untranslatedGenericName() const { 00716 QVariant v = property(QString::fromLatin1("UntranslatedGenericName"), QVariant::String); 00717 return v.isValid() ? v.toString() : QString(); 00718 } 00719 00720 QString KService::parentApp() const { 00721 Q_D(const KService); 00722 QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QLatin1String("X-KDE-ParentApp")); 00723 if ( (it == d->m_mapProps.end()) || (!it->isValid())) 00724 { 00725 return QString(); 00726 } 00727 00728 return it->toString(); 00729 } 00730 00731 QString KService::pluginKeyword() const 00732 { 00733 Q_D(const KService); 00734 QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QString::fromLatin1("X-KDE-PluginKeyword")); 00735 if ((it == d->m_mapProps.end()) || (!it->isValid())) { 00736 return QString(); 00737 } 00738 00739 return it->toString(); 00740 } 00741 00742 QString KService::docPath() const 00743 { 00744 Q_D(const KService); 00745 QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QLatin1String("X-DocPath")); 00746 if ((it == d->m_mapProps.end()) || (!it->isValid())) { 00747 it = d->m_mapProps.find(QString::fromLatin1("DocPath")); 00748 if ((it == d->m_mapProps.end()) || (!it->isValid())) { 00749 return QString(); 00750 } 00751 } 00752 00753 return it->toString(); 00754 } 00755 00756 bool KService::allowMultipleFiles() const { 00757 Q_D(const KService); 00758 // Can we pass multiple files on the command line or do we have to start the application for every single file ? 00759 return (d->m_strExec.contains( QLatin1String("%F") ) || d->m_strExec.contains( QLatin1String("%U") ) || 00760 d->m_strExec.contains( QLatin1String("%N") ) || d->m_strExec.contains( QLatin1String("%D") )); 00761 } 00762 00763 QStringList KService::categories() const 00764 { 00765 Q_D(const KService); 00766 return d->categories; 00767 } 00768 00769 QString KService::menuId() const 00770 { 00771 Q_D(const KService); 00772 return d->menuId; 00773 } 00774 00775 void KService::setMenuId(const QString &_menuId) 00776 { 00777 Q_D(KService); 00778 d->menuId = _menuId; 00779 } 00780 00781 QString KService::storageId() const 00782 { 00783 Q_D(const KService); 00784 return d->storageId(); 00785 } 00786 00787 QString KService::locateLocal() const 00788 { 00789 Q_D(const KService); 00790 if (d->menuId.isEmpty() || entryPath().startsWith(QLatin1String(".hidden")) || 00791 (QDir::isRelativePath(entryPath()) && d->categories.isEmpty())) 00792 return KDesktopFile::locateLocal(entryPath()); 00793 00794 return KStandardDirs::locateLocal("xdgdata-apps", d->menuId); 00795 } 00796 00797 QString KService::newServicePath(bool showInMenu, const QString &suggestedName, 00798 QString *menuId, const QStringList *reservedMenuIds) 00799 { 00800 Q_UNUSED(showInMenu); // TODO KDE5: remove argument 00801 00802 QString base = suggestedName; 00803 QString result; 00804 for(int i = 1; true; i++) 00805 { 00806 if (i == 1) 00807 result = base + QString::fromLatin1(".desktop"); 00808 else 00809 result = base + QString::fromLatin1("-%1.desktop").arg(i); 00810 00811 if (reservedMenuIds && reservedMenuIds->contains(result)) 00812 continue; 00813 00814 // Lookup service by menu-id 00815 KService::Ptr s = serviceByMenuId(result); 00816 if (s) 00817 continue; 00818 00819 if (!KStandardDirs::locate("xdgdata-apps", result).isEmpty()) 00820 continue; 00821 00822 break; 00823 } 00824 if (menuId) 00825 *menuId = result; 00826 00827 return KStandardDirs::locateLocal("xdgdata-apps", result); 00828 } 00829 00830 bool KService::isApplication() const 00831 { 00832 Q_D(const KService); 00833 return d->m_strType == QLatin1String("Application"); 00834 } 00835 00836 #ifndef KDE_NO_DEPRECATED 00837 QString KService::type() const 00838 { 00839 Q_D(const KService); 00840 return d->m_strType; 00841 } 00842 #endif 00843 00844 QString KService::exec() const 00845 { 00846 Q_D(const KService); 00847 if (d->m_strType == QLatin1String("Application") && d->m_strExec.isEmpty()) 00848 { 00849 kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath() 00850 << " has Type=" << d->m_strType << " but has no Exec field." << endl; 00851 } 00852 return d->m_strExec; 00853 } 00854 00855 QString KService::library() const 00856 { 00857 Q_D(const KService); 00858 return d->m_strLibrary; 00859 } 00860 00861 QString KService::icon() const 00862 { 00863 Q_D(const KService); 00864 return d->m_strIcon; 00865 } 00866 00867 QString KService::terminalOptions() const 00868 { 00869 Q_D(const KService); 00870 return d->m_strTerminalOptions; 00871 } 00872 00873 bool KService::terminal() const 00874 { 00875 Q_D(const KService); 00876 return d->m_bTerminal; 00877 } 00878 00879 // KDE5: remove and port code to entryPath? 00880 #ifndef KDE_NO_DEPRECATED 00881 QString KService::desktopEntryPath() const 00882 { 00883 return entryPath(); 00884 } 00885 #endif 00886 00887 QString KService::desktopEntryName() const 00888 { 00889 Q_D(const KService); 00890 return d->m_strDesktopEntryName; 00891 } 00892 00893 KService::DBusStartupType KService::dbusStartupType() const 00894 { 00895 Q_D(const KService); 00896 return d->m_DBUSStartusType; 00897 } 00898 00899 QString KService::path() const 00900 { 00901 Q_D(const KService); 00902 return d->m_strPath; 00903 } 00904 00905 QString KService::comment() const 00906 { 00907 Q_D(const KService); 00908 return d->m_strComment; 00909 } 00910 00911 QString KService::genericName() const 00912 { 00913 Q_D(const KService); 00914 return d->m_strGenName; 00915 } 00916 00917 QStringList KService::keywords() const 00918 { 00919 Q_D(const KService); 00920 return d->m_lstKeywords; 00921 } 00922 00923 QStringList KServicePrivate::serviceTypes() const 00924 { 00925 QStringList ret; 00926 QVector<KService::ServiceTypeAndPreference>::const_iterator it = m_serviceTypes.begin(); 00927 for ( ; it < m_serviceTypes.end(); ++it ) { 00928 Q_ASSERT(!(*it).serviceType.isEmpty()); 00929 ret.append((*it).serviceType); 00930 } 00931 return ret; 00932 } 00933 00934 QStringList KService::serviceTypes() const 00935 { 00936 Q_D(const KService); 00937 return d->serviceTypes(); 00938 } 00939 00940 bool KService::allowAsDefault() const 00941 { 00942 Q_D(const KService); 00943 return d->m_bAllowAsDefault; 00944 } 00945 00946 int KService::initialPreference() const 00947 { 00948 Q_D(const KService); 00949 return d->m_initialPreference; 00950 } 00951 00952 void KService::setTerminal(bool b) 00953 { 00954 Q_D(KService); 00955 d->m_bTerminal = b; 00956 } 00957 00958 void KService::setTerminalOptions(const QString &options) 00959 { 00960 Q_D(KService); 00961 d->m_strTerminalOptions = options; 00962 } 00963 00964 QVector<KService::ServiceTypeAndPreference> & KService::_k_accessServiceTypes() 00965 { 00966 Q_D(KService); 00967 return d->m_serviceTypes; 00968 } 00969 00970 QList<KServiceAction> KService::actions() const 00971 { 00972 Q_D(const KService); 00973 return d->m_actions; 00974 }
KDE 4.6 API Reference