Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16571163
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
38 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/kio/kio/kfileitemactions.cpp b/kio/kio/kfileitemactions.cpp
index 3b44e0be6b..c79a434103 100644
--- a/kio/kio/kfileitemactions.cpp
+++ b/kio/kio/kfileitemactions.cpp
@@ -1,719 +1,721 @@
/* This file is part of the KDE project
Copyright (C) 1998-2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License or
( at your option ) version 3 or, at the discretion of KDE e.V.
( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kfileitemactions.h"
#include "kfileitemactions_p.h"
#include <kaction.h>
#include <krun.h>
#include <kmimetypetrader.h>
#include <kdebug.h>
#include <kdesktopfileactions.h>
#include <kmenu.h>
#include <klocale.h>
#include <kauthorized.h>
#include <kconfiggroup.h>
#include <kdesktopfile.h>
#include <kglobal.h>
#include <kicon.h>
#include <kstandarddirs.h>
#include <kservicetypetrader.h>
#include <QFile>
#include <QtAlgorithms>
#include <QtDBus/QtDBus>
static bool KIOSKAuthorizedAction(const KConfigGroup& cfg)
{
if (!cfg.hasKey("X-KDE-AuthorizeAction")) {
return true;
}
const QStringList list = cfg.readEntry("X-KDE-AuthorizeAction", QStringList());
for(QStringList::ConstIterator it = list.constBegin();
it != list.constEnd(); ++it) {
if (!KAuthorized::authorize((*it).trimmed())) {
return false;
}
}
return true;
}
// This helper class stores the .desktop-file actions and the servicemenus
// in order to support X-KDE-Priority and X-KDE-Submenu.
namespace KIO {
class PopupServices
{
public:
ServiceList& selectList(const QString& priority, const QString& submenuName);
ServiceList builtin;
ServiceList user, userToplevel, userPriority;
QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus;
};
ServiceList& PopupServices::selectList(const QString& priority, const QString& submenuName)
{
// we use the categories .desktop entry to define submenus
// if none is defined, we just pop it in the main menu
if (submenuName.isEmpty()) {
if (priority == "TopLevel") {
return userToplevel;
} else if (priority == "Important") {
return userPriority;
}
} else if (priority == "TopLevel") {
return userToplevelSubmenus[submenuName];
} else if (priority == "Important") {
return userPrioritySubmenus[submenuName];
} else {
return userSubmenus[submenuName];
}
return user;
}
} // namespace
////
-KFileItemActionsPrivate::KFileItemActionsPrivate()
+KFileItemActionsPrivate::KFileItemActionsPrivate(KFileItemActions *qq)
: QObject(),
+ q(qq),
m_executeServiceActionGroup(static_cast<QWidget *>(0)),
m_runApplicationActionGroup(static_cast<QWidget *>(0)),
m_parentWidget(0)
{
QObject::connect(&m_executeServiceActionGroup, SIGNAL(triggered(QAction*)),
this, SLOT(slotExecuteService(QAction*)));
QObject::connect(&m_runApplicationActionGroup, SIGNAL(triggered(QAction*)),
this, SLOT(slotRunApplication(QAction*)));
}
KFileItemActionsPrivate::~KFileItemActionsPrivate()
{
qDeleteAll(m_ownActions);
}
int KFileItemActionsPrivate::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus,
QMenu* menu,
bool isBuiltin)
{
int count = 0;
QMap<QString, ServiceList>::ConstIterator it;
for (it = submenus.begin(); it != submenus.end(); ++it) {
if (it.value().isEmpty()) {
//avoid empty sub-menus
continue;
}
QMenu* actionSubmenu = new KMenu(menu);
actionSubmenu->setTitle(it.key());
actionSubmenu->menuAction()->setObjectName("services_submenu"); // for the unittest
menu->addMenu(actionSubmenu);
count += insertServices(it.value(), actionSubmenu, isBuiltin);
}
return count;
}
int KFileItemActionsPrivate::insertServices(const ServiceList& list,
QMenu* menu,
bool isBuiltin)
{
int count = 0;
ServiceList::const_iterator it = list.begin();
for(; it != list.end(); ++it) {
if ((*it).isSeparator()) {
const QList<QAction*> actions = menu->actions();
if (!actions.isEmpty() && !actions.last()->isSeparator()) {
menu->addSeparator();
}
continue;
}
if (isBuiltin || !(*it).noDisplay()) {
KAction *act = new KAction(m_parentWidget);
m_ownActions.append(act);
act->setObjectName("menuaction"); // for the unittest
QString text = (*it).text();
text.replace('&',"&&");
act->setText(text);
if (!(*it).icon().isEmpty()) {
act->setIcon(KIcon((*it).icon()));
}
act->setData(QVariant::fromValue(*it));
m_executeServiceActionGroup.addAction(act);
menu->addAction(act); // Add to toplevel menu
++count;
}
}
return count;
}
void KFileItemActionsPrivate::slotExecuteService(QAction* act)
{
KServiceAction serviceAction = act->data().value<KServiceAction>();
if (KAuthorized::authorizeKAction(serviceAction.name())) {
KDesktopFileActions::executeService(m_props.urlList(), serviceAction);
}
}
////
KFileItemActions::KFileItemActions(QObject* parent)
- : QObject(parent), d(new KFileItemActionsPrivate)
+ : QObject(parent), d(new KFileItemActionsPrivate(this))
{
}
KFileItemActions::~KFileItemActions()
{
delete d;
}
void KFileItemActions::setItemListProperties(const KFileItemListProperties& itemListProperties)
{
d->m_props = itemListProperties;
d->m_mimeTypeList.clear();
const KFileItemList items = d->m_props.items();
KFileItemList::const_iterator kit = items.constBegin();
const KFileItemList::const_iterator kend = items.constEnd();
for (; kit != kend; ++kit) {
if (!d->m_mimeTypeList.contains((*kit).mimetype()))
d->m_mimeTypeList << (*kit).mimetype();
}
}
int KFileItemActions::addServiceActionsTo(QMenu* mainMenu)
{
const KFileItemList items = d->m_props.items();
const KFileItem firstItem = items.first();
const QString protocol = firstItem.url().protocol(); // assumed to be the same for all items
const bool isLocal = !firstItem.localPath().isEmpty();
const bool isSingleLocal = items.count() == 1 && isLocal;
const KUrl::List urlList = d->m_props.urlList();
KIO::PopupServices s;
// 1 - Look for builtin and user-defined services
if (isSingleLocal && (d->m_props.mimeType() == "application/x-desktop" || // .desktop file
d->m_props.mimeType() == "inode/blockdevice")) { // dev file
// get builtin services, like mount/unmount
const QString path = firstItem.localPath();
s.builtin = KDesktopFileActions::builtinServices(path);
KDesktopFile desktopFile(path);
KConfigGroup cfg = desktopFile.desktopGroup();
const QString priority = cfg.readEntry("X-KDE-Priority");
const QString submenuName = cfg.readEntry("X-KDE-Submenu");
#if 0
if (cfg.readEntry("Type") == "Link") {
d->m_url = cfg.readEntry("URL");
// TODO: Do we want to make all the actions apply on the target
// of the .desktop file instead of the .desktop file itself?
}
#endif
ServiceList& list = s.selectList(priority, submenuName);
list = KDesktopFileActions::userDefinedServices(path, desktopFile, true /*isLocal*/);
}
// 2 - Look for "servicemenus" bindings (user-defined services)
// first check the .directory if this is a directory
if (d->m_props.isDirectory() && isSingleLocal) {
QString dotDirectoryFile = KUrl::fromPath(firstItem.localPath()).path(KUrl::AddTrailingSlash).append(".directory");
if (QFile::exists(dotDirectoryFile)) {
const KDesktopFile desktopFile(dotDirectoryFile);
const KConfigGroup cfg = desktopFile.desktopGroup();
if (KIOSKAuthorizedAction(cfg)) {
const QString priority = cfg.readEntry("X-KDE-Priority");
const QString submenuName = cfg.readEntry("X-KDE-Submenu");
ServiceList& list = s.selectList(priority, submenuName);
list += KDesktopFileActions::userDefinedServices(dotDirectoryFile, desktopFile, true);
}
}
}
const KConfig config("kservicemenurc", KConfig::NoGlobals);
const KConfigGroup showGroup = config.group("Show");
const QString commonMimeType = d->m_props.mimeType();
const QString commonMimeGroup = d->m_props.mimeGroup();
const KMimeType::Ptr mimeTypePtr = commonMimeType.isEmpty() ? KMimeType::Ptr() : KMimeType::mimeType(commonMimeType);
const KService::List entries = KServiceTypeTrader::self()->query("KonqPopupMenu/Plugin");
KService::List::const_iterator eEnd = entries.end();
for (KService::List::const_iterator it2 = entries.begin(); it2 != eEnd; ++it2) {
QString file = KStandardDirs::locate("services", (*it2)->entryPath());
KDesktopFile desktopFile(file);
const KConfigGroup cfg = desktopFile.desktopGroup();
if (!KIOSKAuthorizedAction(cfg)) {
continue;
}
if (cfg.hasKey("X-KDE-ShowIfRunning")) {
const QString app = cfg.readEntry("X-KDE-ShowIfRunning");
if (QDBusConnection::sessionBus().interface()->isServiceRegistered(app)) {
continue;
}
}
if (cfg.hasKey("X-KDE-ShowIfDBusCall")) {
QString calldata = cfg.readEntry("X-KDE-ShowIfDBusCall");
const QStringList parts = calldata.split(' ');
const QString &app = parts.at(0);
const QString &obj = parts.at(1);
QString interface = parts.at(2);
QString method;
int pos = interface.lastIndexOf(QLatin1Char('.'));
if (pos != -1) {
method = interface.mid(pos + 1);
interface.truncate(pos);
}
//if (!QDBus::sessionBus().busService()->nameHasOwner(app))
// continue; //app does not exist so cannot send call
QDBusMessage reply = QDBusInterface(app, obj, interface).
call(method, urlList.toStringList());
if (reply.arguments().count() < 1 || reply.arguments().at(0).type() != QVariant::Bool || !reply.arguments().at(0).toBool()) {
continue;
}
}
if (cfg.hasKey("X-KDE-Protocol")) {
const QString theProtocol = cfg.readEntry("X-KDE-Protocol");
if (theProtocol.startsWith('!')) {
const QString excludedProtocol = theProtocol.mid(1);
if (excludedProtocol == protocol) {
continue;
}
} else if (protocol != theProtocol) {
continue;
}
}
else if (cfg.hasKey("X-KDE-Protocols")) {
const QStringList protocols = cfg.readEntry("X-KDE-Protocols", QStringList());
if (!protocols.contains(protocol)) {
continue;
}
}
else if (protocol == "trash") {
// Require servicemenus for the trash to ask for protocol=trash explicitly.
// Trashed files aren't supposed to be available for actions.
// One might want a servicemenu for trash.desktop itself though.
continue;
}
if (cfg.hasKey("X-KDE-Require")) {
const QStringList capabilities = cfg.readEntry("X-KDE-Require" , QStringList());
if (capabilities.contains("Write") && !d->m_props.supportsWriting()) {
continue;
}
}
if (cfg.hasKey("Actions") || cfg.hasKey("X-KDE-GetActionMenu")) {
// Like KService, we support ServiceTypes, X-KDE-ServiceTypes, and MimeType.
QStringList types = cfg.readEntry("ServiceTypes", QStringList());
types += cfg.readEntry("X-KDE-ServiceTypes", QStringList());
types += cfg.readXdgListEntry("MimeType");
//kDebug() << file << types;
if (types.isEmpty()) {
continue;
}
const QStringList excludeTypes = cfg.readEntry("ExcludeServiceTypes" , QStringList());
bool ok = false;
// check for exact matches or a typeglob'd mimetype if we have a mimetype
for (QStringList::ConstIterator it = types.constBegin();
it != types.constEnd() && !ok;
++it)
{
// first check if we have an all mimetype
bool checkTheMimetypes = false;
if (*it == "all/all" ||
*it == "allfiles" /*compat with KDE up to 3.0.3*/) {
checkTheMimetypes = true;
}
// next, do we match all files?
if (!ok &&
!d->m_props.isDirectory() &&
*it == "all/allfiles") {
checkTheMimetypes = true;
}
// if we have a mimetype, see if we have an exact or a type globbed match
if (!ok && (
(mimeTypePtr && mimeTypePtr->is(*it)) ||
(!commonMimeGroup.isEmpty() &&
((*it).right(1) == "*" &&
(*it).left((*it).indexOf('/')) == commonMimeGroup)))) {
checkTheMimetypes = true;
}
if (checkTheMimetypes) {
ok = true;
for (QStringList::ConstIterator itex = excludeTypes.constBegin(); itex != excludeTypes.constEnd(); ++itex) {
if(((*itex).endsWith('*') && (*itex).left((*itex).indexOf('/')) == commonMimeGroup) ||
((*itex) == commonMimeType)) {
ok = false;
break;
}
}
}
}
if (ok) {
const QString priority = cfg.readEntry("X-KDE-Priority");
const QString submenuName = cfg.readEntry("X-KDE-Submenu");
ServiceList& list = s.selectList(priority, submenuName);
const ServiceList userServices = KDesktopFileActions::userDefinedServices(*(*it2), isLocal, urlList);
foreach (const KServiceAction& action, userServices) {
if (showGroup.readEntry(action.name(), true)) {
list += action;
}
}
}
}
}
QMenu* actionMenu = mainMenu;
int userItemCount = 0;
if (s.user.count() + s.userSubmenus.count() +
s.userPriority.count() + s.userPrioritySubmenus.count() > 1) {
// we have more than one item, so let's make a submenu
actionMenu = new KMenu(i18nc("@title:menu", "&Actions"), mainMenu);
actionMenu->menuAction()->setObjectName("actions_submenu"); // for the unittest
mainMenu->addMenu(actionMenu);
}
userItemCount += d->insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false);
userItemCount += d->insertServices(s.userPriority, actionMenu, false);
// see if we need to put a separator between our priority items and our regular items
if (userItemCount > 0 &&
(s.user.count() > 0 ||
s.userSubmenus.count() > 0 ||
s.builtin.count() > 0) &&
!actionMenu->actions().last()->isSeparator()) {
actionMenu->addSeparator();
}
userItemCount += d->insertServicesSubmenus(s.userSubmenus, actionMenu, false);
userItemCount += d->insertServices(s.user, actionMenu, false);
userItemCount += d->insertServices(s.builtin, mainMenu, true);
userItemCount += d->insertServicesSubmenus(s.userToplevelSubmenus, mainMenu, false);
userItemCount += d->insertServices(s.userToplevel, mainMenu, false);
return userItemCount;
}
// static
KService::List KFileItemActions::associatedApplications(const QStringList& mimeTypeList, const QString& traderConstraint)
{
if (!KAuthorized::authorizeKAction("openwith") || mimeTypeList.isEmpty()) {
return KService::List();
}
const KService::List firstOffers = KMimeTypeTrader::self()->query(mimeTypeList.first(), "Application", traderConstraint);
QList<KFileItemActionsPrivate::ServiceRank> rankings;
QStringList serviceList;
// This section does two things. First, it determines which services are common to all the given mimetypes.
// Second, it ranks them based on their preference level in the associated applications list.
// The more often a service appear near the front of the list, the LOWER its score.
for (int i = 0; i < firstOffers.count(); ++i) {
KFileItemActionsPrivate::ServiceRank tempRank;
tempRank.service = firstOffers[i];
tempRank.score = i;
rankings << tempRank;
serviceList << tempRank.service->storageId();
}
for (int j = 1; j < mimeTypeList.count(); ++j) {
QStringList subservice; // list of services that support this mimetype
const KService::List offers = KMimeTypeTrader::self()->query(mimeTypeList[j], "Application", traderConstraint);
for (int i = 0; i != offers.count(); ++i) {
const QString serviceId = offers[i]->storageId();
subservice << serviceId;
const int idPos = serviceList.indexOf(serviceId);
if (idPos != -1) {
rankings[idPos].score += i;
} // else: we ignore the services that didn't support the previous mimetypes
}
// Remove services which supported the previous mimetypes but don't support this one
for (int i = 0; i < serviceList.count(); ++i) {
if (!subservice.contains(serviceList[i])) {
serviceList.removeAt(i);
rankings.removeAt(i);
--i;
}
}
// Nothing left -> there is no common application for these mimetypes
if (rankings.isEmpty()) {
return KService::List();
}
}
qSort(rankings.begin(), rankings.end(), KFileItemActionsPrivate::lessRank);
KService::List result;
Q_FOREACH(const KFileItemActionsPrivate::ServiceRank& tempRank, rankings) {
result << tempRank.service;
}
return result;
}
// KMimeTypeTrader::preferredService doesn't take a constraint
static KService::Ptr preferredService(const QString& mimeType, const QString& constraint)
{
const KService::List services = KMimeTypeTrader::self()->query(mimeType, QString::fromLatin1("Application"), constraint);
return !services.isEmpty() ? services.first() : KService::Ptr();
}
void KFileItemActions::addOpenWithActionsTo(QMenu* topMenu, const QString& traderConstraint)
{
if (!KAuthorized::authorizeKAction("openwith")) {
return;
}
d->m_traderConstraint = traderConstraint;
KService::List offers = associatedApplications(d->m_mimeTypeList, traderConstraint);
//// Ok, we have everything, now insert
const KFileItemList items = d->m_props.items();
const KFileItem firstItem = items.first();
const bool isLocal = firstItem.url().isLocalFile();
// "Open With..." for folders is really not very useful, especially for remote folders.
// (media:/something, or trash:/, or ftp://...)
if (!d->m_props.isDirectory() || isLocal) {
if (!topMenu->actions().isEmpty()) {
topMenu->addSeparator();
}
KAction *runAct = new KAction(d->m_parentWidget);
QString runActionName;
const QStringList serviceIdList = d->listPreferredServiceIds(d->m_mimeTypeList, traderConstraint);
//kDebug(7010) << "serviceIdList=" << serviceIdList;
// When selecting files with multiple mimetypes, offer either "open with <app for all>"
// or a generic <open> (if there are any apps associated).
if (d->m_mimeTypeList.count() > 1
&& !serviceIdList.isEmpty()
&& !(serviceIdList.count()==1 && serviceIdList.first().isEmpty())) { // empty means "no apps associated"
d->m_ownActions.append(runAct);
if (serviceIdList.count() == 1) {
const KService::Ptr app = preferredService(d->m_mimeTypeList.first(), traderConstraint);
runActionName = i18n("&Open with %1", app->name());
runAct->setIcon(KIcon(app->icon()));
// Remove that app from the offers list (#242731)
for (int i = 0; i < offers.count() ; ++i) {
if (offers[i]->storageId() == app->storageId()) {
offers.removeAt(i);
break;
}
}
} else {
runActionName = i18n("&Open");
}
runAct->setText(runActionName);
d->m_traderConstraint = traderConstraint;
d->m_fileOpenList = d->m_props.items();
QObject::connect(runAct, SIGNAL(triggered()), d, SLOT(slotRunPreferredApplications()));
topMenu->addAction(runAct);
}
if (!offers.isEmpty()) {
QMenu* menu = topMenu;
if (offers.count() > 1) { // submenu 'open with'
menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu);
menu->menuAction()->setObjectName("openWith_submenu"); // for the unittest
topMenu->addMenu(menu);
}
//kDebug() << offers.count() << "offers" << topMenu << menu;
KService::List::ConstIterator it = offers.constBegin();
for(; it != offers.constEnd(); it++) {
KAction* act = d->createAppAction(*it,
// no submenu -> prefix single offer
menu == topMenu);
menu->addAction(act);
}
QString openWithActionName;
if (menu != topMenu) { // submenu
menu->addSeparator();
openWithActionName = i18nc("@action:inmenu Open With", "&Other...");
} else {
openWithActionName = i18nc("@title:menu", "&Open With...");
}
KAction *openWithAct = new KAction(d->m_parentWidget);
d->m_ownActions.append(openWithAct);
openWithAct->setText(openWithActionName);
QObject::connect(openWithAct, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
menu->addAction(openWithAct);
}
else // no app offers -> Open With...
{
KAction *act = new KAction(d->m_parentWidget);
d->m_ownActions.append(act);
act->setText(i18nc("@title:menu", "&Open With..."));
QObject::connect(act, SIGNAL(triggered()), d, SLOT(slotOpenWithDialog()));
topMenu->addAction(act);
}
}
}
void KFileItemActionsPrivate::slotRunPreferredApplications()
{
const KFileItemList fileItems = m_fileOpenList;
const QStringList mimeTypeList = listMimeTypes(fileItems);
const QStringList serviceIdList = listPreferredServiceIds(mimeTypeList, m_traderConstraint);
foreach (const QString serviceId, serviceIdList) {
KFileItemList serviceItems;
foreach (const KFileItem& item, fileItems) {
const KService::Ptr serv = preferredService(item.mimetype(), m_traderConstraint);
const QString preferredServiceId = serv ? serv->storageId() : QString();
if (preferredServiceId == serviceId) {
serviceItems << item;
}
}
if (serviceId.isEmpty()) { // empty means: no associated app for this mimetype
openWithByMime(serviceItems);
continue;
}
const KService::Ptr servicePtr = KService::serviceByStorageId(serviceId);
if (servicePtr.isNull()) {
KRun::displayOpenWithDialog(serviceItems.urlList(), m_parentWidget);
continue;
}
KRun::run(*servicePtr, serviceItems.urlList(), m_parentWidget);
}
}
void KFileItemActions::runPreferredApplications(const KFileItemList& fileOpenList, const QString& traderConstraint)
{
d->m_fileOpenList = fileOpenList;
d->m_traderConstraint = traderConstraint;
d->slotRunPreferredApplications();
}
void KFileItemActionsPrivate::openWithByMime(const KFileItemList& fileItems)
{
const QStringList mimeTypeList = listMimeTypes(fileItems);
foreach (const QString mimeType, mimeTypeList) {
KFileItemList mimeItems;
foreach (const KFileItem& item, fileItems) {
if (item.mimetype() == mimeType) {
mimeItems << item;
}
}
KRun::displayOpenWithDialog(mimeItems.urlList(), m_parentWidget);
}
}
void KFileItemActionsPrivate::slotRunApplication(QAction* act)
{
// Is it an application, from one of the "Open With" actions
KService::Ptr app = act->data().value<KService::Ptr>();
Q_ASSERT(app);
if (app) {
KRun::run(*app, m_props.urlList(), m_parentWidget);
}
}
void KFileItemActionsPrivate::slotOpenWithDialog()
{
// The item 'Other...' or 'Open With...' has been selected
+ emit q->openWithDialogAboutToBeShown();
KRun::displayOpenWithDialog(m_props.urlList(), m_parentWidget);
}
QStringList KFileItemActionsPrivate::listMimeTypes(const KFileItemList& items)
{
QStringList mimeTypeList;
foreach (const KFileItem& item, items) {
if (!mimeTypeList.contains(item.mimetype()))
mimeTypeList << item.mimetype();
}
return mimeTypeList;
}
QStringList KFileItemActionsPrivate::listPreferredServiceIds(const QStringList& mimeTypeList, const QString& traderConstraint)
{
QStringList serviceIdList;
Q_FOREACH(const QString& mimeType, mimeTypeList) {
const KService::Ptr serv = preferredService(mimeType, traderConstraint);
const QString newOffer = serv ? serv->storageId() : QString();
serviceIdList << newOffer;
}
serviceIdList.removeDuplicates();
return serviceIdList;
}
KAction* KFileItemActionsPrivate::createAppAction(const KService::Ptr& service, bool singleOffer)
{
QString actionName(service->name().replace('&', "&&"));
if (singleOffer) {
actionName = i18n("Open &with %1", actionName);
} else {
actionName = i18nc("@item:inmenu Open With, %1 is application name", "%1", actionName);
}
KAction *act = new KAction(m_parentWidget);
m_ownActions.append(act);
act->setIcon(KIcon(service->icon()));
act->setText(actionName);
act->setData(QVariant::fromValue(service));
m_runApplicationActionGroup.addAction(act);
return act;
}
KAction* KFileItemActions::preferredOpenWithAction(const QString& traderConstraint)
{
const KService::List offers = associatedApplications(d->m_mimeTypeList, traderConstraint);
if (offers.isEmpty()) {
return 0;
}
return d->createAppAction(offers.first(), true);
}
void KFileItemActions::setParentWidget(QWidget* widget)
{
d->m_parentWidget = widget;
}
diff --git a/kio/kio/kfileitemactions.h b/kio/kio/kfileitemactions.h
index 685a33e6ee..27ab4e3353 100644
--- a/kio/kio/kfileitemactions.h
+++ b/kio/kio/kfileitemactions.h
@@ -1,148 +1,157 @@
/* This file is part of the KDE project
Copyright (C) 1998-2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License or
( at your option ) version 3 or, at the discretion of KDE e.V.
( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KFILEITEMACTIONS_H
#define KFILEITEMACTIONS_H
#include <kservice.h>
#include <kfileitem.h>
#include <kio/kio_export.h>
class KFileItemListProperties;
class KAction;
class QMenu;
class KFileItemActionsPrivate;
/**
* This class creates and handles the actions for a url (or urls) in a popupmenu.
*
* This includes:
* @li "open with <application>" actions, but also
* @li builtin services like mount/unmount for old-style device desktop files
* @li user-defined actions for a .desktop file, defined in the file itself (see the desktop entry standard)
* @li servicemenus actions, defined in .desktop files and selected based on the mimetype of the url
*
* @since 4.3
*/
class KIO_EXPORT KFileItemActions : public QObject
{
Q_OBJECT
public:
/**
* Creates a KFileItemActions instance.
* Note that this instance must stay alive for at least as long as the popupmenu;
* it has the slots for the actions created by addOpenWithActionsTo/addServiceActionsTo.
*/
KFileItemActions(QObject* parent = 0);
/**
* Destructor
*/
~KFileItemActions();
/**
* Sets all the data for the next instance of the popupmenu.
* @see KFileItemListProperties
*/
void setItemListProperties(const KFileItemListProperties& itemList);
/**
* Set the parent widget for any dialogs being shown.
*
* This should normally be your mainwindow, not a popup menu,
* so that it still exists even after the popup is closed
* (e.g. error message from KRun) and so that QAction::setStatusTip
* can find a statusbar, too.
*/
void setParentWidget(QWidget* widget);
/**
* Generate the "Open With <Application>" actions, and adds them to the @p menu.
* All actions are created as children of the menu.
* @param menu the QMenu where to add actions
* @param traderConstraint this constraint allows to exclude the current application
* from the "open with" list. Example: "DesktopEntryName != 'kfmclient'".
* (Default value added in kdelibs-4.5, pass QString() explicitely for earlier versions).
*/
void addOpenWithActionsTo(QMenu* menu, const QString& traderConstraint = QString());
/**
* Returns an action for the preferred application only.
* @param traderConstraint this constraint allows to exclude the current application
* from the "open with" list. Example: "DesktopEntryName != 'kfmclient'".
* @return the action - or 0 if no application was found.
*/
KAction* preferredOpenWithAction(const QString& traderConstraint);
/**
* Helper method used internally, can also be used for similar GUIs that
* show the list of associated applications.
* Used in KParts::BrowserOpenOrSaveQuestion for example.
*
* This is basically a KMimeTypeTrader::query, but it supports multiple mimetypes, and
* also cleans up "apparent" duplicates, such as the kde3 and kde4 applications with the same name.
*
* The list is sorted according to the user preferences for the given mimetype(s).
* In case multiple mimetypes appear in the url list, the logic is:
* applications that on average appear earlier on the associated applications
* list for the given mimetypes also appear earlier on the final applications list.
*
* Note that for a single mimetype there is no need to use this, you should use
* KMimeTypeTrader instead, e.g. query() or preferredService().
*
* Returns the applications associated with all the given mimetypes.
* @param mimeTypeList the mimetypes
* @param traderConstraint this optional constraint allows to exclude the current application
* from the "open with" list. Example: "DesktopEntryName != 'kfmclient'".
* @return the sorted list of services.
* @since 4.4
*/
static KService::List associatedApplications(const QStringList& mimeTypeList, const QString& traderConstraint);
/**
* Generate the user-defined actions and submenus, and adds them to the @p menu.
* User-defined actions include:
* - builtin services like mount/unmount for old-style device desktop files
* - user-defined actions for a .desktop file, defined in the file itself (see the desktop entry standard)
* - servicemenus actions, defined in .desktop files and selected based on the mimetype of the url
*
* When KFileItemListProperties::supportsWriting() is false, actions that modify the files are not shown.
* This is controlled by Require=Write in the servicemenu desktop files.
*
* All actions are created as children of the menu.
* @return the number of actions added
*/
int addServiceActionsTo(QMenu* menu);
+Q_SIGNALS:
+ /**
+ * Emitted before the "Open With" dialog is shown
+ * This is used e.g in folderview to close the folder peek popups on invoking the "Open With" menu action
+ * @since 4.8.2
+ */
+ void openWithDialogAboutToBeShown();
+
public Q_SLOTS:
/**
* Slot used to execute a list of files in their respective preferred application.
* @param fileOpenList the list of KFileItems to open.
* @param traderConstraint this optional constraint allows to exclude the current application
* @since 4.5
*/
void runPreferredApplications(const KFileItemList& fileOpenList, const QString& traderConstraint);
private:
KFileItemActionsPrivate* const d;
+ friend class KFileItemActionsPrivate;
};
#endif /* KFILEITEMACTIONS_H */
diff --git a/kio/kio/kfileitemactions_p.h b/kio/kio/kfileitemactions_p.h
index 56f1a62f04..f33dfeb50b 100644
--- a/kio/kio/kfileitemactions_p.h
+++ b/kio/kio/kfileitemactions_p.h
@@ -1,91 +1,94 @@
/* This file is part of the KDE project
Copyright (C) 1998-2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License or
( at your option ) version 3 or, at the discretion of KDE e.V.
( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KFILEITEMACTIONS_P_H
#define KFILEITEMACTIONS_P_H
#include <kfileitemlistproperties.h>
#include <kfileitem.h>
#include <kactioncollection.h>
#include <kserviceaction.h>
#include <kservice.h>
#include <QActionGroup>
#include <QObject>
+class KFileItemActions;
+
typedef QList<KServiceAction> ServiceList;
class KFileItemActionsPrivate : public QObject
{
Q_OBJECT
public:
- KFileItemActionsPrivate();
+ KFileItemActionsPrivate(KFileItemActions *qq);
~KFileItemActionsPrivate();
int insertServicesSubmenus(const QMap<QString, ServiceList>& list, QMenu* menu, bool isBuiltin);
int insertServices(const ServiceList& list, QMenu* menu, bool isBuiltin);
// For "open with"
KService::List associatedApplications(const QString& traderConstraint);
KAction* createAppAction(const KService::Ptr& service, bool singleOffer);
struct ServiceRank
{
int score;
KService::Ptr service;
};
// Inline function for sorting lists of ServiceRank
static bool lessRank(const ServiceRank& id1, const ServiceRank& id2)
{
return id1.score < id2.score;
}
QStringList listMimeTypes(const KFileItemList& items);
QStringList listPreferredServiceIds(const QStringList& mimeTypeList, const QString& traderConstraint);
public Q_SLOTS:
void slotRunPreferredApplications();
private:
void openWithByMime(const KFileItemList& fileItems);
private Q_SLOTS:
// For servicemenus
void slotExecuteService(QAction* act);
// For "open with" applications
void slotRunApplication(QAction* act);
void slotOpenWithDialog();
public:
+ KFileItemActions* const q;
KFileItemListProperties m_props;
QStringList m_mimeTypeList;
QString m_traderConstraint;
KFileItemList m_fileOpenList;
QActionGroup m_executeServiceActionGroup;
QActionGroup m_runApplicationActionGroup;
QList<KAction*> m_ownActions;
QWidget* m_parentWidget;
};
Q_DECLARE_METATYPE(KService::Ptr)
Q_DECLARE_METATYPE(KServiceAction)
#endif /* KFILEITEMACTIONS_P_H */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 1, 10:00 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10076274
Default Alt Text
(38 KB)
Attached To
Mode
rKL kdelibs
Attached
Detach File
Event Timeline
Log In to Comment