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