1 | /* |
2 | Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com> |
3 | |
4 | This library is free software; you can redistribute it and/or modify it |
5 | under the terms of the GNU Library General Public License as published by |
6 | the Free Software Foundation; either version 2 of the License, or (at your |
7 | option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
12 | License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to the |
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
17 | 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "specialcollections.h" |
21 | |
22 | #include "specialcollections_p.h" |
23 | #include "specialcollectionattribute_p.h" |
24 | |
25 | #include "akonadi/agentinstance.h" |
26 | #include "akonadi/agentmanager.h" |
27 | #include "akonadi/collectionmodifyjob.h" |
28 | #include "akonadi/collectionfetchjob.h" |
29 | #include "akonadi/monitor.h" |
30 | |
31 | #include <KDebug> |
32 | #include <kcoreconfigskeleton.h> |
33 | |
34 | #include <QtCore/QHash> |
35 | #include <QtCore/QObject> |
36 | #include <QtCore/QSet> |
37 | #include <akonadi/collectionfetchscope.h> |
38 | |
39 | using namespace Akonadi; |
40 | |
41 | SpecialCollectionsPrivate::SpecialCollectionsPrivate(KCoreConfigSkeleton *settings, SpecialCollections *qq) |
42 | : q(qq) |
43 | , mSettings(settings) |
44 | , mBatchMode(false) |
45 | { |
46 | mMonitor = new Monitor(q); |
47 | mMonitor->fetchCollectionStatistics(true); |
48 | |
49 | /// In order to know if items are added or deleted |
50 | /// from one of our specialcollection folders, |
51 | /// we have to watch all mail item add/move/delete notifications |
52 | /// and check for the parent to see if it is one we care about |
53 | QObject::connect(mMonitor, SIGNAL(collectionRemoved(Akonadi::Collection)), |
54 | q, SLOT(collectionRemoved(Akonadi::Collection))); |
55 | QObject::connect(mMonitor, SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), |
56 | q, SLOT(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics))); |
57 | } |
58 | |
59 | SpecialCollectionsPrivate::~SpecialCollectionsPrivate() |
60 | { |
61 | } |
62 | |
63 | QString SpecialCollectionsPrivate::defaultResourceId() const |
64 | { |
65 | if (mDefaultResourceId.isEmpty()) { |
66 | mSettings->readConfig(); |
67 | const KConfigSkeletonItem *item = mSettings->findItem(QLatin1String("DefaultResourceId" )); |
68 | Q_ASSERT(item); |
69 | |
70 | mDefaultResourceId = item->property().toString(); |
71 | } |
72 | return mDefaultResourceId; |
73 | } |
74 | |
75 | void SpecialCollectionsPrivate::emitChanged(const QString &resourceId) |
76 | { |
77 | if (mBatchMode) { |
78 | mToEmitChangedFor.insert(resourceId); |
79 | } else { |
80 | kDebug() << "Emitting changed for" << resourceId; |
81 | const AgentInstance agentInstance = AgentManager::self()->instance(resourceId); |
82 | emit q->collectionsChanged(agentInstance); |
83 | // first compare with local value then with config value (which also updates the local value) |
84 | if (resourceId == mDefaultResourceId || resourceId == defaultResourceId()) { |
85 | kDebug() << "Emitting defaultFoldersChanged." ; |
86 | emit q->defaultCollectionsChanged(); |
87 | } |
88 | } |
89 | } |
90 | |
91 | void SpecialCollectionsPrivate::collectionRemoved(const Collection &collection) |
92 | { |
93 | kDebug() << "Collection" << collection.id() << "resource" << collection.resource(); |
94 | if (mFoldersForResource.contains(collection.resource())) { |
95 | |
96 | // Retrieve the list of special folders for the resource the collection belongs to |
97 | QHash<QByteArray, Collection> &folders = mFoldersForResource[collection.resource()]; |
98 | { |
99 | QMutableHashIterator<QByteArray, Collection> it(folders); |
100 | while (it.hasNext()) { |
101 | it.next(); |
102 | if (it.value() == collection) { |
103 | // The collection to be removed is a special folder |
104 | it.remove(); |
105 | emitChanged(collection.resource()); |
106 | } |
107 | } |
108 | } |
109 | |
110 | if (folders.isEmpty()) { |
111 | // This resource has no more folders, so remove it completely. |
112 | mFoldersForResource.remove(collection.resource()); |
113 | } |
114 | } |
115 | } |
116 | |
117 | void SpecialCollectionsPrivate::collectionStatisticsChanged(Akonadi::Collection::Id collectionId, const Akonadi::CollectionStatistics &statistics) |
118 | { |
119 | // need to get the name of the collection in order to be able to check if we are storing it, |
120 | // but we have the id from the monitor, so fetch the name. |
121 | Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(Collection(collectionId), Akonadi::CollectionFetchJob::Base); |
122 | fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::None); |
123 | fetchJob->setProperty("statistics" , QVariant::fromValue(statistics)); |
124 | |
125 | q->connect(fetchJob, SIGNAL(result(KJob*)), q, SLOT(collectionFetchJobFinished(KJob*))); |
126 | } |
127 | |
128 | void SpecialCollectionsPrivate::collectionFetchJobFinished(KJob *job) |
129 | { |
130 | if (job->error()) { |
131 | kWarning() << "Error fetching collection to get name from id for statistics updating in specialcollections!" ; |
132 | return; |
133 | } |
134 | |
135 | const Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job); |
136 | |
137 | Q_ASSERT(fetchJob->collections().size() > 0); |
138 | const Akonadi::Collection collection = fetchJob->collections().first(); |
139 | const Akonadi::CollectionStatistics statistics = fetchJob->property("statistics" ).value<Akonadi::CollectionStatistics>(); |
140 | |
141 | mFoldersForResource[collection.resource()][collection.name().toUtf8()].setStatistics(statistics); |
142 | } |
143 | |
144 | void SpecialCollectionsPrivate::beginBatchRegister() |
145 | { |
146 | Q_ASSERT(!mBatchMode); |
147 | mBatchMode = true; |
148 | Q_ASSERT(mToEmitChangedFor.isEmpty()); |
149 | } |
150 | |
151 | void SpecialCollectionsPrivate::endBatchRegister() |
152 | { |
153 | Q_ASSERT(mBatchMode); |
154 | mBatchMode = false; |
155 | |
156 | foreach (const QString &resourceId, mToEmitChangedFor) { |
157 | emitChanged(resourceId); |
158 | } |
159 | |
160 | mToEmitChangedFor.clear(); |
161 | } |
162 | |
163 | void SpecialCollectionsPrivate::forgetFoldersForResource(const QString &resourceId) |
164 | { |
165 | if (mFoldersForResource.contains(resourceId)) { |
166 | const Collection::List folders = mFoldersForResource[resourceId].values(); |
167 | |
168 | foreach (const Collection &collection, folders) { |
169 | mMonitor->setCollectionMonitored(collection, false); |
170 | } |
171 | |
172 | mFoldersForResource.remove(resourceId); |
173 | emitChanged(resourceId); |
174 | } |
175 | } |
176 | |
177 | AgentInstance SpecialCollectionsPrivate::defaultResource() const |
178 | { |
179 | const QString identifier = defaultResourceId(); |
180 | return AgentManager::self()->instance(identifier); |
181 | } |
182 | |
183 | SpecialCollections::SpecialCollections(KCoreConfigSkeleton *settings, QObject *parent) |
184 | : QObject(parent) |
185 | , d(new SpecialCollectionsPrivate(settings, this)) |
186 | { |
187 | } |
188 | |
189 | SpecialCollections::~SpecialCollections() |
190 | { |
191 | delete d; |
192 | } |
193 | |
194 | bool SpecialCollections::hasCollection(const QByteArray &type, const AgentInstance &instance) const |
195 | { |
196 | return d->mFoldersForResource.value(instance.identifier()).contains(type); |
197 | } |
198 | |
199 | Akonadi::Collection SpecialCollections::collection(const QByteArray &type, const AgentInstance &instance) const |
200 | { |
201 | return d->mFoldersForResource.value(instance.identifier()).value(type); |
202 | } |
203 | |
204 | void SpecialCollections::setSpecialCollectionType(const QByteArray &type, const Akonadi::Collection &collection) |
205 | { |
206 | if (!collection.hasAttribute<SpecialCollectionAttribute>() || collection.attribute<SpecialCollectionAttribute>()->collectionType() != type) { |
207 | Collection attributeCollection(collection); |
208 | SpecialCollectionAttribute *attribute = attributeCollection.attribute<SpecialCollectionAttribute>(Collection::AddIfMissing); |
209 | attribute->setCollectionType(type); |
210 | new CollectionModifyJob(attributeCollection); |
211 | } |
212 | } |
213 | |
214 | void SpecialCollections::unsetSpecialCollection(const Akonadi::Collection &collection) |
215 | { |
216 | if (collection.hasAttribute<SpecialCollectionAttribute>()) { |
217 | Collection attributeCollection(collection); |
218 | attributeCollection.removeAttribute<SpecialCollectionAttribute>(); |
219 | new CollectionModifyJob(attributeCollection); |
220 | } |
221 | } |
222 | |
223 | bool SpecialCollections::unregisterCollection(const Collection &collection) |
224 | { |
225 | if (!collection.isValid()) { |
226 | kWarning() << "Invalid collection." ; |
227 | return false; |
228 | } |
229 | |
230 | const QString &resourceId = collection.resource(); |
231 | if (resourceId.isEmpty()) { |
232 | kWarning() << "Collection has empty resourceId." ; |
233 | return false; |
234 | } |
235 | |
236 | unsetSpecialCollection(collection); |
237 | |
238 | d->mMonitor->setCollectionMonitored(collection, false); |
239 | //Remove from list of collection |
240 | d->collectionRemoved(collection); |
241 | return true; |
242 | } |
243 | |
244 | bool SpecialCollections::registerCollection(const QByteArray &type, const Collection &collection) |
245 | { |
246 | if (!collection.isValid()) { |
247 | kWarning() << "Invalid collection." ; |
248 | return false; |
249 | } |
250 | |
251 | const QString &resourceId = collection.resource(); |
252 | if (resourceId.isEmpty()) { |
253 | kWarning() << "Collection has empty resourceId." ; |
254 | return false; |
255 | } |
256 | |
257 | setSpecialCollectionType(type, collection); |
258 | |
259 | const Collection oldCollection = d->mFoldersForResource.value(resourceId).value(type); |
260 | if (oldCollection != collection) { |
261 | if (oldCollection.isValid()) { |
262 | d->mMonitor->setCollectionMonitored(oldCollection, false); |
263 | } |
264 | d->mMonitor->setCollectionMonitored(collection, true); |
265 | d->mFoldersForResource[resourceId].insert(type, collection); |
266 | d->emitChanged(resourceId); |
267 | } |
268 | |
269 | return true; |
270 | } |
271 | |
272 | bool SpecialCollections::hasDefaultCollection(const QByteArray &type) const |
273 | { |
274 | return hasCollection(type, d->defaultResource()); |
275 | } |
276 | |
277 | Akonadi::Collection SpecialCollections::defaultCollection(const QByteArray &type) const |
278 | { |
279 | return collection(type, d->defaultResource()); |
280 | } |
281 | |
282 | #include "moc_specialcollections.cpp" |
283 | |