| 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 | |
| 25 | ExecutableFileOpenDialog::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 | |
| 108 | bool ExecutableFileOpenDialog::isDontAskAgainChecked() const |
| 109 | { |
| 110 | return m_ui.dontAgainCheckBox->isChecked(); |
| 111 | } |
| 112 | |
| 113 | void ExecutableFileOpenDialog::executeFile() |
| 114 | { |
| 115 | done(ExecuteFile); |
| 116 | } |
| 117 | |
| 118 | void ExecutableFileOpenDialog::openFile() |
| 119 | { |
| 120 | done(OpenFile); |
| 121 | } |
| 122 | |
| 123 | void 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 | |