• Skip to content
  • Skip to link menu
KDE 4.8 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

akonadi

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"

akonadi

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • 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
Generated for KDE-PIM Libraries by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal