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 }
KDE 4.6 API Reference