Akonadi

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

KDE's Doxygen guidelines are available online.