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 | |