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
22static 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
50static 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
64KService::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
76KService::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
87KService::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
96void 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
121bool 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

source code of kservice/src/services/kapplicationtrader.cpp