00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "konq_menuactions.h"
00021 #include "konq_menuactions_p.h"
00022 #include <kaction.h>
00023 #include <krun.h>
00024 #include <kmimetypetrader.h>
00025 #include <kdebug.h>
00026 #include <kdesktopfileactions.h>
00027 #include <kmenu.h>
00028 #include <klocale.h>
00029 #include <kauthorized.h>
00030 #include <kconfiggroup.h>
00031 #include <kdesktopfile.h>
00032 #include <kglobal.h>
00033 #include <kicon.h>
00034 #include <kstandarddirs.h>
00035 #include <KService>
00036 #include <KServiceTypeTrader>
00037 #include <QFile>
00038
00039 #include <QtDBus/QtDBus>
00040
00041 static bool KIOSKAuthorizedAction(const KConfigGroup& cfg)
00042 {
00043 if ( !cfg.hasKey( "X-KDE-AuthorizeAction") ) {
00044 return true;
00045 }
00046 const QStringList list = cfg.readEntry("X-KDE-AuthorizeAction", QStringList() );
00047 for(QStringList::ConstIterator it = list.constBegin();
00048 it != list.constEnd(); ++it) {
00049 if (!KAuthorized::authorize((*it).trimmed())) {
00050 return false;
00051 }
00052 }
00053 return true;
00054 }
00055
00056
00057
00058 class PopupServices
00059 {
00060 public:
00061 ServiceList& selectList( const QString& priority, const QString& submenuName );
00062
00063 ServiceList builtin;
00064 ServiceList user, userToplevel, userPriority;
00065 QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus;
00066 };
00067
00068 ServiceList& PopupServices::selectList( const QString& priority, const QString& submenuName )
00069 {
00070
00071
00072 if (submenuName.isEmpty()) {
00073 if (priority == "TopLevel") {
00074 return userToplevel;
00075 } else if (priority == "Important") {
00076 return userPriority;
00077 }
00078 } else if (priority == "TopLevel") {
00079 return userToplevelSubmenus[submenuName];
00080 } else if (priority == "Important") {
00081 return userPrioritySubmenus[submenuName];
00082 } else {
00083 return userSubmenus[submenuName];
00084 }
00085 return user;
00086 }
00087
00089
00090 KonqMenuActionsPrivate::KonqMenuActionsPrivate()
00091 : QObject(),
00092 m_executeServiceActionGroup(static_cast<QWidget *>(0)),
00093 m_runApplicationActionGroup(static_cast<QWidget *>(0)),
00094 m_ownActions(static_cast<QWidget *>(0))
00095 {
00096 QObject::connect(&m_executeServiceActionGroup, SIGNAL(triggered(QAction*)),
00097 this, SLOT(slotExecuteService(QAction*)));
00098 QObject::connect(&m_runApplicationActionGroup, SIGNAL(triggered(QAction*)),
00099 this, SLOT(slotRunApplication(QAction*)));
00100 }
00101
00102 int KonqMenuActionsPrivate::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus,
00103 QMenu* menu,
00104 bool isBuiltin)
00105 {
00106 int count = 0;
00107 QMap<QString, ServiceList>::ConstIterator it;
00108 for (it = submenus.begin(); it != submenus.end(); ++it) {
00109 if (it.value().isEmpty()) {
00110
00111 continue;
00112 }
00113
00114 QMenu* actionSubmenu = new KMenu(menu);
00115 actionSubmenu->setTitle( it.key() );
00116 actionSubmenu->menuAction()->setObjectName("services_submenu");
00117 menu->addMenu(actionSubmenu);
00118 count += insertServices(it.value(), actionSubmenu, isBuiltin);
00119 }
00120
00121 return count;
00122 }
00123
00124 int KonqMenuActionsPrivate::insertServices(const ServiceList& list,
00125 QMenu* menu,
00126 bool isBuiltin)
00127 {
00128 int count = 0;
00129 ServiceList::const_iterator it = list.begin();
00130 for( ; it != list.end(); ++it ) {
00131 if ((*it).isSeparator()) {
00132 const QList<QAction*> actions = menu->actions();
00133 if (!actions.isEmpty() && !actions.last()->isSeparator()) {
00134 menu->addSeparator();
00135 }
00136 continue;
00137 }
00138
00139 if (isBuiltin || !(*it).noDisplay()) {
00140 QAction* act = new QAction(&m_ownActions);
00141 act->setObjectName("menuaction");
00142 QString text = (*it).text();
00143 text.replace('&',"&&");
00144 act->setText( text );
00145 if ( !(*it).icon().isEmpty() ) {
00146 act->setIcon( KIcon((*it).icon()) );
00147 }
00148 act->setData(QVariant::fromValue(*it));
00149 m_executeServiceActionGroup.addAction(act);
00150
00151 menu->addAction(act);
00152 ++count;
00153 }
00154 }
00155
00156 return count;
00157 }
00158
00159 void KonqMenuActionsPrivate::slotExecuteService(QAction* act)
00160 {
00161 KServiceAction serviceAction = act->data().value<KServiceAction>();
00162 KDesktopFileActions::executeService(m_info.urlList(), serviceAction);
00163 }
00164
00166
00167 KonqMenuActions::KonqMenuActions()
00168 : d(new KonqMenuActionsPrivate)
00169 {
00170 }
00171
00172
00173 KonqMenuActions::~KonqMenuActions()
00174 {
00175 delete d;
00176 }
00177
00178 void KonqMenuActions::setPopupMenuInfo(const KonqPopupMenuInformation& info)
00179 {
00180 d->m_info = info;
00181 }
00182
00183 int KonqMenuActions::addActionsTo(QMenu* mainMenu)
00184 {
00185 const KFileItemList items = d->m_info.items();
00186 const KFileItem firstItem = items.first();
00187 const QString protocol = firstItem.url().protocol();
00188 const bool isLocal = firstItem.url().isLocalFile();
00189 const bool isSingleLocal = items.count() == 1 && isLocal;
00190 const KUrl::List urlList = d->m_info.urlList();
00191
00192 PopupServices s;
00193
00194
00195 if (isSingleLocal && d->m_info.mimeType() == "application/x-desktop")
00196 {
00197
00198 s.builtin = KDesktopFileActions::builtinServices(firstItem.url());
00199 const QString path = firstItem.url().path();
00200 KDesktopFile desktopFile(path);
00201 KConfigGroup cfg = desktopFile.desktopGroup();
00202 const QString priority = cfg.readEntry("X-KDE-Priority");
00203 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00204 #if 0
00205 if ( cfg.readEntry("Type") == "Link" ) {
00206 d->m_url = cfg.readEntry("URL");
00207
00208
00209 }
00210 #endif
00211 ServiceList& list = s.selectList(priority, submenuName);
00212 list = KDesktopFileActions::userDefinedServices(path, desktopFile, true );
00213 }
00214
00215
00216
00217
00218 if (d->m_info.isDirectory() && isSingleLocal) {
00219 QString dotDirectoryFile = firstItem.url().path(KUrl::AddTrailingSlash).append(".directory");
00220 if (QFile::exists(dotDirectoryFile)) {
00221 const KDesktopFile desktopFile( dotDirectoryFile );
00222 const KConfigGroup cfg = desktopFile.desktopGroup();
00223
00224 if (KIOSKAuthorizedAction(cfg)) {
00225 const QString priority = cfg.readEntry("X-KDE-Priority");
00226 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00227 ServiceList& list = s.selectList( priority, submenuName );
00228 list += KDesktopFileActions::userDefinedServices( dotDirectoryFile, desktopFile, true );
00229 }
00230 }
00231 }
00232
00233 const QString commonMimeType = d->m_info.mimeType();
00234 const QString commonMimeGroup = d->m_info.mimeGroup();
00235 const KMimeType::Ptr mimeTypePtr = commonMimeType.isEmpty() ? KMimeType::Ptr() : KMimeType::mimeType(commonMimeType);
00236 const KService::List entries = KServiceTypeTrader::self()->query( "KonqPopupMenu/Plugin");
00237 KService::List::const_iterator eEnd = entries.end();
00238 for (KService::List::const_iterator it2 = entries.begin(); it2 != eEnd; ++it2 ) {
00239 QString file = KStandardDirs::locate("services", (*it2)->entryPath());
00240 KDesktopFile desktopFile( file );
00241 const KConfigGroup cfg = desktopFile.desktopGroup();
00242
00243 if (!KIOSKAuthorizedAction(cfg)) {
00244 continue;
00245 }
00246
00247 if ( cfg.hasKey( "X-KDE-ShowIfRunning" ) ) {
00248 const QString app = cfg.readEntry( "X-KDE-ShowIfRunning" );
00249 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( app ) )
00250 continue;
00251 }
00252 if ( cfg.hasKey( "X-KDE-ShowIfDBusCall" ) ) {
00253 QString calldata = cfg.readEntry( "X-KDE-ShowIfDBusCall" );
00254 QStringList parts = calldata.split(' ');
00255 const QString &app = parts.at(0);
00256 const QString &obj = parts.at(1);
00257 QString interface = parts.at(2);
00258 QString method;
00259 int pos = interface.lastIndexOf( QLatin1Char( '.' ) );
00260 if ( pos != -1 ) {
00261 method = interface.mid(pos + 1);
00262 interface.truncate(pos);
00263 }
00264
00265
00266
00267
00268 QDBusMessage reply = QDBusInterface( app, obj, interface ).
00269 call( method, urlList.toStringList() );
00270 if ( reply.arguments().count() < 1 || reply.arguments().at(0).type() != QVariant::Bool || !reply.arguments().at(0).toBool() )
00271 continue;
00272
00273 }
00274 if ( cfg.hasKey( "X-KDE-Protocol" ) ) {
00275 const QString protocol = cfg.readEntry( "X-KDE-Protocol" );
00276 if (protocol.startsWith('!')) {
00277 const QString excludedProtocol = protocol.mid(1);
00278 if (excludedProtocol == protocol)
00279 continue;
00280 } else if (protocol != protocol)
00281 continue;
00282 }
00283 else if ( cfg.hasKey( "X-KDE-Protocols" ) ) {
00284 const QStringList protocols = cfg.readEntry("X-KDE-Protocols", QStringList());
00285 if (!protocols.contains(protocol))
00286 continue;
00287 }
00288 else if (protocol == "trash") {
00289
00290
00291
00292 continue;
00293 }
00294
00295 if ( cfg.hasKey( "X-KDE-Require" ) ) {
00296 const QStringList capabilities = cfg.readEntry( "X-KDE-Require" , QStringList() );
00297 if (capabilities.contains("Write") && !d->m_info.capabilities().supportsWriting())
00298 continue;
00299 }
00300 if ( cfg.hasKey( "Actions" ) || cfg.hasKey( "X-KDE-GetActionMenu") ) {
00301
00302 QStringList types = cfg.readEntry("ServiceTypes", QStringList());
00303 types += cfg.readEntry("X-KDE-ServiceTypes", QStringList());
00304 types += cfg.readXdgListEntry("MimeType");
00305
00306
00307 if (types.isEmpty())
00308 continue;
00309 const QStringList excludeTypes = cfg.readEntry( "ExcludeServiceTypes" , QStringList() );
00310 bool ok = false;
00311
00312
00313 for (QStringList::ConstIterator it = types.constBegin();
00314 it != types.constEnd() && !ok;
00315 ++it)
00316 {
00317
00318 bool checkTheMimetypes = false;
00319 if (*it == "all/all" ||
00320 *it == "allfiles" ) {
00321 checkTheMimetypes = true;
00322 }
00323
00324
00325 if (!ok &&
00326 !d->m_info.isDirectory() &&
00327 *it == "all/allfiles") {
00328 checkTheMimetypes = true;
00329 }
00330
00331
00332 if (!ok && (
00333 (mimeTypePtr && mimeTypePtr->is(*it)) ||
00334 (!commonMimeGroup.isEmpty() &&
00335 ((*it).right(1) == "*" &&
00336 (*it).left((*it).indexOf('/')) == commonMimeGroup)))) {
00337 checkTheMimetypes = true;
00338 }
00339
00340 if (checkTheMimetypes) {
00341 ok = true;
00342 for (QStringList::ConstIterator itex = excludeTypes.constBegin(); itex != excludeTypes.constEnd(); ++itex)
00343 {
00344 if( ((*itex).endsWith('*') && (*itex).left((*itex).indexOf('/')) == commonMimeGroup) ||
00345 ((*itex) == commonMimeType) ) {
00346 ok = false;
00347 break;
00348 }
00349 }
00350 }
00351 }
00352
00353 if ( ok ) {
00354 const QString priority = cfg.readEntry("X-KDE-Priority");
00355 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
00356
00357 ServiceList& list = s.selectList( priority, submenuName );
00358 list += KDesktopFileActions::userDefinedServices(*(*it2), isLocal, urlList);
00359 }
00360 }
00361 }
00362
00363
00364
00365 QMenu* actionMenu = mainMenu;
00366 int userItemCount = 0;
00367 if (s.user.count() + s.userSubmenus.count() +
00368 s.userPriority.count() + s.userPrioritySubmenus.count() > 1)
00369 {
00370
00371 actionMenu = new KMenu(i18nc("@title:menu", "&Actions"), mainMenu);
00372 actionMenu->menuAction()->setObjectName("actions_submenu");
00373 mainMenu->addMenu(actionMenu);
00374 }
00375
00376 userItemCount += d->insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
00377 userItemCount += d->insertServices(s.userPriority, actionMenu, false);
00378
00379
00380 if (userItemCount > 0 &&
00381 (s.user.count() > 0 ||
00382 s.userSubmenus.count() > 0 ||
00383 s.builtin.count() > 0) &&
00384 !actionMenu->actions().last()->isSeparator()) {
00385 actionMenu->addSeparator();
00386 }
00387 userItemCount += d->insertServicesSubmenus(s.userSubmenus, actionMenu, false);
00388 userItemCount += d->insertServices(s.user, actionMenu, false);
00389 userItemCount += d->insertServices(s.builtin, mainMenu, true);
00390 userItemCount += d->insertServicesSubmenus(s.userToplevelSubmenus, mainMenu, false);
00391 userItemCount += d->insertServices(s.userToplevel, mainMenu, false);
00392 return userItemCount;
00393 }
00394
00395 void KonqMenuActions::addOpenWithActionsTo(QMenu* topMenu, const QString& traderConstraint)
00396 {
00397 if (!KAuthorized::authorizeKAction("openwith"))
00398 return;
00399
00400 const KFileItemList items = d->m_info.items();
00401 QStringList mimeTypeList;
00402 KFileItemList::const_iterator kit = items.constBegin();
00403 const KFileItemList::const_iterator kend = items.constEnd();
00404 for ( ; kit != kend; ++kit ) {
00405 if (!mimeTypeList.contains((*kit).mimetype()))
00406 mimeTypeList << (*kit).mimetype();
00407 }
00408
00409 QString constraint = traderConstraint;
00410 const QString subConstraint = " and '%1' in ServiceTypes";
00411
00412 QStringList::ConstIterator it = mimeTypeList.constBegin();
00413 const QStringList::ConstIterator end = mimeTypeList.constEnd();
00414 Q_ASSERT( it != end );
00415 QString firstMimeType = *it;
00416 ++it;
00417 for (; it != end ; ++it) {
00418 constraint += subConstraint.arg(*it);
00419 }
00420
00421 KService::List offers = KMimeTypeTrader::self()->query(firstMimeType, "Application", constraint);
00422
00423 QSet<QString> seenTexts;
00424 for (KService::List::iterator it = offers.begin(); it != offers.end(); ) {
00425
00426
00427
00428 const QString appName((*it)->name());
00429 if (!seenTexts.contains(appName)) {
00430 seenTexts.insert(appName);
00431 ++it;
00432 } else {
00433 it = offers.erase(it);
00434 }
00435 }
00436
00437
00439
00440 const KFileItem firstItem = items.first();
00441 const bool isLocal = firstItem.url().isLocalFile();
00442
00443
00444 if ( !d->m_info.isDirectory() || isLocal ) {
00445 if ( !topMenu->actions().isEmpty() )
00446 topMenu->addSeparator();
00447
00448 if ( !offers.isEmpty() ) {
00449 QMenu* menu = topMenu;
00450
00451 if ( offers.count() > 1 ) {
00452
00453 menu = new QMenu(i18n("&Open With"), topMenu);
00454 menu->menuAction()->setObjectName("openWith_submenu");
00455 topMenu->addMenu(menu);
00456 }
00457
00458
00459 KService::List::ConstIterator it = offers.constBegin();
00460 for( ; it != offers.constEnd(); it++ ) {
00461 KService::Ptr service = (*it);
00462
00463
00464
00465
00466
00467 const QString onlyShowIn = service->property("OnlyShowIn", QVariant::String).toString();
00468 if ( !onlyShowIn.isEmpty() ) {
00469 const QStringList aList = onlyShowIn.split(';', QString::SkipEmptyParts);
00470 if (!aList.contains("KDE"))
00471 continue;
00472 }
00473 const QString notShowIn = service->property("NotShowIn", QVariant::String).toString();
00474 if ( !notShowIn.isEmpty() ) {
00475 const QStringList aList = notShowIn.split(';', QString::SkipEmptyParts);
00476 if (aList.contains("KDE"))
00477 continue;
00478 }
00479
00480 QString actionName(service->name().replace('&', "&&"));
00481 if (menu == topMenu)
00482 actionName = i18n("Open &with %1", actionName);
00483
00484 KAction* act = d->m_ownActions.addAction("openwith");
00485 act->setIcon(KIcon(service->icon()));
00486 act->setText(actionName);
00487 act->setData(QVariant::fromValue(service));
00488 d->m_runApplicationActionGroup.addAction(act);
00489 menu->addAction(act);
00490 }
00491
00492 QString openWithActionName;
00493 if ( menu != topMenu ) {
00494 menu->addSeparator();
00495
00496 openWithActionName = i18n("&Other...");
00497 } else {
00498
00499 openWithActionName = i18n("&Open With...");
00500 }
00501 QAction *openWithAct = d->m_ownActions.addAction( "openwith_browse" );
00502 openWithAct->setText( openWithActionName );
00503 QObject::connect(openWithAct, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
00504 menu->addAction(openWithAct);
00505 }
00506 else
00507 {
00508 KAction* act = d->m_ownActions.addAction( "openwith_browse" );
00509
00510 act->setText( i18n( "&Open With..." ) );
00511 QObject::connect(act, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
00512 topMenu->addAction(act);
00513 }
00514
00515 }
00516 }
00517
00518 void KonqMenuActionsPrivate::slotRunApplication(QAction* act)
00519 {
00520
00521 KService::Ptr app = act->data().value<KService::Ptr>();
00522 Q_ASSERT(app);
00523 if (app) {
00524 KRun::run(*app, m_info.urlList(), m_info.parentWidget());
00525 }
00526 }
00527
00528 void KonqMenuActionsPrivate::slotOpenWithDialog()
00529 {
00530
00531 KRun::displayOpenWithDialog(m_info.urlList(), m_info.parentWidget());
00532 }