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 isReallyLocal = m_lstItems.first()->isLocalFile();
00347     bool isLocal        = isReallyLocal
00348                        || m_lstItems.first()->url().protocol()=="media"
00349                        || m_lstItems.first()->url().protocol()=="system";
00350     bool isTrashLink     = false;
00351     m_lstPopupURLs.clear();
00352     int id = 0;
00353     setFont(KGlobalSettings::menuFont());
00354     m_pluginList.setAutoDelete( true );
00355     m_ownActions.setHighlightingEnabled( true );
00356 
00357     attrName = QString::fromLatin1( "name" );
00358 
00359     prepareXMLGUIStuff();
00360     m_builder = new KonqPopupMenuGUIBuilder( this );
00361     m_factory = new KXMLGUIFactory( m_builder );
00362 
00363     KURL url;
00364     KFileItemListIterator it ( m_lstItems );
00365     QStringList mimeTypeList;
00366     // Check whether all URLs are correct
00367     for ( ; it.current(); ++it )
00368     {
00369         url = (*it)->url();
00370 
00371         // Build the list of URLs
00372         m_lstPopupURLs.append( url );
00373 
00374         // Determine if common mode among all URLs
00375         if ( mode != (*it)->mode() )
00376             mode = 0; // modes are different => reset to 0
00377 
00378         // Determine if common mimetype among all URLs
00379         if ( m_sMimeType != (*it)->mimetype() )
00380         {
00381             m_sMimeType = QString::null; // mimetypes are different => null
00382 
00383             if ( mimeGroup != (*it)->mimetype().left((*it)->mimetype().find('/')))
00384                 mimeGroup = QString::null; // mimetype groups are different as well!
00385         }
00386 
00387         if ( mimeTypeList.findIndex( (*it)->mimetype() ) == -1 )
00388             mimeTypeList << (*it)->mimetype();
00389 
00390         if ( isReallyLocal && !url.isLocalFile() )
00391             isReallyLocal = false;
00392         if ( isLocal && !url.isLocalFile() && url.protocol() != "media" && url.protocol() != "system" )
00393             isLocal = false;
00394 
00395         if ( !bTrashIncluded && (
00396              ( url.protocol() == "trash" && url.path().length() <= 1 )
00397              || url.url() == "system:/trash" || url.url() == "system:/trash/" ) ) {
00398             bTrashIncluded = true;
00399             isLocal = false;
00400         }
00401 
00402         if ( sReading )
00403             sReading = KProtocolInfo::supportsReading( url );
00404 
00405         if ( sWriting )
00406             sWriting = KProtocolInfo::supportsWriting( url ) && (*it)->isWritable();
00407 
00408         if ( sDeleting )
00409             sDeleting = KProtocolInfo::supportsDeleting( url );
00410 
00411         if ( sMoving )
00412             sMoving = KProtocolInfo::supportsMoving( url );
00413         if ( (*it)->mimetype().startsWith("media/") )
00414             mediaFiles = true;
00415     }
00416     url = m_sViewURL;
00417     url.cleanPath();
00418 
00419     //check if url is current directory
00420     if ( m_lstItems.count() == 1 )
00421     {
00422         KURL firstPopupURL( m_lstItems.first()->url() );
00423         firstPopupURL.cleanPath();
00424         //kdDebug(1203) << "View path is " << url.url() << endl;
00425         //kdDebug(1203) << "First popup path is " << firstPopupURL.url() << endl;
00426         currentDir = firstPopupURL.equals( url, true /* ignore_trailing */ );
00427         if ( isLocal && m_sMimeType == "application/x-desktop" ) {
00428             KSimpleConfig cfg( firstPopupURL.path(), true );
00429             cfg.setDesktopGroup();
00430             isTrashLink = ( cfg.readEntry("Type") == "Link" && cfg.readEntry("URL") == "trash:/" );
00431         }
00432 
00433         if ( isTrashLink ) {
00434             sDeleting = false;
00435         }
00436     }
00437 
00438     m_info.m_Reading = sReading;
00439     m_info.m_Writing = sWriting;
00440     m_info.m_Deleting = sDeleting;
00441     m_info.m_Moving = sMoving;
00442     m_info.m_TrashIncluded = bTrashIncluded;
00443 
00444     // isCurrentTrash: popup on trash:/ itself, or on the trash.desktop link
00445     bool isCurrentTrash = ( m_lstItems.count() == 1 && bTrashIncluded ) || isTrashLink;
00446     bool isIntoTrash = ( url.protocol() == "trash" || url.url().startsWith( "system:/trash" ) ) && !isCurrentTrash; // trashed file, not trash:/ itself
00447     //kdDebug() << "isLocal=" << isLocal << " url=" << url << " isCurrentTrash=" << isCurrentTrash << " isIntoTrash=" << isIntoTrash << " bTrashIncluded=" << bTrashIncluded << endl;
00448     bool isSingleMedium = m_lstItems.count() == 1 && mediaFiles;
00449     clear();
00450 
00452 
00453     KAction * act;
00454 
00455     if (!isCurrentTrash)
00456         addMerge( "konqueror" );
00457 
00458     bool isKDesktop = QCString( kapp->name() ) == "kdesktop";
00459     KAction *actNewWindow = 0;
00460 
00461     if (( kpf & ShowProperties ) && isKDesktop &&
00462         !kapp->authorize("editable_desktop_icons"))
00463     {
00464         kpf &= ~ShowProperties; // remove flag
00465     }
00466 
00467     // Either 'newview' is in the actions we're given (probably in the tabhandling group)
00468     // or we need to insert it ourselves (e.g. for kdesktop). In the first case, actNewWindow must remain 0.
00469     if ( ((kpf & ShowNewWindow) != 0) && sReading )
00470     {
00471         QString openStr = isKDesktop ? i18n( "&Open" ) : i18n( "Open in New &Window" );
00472         actNewWindow = new KAction( openStr, "window_new", 0, this, SLOT( slotPopupNewView() ), &m_ownActions, "newview" );
00473     }
00474 
00475     if ( actNewWindow && !isKDesktop )
00476     {
00477         if (isCurrentTrash)
00478             actNewWindow->setToolTip( i18n( "Open the trash in a new window" ) );
00479         else if (isSingleMedium)
00480             actNewWindow->setToolTip( i18n( "Open the medium in a new window") );
00481         else
00482             actNewWindow->setToolTip( i18n( "Open the document in a new window" ) );
00483     }
00484 
00485     if ( S_ISDIR(mode) && sWriting && !isCurrentTrash ) // A dir, and we can create things into it
00486     {
00487         if ( currentDir && m_pMenuNew ) // Current dir -> add the "new" menu
00488         {
00489             // As requested by KNewMenu :
00490             m_pMenuNew->slotCheckUpToDate();
00491             m_pMenuNew->setPopupFiles( m_lstPopupURLs );
00492 
00493             addAction( m_pMenuNew );
00494 
00495             addSeparator();
00496         }
00497         else
00498         {
00499             if (d->m_itemFlags & KParts::BrowserExtension::ShowCreateDirectory)
00500             {
00501                 KAction *actNewDir = new KAction( i18n( "Create &Folder..." ), "folder_new", 0, this, SLOT( slotPopupNewDir() ), &m_ownActions, "newdir" );
00502                 addAction( actNewDir );
00503                 addSeparator();
00504             }
00505         }
00506     } else if ( isIntoTrash ) {
00507         // Trashed item, offer restoring
00508         act = new KAction( i18n( "&Restore" ), 0, this, SLOT( slotPopupRestoreTrashedItems() ), &m_ownActions, "restore" );
00509         addAction( act );
00510     }
00511 
00512     if (d->m_itemFlags & KParts::BrowserExtension::ShowNavigationItems)
00513     {
00514         addAction( "back" );
00515         addAction( "forward" );
00516         if (d->m_itemFlags & KParts::BrowserExtension::ShowUp)
00517             addAction( "up" );
00518         if (d->m_itemFlags & KParts::BrowserExtension::ShowReload)
00519             addAction( "reload" );
00520         addSeparator();
00521     }
00522 
00523     // "open in new window" is either provided by us, or by the tabhandling group
00524     if (actNewWindow)
00525     {
00526         addAction( actNewWindow );
00527         addSeparator();
00528     }
00529     addGroup( "tabhandling" ); // includes a separator
00530 
00531     if ( !bIsLink )
00532     {
00533         if ( !currentDir && sReading ) {
00534             if ( sDeleting ) {
00535                 addAction( "cut" );
00536             }
00537             addAction( "copy" );
00538         }
00539 
00540         if ( S_ISDIR(mode) && sWriting ) {
00541             if ( currentDir )
00542                 addAction( "paste" );
00543             else
00544                 addAction( "pasteto" );
00545         }
00546         if ( !currentDir )
00547         {
00548             if ( m_lstItems.count() == 1 && sMoving )
00549                 addAction( "rename" );
00550 
00551             bool addTrash = false;
00552             bool addDel = false;
00553 
00554             if ( sMoving && !isIntoTrash && !isTrashLink )
00555                 addTrash = true;
00556 
00557             if ( sDeleting ) {
00558                 if ( !isLocal )
00559                     addDel = true;
00560                 else if (KApplication::keyboardMouseState() & Qt::ShiftButton) {
00561                     addTrash = false;
00562                     addDel = true;
00563                 }
00564                 else {
00565                     KConfigGroup configGroup( kapp->config(), "KDE" );
00566                     if ( configGroup.readBoolEntry( "ShowDeleteCommand", false ) )
00567                         addDel = true;
00568                 }
00569             }
00570 
00571             if ( addTrash )
00572                 addAction( "trash" );
00573             if ( addDel )
00574                 addAction( "del" );
00575         }
00576     }
00577     if ( isCurrentTrash )
00578     {
00579         act = new KAction( i18n( "&Empty Trash Bin" ), "emptytrash", 0, this, SLOT( slotPopupEmptyTrashBin() ), &m_ownActions, "empytrash" );
00580         KSimpleConfig trashConfig( "trashrc", true );
00581         trashConfig.setGroup( "Status" );
00582         act->setEnabled( !trashConfig.readBoolEntry( "Empty", true ) );
00583         addAction( act );
00584     }
00585     addGroup( "editactions" );
00586 
00587     if (d->m_itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) {
00588       addMerge( 0 );
00589       m_factory->addClient( this );
00590       return;
00591     }
00592 
00593     if ( !isCurrentTrash && !isIntoTrash && (d->m_itemFlags & KParts::BrowserExtension::ShowBookmark))
00594     {
00595         addSeparator();
00596         QString caption;
00597         if (currentDir)
00598         {
00599            bool httpPage = (m_sViewURL.protocol().find("http", 0, false) == 0);
00600            if (httpPage)
00601               caption = i18n("&Bookmark This Page");
00602            else
00603               caption = i18n("&Bookmark This Location");
00604         }
00605         else if (S_ISDIR(mode))
00606            caption = i18n("&Bookmark This Folder");
00607         else if (bIsLink)
00608            caption = i18n("&Bookmark This Link");
00609         else
00610            caption = i18n("&Bookmark This File");
00611 
00612         act = new KAction( caption, "bookmark_add", 0, this, SLOT( slotPopupAddToBookmark() ), &m_ownActions, "bookmark_add" );
00613         if (m_lstItems.count() > 1)
00614             act->setEnabled(false);
00615         if (kapp->authorizeKAction("bookmarks"))
00616             addAction( act );
00617         if (bIsLink)
00618             addGroup( "linkactions" );
00619     }
00620 
00622 
00623     const bool isSingleLocal = m_lstItems.count() == 1 && isLocal;
00624     PopupServices s;
00625     KURL urlForServiceMenu( m_lstItems.first()->url() );
00626     if (isLocal && !isReallyLocal) { // media or system
00627         bool dummy;
00628         urlForServiceMenu = m_lstItems.first()->mostLocalURL(dummy);
00629     }
00630 
00631     // 1 - Look for builtin and user-defined services
00632     if ( m_sMimeType == "application/x-desktop" && isSingleLocal ) // .desktop file
00633     {
00634         // get builtin services, like mount/unmount
00635         s.builtin = KDEDesktopMimeType::builtinServices( urlForServiceMenu );
00636         const QString path = urlForServiceMenu.path();
00637         KSimpleConfig cfg( path, true );
00638         cfg.setDesktopGroup();
00639         const QString priority = cfg.readEntry("X-KDE-Priority");
00640         const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00641         if ( cfg.readEntry("Type") == "Link" ) {
00642            urlForServiceMenu = cfg.readEntry("URL");
00643            // TODO: Do we want to make all the actions apply on the target
00644            // of the .desktop file instead of the .desktop file itself?
00645         }
00646         ServiceList* list = s.selectList( priority, submenuName );
00647         (*list) = KDEDesktopMimeType::userDefinedServices( path, cfg, urlForServiceMenu.isLocalFile() );
00648     }
00649 
00650     if ( sReading )
00651     {
00652 
00653         // 2 - Look for "servicesmenus" bindings (konqueror-specific user-defined services)
00654 
00655         // first check the .directory if this is a directory
00656         if (isDirectory && isSingleLocal)
00657         {
00658             QString dotDirectoryFile = urlForServiceMenu.path(1).append(".directory");
00659             KSimpleConfig cfg( dotDirectoryFile, true );
00660             cfg.setDesktopGroup();
00661 
00662             if (KIOSKAuthorizedAction(cfg))
00663             {
00664                 const QString priority = cfg.readEntry("X-KDE-Priority");
00665                 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00666                 ServiceList* list = s.selectList( priority, submenuName );
00667                 (*list) += KDEDesktopMimeType::userDefinedServices( dotDirectoryFile, cfg, true );
00668             }
00669         }
00670 
00671         // findAllResources() also removes duplicates
00672         const QStringList entries = KGlobal::dirs()->findAllResources("data",
00673                                                                       "konqueror/servicemenus/*.desktop",
00674                                                                       false /* recursive */,
00675                                                                       true /* unique */);
00676         QStringList::ConstIterator eIt = entries.begin();
00677         const QStringList::ConstIterator eEnd = entries.end();
00678         for (; eIt != eEnd; ++eIt )
00679         {
00680             KSimpleConfig cfg( *eIt, true );
00681             cfg.setDesktopGroup();
00682 
00683             if (!KIOSKAuthorizedAction(cfg))
00684             {
00685                 continue;
00686             }
00687 
00688             if ( cfg.hasKey( "X-KDE-ShowIfRunning" ) )
00689             {
00690                 const QString app = cfg.readEntry( "X-KDE-ShowIfRunning" );
00691                 if ( !kapp->dcopClient()->isApplicationRegistered( app.utf8() ) )
00692                     continue;
00693             }
00694             if ( cfg.hasKey( "X-KDE-ShowIfDcopCall" ) )
00695             {
00696                 QString dcopcall = cfg.readEntry( "X-KDE-ShowIfDcopCall" );
00697                 const QCString app = dcopcall.section(' ', 0,0).utf8();
00698 
00699                 //if( !kapp->dcopClient()->isApplicationRegistered( app ))
00700                 //  continue; //app does not exist so cannot send call
00701 
00702                 QByteArray dataToSend;
00703                 QDataStream dataStream(dataToSend, IO_WriteOnly);
00704                 dataStream << m_lstPopupURLs;
00705 
00706                 QCString replyType;
00707                 QByteArray replyData;
00708                 QCString object =    dcopcall.section(' ', 1,-2).utf8();
00709                 QString function =  dcopcall.section(' ', -1);
00710                 if(!function.endsWith("(KURL::List)")) {
00711                     kdWarning() << "Desktop file " << *eIt << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl;
00712                     continue; //Be safe.
00713                 }
00714 
00715                 if(!kapp->dcopClient()->call( app, object,
00716                                               function.utf8(),
00717                                               dataToSend, replyType, replyData, true, 1000))
00718                     continue;
00719                 if(replyType != "bool" || !replyData[0])
00720                     continue;
00721 
00722             }
00723             if ( cfg.hasKey( "X-KDE-Protocol" ) )
00724             {
00725                 const QString protocol = cfg.readEntry( "X-KDE-Protocol" );
00726                 if ( protocol != urlForServiceMenu.protocol() )
00727                     continue;
00728             }
00729             else if ( cfg.hasKey( "X-KDE-Protocols" ) )
00730             {
00731                 QStringList protocols = QStringList::split( "," , cfg.readEntry( "X-KDE-Protocols" ) );
00732                 if ( !protocols.contains( urlForServiceMenu.protocol() ) )
00733                     continue;
00734             }
00735             else if ( urlForServiceMenu.protocol() == "trash" || urlForServiceMenu.url().startsWith( "system:/trash" ) )
00736             {
00737                 // Require servicemenus for the trash to ask for protocol=trash explicitely.
00738                 // Trashed files aren't supposed to be available for actions.
00739                 // One might want a servicemenu for trash.desktop itself though.
00740                 continue;
00741             }
00742 
00743             if ( cfg.hasKey( "X-KDE-Require" ) )
00744             {
00745                 const QStringList capabilities = cfg.readListEntry( "X-KDE-Require" );
00746                 if ( capabilities.contains( "Write" ) && !sWriting )
00747                     continue;
00748             }
00749             if ( (cfg.hasKey( "Actions" ) || cfg.hasKey( "X-KDE-GetActionMenu") ) && cfg.hasKey( "ServiceTypes" ) )
00750             {
00751                 const QStringList types = cfg.readListEntry( "ServiceTypes" );
00752                 const QStringList excludeTypes = cfg.readListEntry( "ExcludeServiceTypes" );
00753                 bool ok = false;
00754 
00755                 // check for exact matches or a typeglob'd mimetype if we have a mimetype
00756                 for (QStringList::ConstIterator it = types.begin();
00757                      it != types.end() && !ok;
00758                      ++it)
00759                 {
00760                     // first check if we have an all mimetype
00761                     bool checkTheMimetypes = false;
00762                     if (*it == "all/all" ||
00763                         *it == "allfiles" /*compat with KDE up to 3.0.3*/)
00764                     {
00765                         checkTheMimetypes = true;
00766                     }
00767 
00768                     // next, do we match all files?
00769                     if (!ok &&
00770                         !isDirectory &&
00771                         *it == "all/allfiles")
00772                     {
00773                         checkTheMimetypes = true;
00774                     }
00775 
00776                     // if we have a mimetype, see if we have an exact or a type globbed match
00777                     if (!ok &&
00778                         (!m_sMimeType.isEmpty() &&
00779                          *it == m_sMimeType) ||
00780                         (!mimeGroup.isEmpty() &&
00781                          ((*it).right(1) == "*" &&
00782                           (*it).left((*it).find('/')) == mimeGroup)))
00783                     {
00784                         checkTheMimetypes = true;
00785                     }
00786 
00787                     if (checkTheMimetypes)
00788                     {
00789                         ok = true;
00790                         for (QStringList::ConstIterator itex = excludeTypes.begin(); itex != excludeTypes.end(); ++itex)
00791                         {
00792                             if( ((*itex).right(1) == "*" && (*itex).left((*itex).find('/')) == mimeGroup) ||
00793                                 ((*itex) == m_sMimeType) )
00794                             {
00795                                 ok = false;
00796                                 break;
00797                             }
00798                         }
00799                     }
00800                 }
00801 
00802                 if ( ok )
00803                 {
00804                     const QString priority = cfg.readEntry("X-KDE-Priority");
00805                     const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00806 
00807                     ServiceList* list = s.selectList( priority, submenuName );
00808                     (*list) += KDEDesktopMimeType::userDefinedServices( *eIt, cfg, url.isLocalFile(), m_lstPopupURLs );
00809                 }
00810             }
00811         }
00812 
00813         KTrader::OfferList offers;
00814 
00815         if (kapp->authorizeKAction("openwith"))
00816         {
00817             QString constraint = "Type == 'Application' and DesktopEntryName != 'kfmclient' and DesktopEntryName != 'kfmclient_dir' and DesktopEntryName != 'kfmclient_html'";
00818             QString subConstraint = " and '%1' in ServiceTypes";
00819 
00820             QStringList::ConstIterator it = mimeTypeList.begin();
00821             QStringList::ConstIterator end = mimeTypeList.end();
00822             Q_ASSERT( it != end );
00823             QString first = *it;
00824             ++it;
00825             while ( it != end ) {
00826                 constraint += subConstraint.arg( *it );
00827                 ++it;
00828             }
00829 
00830             offers = KTrader::self()->query( first, constraint );
00831         }
00832 
00834 
00835         m_mapPopup.clear();
00836         m_mapPopupServices.clear();
00837         // "Open With..." for folders is really not very useful, especially for remote folders.
00838         // (media:/something, or trash:/, or ftp://...)
00839         if ( !isDirectory || isLocal )
00840         {
00841             if ( hasAction() )
00842                 addSeparator();
00843 
00844             if ( !offers.isEmpty() )
00845             {
00846                 // First block, app and preview offers
00847                 id = 1;
00848 
00849                 QDomElement menu = m_menuElement;
00850 
00851                 if ( offers.count() > 1 ) // submenu 'open with'
00852                 {
00853                     menu = m_doc.createElement( "menu" );
00854                     menu.setAttribute( "name", "openwith submenu" );
00855                     m_menuElement.appendChild( menu );
00856                     QDomElement text = m_doc.createElement( "text" );
00857                     menu.appendChild( text );
00858                     text.appendChild( m_doc.createTextNode( i18n("&Open With") ) );
00859                 }
00860 
00861                 KTrader::OfferList::ConstIterator it = offers.begin();
00862                 for( ; it != offers.end(); it++ )
00863                 {
00864                     KService::Ptr service = (*it);
00865 
00866                     // Skip OnlyShowIn=Foo and NotShowIn=KDE entries,
00867                     // but still offer NoDisplay=true entries, that's the
00868                     // whole point of such desktop files. This is why we don't
00869                     // use service->noDisplay() here.
00870                     const QString onlyShowIn = service->property("OnlyShowIn", QVariant::String).toString();
00871                     if ( !onlyShowIn.isEmpty() ) {
00872                         const QStringList aList = QStringList::split(';', onlyShowIn);
00873                         if (!aList.contains("KDE"))
00874                             continue;
00875                     }
00876                     const QString notShowIn = service->property("NotShowIn", QVariant::String).toString();
00877                     if ( !notShowIn.isEmpty() ) {
00878                         const QStringList aList = QStringList::split(';', notShowIn);
00879                         if (aList.contains("KDE"))
00880                             continue;
00881                     }
00882 
00883                     QCString nam;
00884                     nam.setNum( id );
00885 
00886                     QString actionName( (*it)->name().replace("&", "&&") );
00887                     if ( menu == m_menuElement ) // no submenu -> prefix single offer
00888                         actionName = i18n( "Open with %1" ).arg( actionName );
00889 
00890                     act = new KAction( actionName, (*it)->pixmap( KIcon::Small ), 0,
00891                                        this, SLOT( slotRunService() ),
00892                                        &m_ownActions, nam.prepend( "appservice_" ) );
00893                     addAction( act, menu );
00894 
00895                     m_mapPopup[ id++ ] = *it;
00896                 }
00897 
00898                 QString openWithActionName;
00899                 if ( menu != m_menuElement ) // submenu
00900                 {
00901                     addSeparator( menu );
00902                     openWithActionName = i18n( "&Other..." );
00903                 }
00904                 else
00905                 {
00906                     openWithActionName = i18n( "&Open With..." );
00907                 }
00908                 KAction *openWithAct = new KAction( openWithActionName, 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
00909                 addAction( openWithAct, menu );
00910             }
00911             else // no app offers -> Open With...
00912             {
00913                 act = new KAction( i18n( "&Open With..." ), 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" );
00914                 addAction( act );
00915             }
00916 
00917         }
00918         addGroup( "preview" );
00919     }
00920 
00921     // Second block, builtin + user
00922     QDomElement actionMenu = m_menuElement;
00923     int userItemCount = 0;
00924     if (s.user.count() + s.userSubmenus.count() +
00925         s.userPriority.count() + s.userPrioritySubmenus.count() > 1)
00926     {
00927         // we have more than one item, so let's make a submenu
00928         actionMenu = m_doc.createElement( "menu" );
00929         actionMenu.setAttribute( "name", "actions submenu" );
00930         m_menuElement.appendChild( actionMenu );
00931         QDomElement text = m_doc.createElement( "text" );
00932         actionMenu.appendChild( text );
00933         text.appendChild( m_doc.createTextNode( i18n("Ac&tions") ) );
00934     }
00935 
00936     userItemCount += insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
00937     userItemCount += insertServices(s.userPriority, actionMenu, false);
00938 
00939     // see if we need to put a separator between our priority items and our regular items
00940     if (userItemCount > 0 &&
00941         (s.user.count() > 0 ||
00942          s.userSubmenus.count() > 0 ||
00943          s.builtin.count() > 0) &&
00944          actionMenu.lastChild().toElement().tagName().lower() != "separator")
00945     {
00946         QDomElement separator = m_doc.createElement( "separator" );
00947         actionMenu.appendChild(separator);
00948     }
00949 
00950     userItemCount += insertServicesSubmenus(s.userSubmenus, actionMenu, false);
00951     userItemCount += insertServices(s.user, actionMenu, false);
00952     userItemCount += insertServices(s.builtin, m_menuElement, true);
00953 
00954     userItemCount += insertServicesSubmenus(s.userToplevelSubmenus, m_menuElement, false);
00955     userItemCount += insertServices(s.userToplevel, m_menuElement, false);
00956 
00957     if ( userItemCount > 0 )
00958     {
00959         addPendingSeparator();
00960     }
00961 
00962     if ( !isCurrentTrash && !isIntoTrash && !mediaFiles && sReading )
00963         addPlugins(); // now it's time to add plugins
00964 
00965     if ( KPropertiesDialog::canDisplay( m_lstItems ) && (kpf & ShowProperties) )
00966     {
00967         act = new KAction( i18n( "&Properties" ), 0, this, SLOT( slotPopupProperties() ),
00968                            &m_ownActions, "properties" );
00969         addAction( act );
00970     }
00971 
00972     while ( !m_menuElement.lastChild().isNull() &&
00973             m_menuElement.lastChild().toElement().tagName().lower() == "separator" )
00974         m_menuElement.removeChild( m_menuElement.lastChild() );
00975 
00976     if ( isDirectory && isLocal )
00977     {
00978         if ( KFileShare::authorization() == KFileShare::Authorized )
00979         {
00980             addSeparator();
00981             act = new KAction( i18n("Share"), 0, this, SLOT( slotOpenShareFileDialog() ),
00982                                &m_ownActions, "sharefile" );
00983             addAction( act );
00984         }
00985     }
00986 
00987     addMerge( 0 );
00988     //kdDebug() << k_funcinfo << domDocument().toString() << endl;
00989 
00990     m_factory->addClient( this );
00991 }
00992 
00993 void KonqPopupMenu::slotOpenShareFileDialog()
00994 {
00995     KPropertiesDialog* dlg = showPropertiesDialog();
00996     dlg->showFileSharingPage();
00997 }
00998 
00999 KonqPopupMenu::~KonqPopupMenu()
01000 {
01001   m_pluginList.clear();
01002   delete m_factory;
01003   delete m_builder;
01004   delete d;
01005   //kdDebug(1203) << "~KonqPopupMenu leave" << endl;
01006 }
01007 
01008 void KonqPopupMenu::setURLTitle( const QString& urlTitle )
01009 {
01010     d->m_urlTitle = urlTitle;
01011 }
01012 
01013 void KonqPopupMenu::slotPopupNewView()
01014 {
01015   KURL::List::ConstIterator it = m_lstPopupURLs.begin();
01016   for ( ; it != m_lstPopupURLs.end(); it++ )
01017     (void) new KRun(*it);
01018 }
01019 
01020 void KonqPopupMenu::slotPopupNewDir()
01021 {
01022   if (m_lstPopupURLs.empty())
01023     return;
01024 
01025   KonqOperations::newDir(d->m_parentWidget, m_lstPopupURLs.first());
01026 }
01027 
01028 void KonqPopupMenu::slotPopupEmptyTrashBin()
01029 {
01030   KonqOperations::emptyTrash();
01031 }
01032 
01033 void KonqPopupMenu::slotPopupRestoreTrashedItems()
01034 {
01035   KonqOperations::restoreTrashedItems( m_lstPopupURLs );
01036 }
01037 
01038 void KonqPopupMenu::slotPopupOpenWith()
01039 {
01040   KRun::displayOpenWithDialog( m_lstPopupURLs );
01041 }
01042 
01043 void KonqPopupMenu::slotPopupAddToBookmark()
01044 {
01045   KBookmarkGroup root;
01046   if ( m_lstPopupURLs.count() == 1 ) {
01047     KURL url = m_lstPopupURLs.first();
01048     QString title = d->m_urlTitle.isEmpty() ? url.prettyURL() : d->m_urlTitle;
01049     root = m_pManager->addBookmarkDialog( url.prettyURL(), title );
01050   }
01051   else
01052   {
01053     root = m_pManager->root();
01054     KURL::List::ConstIterator it = m_lstPopupURLs.begin();
01055     for ( ; it != m_lstPopupURLs.end(); it++ )
01056       root.addBookmark( m_pManager, (*it).prettyURL(), (*it) );
01057   }
01058   m_pManager->emitChanged( root );
01059 }
01060 
01061 void KonqPopupMenu::slotRunService()
01062 {
01063   QCString senderName = sender()->name();
01064   int id = senderName.mid( senderName.find( '_' ) + 1 ).toInt();
01065 
01066   // Is it a usual service (application)
01067   QMap<int,KService::Ptr>::Iterator it = m_mapPopup.find( id );
01068   if ( it != m_mapPopup.end() )
01069   {
01070     KRun::run( **it, m_lstPopupURLs );
01071     return;
01072   }
01073 
01074   // Is it a service specific to desktop entry files ?
01075   QMap<int,KDEDesktopMimeType::Service>::Iterator it2 = m_mapPopupServices.find( id );
01076   if ( it2 != m_mapPopupServices.end() )
01077   {
01078       KDEDesktopMimeType::executeService( m_lstPopupURLs, it2.data() );
01079   }
01080 
01081   return;
01082 }
01083 
01084 void KonqPopupMenu::slotPopupMimeType()
01085 {
01086     KonqOperations::editMimeType( m_sMimeType );
01087 }
01088 
01089 void KonqPopupMenu::slotPopupProperties()
01090 {
01091     (void)showPropertiesDialog();
01092 }
01093 
01094 KPropertiesDialog* KonqPopupMenu::showPropertiesDialog()
01095 {
01096     // It may be that the kfileitem was created by hand
01097     // (see KonqKfmIconView::slotMouseButtonPressed)
01098     // In that case, we can get more precise info in the properties
01099     // (like permissions) if we stat the URL.
01100     if ( m_lstItems.count() == 1 )
01101     {
01102         KFileItem * item = m_lstItems.first();
01103         if (item->entry().count() == 0) // this item wasn't listed by a slave
01104         {
01105             // KPropertiesDialog will use stat to get more info on the file
01106             return new KPropertiesDialog( item->url(), d->m_parentWidget );
01107         }
01108     }
01109     return new KPropertiesDialog( m_lstItems, d->m_parentWidget );
01110 }
01111 
01112 KAction *KonqPopupMenu::action( const QDomElement &element ) const
01113 {
01114   QCString name = element.attribute( attrName ).ascii();
01115   KAction *res = m_ownActions.action( name );
01116 
01117   if ( !res )
01118     res = m_actions.action( name );
01119 
01120   if ( !res && m_pMenuNew && strcmp( name, m_pMenuNew->name() ) == 0 )
01121     return m_pMenuNew;
01122 
01123   return res;
01124 }
01125 
01126 KActionCollection *KonqPopupMenu::actionCollection() const
01127 {
01128     return const_cast<KActionCollection *>( &m_ownActions );
01129 }
01130 
01131 QString KonqPopupMenu::mimeType() const
01132 {
01133     return m_sMimeType;
01134 }
01135 
01136 KonqPopupMenu::ProtocolInfo KonqPopupMenu::protocolInfo() const
01137 {
01138     return m_info;
01139 }
01140 
01141 void KonqPopupMenu::addPlugins()
01142 {
01143     // search for Konq_PopupMenuPlugins inspired by simons kpropsdlg
01144     //search for a plugin with the right protocol
01145     KTrader::OfferList plugin_offers;
01146     unsigned int pluginCount = 0;
01147     plugin_offers = KTrader::self()->query( m_sMimeType.isNull() ? QString::fromLatin1( "all/all" ) : m_sMimeType, "'KonqPopupMenu/Plugin' in ServiceTypes");
01148     if ( plugin_offers.isEmpty() )
01149         return; // no plugins installed do not bother about it
01150 
01151     KTrader::OfferList::ConstIterator iterator = plugin_offers.begin();
01152     KTrader::OfferList::ConstIterator end = plugin_offers.end();
01153 
01154     addGroup( "plugins" );
01155     // travers the offerlist
01156     for(; iterator != end; ++iterator, ++pluginCount ) {
01157         //kdDebug() << (*iterator)->library() << endl;
01158         KonqPopupMenuPlugin *plugin =
01159             KParts::ComponentFactory::
01160             createInstanceFromLibrary<KonqPopupMenuPlugin>( QFile::encodeName( (*iterator)->library() ),
01161                                                             this,
01162                                                             (*iterator)->name().latin1() );
01163         if ( !plugin )
01164             continue;
01165         // This make the kuick plugin insert its stuff above "Properties"
01166         QString pluginClientName = QString::fromLatin1( "Plugin%1" ).arg( pluginCount );
01167         addMerge( pluginClientName );
01168         plugin->domDocument().documentElement().setAttribute( "name", pluginClientName );
01169         m_pluginList.append( plugin );
01170         insertChildClient( plugin );
01171     }
01172 
01173     // ## Where is this used?
01174     addMerge( "plugins" );
01175 }
01176 
01177 KURL KonqPopupMenu::url() const // ### should be viewURL()
01178 {
01179   return m_sViewURL;
01180 }
01181 
01182 KFileItemList KonqPopupMenu::fileItemList() const
01183 {
01184   return m_lstItems;
01185 }
01186 
01187 KURL::List KonqPopupMenu::popupURLList() const
01188 {
01189   return m_lstPopupURLs;
01190 }
01191 
01196 KonqPopupMenuPlugin::KonqPopupMenuPlugin( KonqPopupMenu *parent, const char *name )
01197     : QObject( parent, name )
01198 {
01199 }
01200 
01201 KonqPopupMenuPlugin::~KonqPopupMenuPlugin()
01202 {
01203 }
01204 
01205 #include "konq_popupmenu.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys