• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.1 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
standardactionmanager.cpp
1 /*
2  Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "standardactionmanager.h"
21 
22 #include "actionstatemanager_p.h"
23 #include "agentfilterproxymodel.h"
24 #include "agentinstancecreatejob.h"
25 #include "agentmanager.h"
26 #include "agenttypedialog.h"
27 #include "collectioncreatejob.h"
28 #include "collectiondeletejob.h"
29 #include "collectiondialog.h"
30 #include "collectionmodel.h"
31 #include "collectionutils_p.h"
32 #include "entitytreemodel.h"
33 #include "favoritecollectionsmodel.h"
34 #include "itemdeletejob.h"
35 #include "itemmodel.h"
36 #include "metatypes.h"
37 #include "pastehelper_p.h"
38 #include "specialcollectionattribute_p.h"
39 #include "collectionpropertiesdialog.h"
40 #include "subscriptiondialog_p.h"
41 #include "renamefavoritedialog.h"
42 #include "trashjob.h"
43 #include "trashrestorejob.h"
44 #include "entitydeletedattribute.h"
45 #include "recentcollectionaction_p.h"
46 
47 #include <KAction>
48 #include <KActionCollection>
49 #include <KActionMenu>
50 #include <KDebug>
51 #include <KInputDialog>
52 #include <KLocale>
53 #include <KMenu>
54 #include <KMessageBox>
55 #include <KToggleAction>
56 
57 #include <QtCore/QMimeData>
58 #include <QtGui/QApplication>
59 #include <QtGui/QClipboard>
60 #include <QtGui/QItemSelectionModel>
61 #include <QWeakPointer>
62 
63 #include <boost/static_assert.hpp>
64 
65 using namespace Akonadi;
66 
67 //@cond PRIVATE
68 
69 enum ActionType
70 {
71  NormalAction,
72  ActionWithAlternative, //Normal action, but with an alternative state
73  ActionAlternative, //Alternative state of the ActionWithAlternative
74  MenuAction,
75  ToggleAction
76 };
77 
78 static const struct {
79  const char *name;
80  const char *label;
81  const char *iconLabel;
82  const char *icon;
83  int shortcut;
84  const char* slot;
85  ActionType actionType;
86 } standardActionData[] = {
87  { "akonadi_collection_create", I18N_NOOP( "&New Folder..." ), I18N_NOOP( "New" ), "folder-new", 0, SLOT(slotCreateCollection()), NormalAction },
88  { "akonadi_collection_copy", 0, 0, "edit-copy", 0, SLOT(slotCopyCollections()), NormalAction },
89  { "akonadi_collection_delete", I18N_NOOP( "&Delete Folder" ), I18N_NOOP( "Delete" ), "edit-delete", 0, SLOT(slotDeleteCollection()), NormalAction },
90  { "akonadi_collection_sync", I18N_NOOP( "&Synchronize Folder" ), I18N_NOOP( "Synchronize" ), "view-refresh", Qt::Key_F5, SLOT(slotSynchronizeCollection()), NormalAction },
91  { "akonadi_collection_properties", I18N_NOOP( "Folder &Properties" ), I18N_NOOP( "Properties" ), "configure", 0, SLOT(slotCollectionProperties()), NormalAction },
92  { "akonadi_item_copy", 0, 0, "edit-copy", 0, SLOT(slotCopyItems()), NormalAction },
93  { "akonadi_paste", I18N_NOOP( "&Paste" ), I18N_NOOP( "Paste" ), "edit-paste", Qt::CTRL + Qt::Key_V, SLOT(slotPaste()), NormalAction },
94  { "akonadi_item_delete", 0, 0, "edit-delete", Qt::Key_Delete, SLOT(slotDeleteItems()), NormalAction },
95  { "akonadi_manage_local_subscriptions", I18N_NOOP( "Manage Local &Subscriptions..." ), I18N_NOOP( "Manage Local Subscriptions" ), "folder-bookmarks", 0, SLOT(slotLocalSubscription()), NormalAction },
96  { "akonadi_collection_add_to_favorites", I18N_NOOP( "Add to Favorite Folders" ), I18N_NOOP( "Add to Favorite" ), "bookmark-new", 0, SLOT(slotAddToFavorites()), NormalAction },
97  { "akonadi_collection_remove_from_favorites", I18N_NOOP( "Remove from Favorite Folders" ), I18N_NOOP( "Remove from Favorite" ), "edit-delete", 0, SLOT(slotRemoveFromFavorites()), NormalAction },
98  { "akonadi_collection_rename_favorite", I18N_NOOP( "Rename Favorite..." ), I18N_NOOP( "Rename" ), "edit-rename", 0, SLOT(slotRenameFavorite()), NormalAction },
99  { "akonadi_collection_copy_to_menu", I18N_NOOP( "Copy Folder To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyCollectionTo(QAction*)), MenuAction },
100  { "akonadi_item_copy_to_menu", I18N_NOOP( "Copy Item To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyItemTo(QAction*)), MenuAction },
101  { "akonadi_item_move_to_menu", I18N_NOOP( "Move Item To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveItemTo(QAction*)), MenuAction },
102  { "akonadi_collection_move_to_menu", I18N_NOOP( "Move Folder To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveCollectionTo(QAction*)), MenuAction },
103  { "akonadi_item_cut", I18N_NOOP( "&Cut Item" ), I18N_NOOP( "Cut" ), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutItems()), NormalAction },
104  { "akonadi_collection_cut", I18N_NOOP( "&Cut Folder" ), I18N_NOOP( "Cut" ), "edit-cut", Qt::CTRL + Qt::Key_X, SLOT(slotCutCollections()), NormalAction },
105  { "akonadi_resource_create", I18N_NOOP( "Create Resource" ), 0, "folder-new", 0, SLOT(slotCreateResource()), NormalAction },
106  { "akonadi_resource_delete", I18N_NOOP( "Delete Resource" ), 0, "edit-delete", 0, SLOT(slotDeleteResource()), NormalAction },
107  { "akonadi_resource_properties", I18N_NOOP( "&Resource Properties" ), I18N_NOOP( "Properties" ), "configure", 0, SLOT(slotResourceProperties()), NormalAction },
108  { "akonadi_resource_synchronize", I18N_NOOP( "Synchronize Resource" ), I18N_NOOP( "Synchronize" ), "view-refresh", 0, SLOT(slotSynchronizeResource()), NormalAction },
109  { "akonadi_work_offline", I18N_NOOP( "Work Offline" ), 0, "user-offline", 0, SLOT(slotToggleWorkOffline(bool)), ToggleAction },
110  { "akonadi_collection_copy_to_dialog", I18N_NOOP( "Copy Folder To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyCollectionTo()), NormalAction },
111  { "akonadi_collection_move_to_dialog", I18N_NOOP( "Move Folder To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveCollectionTo()), NormalAction },
112  { "akonadi_item_copy_to_dialog", I18N_NOOP( "Copy Item To..." ), I18N_NOOP( "Copy To" ), "edit-copy", 0, SLOT(slotCopyItemTo()), NormalAction },
113  { "akonadi_item_move_to_dialog", I18N_NOOP( "Move Item To..." ), I18N_NOOP( "Move To" ), "go-jump", 0, SLOT(slotMoveItemTo()), NormalAction },
114  { "akonadi_collection_sync_recursive", I18N_NOOP( "&Synchronize Folder Recursively" ), I18N_NOOP( "Synchronize Recursively" ), "view-refresh", Qt::CTRL + Qt::Key_F5, SLOT(slotSynchronizeCollectionRecursive()), NormalAction },
115  { "akonadi_move_collection_to_trash", I18N_NOOP( "&Move Folder To Trash" ), I18N_NOOP( "Move Folder To Trash" ), "user-trash", 0, SLOT(slotMoveCollectionToTrash()), NormalAction },
116  { "akonadi_move_item_to_trash", I18N_NOOP( "&Move Item To Trash" ), I18N_NOOP( "Move Item To Trash" ), "user-trash", 0, SLOT(slotMoveItemToTrash()), NormalAction },
117  { "akonadi_restore_collection_from_trash", I18N_NOOP( "&Restore Folder From Trash" ), I18N_NOOP( "Restore Folder From Trash" ), "view-refresh", 0, SLOT(slotRestoreCollectionFromTrash()), NormalAction },
118  { "akonadi_restore_item_from_trash", I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "view-refresh", 0, SLOT(slotRestoreItemFromTrash()), NormalAction },
119  { "akonadi_collection_trash_restore", I18N_NOOP( "&Restore Folder From Trash" ), I18N_NOOP( "Restore Folder From Trash" ), "user-trash", 0, SLOT(slotTrashRestoreCollection()), ActionWithAlternative },
120  { 0, I18N_NOOP( "&Restore Collection From Trash" ), I18N_NOOP( "Restore Collection From Trash" ), "view-refresh", 0, 0, ActionAlternative },
121  { "akonadi_item_trash_restore", I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "user-trash", 0, SLOT(slotTrashRestoreItem()), ActionWithAlternative },
122  { 0, I18N_NOOP( "&Restore Item From Trash" ), I18N_NOOP( "Restore Item From Trash" ), "view-refresh", 0, 0, ActionAlternative },
123  { "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 }
124 
125 };
126 static const int numStandardActionData = sizeof standardActionData / sizeof *standardActionData;
127 
128 BOOST_STATIC_ASSERT( numStandardActionData == StandardActionManager::LastType );
129 
130 static bool canCreateCollection( const Akonadi::Collection &collection )
131 {
132  if ( !( collection.rights() & Akonadi::Collection::CanCreateCollection ) )
133  return false;
134 
135  if ( !collection.contentMimeTypes().contains( Akonadi::Collection::mimeType() ) )
136  return false;
137 
138  return true;
139 }
140 
141 static inline bool isRootCollection( const Akonadi::Collection &collection )
142 {
143  return (collection == Akonadi::Collection::root());
144 }
145 
146 static void setWorkOffline( bool offline )
147 {
148  KConfig config( QLatin1String( "akonadikderc" ) );
149  KConfigGroup group( &config, QLatin1String( "Actions" ) );
150 
151  group.writeEntry( "WorkOffline", offline );
152 }
153 
154 static bool workOffline()
155 {
156  KConfig config( QLatin1String( "akonadikderc" ) );
157  const KConfigGroup group( &config, QLatin1String( "Actions" ) );
158 
159  return group.readEntry( "WorkOffline", false );
160 }
161 
162 static QModelIndexList safeSelectedRows( QItemSelectionModel *selectionModel )
163 {
164  QModelIndexList selectedRows = selectionModel->selectedRows();
165  if (!selectedRows.isEmpty())
166  return selectedRows;
167 
168  // try harder for selected rows that don't span the full row for some reason (e.g. due to buggy column adding proxy models etc)
169  foreach ( const QItemSelectionRange &range, selectionModel->selection() ) {
170  if ( !range.isValid() || range.isEmpty() )
171  continue;
172  const QModelIndex parent = range.parent();
173  for ( int row = range.top(); row <= range.bottom(); ++row ) {
174  const QModelIndex index = range.model()->index( row, range.left(), parent );
175  const Qt::ItemFlags flags = range.model()->flags( index );
176  if ( (flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled) )
177  selectedRows.push_back( index );
178  }
179  }
180 
181  return selectedRows;
182 }
183 
184 
188 class StandardActionManager::Private
189 {
190  public:
191  Private( StandardActionManager *parent ) :
192  q( parent ),
193  collectionSelectionModel( 0 ),
194  itemSelectionModel( 0 ),
195  favoritesModel( 0 ),
196  favoriteSelectionModel( 0 )
197  {
198  actions.fill( 0, StandardActionManager::LastType );
199 
200  pluralLabels.insert( StandardActionManager::CopyCollections,
201  ki18np( "&Copy Folder", "&Copy %1 Folders" ) );
202  pluralLabels.insert( StandardActionManager::CopyItems,
203  ki18np( "&Copy Item", "&Copy %1 Items" ) );
204  pluralLabels.insert( StandardActionManager::CutItems,
205  ki18np( "&Cut Item", "&Cut %1 Items" ) );
206  pluralLabels.insert( StandardActionManager::CutCollections,
207  ki18np( "&Cut Folder", "&Cut %1 Folders" ) );
208  pluralLabels.insert( StandardActionManager::DeleteItems,
209  ki18np( "&Delete Item", "&Delete %1 Items" ) );
210  pluralLabels.insert( StandardActionManager::DeleteCollections,
211  ki18np( "&Delete Folder", "&Delete %1 Folders" ) );
212  pluralLabels.insert( StandardActionManager::SynchronizeCollections,
213  ki18np( "&Synchronize Folder", "&Synchronize %1 Folders" ) );
214  pluralLabels.insert( StandardActionManager::DeleteResources,
215  ki18np( "&Delete Resource", "&Delete %1 Resources" ) );
216  pluralLabels.insert( StandardActionManager::SynchronizeResources,
217  ki18np( "&Synchronize Resource", "&Synchronize %1 Resources" ) );
218 
219 
220  pluralIconLabels.insert( StandardActionManager::CopyCollections,
221  ki18np( "Copy Folder", "Copy %1 Folders" ) );
222  pluralIconLabels.insert( StandardActionManager::CopyItems,
223  ki18np( "Copy Item", "Copy %1 Items" ) );
224  pluralIconLabels.insert( StandardActionManager::CutItems,
225  ki18np( "Cut Item", "Cut %1 Items" ) );
226  pluralIconLabels.insert( StandardActionManager::CutCollections,
227  ki18np( "Cut Folder", "Cut %1 Folders" ) );
228  pluralIconLabels.insert( StandardActionManager::DeleteItems,
229  ki18np( "Delete Item", "Delete %1 Items" ) );
230  pluralIconLabels.insert( StandardActionManager::DeleteCollections,
231  ki18np( "Delete Folder", "Delete %1 Folders" ) );
232  pluralIconLabels.insert( StandardActionManager::SynchronizeCollections,
233  ki18np( "Synchronize Folder", "Synchronize %1 Folders" ) );
234  pluralIconLabels.insert( StandardActionManager::DeleteResources,
235  ki18np( "Delete Resource", "Delete %1 Resources" ) );
236  pluralIconLabels.insert( StandardActionManager::SynchronizeResources,
237  ki18np( "Synchronize Resource", "Synchronize %1 Resources" ) );
238 
239  setContextText( StandardActionManager::CreateCollection, StandardActionManager::DialogTitle,
240  i18nc( "@title:window", "New Folder" ) );
241  setContextText( StandardActionManager::CreateCollection, StandardActionManager::DialogText,
242  i18nc( "@label:textbox name of a thing", "Name" ) );
243  setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText,
244  ki18n( "Could not create folder: %1" ) );
245  setContextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle,
246  i18n( "Folder creation failed" ) );
247 
248  setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText,
249  ki18np( "Do you really want to delete this folder and all its sub-folders?",
250  "Do you really want to delete %1 folders and all their sub-folders?" ) );
251  setContextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle,
252  ki18ncp( "@title:window", "Delete folder?", "Delete folders?" ) );
253  setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText,
254  ki18n( "Could not delete folder: %1" ) );
255  setContextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle,
256  i18n( "Folder deletion failed" ) );
257 
258  setContextText( StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle,
259  ki18nc( "@title:window", "Properties of Folder %1" ) );
260 
261  setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText,
262  ki18np( "Do you really want to delete the selected item?",
263  "Do you really want to delete %1 items?" ) );
264  setContextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle,
265  ki18ncp( "@title:window", "Delete item?", "Delete items?" ) );
266  setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText,
267  ki18n( "Could not delete item: %1" ) );
268  setContextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle,
269  i18n( "Item deletion failed" ) );
270 
271  setContextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogTitle,
272  i18nc( "@title:window", "Rename Favorite" ) );
273  setContextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogText,
274  i18nc( "@label:textbox name of the folder", "Name:" ) );
275 
276  setContextText( StandardActionManager::CreateResource, StandardActionManager::DialogTitle,
277  i18nc( "@title:window", "New Resource" ) );
278  setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText,
279  ki18n( "Could not create resource: %1" ) );
280  setContextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle,
281  i18n( "Resource creation failed" ) );
282 
283  setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText,
284  ki18np( "Do you really want to delete this resource?",
285  "Do you really want to delete %1 resources?" ) );
286  setContextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle,
287  ki18ncp( "@title:window", "Delete Resource?", "Delete Resources?" ) );
288 
289  setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageText,
290  ki18n( "Could not paste data: %1" ) );
291  setContextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle,
292  i18n( "Paste failed" ) );
293 
294  qRegisterMetaType<Akonadi::Item::List>("Akonadi::Item::List");
295  }
296 
297  void enableAction( int type, bool enable )
298  {
299  enableAction( static_cast<StandardActionManager::Type>( type ), enable );
300  }
301 
302  void enableAction( StandardActionManager::Type type, bool enable )
303  {
304  Q_ASSERT( type < StandardActionManager::LastType );
305  if ( actions[type] )
306  actions[type]->setEnabled( enable );
307 
308  // Update the action menu
309  KActionMenu *actionMenu = qobject_cast<KActionMenu*>( actions[type] );
310  if ( actionMenu ) {
311  //get rid of the submenus, they are re-created in enableAction. clear() is not enough, doesn't remove the submenu object instances.
312  KMenu *menu = actionMenu->menu();
313  //Not necessary to delete and recreate menu when it was not created
314  if ( menu->property( "actionType" ).isValid() && menu->isEmpty() )
315  return;
316  delete menu;
317  menu = new KMenu();
318 
319  menu->setProperty( "actionType", static_cast<int>( type ) );
320  q->connect( menu, SIGNAL(aboutToShow()), SLOT(aboutToShowMenu()) );
321  q->connect( menu, SIGNAL(triggered(QAction*)), standardActionData[ type ].slot );
322  actionMenu->setMenu( menu );
323  }
324  }
325 
326  void aboutToShowMenu()
327  {
328  QMenu *menu = qobject_cast<QMenu*>( q->sender() );
329  if ( !menu )
330  return;
331 
332  if ( !menu->isEmpty() )
333  return;
334  // collect all selected collections
335  const Akonadi::Collection::List selectedCollectionsList = selectedCollections();
336  const StandardActionManager::Type type = static_cast<StandardActionManager::Type>( menu->property( "actionType" ).toInt() );
337 
338  QWeakPointer<RecentCollectionAction> recentCollection = new RecentCollectionAction( collectionSelectionModel->model(), menu );
339  mRecentCollectionsMenu.insert( type, recentCollection );
340  const QSet<QString> mimeTypes = mimeTypesOfSelection( type );
341  fillFoldersMenu( selectedCollectionsList,
342  mimeTypes,
343  type,
344  menu,
345  collectionSelectionModel->model(),
346  QModelIndex() );
347  }
348 
349  void createActionFolderMenu(QMenu *menu, StandardActionManager::Type type)
350  {
351  if ( type == CopyCollectionToMenu ||
352  type == CopyItemToMenu ||
353  type == MoveItemToMenu ||
354  type ==MoveCollectionToMenu )
355  {
356 
357  QWeakPointer<RecentCollectionAction> recentCollection = new RecentCollectionAction( collectionSelectionModel->model(), menu );
358  Collection::List selectedCollectionsList = selectedCollections();
359  const QSet<QString> mimeTypes = mimeTypesOfSelection( type );
360  fillFoldersMenu( selectedCollectionsList,
361  mimeTypes,
362  type,
363  menu,
364  collectionSelectionModel->model(),
365  QModelIndex() );
366  }
367  }
368 
369 
370  void updateAlternatingAction( int type )
371  {
372  updateAlternatingAction( static_cast<StandardActionManager::Type>( type ) );
373  }
374 
375  void updateAlternatingAction( StandardActionManager::Type type )
376  {
377  Q_ASSERT( type < StandardActionManager::LastType );
378  if (!actions[type]) {
379  return;
380  }
381 
382  /*
383  * The same action is stored at the ActionWithAlternative indexes as well as the corresponding ActionAlternative indexes in the actions array.
384  * The following simply changes the standardActionData
385  */
386  if ( ( standardActionData[type].actionType == ActionWithAlternative ) || ( standardActionData[type].actionType == ActionAlternative ) ) {
387  actions[type]->setText( i18n ( standardActionData[type].label ) );
388  actions[type]->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) );
389 
390  if ( pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() )
391  actions[type]->setText( pluralLabels.value( type ).subs( 1 ).toString() );
392  else if ( standardActionData[type].label )
393  actions[type]->setText( i18n( standardActionData[type].label ) );
394 
395  if ( pluralIconLabels.contains( type ) && !pluralIconLabels.value( type ).isEmpty() )
396  actions[type]->setIconText( pluralIconLabels.value( type ).subs( 1 ).toString() );
397  else if ( standardActionData[type].iconLabel )
398  actions[type]->setIconText( i18n( standardActionData[type].iconLabel ) );
399 
400  if ( standardActionData[type].icon )
401  actions[type]->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) );
402 
403  //actions[type]->setShortcut( standardActionData[type].shortcut );
404 
405  /*if ( standardActionData[type].slot ) {
406  switch ( standardActionData[type].actionType ) {
407  case NormalAction:
408  case ActionWithAlternative:
409  connect( action, SIGNAL(triggered()), standardActionData[type].slot );
410  break;
411  }
412  }*/
413  }
414  }
415 
416  void updatePluralLabel( int type, int count )
417  {
418  updatePluralLabel( static_cast<StandardActionManager::Type>( type ), count );
419  }
420 
421  void updatePluralLabel( StandardActionManager::Type type, int count )
422  {
423  Q_ASSERT( type < StandardActionManager::LastType );
424  if ( actions[type] && pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() ) {
425  actions[type]->setText( pluralLabels.value( type ).subs( qMax( count, 1 ) ).toString() );
426  }
427  }
428 
429  bool isFavoriteCollection( const Akonadi::Collection &collection )
430  {
431  if ( !favoritesModel )
432  return false;
433 
434  return favoritesModel->collections().contains( collection );
435  }
436 
437  void encodeToClipboard( QItemSelectionModel* selectionModel, bool cut = false )
438  {
439  Q_ASSERT( selectionModel );
440  if ( safeSelectedRows( selectionModel ).count() <= 0 )
441  return;
442 
443 #ifndef QT_NO_CLIPBOARD
444  QMimeData *mimeData = selectionModel->model()->mimeData( safeSelectedRows( selectionModel ) );
445  markCutAction( mimeData, cut );
446  QApplication::clipboard()->setMimeData( mimeData );
447 
448  QAbstractItemModel *model = const_cast<QAbstractItemModel *>( selectionModel->model() );
449 
450  foreach ( const QModelIndex &index, safeSelectedRows( selectionModel ) )
451  model->setData( index, true, EntityTreeModel::PendingCutRole );
452 #endif
453  }
454 
455  void updateActions()
456  {
457  // collect all selected collections
458  Collection::List selectedCollectionsList;
459  if ( collectionSelectionModel ) {
460  const QModelIndexList rows = safeSelectedRows( collectionSelectionModel );
461  foreach ( const QModelIndex &index, rows ) {
462  Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>();
463  if ( !collection.isValid() )
464  continue;
465 
466  const Collection parentCollection = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>();
467  collection.setParentCollection( parentCollection );
468 
469  selectedCollectionsList << collection;
470  }
471  }
472 
473  // collect all selected items
474  Item::List selectedItems;
475  if ( itemSelectionModel ) {
476  const QModelIndexList rows = safeSelectedRows( itemSelectionModel );
477  foreach ( const QModelIndex &index, rows ) {
478  Item item = index.data( EntityTreeModel::ItemRole ).value<Item>();
479  if ( !item.isValid() )
480  continue;
481 
482  const Collection parentCollection = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>();
483  item.setParentCollection( parentCollection );
484 
485  selectedItems << item;
486  }
487  }
488 
489  mActionStateManager.updateState( selectedCollectionsList, selectedItems );
490  if( favoritesModel)
491  enableAction( StandardActionManager::SynchronizeFavoriteCollections, (favoritesModel->rowCount() > 0));
492  emit q->actionStateUpdated();
493  }
494 
495 #ifndef QT_NO_CLIPBOARD
496  void clipboardChanged( QClipboard::Mode mode )
497  {
498  if ( mode == QClipboard::Clipboard )
499  updateActions();
500  }
501 #endif
502 
503  QItemSelection mapToEntityTreeModel( const QAbstractItemModel *model, const QItemSelection &selection ) const
504  {
505  const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model );
506  if ( proxy ) {
507  return mapToEntityTreeModel( proxy->sourceModel(), proxy->mapSelectionToSource( selection ) );
508  } else {
509  return selection;
510  }
511  }
512 
513  QItemSelection mapFromEntityTreeModel( const QAbstractItemModel *model, const QItemSelection &selection ) const
514  {
515  const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model );
516  if ( proxy ) {
517  const QItemSelection select = mapFromEntityTreeModel( proxy->sourceModel(), selection );
518  return proxy->mapSelectionFromSource( select );
519  } else {
520  return selection;
521  }
522  }
523 
524  void collectionSelectionChanged()
525  {
526  q->blockSignals( true );
527 
528  QItemSelection selection = collectionSelectionModel->selection();
529  selection = mapToEntityTreeModel( collectionSelectionModel->model(), selection );
530  selection = mapFromEntityTreeModel( favoritesModel, selection );
531 
532  if ( favoriteSelectionModel )
533  favoriteSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect );
534 
535  q->blockSignals( false );
536 
537  updateActions();
538  }
539 
540  void favoriteSelectionChanged()
541  {
542  q->blockSignals( true );
543 
544  QItemSelection selection = favoriteSelectionModel->selection();
545  if ( selection.indexes().isEmpty() )
546  return;
547 
548  selection = mapToEntityTreeModel( favoritesModel, selection );
549  selection = mapFromEntityTreeModel( collectionSelectionModel->model(), selection );
550 
551  collectionSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
552  q->blockSignals( false );
553 
554  updateActions();
555  }
556 
557  void slotCreateCollection()
558  {
559  Q_ASSERT( collectionSelectionModel );
560  if ( collectionSelectionModel->selection().indexes().isEmpty() )
561  return;
562 
563  const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 );
564  Q_ASSERT( index.isValid() );
565  const Collection parentCollection = index.data( CollectionModel::CollectionRole ).value<Collection>();
566  Q_ASSERT( parentCollection.isValid() );
567 
568  if ( !canCreateCollection( parentCollection ) )
569  return;
570 
571  QString name = KInputDialog::getText( contextText( StandardActionManager::CreateCollection, StandardActionManager::DialogTitle ),
572  contextText( StandardActionManager::CreateCollection, StandardActionManager::DialogText ),
573  QString(), 0, parentWidget );
574  name = name.trimmed();
575  if ( name.isEmpty() )
576  return;
577 
578  if ( name.contains( QLatin1Char( '/' ) ) ) {
579  KMessageBox::error( parentWidget,
580  i18n( "We can not add \"/\" in folder name." ),
581  i18n( "Create new folder error" ) );
582  return;
583  }
584  if ( name.startsWith( QLatin1Char('.') ) ||
585  name.endsWith( QLatin1Char('.') ) ) {
586  kDebug()<<" We can not add \".\" at begin or end of folder name ";
587  return;
588  }
589 
590  Collection collection;
591  collection.setName( name );
592  collection.setParentCollection( parentCollection );
593  if ( actions[StandardActionManager::CreateCollection] ) {
594  const QStringList mts = actions[StandardActionManager::CreateCollection]->property( "ContentMimeTypes" ).toStringList();
595  if ( !mts.isEmpty() )
596  collection.setContentMimeTypes( mts );
597  }
598  CollectionCreateJob *job = new CollectionCreateJob( collection );
599  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionCreationResult(KJob*)) );
600  }
601 
602  void slotCopyCollections()
603  {
604  encodeToClipboard( collectionSelectionModel );
605  }
606 
607  void slotCutCollections()
608  {
609  encodeToClipboard( collectionSelectionModel, true );
610  }
611 
612  Collection::List selectedCollections()
613  {
614  Collection::List collections;
615 
616  Q_ASSERT( collectionSelectionModel );
617 
618  foreach ( const QModelIndex &index, safeSelectedRows( collectionSelectionModel ) ) {
619  Q_ASSERT( index.isValid() );
620  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
621  Q_ASSERT( collection.isValid() );
622 
623  collections << collection;
624  }
625 
626  return collections;
627  }
628 
629  void slotDeleteCollection()
630  {
631  const Collection::List collections = selectedCollections();
632  if ( collections.isEmpty() )
633  return;
634 
635  const QString collectionName = collections.first().name();
636  const QString text = contextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxText,
637  collections.count(), collectionName );
638 
639  if ( KMessageBox::questionYesNo( parentWidget, text,
640  contextText( StandardActionManager::DeleteCollections, StandardActionManager::MessageBoxTitle, collections.count(), collectionName ),
641  KStandardGuiItem::del(), KStandardGuiItem::cancel(),
642  QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
643  return;
644 
645  foreach ( const Collection &collection, collections ) {
646  CollectionDeleteJob *job = new CollectionDeleteJob( collection, q );
647  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionDeletionResult(KJob*)) );
648  }
649  }
650 
651  void slotMoveCollectionToTrash()
652  {
653  const Collection::List collections = selectedCollections();
654  if ( collections.isEmpty() )
655  return;
656 
657  foreach ( const Collection &collection, collections ) {
658  TrashJob *job = new TrashJob( collection, q );
659  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveCollectionToTrashResult(KJob*)) );
660  }
661  }
662 
663  void slotRestoreCollectionFromTrash()
664  {
665  const Collection::List collections = selectedCollections();
666  if ( collections.isEmpty() )
667  return;
668 
669  foreach ( const Collection &collection, collections ) {
670  TrashRestoreJob *job = new TrashRestoreJob( collection, q );
671  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveCollectionToTrashResult(KJob*)) );
672  }
673  }
674 
675  Item::List selectedItems() const
676  {
677  Item::List items;
678 
679  Q_ASSERT( itemSelectionModel );
680 
681  foreach ( const QModelIndex &index, safeSelectedRows( itemSelectionModel ) ) {
682  Q_ASSERT( index.isValid() );
683  const Item item = index.data( ItemModel::ItemRole ).value<Item>();
684  Q_ASSERT( item.isValid() );
685 
686  items << item;
687  }
688 
689  return items;
690  }
691 
692  void slotMoveItemToTrash()
693  {
694  const Item::List items = selectedItems();
695  if ( items.isEmpty() )
696  return;
697 
698  TrashJob *job = new TrashJob( items, q );
699  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveItemToTrashResult(KJob*)) );
700  }
701 
702  void slotRestoreItemFromTrash()
703  {
704  const Item::List items = selectedItems();
705  if ( items.isEmpty() )
706  return;
707 
708  TrashRestoreJob *job = new TrashRestoreJob( items, q );
709  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(moveItemToTrashResult(KJob*)) );
710  }
711 
712  void slotTrashRestoreCollection()
713  {
714  const Collection::List collections = selectedCollections();
715  if ( collections.isEmpty() )
716  return;
717 
718  bool collectionsAreInTrash = false;
719  foreach ( const Collection &collection, collections ) {
720  if ( collection.hasAttribute<EntityDeletedAttribute>() ) {
721  collectionsAreInTrash = true;
722  break;
723  }
724  }
725 
726  if (collectionsAreInTrash) {
727  slotRestoreCollectionFromTrash();
728  } else {
729  slotMoveCollectionToTrash();
730  }
731  }
732 
733  void slotTrashRestoreItem()
734  {
735  const Item::List items = selectedItems();
736  if ( items.isEmpty() )
737  return;
738 
739  bool itemsAreInTrash = false;
740  foreach ( const Item &item, items ) {
741  if ( item.hasAttribute<EntityDeletedAttribute>() ) {
742  itemsAreInTrash = true;
743  break;
744  }
745  }
746 
747  if (itemsAreInTrash) {
748  slotRestoreItemFromTrash();
749  } else {
750  slotMoveItemToTrash();
751  }
752  }
753 
754  void slotSynchronizeCollection()
755  {
756  Q_ASSERT( collectionSelectionModel );
757  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
758  if ( list.isEmpty() )
759  return;
760 
761  const Collection::List collections = selectedCollections();
762  if ( collections.isEmpty() )
763  return;
764 
765  foreach( const Collection &collection, collections ) {
766  if ( !testAndSetOnlineResources(collection) )
767  break;
768  AgentManager::self()->synchronizeCollection( collection, false );
769  }
770  }
771 
772  bool testAndSetOnlineResources(const Akonadi::Collection& collection)
773  {
774  Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance( collection.resource() );
775  if ( !instance.isOnline() ) {
776  if ( KMessageBox::questionYesNo( parentWidget, i18n( "Before syncing folder \"%1\" it's necessary to have resource online. Do you want to make it online?" , collection.name() ), i18n( "Account \"%1\" is offline", instance.name() ) ) != KMessageBox::Yes )
777  return false;
778  instance.setIsOnline( true );
779  }
780  return true;
781  }
782 
783  void slotSynchronizeCollectionRecursive()
784  {
785  Q_ASSERT( collectionSelectionModel );
786  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
787  if ( list.isEmpty() )
788  return;
789 
790  const Collection::List collections = selectedCollections();
791  if ( collections.isEmpty() )
792  return;
793 
794  foreach( const Collection &collection, collections ) {
795  if ( !testAndSetOnlineResources(collection) )
796  break;
797  AgentManager::self()->synchronizeCollection( collection, true );
798  }
799  }
800 
801  void slotCollectionProperties()
802  {
803  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
804  if ( list.isEmpty() )
805  return;
806 
807  const QModelIndex index = list.first();
808  Q_ASSERT( index.isValid() );
809 
810  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
811  Q_ASSERT( collection.isValid() );
812 
813  const QString displayName = collection.hasAttribute<EntityDisplayAttribute>() ? collection.attribute<EntityDisplayAttribute>()->displayName()
814  : collection.name();
815 
816  CollectionPropertiesDialog* dlg = new CollectionPropertiesDialog( collection, mCollectionPropertiesPageNames, parentWidget );
817  dlg->setCaption( contextText( StandardActionManager::CollectionProperties, StandardActionManager::DialogTitle ).arg( displayName ) );
818  dlg->show();
819  }
820 
821  void slotCopyItems()
822  {
823  encodeToClipboard( itemSelectionModel );
824  }
825 
826  void slotCutItems()
827  {
828  encodeToClipboard( itemSelectionModel, true );
829  }
830 
831  void slotPaste()
832  {
833  Q_ASSERT( collectionSelectionModel );
834 
835  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
836  if ( list.isEmpty() )
837  return;
838 
839  const QModelIndex index = list.first();
840  Q_ASSERT( index.isValid() );
841 
842 #ifndef QT_NO_CLIPBOARD
843  // TODO: Copy or move? We can't seem to cut yet
844  QAbstractItemModel *model = const_cast<QAbstractItemModel *>( collectionSelectionModel->model() );
845  const QMimeData *mimeData = QApplication::clipboard()->mimeData();
846  model->dropMimeData( mimeData, isCutAction( mimeData ) ? Qt::MoveAction : Qt::CopyAction, -1, -1, index );
847  model->setData( QModelIndex(), false, EntityTreeModel::PendingCutRole );
848  QApplication::clipboard()->clear();
849 #endif
850  }
851 
852  void slotDeleteItems()
853  {
854  Q_ASSERT( itemSelectionModel );
855 
856  Item::List items;
857  foreach ( const QModelIndex &index, safeSelectedRows( itemSelectionModel ) ) {
858  bool ok;
859  const qlonglong id = index.data( ItemModel::IdRole ).toLongLong( &ok );
860  Q_ASSERT( ok );
861  items << Item( id );
862  }
863 
864  if ( items.isEmpty() )
865  return;
866 
867  QMetaObject::invokeMethod(q, "slotDeleteItemsDeferred",
868  Qt::QueuedConnection,
869  Q_ARG(Akonadi::Item::List, items));
870  }
871 
872  void slotDeleteItemsDeferred(const Akonadi::Item::List &items)
873  {
874  Q_ASSERT( itemSelectionModel );
875 
876  if ( KMessageBox::questionYesNo( parentWidget,
877  contextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxText, items.count(), QString() ),
878  contextText( StandardActionManager::DeleteItems, StandardActionManager::MessageBoxTitle, items.count(), QString() ),
879  KStandardGuiItem::del(), KStandardGuiItem::cancel(),
880  QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
881  return;
882 
883  ItemDeleteJob *job = new ItemDeleteJob( items, q );
884  q->connect( job, SIGNAL(result(KJob*)), q, SLOT(itemDeletionResult(KJob*)) );
885  }
886 
887  void slotLocalSubscription()
888  {
889  SubscriptionDialog* dlg = new SubscriptionDialog( mMimeTypeFilter, parentWidget );
890  dlg->showHiddenCollection(true);
891  dlg->show();
892  }
893 
894  void slotAddToFavorites()
895  {
896  Q_ASSERT( collectionSelectionModel );
897  Q_ASSERT( favoritesModel );
898  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
899  if ( list.isEmpty() )
900  return;
901 
902  foreach ( const QModelIndex &index, list ) {
903  Q_ASSERT( index.isValid() );
904  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
905  Q_ASSERT( collection.isValid() );
906 
907  favoritesModel->addCollection( collection );
908  }
909 
910  updateActions();
911  }
912 
913  void slotRemoveFromFavorites()
914  {
915  Q_ASSERT( collectionSelectionModel );
916  Q_ASSERT( favoritesModel );
917  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
918  if ( list.isEmpty() )
919  return;
920 
921  foreach ( const QModelIndex &index, list ) {
922  Q_ASSERT( index.isValid() );
923  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
924  Q_ASSERT( collection.isValid() );
925 
926  favoritesModel->removeCollection( collection );
927  }
928 
929  updateActions();
930  }
931 
932  void slotRenameFavorite()
933  {
934  Q_ASSERT( collectionSelectionModel );
935  Q_ASSERT( favoritesModel );
936  const QModelIndexList list = safeSelectedRows( collectionSelectionModel );
937  if ( list.isEmpty() )
938  return;
939  const QModelIndex index = list.first();
940  Q_ASSERT( index.isValid() );
941  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
942  Q_ASSERT( collection.isValid() );
943 
944  const QString displayName = collection.hasAttribute<EntityDisplayAttribute>() ? collection.attribute<EntityDisplayAttribute>()->displayName() : collection.name();
945 
946  RenameFavoriteDialog dlg(contextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogTitle ),contextText( StandardActionManager::RenameFavoriteCollection, StandardActionManager::DialogText ) , favoritesModel->favoriteLabel( collection ), displayName, parentWidget );
947  if ( dlg.exec() )
948  {
949  favoritesModel->setFavoriteLabel( collection, dlg.newName() );
950  }
951  }
952 
953  void slotSynchronizeFavoriteCollections()
954  {
955  Q_ASSERT( favoritesModel );
956  foreach( const Collection& collection, favoritesModel->collections() ) {
957  // there might be virtual collections in favorites which cannot be checked
958  // so let's be safe here, agentmanager asserts otherwise
959  if ( !collection.resource().isEmpty() ) {
960  AgentManager::self()->synchronizeCollection( collection, false );
961  }
962  }
963  }
964 
965  void slotCopyCollectionTo()
966  {
967  pasteTo( collectionSelectionModel, collectionSelectionModel->model(), CopyCollectionToMenu, Qt::CopyAction );
968  }
969 
970  void slotCopyItemTo()
971  {
972  pasteTo( itemSelectionModel, collectionSelectionModel->model(), CopyItemToMenu, Qt::CopyAction );
973  }
974 
975  void slotMoveCollectionTo()
976  {
977  pasteTo( collectionSelectionModel, collectionSelectionModel->model(), MoveCollectionToMenu, Qt::MoveAction );
978  }
979 
980  void slotMoveItemTo()
981  {
982  pasteTo( itemSelectionModel, collectionSelectionModel->model(), MoveItemToMenu, Qt::MoveAction );
983  }
984 
985  void slotCopyCollectionTo( QAction *action )
986  {
987  pasteTo( collectionSelectionModel, action, Qt::CopyAction );
988  }
989 
990  void slotCopyItemTo( QAction *action )
991  {
992  pasteTo( itemSelectionModel, action, Qt::CopyAction );
993  }
994 
995  void slotMoveCollectionTo( QAction *action )
996  {
997  pasteTo( collectionSelectionModel, action, Qt::MoveAction );
998  }
999 
1000  void slotMoveItemTo( QAction *action )
1001  {
1002  pasteTo( itemSelectionModel, action, Qt::MoveAction );
1003  }
1004 
1005  AgentInstance::List selectedAgentInstances() const
1006  {
1007  AgentInstance::List instances;
1008 
1009  Q_ASSERT( collectionSelectionModel );
1010  if ( collectionSelectionModel->selection().indexes().isEmpty() )
1011  return instances;
1012 
1013  foreach ( const QModelIndex &index, collectionSelectionModel->selection().indexes() ) {
1014  Q_ASSERT( index.isValid() );
1015  const Collection collection = index.data( CollectionModel::CollectionRole ).value<Collection>();
1016  Q_ASSERT( collection.isValid() );
1017 
1018  if ( collection.isValid() ) {
1019  const QString identifier = collection.resource();
1020  instances << AgentManager::self()->instance( identifier );
1021  }
1022  }
1023 
1024  return instances;
1025  }
1026 
1027  AgentInstance selectedAgentInstance() const
1028  {
1029  const AgentInstance::List instances = selectedAgentInstances();
1030 
1031  if ( instances.isEmpty() )
1032  return AgentInstance();
1033 
1034  return instances.first();
1035  }
1036 
1037  void slotCreateResource()
1038  {
1039  Akonadi::AgentTypeDialog dlg( parentWidget );
1040  dlg.setCaption( contextText( StandardActionManager::CreateResource, StandardActionManager::DialogTitle ) );
1041 
1042  foreach ( const QString &mimeType, mMimeTypeFilter )
1043  dlg.agentFilterProxyModel()->addMimeTypeFilter( mimeType );
1044 
1045  foreach ( const QString &capability, mCapabilityFilter )
1046  dlg.agentFilterProxyModel()->addCapabilityFilter( capability );
1047 
1048  if ( dlg.exec() ) {
1049  const AgentType agentType = dlg.agentType();
1050 
1051  if ( agentType.isValid() ) {
1052  AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, q );
1053  q->connect( job, SIGNAL(result(KJob*)), SLOT(resourceCreationResult(KJob*)) );
1054  job->configure( parentWidget );
1055  job->start();
1056  }
1057  }
1058  }
1059 
1060  void slotDeleteResource()
1061  {
1062  const AgentInstance::List instances = selectedAgentInstances();
1063  if ( instances.isEmpty() )
1064  return;
1065 
1066  if ( KMessageBox::questionYesNo( parentWidget,
1067  contextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxText, instances.count(), instances.first().name() ),
1068  contextText( StandardActionManager::DeleteResources, StandardActionManager::MessageBoxTitle, instances.count(), instances.first().name() ),
1069  KStandardGuiItem::del(), KStandardGuiItem::cancel(),
1070  QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
1071  return;
1072 
1073  foreach ( const AgentInstance &instance, instances )
1074  AgentManager::self()->removeInstance( instance );
1075  }
1076 
1077  void slotSynchronizeResource()
1078  {
1079  const AgentInstance::List instances = selectedAgentInstances();
1080  if ( instances.isEmpty() )
1081  return;
1082 
1083  foreach ( AgentInstance instance, instances ) { //krazy:exclude=foreach
1084  instance.synchronize();
1085  }
1086  }
1087 
1088  void slotResourceProperties()
1089  {
1090  AgentInstance instance = selectedAgentInstance();
1091  if ( !instance.isValid() )
1092  return;
1093 
1094  instance.configure( parentWidget );
1095  }
1096 
1097  void slotToggleWorkOffline( bool offline )
1098  {
1099  setWorkOffline( offline );
1100 
1101  AgentInstance::List instances = AgentManager::self()->instances();
1102  foreach ( AgentInstance instance, instances ) { //krazy:exclude=foreach
1103  instance.setIsOnline( !offline );
1104  }
1105  }
1106 
1107  void pasteTo( QItemSelectionModel *selectionModel, const QAbstractItemModel *model, StandardActionManager::Type type, Qt::DropAction dropAction )
1108  {
1109  const QSet<QString> mimeTypes = mimeTypesOfSelection( type );
1110 
1111  CollectionDialog dlg( const_cast<QAbstractItemModel*>( model ) );
1112  dlg.setMimeTypeFilter( mimeTypes.toList() );
1113 
1114  if ( type == CopyItemToMenu || type == MoveItemToMenu )
1115  dlg.setAccessRightsFilter( Collection::CanCreateItem );
1116  else if ( type == CopyCollectionToMenu || type == MoveCollectionToMenu )
1117  dlg.setAccessRightsFilter( Collection::CanCreateCollection );
1118 
1119  if ( dlg.exec() ) {
1120  const QModelIndex index = EntityTreeModel::modelIndexForCollection( collectionSelectionModel->model(), dlg.selectedCollection() );
1121  if ( !index.isValid() )
1122  return;
1123 
1124  const QMimeData *mimeData = selectionModel->model()->mimeData( safeSelectedRows( selectionModel ) );
1125 
1126  QAbstractItemModel *model = const_cast<QAbstractItemModel *>( index.model() );
1127  model->dropMimeData( mimeData, dropAction, -1, -1, index );
1128  }
1129  }
1130 
1131  void pasteTo( QItemSelectionModel *selectionModel, QAction *action, Qt::DropAction dropAction )
1132  {
1133  Q_ASSERT( selectionModel );
1134  Q_ASSERT( action );
1135 
1136  if ( safeSelectedRows( selectionModel ).count() <= 0 )
1137  return;
1138 
1139  const QMimeData *mimeData = selectionModel->model()->mimeData( selectionModel->selectedRows() );
1140 
1141  const QModelIndex index = action->data().value<QModelIndex>();
1142  Q_ASSERT( index.isValid() );
1143 
1144  QAbstractItemModel *model = const_cast<QAbstractItemModel *>( index.model() );
1145  const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>();
1146  addRecentCollection( collection.id() );
1147  model->dropMimeData( mimeData, dropAction, -1, -1, index );
1148  }
1149 
1150  void addRecentCollection( Akonadi::Collection::Id id )
1151  {
1152  QMapIterator<StandardActionManager::Type, QWeakPointer<RecentCollectionAction> > item(mRecentCollectionsMenu);
1153  while (item.hasNext()) {
1154  item.next();
1155  if ( item.value().data() ) {
1156  item.value().data()->addRecentCollection( id );
1157  }
1158  }
1159  }
1160 
1161  void collectionCreationResult( KJob *job )
1162  {
1163  if ( job->error() ) {
1164  KMessageBox::error( parentWidget,
1165  contextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1166  contextText( StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle ) );
1167  }
1168  }
1169 
1170  void collectionDeletionResult( KJob *job )
1171  {
1172  if ( job->error() ) {
1173  KMessageBox::error( parentWidget,
1174  contextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1175  contextText( StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle ) );
1176  }
1177  }
1178 
1179  void moveCollectionToTrashResult( KJob *job )
1180  {
1181  if ( job->error() ) {
1182  KMessageBox::error( parentWidget,
1183  contextText( StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1184  contextText( StandardActionManager::MoveCollectionsToTrash, StandardActionManager::ErrorMessageTitle ) );
1185  }
1186  }
1187 
1188  void moveItemToTrashResult( KJob *job )
1189  {
1190  if ( job->error() ) {
1191  KMessageBox::error( parentWidget,
1192  contextText( StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1193  contextText( StandardActionManager::MoveItemsToTrash, StandardActionManager::ErrorMessageTitle ) );
1194  }
1195  }
1196 
1197  void itemDeletionResult( KJob *job )
1198  {
1199  if ( job->error() ) {
1200  KMessageBox::error( parentWidget,
1201  contextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1202  contextText( StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle ) );
1203  }
1204  }
1205 
1206  void resourceCreationResult( KJob *job )
1207  {
1208  if ( job->error() ) {
1209  KMessageBox::error( parentWidget,
1210  contextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1211  contextText( StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle ) );
1212  }
1213  }
1214 
1215  void pasteResult( KJob *job )
1216  {
1217  if ( job->error() ) {
1218  KMessageBox::error( parentWidget,
1219  contextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageText ).arg( job->errorString() ),
1220  contextText( StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle ) );
1221  }
1222  }
1223 
1227  QSet<QString> mimeTypesOfSelection( StandardActionManager::Type type ) const
1228  {
1229  QModelIndexList list;
1230  QSet<QString> mimeTypes;
1231 
1232  const bool isItemAction = ( type == CopyItemToMenu || type == MoveItemToMenu );
1233  const bool isCollectionAction = ( type == CopyCollectionToMenu || type == MoveCollectionToMenu );
1234 
1235  if ( isItemAction ) {
1236  list = safeSelectedRows( itemSelectionModel );
1237  foreach ( const QModelIndex &index, list )
1238  mimeTypes << index.data( EntityTreeModel::MimeTypeRole ).toString();
1239  }
1240 
1241  if ( isCollectionAction ) {
1242  list = safeSelectedRows( collectionSelectionModel );
1243  foreach ( const QModelIndex &index, list ) {
1244  const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>();
1245 
1246  // The mimetypes that the selected collection can possibly contain
1247  mimeTypes = AgentManager::self()->instance( collection.resource() ).type().mimeTypes().toSet();
1248  }
1249  }
1250 
1251  return mimeTypes;
1252  }
1253 
1257  bool isWritableTargetCollectionForMimeTypes( const Collection &collection, const QSet<QString> &mimeTypes, StandardActionManager::Type type ) const
1258  {
1259  if ( CollectionUtils::isVirtual( collection ) )
1260  return false;
1261 
1262  const bool isItemAction = ( type == CopyItemToMenu || type == MoveItemToMenu );
1263  const bool isCollectionAction = ( type == CopyCollectionToMenu || type == MoveCollectionToMenu );
1264 
1265  const bool canContainRequiredMimeTypes = !collection.contentMimeTypes().toSet().intersect( mimeTypes ).isEmpty();
1266  const bool canCreateNewItems = (collection.rights() & Collection::CanCreateItem);
1267 
1268  const bool canCreateNewCollections = (collection.rights() & Collection::CanCreateCollection);
1269  const bool canContainCollections = collection.contentMimeTypes().contains( Collection::mimeType() );
1270  const bool resourceAllowsRequiredMimeTypes = AgentManager::self()->instance( collection.resource() ).type().mimeTypes().toSet().contains( mimeTypes );
1271 
1272  const bool isReadOnlyForItems = (isItemAction && (!canCreateNewItems || !canContainRequiredMimeTypes));
1273  const bool isReadOnlyForCollections = (isCollectionAction && (!canCreateNewCollections || !canContainCollections || !resourceAllowsRequiredMimeTypes));
1274 
1275  return !(CollectionUtils::isStructural( collection ) || isReadOnlyForItems || isReadOnlyForCollections);
1276  }
1277 
1278  void fillFoldersMenu( const Akonadi::Collection::List& selectedCollectionsList, const QSet<QString>& mimeTypes, StandardActionManager::Type type, QMenu *menu,
1279  const QAbstractItemModel *model, QModelIndex parentIndex )
1280  {
1281  const int rowCount = model->rowCount( parentIndex );
1282 
1283  for ( int row = 0; row < rowCount; ++row ) {
1284  const QModelIndex index = model->index( row, 0, parentIndex );
1285  const Collection collection = model->data( index, CollectionModel::CollectionRole ).value<Collection>();
1286 
1287  if ( CollectionUtils::isVirtual( collection ) )
1288  continue;
1289 
1290  const bool readOnly = !isWritableTargetCollectionForMimeTypes( collection, mimeTypes, type );
1291  const bool collectionIsSelected = selectedCollectionsList.contains( collection );
1292 
1293  QString label = model->data( index ).toString();
1294  label.replace( QLatin1String( "&" ), QLatin1String( "&&" ) );
1295 
1296  const QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>();
1297 
1298  if ( model->rowCount( index ) > 0 ) {
1299  // new level
1300  QMenu* popup = new QMenu( menu );
1301  const bool moveAction = (type == MoveCollectionToMenu || type == MoveItemToMenu);
1302  popup->setObjectName( QString::fromUtf8( "subMenu" ) );
1303  popup->setTitle( label );
1304  popup->setIcon( icon );
1305 
1306  fillFoldersMenu( selectedCollectionsList, mimeTypes, type, popup, model, index );
1307 
1308  if ( !readOnly ) {
1309  popup->addSeparator();
1310 
1311  QAction *action = popup->addAction( moveAction ? i18n( "Move to This Folder" ) : i18n( "Copy to This Folder" ) );
1312  action->setData( QVariant::fromValue<QModelIndex>( index ) );
1313  }
1314 
1315  menu->addMenu( popup );
1316 
1317  } else {
1318  // insert an item
1319  QAction* action = menu->addAction( icon, label );
1320  action->setData( QVariant::fromValue<QModelIndex>( index ) );
1321  action->setEnabled( !readOnly && !collectionIsSelected );
1322  }
1323  }
1324  }
1325 
1326  void checkModelsConsistency()
1327  {
1328  if ( favoritesModel == 0 || favoriteSelectionModel == 0 ) {
1329  // No need to check when the favorite collections feature is not used
1330  return;
1331  }
1332 
1333  // find the base ETM of the favourites view
1334  const QAbstractItemModel *favModel = favoritesModel;
1335  while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( favModel ) ) {
1336  favModel = proxy->sourceModel();
1337  }
1338 
1339  // Check that the collection selection model maps to the same
1340  // EntityTreeModel than favoritesModel
1341  if ( collectionSelectionModel != 0 ) {
1342  const QAbstractItemModel *model = collectionSelectionModel->model();
1343  while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ) ) {
1344  model = proxy->sourceModel();
1345  }
1346 
1347  Q_ASSERT( model == favModel );
1348  }
1349 
1350  // Check that the favorite selection model maps to favoritesModel
1351  const QAbstractItemModel *model = favoriteSelectionModel->model();
1352  while ( const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>( model ) ) {
1353  model = proxy->sourceModel();
1354  }
1355  Q_ASSERT( model == favModel );
1356  }
1357 
1358  void markCutAction( QMimeData *mimeData, bool cut ) const
1359  {
1360  if ( !cut )
1361  return;
1362 
1363  const QByteArray cutSelectionData = "1"; //krazy:exclude=doublequote_chars
1364  mimeData->setData( QLatin1String( "application/x-kde.akonadi-cutselection" ), cutSelectionData);
1365  }
1366 
1367  bool isCutAction( const QMimeData *mimeData ) const
1368  {
1369  const QByteArray data = mimeData->data( QLatin1String( "application/x-kde.akonadi-cutselection" ) );
1370  if ( data.isEmpty() )
1371  return false;
1372  else
1373  return (data.at( 0 ) == '1'); // true if 1
1374  }
1375 
1376  void setContextText( StandardActionManager::Type type, StandardActionManager::TextContext context, const QString &data )
1377  {
1378  ContextTextEntry entry;
1379  entry.text = data;
1380 
1381  contextTexts[ type ].insert( context, entry );
1382  }
1383 
1384  void setContextText( StandardActionManager::Type type, StandardActionManager::TextContext context, const KLocalizedString &data )
1385  {
1386  ContextTextEntry entry;
1387  entry.localizedText = data;
1388 
1389  contextTexts[ type ].insert( context, entry );
1390  }
1391 
1392  QString contextText( StandardActionManager::Type type, StandardActionManager::TextContext context ) const
1393  {
1394  return contextTexts[ type ].value( context ).text;
1395  }
1396 
1397  QString contextText( StandardActionManager::Type type, StandardActionManager::TextContext context, int count, const QString &value ) const
1398  {
1399  if ( contextTexts[ type ].value( context ).localizedText.isEmpty() )
1400  return contextTexts[ type ].value( context ).text;
1401 
1402  KLocalizedString text = contextTexts[ type ].value( context ).localizedText;
1403  const QString str = text.subs( count ).toString();
1404  const int argCount = str.count( QRegExp( QLatin1String( "%[0-9]" ) ) );
1405  if ( argCount > 0 ) {
1406  return text.subs( count ).subs( value ).toString();
1407  } else {
1408  return text.subs( count ).toString();
1409  }
1410  }
1411 
1412  StandardActionManager *q;
1413  KActionCollection *actionCollection;
1414  QWidget *parentWidget;
1415  QItemSelectionModel *collectionSelectionModel;
1416  QItemSelectionModel *itemSelectionModel;
1417  FavoriteCollectionsModel *favoritesModel;
1418  QItemSelectionModel *favoriteSelectionModel;
1419  QVector<KAction*> actions;
1420  QHash<StandardActionManager::Type, KLocalizedString> pluralLabels;
1421  QHash<StandardActionManager::Type, KLocalizedString> pluralIconLabels;
1422 
1423  struct ContextTextEntry
1424  {
1425  QString text;
1426  KLocalizedString localizedText;
1427  bool isLocalized;
1428  };
1429 
1430  typedef QHash<StandardActionManager::TextContext, ContextTextEntry> ContextTexts;
1431  QHash<StandardActionManager::Type, ContextTexts> contextTexts;
1432 
1433  ActionStateManager mActionStateManager;
1434 
1435  QStringList mMimeTypeFilter;
1436  QStringList mCapabilityFilter;
1437  QStringList mCollectionPropertiesPageNames;
1438  QMap<StandardActionManager::Type, QWeakPointer<RecentCollectionAction> > mRecentCollectionsMenu;
1439 };
1440 
1441 //@endcond
1442 
1443 StandardActionManager::StandardActionManager( KActionCollection * actionCollection,
1444  QWidget * parent) :
1445  QObject( parent ),
1446  d( new Private( this ) )
1447 {
1448  d->parentWidget = parent;
1449  d->actionCollection = actionCollection;
1450  d->mActionStateManager.setReceiver( this );
1451 #ifndef QT_NO_CLIPBOARD
1452  connect( QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), SLOT(clipboardChanged(QClipboard::Mode)) );
1453 #endif
1454 }
1455 
1456 StandardActionManager::~ StandardActionManager()
1457 {
1458  delete d;
1459 }
1460 
1461 void StandardActionManager::setCollectionSelectionModel( QItemSelectionModel * selectionModel )
1462 {
1463  d->collectionSelectionModel = selectionModel;
1464  connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1465  SLOT(collectionSelectionChanged()) );
1466 
1467  d->checkModelsConsistency();
1468 }
1469 
1470 void StandardActionManager::setItemSelectionModel( QItemSelectionModel * selectionModel )
1471 {
1472  d->itemSelectionModel = selectionModel;
1473  connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1474  SLOT(updateActions()) );
1475 }
1476 
1477 void StandardActionManager::setFavoriteCollectionsModel( FavoriteCollectionsModel *favoritesModel )
1478 {
1479  d->favoritesModel = favoritesModel;
1480  d->checkModelsConsistency();
1481 }
1482 
1483 void StandardActionManager::setFavoriteSelectionModel( QItemSelectionModel *selectionModel )
1484 {
1485  d->favoriteSelectionModel = selectionModel;
1486  connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1487  SLOT(favoriteSelectionChanged()) );
1488  d->checkModelsConsistency();
1489 }
1490 
1491 KAction* StandardActionManager::createAction( Type type )
1492 {
1493  Q_ASSERT( type < LastType );
1494  if ( d->actions[type] )
1495  return d->actions[type];
1496  KAction *action = 0;
1497  switch ( standardActionData[type].actionType ) {
1498  case NormalAction:
1499  case ActionWithAlternative:
1500  action = new KAction( d->parentWidget );
1501  break;
1502  case ActionAlternative:
1503  d->actions[type] = d->actions[type-1];
1504  Q_ASSERT( d->actions[type] );
1505  if ( (LastType > type+1) && (standardActionData[type+1].actionType == ActionAlternative) ) {
1506  createAction(static_cast<Type>(type+1)); //ensure that alternative actions are initialized when not created by createAllActions
1507  }
1508  return d->actions[type];
1509  case MenuAction:
1510  action = new KActionMenu( d->parentWidget );
1511  break;
1512  case ToggleAction:
1513  action = new KToggleAction( d->parentWidget );
1514  break;
1515  }
1516 
1517  if ( d->pluralLabels.contains( type ) && !d->pluralLabels.value( type ).isEmpty() )
1518  action->setText( d->pluralLabels.value( type ).subs( 1 ).toString() );
1519  else if ( standardActionData[type].label )
1520  action->setText( i18n( standardActionData[type].label ) );
1521 
1522  if ( d->pluralIconLabels.contains( type ) && !d->pluralIconLabels.value( type ).isEmpty() )
1523  action->setIconText( d->pluralIconLabels.value( type ).subs( 1 ).toString() );
1524  else if ( standardActionData[type].iconLabel )
1525  action->setIconText( i18n( standardActionData[type].iconLabel ) );
1526 
1527  if ( standardActionData[type].icon )
1528  action->setIcon( KIcon( QString::fromLatin1( standardActionData[type].icon ) ) );
1529 
1530  action->setShortcut( standardActionData[type].shortcut );
1531 
1532  if ( standardActionData[type].slot ) {
1533  switch ( standardActionData[type].actionType ) {
1534  case NormalAction:
1535  case ActionWithAlternative:
1536  connect( action, SIGNAL(triggered()), standardActionData[type].slot );
1537  break;
1538  case MenuAction:
1539  {
1540  KActionMenu *actionMenu = qobject_cast<KActionMenu*>( action );
1541  connect( actionMenu->menu(), SIGNAL(triggered(QAction*)), standardActionData[type].slot );
1542  }
1543  break;
1544  case ToggleAction:
1545  {
1546  connect( action, SIGNAL(triggered(bool)), standardActionData[type].slot );
1547  }
1548  break;
1549  case ActionAlternative:
1550  Q_ASSERT(0);
1551  }
1552  }
1553 
1554  if ( type == ToggleWorkOffline ) {
1555  // inititalize the action state with information from config file
1556  disconnect( action, SIGNAL(triggered(bool)), this, standardActionData[type].slot );
1557  action->setChecked( workOffline() );
1558  connect( action, SIGNAL(triggered(bool)), this, standardActionData[type].slot );
1559 
1560  //TODO: find a way to check for updates to the config file
1561  }
1562 
1563  Q_ASSERT( standardActionData[type].name );
1564  d->actionCollection->addAction( QString::fromLatin1(standardActionData[type].name), action );
1565  d->actions[type] = action;
1566  if ( ( standardActionData[type].actionType == ActionWithAlternative ) && (standardActionData[type+1].actionType == ActionAlternative)) {
1567  createAction(static_cast<Type>(type+1)); //ensure that alternative actions are initialized when not created by createAllActions
1568  }
1569  d->updateActions();
1570  return action;
1571 }
1572 
1573 void StandardActionManager::createAllActions()
1574 {
1575  for ( uint i = 0; i < LastType; ++i )
1576  createAction( (Type)i );
1577 }
1578 
1579 KAction * StandardActionManager::action( Type type ) const
1580 {
1581  Q_ASSERT( type < LastType );
1582  return d->actions[type];
1583 }
1584 
1585 void StandardActionManager::setActionText( Type type, const KLocalizedString & text )
1586 {
1587  Q_ASSERT( type < LastType );
1588  d->pluralLabels.insert( type, text );
1589  d->updateActions();
1590 }
1591 
1592 void StandardActionManager::interceptAction( Type type, bool intercept )
1593 {
1594  Q_ASSERT( type < LastType );
1595 
1596  const KAction *action = d->actions[type];
1597 
1598  if ( !action )
1599  return;
1600 
1601  if ( intercept )
1602  disconnect( action, SIGNAL(triggered()), this, standardActionData[type].slot );
1603  else
1604  connect( action, SIGNAL(triggered()), standardActionData[type].slot );
1605 }
1606 
1607 Akonadi::Collection::List StandardActionManager::selectedCollections() const
1608 {
1609  Collection::List collections;
1610 
1611  if ( !d->collectionSelectionModel )
1612  return collections;
1613 
1614  foreach ( const QModelIndex &index, safeSelectedRows( d->collectionSelectionModel ) ) {
1615  const Collection collection = index.data( EntityTreeModel::CollectionRole ).value<Collection>();
1616  if ( collection.isValid() )
1617  collections << collection;
1618  }
1619 
1620  return collections;
1621 }
1622 
1623 Item::List StandardActionManager::selectedItems() const
1624 {
1625  Item::List items;
1626 
1627  if ( !d->itemSelectionModel )
1628  return items;
1629 
1630  foreach ( const QModelIndex &index, safeSelectedRows( d->itemSelectionModel ) ) {
1631  const Item item = index.data( EntityTreeModel::ItemRole ).value<Item>();
1632  if ( item.isValid() )
1633  items << item;
1634  }
1635 
1636  return items;
1637 }
1638 
1639 void StandardActionManager::setContextText( Type type, TextContext context, const QString &text )
1640 {
1641  d->setContextText( type, context, text );
1642 }
1643 
1644 void StandardActionManager::setContextText( Type type, TextContext context, const KLocalizedString &text )
1645 {
1646  d->setContextText( type, context, text );
1647 }
1648 
1649 void StandardActionManager::setMimeTypeFilter( const QStringList &mimeTypes )
1650 {
1651  d->mMimeTypeFilter = mimeTypes;
1652 }
1653 
1654 void StandardActionManager::setCapabilityFilter( const QStringList &capabilities )
1655 {
1656  d->mCapabilityFilter = capabilities;
1657 }
1658 
1659 void StandardActionManager::setCollectionPropertiesPageNames( const QStringList &names )
1660 {
1661  d->mCollectionPropertiesPageNames = names;
1662 }
1663 
1664 void StandardActionManager::createActionFolderMenu(QMenu *menu, Type type)
1665 {
1666  d->createActionFolderMenu( menu, type );
1667 }
1668 
1669 
1670 
1671 #include "standardactionmanager.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Sep 24 2012 09:06:28 by doxygen 1.8.1.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs-4.9.1 API Reference

Skip menu "kdepimlibs-4.9.1 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal