summaryrefslogtreecommitdiffstats
path: root/libkonq/konq_popupmenu.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libkonq/konq_popupmenu.cc')
-rw-r--r--libkonq/konq_popupmenu.cc1205
1 files changed, 1205 insertions, 0 deletions
diff --git a/libkonq/konq_popupmenu.cc b/libkonq/konq_popupmenu.cc
new file mode 100644
index 000000000..9238122c0
--- /dev/null
+++ b/libkonq/konq_popupmenu.cc
@@ -0,0 +1,1205 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 David Faure <faure@kde.org>
+ Copyright (C) 2001 Holger Freyther <freyther@yahoo.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qdir.h>
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <kbookmarkmanager.h>
+#include <kdebug.h>
+#include <krun.h>
+#include <kprotocolinfo.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <kglobalsettings.h>
+#include <kstandarddirs.h>
+#include <kxmlguifactory.h>
+#include <kxmlguibuilder.h>
+#include <kparts/componentfactory.h>
+
+#include <assert.h>
+
+#include <kfileshare.h>
+#include <kprocess.h>
+
+#include "kpropertiesdialog.h"
+#include "knewmenu.h"
+#include "konq_popupmenu.h"
+#include "konq_operations.h"
+#include <dcopclient.h>
+
+/*
+ Test cases:
+ iconview file: background
+ iconview file: file (with and without servicemenus)
+ iconview file: directory
+ iconview remote protocol (e.g. ftp: or fish:)
+ iconview trash:/
+ sidebar directory tree
+ sidebar Devices / Hard Disc
+ khtml background
+ khtml link
+ khtml image (www.kde.org RMB on K logo)
+ khtmlimage (same as above, then choose View image, then RMB)
+ selected text in khtml
+ embedded katepart
+ kdesktop folder
+ trash link on desktop
+ trashed file or directory
+ application .desktop file
+ Then the same after uninstalling kdeaddons/konq-plugins (kuick and arkplugin in particular)
+*/
+
+class KonqPopupMenuGUIBuilder : public KXMLGUIBuilder
+{
+public:
+ KonqPopupMenuGUIBuilder( QPopupMenu *menu )
+ : KXMLGUIBuilder( 0 )
+ {
+ m_menu = menu;
+ }
+ virtual ~KonqPopupMenuGUIBuilder()
+ {
+ }
+
+ virtual QWidget *createContainer( QWidget *parent, int index,
+ const QDomElement &element,
+ int &id )
+ {
+ if ( !parent && element.attribute( "name" ) == "popupmenu" )
+ return m_menu;
+
+ return KXMLGUIBuilder::createContainer( parent, index, element, id );
+ }
+
+ QPopupMenu *m_menu;
+};
+
+class KonqPopupMenu::KonqPopupMenuPrivate
+{
+public:
+ KonqPopupMenuPrivate() : m_parentWidget( 0 ),
+ m_itemFlags( KParts::BrowserExtension::DefaultPopupItems )
+ {
+ }
+ QString m_urlTitle;
+ QWidget *m_parentWidget;
+ KParts::BrowserExtension::PopupFlags m_itemFlags;
+};
+
+KonqPopupMenu::ProtocolInfo::ProtocolInfo()
+{
+ m_Reading = false;
+ m_Writing = false;
+ m_Deleting = false;
+ m_Moving = false;
+ m_TrashIncluded = false;
+}
+
+bool KonqPopupMenu::ProtocolInfo::supportsReading() const
+{
+ return m_Reading;
+}
+
+bool KonqPopupMenu::ProtocolInfo::supportsWriting() const
+{
+ return m_Writing;
+}
+
+bool KonqPopupMenu::ProtocolInfo::supportsDeleting() const
+{
+ return m_Deleting;
+}
+
+bool KonqPopupMenu::ProtocolInfo::supportsMoving() const
+{
+ return m_Moving;
+}
+
+bool KonqPopupMenu::ProtocolInfo::trashIncluded() const
+{
+ return m_TrashIncluded;
+}
+
+// This helper class stores the .desktop-file actions and the servicemenus
+// in order to support X-KDE-Priority and X-KDE-Submenu.
+class PopupServices
+{
+public:
+ ServiceList* selectList( const QString& priority, const QString& submenuName );
+
+ ServiceList builtin;
+ ServiceList user, userToplevel, userPriority;
+ QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus;
+};
+
+ServiceList* PopupServices::selectList( const QString& priority, const QString& submenuName )
+{
+ // we use the categories .desktop entry to define submenus
+ // if none is defined, we just pop it in the main menu
+ if (submenuName.isEmpty())
+ {
+ if (priority == "TopLevel")
+ {
+ return &userToplevel;
+ }
+ else if (priority == "Important")
+ {
+ return &userPriority;
+ }
+ }
+ else if (priority == "TopLevel")
+ {
+ return &(userToplevelSubmenus[submenuName]);
+ }
+ else if (priority == "Important")
+ {
+ return &(userPrioritySubmenus[submenuName]);
+ }
+ else
+ {
+ return &(userSubmenus[submenuName]);
+ }
+ return &user;
+}
+
+//////////////////
+
+KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
+ KURL viewURL,
+ KActionCollection & actions,
+ KNewMenu * newMenu,
+ bool showProperties )
+ : QPopupMenu( 0L, "konq_popupmenu" ),
+ m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ),
+ m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr)
+{
+ KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow;
+ init(0, kpf, KParts::BrowserExtension::DefaultPopupItems);
+}
+
+KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
+ KURL viewURL,
+ KActionCollection & actions,
+ KNewMenu * newMenu,
+ QWidget * parentWidget,
+ bool showProperties )
+ : QPopupMenu( parentWidget, "konq_popupmenu" ), m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ), m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr)
+{
+ KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow;
+ init(parentWidget, kpf, KParts::BrowserExtension::DefaultPopupItems);
+}
+
+KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
+ const KURL& viewURL,
+ KActionCollection & actions,
+ KNewMenu * newMenu,
+ QWidget * parentWidget,
+ KonqPopupFlags kpf,
+ KParts::BrowserExtension::PopupFlags flags)
+ : QPopupMenu( parentWidget, "konq_popupmenu" ), m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ), m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr)
+{
+ init(parentWidget, kpf, flags);
+}
+
+void KonqPopupMenu::init (QWidget * parentWidget, KonqPopupFlags kpf, KParts::BrowserExtension::PopupFlags flags)
+{
+ d = new KonqPopupMenuPrivate;
+ d->m_parentWidget = parentWidget;
+ d->m_itemFlags = flags;
+ setup(kpf);
+}
+
+int KonqPopupMenu::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus,
+ QDomElement& menu,
+ bool isBuiltin)
+{
+ int count = 0;
+ QMap<QString, ServiceList>::ConstIterator it;
+
+ for (it = submenus.begin(); it != submenus.end(); ++it)
+ {
+ if (it.data().isEmpty())
+ {
+ //avoid empty sub-menus
+ continue;
+ }
+
+ QDomElement actionSubmenu = m_doc.createElement( "menu" );
+ actionSubmenu.setAttribute( "name", "actions " + it.key() );
+ menu.appendChild( actionSubmenu );
+ QDomElement subtext = m_doc.createElement( "text" );
+ actionSubmenu.appendChild( subtext );
+ subtext.appendChild( m_doc.createTextNode( it.key() ) );
+ count += insertServices(it.data(), actionSubmenu, isBuiltin);
+ }
+
+ return count;
+}
+
+int KonqPopupMenu::insertServices(const ServiceList& list,
+ QDomElement& menu,
+ bool isBuiltin)
+{
+ static int id = 1000;
+ int count = 0;
+
+ ServiceList::const_iterator it = list.begin();
+ for( ; it != list.end(); ++it )
+ {
+ if ((*it).isEmpty())
+ {
+ if (!menu.firstChild().isNull() &&
+ menu.lastChild().toElement().tagName().lower() != "separator")
+ {
+ QDomElement separator = m_doc.createElement( "separator" );
+ menu.appendChild(separator);
+ }
+ continue;
+ }
+
+ if (isBuiltin || (*it).m_display == true)
+ {
+ QCString name;
+ name.setNum( id );
+ name.prepend( isBuiltin ? "builtinservice_" : "userservice_" );
+ KAction * act = new KAction( QString((*it).m_strName).replace('&',"&&"), 0,
+ this, SLOT( slotRunService() ),
+ &m_ownActions, name );
+
+ if ( !(*it).m_strIcon.isEmpty() )
+ {
+ QPixmap pix = SmallIcon( (*it).m_strIcon );
+ act->setIconSet( pix );
+ }
+
+ addAction( act, menu ); // Add to toplevel menu
+
+ m_mapPopupServices[ id++ ] = *it;
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+bool KonqPopupMenu::KIOSKAuthorizedAction(KConfig& cfg)
+{
+ if ( !cfg.hasKey( "X-KDE-AuthorizeAction") )
+ {
+ return true;
+ }
+
+ QStringList list = cfg.readListEntry("X-KDE-AuthorizeAction");
+ if (kapp && !list.isEmpty())
+ {
+ for(QStringList::ConstIterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+ if (!kapp->authorize((*it).stripWhiteSpace()))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void KonqPopupMenu::setup(KonqPopupFlags kpf)
+{
+ assert( m_lstItems.count() >= 1 );
+
+ m_ownActions.setWidget( this );
+
+ const bool bIsLink = (kpf & IsLink);
+ bool currentDir = false;
+ bool sReading = true;
+ bool sDeleting = ( d->m_itemFlags & KParts::BrowserExtension::NoDeletion ) == 0;
+ bool sMoving = sDeleting;
+ bool sWriting = sDeleting && m_lstItems.first()->isWritable();
+ m_sMimeType = m_lstItems.first()->mimetype();
+ QString mimeGroup = m_sMimeType.left(m_sMimeType.find('/'));
+ mode_t mode = m_lstItems.first()->mode();
+ bool isDirectory = S_ISDIR(mode);
+ bool bTrashIncluded = false;
+ bool mediaFiles = false;
+ bool isReallyLocal = m_lstItems.first()->isLocalFile();
+ bool isLocal = isReallyLocal
+ || m_lstItems.first()->url().protocol()=="media"
+ || m_lstItems.first()->url().protocol()=="system";
+ bool isTrashLink = false;
+ m_lstPopupURLs.clear();
+ int id = 0;
+ setFont(KGlobalSettings::menuFont());
+ m_pluginList.setAutoDelete( true );
+ m_ownActions.setHighlightingEnabled( true );
+
+ attrName = QString::fromLatin1( "name" );
+
+ prepareXMLGUIStuff();
+ m_builder = new KonqPopupMenuGUIBuilder( this );
+ m_factory = new KXMLGUIFactory( m_builder );
+
+ KURL url;
+ KFileItemListIterator it ( m_lstItems );
+ QStringList mimeTypeList;
+ // Check whether all URLs are correct
+ for ( ; it.current(); ++it )
+ {
+ url = (*it)->url();
+
+ // Build the list of URLs
+ m_lstPopupURLs.append( url );
+
+ // Determine if common mode among all URLs
+ if ( mode != (*it)->mode() )
+ mode = 0; // modes are different => reset to 0
+
+ // Determine if common mimetype among all URLs
+ if ( m_sMimeType != (*it)->mimetype() )
+ {
+ m_sMimeType = QString::null; // mimetypes are different => null
+
+ if ( mimeGroup != (*it)->mimetype().left((*it)->mimetype().find('/')))
+ mimeGroup = QString::null; // mimetype groups are different as well!
+ }
+
+ if ( mimeTypeList.findIndex( (*it)->mimetype() ) == -1 )
+ mimeTypeList << (*it)->mimetype();
+
+ if ( isReallyLocal && !url.isLocalFile() )
+ isReallyLocal = false;
+ if ( isLocal && !url.isLocalFile() && url.protocol() != "media" && url.protocol() != "system" )
+ isLocal = false;
+
+ if ( !bTrashIncluded && (
+ ( url.protocol() == "trash" && url.path().length() <= 1 )
+ || url.url() == "system:/trash" || url.url() == "system:/trash/" ) ) {
+ bTrashIncluded = true;
+ isLocal = false;
+ }
+
+ if ( sReading )
+ sReading = KProtocolInfo::supportsReading( url );
+
+ if ( sWriting )
+ sWriting = KProtocolInfo::supportsWriting( url ) && (*it)->isWritable();
+
+ if ( sDeleting )
+ sDeleting = KProtocolInfo::supportsDeleting( url );
+
+ if ( sMoving )
+ sMoving = KProtocolInfo::supportsMoving( url );
+ if ( (*it)->mimetype().startsWith("media/") )
+ mediaFiles = true;
+ }
+ url = m_sViewURL;
+ url.cleanPath();
+
+ //check if url is current directory
+ if ( m_lstItems.count() == 1 )
+ {
+ KURL firstPopupURL( m_lstItems.first()->url() );
+ firstPopupURL.cleanPath();
+ //kdDebug(1203) << "View path is " << url.url() << endl;
+ //kdDebug(1203) << "First popup path is " << firstPopupURL.url() << endl;
+ currentDir = firstPopupURL.equals( url, true /* ignore_trailing */ );
+ if ( isLocal && m_sMimeType == "application/x-desktop" ) {
+ KSimpleConfig cfg( firstPopupURL.path(), true );
+ cfg.setDesktopGroup();
+ isTrashLink = ( cfg.readEntry("Type") == "Link" && cfg.readEntry("URL") == "trash:/" );
+ }
+
+ if ( isTrashLink ) {
+ sDeleting = false;
+ }
+ }
+
+ m_info.m_Reading = sReading;
+ m_info.m_Writing = sWriting;
+ m_info.m_Deleting = sDeleting;
+ m_info.m_Moving = sMoving;
+ m_info.m_TrashIncluded = bTrashIncluded;
+
+ // isCurrentTrash: popup on trash:/ itself, or on the trash.desktop link
+ bool isCurrentTrash = ( m_lstItems.count() == 1 && bTrashIncluded ) || isTrashLink;
+ bool isIntoTrash = ( url.protocol() == "trash" || url.url().startsWith( "system:/trash" ) ) && !isCurrentTrash; // trashed file, not trash:/ itself
+ //kdDebug() << "isLocal=" << isLocal << " url=" << url << " isCurrentTrash=" << isCurrentTrash << " isIntoTrash=" << isIntoTrash << " bTrashIncluded=" << bTrashIncluded << endl;
+ bool isSingleMedium = m_lstItems.count() == 1 && mediaFiles;
+ clear();
+
+ //////////////////////////////////////////////////////////////////////////
+
+ KAction * act;
+
+ if (!isCurrentTrash)
+ addMerge( "konqueror" );
+
+ bool isKDesktop = QCString( kapp->name() ) == "kdesktop";
+ KAction *actNewWindow = 0;
+
+ if (( kpf & ShowProperties ) && isKDesktop &&
+ !kapp->authorize("editable_desktop_icons"))
+ {
+ kpf &= ~ShowProperties; // remove flag
+ }
+
+ // Either 'newview' is in the actions we're given (probably in the tabhandling group)
+ // or we need to insert it ourselves (e.g. for kdesktop). In the first case, actNewWindow must remain 0.
+ if ( ((kpf & ShowNewWindow) != 0) && sReading )
+ {
+ QString openStr = isKDesktop ? i18n( "&Open" ) : i18n( "Open in New &Window" );
+ actNewWindow = new KAction( openStr, "window_new", 0, this, SLOT( slotPopupNewView() ), &m_ownActions, "newview" );
+ }
+
+ if ( actNewWindow && !isKDesktop )
+ {
+ if (isCurrentTrash)
+ actNewWindow->setToolTip( i18n( "Open the trash in a new window" ) );
+ else if (isSingleMedium)
+ actNewWindow->setToolTip( i18n( "Open the medium in a new window") );
+ else
+ actNewWindow->setToolTip( i18n( "Open the document in a new window" ) );
+ }
+
+ if ( S_ISDIR(mode) && sWriting && !isCurrentTrash ) // A dir, and we can create things into it
+ {
+ if ( currentDir && m_pMenuNew ) // Current dir -> add the "new" menu
+ {
+ // As requested by KNewMenu :
+ m_pMenuNew->slotCheckUpToDate();
+ m_pMenuNew->setPopupFiles( m_lstPopupURLs );
+
+ addAction( m_pMenuNew );
+
+ addSeparator();
+ }
+ else
+ {
+ if (d->m_itemFlags & KParts::BrowserExtension::ShowCreateDirectory)
+ {
+ KAction *actNewDir = new KAction( i18n( "Create &Folder..." ), "folder_new", 0, this, SLOT( slotPopupNewDir() ), &m_ownActions, "newdir" );
+ addAction( actNewDir );
+ addSeparator();
+ }
+ }
+ } else if ( isIntoTrash ) {
+ // Trashed item, offer restoring
+ act = new KAction( i18n( "&Restore" ), 0, this, SLOT( slotPopupRestoreTrashedItems() ), &m_ownActions, "restore" );
+ addAction( act );
+ }
+
+ if (d->m_itemFlags & KParts::BrowserExtension::ShowNavigationItems)
+ {
+ if (d->m_itemFlags & KParts::BrowserExtension::ShowUp)
+ addAction( "up" );
+ addAction( "back" );
+ addAction( "forward" );
+ if (d->m_itemFlags & KParts::BrowserExtension::ShowReload)
+ addAction( "reload" );
+ addSeparator();
+ }
+
+ // "open in new window" is either provided by us, or by the tabhandling group
+ if (actNewWindow)
+ {
+ addAction( actNewWindow );
+ addSeparator();
+ }
+ addGroup( "tabhandling" ); // includes a separator
+
+ if ( !bIsLink )
+ {
+ if ( !currentDir && sReading ) {
+ if ( sDeleting ) {
+ addAction( "cut" );
+ }
+ addAction( "copy" );
+ }
+
+ if ( S_ISDIR(mode) && sWriting ) {
+ if ( currentDir )
+ addAction( "paste" );
+ else
+ addAction( "pasteto" );
+ }
+ if ( !currentDir )
+ {
+ if ( m_lstItems.count() == 1 && sMoving )
+ addAction( "rename" );
+
+ bool addTrash = false;
+ bool addDel = false;
+
+ if ( sMoving && !isIntoTrash && !isTrashLink )
+ addTrash = true;
+
+ if ( sDeleting ) {
+ if ( !isLocal )
+ addDel = true;
+ else if (KApplication::keyboardMouseState() & Qt::ShiftButton) {
+ addTrash = false;
+ addDel = true;
+ }
+ else {
+ KConfigGroup configGroup( kapp->config(), "KDE" );
+ if ( configGroup.readBoolEntry( "ShowDeleteCommand", false ) )
+ addDel = true;
+ }
+ }
+
+ if ( addTrash )
+ addAction( "trash" );
+ if ( addDel )
+ addAction( "del" );
+ }
+ }
+ if ( isCurrentTrash )
+ {
+ act = new KAction( i18n( "&Empty Trash Bin" ), "emptytrash", 0, this, SLOT( slotPopupEmptyTrashBin() ), &m_ownActions, "empytrash" );
+ KSimpleConfig trashConfig( "trashrc", true );
+ trashConfig.setGroup( "Status" );
+ act->setEnabled( !trashConfig.readBoolEntry( "Empty", true ) );
+ addAction( act );
+ }
+ addGroup( "editactions" );
+
+ if (d->m_itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) {
+ addMerge( 0 );
+ m_factory->addClient( this );
+ return;
+ }
+
+ if ( !isCurrentTrash && !isIntoTrash && (d->m_itemFlags & KParts::BrowserExtension::ShowBookmark))
+ {
+ addSeparator();
+ QString caption;
+ if (currentDir)
+ {
+ bool httpPage = (m_sViewURL.protocol().find("http", 0, false) == 0);
+ if (httpPage)
+ caption = i18n("&Bookmark This Page");
+ else
+ caption = i18n("&Bookmark This Location");
+ }
+ else if (S_ISDIR(mode))
+ caption = i18n("&Bookmark This Folder");
+ else if (bIsLink)
+ caption = i18n("&Bookmark This Link");
+ else
+ caption = i18n("&Bookmark This File");
+
+ act = new KAction( caption, "bookmark_add", 0, this, SLOT( slotPopupAddToBookmark() ), &m_ownActions, "bookmark_add" );
+ if (m_lstItems.count() > 1)
+ act->setEnabled(false);
+ if (kapp->authorizeKAction("bookmarks"))
+ addAction( act );
+ if (bIsLink)
+ addGroup( "linkactions" );
+ }
+
+ //////////////////////////////////////////////////////
+
+ const bool isSingleLocal = m_lstItems.count() == 1 && isLocal;
+ PopupServices s;
+ KURL urlForServiceMenu( m_lstItems.first()->url() );
+ if (isLocal && !isReallyLocal) { // media or system
+ bool dummy;
+ urlForServiceMenu = m_lstItems.first()->mostLocalURL(dummy);
+ }
+
+ // 1 - Look for builtin and user-defined services
+ if ( m_sMimeType == "application/x-desktop" && isSingleLocal ) // .desktop file
+ {
+ // get builtin services, like mount/unmount
+ s.builtin = KDEDesktopMimeType::builtinServices( urlForServiceMenu );
+ const QString path = urlForServiceMenu.path();
+ KSimpleConfig cfg( path, true );
+ cfg.setDesktopGroup();
+ const QString priority = cfg.readEntry("X-KDE-Priority");
+ const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
+ if ( cfg.readEntry("Type") == "Link" ) {
+ urlForServiceMenu = cfg.readEntry("URL");
+ // TODO: Do we want to make all the actions apply on the target
+ // of the .desktop file instead of the .desktop file itself?
+ }
+ ServiceList* list = s.selectList( priority, submenuName );
+ (*list) = KDEDesktopMimeType::userDefinedServices( path, cfg, urlForServiceMenu.isLocalFile() );
+ }
+
+ if ( sReading )
+ {
+
+ // 2 - Look for "servicesmenus" bindings (konqueror-specific user-defined services)
+
+ // first check the .directory if this is a directory
+ if (isDirectory && isSingleLocal)
+ {
+ QString dotDirectoryFile = urlForServiceMenu.path(1).append(".directory");
+ KSimpleConfig cfg( dotDirectoryFile, true );
+ cfg.setDesktopGroup();
+
+ if (KIOSKAuthorizedAction(cfg))
+ {
+ const QString priority = cfg.readEntry("X-KDE-Priority");
+ const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
+ ServiceList* list = s.selectList( priority, submenuName );
+ (*list) += KDEDesktopMimeType::userDefinedServices( dotDirectoryFile, cfg, true );
+ }
+ }
+
+ // findAllResources() also removes duplicates
+ const QStringList entries = KGlobal::dirs()->findAllResources("data",
+ "konqueror/servicemenus/*.desktop",
+ false /* recursive */,
+ true /* unique */);
+ QStringList::ConstIterator eIt = entries.begin();
+ const QStringList::ConstIterator eEnd = entries.end();
+ for (; eIt != eEnd; ++eIt )
+ {
+ KSimpleConfig cfg( *eIt, true );
+ cfg.setDesktopGroup();
+
+ if (!KIOSKAuthorizedAction(cfg))
+ {
+ continue;
+ }
+
+ if ( cfg.hasKey( "X-KDE-ShowIfRunning" ) )
+ {
+ const QString app = cfg.readEntry( "X-KDE-ShowIfRunning" );
+ if ( !kapp->dcopClient()->isApplicationRegistered( app.utf8() ) )
+ continue;
+ }
+ if ( cfg.hasKey( "X-KDE-ShowIfDcopCall" ) )
+ {
+ QString dcopcall = cfg.readEntry( "X-KDE-ShowIfDcopCall" );
+ const QCString app = dcopcall.section(' ', 0,0).utf8();
+
+ //if( !kapp->dcopClient()->isApplicationRegistered( app ))
+ // continue; //app does not exist so cannot send call
+
+ QByteArray dataToSend;
+ QDataStream dataStream(dataToSend, IO_WriteOnly);
+ dataStream << m_lstPopupURLs;
+
+ QCString replyType;
+ QByteArray replyData;
+ QCString object = dcopcall.section(' ', 1,-2).utf8();
+ QString function = dcopcall.section(' ', -1);
+ if(!function.endsWith("(KURL::List)")) {
+ kdWarning() << "Desktop file " << *eIt << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl;
+ continue; //Be safe.
+ }
+
+ if(!kapp->dcopClient()->call( app, object,
+ function.utf8(),
+ dataToSend, replyType, replyData, true, 1000))
+ continue;
+ if(replyType != "bool" || !replyData[0])
+ continue;
+
+ }
+ if ( cfg.hasKey( "X-KDE-Protocol" ) )
+ {
+ const QString protocol = cfg.readEntry( "X-KDE-Protocol" );
+ if ( protocol != urlForServiceMenu.protocol() )
+ continue;
+ }
+ else if ( cfg.hasKey( "X-KDE-Protocols" ) )
+ {
+ QStringList protocols = QStringList::split( "," , cfg.readEntry( "X-KDE-Protocols" ) );
+ if ( !protocols.contains( urlForServiceMenu.protocol() ) )
+ continue;
+ }
+ else if ( urlForServiceMenu.protocol() == "trash" || urlForServiceMenu.url().startsWith( "system:/trash" ) )
+ {
+ // Require servicemenus for the trash to ask for protocol=trash explicitely.
+ // Trashed files aren't supposed to be available for actions.
+ // One might want a servicemenu for trash.desktop itself though.
+ continue;
+ }
+
+ if ( cfg.hasKey( "X-KDE-Require" ) )
+ {
+ const QStringList capabilities = cfg.readListEntry( "X-KDE-Require" );
+ if ( capabilities.contains( "Write" ) && !sWriting )
+ continue;
+ }
+ if ( (cfg.hasKey( "Actions" ) || cfg.hasKey( "X-KDE-GetActionMenu") ) && cfg.hasKey( "ServiceTypes" ) )
+ {
+ const QStringList types = cfg.readListEntry( "ServiceTypes" );
+ const QStringList excludeTypes = cfg.readListEntry( "ExcludeServiceTypes" );
+ bool ok = false;
+
+ // check for exact matches or a typeglob'd mimetype if we have a mimetype
+ for (QStringList::ConstIterator it = types.begin();
+ it != types.end() && !ok;
+ ++it)
+ {
+ // first check if we have an all mimetype
+ bool checkTheMimetypes = false;
+ if (*it == "all/all" ||
+ *it == "allfiles" /*compat with KDE up to 3.0.3*/)
+ {
+ checkTheMimetypes = true;
+ }
+
+ // next, do we match all files?
+ if (!ok &&
+ !isDirectory &&
+ *it == "all/allfiles")
+ {
+ checkTheMimetypes = true;
+ }
+
+ // if we have a mimetype, see if we have an exact or a type globbed match
+ if (!ok &&
+ (!m_sMimeType.isEmpty() &&
+ *it == m_sMimeType) ||
+ (!mimeGroup.isEmpty() &&
+ ((*it).right(1) == "*" &&
+ (*it).left((*it).find('/')) == mimeGroup)))
+ {
+ checkTheMimetypes = true;
+ }
+
+ if (checkTheMimetypes)
+ {
+ ok = true;
+ for (QStringList::ConstIterator itex = excludeTypes.begin(); itex != excludeTypes.end(); ++itex)
+ {
+ if( ((*itex).right(1) == "*" && (*itex).left((*itex).find('/')) == mimeGroup) ||
+ ((*itex) == m_sMimeType) )
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( ok )
+ {
+ const QString priority = cfg.readEntry("X-KDE-Priority");
+ const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
+
+ ServiceList* list = s.selectList( priority, submenuName );
+ (*list) += KDEDesktopMimeType::userDefinedServices( *eIt, cfg, url.isLocalFile(), m_lstPopupURLs );
+ }
+ }
+ }
+
+ KTrader::OfferList offers;
+
+ if (kapp->authorizeKAction("openwith"))
+ {
+ QString constraint = "Type == 'Application' and DesktopEntryName != 'kfmclient' and DesktopEntryName != 'kfmclient_dir' and DesktopEntryName != 'kfmclient_html'";
+ QString subConstraint = " and '%1' in ServiceTypes";
+
+ QStringList::ConstIterator it = mimeTypeList.begin();
+ QStringList::ConstIterator end = mimeTypeList.end();
+ Q_ASSERT( it != end );
+ QString first = *it;
+ ++it;
+ while ( it != end ) {
+ constraint += subConstraint.arg( *it );
+ ++it;
+ }
+
+ offers = KTrader::self()->query( first, constraint );
+ }
+
+ //// Ok, we have everything, now insert
+
+ m_mapPopup.clear();
+ m_mapPopupServices.clear();
+ // "Open With..." for folders is really not very useful, especially for remote folders.
+ // (media:/something, or trash:/, or ftp://...)
+ if ( !isDirectory || isLocal )
+ {
+ if ( hasAction() )
+ addSeparator();
+
+ if ( !offers.isEmpty() )
+ {
+ // First block, app and preview offers
+ id = 1;
+
+ QDomElement menu = m_menuElement;
+
+ if ( offers.count() > 1 ) // submenu 'open with'
+ {
+ menu = m_doc.createElement( "menu" );
+ menu.setAttribute( "name", "openwith submenu" );
+ m_menuElement.appendChild( menu );
+ QDomElement text = m_doc.createElement( "text" );
+ menu.appendChild( text );
+ text.appendChild( m_doc.createTextNode( i18n("&Open With") ) );
+ }
+
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ for( ; it != offers.end(); it++ )
+ {
+ KService::Ptr service = (*it);
+
+ // Skip OnlyShowIn=Foo and NotShowIn=KDE entries,
+ // but still offer NoDisplay=true entries, that's the
+ // whole point of such desktop files. This is why we don't
+ // use service->noDisplay() here.
+ const QString onlyShowIn = service->property("OnlyShowIn", QVariant::String).toString();
+ if ( !onlyShowIn.isEmpty() ) {
+ const QStringList aList = QStringList::split(';', onlyShowIn);
+ if (!aList.contains("KDE"))
+ continue;
+ }
+ const QString notShowIn = service->property("NotShowIn", QVariant::String).toString();
+ if ( !notShowIn.isEmpty() ) {
+ const QStringList aList = QStringList::split(';', notShowIn);
+ if (aList.contains("KDE"))
+ continue;
+ }
+
+ QCString nam;
+ nam.setNum( id );
+
+ QString actionName( (*it)->name().replace("&", "&&") );
+ if ( menu == m_menuElement ) // no submenu -> prefix single offer
+ actionName = i18n( "Open with %1" ).arg( actionName );
+
+ act = new KAction( actionName, (*it)->pixmap( KIcon::Small ), 0,
+ this, SLOT( slotRunService() ),
+ &m_ownActions, nam.prepend( "appservice_" ) );
+ addAction( act, menu );
+
+ m_mapPopup[ id++ ] = *it;
+ }
+
+ QString openWithActionName;
+ if ( menu != m_menuElement ) // submenu
+ {
+ addSeparator( menu );
+ openWithActionName = i18n( "&Other..." );
+ }
+ else
+ {
+ openWithActionName = i18n( "&Open With..." );
+ }
+ KAction *openWithAct = new KAction( openWithActionName, 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
+ addAction( openWithAct, menu );
+ }
+ else // no app offers -> Open With...
+ {
+ act = new KAction( i18n( "&Open With..." ), 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
+ addAction( act );
+ }
+
+ }
+ addGroup( "preview" );
+ }
+
+ // Second block, builtin + user
+ QDomElement actionMenu = m_menuElement;
+ int userItemCount = 0;
+ if (s.user.count() + s.userSubmenus.count() +
+ s.userPriority.count() + s.userPrioritySubmenus.count() > 1)
+ {
+ // we have more than one item, so let's make a submenu
+ actionMenu = m_doc.createElement( "menu" );
+ actionMenu.setAttribute( "name", "actions submenu" );
+ m_menuElement.appendChild( actionMenu );
+ QDomElement text = m_doc.createElement( "text" );
+ actionMenu.appendChild( text );
+ text.appendChild( m_doc.createTextNode( i18n("Ac&tions") ) );
+ }
+
+ userItemCount += insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
+ userItemCount += insertServices(s.userPriority, actionMenu, false);
+
+ // see if we need to put a separator between our priority items and our regular items
+ if (userItemCount > 0 &&
+ (s.user.count() > 0 ||
+ s.userSubmenus.count() > 0 ||
+ s.builtin.count() > 0) &&
+ actionMenu.lastChild().toElement().tagName().lower() != "separator")
+ {
+ QDomElement separator = m_doc.createElement( "separator" );
+ actionMenu.appendChild(separator);
+ }
+
+ userItemCount += insertServicesSubmenus(s.userSubmenus, actionMenu, false);
+ userItemCount += insertServices(s.user, actionMenu, false);
+ userItemCount += insertServices(s.builtin, m_menuElement, true);
+
+ userItemCount += insertServicesSubmenus(s.userToplevelSubmenus, m_menuElement, false);
+ userItemCount += insertServices(s.userToplevel, m_menuElement, false);
+
+ if ( userItemCount > 0 )
+ {
+ addPendingSeparator();
+ }
+
+ if ( !isCurrentTrash && !isIntoTrash && !mediaFiles && sReading )
+ addPlugins(); // now it's time to add plugins
+
+ if ( KPropertiesDialog::canDisplay( m_lstItems ) && (kpf & ShowProperties) )
+ {
+ act = new KAction( i18n( "&Properties" ), 0, this, SLOT( slotPopupProperties() ),
+ &m_ownActions, "properties" );
+ addAction( act );
+ }
+
+ while ( !m_menuElement.lastChild().isNull() &&
+ m_menuElement.lastChild().toElement().tagName().lower() == "separator" )
+ m_menuElement.removeChild( m_menuElement.lastChild() );
+
+ if ( isDirectory && isLocal )
+ {
+ if ( KFileShare::authorization() == KFileShare::Authorized )
+ {
+ addSeparator();
+ act = new KAction( i18n("Share"), 0, this, SLOT( slotOpenShareFileDialog() ),
+ &m_ownActions, "sharefile" );
+ addAction( act );
+ }
+ }
+
+ addMerge( 0 );
+ //kdDebug() << k_funcinfo << domDocument().toString() << endl;
+
+ m_factory->addClient( this );
+}
+
+void KonqPopupMenu::slotOpenShareFileDialog()
+{
+ KPropertiesDialog* dlg = showPropertiesDialog();
+ dlg->showFileSharingPage();
+}
+
+KonqPopupMenu::~KonqPopupMenu()
+{
+ m_pluginList.clear();
+ delete m_factory;
+ delete m_builder;
+ delete d;
+ //kdDebug(1203) << "~KonqPopupMenu leave" << endl;
+}
+
+void KonqPopupMenu::setURLTitle( const QString& urlTitle )
+{
+ d->m_urlTitle = urlTitle;
+}
+
+void KonqPopupMenu::slotPopupNewView()
+{
+ KURL::List::ConstIterator it = m_lstPopupURLs.begin();
+ for ( ; it != m_lstPopupURLs.end(); it++ )
+ (void) new KRun(*it);
+}
+
+void KonqPopupMenu::slotPopupNewDir()
+{
+ if (m_lstPopupURLs.empty())
+ return;
+
+ KonqOperations::newDir(d->m_parentWidget, m_lstPopupURLs.first());
+}
+
+void KonqPopupMenu::slotPopupEmptyTrashBin()
+{
+ KonqOperations::emptyTrash();
+}
+
+void KonqPopupMenu::slotPopupRestoreTrashedItems()
+{
+ KonqOperations::restoreTrashedItems( m_lstPopupURLs );
+}
+
+void KonqPopupMenu::slotPopupOpenWith()
+{
+ KRun::displayOpenWithDialog( m_lstPopupURLs );
+}
+
+void KonqPopupMenu::slotPopupAddToBookmark()
+{
+ KBookmarkGroup root;
+ if ( m_lstPopupURLs.count() == 1 ) {
+ KURL url = m_lstPopupURLs.first();
+ QString title = d->m_urlTitle.isEmpty() ? url.prettyURL() : d->m_urlTitle;
+ root = m_pManager->addBookmarkDialog( url.prettyURL(), title );
+ }
+ else
+ {
+ root = m_pManager->root();
+ KURL::List::ConstIterator it = m_lstPopupURLs.begin();
+ for ( ; it != m_lstPopupURLs.end(); it++ )
+ root.addBookmark( m_pManager, (*it).prettyURL(), (*it) );
+ }
+ m_pManager->emitChanged( root );
+}
+
+void KonqPopupMenu::slotRunService()
+{
+ QCString senderName = sender()->name();
+ int id = senderName.mid( senderName.find( '_' ) + 1 ).toInt();
+
+ // Is it a usual service (application)
+ QMap<int,KService::Ptr>::Iterator it = m_mapPopup.find( id );
+ if ( it != m_mapPopup.end() )
+ {
+ KRun::run( **it, m_lstPopupURLs );
+ return;
+ }
+
+ // Is it a service specific to desktop entry files ?
+ QMap<int,KDEDesktopMimeType::Service>::Iterator it2 = m_mapPopupServices.find( id );
+ if ( it2 != m_mapPopupServices.end() )
+ {
+ KDEDesktopMimeType::executeService( m_lstPopupURLs, it2.data() );
+ }
+
+ return;
+}
+
+void KonqPopupMenu::slotPopupMimeType()
+{
+ KonqOperations::editMimeType( m_sMimeType );
+}
+
+void KonqPopupMenu::slotPopupProperties()
+{
+ (void)showPropertiesDialog();
+}
+
+KPropertiesDialog* KonqPopupMenu::showPropertiesDialog()
+{
+ // It may be that the kfileitem was created by hand
+ // (see KonqKfmIconView::slotMouseButtonPressed)
+ // In that case, we can get more precise info in the properties
+ // (like permissions) if we stat the URL.
+ if ( m_lstItems.count() == 1 )
+ {
+ KFileItem * item = m_lstItems.first();
+ if (item->entry().count() == 0) // this item wasn't listed by a slave
+ {
+ // KPropertiesDialog will use stat to get more info on the file
+ return new KPropertiesDialog( item->url(), d->m_parentWidget );
+ }
+ }
+ return new KPropertiesDialog( m_lstItems, d->m_parentWidget );
+}
+
+KAction *KonqPopupMenu::action( const QDomElement &element ) const
+{
+ QCString name = element.attribute( attrName ).ascii();
+ KAction *res = m_ownActions.action( name );
+
+ if ( !res )
+ res = m_actions.action( name );
+
+ if ( !res && m_pMenuNew && strcmp( name, m_pMenuNew->name() ) == 0 )
+ return m_pMenuNew;
+
+ return res;
+}
+
+KActionCollection *KonqPopupMenu::actionCollection() const
+{
+ return const_cast<KActionCollection *>( &m_ownActions );
+}
+
+QString KonqPopupMenu::mimeType() const
+{
+ return m_sMimeType;
+}
+
+KonqPopupMenu::ProtocolInfo KonqPopupMenu::protocolInfo() const
+{
+ return m_info;
+}
+
+void KonqPopupMenu::addPlugins()
+{
+ // search for Konq_PopupMenuPlugins inspired by simons kpropsdlg
+ //search for a plugin with the right protocol
+ KTrader::OfferList plugin_offers;
+ unsigned int pluginCount = 0;
+ plugin_offers = KTrader::self()->query( m_sMimeType.isNull() ? QString::fromLatin1( "all/all" ) : m_sMimeType, "'KonqPopupMenu/Plugin' in ServiceTypes");
+ if ( plugin_offers.isEmpty() )
+ return; // no plugins installed do not bother about it
+
+ KTrader::OfferList::ConstIterator iterator = plugin_offers.begin();
+ KTrader::OfferList::ConstIterator end = plugin_offers.end();
+
+ addGroup( "plugins" );
+ // travers the offerlist
+ for(; iterator != end; ++iterator, ++pluginCount ) {
+ //kdDebug() << (*iterator)->library() << endl;
+ KonqPopupMenuPlugin *plugin =
+ KParts::ComponentFactory::
+ createInstanceFromLibrary<KonqPopupMenuPlugin>( QFile::encodeName( (*iterator)->library() ),
+ this,
+ (*iterator)->name().latin1() );
+ if ( !plugin )
+ continue;
+ // This make the kuick plugin insert its stuff above "Properties"
+ QString pluginClientName = QString::fromLatin1( "Plugin%1" ).arg( pluginCount );
+ addMerge( pluginClientName );
+ plugin->domDocument().documentElement().setAttribute( "name", pluginClientName );
+ m_pluginList.append( plugin );
+ insertChildClient( plugin );
+ }
+
+ // ## Where is this used?
+ addMerge( "plugins" );
+}
+
+KURL KonqPopupMenu::url() const // ### should be viewURL()
+{
+ return m_sViewURL;
+}
+
+KFileItemList KonqPopupMenu::fileItemList() const
+{
+ return m_lstItems;
+}
+
+KURL::List KonqPopupMenu::popupURLList() const
+{
+ return m_lstPopupURLs;
+}
+
+/**
+ Plugin
+*/
+
+KonqPopupMenuPlugin::KonqPopupMenuPlugin( KonqPopupMenu *parent, const char *name )
+ : QObject( parent, name )
+{
+}
+
+KonqPopupMenuPlugin::~KonqPopupMenuPlugin()
+{
+}
+
+#include "konq_popupmenu.moc"