| 1 | /* |
| 2 | This file is part of the KDE libraries |
| 3 | SPDX-FileCopyrightText: 2000 Torben Weis <weis@kde.org> |
| 4 | SPDX-FileCopyrightText: 2006-2020 David Faure <faure@kde.org> |
| 5 | |
| 6 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #include "kapplicationtrader.h" |
| 10 | |
| 11 | #include "kmimetypefactory_p.h" |
| 12 | #include "kservicefactory_p.h" |
| 13 | #include "ksycoca.h" |
| 14 | #include "ksycoca_p.h" |
| 15 | #include "servicesdebug.h" |
| 16 | |
| 17 | #include <QMimeDatabase> |
| 18 | |
| 19 | #include <KConfigGroup> |
| 20 | #include <KSharedConfig> |
| 21 | |
| 22 | static KService::List mimeTypeSycocaServiceOffers(const QString &mimeType) |
| 23 | { |
| 24 | KService::List lst; |
| 25 | QMimeDatabase db; |
| 26 | QString mime = db.mimeTypeForName(nameOrAlias: mimeType).name(); |
| 27 | if (mime.isEmpty()) { |
| 28 | if (!mimeType.startsWith(s: QLatin1String("x-scheme-handler/" ))) { // don't warn for unknown scheme handler mimetypes |
| 29 | qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found" ; |
| 30 | return lst; // empty |
| 31 | } |
| 32 | mime = mimeType; |
| 33 | } |
| 34 | KSycoca::self()->ensureCacheValid(); |
| 35 | KMimeTypeFactory *factory = KSycocaPrivate::self()->mimeTypeFactory(); |
| 36 | const int offset = factory->entryOffset(mimeTypeName: mime); |
| 37 | if (!offset) { |
| 38 | if (!mimeType.startsWith(s: QLatin1String("x-scheme-handler/" ))) { // don't warn for unknown scheme handler mimetypes |
| 39 | qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found" ; |
| 40 | } |
| 41 | return lst; // empty |
| 42 | } |
| 43 | const int serviceOffersOffset = factory->serviceOffersOffset(mimeTypeName: mime); |
| 44 | if (serviceOffersOffset > -1) { |
| 45 | lst = KSycocaPrivate::self()->serviceFactory()->serviceOffers(serviceTypeOffset: offset, serviceOffersOffset); |
| 46 | } |
| 47 | return lst; |
| 48 | } |
| 49 | |
| 50 | static void applyFilter(KService::List &list, KApplicationTrader::FilterFunc filterFunc, bool mustShowInCurrentDesktop) |
| 51 | { |
| 52 | if (list.isEmpty()) { |
| 53 | return; |
| 54 | } |
| 55 | |
| 56 | // Find all services matching the constraint |
| 57 | // and remove the other ones |
| 58 | auto removeFunc = [&](const KService::Ptr &serv) { |
| 59 | return (filterFunc && !filterFunc(serv)) || (mustShowInCurrentDesktop && !serv->showInCurrentDesktop()); |
| 60 | }; |
| 61 | list.erase(abegin: std::remove_if(first: list.begin(), last: list.end(), pred: removeFunc), aend: list.end()); |
| 62 | } |
| 63 | |
| 64 | KService::List KApplicationTrader::query(FilterFunc filterFunc) |
| 65 | { |
| 66 | // Get all applications |
| 67 | KSycoca::self()->ensureCacheValid(); |
| 68 | KService::List lst = KSycocaPrivate::self()->serviceFactory()->allServices(); |
| 69 | |
| 70 | applyFilter(list&: lst, filterFunc, mustShowInCurrentDesktop: true); // true = filter out service with NotShowIn=KDE or equivalent |
| 71 | |
| 72 | qCDebug(SERVICES) << "query returning" << lst.count() << "offers" ; |
| 73 | return lst; |
| 74 | } |
| 75 | |
| 76 | KService::List KApplicationTrader::queryByMimeType(const QString &mimeType, FilterFunc filterFunc) |
| 77 | { |
| 78 | // Get all services of this MIME type. |
| 79 | KService::List lst = mimeTypeSycocaServiceOffers(mimeType); |
| 80 | |
| 81 | applyFilter(list&: lst, filterFunc, mustShowInCurrentDesktop: false); // false = allow NotShowIn=KDE services listed in mimeapps.list |
| 82 | |
| 83 | qCDebug(SERVICES) << "query for mimeType" << mimeType << "returning" << lst.count() << "offers" ; |
| 84 | return lst; |
| 85 | } |
| 86 | |
| 87 | KService::Ptr KApplicationTrader::preferredService(const QString &mimeType) |
| 88 | { |
| 89 | const KService::List offers = queryByMimeType(mimeType); |
| 90 | if (!offers.isEmpty()) { |
| 91 | return offers.at(i: 0); |
| 92 | } |
| 93 | return KService::Ptr(); |
| 94 | } |
| 95 | |
| 96 | void KApplicationTrader::setPreferredService(const QString &mimeType, const KService::Ptr service) |
| 97 | { |
| 98 | if (mimeType.isEmpty() || !(service && service->isValid())) { |
| 99 | return; |
| 100 | } |
| 101 | KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list" ), mode: KConfig::NoGlobals, type: QStandardPaths::GenericConfigLocation); |
| 102 | |
| 103 | // Save the default application according to mime-apps-spec 1.0 |
| 104 | KConfigGroup defaultApp(profile, QStringLiteral("Default Applications" )); |
| 105 | defaultApp.writeXdgListEntry(pKey: mimeType, value: QStringList(service->storageId())); |
| 106 | |
| 107 | KConfigGroup addedApps(profile, QStringLiteral("Added Associations" )); |
| 108 | QStringList apps = addedApps.readXdgListEntry(pKey: mimeType); |
| 109 | apps.removeAll(t: service->storageId()); |
| 110 | apps.prepend(t: service->storageId()); // make it the preferred app |
| 111 | addedApps.writeXdgListEntry(pKey: mimeType, value: apps); |
| 112 | |
| 113 | profile->sync(); |
| 114 | |
| 115 | // Also make sure the "auto embed" setting for this MIME type is off |
| 116 | KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig(QStringLiteral("filetypesrc" ), mode: KConfig::NoGlobals); |
| 117 | fileTypesConfig->group(QStringLiteral("EmbedSettings" )).writeEntry(QStringLiteral("embed-" ) + mimeType, value: false); |
| 118 | fileTypesConfig->sync(); |
| 119 | } |
| 120 | |
| 121 | bool KApplicationTrader::isSubsequence(const QString &pattern, const QString &text, Qt::CaseSensitivity cs) |
| 122 | { |
| 123 | if (pattern.isEmpty()) { |
| 124 | return false; |
| 125 | } |
| 126 | const bool chk_case = cs == Qt::CaseSensitive; |
| 127 | |
| 128 | auto textIt = text.cbegin(); |
| 129 | auto patternIt = pattern.cbegin(); |
| 130 | for (; textIt != text.cend() && patternIt != pattern.cend(); ++textIt) { |
| 131 | if ((chk_case && *textIt == *patternIt) || (!chk_case && textIt->toLower() == patternIt->toLower())) { |
| 132 | ++patternIt; |
| 133 | } |
| 134 | } |
| 135 | return patternIt == pattern.cend(); |
| 136 | } |
| 137 | |