konq_popupmenu.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 David Faure <faure@kde.org>
00003    Copyright (C) 2001 Holger Freyther <freyther@yahoo.com>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <qdir.h>
00022 
00023 #include <klocale.h>
00024 #include <kapplication.h>
00025 #include <kbookmarkmanager.h>
00026 #include <kdebug.h>
00027 #include <krun.h>
00028 #include <kprotocolinfo.h>
00029 #include <kiconloader.h>
00030 #include <kinputdialog.h>
00031 #include <kglobalsettings.h>
00032 #include <kstandarddirs.h>
00033 #include <kxmlguifactory.h>
00034 #include <kxmlguibuilder.h>
00035 #include <kparts/componentfactory.h>
00036 
00037 #include <assert.h>
00038 
00039 #include <kfileshare.h>
00040 #include <kprocess.h>
00041 
00042 #include "kpropertiesdialog.h"
00043 #include "knewmenu.h"
00044 #include "konq_popupmenu.h"
00045 #include "konq_operations.h"
00046 #include <dcopclient.h>
00047 
00048 /*
00049  Test cases:
00050   iconview file: background
00051   iconview file: file (with and without servicemenus)
00052   iconview file: directory
00053   iconview remote protocol (e.g. ftp: or fish:)
00054   iconview trash:/
00055   sidebar directory tree
00056   sidebar Devices / Hard Disc
00057   khtml background
00058   khtml link
00059   khtml image (www.kde.org RMB on K logo)
00060   khtmlimage (same as above, then choose View image, then RMB)
00061   selected text in khtml
00062   embedded katepart
00063   kdesktop folder
00064   trash link on desktop
00065   trashed file or directory
00066   application .desktop file
00067  Then the same after uninstalling kdeaddons/konq-plugins (kuick and arkplugin in particular)
00068 */
00069 
00070 class KonqPopupMenuGUIBuilder : public KXMLGUIBuilder
00071 {
00072 public:
00073   KonqPopupMenuGUIBuilder( QPopupMenu *menu )
00074   : KXMLGUIBuilder( 0 )
00075   {
00076     m_menu = menu;
00077   }
00078   virtual ~KonqPopupMenuGUIBuilder()
00079   {
00080   }
00081 
00082   virtual QWidget *createContainer( QWidget *parent, int index,
00083           const QDomElement &element,
00084           int &id )
00085   {
00086     if ( !parent && element.attribute( "name" ) == "popupmenu" )
00087       return m_menu;
00088 
00089     return KXMLGUIBuilder::createContainer( parent, index, element, id );
00090   }
00091 
00092   QPopupMenu *m_menu;
00093 };
00094 
00095 class KonqPopupMenu::KonqPopupMenuPrivate
00096 {
00097 public:
00098   KonqPopupMenuPrivate() : m_parentWidget( 0 ),
00099                            m_itemFlags( KParts::BrowserExtension::DefaultPopupItems )
00100   {
00101   }
00102   QString m_urlTitle;
00103   QWidget *m_parentWidget;
00104   KParts::BrowserExtension::PopupFlags m_itemFlags;
00105 };
00106 
00107 KonqPopupMenu::ProtocolInfo::ProtocolInfo()
00108 {
00109   m_Reading = false;
00110   m_Writing = false;
00111   m_Deleting = false;
00112   m_Moving = false;
00113   m_TrashIncluded = false;
00114 }
00115 
00116 bool KonqPopupMenu::ProtocolInfo::supportsReading() const
00117 {
00118   return m_Reading;
00119 }
00120 
00121 bool KonqPopupMenu::ProtocolInfo::supportsWriting() const
00122 {
00123   return m_Writing;
00124 }
00125 
00126 bool KonqPopupMenu::ProtocolInfo::supportsDeleting() const
00127 {
00128   return m_Deleting;
00129 }
00130 
00131 bool KonqPopupMenu::ProtocolInfo::supportsMoving() const
00132 {
00133   return m_Moving;
00134 }
00135 
00136 bool KonqPopupMenu::ProtocolInfo::trashIncluded() const
00137 {
00138   return m_TrashIncluded;
00139 }
00140 
00141 // This helper class stores the .desktop-file actions and the servicemenus
00142 // in order to support X-KDE-Priority and X-KDE-Submenu.
00143 class PopupServices
00144 {
00145 public:
00146     ServiceList* selectList( const QString& priority, const QString& submenuName );
00147 
00148     ServiceList builtin;
00149     ServiceList user, userToplevel, userPriority;
00150     QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus;
00151 };
00152 
00153 ServiceList* PopupServices::selectList( const QString& priority, const QString& submenuName )
00154 {
00155     // we use the categories .desktop entry to define submenus
00156     // if none is defined, we just pop it in the main menu
00157     if (submenuName.isEmpty())
00158     {
00159         if (priority == "TopLevel")
00160         {
00161             return &userToplevel;
00162         }
00163         else if (priority == "Important")
00164         {
00165             return &userPriority;
00166         }
00167     }
00168     else if (priority == "TopLevel")
00169     {
00170         return &(userToplevelSubmenus[submenuName]);
00171     }
00172     else if (priority == "Important")
00173     {
00174         return &(userPrioritySubmenus[submenuName]);
00175     }
00176     else
00177     {
00178         return &(userSubmenus[submenuName]);
00179     }
00180     return &user;
00181 }
00182 
00184 
00185 KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
00186                               KURL viewURL,
00187                               KActionCollection & actions,
00188                               KNewMenu * newMenu,
00189                               bool showProperties )
00190     : QPopupMenu( 0L, "konq_popupmenu" ),
00191       m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ),
00192           m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr)
00193 {
00194     KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow;
00195     init(0, kpf, KParts::BrowserExtension::DefaultPopupItems);
00196 }
00197 
00198 KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
00199                               KURL viewURL,
00200                               KActionCollection & actions,
00201                               KNewMenu * newMenu,
00202                               QWidget * parentWidget,
00203                               bool showProperties )
00204     : 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)
00205 {
00206     KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow;
00207     init(parentWidget, kpf, KParts::BrowserExtension::DefaultPopupItems);
00208 }
00209 
00210 KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items,
00211                               const KURL& viewURL,
00212                               KActionCollection & actions,
00213                               KNewMenu * newMenu,
00214                               QWidget * parentWidget,
00215                               KonqPopupFlags kpf,
00216                               KParts::BrowserExtension::PopupFlags flags)
00217   : 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)
00218 {
00219     init(parentWidget, kpf, flags);
00220 }
00221 
00222 void KonqPopupMenu::init (QWidget * parentWidget, KonqPopupFlags kpf, KParts::BrowserExtension::PopupFlags flags)
00223 {
00224     d = new KonqPopupMenuPrivate;
00225     d->m_parentWidget = parentWidget;
00226     d->m_itemFlags = flags;
00227     setup(kpf);
00228 }
00229 
00230 int KonqPopupMenu::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus,
00231                                           QDomElement& menu,
00232                                           bool isBuiltin)
00233 {
00234     int count = 0;
00235     QMap<QString, ServiceList>::ConstIterator it;
00236 
00237     for (it = submenus.begin(); it != submenus.end(); ++it)
00238     {
00239         if (it.data().isEmpty())
00240         {
00241             //avoid empty sub-menus
00242             continue;
00243         }
00244 
00245         QDomElement actionSubmenu = m_doc.createElement( "menu" );
00246         actionSubmenu.setAttribute( "name", "actions " + it.key() );
00247         menu.appendChild( actionSubmenu );
00248         QDomElement subtext = m_doc.createElement( "text" );
00249         actionSubmenu.appendChild( subtext );
00250         subtext.appendChild( m_doc.createTextNode( it.key() ) );
00251         count += insertServices(it.data(), actionSubmenu, isBuiltin);
00252     }
00253 
00254     return count;
00255 }
00256 
00257 int KonqPopupMenu::insertServices(const ServiceList& list,
00258                                   QDomElement& menu,
00259                                   bool isBuiltin)
00260 {
00261     static int id = 1000;
00262     int count = 0;
00263 
00264     ServiceList::const_iterator it = list.begin();
00265     for( ; it != list.end(); ++it )
00266     {
00267         if ((*it).isEmpty())
00268         {
00269             if (!menu.firstChild().isNull() &&
00270                 menu.lastChild().toElement().tagName().lower() != "separator")
00271             {
00272                 QDomElement separator = m_doc.createElement( "separator" );
00273                 menu.appendChild(separator);
00274             }
00275             continue;
00276         }
00277 
00278         if (isBuiltin || (*it).m_display == true)
00279         {
00280             QCString name;
00281             name.setNum( id );
00282             name.prepend( isBuiltin ? "builtinservice_" : "userservice_" );
00283             KAction * act = new KAction( QString((*it).m_strName).replace('&',"&&"), 0,
00284                                          this, SLOT( slotRunService() ),
00285                                          &m_ownActions, name );
00286 
00287             if ( !(*it).m_strIcon.isEmpty() )
00288             {
00289                 QPixmap pix = SmallIcon( (*it).m_strIcon );
00290                 act->setIconSet( pix );
00291             }
00292 
00293             addAction( act, menu ); // Add to toplevel menu
00294 
00295             m_mapPopupServices[ id++ ] = *it;
00296             ++count;
00297         }
00298     }
00299 
00300     return count;
00301 }
00302 
00303 bool KonqPopupMenu::KIOSKAuthorizedAction(KConfig& cfg)
00304 {
00305     if ( !cfg.hasKey( "X-KDE-AuthorizeAction") )
00306     {
00307         return true;
00308     }
00309 
00310     QStringList list = cfg.readListEntry("X-KDE-AuthorizeAction");
00311     if (kapp && !list.isEmpty())
00312     {
00313         for(QStringList::ConstIterator it = list.begin();
00314             it != list.end();
00315             ++it)
00316         {
00317             if (!kapp->authorize((*it).stripWhiteSpace()))
00318             {
00319                 return false;
00320             }
00321         }
00322     }
00323 
00324     return true;
00325 }
00326 
00327 
00328 void KonqPopupMenu::setup(KonqPopupFlags kpf)
00329 {
00330     assert( m_lstItems.count() >= 1 );
00331 
00332     m_ownActions.setWidget( this );
00333 
00334     const bool bIsLink  = (kpf & IsLink);
00335     bool currentDir     = false;
00336     bool sReading       = true;
00337     bool sDeleting      = ( d->m_itemFlags & KParts::BrowserExtension::NoDeletion ) == 0;
00338     bool sMoving        = sDeleting;
00339     bool sWriting       = sDeleting && m_lstItems.first()->isWritable();
00340     m_sMimeType         = m_lstItems.first()->mimetype();
00341     QString mimeGroup   = m_sMimeType.left(m_sMimeType.find('/'));
00342     mode_t mode         = m_lstItems.first()->mode();
00343     bool isDirectory    = S_ISDIR(mode);
00344     bool bTrashIncluded = false;
00345     bool mediaFiles     = false;
00346     bool isLocal        = m_lstItems.first()->isLocalFile()
00347                        || m_lstItems.first()->url().protocol()=="media"
00348                        || m_lstItems.first()->url().protocol()=="system";
00349     bool isTrashLink     = false;
00350     m_lstPopupURLs.clear();
00351     int id = 0;
00352     setFont(KGlobalSettings::menuFont());
00353     m_pluginList.setAutoDelete( true );
00354     m_ownActions.setHighlightingEnabled( true );
00355 
00356     attrName = QString::fromLatin1( "name" );
00357 
00358     prepareXMLGUIStuff();
00359     m_builder = new KonqPopupMenuGUIBuilder( this );
00360     m_factory = new KXMLGUIFactory( m_builder );
00361 
00362     KURL url;
00363     KFileItemListIterator it ( m_lstItems );
00364     QStringList mimeTypeList;
00365     // Check whether all URLs are correct
00366     for ( ; it.current(); ++it )
00367     {
00368         url = (*it)->url();
00369 
00370         // Build the list of URLs
00371         m_lstPopupURLs.append( url );
00372 
00373         // Determine if common mode among all URLs
00374         if ( mode != (*it)->mode() )
00375             mode = 0; // modes are different => reset to 0
00376 
00377         // Determine if common mimetype among all URLs
00378         if ( m_sMimeType != (*it)->mimetype() )
00379         {
00380             m_sMimeType = QString::null; // mimetypes are different => null
00381 
00382             if ( mimeGroup != (*it)->mimetype().left((*it)->mimetype().find('/')))
00383                 mimeGroup = QString::null; // mimetype groups are different as well!
00384         }
00385 
00386         if ( mimeTypeList.findIndex( (*it)->mimetype() ) == -1 )
00387             mimeTypeList << (*it)->mimetype();
00388 
00389         if ( isLocal && !url.isLocalFile() && url.protocol() != "media" && url.protocol() != "system" )
00390             isLocal = false;
00391 
00392         if ( !bTrashIncluded && (
00393              ( url.protocol() == "trash" && url.path().length() <= 1 )
00394              || url.url() == "system:/trash" || url.url() == "system:/trash/" ) ) {
00395             bTrashIncluded = true;
00396             isLocal = false;
00397         }
00398 
00399         if ( sReading )
00400             sReading = KProtocolInfo::supportsReading( url );
00401 
00402         if ( sWriting )
00403             sWriting = KProtocolInfo::supportsWriting( url ) && (*it)->isWritable();
00404 
00405         if ( sDeleting )
00406             sDeleting = KProtocolInfo::supportsDeleting( url );
00407 
00408         if ( sMoving )
00409             sMoving = KProtocolInfo::supportsMoving( url );
00410         if ( (*it)->mimetype().startsWith("media/") )
00411             mediaFiles = true;
00412     }
00413     url = m_sViewURL;
00414     url.cleanPath();
00415 
00416     //check if url is current directory
00417     if ( m_lstItems.count() == 1 )
00418     {
00419         KURL firstPopupURL( m_lstItems.first()->url() );
00420         firstPopupURL.cleanPath();
00421         //kdDebug(1203) << "View path is " << url.url() << endl;
00422         //kdDebug(1203) << "First popup path is " << firstPopupURL.url() << endl;
00423         currentDir = firstPopupURL.equals( url, true /* ignore_trailing */ );
00424         if ( isLocal && m_sMimeType == "application/x-desktop" ) {
00425             KSimpleConfig cfg( firstPopupURL.path(), true );
00426             cfg.setDesktopGroup();
00427             isTrashLink = ( cfg.readEntry("Type") == "Link" && cfg.readEntry("URL") == "trash:/" );
00428         }
00429 
00430         if ( isTrashLink ) {
00431             sDeleting = false;
00432         }
00433     }
00434     const bool isSingleLocal = m_lstItems.count() == 1 && isLocal;
00435 
00436     m_info.m_Reading = sReading;
00437     m_info.m_Writing = sWriting;
00438     m_info.m_Deleting = sDeleting;
00439     m_info.m_Moving = sMoving;
00440     m_info.m_TrashIncluded = bTrashIncluded;
00441 
00442     // isCurrentTrash: popup on trash:/ itself, or on the trash.desktop link
00443     bool isCurrentTrash = ( m_lstItems.count() == 1 && bTrashIncluded ) || isTrashLink;
00444     bool isIntoTrash = ( url.protocol() == "trash" || url.url().startsWith( "system:/trash" ) ) && !isCurrentTrash; // trashed file, not trash:/ itself
00445     //kdDebug() << "isLocal=" << isLocal << " url=" << url << " isCurrentTrash=" << isCurrentTrash << " isIntoTrash=" << isIntoTrash << " bTrashIncluded=" << bTrashIncluded << endl;
00446     bool isSingleMedium = m_lstItems.count() == 1 && mediaFiles;
00447     clear();
00448 
00450 
00451     KAction * act;
00452 
00453     if (!isCurrentTrash)
00454         addMerge( "konqueror" );
00455 
00456     bool isKDesktop = QCString( kapp->name() ) == "kdesktop";
00457     KAction *actNewWindow = 0;
00458 
00459     if (( kpf & ShowProperties ) && isKDesktop &&
00460         !kapp->authorize("editable_desktop_icons"))
00461     {
00462         kpf &= ~ShowProperties; // remove flag
00463     }
00464 
00465     // Either 'newview' is in the actions we're given (probably in the tabhandling group)
00466     // or we need to insert it ourselves (e.g. for kdesktop). In the first case, actNewWindow must remain 0.
00467     if ( ((kpf & ShowNewWindow) != 0) && sReading )
00468     {
00469         QString openStr = isKDesktop ? i18n( "&Open" ) : i18n( "Open in New &Window" );
00470         actNewWindow = new KAction( openStr, "window_new", 0, this, SLOT( slotPopupNewView() ), &m_ownActions, "newview" );
00471     }
00472 
00473     if ( actNewWindow && !isKDesktop )
00474     {
00475         if (isCurrentTrash)
00476             actNewWindow->setToolTip( i18n( "Open the trash in a new window" ) );
00477         else if (isSingleMedium)
00478             actNewWindow->setToolTip( i18n( "Open the medium in a new window") );
00479         else
00480             actNewWindow->setToolTip( i18n( "Open the document in a new window" ) );
00481     }
00482 
00483     if ( S_ISDIR(mode) && sWriting && !isCurrentTrash ) // A dir, and we can create things into it
00484     {
00485         if ( currentDir && m_pMenuNew ) // Current dir -> add the "new" menu
00486         {
00487             // As requested by KNewMenu :
00488             m_pMenuNew->slotCheckUpToDate();
00489             m_pMenuNew->setPopupFiles( m_lstPopupURLs );
00490 
00491             addAction( m_pMenuNew );
00492 
00493             addSeparator();
00494         }
00495         else
00496         {
00497             if (d->m_itemFlags & KParts::BrowserExtension::ShowCreateDirectory)
00498             {
00499                 KAction *actNewDir = new KAction( i18n( "Create &Folder..." ), "folder_new", 0, this, SLOT( slotPopupNewDir() ), &m_ownActions, "newdir" );
00500                 addAction( actNewDir );
00501                 addSeparator();
00502             }
00503         }
00504     } else if ( isIntoTrash ) {
00505         // Trashed item, offer restoring
00506         act = new KAction( i18n( "&Restore" ), 0, this, SLOT( slotPopupRestoreTrashedItems() ), &m_ownActions, "restore" );
00507         addAction( act );
00508     }
00509 
00510     if (d->m_itemFlags & KParts::BrowserExtension::ShowNavigationItems)
00511     {
00512         if (d->m_itemFlags & KParts::BrowserExtension::ShowUp)
00513             addAction( "up" );
00514         addAction( "back" );
00515         addAction( "forward" );
00516         if (d->m_itemFlags & KParts::BrowserExtension::ShowReload)
00517             addAction( "reload" );
00518         addSeparator();
00519     }
00520 
00521     // "open in new window" is either provided by us, or by the tabhandling group
00522     if (actNewWindow)
00523     {
00524         addAction( actNewWindow );
00525         addSeparator();
00526     }
00527     addGroup( "tabhandling" ); // includes a separator
00528 
00529     if ( !bIsLink )
00530     {
00531         if ( !currentDir && sReading ) {
00532             if ( sDeleting ) {
00533                 addAction( "cut" );
00534             }
00535             addAction( "copy" );
00536         }
00537 
00538         if ( S_ISDIR(mode) && sWriting ) {
00539             if ( currentDir )
00540                 addAction( "paste" );
00541             else
00542                 addAction( "pasteto" );
00543         }
00544         if ( !currentDir )
00545         {
00546             if ( m_lstItems.count() == 1 && sMoving )
00547                 addAction( "rename" );
00548 
00549             bool addTrash = false;
00550             bool addDel = false;
00551 
00552             if ( sMoving && !isIntoTrash && !isTrashLink )
00553                 addTrash = true;
00554 
00555             if ( sDeleting ) {
00556                 if ( !isLocal )
00557                     addDel = true;
00558                 else if (KApplication::keyboardMouseState() & Qt::ShiftButton) {
00559                     addTrash = false;
00560                     addDel = true;
00561                 }
00562                 else {
00563                     KConfigGroup configGroup( kapp->config(), "KDE" );
00564                     if ( configGroup.readBoolEntry( "ShowDeleteCommand", false ) )
00565                         addDel = true;
00566                 }
00567             }
00568 
00569             if ( addTrash )
00570                 addAction( "trash" );
00571             if ( addDel )
00572                 addAction( "del" );
00573         }
00574     }
00575     if ( isCurrentTrash )
00576     {
00577         act = new KAction( i18n( "&Empty Trash Bin" ), "emptytrash", 0, this, SLOT( slotPopupEmptyTrashBin() ), &m_ownActions, "empytrash" );
00578         KSimpleConfig trashConfig( "trashrc", true );
00579         trashConfig.setGroup( "Status" );
00580         act->setEnabled( !trashConfig.readBoolEntry( "Empty", true ) );
00581         addAction( act );
00582     }
00583     addGroup( "editactions" );
00584 
00585     if (d->m_itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) {
00586       addMerge( 0 );
00587       m_factory->addClient( this );
00588       return;
00589     }
00590 
00591     if ( !isCurrentTrash && !isIntoTrash && (d->m_itemFlags & KParts::BrowserExtension::ShowBookmark))
00592     {
00593         addSeparator();
00594         QString caption;
00595         if (currentDir)
00596         {
00597            bool httpPage = (m_sViewURL.protocol().find("http", 0, false) == 0);
00598            if (httpPage)
00599               caption = i18n("&Bookmark This Page");
00600            else
00601               caption = i18n("&Bookmark This Location");
00602         }
00603         else if (S_ISDIR(mode))
00604            caption = i18n("&Bookmark This Folder");
00605         else if (bIsLink)
00606            caption = i18n("&Bookmark This Link");
00607         else
00608            caption = i18n("&Bookmark This File");
00609 
00610         act = new KAction( caption, "bookmark_add", 0, this, SLOT( slotPopupAddToBookmark() ), &m_ownActions, "bookmark_add" );
00611         if (m_lstItems.count() > 1)
00612             act->setEnabled(false);
00613         if (kapp->authorizeKAction("bookmarks"))
00614             addAction( act );
00615         if (bIsLink)
00616             addGroup( "linkactions" );
00617     }
00618 
00620 
00621     PopupServices s;
00622     KURL urlForServiceMenu( m_lstItems.first()->url() );
00623 
00624     // 1 - Look for builtin and user-defined services
00625     if ( m_sMimeType == "application/x-desktop" && isSingleLocal ) // .desktop file
00626     {
00627         // get builtin services, like mount/unmount
00628         s.builtin = KDEDesktopMimeType::builtinServices( m_lstItems.first()->url() );
00629         const QString path = m_lstItems.first()->url().path();
00630         KSimpleConfig cfg( path, true );
00631         cfg.setDesktopGroup();
00632         const QString priority = cfg.readEntry("X-KDE-Priority");
00633         const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00634         if ( cfg.readEntry("Type") == "Link" ) {
00635            urlForServiceMenu = cfg.readEntry("URL");
00636            // TODO: Do we want to make all the actions apply on the target
00637            // of the .desktop file instead of the .desktop file itself?
00638         }
00639         ServiceList* list = s.selectList( priority, submenuName );
00640         (*list) = KDEDesktopMimeType::userDefinedServices( path, cfg, url.isLocalFile() );
00641     }
00642 
00643     if ( sReading )
00644     {
00645 
00646         // 2 - Look for "servicesmenus" bindings (konqueror-specific user-defined services)
00647 
00648         // first check the .directory if this is a directory
00649         if (isDirectory && isSingleLocal)
00650         {
00651             QString dotDirectoryFile = m_lstItems.first()->url().path(1).append(".directory");
00652             KSimpleConfig cfg( dotDirectoryFile, true );
00653             cfg.setDesktopGroup();
00654 
00655             if (KIOSKAuthorizedAction(cfg))
00656             {
00657                 const QString priority = cfg.readEntry("X-KDE-Priority");
00658                 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00659                 ServiceList* list = s.selectList( priority, submenuName );
00660                 (*list) += KDEDesktopMimeType::userDefinedServices( dotDirectoryFile, cfg, true );
00661             }
00662         }
00663 
00664         // findAllResources() also removes duplicates
00665         const QStringList entries = KGlobal::dirs()->findAllResources("data",
00666                                                                       "konqueror/servicemenus/*.desktop",
00667                                                                       false /* recursive */,
00668                                                                       true /* unique */);
00669         QStringList::ConstIterator eIt = entries.begin();
00670         const QStringList::ConstIterator eEnd = entries.end();
00671         for (; eIt != eEnd; ++eIt )
00672         {
00673             KSimpleConfig cfg( *eIt, true );
00674             cfg.setDesktopGroup();
00675 
00676             if (!KIOSKAuthorizedAction(cfg))
00677             {
00678                 continue;
00679             }
00680 
00681             if ( cfg.hasKey( "X-KDE-ShowIfRunning" ) )
00682             {
00683                 const QString app = cfg.readEntry( "X-KDE-ShowIfRunning" );
00684                 if ( !kapp->dcopClient()->isApplicationRegistered( app.utf8() ) )
00685                     continue;
00686             }
00687             if ( cfg.hasKey( "X-KDE-ShowIfDcopCall" ) )
00688             {
00689                 QString dcopcall = cfg.readEntry( "X-KDE-ShowIfDcopCall" );
00690                 const QCString app = dcopcall.section(' ', 0,0).utf8();
00691 
00692                 //if( !kapp->dcopClient()->isApplicationRegistered( app ))
00693                 //  continue; //app does not exist so cannot send call
00694 
00695                 QByteArray dataToSend;
00696                 QDataStream dataStream(dataToSend, IO_WriteOnly);
00697                 dataStream << m_lstPopupURLs;
00698 
00699                 QCString replyType;
00700                 QByteArray replyData;
00701                 QCString object =    dcopcall.section(' ', 1,-2).utf8();
00702                 QString function =  dcopcall.section(' ', -1);
00703                 if(!function.endsWith("(KURL::List)")) {
00704                     kdWarning() << "Desktop file " << *eIt << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl;
00705                     continue; //Be safe.
00706                 }
00707 
00708                 if(!kapp->dcopClient()->call( app, object,
00709                                               function.utf8(),
00710                                               dataToSend, replyType, replyData, true, 1000))
00711                     continue;
00712                 if(replyType != "bool" || !replyData[0])
00713                     continue;
00714 
00715             }
00716             if ( cfg.hasKey( "X-KDE-Protocol" ) )
00717             {
00718                 const QString protocol = cfg.readEntry( "X-KDE-Protocol" );
00719                 if ( protocol != urlForServiceMenu.protocol() )
00720                     continue;
00721             }
00722             else if ( cfg.hasKey( "X-KDE-Protocols" ) )
00723             {
00724                 QStringList protocols = QStringList::split( "," , cfg.readEntry( "X-KDE-Protocols" ) );
00725                 if ( !protocols.contains( urlForServiceMenu.protocol() ) )
00726                     continue;
00727             }
00728             else if ( urlForServiceMenu.protocol() == "trash" || urlForServiceMenu.url().startsWith( "system:/trash" ) )
00729             {
00730                 // Require servicemenus for the trash to ask for protocol=trash explicitely.
00731                 // Trashed files aren't supposed to be available for actions.
00732                 // One might want a servicemenu for trash.desktop itself though.
00733                 continue;
00734             }
00735 
00736             if ( cfg.hasKey( "X-KDE-Require" ) )
00737             {
00738                 const QStringList capabilities = cfg.readListEntry( "X-KDE-Require" );
00739                 if ( capabilities.contains( "Write" ) && !sWriting )
00740                     continue;
00741             }
00742             if ( (cfg.hasKey( "Actions" ) || cfg.hasKey( "X-KDE-GetActionMenu") ) && cfg.hasKey( "ServiceTypes" ) )
00743             {
00744                 const QStringList types = cfg.readListEntry( "ServiceTypes" );
00745                 const QStringList excludeTypes = cfg.readListEntry( "ExcludeServiceTypes" );
00746                 bool ok = false;
00747 
00748                 // check for exact matches or a typeglob'd mimetype if we have a mimetype
00749                 for (QStringList::ConstIterator it = types.begin();
00750                      it != types.end() && !ok;
00751                      ++it)
00752                 {
00753                     // first check if we have an all mimetype
00754                     bool checkTheMimetypes = false;
00755                     if (*it == "all/all" ||
00756                         *it == "allfiles" /*compat with KDE up to 3.0.3*/)
00757                     {
00758                         checkTheMimetypes = true;
00759                     }
00760 
00761                     // next, do we match all files?
00762                     if (!ok &&
00763                         !isDirectory &&
00764                         *it == "all/allfiles")
00765                     {
00766                         checkTheMimetypes = true;
00767                     }
00768 
00769                     // if we have a mimetype, see if we have an exact or a type globbed match
00770                     if (!ok &&
00771                         (!m_sMimeType.isEmpty() &&
00772                          *it == m_sMimeType) ||
00773                         (!mimeGroup.isEmpty() &&
00774                          ((*it).right(1) == "*" &&
00775                           (*it).left((*it).find('/')) == mimeGroup)))
00776                     {
00777                         checkTheMimetypes = true;
00778                     }
00779 
00780                     if (checkTheMimetypes)
00781                     {
00782                         ok = true;
00783                         for (QStringList::ConstIterator itex = excludeTypes.begin(); itex != excludeTypes.end(); ++itex)
00784                         {
00785                             if( ((*itex).right(1) == "*" && (*itex).left((*itex).find('/')) == mimeGroup) ||
00786                                 ((*itex) == m_sMimeType) )
00787                             {
00788                                 ok = false;
00789                                 break;
00790                             }
00791                         }
00792                     }
00793                 }
00794 
00795                 if ( ok )
00796                 {
00797                     const QString priority = cfg.readEntry("X-KDE-Priority");
00798                     const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00799 
00800                     ServiceList* list = s.selectList( priority, submenuName );
00801                     (*list) += KDEDesktopMimeType::userDefinedServices( *eIt, cfg, url.isLocalFile(), m_lstPopupURLs );
00802                 }
00803             }
00804         }
00805 
00806         KTrader::OfferList offers;
00807 
00808         if (kapp->authorizeKAction("openwith"))
00809         {
00810             QString constraint = "Type == 'Application' and DesktopEntryName != 'kfmclient' and DesktopEntryName != 'kfmclient_dir' and DesktopEntryName != 'kfmclient_html'";
00811             QString subConstraint = " and '%1' in ServiceTypes";
00812 
00813             QStringList::ConstIterator it = mimeTypeList.begin();
00814             QStringList::ConstIterator end = mimeTypeList.end();
00815             Q_ASSERT( it != end );
00816             QString first = *it;
00817             ++it;
00818             while ( it != end ) {
00819                 constraint += subConstraint.arg( *it );
00820                 ++it;
00821             }
00822 
00823             offers = KTrader::self()->query( first, constraint );
00824         }
00825 
00827 
00828         m_mapPopup.clear();
00829         m_mapPopupServices.clear();
00830         // "Open With..." for folders is really not very useful, especially for remote folders.
00831         // (media:/something, or trash:/, or ftp://...)
00832         if ( !isDirectory || isLocal )
00833         {
00834             if ( hasAction() )
00835                 addSeparator();
00836 
00837             if ( !offers.isEmpty() )
00838             {
00839                 // First block, app and preview offers
00840                 id = 1;
00841 
00842                 QDomElement menu = m_menuElement;
00843 
00844                 if ( offers.count() > 1 ) // submenu 'open with'
00845                 {
00846                     menu = m_doc.createElement( "menu" );
00847                     menu.setAttribute( "name", "openwith submenu" );
00848                     m_menuElement.appendChild( menu );
00849                     QDomElement text = m_doc.createElement( "text" );
00850                     menu.appendChild( text );
00851                     text.appendChild( m_doc.createTextNode( i18n("&Open With") ) );
00852                 }
00853 
00854                 KTrader::OfferList::ConstIterator it = offers.begin();
00855                 for( ; it != offers.end(); it++ )
00856                 {
00857                     KService::Ptr service = (*it);
00858 
00859                     // Skip OnlyShowIn=Foo and NotShowIn=KDE entries,
00860                     // but still offer NoDisplay=true entries, that's the
00861                     // whole point of such desktop files. This is why we don't
00862                     // use service->noDisplay() here.
00863                     const QString onlyShowIn = service->property("OnlyShowIn", QVariant::String).toString();
00864                     if ( !onlyShowIn.isEmpty() ) {
00865                         const QStringList aList = QStringList::split(';', onlyShowIn);
00866                         if (!aList.contains("KDE"))
00867                             continue;
00868                     }
00869                     const QString notShowIn = service->property("NotShowIn", QVariant::String).toString();
00870                     if ( !notShowIn.isEmpty() ) {
00871                         const QStringList aList = QStringList::split(';', notShowIn);
00872                         if (aList.contains("KDE"))
00873                             continue;
00874                     }
00875 
00876                     QCString nam;
00877                     nam.setNum( id );
00878 
00879                     QString actionName( (*it)->name().replace("&", "&&") );
00880                     if ( menu == m_menuElement ) // no submenu -> prefix single offer
00881                         actionName = i18n( "Open with %1" ).arg( actionName );
00882 
00883                     act = new KAction( actionName, (*it)->pixmap( KIcon::Small ), 0,
00884                                        this, SLOT( slotRunService() ),
00885                                        &m_ownActions, nam.prepend( "appservice_" ) );
00886                     addAction( act, menu );
00887 
00888                     m_mapPopup[ id++ ] = *it;
00889                 }
00890 
00891                 QString openWithActionName;
00892                 if ( menu != m_menuElement ) // submenu
00893                 {
00894                     addSeparator( menu );
00895                     openWithActionName = i18n( "&Other..." );
00896                 }
00897                 else
00898                 {
00899                     openWithActionName = i18n( "&Open With..." );
00900                 }
00901                 KAction *openWithAct = new KAction( openWithActionName, 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
00902                 addAction( openWithAct, menu );
00903             }
00904             else // no app offers -> Open With...
00905             {
00906                 act = new KAction( i18n( "&Open With..." ), 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
00907                 addAction( act );
00908             }
00909 
00910         }
00911         addGroup( "preview" );
00912     }
00913 
00914     // Second block, builtin + user
00915     QDomElement actionMenu = m_menuElement;
00916     int userItemCount = 0;
00917     if (s.user.count() + s.userSubmenus.count() +
00918         s.userPriority.count() + s.userPrioritySubmenus.count() > 1)
00919     {
00920         // we have more than one item, so let's make a submenu
00921         actionMenu = m_doc.createElement( "menu" );
00922         actionMenu.setAttribute( "name", "actions submenu" );
00923         m_menuElement.appendChild( actionMenu );
00924         QDomElement text = m_doc.createElement( "text" );
00925         actionMenu.appendChild( text );
00926         text.appendChild( m_doc.createTextNode( i18n("Ac&tions") ) );
00927     }
00928 
00929     userItemCount += insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
00930     userItemCount += insertServices(s.userPriority, actionMenu, false);
00931 
00932     // see if we need to put a separator between our priority items and our regular items
00933     if (userItemCount > 0 &&
00934         (s.user.count() > 0 ||
00935          s.userSubmenus.count() > 0 ||
00936          s.builtin.count() > 0) &&
00937          actionMenu.lastChild().toElement().tagName().lower() != "separator")
00938     {
00939         QDomElement separator = m_doc.createElement( "separator" );
00940         actionMenu.appendChild(separator);
00941     }
00942 
00943     userItemCount += insertServicesSubmenus(s.userSubmenus, actionMenu, false);
00944     userItemCount += insertServices(s.user, actionMenu, false);
00945     userItemCount += insertServices(s.builtin, m_menuElement, true);
00946 
00947     userItemCount += insertServicesSubmenus(s.userToplevelSubmenus, m_menuElement, false);
00948     userItemCount += insertServices(s.userToplevel, m_menuElement, false);
00949 
00950     if ( userItemCount > 0 )
00951     {
00952         addPendingSeparator();
00953     }
00954 
00955     if ( !isCurrentTrash && !isIntoTrash && !mediaFiles && sReading )
00956         addPlugins(); // now it's time to add plugins
00957 
00958     if ( KPropertiesDialog::canDisplay( m_lstItems ) && (kpf & ShowProperties) )
00959     {
00960         act = new KAction( i18n( "&Properties" ), 0, this, SLOT( slotPopupProperties() ),
00961                            &m_ownActions, "properties" );
00962         addAction( act );
00963     }
00964 
00965     while ( !m_menuElement.lastChild().isNull() &&
00966             m_menuElement.lastChild().toElement().tagName().lower() == "separator" )
00967         m_menuElement.removeChild( m_menuElement.lastChild() );
00968 
00969     if ( isDirectory && isLocal )
00970     {
00971         if ( KFileShare::authorization() == KFileShare::Authorized )
00972         {
00973             addSeparator();
00974             act = new KAction( i18n("Share"), 0, this, SLOT( slotOpenShareFileDialog() ),
00975                                &m_ownActions, "sharefile" );
00976             addAction( act );
00977         }
00978     }
00979 
00980     addMerge( 0 );
00981     //kdDebug() << k_funcinfo << domDocument().toString() << endl;
00982 
00983     m_factory->addClient( this );
00984 }
00985 
00986 void KonqPopupMenu::slotOpenShareFileDialog()
00987 {
00988     KPropertiesDialog* dlg = showPropertiesDialog();
00989     dlg->showFileSharingPage();
00990 }
00991 
00992 KonqPopupMenu::~KonqPopupMenu()
00993 {
00994   m_pluginList.clear();
00995   delete m_factory;
00996   delete m_builder;
00997   delete d;
00998   //kdDebug(1203) << "~KonqPopupMenu leave" << endl;
00999 }
01000 
01001 void KonqPopupMenu::setURLTitle( const QString& urlTitle )
01002 {
01003     d->m_urlTitle = urlTitle;
01004 }
01005 
01006 void KonqPopupMenu::slotPopupNewView()
01007 {
01008   KURL::List::ConstIterator it = m_lstPopupURLs.begin();
01009   for ( ; it != m_lstPopupURLs.end(); it++ )
01010     (void) new KRun(*it);
01011 }
01012 
01013 void KonqPopupMenu::slotPopupNewDir()
01014 {
01015   if (m_lstPopupURLs.empty())
01016     return;
01017 
01018   KonqOperations::newDir(d->m_parentWidget, m_lstPopupURLs.first());
01019 }
01020 
01021 void KonqPopupMenu::slotPopupEmptyTrashBin()
01022 {
01023   KonqOperations::emptyTrash();
01024 }
01025 
01026 void KonqPopupMenu::slotPopupRestoreTrashedItems()
01027 {
01028   KonqOperations::restoreTrashedItems( m_lstPopupURLs );
01029 }
01030 
01031 void KonqPopupMenu::slotPopupOpenWith()
01032 {
01033   KRun::displayOpenWithDialog( m_lstPopupURLs );
01034 }
01035 
01036 void KonqPopupMenu::slotPopupAddToBookmark()
01037 {
01038   KBookmarkGroup root;
01039   if ( m_lstPopupURLs.count() == 1 ) {
01040     KURL url = m_lstPopupURLs.first();
01041     QString title = d->m_urlTitle.isEmpty() ? url.prettyURL() : d->m_urlTitle;
01042     root = m_pManager->addBookmarkDialog( url.prettyURL(), title );
01043   }
01044   else
01045   {
01046     root = m_pManager->root();
01047     KURL::List::ConstIterator it = m_lstPopupURLs.begin();
01048     for ( ; it != m_lstPopupURLs.end(); it++ )
01049       root.addBookmark( m_pManager, (*it).prettyURL(), (*it) );
01050   }
01051   m_pManager->emitChanged( root );
01052 }
01053 
01054 void KonqPopupMenu::slotRunService()
01055 {
01056   QCString senderName = sender()->name();
01057   int id = senderName.mid( senderName.find( '_' ) + 1 ).toInt();
01058 
01059   // Is it a usual service (application)
01060   QMap<int,KService::Ptr>::Iterator it = m_mapPopup.find( id );
01061   if ( it != m_mapPopup.end() )
01062   {
01063     KRun::run( **it, m_lstPopupURLs );
01064     return;
01065   }
01066 
01067   // Is it a service specific to desktop entry files ?
01068   QMap<int,KDEDesktopMimeType::Service>::Iterator it2 = m_mapPopupServices.find( id );
01069   if ( it2 != m_mapPopupServices.end() )
01070   {
01071       KDEDesktopMimeType::executeService( m_lstPopupURLs, it2.data() );
01072   }
01073 
01074   return;
01075 }
01076 
01077 void KonqPopupMenu::slotPopupMimeType()
01078 {
01079     KonqOperations::editMimeType( m_sMimeType );
01080 }
01081 
01082 void KonqPopupMenu::slotPopupProperties()
01083 {
01084     (void)showPropertiesDialog();
01085 }
01086 
01087 KPropertiesDialog* KonqPopupMenu::showPropertiesDialog()
01088 {
01089     // It may be that the kfileitem was created by hand
01090     // (see KonqKfmIconView::slotMouseButtonPressed)
01091     // In that case, we can get more precise info in the properties
01092     // (like permissions) if we stat the URL.
01093     if ( m_lstItems.count() == 1 )
01094     {
01095         KFileItem * item = m_lstItems.first();
01096         if (item->entry().count() == 0) // this item wasn't listed by a slave
01097         {
01098             // KPropertiesDialog will use stat to get more info on the file
01099             return new KPropertiesDialog( item->url(), d->m_parentWidget );
01100         }
01101     }
01102     return new KPropertiesDialog( m_lstItems, d->m_parentWidget );
01103 }
01104 
01105 KAction *KonqPopupMenu::action( const QDomElement &element ) const
01106 {
01107   QCString name = element.attribute( attrName ).ascii();
01108   KAction *res = m_ownActions.action( name );
01109 
01110   if ( !res )
01111     res = m_actions.action( name );
01112 
01113   if ( !res && m_pMenuNew && strcmp( name, m_pMenuNew->name() ) == 0 )
01114     return m_pMenuNew;
01115 
01116   return res;
01117 }
01118 
01119 KActionCollection *KonqPopupMenu::actionCollection() const
01120 {
01121     return const_cast<KActionCollection *>( &m_ownActions );
01122 }
01123 
01124 QString KonqPopupMenu::mimeType() const
01125 {
01126     return m_sMimeType;
01127 }
01128 
01129 KonqPopupMenu::ProtocolInfo KonqPopupMenu::protocolInfo() const
01130 {
01131     return m_info;
01132 }
01133 
01134 void KonqPopupMenu::addPlugins()
01135 {
01136     // search for Konq_PopupMenuPlugins inspired by simons kpropsdlg
01137     //search for a plugin with the right protocol
01138     KTrader::OfferList plugin_offers;
01139     unsigned int pluginCount = 0;
01140     plugin_offers = KTrader::self()->query( m_sMimeType.isNull() ? QString::fromLatin1( "all/all" ) : m_sMimeType, "'KonqPopupMenu/Plugin' in ServiceTypes");
01141     if ( plugin_offers.isEmpty() )
01142         return; // no plugins installed do not bother about it
01143 
01144     KTrader::OfferList::ConstIterator iterator = plugin_offers.begin();
01145     KTrader::OfferList::ConstIterator end = plugin_offers.end();
01146 
01147     addGroup( "plugins" );
01148     // travers the offerlist
01149     for(; iterator != end; ++iterator, ++pluginCount ) {
01150         //kdDebug() << (*iterator)->library() << endl;
01151         KonqPopupMenuPlugin *plugin =
01152             KParts::ComponentFactory::
01153             createInstanceFromLibrary<KonqPopupMenuPlugin>( QFile::encodeName( (*iterator)->library() ),
01154                                                             this,
01155                                                             (*iterator)->name().latin1() );
01156         if ( !plugin )
01157             continue;
01158         QString pluginClientName = QString::fromLatin1( "Plugin%1" ).arg( pluginCount );
01159         addMerge( pluginClientName );
01160         plugin->domDocument().documentElement().setAttribute( "name", pluginClientName );
01161         m_pluginList.append( plugin );
01162         insertChildClient( plugin );
01163     }
01164 
01165     // ## Where is this used?
01166     addMerge( "plugins" );
01167 }
01168 
01169 KURL KonqPopupMenu::url() const // ### should be viewURL()
01170 {
01171   return m_sViewURL;
01172 }
01173 
01174 KFileItemList KonqPopupMenu::fileItemList() const
01175 {
01176   return m_lstItems;
01177 }
01178 
01179 KURL::List KonqPopupMenu::popupURLList() const
01180 {
01181   return m_lstPopupURLs;
01182 }
01183 
01188 KonqPopupMenuPlugin::KonqPopupMenuPlugin( KonqPopupMenu *parent, const char *name )
01189     : QObject( parent, name )
01190 {
01191 }
01192 
01193 KonqPopupMenuPlugin::~KonqPopupMenuPlugin()
01194 {
01195 }
01196 
01197 #include "konq_popupmenu.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys