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

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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.3
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