kdelibs/kio/kfile/kopenwithdialog.cpp
Ivailo Monev 779f3b5635 kio: update placeholders references in KOpenWithDialog
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2024-05-12 14:44:36 +03:00

974 lines
31 KiB
C++

/* This file is part of the KDE libraries
Copyright (C) 1997 Torben Weis <weis@stud.uni-frankfurt.de>
Copyright (C) 1999 Dirk Mueller <mueller@kde.org>
Portions copyright (C) 1999 Preston Brown <pbrown@kde.org>
Copyright (C) 2007 Pino Toscano <pino@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) 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 "kopenwithdialog.h"
#include "kopenwithdialog_p.h"
#include <QtCore/qalgorithms.h>
#include <QtCore/QList>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QCheckBox>
#include <QtGui/QStyle>
#include <QtGui/qstyleoption.h>
#include <khistorycombobox.h>
#include <kdesktopfile.h>
#include <klineedit.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kshell.h>
#include <krun.h>
#include <kstandarddirs.h>
#include <kstringhandler.h>
#include <kurlcompletion.h>
#include <kurlrequester.h>
#include <kmimetype.h>
#include <kservicegroup.h>
#include <kserviceoffer.h>
#include <kbuildsycocaprogressdialog.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <assert.h>
#include <stdlib.h>
static QString simplifiedExecLineFromService(const QString& fullExec)
{
QStringList args = KShell::splitArgs(fullExec);
if (args.isEmpty()) {
return QString();
}
return args.first().simplified();
}
namespace KDEPrivate {
class AppNode
{
public:
AppNode()
: isDir(false), parent(nullptr), fetched(false)
{
}
~AppNode()
{
qDeleteAll(children);
}
QString icon;
QString text;
QString entryPath;
QString exec;
bool isDir;
AppNode *parent;
bool fetched;
QList<AppNode*> children;
};
bool AppNodeLessThan(KDEPrivate::AppNode *n1, KDEPrivate::AppNode *n2)
{
if (n1->isDir) {
if (n2->isDir) {
return (n1->text.compare(n2->text, Qt::CaseInsensitive) < 0);
}
return true;
}
if (n2->isDir) {
return false;
}
return (n1->text.compare(n2->text, Qt::CaseInsensitive) < 0);
}
}
class KApplicationModelPrivate
{
public:
KApplicationModelPrivate(KApplicationModel *qq)
: q(qq), root(new KDEPrivate::AppNode())
{
}
~KApplicationModelPrivate()
{
delete root;
}
void fillNode(const QString &entryPath, KDEPrivate::AppNode *node);
KApplicationModel *q;
KDEPrivate::AppNode *root;
};
void KApplicationModelPrivate::fillNode(const QString &_entryPath, KDEPrivate::AppNode *node)
{
KServiceGroup::Ptr root = KServiceGroup::group(_entryPath);
if (!root || !root->isValid()) {
return;
}
const KServiceGroup::List list = root->entries();
for( KServiceGroup::List::ConstIterator it = list.begin(); it != list.end(); ++it) {
QString icon;
QString text;
QString entryPath;
QString exec;
bool isDir = false;
const KSycocaEntry::Ptr p = (*it);
if (p->isType(KST_KService)) {
const KService::Ptr service = KService::Ptr::staticCast(p);
if (service->noDisplay()) {
continue;
}
icon = service->icon();
text = service->name();
exec = service->exec();
entryPath = service->entryPath();
} else if (p->isType(KST_KServiceGroup)) {
const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) {
continue;
}
icon = serviceGroup->icon();
text = serviceGroup->caption();
entryPath = serviceGroup->entryPath();
isDir = true;
} else {
kWarning() << "KServiceGroup: Unexpected object in list!";
continue;
}
KDEPrivate::AppNode *newnode = new KDEPrivate::AppNode();
newnode->icon = icon;
newnode->text = text;
newnode->entryPath = entryPath;
newnode->exec = exec;
newnode->isDir = isDir;
newnode->parent = node;
node->children.append(newnode);
}
qStableSort(node->children.begin(), node->children.end(), KDEPrivate::AppNodeLessThan);
}
KApplicationModel::KApplicationModel(QObject *parent)
: QAbstractItemModel(parent),
d(new KApplicationModelPrivate(this))
{
d->fillNode(QString(), d->root);
}
KApplicationModel::~KApplicationModel()
{
delete d;
}
bool KApplicationModel::canFetchMore(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return false;
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
return node->isDir && !node->fetched;
}
int KApplicationModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant KApplicationModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole: {
return node->text;
}
case Qt::DecorationRole: {
if (!node->icon.isEmpty()) {
return KIcon(node->icon);
}
break;
}
default: {
break;
}
}
return QVariant();
}
void KApplicationModel::fetchMore(const QModelIndex &parent)
{
if (!parent.isValid()) {
return;
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
if (!node->isDir) {
return;
}
emit layoutAboutToBeChanged();
d->fillNode(node->entryPath, node);
node->fetched = true;
emit layoutChanged();
}
bool KApplicationModel::hasChildren(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return true;
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
return node->isDir;
}
QVariant KApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole: {
return i18n("Known Applications");
}
default: {
return QVariant();
}
}
}
QModelIndex KApplicationModel::index(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column != 0) {
return QModelIndex();
}
KDEPrivate::AppNode *node = d->root;
if (parent.isValid()) {
node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
}
if (row >= node->children.count()) {
return QModelIndex();
}
return createIndex(row, 0, node->children.at(row));
}
QModelIndex KApplicationModel::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
if (node->parent->parent) {
int id = node->parent->parent->children.indexOf(node->parent);
if (id >= 0 && id < node->parent->parent->children.count()) {
return createIndex(id, 0, node->parent);
}
return QModelIndex();
}
return QModelIndex();
}
int KApplicationModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return d->root->children.count();
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
return node->children.count();
}
QString KApplicationModel::entryPathFor(const QModelIndex &index) const
{
if (!index.isValid()) {
return QString();
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
return node->entryPath;
}
QString KApplicationModel::execFor(const QModelIndex &index) const
{
if (!index.isValid()) {
return QString();
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
return node->exec;
}
bool KApplicationModel::isDirectory(const QModelIndex &index) const
{
if (!index.isValid()) {
return false;
}
KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
return node->isDir;
}
class KApplicationViewPrivate
{
public:
KApplicationViewPrivate()
: appModel(nullptr)
{
}
KApplicationModel *appModel;
};
KApplicationView::KApplicationView(QWidget *parent)
: QTreeView(parent), d(new KApplicationViewPrivate)
{
}
KApplicationView::~KApplicationView()
{
delete d;
}
void KApplicationView::setModel(QAbstractItemModel *model)
{
if (d->appModel) {
disconnect(
selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))
);
}
QTreeView::setModel(model);
d->appModel = qobject_cast<KApplicationModel*>(model);
if (d->appModel) {
connect(
selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))
);
}
}
bool KApplicationView::isDirSel() const
{
if (d->appModel) {
QModelIndex index = selectionModel()->currentIndex();
return d->appModel->isDirectory(index);
}
return false;
}
void KApplicationView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
QTreeView::currentChanged(current, previous);
if (d->appModel && !d->appModel->isDirectory(current)) {
QString exec = d->appModel->execFor(current);
if (!exec.isEmpty()) {
emit highlighted(d->appModel->entryPathFor(current), exec);
}
}
}
void KApplicationView::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
Q_UNUSED(deselected)
const QModelIndexList indexes = selected.indexes();
if (indexes.count() == 1 && !d->appModel->isDirectory(indexes.at(0))) {
QString exec = d->appModel->execFor(indexes.at(0));
if (!exec.isEmpty()) {
emit this->selected(d->appModel->entryPathFor(indexes.at(0)), exec);
}
}
}
/***************************************************************
*
* KOpenWithDialog
*
***************************************************************/
class KOpenWithDialogPrivate
{
public:
KOpenWithDialogPrivate(KOpenWithDialog *qq)
: q(qq), saveNewApps(false)
{
}
KOpenWithDialog *q;
/**
* Determine mime type from URLs
*/
void setMimeType(const KUrl::List &_urls);
void addToMimeAppsList(const QString& serviceId);
/**
* Create a dialog that asks for a application to open a given
* URL(s) with.
*
* @param text appears as a label on top of the entry box.
* @param value is the initial value of the line
*/
void init(const QString &text, const QString &value);
/**
* Called by checkAccept() in order to save the history of the combobox
*/
void saveComboboxHistory();
/**
* Process the choices made by the user, and return true if everything is OK.
* Called by KOpenWithDialog::accept(), i.e. when clicking on OK or typing Return.
*/
bool checkAccept();
// slots
void _k_slotDbClick();
void _k_slotFileSelected();
bool saveNewApps;
bool m_terminaldirty;
KService::Ptr curService;
KApplicationView *view;
KUrlRequester *edit;
QString m_command;
QLabel *label;
QString qMimeType;
QCheckBox *terminal;
QCheckBox *remember;
QCheckBox *nocloseonexit;
KService::Ptr m_pService;
};
KOpenWithDialog::KOpenWithDialog( const KUrl::List &urls, QWidget *parent)
: KDialog(parent),
d(new KOpenWithDialogPrivate(this))
{
setObjectName(QLatin1String("openwith"));
setModal(true);
setCaption(i18n("Open With"));
QString text;
if (urls.count() == 1) {
text = i18n("<qt>Select the program that should be used to open <b>%1</b>. "
"If the program is not listed, enter the name or click "
"the browse button.</qt>", urls.first().fileName());
} else {
// Should never happen ??
text = i18n("Choose the name of the program with which to open the selected files.");
}
d->setMimeType(urls);
d->init(text, QString());
}
KOpenWithDialog::KOpenWithDialog(const KUrl::List &urls, const QString &text,
const QString &value, QWidget *parent)
: KDialog(parent),
d(new KOpenWithDialogPrivate(this))
{
setObjectName(QLatin1String("openwith"));
setModal(true);
QString caption;
if (urls.count() > 0 && !urls.first().isEmpty()) {
caption = KStringHandler::csqueeze(urls.first().prettyUrl());
}
if (urls.count() > 1) {
caption += QString::fromLatin1("...");
}
setCaption(caption);
d->setMimeType(urls);
d->init(text, value);
}
KOpenWithDialog::KOpenWithDialog(const QString &mimeType, const QString &value,
QWidget *parent)
: KDialog(parent),
d(new KOpenWithDialogPrivate(this))
{
setObjectName(QLatin1String("openwith"));
setModal(true);
setCaption(i18n("Choose Application for %1", mimeType));
QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. "
"If the program is not listed, enter the name or click "
"the browse button.</qt>", mimeType);
d->qMimeType = mimeType;
d->init(text, value);
if (d->remember) {
d->remember->hide();
}
}
KOpenWithDialog::KOpenWithDialog(QWidget *parent)
: KDialog(parent),
d(new KOpenWithDialogPrivate(this))
{
setObjectName(QLatin1String("openwith"));
setModal(true);
setCaption(i18n("Choose Application"));
QString text = i18n("<qt>Select a program. "
"If the program is not listed, enter the name or click "
"the browse button.</qt>");
d->qMimeType.clear();
d->init(text, QString());
}
void KOpenWithDialogPrivate::setMimeType(const KUrl::List &urls)
{
if (urls.count() == 1) {
qMimeType = KMimeType::findByUrl(urls.first())->name();
if (qMimeType == QLatin1String("application/octet-stream")) {
qMimeType.clear();
}
} else {
qMimeType.clear();
}
}
void KOpenWithDialogPrivate::init(const QString &text, const QString &value)
{
m_terminaldirty = false;
view = 0;
m_pService = 0;
curService = 0;
q->setButtons(KDialog::Ok | KDialog::Cancel);
QWidget *mainWidget = q->mainWidget();
QBoxLayout *topLayout = new QVBoxLayout(mainWidget);
topLayout->setMargin(0);
label = new QLabel(text, q);
label->setWordWrap(true);
topLayout->addWidget(label);
// init the history combo and insert it into the URL-Requester
KHistoryComboBox *combo = new KHistoryComboBox();
KLineEdit *lineEdit = new KLineEdit(q);
lineEdit->setClearButtonShown(true);
combo->setLineEdit(lineEdit);
combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
combo->setDuplicatesEnabled(false);
KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings"));
int max = cg.readEntry("Maximum history", 15);
combo->setMaxCount(max);
int mode = cg.readEntry("CompletionMode", int(KGlobalSettings::completionMode()));
combo->setCompletionMode((KGlobalSettings::Completion)mode);
const QStringList list = cg.readEntry("History", QStringList());
combo->setHistoryItems(list, true);
edit = new KUrlRequester(combo, mainWidget);
edit->setText(value);
edit->setWhatsThis(
i18n(
"Following the command, you can have several place holders which will be replaced "
"with the actual values when the actual program is run:\n"
"%f - a single file name\n"
"%F - a list of files; use for applications that can open several local files at once\n"
"%u - a single URL\n"
"%U - a list of URLs\n"
"%i - the icon\n"
"%c - the comment\n"
"%k - the location of the desktop file"
)
);
topLayout->addWidget(edit);
if (edit->comboBox()) {
KUrlCompletion *comp = new KUrlCompletion(KUrlCompletion::ExeCompletion);
edit->comboBox()->setCompletionObject(comp);
edit->comboBox()->setAutoDeleteCompletionObject(true);
}
QObject::connect(edit, SIGNAL(textChanged(QString)), q, SLOT(slotTextChanged()));
QObject::connect(edit, SIGNAL(urlSelected(KUrl)), q, SLOT(_k_slotFileSelected()));
view = new KApplicationView(mainWidget);
view->setModel(new KApplicationModel(view));
topLayout->addWidget(view);
topLayout->setStretchFactor(view, 1);
QObject::connect(view, SIGNAL(selected(QString,QString)), q, SLOT(slotSelected(QString,QString)));
QObject::connect(view, SIGNAL(highlighted(QString,QString)), q, SLOT(slotHighlighted(QString,QString)));
QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(_k_slotDbClick()));
terminal = new QCheckBox(i18n("Run in &terminal"), mainWidget);
QObject::connect(terminal, SIGNAL(toggled(bool)), q, SLOT(slotTerminalToggled(bool)));
topLayout->addWidget(terminal);
QStyleOptionButton checkBoxOption;
checkBoxOption.initFrom(terminal);
int checkBoxIndentation = terminal->style()->pixelMetric(QStyle::PM_IndicatorWidth, &checkBoxOption, terminal);
checkBoxIndentation += terminal->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &checkBoxOption, terminal);
QBoxLayout* nocloseonexitLayout = new QHBoxLayout();
nocloseonexitLayout->setMargin(0);
QSpacerItem* spacer = new QSpacerItem(checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
nocloseonexitLayout->addItem(spacer);
nocloseonexit = new QCheckBox(i18n("&Do not close when command exits"), mainWidget);
nocloseonexit->setChecked(false);
nocloseonexit->setDisabled(true);
// check to see if we use konsole if not disable the nocloseonexit
// because we don't know how to do this on other terminal applications
KConfigGroup confGroup(KGlobal::config(), QString::fromLatin1("General") );
QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
if (preferredTerminal != "konsole") {
nocloseonexit->hide();
}
nocloseonexitLayout->addWidget( nocloseonexit );
topLayout->addLayout( nocloseonexitLayout );
if (!qMimeType.isNull()) {
remember = new QCheckBox(i18n("&Remember application association for this type of file"), mainWidget);
// remember->setChecked(true);
topLayout->addWidget(remember);
} else {
remember = 0L;
}
q->setMinimumSize(q->minimumSizeHint());
// edit->setText( _value );
// This is what caused "can't click on items before clicking on Name header".
// Probably due to the resizeEvent handler using width().
// resize( minimumWidth(), sizeHint().height() );
edit->setFocus();
q->slotTextChanged();
}
// ----------------------------------------------------------------------
KOpenWithDialog::~KOpenWithDialog()
{
delete d;
}
// ----------------------------------------------------------------------
void KOpenWithDialog::slotSelected(const QString &/*name*/, const QString &exec)
{
KService::Ptr pService = d->curService;
d->edit->setText(exec); // calls slotTextChanged :(
d->curService = pService;
}
// ----------------------------------------------------------------------
void KOpenWithDialog::slotHighlighted(const QString &entryPath, const QString&)
{
d->curService = KService::serviceByDesktopPath(entryPath);
if (d->curService && !d->m_terminaldirty) {
// ### indicate that default value was restored
d->terminal->setChecked(d->curService->terminal());
QString terminalOptions = d->curService->terminalOptions();
d->nocloseonexit->setChecked(terminalOptions.contains(QLatin1String("--noclose")));
d->m_terminaldirty = false; // slotTerminalToggled changed it
}
}
// ----------------------------------------------------------------------
void KOpenWithDialog::slotTextChanged()
{
// Forget about the service
d->curService = 0L;
enableButton(KDialog::Ok, !d->edit->text().isEmpty());
}
// ----------------------------------------------------------------------
void KOpenWithDialog::slotTerminalToggled(bool)
{
// ### indicate that default value was overridden
d->m_terminaldirty = true;
d->nocloseonexit->setDisabled(!d->terminal->isChecked());
}
// ----------------------------------------------------------------------
void KOpenWithDialogPrivate::_k_slotDbClick()
{
// check if a directory is selected
if (view->isDirSel()) {
return;
}
q->accept();
}
void KOpenWithDialogPrivate::_k_slotFileSelected()
{
// quote the path to avoid unescaped whitespace, backslashes, etc.
edit->setText(KShell::quoteArg(edit->text()));
}
void KOpenWithDialog::setSaveNewApplications(bool b)
{
d->saveNewApps = b;
}
void KOpenWithDialogPrivate::addToMimeAppsList(const QString &serviceId /*menu id or storage id*/)
{
KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps");
KConfigGroup addedApps(profile, "Added Associations");
QStringList apps = addedApps.readXdgListEntry(qMimeType);
apps.removeAll(serviceId);
apps.prepend(serviceId); // make it the preferred app
addedApps.writeXdgListEntry(qMimeType, apps);
addedApps.sync();
// Also make sure the "auto embed" setting for this mimetype is off
KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals);
fileTypesConfig->group("EmbedSettings").writeEntry(QString("embed-")+qMimeType, false);
fileTypesConfig->sync();
kDebug() << "rebuilding ksycoca...";
// kbuildsycoca is the one reading mimeapps.list, so we need to run it now
KBuildSycocaProgressDialog::rebuildKSycoca(q);
m_pService = KService::serviceByStorageId(serviceId);
Q_ASSERT(m_pService);
}
bool KOpenWithDialogPrivate::checkAccept()
{
const QString typedExec(edit->text());
if (typedExec.isEmpty()) {
return false;
}
QString fullExec(typedExec);
QString serviceName;
QString initialServiceName;
QString preferredTerminal;
QString configPath;
QString serviceExec;
m_pService = curService;
if (!m_pService) {
// No service selected - check the command line
// Find out the name of the service from the command line, removing args and paths
serviceName = KRun::binaryName(typedExec, true);
if (serviceName.isEmpty()) {
KMessageBox::error(q, i18n("Could not extract executable name from '%1', please type a valid program name.", serviceName));
return false;
}
initialServiceName = serviceName;
// Also remember the binaryName with a path, if any, for the
// check that the binary exists.
kDebug() << "initialServiceName=" << initialServiceName;
int i = 1; // We have app, app-2, app-3... Looks better for the user.
bool ok = false;
// Check if there's already a service by that name, with the same Exec line
do {
kDebug() << "looking for service" << serviceName;
KService::Ptr serv = KService::serviceByDesktopName(serviceName);
ok = !serv; // ok if no such service yet
// also ok if we find the exact same service (well, "kwrite" == "kwrite %U")
if (serv && !serv->noDisplay() /* #297720 */) {
if (serv->isApplication()) {
/*kDebug() << "typedExec=" << typedExec
<< "serv->exec=" << serv->exec()
<< "simplifiedExecLineFromService=" << simplifiedExecLineFromService(fullExec);*/
serviceExec = simplifiedExecLineFromService(serv->exec());
if (typedExec == serviceExec){
ok = true;
m_pService = serv;
kDebug() << "OK, found identical service: " << serv->entryPath();
} else {
kDebug() << "Exec line differs, service says:" << serviceExec;
configPath = serv->entryPath();
serviceExec = serv->exec();
}
} else {
kDebug() << "Found, but not an application:" << serv->entryPath();
}
}
if (!ok) { // service was found, but it was different -> keep looking
++i;
serviceName = initialServiceName + '-' + QString::number(i);
}
} while (!ok);
}
if (m_pService) {
// Existing service selected
serviceName = m_pService->name();
initialServiceName = serviceName;
fullExec = m_pService->exec();
} else {
const QString binaryName = KRun::binaryName(typedExec, false);
kDebug() << "binaryName=" << binaryName;
// Ensure that the typed binary name actually exists (#81190)
if (KStandardDirs::findExe(binaryName).isEmpty()) {
KMessageBox::error(q, i18n("'%1' not found, please type a valid program name.", binaryName));
return false;
}
}
if (terminal->isChecked()) {
KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
m_command = preferredTerminal;
// only add --noclose when we are sure it is konsole we're using
if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) {
m_command += QString::fromLatin1(" --noclose");
}
m_command += QString::fromLatin1(" -e ");
m_command += edit->text();
kDebug() << "Setting m_command to" << m_command;
}
if (m_pService && terminal->isChecked() != m_pService->terminal()) {
m_pService = 0; // It's not exactly this service we're running
}
const bool bRemember = remember && remember->isChecked();
kDebug() << "bRemember=" << bRemember << "service found=" << m_pService;
if (m_pService) {
if (bRemember) {
// Associate this app with qMimeType in mimeapps.list
Q_ASSERT(!qMimeType.isEmpty()); // we don't show the remember checkbox otherwise
addToMimeAppsList(m_pService->storageId());
}
} else {
const bool createDesktopFile = bRemember || saveNewApps;
if (!createDesktopFile) {
// Create temp service
if (configPath.isEmpty()) {
m_pService = new KService(initialServiceName, fullExec, QString());
} else {
if (!typedExec.contains(QLatin1String("%u"), Qt::CaseInsensitive) &&
!typedExec.contains(QLatin1String("%f"), Qt::CaseInsensitive)) {
int index = serviceExec.indexOf(QLatin1String("%u"), 0, Qt::CaseInsensitive);
if (index == -1) {
index = serviceExec.indexOf(QLatin1String("%f"), 0, Qt::CaseInsensitive);
}
if (index > -1) {
fullExec += QLatin1Char(' ');
fullExec += serviceExec.mid(index, 2);
}
}
kDebug() << "Creating service with Exec=" << fullExec;
m_pService = new KService(configPath);
m_pService->setExec(fullExec);
}
if (terminal->isChecked()) {
m_pService->setTerminal(true);
// only add --noclose when we are sure it is konsole we're using
if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) {
m_pService->setTerminalOptions("--noclose");
}
}
} else {
// If we got here, we can't seem to find a service for what they wanted. Create one.
QString menuId;
QString newPath = KService::newServicePath(serviceName, &menuId);
kDebug() << "Creating new service" << serviceName << "(" << newPath << ")" << "menuId=" << menuId;
KDesktopFile desktopFile(newPath);
KConfigGroup cg = desktopFile.desktopGroup();
cg.writeEntry("Type", "Application");
cg.writeEntry("Name", initialServiceName);
cg.writeEntry("Exec", fullExec);
cg.writeEntry("NoDisplay", true); // don't make it appear in the K menu
if (terminal->isChecked()) {
cg.writeEntry("Terminal", true);
// only add --noclose when we are sure it is konsole we're using
if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) {
cg.writeEntry("TerminalOptions", "--noclose");
}
}
cg.writeXdgListEntry("MimeType", QStringList() << qMimeType);
cg.sync();
addToMimeAppsList(menuId);
}
}
saveComboboxHistory();
return true;
}
void KOpenWithDialog::accept()
{
if (d->checkAccept()) {
KDialog::accept();
}
}
QString KOpenWithDialog::text() const
{
if (!d->m_command.isEmpty()) {
return d->m_command;
}
return d->edit->text();
}
void KOpenWithDialog::hideNoCloseOnExit()
{
// uncheck the checkbox because the value could be used when "Run in Terminal" is selected
d->nocloseonexit->setChecked(false);
d->nocloseonexit->hide();
}
void KOpenWithDialog::hideRunInTerminal()
{
d->terminal->hide();
hideNoCloseOnExit();
}
KService::Ptr KOpenWithDialog::service() const
{
return d->m_pService;
}
void KOpenWithDialogPrivate::saveComboboxHistory()
{
KHistoryComboBox *combo = static_cast<KHistoryComboBox*>(edit->comboBox());
if (combo) {
combo->addToHistory(edit->text());
KConfigGroup cg(KGlobal::config(), QString::fromLatin1("Open-with settings"));
cg.writeEntry("History", combo->historyItems());
cg.writeEntry("CompletionMode", int(combo->completionMode()));
// don't store the completion-list, as it contains all of KUrlCompletion's
// executables
cg.sync();
}
}
#include "moc_kopenwithdialog.cpp"
#include "moc_kopenwithdialog_p.cpp"