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 | |
67 | using namespace Akonadi; |
68 | |
69 | //@cond PRIVATE |
70 | |
71 | enum ActionType |
72 | { |
73 | NormalAction, |
74 | ActionWithAlternative, //Normal action, but with an alternative state |
75 | ActionAlternative, //Alternative state of the ActionWithAlternative |
76 | , |
77 | ToggleAction |
78 | }; |
79 | |
80 | static 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 | }; |
128 | static const int numStandardActionData = sizeof standardActionData / sizeof * standardActionData; |
129 | |
130 | BOOST_STATIC_ASSERT(numStandardActionData == StandardActionManager::LastType); |
131 | |
132 | static 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 | /* |
147 | static inline bool isRootCollection( const Akonadi::Collection &collection ) |
148 | { |
149 | return (collection == Akonadi::Collection::root()); |
150 | } |
151 | */ |
152 | |
153 | static void setWorkOffline(bool offline) |
154 | { |
155 | KConfig config(QLatin1String("akonadikderc" )); |
156 | KConfigGroup group(&config, QLatin1String("Actions" )); |
157 | |
158 | group.writeEntry("WorkOffline" , offline); |
159 | } |
160 | |
161 | static bool workOffline() |
162 | { |
163 | KConfig config(QLatin1String("akonadikderc" )); |
164 | const KConfigGroup group(&config, QLatin1String("Actions" )); |
165 | |
166 | return group.readEntry("WorkOffline" , false); |
167 | } |
168 | |
169 | static 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 | */ |
197 | class StandardActionManager::Private |
198 | { |
199 | public: |
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 * = 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 * = 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 * = 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 *, 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 *, |
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 * = 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 | |
1569 | StandardActionManager::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 | |
1581 | StandardActionManager::~ StandardActionManager() |
1582 | { |
1583 | delete d; |
1584 | } |
1585 | |
1586 | void 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 | |
1595 | void StandardActionManager::setItemSelectionModel(QItemSelectionModel *selectionModel) |
1596 | { |
1597 | d->itemSelectionModel = selectionModel; |
1598 | connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), |
1599 | SLOT(updateActions())); |
1600 | } |
1601 | |
1602 | void StandardActionManager::setFavoriteCollectionsModel(FavoriteCollectionsModel *favoritesModel) |
1603 | { |
1604 | d->favoritesModel = favoritesModel; |
1605 | d->checkModelsConsistency(); |
1606 | } |
1607 | |
1608 | void StandardActionManager::setFavoriteSelectionModel(QItemSelectionModel *selectionModel) |
1609 | { |
1610 | d->favoriteSelectionModel = selectionModel; |
1611 | connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), |
1612 | SLOT(favoriteSelectionChanged())); |
1613 | d->checkModelsConsistency(); |
1614 | } |
1615 | |
1616 | KAction *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 * = 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 | |
1700 | void StandardActionManager::createAllActions() |
1701 | { |
1702 | for (uint i = 0; i < LastType; ++i) { |
1703 | createAction((Type)i); |
1704 | } |
1705 | } |
1706 | |
1707 | KAction *StandardActionManager::action(Type type) const |
1708 | { |
1709 | Q_ASSERT(type < LastType); |
1710 | return d->actions[type]; |
1711 | } |
1712 | |
1713 | void StandardActionManager::setActionText(Type type, const KLocalizedString &text) |
1714 | { |
1715 | Q_ASSERT(type < LastType); |
1716 | d->pluralLabels.insert(type, text); |
1717 | d->updateActions(); |
1718 | } |
1719 | |
1720 | void 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 | |
1737 | Akonadi::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 | |
1755 | Item::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 | |
1773 | void StandardActionManager::setContextText(Type type, TextContext context, const QString &text) |
1774 | { |
1775 | d->setContextText(type, context, text); |
1776 | } |
1777 | |
1778 | void StandardActionManager::setContextText(Type type, TextContext context, const KLocalizedString &text) |
1779 | { |
1780 | d->setContextText(type, context, text); |
1781 | } |
1782 | |
1783 | void StandardActionManager::setMimeTypeFilter(const QStringList &mimeTypes) |
1784 | { |
1785 | d->mMimeTypeFilter = mimeTypes; |
1786 | } |
1787 | |
1788 | void StandardActionManager::setCapabilityFilter(const QStringList &capabilities) |
1789 | { |
1790 | d->mCapabilityFilter = capabilities; |
1791 | } |
1792 | |
1793 | void StandardActionManager::setCollectionPropertiesPageNames(const QStringList &names) |
1794 | { |
1795 | d->mCollectionPropertiesPageNames = names; |
1796 | } |
1797 | |
1798 | void StandardActionManager::createActionFolderMenu(QMenu *, Type type) |
1799 | { |
1800 | d->createActionFolderMenu(menu, type); |
1801 | } |
1802 | |
1803 | #include "moc_standardactionmanager.cpp" |
1804 | |