File indexing completed on 2025-01-05 04:47:06

0001 /*
0002     SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "actionstatemanager_p.h"
0008 
0009 #include "agentmanager.h"
0010 #include "collectionutils.h"
0011 #include "entitydeletedattribute.h"
0012 #include "pastehelper_p.h"
0013 #include "specialcollectionattribute.h"
0014 #include "standardactionmanager.h"
0015 
0016 #include <QApplication>
0017 #include <QClipboard>
0018 
0019 using namespace Akonadi;
0020 
0021 static bool canCreateSubCollection(const Collection &collection)
0022 {
0023     if (!(collection.rights() & Collection::CanCreateCollection)) {
0024         return false;
0025     }
0026 
0027     if (!collection.contentMimeTypes().contains(Collection::mimeType()) && !collection.contentMimeTypes().contains(Collection::virtualMimeType())) {
0028         return false;
0029     }
0030 
0031     return true;
0032 }
0033 
0034 static inline bool canContainItems(const Collection &collection)
0035 {
0036     if (collection.contentMimeTypes().isEmpty()) {
0037         return false;
0038     }
0039 
0040     if ((collection.contentMimeTypes().count() == 1)
0041         && ((collection.contentMimeTypes().at(0) == Collection::mimeType()) || (collection.contentMimeTypes().at(0) == Collection::virtualMimeType()))) {
0042         return false;
0043     }
0044 
0045     return true;
0046 }
0047 
0048 void ActionStateManager::setReceiver(QObject *object)
0049 {
0050     mReceiver = object;
0051 }
0052 
0053 void ActionStateManager::updateState(const Collection::List &collections, const Collection::List &favoriteCollections, const Item::List &items)
0054 {
0055     const int collectionCount = collections.count();
0056     const bool singleCollectionSelected = (collectionCount == 1);
0057     const bool multipleCollectionsSelected = (collectionCount > 1);
0058     const bool atLeastOneCollectionSelected = (singleCollectionSelected || multipleCollectionsSelected);
0059 
0060     const int itemCount = items.count();
0061     const bool singleItemSelected = (itemCount == 1);
0062     const bool multipleItemsSelected = (itemCount > 1);
0063     const bool atLeastOneItemSelected = (singleItemSelected || multipleItemsSelected);
0064 
0065     const bool listOfCollectionNotEmpty = !collections.isEmpty();
0066     bool canDeleteCollections = listOfCollectionNotEmpty;
0067     if (canDeleteCollections) {
0068         for (const Collection &collection : collections) {
0069             // do we have the necessary rights?
0070             if (!(collection.rights() & Collection::CanDeleteCollection)) {
0071                 canDeleteCollections = false;
0072                 break;
0073             }
0074 
0075             if (isRootCollection(collection)) {
0076                 canDeleteCollections = false;
0077                 break;
0078             }
0079 
0080             if (isResourceCollection(collection)) {
0081                 canDeleteCollections = false;
0082                 break;
0083             }
0084         }
0085     }
0086 
0087     bool canCutCollections = canDeleteCollections; // we must be able to delete for cutting
0088     for (const Collection &collection : collections) {
0089         if (isSpecialCollection(collection)) {
0090             canCutCollections = false;
0091             break;
0092         }
0093 
0094         if (!isFolderCollection(collection)) {
0095             canCutCollections = false;
0096             break;
0097         }
0098     }
0099 
0100     const bool canMoveCollections = canCutCollections; // we must be able to cut for moving
0101 
0102     bool canCopyCollections = listOfCollectionNotEmpty;
0103     if (canCopyCollections) {
0104         for (const Collection &collection : collections) {
0105             if (isRootCollection(collection)) {
0106                 canCopyCollections = false;
0107                 break;
0108             }
0109 
0110             if (!isFolderCollection(collection)) {
0111                 canCopyCollections = false;
0112                 break;
0113             }
0114         }
0115     }
0116     bool canAddToFavoriteCollections = listOfCollectionNotEmpty;
0117     if (canAddToFavoriteCollections) {
0118         for (const Collection &collection : collections) {
0119             if (isRootCollection(collection)) {
0120                 canAddToFavoriteCollections = false;
0121                 break;
0122             }
0123 
0124             if (isFavoriteCollection(collection)) {
0125                 canAddToFavoriteCollections = false;
0126                 break;
0127             }
0128 
0129             if (!isFolderCollection(collection)) {
0130                 canAddToFavoriteCollections = false;
0131                 break;
0132             }
0133 
0134             if (!canContainItems(collection)) {
0135                 canAddToFavoriteCollections = false;
0136                 break;
0137             }
0138         }
0139     }
0140 
0141     bool collectionsAreFolders = listOfCollectionNotEmpty;
0142 
0143     for (const Collection &collection : collections) {
0144         if (!isFolderCollection(collection)) {
0145             collectionsAreFolders = false;
0146             break;
0147         }
0148     }
0149 
0150     bool collectionsAreInTrash = false;
0151     for (const Collection &collection : collections) {
0152         if (collection.hasAttribute<EntityDeletedAttribute>()) {
0153             collectionsAreInTrash = true;
0154             break;
0155         }
0156     }
0157 
0158     bool atLeastOneCollectionCanHaveItems = false;
0159     for (const Collection &collection : collections) {
0160         if (collectionCanHaveItems(collection)) {
0161             atLeastOneCollectionCanHaveItems = true;
0162             break;
0163         }
0164     }
0165     for (const Collection &collection : favoriteCollections) {
0166         if (collectionCanHaveItems(collection)) {
0167             atLeastOneCollectionCanHaveItems = true;
0168             break;
0169         }
0170     }
0171 
0172     const Collection collection = (!collections.isEmpty() ? collections.first() : Collection());
0173 
0174     // collection specific actions
0175     enableAction(StandardActionManager::CreateCollection,
0176                  singleCollectionSelected && // we can create only inside one collection
0177                      canCreateSubCollection(collection)); // we need the necessary rights
0178 
0179     enableAction(StandardActionManager::DeleteCollections, canDeleteCollections);
0180 
0181     enableAction(StandardActionManager::CopyCollections, canCopyCollections);
0182 
0183     enableAction(StandardActionManager::CutCollections, canCutCollections);
0184 
0185     enableAction(StandardActionManager::CopyCollectionToMenu, canCopyCollections);
0186 
0187     enableAction(StandardActionManager::MoveCollectionToMenu, canMoveCollections);
0188 
0189     enableAction(StandardActionManager::MoveCollectionsToTrash, atLeastOneCollectionSelected && canMoveCollections && !collectionsAreInTrash);
0190 
0191     enableAction(StandardActionManager::RestoreCollectionsFromTrash, atLeastOneCollectionSelected && canMoveCollections && collectionsAreInTrash);
0192 
0193     enableAction(StandardActionManager::CopyCollectionToDialog, canCopyCollections);
0194 
0195     enableAction(StandardActionManager::MoveCollectionToDialog, canMoveCollections);
0196 
0197     enableAction(StandardActionManager::CollectionProperties,
0198                  singleCollectionSelected && // we can only configure one collection at a time
0199                      !isRootCollection(collection)); // we can not configure the root collection
0200 
0201     enableAction(StandardActionManager::SynchronizeCollections, atLeastOneCollectionCanHaveItems); // it must be a valid folder collection
0202 
0203     enableAction(StandardActionManager::SynchronizeCollectionsRecursive,
0204                  atLeastOneCollectionSelected && collectionsAreFolders); // it must be a valid folder collection
0205 #ifndef QT_NO_CLIPBOARD
0206     enableAction(StandardActionManager::Paste,
0207                  singleCollectionSelected && // we can paste only into a single collection
0208                      PasteHelper::canPaste(QApplication::clipboard()->mimeData(), collection, Qt::CopyAction)); // there must be data on the clipboard
0209 #else
0210     enableAction(StandardActionManager::Paste, false); // no support for clipboard -> no paste
0211 #endif
0212 
0213     // favorite collections specific actions
0214     enableAction(StandardActionManager::AddToFavoriteCollections, canAddToFavoriteCollections);
0215 
0216     const bool canRemoveFromFavoriteCollections = !favoriteCollections.isEmpty();
0217     enableAction(StandardActionManager::RemoveFromFavoriteCollections, canRemoveFromFavoriteCollections);
0218 
0219     enableAction(StandardActionManager::RenameFavoriteCollection, favoriteCollections.count() == 1); // we can rename only one collection at a time
0220 
0221     // resource specific actions
0222     int resourceCollectionCount = 0;
0223     bool canDeleteResources = true;
0224     bool canConfigureResource = true;
0225     bool canSynchronizeResources = true;
0226     for (const Collection &collection : collections) {
0227         if (isResourceCollection(collection)) {
0228             resourceCollectionCount++;
0229 
0230             // check that the 'NoConfig' flag is not set for the resource
0231             if (hasResourceCapability(collection, QStringLiteral("NoConfig"))) {
0232                 canConfigureResource = false;
0233             }
0234         } else {
0235             // we selected a non-resource collection
0236             canDeleteResources = false;
0237             canConfigureResource = false;
0238             canSynchronizeResources = false;
0239         }
0240     }
0241 
0242     if (resourceCollectionCount == 0) {
0243         // not a single resource collection has been selected
0244         canDeleteResources = false;
0245         canConfigureResource = false;
0246         canSynchronizeResources = false;
0247     }
0248 
0249     enableAction(StandardActionManager::CreateResource, true);
0250     enableAction(StandardActionManager::DeleteResources, canDeleteResources);
0251     enableAction(StandardActionManager::ResourceProperties, canConfigureResource);
0252     enableAction(StandardActionManager::SynchronizeResources, canSynchronizeResources);
0253     enableAction(StandardActionManager::SynchronizeCollectionTree, canSynchronizeResources);
0254 
0255     if (collectionsAreInTrash) {
0256         updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollectionAlternative);
0257         // updatePluralLabel( StandardActionManager::MoveToTrashRestoreCollectionAlternative, collectionCount );
0258     } else {
0259         updateAlternatingAction(StandardActionManager::MoveToTrashRestoreCollection);
0260     }
0261     enableAction(StandardActionManager::MoveToTrashRestoreCollection, atLeastOneCollectionSelected && canMoveCollections);
0262 
0263     // item specific actions
0264     bool canDeleteItems = (!items.isEmpty()); // TODO: fixme
0265     for (const Item &item : std::as_const(items)) {
0266         const Collection parentCollection = item.parentCollection();
0267         if (!parentCollection.isValid()) {
0268             continue;
0269         }
0270 
0271         canDeleteItems = canDeleteItems && (parentCollection.rights() & Collection::CanDeleteItem);
0272     }
0273 
0274     bool itemsAreInTrash = false;
0275     for (const Item &item : std::as_const(items)) {
0276         if (item.hasAttribute<EntityDeletedAttribute>()) {
0277             itemsAreInTrash = true;
0278             break;
0279         }
0280     }
0281 
0282     enableAction(StandardActionManager::CopyItems, atLeastOneItemSelected); // we need items to work with
0283 
0284     enableAction(StandardActionManager::CutItems,
0285                  atLeastOneItemSelected && // we need items to work with
0286                      canDeleteItems); // we need the necessary rights
0287 
0288     enableAction(StandardActionManager::DeleteItems,
0289                  atLeastOneItemSelected && // we need items to work with
0290                      canDeleteItems); // we need the necessary rights
0291 
0292     enableAction(StandardActionManager::CopyItemToMenu, atLeastOneItemSelected); // we need items to work with
0293 
0294     enableAction(StandardActionManager::MoveItemToMenu,
0295                  atLeastOneItemSelected && // we need items to work with
0296                      canDeleteItems); // we need the necessary rights
0297 
0298     enableAction(StandardActionManager::MoveItemsToTrash, atLeastOneItemSelected && canDeleteItems && !itemsAreInTrash);
0299 
0300     enableAction(StandardActionManager::RestoreItemsFromTrash, atLeastOneItemSelected && itemsAreInTrash);
0301 
0302     enableAction(StandardActionManager::CopyItemToDialog, atLeastOneItemSelected); // we need items to work with
0303 
0304     enableAction(StandardActionManager::MoveItemToDialog,
0305                  atLeastOneItemSelected && // we need items to work with
0306                      canDeleteItems); // we need the necessary rights
0307 
0308     if (itemsAreInTrash) {
0309         updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItemAlternative);
0310         // updatePluralLabel( StandardActionManager::MoveToTrashRestoreItemAlternative, itemCount );
0311     } else {
0312         updateAlternatingAction(StandardActionManager::MoveToTrashRestoreItem);
0313     }
0314     enableAction(StandardActionManager::MoveToTrashRestoreItem,
0315                  atLeastOneItemSelected && // we need items to work with
0316                      canDeleteItems); // we need the necessary rights
0317 
0318     // update the texts of the actions
0319     updatePluralLabel(StandardActionManager::CopyCollections, collectionCount);
0320     updatePluralLabel(StandardActionManager::CopyItems, itemCount);
0321     updatePluralLabel(StandardActionManager::DeleteItems, itemCount);
0322     updatePluralLabel(StandardActionManager::CutItems, itemCount);
0323     updatePluralLabel(StandardActionManager::CutCollections, collectionCount);
0324     updatePluralLabel(StandardActionManager::DeleteCollections, collectionCount);
0325     updatePluralLabel(StandardActionManager::SynchronizeCollections, collectionCount);
0326     updatePluralLabel(StandardActionManager::SynchronizeCollectionsRecursive, collectionCount);
0327     updatePluralLabel(StandardActionManager::DeleteResources, resourceCollectionCount);
0328     updatePluralLabel(StandardActionManager::SynchronizeResources, resourceCollectionCount);
0329     updatePluralLabel(StandardActionManager::SynchronizeCollectionTree, resourceCollectionCount);
0330 }
0331 
0332 bool ActionStateManager::isRootCollection(const Collection &collection) const
0333 {
0334     return CollectionUtils::isRoot(collection);
0335 }
0336 
0337 bool ActionStateManager::isResourceCollection(const Collection &collection) const
0338 {
0339     return CollectionUtils::isResource(collection);
0340 }
0341 
0342 bool ActionStateManager::isFolderCollection(const Collection &collection) const
0343 {
0344     return (CollectionUtils::isFolder(collection) || CollectionUtils::isResource(collection) || CollectionUtils::isStructural(collection));
0345 }
0346 
0347 bool ActionStateManager::isSpecialCollection(const Collection &collection) const
0348 {
0349     return collection.hasAttribute<SpecialCollectionAttribute>();
0350 }
0351 
0352 bool ActionStateManager::isFavoriteCollection(const Collection &collection) const
0353 {
0354     if (!mReceiver) {
0355         return false;
0356     }
0357 
0358     bool result = false;
0359     QMetaObject::invokeMethod(mReceiver, "isFavoriteCollection", Qt::DirectConnection, Q_RETURN_ARG(bool, result), Q_ARG(Akonadi::Collection, collection));
0360 
0361     return result;
0362 }
0363 
0364 bool ActionStateManager::hasResourceCapability(const Collection &collection, const QString &capability) const
0365 {
0366     const Akonadi::AgentInstance instance = AgentManager::self()->instance(collection.resource());
0367 
0368     return instance.type().capabilities().contains(capability);
0369 }
0370 
0371 bool ActionStateManager::collectionCanHaveItems(const Collection &collection) const
0372 {
0373     return !(collection.contentMimeTypes() == (QStringList() << QStringLiteral("inode/directory")) || CollectionUtils::isStructural(collection));
0374 }
0375 
0376 void ActionStateManager::enableAction(int action, bool state)
0377 {
0378     if (!mReceiver) {
0379         return;
0380     }
0381 
0382     QMetaObject::invokeMethod(mReceiver, "enableAction", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(bool, state));
0383 }
0384 
0385 void ActionStateManager::updatePluralLabel(int action, int count)
0386 {
0387     if (!mReceiver) {
0388         return;
0389     }
0390 
0391     QMetaObject::invokeMethod(mReceiver, "updatePluralLabel", Qt::DirectConnection, Q_ARG(int, action), Q_ARG(int, count));
0392 }
0393 
0394 void ActionStateManager::updateAlternatingAction(int action)
0395 {
0396     if (!mReceiver) {
0397         return;
0398     }
0399 
0400     QMetaObject::invokeMethod(mReceiver, "updateAlternatingAction", Qt::DirectConnection, Q_ARG(int, action));
0401 }