1 | /* This file is part of the KDE libraries |
2 | * Copyright 2008 David Faure <faure@kde.org> |
3 | * |
4 | * This library is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU Lesser General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License or ( at |
7 | * your option ) version 3 or, at the discretion of KDE e.V. ( which shall |
8 | * act as a proxy as in section 14 of the GPLv3 ), any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "kmimeassociations.h" |
22 | #include <kmimetype.h> |
23 | #include <kmimetyperepository_p.h> |
24 | #include <kservice.h> |
25 | #include <kconfiggroup.h> |
26 | #include <kconfig.h> |
27 | #include <kdebug.h> |
28 | #include <kglobal.h> |
29 | #include <kstandarddirs.h> |
30 | |
31 | KMimeAssociations::KMimeAssociations(KOfferHash& offerHash) |
32 | : m_offerHash(offerHash) |
33 | { |
34 | } |
35 | |
36 | /* |
37 | |
38 | The goal of this class is to parse mimeapps.list files, which are used to |
39 | let users configure the application-mimetype associations. |
40 | |
41 | Example file: |
42 | |
43 | [Added Associations] |
44 | text/plain=kate.desktop; |
45 | |
46 | [Removed Associations] |
47 | text/plain=gnome-gedit.desktop;gnu-emacs.desktop; |
48 | |
49 | |
50 | |
51 | */ |
52 | |
53 | bool KMimeAssociations::parseAllMimeAppsList() |
54 | { |
55 | // Using the "merged view" from KConfig is not enough since we -add- at every level, we don't replace. |
56 | const QStringList mimeappsFiles = KGlobal::dirs()->findAllResources("xdgdata-apps" , "mimeapps.list" ); |
57 | if (mimeappsFiles.isEmpty()) |
58 | return false; |
59 | |
60 | int basePreference = 1000; // start high :) |
61 | QListIterator<QString> mimeappsIter( mimeappsFiles ); |
62 | mimeappsIter.toBack(); |
63 | while (mimeappsIter.hasPrevious()) { // global first, then local. |
64 | const QString mimeappsFile = mimeappsIter.previous(); |
65 | kDebug(7021) << "Parsing" << mimeappsFile; |
66 | parseMimeAppsList(mimeappsFile, basePreference); |
67 | basePreference += 50; |
68 | } |
69 | return true; |
70 | } |
71 | |
72 | void KMimeAssociations::parseMimeAppsList(const QString& file, int basePreference) |
73 | { |
74 | KConfig profile(file, KConfig::SimpleConfig); |
75 | parseAddedAssociations(KConfigGroup(&profile, "Added Associations" ), file, basePreference); |
76 | parseRemovedAssociations(KConfigGroup(&profile, "Removed Associations" ), file); |
77 | |
78 | // KDE extension for parts and plugins, see settings/filetypes/mimetypedata.cpp |
79 | parseAddedAssociations(KConfigGroup(&profile, "Added KDE Service Associations" ), file, basePreference); |
80 | parseRemovedAssociations(KConfigGroup(&profile, "Removed KDE Service Associations" ), file); |
81 | } |
82 | |
83 | void KMimeAssociations::parseAddedAssociations(const KConfigGroup& group, const QString& file, int basePreference) |
84 | { |
85 | Q_FOREACH(const QString& mimeName, group.keyList()) { |
86 | const QStringList services = group.readXdgListEntry(mimeName); |
87 | const QString resolvedMimeName = KMimeTypeRepository::self()->canonicalName(mimeName); |
88 | int pref = basePreference; |
89 | Q_FOREACH(const QString &service, services) { |
90 | KService::Ptr pService = KService::serviceByStorageId(service); |
91 | if (!pService) { |
92 | kDebug(7021) << file << "specifies unknown service" << service << "in" << group.name(); |
93 | } else { |
94 | //kDebug(7021) << "adding mime" << resolvedMimeName << "to service" << pService->entryPath() << "pref=" << pref; |
95 | m_offerHash.addServiceOffer(resolvedMimeName, KServiceOffer(pService, pref, 0, pService->allowAsDefault())); |
96 | --pref; |
97 | } |
98 | } |
99 | } |
100 | } |
101 | |
102 | void KMimeAssociations::parseRemovedAssociations(const KConfigGroup& group, const QString& file) |
103 | { |
104 | Q_FOREACH(const QString& mime, group.keyList()) { |
105 | const QStringList services = group.readXdgListEntry(mime); |
106 | Q_FOREACH(const QString& service, services) { |
107 | KService::Ptr pService = KService::serviceByStorageId(service); |
108 | if (!pService) { |
109 | kDebug(7021) << file << "specifies unknown service" << service << "in" << group.name(); |
110 | } else { |
111 | //kDebug(7021) << "removing mime" << mime << "from service" << pService.data() << pService->entryPath(); |
112 | m_offerHash.removeServiceOffer(mime, pService); |
113 | } |
114 | } |
115 | } |
116 | } |
117 | |
118 | void KOfferHash::addServiceOffer(const QString& serviceType, const KServiceOffer& offer) |
119 | { |
120 | KService::Ptr service = offer.service(); |
121 | //kDebug(7021) << "Adding" << service->entryPath() << "to" << serviceType << offer.preference(); |
122 | ServiceTypeOffersData& data = m_serviceTypeData[serviceType]; // find or create |
123 | QList<KServiceOffer>& offers = data.offers; |
124 | QSet<KService::Ptr>& offerSet = data.offerSet; |
125 | if ( !offerSet.contains( service ) ) { |
126 | offers.append( offer ); |
127 | offerSet.insert( service ); |
128 | } else { |
129 | //kDebug(7021) << service->entryPath() << "already in" << serviceType; |
130 | // This happens when mimeapps.list mentions a service (to make it preferred) |
131 | // Update initialPreference to qMax(existing offer, new offer) |
132 | QMutableListIterator<KServiceOffer> sfit(data.offers); |
133 | while (sfit.hasNext()) { |
134 | if (sfit.next().service() == service) // we can compare KService::Ptrs because they are from the memory hash |
135 | sfit.value().setPreference( qMax(sfit.value().preference(), offer.preference()) ); |
136 | } |
137 | } |
138 | } |
139 | |
140 | void KOfferHash::removeServiceOffer(const QString& serviceType, KService::Ptr service) |
141 | { |
142 | ServiceTypeOffersData& data = m_serviceTypeData[serviceType]; // find or create |
143 | data.removedOffers.insert(service); |
144 | data.offerSet.remove(service); |
145 | QMutableListIterator<KServiceOffer> sfit(data.offers); |
146 | while (sfit.hasNext()) { |
147 | if (sfit.next().service()->storageId() == service->storageId()) |
148 | sfit.remove(); |
149 | } |
150 | } |
151 | |
152 | bool KOfferHash::hasRemovedOffer(const QString& serviceType, KService::Ptr service) const |
153 | { |
154 | QHash<QString, ServiceTypeOffersData>::const_iterator it = m_serviceTypeData.find(serviceType); |
155 | if (it != m_serviceTypeData.end()) { |
156 | return (*it).removedOffers.contains(service); |
157 | } |
158 | return false; |
159 | } |
160 | |