]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincontextmenu.cpp
dolphin: convert the remaining code to Qt5 signal/slot syntax
[dolphin.git] / src / dolphincontextmenu.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and *
3 * Cvetoslav Ludmiloff *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "dolphincontextmenu.h"
22
23 #include "dolphinmainwindow.h"
24 #include "dolphinnewfilemenu.h"
25 #include "dolphinviewcontainer.h"
26 #include "dolphin_generalsettings.h"
27 #include "dolphinremoveaction.h"
28
29 #include <KActionCollection>
30 #include <KDesktopFile>
31 #include <kfileitemactionplugin.h>
32 #include <kabstractfileitemactionplugin.h>
33 #include <KFileItemActions>
34 #include <KFileItemListProperties>
35 #include <KGlobal>
36 #include <KIconLoader>
37 #include <KIO/NetAccess>
38 #include <KMenu>
39 #include <KMenuBar>
40 #include <KMessageBox>
41 #include <KMimeTypeTrader>
42 #include <KMimeType>
43 #include <KNewFileMenu>
44 #include <konqmimedata.h>
45 #include <konq_operations.h>
46 #include <KService>
47 #include <KLocale>
48 #include <KPropertiesDialog>
49 #include <KStandardAction>
50 #include <KStandardDirs>
51 #include <KToolBar>
52
53 #include <panels/places/placesitem.h>
54 #include <panels/places/placesitemmodel.h>
55
56 #include <QApplication>
57 #include <QClipboard>
58 #include <QDir>
59
60 #include "views/dolphinview.h"
61 #include "views/viewmodecontroller.h"
62
63 DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent,
64 const QPoint& pos,
65 const KFileItem& fileInfo,
66 const KUrl& baseUrl) :
67 KMenu(parent),
68 m_pos(pos),
69 m_mainWindow(parent),
70 m_fileInfo(fileInfo),
71 m_baseUrl(baseUrl),
72 m_baseFileItem(0),
73 m_selectedItems(),
74 m_selectedItemsProperties(0),
75 m_context(NoContext),
76 m_copyToMenu(parent),
77 m_customActions(),
78 m_command(None),
79 m_removeAction(0)
80 {
81 // The context menu either accesses the URLs of the selected items
82 // or the items itself. To increase the performance both lists are cached.
83 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
84 m_selectedItems = view->selectedItems();
85 }
86
87 DolphinContextMenu::~DolphinContextMenu()
88 {
89 delete m_selectedItemsProperties;
90 m_selectedItemsProperties = 0;
91 }
92
93 void DolphinContextMenu::setCustomActions(const QList<QAction*>& actions)
94 {
95 m_customActions = actions;
96 }
97
98 DolphinContextMenu::Command DolphinContextMenu::open()
99 {
100 // get the context information
101 if (m_baseUrl.protocol() == QLatin1String("trash")) {
102 m_context |= TrashContext;
103 }
104
105 if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) {
106 m_context |= ItemContext;
107 // TODO: handle other use cases like devices + desktop files
108 }
109
110 // open the corresponding popup for the context
111 if (m_context & TrashContext) {
112 if (m_context & ItemContext) {
113 openTrashItemContextMenu();
114 } else {
115 openTrashContextMenu();
116 }
117 } else if (m_context & ItemContext) {
118 openItemContextMenu();
119 } else {
120 Q_ASSERT(m_context == NoContext);
121 openViewportContextMenu();
122 }
123
124 return m_command;
125 }
126
127 void DolphinContextMenu::keyPressEvent(QKeyEvent *ev)
128 {
129 if (m_removeAction && ev->key() == Qt::Key_Shift) {
130 m_removeAction->update();
131 }
132 KMenu::keyPressEvent(ev);
133 }
134
135 void DolphinContextMenu::keyReleaseEvent(QKeyEvent *ev)
136 {
137 if (m_removeAction && ev->key() == Qt::Key_Shift) {
138 m_removeAction->update();
139 }
140 KMenu::keyReleaseEvent(ev);
141 }
142
143 void DolphinContextMenu::openTrashContextMenu()
144 {
145 Q_ASSERT(m_context & TrashContext);
146
147 QAction* emptyTrashAction = new QAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"), this);
148 KConfig trashConfig("trashrc", KConfig::SimpleConfig);
149 emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
150 addAction(emptyTrashAction);
151
152 addCustomActions();
153
154 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
155 addAction(propertiesAction);
156
157 addShowMenuBarAction();
158
159 if (exec(m_pos) == emptyTrashAction) {
160 KonqOperations::emptyTrash(m_mainWindow);
161 }
162 }
163
164 void DolphinContextMenu::openTrashItemContextMenu()
165 {
166 Q_ASSERT(m_context & TrashContext);
167 Q_ASSERT(m_context & ItemContext);
168
169 QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow);
170 addAction(restoreAction);
171
172 QAction* deleteAction = m_mainWindow->actionCollection()->action("delete");
173 addAction(deleteAction);
174
175 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
176 addAction(propertiesAction);
177
178 if (exec(m_pos) == restoreAction) {
179 KUrl::List selectedUrls;
180 foreach (const KFileItem &item, m_selectedItems) {
181 selectedUrls.append(item.url());
182 }
183
184 KonqOperations::restoreTrashedItems(selectedUrls, m_mainWindow);
185 }
186 }
187
188 void DolphinContextMenu::openItemContextMenu()
189 {
190 Q_ASSERT(!m_fileInfo.isNull());
191
192 QAction* openParentInNewWindowAction = 0;
193 QAction* openParentInNewTabAction = 0;
194 QAction* addToPlacesAction = 0;
195 const KFileItemListProperties& selectedItemsProps = selectedItemsProperties();
196
197 if (m_selectedItems.count() == 1) {
198 if (m_fileInfo.isDir()) {
199 // setup 'Create New' menu
200 DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow);
201 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
202 newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
203 newFileMenu->checkUpToDate();
204 newFileMenu->setPopupFiles(m_fileInfo.url());
205 newFileMenu->setEnabled(selectedItemsProps.supportsWriting());
206 connect(newFileMenu, &DolphinNewFileMenu::fileCreated, newFileMenu, &DolphinNewFileMenu::deleteLater);
207 connect(newFileMenu, &DolphinNewFileMenu::directoryCreated, newFileMenu, &DolphinNewFileMenu::deleteLater);
208
209 QMenu* menu = newFileMenu->menu();
210 menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
211 menu->setIcon(KIcon("document-new"));
212 addMenu(menu);
213 addSeparator();
214
215 // insert 'Open in new window' and 'Open in new tab' entries
216 addAction(m_mainWindow->actionCollection()->action("open_in_new_window"));
217 addAction(m_mainWindow->actionCollection()->action("open_in_new_tab"));
218
219 // insert 'Add to Places' entry
220 if (!placeExists(m_fileInfo.url())) {
221 addToPlacesAction = addAction(KIcon("bookmark-new"),
222 i18nc("@action:inmenu Add selected folder to places",
223 "Add to Places"));
224 }
225
226 addSeparator();
227 } else if (m_baseUrl.protocol().contains("search")) {
228 openParentInNewWindowAction = new QAction(KIcon("window-new"),
229 i18nc("@action:inmenu",
230 "Open Path in New Window"),
231 this);
232 addAction(openParentInNewWindowAction);
233
234 openParentInNewTabAction = new QAction(KIcon("tab-new"),
235 i18nc("@action:inmenu",
236 "Open Path in New Tab"),
237 this);
238 addAction(openParentInNewTabAction);
239
240 addSeparator();
241 } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) {
242 // insert 'Open in new window' and 'Open in new tab' entries
243 addAction(m_mainWindow->actionCollection()->action("open_in_new_window"));
244 addAction(m_mainWindow->actionCollection()->action("open_in_new_tab"));
245
246 addSeparator();
247 }
248 } else {
249 bool selectionHasOnlyDirs = true;
250 foreach (const KFileItem& item, m_selectedItems) {
251 const KUrl& url = DolphinView::openItemAsFolderUrl(item);
252 if (url.isEmpty()) {
253 selectionHasOnlyDirs = false;
254 break;
255 }
256 }
257
258 if (selectionHasOnlyDirs) {
259 // insert 'Open in new tab' entry
260 addAction(m_mainWindow->actionCollection()->action("open_in_new_tabs"));
261 addSeparator();
262 }
263 }
264
265 insertDefaultItemActions(selectedItemsProps);
266
267 addSeparator();
268
269 KFileItemActions fileItemActions;
270 fileItemActions.setItemListProperties(selectedItemsProps);
271 addServiceActions(fileItemActions);
272
273 addFileItemPluginActions();
274
275 addVersionControlPluginActions();
276
277 // insert 'Copy To' and 'Move To' sub menus
278 if (GeneralSettings::showCopyMoveMenu()) {
279 m_copyToMenu.setItems(m_selectedItems);
280 m_copyToMenu.setReadOnly(!selectedItemsProps.supportsWriting());
281 m_copyToMenu.addActionsTo(this);
282 }
283
284 // insert 'Properties...' entry
285 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
286 addAction(propertiesAction);
287
288 QAction* activatedAction = exec(m_pos);
289 if (activatedAction) {
290 if (activatedAction == addToPlacesAction) {
291 const KUrl selectedUrl(m_fileInfo.url());
292 if (selectedUrl.isValid()) {
293 PlacesItemModel model;
294 const QString text = selectedUrl.fileName();
295 PlacesItem* item = model.createPlacesItem(text, selectedUrl);
296 model.appendItemToGroup(item);
297 }
298 } else if (activatedAction == openParentInNewWindowAction) {
299 m_command = OpenParentFolderInNewWindow;
300 } else if (activatedAction == openParentInNewTabAction) {
301 m_command = OpenParentFolderInNewTab;
302 }
303 }
304 }
305
306 void DolphinContextMenu::openViewportContextMenu()
307 {
308 // setup 'Create New' menu
309 KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu();
310 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
311 newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
312 newFileMenu->checkUpToDate();
313 newFileMenu->setPopupFiles(m_baseUrl);
314 addMenu(newFileMenu->menu());
315 addSeparator();
316
317 // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and
318 // "open_in_new_tab" here, as the current selection should get ignored.
319 addAction(m_mainWindow->actionCollection()->action("new_window"));
320 addAction(m_mainWindow->actionCollection()->action("new_tab"));
321
322 // Insert 'Add to Places' entry if exactly one item is selected
323 QAction* addToPlacesAction = 0;
324 if (!placeExists(m_mainWindow->activeViewContainer()->url())) {
325 addToPlacesAction = addAction(KIcon("bookmark-new"),
326 i18nc("@action:inmenu Add current folder to places", "Add to Places"));
327 }
328
329 addSeparator();
330
331 QAction* pasteAction = createPasteAction();
332 addAction(pasteAction);
333 addSeparator();
334
335 // Insert service actions
336 const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem());
337 KFileItemActions fileItemActions;
338 fileItemActions.setItemListProperties(baseUrlProperties);
339 addServiceActions(fileItemActions);
340
341 addFileItemPluginActions();
342
343 addVersionControlPluginActions();
344
345 addCustomActions();
346
347 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
348 addAction(propertiesAction);
349
350 addShowMenuBarAction();
351
352 QAction* action = exec(m_pos);
353 if (addToPlacesAction && (action == addToPlacesAction)) {
354 const DolphinViewContainer* container = m_mainWindow->activeViewContainer();
355 if (container->url().isValid()) {
356 PlacesItemModel model;
357 PlacesItem* item = model.createPlacesItem(container->placesText(),
358 container->url());
359 model.appendItemToGroup(item);
360 }
361 }
362 }
363
364 void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties)
365 {
366 const KActionCollection* collection = m_mainWindow->actionCollection();
367
368 // Insert 'Cut', 'Copy' and 'Paste'
369 addAction(collection->action(KStandardAction::name(KStandardAction::Cut)));
370 addAction(collection->action(KStandardAction::name(KStandardAction::Copy)));
371 addAction(createPasteAction());
372
373 addSeparator();
374
375 // Insert 'Rename'
376 QAction* renameAction = collection->action("rename");
377 addAction(renameAction);
378
379 // Insert 'Move to Trash' and/or 'Delete'
380 if (properties.supportsDeleting()) {
381 const bool showDeleteAction = (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false) ||
382 !properties.isLocal());
383 const bool showMoveToTrashAction = (properties.isLocal() &&
384 properties.supportsMoving());
385
386 if (showDeleteAction && showMoveToTrashAction) {
387 delete m_removeAction;
388 m_removeAction = 0;
389 addAction(m_mainWindow->actionCollection()->action("move_to_trash"));
390 addAction(m_mainWindow->actionCollection()->action("delete"));
391 } else if (showDeleteAction && !showMoveToTrashAction) {
392 addAction(m_mainWindow->actionCollection()->action("delete"));
393 } else {
394 if (!m_removeAction) {
395 m_removeAction = new DolphinRemoveAction(this, m_mainWindow->actionCollection());
396 }
397 addAction(m_removeAction);
398 m_removeAction->update();
399 }
400 }
401 }
402
403 void DolphinContextMenu::addShowMenuBarAction()
404 {
405 const KActionCollection* ac = m_mainWindow->actionCollection();
406 QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar));
407 if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) {
408 addSeparator();
409 addAction(showMenuBar);
410 }
411 }
412
413 bool DolphinContextMenu::placeExists(const KUrl& url) const
414 {
415 PlacesItemModel model;
416
417 const int count = model.count();
418 for (int i = 0; i < count; ++i) {
419 const KUrl placeUrl = model.placesItem(i)->url();
420 if (placeUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
421 return true;
422 }
423 }
424
425 return false;
426 }
427
428 QAction* DolphinContextMenu::createPasteAction()
429 {
430 QAction* action = 0;
431 const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir();
432 if (isDir && (m_selectedItems.count() == 1)) {
433 action = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste Into Folder"), this);
434 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
435 const KUrl::List pasteData = KUrl::List::fromMimeData(mimeData);
436 action->setEnabled(!pasteData.isEmpty() && selectedItemsProperties().supportsWriting());
437 connect(action, &QAction::triggered, m_mainWindow, &DolphinMainWindow::pasteIntoFolder);
438 } else {
439 action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
440 }
441
442 return action;
443 }
444
445 KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const
446 {
447 if (!m_selectedItemsProperties) {
448 m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems);
449 }
450 return *m_selectedItemsProperties;
451 }
452
453 KFileItem DolphinContextMenu::baseFileItem()
454 {
455 if (!m_baseFileItem) {
456 m_baseFileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_baseUrl);
457 }
458 return *m_baseFileItem;
459 }
460
461 void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions)
462 {
463 fileItemActions.setParentWidget(m_mainWindow);
464
465 // insert 'Open With...' action or sub menu
466 fileItemActions.addOpenWithActionsTo(this, "DesktopEntryName != 'dolphin'");
467
468 // insert 'Actions' sub menu
469 fileItemActions.addServiceActionsTo(this);
470 }
471
472 void DolphinContextMenu::addFileItemPluginActions()
473 {
474 KFileItemListProperties props;
475 if (m_selectedItems.isEmpty()) {
476 props.setItems(KFileItemList() << baseFileItem());
477 } else {
478 props = selectedItemsProperties();
479 }
480
481 QString commonMimeType = props.mimeType();
482 if (commonMimeType.isEmpty()) {
483 commonMimeType = QLatin1String("application/octet-stream");
484 }
485
486 const KService::List pluginServices = KMimeTypeTrader::self()->query(commonMimeType, "KFileItemAction/Plugin", "exist Library");
487 if (pluginServices.isEmpty()) {
488 return;
489 }
490
491 const KConfig config("kservicemenurc", KConfig::NoGlobals);
492 const KConfigGroup showGroup = config.group("Show");
493
494 foreach (const KService::Ptr& service, pluginServices) {
495 if (!showGroup.readEntry(service->desktopEntryName(), true)) {
496 // The plugin has been disabled
497 continue;
498 }
499
500 // Old API (kdelibs-4.6.0 only)
501 KFileItemActionPlugin* plugin = service->createInstance<KFileItemActionPlugin>();
502 if (plugin) {
503 plugin->setParent(this);
504 addActions(plugin->actions(props, m_mainWindow));
505 }
506 // New API (kdelibs >= 4.6.1)
507 KAbstractFileItemActionPlugin* abstractPlugin = service->createInstance<KAbstractFileItemActionPlugin>();
508 if (abstractPlugin) {
509 abstractPlugin->setParent(this);
510 addActions(abstractPlugin->actions(props, m_mainWindow));
511 }
512 }
513 }
514
515 void DolphinContextMenu::addVersionControlPluginActions()
516 {
517 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
518 const QList<QAction*> versionControlActions = view->versionControlActions(m_selectedItems);
519 if (!versionControlActions.isEmpty()) {
520 foreach (QAction* action, versionControlActions) {
521 addAction(action);
522 }
523 addSeparator();
524 }
525 }
526
527 void DolphinContextMenu::addCustomActions()
528 {
529 foreach (QAction* action, m_customActions) {
530 addAction(action);
531 }
532 }
533
534 #include "dolphincontextmenu.moc"