File indexing completed on 2024-11-10 03:40:23
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999-2001 Waldo Bastian <bastian@kde.org> 0004 SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org> 0005 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include "kservice.h" 0011 #include "kmimetypefactory_p.h" 0012 #include "kservice_p.h" 0013 #include "ksycoca.h" 0014 #include "ksycoca_p.h" 0015 0016 #include <qplatformdefs.h> 0017 0018 #include <QDir> 0019 #include <QMap> 0020 #include <QMimeDatabase> 0021 0022 #include <KConfigGroup> 0023 #include <KDesktopFile> 0024 #include <KShell> 0025 0026 #include <QDebug> 0027 #include <QStandardPaths> 0028 0029 #include "kservicefactory_p.h" 0030 #include "kserviceutil_p.h" 0031 #include "servicesdebug.h" 0032 0033 QDataStream &operator<<(QDataStream &s, const KService::ServiceTypeAndPreference &st) 0034 { 0035 s << st.preference << st.serviceType; 0036 return s; 0037 } 0038 QDataStream &operator>>(QDataStream &s, KService::ServiceTypeAndPreference &st) 0039 { 0040 s >> st.preference >> st.serviceType; 0041 return s; 0042 } 0043 0044 void KServicePrivate::init(const KDesktopFile *config, KService *q) 0045 { 0046 const QString entryPath = q->entryPath(); 0047 if (entryPath.isEmpty()) { 0048 // We are opening a "" service, this means whatever warning we might get is going to be misleading 0049 m_bValid = false; 0050 return; 0051 } 0052 0053 bool absPath = !QDir::isRelativePath(entryPath); 0054 0055 const KConfigGroup desktopGroup = config->desktopGroup(); 0056 QMap<QString, QString> entryMap = desktopGroup.entryMap(); 0057 0058 entryMap.remove(QStringLiteral("Encoding")); // reserved as part of Desktop Entry Standard 0059 entryMap.remove(QStringLiteral("Version")); // reserved as part of Desktop Entry Standard 0060 0061 q->setDeleted(desktopGroup.readEntry("Hidden", false)); 0062 entryMap.remove(QStringLiteral("Hidden")); 0063 if (q->isDeleted()) { 0064 m_bValid = false; 0065 return; 0066 } 0067 0068 m_strName = config->readName(); 0069 entryMap.remove(QStringLiteral("Name")); 0070 if (m_strName.isEmpty()) { 0071 // Try to make up a name. 0072 m_strName = entryPath; 0073 int i = m_strName.lastIndexOf(QLatin1Char('/')); 0074 m_strName = m_strName.mid(i + 1); 0075 i = m_strName.lastIndexOf(QLatin1Char('.')); 0076 if (i != -1) { 0077 m_strName.truncate(i); 0078 } 0079 } 0080 0081 m_strType = entryMap.take(QStringLiteral("Type")); 0082 if (m_strType.isEmpty()) { 0083 qCWarning(SERVICES) << "The desktop entry file" << entryPath << "does not have a \"Type=Application\" set."; 0084 m_strType = QStringLiteral("Application"); 0085 } else if (m_strType != QLatin1String("Application") && m_strType != QLatin1String("Service")) { 0086 qCWarning(SERVICES) << "The desktop entry file" << entryPath << "has Type=" << m_strType << "instead of \"Application\" or \"Service\""; 0087 m_bValid = false; 0088 return; 0089 } 0090 0091 // NOT readPathEntry, it is not XDG-compliant: it performs 0092 // various expansions, like $HOME. Note that the expansion 0093 // behaviour still happens if the "e" flag is set, maintaining 0094 // backwards compatibility. 0095 m_strExec = entryMap.take(QStringLiteral("Exec")); 0096 0097 // In case Try Exec is set, check if the application is available 0098 if (!config->tryExec()) { 0099 q->setDeleted(true); 0100 m_bValid = false; 0101 return; 0102 } 0103 0104 const QStandardPaths::StandardLocation locationType = config->locationType(); 0105 0106 if ((m_strType == QLatin1String("Application")) && (locationType != QStandardPaths::ApplicationsLocation) && !absPath) { 0107 qCWarning(SERVICES) << "The desktop entry file" << entryPath << "has Type=" << m_strType << "but is located under \"" 0108 << QStandardPaths::displayName(locationType) << "\" instead of \"Applications\""; 0109 m_bValid = false; 0110 return; 0111 } 0112 0113 // entryPath To desktopEntryName 0114 // (e.g. "/usr/share/applications/org.kde.kate" --> "org.kde.kate") 0115 QString _name = KServiceUtilPrivate::completeBaseName(entryPath); 0116 0117 m_strIcon = entryMap.take(QStringLiteral("Icon")); 0118 m_bTerminal = desktopGroup.readEntry("Terminal", false); 0119 entryMap.remove(QStringLiteral("Terminal")); 0120 m_strTerminalOptions = entryMap.take(QStringLiteral("TerminalOptions")); 0121 m_strWorkingDirectory = KShell::tildeExpand(entryMap.take(QStringLiteral("Path"))); 0122 m_strComment = entryMap.take(QStringLiteral("Comment")); 0123 m_strGenName = entryMap.take(QStringLiteral("GenericName")); 0124 0125 // Store these as member variables too, because the lookup will be significanly faster 0126 m_untranslatedGenericName = desktopGroup.readEntryUntranslated("GenericName"); 0127 m_untranslatedName = desktopGroup.readEntryUntranslated("Name"); 0128 0129 m_lstFormFactors = entryMap.take(QStringLiteral("X-KDE-FormFactors")).split(QLatin1Char(' '), Qt::SkipEmptyParts); 0130 0131 if (entryMap.remove(QStringLiteral("Keywords"))) { 0132 m_lstKeywords = desktopGroup.readXdgListEntry("Keywords"); 0133 } 0134 m_lstKeywords += entryMap.take(QStringLiteral("X-KDE-Keywords")).split(QLatin1Char(' '), Qt::SkipEmptyParts); 0135 if (entryMap.remove(QStringLiteral("Categories"))) { 0136 categories = desktopGroup.readXdgListEntry("Categories"); 0137 } 0138 0139 if (entryMap.remove(QStringLiteral("InitialPreference"))) { 0140 m_initialPreference = desktopGroup.readEntry("InitialPreference", 1); 0141 } 0142 0143 if (entryMap.remove(QStringLiteral("MimeType"))) { 0144 const QStringList lstServiceTypes = desktopGroup.readXdgListEntry("MimeType"); 0145 // Assign the "initial preference" to each mimetype/servicetype 0146 // (and to set such preferences in memory from kbuildsycoca) 0147 m_serviceTypes.reserve(lstServiceTypes.size()); 0148 QListIterator<QString> st_it(lstServiceTypes); 0149 while (st_it.hasNext()) { 0150 const QString st = st_it.next(); 0151 if (st.isEmpty()) { 0152 qCWarning(SERVICES) << "The desktop entry file" << entryPath << "has an empty MimeType!"; 0153 continue; 0154 } 0155 int initialPreference = m_initialPreference; 0156 if (st_it.hasNext()) { 0157 // TODO better syntax - separate group with mimetype=number entries? 0158 bool isNumber; 0159 const int val = st_it.peekNext().toInt(&isNumber); 0160 if (isNumber) { 0161 initialPreference = val; 0162 st_it.next(); 0163 } 0164 } 0165 m_serviceTypes.push_back(KService::ServiceTypeAndPreference(initialPreference, st)); 0166 } 0167 } 0168 0169 m_strDesktopEntryName = _name; 0170 0171 if (entryMap.remove(QStringLiteral("AllowDefault"))) { 0172 m_bAllowAsDefault = desktopGroup.readEntry("AllowDefault", true); 0173 } 0174 0175 // Store all additional entries in the property map. 0176 // A QMap<QString,QString> would be easier for this but we can't 0177 // break BC, so we have to store it in m_mapProps. 0178 // qDebug("Path = %s", entryPath.toLatin1().constData()); 0179 auto it = entryMap.constBegin(); 0180 for (; it != entryMap.constEnd(); ++it) { 0181 const QString key = it.key(); 0182 0183 // Ignore Actions, we parse that below 0184 if (key == QLatin1String("Actions")) { 0185 continue; 0186 } 0187 0188 // do not store other translations like Name[fr]; kbuildsycoca will rerun if we change languages anyway 0189 if (!key.contains(QLatin1Char('['))) { 0190 // qCDebug(SERVICES) << " Key =" << key << " Data =" << it.value(); 0191 if (key == QLatin1String("X-Flatpak-RenamedFrom")) { 0192 m_mapProps.insert(key, desktopGroup.readXdgListEntry(key)); 0193 } else { 0194 m_mapProps.insert(key, QVariant(it.value())); 0195 } 0196 } 0197 } 0198 0199 // parse actions last since that may clone the service 0200 // we want all other information parsed by then 0201 if (entryMap.contains(QLatin1String("Actions"))) { 0202 parseActions(config, q); 0203 } 0204 } 0205 0206 void KServicePrivate::parseActions(const KDesktopFile *config, KService *q) 0207 { 0208 const QStringList keys = config->readActions(); 0209 if (keys.isEmpty()) { 0210 return; 0211 } 0212 0213 KService::Ptr serviceClone(new KService(*q)); 0214 0215 for (const QString &group : keys) { 0216 if (group == QLatin1String("_SEPARATOR_")) { 0217 m_actions.append(KServiceAction(group, QString(), QString(), QString(), false, serviceClone)); 0218 continue; 0219 } 0220 0221 if (config->hasActionGroup(group)) { 0222 const KConfigGroup cg = config->actionGroup(group); 0223 if (!cg.hasKey("Name") || !cg.hasKey("Exec")) { 0224 qCWarning(SERVICES) << "The action" << group << "in the desktop file" << q->entryPath() << "has no Name or no Exec key"; 0225 } else { 0226 const QMap<QString, QString> entries = cg.entryMap(); 0227 0228 QVariantMap entriesVariants; 0229 0230 for (auto it = entries.constKeyValueBegin(); it != entries.constKeyValueEnd(); ++it) { 0231 // Those are stored separately 0232 if (it->first == QLatin1String("Name") || it->first == QLatin1String("Icon") || it->first == QLatin1String("Exec") 0233 || it->first == QLatin1String("NoDisplay")) { 0234 continue; 0235 } 0236 0237 entriesVariants.insert(it->first, it->second); 0238 } 0239 0240 KServiceAction action(group, cg.readEntry("Name"), cg.readEntry("Icon"), cg.readEntry("Exec"), cg.readEntry("NoDisplay", false), serviceClone); 0241 action.setData(QVariant::fromValue(entriesVariants)); 0242 m_actions.append(action); 0243 } 0244 } else { 0245 qCWarning(SERVICES) << "The desktop file" << q->entryPath() << "references the action" << group << "but doesn't define it"; 0246 } 0247 } 0248 0249 serviceClone->setActions(m_actions); 0250 } 0251 0252 void KServicePrivate::load(QDataStream &s) 0253 { 0254 qint8 def; 0255 qint8 term; 0256 qint8 dst; 0257 qint8 initpref; 0258 0259 // WARNING: THIS NEEDS TO REMAIN COMPATIBLE WITH PREVIOUS KService 5.x VERSIONS! 0260 // !! This data structure should remain binary compatible at all times !! 0261 // You may add new fields at the end. Make sure to update KSYCOCA_VERSION 0262 // number in ksycoca.cpp 0263 // clang-format off 0264 s >> m_strType >> m_strName >> m_strExec >> m_strIcon 0265 >> term >> m_strTerminalOptions 0266 >> m_strWorkingDirectory >> m_strComment >> def >> m_mapProps 0267 >> m_strLibrary 0268 >> dst 0269 >> m_strDesktopEntryName 0270 >> initpref 0271 >> m_lstKeywords >> m_strGenName 0272 >> categories >> menuId >> m_actions >> m_serviceTypes 0273 >> m_lstFormFactors 0274 >> m_untranslatedName >> m_untranslatedGenericName; 0275 // clang-format on 0276 0277 m_bAllowAsDefault = bool(def); 0278 m_bTerminal = bool(term); 0279 m_initialPreference = initpref; 0280 0281 m_bValid = true; 0282 } 0283 0284 void KServicePrivate::save(QDataStream &s) 0285 { 0286 KSycocaEntryPrivate::save(s); 0287 qint8 def = m_bAllowAsDefault; 0288 qint8 initpref = m_initialPreference; 0289 qint8 term = m_bTerminal; 0290 qint8 dst = 0; 0291 0292 // WARNING: THIS NEEDS TO REMAIN COMPATIBLE WITH PREVIOUS KService 5.x VERSIONS! 0293 // !! This data structure should remain binary compatible at all times !! 0294 // You may add new fields at the end. Make sure to update KSYCOCA_VERSION 0295 // number in ksycoca.cpp 0296 s << m_strType << m_strName << m_strExec << m_strIcon << term << m_strTerminalOptions << m_strWorkingDirectory << m_strComment << def << m_mapProps 0297 << m_strLibrary << dst << m_strDesktopEntryName << initpref << m_lstKeywords << m_strGenName << categories << menuId << m_actions << m_serviceTypes 0298 << m_lstFormFactors << m_untranslatedName << m_untranslatedGenericName; 0299 } 0300 0301 //// 0302 0303 KService::KService(const QString &_name, const QString &_exec, const QString &_icon) 0304 : KSycocaEntry(*new KServicePrivate(QString())) 0305 { 0306 Q_D(KService); 0307 d->m_strType = QStringLiteral("Application"); 0308 d->m_strName = _name; 0309 d->m_strExec = _exec; 0310 d->m_strIcon = _icon; 0311 d->m_bTerminal = false; 0312 d->m_bAllowAsDefault = true; 0313 d->m_initialPreference = 10; 0314 } 0315 0316 KService::KService(const QString &_fullpath) 0317 : KSycocaEntry(*new KServicePrivate(_fullpath)) 0318 { 0319 Q_D(KService); 0320 0321 KDesktopFile config(_fullpath); 0322 d->init(&config, this); 0323 } 0324 0325 KService::KService(const KDesktopFile *config, const QString &entryPath) 0326 : KSycocaEntry(*new KServicePrivate(entryPath.isEmpty() ? config->fileName() : entryPath)) 0327 { 0328 Q_D(KService); 0329 0330 d->init(config, this); 0331 } 0332 0333 KService::KService(QDataStream &_str, int _offset) 0334 : KSycocaEntry(*new KServicePrivate(_str, _offset)) 0335 { 0336 Q_D(KService); 0337 KService::Ptr serviceClone(new KService(*this)); 0338 for (KServiceAction &action : d->m_actions) { 0339 action.setService(serviceClone); 0340 } 0341 } 0342 0343 KService::KService(const KService &other) 0344 : KSycocaEntry(*new KServicePrivate(*other.d_func())) 0345 { 0346 } 0347 0348 KService::~KService() 0349 { 0350 } 0351 0352 bool KService::hasMimeType(const QString &mimeType) const 0353 { 0354 Q_D(const KService); 0355 QMimeDatabase db; 0356 const QString mime = db.mimeTypeForName(mimeType).name(); 0357 if (mime.isEmpty()) { 0358 return false; 0359 } 0360 int serviceOffset = offset(); 0361 if (serviceOffset) { 0362 KSycoca::self()->ensureCacheValid(); 0363 KMimeTypeFactory *factory = KSycocaPrivate::self()->mimeTypeFactory(); 0364 const int mimeOffset = factory->entryOffset(mime); 0365 const int serviceOffersOffset = factory->serviceOffersOffset(mime); 0366 if (serviceOffersOffset == -1) { 0367 return false; 0368 } 0369 return KSycocaPrivate::self()->serviceFactory()->hasOffer(mimeOffset, serviceOffersOffset, serviceOffset); 0370 } 0371 0372 auto matchFunc = [&mime](const ServiceTypeAndPreference &typePref) { 0373 // qCDebug(SERVICES) << " has " << typePref; 0374 if (typePref.serviceType == mime) { 0375 return true; 0376 } 0377 // TODO: should we handle inherited MIME types here? 0378 // KMimeType was in kio when this code was written, this is the only reason it's not done. 0379 // But this should matter only in a very rare case, since most code gets KServices from ksycoca. 0380 // Warning, change hasServiceType if you implement this here (and check kbuildservicefactory). 0381 return false; 0382 }; 0383 0384 // fall-back code for services that are NOT from ksycoca 0385 return std::any_of(d->m_serviceTypes.cbegin(), d->m_serviceTypes.cend(), matchFunc); 0386 } 0387 0388 QVariant KService::property(const QString &_name, QMetaType::Type t) const 0389 { 0390 Q_D(const KService); 0391 return d->property(_name, t); 0392 } 0393 0394 template<> 0395 QString KService::property<QString>(const QString &_name) const 0396 { 0397 Q_D(const KService); 0398 0399 if (_name == QLatin1String("Type")) { 0400 return d->m_strType; 0401 } else if (_name == QLatin1String("Name")) { 0402 return d->m_strName; 0403 } else if (_name == QLatin1String("Exec")) { 0404 return d->m_strExec; 0405 } else if (_name == QLatin1String("Icon")) { 0406 return d->m_strIcon; 0407 } else if (_name == QLatin1String("TerminalOptions")) { 0408 return d->m_strTerminalOptions; 0409 } else if (_name == QLatin1String("Path")) { 0410 return d->m_strWorkingDirectory; 0411 } else if (_name == QLatin1String("Comment")) { 0412 return d->m_strComment; 0413 } else if (_name == QLatin1String("GenericName")) { 0414 return d->m_strGenName; 0415 } else if (_name == QLatin1String("DesktopEntryPath")) { 0416 return d->path; 0417 } else if (_name == QLatin1String("DesktopEntryName")) { 0418 return d->m_strDesktopEntryName; 0419 } else if (_name == QLatin1String("UntranslatedName")) { 0420 return d->m_untranslatedName; 0421 } else if (_name == QLatin1String("UntranslatedGenericName")) { 0422 return d->m_untranslatedGenericName; 0423 } 0424 0425 auto it = d->m_mapProps.constFind(_name); 0426 0427 if (it != d->m_mapProps.cend()) { 0428 return it.value().toString(); 0429 } 0430 0431 return QString(); 0432 } 0433 0434 QVariant KServicePrivate::property(const QString &_name, QMetaType::Type t) const 0435 { 0436 if (_name == QLatin1String("Terminal")) { 0437 return QVariant(m_bTerminal); 0438 } else if (_name == QLatin1String("AllowAsDefault")) { 0439 return QVariant(m_bAllowAsDefault); 0440 } else if (_name == QLatin1String("InitialPreference")) { 0441 return QVariant(m_initialPreference); 0442 } else if (_name == QLatin1String("Categories")) { 0443 return QVariant(categories); 0444 } else if (_name == QLatin1String("Keywords")) { 0445 return QVariant(m_lstKeywords); 0446 } else if (_name == QLatin1String("FormFactors")) { 0447 return QVariant(m_lstFormFactors); 0448 } 0449 0450 auto it = m_mapProps.constFind(_name); 0451 if (it == m_mapProps.cend() || !it.value().isValid()) { 0452 // qCDebug(SERVICES) << "Property not found " << _name; 0453 return QVariant(); // No property set. 0454 } 0455 0456 if (it->typeId() == t) { 0457 return it.value(); // no conversion necessary 0458 } else { 0459 // All others 0460 // For instance properties defined as StringList, like MimeTypes. 0461 // XXX This API is accessible only through a friend declaration. 0462 return KConfigGroup::convertToQVariant(_name.toUtf8().constData(), it.value().toString().toUtf8(), QVariant(QMetaType(t))); 0463 } 0464 } 0465 0466 KService::List KService::allServices() 0467 { 0468 KSycoca::self()->ensureCacheValid(); 0469 return KSycocaPrivate::self()->serviceFactory()->allServices(); 0470 } 0471 0472 KService::Ptr KService::serviceByDesktopPath(const QString &_name) 0473 { 0474 KSycoca::self()->ensureCacheValid(); 0475 return KSycocaPrivate::self()->serviceFactory()->findServiceByDesktopPath(_name); 0476 } 0477 0478 KService::Ptr KService::serviceByDesktopName(const QString &_name) 0479 { 0480 KSycoca::self()->ensureCacheValid(); 0481 return KSycocaPrivate::self()->serviceFactory()->findServiceByDesktopName(_name); 0482 } 0483 0484 KService::Ptr KService::serviceByMenuId(const QString &_name) 0485 { 0486 KSycoca::self()->ensureCacheValid(); 0487 return KSycocaPrivate::self()->serviceFactory()->findServiceByMenuId(_name); 0488 } 0489 0490 KService::Ptr KService::serviceByStorageId(const QString &_storageId) 0491 { 0492 KSycoca::self()->ensureCacheValid(); 0493 return KSycocaPrivate::self()->serviceFactory()->findServiceByStorageId(_storageId); 0494 } 0495 0496 bool KService::substituteUid() const 0497 { 0498 return property<bool>(QStringLiteral("X-KDE-SubstituteUID")); 0499 } 0500 0501 QString KService::username() const 0502 { 0503 // See also KDesktopFile::tryExec() 0504 QString user = property<QString>(QStringLiteral("X-KDE-Username")); 0505 if (user.isEmpty()) { 0506 user = QString::fromLocal8Bit(qgetenv("ADMIN_ACCOUNT")); 0507 } 0508 if (user.isEmpty()) { 0509 user = QStringLiteral("root"); 0510 } 0511 return user; 0512 } 0513 0514 bool KService::showInCurrentDesktop() const 0515 { 0516 Q_D(const KService); 0517 0518 const QString envVar = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP")); 0519 0520 QList<QStringView> currentDesktops = QStringView(envVar).split(QLatin1Char(':'), Qt::SkipEmptyParts); 0521 0522 const QString kde = QStringLiteral("KDE"); 0523 if (currentDesktops.isEmpty()) { 0524 // This could be an old display manager, or e.g. a failsafe session with no desktop name 0525 // In doubt, let's say we show KDE stuff. 0526 currentDesktops.append(kde); 0527 } 0528 0529 // This algorithm is described in the desktop entry spec 0530 0531 auto it = d->m_mapProps.constFind(QStringLiteral("OnlyShowIn")); 0532 if (it != d->m_mapProps.cend()) { 0533 const QVariant &val = it.value(); 0534 if (val.isValid()) { 0535 const QStringList aList = val.toString().split(QLatin1Char(';')); 0536 return std::any_of(currentDesktops.cbegin(), currentDesktops.cend(), [&aList](const auto desktop) { 0537 return aList.contains(desktop); 0538 }); 0539 } 0540 } 0541 0542 it = d->m_mapProps.constFind(QStringLiteral("NotShowIn")); 0543 if (it != d->m_mapProps.cend()) { 0544 const QVariant &val = it.value(); 0545 if (val.isValid()) { 0546 const QStringList aList = val.toString().split(QLatin1Char(';')); 0547 return std::none_of(currentDesktops.cbegin(), currentDesktops.cend(), [&aList](const auto desktop) { 0548 return aList.contains(desktop); 0549 }); 0550 } 0551 } 0552 0553 return true; 0554 } 0555 0556 bool KService::showOnCurrentPlatform() const 0557 { 0558 Q_D(const KService); 0559 const QString platform = QCoreApplication::instance()->property("platformName").toString(); 0560 if (platform.isEmpty()) { 0561 return true; 0562 } 0563 0564 auto it = d->m_mapProps.find(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms")); 0565 if ((it != d->m_mapProps.end()) && (it->isValid())) { 0566 const QStringList aList = it->toString().split(QLatin1Char(';')); 0567 if (!aList.contains(platform)) { 0568 return false; 0569 } 0570 } 0571 0572 it = d->m_mapProps.find(QStringLiteral("X-KDE-NotShowOnQtPlatforms")); 0573 if ((it != d->m_mapProps.end()) && (it->isValid())) { 0574 const QStringList aList = it->toString().split(QLatin1Char(';')); 0575 if (aList.contains(platform)) { 0576 return false; 0577 } 0578 } 0579 return true; 0580 } 0581 0582 bool KService::noDisplay() const 0583 { 0584 if (property<bool>(QStringLiteral("NoDisplay"))) { 0585 return true; 0586 } 0587 0588 if (!showInCurrentDesktop()) { 0589 return true; 0590 } 0591 0592 if (!showOnCurrentPlatform()) { 0593 return true; 0594 } 0595 return false; 0596 } 0597 0598 QString KService::untranslatedGenericName() const 0599 { 0600 Q_D(const KService); 0601 return d->m_untranslatedGenericName; 0602 } 0603 0604 QString KService::untranslatedName() const 0605 { 0606 Q_D(const KService); 0607 return d->m_untranslatedName; 0608 } 0609 0610 QString KService::docPath() const 0611 { 0612 Q_D(const KService); 0613 0614 for (const QString &str : {QStringLiteral("X-DocPath"), QStringLiteral("DocPath")}) { 0615 auto it = d->m_mapProps.constFind(str); 0616 if (it != d->m_mapProps.cend()) { 0617 const QVariant variant = it.value(); 0618 Q_ASSERT(variant.isValid()); 0619 const QString path = variant.toString(); 0620 if (!path.isEmpty()) { 0621 return path; 0622 } 0623 } 0624 } 0625 0626 return {}; 0627 } 0628 0629 bool KService::allowMultipleFiles() const 0630 { 0631 Q_D(const KService); 0632 // Can we pass multiple files on the command line or do we have to start the application for every single file ? 0633 return (d->m_strExec.contains(QLatin1String("%F")) // 0634 || d->m_strExec.contains(QLatin1String("%U")) // 0635 || d->m_strExec.contains(QLatin1String("%N")) // 0636 || d->m_strExec.contains(QLatin1String("%D"))); 0637 } 0638 0639 QStringList KService::categories() const 0640 { 0641 Q_D(const KService); 0642 return d->categories; 0643 } 0644 0645 QString KService::menuId() const 0646 { 0647 Q_D(const KService); 0648 return d->menuId; 0649 } 0650 0651 void KService::setMenuId(const QString &_menuId) 0652 { 0653 Q_D(KService); 0654 d->menuId = _menuId; 0655 } 0656 0657 QString KService::storageId() const 0658 { 0659 Q_D(const KService); 0660 return d->storageId(); 0661 } 0662 0663 // not sure this is still used anywhere... 0664 QString KService::locateLocal() const 0665 { 0666 Q_D(const KService); 0667 if (d->menuId.isEmpty() // 0668 || entryPath().startsWith(QLatin1String(".hidden")) // 0669 || (QDir::isRelativePath(entryPath()) && d->categories.isEmpty())) { 0670 return KDesktopFile::locateLocal(entryPath()); 0671 } 0672 0673 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/applications/") + d->menuId; 0674 } 0675 0676 QString KService::newServicePath(bool showInMenu, const QString &suggestedName, QString *menuId, const QStringList *reservedMenuIds) 0677 { 0678 Q_UNUSED(showInMenu); // TODO KDE5: remove argument 0679 0680 QString base = suggestedName; 0681 QString result; 0682 for (int i = 1; true; i++) { 0683 if (i == 1) { 0684 result = base + QStringLiteral(".desktop"); 0685 } else { 0686 result = base + QStringLiteral("-%1.desktop").arg(i); 0687 } 0688 0689 if (reservedMenuIds && reservedMenuIds->contains(result)) { 0690 continue; 0691 } 0692 0693 // Lookup service by menu-id 0694 KService::Ptr s = serviceByMenuId(result); 0695 if (s) { 0696 continue; 0697 } 0698 0699 if (!QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("applications/") + result).isEmpty()) { 0700 continue; 0701 } 0702 0703 break; 0704 } 0705 if (menuId) { 0706 *menuId = result; 0707 } 0708 0709 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/applications/") + result; 0710 } 0711 0712 bool KService::isApplication() const 0713 { 0714 Q_D(const KService); 0715 return d->m_strType == QLatin1String("Application"); 0716 } 0717 0718 QString KService::exec() const 0719 { 0720 Q_D(const KService); 0721 return d->m_strExec; 0722 } 0723 0724 QString KService::icon() const 0725 { 0726 Q_D(const KService); 0727 return d->m_strIcon; 0728 } 0729 0730 QString KService::terminalOptions() const 0731 { 0732 Q_D(const KService); 0733 return d->m_strTerminalOptions; 0734 } 0735 0736 bool KService::terminal() const 0737 { 0738 Q_D(const KService); 0739 return d->m_bTerminal; 0740 } 0741 0742 bool KService::runOnDiscreteGpu() const 0743 { 0744 QVariant prop = property<bool>(QStringLiteral("PrefersNonDefaultGPU")); 0745 if (!prop.isValid()) { 0746 // For backwards compatibility 0747 prop = property<bool>(QStringLiteral("X-KDE-RunOnDiscreteGpu")); 0748 } 0749 0750 return prop.isValid() && prop.toBool(); 0751 } 0752 0753 QString KService::desktopEntryName() const 0754 { 0755 Q_D(const KService); 0756 return d->m_strDesktopEntryName; 0757 } 0758 0759 QString KService::workingDirectory() const 0760 { 0761 Q_D(const KService); 0762 return d->m_strWorkingDirectory; 0763 } 0764 0765 QString KService::comment() const 0766 { 0767 Q_D(const KService); 0768 return d->m_strComment; 0769 } 0770 0771 QString KService::genericName() const 0772 { 0773 Q_D(const KService); 0774 return d->m_strGenName; 0775 } 0776 0777 QStringList KService::keywords() const 0778 { 0779 Q_D(const KService); 0780 return d->m_lstKeywords; 0781 } 0782 0783 QStringList KService::mimeTypes() const 0784 { 0785 Q_D(const KService); 0786 0787 QMimeDatabase db; 0788 QStringList ret; 0789 0790 for (const KService::ServiceTypeAndPreference &s : d->m_serviceTypes) { 0791 const QString servType = s.serviceType; 0792 if (db.mimeTypeForName(servType).isValid()) { // keep only mimetypes, filter out servicetypes 0793 ret.append(servType); 0794 } 0795 } 0796 return ret; 0797 } 0798 0799 QStringList KService::schemeHandlers() const 0800 { 0801 Q_D(const KService); 0802 0803 QStringList ret; 0804 0805 const QLatin1String schemeHandlerPrefix("x-scheme-handler/"); 0806 for (const KService::ServiceTypeAndPreference &s : d->m_serviceTypes) { 0807 const QString servType = s.serviceType; 0808 if (servType.startsWith(schemeHandlerPrefix)) { 0809 ret.append(servType.mid(schemeHandlerPrefix.size())); 0810 } 0811 } 0812 0813 return ret; 0814 } 0815 0816 QStringList KService::supportedProtocols() const 0817 { 0818 Q_D(const KService); 0819 0820 QStringList ret; 0821 0822 ret << schemeHandlers(); 0823 0824 const QStringList protocols = property<QStringList>(QStringLiteral("X-KDE-Protocols")); 0825 for (const QString &protocol : protocols) { 0826 if (!ret.contains(protocol)) { 0827 ret.append(protocol); 0828 } 0829 } 0830 0831 return ret; 0832 } 0833 0834 int KService::initialPreference() const 0835 { 0836 Q_D(const KService); 0837 return d->m_initialPreference; 0838 } 0839 0840 void KService::setTerminal(bool b) 0841 { 0842 Q_D(KService); 0843 d->m_bTerminal = b; 0844 } 0845 0846 void KService::setTerminalOptions(const QString &options) 0847 { 0848 Q_D(KService); 0849 d->m_strTerminalOptions = options; 0850 } 0851 0852 void KService::setExec(const QString &exec) 0853 { 0854 Q_D(KService); 0855 0856 if (!exec.isEmpty()) { 0857 d->m_strExec = exec; 0858 d->path.clear(); 0859 } 0860 } 0861 0862 void KService::setWorkingDirectory(const QString &workingDir) 0863 { 0864 Q_D(KService); 0865 0866 if (!workingDir.isEmpty()) { 0867 d->m_strWorkingDirectory = workingDir; 0868 d->path.clear(); 0869 } 0870 } 0871 0872 QList<KService::ServiceTypeAndPreference> KService::_k_accessServiceTypes() 0873 { 0874 Q_D(KService); 0875 0876 return d->m_serviceTypes; 0877 } 0878 0879 QList<KServiceAction> KService::actions() const 0880 { 0881 Q_D(const KService); 0882 return d->m_actions; 0883 } 0884 0885 QString KService::aliasFor() const 0886 { 0887 return KServiceUtilPrivate::completeBaseName(property<QString>(QStringLiteral("X-KDE-AliasFor"))); 0888 } 0889 0890 void KService::setActions(const QList<KServiceAction> &actions) 0891 { 0892 Q_D(KService); 0893 d->m_actions = actions; 0894 } 0895 0896 std::optional<bool> KService::startupNotify() const 0897 { 0898 Q_D(const KService); 0899 0900 if (QVariant value = d->m_mapProps.value(QStringLiteral("StartupNotify")); value.isValid()) { 0901 return value.toBool(); 0902 } 0903 0904 if (QVariant value = d->m_mapProps.value(QStringLiteral("X-KDE-StartupNotify")); value.isValid()) { 0905 return value.toBool(); 0906 } 0907 0908 return {}; 0909 }