1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2014 Arjun A.K. <arjunak234@gmail.com>
4 SPDX-FileCopyrightText: 2025 Kai Uwe Broulik <kde@broulik.de>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "executablefileopendialog_p.h"
10
11#include <QDialogButtonBox>
12#include <QLabel>
13#include <QMimeType>
14#include <QPushButton>
15#include <QShowEvent>
16
17#include <KApplicationTrader>
18#include <KFileItem>
19#include <KIconLoader>
20#include <KLocalizedString>
21#include <KMessageDialog>
22
23#include <KIO/PreviewJob>
24
25ExecutableFileOpenDialog::ExecutableFileOpenDialog(const QUrl &url, const QMimeType &mimeType, ExecutableFileOpenDialog::Mode mode, QWidget *parent)
26 : QDialog(parent)
27{
28 m_ui.setupUi(this);
29
30 std::optional<KFileItem> fileItem;
31
32 if (url.isValid()) {
33 fileItem = KFileItem{url, mimeType.name()};
34
35 m_ui.nameLabel->setText(fileItem->name());
36 m_ui.nameLabel->setToolTip(url.toDisplayString(options: QUrl::PreferLocalFile));
37 } else {
38 m_ui.nameLabel->hide();
39 }
40
41 m_ui.mimeTypeLabel->setForegroundRole(QPalette::PlaceholderText);
42 // Not using KFileItem::comment() since that also reads the Comment from the .desktop file
43 // which could spoof the user.
44 m_ui.mimeTypeLabel->setText(mimeType.comment());
45 m_ui.mimeTypeLabel->setToolTip(mimeType.name());
46
47 const QSize iconSize{KIconLoader::SizeHuge, KIconLoader::SizeHuge};
48 QIcon icon;
49
50 if (fileItem) {
51 icon = QIcon::fromTheme(name: fileItem->iconName());
52
53 auto *previewJob = KIO::filePreview(items: {*fileItem}, size: iconSize);
54 previewJob->setDevicePixelRatio(devicePixelRatioF());
55 connect(sender: previewJob, signal: &KIO::PreviewJob::gotPreview, context: this, slot: [this](const KFileItem &item, const QPixmap &pixmap) {
56 Q_UNUSED(item);
57 m_ui.iconLabel->setPixmap(pixmap);
58 });
59 }
60
61 if (icon.isNull()) {
62 icon = QIcon::fromTheme(name: mimeType.iconName());
63 }
64 if (icon.isNull()) {
65 icon = QIcon::fromTheme(QStringLiteral("unknown"));
66 }
67
68 m_ui.iconLabel->setPixmap(icon.pixmap(size: iconSize, devicePixelRatio: devicePixelRatioF()));
69
70 connect(sender: m_ui.buttonBox, signal: &QDialogButtonBox::rejected, context: this, slot: &ExecutableFileOpenDialog::reject);
71
72 QPushButton *launchButton = new QPushButton(i18nc("@action:button Launch script", "&Launch"), this);
73 launchButton->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
74
75 // Script execution settings UI is in Dolphin, not KIO, only show the explanation
76 // on how to undo "dont ask again" when Dolphin is the default file manager.
77 const auto fileManagerService = KApplicationTrader::preferredService(QStringLiteral("inode/directory"));
78 m_ui.dontAgainHelpButton->setVisible(fileManagerService && fileManagerService->desktopEntryName() == QLatin1String("org.kde.dolphin"));
79
80 if (mode == OnlyExecute) {
81 m_ui.dontAgainCheckBox->setText(i18nc("@option:check", "Launch executable files without asking"));
82 connect(sender: launchButton, signal: &QPushButton::clicked, context: this, slot: &ExecutableFileOpenDialog::executeFile);
83 } else if (mode == OpenAsExecute) {
84 m_ui.dontAgainCheckBox->setText(i18nc("@option:check Open in the associated app", "Open executable files in the default application without asking"));
85 connect(sender: launchButton, signal: &QPushButton::clicked, context: this, slot: &ExecutableFileOpenDialog::openFile);
86 } else { // mode == OpenOrExecute
87 m_ui.label->setText(i18n("What do you wish to do with this file?"));
88 connect(sender: launchButton, signal: &QPushButton::clicked, context: this, slot: &ExecutableFileOpenDialog::executeFile);
89
90 QPushButton *openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-preview")), i18nc("@action:button", "&Open"), this);
91 if (KService::Ptr service = KApplicationTrader::preferredService(mimeType: mimeType.name())) {
92 openButton->setText(i18nc("@action:button", "&Open with %1", service->name()));
93 const QIcon serviceIcon = QIcon::fromTheme(name: service->icon());
94 if (!serviceIcon.isNull()) {
95 openButton->setIcon(serviceIcon);
96 }
97 }
98 m_ui.buttonBox->addButton(button: openButton, role: QDialogButtonBox::AcceptRole);
99
100 connect(sender: openButton, signal: &QPushButton::clicked, context: this, slot: &ExecutableFileOpenDialog::openFile);
101 }
102
103 // Add Execute button last so that Open is first in the button box
104 m_ui.buttonBox->addButton(button: launchButton, role: QDialogButtonBox::AcceptRole);
105 m_ui.buttonBox->button(which: QDialogButtonBox::Cancel)->setFocus();
106}
107
108bool ExecutableFileOpenDialog::isDontAskAgainChecked() const
109{
110 return m_ui.dontAgainCheckBox->isChecked();
111}
112
113void ExecutableFileOpenDialog::executeFile()
114{
115 done(ExecuteFile);
116}
117
118void ExecutableFileOpenDialog::openFile()
119{
120 done(OpenFile);
121}
122
123void ExecutableFileOpenDialog::showEvent(QShowEvent *event)
124{
125 if (!event->spontaneous()) {
126 KMessageDialog::beep(type: KMessageDialog::QuestionTwoActionsCancel, text: m_ui.label->text(), dialog: this);
127 }
128 QDialog::showEvent(event);
129}
130
131#include "moc_executablefileopendialog_p.cpp"
132

source code of kio/src/widgets/executablefileopendialog.cpp