Akonadi

actionstatemanager.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "actionstatemanager_p.h"
8
9#include "agentmanager.h"
10#include "collectionutils.h"
11#include "entitydeletedattribute.h"
12#include "pastehelper_p.h"
13#include "specialcollectionattribute.h"
14#include "standardactionmanager.h"
15
16#include <QApplication>
17#include <QClipboard>
18
19using namespace Akonadi;
20
21static bool canCreateSubCollection(const Collection &collection)
22{
23 if (!(collection.rights() & Collection::CanCreateCollection)) {
24 return false;
25 }
26
27 if (!collection.contentMimeTypes().contains(Collection::mimeType()) && !collection.contentMimeTypes().contains(Collection::virtualMimeType())) {
28 return false;
29 }
30
31 return true;
32}
33
34static inline bool canContainItems(const Collection &collection)
35{
36 if (collection.contentMimeTypes().isEmpty()) {
37 return false;
38 }
39
40 if ((collection.contentMimeTypes().count() == 1)
41 && ((collection.contentMimeTypes().at(0) == Collection::mimeType()) || (collection.contentMimeTypes().at(0) == Collection::virtualMimeType()))) {
42 return false;
43 }
44
45 return true;
46}
47
48void ActionStateManager::setReceiver(QObject *object)
49{
50 mReceiver = object;
51}
52
53void ActionStateManager::updateState(const Collection::List &collections, const Collection::List &favoriteCollections, const Item::List &items)
54{
55 const int collectionCount = collections.count();
56 const bool singleCollectionSelected = (collectionCount == 1);
57 const bool multipleCollectionsSelected = (collectionCount > 1);
58 const bool atLeastOneCollectionSelected = (singleCollectionSelected || multipleCollectionsSelected);
59
60 const int itemCount = items.count();
61 const bool singleItemSelected = (itemCount == 1);
62 const bool multipleItemsSelected = (itemCount > 1);
63 const bool atLeastOneItemSelected = (singleItemSelected || multipleItemsSelected);
64
65 const bool listOfCollectionNotEmpty = !collections.isEmpty();
66 bool canDeleteCollections = listOfCollectionNotEmpty;
67 if (canDeleteCollections) {
68 for (const Collection &collection : collections) {
69 // do we have the necessary rights?
70 if (!(collection.rights() & Collection::CanDeleteCollection)) {
71 canDeleteCollections = false;
72 break;
73 }
74
75 if (isRootCollection(collection)) {
76 canDeleteCollections = false;
77 break;
78 }
79
80 if (isResourceCollection(collection)) {
81 canDeleteCollections = false;
82 break;
83 }
84 }
85 }
86
87 bool canCutCollections = canDeleteCollections; // we must be able to delete for cutting
88 for (const Collection &collection : collections) {
89 if (isSpecialCollection(collection)) {
90 canCutCollections = false;
91 break;
92 }
93
94 if (!isFolderCollection(collection)) {
95 canCutCollections = false;
96 break;
97 }
98 }
99
100 const bool canMoveCollections = canCutCollections; // we must be able to cut for moving
101
102 bool canCopyCollections = listOfCollectionNotEmpty;
103 if (canCopyCollections) {
104 for (const Collection &collection : collections) {
105 if (isRootCollection(collection)) {
106 canCopyCollections = false;
107 break;
108 }
109
110 if (!isFolderCollection(collection)) {
111 canCopyCollections = false;
112 break;
113 }
114 }
115 }
116 bool canAddToFavoriteCollections = listOfCollectionNotEmpty;
117 if (canAddToFavoriteCollections) {
118 for (const Collection &collection : collections) {
119 if (isRootCollection(collection)) {
120 canAddToFavoriteCollections = false;
121 break;
122 }
123
124 if (isFavoriteCollection(collection)) {
125 canAddToFavoriteCollections = false;
126 break;
127 }
128
129 if (!isFolderCollection(collection)) {
130 canAddToFavoriteCollections = false;
131 break;
132 }
133
134 if (!canContainItems(collection)) {
135 canAddToFavoriteCollections = false;
136 break;
137 }
138 }
139 }
140
141 bool collectionsAreFolders = listOfCollectionNotEmpty;
142
143 for (const Collection &collection : collections) {
144 if (!isFolderCollection(collection)) {
145 collectionsAreFolders = false;
146 break;
147 }
148 }
149
150 bool collectionsAreInTrash = false;
151 for (const Collection &collection : collections) {
152 if (collection.hasAttribute<EntityDeletedAttribute>()) {
153 collectionsAreInTrash = true;
154 break;
155 }
156 }
157
158 bool atLeastOneCollectionCanHaveItems = false;
159 for (const Collection &collection : collections) {
160 if (collectionCanHaveItems(collection)) {
161 atLeastOneCollectionCanHaveItems = true;
162 break;
163 }
164 }
165 for (const Collection &collection : favoriteCollections) {
166 if (collectionCanHaveItems(collection)) {
167 atLeastOneCollectionCanHaveItems = true;
168 break;
169 }
170 }
171
172 const Collection collection = (!collections.isEmpty() ? collections.first() : Collection());
173
174 // collection specific actions
176 singleCollectionSelected && // we can create only inside one collection
177 canCreateSubCollection(collection)); // we need the necessary rights
178
179 enableAction(StandardActionManager::DeleteCollections, canDeleteCollections);
180
181 enableAction(StandardActionManager::CopyCollections, canCopyCollections);
182
183 enableAction(StandardActionManager::CutCollections, canCutCollections);
184
185 enableAction(StandardActionManager::CopyCollectionToMenu, canCopyCollections);
186
187 enableAction(StandardActionManager::MoveCollectionToMenu, canMoveCollections);
188
189 enableAction(StandardActionManager::MoveCollectionsToTrash, atLeastOneCollectionSelected && canMoveCollections && !collectionsAreInTrash);
190
191 enableAction(StandardActionManager::RestoreCollectionsFromTrash, atLeastOneCollectionSelected && canMoveCollections && collectionsAreInTrash);
192
193 enableAction(StandardActionManager::CopyCollectionToDialog, canCopyCollections);
194
195 enableAction(StandardActionManager::MoveCollectionToDialog, canMoveCollections);
196
198 singleCollectionSelected && // we can only configure one collection at a time
199 !isRootCollection(collection)); // we can not configure the root collection
200
201 enableAction(StandardActionManager::SynchronizeCollections, atLeastOneCollectionCanHaveItems); // it must be a valid folder collection
202
204 atLeastOneCollectionSelected && collectionsAreFolders); // it must be a valid folder collection
205#ifndef QT_NO_CLIPBOARD
206 enableAction(StandardActionManager::Paste,
207 singleCollectionSelected && // we can paste only into a single collection
208 PasteHelper::canPaste(QApplication::clipboard()->mimeData(), collection, Qt::CopyAction)); // there must be data on the clipboard
209#else
210 enableAction(StandardActionManager::Paste, false); // no support for clipboard -> no paste
211#endif
212
213 // favorite collections specific actions
214 enableAction(StandardActionManager::AddToFavoriteCollections, canAddToFavoriteCollections);
215
216 const bool canRemoveFromFavoriteCollections = !favoriteCollections.isEmpty();
217 enableAction(StandardActionManager::RemoveFromFavoriteCollections, canRemoveFromFavoriteCollections);
218
219 enableAction(StandardActionManager::RenameFavoriteCollection, favoriteCollections.count() == 1); // we can rename only one collection at a time
220
221 // resource specific actions
222 int resourceCollectionCount = 0;
223 bool canDeleteResources = true;
224 bool canConfigureResource = true;
225 bool canSynchronizeResources = true;
226 for (const Collection &collection : collections) {
227 if (isResourceCollection(collection)) {
228 resourceCollectionCount++;
229
230 // check that the 'NoConfig' flag is not set for the resource
231 if (hasResourceCapability(collection, QStringLiteral("NoConfig"))) {
232 canConfigureResource = false;
233 }
234 } else {
235 // we selected a non-resource collection
236 canDeleteResources = false;
237 canConfigureResource = false;
238 canSynchronizeResources = false;
239 }
240 }
241
242 if (resourceCollectionCount == 0) {
243 // not a single resource collection has been selected
244 canDeleteResources = false;
245 canConfigureResource = false;
246 canSynchronizeResources = false;
247 }
248
249 enableAction(StandardActionManager::CreateResource, true);
250 enableAction(StandardActionManager::DeleteResources, canDeleteResources);
251 enableAction(StandardActionManager::ResourceProperties, canConfigureResource);
252 enableAction(StandardActionManager::SynchronizeResources, canSynchronizeResources);
253 enableAction(StandardActionManager::SynchronizeCollectionTree, canSynchronizeResources);
254
255 if (collectionsAreInTrash) {
257 // updatePluralLabel( StandardActionManager::MoveToTrashRestoreCollectionAlternative, collectionCount );
258 } else {
260 }
261 enableAction(StandardActionManager::MoveToTrashRestoreCollection, atLeastOneCollectionSelected && canMoveCollections);
262
263 // item specific actions
264 bool canDeleteItems = (!items.isEmpty()); // TODO: fixme
265 for (const Item &item : std::as_const(items)) {
266 const Collection parentCollection = item.parentCollection();
267 if (!parentCollection.isValid()) {
268 continue;
269 }
270
271 canDeleteItems = canDeleteItems && (parentCollection.rights() & Collection::CanDeleteItem);
272 }
273
274 bool itemsAreInTrash = false;
275 for (const Item &item : std::as_const(items)) {
276 if (item.hasAttribute<EntityDeletedAttribute>()) {
277 itemsAreInTrash = true;
278 break;
279 }
280 }
281
282 enableAction(StandardActionManager::CopyItems, atLeastOneItemSelected); // we need items to work with
283
285 atLeastOneItemSelected && // we need items to work with
286 canDeleteItems); // we need the necessary rights
287
289 atLeastOneItemSelected && // we need items to work with
290 canDeleteItems); // we need the necessary rights
291
292 enableAction(StandardActionManager::CopyItemToMenu, atLeastOneItemSelected); // we need items to work with
293
295 atLeastOneItemSelected && // we need items to work with
296 canDeleteItems); // we need the necessary rights
297
298 enableAction(StandardActionManager::MoveItemsToTrash, atLeastOneItemSelected && canDeleteItems && !itemsAreInTrash);
299
300 enableAction(StandardActionManager::RestoreItemsFromTrash, atLeastOneItemSelected && itemsAreInTrash);
301
302 enableAction(StandardActionManager::CopyItemToDialog, atLeastOneItemSelected); // we need items to work with
303
305 atLeastOneItemSelected && // we need items to work with
306 canDeleteItems); // we need the necessary rights
307
308 if (itemsAreInTrash) {
310 // updatePluralLabel( StandardActionManager::MoveToTrashRestoreItemAlternative, itemCount );
311 } else {
312 updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItem);
313 }
315 atLeastOneItemSelected && // we need items to work with
316 canDeleteItems); // we need the necessary rights
317
318 // update the texts of the actions
319 updatePluralLabel(StandardActionManager::CopyCollections, collectionCount);
320 updatePluralLabel(StandardActionManager::CopyItems, itemCount);
321 updatePluralLabel(StandardActionManager::DeleteItems, itemCount);
322 updatePluralLabel(StandardActionManager::CutItems, itemCount);
323 updatePluralLabel(StandardActionManager::CutCollections, collectionCount);
324 updatePluralLabel(StandardActionManager::DeleteCollections, collectionCount);
325 updatePluralLabel(StandardActionManager::SynchronizeCollections, collectionCount);
326 updatePluralLabel(StandardActionManager::SynchronizeCollectionsRecursive, collectionCount);
327 updatePluralLabel(StandardActionManager::DeleteResources, resourceCollectionCount);
328 updatePluralLabel(StandardActionManager::SynchronizeResources, resourceCollectionCount);
329 updatePluralLabel(StandardActionManager::SynchronizeCollectionTree, resourceCollectionCount);
330}
331
332bool ActionStateManager::isRootCollection(const Collection &collection) const
333{
334 return CollectionUtils::isRoot(collection);
335}
336
337bool ActionStateManager::isResourceCollection(const Collection &collection) const
338{
339 return CollectionUtils::isResource(collection);
340}
341
342bool ActionStateManager::isFolderCollection(const Collection &collection) const
343{
344 return (CollectionUtils::isFolder(collection) || CollectionUtils::isResource(collection) || CollectionUtils::isStructural(collection));
345}
346
347bool ActionStateManager::isSpecialCollection(const Collection &collection) const
348{
349 return collection.hasAttribute<SpecialCollectionAttribute>();
350}
351
352bool ActionStateManager::isFavoriteCollection(const Collection &collection) const
353{
354 if (!mReceiver) {
355 return false;
356 }
357
358 bool result = false;
359 QMetaObject::invokeMethod(mReceiver, "isFavoriteCollection", Qt::DirectConnection, Q_RETURN_ARG(bool, result), Q_ARG(Akonadi::Collection, collection));
360
361 return result;
362}
363
364bool ActionStateManager::hasResourceCapability(const Collection &collection, const QString &capability) const
365{
366 const Akonadi::AgentInstance instance = AgentManager::self()->instance(collection.resource());
367
368 return instance.type().capabilities().contains(capability);
369}
370
371bool ActionStateManager::collectionCanHaveItems(const Collection &collection) const
372{
373 return !(collection.contentMimeTypes() == (QStringList() << QStringLiteral("inode/directory")) || CollectionUtils::isStructural(collection));
374}
375
376void ActionStateManager::enableAction(int action, bool state)
377{
378 if (!mReceiver) {
379 return;
380 }
381
382 QMetaObject::invokeMethod(mReceiver, "enableAction", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(bool, state));
383}
384
385void ActionStateManager::updatePluralLabel(int action, int count)
386{
387 if (!mReceiver) {
388 return;
389 }
390
391 QMetaObject::invokeMethod(mReceiver, "updatePluralLabel", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(int, count));
392}
393
394void ActionStateManager::updateAlternatingAction(int action)
395{
396 if (!mReceiver) {
397 return;
398 }
399
400 QMetaObject::invokeMethod(mReceiver, "updateAlternatingAction", Qt::DirectConnection, Q_ARG(int, action));
401}
A representation of an agent instance.
AgentType type() const
Returns the agent type of this instance.
QStringList capabilities() const
Returns the list of supported capabilities of the agent type.
Represents a collection of PIM items.
Definition collection.h:62
static QString mimeType()
Returns the mimetype used for collections.
bool hasAttribute(const QByteArray &name) const
Returns true if the collection has an attribute of the given type name, false otherwise.
static QString virtualMimeType()
Returns the mimetype used for virtual collections.
@ CanDeleteItem
Can delete items in this collection.
Definition collection.h:93
@ CanDeleteCollection
Can delete this collection.
Definition collection.h:96
@ CanCreateCollection
Can create new subcollections in this collection.
Definition collection.h:95
Collection parentCollection() const
Returns the parent collection of this object.
An Attribute that marks that an entity was marked as deleted.
An Attribute that stores the special collection type of a collection.
@ CreateResource
Creates a new resource.
@ CreateCollection
Creates an collection.
@ MoveToTrashRestoreItem
Move Item to Trash or Restore it from Trash, needs EntityDeletedAttribute.
@ SynchronizeResources
Synchronizes the selected resources.
@ RemoveFromFavoriteCollections
Remove the collection from the favorite collections model.
@ DeleteItems
Deletes the selected items.
@ CopyCollectionToDialog
Copy a collection into another collection, select the target in a dialog.
@ SynchronizeCollections
Synchronizes collections.
@ MoveCollectionToDialog
Move a collection into another collection, select the target in a dialog.
@ CopyItemToMenu
Menu allowing to quickly copy an item into a collection.
@ RenameFavoriteCollection
Rename the collection of the favorite collections model.
@ ResourceProperties
Provides the resource properties.
@ CutItems
Cuts the selected items.
@ DeleteCollections
Deletes the selected collections.
@ MoveToTrashRestoreCollection
Move Collection to Trash or Restore it from Trash, needs EntityDeletedAttribute.
@ CutCollections
Cuts the selected collections.
@ CopyCollectionToMenu
Menu allowing to quickly copy a collection into another collection.
@ MoveItemToMenu
Menu allowing to move item into a collection.
@ AddToFavoriteCollections
Add the collection to the favorite collections model.
@ SynchronizeCollectionsRecursive
Synchronizes collections in a recursive way.
@ SynchronizeCollectionTree
Synchronize collection tree.
@ MoveItemsToTrash
Moves the selected items to trash and marks them as deleted, needs EntityDeletedAttribute.
@ CopyCollections
Copies the selected collections.
@ Paste
Paste collections or items.
@ CollectionProperties
Provides collection properties.
@ MoveToTrashRestoreCollectionAlternative
Helper type for MoveToTrashRestoreCollection, do not create directly.
@ DeleteResources
Deletes the selected resources.
@ RestoreItemsFromTrash
Restores the selected items from trash, needs EntityDeletedAttribute.
@ MoveToTrashRestoreItemAlternative
Helper type for MoveToTrashRestoreItem, do not create directly.
@ RestoreCollectionsFromTrash
Restores the selected collection from trash, needs EntityDeletedAttribute.
@ CopyItems
Copies the selected items.
@ MoveCollectionToMenu
Menu allowing to move a collection into another collection.
@ MoveItemToDialog
Move an item into a collection, select the target in a dialog.
@ CopyItemToDialog
Copy an item into a collection, select the target in a dialog.
@ MoveCollectionsToTrash
Moves the selected collection to trash and marks it as deleted, needs EntityDeletedAttribute.
Helper integration between Akonadi and Qt.
QClipboard * clipboard()
const_reference at(qsizetype i) const const
qsizetype count() const const
bool isEmpty() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
DirectConnection
CopyAction
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:30 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.