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 }