kdelibs/kdecore/services/kservice.cpp
Ivailo Monev b90f90905b kio: move most of the KRun bits to KLauncher
two things doing the same thing - one has to go away. also KRun does not
fork and the launched service/application lifetime was bound to the
process launching it, that is not the case with klauncher - it is bound
to the session (in the usual case)

a few things on the TODO list but mostly for services/applications
lacking features

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2024-05-12 03:21:17 +03:00

912 lines
29 KiB
C++

/* This file is part of the KDE libraries
* Copyright (C) 1999 - 2001 Waldo Bastian <bastian@kde.org>
* Copyright (C) 1999 - 2005 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation;
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kservice.h"
#include "kservice_p.h"
#include "kmimetypefactory.h"
#include "kmimetyperepository_p.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include <unistd.h>
#include <stdlib.h>
#include <QtCore/qstring.h>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QMap>
#include <kdebug.h>
#include <kdesktopfile.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>
#include "kservicefactory.h"
#include "kservicetypefactory.h"
// see kdebug.areas
int servicesDebugArea()
{
return 183;
}
QDataStream &operator<<(QDataStream &s, const KService::ServiceTypeAndPreference &st)
{
s << st.preference << st.serviceType;
return s;
}
QDataStream &operator>>(QDataStream &s, KService::ServiceTypeAndPreference &st)
{
s >> st.preference >> st.serviceType;
return s;
}
void KServicePrivate::init( const KDesktopFile *config, KService* q )
{
const QString entryPath = q->entryPath();
bool absPath = !QDir::isRelativePath(entryPath);
// TODO: it makes sense to have a KConstConfigGroup I guess
const KConfigGroup desktopGroup = config->desktopGroup();
QMap<QString, QString> entryMap = desktopGroup.entryMap();
entryMap.remove(QLatin1String("Encoding")); // reserved as part of Desktop Entry Standard
entryMap.remove(QLatin1String("Version")); // reserved as part of Desktop Entry Standard
q->setDeleted( desktopGroup.readEntry("Hidden", false) );
entryMap.remove(QLatin1String("Hidden"));
if ( q->isDeleted() ) {
m_bValid = false;
return;
}
m_strName = config->readName();
entryMap.remove(QLatin1String("Name"));
if ( m_strName.isEmpty() )
{
// Try to make up a name.
m_strName = entryPath;
int i = m_strName.lastIndexOf(QLatin1Char('/'));
m_strName = m_strName.mid(i+1);
i = m_strName.lastIndexOf(QLatin1Char('.'));
if (i != -1)
m_strName = m_strName.left(i);
}
m_strType = config->readType();
entryMap.remove(QLatin1String("Type"));
if ( m_strType.isEmpty() )
{
/*kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath
<< " has no Type=... entry."
<< " It should be \"Application\", \"Service\"" or \"FSDevice\";
m_bValid = false;
return;*/
m_strType = QString::fromLatin1("Application");
} else if (m_strType != QLatin1String("Application") && m_strType != QLatin1String("Service")
&& m_strType != QLatin1String("FSDevice")) {
kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath
<< " has Type=" << m_strType
<< " instead of \"Application\", \"Service\" or \"FSDevice\"";
m_bValid = false;
return;
}
// NOT readPathEntry, it is not XDG-compliant. Path entries written by
// KDE4 will be still treated as such, though.
m_strExec = desktopGroup.readEntry( "Exec", QString() );
entryMap.remove(QLatin1String("Exec"));
if (m_strType == QLatin1String("Application")) {
// It's an application? Should have an Exec line then, otherwise we can't run it
if (m_strExec.isEmpty()) {
kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath
<< " has Type=" << m_strType
<< " but no Exec line";
m_bValid = false;
return;
}
}
// In case Try Exec is set, check if the application is available
if (!config->tryExec()) {
q->setDeleted( true );
m_bValid = false;
return;
}
const QByteArray resource = config->resource();
if ( (m_strType == QLatin1String("Application")) &&
(!resource.isEmpty()) &&
(resource != "apps") &&
!absPath)
{
kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath
<< " has Type=" << m_strType << " but is located under \"" << resource
<< "\" instead of \"apps\"";
m_bValid = false;
return;
}
if ( (m_strType == QLatin1String("Service")) &&
(!resource.isEmpty()) &&
(resource != "services") &&
!absPath)
{
kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath
<< " has Type=" << m_strType << " but is located under \"" << resource
<< "\" instead of \"services\"";
m_bValid = false;
return;
}
QString _name = entryPath;
int pos = _name.lastIndexOf(QLatin1Char('/'));
if (pos != -1)
_name = _name.mid(pos+1);
pos = _name.indexOf(QLatin1Char('.'));
if (pos != -1)
_name = _name.left(pos);
m_strIcon = config->readIcon();
entryMap.remove(QLatin1String("Icon"));
m_bTerminal = desktopGroup.readEntry( "Terminal", false); // should be a property IMHO
entryMap.remove(QLatin1String("Terminal"));
m_strTerminalOptions = desktopGroup.readEntry( "TerminalOptions" ); // should be a property IMHO
entryMap.remove(QLatin1String("TerminalOptions"));
m_strPath = config->readPath();
entryMap.remove(QLatin1String("Path"));
m_strComment = config->readComment();
entryMap.remove(QLatin1String("Comment"));
m_strGenName = config->readGenericName();
entryMap.remove(QLatin1String("GenericName"));
m_lstKeywords = desktopGroup.readXdgListEntry("Keywords", QStringList());
entryMap.remove(QLatin1String("Keywords"));
m_lstKeywords += desktopGroup.readEntry("X-KDE-Keywords", QStringList());
entryMap.remove(QLatin1String("X-KDE-Keywords"));
categories = desktopGroup.readXdgListEntry("Categories");
entryMap.remove(QLatin1String("Categories"));
// TODO KDE5: only care for X-KDE-Library in Type=Service desktop files
// This will prevent people defining a part and an app in the same desktop file
// which makes user-preference handling difficult.
m_strLibrary = desktopGroup.readEntry( "X-KDE-Library" );
entryMap.remove(QLatin1String("X-KDE-Library"));
if (!m_strLibrary.isEmpty() && m_strType == QLatin1String("Application")) {
kWarning(servicesDebugArea()) << "The desktop entry file" << entryPath
<< "has Type=" << m_strType
<< "but also has a X-KDE-Library key. This works for now,"
" but makes user-preference handling difficult, so support for this might"
" be removed at some point. Consider splitting it into two desktop files.";
}
QStringList lstServiceTypes = desktopGroup.readEntry( "ServiceTypes", QStringList() );
entryMap.remove(QLatin1String("ServiceTypes"));
lstServiceTypes += desktopGroup.readEntry( "X-KDE-ServiceTypes", QStringList() );
entryMap.remove(QLatin1String("X-KDE-ServiceTypes"));
lstServiceTypes += desktopGroup.readXdgListEntry( "MimeType" );
entryMap.remove(QLatin1String("MimeType"));
if ( m_strType == QLatin1String("Application") && !lstServiceTypes.contains(QLatin1String("Application")) )
// Applications implement the service type "Application" ;-)
lstServiceTypes += QString::fromLatin1("Application");
m_initialPreference = desktopGroup.readEntry( "InitialPreference", 1 );
entryMap.remove(QLatin1String("InitialPreference"));
// Assign the "initial preference" to each mimetype/servicetype
// (and to set such preferences in memory from kbuildsycoca)
m_serviceTypes.reserve(lstServiceTypes.size());
QListIterator<QString> st_it(lstServiceTypes);
while ( st_it.hasNext() ) {
const QString st = st_it.next();
if (st.isEmpty()) {
kWarning(servicesDebugArea()) << "The desktop entry file" << entryPath
<< "has an empty mimetype!";
continue;
}
int initialPreference = m_initialPreference;
if ( st_it.hasNext() ) {
// TODO better syntax - separate group with mimetype=number entries?
bool isNumber;
const int val = st_it.peekNext().toInt(&isNumber);
if (isNumber) {
initialPreference = val;
st_it.next();
}
}
m_serviceTypes.push_back(KService::ServiceTypeAndPreference(initialPreference, st));
}
if (entryMap.contains(QLatin1String("Actions"))) {
parseActions(config, q);
}
m_strDesktopEntryName = _name.toLower();
m_bAllowAsDefault = desktopGroup.readEntry("AllowDefault", true);
entryMap.remove(QLatin1String("AllowDefault"));
// allow plugin users to translate categories without needing a separate key
QMap<QString,QString>::Iterator entry = entryMap.find(QString::fromLatin1("X-KDE-PluginInfo-Category"));
if (entry != entryMap.end()) {
const QString& key = entry.key();
m_mapProps.insert(key, QVariant(desktopGroup.readEntryUntranslated(key)));
m_mapProps.insert(key + QLatin1String("-Translated"), QVariant(*entry));
entryMap.erase(entry);
}
// Store all additional entries in the property map.
// A QMap<QString,QString> would be easier for this but we can't
// break BC, so we have to store it in m_mapProps.
// qDebug("Path = %s", entryPath.toLatin1().constData());
QMap<QString,QString>::ConstIterator it = entryMap.constBegin();
for( ; it != entryMap.constEnd();++it) {
const QString key = it.key();
// do not store other translations like Name[fr]; kbuildsycoca will rerun if we change languages anyway
if (!key.contains(QLatin1Char('['))) {
//kDebug(servicesDebugArea()) << " Key =" << key << " Data =" << *it;
m_mapProps.insert(key, QVariant(*it));
}
}
}
void KServicePrivate::parseActions(const KDesktopFile *config, KService* q)
{
const QStringList keys = config->readActions();
if (keys.isEmpty())
return;
QStringList::ConstIterator it = keys.begin();
const QStringList::ConstIterator end = keys.end();
for ( ; it != end; ++it ) {
const QString group = *it;
if (group == QLatin1String("_SEPARATOR_")) {
m_actions.append(KServiceAction(group, QString(), QString(), QString(), false));
continue;
}
if (config->hasActionGroup(group)) {
const KConfigGroup cg = config->actionGroup(group);
if ( !cg.hasKey( "Name" ) || !cg.hasKey( "Exec" ) ) {
kWarning(servicesDebugArea()) << "The action" << group << "in the desktop file" << q->entryPath()
<< "has no Name or no Exec key";
} else {
m_actions.append(KServiceAction(group,
cg.readEntry("Name"),
cg.readEntry("Icon"),
cg.readEntry("Exec"),
cg.readEntry("NoDisplay", false)));
}
} else {
kWarning(servicesDebugArea()) << "The desktop file" << q->entryPath()
<< "references the action" << group << "but doesn't define it";
}
}
}
void KServicePrivate::load(QDataStream& s)
{
qint8 def, term;
qint8 initpref;
// NOTE: make sure to update the version number in ksycoca_p.h
s >> m_strType >> m_strName >> m_strExec >> m_strIcon
>> term >> m_strTerminalOptions
>> m_strPath >> m_strComment >> def >> m_mapProps
>> m_strLibrary
>> m_strDesktopEntryName
>> initpref
>> m_lstKeywords >> m_strGenName
>> categories >> menuId >> m_actions >> m_serviceTypes;
m_bAllowAsDefault = (bool)def;
m_bTerminal = (bool)term;
m_initialPreference = initpref;
m_bValid = true;
}
void KServicePrivate::save(QDataStream& s)
{
KSycocaEntryPrivate::save( s );
qint8 def = m_bAllowAsDefault, initpref = m_initialPreference;
qint8 term = m_bTerminal;
// NOTE: make sure to update the version number in ksycoca_p.h
s << m_strType << m_strName << m_strExec << m_strIcon
<< term << m_strTerminalOptions
<< m_strPath << m_strComment << def << m_mapProps
<< m_strLibrary
<< m_strDesktopEntryName
<< initpref
<< m_lstKeywords << m_strGenName
<< categories << menuId << m_actions << m_serviceTypes;
}
////
KService::KService( const QString & _name, const QString &_exec, const QString &_icon)
: KSycocaEntry(*new KServicePrivate(QString()))
{
Q_D(KService);
d->m_strType = QString::fromLatin1("Application");
d->m_strName = _name;
d->m_strExec = _exec;
d->m_strIcon = _icon;
d->m_bTerminal = false;
d->m_bAllowAsDefault = true;
d->m_initialPreference = 10;
}
KService::KService( const QString & _fullpath )
: KSycocaEntry(*new KServicePrivate(_fullpath))
{
Q_D(KService);
KDesktopFile config( _fullpath );
d->init(&config, this);
}
KService::KService( const KDesktopFile *config )
: KSycocaEntry(*new KServicePrivate(config->fileName()))
{
Q_D(KService);
d->init(config, this);
}
KService::KService( QDataStream& _str, int _offset )
: KSycocaEntry(*new KServicePrivate(_str, _offset))
{
}
KService::~KService()
{
}
bool KService::hasServiceType( const QString& serviceType ) const
{
Q_D(const KService);
if (!d->m_bValid) return false; // (useless) safety test
const KServiceType::Ptr ptr = KServiceType::serviceType( serviceType );
if (!ptr)
return false;
const int serviceOffset = offset();
// doesn't seem to work:
//if ( serviceOffset == 0 )
// serviceOffset = serviceByStorageId( storageId() );
if ( serviceOffset )
return KServiceFactory::self()->hasOffer( ptr->offset(), ptr->serviceOffersOffset(), serviceOffset );
// fall-back code for services that are NOT from ksycoca
// For each service type we are associated with, if it doesn't
// match then we try its parent service types.
QVector<ServiceTypeAndPreference>::ConstIterator it = d->m_serviceTypes.begin();
for( ; it != d->m_serviceTypes.end(); ++it ) {
const QString& st = (*it).serviceType;
//kDebug(servicesDebugArea()) << " has " << (*it);
if ( st == ptr->name() )
return true;
// also the case of parent servicetypes
KServiceType::Ptr p = KServiceType::serviceType( st );
if ( p && p->inherits( ptr->name() ) )
return true;
}
return false;
}
bool KService::hasMimeType(const QString& mimeType) const
{
Q_D(const KService);
const QString mime = KMimeTypeRepository::self()->canonicalName(mimeType);
int serviceOffset = offset();
if ( serviceOffset ) {
KMimeTypeFactory *factory = KMimeTypeFactory::self();
const int mimeOffset = factory->entryOffset(mime);
const int serviceOffersOffset = factory->serviceOffersOffset(mime);
if (serviceOffersOffset == -1)
return false;
return KServiceFactory::self()->hasOffer(mimeOffset, serviceOffersOffset, serviceOffset);
}
// fall-back code for services that are NOT from ksycoca
QVector<ServiceTypeAndPreference>::ConstIterator it = d->m_serviceTypes.begin();
for( ; it != d->m_serviceTypes.end(); ++it ) {
const QString& st = (*it).serviceType;
//kDebug(servicesDebugArea()) << " has " << (*it);
if ( st == mime )
return true;
// TODO: should we handle inherited mimetypes here?
// KMimeType was in kio when this code was written, this is the only reason it's not done.
// But this should matter only in a very rare case, since most code gets KServices from ksycoca.
// Warning, change hasServiceType if you implement this here (and check kbuildservicefactory).
}
return false;
}
QVariant KServicePrivate::property( const QString& _name) const
{
return property( _name, QVariant::Invalid);
}
// Return a string QVariant if string isn't null, and invalid variant otherwise
// (the variant must be invalid if the field isn't in the .desktop file)
// This allows trader queries like "exist Library" to work.
static QVariant makeStringVariant( const QString& string )
{
// Using isEmpty here would be wrong.
// Empty is "specified but empty", null is "not specified" (in the .desktop file)
return string.isNull() ? QVariant() : QVariant( string );
}
QVariant KService::property( const QString& _name, QVariant::Type t ) const
{
Q_D(const KService);
return d->property(_name, t);
}
QVariant KServicePrivate::property( const QString& _name, QVariant::Type t ) const
{
if ( _name == QLatin1String("Type") )
return QVariant( m_strType ); // can't be null
else if ( _name == QLatin1String("Name") )
return QVariant( m_strName ); // can't be null
else if ( _name == QLatin1String("Exec") )
return makeStringVariant( m_strExec );
else if ( _name == QLatin1String("Icon") )
return makeStringVariant( m_strIcon );
else if ( _name == QLatin1String("Terminal") )
return QVariant( m_bTerminal );
else if ( _name == QLatin1String("TerminalOptions") )
return makeStringVariant( m_strTerminalOptions );
else if ( _name == QLatin1String("Path") )
return makeStringVariant( m_strPath );
else if ( _name == QLatin1String("Comment") )
return makeStringVariant( m_strComment );
else if ( _name == QLatin1String("GenericName") )
return makeStringVariant( m_strGenName );
else if ( _name == QLatin1String("ServiceTypes") )
return QVariant( serviceTypes() );
else if ( _name == QLatin1String("AllowAsDefault") )
return QVariant( m_bAllowAsDefault );
else if ( _name == QLatin1String("InitialPreference") )
return QVariant( m_initialPreference );
else if ( _name == QLatin1String("Library") )
return makeStringVariant( m_strLibrary );
else if ( _name == QLatin1String("DesktopEntryPath") ) // can't be null
return QVariant( path );
else if ( _name == QLatin1String("DesktopEntryName"))
return QVariant( m_strDesktopEntryName ); // can't be null
else if ( _name == QLatin1String("Categories"))
return QVariant( categories );
else if ( _name == QLatin1String("Keywords"))
return QVariant( m_lstKeywords );
// Ok we need to convert the property from a QString to its real type.
// Maybe the caller helped us.
if (t == QVariant::Invalid)
{
// No luck, let's ask KServiceTypeFactory what the type of this property
// is supposed to be.
t = KServiceTypeFactory::self()->findPropertyTypeByName(_name);
if (t == QVariant::Invalid)
{
kDebug(servicesDebugArea()) << "Request for unknown property '" << _name << "'\n";
return QVariant(); // Unknown property: Invalid variant.
}
}
QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( _name );
if ( (it == m_mapProps.end()) || (!it->isValid()))
{
//kDebug(servicesDebugArea()) << "Property not found " << _name;
return QVariant(); // No property set.
}
switch(t)
{
case QVariant::String:
return *it; // no conversion necessary
default:
// All others
// For instance properties defined as StringList, like MimeTypes.
// XXX This API is accessible only through a friend declaration.
return KConfigGroup::convertToQVariant(_name.toUtf8().constData(), it->toString().toUtf8(), t);
}
}
QStringList KServicePrivate::propertyNames() const
{
QStringList res;
QMap<QString,QVariant>::ConstIterator it = m_mapProps.begin();
for( ; it != m_mapProps.end(); ++it )
res.append( it.key() );
res.append( QString::fromLatin1("Type") );
res.append( QString::fromLatin1("Name") );
res.append( QString::fromLatin1("Comment") );
res.append( QString::fromLatin1("GenericName") );
res.append( QString::fromLatin1("Icon") );
res.append( QString::fromLatin1("Exec") );
res.append( QString::fromLatin1("Terminal") );
res.append( QString::fromLatin1("TerminalOptions") );
res.append( QString::fromLatin1("Path") );
res.append( QString::fromLatin1("ServiceTypes") );
res.append( QString::fromLatin1("AllowAsDefault") );
res.append( QString::fromLatin1("InitialPreference") );
res.append( QString::fromLatin1("Library") );
res.append( QString::fromLatin1("DesktopEntryPath") );
res.append( QString::fromLatin1("DesktopEntryName") );
res.append( QString::fromLatin1("Keywords") );
res.append( QString::fromLatin1("Categories") );
return res;
}
KService::List KService::allServices()
{
return KServiceFactory::self()->allServices();
}
KService::Ptr KService::serviceByDesktopPath( const QString& _name )
{
return KServiceFactory::self()->findServiceByDesktopPath( _name );
}
KService::Ptr KService::serviceByDesktopName( const QString& _name )
{
return KServiceFactory::self()->findServiceByDesktopName( _name.toLower() );
}
KService::Ptr KService::serviceByMenuId( const QString& _name )
{
return KServiceFactory::self()->findServiceByMenuId( _name );
}
KService::Ptr KService::serviceByStorageId( const QString& _storageId )
{
KService::Ptr service = KService::serviceByMenuId( _storageId );
if (service)
return service;
service = KService::serviceByDesktopPath(_storageId);
if (service)
return service;
if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId))
return KService::Ptr(new KService(_storageId));
QString tmp = _storageId;
tmp = tmp.mid(tmp.lastIndexOf(QLatin1Char('/'))+1); // Strip dir
if (tmp.endsWith(QLatin1String(".desktop")))
tmp.truncate(tmp.length()-8);
service = KService::serviceByDesktopName(tmp);
return service;
}
bool KService::substituteUid() const {
QVariant v = property(QLatin1String("X-KDE-SubstituteUID"), QVariant::Bool);
return v.isValid() && v.toBool();
}
QString KService::username() const {
// See also KDesktopFile::tryExec()
QString user;
QVariant v = property(QLatin1String("X-KDE-Username"), QVariant::String);
user = v.isValid() ? v.toString() : QString();
if (user.isEmpty())
user = QString::fromLatin1("root");
return user;
}
bool KService::showInKDE() const
{
Q_D(const KService);
QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find( QString::fromLatin1("OnlyShowIn") );
if ( (it != d->m_mapProps.end()) && (it->isValid()))
{
const QStringList aList = it->toString().split(QLatin1Char(';'));
if (!aList.contains(QString::fromLatin1("KDE")))
return false;
}
it = d->m_mapProps.find( QString::fromLatin1("NotShowIn") );
if ( (it != d->m_mapProps.end()) && (it->isValid()))
{
const QStringList aList = it->toString().split(QLatin1Char(';'));
if (aList.contains(QString::fromLatin1("KDE")))
return false;
}
return true;
}
bool KService::noDisplay() const {
if ( qvariant_cast<bool>(property(QString::fromLatin1("NoDisplay"), QVariant::Bool)) )
return true;
if (!showInKDE())
return true;
return false;
}
QString KService::parentApp() const {
Q_D(const KService);
QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QLatin1String("X-KDE-ParentApp"));
if ( (it == d->m_mapProps.end()) || (!it->isValid()))
{
return QString();
}
return it->toString();
}
QString KService::pluginKeyword() const
{
Q_D(const KService);
QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QString::fromLatin1("X-KDE-PluginKeyword"));
if ((it == d->m_mapProps.end()) || (!it->isValid())) {
return QString();
}
return it->toString();
}
QString KService::docPath() const
{
Q_D(const KService);
QMap<QString,QVariant>::ConstIterator it = d->m_mapProps.find(QLatin1String("X-DocPath"));
if ((it == d->m_mapProps.end()) || (!it->isValid())) {
return QString();
}
return it->toString();
}
bool KService::allowMultipleFiles() const
{
Q_D(const KService);
// Can we pass multiple files on the command line or do we have to start the application for every single file ?
return (d->m_strExec.contains( QLatin1String("%F") ) || d->m_strExec.contains( QLatin1String("%U") ));
}
QStringList KService::categories() const
{
Q_D(const KService);
return d->categories;
}
QString KService::menuId() const
{
Q_D(const KService);
return d->menuId;
}
void KService::setMenuId(const QString &_menuId)
{
Q_D(KService);
d->menuId = _menuId;
}
QString KService::storageId() const
{
Q_D(const KService);
return d->storageId();
}
QString KService::locateLocal() const
{
Q_D(const KService);
if (d->menuId.isEmpty() || entryPath().startsWith(QLatin1String(".hidden")) ||
(QDir::isRelativePath(entryPath()) && d->categories.isEmpty()))
return KDesktopFile::locateLocal(entryPath());
return KStandardDirs::locateLocal("xdgdata-apps", d->menuId);
}
QString KService::newServicePath(const QString &suggestedName,
QString *menuId, const QStringList *reservedMenuIds)
{
QString base = suggestedName;
QString result;
for(int i = 1; true; i++)
{
if (i == 1)
result = base + QString::fromLatin1(".desktop");
else
result = base + QString::fromLatin1("-%1.desktop").arg(i);
if (reservedMenuIds && reservedMenuIds->contains(result))
continue;
// Lookup service by menu-id
KService::Ptr s = serviceByMenuId(result);
if (s)
continue;
if (!KStandardDirs::locate("xdgdata-apps", result).isEmpty())
continue;
break;
}
if (menuId)
*menuId = result;
return KStandardDirs::locateLocal("xdgdata-apps", result);
}
bool KService::isApplication() const
{
Q_D(const KService);
return d->m_strType == QLatin1String("Application");
}
QString KService::exec() const
{
Q_D(const KService);
if (d->m_strType == QLatin1String("Application") && d->m_strExec.isEmpty())
{
kWarning(servicesDebugArea()) << "The desktop entry file " << entryPath()
<< " has Type=" << d->m_strType << " but has no Exec field.";
}
return d->m_strExec;
}
QString KService::library() const
{
Q_D(const KService);
return d->m_strLibrary;
}
QString KService::icon() const
{
Q_D(const KService);
return d->m_strIcon;
}
QString KService::terminalOptions() const
{
Q_D(const KService);
return d->m_strTerminalOptions;
}
bool KService::terminal() const
{
Q_D(const KService);
return d->m_bTerminal;
}
QString KService::desktopEntryName() const
{
Q_D(const KService);
return d->m_strDesktopEntryName;
}
QString KService::path() const
{
Q_D(const KService);
return d->m_strPath;
}
QString KService::comment() const
{
Q_D(const KService);
return d->m_strComment;
}
QString KService::genericName() const
{
Q_D(const KService);
return d->m_strGenName;
}
QStringList KService::keywords() const
{
Q_D(const KService);
return d->m_lstKeywords;
}
QStringList KServicePrivate::serviceTypes() const
{
QStringList ret;
ret.reserve(m_serviceTypes.size());
foreach(const KService::ServiceTypeAndPreference &it, m_serviceTypes) {
Q_ASSERT(!it.serviceType.isEmpty());
ret.append(it.serviceType);
}
return ret;
}
QStringList KService::serviceTypes() const
{
Q_D(const KService);
return d->serviceTypes();
}
QStringList KService::mimeTypes() const
{
Q_D(const KService);
QStringList ret;
foreach(const KService::ServiceTypeAndPreference &it, d->m_serviceTypes) {
if (KMimeType::mimeType(it.serviceType)) // keep only mimetypes, filter out servicetypes
ret.append(it.serviceType);
}
return ret;
}
bool KService::allowAsDefault() const
{
Q_D(const KService);
return d->m_bAllowAsDefault;
}
int KService::initialPreference() const
{
Q_D(const KService);
return d->m_initialPreference;
}
void KService::setTerminal(bool b)
{
Q_D(KService);
d->m_bTerminal = b;
}
void KService::setTerminalOptions(const QString &options)
{
Q_D(KService);
d->m_strTerminalOptions = options;
}
void KService::setExec(const QString& exec)
{
Q_D(KService);
if (!exec.isEmpty()) {
d->m_strExec = exec;
d->path.clear();
}
}
QVector<KService::ServiceTypeAndPreference> & KService::_k_accessServiceTypes()
{
Q_D(KService);
return d->m_serviceTypes;
}
QList<KServiceAction> KService::actions() const
{
Q_D(const KService);
return d->m_actions;
}