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