• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • libkonq
 

libkonq

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

libkonq

Skip menu "libkonq"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

libkonq

Skip menu "libkonq"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for libkonq by doxygen 1.5.6
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |