File indexing completed on 2024-12-01 09:55:28
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Torben Weis <weis@kde.org> 0004 SPDX-FileCopyrightText: 2006-2020 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kapplicationtrader.h" 0010 0011 #include "kmimetypefactory_p.h" 0012 #include "kservicefactory_p.h" 0013 #include "kservicetypefactory_p.h" 0014 #include "ksycoca.h" 0015 #include "ksycoca_p.h" 0016 #include "servicesdebug.h" 0017 0018 #include <QMimeDatabase> 0019 0020 #include <KConfigGroup> 0021 #include <KSharedConfig> 0022 0023 static KService::List mimeTypeSycocaServiceOffers(const QString &mimeType) 0024 { 0025 KService::List lst; 0026 QMimeDatabase db; 0027 QString mime = db.mimeTypeForName(mimeType).name(); 0028 if (mime.isEmpty()) { 0029 if (!mimeType.startsWith(QLatin1String("x-scheme-handler/"))) { // don't warn for unknown scheme handler mimetypes 0030 qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found"; 0031 return lst; // empty 0032 } 0033 mime = mimeType; 0034 } 0035 KSycoca::self()->ensureCacheValid(); 0036 KMimeTypeFactory *factory = KSycocaPrivate::self()->mimeTypeFactory(); 0037 const int offset = factory->entryOffset(mime); 0038 if (!offset) { 0039 qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found"; 0040 return lst; // empty 0041 } 0042 const int serviceOffersOffset = factory->serviceOffersOffset(mime); 0043 if (serviceOffersOffset > -1) { 0044 lst = KSycocaPrivate::self()->serviceFactory()->serviceOffers(offset, serviceOffersOffset); 0045 } 0046 return lst; 0047 } 0048 0049 // Filter the offers for the requested MIME type in order to keep only applications. 0050 static void filterMimeTypeOffers(KService::List &list) // static, internal 0051 { 0052 KServiceType::Ptr genericServiceTypePtr = KServiceType::serviceType(QStringLiteral("Application")); 0053 Q_ASSERT(genericServiceTypePtr); 0054 0055 KSycoca::self()->ensureCacheValid(); 0056 KServiceFactory *serviceFactory = KSycocaPrivate::self()->serviceFactory(); 0057 0058 // Remove non-Applications (TODO KF6: kill plugin desktop files, then kill this code) 0059 auto removeFunc = [&](const KService::Ptr &serv) { 0060 return !serviceFactory->hasOffer(genericServiceTypePtr, serv); 0061 }; 0062 list.erase(std::remove_if(list.begin(), list.end(), removeFunc), list.end()); 0063 } 0064 0065 static void applyFilter(KService::List &list, KApplicationTrader::FilterFunc filterFunc, bool mustShowInCurrentDesktop) 0066 { 0067 if (list.isEmpty()) { 0068 return; 0069 } 0070 0071 // Find all services matching the constraint 0072 // and remove the other ones 0073 auto removeFunc = [&](const KService::Ptr &serv) { 0074 return (filterFunc && !filterFunc(serv)) || (mustShowInCurrentDesktop && !serv->showInCurrentDesktop()); 0075 }; 0076 list.erase(std::remove_if(list.begin(), list.end(), removeFunc), list.end()); 0077 } 0078 0079 KService::List KApplicationTrader::query(FilterFunc filterFunc) 0080 { 0081 // Get all applications 0082 KSycoca::self()->ensureCacheValid(); 0083 KServiceType::Ptr servTypePtr = KSycocaPrivate::self()->serviceTypeFactory()->findServiceTypeByName(QStringLiteral("Application")); 0084 Q_ASSERT(servTypePtr); 0085 if (servTypePtr->serviceOffersOffset() == -1) { 0086 return KService::List(); 0087 } 0088 0089 KService::List lst = KSycocaPrivate::self()->serviceFactory()->serviceOffers(servTypePtr); 0090 0091 applyFilter(lst, filterFunc, true); // true = filter out service with NotShowIn=KDE or equivalent 0092 0093 qCDebug(SERVICES) << "query returning" << lst.count() << "offers"; 0094 return lst; 0095 } 0096 0097 KService::List KApplicationTrader::queryByMimeType(const QString &mimeType, FilterFunc filterFunc) 0098 { 0099 // Get all services of this MIME type. 0100 KService::List lst = mimeTypeSycocaServiceOffers(mimeType); 0101 filterMimeTypeOffers(lst); 0102 0103 applyFilter(lst, filterFunc, false); // false = allow NotShowIn=KDE services listed in mimeapps.list 0104 0105 qCDebug(SERVICES) << "query for mimeType" << mimeType << "returning" << lst.count() << "offers"; 0106 return lst; 0107 } 0108 0109 KService::Ptr KApplicationTrader::preferredService(const QString &mimeType) 0110 { 0111 const KService::List offers = queryByMimeType(mimeType); 0112 if (!offers.isEmpty()) { 0113 return offers.at(0); 0114 } 0115 return KService::Ptr(); 0116 } 0117 0118 void KApplicationTrader::setPreferredService(const QString &mimeType, const KService::Ptr service) 0119 { 0120 if (mimeType.isEmpty() || !(service && service->isValid())) { 0121 return; 0122 } 0123 KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), KConfig::NoGlobals, QStandardPaths::GenericConfigLocation); 0124 0125 // Save the default application according to mime-apps-spec 1.0 0126 KConfigGroup defaultApp(profile, "Default Applications"); 0127 defaultApp.writeXdgListEntry(mimeType, QStringList(service->storageId())); 0128 0129 KConfigGroup addedApps(profile, "Added Associations"); 0130 QStringList apps = addedApps.readXdgListEntry(mimeType); 0131 apps.removeAll(service->storageId()); 0132 apps.prepend(service->storageId()); // make it the preferred app 0133 addedApps.writeXdgListEntry(mimeType, apps); 0134 0135 profile->sync(); 0136 0137 // Also make sure the "auto embed" setting for this MIME type is off 0138 KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig(QStringLiteral("filetypesrc"), KConfig::NoGlobals); 0139 fileTypesConfig->group("EmbedSettings").writeEntry(QStringLiteral("embed-") + mimeType, false); 0140 fileTypesConfig->sync(); 0141 } 0142 0143 bool KApplicationTrader::isSubsequence(const QString &pattern, const QString &text, Qt::CaseSensitivity cs) 0144 { 0145 if (pattern.isEmpty()) { 0146 return false; 0147 } 0148 const bool chk_case = cs == Qt::CaseSensitive; 0149 0150 auto textIt = text.cbegin(); 0151 auto patternIt = pattern.cbegin(); 0152 for (; textIt != text.cend() && patternIt != pattern.cend(); ++textIt) { 0153 if ((chk_case && *textIt == *patternIt) || (!chk_case && textIt->toLower() == patternIt->toLower())) { 0154 ++patternIt; 0155 } 0156 } 0157 return patternIt == pattern.cend(); 0158 }