• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • KDE Home
  • Contact Us
 

KIO

kfileitemactions.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 1998-2009 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or modify
00005    it under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2 of the License or
00007    ( at your option ) version 3 or, at the discretion of KDE e.V.
00008    ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "kfileitemactions.h"
00022 #include "kfileitemactions_p.h"
00023 #include <kaction.h>
00024 #include <krun.h>
00025 #include <kmimetypetrader.h>
00026 #include <kdebug.h>
00027 #include <kdesktopfileactions.h>
00028 #include <kmenu.h>
00029 #include <klocale.h>
00030 #include <kauthorized.h>
00031 #include <kconfiggroup.h>
00032 #include <kdesktopfile.h>
00033 #include <kglobal.h>
00034 #include <kicon.h>
00035 #include <kstandarddirs.h>
00036 #include <kservicetypetrader.h>
00037 #include <QFile>
00038 #include <QtAlgorithms>
00039 
00040 #include <QtDBus/QtDBus>
00041 
00042 static bool KIOSKAuthorizedAction(const KConfigGroup& cfg)
00043 {
00044     if (!cfg.hasKey("X-KDE-AuthorizeAction")) {
00045         return true;
00046     }
00047     const QStringList list = cfg.readEntry("X-KDE-AuthorizeAction", QStringList());
00048     for(QStringList::ConstIterator it = list.constBegin();
00049         it != list.constEnd(); ++it) {
00050         if (!KAuthorized::authorize((*it).trimmed())) {
00051             return false;
00052         }
00053     }
00054     return true;
00055 }
00056 
00057 // This helper class stores the .desktop-file actions and the servicemenus
00058 // in order to support X-KDE-Priority and X-KDE-Submenu.
00059 namespace KIO {
00060 class PopupServices
00061 {
00062 public:
00063     ServiceList& selectList(const QString& priority, const QString& submenuName);
00064 
00065     ServiceList builtin;
00066     ServiceList user, userToplevel, userPriority;
00067     QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus;
00068 };
00069 
00070 ServiceList& PopupServices::selectList(const QString& priority, const QString& submenuName)
00071 {
00072     // we use the categories .desktop entry to define submenus
00073     // if none is defined, we just pop it in the main menu
00074     if (submenuName.isEmpty()) {
00075         if (priority == "TopLevel") {
00076             return userToplevel;
00077         } else if (priority == "Important") {
00078             return userPriority;
00079         }
00080     } else if (priority == "TopLevel") {
00081         return userToplevelSubmenus[submenuName];
00082     } else if (priority == "Important") {
00083         return userPrioritySubmenus[submenuName];
00084     } else {
00085         return userSubmenus[submenuName];
00086     }
00087     return user;
00088 }
00089 } // namespace
00090 
00092 
00093 KFileItemActionsPrivate::KFileItemActionsPrivate(KFileItemActions *qq)
00094     : QObject(),
00095       q(qq),
00096       m_executeServiceActionGroup(static_cast<QWidget *>(0)),
00097       m_runApplicationActionGroup(static_cast<QWidget *>(0)),
00098       m_parentWidget(0)
00099 {
00100     QObject::connect(&m_executeServiceActionGroup, SIGNAL(triggered(QAction*)),
00101                      this, SLOT(slotExecuteService(QAction*)));
00102     QObject::connect(&m_runApplicationActionGroup, SIGNAL(triggered(QAction*)),
00103                      this, SLOT(slotRunApplication(QAction*)));
00104 }
00105 
00106 KFileItemActionsPrivate::~KFileItemActionsPrivate()
00107 {
00108     qDeleteAll(m_ownActions);
00109 }
00110 
00111 int KFileItemActionsPrivate::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus,
00112                                                    QMenu* menu,
00113                                                    bool isBuiltin)
00114 {
00115     int count = 0;
00116     QMap<QString, ServiceList>::ConstIterator it;
00117     for (it = submenus.begin(); it != submenus.end(); ++it) {
00118         if (it.value().isEmpty()) {
00119             //avoid empty sub-menus
00120             continue;
00121         }
00122 
00123         QMenu* actionSubmenu = new KMenu(menu);
00124         actionSubmenu->setTitle(it.key());
00125         actionSubmenu->menuAction()->setObjectName("services_submenu"); // for the unittest
00126         menu->addMenu(actionSubmenu);
00127         count += insertServices(it.value(), actionSubmenu, isBuiltin);
00128     }
00129 
00130     return count;
00131 }
00132 
00133 int KFileItemActionsPrivate::insertServices(const ServiceList& list,
00134                                            QMenu* menu,
00135                                            bool isBuiltin)
00136 {
00137     int count = 0;
00138     ServiceList::const_iterator it = list.begin();
00139     for(; it != list.end(); ++it) {
00140         if ((*it).isSeparator()) {
00141             const QList<QAction*> actions = menu->actions();
00142             if (!actions.isEmpty() && !actions.last()->isSeparator()) {
00143                 menu->addSeparator();
00144             }
00145             continue;
00146         }
00147 
00148         if (isBuiltin || !(*it).noDisplay()) {
00149             KAction *act = new KAction(m_parentWidget);
00150             m_ownActions.append(act);
00151             act->setObjectName("menuaction"); // for the unittest
00152             QString text = (*it).text();
00153             text.replace('&',"&&");
00154             act->setText(text);
00155             if (!(*it).icon().isEmpty()) {
00156                 act->setIcon(KIcon((*it).icon()));
00157             }
00158             act->setData(QVariant::fromValue(*it));
00159             m_executeServiceActionGroup.addAction(act);
00160 
00161             menu->addAction(act); // Add to toplevel menu
00162             ++count;
00163         }
00164     }
00165 
00166     return count;
00167 }
00168 
00169 void KFileItemActionsPrivate::slotExecuteService(QAction* act)
00170 {
00171     KServiceAction serviceAction = act->data().value<KServiceAction>();
00172     if (KAuthorized::authorizeKAction(serviceAction.name())) {
00173         KDesktopFileActions::executeService(m_props.urlList(), serviceAction);
00174     }
00175 }
00176 
00178 
00179 KFileItemActions::KFileItemActions(QObject* parent)
00180     : QObject(parent), d(new KFileItemActionsPrivate(this))
00181 {
00182 }
00183 
00184 
00185 KFileItemActions::~KFileItemActions()
00186 {
00187     delete d;
00188 }
00189 
00190 void KFileItemActions::setItemListProperties(const KFileItemListProperties& itemListProperties)
00191 {
00192     d->m_props = itemListProperties;
00193 
00194     d->m_mimeTypeList.clear();
00195     const KFileItemList items = d->m_props.items();
00196     KFileItemList::const_iterator kit = items.constBegin();
00197     const KFileItemList::const_iterator kend = items.constEnd();
00198     for (; kit != kend; ++kit) {
00199         if (!d->m_mimeTypeList.contains((*kit).mimetype()))
00200             d->m_mimeTypeList << (*kit).mimetype();
00201     }
00202 }
00203 
00204 int KFileItemActions::addServiceActionsTo(QMenu* mainMenu)
00205 {
00206     const KFileItemList items = d->m_props.items();
00207     const KFileItem firstItem = items.first();
00208     const QString protocol = firstItem.url().protocol(); // assumed to be the same for all items
00209     const bool isLocal = !firstItem.localPath().isEmpty();
00210     const bool isSingleLocal = items.count() == 1 && isLocal;
00211     const KUrl::List urlList = d->m_props.urlList();
00212 
00213     KIO::PopupServices s;
00214 
00215     // 1 - Look for builtin and user-defined services
00216     if (isSingleLocal && (d->m_props.mimeType() == "application/x-desktop" || // .desktop file
00217         d->m_props.mimeType() == "inode/blockdevice")) { // dev file
00218         // get builtin services, like mount/unmount
00219         const QString path = firstItem.localPath();
00220         s.builtin = KDesktopFileActions::builtinServices(path);
00221         KDesktopFile desktopFile(path);
00222         KConfigGroup cfg = desktopFile.desktopGroup();
00223         const QString priority = cfg.readEntry("X-KDE-Priority");
00224         const QString submenuName = cfg.readEntry("X-KDE-Submenu");
00225 #if 0
00226         if (cfg.readEntry("Type") == "Link") {
00227            d->m_url = cfg.readEntry("URL");
00228            // TODO: Do we want to make all the actions apply on the target
00229            // of the .desktop file instead of the .desktop file itself?
00230         }
00231 #endif
00232         ServiceList& list = s.selectList(priority, submenuName);
00233         list = KDesktopFileActions::userDefinedServices(path, desktopFile, true /*isLocal*/);
00234     }
00235 
00236     // 2 - Look for "servicemenus" bindings (user-defined services)
00237 
00238     // first check the .directory if this is a directory
00239     if (d->m_props.isDirectory() && isSingleLocal) {
00240         QString dotDirectoryFile = KUrl::fromPath(firstItem.localPath()).path(KUrl::AddTrailingSlash).append(".directory");
00241         if (QFile::exists(dotDirectoryFile)) {
00242             const KDesktopFile desktopFile(dotDirectoryFile);
00243             const KConfigGroup cfg = desktopFile.desktopGroup();
00244 
00245             if (KIOSKAuthorizedAction(cfg)) {
00246                 const QString priority = cfg.readEntry("X-KDE-Priority");
00247                 const QString submenuName = cfg.readEntry("X-KDE-Submenu");
00248                 ServiceList& list = s.selectList(priority, submenuName);
00249                 list += KDesktopFileActions::userDefinedServices(dotDirectoryFile, desktopFile, true);
00250             }
00251         }
00252     }
00253 
00254     const KConfig config("kservicemenurc", KConfig::NoGlobals);
00255     const KConfigGroup showGroup = config.group("Show");
00256 
00257     const QString commonMimeType = d->m_props.mimeType();
00258     const QString commonMimeGroup = d->m_props.mimeGroup();
00259     const KMimeType::Ptr mimeTypePtr = commonMimeType.isEmpty() ? KMimeType::Ptr() : KMimeType::mimeType(commonMimeType);
00260     const KService::List entries = KServiceTypeTrader::self()->query("KonqPopupMenu/Plugin");
00261     KService::List::const_iterator eEnd = entries.end();
00262     for (KService::List::const_iterator it2 = entries.begin(); it2 != eEnd; ++it2) {
00263         QString file = KStandardDirs::locate("services", (*it2)->entryPath());
00264         KDesktopFile desktopFile(file);
00265         const KConfigGroup cfg = desktopFile.desktopGroup();
00266 
00267         if (!KIOSKAuthorizedAction(cfg)) {
00268             continue;
00269         }
00270 
00271         if (cfg.hasKey("X-KDE-ShowIfRunning")) {
00272             const QString app = cfg.readEntry("X-KDE-ShowIfRunning");
00273             if (QDBusConnection::sessionBus().interface()->isServiceRegistered(app)) {
00274                 continue;
00275             }
00276         }
00277         if (cfg.hasKey("X-KDE-ShowIfDBusCall")) {
00278             QString calldata = cfg.readEntry("X-KDE-ShowIfDBusCall");
00279             const QStringList parts = calldata.split(' ');
00280             const QString &app = parts.at(0);
00281             const QString &obj = parts.at(1);
00282             QString interface = parts.at(2);
00283             QString method;
00284             int pos = interface.lastIndexOf(QLatin1Char('.'));
00285             if (pos != -1) {
00286                 method = interface.mid(pos + 1);
00287                 interface.truncate(pos);
00288             }
00289 
00290             //if (!QDBus::sessionBus().busService()->nameHasOwner(app))
00291             //    continue; //app does not exist so cannot send call
00292 
00293             QDBusMessage reply = QDBusInterface(app, obj, interface).
00294                                  call(method, urlList.toStringList());
00295             if (reply.arguments().count() < 1 || reply.arguments().at(0).type() != QVariant::Bool || !reply.arguments().at(0).toBool()) {
00296                 continue;
00297             }
00298 
00299         }
00300         if (cfg.hasKey("X-KDE-Protocol")) {
00301             const QString theProtocol = cfg.readEntry("X-KDE-Protocol");
00302             if (theProtocol.startsWith('!')) {
00303                 const QString excludedProtocol = theProtocol.mid(1);
00304                 if (excludedProtocol == protocol) {
00305                     continue;
00306                 }
00307             } else if (protocol != theProtocol) {
00308                 continue;
00309             }
00310         }
00311         else if (cfg.hasKey("X-KDE-Protocols")) {
00312             const QStringList protocols = cfg.readEntry("X-KDE-Protocols", QStringList());
00313             if (!protocols.contains(protocol)) {
00314                 continue;
00315             }
00316         }
00317         else if (protocol == "trash") {
00318             // Require servicemenus for the trash to ask for protocol=trash explicitly.
00319             // Trashed files aren't supposed to be available for actions.
00320             // One might want a servicemenu for trash.desktop itself though.
00321             continue;
00322         }
00323 
00324         if (cfg.hasKey("X-KDE-Require")) {
00325             const QStringList capabilities = cfg.readEntry("X-KDE-Require" , QStringList());
00326             if (capabilities.contains("Write") && !d->m_props.supportsWriting()) {
00327                 continue;
00328             }
00329         }
00330         if (cfg.hasKey("Actions") || cfg.hasKey("X-KDE-GetActionMenu")) {
00331             // Like KService, we support ServiceTypes, X-KDE-ServiceTypes, and MimeType.
00332             QStringList types = cfg.readEntry("ServiceTypes", QStringList());
00333             types += cfg.readEntry("X-KDE-ServiceTypes", QStringList());
00334             types += cfg.readXdgListEntry("MimeType");
00335             //kDebug() << file << types;
00336 
00337             if (types.isEmpty()) {
00338                 continue;
00339             }
00340             const QStringList excludeTypes = cfg.readEntry("ExcludeServiceTypes" , QStringList());
00341             bool ok = false;
00342 
00343             // check for exact matches or a typeglob'd mimetype if we have a mimetype
00344             for (QStringList::ConstIterator it = types.constBegin();
00345                  it != types.constEnd() && !ok;
00346                  ++it)
00347             {
00348                 // first check if we have an all mimetype
00349                 bool checkTheMimetypes = false;
00350                 if (*it == "all/all" ||
00351                     *it == "allfiles" /*compat with KDE up to 3.0.3*/) {
00352                     checkTheMimetypes = true;
00353                 }
00354 
00355                 // next, do we match all files?
00356                 if (!ok &&
00357                     !d->m_props.isDirectory() &&
00358                     *it == "all/allfiles") {
00359                     checkTheMimetypes = true;
00360                 }
00361 
00362                 // if we have a mimetype, see if we have an exact or a type globbed match
00363                 if (!ok && (
00364                     (mimeTypePtr && mimeTypePtr->is(*it)) ||
00365                     (!commonMimeGroup.isEmpty() &&
00366                      ((*it).right(1) == "*" &&
00367                       (*it).left((*it).indexOf('/')) == commonMimeGroup)))) {
00368                     checkTheMimetypes = true;
00369                 }
00370 
00371                 if (checkTheMimetypes) {
00372                     ok = true;
00373                     for (QStringList::ConstIterator itex = excludeTypes.constBegin(); itex != excludeTypes.constEnd(); ++itex) {
00374                         if(((*itex).endsWith('*') && (*itex).left((*itex).indexOf('/')) == commonMimeGroup) ||
00375                             ((*itex) == commonMimeType)) {
00376                             ok = false;
00377                             break;
00378                         }
00379                     }
00380                 }
00381             }
00382 
00383             if (ok) {
00384                 const QString priority = cfg.readEntry("X-KDE-Priority");
00385                 const QString submenuName = cfg.readEntry("X-KDE-Submenu");
00386 
00387                 ServiceList& list = s.selectList(priority, submenuName);
00388                 const ServiceList userServices = KDesktopFileActions::userDefinedServices(*(*it2), isLocal, urlList);
00389                 foreach (const KServiceAction& action, userServices) {
00390                     if (showGroup.readEntry(action.name(), true)) {
00391                         list += action;
00392                     }
00393                 }
00394             }
00395         }
00396     }
00397 
00398 
00399 
00400     QMenu* actionMenu = mainMenu;
00401     int userItemCount = 0;
00402     if (s.user.count() + s.userSubmenus.count() +
00403         s.userPriority.count() + s.userPrioritySubmenus.count() > 1) {
00404         // we have more than one item, so let's make a submenu
00405         actionMenu = new KMenu(i18nc("@title:menu", "&Actions"), mainMenu);
00406         actionMenu->menuAction()->setObjectName("actions_submenu"); // for the unittest
00407         mainMenu->addMenu(actionMenu);
00408     }
00409 
00410     userItemCount += d->insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
00411     userItemCount += d->insertServices(s.userPriority, actionMenu, false);
00412 
00413     // see if we need to put a separator between our priority items and our regular items
00414     if (userItemCount > 0 &&
00415         (s.user.count() > 0 ||
00416          s.userSubmenus.count() > 0 ||
00417          s.builtin.count() > 0) &&
00418         !actionMenu->actions().last()->isSeparator()) {
00419         actionMenu->addSeparator();
00420     }
00421     userItemCount += d->insertServicesSubmenus(s.userSubmenus, actionMenu, false);
00422     userItemCount += d->insertServices(s.user, actionMenu, false);
00423     userItemCount += d->insertServices(s.builtin, mainMenu, true);
00424     userItemCount += d->insertServicesSubmenus(s.userToplevelSubmenus, mainMenu, false);
00425     userItemCount += d->insertServices(s.userToplevel, mainMenu, false);
00426     return userItemCount;
00427 }
00428 
00429 
00430 // static
00431 KService::List KFileItemActions::associatedApplications(const QStringList& mimeTypeList, const QString& traderConstraint)
00432 {
00433     if (!KAuthorized::authorizeKAction("openwith") || mimeTypeList.isEmpty()) {
00434         return KService::List();
00435     }
00436 
00437     const KService::List firstOffers = KMimeTypeTrader::self()->query(mimeTypeList.first(), "Application", traderConstraint);
00438 
00439     QList<KFileItemActionsPrivate::ServiceRank> rankings;
00440     QStringList serviceList;
00441 
00442     // This section does two things.  First, it determines which services are common to all the given mimetypes.
00443     // Second, it ranks them based on their preference level in the associated applications list.
00444     // The more often a service appear near the front of the list, the LOWER its score.
00445 
00446     for (int i = 0; i < firstOffers.count(); ++i) {
00447         KFileItemActionsPrivate::ServiceRank tempRank;
00448         tempRank.service = firstOffers[i];
00449         tempRank.score = i;
00450         rankings << tempRank;
00451         serviceList << tempRank.service->storageId();
00452     }
00453 
00454     for (int j = 1; j < mimeTypeList.count(); ++j) {
00455         QStringList subservice; // list of services that support this mimetype
00456         const KService::List offers = KMimeTypeTrader::self()->query(mimeTypeList[j], "Application", traderConstraint);
00457         for (int i = 0; i != offers.count(); ++i) {
00458             const QString serviceId = offers[i]->storageId();
00459             subservice << serviceId;
00460             const int idPos = serviceList.indexOf(serviceId);
00461             if (idPos != -1) {
00462                 rankings[idPos].score += i;
00463             } // else: we ignore the services that didn't support the previous mimetypes
00464         }
00465 
00466         // Remove services which supported the previous mimetypes but don't support this one
00467         for (int i = 0; i < serviceList.count(); ++i) {
00468             if (!subservice.contains(serviceList[i])) {
00469                 serviceList.removeAt(i);
00470                 rankings.removeAt(i);
00471                 --i;
00472             }
00473         }
00474         // Nothing left -> there is no common application for these mimetypes
00475         if (rankings.isEmpty()) {
00476             return KService::List();
00477         }
00478     }
00479 
00480     qSort(rankings.begin(), rankings.end(), KFileItemActionsPrivate::lessRank);
00481 
00482     KService::List result;
00483     Q_FOREACH(const KFileItemActionsPrivate::ServiceRank& tempRank, rankings) {
00484         result << tempRank.service;
00485     }
00486 
00487     return result;
00488 }
00489 
00490 // KMimeTypeTrader::preferredService doesn't take a constraint
00491 static KService::Ptr preferredService(const QString& mimeType, const QString& constraint)
00492 {
00493     const KService::List services = KMimeTypeTrader::self()->query(mimeType, QString::fromLatin1("Application"), constraint);
00494     return !services.isEmpty() ? services.first() : KService::Ptr();
00495 }
00496 
00497 void KFileItemActions::addOpenWithActionsTo(QMenu* topMenu, const QString& traderConstraint)
00498 {
00499     if (!KAuthorized::authorizeKAction("openwith")) {
00500         return;
00501     }
00502 
00503     d->m_traderConstraint = traderConstraint;
00504     KService::List offers = associatedApplications(d->m_mimeTypeList, traderConstraint);
00505 
00507 
00508     const KFileItemList items = d->m_props.items();
00509     const KFileItem firstItem = items.first();
00510     const bool isLocal = firstItem.url().isLocalFile();
00511     // "Open With..." for folders is really not very useful, especially for remote folders.
00512     // (media:/something, or trash:/, or ftp://...)
00513     if (!d->m_props.isDirectory() || isLocal) {
00514         if (!topMenu->actions().isEmpty()) {
00515             topMenu->addSeparator();
00516         }
00517 
00518         KAction *runAct = new KAction(d->m_parentWidget);
00519         QString runActionName;
00520 
00521 
00522         const QStringList serviceIdList = d->listPreferredServiceIds(d->m_mimeTypeList, traderConstraint);
00523         //kDebug(7010) << "serviceIdList=" << serviceIdList;
00524 
00525         // When selecting files with multiple mimetypes, offer either "open with <app for all>"
00526         // or a generic <open> (if there are any apps associated).
00527         if (d->m_mimeTypeList.count() > 1
00528             && !serviceIdList.isEmpty()
00529             && !(serviceIdList.count()==1 && serviceIdList.first().isEmpty())) { // empty means "no apps associated"
00530 
00531             d->m_ownActions.append(runAct);
00532 
00533             if (serviceIdList.count() == 1) {
00534                 const KService::Ptr app = preferredService(d->m_mimeTypeList.first(), traderConstraint);
00535                 runActionName = i18n("&Open with %1", app->name());
00536                 runAct->setIcon(KIcon(app->icon()));
00537 
00538                 // Remove that app from the offers list (#242731)
00539                 for (int i = 0; i < offers.count() ; ++i) {
00540                     if (offers[i]->storageId() == app->storageId()) {
00541                         offers.removeAt(i);
00542                         break;
00543                     }
00544                 }
00545             } else {
00546                 runActionName = i18n("&Open");
00547             }
00548 
00549             runAct->setText(runActionName);
00550 
00551             d->m_traderConstraint = traderConstraint;
00552             d->m_fileOpenList = d->m_props.items();
00553             QObject::connect(runAct, SIGNAL(triggered()), d, SLOT(slotRunPreferredApplications()));
00554             topMenu->addAction(runAct);
00555         }
00556 
00557         if (!offers.isEmpty()) {
00558             QMenu* menu = topMenu;
00559 
00560             if (offers.count() > 1) { // submenu 'open with'
00561                 menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu);
00562                 menu->menuAction()->setObjectName("openWith_submenu"); // for the unittest
00563                 topMenu->addMenu(menu);
00564             }
00565             //kDebug() << offers.count() << "offers" << topMenu << menu;
00566 
00567             KService::List::ConstIterator it = offers.constBegin();
00568             for(; it != offers.constEnd(); it++) {
00569                 KAction* act = d->createAppAction(*it,
00570                                                   // no submenu -> prefix single offer
00571                                                   menu == topMenu);
00572                 menu->addAction(act);
00573             }
00574 
00575             QString openWithActionName;
00576             if (menu != topMenu) { // submenu
00577                 menu->addSeparator();
00578                 openWithActionName = i18nc("@action:inmenu Open With", "&Other...");
00579             } else {
00580                 openWithActionName = i18nc("@title:menu", "&Open With...");
00581             }
00582             KAction *openWithAct = new KAction(d->m_parentWidget);
00583             d->m_ownActions.append(openWithAct);
00584             openWithAct->setText(openWithActionName);
00585             QObject::connect(openWithAct, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
00586             menu->addAction(openWithAct);
00587         }
00588         else // no app offers -> Open With...
00589         {
00590             KAction *act = new KAction(d->m_parentWidget);
00591             d->m_ownActions.append(act);
00592             act->setText(i18nc("@title:menu", "&Open With..."));
00593             QObject::connect(act, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
00594             topMenu->addAction(act);
00595         }
00596 
00597     }
00598 }
00599 
00600 void KFileItemActionsPrivate::slotRunPreferredApplications()
00601 {
00602     const KFileItemList fileItems = m_fileOpenList;
00603 
00604     const QStringList mimeTypeList = listMimeTypes(fileItems);
00605     const QStringList serviceIdList = listPreferredServiceIds(mimeTypeList, m_traderConstraint);
00606 
00607     foreach (const QString serviceId, serviceIdList) {
00608         KFileItemList serviceItems;
00609         foreach (const KFileItem& item, fileItems) {
00610             const KService::Ptr serv = preferredService(item.mimetype(), m_traderConstraint);
00611             const QString preferredServiceId = serv ? serv->storageId() : QString();
00612             if (preferredServiceId == serviceId) {
00613                 serviceItems << item;
00614             }
00615         }
00616 
00617         if (serviceId.isEmpty()) { // empty means: no associated app for this mimetype
00618             openWithByMime(serviceItems);
00619             continue;
00620         }
00621 
00622         const KService::Ptr servicePtr = KService::serviceByStorageId(serviceId);
00623         if (servicePtr.isNull()) {
00624             KRun::displayOpenWithDialog(serviceItems.urlList(), m_parentWidget);
00625             continue;
00626         }
00627         KRun::run(*servicePtr, serviceItems.urlList(), m_parentWidget);
00628     }
00629 }
00630 
00631 void KFileItemActions::runPreferredApplications(const KFileItemList& fileOpenList, const QString& traderConstraint)
00632 {
00633     d->m_fileOpenList = fileOpenList;
00634     d->m_traderConstraint = traderConstraint;
00635     d->slotRunPreferredApplications();
00636 }
00637 
00638 void KFileItemActionsPrivate::openWithByMime(const KFileItemList& fileItems)
00639 {
00640     const QStringList mimeTypeList = listMimeTypes(fileItems);
00641     foreach (const QString mimeType, mimeTypeList) {
00642         KFileItemList mimeItems;
00643         foreach (const KFileItem& item, fileItems) {
00644             if (item.mimetype() == mimeType) {
00645                 mimeItems << item;
00646             }
00647         }
00648         KRun::displayOpenWithDialog(mimeItems.urlList(), m_parentWidget);
00649     }
00650 }
00651 
00652 void KFileItemActionsPrivate::slotRunApplication(QAction* act)
00653 {
00654     // Is it an application, from one of the "Open With" actions
00655     KService::Ptr app = act->data().value<KService::Ptr>();
00656     Q_ASSERT(app);
00657     if (app) {
00658         KRun::run(*app, m_props.urlList(), m_parentWidget);
00659     }
00660 }
00661 
00662 void KFileItemActionsPrivate::slotOpenWithDialog()
00663 {
00664     // The item 'Other...' or 'Open With...' has been selected
00665     emit q->openWithDialogAboutToBeShown();
00666     KRun::displayOpenWithDialog(m_props.urlList(), m_parentWidget);
00667 }
00668 
00669 QStringList KFileItemActionsPrivate::listMimeTypes(const KFileItemList& items)
00670 {
00671     QStringList mimeTypeList;
00672     foreach (const KFileItem& item, items) {
00673         if (!mimeTypeList.contains(item.mimetype()))
00674             mimeTypeList << item.mimetype();
00675     }
00676     return mimeTypeList;
00677 }
00678 
00679 QStringList KFileItemActionsPrivate::listPreferredServiceIds(const QStringList& mimeTypeList, const QString& traderConstraint)
00680 {
00681     QStringList serviceIdList;
00682     Q_FOREACH(const QString& mimeType, mimeTypeList) {
00683         const KService::Ptr serv = preferredService(mimeType, traderConstraint);
00684         const QString newOffer = serv ? serv->storageId() : QString();
00685         serviceIdList << newOffer;
00686     }
00687     serviceIdList.removeDuplicates();
00688     return serviceIdList;
00689 }
00690 
00691 KAction* KFileItemActionsPrivate::createAppAction(const KService::Ptr& service, bool singleOffer)
00692 {
00693     QString actionName(service->name().replace('&', "&&"));
00694     if (singleOffer) {
00695         actionName = i18n("Open &with %1", actionName);
00696     } else {
00697         actionName = i18nc("@item:inmenu Open With, %1 is application name", "%1", actionName);
00698     }
00699 
00700     KAction *act = new KAction(m_parentWidget);
00701     m_ownActions.append(act);
00702     act->setIcon(KIcon(service->icon()));
00703     act->setText(actionName);
00704     act->setData(QVariant::fromValue(service));
00705     m_runApplicationActionGroup.addAction(act);
00706     return act;
00707 }
00708 
00709 KAction* KFileItemActions::preferredOpenWithAction(const QString& traderConstraint)
00710 {
00711     const KService::List offers = associatedApplications(d->m_mimeTypeList, traderConstraint);
00712     if (offers.isEmpty()) {
00713         return 0;
00714     }
00715     return d->createAppAction(offers.first(), true);
00716 }
00717 
00718 void KFileItemActions::setParentWidget(QWidget* widget)
00719 {
00720     d->m_parentWidget = widget;
00721 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:55:20 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal