File indexing completed on 2024-12-01 03:42:10

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 "ksycoca.h"
0014 #include "ksycoca_p.h"
0015 #include "servicesdebug.h"
0016 
0017 #include <QMimeDatabase>
0018 
0019 #include <KConfigGroup>
0020 #include <KSharedConfig>
0021 
0022 static KService::List mimeTypeSycocaServiceOffers(const QString &mimeType)
0023 {
0024     KService::List lst;
0025     QMimeDatabase db;
0026     QString mime = db.mimeTypeForName(mimeType).name();
0027     if (mime.isEmpty()) {
0028         if (!mimeType.startsWith(QLatin1String("x-scheme-handler/"))) { // don't warn for unknown scheme handler mimetypes
0029             qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found";
0030             return lst; // empty
0031         }
0032         mime = mimeType;
0033     }
0034     KSycoca::self()->ensureCacheValid();
0035     KMimeTypeFactory *factory = KSycocaPrivate::self()->mimeTypeFactory();
0036     const int offset = factory->entryOffset(mime);
0037     if (!offset) {
0038         qCWarning(SERVICES) << "KApplicationTrader: mimeType" << mimeType << "not found";
0039         return lst; // empty
0040     }
0041     const int serviceOffersOffset = factory->serviceOffersOffset(mime);
0042     if (serviceOffersOffset > -1) {
0043         lst = KSycocaPrivate::self()->serviceFactory()->serviceOffers(offset, serviceOffersOffset);
0044     }
0045     return lst;
0046 }
0047 
0048 static void applyFilter(KService::List &list, KApplicationTrader::FilterFunc filterFunc, bool mustShowInCurrentDesktop)
0049 {
0050     if (list.isEmpty()) {
0051         return;
0052     }
0053 
0054     // Find all services matching the constraint
0055     // and remove the other ones
0056     auto removeFunc = [&](const KService::Ptr &serv) {
0057         return (filterFunc && !filterFunc(serv)) || (mustShowInCurrentDesktop && !serv->showInCurrentDesktop());
0058     };
0059     list.erase(std::remove_if(list.begin(), list.end(), removeFunc), list.end());
0060 }
0061 
0062 KService::List KApplicationTrader::query(FilterFunc filterFunc)
0063 {
0064     // Get all applications
0065     KSycoca::self()->ensureCacheValid();
0066     KService::List lst = KSycocaPrivate::self()->serviceFactory()->allServices();
0067 
0068     applyFilter(lst, filterFunc, true); // true = filter out service with NotShowIn=KDE or equivalent
0069 
0070     qCDebug(SERVICES) << "query returning" << lst.count() << "offers";
0071     return lst;
0072 }
0073 
0074 KService::List KApplicationTrader::queryByMimeType(const QString &mimeType, FilterFunc filterFunc)
0075 {
0076     // Get all services of this MIME type.
0077     KService::List lst = mimeTypeSycocaServiceOffers(mimeType);
0078 
0079     applyFilter(lst, filterFunc, false); // false = allow NotShowIn=KDE services listed in mimeapps.list
0080 
0081     qCDebug(SERVICES) << "query for mimeType" << mimeType << "returning" << lst.count() << "offers";
0082     return lst;
0083 }
0084 
0085 KService::Ptr KApplicationTrader::preferredService(const QString &mimeType)
0086 {
0087     const KService::List offers = queryByMimeType(mimeType);
0088     if (!offers.isEmpty()) {
0089         return offers.at(0);
0090     }
0091     return KService::Ptr();
0092 }
0093 
0094 void KApplicationTrader::setPreferredService(const QString &mimeType, const KService::Ptr service)
0095 {
0096     if (mimeType.isEmpty() || !(service && service->isValid())) {
0097         return;
0098     }
0099     KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), KConfig::NoGlobals, QStandardPaths::GenericConfigLocation);
0100 
0101     // Save the default application according to mime-apps-spec 1.0
0102     KConfigGroup defaultApp(profile, QStringLiteral("Default Applications"));
0103     defaultApp.writeXdgListEntry(mimeType, QStringList(service->storageId()));
0104 
0105     KConfigGroup addedApps(profile, QStringLiteral("Added Associations"));
0106     QStringList apps = addedApps.readXdgListEntry(mimeType);
0107     apps.removeAll(service->storageId());
0108     apps.prepend(service->storageId()); // make it the preferred app
0109     addedApps.writeXdgListEntry(mimeType, apps);
0110 
0111     profile->sync();
0112 
0113     // Also make sure the "auto embed" setting for this MIME type is off
0114     KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig(QStringLiteral("filetypesrc"), KConfig::NoGlobals);
0115     fileTypesConfig->group(QStringLiteral("EmbedSettings")).writeEntry(QStringLiteral("embed-") + mimeType, false);
0116     fileTypesConfig->sync();
0117 }
0118 
0119 bool KApplicationTrader::isSubsequence(const QString &pattern, const QString &text, Qt::CaseSensitivity cs)
0120 {
0121     if (pattern.isEmpty()) {
0122         return false;
0123     }
0124     const bool chk_case = cs == Qt::CaseSensitive;
0125 
0126     auto textIt = text.cbegin();
0127     auto patternIt = pattern.cbegin();
0128     for (; textIt != text.cend() && patternIt != pattern.cend(); ++textIt) {
0129         if ((chk_case && *textIt == *patternIt) || (!chk_case && textIt->toLower() == patternIt->toLower())) {
0130             ++patternIt;
0131         }
0132     }
0133     return patternIt == pattern.cend();
0134 }