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

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal