]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincontextmenu.cpp
Get rid of obsolete DolphinPlacesModel
[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
28 #include <KActionCollection>
29 #include <KDesktopFile>
30 #include <kfileitemactionplugin.h>
31 #include <kabstractfileitemactionplugin.h>
32 #include <KFileItemActions>
33 #include <KFileItemListProperties>
34 #include <KFilePlacesModel>
35 #include <KGlobal>
36 #include <KIconLoader>
37 #include <KIO/NetAccess>
38 #include <KMenu>
39 #include <KMenuBar>
40 #include <KMessageBox>
41 #include <KMimeTypeTrader>
42 #include <KModifierKeyInfo>
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 <QApplication>
54 #include <QClipboard>
55 #include <QDir>
56
57 #include "views/dolphinview.h"
58 #include "views/viewmodecontroller.h"
59
60 K_GLOBAL_STATIC(KModifierKeyInfo, m_keyInfo)
61
62 DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent,
63 const QPoint& pos,
64 const KFileItem& fileInfo,
65 const KUrl& baseUrl) :
66 QObject(parent),
67 m_pos(pos),
68 m_mainWindow(parent),
69 m_fileInfo(fileInfo),
70 m_baseUrl(baseUrl),
71 m_baseFileItem(0),
72 m_selectedItems(),
73 m_selectedItemsProperties(0),
74 m_context(NoContext),
75 m_copyToMenu(parent),
76 m_customActions(),
77 m_popup(0),
78 m_command(None),
79 m_shiftPressed(false),
80 m_removeAction(0)
81 {
82 // The context menu either accesses the URLs of the selected items
83 // or the items itself. To increase the performance both lists are cached.
84 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
85 m_selectedItems = view->selectedItems();
86
87 if (m_keyInfo) {
88 if (m_keyInfo->isKeyPressed(Qt::Key_Shift) || m_keyInfo->isKeyLatched(Qt::Key_Shift)) {
89 m_shiftPressed = true;
90 }
91 connect(m_keyInfo, SIGNAL(keyPressed(Qt::Key,bool)),
92 this, SLOT(slotKeyModifierPressed(Qt::Key,bool)));
93 }
94
95 m_removeAction = new QAction(this);
96 connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRemoveActionTriggered()));
97
98 m_popup = new KMenu(m_mainWindow);
99 }
100
101 DolphinContextMenu::~DolphinContextMenu()
102 {
103 delete m_selectedItemsProperties;
104 m_selectedItemsProperties = 0;
105
106 delete m_popup;
107 m_popup = 0;
108 }
109
110 void DolphinContextMenu::setCustomActions(const QList<QAction*>& actions)
111 {
112 m_customActions = actions;
113 }
114
115 DolphinContextMenu::Command DolphinContextMenu::open()
116 {
117 // get the context information
118 if (m_baseUrl.protocol() == QLatin1String("trash")) {
119 m_context |= TrashContext;
120 }
121
122 if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) {
123 m_context |= ItemContext;
124 // TODO: handle other use cases like devices + desktop files
125 }
126
127 // open the corresponding popup for the context
128 if (m_context & TrashContext) {
129 if (m_context & ItemContext) {
130 openTrashItemContextMenu();
131 } else {
132 openTrashContextMenu();
133 }
134 } else if (m_context & ItemContext) {
135 openItemContextMenu();
136 } else {
137 Q_ASSERT(m_context == NoContext);
138 openViewportContextMenu();
139 }
140
141 return m_command;
142 }
143
144 void DolphinContextMenu::initializeModifierKeyInfo()
145 {
146 // Access m_keyInfo, so that it gets instantiated by
147 // K_GLOBAL_STATIC
148 KModifierKeyInfo* keyInfo = m_keyInfo;
149 Q_UNUSED(keyInfo);
150 }
151
152 void DolphinContextMenu::slotKeyModifierPressed(Qt::Key key, bool pressed)
153 {
154 m_shiftPressed = (key == Qt::Key_Shift) && pressed;
155 updateRemoveAction();
156 }
157
158 void DolphinContextMenu::slotRemoveActionTriggered()
159 {
160 const KActionCollection* collection = m_mainWindow->actionCollection();
161 if (moveToTrash()) {
162 collection->action("move_to_trash")->trigger();
163 } else {
164 collection->action("delete")->trigger();
165 }
166 }
167
168 void DolphinContextMenu::openTrashContextMenu()
169 {
170 Q_ASSERT(m_context & TrashContext);
171
172 QAction* emptyTrashAction = new QAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"), m_popup);
173 KConfig trashConfig("trashrc", KConfig::SimpleConfig);
174 emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
175 m_popup->addAction(emptyTrashAction);
176
177 addCustomActions();
178
179 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
180 m_popup->addAction(propertiesAction);
181
182 addShowMenuBarAction();
183
184 if (m_popup->exec(m_pos) == emptyTrashAction) {
185 KonqOperations::emptyTrash(m_mainWindow);
186 }
187 }
188
189 void DolphinContextMenu::openTrashItemContextMenu()
190 {
191 Q_ASSERT(m_context & TrashContext);
192 Q_ASSERT(m_context & ItemContext);
193
194 QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow);
195 m_popup->addAction(restoreAction);
196
197 QAction* deleteAction = m_mainWindow->actionCollection()->action("delete");
198 m_popup->addAction(deleteAction);
199
200 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
201 m_popup->addAction(propertiesAction);
202
203 if (m_popup->exec(m_pos) == restoreAction) {
204 KUrl::List selectedUrls;
205 foreach (const KFileItem &item, m_selectedItems) {
206 selectedUrls.append(item.url());
207 }
208
209 KonqOperations::restoreTrashedItems(selectedUrls, m_mainWindow);
210 }
211 }
212
213 void DolphinContextMenu::openItemContextMenu()
214 {
215 Q_ASSERT(!m_fileInfo.isNull());
216
217 QAction* openParentInNewWindowAction = 0;
218 QAction* openParentInNewTabAction = 0;
219 QAction* addToPlacesAction = 0;
220 if (m_selectedItems.count() == 1) {
221 if (m_fileInfo.isDir()) {
222 // setup 'Create New' menu
223 DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow);
224 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
225 newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
226 newFileMenu->checkUpToDate();
227 newFileMenu->setPopupFiles(m_fileInfo.url());
228 newFileMenu->setEnabled(selectedItemsProperties().supportsWriting());
229 connect(newFileMenu, SIGNAL(fileCreated(KUrl)), newFileMenu, SLOT(deleteLater()));
230 connect(newFileMenu, SIGNAL(directoryCreated(KUrl)), newFileMenu, SLOT(deleteLater()));
231
232 KMenu* menu = newFileMenu->menu();
233 menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
234 menu->setIcon(KIcon("document-new"));
235 m_popup->addMenu(menu);
236 m_popup->addSeparator();
237
238 // insert 'Open in new window' and 'Open in new tab' entries
239 m_popup->addAction(m_mainWindow->actionCollection()->action("open_in_new_window"));
240 m_popup->addAction(m_mainWindow->actionCollection()->action("open_in_new_tab"));
241
242 // insert 'Add to Places' entry
243 if (!placeExists(m_fileInfo.url())) {
244 addToPlacesAction = m_popup->addAction(KIcon("bookmark-new"),
245 i18nc("@action:inmenu Add selected folder to places",
246 "Add to Places"));
247 }
248
249 m_popup->addSeparator();
250 } else if (m_baseUrl.protocol().contains("search")) {
251 openParentInNewWindowAction = new QAction(KIcon("window-new"),
252 i18nc("@action:inmenu",
253 "Open Path in New Window"),
254 this);
255 m_popup->addAction(openParentInNewWindowAction);
256
257 openParentInNewTabAction = new QAction(KIcon("tab-new"),
258 i18nc("@action:inmenu",
259 "Open Path in New Tab"),
260 this);
261 m_popup->addAction(openParentInNewTabAction);
262
263 m_popup->addSeparator();
264 }
265 }
266
267 insertDefaultItemActions();
268
269 m_popup->addSeparator();
270
271 KFileItemActions fileItemActions;
272 fileItemActions.setItemListProperties(selectedItemsProperties());
273 addServiceActions(fileItemActions);
274
275 addFileItemPluginActions();
276
277 addVersionControlPluginActions();
278
279 // insert 'Copy To' and 'Move To' sub menus
280 if (GeneralSettings::showCopyMoveMenu()) {
281 m_copyToMenu.setItems(m_selectedItems);
282 m_copyToMenu.setReadOnly(!selectedItemsProperties().supportsWriting());
283 m_copyToMenu.addActionsTo(m_popup);
284 }
285
286 // insert 'Properties...' entry
287 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
288 m_popup->addAction(propertiesAction);
289
290 QAction* activatedAction = m_popup->exec(m_pos);
291 if (activatedAction) {
292 if (activatedAction == addToPlacesAction) {
293 const KUrl selectedUrl(m_fileInfo.url());
294 if (selectedUrl.isValid()) {
295 KFilePlacesModel model;
296 model.addPlace(placesName(selectedUrl), selectedUrl);
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 m_popup->addMenu(newFileMenu->menu());
315 m_popup->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 m_popup->addAction(m_mainWindow->actionCollection()->action("new_window"));
320 m_popup->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 = m_popup->addAction(KIcon("bookmark-new"),
326 i18nc("@action:inmenu Add current folder to places", "Add to Places"));
327 }
328
329 m_popup->addSeparator();
330
331 QAction* pasteAction = createPasteAction();
332 m_popup->addAction(pasteAction);
333 m_popup->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 m_popup->addAction(propertiesAction);
349
350 addShowMenuBarAction();
351
352 QAction* action = m_popup->exec(m_pos);
353 if (addToPlacesAction && (action == addToPlacesAction)) {
354 const KUrl url = m_mainWindow->activeViewContainer()->url();
355 if (url.isValid()) {
356 KFilePlacesModel model;
357 model.addPlace(placesName(url), url);
358 }
359 }
360 }
361
362 void DolphinContextMenu::insertDefaultItemActions()
363 {
364 const KActionCollection* collection = m_mainWindow->actionCollection();
365
366 // Insert 'Cut', 'Copy' and 'Paste'
367 m_popup->addAction(collection->action(KStandardAction::name(KStandardAction::Cut)));
368 m_popup->addAction(collection->action(KStandardAction::name(KStandardAction::Copy)));
369 m_popup->addAction(createPasteAction());
370
371 m_popup->addSeparator();
372
373 // Insert 'Rename'
374 QAction* renameAction = collection->action("rename");
375 m_popup->addAction(renameAction);
376
377 // Insert 'Move to Trash' and/or 'Delete'
378 if (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false)) {
379 m_popup->addAction(collection->action("move_to_trash"));
380 m_popup->addAction(collection->action("delete"));
381 } else {
382 m_popup->addAction(m_removeAction);
383 updateRemoveAction();
384 }
385 }
386
387 void DolphinContextMenu::addShowMenuBarAction()
388 {
389 const KActionCollection* ac = m_mainWindow->actionCollection();
390 QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar));
391 if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) {
392 m_popup->addSeparator();
393 m_popup->addAction(showMenuBar);
394 }
395 }
396
397 QString DolphinContextMenu::placesName(const KUrl& url) const
398 {
399 QString name = url.fileName();
400 if (name.isEmpty()) {
401 name = url.host();
402 }
403 return name;
404 }
405
406 bool DolphinContextMenu::placeExists(const KUrl& url) const
407 {
408 KFilePlacesModel model;
409
410 const int count = model.rowCount();
411 for (int i = 0; i < count; ++i) {
412 const QModelIndex index = model.index(i, 0);
413 if (url.equals(model.url(index), KUrl::CompareWithoutTrailingSlash)) {
414 return true;
415 }
416 }
417 return false;
418 }
419
420 QAction* DolphinContextMenu::createPasteAction()
421 {
422 QAction* action = 0;
423 const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir();
424 if (isDir && (m_selectedItems.count() == 1)) {
425 action = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste Into Folder"), this);
426 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
427 const KUrl::List pasteData = KUrl::List::fromMimeData(mimeData);
428 action->setEnabled(!pasteData.isEmpty() && selectedItemsProperties().supportsWriting());
429 connect(action, SIGNAL(triggered()), m_mainWindow, SLOT(pasteIntoFolder()));
430 } else {
431 action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
432 }
433
434 return action;
435 }
436
437 KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const
438 {
439 if (!m_selectedItemsProperties) {
440 m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems);
441 }
442 return *m_selectedItemsProperties;
443 }
444
445 KFileItem DolphinContextMenu::baseFileItem()
446 {
447 if (!m_baseFileItem) {
448 m_baseFileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_baseUrl);
449 }
450 return *m_baseFileItem;
451 }
452
453 void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions)
454 {
455 fileItemActions.setParentWidget(m_mainWindow);
456
457 // insert 'Open With...' action or sub menu
458 fileItemActions.addOpenWithActionsTo(m_popup, "DesktopEntryName != 'dolphin'");
459
460 // insert 'Actions' sub menu
461 fileItemActions.addServiceActionsTo(m_popup);
462 }
463
464 void DolphinContextMenu::addFileItemPluginActions()
465 {
466 KFileItemListProperties props;
467 if (m_selectedItems.isEmpty()) {
468 props.setItems(KFileItemList() << baseFileItem());
469 } else {
470 props = selectedItemsProperties();
471 }
472
473 QString commonMimeType = props.mimeType();
474 if (commonMimeType.isEmpty()) {
475 commonMimeType = QLatin1String("application/octet-stream");
476 }
477
478 const KService::List pluginServices = KMimeTypeTrader::self()->query(commonMimeType, "KFileItemAction/Plugin", "exist Library");
479 if (pluginServices.isEmpty()) {
480 return;
481 }
482
483 const KConfig config("kservicemenurc", KConfig::NoGlobals);
484 const KConfigGroup showGroup = config.group("Show");
485
486 foreach (const KSharedPtr<KService>& service, pluginServices) {
487 if (!showGroup.readEntry(service->desktopEntryName(), true)) {
488 // The plugin has been disabled
489 continue;
490 }
491
492 // Old API (kdelibs-4.6.0 only)
493 KFileItemActionPlugin* plugin = service->createInstance<KFileItemActionPlugin>();
494 if (plugin) {
495 plugin->setParent(m_popup);
496 m_popup->addActions(plugin->actions(props, m_mainWindow));
497 }
498 // New API (kdelibs >= 4.6.1)
499 KAbstractFileItemActionPlugin* abstractPlugin = service->createInstance<KAbstractFileItemActionPlugin>();
500 if (abstractPlugin) {
501 abstractPlugin->setParent(m_popup);
502 m_popup->addActions(abstractPlugin->actions(props, m_mainWindow));
503 }
504 }
505 }
506
507 void DolphinContextMenu::addVersionControlPluginActions()
508 {
509 const DolphinView* view = m_mainWindow->activeViewContainer()->view();
510 const QList<QAction*> versionControlActions = view->versionControlActions(m_selectedItems);
511 if (!versionControlActions.isEmpty()) {
512 foreach (QAction* action, versionControlActions) {
513 m_popup->addAction(action);
514 }
515 m_popup->addSeparator();
516 }
517 }
518
519 void DolphinContextMenu::addCustomActions()
520 {
521 foreach (QAction* action, m_customActions) {
522 m_popup->addAction(action);
523 }
524 }
525
526 void DolphinContextMenu::updateRemoveAction()
527 {
528 const KActionCollection* collection = m_mainWindow->actionCollection();
529
530 // Using m_removeAction->setText(action->text()) does not apply the &-shortcut.
531 // This is only done until the original action has been shown at least once. To
532 // bypass this issue, the text and &-shortcut is applied manually.
533 const QAction* action = 0;
534 if (moveToTrash()) {
535 action = collection->action("move_to_trash");
536 m_removeAction->setText(i18nc("@action:inmenu", "&Move to Trash"));
537 } else {
538 action = collection->action("delete");
539 m_removeAction->setText(i18nc("@action:inmenu", "&Delete"));
540 }
541 m_removeAction->setIcon(action->icon());
542 m_removeAction->setShortcuts(action->shortcuts());
543 }
544
545 bool DolphinContextMenu::moveToTrash() const
546 {
547 return !m_shiftPressed;
548 }
549
550 #include "dolphincontextmenu.moc"