File indexing completed on 2023-09-24 04:11:10
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org> 0004 SPDX-FileCopyrightText: 2006 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #include "kservicetypeprofile.h" 0010 #include "kservicetypeprofile_p.h" 0011 0012 #include "kservice.h" 0013 #include "kserviceoffer.h" 0014 #include "kservicetype.h" 0015 #include "ksycoca_p.h" 0016 0017 #include <KConfig> 0018 #include <KConfigGroup> 0019 0020 #include <QHash> 0021 #include <QMutex> 0022 #include <QtAlgorithms> 0023 0024 // servicetype -> profile 0025 class KServiceTypeProfiles : public QHash<QString, KServiceTypeProfileEntry *> 0026 { 0027 public: 0028 KServiceTypeProfiles() 0029 { 0030 m_parsed = false; 0031 ensureParsed(); 0032 } 0033 ~KServiceTypeProfiles() 0034 { 0035 clear(); 0036 } 0037 void clear() 0038 { 0039 QMutexLocker lock(&m_mutex); 0040 qDeleteAll(*this); 0041 QHash<QString, KServiceTypeProfileEntry *>::clear(); 0042 m_parsed = false; 0043 } 0044 bool hasProfile(const QString &serviceType) 0045 { 0046 QMutexLocker lock(&m_mutex); 0047 ensureParsed(); 0048 return contains(serviceType); 0049 } 0050 void ensureParsed(); // mutex must be locked when calling this 0051 QMutex m_mutex; 0052 0053 private: 0054 bool m_parsed; 0055 }; 0056 0057 Q_GLOBAL_STATIC(KServiceTypeProfiles, s_serviceTypeProfiles) 0058 0059 void KServiceTypeProfiles::ensureParsed() 0060 { 0061 if (m_parsed) { 0062 return; 0063 } 0064 m_parsed = true; 0065 0066 // Read the service type profiles from servicetype_profilerc 0067 // See writeServiceTypeProfile for a description of the file format. 0068 // ### Since this new format names groups after servicetypes maybe we can even 0069 // avoid doing any init upfront, and just look up the group when asked... 0070 KConfig configFile(QStringLiteral("servicetype_profilerc"), KConfig::NoGlobals); 0071 const QStringList tmpList = configFile.groupList(); 0072 for (const auto &type : tmpList) { 0073 KConfigGroup config(&configFile, type); 0074 const int count = config.readEntry("NumberOfEntries", 0); 0075 KServiceTypeProfileEntry *p = this->value(type, nullptr); 0076 if (!p) { 0077 p = new KServiceTypeProfileEntry(); 0078 this->insert(type, p); 0079 } 0080 0081 for (int i = 0; i < count; ++i) { 0082 const QString num = QLatin1String("Entry") + QString::number(i); 0083 const QString serviceId = config.readEntry(num + QLatin1String("_Service"), QString()); 0084 if (!serviceId.isEmpty()) { 0085 const int pref = config.readEntry(num + QLatin1String("_Preference"), 0); 0086 // qDebug() << "adding service " << serviceId << " to profile for " << type << " with preference " << pref; 0087 p->addService(serviceId, pref); 0088 } 0089 } 0090 } 0091 } 0092 0093 // static 0094 void KServiceTypeProfile::clearCache() 0095 { 0096 if (s_serviceTypeProfiles.exists()) { 0097 s_serviceTypeProfiles()->clear(); 0098 } 0099 } 0100 0101 /** 0102 * Returns the offers in the profile for the requested service type. 0103 * @param list list of offers (including initialPreference) 0104 * @param servicetype the service type 0105 * @return the weighted and sorted offer list 0106 * @internal used by KServiceTypeTrader 0107 */ 0108 namespace KServiceTypeProfile 0109 { 0110 KServiceOfferList sortServiceTypeOffers(const KServiceOfferList &list, const QString &servicetype); 0111 } 0112 0113 KServiceOfferList KServiceTypeProfile::sortServiceTypeOffers(const KServiceOfferList &list, const QString &serviceType) 0114 { 0115 QMutexLocker lock(&s_serviceTypeProfiles()->m_mutex); 0116 s_serviceTypeProfiles()->ensureParsed(); 0117 KServiceTypeProfileEntry *profile = s_serviceTypeProfiles()->value(serviceType, nullptr); 0118 0119 KServiceOfferList offers; 0120 0121 for (const auto &offer : list) { 0122 const KService::Ptr servPtr = offer.service(); 0123 // qDebug() << "KServiceTypeProfile::offers considering " << servPtr->storageId(); 0124 // Look into the profile (if there's one), to find this service's preference. 0125 bool foundInProfile = false; 0126 if (profile) { 0127 auto it2 = profile->m_mapServices.constFind(servPtr->storageId()); 0128 if (it2 != profile->m_mapServices.constEnd()) { 0129 const int pref = it2.value(); 0130 // qDebug() << "found in mapServices pref=" << pref; 0131 if (pref > 0) { // 0 disables the service 0132 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 69) 0133 offers.append(KServiceOffer(servPtr, pref, 0, servPtr->allowAsDefault())); 0134 #else 0135 offers.append(KServiceOffer(servPtr, pref, 0)); 0136 #endif 0137 } 0138 foundInProfile = true; 0139 } 0140 } 0141 if (!foundInProfile) { 0142 // This offer isn't in the profile 0143 // This can be because we have no profile at all, or because the 0144 // services have been installed after the profile was written, 0145 // but it's also the case for any service that's neither App nor ReadOnlyPart, e.g. RenameDlg/Plugin 0146 // qDebug() << "not found in mapServices. Appending."; 0147 0148 // If there's a profile, we use 0 as the preference to ensure new apps don't take over existing apps (which default to 1) 0149 offers.append(KServiceOffer(servPtr, 0150 profile ? 0 : offer.preference(), 0151 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 69) 0152 0, 0153 servPtr->allowAsDefault())); 0154 #else 0155 0)); 0156 #endif 0157 } 0158 } 0159 0160 std::stable_sort(offers.begin(), offers.end()); 0161 0162 // qDebug() << "KServiceTypeProfile::offers returning " << offers.count() << " offers"; 0163 return offers; 0164 } 0165 0166 bool KServiceTypeProfile::hasProfile(const QString &serviceType) 0167 { 0168 return s_serviceTypeProfiles()->hasProfile(serviceType); 0169 } 0170 0171 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 66) 0172 void KServiceTypeProfile::writeServiceTypeProfile(const QString &serviceType, const KService::List &services, const KService::List &disabledServices) 0173 { 0174 /* 0175 * [ServiceType] 0176 * NumEntries=3 0177 * Entry0_Service=serv.desktop 0178 * Entry0_Preference=10 0179 * Entry1_Service=otherserv.desktop 0180 * Entry1_Preference=5 0181 * Entry2_Service=broken_service.desktop 0182 * Entry2_Preference=0 0183 */ 0184 0185 KConfig configFile(QStringLiteral("servicetype_profilerc"), KConfig::SimpleConfig); 0186 configFile.deleteGroup(serviceType); 0187 0188 KConfigGroup config(&configFile, serviceType); 0189 const int count = services.count(); 0190 config.writeEntry("NumberOfEntries", count + disabledServices.count()); 0191 KService::List::ConstIterator servit = services.begin(); 0192 int i = 0; 0193 for (; servit != services.end(); ++servit, ++i) { 0194 if (*servit) { 0195 const QString num = QLatin1String("Entry") + QString::number(i); 0196 config.writeEntry(num + QLatin1String("_Service"), (*servit)->storageId()); 0197 config.writeEntry(num + QLatin1String("_Preference"), count - i); 0198 } 0199 } 0200 servit = disabledServices.begin(); 0201 for (; servit != disabledServices.end(); ++servit, ++i) { 0202 if (*servit) { 0203 const QString num = QLatin1String("Entry") + QString::number(i); 0204 config.writeEntry(num + QLatin1String("_Service"), (*servit)->storageId()); 0205 config.writeEntry(num + QLatin1String("_Preference"), 0); 0206 } 0207 } 0208 configFile.sync(); 0209 0210 // Drop the whole cache... 0211 clearCache(); 0212 } 0213 #endif 0214 0215 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 66) 0216 void KServiceTypeProfile::deleteServiceTypeProfile(const QString &serviceType) 0217 { 0218 KConfig config(QStringLiteral("servicetype_profilerc"), KConfig::SimpleConfig); 0219 config.deleteGroup(serviceType); 0220 config.sync(); 0221 0222 // Not threadsafe, but well the whole idea of using this method isn't 0223 // threadsafe in the first place. 0224 if (s_serviceTypeProfiles.exists()) { 0225 delete s_serviceTypeProfiles()->take(serviceType); 0226 } 0227 } 0228 #endif