00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "standardactionmanager.h"
00021
00022 #include "agentmanager.h"
00023 #include "collectioncreatejob.h"
00024 #include "collectiondeletejob.h"
00025 #include "collectionmodel.h"
00026 #include "collectionutils_p.h"
00027 #include "collectionpropertiesdialog.h"
00028 #include "itemdeletejob.h"
00029 #include "itemmodel.h"
00030 #include "pastehelper_p.h"
00031 #include "subscriptiondialog_p.h"
00032
00033 #include <KAction>
00034 #include <KActionCollection>
00035 #include <KDebug>
00036 #include <KInputDialog>
00037 #include <KLocale>
00038 #include <KMessageBox>
00039
00040 #include <QtCore/QMimeData>
00041 #include <QtGui/QApplication>
00042 #include <QtGui/QClipboard>
00043 #include <QtGui/QItemSelectionModel>
00044
00045 #include <boost/static_assert.hpp>
00046
00047 using namespace Akonadi;
00048
00049
00050
00051 static const struct {
00052 const char *name;
00053 const char *label;
00054 const char *icon;
00055 int shortcut;
00056 const char* slot;
00057 } actionData[] = {
00058 { "akonadi_collection_create", I18N_NOOP("&New Folder..."), "folder-new", 0, SLOT(slotCreateCollection()) },
00059 { "akonadi_collection_copy", 0, "edit-copy", 0, SLOT(slotCopyCollections()) },
00060 { "akonadi_collection_delete", I18N_NOOP("&Delete Folder"), "edit-delete", 0, SLOT(slotDeleteCollection()) },
00061 { "akonadi_collection_sync", I18N_NOOP("&Synchronize Folder"), "view-refresh", Qt::Key_F5, SLOT(slotSynchronizeCollection()) },
00062 { "akonadi_collection_properties", I18N_NOOP("Folder &Properties"), "configure", 0, SLOT(slotCollectionProperties()) },
00063 { "akonadi_item_copy", 0, "edit-copy", 0, SLOT(slotCopyItems()) },
00064 { "akonadi_paste", I18N_NOOP("&Paste"), "edit-paste", Qt::CTRL + Qt::Key_V, SLOT(slotPaste()) },
00065 { "akonadi_item_delete", 0, "edit-delete", Qt::Key_Delete, SLOT(slotDeleteItems()) },
00066 { "akonadi_manage_local_subscriptions", I18N_NOOP("Manage Local &Subscriptions..."), 0, 0, SLOT(slotLocalSubscription()) }
00067 };
00068 static const int numActionData = sizeof actionData / sizeof *actionData;
00069
00070 BOOST_STATIC_ASSERT( numActionData == StandardActionManager::LastType );
00071
00072 static bool canCreateCollection( const Collection &collection )
00073 {
00074 if ( !( collection.rights() & Collection::CanCreateCollection ) )
00075 return false;
00076
00077 if ( !collection.contentMimeTypes().contains( Collection::mimeType() ) )
00078 return false;
00079
00080 return true;
00081 }
00082
00086 class StandardActionManager::Private
00087 {
00088 public:
00089 Private( StandardActionManager *parent ) :
00090 q( parent ),
00091 collectionSelectionModel( 0 ),
00092 itemSelectionModel( 0 )
00093 {
00094 actions.fill( 0, StandardActionManager::LastType );
00095
00096 pluralLabels.insert( StandardActionManager::CopyCollections, ki18np( "&Copy Folder", "&Copy %1 Folders" ) );
00097 pluralLabels.insert( StandardActionManager::CopyItems, ki18np( "&Copy Item", "&Copy %1 Items" ) );
00098 pluralLabels.insert( StandardActionManager::DeleteItems, ki18np( "&Delete Item", "&Delete %1 Items" ) );
00099 }
00100
00101 void enableAction( StandardActionManager::Type type, bool enable )
00102 {
00103 Q_ASSERT( type >= 0 && type < StandardActionManager::LastType );
00104 if ( actions[type] )
00105 actions[type]->setEnabled( enable );
00106 }
00107
00108 void updatePluralLabel( StandardActionManager::Type type, int count )
00109 {
00110 Q_ASSERT( type >= 0 && type < StandardActionManager::LastType );
00111 if ( actions[type] && pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() ) {
00112 actions[type]->setText( pluralLabels.value( type ).subs( qMax( count, 1 ) ).toString() );
00113 }
00114 }
00115
00116 void copy( QItemSelectionModel* selModel )
00117 {
00118 Q_ASSERT( selModel );
00119 if ( selModel->selectedRows().count() <= 0 )
00120 return;
00121 QMimeData *mimeData = selModel->model()->mimeData( selModel->selectedRows() );
00122 QApplication::clipboard()->setMimeData( mimeData );
00123 }
00124
00125 void updateActions()
00126 {
00127 bool singleColSelected = false;
00128 bool multiColSelected = false;
00129 int colCount = 0;
00130 QModelIndex selectedIndex;
00131 if ( collectionSelectionModel ) {
00132 colCount = collectionSelectionModel->selectedRows().count();
00133 singleColSelected = colCount == 1;
00134 multiColSelected = colCount > 0;
00135 if ( singleColSelected )
00136 selectedIndex = collectionSelectionModel->selectedRows().first();
00137 }
00138
00139 enableAction( CopyCollections, multiColSelected );
00140 enableAction( CollectionProperties, singleColSelected );
00141
00142 Collection col;
00143 if ( singleColSelected && selectedIndex.isValid() ) {
00144 col = selectedIndex.data( CollectionModel::CollectionRole ).value<Collection>();
00145 enableAction( CreateCollection, canCreateCollection( col ) );
00146 enableAction( DeleteCollections, col.rights() & Collection::CanDeleteCollection );
00147 enableAction( CopyCollections, multiColSelected && (col != Collection::root()) );
00148 enableAction( CollectionProperties, singleColSelected && (col != Collection::root()) );
00149 enableAction( SynchronizeCollections, CollectionUtils::isResource( col ) || CollectionUtils::isFolder( col ) );
00150 enableAction( Paste, PasteHelper::canPaste( QApplication::clipboard()->mimeData(), col ) );
00151 } else {
00152 enableAction( CreateCollection, false );
00153 enableAction( DeleteCollections, false );
00154 enableAction( SynchronizeCollections, false );
00155 enableAction( Paste, false );
00156 }
00157
00158 bool multiItemSelected = false;
00159 int itemCount = 0;
00160 if ( itemSelectionModel ) {
00161 itemCount = itemSelectionModel->selectedRows().count();
00162 multiItemSelected = itemCount > 0;
00163 }
00164
00165 enableAction( CopyItems, multiItemSelected );
00166 const bool canDeleteItem = !col.isValid() || (col.rights() & Collection::CanDeleteItem);
00167 enableAction( DeleteItems, multiItemSelected && canDeleteItem );
00168
00169 updatePluralLabel( CopyCollections, colCount );
00170 updatePluralLabel( CopyItems, itemCount );
00171 updatePluralLabel( DeleteItems, itemCount );
00172
00173 emit q->actionStateUpdated();
00174 }
00175
00176 void clipboardChanged( QClipboard::Mode mode )
00177 {
00178 if ( mode == QClipboard::Clipboard )
00179 updateActions();
00180 }
00181
00182 void slotCreateCollection()
00183 {
00184 Q_ASSERT( collectionSelectionModel );
00185 if ( collectionSelectionModel->selection().indexes().isEmpty() )
00186 return;
00187
00188 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
00189 Q_ASSERT( index.isValid() );
00190 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
00191 Q_ASSERT( collection.isValid() );
00192
00193 if ( !canCreateCollection( collection ) )
00194 return;
00195
00196 const QString name = KInputDialog::getText( i18nc( "@title:window", "New Folder"),
00197 i18nc( "@label:textbox, name of a thing", "Name"),
00198 QString(), 0, parentWidget );
00199 if ( name.isEmpty() )
00200 return;
00201 Collection::Id parentId = index.data( CollectionModel::CollectionIdRole ).toLongLong();
00202 if ( parentId <= 0 )
00203 return;
00204
00205 Collection col;
00206 col.setName( name );
00207 col.setParent( parentId );
00208 CollectionCreateJob *job = new CollectionCreateJob( col );
00209 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionCreationResult(KJob*)) );
00210 }
00211
00212 void slotCopyCollections()
00213 {
00214 copy( collectionSelectionModel );
00215 }
00216
00217 void slotDeleteCollection()
00218 {
00219 Q_ASSERT( collectionSelectionModel );
00220 if ( collectionSelectionModel->selection().indexes().isEmpty() )
00221 return;
00222
00223 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
00224 Q_ASSERT( index.isValid() );
00225 const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
00226 Q_ASSERT( collection.isValid() );
00227
00228 QString text = i18n( "Do you really want to delete folder '%1' and all its sub-folders?", index.data().toString() );
00229 if ( CollectionUtils::isVirtual( collection ) )
00230 text = i18n( "Do you really want to delete the search view '%1'?", index.data().toString() );
00231
00232 if ( KMessageBox::questionYesNo( parentWidget, text,
00233 i18n("Delete folder?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(),
00234 QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
00235 return;
00236 const Collection::Id colId = index.data( CollectionModel::CollectionIdRole ).toLongLong();
00237 if ( colId <= 0 )
00238 return;
00239
00240 CollectionDeleteJob *job = new CollectionDeleteJob( Collection( colId ), q );
00241 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionDeletionResult(KJob*)) );
00242 }
00243
00244 void slotSynchronizeCollection()
00245 {
00246 Q_ASSERT( collectionSelectionModel );
00247 if ( collectionSelectionModel->selection().indexes().isEmpty() )
00248 return;
00249
00250 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
00251 Q_ASSERT( index.isValid() );
00252 const Collection col = index.data( CollectionModel::CollectionRole ).value<Collection>();
00253 Q_ASSERT( col.isValid() );
00254
00255 AgentManager::self()->synchronizeCollection( col );
00256 }
00257
00258 void slotCollectionProperties()
00259 {
00260 if ( collectionSelectionModel->selection().indexes().isEmpty() )
00261 return;
00262 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
00263 Q_ASSERT( index.isValid() );
00264 Collection col = index.data( CollectionModel::CollectionRole ).value<Collection>();
00265 Q_ASSERT( col.isValid() );
00266
00267 CollectionPropertiesDialog* dlg = new CollectionPropertiesDialog( col, parentWidget );
00268 dlg->show();
00269 }
00270
00271 void slotCopyItems()
00272 {
00273 copy( itemSelectionModel );
00274 }
00275
00276 void slotPaste()
00277 {
00278 Q_ASSERT( collectionSelectionModel );
00279 if ( collectionSelectionModel->selection().indexes().isEmpty() )
00280 return;
00281
00282 const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
00283 Q_ASSERT( index.isValid() );
00284 const Collection col = index.data( CollectionModel::CollectionRole ).value<Collection>();
00285 Q_ASSERT( col.isValid() );
00286
00287 KJob *job = PasteHelper::paste( QApplication::clipboard()->mimeData(), col );
00288 q->connect( job, SIGNAL(result(KJob*)), q, SLOT(pasteResult(KJob*)) );
00289 }
00290
00291 void slotDeleteItems()
00292 {
00293 if ( KMessageBox::questionYesNo( parentWidget,
00294 i18n( "Do you really want to delete all selected items?" ),
00295 i18n("Delete?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(),
00296 QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
00297 return;
00298
00299 Q_ASSERT( itemSelectionModel );
00300
00301
00302 foreach ( const QModelIndex &index, itemSelectionModel->selectedRows() ) {
00303 bool ok;
00304 qlonglong id = index.data( ItemModel::IdRole ).toLongLong(&ok);
00305 Q_ASSERT(ok);
00306 new ItemDeleteJob( Item( id ), q );
00307 }
00308 }
00309
00310 void slotLocalSubscription()
00311 {
00312 SubscriptionDialog* dlg = new SubscriptionDialog( parentWidget );
00313 dlg->show();
00314 }
00315
00316 void collectionCreationResult( KJob *job )
00317 {
00318 if ( job->error() ) {
00319 KMessageBox::error( parentWidget, i18n("Could not create folder: %1", job->errorString()),
00320 i18n("Folder creation failed") );
00321 }
00322 }
00323
00324 void collectionDeletionResult( KJob *job )
00325 {
00326 if ( job->error() ) {
00327 KMessageBox::error( parentWidget, i18n("Could not delete folder: %1", job->errorString()),
00328 i18n("Folder deletion failed") );
00329 }
00330 }
00331
00332 void pasteResult( KJob *job )
00333 {
00334 if ( job->error() ) {
00335 KMessageBox::error( parentWidget, i18n("Could not paste data: %1", job->errorString()),
00336 i18n("Paste failed") );
00337 }
00338 }
00339
00340 StandardActionManager *q;
00341 KActionCollection *actionCollection;
00342 QWidget *parentWidget;
00343 QItemSelectionModel *collectionSelectionModel;
00344 QItemSelectionModel *itemSelectionModel;
00345 QVector<KAction*> actions;
00346 AgentManager *agentManager;
00347 QHash<StandardActionManager::Type, KLocalizedString> pluralLabels;
00348 };
00349
00350
00351
00352 StandardActionManager::StandardActionManager( KActionCollection * actionCollection,
00353 QWidget * parent) :
00354 QObject( parent ),
00355 d( new Private( this ) )
00356 {
00357 d->parentWidget = parent;
00358 d->actionCollection = actionCollection;
00359 connect( QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), SLOT(clipboardChanged(QClipboard::Mode)) );
00360 }
00361
00362 StandardActionManager::~ StandardActionManager()
00363 {
00364 delete d;
00365 }
00366
00367 void StandardActionManager::setCollectionSelectionModel(QItemSelectionModel * selectionModel)
00368 {
00369 d->collectionSelectionModel = selectionModel;
00370 connect( selectionModel, SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection& )),
00371 SLOT(updateActions()) );
00372 }
00373
00374 void StandardActionManager::setItemSelectionModel(QItemSelectionModel * selectionModel)
00375 {
00376 d->itemSelectionModel = selectionModel;
00377 connect( selectionModel, SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection& )),
00378 SLOT(updateActions()) );
00379 }
00380
00381 KAction* StandardActionManager::createAction( Type type )
00382 {
00383 Q_ASSERT( type >= 0 && type < LastType );
00384 Q_ASSERT( actionData[type].name );
00385 if ( d->actions[type] )
00386 return d->actions[type];
00387 KAction *action = new KAction( d->parentWidget );
00388 if ( d->pluralLabels.contains( type ) && !d->pluralLabels.value( type ).isEmpty() )
00389 action->setText( d->pluralLabels.value( type ).subs( 1 ).toString() );
00390 else if ( actionData[type].label )
00391 action->setText( i18n( actionData[type].label ) );
00392 if ( actionData[type].icon )
00393 action->setIcon( KIcon( QString::fromLatin1( actionData[type].icon ) ) );
00394 action->setShortcut( actionData[type].shortcut );
00395 if ( actionData[type].slot )
00396 connect( action, SIGNAL(triggered()), actionData[type].slot );
00397 d->actionCollection->addAction( QString::fromLatin1(actionData[type].name), action );
00398 d->actions[type] = action;
00399 d->updateActions();
00400 return action;
00401 }
00402
00403 void StandardActionManager::createAllActions()
00404 {
00405 for ( int i = 0; i < LastType; ++i )
00406 createAction( (Type)i );
00407 }
00408
00409 KAction * StandardActionManager::action( Type type ) const
00410 {
00411 Q_ASSERT( type >= 0 && type < LastType );
00412 return d->actions[type];
00413 }
00414
00415 void StandardActionManager::setActionText(Type type, const KLocalizedString & text)
00416 {
00417 Q_ASSERT( type >= 0 && type < LastType );
00418 d->pluralLabels.insert( type, text );
00419 d->updateActions();
00420 }
00421
00422 #include "standardactionmanager.moc"