Akonadi

actionstatemanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Tobias Koenig <[email protected]>
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 
19 using namespace Akonadi;
20 
21 static 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 
34 static 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 
48 void ActionStateManager::setReceiver(QObject *object)
49 {
50  mReceiver = object;
51 }
52 
53 void 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 
284  enableAction(StandardActionManager::CutItems,
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 
332 bool ActionStateManager::isRootCollection(const Collection &collection) const
333 {
334  return CollectionUtils::isRoot(collection);
335 }
336 
337 bool ActionStateManager::isResourceCollection(const Collection &collection) const
338 {
339  return CollectionUtils::isResource(collection);
340 }
341 
342 bool ActionStateManager::isFolderCollection(const Collection &collection) const
343 {
344  return (CollectionUtils::isFolder(collection) || CollectionUtils::isResource(collection) || CollectionUtils::isStructural(collection));
345 }
346 
347 bool ActionStateManager::isSpecialCollection(const Collection &collection) const
348 {
349  return collection.hasAttribute<SpecialCollectionAttribute>();
350 }
351 
352 bool 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 
364 bool 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 
371 bool ActionStateManager::collectionCanHaveItems(const Collection &collection) const
372 {
373  return !(collection.contentMimeTypes() == (QStringList() << QStringLiteral("inode/directory")) || CollectionUtils::isStructural(collection));
374 }
375 
376 void 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 
385 void 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 
394 void ActionStateManager::updateAlternatingAction(int action)
395 {
396  if (!mReceiver) {
397  return;
398  }
399 
400  QMetaObject::invokeMethod(mReceiver, "updateAlternatingAction", Qt::DirectConnection, Q_ARG(int, action));
401 }
@ CopyItems
Copies the selected items.
bool isEmpty() const const
@ CutItems
Cuts the selected items.
@ CanCreateCollection
Can create new subcollections in this collection.
Definition: collection.h:95
AgentType type() const
Returns the agent type of this instance.
int count(const T &value) const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
Represents a collection of PIM items.
Definition: collection.h:61
@ MoveToTrashRestoreCollectionAlternative
Helper type for MoveToTrashRestoreCollection, do not create directly.
@ Paste
Paste collections or items.
@ CopyCollectionToDialog
Copy a collection into another collection, select the target in a dialog.
QVector< Item > List
Describes a list of items.
Definition: item.h:115
@ CollectionProperties
Provides collection properties.
@ RemoveFromFavoriteCollections
Remove the collection from the favorite collections model.
@ ResourceProperties
Provides the resource properties.
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
QClipboard * clipboard()
QStringList capabilities() const
Returns the list of supported capabilities of the agent type.
@ RestoreCollectionsFromTrash
Restores the selected collection from trash, needs EntityDeletedAttribute.
@ SynchronizeCollectionsRecursive
Synchronizes collections in a recursive way.
@ RestoreItemsFromTrash
Restores the selected items from trash, needs EntityDeletedAttribute.
bool hasAttribute(const QByteArray &name) const
Returns true if the collection has an attribute of the given type name, false otherwise.
Definition: collection.cpp:161
const T & at(int i) const const
@ AddToFavoriteCollections
Add the collection to the favorite collections model.
static QString virtualMimeType()
Returns the mimetype used for virtual collections.
Definition: collection.cpp:297
DirectConnection
@ CopyItemToDialog
Copy an item into a collection, select the target in a dialog.
@ SynchronizeResources
Synchronizes the selected resources.
bool isEmpty() const const
An Attribute that stores the special collection type of a collection.
static QString mimeType()
Returns the mimetype used for collections.
Definition: collection.cpp:292
@ CopyCollections
Copies the selected collections.
@ DeleteResources
Deletes the selected resources.
@ CopyCollectionToMenu
Menu allowing to quickly copy a collection into another collection.
Collection parentCollection() const
Returns the parent collection of this object.
Definition: collection.cpp:187
@ CanDeleteCollection
Can delete this collection.
Definition: collection.h:96
CopyAction
@ CutCollections
Cuts the selected collections.
An Attribute that marks that an entity was marked as deleted.
@ CreateResource
Creates a new resource.
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
@ MoveItemToDialog
Move an item into a collection, select the target in a dialog.
@ SynchronizeCollectionTree
Synchronize collection tree.
static AgentManager * self()
Returns the global instance of the agent manager.
int count(const T &value) const const
@ DeleteCollections
Deletes the selected collections.
@ MoveToTrashRestoreCollection
Move Collection to Trash or Restore it from Trash, needs EntityDeletedAttribute.
@ CopyItemToMenu
Menu allowing to quickly copy an item into a collection.
@ CanDeleteItem
Can delete items in this collection.
Definition: collection.h:93
@ MoveCollectionToMenu
Menu allowing to move a collection into another collection.
@ DeleteItems
Deletes the selected items.
@ MoveToTrashRestoreItemAlternative
Helper type for MoveToTrashRestoreItem, do not create directly.
@ MoveToTrashRestoreItem
Move Item to Trash or Restore it from Trash, needs EntityDeletedAttribute.
@ CreateCollection
Creates an collection.
@ RenameFavoriteCollection
Rename the collection of the favorite collections model.
A representation of an agent instance.
@ MoveCollectionsToTrash
Moves the selected collection to trash and marks it as deleted, needs EntityDeletedAttribute.
@ MoveItemsToTrash
Moves the selected items to trash and marks them as deleted, needs EntityDeletedAttribute.
@ MoveCollectionToDialog
Move a collection into another collection, select the target in a dialog.
@ SynchronizeCollections
Synchronizes collections.
Represents a PIM item stored in Akonadi storage.
Definition: item.h:104
@ MoveItemToMenu
Menu allowing to move item into a collection.
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:00:31 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.