1/*
2 Copyright (c) 2010 Tobias Koenig <tokoe@kde.org>
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 "actionstatemanager_p.h"
21
22#include "agentmanager.h"
23#include "collectionutils_p.h"
24#include "pastehelper_p.h"
25#include "specialcollectionattribute_p.h"
26#include "standardactionmanager.h"
27
28#include <akonadi/entitydeletedattribute.h>
29
30#include <QApplication>
31#include <QClipboard>
32
33using namespace Akonadi;
34
35static bool canCreateSubCollection(const Collection &collection)
36{
37 if (!(collection.rights() & Collection::CanCreateCollection)) {
38 return false;
39 }
40
41 if (!collection.contentMimeTypes().contains(Collection::mimeType()) &&
42 !collection.contentMimeTypes().contains(Collection::virtualMimeType())) {
43 return false;
44 }
45
46 return true;
47}
48
49static inline bool canContainItems(const Collection &collection)
50{
51 if (collection.contentMimeTypes().isEmpty()) {
52 return false;
53 }
54
55 if ((collection.contentMimeTypes().count() == 1) &&
56 ((collection.contentMimeTypes().first() == Collection::mimeType()) ||
57 (collection.contentMimeTypes().first() == Collection::virtualMimeType()))) {
58 return false;
59 }
60
61 return true;
62}
63
64ActionStateManager::ActionStateManager()
65 : mReceiver(0)
66{
67}
68
69ActionStateManager::~ActionStateManager()
70{
71}
72
73void ActionStateManager::setReceiver(QObject *object)
74{
75 mReceiver = object;
76}
77
78void ActionStateManager::updateState(const Collection::List &collections, const Item::List &items)
79{
80 const int collectionCount = collections.count();
81 const bool singleCollectionSelected = (collectionCount == 1);
82 const bool multipleCollectionsSelected = (collectionCount > 1);
83 const bool atLeastOneCollectionSelected = (singleCollectionSelected || multipleCollectionsSelected);
84
85 const int itemCount = items.count();
86 const bool singleItemSelected = (itemCount == 1);
87 const bool multipleItemsSelected = (itemCount > 1);
88 const bool atLeastOneItemSelected = (singleItemSelected || multipleItemsSelected);
89
90 const bool listOfCollectionNotEmpty = collections.isEmpty() ? false : true;
91 bool canDeleteCollections = listOfCollectionNotEmpty;
92 if (canDeleteCollections) {
93 foreach (const Collection &collection, collections) {
94 // do we have the necessary rights?
95 if (!(collection.rights() &Collection::CanDeleteCollection)) {
96 canDeleteCollections = false;
97 break;
98 }
99
100 if (isRootCollection(collection)) {
101 canDeleteCollections = false;
102 break;
103 }
104
105 if (isResourceCollection(collection)) {
106 canDeleteCollections = false;
107 break;
108 }
109 }
110 }
111
112 bool canCutCollections = canDeleteCollections; // we must be able to delete for cutting
113 foreach (const Collection &collection, collections) {
114 if (isSpecialCollection(collection)) {
115 canCutCollections = false;
116 break;
117 }
118
119 if (!isFolderCollection(collection)) {
120 canCutCollections = false;
121 break;
122 }
123 }
124
125 const bool canMoveCollections = canCutCollections; // we must be able to cut for moving
126
127 bool canCopyCollections = listOfCollectionNotEmpty;
128 if (canCopyCollections) {
129 foreach (const Collection &collection, collections) {
130 if (isRootCollection(collection)) {
131 canCopyCollections = false;
132 break;
133 }
134
135 if (!isFolderCollection(collection)) {
136 canCopyCollections = false;
137 break;
138 }
139 }
140 }
141 bool canAddToFavoriteCollections = listOfCollectionNotEmpty;
142 if (canAddToFavoriteCollections) {
143 foreach (const Collection &collection, collections) {
144 if (isRootCollection(collection)) {
145 canAddToFavoriteCollections = false;
146 break;
147 }
148
149 if (isFavoriteCollection(collection)) {
150 canAddToFavoriteCollections = false;
151 break;
152 }
153
154 if (!isFolderCollection(collection)) {
155 canAddToFavoriteCollections = false;
156 break;
157 }
158
159 if (!canContainItems(collection)) {
160 canAddToFavoriteCollections = false;
161 break;
162 }
163 }
164 }
165 bool canRemoveFromFavoriteCollections = listOfCollectionNotEmpty;
166 foreach (const Collection &collection, collections) {
167 if (!isFavoriteCollection(collection)) {
168 canRemoveFromFavoriteCollections = false;
169 break;
170 }
171 }
172
173 bool collectionsAreFolders = listOfCollectionNotEmpty;
174
175 foreach (const Collection &collection, collections) {
176 if (!isFolderCollection(collection)) {
177 collectionsAreFolders = false;
178 break;
179 }
180 }
181
182 bool collectionsAreInTrash = false;
183 foreach (const Collection &collection, collections) {
184 if (collection.hasAttribute<EntityDeletedAttribute>()) {
185 collectionsAreInTrash = true;
186 break;
187 }
188 }
189
190 bool atLeastOneCollectionCanHaveItems = false;
191 foreach (const Collection &collection, collections) {
192 if (collectionCanHaveItems(collection)) {
193 atLeastOneCollectionCanHaveItems = true;
194 break;
195 }
196 }
197
198 const Collection collection = (!collections.isEmpty() ? collections.first() : Collection());
199
200 // collection specific actions
201 enableAction(StandardActionManager::CreateCollection, singleCollectionSelected && // we can create only inside one collection
202 canCreateSubCollection(collection)); // we need the necessary rights
203
204 enableAction(StandardActionManager::DeleteCollections, canDeleteCollections);
205
206 enableAction(StandardActionManager::CopyCollections, canCopyCollections);
207
208 enableAction(StandardActionManager::CutCollections, canCutCollections);
209
210 enableAction(StandardActionManager::CopyCollectionToMenu, canCopyCollections);
211
212 enableAction(StandardActionManager::MoveCollectionToMenu, canMoveCollections);
213
214 enableAction(StandardActionManager::MoveCollectionsToTrash, atLeastOneCollectionSelected && canMoveCollections && !collectionsAreInTrash);
215
216 enableAction(StandardActionManager::RestoreCollectionsFromTrash, atLeastOneCollectionSelected && canMoveCollections && collectionsAreInTrash);
217
218 enableAction(StandardActionManager::CopyCollectionToDialog, canCopyCollections);
219
220 enableAction(StandardActionManager::MoveCollectionToDialog, canMoveCollections);
221
222 enableAction(StandardActionManager::CollectionProperties, singleCollectionSelected && // we can only configure one collection at a time
223 !isRootCollection(collection)); // we can not configure the root collection
224
225 enableAction(StandardActionManager::SynchronizeCollections, atLeastOneCollectionCanHaveItems); // it must be a valid folder collection
226
227 enableAction(StandardActionManager::SynchronizeCollectionsRecursive, atLeastOneCollectionSelected &&
228 collectionsAreFolders); // it must be a valid folder collection
229#ifndef QT_NO_CLIPBOARD
230 enableAction(StandardActionManager::Paste, singleCollectionSelected && // we can paste only into a single collection
231 PasteHelper::canPaste(QApplication::clipboard()->mimeData(), collection)); // there must be data on the clipboard
232#else
233 enableAction(StandardActionManager::Paste, false); // no support for clipboard -> no paste
234#endif
235
236 // favorite collections specific actions
237 enableAction(StandardActionManager::AddToFavoriteCollections, canAddToFavoriteCollections);
238
239 enableAction(StandardActionManager::RemoveFromFavoriteCollections, canRemoveFromFavoriteCollections);
240
241 enableAction(StandardActionManager::RenameFavoriteCollection, singleCollectionSelected && // we can rename only one collection at a time
242 isFavoriteCollection(collection)); // it must be a favorite collection already
243
244 // resource specific actions
245 int resourceCollectionCount = 0;
246 bool canDeleteResources = true;
247 bool canConfigureResource = true;
248 bool canSynchronizeResources = true;
249 foreach (const Collection &collection, collections) {
250 if (isResourceCollection(collection)) {
251 resourceCollectionCount++;
252
253 // check that the 'NoConfig' flag is not set for the resource
254 if (hasResourceCapability(collection, QLatin1String("NoConfig"))) {
255 canConfigureResource = false;
256 }
257 } else {
258 // we selected a non-resource collection
259 canDeleteResources = false;
260 canConfigureResource = false;
261 canSynchronizeResources = false;
262 }
263 }
264
265 if (resourceCollectionCount == 0) {
266 // not a single resource collection has been selected
267 canDeleteResources = false;
268 canConfigureResource = false;
269 canSynchronizeResources = false;
270 }
271
272 enableAction(StandardActionManager::CreateResource, true);
273 enableAction(StandardActionManager::DeleteResources, canDeleteResources);
274 enableAction(StandardActionManager::ResourceProperties, canConfigureResource);
275 enableAction(StandardActionManager::SynchronizeResources, canSynchronizeResources);
276
277 if (collectionsAreInTrash) {
278 updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollectionAlternative);
279 //updatePluralLabel( StandardActionManager::MoveToTrashRestoreCollectionAlternative, collectionCount );
280 } else {
281 updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollection);
282 }
283 enableAction(StandardActionManager::MoveToTrashRestoreCollection, atLeastOneCollectionSelected && canMoveCollections);
284
285 // item specific actions
286 bool canDeleteItems = (items.count() > 0); //TODO: fixme
287 foreach (const Item &item, items) {
288 const Collection parentCollection = item.parentCollection();
289 if (!parentCollection.isValid()) {
290 continue;
291 }
292
293 canDeleteItems = canDeleteItems && (parentCollection.rights() &Collection::CanDeleteItem);
294 }
295
296 bool itemsAreInTrash = false;
297 foreach (const Item &item, items) {
298 if (item.hasAttribute<EntityDeletedAttribute>()) {
299 itemsAreInTrash = true;
300 break;
301 }
302 }
303
304 enableAction(StandardActionManager::CopyItems, atLeastOneItemSelected); // we need items to work with
305
306 enableAction(StandardActionManager::CutItems, atLeastOneItemSelected && // we need items to work with
307 canDeleteItems); // we need the necessary rights
308
309 enableAction(StandardActionManager::DeleteItems, atLeastOneItemSelected && // we need items to work with
310 canDeleteItems); // we need the necessary rights
311
312 enableAction(StandardActionManager::CopyItemToMenu, atLeastOneItemSelected); // we need items to work with
313
314 enableAction(StandardActionManager::MoveItemToMenu, atLeastOneItemSelected && // we need items to work with
315 canDeleteItems); // we need the necessary rights
316
317 enableAction(StandardActionManager::MoveItemsToTrash, atLeastOneItemSelected && canDeleteItems && !itemsAreInTrash);
318
319 enableAction(StandardActionManager::RestoreItemsFromTrash, atLeastOneItemSelected && itemsAreInTrash);
320
321 enableAction(StandardActionManager::CopyItemToDialog, atLeastOneItemSelected); // we need items to work with
322
323 enableAction(StandardActionManager::MoveItemToDialog, atLeastOneItemSelected && // we need items to work with
324 canDeleteItems); // we need the necessary rights
325
326 if (itemsAreInTrash) {
327 updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItemAlternative);
328 //updatePluralLabel( StandardActionManager::MoveToTrashRestoreItemAlternative, itemCount );
329 } else {
330 updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItem);
331 }
332 enableAction(StandardActionManager::MoveToTrashRestoreItem, atLeastOneItemSelected && // we need items to work with
333 canDeleteItems); // we need the necessary rights
334
335 // update the texts of the actions
336 updatePluralLabel(StandardActionManager::CopyCollections, collectionCount);
337 updatePluralLabel(StandardActionManager::CopyItems, itemCount);
338 updatePluralLabel(StandardActionManager::DeleteItems, itemCount);
339 updatePluralLabel(StandardActionManager::CutItems, itemCount);
340 updatePluralLabel(StandardActionManager::CutCollections, collectionCount);
341 updatePluralLabel(StandardActionManager::DeleteCollections, collectionCount);
342 updatePluralLabel(StandardActionManager::SynchronizeCollections, collectionCount);
343 updatePluralLabel(StandardActionManager::SynchronizeCollectionsRecursive, collectionCount);
344 updatePluralLabel(StandardActionManager::DeleteResources, resourceCollectionCount);
345 updatePluralLabel(StandardActionManager::SynchronizeResources, resourceCollectionCount);
346
347}
348
349bool ActionStateManager::isRootCollection(const Collection &collection) const
350{
351 return CollectionUtils::isRoot(collection);
352}
353
354bool ActionStateManager::isResourceCollection(const Collection &collection) const
355{
356 return CollectionUtils::isResource(collection);
357}
358
359bool ActionStateManager::isFolderCollection(const Collection &collection) const
360{
361 return (CollectionUtils::isFolder(collection) ||
362 CollectionUtils::isResource(collection) ||
363 CollectionUtils::isStructural(collection));
364}
365
366bool ActionStateManager::isSpecialCollection(const Collection &collection) const
367{
368 return collection.hasAttribute<SpecialCollectionAttribute>();
369}
370
371bool ActionStateManager::isFavoriteCollection(const Collection &collection) const
372{
373 if (!mReceiver) {
374 return false;
375 }
376
377 bool result = false;
378 QMetaObject::invokeMethod(mReceiver, "isFavoriteCollection", Qt::DirectConnection,
379 Q_RETURN_ARG(bool, result), Q_ARG(Akonadi::Collection, collection));
380
381 return result;
382}
383
384bool ActionStateManager::hasResourceCapability(const Collection &collection, const QString &capability) const
385{
386 const Akonadi::AgentInstance instance = AgentManager::self()->instance(collection.resource());
387
388 return instance.type().capabilities().contains(capability);
389}
390
391bool ActionStateManager::collectionCanHaveItems(const Collection &collection) const
392{
393 return !(collection.contentMimeTypes() == (QStringList() << QLatin1String("inode/directory")) ||
394 CollectionUtils::isStructural(collection));
395}
396
397void ActionStateManager::enableAction(int action, bool state)
398{
399 if (!mReceiver) {
400 return;
401 }
402
403 QMetaObject::invokeMethod(mReceiver, "enableAction", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(bool, state));
404}
405
406void ActionStateManager::updatePluralLabel(int action, int count)
407{
408 if (!mReceiver) {
409 return;
410 }
411
412 QMetaObject::invokeMethod(mReceiver, "updatePluralLabel", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(int, count));
413}
414
415void ActionStateManager::updateAlternatingAction(int action)
416{
417 if (!mReceiver) {
418 return;
419 }
420
421 QMetaObject::invokeMethod(mReceiver, "updateAlternatingAction", Qt::DirectConnection, Q_ARG(int, action));
422}
423