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