]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincontextmenu.cpp
use correct icons
[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 "dolphinsettings.h"
25 #include "dolphinview.h"
26 #include "editbookmarkdialog.h"
27
28 #include <kactioncollection.h>
29 #include <kbookmarkmanager.h>
30 #include <kbookmark.h>
31 #include <kdesktopfile.h>
32 #include <kglobal.h>
33 #include <kiconloader.h>
34 #include <kio/netaccess.h>
35 #include <kmenu.h>
36 #include <kmessagebox.h>
37 #include <kmimetypetrader.h>
38 #include <knewmenu.h>
39 #include <konqmimedata.h>
40 #include <konq_operations.h>
41 #include <klocale.h>
42 #include <kpropertiesdialog.h>
43 #include <krun.h>
44 #include <kstandardaction.h>
45 #include <kstandarddirs.h>
46
47 #include <QApplication>
48 #include <QClipboard>
49 #include <QDir>
50
51 DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent,
52 KFileItem* fileInfo,
53 const KUrl& baseUrl,
54 ViewType viewType) :
55 m_mainWindow(parent),
56 m_fileInfo(fileInfo),
57 m_baseUrl(baseUrl),
58 m_viewType(viewType),
59 m_context(NoContext)
60 {
61 if (viewType == ItemsView) {
62 // The context menu either accesses the URLs of the selected items
63 // or the items itself. To increase the performance both lists are cached.
64 DolphinView* view = m_mainWindow->activeView();
65 m_selectedUrls = view->selectedUrls();
66 m_selectedItems = view->selectedItems();
67 }
68 else if (fileInfo != 0) {
69 m_selectedUrls.append(fileInfo->url());
70 m_selectedItems.append(fileInfo);
71 }
72 }
73
74 DolphinContextMenu::~DolphinContextMenu()
75 {
76 }
77
78 void DolphinContextMenu::open()
79 {
80 // get the context information
81 if (m_baseUrl.protocol() == "trash") {
82 m_context |= TrashContext;
83 }
84
85 if (m_fileInfo != 0) {
86 m_context |= ItemContext;
87 // TODO: handle other use cases like devices + desktop files
88 }
89
90 // open the corresponding popup for the context
91 if (m_context & TrashContext) {
92 if (m_context & ItemContext) {
93 openTrashItemContextMenu();
94 }
95 else {
96 openTrashContextMenu();
97 }
98 }
99 else if (m_context & ItemContext) {
100 openItemContextMenu();
101 }
102 else {
103 Q_ASSERT(m_context == NoContext);
104 openViewportContextMenu();
105 }
106 }
107
108 void DolphinContextMenu::cut()
109 {
110 QMimeData* mimeData = new QMimeData();
111 KUrl::List kdeUrls;
112 kdeUrls.append(m_fileInfo->url());
113 KonqMimeData::populateMimeData(mimeData, kdeUrls, KUrl::List(), true);
114 QApplication::clipboard()->setMimeData(mimeData);
115 }
116
117 void DolphinContextMenu::copy()
118 {
119 QMimeData* mimeData = new QMimeData();
120 KUrl::List kdeUrls;
121 kdeUrls.append(m_fileInfo->url());
122 KonqMimeData::populateMimeData(mimeData, kdeUrls, KUrl::List(), false);
123 QApplication::clipboard()->setMimeData(mimeData);
124 }
125
126 void DolphinContextMenu::paste()
127 {
128 QClipboard* clipboard = QApplication::clipboard();
129 const QMimeData* mimeData = clipboard->mimeData();
130
131 const KUrl::List source = KUrl::List::fromMimeData(mimeData);
132 const KUrl& dest = m_fileInfo->url();
133 if (KonqMimeData::decodeIsCutSelection(mimeData)) {
134 KonqOperations::copy(m_mainWindow, KonqOperations::MOVE, source, dest);
135 clipboard->clear();
136 }
137 else {
138 KonqOperations::copy(m_mainWindow, KonqOperations::COPY, source, dest);
139 }
140 }
141
142 void DolphinContextMenu::rename()
143 {
144 // TODO
145 }
146
147 void DolphinContextMenu::moveToTrash()
148 {
149 // TODO
150 }
151
152 void DolphinContextMenu::deleteItem()
153 {
154 // TODO
155 }
156
157 void DolphinContextMenu::showProperties()
158 {
159 new KPropertiesDialog(m_fileInfo->url());
160 }
161
162 void DolphinContextMenu::openTrashContextMenu()
163 {
164 Q_ASSERT(m_context & TrashContext);
165
166 KMenu* popup = new KMenu(m_mainWindow);
167
168 QAction* emptyTrashAction = new QAction(KIcon("emptytrash"), i18n("Emtpy Trash"), popup);
169 KConfig trashConfig("trashrc", KConfig::OnlyLocal);
170 emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
171 popup->addAction(emptyTrashAction);
172
173 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
174 popup->addAction(propertiesAction);
175
176 if (popup->exec(QCursor::pos()) == emptyTrashAction) {
177 const QString text(i18n("Do you really want to empty the Trash? All items will get deleted."));
178 const bool del = KMessageBox::warningContinueCancel(m_mainWindow,
179 text,
180 QString(),
181 KGuiItem(i18n("Empty Trash"), KIcon("user-trash"))
182 ) == KMessageBox::Continue;
183 if (del) {
184 KonqOperations::emptyTrash(m_mainWindow);
185 }
186 }
187
188 popup->deleteLater();
189 }
190
191 void DolphinContextMenu::openTrashItemContextMenu()
192 {
193 Q_ASSERT(m_context & TrashContext);
194 Q_ASSERT(m_context & ItemContext);
195
196 KMenu* popup = new KMenu(m_mainWindow);
197
198 QAction* restoreAction = new QAction(i18n("Restore"), m_mainWindow);
199 popup->addAction(restoreAction);
200
201 QAction* deleteAction = m_mainWindow->actionCollection()->action("delete");
202 popup->addAction(deleteAction);
203
204 QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
205 popup->addAction(propertiesAction);
206
207 if (popup->exec(QCursor::pos()) == restoreAction) {
208 KonqOperations::restoreTrashedItems(m_selectedUrls, m_mainWindow);
209 }
210
211 popup->deleteLater();
212 }
213
214 void DolphinContextMenu::openItemContextMenu()
215 {
216 Q_ASSERT(m_fileInfo != 0);
217
218 KMenu* popup = new KMenu(m_mainWindow);
219 insertDefaultItemActions(popup);
220
221 popup->addSeparator();
222
223 // insert 'Bookmark this folder' entry if exactly one item is selected
224 QAction* bookmarkAction = 0;
225 if (m_fileInfo->isDir() && (m_selectedUrls.count() == 1)) {
226 bookmarkAction = popup->addAction(KIcon("bookmark-folder"), i18n("Bookmark folder"));
227 }
228
229 // Insert 'Open With...' sub menu
230 QVector<KService::Ptr> openWithVector;
231 const QList<QAction*> openWithActions = insertOpenWithItems(popup, openWithVector);
232
233 // Insert 'Actions' sub menu
234 QVector<KDEDesktopMimeType::Service> actionsVector;
235 const QList<QAction*> serviceActions = insertActionItems(popup, actionsVector);
236 popup->addSeparator();
237
238 // insert 'Properties...' entry
239 QAction* propertiesAction = 0;
240 if (m_viewType == SidebarView) {
241 propertiesAction = new QAction(i18n("Properties..."), this);
242 connect(this, SIGNAL(triggered()), this, SLOT(showProperties()));
243 }
244 else {
245 propertiesAction = m_mainWindow->actionCollection()->action("properties");
246 }
247 popup->addAction(propertiesAction);
248
249 QAction* activatedAction = popup->exec(QCursor::pos());
250
251 if ((bookmarkAction!= 0) && (activatedAction == bookmarkAction)) {
252 const KUrl selectedUrl(m_fileInfo->url());
253 KBookmark bookmark = EditBookmarkDialog::getBookmark(i18n("Add folder as bookmark"),
254 selectedUrl.fileName(),
255 selectedUrl,
256 "bookmark");
257 if (!bookmark.isNull()) {
258 KBookmarkManager* manager = DolphinSettings::instance().bookmarkManager();
259 KBookmarkGroup root = manager->root();
260 root.addBookmark(manager, bookmark);
261 manager->emitChanged(root);
262 }
263 }
264 else if (serviceActions.contains(activatedAction)) {
265 // one of the 'Actions' items has been selected
266 int id = serviceActions.indexOf(activatedAction);
267 KDEDesktopMimeType::executeService(m_selectedUrls, actionsVector[id]);
268 }
269 else if (openWithActions.contains(activatedAction)) {
270 // one of the 'Open With' items has been selected
271 if (openWithActions.last() == activatedAction) {
272 // the item 'Other...' has been selected
273 KRun::displayOpenWithDialog(m_selectedUrls, m_mainWindow);
274 }
275 else {
276 int id = openWithActions.indexOf(activatedAction);
277 KService::Ptr servicePtr = openWithVector[id];
278 KRun::run(*servicePtr, m_selectedUrls, m_mainWindow);
279 }
280 }
281
282 openWithVector.clear();
283 actionsVector.clear();
284 popup->deleteLater();
285 }
286
287 void DolphinContextMenu::openViewportContextMenu()
288 {
289 Q_ASSERT(m_fileInfo == 0);
290 KMenu* popup = new KMenu(m_mainWindow);
291
292 // setup 'Create New' menu
293 KNewMenu* newMenu = m_mainWindow->newMenu();
294 newMenu->slotCheckUpToDate();
295 newMenu->setPopupFiles(m_baseUrl);
296 popup->addMenu(newMenu->menu());
297 popup->addSeparator();
298
299 QAction* pasteAction = m_mainWindow->actionCollection()->action(KStandardAction::stdName(KStandardAction::Paste));
300 popup->addAction(pasteAction);
301
302 // setup 'View Mode' menu
303 KMenu* viewModeMenu = new KMenu(i18n("View Mode"));
304
305 QAction* iconsMode = m_mainWindow->actionCollection()->action("icons");
306 viewModeMenu->addAction(iconsMode);
307
308 QAction* detailsMode = m_mainWindow->actionCollection()->action("details");
309 viewModeMenu->addAction(detailsMode);
310
311 QAction* previewsMode = m_mainWindow->actionCollection()->action("previews");
312 viewModeMenu->addAction(previewsMode);
313
314 popup->addMenu(viewModeMenu);
315 popup->addSeparator();
316
317 QAction* bookmarkAction = popup->addAction(KIcon("bookmark-folder"), i18n("Bookmark this folder"));
318 popup->addSeparator();
319
320 QAction* propertiesAction = popup->addAction(i18n("Properties..."));
321
322 QAction* activatedAction = popup->exec(QCursor::pos());
323 if (activatedAction == propertiesAction) {
324 new KPropertiesDialog(m_mainWindow->activeView()->url());
325 }
326 else if (activatedAction == bookmarkAction) {
327 const KUrl& url = m_mainWindow->activeView()->url();
328 KBookmark bookmark = EditBookmarkDialog::getBookmark(i18n("Add folder as bookmark"),
329 url.fileName(),
330 url,
331 "bookmark");
332 if (!bookmark.isNull()) {
333 KBookmarkManager* manager = DolphinSettings::instance().bookmarkManager();
334 KBookmarkGroup root = manager->root();
335 root.addBookmark(manager, bookmark);
336 manager->emitChanged(root);
337 }
338 }
339
340 popup->deleteLater();
341 }
342
343 void DolphinContextMenu::insertDefaultItemActions(KMenu* popup)
344 {
345 Q_ASSERT(popup != 0);
346 const KActionCollection* collection = m_mainWindow->actionCollection();
347 const bool insertSidebarActions = (m_viewType == SidebarView);
348
349 // insert 'Cut', 'Copy' and 'Paste'
350 QAction* cutAction = 0;
351 QAction* copyAction = 0;
352 QAction* pasteAction = 0;
353 if (insertSidebarActions) {
354 cutAction = new QAction(KIcon("edit-cut"), i18n("Cut"), this);
355 connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
356
357 copyAction = new QAction(KIcon("edit-copy"), i18n("Copy"), this);
358 connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
359
360 const QAction* menuPasteAction = collection->action(KStandardAction::stdName(KStandardAction::Paste));
361 pasteAction = new QAction(KIcon("edit-paste"), menuPasteAction->text(), this);
362 pasteAction->setEnabled(menuPasteAction->isEnabled());
363 connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
364 }
365 else {
366 cutAction = collection->action(KStandardAction::stdName(KStandardAction::Cut));
367 copyAction = collection->action(KStandardAction::stdName(KStandardAction::Copy));
368 pasteAction = collection->action(KStandardAction::stdName(KStandardAction::Paste));
369 }
370
371 popup->addAction(cutAction);
372 popup->addAction(copyAction);
373 popup->addAction(pasteAction);
374 popup->addSeparator();
375
376 // insert 'Rename'
377 QAction* renameAction = 0;
378 if (insertSidebarActions) {
379 renameAction = new QAction(i18n("Rename"), this);
380 connect(renameAction, SIGNAL(triggered()), this, SLOT(rename()));
381 }
382 else {
383 renameAction = collection->action("rename");
384 }
385 popup->addAction(renameAction);
386
387 // insert 'Move to Trash' and (optionally) 'Delete'
388 const KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::NoGlobals);
389 const KConfigGroup kdeConfig(globalConfig, "KDE");
390 bool showDeleteCommand = kdeConfig.readEntry("ShowDeleteCommand", false);
391 const KUrl& url = insertSidebarActions ? m_fileInfo->url():
392 m_mainWindow->activeView()->url();
393 if (url.isLocalFile()) {
394 QAction* moveToTrashAction = 0;
395 if (insertSidebarActions) {
396 moveToTrashAction = new QAction(KIcon("edit-trash"), i18n("Move To Trash"), this);
397 connect(moveToTrashAction, SIGNAL(triggered()), this, SLOT(moveToTrash()));
398 }
399 else {
400 moveToTrashAction = collection->action("move_to_trash");
401 }
402 popup->addAction(moveToTrashAction);
403 }
404 else {
405 showDeleteCommand = true;
406 }
407
408 if (showDeleteCommand) {
409 QAction* deleteAction = 0;
410 if (insertSidebarActions) {
411 deleteAction = new QAction(KIcon("edit-delete"), i18n("Delete"), this);
412 connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteItem()));
413 }
414 else {
415 deleteAction = collection->action("delete");
416 }
417 popup->addAction(deleteAction);
418 }
419 }
420
421 QList<QAction*> DolphinContextMenu::insertOpenWithItems(KMenu* popup,
422 QVector<KService::Ptr>& openWithVector)
423 {
424 // Parts of the following code have been taken
425 // from the class KonqOperations located in
426 // libqonq/konq_operations.h of Konqueror.
427 // (Copyright (C) 2000 David Faure <faure@kde.org>)
428
429 // Prepare 'Open With' sub menu. Usually a sub menu is created, where all applications
430 // are listed which are registered to open the item. As last entry "Other..." will be
431 // attached which allows to select a custom application. If no applications are registered
432 // no sub menu is created at all, only "Open With..." will be offered.
433 bool insertOpenWithItems = true;
434 const QString contextMimeType(m_fileInfo->mimetype());
435
436 QListIterator<KFileItem*> mimeIt(m_selectedItems);
437 while (insertOpenWithItems && mimeIt.hasNext()) {
438 KFileItem* item = mimeIt.next();
439 insertOpenWithItems = (contextMimeType == item->mimetype());
440 }
441
442 QList<QAction*> openWithActions;
443 if (insertOpenWithItems) {
444 // fill the 'Open with' sub menu with application types
445 const KMimeType::Ptr mimePtr = KMimeType::findByUrl(m_fileInfo->url());
446 KService::List offers = KMimeTypeTrader::self()->query(mimePtr->name(),
447 "Application",
448 "Type == 'Application'");
449 if (offers.count() > 0) {
450 KService::List::Iterator it;
451 KMenu* openWithMenu = new KMenu(i18n("Open With"));
452 for(it = offers.begin(); it != offers.end(); ++it) {
453 // The offer list from the KTrader returns duplicate
454 // application entries. Although this seems to be a configuration
455 // problem outside the scope of Dolphin, duplicated entries just
456 // will be skipped here.
457 const QString appName((*it)->name());
458 if (!containsEntry(openWithMenu, appName)) {
459 const KIcon icon((*it)->icon());
460 QAction* action = openWithMenu->addAction(icon, appName);
461 openWithVector.append(*it);
462 openWithActions << action;
463 }
464 }
465
466 openWithMenu->addSeparator();
467 QAction* action = openWithMenu->addAction(i18n("&Other..."));
468
469 openWithActions << action;
470 popup->addMenu(openWithMenu);
471 }
472 else {
473 // No applications are registered, hence just offer
474 // a "Open With..." item instead of a sub menu containing
475 // only one entry.
476 QAction* action = popup->addAction(i18n("Open With..."));
477 openWithActions << action;
478 }
479 }
480 else {
481 // At least one of the selected items has a different MIME type. In this case
482 // just show a disabled "Open With..." entry.
483 QAction* action = popup->addAction(i18n("Open With..."));
484 action->setEnabled(false);
485 }
486
487 return openWithActions;
488 }
489
490 QList<QAction*> DolphinContextMenu::insertActionItems(KMenu* popup,
491 QVector<KDEDesktopMimeType::Service>& actionsVector)
492 {
493 // Parts of the following code have been taken
494 // from the class KonqOperations located in
495 // libqonq/konq_operations.h of Konqueror.
496 // (Copyright (C) 2000 David Faure <faure@kde.org>)
497
498 KMenu* actionsMenu = new KMenu(i18n("Actions"));
499
500 QList<QAction*> serviceActions;
501
502 QStringList dirs = KGlobal::dirs()->findDirs("data", "dolphin/servicemenus/");
503
504 KMenu* menu = 0;
505 for (QStringList::ConstIterator dirIt = dirs.begin(); dirIt != dirs.end(); ++dirIt) {
506 QDir dir(*dirIt);
507 QStringList filters;
508 filters << "*.desktop";
509 dir.setNameFilters(filters);
510 QStringList entries = dir.entryList(QDir::Files);
511
512 for (QStringList::ConstIterator entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) {
513 KConfigGroup cfg(KSharedConfig::openConfig( *dirIt + *entryIt, KConfig::OnlyLocal), "Desktop Entry" );
514 if ((cfg.hasKey("Actions") || cfg.hasKey("X-KDE-GetActionMenu")) && cfg.hasKey("ServiceTypes")) {
515 //const QStringList types = cfg.readListEntry("ServiceTypes");
516 QStringList types;
517 types = cfg.readEntry("ServiceTypes", types);
518 for (QStringList::ConstIterator it = types.begin(); it != types.end(); ++it) {
519 // check whether the mime type is equal or whether the
520 // mimegroup (e. g. image/*) is supported
521
522 bool insert = false;
523 if ((*it) == "all/allfiles") {
524 // The service type is valid for all files, but not for directories.
525 // Check whether the selected items only consist of files...
526 QListIterator<KFileItem*> mimeIt(m_selectedItems);
527 insert = true;
528 while (insert && mimeIt.hasNext()) {
529 KFileItem* item = mimeIt.next();
530 insert = !item->isDir();
531 }
532 }
533
534 if (!insert) {
535 // Check whether the MIME types of all selected files match
536 // to the mimetype of the service action. As soon as one MIME
537 // type does not match, no service menu is shown at all.
538 QListIterator<KFileItem*> mimeIt(m_selectedItems);
539 insert = true;
540 while (insert && mimeIt.hasNext()) {
541 KFileItem* item = mimeIt.next();
542 const QString mimeType(item->mimetype());
543 const QString mimeGroup(mimeType.left(mimeType.indexOf('/')));
544
545 insert = (*it == mimeType) ||
546 ((*it).right(1) == "*") &&
547 ((*it).left((*it).indexOf('/')) == mimeGroup);
548 }
549 }
550
551 if (insert) {
552 menu = actionsMenu;
553
554 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
555 if (!submenuName.isEmpty()) {
556 menu = new KMenu(submenuName);
557 actionsMenu->addMenu(menu);
558 }
559
560 Q3ValueList<KDEDesktopMimeType::Service> userServices =
561 KDEDesktopMimeType::userDefinedServices(*dirIt + *entryIt, true);
562
563 Q3ValueList<KDEDesktopMimeType::Service>::Iterator serviceIt;
564 for (serviceIt = userServices.begin(); serviceIt != userServices.end(); ++serviceIt) {
565 KDEDesktopMimeType::Service service = (*serviceIt);
566 if (!service.m_strIcon.isEmpty()) {
567 QAction* action = menu->addAction(SmallIcon(service.m_strIcon),
568 service.m_strName);
569 serviceActions << action;
570 }
571 else {
572 QAction *action = menu->addAction(service.m_strName);
573 serviceActions << action;
574 }
575 actionsVector.append(service);
576 }
577 }
578 }
579 }
580 }
581 }
582
583 const int itemsCount = actionsMenu->actions().count();
584 if (itemsCount == 0) {
585 // no actions are available at all, hence show the "Actions"
586 // submenu disabled
587 actionsMenu->setEnabled(false);
588 }
589
590 if (itemsCount == 1) {
591 // Exactly one item is available. Instead of showing a sub menu with
592 // only one item, show the item directly in the root menu.
593 if (menu == actionsMenu) {
594 // The item is an action, hence show the action in the root menu.
595 const QList<QAction*> actions = actionsMenu->actions();
596 Q_ASSERT(actions.count() == 1);
597
598 const QString text = actions[0]->text();
599 const QIcon icon = actions[0]->icon();
600 if (icon.isNull()) {
601 QAction* action = popup->addAction(text);
602 serviceActions.clear();
603 serviceActions << action;
604 }
605 else {
606 QAction* action = popup->addAction(icon, text);
607 serviceActions.clear();
608 serviceActions << action;
609 }
610 }
611 else {
612 // The item is a sub menu, hence show the sub menu in the root menu.
613 popup->addMenu(menu);
614 }
615 actionsMenu->deleteLater();
616 actionsMenu = 0;
617 }
618 else {
619 popup->addMenu(actionsMenu);
620 }
621
622 return serviceActions;
623 }
624
625 bool DolphinContextMenu::containsEntry(const KMenu* menu,
626 const QString& entryName) const
627 {
628 Q_ASSERT(menu != 0);
629
630 const QList<QAction*> list = menu->actions();
631 const uint count = list.count();
632 for (uint i = 0; i < count; ++i) {
633 const QAction* action = list.at(i);
634 if (action->text() == entryName) {
635 return true;
636 }
637 }
638
639 return false;
640 }
641
642 #include "dolphincontextmenu.moc"