standardactionmanager.cpp
00001 /* 00002 Copyright (c) 2008 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "standardactionmanager.h" 00021 00022 #include "actionstatemanager_p.h" 00023 #include "agentfilterproxymodel.h" 00024 #include "agentinstancecreatejob.h" 00025 #include "agentmanager.h" 00026 #include "agenttypedialog.h" 00027 #include "collectioncreatejob.h" 00028 #include "collectiondeletejob.h" 00029 #include "collectiondialog.h" 00030 #include "collectionmodel.h" 00031 #include "collectionutils_p.h" 00032 #include "entitytreemodel.h" 00033 #include "favoritecollectionsmodel.h" 00034 #include "itemdeletejob.h" 00035 #include "itemmodel.h" 00036 #include "metatypes.h" 00037 #include "pastehelper_p.h" 00038 #include "specialcollectionattribute_p.h" 00039 #include "collectionpropertiesdialog.h" 00040 #include "subscriptiondialog_p.h" 00041 #include "renamefavoritedialog.h" 00042 #include "trashjob.h" 00043 #include "trashrestorejob.h" 00044 #include "entitydeletedattribute.h" 00045 #include "recentcollectionaction_p.h" 00046 00047 #include <KAction> 00048 #include <KActionCollection> 00049 #include <KActionMenu> 00050 #include <KDebug> 00051 #include <KInputDialog> 00052 #include <KLocale> 00053 #include <KMenu> 00054 #include <KMessageBox> 00055 #include <KToggleAction> 00056 00057 #include <QtCore/QMimeData> 00058 #include <QtGui/QApplication> 00059 #include <QtGui/QClipboard> 00060 #include <QtGui/QItemSelectionModel> 00061 #include <QWeakPointer> 00062 00063 #include <boost/static_assert.hpp> 00064 00065 using namespace Akonadi; 00066 00067 //@cond PRIVATE 00068 00069 enum ActionType 00070 { 00071 NormalAction, 00072 ActionWithAlternative, //Normal action, but with an alternative state 00073 ActionAlternative, //Alternative state of the ActionWithAlternative 00074 MenuAction, 00075 ToggleAction 00076 }; 00077 00078 static const struct { 00079 const char *name; 00080 const char *label; 00081 const char *iconLabel; 00082 const char *icon; 00083 int shortcut; 00084 const char* slot; 00085 ActionType actionType; 00086 } standardActionData[] = { 00087 { "akonadi_collection_create", I18N_NOOP( "&New Folder..." ), I18N_NOOP( "New" ), "folder-new", 0, SLOT(slotCreateCollection()), NormalAction }, 00088 { "akonadi_collection_copy", 0, 0, "edit-copy", 0, SLOT(slotCopyCollections()), NormalAction }, 00089 { "akonadi_collection_delete", I18N_NOOP( "&Delete Folder" ), I18N_NOOP( "Delete" ), "edit-delete", 0, SLOT(slotDeleteCollection()), NormalAction }, 00090 { "akonadi_collection_sync", I18N_NOOP( "&Synchronize Folder" ), I18N_NOOP( "Synchronize" ), "view-refresh", Qt::Key_F5, SLOT(slotSynchronizeCollection()), NormalAction }, 00091 { "akonadi_collection_properties", I18N_NOOP( "Folder &Properties" ), I18N_NOOP( "Properties" ), "configure", 0, SLOT(slotCollectionProperties()), NormalAction }, 00092 { "akonadi_item_copy", 0, 0, "edit-copy", 0, SLOT(slotCopyItems()), NormalAction }, 00093 { "akonadi_paste", I18N_NOOP( "&Paste" ), I18N_NOOP( "Paste" ), "edit-paste", Qt::CTRL + Qt::Key_V, SLOT(slotPaste()), NormalAction }, 00094 { "akonadi_item_delete", 0, 0, "edit-delete", Qt::Key_Delete, SLOT(slotDeleteItems()), NormalAction }, 00095 { "akonadi_manage_local_subscriptions", I18N_NOOP( "Manage Local &Subscriptions..." ), I18N_NOOP( "Manage Local Subscriptions" ), "folder-bookmarks", 0, SLOT(slotLocalSubscription()), NormalAction }, 00096 { "akonadi_collection_add_to_favorites", I18N_NOOP( "Add to Favorite Folders" ), I18N_NOOP( "Add to Favorite" ), "bookmark-new", 0, SLOT(slotAddToFavorites()), NormalAction }, 00097 { "akonadi_collection_remove_from_favorites", I18N_NOOP( "Remove from Favorite Folders" ), I18N_NOOP( "Remove from Favorite" ), "edit-delete", 0, SLOT(slotRemoveFromFavorites()), NormalAction }, 00098 { "akonadi_collection_rename_favorite", I18N_NOOP( "Rename Favorite..." ), I18N_NOOP( "Rename" ), "edit-rename", 0, SLOT(slotRenameFavorite()), NormalAction }, 00099 { "akonadi_collection_copy_to_menu", I18N_NOOP( "Copy Folder To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyCollectionTo(QAction*)), MenuAction }, 00100 { "akonadi_item_copy_to_menu", I18N_NOOP( "Copy Item To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyItemTo(QAction*)), MenuAction }, 00101 { "akonadi_item_move_to_menu", I18N_NOOP( "Move Item To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveItemTo(QAction*)), MenuAction }, 00102 { "akonadi_collection_move_to_menu", I18N_NOOP( "Move Folder To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveCollectionTo(QAction*)), MenuAction }, 00103 { "akonadi_item_cut", I18N_NOOP( "&Cut Item" ), I18N_NOOP( "Cut" ), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutItems()), NormalAction }, 00104 { "akonadi_collection_cut", I18N_NOOP( "&Cut Folder" ), I18N_NOOP( "Cut" ), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutCollections()), NormalAction }, 00105 { "akonadi_resource_create", I18N_NOOP( "Create Resource" ), 0, "folder-new", 0, SLOT(slotCreateResource()), NormalAction }, 00106 { "akonadi_resource_delete", I18N_NOOP( "Delete Resource" ), 0, "edit-delete", 0, SLOT(slotDeleteResource()), NormalAction }, 00107 { "akonadi_resource_properties", I18N_NOOP( "&Resource Properties" ), I18N_NOOP( "Properties" ), "configure", 0, SLOT(slotResourceProperties()), NormalAction }, 00108 { "akonadi_resource_synchronize", I18N_NOOP( "Synchronize Resource" ), I18N_NOOP( "Synchronize" ), "view-refresh", 0, SLOT(slotSynchronizeResource()), NormalAction }, 00109 { "akonadi_work_offline", I18N_NOOP( "Work Offline" ), 0, "user-offline", 0, SLOT(slotToggleWorkOffline(bool)), ToggleAction }, 00110 { "akonadi_collection_copy_to_dialog", I18N_NOOP( "Copy Folder To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyCollectionTo()), NormalAction }, 00111 { "akonadi_collection_move_to_dialog", I18N_NOOP( "Move Folder To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveCollectionTo()), NormalAction }, 00112 { "akonadi_item_copy_to_dialog", I18N_NOOP( "Copy Item To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyItemTo()), NormalAction }, 00113 { "akonadi_item_move_to_dialog", I18N_NOOP( "Move Item To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveItemTo()), NormalAction }, 00114 { "akonadi_collection_sync_recursive", I18N_NOOP( "&Synchronize Folder Recursively" ), I18N_NOOP( "Synchronize Recursively" ), "view-refresh", Qt::CTRL + Qt::Key_F5, SLOT(slotSynchronizeCollectionRecursive()), NormalAction }, 00115 { "akonadi_move_collection_to_trash", I18N_NOOP( "&Move Folder To Trash" ), I18N_NOOP( "Move Folder To Trash" ), "user-trash", 0, SLOT(slotMoveCollectionToTrash()), NormalAction }, 00116 { "akonadi_move_item_to_trash", I18N_NOOP( "&Move Item To Trash" ), I18N_NOOP( "Move Item To Trash" ), "user-trash", 0, SLOT(slotMoveItemToTrash()), NormalAction }, 00117 { "akonadi_restore_collection_from_trash", I18N_NOOP( "&Restore Folder From Trash" ), I18N_NOOP( "Restore Folder From Trash" ), "view-refresh", 0, SLOT(slotRestoreCollectionFromTrash()), NormalAction }, 00118 { "akonadi_restore_item_from_trash", I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "view-refresh", 0, SLOT(slotRestoreItemFromTrash()), NormalAction }, 00119 { "akonadi_collection_trash_restore", I18N_NOOP( "&Restore Folder From Trash" ), I18N_NOOP( "Restore Folder From Trash" ), "user-trash", 0, SLOT(slotTrashRestoreCollection()), ActionWithAlternative }, 00120 { 0, I18N_NOOP( "&Restore Collection From Trash" ), I18N_NOOP( "Restore Collection From Trash" ), "view-refresh", 0, 0, ActionAlternative }, 00121 { "akonadi_item_trash_restore", I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "user-trash", 0, SLOT(slotTrashRestoreItem()), ActionWithAlternative }, 00122 { 0, I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "view-refresh", 0, 0, ActionAlternative }, 00123 { "akonadi_collection_sync_favorite_folders", I18N_NOOP( "&Synchronize Favorite Folders" ), I18N_NOOP( "Synchronize Favorite Folders" ), "view-refresh", Qt::CTRL+Qt::SHIFT+Qt::Key_L , SLOT(slotSynchronizeFavoriteCollections()), NormalAction } 00124 00125 }; 00126 static const int numStandardActionData = sizeof standardActionData / sizeof *standardActionData; 00127 00128 BOOST_STATIC_ASSERT( numStandardActionData == StandardActionManager::LastType ); 00129 00130 static bool canCreateCollection( const Akonadi::Collection &collection ) 00131 { 00132 if ( !( collection.rights() & Akonadi::Collection::CanCreateCollection ) ) 00133 return false; 00134 00135 if ( !collection.contentMimeTypes().contains( Akonadi::Collection::mimeType() ) ) 00136 return false; 00137 00138 return true; 00139 } 00140 00141 static inline bool isRootCollection( const Akonadi::Collection &collection ) 00142 { 00143 return (collection == Akonadi::Collection::root()); 00144 } 00145 00146 static void setWorkOffline( bool offline ) 00147 { 00148 KConfig config( QLatin1String( "akonadikderc" ) ); 00149 KConfigGroup group( &config, QLatin1String( "Actions" ) ); 00150 00151 group.writeEntry( "WorkOffline", offline ); 00152 } 00153 00154 static bool workOffline() 00155 { 00156 KConfig config( QLatin1String( "akonadikderc" ) ); 00157 const KConfigGroup group( &config, QLatin1String( "Actions" ) ); 00158 00159 return group.readEntry( "WorkOffline", false ); 00160 } 00161 00165 class StandardActionManager::Private 00166 { 00167 public: 00168 Private( StandardActionManager *parent ) : 00169 q( parent ), 00170 collectionSelectionModel( 0 ), 00171 itemSelectionModel( 0 ), 00172 favoritesModel( 0 ), 00173 favoriteSelectionModel( 0 ) 00174 { 00175 actions.fill( 0, StandardActionManager::LastType ); 00176 00177 pluralLabels.insert( StandardActionManager::CopyCollections, 00178 ki18np( "&Copy Folder", "&Copy %1 Folders" ) ); 00179 pluralLabels.insert( StandardActionManager::CopyItems, 00180 ki18np( "&Copy Item", "&Copy %1 Items" ) ); 00181 pluralLabels.insert( StandardActionManager::CutItems, 00182 ki18np( "&Cut Item", "&Cut %1 Items" ) ); 00183 pluralLabels.insert( StandardActionManager::CutCollections, 00184 ki18np( "&Cut Folder", "&Cut %1 Folders" ) ); 00185 pluralLabels.insert( StandardActionManager::DeleteItems, 00186 ki18np( "&Delete Item", "&Delete %1 Items" ) ); 00187 pluralLabels.insert( StandardActionManager::DeleteCollections, 00188 ki18np( "&Delete Folder", "&Delete %1 Folders" ) ); 00189 pluralLabels.insert( StandardActionManager::SynchronizeCollections, 00190 ki18np( "&Synchronize Folder", "&Synchronize %1 Folders" ) ); 00191 pluralLabels.insert( StandardActionManager::DeleteResources, 00192 ki18np( "&Delete Resource", "&Delete %1 Resources" ) ); 00193 pluralLabels.insert( StandardActionManager::SynchronizeResources, 00194 ki18np( "&Synchronize Resource", "&Synchronize %1 Resources" ) ); 00195 00196 00197 pluralIconLabels.insert( StandardActionManager::CopyCollections, 00198 ki18np( "Copy Folder", "Copy %1 Folders" ) ); 00199 pluralIconLabels.insert( StandardActionManager::CopyItems, 00200 ki18np( "Copy Item", "Copy %1 Items" ) ); 00201 pluralIconLabels.insert( StandardActionManager::CutItems, 00202 ki18np( "Cut Item", "Cut %1 Items" ) ); 00203 pluralIconLabels.insert( StandardActionManager::CutCollections, 00204 ki18np( "Cut Folder", "Cut %1 Folders" ) ); 00205 pluralIconLabels.insert( StandardActionManager::DeleteItems, 00206 ki18np( "Delete Item", "Delete %1 Items" ) ); 00207 pluralIconLabels.insert( StandardActionManager::DeleteCollections, 00208 ki18np( "Delete Folder", "Delete %1 Folders" ) ); 00209 pluralIconLabels.insert( StandardActionManager::SynchronizeCollections, 00210 ki18np( "Synchronize Folder", "Synchronize %1 Folders" ) ); 00211 pluralIconLabels.insert( StandardActionManager::DeleteResources, 00212 ki18np( "Delete Resource", "Delete %1 Resources" ) ); 00213 pluralIconLabels.insert( StandardActionManager::SynchronizeResources, 00214 ki18np( "Synchronize Resource", "Synchronize %1 Resources" ) ); 00215 00216 setContextText( StandardActionManager::CreateCollection, StandardActionManager::DialogTitle, 00217 i18nc( "@title:window", "New Folder" ) ); 00218 setContextText( StandardActionManager::CreateCollection, StandardActionManager::DialogText, 00219 i18nc( "@label:textbox name of a thing", "Name" ) ); 00220 setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText, 00221 ki18n( "Could not create folder: %1" ) ); 00222 setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle, 00223 i18n( "Folder creation failed" ) ); 00224 00225 setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText, 00226 ki18np( "Do you really want to delete this folder and all its sub-folders?", 00227 "Do you really want to delete %1 folders and all their sub-folders?" ) ); 00228 setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, 00229 ki18ncp( "@title:window", "Delete folder?", "Delete folders?" ) ); 00230 setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText, 00231 ki18n( "Could not delete folder: %1" ) ); 00232 setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle, 00233 i18n( "Folder deletion failed" ) ); 00234 00235 setContextText( StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle, 00236 ki18nc( "@title:window", "Properties of Folder %1" ) ); 00237 00238 setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, 00239 ki18np( "Do you really want to delete the selected item?", 00240 "Do you really want to delete %1 items?" ) ); 00241 setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, 00242 ki18ncp( "@title:window", "Delete item?", "Delete items?" ) ); 00243 setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText, 00244 ki18n( "Could not delete item: %1" ) ); 00245 setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle, 00246 i18n( "Item deletion failed" ) ); 00247 00248 setContextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogTitle, 00249 i18nc( "@title:window", "Rename Favorite" ) ); 00250 setContextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogText, 00251 i18nc( "@label:textbox name of the folder", "Name:" ) ); 00252 00253 setContextText( StandardActionManager::CreateResource, StandardActionManager::DialogTitle, 00254 i18nc( "@title:window", "New Resource" ) ); 00255 setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText, 00256 ki18n( "Could not create resource: %1" ) ); 00257 setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle, 00258 i18n( "Resource creation failed" ) ); 00259 00260 setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, 00261 ki18np( "Do you really want to delete this resource?", 00262 "Do you really want to delete %1 resources?" ) ); 00263 setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, 00264 ki18ncp( "@title:window", "Delete Resource?", "Delete Resources?" ) ); 00265 00266 setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageText, 00267 ki18n( "Could not paste data: %1" ) ); 00268 setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle, 00269 i18n( "Paste failed" ) ); 00270 00271 qRegisterMetaType<Akonadi::Item::List>("Akonadi::Item::List"); 00272 } 00273 00274 void enableAction( int type, bool enable ) 00275 { 00276 enableAction( static_cast<StandardActionManager::Type>( type ), enable ); 00277 } 00278 00279 void enableAction( StandardActionManager::Type type, bool enable ) 00280 { 00281 Q_ASSERT( type < StandardActionManager::LastType ); 00282 if ( actions[type] ) 00283 actions[type]->setEnabled( enable ); 00284 00285 // Update the action menu 00286 KActionMenu *actionMenu = qobject_cast<KActionMenu*>( actions[type] ); 00287 if ( actionMenu ) { 00288 //get rid of the submenus, they are re-created in enableAction. clear() is not enough, doesn't remove the submenu object instances. 00289 KMenu *menu = actionMenu->menu(); 00290 //Not necessary to delete and recreate menu when it was not created 00291 if ( menu->property( "actionType" ).isValid() && menu->isEmpty() ) 00292 return; 00293 delete menu; 00294 menu = new KMenu(); 00295 00296 menu->setProperty( "actionType", static_cast<int>( type ) ); 00297 q->connect( menu, SIGNAL(aboutToShow()), SLOT(aboutToShowMenu()) ); 00298 q->connect( menu, SIGNAL(triggered(QAction*)), standardActionData[ type ].slot ); 00299 actionMenu->setMenu( menu ); 00300 } 00301 } 00302 00303 void aboutToShowMenu() 00304 { 00305 QMenu *menu = qobject_cast<QMenu*>( q->sender() ); 00306 if ( !menu ) 00307 return; 00308 00309 if ( !menu->isEmpty() ) 00310 return; 00311 const StandardActionManager::Type type = static_cast<StandardActionManager::Type>( menu->property( "actionType" ).toInt() ); 00312 00313 QWeakPointer<RecentCollectionAction> recentCollection = new RecentCollectionAction( collectionSelectionModel->model(), menu ); 00314 mRecentCollectionsMenu.insert( type, recentCollection ); 00315 const QSet<QString> mimeTypes = mimeTypesOfSelection( type ); 00316 fillFoldersMenu( mimeTypes, 00317 type, 00318 menu, 00319 collectionSelectionModel->model(), 00320 QModelIndex() ); 00321 } 00322 00323 void createActionFolderMenu(QMenu *menu, StandardActionManager::Type type) 00324 { 00325 if ( type == CopyCollectionToMenu || 00326 type == CopyItemToMenu || 00327 type == MoveItemToMenu || 00328 type ==MoveCollectionToMenu ) 00329 { 00330 00331 QWeakPointer<RecentCollectionAction> recentCollection = new RecentCollectionAction( collectionSelectionModel->model(), menu ); 00332 const QSet<QString> mimeTypes = mimeTypesOfSelection( type ); 00333 fillFoldersMenu( mimeTypes, 00334 type, 00335 menu, 00336 collectionSelectionModel->model(), 00337 QModelIndex() ); 00338 } 00339 } 00340 00341 00342 void updateAlternatingAction( int type ) 00343 { 00344 updateAlternatingAction( static_cast<StandardActionManager::Type>( type ) ); 00345 } 00346 00347 void updateAlternatingAction( StandardActionManager::Type type ) 00348 { 00349 Q_ASSERT( type < StandardActionManager::LastType ); 00350 if (!actions[type]) { 00351 return; 00352 } 00353 00354 /* 00355 * The same action is stored at the ActionWithAlternative indexes as well as the corresponding ActionAlternative indexes in the actions array. 00356 * The following simply changes the standardActionData 00357 */ 00358 if ( ( standardActionData[type].actionType == ActionWithAlternative ) || ( standardActionData[type].actionType == ActionAlternative ) ) { 00359 actions[type]->setText( i18n ( standardActionData[type].label ) ); 00360 actions[type]->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) ); 00361 00362 if ( pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() ) 00363 actions[type]->setText( pluralLabels.value( type ).subs( 1 ).toString() ); 00364 else if ( standardActionData[type].label ) 00365 actions[type]->setText( i18n( standardActionData[type].label ) ); 00366 00367 if ( pluralIconLabels.contains( type ) && !pluralIconLabels.value( type ).isEmpty() ) 00368 actions[type]->setIconText( pluralIconLabels.value( type ).subs( 1 ).toString() ); 00369 else if ( standardActionData[type].iconLabel ) 00370 actions[type]->setIconText( i18n( standardActionData[type].iconLabel ) ); 00371 00372 if ( standardActionData[type].icon ) 00373 actions[type]->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) ); 00374 00375 //actions[type]->setShortcut( standardActionData[type].shortcut ); 00376 00377 /*if ( standardActionData[type].slot ) { 00378 switch ( standardActionData[type].actionType ) { 00379 case NormalAction: 00380 case ActionWithAlternative: 00381 connect( action, SIGNAL(triggered()), standardActionData[type].slot ); 00382 break; 00383 } 00384 }*/ 00385 } 00386 } 00387 00388 void updatePluralLabel( int type, int count ) 00389 { 00390 updatePluralLabel( static_cast<StandardActionManager::Type>( type ), count ); 00391 } 00392 00393 void updatePluralLabel( StandardActionManager::Type type, int count ) 00394 { 00395 Q_ASSERT( type < StandardActionManager::LastType ); 00396 if ( actions[type] && pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() ) { 00397 actions[type]->setText( pluralLabels.value( type ).subs( qMax( count, 1 ) ).toString() ); 00398 } 00399 } 00400 00401 bool isFavoriteCollection( const Akonadi::Collection &collection ) 00402 { 00403 if ( !favoritesModel ) 00404 return false; 00405 00406 return favoritesModel->collections().contains( collection ); 00407 } 00408 00409 void encodeToClipboard( QItemSelectionModel* selectionModel, bool cut = false ) 00410 { 00411 Q_ASSERT( selectionModel ); 00412 if ( selectionModel->selectedRows().count() <= 0 ) 00413 return; 00414 00415 #ifndef QT_NO_CLIPBOARD 00416 QMimeData *mimeData = selectionModel->model()->mimeData( selectionModel->selectedRows() ); 00417 markCutAction( mimeData, cut ); 00418 QApplication::clipboard()->setMimeData( mimeData ); 00419 00420 QAbstractItemModel *model = const_cast<QAbstractItemModel *>( selectionModel->model() ); 00421 00422 foreach ( const QModelIndex &index, selectionModel->selectedRows() ) 00423 model->setData( index, true, EntityTreeModel::PendingCutRole ); 00424 #endif 00425 } 00426 00427 void updateActions() 00428 { 00429 // collect all selected collections 00430 Collection::List selectedCollections; 00431 if ( collectionSelectionModel ) { 00432 const QModelIndexList rows = collectionSelectionModel->selectedRows(); 00433 foreach ( const QModelIndex &index, rows ) { 00434 Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 00435 if ( !collection.isValid() ) 00436 continue; 00437 00438 const Collection parentCollection = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>(); 00439 collection.setParentCollection( parentCollection ); 00440 00441 selectedCollections << collection; 00442 } 00443 } 00444 00445 // collect all selected items 00446 Item::List selectedItems; 00447 if ( itemSelectionModel ) { 00448 const QModelIndexList rows = itemSelectionModel->selectedRows(); 00449 foreach ( const QModelIndex &index, rows ) { 00450 Item item = index.data( EntityTreeModel::ItemRole ).value<Item>(); 00451 if ( !item.isValid() ) 00452 continue; 00453 00454 const Collection parentCollection = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>(); 00455 item.setParentCollection( parentCollection ); 00456 00457 selectedItems << item; 00458 } 00459 } 00460 00461 mActionStateManager.updateState( selectedCollections, selectedItems ); 00462 00463 emit q->actionStateUpdated(); 00464 } 00465 00466 #ifndef QT_NO_CLIPBOARD 00467 void clipboardChanged( QClipboard::Mode mode ) 00468 { 00469 if ( mode == QClipboard::Clipboard ) 00470 updateActions(); 00471 } 00472 #endif 00473 00474 QItemSelection mapToEntityTreeModel( const QAbstractItemModel *model, const QItemSelection &selection ) const 00475 { 00476 const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ); 00477 if ( proxy ) { 00478 return mapToEntityTreeModel( proxy->sourceModel(), proxy->mapSelectionToSource( selection ) ); 00479 } else { 00480 return selection; 00481 } 00482 } 00483 00484 QItemSelection mapFromEntityTreeModel( const QAbstractItemModel *model, const QItemSelection &selection ) const 00485 { 00486 const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ); 00487 if ( proxy ) { 00488 const QItemSelection select = mapFromEntityTreeModel( proxy->sourceModel(), selection ); 00489 return proxy->mapSelectionFromSource( select ); 00490 } else { 00491 return selection; 00492 } 00493 } 00494 00495 void collectionSelectionChanged() 00496 { 00497 q->blockSignals( true ); 00498 00499 QItemSelection selection = collectionSelectionModel->selection(); 00500 selection = mapToEntityTreeModel( collectionSelectionModel->model(), selection ); 00501 selection = mapFromEntityTreeModel( favoritesModel, selection ); 00502 00503 if ( favoriteSelectionModel ) 00504 favoriteSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect ); 00505 00506 q->blockSignals( false ); 00507 00508 updateActions(); 00509 } 00510 00511 void favoriteSelectionChanged() 00512 { 00513 q->blockSignals( true ); 00514 00515 QItemSelection selection = favoriteSelectionModel->selection(); 00516 if ( selection.indexes().isEmpty() ) 00517 return; 00518 00519 selection = mapToEntityTreeModel( favoritesModel, selection ); 00520 selection = mapFromEntityTreeModel( collectionSelectionModel->model(), selection ); 00521 00522 collectionSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); 00523 q->blockSignals( false ); 00524 00525 updateActions(); 00526 } 00527 00528 void slotCreateCollection() 00529 { 00530 Q_ASSERT( collectionSelectionModel ); 00531 if ( collectionSelectionModel->selection().indexes().isEmpty() ) 00532 return; 00533 00534 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); 00535 Q_ASSERT( index.isValid() ); 00536 const Collection parentCollection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00537 Q_ASSERT( parentCollection.isValid() ); 00538 00539 if ( !canCreateCollection( parentCollection ) ) 00540 return; 00541 00542 const QString name = KInputDialog::getText( contextText( StandardActionManager::CreateCollection, StandardActionManager::DialogTitle ), 00543 contextText( StandardActionManager::CreateCollection, StandardActionManager::DialogText ), 00544 QString(), 0, parentWidget ); 00545 if ( name.isEmpty() ) 00546 return; 00547 00548 if ( name.contains( QLatin1Char( '/' ) ) ) { 00549 KMessageBox::error( parentWidget, 00550 i18n( "We can not add \"/\" in folder name." ), 00551 i18n( "Create new folder error" ) ); 00552 00553 return; 00554 } 00555 00556 Collection collection; 00557 collection.setName( name ); 00558 collection.setParentCollection( parentCollection ); 00559 if ( actions[StandardActionManager::CreateCollection] ) { 00560 const QStringList mts = actions[StandardActionManager::CreateCollection]->property( "ContentMimeTypes" ).toStringList(); 00561 if ( !mts.isEmpty() ) 00562 collection.setContentMimeTypes( mts ); 00563 } 00564 CollectionCreateJob *job = new CollectionCreateJob( collection ); 00565 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionCreationResult(KJob*)) ); 00566 } 00567 00568 void slotCopyCollections() 00569 { 00570 encodeToClipboard( collectionSelectionModel ); 00571 } 00572 00573 void slotCutCollections() 00574 { 00575 encodeToClipboard( collectionSelectionModel, true ); 00576 } 00577 00578 Collection::List selectedCollections() const 00579 { 00580 Collection::List collections; 00581 00582 Q_ASSERT( collectionSelectionModel ); 00583 00584 foreach ( const QModelIndex &index, collectionSelectionModel->selectedRows() ) { 00585 Q_ASSERT( index.isValid() ); 00586 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00587 Q_ASSERT( collection.isValid() ); 00588 00589 collections << collection; 00590 } 00591 00592 return collections; 00593 } 00594 00595 void slotDeleteCollection() 00596 { 00597 const Collection::List collections = selectedCollections(); 00598 if ( collections.isEmpty() ) 00599 return; 00600 00601 const QString collectionName = collections.first().name(); 00602 const QString text = contextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText, 00603 collections.count(), collectionName ); 00604 00605 if ( KMessageBox::questionYesNo( parentWidget, text, 00606 contextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, collections.count(), collectionName ), 00607 KStandardGuiItem::del(), KStandardGuiItem::cancel(), 00608 QString(), KMessageBox::Dangerous ) != KMessageBox::Yes ) 00609 return; 00610 00611 foreach ( const Collection &collection, collections ) { 00612 CollectionDeleteJob *job = new CollectionDeleteJob( collection, q ); 00613 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionDeletionResult(KJob*)) ); 00614 } 00615 } 00616 00617 void slotMoveCollectionToTrash() 00618 { 00619 const Collection::List collections = selectedCollections(); 00620 if ( collections.isEmpty() ) 00621 return; 00622 00623 foreach ( const Collection &collection, collections ) { 00624 TrashJob *job = new TrashJob( collection, q ); 00625 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveCollectionToTrashResult(KJob*)) ); 00626 } 00627 } 00628 00629 void slotRestoreCollectionFromTrash() 00630 { 00631 const Collection::List collections = selectedCollections(); 00632 if ( collections.isEmpty() ) 00633 return; 00634 00635 foreach ( const Collection &collection, collections ) { 00636 TrashRestoreJob *job = new TrashRestoreJob( collection, q ); 00637 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveCollectionToTrashResult(KJob*)) ); 00638 } 00639 } 00640 00641 Item::List selectedItems() const 00642 { 00643 Item::List items; 00644 00645 Q_ASSERT( itemSelectionModel ); 00646 00647 foreach ( const QModelIndex &index, itemSelectionModel->selectedRows() ) { 00648 Q_ASSERT( index.isValid() ); 00649 const Item item = index.data( ItemModel::ItemRole ).value<Item>(); 00650 Q_ASSERT( item.isValid() ); 00651 00652 items << item; 00653 } 00654 00655 return items; 00656 } 00657 00658 void slotMoveItemToTrash() 00659 { 00660 const Item::List items = selectedItems(); 00661 if ( items.isEmpty() ) 00662 return; 00663 00664 TrashJob *job = new TrashJob( items, q ); 00665 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveItemToTrashResult(KJob*)) ); 00666 } 00667 00668 void slotRestoreItemFromTrash() 00669 { 00670 const Item::List items = selectedItems(); 00671 if ( items.isEmpty() ) 00672 return; 00673 00674 TrashRestoreJob *job = new TrashRestoreJob( items, q ); 00675 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveItemToTrashResult(KJob*)) ); 00676 } 00677 00678 void slotTrashRestoreCollection() 00679 { 00680 const Collection::List collections = selectedCollections(); 00681 if ( collections.isEmpty() ) 00682 return; 00683 00684 bool collectionsAreInTrash = false; 00685 foreach ( const Collection &collection, collections ) { 00686 if ( collection.hasAttribute<EntityDeletedAttribute>() ) { 00687 collectionsAreInTrash = true; 00688 break; 00689 } 00690 } 00691 00692 if (collectionsAreInTrash) { 00693 slotRestoreCollectionFromTrash(); 00694 } else { 00695 slotMoveCollectionToTrash(); 00696 } 00697 } 00698 00699 void slotTrashRestoreItem() 00700 { 00701 const Item::List items = selectedItems(); 00702 if ( items.isEmpty() ) 00703 return; 00704 00705 bool itemsAreInTrash = false; 00706 foreach ( const Item &item, items ) { 00707 if ( item.hasAttribute<EntityDeletedAttribute>() ) { 00708 itemsAreInTrash = true; 00709 break; 00710 } 00711 } 00712 00713 if (itemsAreInTrash) { 00714 slotRestoreItemFromTrash(); 00715 } else { 00716 slotMoveItemToTrash(); 00717 } 00718 } 00719 00720 void slotSynchronizeCollection() 00721 { 00722 Q_ASSERT( collectionSelectionModel ); 00723 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00724 if ( list.isEmpty() ) 00725 return; 00726 00727 const Collection::List collections = selectedCollections(); 00728 if ( collections.isEmpty() ) 00729 return; 00730 00731 foreach( const Collection &collection, collections ) { 00732 AgentManager::self()->synchronizeCollection( collection, false ); 00733 } 00734 } 00735 00736 void slotSynchronizeCollectionRecursive() 00737 { 00738 Q_ASSERT( collectionSelectionModel ); 00739 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00740 if ( list.isEmpty() ) 00741 return; 00742 00743 const Collection::List collections = selectedCollections(); 00744 if ( collections.isEmpty() ) 00745 return; 00746 00747 foreach( const Collection &collection, collections ) { 00748 AgentManager::self()->synchronizeCollection( collection, true ); 00749 } 00750 } 00751 00752 void slotCollectionProperties() 00753 { 00754 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00755 if ( list.isEmpty() ) 00756 return; 00757 00758 const QModelIndex index = list.first(); 00759 Q_ASSERT( index.isValid() ); 00760 00761 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00762 Q_ASSERT( collection.isValid() ); 00763 00764 const QString displayName = collection.hasAttribute<EntityDisplayAttribute>() ? collection.attribute<EntityDisplayAttribute>()->displayName() 00765 : collection.name(); 00766 00767 CollectionPropertiesDialog* dlg = new CollectionPropertiesDialog( collection, mCollectionPropertiesPageNames, parentWidget ); 00768 dlg->setCaption( contextText( StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle ).arg( displayName ) ); 00769 dlg->show(); 00770 } 00771 00772 void slotCopyItems() 00773 { 00774 encodeToClipboard( itemSelectionModel ); 00775 } 00776 00777 void slotCutItems() 00778 { 00779 encodeToClipboard( itemSelectionModel, true ); 00780 } 00781 00782 void slotPaste() 00783 { 00784 Q_ASSERT( collectionSelectionModel ); 00785 00786 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00787 if ( list.isEmpty() ) 00788 return; 00789 00790 const QModelIndex index = list.first(); 00791 Q_ASSERT( index.isValid() ); 00792 00793 #ifndef QT_NO_CLIPBOARD 00794 // TODO: Copy or move? We can't seem to cut yet 00795 QAbstractItemModel *model = const_cast<QAbstractItemModel *>( collectionSelectionModel->model() ); 00796 const QMimeData *mimeData = QApplication::clipboard()->mimeData(); 00797 model->dropMimeData( mimeData, isCutAction( mimeData ) ? Qt::MoveAction : Qt::CopyAction, -1, -1, index ); 00798 model->setData( QModelIndex(), false, EntityTreeModel::PendingCutRole ); 00799 QApplication::clipboard()->clear(); 00800 #endif 00801 } 00802 00803 void slotDeleteItems() 00804 { 00805 Q_ASSERT( itemSelectionModel ); 00806 00807 Item::List items; 00808 foreach ( const QModelIndex &index, itemSelectionModel->selectedRows() ) { 00809 bool ok; 00810 const qlonglong id = index.data( ItemModel::IdRole ).toLongLong( &ok ); 00811 Q_ASSERT( ok ); 00812 items << Item( id ); 00813 } 00814 00815 if ( items.isEmpty() ) 00816 return; 00817 00818 QMetaObject::invokeMethod(q, "slotDeleteItemsDeferred", 00819 Qt::QueuedConnection, 00820 Q_ARG(Akonadi::Item::List, items)); 00821 } 00822 00823 void slotDeleteItemsDeferred(const Akonadi::Item::List &items) 00824 { 00825 Q_ASSERT( itemSelectionModel ); 00826 00827 if ( KMessageBox::questionYesNo( parentWidget, 00828 contextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, items.count(), QString() ), 00829 contextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, items.count(), QString() ), 00830 KStandardGuiItem::del(), KStandardGuiItem::cancel(), 00831 QString(), KMessageBox::Dangerous ) != KMessageBox::Yes ) 00832 return; 00833 00834 ItemDeleteJob *job = new ItemDeleteJob( items, q ); 00835 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(itemDeletionResult(KJob*)) ); 00836 } 00837 00838 void slotLocalSubscription() 00839 { 00840 SubscriptionDialog* dlg = new SubscriptionDialog( mMimeTypeFilter, parentWidget ); 00841 dlg->show(); 00842 } 00843 00844 void slotAddToFavorites() 00845 { 00846 Q_ASSERT( collectionSelectionModel ); 00847 Q_ASSERT( favoritesModel ); 00848 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00849 if ( list.isEmpty() ) 00850 return; 00851 00852 foreach ( const QModelIndex &index, list ) { 00853 Q_ASSERT( index.isValid() ); 00854 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00855 Q_ASSERT( collection.isValid() ); 00856 00857 favoritesModel->addCollection( collection ); 00858 } 00859 00860 updateActions(); 00861 } 00862 00863 void slotRemoveFromFavorites() 00864 { 00865 Q_ASSERT( collectionSelectionModel ); 00866 Q_ASSERT( favoritesModel ); 00867 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00868 if ( list.isEmpty() ) 00869 return; 00870 00871 foreach ( const QModelIndex &index, list ) { 00872 Q_ASSERT( index.isValid() ); 00873 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00874 Q_ASSERT( collection.isValid() ); 00875 00876 favoritesModel->removeCollection( collection ); 00877 } 00878 00879 updateActions(); 00880 } 00881 00882 void slotRenameFavorite() 00883 { 00884 Q_ASSERT( collectionSelectionModel ); 00885 Q_ASSERT( favoritesModel ); 00886 const QModelIndexList list = collectionSelectionModel->selectedRows(); 00887 if ( list.isEmpty() ) 00888 return; 00889 const QModelIndex index = list.first(); 00890 Q_ASSERT( index.isValid() ); 00891 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00892 Q_ASSERT( collection.isValid() ); 00893 00894 const QString displayName = collection.hasAttribute<EntityDisplayAttribute>() ? collection.attribute<EntityDisplayAttribute>()->displayName() : collection.name(); 00895 00896 RenameFavoriteDialog dlg(contextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogTitle ),contextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogText ) , favoritesModel->favoriteLabel( collection ), displayName, parentWidget ); 00897 if ( dlg.exec() ) 00898 { 00899 favoritesModel->setFavoriteLabel( collection, dlg.newName() ); 00900 } 00901 } 00902 00903 void slotSynchronizeFavoriteCollections() 00904 { 00905 Q_ASSERT( favoritesModel ); 00906 foreach( const Collection& collection, favoritesModel->collections() ) { 00907 // there might be virtual collections in favorites which cannot be checked 00908 // so let's be safe here, agentmanager asserts otherwise 00909 if ( !collection.resource().isEmpty() ) { 00910 AgentManager::self()->synchronizeCollection( collection, false ); 00911 } 00912 } 00913 } 00914 00915 void slotCopyCollectionTo() 00916 { 00917 pasteTo( collectionSelectionModel, collectionSelectionModel->model(), CopyCollectionToMenu, Qt::CopyAction ); 00918 } 00919 00920 void slotCopyItemTo() 00921 { 00922 pasteTo( itemSelectionModel, collectionSelectionModel->model(), CopyItemToMenu, Qt::CopyAction ); 00923 } 00924 00925 void slotMoveCollectionTo() 00926 { 00927 pasteTo( collectionSelectionModel, collectionSelectionModel->model(), MoveCollectionToMenu, Qt::MoveAction ); 00928 } 00929 00930 void slotMoveItemTo() 00931 { 00932 pasteTo( itemSelectionModel, collectionSelectionModel->model(), MoveItemToMenu, Qt::MoveAction ); 00933 } 00934 00935 void slotCopyCollectionTo( QAction *action ) 00936 { 00937 pasteTo( collectionSelectionModel, action, Qt::CopyAction ); 00938 } 00939 00940 void slotCopyItemTo( QAction *action ) 00941 { 00942 pasteTo( itemSelectionModel, action, Qt::CopyAction ); 00943 } 00944 00945 void slotMoveCollectionTo( QAction *action ) 00946 { 00947 pasteTo( collectionSelectionModel, action, Qt::MoveAction ); 00948 } 00949 00950 void slotMoveItemTo( QAction *action ) 00951 { 00952 pasteTo( itemSelectionModel, action, Qt::MoveAction ); 00953 } 00954 00955 AgentInstance::List selectedAgentInstances() const 00956 { 00957 AgentInstance::List instances; 00958 00959 Q_ASSERT( collectionSelectionModel ); 00960 if ( collectionSelectionModel->selection().indexes().isEmpty() ) 00961 return instances; 00962 00963 foreach ( const QModelIndex &index, collectionSelectionModel->selection().indexes() ) { 00964 Q_ASSERT( index.isValid() ); 00965 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>(); 00966 Q_ASSERT( collection.isValid() ); 00967 00968 if ( collection.isValid() ) { 00969 const QString identifier = collection.resource(); 00970 instances << AgentManager::self()->instance( identifier ); 00971 } 00972 } 00973 00974 return instances; 00975 } 00976 00977 AgentInstance selectedAgentInstance() const 00978 { 00979 const AgentInstance::List instances = selectedAgentInstances(); 00980 00981 if ( instances.isEmpty() ) 00982 return AgentInstance(); 00983 00984 return instances.first(); 00985 } 00986 00987 void slotCreateResource() 00988 { 00989 Akonadi::AgentTypeDialog dlg( parentWidget ); 00990 dlg.setCaption( contextText( StandardActionManager::CreateResource, StandardActionManager::DialogTitle ) ); 00991 00992 foreach ( const QString &mimeType, mMimeTypeFilter ) 00993 dlg.agentFilterProxyModel()->addMimeTypeFilter( mimeType ); 00994 00995 foreach ( const QString &capability, mCapabilityFilter ) 00996 dlg.agentFilterProxyModel()->addCapabilityFilter( capability ); 00997 00998 if ( dlg.exec() ) { 00999 const AgentType agentType = dlg.agentType(); 01000 01001 if ( agentType.isValid() ) { 01002 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, q ); 01003 q->connect( job, SIGNAL(result(KJob*)), SLOT(resourceCreationResult(KJob*)) ); 01004 job->configure( parentWidget ); 01005 job->start(); 01006 } 01007 } 01008 } 01009 01010 void slotDeleteResource() 01011 { 01012 const AgentInstance::List instances = selectedAgentInstances(); 01013 if ( instances.isEmpty() ) 01014 return; 01015 01016 if ( KMessageBox::questionYesNo( parentWidget, 01017 contextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, instances.count(), instances.first().name() ), 01018 contextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, instances.count(), instances.first().name() ), 01019 KStandardGuiItem::del(), KStandardGuiItem::cancel(), 01020 QString(), KMessageBox::Dangerous ) != KMessageBox::Yes ) 01021 return; 01022 01023 foreach ( const AgentInstance &instance, instances ) 01024 AgentManager::self()->removeInstance( instance ); 01025 } 01026 01027 void slotSynchronizeResource() 01028 { 01029 const AgentInstance::List instances = selectedAgentInstances(); 01030 if ( instances.isEmpty() ) 01031 return; 01032 01033 foreach ( AgentInstance instance, instances ) { //krazy:exclude=foreach 01034 instance.synchronize(); 01035 } 01036 } 01037 01038 void slotResourceProperties() 01039 { 01040 AgentInstance instance = selectedAgentInstance(); 01041 if ( !instance.isValid() ) 01042 return; 01043 01044 instance.configure( parentWidget ); 01045 } 01046 01047 void slotToggleWorkOffline( bool offline ) 01048 { 01049 setWorkOffline( offline ); 01050 01051 AgentInstance::List instances = AgentManager::self()->instances(); 01052 foreach ( AgentInstance instance, instances ) { //krazy:exclude=foreach 01053 instance.setIsOnline( !offline ); 01054 } 01055 } 01056 01057 void pasteTo( QItemSelectionModel *selectionModel, const QAbstractItemModel *model, StandardActionManager::Type type, Qt::DropAction dropAction ) 01058 { 01059 const QSet<QString> mimeTypes = mimeTypesOfSelection( type ); 01060 01061 CollectionDialog dlg( const_cast<QAbstractItemModel*>( model ) ); 01062 dlg.setMimeTypeFilter( mimeTypes.toList() ); 01063 01064 if ( type == CopyItemToMenu || type == MoveItemToMenu ) 01065 dlg.setAccessRightsFilter( Collection::CanCreateItem ); 01066 else if ( type == CopyCollectionToMenu || type == MoveCollectionToMenu ) 01067 dlg.setAccessRightsFilter( Collection::CanCreateCollection ); 01068 01069 if ( dlg.exec() ) { 01070 const QModelIndex index = EntityTreeModel::modelIndexForCollection( collectionSelectionModel->model(), dlg.selectedCollection() ); 01071 if ( !index.isValid() ) 01072 return; 01073 01074 const QMimeData *mimeData = selectionModel->model()->mimeData( selectionModel->selectedRows() ); 01075 01076 QAbstractItemModel *model = const_cast<QAbstractItemModel *>( index.model() ); 01077 model->dropMimeData( mimeData, dropAction, -1, -1, index ); 01078 } 01079 } 01080 01081 void pasteTo( QItemSelectionModel *selectionModel, QAction *action, Qt::DropAction dropAction ) 01082 { 01083 Q_ASSERT( selectionModel ); 01084 Q_ASSERT( action ); 01085 01086 if ( selectionModel->selectedRows().count() <= 0 ) 01087 return; 01088 01089 const QMimeData *mimeData = selectionModel->model()->mimeData( selectionModel->selectedRows() ); 01090 01091 const QModelIndex index = action->data().value<QModelIndex>(); 01092 Q_ASSERT( index.isValid() ); 01093 01094 QAbstractItemModel *model = const_cast<QAbstractItemModel *>( index.model() ); 01095 const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 01096 addRecentCollection( collection.id() ); 01097 model->dropMimeData( mimeData, dropAction, -1, -1, index ); 01098 } 01099 01100 void addRecentCollection( Akonadi::Collection::Id id ) 01101 { 01102 QMapIterator<StandardActionManager::Type, QWeakPointer<RecentCollectionAction> > item(mRecentCollectionsMenu); 01103 while (item.hasNext()) { 01104 item.next(); 01105 if ( item.value().data() ) { 01106 item.value().data()->addRecentCollection( id ); 01107 } 01108 } 01109 } 01110 01111 void collectionCreationResult( KJob *job ) 01112 { 01113 if ( job->error() ) { 01114 KMessageBox::error( parentWidget, 01115 contextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01116 contextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle ) ); 01117 } 01118 } 01119 01120 void collectionDeletionResult( KJob *job ) 01121 { 01122 if ( job->error() ) { 01123 KMessageBox::error( parentWidget, 01124 contextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01125 contextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle ) ); 01126 } 01127 } 01128 01129 void moveCollectionToTrashResult( KJob *job ) 01130 { 01131 if ( job->error() ) { 01132 KMessageBox::error( parentWidget, 01133 contextText( StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01134 contextText( StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageTitle ) ); 01135 } 01136 } 01137 01138 void moveItemToTrashResult( KJob *job ) 01139 { 01140 if ( job->error() ) { 01141 KMessageBox::error( parentWidget, 01142 contextText( StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01143 contextText( StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageTitle ) ); 01144 } 01145 } 01146 01147 void itemDeletionResult( KJob *job ) 01148 { 01149 if ( job->error() ) { 01150 KMessageBox::error( parentWidget, 01151 contextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01152 contextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle ) ); 01153 } 01154 } 01155 01156 void resourceCreationResult( KJob *job ) 01157 { 01158 if ( job->error() ) { 01159 KMessageBox::error( parentWidget, 01160 contextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01161 contextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle ) ); 01162 } 01163 } 01164 01165 void pasteResult( KJob *job ) 01166 { 01167 if ( job->error() ) { 01168 KMessageBox::error( parentWidget, 01169 contextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageText ).arg( job->errorString() ), 01170 contextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle ) ); 01171 } 01172 } 01173 01177 QSet<QString> mimeTypesOfSelection( StandardActionManager::Type type ) const 01178 { 01179 QModelIndexList list; 01180 QSet<QString> mimeTypes; 01181 01182 const bool isItemAction = ( type == CopyItemToMenu || type == MoveItemToMenu ); 01183 const bool isCollectionAction = ( type == CopyCollectionToMenu || type == MoveCollectionToMenu ); 01184 01185 if ( isItemAction ) { 01186 list = itemSelectionModel->selectedRows(); 01187 foreach ( const QModelIndex &index, list ) 01188 mimeTypes << index.data( EntityTreeModel::MimeTypeRole ).toString(); 01189 } 01190 01191 if ( isCollectionAction ) { 01192 list = collectionSelectionModel->selectedRows(); 01193 foreach ( const QModelIndex &index, list ) { 01194 const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 01195 01196 // The mimetypes that the selected collection can possibly contain 01197 mimeTypes = AgentManager::self()->instance( collection.resource() ).type().mimeTypes().toSet(); 01198 } 01199 } 01200 01201 return mimeTypes; 01202 } 01203 01207 bool isWritableTargetCollectionForMimeTypes( const Collection &collection, const QSet<QString> &mimeTypes, StandardActionManager::Type type ) const 01208 { 01209 if ( CollectionUtils::isVirtual( collection ) ) 01210 return false; 01211 01212 const bool isItemAction = ( type == CopyItemToMenu || type == MoveItemToMenu ); 01213 const bool isCollectionAction = ( type == CopyCollectionToMenu || type == MoveCollectionToMenu ); 01214 01215 const bool canContainRequiredMimeTypes = !collection.contentMimeTypes().toSet().intersect( mimeTypes ).isEmpty(); 01216 const bool canCreateNewItems = (collection.rights() & Collection::CanCreateItem); 01217 01218 const bool canCreateNewCollections = (collection.rights() & Collection::CanCreateCollection); 01219 const bool canContainCollections = collection.contentMimeTypes().contains( Collection::mimeType() ); 01220 const bool resourceAllowsRequiredMimeTypes = AgentManager::self()->instance( collection.resource() ).type().mimeTypes().toSet().contains( mimeTypes ); 01221 01222 const bool isReadOnlyForItems = (isItemAction && (!canCreateNewItems || !canContainRequiredMimeTypes)); 01223 const bool isReadOnlyForCollections = (isCollectionAction && (!canCreateNewCollections || !canContainCollections || !resourceAllowsRequiredMimeTypes)); 01224 01225 return !(CollectionUtils::isStructural( collection ) || isReadOnlyForItems || isReadOnlyForCollections); 01226 } 01227 01228 void fillFoldersMenu( const QSet<QString>& mimeTypes, StandardActionManager::Type type, QMenu *menu, 01229 const QAbstractItemModel *model, QModelIndex parentIndex ) 01230 { 01231 const int rowCount = model->rowCount( parentIndex ); 01232 01233 for ( int row = 0; row < rowCount; ++row ) { 01234 const QModelIndex index = model->index( row, 0, parentIndex ); 01235 const Collection collection = model->data( index, CollectionModel::CollectionRole ).value<Collection>(); 01236 01237 if ( CollectionUtils::isVirtual( collection ) ) 01238 continue; 01239 01240 const bool readOnly = !isWritableTargetCollectionForMimeTypes( collection, mimeTypes, type ); 01241 01242 QString label = model->data( index ).toString(); 01243 label.replace( QLatin1String( "&" ), QLatin1String( "&&" ) ); 01244 01245 const QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>(); 01246 01247 if ( model->rowCount( index ) > 0 ) { 01248 // new level 01249 QMenu* popup = new QMenu( menu ); 01250 const bool moveAction = (type == MoveCollectionToMenu || type == MoveItemToMenu); 01251 popup->setObjectName( QString::fromUtf8( "subMenu" ) ); 01252 popup->setTitle( label ); 01253 popup->setIcon( icon ); 01254 01255 fillFoldersMenu( mimeTypes, type, popup, model, index ); 01256 01257 if ( !readOnly ) { 01258 popup->addSeparator(); 01259 01260 QAction *action = popup->addAction( moveAction ? i18n( "Move to This Folder" ) : i18n( "Copy to This Folder" ) ); 01261 action->setData( QVariant::fromValue<QModelIndex>( index ) ); 01262 } 01263 01264 menu->addMenu( popup ); 01265 01266 } else { 01267 // insert an item 01268 QAction* action = menu->addAction( icon, label ); 01269 action->setData( QVariant::fromValue<QModelIndex>( index ) ); 01270 action->setEnabled( !readOnly ); 01271 } 01272 } 01273 } 01274 01275 void checkModelsConsistency() 01276 { 01277 if ( favoritesModel == 0 || favoriteSelectionModel == 0 ) { 01278 // No need to check when the favorite collections feature is not used 01279 return; 01280 } 01281 01282 // find the base ETM of the favourites view 01283 const QAbstractItemModel *favModel = favoritesModel; 01284 while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( favModel ) ) { 01285 favModel = proxy->sourceModel(); 01286 } 01287 01288 // Check that the collection selection model maps to the same 01289 // EntityTreeModel than favoritesModel 01290 if ( collectionSelectionModel != 0 ) { 01291 const QAbstractItemModel *model = collectionSelectionModel->model(); 01292 while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ) ) { 01293 model = proxy->sourceModel(); 01294 } 01295 01296 Q_ASSERT( model == favModel ); 01297 } 01298 01299 // Check that the favorite selection model maps to favoritesModel 01300 const QAbstractItemModel *model = favoriteSelectionModel->model(); 01301 while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ) ) { 01302 model = proxy->sourceModel(); 01303 } 01304 Q_ASSERT( model == favModel ); 01305 } 01306 01307 void markCutAction( QMimeData *mimeData, bool cut ) const 01308 { 01309 if ( !cut ) 01310 return; 01311 01312 const QByteArray cutSelectionData = "1"; //krazy:exclude=doublequote_chars 01313 mimeData->setData( QLatin1String( "application/x-kde.akonadi-cutselection" ), cutSelectionData); 01314 } 01315 01316 bool isCutAction( const QMimeData *mimeData ) const 01317 { 01318 const QByteArray data = mimeData->data( QLatin1String( "application/x-kde.akonadi-cutselection" ) ); 01319 if ( data.isEmpty() ) 01320 return false; 01321 else 01322 return (data.at( 0 ) == '1'); // true if 1 01323 } 01324 01325 void setContextText( StandardActionManager::Type type, StandardActionManager::TextContext context, const QString &data ) 01326 { 01327 ContextTextEntry entry; 01328 entry.text = data; 01329 01330 contextTexts[ type ].insert( context, entry ); 01331 } 01332 01333 void setContextText( StandardActionManager::Type type, StandardActionManager::TextContext context, const KLocalizedString &data ) 01334 { 01335 ContextTextEntry entry; 01336 entry.localizedText = data; 01337 01338 contextTexts[ type ].insert( context, entry ); 01339 } 01340 01341 QString contextText( StandardActionManager::Type type, StandardActionManager::TextContext context ) const 01342 { 01343 return contextTexts[ type ].value( context ).text; 01344 } 01345 01346 QString contextText( StandardActionManager::Type type, StandardActionManager::TextContext context, int count, const QString &value ) const 01347 { 01348 if ( contextTexts[ type ].value( context ).localizedText.isEmpty() ) 01349 return contextTexts[ type ].value( context ).text; 01350 01351 KLocalizedString text = contextTexts[ type ].value( context ).localizedText; 01352 const QString str = text.subs( count ).toString(); 01353 const int argCount = str.count( QRegExp( QLatin1String( "%[0-9]" ) ) ); 01354 if ( argCount > 0 ) { 01355 return text.subs( count ).subs( value ).toString(); 01356 } else { 01357 return text.subs( count ).toString(); 01358 } 01359 } 01360 01361 StandardActionManager *q; 01362 KActionCollection *actionCollection; 01363 QWidget *parentWidget; 01364 QItemSelectionModel *collectionSelectionModel; 01365 QItemSelectionModel *itemSelectionModel; 01366 FavoriteCollectionsModel *favoritesModel; 01367 QItemSelectionModel *favoriteSelectionModel; 01368 QVector<KAction*> actions; 01369 QHash<StandardActionManager::Type, KLocalizedString> pluralLabels; 01370 QHash<StandardActionManager::Type, KLocalizedString> pluralIconLabels; 01371 01372 struct ContextTextEntry 01373 { 01374 QString text; 01375 KLocalizedString localizedText; 01376 bool isLocalized; 01377 }; 01378 01379 typedef QHash<StandardActionManager::TextContext, ContextTextEntry> ContextTexts; 01380 QHash<StandardActionManager::Type, ContextTexts> contextTexts; 01381 01382 ActionStateManager mActionStateManager; 01383 01384 QStringList mMimeTypeFilter; 01385 QStringList mCapabilityFilter; 01386 QStringList mCollectionPropertiesPageNames; 01387 QMap<StandardActionManager::Type, QWeakPointer<RecentCollectionAction> > mRecentCollectionsMenu; 01388 }; 01389 01390 //@endcond 01391 01392 StandardActionManager::StandardActionManager( KActionCollection * actionCollection, 01393 QWidget * parent) : 01394 QObject( parent ), 01395 d( new Private( this ) ) 01396 { 01397 d->parentWidget = parent; 01398 d->actionCollection = actionCollection; 01399 d->mActionStateManager.setReceiver( this ); 01400 #ifndef QT_NO_CLIPBOARD 01401 connect( QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), SLOT(clipboardChanged(QClipboard::Mode)) ); 01402 #endif 01403 } 01404 01405 StandardActionManager::~ StandardActionManager() 01406 { 01407 delete d; 01408 } 01409 01410 void StandardActionManager::setCollectionSelectionModel( QItemSelectionModel * selectionModel ) 01411 { 01412 d->collectionSelectionModel = selectionModel; 01413 connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 01414 SLOT(collectionSelectionChanged()) ); 01415 01416 d->checkModelsConsistency(); 01417 } 01418 01419 void StandardActionManager::setItemSelectionModel( QItemSelectionModel * selectionModel ) 01420 { 01421 d->itemSelectionModel = selectionModel; 01422 connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 01423 SLOT(updateActions()) ); 01424 } 01425 01426 void StandardActionManager::setFavoriteCollectionsModel( FavoriteCollectionsModel *favoritesModel ) 01427 { 01428 d->favoritesModel = favoritesModel; 01429 d->checkModelsConsistency(); 01430 } 01431 01432 void StandardActionManager::setFavoriteSelectionModel( QItemSelectionModel *selectionModel ) 01433 { 01434 d->favoriteSelectionModel = selectionModel; 01435 connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 01436 SLOT(favoriteSelectionChanged()) ); 01437 d->checkModelsConsistency(); 01438 } 01439 01440 KAction* StandardActionManager::createAction( Type type ) 01441 { 01442 Q_ASSERT( type < LastType ); 01443 if ( d->actions[type] ) 01444 return d->actions[type]; 01445 KAction *action = 0; 01446 switch ( standardActionData[type].actionType ) { 01447 case NormalAction: 01448 case ActionWithAlternative: 01449 action = new KAction( d->parentWidget ); 01450 break; 01451 case ActionAlternative: 01452 d->actions[type] = d->actions[type-1]; 01453 Q_ASSERT( d->actions[type] ); 01454 if ( (LastType > type+1) && (standardActionData[type+1].actionType == ActionAlternative) ) { 01455 createAction(static_cast<Type>(type+1)); //ensure that alternative actions are initialized when not created by createAllActions 01456 } 01457 return d->actions[type]; 01458 case MenuAction: 01459 action = new KActionMenu( d->parentWidget ); 01460 break; 01461 case ToggleAction: 01462 action = new KToggleAction( d->parentWidget ); 01463 break; 01464 } 01465 01466 if ( d->pluralLabels.contains( type ) && !d->pluralLabels.value( type ).isEmpty() ) 01467 action->setText( d->pluralLabels.value( type ).subs( 1 ).toString() ); 01468 else if ( standardActionData[type].label ) 01469 action->setText( i18n( standardActionData[type].label ) ); 01470 01471 if ( d->pluralIconLabels.contains( type ) && !d->pluralIconLabels.value( type ).isEmpty() ) 01472 action->setIconText( d->pluralIconLabels.value( type ).subs( 1 ).toString() ); 01473 else if ( standardActionData[type].iconLabel ) 01474 action->setIconText( i18n( standardActionData[type].iconLabel ) ); 01475 01476 if ( standardActionData[type].icon ) 01477 action->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) ); 01478 01479 action->setShortcut( standardActionData[type].shortcut ); 01480 01481 if ( standardActionData[type].slot ) { 01482 switch ( standardActionData[type].actionType ) { 01483 case NormalAction: 01484 case ActionWithAlternative: 01485 connect( action, SIGNAL(triggered()), standardActionData[type].slot ); 01486 break; 01487 case MenuAction: 01488 { 01489 KActionMenu *actionMenu = qobject_cast<KActionMenu*>( action ); 01490 connect( actionMenu->menu(), SIGNAL(triggered(QAction*)), standardActionData[type].slot ); 01491 } 01492 break; 01493 case ToggleAction: 01494 { 01495 connect( action, SIGNAL(triggered(bool)), standardActionData[type].slot ); 01496 } 01497 break; 01498 case ActionAlternative: 01499 Q_ASSERT(0); 01500 } 01501 } 01502 01503 if ( type == ToggleWorkOffline ) { 01504 // inititalize the action state with information from config file 01505 disconnect( action, SIGNAL(triggered(bool)), this, standardActionData[type].slot ); 01506 action->setChecked( workOffline() ); 01507 connect( action, SIGNAL(triggered(bool)), this, standardActionData[type].slot ); 01508 01509 //TODO: find a way to check for updates to the config file 01510 } 01511 01512 Q_ASSERT( standardActionData[type].name ); 01513 d->actionCollection->addAction( QString::fromLatin1(standardActionData[type].name), action ); 01514 d->actions[type] = action; 01515 if ( ( standardActionData[type].actionType == ActionWithAlternative ) && (standardActionData[type+1].actionType == ActionAlternative)) { 01516 createAction(static_cast<Type>(type+1)); //ensure that alternative actions are initialized when not created by createAllActions 01517 } 01518 d->updateActions(); 01519 return action; 01520 } 01521 01522 void StandardActionManager::createAllActions() 01523 { 01524 for ( uint i = 0; i < LastType; ++i ) 01525 createAction( (Type)i ); 01526 } 01527 01528 KAction * StandardActionManager::action( Type type ) const 01529 { 01530 Q_ASSERT( type < LastType ); 01531 return d->actions[type]; 01532 } 01533 01534 void StandardActionManager::setActionText( Type type, const KLocalizedString & text ) 01535 { 01536 Q_ASSERT( type < LastType ); 01537 d->pluralLabels.insert( type, text ); 01538 d->updateActions(); 01539 } 01540 01541 void StandardActionManager::interceptAction( Type type, bool intercept ) 01542 { 01543 Q_ASSERT( type < LastType ); 01544 01545 const KAction *action = d->actions[type]; 01546 01547 if ( !action ) 01548 return; 01549 01550 if ( intercept ) 01551 disconnect( action, SIGNAL(triggered()), this, standardActionData[type].slot ); 01552 else 01553 connect( action, SIGNAL(triggered()), standardActionData[type].slot ); 01554 } 01555 01556 Akonadi::Collection::List StandardActionManager::selectedCollections() const 01557 { 01558 Collection::List collections; 01559 01560 if ( !d->collectionSelectionModel ) 01561 return collections; 01562 01563 foreach ( const QModelIndex &index, d->collectionSelectionModel->selectedRows() ) { 01564 const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 01565 if ( collection.isValid() ) 01566 collections << collection; 01567 } 01568 01569 return collections; 01570 } 01571 01572 Item::List StandardActionManager::selectedItems() const 01573 { 01574 Item::List items; 01575 01576 if ( !d->itemSelectionModel ) 01577 return items; 01578 01579 foreach ( const QModelIndex &index, d->itemSelectionModel->selectedRows() ) { 01580 const Item item = index.data( EntityTreeModel::ItemRole ).value<Item>(); 01581 if ( item.isValid() ) 01582 items << item; 01583 } 01584 01585 return items; 01586 } 01587 01588 void StandardActionManager::setContextText( Type type, TextContext context, const QString &text ) 01589 { 01590 d->setContextText( type, context, text ); 01591 } 01592 01593 void StandardActionManager::setContextText( Type type, TextContext context, const KLocalizedString &text ) 01594 { 01595 d->setContextText( type, context, text ); 01596 } 01597 01598 void StandardActionManager::setMimeTypeFilter( const QStringList &mimeTypes ) 01599 { 01600 d->mMimeTypeFilter = mimeTypes; 01601 } 01602 01603 void StandardActionManager::setCapabilityFilter( const QStringList &capabilities ) 01604 { 01605 d->mCapabilityFilter = capabilities; 01606 } 01607 01608 void StandardActionManager::setCollectionPropertiesPageNames( const QStringList &names ) 01609 { 01610 d->mCollectionPropertiesPageNames = names; 01611 } 01612 01613 void StandardActionManager::createActionFolderMenu(QMenu *menu, Type type) 01614 { 01615 d->createActionFolderMenu( menu, type ); 01616 } 01617 01618 01619 01620 #include "standardactionmanager.moc"