Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / lib / konq / konq_popupmenu.cpp
blobbf4ff33de51eb746b660ca9a6353268206a6f453
1 /* This file is part of the KDE project
2 Copyright (C) 1998-2008 David Faure <faure@kde.org>
3 Copyright (C) 2001 Holger Freyther <freyther@yahoo.com>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library 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 GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "konq_popupmenu.h"
22 #include "konq_popupmenuplugin.h"
23 #include "konq_popupmenuinformation.h"
24 #include "konq_copytomenu.h"
25 #include "konq_menuactions.h"
26 #include "kpropertiesdialog.h"
27 #include "knewmenu.h"
28 #include "konq_operations.h"
30 #include <klocale.h>
31 #include <kbookmarkmanager.h>
32 #include <kbookmarkdialog.h>
33 #include <kdebug.h>
34 #include <krun.h>
35 #include <kprotocolmanager.h>
36 #include <kicon.h>
37 #include <kiconloader.h>
38 #include <kinputdialog.h>
39 #include <kglobalsettings.h>
40 #include <kmimetypetrader.h>
41 #include <kstandarddirs.h>
42 #include <kconfiggroup.h>
43 #include <kdesktopfile.h>
44 #include <kfileshare.h>
45 #include <kauthorized.h>
46 #include <kglobal.h>
47 #include <kacceleratormanager.h>
49 #include <QFileInfo>
52 Test cases:
53 iconview file: background
54 iconview file: file (with and without servicemenus)
55 iconview file: directory
56 iconview remote protocol (e.g. ftp: or fish:)
57 iconview trash:/
58 sidebar directory tree
59 sidebar Devices / Hard Disc
60 khtml background
61 khtml link
62 khtml image (www.kde.org RMB on K logo)
63 khtmlimage (same as above, then choose View image, then RMB)
64 selected text in khtml
65 embedded katepart
66 folder on the desktop
67 trash link on the desktop
68 trashed file or directory
69 application .desktop file
70 Then the same after uninstalling kdeaddons/konq-plugins (arkplugin in particular)
73 class KonqPopupMenuPrivate
75 public:
76 KonqPopupMenuPrivate(KonqPopupMenu* qq, KActionCollection & actions)
77 : q(qq),
78 m_itemFlags(KParts::BrowserExtension::DefaultPopupItems),
79 m_actions(actions),
80 m_ownActions(static_cast<QWidget *>(0))
83 void addNamedAction(const QString& name);
84 void addGroup(const QString& name);
85 void addPlugins();
86 void init(KonqPopupMenu::Flags kpf, KParts::BrowserExtension::PopupFlags itemFlags);
88 void slotPopupNewDir();
89 void slotPopupNewView();
90 void slotPopupEmptyTrashBin();
91 void slotPopupRestoreTrashedItems();
92 void slotPopupAddToBookmark();
93 void slotPopupMimeType();
94 void slotPopupProperties();
95 void slotOpenShareFileDialog();
97 KonqPopupMenu* q;
98 QString m_urlTitle;
99 KParts::BrowserExtension::PopupFlags m_itemFlags;
100 KNewMenu *m_pMenuNew;
101 KUrl m_sViewURL;
102 KonqPopupMenuInformation m_popupMenuInfo;
103 KonqMenuActions m_menuActions;
104 KonqCopyToMenu m_copyToMenu;
105 KBookmarkManager* m_bookmarkManager;
106 KActionCollection &m_actions;
107 KActionCollection m_ownActions; // TODO connect to statusbar for help on actions
108 KParts::BrowserExtension::ActionGroupMap m_actionGroups;
111 //////////////////
113 KonqPopupMenu::KonqPopupMenu(const KFileItemList &items,
114 const KUrl& viewURL,
115 KActionCollection & actions,
116 KNewMenu * newMenu,
117 Flags kpf,
118 KParts::BrowserExtension::PopupFlags flags,
119 QWidget * parentWidget,
120 KBookmarkManager *mgr,
121 const KParts::BrowserExtension::ActionGroupMap& actionGroups)
122 : QMenu(parentWidget),
123 d(new KonqPopupMenuPrivate(this, actions))
125 d->m_actionGroups = actionGroups;
126 d->m_pMenuNew = newMenu;
127 d->m_sViewURL = viewURL;
128 d->m_bookmarkManager = mgr;
129 d->m_popupMenuInfo.setItems(items);
130 d->m_popupMenuInfo.setParentWidget(parentWidget);
131 d->init(kpf, flags);
133 KAcceleratorManager::manage(this);
136 void KonqPopupMenuPrivate::addNamedAction(const QString& name)
138 QAction* act = m_actions.action(name);
139 if (act)
140 q->addAction(act);
143 void KonqPopupMenuPrivate::init(KonqPopupMenu::Flags kpf, KParts::BrowserExtension::PopupFlags flags)
145 m_ownActions.setObjectName("KonqPopupMenu::m_ownActions");
146 m_itemFlags = flags;
147 q->setFont(KGlobalSettings::menuFont());
149 Q_ASSERT(m_popupMenuInfo.items().count() >= 1);
151 bool bTrashIncluded = false;
153 const KFileItemList lstItems = m_popupMenuInfo.items();
154 KFileItemList::const_iterator it = lstItems.constBegin();
155 const KFileItemList::const_iterator kend = lstItems.constEnd();
156 for ( ; it != kend; ++it )
158 const KUrl url = (*it).url();
159 if ( !bTrashIncluded && (
160 ( url.protocol() == "trash" && url.path().length() <= 1 ) ) ) {
161 bTrashIncluded = true;
165 const bool isDirectory = m_popupMenuInfo.isDirectory();
166 const bool sReading = m_popupMenuInfo.capabilities().supportsReading();
167 bool sDeleting = (m_itemFlags & KParts::BrowserExtension::NoDeletion) == 0
168 && m_popupMenuInfo.capabilities().supportsDeleting();
169 const bool sWriting = m_popupMenuInfo.capabilities().supportsWriting();
170 const bool sMoving = sDeleting && m_popupMenuInfo.capabilities().supportsMoving();
171 const bool isLocal = m_popupMenuInfo.capabilities().isLocal();
173 KUrl url = m_sViewURL;
174 url.cleanPath();
176 bool isTrashLink = false;
177 bool isCurrentTrash = false;
178 bool currentDir = false;
180 //check if url is current directory
181 if ( lstItems.count() == 1 )
183 KFileItem firstPopupItem( lstItems.first() );
184 KUrl firstPopupURL( firstPopupItem.url() );
185 firstPopupURL.cleanPath();
186 //kDebug(1203) << "View path is " << url.url();
187 //kDebug(1203) << "First popup path is " << firstPopupURL.url();
188 currentDir = firstPopupURL.equals( url, KUrl::CompareWithoutTrailingSlash );
189 if ( firstPopupItem.isDesktopFile() ) {
190 KDesktopFile desktopFile( firstPopupItem.localPath() );
191 const KConfigGroup cfg = desktopFile.desktopGroup();
192 isTrashLink = ( cfg.readEntry("Type") == "Link" && cfg.readEntry("URL") == "trash:/" );
195 if (isTrashLink) {
196 sDeleting = false;
199 // isCurrentTrash: popup on trash:/ itself, or on the trash.desktop link
200 isCurrentTrash = (firstPopupURL.protocol() == "trash" && firstPopupURL.path().length() <= 1)
201 || isTrashLink;
204 const bool isIntoTrash = (url.protocol() == "trash") && !isCurrentTrash; // trashed file, not trash:/ itself
206 const bool bIsLink = (m_itemFlags & KParts::BrowserExtension::IsLink);
208 //kDebug() << "isLocal=" << isLocal << " url=" << url << " isCurrentTrash=" << isCurrentTrash << " isIntoTrash=" << isIntoTrash << " bTrashIncluded=" << bTrashIncluded;
210 //////////////////////////////////////////////////////////////////////////
212 addGroup( "topactions" ); // used e.g. for ShowMenuBar. includes a separator at the end
214 QAction * act;
216 QAction *actNewWindow = 0;
218 #if 0 // TODO in the desktop code itself.
219 if (( flags & KParts::BrowserExtension::ShowProperties ) && isOnDesktop &&
220 !KAuthorized::authorizeKAction("editable_desktop_icons"))
222 flags &= ~KParts::BrowserExtension::ShowProperties; // remove flag
224 #endif
226 // Either 'newview' is in the actions we're given (probably in the tabhandling group)
227 // or we need to insert it ourselves (e.g. for the desktop).
228 // In the first case, actNewWindow must remain 0.
229 if ( ((kpf & KonqPopupMenu::ShowNewWindow) != 0) && sReading )
231 const QString openStr = i18n("&Open");
232 actNewWindow = m_ownActions.addAction( "newview" );
233 actNewWindow->setIcon( KIcon("window-new") );
234 actNewWindow->setText( openStr );
235 QObject::connect(actNewWindow, SIGNAL(triggered()), q, SLOT(slotPopupNewView()));
238 if ( isDirectory && sWriting && !isCurrentTrash ) // A dir, and we can create things into it
240 const bool mkdirRequested = m_itemFlags & KParts::BrowserExtension::ShowCreateDirectory;
241 if ( (currentDir || mkdirRequested) && m_pMenuNew ) // Current dir -> add the "new" menu
243 // As requested by KNewMenu :
244 m_pMenuNew->slotCheckUpToDate();
245 m_pMenuNew->setPopupFiles(m_popupMenuInfo.urlList());
247 q->addAction( m_pMenuNew );
248 q->addSeparator();
250 else if (mkdirRequested)
252 KAction *actNewDir = m_ownActions.addAction( "newdir" );
253 actNewDir->setIcon( KIcon("folder-new") );
254 actNewDir->setText( i18n( "Create &Folder..." ) );
255 QObject::connect(actNewDir, SIGNAL(triggered()), q, SLOT(slotPopupNewDir()));
256 q->addAction( actNewDir );
257 q->addSeparator();
259 } else if ( isIntoTrash ) {
260 // Trashed item, offer restoring
261 act = m_ownActions.addAction( "restore" );
262 act->setText( i18n( "&Restore" ) );
263 QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotPopupRestoreTrashedItems()));
264 q->addAction(act);
267 if (m_itemFlags & KParts::BrowserExtension::ShowNavigationItems)
269 if (m_itemFlags & KParts::BrowserExtension::ShowUp)
270 addNamedAction( "go_up" );
271 addNamedAction( "go_back" );
272 addNamedAction( "go_forward" );
273 if (m_itemFlags & KParts::BrowserExtension::ShowReload)
274 addNamedAction( "reload" );
275 q->addSeparator();
278 // "open in new window" is either provided by us, or by the tabhandling group
279 if (actNewWindow) {
280 q->addAction(actNewWindow);
281 q->addSeparator();
283 addGroup( "tabhandling" ); // includes a separator at the end
285 if (m_itemFlags & KParts::BrowserExtension::ShowUrlOperations) {
286 if ( !currentDir && sReading ) {
287 if ( sDeleting ) {
288 addNamedAction( "cut" );
290 addNamedAction( "copy" );
293 if ( isDirectory && sWriting ) {
294 if ( currentDir )
295 addNamedAction( "paste" );
296 else
297 addNamedAction( "pasteto" );
300 if ( isCurrentTrash )
302 act = m_ownActions.addAction( "emptytrash" );
303 act->setIcon( KIcon("trash-empty") );
304 act->setText( i18n( "&Empty Trash Bin" ) );
305 KConfig trashConfig( "trashrc", KConfig::SimpleConfig);
306 act->setEnabled( !trashConfig.group("Status").readEntry( "Empty", true ) );
307 QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotPopupEmptyTrashBin()));
308 q->addAction(act);
311 // This is used by KHTML, see khtml_popupmenu.rc (copy, selectAll, searchProvider etc.)
312 // and by DolphinPart (rename, trash, delete)
313 addGroup( "editactions" );
315 if (m_itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) {
316 // OK, we have to stop here.
318 // Anything else that is provided by the part
319 addGroup( "partactions" );
320 return;
323 if ( !isCurrentTrash && !isIntoTrash && (m_itemFlags & KParts::BrowserExtension::ShowBookmark))
325 QString caption;
326 if (currentDir)
328 const bool httpPage = m_sViewURL.protocol().startsWith("http", Qt::CaseInsensitive);
329 if (httpPage)
330 caption = i18n("&Bookmark This Page");
331 else
332 caption = i18n("&Bookmark This Location");
334 else if (isDirectory)
335 caption = i18n("&Bookmark This Folder");
336 else if (bIsLink)
337 caption = i18n("&Bookmark This Link");
338 else
339 caption = i18n("&Bookmark This File");
341 act = m_ownActions.addAction( "bookmark_add" );
342 act->setIcon( KIcon("bookmark-new") );
343 act->setText( caption );
344 QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotPopupAddToBookmark()));
345 if (lstItems.count() > 1)
346 act->setEnabled(false);
347 if (KAuthorized::authorizeKAction("bookmarks"))
348 q->addAction( act );
349 if (bIsLink)
350 addGroup( "linkactions" ); // see khtml
353 // "Open With" actions
355 m_menuActions.setPopupMenuInfo(m_popupMenuInfo);
357 if ( sReading ) {
358 m_menuActions.addOpenWithActionsTo(q, "DesktopEntryName != 'kfmclient' and DesktopEntryName != 'kfmclient_dir' and DesktopEntryName != 'kfmclient_html'");
360 QList<QAction *> previewActions = m_actionGroups.value("preview");
361 if (!previewActions.isEmpty()) {
362 if (previewActions.count() == 1) {
363 q->addAction(previewActions.first());
364 } else {
365 QMenu* subMenu = new QMenu(i18n("Preview In"), q);
366 subMenu->menuAction()->setObjectName("preview_submenu"); // for the unittest
367 q->addMenu(subMenu);
368 subMenu->addActions(previewActions);
373 // Second block, builtin + user
374 m_menuActions.addActionsTo(q);
376 q->addSeparator();
378 // CopyTo/MoveTo menus
379 if (m_itemFlags & KParts::BrowserExtension::ShowUrlOperations) {
380 m_copyToMenu.setItems(lstItems);
381 m_copyToMenu.setReadOnly(sMoving == false);
382 m_copyToMenu.addActionsTo(q);
383 q->addSeparator();
386 if ( !isCurrentTrash && !isIntoTrash && sReading )
387 addPlugins(); // now it's time to add plugins
389 if ( (m_itemFlags & KParts::BrowserExtension::ShowProperties) && KPropertiesDialog::canDisplay( lstItems ) ) {
390 act = m_ownActions.addAction( "properties" );
391 act->setText( i18n( "&Properties" ) );
392 QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotPopupProperties()));
393 q->addAction(act);
396 while ( !q->actions().isEmpty() &&
397 q->actions().last()->isSeparator() )
398 delete q->actions().last();
400 if ( isDirectory && isLocal ) {
401 if ( KFileShare::authorization() == KFileShare::Authorized ) {
402 q->addSeparator();
403 act = m_ownActions.addAction( "sharefile" );
404 act->setText( i18n("Share") );
405 QObject::connect(act, SIGNAL(triggered()), q, SLOT(slotOpenShareFileDialog()));
406 q->addAction(act);
410 // Anything else that is provided by the part
411 addGroup( "partactions" );
414 void KonqPopupMenuPrivate::slotOpenShareFileDialog()
416 KPropertiesDialog* dlg = new KPropertiesDialog( m_popupMenuInfo.items(), m_popupMenuInfo.parentWidget() );
417 dlg->showFileSharingPage();
418 dlg->exec();
421 KonqPopupMenu::~KonqPopupMenu()
423 delete d;
424 //kDebug(1203) << "~KonqPopupMenu leave";
427 void KonqPopupMenu::setURLTitle( const QString& urlTitle )
429 d->m_urlTitle = urlTitle;
432 void KonqPopupMenuPrivate::slotPopupNewView()
434 Q_FOREACH(const KUrl& url, m_popupMenuInfo.urlList()) {
435 (void) new KRun(url, m_popupMenuInfo.parentWidget());
439 void KonqPopupMenuPrivate::slotPopupNewDir()
441 if (m_popupMenuInfo.urlList().empty())
442 return;
444 KonqOperations::newDir(m_popupMenuInfo.parentWidget(), m_popupMenuInfo.urlList().first());
447 void KonqPopupMenuPrivate::slotPopupEmptyTrashBin()
449 KonqOperations::emptyTrash(m_popupMenuInfo.parentWidget());
452 void KonqPopupMenuPrivate::slotPopupRestoreTrashedItems()
454 KonqOperations::restoreTrashedItems(m_popupMenuInfo.urlList(), m_popupMenuInfo.parentWidget());
457 void KonqPopupMenuPrivate::slotPopupAddToBookmark()
459 KBookmarkGroup root;
460 if (m_popupMenuInfo.urlList().count() == 1) {
461 const KUrl url = m_popupMenuInfo.urlList().first();
462 const QString title = m_urlTitle.isEmpty() ? url.prettyUrl() : m_urlTitle;
463 KBookmarkDialog dlg(m_bookmarkManager, m_popupMenuInfo.parentWidget());
464 dlg.addBookmark(title, url.url());
466 else
468 root = m_bookmarkManager->root();
469 Q_FOREACH(const KUrl& url, m_popupMenuInfo.urlList()) {
470 root.addBookmark(url.prettyUrl(), url);
472 m_bookmarkManager->emitChanged(root);
476 void KonqPopupMenuPrivate::slotPopupMimeType()
478 KonqOperations::editMimeType(m_popupMenuInfo.mimeType(), m_popupMenuInfo.parentWidget());
481 void KonqPopupMenuPrivate::slotPopupProperties()
483 KPropertiesDialog::showDialog(m_popupMenuInfo.items(), m_popupMenuInfo.parentWidget(), false);
486 void KonqPopupMenuPrivate::addGroup(const QString& name)
488 QList<QAction *> actions = m_actionGroups.value(name);
489 q->addActions(actions);
492 void KonqPopupMenuPrivate::addPlugins()
494 const QString commonMimeType = m_popupMenuInfo.mimeType();
495 const KService::List plugin_offers = KMimeTypeTrader::self()->query(commonMimeType.isEmpty() ? QLatin1String("application/octet-stream") : commonMimeType, "KonqPopupMenu/Plugin", "exist Library");
497 KService::List::ConstIterator iterator = plugin_offers.begin();
498 const KService::List::ConstIterator end = plugin_offers.end();
499 for(; iterator != end; ++iterator) {
500 //kDebug() << (*iterator)->name() << (*iterator)->library();
501 KonqPopupMenuPlugin *plugin = (*iterator)->createInstance<KonqPopupMenuPlugin>(q);
502 if (!plugin)
503 continue;
504 plugin->setup(&m_ownActions, m_popupMenuInfo, q);
508 #include "konq_popupmenu.moc"