[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1107344: marked as done (unblock: kf6-kio/6.13.0-4)



Your message dated Mon, 09 Jun 2025 07:35:08 +0000
with message-id <E1uOX2C-00BgJT-34@respighi.debian.org>
and subject line unblock kf6-kio
has caused the Debian Bug report #1107344,
regarding unblock: kf6-kio/6.13.0-4
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1107344: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1107344
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: kf6-kio@packages.debian.org, Debian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Control: affects -1 + src:kf6-kio
User: release.debian.org@packages.debian.org
Usertags: unblock

Dear Release Team,

please unblock package kf6-kio.

[ Reason ]
It contains the following changes:
* Backport upstream commits:
  - Show single-click selection emblem in open/save dialogs when using
  single-click mouse mode. (kde#185793)
  - When saving a file, don’t override user provided filename with folder
  name when navigating inside a folder with the keyboard. (kde#502794)
  - Fix actions described by D-Bus activatable desktop files not being
  triggered when there’s no Exec key.
  - Inhibit suspend while there’s a file copy job running. (kde#362542)
    - Update symbols accordingly.
  - Show busy indictor while emptying the trash.
  - Fix keyboard navigation unexpectedly switching to file name input when
  navigating the files panel in save dialogs. (kde#466206)
  - Fix excessive I/O when hovering over a folder showing thumbnails of its
  contents.
  - Fix incorrect usage percentage in Properties dialog for virtual
  filesystems.
  - Fix opening the “Advanced Options” window from the file properties dialog
  freezing Plasma completely until it’s closed. (kde#504608)


[ Tests ]
Upstream testsuite passes in sbuild.
Each changes tested but this one for which I don’t have a test case and
only non-regression was tested:
- Fix actions described by D-Bus activatable desktop files not being
triggered when there’s no Exec key.


[ Risks ]
Only backport of upstream commits that apply cleanly. Further fixes can
easily be backported or the changes reverted.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing


Thanks!


unblock kf6-kio/6.13.0-4
diff -Nru kf6-kio-6.13.0/debian/changelog kf6-kio-6.13.0/debian/changelog
--- kf6-kio-6.13.0/debian/changelog	2025-04-25 22:41:52.000000000 +0200
+++ kf6-kio-6.13.0/debian/changelog	2025-05-20 08:47:36.000000000 +0200
@@ -1,3 +1,27 @@
+kf6-kio (6.13.0-4) unstable; urgency=medium
+
+  [ Aurélien COUDERC ]
+  * Backport upstream commits:
+    - Show single-click selection emblem in open/save dialogs when using
+    single-click mouse mode. (kde#185793)
+    - When saving a file, don’t override user provided filename with folder
+    name when navigating inside a folder with the keyboard. (kde#502794)
+    - Fix actions described by D-Bus activatable desktop files not being
+    triggered when there’s no Exec key.
+    - Inhibit suspend while there’s a file copy job running. (kde#362542)
+      - Update symbols accordingly.
+    - Show busy indictor while emptying the trash.
+    - Fix keyboard navigation unexpectedly switching to file name input when
+    navigating the files panel in save dialogs. (kde#466206)
+    - Fix excessive I/O when hovering over a folder showing thumbnails of its
+    contents.
+    - Fix incorrect usage percentage in Properties dialog for virtual
+    filesystems.
+    - Fix opening the “Advanced Options” window from the file properties dialog
+    freezing Plasma completely until it’s closed. (kde#504608)
+
+ -- Aurélien COUDERC <coucouf@debian.org>  Tue, 20 May 2025 08:47:36 +0200
+
 kf6-kio (6.13.0-3) unstable; urgency=medium
 
   [ Patrick Franz ]
diff -Nru kf6-kio-6.13.0/debian/libkf6kiocore6.symbols kf6-kio-6.13.0/debian/libkf6kiocore6.symbols
--- kf6-kio-6.13.0/debian/libkf6kiocore6.symbols	2025-04-23 19:07:08.000000000 +0200
+++ kf6-kio-6.13.0/debian/libkf6kiocore6.symbols	2025-05-20 08:47:36.000000000 +0200
@@ -279,8 +279,11 @@
  _ZN3KIO10JobPrivate12emitMountingEPNS_3JobERK7QStringS5_@Base 6.0.0
  _ZN3KIO10JobPrivate12emitRenamingEPNS_3JobERK4QUrlS5_@Base 6.0.0
  _ZN3KIO10JobPrivate14emitUnmountingEPNS_3JobERK7QString@Base 6.0.0
+ (arch=amd64)_ZN3KIO10JobPrivate14inhibitSuspendERK7QString@Base 6.13.0
  _ZN3KIO10JobPrivate15emitCreatingDirEPNS_3JobERK4QUrl@Base 6.0.0
+ (arch=amd64)_ZN3KIO10JobPrivate16doInhibitSuspendEv@Base 6.13.0
  _ZN3KIO10JobPrivate16emitTransferringEPNS_3JobERK4QUrl@Base 6.0.0
+ (arch=amd64)_ZN3KIO10JobPrivate16uninhibitSuspendEv@Base 6.13.0
  _ZN3KIO10JobPrivate22privilegeOperationDataEv@Base 6.0.0
  _ZN3KIO10JobPrivateD0Ev@Base 6.0.0
  _ZN3KIO10JobPrivateD1Ev@Base 6.0.0
diff -Nru kf6-kio-6.13.0/debian/libkf6kiofilewidgets6.symbols kf6-kio-6.13.0/debian/libkf6kiofilewidgets6.symbols
--- kf6-kio-6.13.0/debian/libkf6kiofilewidgets6.symbols	2025-04-23 19:14:52.000000000 +0200
+++ kf6-kio-6.13.0/debian/libkf6kiofilewidgets6.symbols	2025-05-20 08:47:36.000000000 +0200
@@ -104,6 +104,7 @@
  _ZN12KDirOperator17updateSortActionsEv@Base 6.0.0
  _ZN12KDirOperator17updateViewActionsEv@Base 6.0.0
  _ZN12KDirOperator18setShowHiddenFilesEb@Base 6.0.0
+ _ZN12KDirOperator18usingKeyNavigationEv@Base 6.13.0
  _ZN12KDirOperator19checkPreviewSupportEv@Base 6.0.0
  _ZN12KDirOperator19setSupportedSchemesERK5QListI7QStringE@Base 6.0.0
  _ZN12KDirOperator19showOpenWithActionsEb@Base 6.0.0
diff -Nru kf6-kio-6.13.0/debian/libkf6kiogui6.symbols kf6-kio-6.13.0/debian/libkf6kiogui6.symbols
--- kf6-kio-6.13.0/debian/libkf6kiogui6.symbols	2024-12-08 05:08:50.000000000 +0100
+++ kf6-kio-6.13.0/debian/libkf6kiogui6.symbols	2025-05-20 08:47:36.000000000 +0200
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 6.8.0 amd64
+# SymbolsHelper-Confirmed: 6.13.0 amd64
 libKF6KIOGui.so.6 libkf6kiogui6 #MINVER#
 * Build-Depends-Package: libkf6kio-dev
  (arch=linux-any)_ZGVZN9QMetaType21registerConverterImplI5QListI11ExecCommandE9QIterableI13QMetaSequenceEEEbSt8functionIFbPKvPvEES_S_E10unregister@Base 6.0.0
@@ -31,7 +31,7 @@
  _ZN14KProcessRunner14escapeUnitNameERK7QString@Base 6.0.0
  _ZN14KProcessRunner14fromExecutableERK7QStringRK5QListIS0_ES2_RK10QByteArrayS2_RK19QProcessEnvironment@Base 6.0.0
  _ZN14KProcessRunner14processStartedEx@Base 6.0.0
- _ZN14KProcessRunner15fromApplicationERK28QExplicitlySharedDataPointerI8KServiceERK7QStringRK5QListI4QUrlE6QFlagsIN3KIO22ApplicationLauncherJob7RunFlagEES7_RK10QByteArray@Base 6.0.0
+ _ZN14KProcessRunner15fromApplicationERK28QExplicitlySharedDataPointerI8KServiceERK7QStringRK5QListI4QUrlES7_6QFlagsIN3KIO22ApplicationLauncherJob7RunFlagEES7_RK10QByteArray@Base 6.13.0
  _ZN14KProcessRunner16emitDelayedErrorERK7QString@Base 6.0.0
  _ZN14KProcessRunner16staticMetaObjectE@Base 6.0.0
  _ZN14KProcessRunner19initFromDesktopNameERK7QStringS2_RK10QByteArrayS2_RK19QProcessEnvironment@Base 6.0.0
diff -Nru kf6-kio-6.13.0/debian/libkf6kiowidgets6.symbols kf6-kio-6.13.0/debian/libkf6kiowidgets6.symbols
--- kf6-kio-6.13.0/debian/libkf6kiowidgets6.symbols	2024-09-08 09:38:40.000000000 +0200
+++ kf6-kio-6.13.0/debian/libkf6kiowidgets6.symbols	2025-05-20 08:47:36.000000000 +0200
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 6.0.0 amd64 armel armhf riscv64
+# SymbolsHelper-Confirmed: 6.13.0 amd64
 libKF6KIOWidgets.so.6 libkf6kiowidgets6 #MINVER#, kio6
 * Build-Depends-Package: libkf6kio-dev
  _ZGVZN9QMetaType21registerConverterImplI5QListI4QUrlE9QIterableI13QMetaSequenceEEEbSt8functionIFbPKvPvEES_S_E10unregister@Base 6.0.0
@@ -164,6 +164,7 @@
  _ZN17KFileItemDelegate18setShowInformationENS_11InformationE@Base 6.0.0
  _ZN17KFileItemDelegate18setShowInformationERK5QListINS_11InformationEE@Base 6.0.0
  _ZN17KFileItemDelegate22setJobTransfersVisibleEb@Base 6.0.0
+ _ZN17KFileItemDelegate22setSelectionEmblemRectE5QRecti@Base 6.13.0
  _ZN17KFileItemDelegate24setShowToolTipWhenElidedEb@Base 6.0.0
  _ZN17KFileItemDelegate5shapeERK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
  _ZN17KFileItemDelegate9helpEventEP10QHelpEventP17QAbstractItemViewRK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
@@ -330,6 +331,7 @@
  _ZN3KIO16DeleteOrTrashJob11qt_metacastEPKc@Base 6.0.0
  _ZN3KIO16DeleteOrTrashJob16staticMetaObjectE@Base 6.0.0
  _ZN3KIO16DeleteOrTrashJob5startEv@Base 6.0.0
+ _ZN3KIO16DeleteOrTrashJob7startedEv@Base 6.13.0
  _ZN3KIO16DeleteOrTrashJobC1ERK5QListI4QUrlENS_22AskUserActionInterface12DeletionTypeENS6_16ConfirmationTypeEP7QObject@Base 6.0.0
  _ZN3KIO16DeleteOrTrashJobC2ERK5QListI4QUrlENS_22AskUserActionInterface12DeletionTypeENS6_16ConfirmationTypeEP7QObject@Base 6.0.0
  _ZN3KIO16DeleteOrTrashJobD0Ev@Base 6.0.0
@@ -489,10 +491,13 @@
  _ZNK17KFileItemDelegate12shadowOffsetEv@Base 6.0.0
  _ZNK17KFileItemDelegate13setEditorDataEP7QWidgetRK11QModelIndex@Base 6.0.0
  _ZNK17KFileItemDelegate15showInformationEv@Base 6.0.0
+ _ZNK17KFileItemDelegate19drawSelectionEmblemE20QStyleOptionViewItemP8QPainterRK11QModelIndex@Base 6.13.0
  _ZNK17KFileItemDelegate19jobTransfersVisibleEv@Base 6.0.0
+ _ZNK17KFileItemDelegate19selectionEmblemRectEv@Base 6.13.0
  _ZNK17KFileItemDelegate20updateEditorGeometryEP7QWidgetRK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
  _ZNK17KFileItemDelegate21showToolTipWhenElidedEv@Base 6.0.0
  _ZNK17KFileItemDelegate5paintEP8QPainterRK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
+ _ZNK17KFileItemDelegate8fileItemERK11QModelIndex@Base 6.13.0
  _ZNK17KFileItemDelegate8iconRectERK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
  _ZNK17KFileItemDelegate8sizeHintERK20QStyleOptionViewItemRK11QModelIndex@Base 6.0.0
  _ZNK17KFileItemDelegate8wrapModeEv@Base 6.0.0
diff -Nru kf6-kio-6.13.0/debian/patches/series kf6-kio-6.13.0/debian/patches/series
--- kf6-kio-6.13.0/debian/patches/series	2025-04-23 19:10:13.000000000 +0200
+++ kf6-kio-6.13.0/debian/patches/series	2025-05-20 08:47:36.000000000 +0200
@@ -1,6 +1,17 @@
-# fixed in 6.14
-upstream_KUrlNavigatorButton.patch
-
 report_error_removing_dirs
 hurd_disable_unimplemented.diff
 Use-CXX_FLAGS-for-moc_predefs.h.patch
+
+upstream_d8441b7b_Show-single-click-selection-emblem-when-using-single-click-mouse-mode.patch
+# fixed in 6.14
+upstream_KUrlNavigatorButton.patch
+upstream_cd0810f8_KFileWidget-Do-not-override-filename-with-folder-name-if-edited.patch
+upstream_31806c51_KProcessRunner-Fix-launching-actions-without-executable.patch
+upstream_3c3d5904_core-Add-infrastructure-for-inhibiting-suspend-in-jobs.patch
+upstream_60191c04_CopyJob-Inhibit-suspend-during-copy-operation.patch
+upstream_17cd1c9b_DeleteOrTrashJob-Add-started-signal.patch
+upstream_3e6175e4_KFilePlacesView-Show-busy-indicator-while-emptying-trash.patch
+upstream_719e0b00_KFileWidget-Fix-key-navigation-escaping-in-save-dialogs.patch
+upstream_c747fa0f_Avoid-unnecessary-sequencing-jobs-in-PreviewGenerator.patch
+upstream_81ca0e2c_Fix-incorrect-usage-percentage-in-Properties-dialog-for-virtual-filesystems.patch
+upstream_9143fc2a_Fix-dialog-modality-settings.patch
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_17cd1c9b_DeleteOrTrashJob-Add-started-signal.patch kf6-kio-6.13.0/debian/patches/upstream_17cd1c9b_DeleteOrTrashJob-Add-started-signal.patch
--- kf6-kio-6.13.0/debian/patches/upstream_17cd1c9b_DeleteOrTrashJob-Add-started-signal.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_17cd1c9b_DeleteOrTrashJob-Add-started-signal.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,50 @@
+From 17cd1c9bb99c4043018eda7b1a789af98b29f2b4 Mon Sep 17 00:00:00 2001
+From: Kai Uwe Broulik <kde@privat.broulik.de>
+Date: Thu, 1 May 2025 19:01:23 +0200
+Subject: [PATCH] DeleteOrTrashJob: Add started signal
+
+It's emitted when the user has confirmed emptying trash and
+it will actually commence emptying it.
+---
+ src/widgets/deleteortrashjob.cpp |  2 ++
+ src/widgets/deleteortrashjob.h   | 11 +++++++++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/src/widgets/deleteortrashjob.cpp b/src/widgets/deleteortrashjob.cpp
+index 1f182c99c..1af01a236 100644
+--- a/src/widgets/deleteortrashjob.cpp
++++ b/src/widgets/deleteortrashjob.cpp
+@@ -87,6 +87,8 @@ void DeleteOrTrashJobPrivate::slotAskUser(bool allowDelete, const QList<QUrl> &u
+         // show the "File is too large to Trash" error message
+         job->uiDelegate()->setAutoErrorHandlingEnabled(false);
+         q->addSubjob(job);
++
++        Q_EMIT q->started();
+     }
+ }
+ 
+diff --git a/src/widgets/deleteortrashjob.h b/src/widgets/deleteortrashjob.h
+index dfcc6c8ee..486a1be70 100644
+--- a/src/widgets/deleteortrashjob.h
++++ b/src/widgets/deleteortrashjob.h
+@@ -74,6 +74,17 @@ public:
+      */
+     void start() override;
+ 
++Q_SIGNALS:
++    /**
++     * Emitted when the actual delete or trash job has been started.
++     *
++     * This can be used to display a busy indicator after the user has confirmed
++     * this operation.
++     *
++     * @since 6.15
++     */
++    void started();
++
+ private:
+     void slotResult(KJob *job) override;
+ 
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_31806c51_KProcessRunner-Fix-launching-actions-without-executable.patch kf6-kio-6.13.0/debian/patches/upstream_31806c51_KProcessRunner-Fix-launching-actions-without-executable.patch
--- kf6-kio-6.13.0/debian/patches/upstream_31806c51_KProcessRunner-Fix-launching-actions-without-executable.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_31806c51_KProcessRunner-Fix-launching-actions-without-executable.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,108 @@
+From 31806c5180eb7cd1ad2f354eafd84f8fc490eae2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ball=C3=B3=20Gy=C3=B6rgy?= <ballogyor@gmail.com>
+Date: Fri, 4 Apr 2025 08:08:31 +0200
+Subject: [PATCH] KProcessRunner: Fix launching actions without executable
+
+If D-Bus activation is possible, it's not needed to have an executable,
+since everything is done via D-Bus. Therefore look for the matching action
+name instead of the exec value when launching an action. The specification
+allows to omit the "Exec" key if "DBusActivatable" is true.
+
+Most applications specify the Exec parameter for compatibility reasons, but
+if the action's "Exec" line got removed from a D-Bus activatable desktop
+file, then KIO just activates the application rather than calling the
+requested action. This change fixes that issue.
+---
+ src/gui/applicationlauncherjob.cpp | 7 +++++--
+ src/gui/kprocessrunner.cpp         | 8 ++++++--
+ src/gui/kprocessrunner_p.h         | 1 +
+ 3 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/src/gui/applicationlauncherjob.cpp b/src/gui/applicationlauncherjob.cpp
+index 463cec1be..f3cafafc8 100644
+--- a/src/gui/applicationlauncherjob.cpp
++++ b/src/gui/applicationlauncherjob.cpp
+@@ -49,6 +49,7 @@ public:
+     KService::Ptr m_service;
+     QString m_serviceEntryPath;
+     QList<QUrl> m_urls;
++    QString m_actionName;
+     KIO::ApplicationLauncherJob::RunFlags m_runFlags;
+     QString m_suggestedFileName;
+     QString m_mimeTypeName;
+@@ -75,6 +76,7 @@ KIO::ApplicationLauncherJob::ApplicationLauncherJob(const KServiceAction &servic
+     Q_ASSERT(d->m_service);
+     d->m_service.detach();
+     d->m_service->setExec(serviceAction.exec());
++    d->m_actionName = serviceAction.name();
+ }
+ KIO::ApplicationLauncherJob::ApplicationLauncherJob(const KDesktopFileAction &desktopFileAction, QObject *parent)
+     : ApplicationLauncherJob(KService::Ptr(new KService(desktopFileAction.desktopFilePath())), parent)
+@@ -82,6 +84,7 @@ KIO::ApplicationLauncherJob::ApplicationLauncherJob(const KDesktopFileAction &de
+     Q_ASSERT(d->m_service);
+     d->m_service.detach();
+     d->m_service->setExec(desktopFileAction.exec());
++    d->m_actionName = desktopFileAction.name();
+ }
+ 
+ KIO::ApplicationLauncherJob::ApplicationLauncherJob(QObject *parent)
+@@ -196,7 +199,7 @@ void KIO::ApplicationLauncherJob::proceedAfterSecurityChecks()
+         d->m_processRunners.reserve(d->m_numProcessesPending);
+         for (int i = 1; i < d->m_urls.count(); ++i) {
+             auto *processRunner =
+-                KProcessRunner::fromApplication(d->m_service, d->m_serviceEntryPath, {d->m_urls.at(i)}, d->m_runFlags, d->m_suggestedFileName, QByteArray{});
++                KProcessRunner::fromApplication(d->m_service, d->m_serviceEntryPath, {d->m_urls.at(i)}, d->m_actionName, d->m_runFlags, d->m_suggestedFileName, QByteArray{});
+             d->m_processRunners.push_back(processRunner);
+             connect(processRunner, &KProcessRunner::processStarted, this, [this](qint64 pid) {
+                 d->slotStarted(pid);
+@@ -208,7 +211,7 @@ void KIO::ApplicationLauncherJob::proceedAfterSecurityChecks()
+     }
+ 
+     auto *processRunner =
+-        KProcessRunner::fromApplication(d->m_service, d->m_serviceEntryPath, d->m_urls, d->m_runFlags, d->m_suggestedFileName, d->m_startupId);
++        KProcessRunner::fromApplication(d->m_service, d->m_serviceEntryPath, d->m_urls, d->m_actionName, d->m_runFlags, d->m_suggestedFileName, d->m_startupId);
+     d->m_processRunners.push_back(processRunner);
+     connect(processRunner, &KProcessRunner::error, this, [this](const QString &errorText) {
+         setError(KJob::UserDefinedError);
+diff --git a/src/gui/kprocessrunner.cpp b/src/gui/kprocessrunner.cpp
+index 64fa3afb2..00f7811e8 100644
+--- a/src/gui/kprocessrunner.cpp
++++ b/src/gui/kprocessrunner.cpp
+@@ -88,6 +88,7 @@ static void modifyEnv(KProcess &process, QProcessEnvironment mod)
+ KProcessRunner *KProcessRunner::fromApplication(const KService::Ptr &service,
+                                                 const QString &serviceEntryPath,
+                                                 const QList<QUrl> &urls,
++                                                const QString &actionName,
+                                                 KIO::ApplicationLauncherJob::RunFlags flags,
+                                                 const QString &suggestedFileName,
+                                                 const QByteArray &asn)
+@@ -103,9 +104,12 @@ KProcessRunner *KProcessRunner::fromApplication(const KService::Ptr &service,
+     const bool notYetSupportedOpenActivationNeeded = !urls.isEmpty();
+     if (!notYetSupportedOpenActivationNeeded && DBusActivationRunner::activationPossible(service, flags, suggestedFileName)) {
+         const auto actions = service->actions();
+-        auto action = std::find_if(actions.cbegin(), actions.cend(), [service](const KServiceAction &action) {
+-            return action.exec() == service->exec();
++        auto action = std::find_if(actions.cbegin(), actions.cend(), [actionName](const KServiceAction &action) {
++            return action.name() == actionName;
+         });
++        if (!actionName.isEmpty() && action == actions.cend()) {
++            qCWarning(KIO_GUI) << "Requested action" << actionName << "cannot be found for" << service->name();
++        }
+         instance = new DBusActivationRunner(action != actions.cend() ? action->name() : QString());
+     } else {
+         instance = makeInstance();
+diff --git a/src/gui/kprocessrunner_p.h b/src/gui/kprocessrunner_p.h
+index 63e517d37..7f084a49e 100644
+--- a/src/gui/kprocessrunner_p.h
++++ b/src/gui/kprocessrunner_p.h
+@@ -59,6 +59,7 @@ public:
+     static KProcessRunner *fromApplication(const KService::Ptr &service,
+                                            const QString &serviceEntryPath,
+                                            const QList<QUrl> &urls,
++                                           const QString &actionName = {},
+                                            KIO::ApplicationLauncherJob::RunFlags flags = {},
+                                            const QString &suggestedFileName = {},
+                                            const QByteArray &asn = {});
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_3c3d5904_core-Add-infrastructure-for-inhibiting-suspend-in-jobs.patch kf6-kio-6.13.0/debian/patches/upstream_3c3d5904_core-Add-infrastructure-for-inhibiting-suspend-in-jobs.patch
--- kf6-kio-6.13.0/debian/patches/upstream_3c3d5904_core-Add-infrastructure-for-inhibiting-suspend-in-jobs.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_3c3d5904_core-Add-infrastructure-for-inhibiting-suspend-in-jobs.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,594 @@
+From 3c3d590472b1878660ae55f0f7c1bed5600ebb65 Mon Sep 17 00:00:00 2001
+From: Kai Uwe Broulik <kde@privat.broulik.de>
+Date: Sun, 20 Apr 2025 12:48:26 +0200
+Subject: [PATCH] core: Add infrastructure for inhibiting suspend in jobs
+
+This calls the freedesktop Inhibit interface on DBus which will
+inhibit suspend (but not display power management/screensaver).
+When inside a sandbox it instead calls the XDG Desktop Portal
+Inhibit interface.
+
+When a job is destroyed or gets suspended, the inhibition is lifted.
+When a job is resumed, `doInhibitSuspend` is called again to re-instate
+the inhibition.
+
+It is the job's responsibility to call `doInhibitSuspend` at
+the appropriate time (e.g. in doStart/slotStart).
+---
+ src/core/CMakeLists.txt                       |   5 +
+ src/core/config-kiocore.h.cmake               |   2 +
+ src/core/job.cpp                              | 139 +++++++++++++-
+ src/core/job_p.h                              |  16 ++
+ ...rg.freedesktop.PowerManagement.Inhibit.xml |  20 ++
+ src/core/org.freedesktop.portal.Inhibit.xml   | 173 ++++++++++++++++++
+ src/core/org.freedesktop.portal.Request.xml   |  93 ++++++++++
+ 7 files changed, 446 insertions(+), 2 deletions(-)
+ create mode 100644 src/core/org.freedesktop.PowerManagement.Inhibit.xml
+ create mode 100644 src/core/org.freedesktop.portal.Inhibit.xml
+ create mode 100644 src/core/org.freedesktop.portal.Request.xml
+
+diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
+index 1dacfa081..599e001f9 100644
+--- a/src/core/CMakeLists.txt
++++ b/src/core/CMakeLists.txt
+@@ -164,6 +164,11 @@ if (HAVE_QTDBUS)
+             PROPERTIES INCLUDE authinfo.h
+     )
+     qt_add_dbus_interface(kiocore_dbus_SRCS org.kde.KPasswdServer.xml kpasswdserver_interface)
++
++    qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.PowerManagement.Inhibit.xml inhibit_interface)
++
++    qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.portal.Inhibit.xml portal_inhibit_interface)
++    qt_add_dbus_interface(kiocore_dbus_SRCS org.freedesktop.portal.Request.xml portal_request_interface)
+ endif()
+ 
+ target_sources(KF6KIOCore PRIVATE
+diff --git a/src/core/config-kiocore.h.cmake b/src/core/config-kiocore.h.cmake
+index 1f0bc42f2..bd9e7582a 100644
+--- a/src/core/config-kiocore.h.cmake
++++ b/src/core/config-kiocore.h.cmake
+@@ -7,6 +7,8 @@
+ /* Defined if sys/acl.h exists */
+ #cmakedefine01 HAVE_SYS_ACL_H
+ 
++#cmakedefine01 HAVE_QTDBUS
++
+ #define KDE_INSTALL_FULL_LIBEXECDIR_KF "${KDE_INSTALL_FULL_LIBEXECDIR_KF}"
+ 
+ #define KDE_INSTALL_FULL_KIO_PLUGINDIR "${KDE_INSTALL_FULL_PLUGINDIR}/kf6/kio/"
+diff --git a/src/core/job.cpp b/src/core/job.cpp
+index e8360d468..4eb037816 100644
+--- a/src/core/job.cpp
++++ b/src/core/job.cpp
+@@ -13,13 +13,30 @@
+ #include <time.h>
+ 
+ #include <KLocalizedString>
++#include <KSandbox>
+ #include <KStringHandler>
+ 
++#include "kiocoredebug.h"
+ #include "worker_p.h"
+ #include <kio/jobuidelegateextension.h>
+ 
++#if HAVE_QTDBUS
++#include <QDBusConnection>
++#include <QDBusPendingCallWatcher>
++
++#include "inhibit_interface.h"
++#include "portal_inhibit_interface.h"
++#include "portal_request_interface.h"
++#endif
++
+ using namespace KIO;
+ 
++static constexpr QLatin1String g_portalServiceName{"org.freedesktop.portal.Desktop"};
++static constexpr QLatin1String g_portalInhibitObjectPath{"/org/freedesktop/portal/desktop"};
++
++static constexpr QLatin1String g_inhibitServiceName{"org.freedesktop.PowerManagement.Inhibit"};
++static constexpr QLatin1String g_inhibitObjectPath{"/org/freedesktop/PowerManagement/Inhibit"};
++
+ Job::Job()
+     : KCompositeJob(nullptr)
+     , d_ptr(new JobPrivate)
+@@ -89,9 +106,127 @@ static QString url_description_string(const QUrl &url)
+ }
+ 
+ KIO::JobPrivate::~JobPrivate()
++{
++    uninhibitSuspend();
++}
++
++void JobPrivate::doInhibitSuspend()
+ {
+ }
+ 
++void JobPrivate::inhibitSuspend(const QString &reason)
++{
++#if HAVE_QTDBUS
++    if (KSandbox::isInside()) {
++        Q_ASSERT(m_portalInhibitionRequest.path().isEmpty());
++
++        org::freedesktop::portal::Inhibit inhibitInterface{g_portalServiceName, g_portalInhibitObjectPath, QDBusConnection::sessionBus()};
++        QVariantMap args;
++        if (!reason.isEmpty()) {
++            args.insert(QStringLiteral("reason"), reason);
++        }
++        auto call = inhibitInterface.Inhibit(QString() /* TODO window. */, 4 /* Suspend */, args);
++        // This is not parented to the job, so we can properly clean up the inhibiton
++        // should the job finish before the inhibition has been processed.
++        auto *watcher = new QDBusPendingCallWatcher(call);
++        QPointer<Job> guard(q_ptr);
++        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this, guard, watcher, reason] {
++            QDBusPendingReply<QDBusObjectPath> reply = *watcher;
++
++            if (reply.isError()) {
++                qCWarning(KIO_CORE).nospace() << "Failed to inhibit suspend with reason " << reason << ": " << reply.error().message();
++            } else {
++                const QDBusObjectPath requestPath = reply.value();
++
++                // By the time the inhibition returned, the job was already gone. Uninhibit again.
++                if (!guard) {
++                    org::freedesktop::portal::Request requestInterface{g_portalServiceName, requestPath.path(), QDBusConnection::sessionBus()};
++                    requestInterface.Close();
++                } else {
++                    m_portalInhibitionRequest = requestPath;
++                }
++            }
++
++            watcher->deleteLater();
++        });
++    } else {
++        Q_ASSERT(!m_inhibitionCookie);
++
++        QString appName = q_ptr->property("desktopFileName").toString();
++        if (appName.isEmpty()) {
++            // desktopFileName is in QGuiApplication but we're in KIO Core here.
++            appName = QCoreApplication::instance()->property("desktopFileName").toString();
++        }
++        if (appName.isEmpty()) {
++            appName = QCoreApplication::applicationName();
++        }
++
++        org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
++        auto call = inhibitInterface.Inhibit(appName, reason);
++        auto *watcher = new QDBusPendingCallWatcher(call);
++        QPointer<Job> guard(q_ptr);
++        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this, guard, watcher, appName, reason] {
++            QDBusPendingReply<uint> reply = *watcher;
++
++            if (reply.isError()) {
++                qCWarning(KIO_CORE).nospace() << "Failed to inhibit suspend for " << appName << " with reason " << reason << ": " << reply.error().message();
++            } else {
++                const uint cookie = reply.value();
++
++                if (!guard) {
++                    org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
++                    inhibitInterface.UnInhibit(cookie);
++                } else {
++                    m_inhibitionCookie = cookie;
++                }
++            }
++
++            watcher->deleteLater();
++        });
++    }
++#else
++    Q_UNUSED(reason)
++#endif
++}
++
++void JobPrivate::uninhibitSuspend()
++{
++#if HAVE_QTDBUS
++    if (!m_portalInhibitionRequest.path().isEmpty()) {
++        org::freedesktop::portal::Request requestInterface{g_portalServiceName, m_portalInhibitionRequest.path(), QDBusConnection::sessionBus()};
++        auto call = requestInterface.Close();
++        auto *watcher = new QDBusPendingCallWatcher(call, q_ptr);
++        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q_ptr, [this, watcher] {
++            QDBusPendingReply<> reply = *watcher;
++
++            if (reply.isError()) {
++                qCWarning(KIO_CORE) << "Failed to uninhibit suspend:" << reply.error().message();
++            } else {
++                m_portalInhibitionRequest = QDBusObjectPath();
++            }
++
++            watcher->deleteLater();
++        });
++    } else if (m_inhibitionCookie) {
++        org::freedesktop::PowerManagement::Inhibit inhibitInterface{g_inhibitServiceName, g_inhibitObjectPath, QDBusConnection::sessionBus()};
++        const int cookie = *m_inhibitionCookie;
++        auto call = inhibitInterface.UnInhibit(cookie);
++        auto *watcher = new QDBusPendingCallWatcher(call, q_ptr);
++        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q_ptr, [this, watcher, cookie] {
++            QDBusPendingReply<> reply = *watcher;
++
++            if (reply.isError()) {
++                qCWarning(KIO_CORE).nospace() << "Failed to uninhibit suspend for cookie" << cookie << ": " << reply.error().message();
++            } else {
++                m_inhibitionCookie.reset();
++            }
++
++            watcher->deleteLater();
++        });
++    }
++#endif
++}
++
+ void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest)
+ {
+     static const QString s_title = i18nc("@title job", "Moving");
+@@ -172,7 +307,7 @@ bool Job::doSuspend()
+             return false;
+         }
+     }
+-
++    d_ptr->uninhibitSuspend();
+     return true;
+ }
+ 
+@@ -183,7 +318,7 @@ bool Job::doResume()
+             return false;
+         }
+     }
+-
++    d_ptr->doInhibitSuspend();
+     return true;
+ }
+ 
+diff --git a/src/core/job_p.h b/src/core/job_p.h
+index e9eab0cf6..e8191198c 100644
+--- a/src/core/job_p.h
++++ b/src/core/job_p.h
+@@ -12,6 +12,8 @@
+ #ifndef KIO_JOB_P_H
+ #define KIO_JOB_P_H
+ 
++#include "config-kiocore.h"
++
+ #include "commands_p.h"
+ #include "global.h"
+ #include "jobtracker.h"
+@@ -26,6 +28,12 @@
+ #include <kio/jobuidelegateextension.h>
+ #include <kio/jobuidelegatefactory.h>
+ 
++#if HAVE_QTDBUS
++#include <QDBusObjectPath>
++
++#include <optional>
++#endif
++
+ /* clang-format off */
+ #define KIO_ARGS \
+     QByteArray packedArgs; \
+@@ -84,6 +92,10 @@ public:
+     MetaData m_outgoingMetaData;
+     JobUiDelegateExtension *m_uiDelegateExtension;
+     Job *q_ptr;
++#if HAVE_QTDBUS
++    std::optional<uint> m_inhibitionCookie; // fdo.
++    QDBusObjectPath m_portalInhibitionRequest; // portal.
++#endif
+     // For privilege operation
+     bool m_privilegeExecutionEnabled;
+     QString m_title, m_message;
+@@ -92,6 +104,10 @@ public:
+     QByteArray privilegeOperationData();
+     void slotSpeed(KJob *job, unsigned long speed);
+ 
++    void inhibitSuspend(const QString &reason);
++    void uninhibitSuspend();
++    virtual void doInhibitSuspend();
++
+     static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest);
+     static void emitRenaming(KIO::Job *, const QUrl &src, const QUrl &dest);
+     static void emitCopying(KIO::Job *, const QUrl &src, const QUrl &dest);
+diff --git a/src/core/org.freedesktop.PowerManagement.Inhibit.xml b/src/core/org.freedesktop.PowerManagement.Inhibit.xml
+new file mode 100644
+index 000000000..21dfce8c7
+--- /dev/null
++++ b/src/core/org.freedesktop.PowerManagement.Inhibit.xml
+@@ -0,0 +1,20 @@
++<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
++"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
++<node>
++  <interface name="org.freedesktop.PowerManagement.Inhibit">
++    <method name="Inhibit">
++      <arg direction="in" type="s" name="application"/>
++      <arg direction="in" type="s" name="reason"/>
++      <arg direction="out" type="u" name="cookie"/>
++    </method>
++    <method name="UnInhibit">
++      <arg direction="in" type="u" name="cookie"/>
++    </method>
++    <signal name="HasInhibitChanged">
++      <arg direction="out" type="b" name="has_inhibit"/>
++    </signal>
++    <method name="HasInhibit">
++      <arg direction="out" type="b" name="has_inhibit"/>
++    </method>
++  </interface>
++</node>
+diff --git a/src/core/org.freedesktop.portal.Inhibit.xml b/src/core/org.freedesktop.portal.Inhibit.xml
+new file mode 100644
+index 000000000..1ae413e18
+--- /dev/null
++++ b/src/core/org.freedesktop.portal.Inhibit.xml
+@@ -0,0 +1,173 @@
++<?xml version="1.0"?>
++<!--
++ Copyright (C) 2016 Red Hat, Inc.
++
++ SPDX-License-Identifier: LGPL-2.1-or-later
++
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 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
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with this library. If not, see <http://www.gnu.org/licenses/>.
++
++ Author: Matthias Clasen <mclasen@redhat.com>
++-->
++
++<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
++  <!--
++      org.freedesktop.portal.Inhibit:
++      @short_description: Portal for inhibiting session transitions
++
++      This simple interface lets sandboxed applications inhibit the user
++      session from ending, suspending, idling or getting switched away.
++
++      This documentation describes version 3 of this interface.
++  -->
++  <interface name="org.freedesktop.portal.Inhibit">
++    <!--
++        Inhibit:
++        @window: Identifier for the window
++        @flags: Flags identifying what is inhibited
++        @options: Vardict with optional further information
++        @handle: Object path for the :ref:`org.freedesktop.portal.Request` object representing this call
++
++        Inhibits a session status changes. To remove the inhibition,
++        call :ref:`org.freedesktop.portal.Request.Close` on the returned
++        handle.
++
++        The flags determine what changes are inhibited:
++
++        - ``1``: Logout
++        - ``2``: User Switch
++        - ``4``: Suspend
++        - ``8``: Idle
++
++        Supported keys in the @options vardict include:
++
++        * ``handle_token`` (``s``)
++
++          A string that will be used as the last element of the @handle. Must be a valid
++          object path element. See the :ref:`org.freedesktop.portal.Request` documentation for
++          more information about the @handle.
++
++        * ``reason`` (``s``)
++
++          User-visible reason for the inhibition.
++    -->
++    <method name="Inhibit">
++      <arg type="s" name="window" direction="in"/>
++      <arg type="u" name="flags" direction="in"/>
++      <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
++      <arg type="a{sv}" name="options" direction="in"/>
++      <arg type="o" name="handle" direction="out"/>
++    </method>
++
++    <!--
++        CreateMonitor:
++        @window: the parent window
++        @options: Vardict with optional further information
++        @handle: Object path for the :ref:`org.freedesktop.portal.Request` object representing this call
++
++        Creates a monitoring session. While this session is
++        active, the caller will receive StateChanged signals
++        with updates on the session state.
++
++        A successfully created session can at any time be closed using
++        org.freedesktop.portal.Session::Close, or may at any time be closed
++        by the portal implementation, which will be signalled via
++        :ref:`org.freedesktop.portal.Session::Closed`.
++
++        Supported keys in the @options vardict include:
++
++        * ``handle_token`` (``s``)
++
++          A string that will be used as the last element of the @handle. Must be a valid
++          object path element. See the :ref:`org.freedesktop.portal.Request` documentation for
++          more information about the @handle.
++
++        * ``session_handle_token`` (``s``)
++
++          A string that will be used as the last element of the session handle. Must be a valid
++          object path element. See the :ref:`org.freedesktop.portal.Session` documentation for
++          more information about the session handle.
++
++        The following results get returned via the :ref:`org.freedesktop.portal.Request::Response` signal:
++
++        * ``session_handle`` (``s``)
++
++          The session handle. An object path for the
++          :ref:`org.freedesktop.portal.Session` object representing the created
++          session.
++
++          .. note::
++            The ``session_handle`` is an object path that was erroneously implemented
++            as ``s``. For backwards compatibility it will remain this type.
++
++        This method was added in version 2 of this interface.
++    -->
++    <method name="CreateMonitor">
++      <arg type="s" name="window" direction="in"/>
++      <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
++      <arg type="a{sv}" name="options" direction="in"/>
++      <arg type="o" name="handle" direction="out"/>
++    </method>
++
++    <!--
++        StateChanged:
++        @session_handle: Object path for the :ref:`org.freedesktop.portal.Session` object
++        @state: Vardict with information about the session state
++
++        The StateChanged signal is sent to active monitoring sessions when
++        the session state changes.
++
++        When the session state changes to 'Query End', clients with active monitoring
++        sessions are expected to respond by calling
++        org.freedesktop.portal.Inhibit.QueryEndResponse() within a second
++        of receiving the StateChanged signal. They may call org.freedesktop.portal.Inhibit.Inhibit()
++        first to inhibit logout, to prevent the session from proceeding to the Ending state.
++
++        The following information may get returned in the @state vardict:
++
++        * ``screensaver-active`` (``b``)
++
++          Whether the screensaver is active.
++
++        * ``session-state`` (``u``)
++
++          The state of the session. This member is new in version 3.
++
++          - ``1``: Running
++          - ``2``: Query End
++          - ``3``: Ending
++
++    -->
++    <signal name="StateChanged">
++      <arg type="o" name="session_handle" direction="out"/>
++      <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
++      <arg type="a{sv}" name="state" direction="out"/>
++    </signal>
++
++    <!--
++      QueryEndResponse:
++      @session_handle: Object path for the :ref:`org.freedesktop.portal.Session` object
++
++      Acknowledges that the caller received the #org.freedesktop.portal.Inhibit::StateChanged
++      signal. This method should be called within one second or receiving a StateChanged
++      signal with the 'Query End' state.
++
++      Since version 3.
++    -->
++    <method name="QueryEndResponse">
++      <arg type="o" name="session_handle" direction="in"/>
++    </method>
++
++    <property name="version" type="u" access="read"/>
++  </interface>
++</node>
+diff --git a/src/core/org.freedesktop.portal.Request.xml b/src/core/org.freedesktop.portal.Request.xml
+new file mode 100644
+index 000000000..e8a26484e
+--- /dev/null
++++ b/src/core/org.freedesktop.portal.Request.xml
+@@ -0,0 +1,93 @@
++<?xml version="1.0"?>
++<!--
++ Copyright (C) 2015 Red Hat, Inc.
++
++ SPDX-License-Identifier: LGPL-2.1-or-later
++
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 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
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with this library. If not, see <http://www.gnu.org/licenses/>.
++
++ Author: Alexander Larsson <alexl@redhat.com>
++-->
++
++<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
++  <!--
++      org.freedesktop.portal.Request:
++      @short_description: Shared request interface
++
++      The Request interface is shared by all portal interfaces. When a
++      portal method is called, the reply includes a handle (i.e. object path)
++      for a Request object, which will stay alive for the duration of the
++      user interaction related to the method call.
++
++      The portal indicates that a portal request interaction is over by
++      emitting the #org.freedesktop.portal.Request::Response signal on the
++      Request object.
++
++      The application can abort the interaction calling
++      org.freedesktop.portal.Request.Close() on the Request object.
++
++      Since version 0.9 of xdg-desktop-portal, the handle will be of the form
++
++      ::
++
++        /org/freedesktop/portal/desktop/request/SENDER/TOKEN
++
++
++      where ``SENDER`` is the callers unique name, with the initial ``':'`` removed and
++      all ``'.'`` replaced by ``'_'``, and ``TOKEN`` is a unique token that the caller provided
++      with the handle_token key in the options vardict.
++
++      This change was made to let applications subscribe to the Response signal before
++      making the initial portal call, thereby avoiding a race condition. It is recommended
++      that the caller should verify that the returned handle is what it expected, and update
++      its signal subscription if it isn't. This ensures that applications will work with both
++      old and new versions of xdg-desktop-portal.
++
++      The token that the caller provides should be unique and not guessable. To avoid clashes
++      with calls made from unrelated libraries, it is a good idea to use a per-library prefix
++      combined with a random number.
++  -->
++  <interface name="org.freedesktop.portal.Request">
++
++    <!--
++        Close:
++
++        Closes the portal request to which this object refers and ends all
++        related user interaction (dialogs, etc).
++
++        A Response signal will not be emitted in this case.
++    -->
++    <method name="Close">
++    </method>
++
++    <!--
++        Response:
++        @response: Numeric response
++        @results: Vardict with results. The keys and values in the vardict depend on the request.
++
++        Emitted when the user interaction for a portal request is over.
++
++        The @response indicates how the user interaction ended:
++
++        - 0: Success, the request is carried out
++        - 1: The user cancelled the interaction
++        - 2: The user interaction was ended in some other way
++    -->
++    <signal name="Response">
++      <arg type="u" name="response"/>
++      <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
++      <arg type="a{sv}" name="results"/>
++    </signal>
++  </interface>
++</node>
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_3e6175e4_KFilePlacesView-Show-busy-indicator-while-emptying-trash.patch kf6-kio-6.13.0/debian/patches/upstream_3e6175e4_KFilePlacesView-Show-busy-indicator-while-emptying-trash.patch
--- kf6-kio-6.13.0/debian/patches/upstream_3e6175e4_KFilePlacesView-Show-busy-indicator-while-emptying-trash.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_3e6175e4_KFilePlacesView-Show-busy-indicator-while-emptying-trash.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,151 @@
+From 3e6175e4cf2898a0b9a15c6218765f6e8d7fd57c Mon Sep 17 00:00:00 2001
+From: Kai Uwe Broulik <kde@privat.broulik.de>
+Date: Thu, 1 May 2025 19:02:48 +0200
+Subject: [PATCH] KFilePlacesView: Show busy indicator while emptying trash
+
+Provide some feedback to the user in case it's taking a bit.
+---
+ src/filewidgets/kfileplacesview.cpp | 49 +++++++++++++++++++++++------
+ src/filewidgets/kfileplacesview_p.h |  4 +++
+ 2 files changed, 43 insertions(+), 10 deletions(-)
+
+diff --git a/src/filewidgets/kfileplacesview.cpp b/src/filewidgets/kfileplacesview.cpp
+index d2488ec32..30ca95f22 100644
+--- a/src/filewidgets/kfileplacesview.cpp
++++ b/src/filewidgets/kfileplacesview.cpp
+@@ -154,7 +154,8 @@ void KFilePlacesViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte
+     QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
+ 
+     const auto accessibility = placesModel->deviceAccessibility(index);
+-    const bool isBusy = (accessibility == KFilePlacesModel::SetupInProgress || accessibility == KFilePlacesModel::TeardownInProgress);
++    const bool isBusy = (accessibility == KFilePlacesModel::SetupInProgress || accessibility == KFilePlacesModel::TeardownInProgress)
++        || (m_emptyingTrashIndex.isValid() && m_emptyingTrashIndex == index);
+ 
+     QIcon actionIcon;
+     if (isBusy) {
+@@ -458,6 +459,16 @@ void KFilePlacesViewDelegate::setHoveredAction(const QModelIndex &index)
+     m_hoveredAction = index;
+ }
+ 
++QModelIndex KFilePlacesViewDelegate::emptyingTrashIndex() const
++{
++    return m_emptyingTrashIndex;
++}
++
++void KFilePlacesViewDelegate::setEmptyingTrashIndex(const QModelIndex &index)
++{
++    m_emptyingTrashIndex = index;
++}
++
+ bool KFilePlacesViewDelegate::pointIsHeaderArea(const QPoint &pos) const
+ {
+     // we only accept drag events starting from item body, ignore drag request from header
+@@ -769,9 +780,11 @@ public:
+     void itemAppearUpdate(qreal value);
+     void itemDisappearUpdate(qreal value);
+     void enableSmoothItemResizing();
+-    void slotEmptyTrash();
++    void slotEmptyTrash(const QModelIndex &index);
+ 
+     void deviceBusyAnimationValueChanged(const QVariant &value);
++    void startOrStopBusyAnimation();
++    void setEmptyingTrashIndex(const QModelIndex &index);
+ 
+     KFilePlacesView *const q;
+ 
+@@ -1123,8 +1136,9 @@ void KFilePlacesViewPrivate::writeConfig()
+     cg.sync();
+ }
+ 
+-void KFilePlacesViewPrivate::slotEmptyTrash()
++void KFilePlacesViewPrivate::slotEmptyTrash(const QModelIndex &index)
+ {
++    QPersistentModelIndex persistentIndex(index);
+     auto *parentWindow = q->window();
+ 
+     using AskIface = KIO::AskUserActionInterface;
+@@ -1132,9 +1146,26 @@ void KFilePlacesViewPrivate::slotEmptyTrash()
+                                                     AskIface::EmptyTrash,
+                                                     AskIface::DefaultConfirmation,
+                                                     parentWindow);
++    QObject::connect(emptyTrashJob, &KIO::DeleteOrTrashJob::started, q, [this, persistentIndex] {
++        m_delegate->setEmptyingTrashIndex(persistentIndex);
++        startOrStopBusyAnimation();
++    });
++    QObject::connect(emptyTrashJob, &KJob::finished, q, [this] {
++        m_delegate->setEmptyingTrashIndex({});
++        startOrStopBusyAnimation();
++    });
+     emptyTrashJob->start();
+ }
+ 
++void KFilePlacesViewPrivate::startOrStopBusyAnimation()
++{
++    if (!m_busyDevices.isEmpty() || m_delegate->emptyingTrashIndex().isValid()) {
++        m_deviceBusyAnimation.start();
++    } else {
++        m_deviceBusyAnimation.stop();
++    }
++}
++
+ void KFilePlacesView::contextMenuEvent(QContextMenuEvent *event)
+ {
+     KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel *>(model());
+@@ -1327,7 +1358,7 @@ void KFilePlacesView::contextMenuEvent(QContextMenuEvent *event)
+ 
+     if (result) {
+         if (result == emptyTrash) {
+-            d->slotEmptyTrash();
++            d->slotEmptyTrash(index);
+ 
+         } else if (result == eject) {
+             placesModel->requestEject(index);
+@@ -2136,6 +2167,9 @@ void KFilePlacesViewPrivate::deviceBusyAnimationValueChanged(const QVariant &val
+     for (const auto &idx : std::as_const(m_busyDevices)) {
+         q->update(idx);
+     }
++    if (m_delegate->emptyingTrashIndex().isValid()) {
++        q->update(m_delegate->emptyingTrashIndex());
++    }
+ }
+ 
+ void KFilePlacesView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles)
+@@ -2156,12 +2190,7 @@ void KFilePlacesView::dataChanged(const QModelIndex &topLeft, const QModelIndex
+         }
+ 
+         d->m_busyDevices = busyDevices;
+-
+-        if (busyDevices.isEmpty()) {
+-            d->m_deviceBusyAnimation.stop();
+-        } else {
+-            d->m_deviceBusyAnimation.start();
+-        }
++        d->startOrStopBusyAnimation();
+     }
+ }
+ 
+diff --git a/src/filewidgets/kfileplacesview_p.h b/src/filewidgets/kfileplacesview_p.h
+index 694d555e0..b51516dce 100644
+--- a/src/filewidgets/kfileplacesview_p.h
++++ b/src/filewidgets/kfileplacesview_p.h
+@@ -58,6 +58,9 @@ public:
+     void setHoveredHeaderArea(const QModelIndex &index);
+     void setHoveredAction(const QModelIndex &index);
+ 
++    QModelIndex emptyingTrashIndex() const;
++    void setEmptyingTrashIndex(const QModelIndex &index);
++
+     qreal contentsOpacity(const QModelIndex &index) const;
+ 
+     bool pointIsHeaderArea(const QPoint &pos) const;
+@@ -100,6 +103,7 @@ private:
+     qreal m_disappearingOpacity;
+ 
+     qreal m_busyAnimationRotation = 0.0;
++    QPersistentModelIndex m_emptyingTrashIndex;
+ 
+     bool m_showHoverIndication;
+     QPersistentModelIndex m_hoveredHeaderArea;
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_60191c04_CopyJob-Inhibit-suspend-during-copy-operation.patch kf6-kio-6.13.0/debian/patches/upstream_60191c04_CopyJob-Inhibit-suspend-during-copy-operation.patch
--- kf6-kio-6.13.0/debian/patches/upstream_60191c04_CopyJob-Inhibit-suspend-during-copy-operation.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_60191c04_CopyJob-Inhibit-suspend-during-copy-operation.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,75 @@
+From 60191c0473a96c41279519845030efa12fdd0bb4 Mon Sep 17 00:00:00 2001
+From: Kai Uwe Broulik <kde@privat.broulik.de>
+Date: Sun, 20 Apr 2025 12:48:58 +0200
+Subject: [PATCH] CopyJob: Inhibit suspend during copy operation
+
+Avoids the system going to sleep during a lengthy copy operation.
+
+BUG: 362542
+---
+ src/core/copyjob.cpp | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+diff --git a/src/core/copyjob.cpp b/src/core/copyjob.cpp
+index d1e3eef61..21e1ff487 100644
+--- a/src/core/copyjob.cpp
++++ b/src/core/copyjob.cpp
+@@ -57,6 +57,7 @@
+ #include <KFileUtils>
+ #include <KIO/FileSystemFreeSpaceJob>
+ 
++#include <chrono>
+ #include <list>
+ #include <set>
+ 
+@@ -65,6 +66,7 @@ Q_DECLARE_LOGGING_CATEGORY(KIO_COPYJOB_DEBUG)
+ Q_LOGGING_CATEGORY(KIO_COPYJOB_DEBUG, "kf.kio.core.copyjob", QtWarningMsg)
+ 
+ using namespace KIO;
++using namespace std::literals::chrono_literals;
+ 
+ // this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
+ static constexpr int s_reportTimeout = 200;
+@@ -399,6 +401,8 @@ public:
+ 
+     void slotReport();
+ 
++    void doInhibitSuspend() override;
++
+     Q_DECLARE_PUBLIC(CopyJob)
+ 
+     static inline CopyJob *newJob(const QList<QUrl> &src, const QUrl &dest, CopyJob::CopyMode mode, bool asMethod, JobFlags flags)
+@@ -493,6 +497,11 @@ void CopyJobPrivate::slotStart()
+         }
+     }
+ 
++    // Avoid DBus traffic for short-lived jobs.
++    QTimer::singleShot(10s, q, [this] {
++        doInhibitSuspend();
++    });
++
+     /**
+        We call the functions directly instead of using signals.
+        Calling a function via a signal takes approx. 65 times the time
+@@ -736,6 +745,18 @@ bool CopyJob::doResume()
+     return Job::doResume();
+ }
+ 
++void CopyJobPrivate::doInhibitSuspend()
++{
++    QString reason;
++    if (m_mode == CopyJob::Move) {
++        reason = i18nc("Reason why standby is blocked", "Files are being moved");
++    } else {
++        reason = i18nc("Reason why standby is blocked", "Files are being copied");
++    }
++
++    inhibitSuspend(reason);
++}
++
+ void CopyJobPrivate::slotReport()
+ {
+     Q_Q(CopyJob);
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_719e0b00_KFileWidget-Fix-key-navigation-escaping-in-save-dialogs.patch kf6-kio-6.13.0/debian/patches/upstream_719e0b00_KFileWidget-Fix-key-navigation-escaping-in-save-dialogs.patch
--- kf6-kio-6.13.0/debian/patches/upstream_719e0b00_KFileWidget-Fix-key-navigation-escaping-in-save-dialogs.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_719e0b00_KFileWidget-Fix-key-navigation-escaping-in-save-dialogs.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,143 @@
+From 719e0b0031155ef83a2cf71d6d24114dea181353 Mon Sep 17 00:00:00 2001
+From: Akseli Lahtinen <akselmo@akselmo.dev>
+Date: Wed, 7 May 2025 16:35:19 +0300
+Subject: [PATCH] KFileWidget: Fix key navigation escaping in save dialogs
+
+In save dialogs the keyboard navigation would escape during file
+highlighting, since the fileHighlight sets the focus for fileName bar
+for mouse operations.
+
+This makes sure the user has to press Tab to explicitly escape the
+keyboard navigation mode. For clicking the items, it should not affect
+at all.
+
+CCBUG: 466206
+FIXED-IN: 6.14
+(cherry picked from commit 8e4e84f045b7459c0b02b1b1b51a9df73cea068a)
+---
+ src/filewidgets/kdiroperator.cpp | 21 +++++++++++++++++++++
+ src/filewidgets/kdiroperator.h   |  7 +++++++
+ src/filewidgets/kfilewidget.cpp  |  8 ++++----
+ 3 files changed, 32 insertions(+), 4 deletions(-)
+
+diff --git a/src/filewidgets/kdiroperator.cpp b/src/filewidgets/kdiroperator.cpp
+index e1d3afeea..4a2e400c7 100644
+--- a/src/filewidgets/kdiroperator.cpp
++++ b/src/filewidgets/kdiroperator.cpp
+@@ -221,6 +221,7 @@ public:
+     bool m_showOpenWithActions = false;
+     bool m_isTouchEvent = false;
+     bool m_isTouchDrag = false;
++    bool m_keyNavigation = false;
+ 
+     QList<QUrl> m_itemsToBeSetAsCurrent;
+     QStringList m_supportedSchemes;
+@@ -1244,6 +1245,11 @@ void KDirOperator::showOpenWithActions(bool enable)
+     d->m_showOpenWithActions = enable;
+ }
+ 
++bool KDirOperator::usingKeyNavigation()
++{
++    return d->m_keyNavigation;
++}
++
+ void KDirOperator::changeEvent(QEvent *event)
+ {
+     QWidget::changeEvent(event);
+@@ -1429,6 +1435,19 @@ bool KDirOperator::eventFilter(QObject *watched, QEvent *event)
+                 return true;
+             }
+         }
++        // Only use tab key to escape the view navigation
++        if (evt->key() == Qt::Key_Tab) {
++            d->m_keyNavigation = false;
++            d->slotSelectionChanged();
++            // When saving we need to return here,
++            // otherwise we skip over the next item with our tab press
++            // since we focus on that item in slotSelectionChanged
++            if (d->m_isSaving) {
++                return true;
++            }
++        } else {
++            d->m_keyNavigation = true;
++        }
+         break;
+     }
+     case QEvent::Resize: {
+@@ -1833,6 +1852,7 @@ void KDirOperator::selectFile(const KFileItem &item)
+     QApplication::restoreOverrideCursor();
+ 
+     Q_EMIT fileSelected(item);
++    d->m_keyNavigation = false;
+ }
+ 
+ void KDirOperator::highlightFile(const KFileItem &item)
+@@ -1842,6 +1862,7 @@ void KDirOperator::highlightFile(const KFileItem &item)
+     }
+ 
+     Q_EMIT fileHighlighted(item);
++    d->m_keyNavigation = false;
+ }
+ 
+ void KDirOperator::setCurrentItem(const QUrl &url)
+diff --git a/src/filewidgets/kdiroperator.h b/src/filewidgets/kdiroperator.h
+index c34880564..71a20ed2e 100644
+--- a/src/filewidgets/kdiroperator.h
++++ b/src/filewidgets/kdiroperator.h
+@@ -744,6 +744,13 @@ public:
+      */
+     void showOpenWithActions(bool enable);
+ 
++    /*!
++     * @returns true if the user was using keys to navigate.
++     *
++     * \since 6.14
++     */
++    bool usingKeyNavigation();
++
+ protected:
+     /**
+      * A view factory for creating predefined fileviews. Called internally by setView,
+diff --git a/src/filewidgets/kfilewidget.cpp b/src/filewidgets/kfilewidget.cpp
+index 5175a085a..017e65d1e 100644
+--- a/src/filewidgets/kfilewidget.cpp
++++ b/src/filewidgets/kfilewidget.cpp
+@@ -168,7 +168,7 @@ public:
+     void enterUrl(const QString &);
+     void locationAccepted(const QString &);
+     void slotFilterChanged();
+-    void fileHighlighted(const KFileItem &);
++    void fileHighlighted(const KFileItem &, bool);
+     void fileSelected(const KFileItem &);
+     void slotLoadingFinished();
+     void togglePlacesPanel(bool show, QObject *sender = nullptr);
+@@ -894,7 +894,7 @@ void KFileWidget::accept()
+     d->m_ops->close();
+ }
+ 
+-void KFileWidgetPrivate::fileHighlighted(const KFileItem &i)
++void KFileWidgetPrivate::fileHighlighted(const KFileItem &i, bool isKeyNavigation)
+ {
+     if ((m_locationEdit->hasFocus() && !m_locationEdit->currentText().isEmpty())) { // don't disturb
+         return;
+@@ -933,7 +933,7 @@ void KFileWidgetPrivate::fileHighlighted(const KFileItem &i)
+     // rename it if desired
+     // Note that double-clicking will override this and overwrite regardless of
+     // single/double click mouse setting (see slotViewDoubleClicked() )
+-    if (m_operationMode == KFileWidget::Saving) {
++    if (!isKeyNavigation && m_operationMode == KFileWidget::Saving) {
+         m_locationEdit->setFocus();
+     }
+ }
+@@ -1100,7 +1100,7 @@ void KFileWidgetPrivate::initDirOpWidgets()
+         urlEntered(url);
+     });
+     q->connect(m_ops, &KDirOperator::fileHighlighted, q, [this](const KFileItem &item) {
+-        fileHighlighted(item);
++        fileHighlighted(item, m_ops->usingKeyNavigation());
+     });
+     q->connect(m_ops, &KDirOperator::fileSelected, q, [this](const KFileItem &item) {
+         fileSelected(item);
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_81ca0e2c_Fix-incorrect-usage-percentage-in-Properties-dialog-for-virtual-filesystems.patch kf6-kio-6.13.0/debian/patches/upstream_81ca0e2c_Fix-incorrect-usage-percentage-in-Properties-dialog-for-virtual-filesystems.patch
--- kf6-kio-6.13.0/debian/patches/upstream_81ca0e2c_Fix-incorrect-usage-percentage-in-Properties-dialog-for-virtual-filesystems.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_81ca0e2c_Fix-incorrect-usage-percentage-in-Properties-dialog-for-virtual-filesystems.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,30 @@
+From 81ca0e2c50dc02873f04dfc6266ae4e4b88607a7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Efe=20=C3=87iftci?= <efeciftci@gmail.com>
+Date: Fri, 16 May 2025 20:50:21 +0300
+Subject: [PATCH] Fix incorrect usage percentage in Properties dialog for
+ virtual filesystems
+
+The Properties dialog for filesystems such as `/proc/` or `/sys/` shows an
+incorrect usage percentage (i.e., "-2,147,483,648% used"). This commit fixes
+that issue by avoiding a divbyzero: if the `size` variable is `0`, the
+`percentUsed` variable is set to `0`.
+---
+ src/widgets/kpropertiesdialogbuiltin_p.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/widgets/kpropertiesdialogbuiltin_p.cpp b/src/widgets/kpropertiesdialogbuiltin_p.cpp
+index 45091fe755..919c044c4c 100644
+--- a/src/widgets/kpropertiesdialogbuiltin_p.cpp
++++ b/src/widgets/kpropertiesdialogbuiltin_p.cpp
+@@ -664,7 +664,7 @@ void KFilePropsPlugin::slotFreeSpaceResult(KJob *_job)
+         const qint64 size = job->size();
+         const qint64 available = job->availableSize();
+         const quint64 used = size - available;
+-        const int percentUsed = qRound(100.0 * qreal(used) / qreal(size));
++        const int percentUsed = (size == 0) ? 0 : qRound(100.0 * qreal(used) / qreal(size));
+ 
+         d->m_ui->capacityBar->setText(i18nc("Available space out of total partition size (percent used)",
+                                             "%1 free of %2 (%3% used)",
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_9143fc2a_Fix-dialog-modality-settings.patch kf6-kio-6.13.0/debian/patches/upstream_9143fc2a_Fix-dialog-modality-settings.patch
--- kf6-kio-6.13.0/debian/patches/upstream_9143fc2a_Fix-dialog-modality-settings.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_9143fc2a_Fix-dialog-modality-settings.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,38 @@
+From 9143fc2a5962d190e36f6e99d9ca1c02506097a5 Mon Sep 17 00:00:00 2001
+From: Akseli Lahtinen <akselmo@akselmo.dev>
+Date: Thu, 22 May 2025 17:35:24 +0300
+Subject: [PATCH] Fix dialog modality settings
+
+By default all dialogs are ApplicationModal, thus they block clicking
+on any other app while the dialog is open.
+
+Set them to WindowModal so they only block the parent window.
+
+BUG: 504608
+---
+ src/widgets/kpropertiesdialogbuiltin_p.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/widgets/kpropertiesdialogbuiltin_p.cpp b/src/widgets/kpropertiesdialogbuiltin_p.cpp
+index 919c044c4c..a21330d0b2 100644
+--- a/src/widgets/kpropertiesdialogbuiltin_p.cpp
++++ b/src/widgets/kpropertiesdialogbuiltin_p.cpp
+@@ -1354,6 +1354,7 @@ void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions()
+ {
+     bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
+     QDialog dlg(properties);
++    dlg.setWindowModality(Qt::WindowModal);
+     dlg.setModal(true);
+     dlg.setWindowTitle(i18n("Advanced Permissions"));
+ 
+@@ -2880,6 +2881,7 @@ void KDesktopPropsPlugin::slotAdvanced()
+ {
+     auto *dlg = new QDialog(d->m_frame);
+     dlg->setObjectName(QStringLiteral("KPropertiesDesktopAdv"));
++    dlg->setWindowModality(Qt::WindowModal);
+     dlg->setModal(true);
+     dlg->setAttribute(Qt::WA_DeleteOnClose);
+     dlg->setWindowTitle(i18n("Advanced Options for %1", properties->url().fileName()));
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_c747fa0f_Avoid-unnecessary-sequencing-jobs-in-PreviewGenerator.patch kf6-kio-6.13.0/debian/patches/upstream_c747fa0f_Avoid-unnecessary-sequencing-jobs-in-PreviewGenerator.patch
--- kf6-kio-6.13.0/debian/patches/upstream_c747fa0f_Avoid-unnecessary-sequencing-jobs-in-PreviewGenerator.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_c747fa0f_Avoid-unnecessary-sequencing-jobs-in-PreviewGenerator.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,138 @@
+From c747fa0f4143ee730cefd6cd26d14c09e89cf3ef Mon Sep 17 00:00:00 2001
+From: Akseli Lahtinen <akselmo@akselmo.dev>
+Date: Tue, 13 May 2025 13:01:07 +0300
+Subject: [PATCH] Avoid unnecessary sequencing jobs in PreviewGenerator
+
+Currently we keep constantly asking if the current item has sequences
+support or not.
+
+By sequences we mean things like hovering mouse over a folder thumbnail
+and it goes through the files in it.
+
+This MR will always run for the first sequence (the initial thumbnail)
+but for the rest, then toggles a flag for the node with
+Qt::DecorationPropertyRole. If the propertyrole is false, it skips any
+further updates to avoid any unnecessary previewjob runs.
+---
+ src/filewidgets/kfilepreviewgenerator.cpp |  5 +++++
+ src/gui/previewjob.cpp                    |  2 ++
+ src/widgets/delegateanimationhandler.cpp  |  7 +++++--
+ src/widgets/kdirmodel.cpp                 | 24 +++++++++++++++++++++++
+ src/widgets/kdirmodel.h                   |  1 +
+ 5 files changed, 37 insertions(+), 2 deletions(-)
+
+diff --git a/src/filewidgets/kfilepreviewgenerator.cpp b/src/filewidgets/kfilepreviewgenerator.cpp
+index 019124c618..639221fbe5 100644
+--- a/src/filewidgets/kfilepreviewgenerator.cpp
++++ b/src/filewidgets/kfilepreviewgenerator.cpp
+@@ -1015,6 +1015,11 @@ void KFilePreviewGeneratorPrivate::startPreviewJob(const KFileItemList &items, i
+ 
+     q->connect(job, &KIO::PreviewJob::gotPreview, q, [this, job](const KFileItem &item, const QPixmap &pixmap) {
+         addToPreviewQueue(item, pixmap, job);
++        m_dirModel->setData(m_dirModel->indexForItem(item), job->handlesSequences(), KDirModel::HandleSequencesRole);
++    });
++
++    q->connect(job, &KIO::PreviewJob::failed, q, [this, job](const KFileItem &item) {
++        m_dirModel->setData(m_dirModel->indexForItem(item), job->handlesSequences(), KDirModel::HandleSequencesRole);
+     });
+ 
+     q->connect(job, &KIO::PreviewJob::finished, q, [this, job]() {
+diff --git a/src/gui/previewjob.cpp b/src/gui/previewjob.cpp
+index bca18c9907..4d0ed030df 100644
+--- a/src/gui/previewjob.cpp
++++ b/src/gui/previewjob.cpp
+@@ -410,6 +410,8 @@ void PreviewJobPrivate::startPreview()
+             item.standardThumbnailer = plugin.description() == QStringLiteral("standardthumbnailer");
+             item.plugin = plugin;
+             items.push_back(item);
++            bool handlesSequencesValue = item.plugin.value(QStringLiteral("HandleSequences"), false);
++            thumbnailWorkerMetaData.insert(QStringLiteral("handlesSequences"), QString::number(handlesSequencesValue));
+ 
+             if (!bNeedCache && bSave && plugin.value(QStringLiteral("CacheThumbnail"), true)) {
+                 const QUrl url = fileItem.targetUrl();
+diff --git a/src/widgets/delegateanimationhandler.cpp b/src/widgets/delegateanimationhandler.cpp
+index 9d8f81beb8..741416a944 100644
+--- a/src/widgets/delegateanimationhandler.cpp
++++ b/src/widgets/delegateanimationhandler.cpp
+@@ -200,8 +200,11 @@ void DelegateAnimationHandler::sequenceTimerTimeout()
+     KDirModel *dirModel = dynamic_cast<KDirModel *>(model);
+     if (dirModel) {
+         // qDebug() << "requesting" << currentSequenceIndex;
+-        dirModel->requestSequenceIcon(index, currentSequenceIndex);
+-        iconSequenceTimer.start(); // Some upper-bound interval is needed, in case items are not generated
++        // Only request sequence icons for items that have them
++        if (dirModel->data(index, KDirModel::HandleSequencesRole).toBool()) {
++            dirModel->requestSequenceIcon(index, currentSequenceIndex);
++            iconSequenceTimer.start(); // Some upper-bound interval is needed, in case items are not generated
++        }
+     }
+ }
+ 
+diff --git a/src/widgets/kdirmodel.cpp b/src/widgets/kdirmodel.cpp
+index 281a8a69e4..aa73a7f776 100644
+--- a/src/widgets/kdirmodel.cpp
++++ b/src/widgets/kdirmodel.cpp
+@@ -101,10 +101,21 @@ public:
+         m_preview = icn;
+     }
+ 
++    bool previewHandlesSequences()
++    {
++        return m_previewHandlesSequences;
++    }
++
++    void setPreviewHandlesSequences(bool handlesSequences)
++    {
++        m_previewHandlesSequences = handlesSequences;
++    }
++
+ private:
+     KFileItem m_item;
+     KDirModelDirNode *const m_parent;
+     QIcon m_preview;
++    bool m_previewHandlesSequences = true; // First sequence is always allowed
+ };
+ 
+ // Specialization for directory nodes
+@@ -915,6 +926,11 @@ QVariant KDirModel::data(const QModelIndex &index, int role) const
+                 }
+             }
+             break;
++        case HandleSequencesRole:
++            if (index.column() == Name) {
++                return node->previewHandlesSequences();
++            }
++            break;
+         case Qt::TextAlignmentRole:
+             if (index.column() == Size) {
+                 // use a right alignment for L2R and R2L languages
+@@ -1034,6 +1050,14 @@ bool KDirModel::setData(const QModelIndex &index, const QVariant &value, int rol
+             return true;
+         }
+         break;
++    case HandleSequencesRole:
++        if (index.column() == Name) {
++            KDirModelNode *node = static_cast<KDirModelNode *>(index.internalPointer());
++            Q_ASSERT(node);
++            node->setPreviewHandlesSequences(value.toBool());
++            return true;
++        }
++        break;
+     default:
+         break;
+     }
+diff --git a/src/widgets/kdirmodel.h b/src/widgets/kdirmodel.h
+index 64d315c43b..bf5a72c28c 100644
+--- a/src/widgets/kdirmodel.h
++++ b/src/widgets/kdirmodel.h
+@@ -180,6 +180,7 @@ public:
+         FileItemRole = 0x07A263FF, ///< returns the KFileItem for a given index. roleName is "fileItem".
+         ChildCountRole = 0x2C4D0A40, ///< returns the number of items in a directory, or ChildCountUnknown. roleName is "childCount".
+         HasJobRole = 0x01E555A5, ///< returns whether or not there is a job on an item (file/directory). roleName is "hasJob".
++        HandleSequencesRole = 0x1E642272,
+     };
+ 
+     /**
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_cd0810f8_KFileWidget-Do-not-override-filename-with-folder-name-if-edited.patch kf6-kio-6.13.0/debian/patches/upstream_cd0810f8_KFileWidget-Do-not-override-filename-with-folder-name-if-edited.patch
--- kf6-kio-6.13.0/debian/patches/upstream_cd0810f8_KFileWidget-Do-not-override-filename-with-folder-name-if-edited.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_cd0810f8_KFileWidget-Do-not-override-filename-with-folder-name-if-edited.patch	2025-05-20 08:47:36.000000000 +0200
@@ -0,0 +1,150 @@
+From cd0810f82d9fce629fa27e2a1d330fbcc8d2edf6 Mon Sep 17 00:00:00 2001
+From: Akseli Lahtinen <akselmo@akselmo.dev>
+Date: Thu, 17 Apr 2025 08:43:26 +0000
+Subject: [PATCH] KFileWidget: Do not override filename with folder name if
+ edited
+
+If the filename input has been modified, do not override it with
+the name of the folder when selecting a folder.
+
+BUG: 502794
+FIXED-IN: 6.14
+---
+ autotests/kfilewidgettest.cpp   | 85 +++++++++++++++++++++++++++++++++
+ src/filewidgets/kdiroperator.h  |  1 +
+ src/filewidgets/kfilewidget.cpp |  4 ++
+ 3 files changed, 90 insertions(+)
+
+diff --git a/autotests/kfilewidgettest.cpp b/autotests/kfilewidgettest.cpp
+index cd04b57c4..e0ff3945d 100644
+--- a/autotests/kfilewidgettest.cpp
++++ b/autotests/kfilewidgettest.cpp
+@@ -87,6 +87,8 @@ private Q_SLOTS:
+     void testTokenizeForSave_data();
+     void testTokenizeForSave();
+     void testThumbnailPreviewSetting();
++    void testReplaceLocationEditFilename_data();
++    void testReplaceLocationEditFilename();
+ };
+ 
+ void KFileWidgetTest::initTestCase()
+@@ -932,6 +934,89 @@ void KFileWidgetTest::testThumbnailPreviewSetting()
+     fwPreviewFalse.cancelButton()->click();
+ }
+ 
++struct LocationTestItem {
++    bool dir;
++    QString name;
++};
++
++void KFileWidgetTest::testReplaceLocationEditFilename_data()
++{
++    QTest::addColumn<LocationTestItem>("initialItem");
++    QTest::addColumn<LocationTestItem>("selectedItem");
++    QTest::addColumn<QString>("lineEditTextResult");
++    QTest::addColumn<bool>("overrideModifiedText");
++
++    QTest::newRow("replace-dir-with-dir") << LocationTestItem(true, "folder1") << LocationTestItem(true, "folder2") << "" << false;
++    QTest::newRow("replace-dir-with-file") << LocationTestItem(true, "folder1") << LocationTestItem(false, "file1") << "file1" << true;
++    QTest::newRow("replace-file-with-file") << LocationTestItem(false, "file1") << LocationTestItem(false, "file2") << "file2" << true;
++    QTest::newRow("replace-file-with-dir") << LocationTestItem(false, "file1") << LocationTestItem(true, "folder1") << "file1" << false;
++}
++
++// BUG: 502794
++// Test that we don't override file names with folder names
++void KFileWidgetTest::testReplaceLocationEditFilename()
++{
++    QFETCH(LocationTestItem, initialItem);
++    QFETCH(LocationTestItem, selectedItem);
++    QFETCH(QString, lineEditTextResult);
++    QFETCH(bool, overrideModifiedText);
++
++    // Setup - Create folders/files in temp dir
++    QTemporaryDir tempDir;
++    const QString tempDirPath = tempDir.path();
++    QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath);
++    QUrl replacedUrl = QUrl::fromLocalFile(tempDirPath + QLatin1Char('/') + initialItem.name);
++    QUrl selectedUrl = QUrl::fromLocalFile(tempDirPath + QLatin1Char('/') + selectedItem.name);
++
++    auto createTestItem = [tempDirUrl](LocationTestItem item, const QUrl &url) {
++        if (item.dir) {
++            QDir(tempDirUrl.toLocalFile()).mkdir(url.toLocalFile());
++            QVERIFY(QDir(url.toLocalFile()).exists());
++        } else {
++            QFile file(url.toLocalFile());
++            if (!file.open(QIODevice::WriteOnly)) {
++                qFatal("Couldn't create %s", qPrintable(url.toLocalFile()));
++            }
++            file.write(QByteArray("Test file"));
++            file.close();
++            QVERIFY(file.exists());
++        }
++    };
++
++    createTestItem(initialItem, replacedUrl);
++    createTestItem(selectedItem, selectedUrl);
++
++    // Open the filewidget in tempdir
++    KFileWidget fw(tempDirUrl);
++    fw.setOperationMode(KFileWidget::Saving);
++
++    // Highlight the item, then another
++    auto highlightItem = [&fw](QUrl url) {
++        KFileItem fileItem(url);
++        QSignalSpy fileHighlightedSpy(fw.dirOperator(), &KDirOperator::fileHighlighted);
++        fw.dirOperator()->highlightFile(fileItem);
++        fileHighlightedSpy.wait(500);
++        QVERIFY(fileHighlightedSpy.count());
++    };
++
++    highlightItem(replacedUrl);
++    highlightItem(selectedUrl);
++
++    // Compare that we have the wanted result when selecting items
++    QCOMPARE(fw.locationEdit()->lineEdit()->text(), lineEditTextResult);
++
++    // Make sure we don't overwrite any text user has modified in some cases
++    const QString modifiedText("New Filename.txt");
++    fw.locationEdit()->setEditText(modifiedText);
++    highlightItem(selectedUrl);
++
++    if (overrideModifiedText) {
++        QCOMPARE(fw.locationEdit()->lineEdit()->text(), lineEditTextResult);
++    } else {
++        QCOMPARE(fw.locationEdit()->lineEdit()->text(), modifiedText);
++    }
++}
++
+ QTEST_MAIN(KFileWidgetTest)
+ 
+ #include "kfilewidgettest.moc"
+diff --git a/src/filewidgets/kdiroperator.h b/src/filewidgets/kdiroperator.h
+index 6013a39cc..818432141 100644
+--- a/src/filewidgets/kdiroperator.h
++++ b/src/filewidgets/kdiroperator.h
+@@ -1042,6 +1042,7 @@ private:
+     KIOFILEWIDGETS_NO_EXPORT void setViewInternal(QAbstractItemView *view);
+ 
+     friend class KDirOperatorPrivate;
++    friend class KFileWidgetTest; // For testing
+     std::unique_ptr<KDirOperatorPrivate> d;
+ };
+ 
+diff --git a/src/filewidgets/kfilewidget.cpp b/src/filewidgets/kfilewidget.cpp
+index 064efc3b3..d5afb9de3 100644
+--- a/src/filewidgets/kfilewidget.cpp
++++ b/src/filewidgets/kfilewidget.cpp
+@@ -900,6 +900,10 @@ void KFileWidgetPrivate::fileHighlighted(const KFileItem &i, bool isKeyNavigatio
+         return;
+     }
+ 
++    if (!i.isNull() && i.isDir() && !(m_ops->mode() & KFile::Directory)) {
++        return;
++    }
++
+     const bool modified = m_locationEdit->lineEdit()->isModified();
+ 
+     if (!(m_ops->mode() & KFile::Files)) {
+-- 
+GitLab
+
diff -Nru kf6-kio-6.13.0/debian/patches/upstream_d8441b7b_Show-single-click-selection-emblem-when-using-single-click-mouse-mode.patch kf6-kio-6.13.0/debian/patches/upstream_d8441b7b_Show-single-click-selection-emblem-when-using-single-click-mouse-mode.patch
--- kf6-kio-6.13.0/debian/patches/upstream_d8441b7b_Show-single-click-selection-emblem-when-using-single-click-mouse-mode.patch	1970-01-01 01:00:00.000000000 +0100
+++ kf6-kio-6.13.0/debian/patches/upstream_d8441b7b_Show-single-click-selection-emblem-when-using-single-click-mouse-mode.patch	2025-05-16 22:07:04.000000000 +0200
@@ -0,0 +1,587 @@
+From d8441b7b5b2c8ebda18f5bde01f77ae802f4f48e Mon Sep 17 00:00:00 2001
+From: Akseli Lahtinen <akselmo@akselmo.dev>
+Date: Wed, 9 Apr 2025 07:57:20 +0000
+Subject: [PATCH] Show single-click selection emblem when using single-click
+ mouse mode
+
+In open/save dialog, we had no way to select items when using single-click mouse mode. (Except holding down control key).
+
+This adds the same emblem that Dolphin uses for its selections.
+
+It is shown in both treeviews and listviews, but only when single-click mode is active and the filewidget allows selecting multiple items.
+
+BUG: 185793
+FIXED-IN: 6.14
+---
+ src/filewidgets/CMakeLists.txt               |  1 +
+ src/filewidgets/kdiroperator.cpp             | 13 ++--
+ src/filewidgets/kdiroperatordetailview.cpp   | 31 +++++++++-
+ src/filewidgets/kdiroperatordetailview_p.h   |  7 ++-
+ src/filewidgets/kdiroperatoriconview.cpp     | 33 +++++++++-
+ src/filewidgets/kdiroperatoriconview_p.h     |  9 ++-
+ src/filewidgets/kfileitemselectionemblem.cpp | 63 ++++++++++++++++++++
+ src/filewidgets/kfileitemselectionemblem.h   | 39 ++++++++++++
+ src/widgets/kfileitemdelegate.cpp            | 58 ++++++++++++++++++
+ src/widgets/kfileitemdelegate.h              | 15 +++++
+ 10 files changed, 259 insertions(+), 10 deletions(-)
+ create mode 100644 src/filewidgets/kfileitemselectionemblem.cpp
+ create mode 100644 src/filewidgets/kfileitemselectionemblem.h
+
+diff --git a/src/filewidgets/CMakeLists.txt b/src/filewidgets/CMakeLists.txt
+index e5fc87f75..cf4520191 100644
+--- a/src/filewidgets/CMakeLists.txt
++++ b/src/filewidgets/CMakeLists.txt
+@@ -22,6 +22,7 @@ target_sources(KF6KIOFileWidgets PRIVATE
+   kdiroperatoriconview.cpp
+   kdirsortfilterproxymodel.cpp #used in combination with kdirmodel.cpp
+   kencodingfiledialog.cpp
++  kfileitemselectionemblem.cpp
+   kfilebookmarkhandler.cpp
+   kfilecopytomenu.cpp
+   kfilecustomdialog.cpp
+diff --git a/src/filewidgets/kdiroperator.cpp b/src/filewidgets/kdiroperator.cpp
+index d2d8a5513..e1d3afeea 100644
+--- a/src/filewidgets/kdiroperator.cpp
++++ b/src/filewidgets/kdiroperator.cpp
+@@ -17,6 +17,7 @@
+ #include "kdiroperatoriconview_p.h"
+ #include "kdirsortfilterproxymodel.h"
+ #include "kfileitem.h"
++#include "kfileitemselectionemblem.h"
+ #include "kfilemetapreview_p.h"
+ #include "knewfilemenu.h"
+ #include "kpreviewwidgetbase.h"
+@@ -1260,9 +1261,13 @@ bool KDirOperator::eventFilter(QObject *watched, QEvent *event)
+         if (d->m_isTouchEvent) {
+             return true;
+         }
+-        if (d->m_preview && !d->m_preview->isHidden()) {
+-            const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
+ 
++        const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
++        if (hoveredIndex.isValid()) {
++            KFileItemSelectionEmblem(d->m_itemView, hoveredIndex, this).updateSelectionEmblemRectForIndex(iconSize());
++        }
++
++        if (d->m_preview && !d->m_preview->isHidden()) {
+             if (d->m_lastHoveredIndex == hoveredIndex) {
+                 return QWidget::eventFilter(watched, event);
+             }
+@@ -1510,11 +1515,11 @@ QAbstractItemView *KDirOperator::createView(QWidget *parent, KFile::FileView vie
+ {
+     QAbstractItemView *itemView = nullptr;
+     if (KFile::isDetailView(viewKind) || KFile::isTreeView(viewKind) || KFile::isDetailTreeView(viewKind)) {
+-        KDirOperatorDetailView *detailView = new KDirOperatorDetailView(parent);
++        KDirOperatorDetailView *detailView = new KDirOperatorDetailView(this, parent);
+         detailView->setViewMode(viewKind);
+         itemView = detailView;
+     } else {
+-        itemView = new KDirOperatorIconView(parent, decorationPosition());
++        itemView = new KDirOperatorIconView(this, parent, decorationPosition());
+     }
+ 
+     return itemView;
+diff --git a/src/filewidgets/kdiroperatordetailview.cpp b/src/filewidgets/kdiroperatordetailview.cpp
+index 8bbd3e42c..b693e6d9d 100644
+--- a/src/filewidgets/kdiroperatordetailview.cpp
++++ b/src/filewidgets/kdiroperatordetailview.cpp
+@@ -5,6 +5,7 @@
+ */
+ 
+ #include "kdiroperatordetailview_p.h"
++#include "kfileitemselectionemblem.h"
+ 
+ #include <kdirlister.h>
+ #include <kdirmodel.h>
+@@ -17,9 +18,11 @@
+ #include <QMimeData>
+ #include <QScrollBar>
+ 
+-KDirOperatorDetailView::KDirOperatorDetailView(QWidget *parent)
++KDirOperatorDetailView::KDirOperatorDetailView(KDirOperator *dirOperator, QWidget *parent)
+     : QTreeView(parent)
+     , m_hideDetailColumns(false)
++    , m_isEmblemClicked(false)
++    , m_dirOperator(dirOperator)
+ {
+     setRootIsDecorated(false);
+     setSortingEnabled(true);
+@@ -115,9 +118,15 @@ void KDirOperatorDetailView::dragEnterEvent(QDragEnterEvent *event)
+ 
+ void KDirOperatorDetailView::mousePressEvent(QMouseEvent *event)
+ {
++    const QModelIndex index = indexAt(event->pos());
++    // When selection emblem is clicked, select it and don't do anything else
++    m_isEmblemClicked = KFileItemSelectionEmblem(this, index, m_dirOperator).handleMousePressEvent(event->pos());
++    if (m_isEmblemClicked) {
++        return;
++    }
++
+     QTreeView::mousePressEvent(event);
+ 
+-    const QModelIndex index = indexAt(event->pos());
+     if (!index.isValid() || (index.column() != KDirModel::Name)) {
+         const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
+         if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) {
+@@ -126,6 +135,24 @@ void KDirOperatorDetailView::mousePressEvent(QMouseEvent *event)
+     }
+ }
+ 
++void KDirOperatorDetailView::mouseMoveEvent(QMouseEvent *event)
++{
++    // Disallow selection dragging when emblem is clicked
++    if (m_isEmblemClicked) {
++        return;
++    }
++    QTreeView::mouseMoveEvent(event);
++}
++
++void KDirOperatorDetailView::mouseReleaseEvent(QMouseEvent *event)
++{
++    // Reset the emblem selection
++    if (m_isEmblemClicked) {
++        m_isEmblemClicked = false;
++    }
++    QTreeView::mouseReleaseEvent(event);
++}
++
+ void KDirOperatorDetailView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+ {
+     QTreeView::currentChanged(current, previous);
+diff --git a/src/filewidgets/kdiroperatordetailview_p.h b/src/filewidgets/kdiroperatordetailview_p.h
+index f30ab9482..df45142d4 100644
+--- a/src/filewidgets/kdiroperatordetailview_p.h
++++ b/src/filewidgets/kdiroperatordetailview_p.h
+@@ -7,6 +7,7 @@
+ #ifndef KDIROPERATORDETAILVIEW_P_H
+ #define KDIROPERATORDETAILVIEW_P_H
+ 
++#include <KDirOperator>
+ #include <QTreeView>
+ 
+ #include <kfile.h>
+@@ -22,7 +23,7 @@ class KDirOperatorDetailView : public QTreeView
+     Q_OBJECT
+ 
+ public:
+-    explicit KDirOperatorDetailView(QWidget *parent = nullptr);
++    explicit KDirOperatorDetailView(KDirOperator *dirOperator, QWidget *parent = nullptr);
+     ~KDirOperatorDetailView() override;
+ 
+     /**
+@@ -36,10 +37,14 @@ protected:
+     bool event(QEvent *event) override;
+     void dragEnterEvent(QDragEnterEvent *event) override;
+     void mousePressEvent(QMouseEvent *event) override;
++    void mouseMoveEvent(QMouseEvent *event) override;
++    void mouseReleaseEvent(QMouseEvent *event) override;
+     void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
+ 
+ private:
+     bool m_hideDetailColumns;
++    bool m_isEmblemClicked;
++    KDirOperator *m_dirOperator;
+ };
+ 
+ #endif
+diff --git a/src/filewidgets/kdiroperatoriconview.cpp b/src/filewidgets/kdiroperatoriconview.cpp
+index ace0c0098..9180ea1ed 100644
+--- a/src/filewidgets/kdiroperatoriconview.cpp
++++ b/src/filewidgets/kdiroperatoriconview.cpp
+@@ -6,6 +6,7 @@
+ */
+ 
+ #include "kdiroperatoriconview_p.h"
++#include "kfileitemselectionemblem.h"
+ 
+ #include <QApplication>
+ #include <QDragEnterEvent>
+@@ -15,8 +16,10 @@
+ #include <KFileItemDelegate>
+ #include <KIconLoader>
+ 
+-KDirOperatorIconView::KDirOperatorIconView(QWidget *parent, QStyleOptionViewItem::Position aDecorationPosition)
++KDirOperatorIconView::KDirOperatorIconView(KDirOperator *dirOperator, QWidget *parent, QStyleOptionViewItem::Position aDecorationPosition)
+     : QListView(parent)
++    , m_isEmblemClicked(false)
++    , m_dirOperator(dirOperator)
+ {
+     setViewMode(QListView::IconMode);
+     setResizeMode(QListView::Adjust);
+@@ -74,7 +77,15 @@ void KDirOperatorIconView::dragEnterEvent(QDragEnterEvent *event)
+ 
+ void KDirOperatorIconView::mousePressEvent(QMouseEvent *event)
+ {
+-    if (!indexAt(event->pos()).isValid()) {
++    const QModelIndex index = indexAt(event->pos());
++
++    // When selection emblem is clicked, select it and don't do anything else
++    m_isEmblemClicked = KFileItemSelectionEmblem(this, index, m_dirOperator).handleMousePressEvent(event->pos());
++    if (m_isEmblemClicked) {
++        return;
++    }
++
++    if (!index.isValid()) {
+         const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
+         if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) {
+             clearSelection();
+@@ -84,6 +95,24 @@ void KDirOperatorIconView::mousePressEvent(QMouseEvent *event)
+     QListView::mousePressEvent(event);
+ }
+ 
++void KDirOperatorIconView::mouseMoveEvent(QMouseEvent *event)
++{
++    // Disallow selection dragging when emblem is clicked
++    if (m_isEmblemClicked) {
++        return;
++    }
++    QListView::mouseMoveEvent(event);
++}
++
++void KDirOperatorIconView::mouseReleaseEvent(QMouseEvent *event)
++{
++    // Reset the emblem selection
++    if (m_isEmblemClicked) {
++        m_isEmblemClicked = false;
++    }
++    QListView::mouseReleaseEvent(event);
++}
++
+ void KDirOperatorIconView::wheelEvent(QWheelEvent *event)
+ {
+     QListView::wheelEvent(event);
+diff --git a/src/filewidgets/kdiroperatoriconview_p.h b/src/filewidgets/kdiroperatoriconview_p.h
+index c4ab96738..f1caba9e0 100644
+--- a/src/filewidgets/kdiroperatoriconview_p.h
++++ b/src/filewidgets/kdiroperatoriconview_p.h
+@@ -8,6 +8,7 @@
+ #ifndef KDIROPERATORICONVIEW_P_H
+ #define KDIROPERATORICONVIEW_P_H
+ 
++#include <KDirOperator>
+ #include <QListView>
+ 
+ /**
+@@ -18,7 +19,9 @@ class KDirOperatorIconView : public QListView
+ {
+     Q_OBJECT
+ public:
+-    KDirOperatorIconView(QWidget *parent = nullptr, QStyleOptionViewItem::Position decorationPosition = QStyleOptionViewItem::Position::Top);
++    KDirOperatorIconView(KDirOperator *dirOperator,
++                         QWidget *parent = nullptr,
++                         QStyleOptionViewItem::Position decorationPosition = QStyleOptionViewItem::Position::Top);
+     ~KDirOperatorIconView() override;
+     void setDecorationPosition(QStyleOptionViewItem::Position decorationPosition);
+ 
+@@ -26,6 +29,8 @@ protected:
+     void initViewItemOption(QStyleOptionViewItem *option) const override;
+     void dragEnterEvent(QDragEnterEvent *event) override;
+     void mousePressEvent(QMouseEvent *event) override;
++    void mouseMoveEvent(QMouseEvent *event) override;
++    void mouseReleaseEvent(QMouseEvent *event) override;
+     void wheelEvent(QWheelEvent *event) override;
+     void resizeEvent(QResizeEvent *event) override;
+ 
+@@ -34,6 +39,8 @@ protected
+ 
+ private:
+     QStyleOptionViewItem::Position decorationPosition;
++    bool m_isEmblemClicked;
++    KDirOperator *m_dirOperator;
+ };
+ 
+ #endif // KDIROPERATORICONVIEW_P_H
+diff --git a/src/filewidgets/kfileitemselectionemblem.cpp b/src/filewidgets/kfileitemselectionemblem.cpp
+new file mode 100644
+index 000000000..566dee306
+--- /dev/null
++++ b/src/filewidgets/kfileitemselectionemblem.cpp
+@@ -0,0 +1,63 @@
++/*
++    SPDX-FileCopyrightText: 2025 Akseli Lahtinen <akselmo@akselmo.dev>
++
++    SPDX-License-Identifier: LGPL-2.0-or-later
++*/
++
++#include "kfileitemselectionemblem.h"
++#include "kfileitemdelegate.h"
++
++#include <QAbstractItemDelegate>
++#include <QAbstractItemView>
++#include <QApplication>
++#include <QModelIndex>
++#include <QPoint>
++
++KFileItemSelectionEmblem::KFileItemSelectionEmblem(QAbstractItemView *itemView, QModelIndex index, KDirOperator *dirOperator)
++{
++    m_itemView = itemView;
++    m_index = index;
++    m_fileItemDelegate = fileItemDelegate();
++    m_dirOperator = dirOperator;
++    m_fileItem = m_fileItemDelegate->fileItem(m_index);
++}
++
++KFileItemSelectionEmblem::~KFileItemSelectionEmblem()
++{
++}
++
++bool KFileItemSelectionEmblem::isEmblemEnabled()
++{
++    if (m_itemView->selectionMode() == QAbstractItemView::ExtendedSelection && qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
++        if (m_fileItem.isDir()) {
++            return m_dirOperator->isSelected(m_fileItem);
++        }
++        return true;
++    }
++    return false;
++}
++
++KFileItemDelegate *KFileItemSelectionEmblem::fileItemDelegate()
++{
++    auto itemDelegate = m_itemView->itemDelegateForIndex(m_index);
++    if (itemDelegate) {
++        return qobject_cast<KFileItemDelegate *>(itemDelegate);
++    }
++    return nullptr;
++}
++
++void KFileItemSelectionEmblem::updateSelectionEmblemRectForIndex(const int iconSize)
++{
++    if (isEmblemEnabled() && m_fileItemDelegate) {
++        m_fileItemDelegate->setSelectionEmblemRect(m_itemView->visualRect(m_index), iconSize);
++    }
++}
++
++bool KFileItemSelectionEmblem::handleMousePressEvent(const QPoint mousePos)
++{
++    if (isEmblemEnabled() && m_fileItemDelegate && m_fileItemDelegate->selectionEmblemRect().contains(mousePos)) {
++        m_itemView->selectionModel()->select(m_index, QItemSelectionModel::Toggle);
++        return true;
++    }
++    return false;
++}
+diff --git a/src/filewidgets/kfileitemselectionemblem.h b/src/filewidgets/kfileitemselectionemblem.h
+new file mode 100644
+index 000000000..4bfd78a8b
+--- /dev/null
++++ b/src/filewidgets/kfileitemselectionemblem.h
+@@ -0,0 +1,39 @@
++/*
++    SPDX-FileCopyrightText: 2025 Akseli Lahtinen <akselmo@akselmo.dev>
++
++    SPDX-License-Identifier: LGPL-2.0-or-later
++*/
++
++#ifndef KFILEITEMSELECTIONEMBLEM_H
++#define KFILEITEMSELECTIONEMBLEM_H
++
++#include <KDirOperator>
++#include <QAbstractItemView>
++#include <QModelIndex>
++
++class KFileItem;
++class KFileItemDelegate;
++class QPoint;
++
++class KFileItemSelectionEmblem
++{
++public:
++    KFileItemSelectionEmblem(QAbstractItemView *itemView, QModelIndex index, KDirOperator *dirOperator);
++    ~KFileItemSelectionEmblem();
++
++    void updateSelectionEmblemRectForIndex(const int iconSize);
++    bool handleMousePressEvent(const QPoint mousePos);
++    bool isEmblemEnabled();
++
++private:
++    KFileItemDelegate *fileItemDelegate();
++
++    QAbstractItemView *m_itemView;
++    QModelIndex m_index;
++    KDirOperator *m_dirOperator;
++    KFileItemDelegate *m_fileItemDelegate;
++    KFileItem m_fileItem;
++    bool m_isDir;
++};
++
++#endif
+diff --git a/src/widgets/kfileitemdelegate.cpp b/src/widgets/kfileitemdelegate.cpp
+index c00c69523..83acd9910 100644
+--- a/src/widgets/kfileitemdelegate.cpp
++++ b/src/widgets/kfileitemdelegate.cpp
+@@ -101,6 +101,7 @@ public:
+     void gotNewIcon(const QModelIndex &index);
+ 
+     void paintJobTransfers(QPainter *painter, const qreal &jobAnimationAngle, const QPoint &iconPos, const QStyleOptionViewItem &opt);
++    int scaledEmblemSize(int iconSize) const;
+ 
+ public:
+     KFileItemDelegate::InformationList informationList;
+@@ -112,6 +113,7 @@ public:
+     QTextOption::WrapMode wrapMode;
+     bool jobTransfersVisible;
+     QIcon downArrowIcon;
++    QRect emblemRect;
+ 
+ private:
+     KIO::DelegateAnimationHandler *animationHandler;
+@@ -128,6 +130,7 @@ KFileItemDelegate::Private::Private(KFileItemDelegate *parent)
+     , showToolTipWhenElided(true)
+     , wrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere)
+     , jobTransfersVisible(false)
++    , emblemRect(QRect())
+     , animationHandler(new KIO::DelegateAnimationHandler(parent))
+     , activeMargins(nullptr)
+ {
+@@ -1153,6 +1156,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+     QPixmap icon = opt.icon.pixmap(opt.decorationSize, iconMode, iconState);
+ 
+     const KFileItem fileItem = d->fileItem(index);
++    const bool isDir = fileItem.isDir();
+     if (fileItem.isHidden()) {
+         KIconEffect::semiTransparent(icon);
+     }
+@@ -1176,6 +1180,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+                     pixmap = d->transition(fadeFromPixmap, pixmap, state->fadeProgress());
+                 }
+                 painter->drawPixmap(option.rect.topLeft(), pixmap);
++                drawSelectionEmblem(option, painter, index);
+                 if (d->jobTransfersVisible && index.column() == 0) {
+                     if (index.data(KDirModel::HasJobRole).toBool()) {
+                         d->paintJobTransfers(painter, state->jobAnimationAngle(), iconPos, opt);
+@@ -1251,6 +1256,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+         p.setRenderHint(QPainter::Antialiasing);
+         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
+         p.drawPixmap(iconPos, icon);
++        drawSelectionEmblem(option, painter, index);
+         d->drawTextItems(&p, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
+         d->drawFocusRect(&p, opt, focusRect);
+         p.end();
+@@ -1263,6 +1269,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+         p.setRenderHint(QPainter::Antialiasing);
+         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
+         p.drawPixmap(iconPos, icon);
++        drawSelectionEmblem(option, painter, index);
+         d->drawTextItems(&p, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
+         d->drawFocusRect(&p, opt, focusRect);
+         p.end();
+@@ -1282,6 +1289,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+         }
+ 
+         painter->drawPixmap(option.rect.topLeft(), pixmap);
++        drawSelectionEmblem(option, painter, index);
+         painter->setRenderHint(QPainter::Antialiasing);
+         if (d->jobTransfersVisible && index.column() == 0) {
+             if (index.data(KDirModel::HasJobRole).toBool()) {
+@@ -1306,6 +1314,7 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+ 
+     d->drawTextItems(painter, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
+     d->drawFocusRect(painter, opt, focusRect);
++    drawSelectionEmblem(option, painter, index);
+ 
+     if (d->jobTransfersVisible && index.column() == 0 && state) {
+         if (index.data(KDirModel::HasJobRole).toBool()) {
+@@ -1315,6 +1324,33 @@ void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt
+     painter->restore();
+ }
+ 
++void KFileItemDelegate::drawSelectionEmblem(QStyleOptionViewItem option, QPainter *painter, const QModelIndex &index) const
++{
++    if (index.column() != 0 || !qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
++        return;
++    }
++    const auto state = option.state;
++    if ((state & QStyle::State_MouseOver && !fileItem(index).isDir()) || (state & QStyle::State_Selected)) {
++        const QString selectionEmblem = state & QStyle::State_Selected ? QStringLiteral("emblem-remove") : QStringLiteral("emblem-added");
++        const auto emblem = QIcon::fromTheme(selectionEmblem).pixmap(d->emblemRect.size(), state & QStyle::State_MouseOver ? QIcon::Active : QIcon::Disabled);
++
++        painter->drawPixmap(d->emblemRect.topLeft(), emblem);
++    }
++}
++
++int KFileItemDelegate::Private::scaledEmblemSize(int iconSize) const
++{
++    if (iconSize <= KIconLoader::SizeSmallMedium) {
++        return KIconLoader::SizeSmall;
++    } else if (iconSize <= KIconLoader::SizeHuge) {
++        return KIconLoader::SizeSmallMedium;
++    } else if (iconSize <= KIconLoader::SizeEnormous) {
++        return KIconLoader::SizeMedium;
++    }
++
++    return KIconLoader::SizeHuge;
++}
++
+ QWidget *KFileItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+ {
+     QStyleOptionViewItem opt(option);
+@@ -1551,4 +1587,26 @@ bool KFileItemDelegate::eventFilter(QObject *object, QEvent *event)
+     } // switch (event->type())
+ }
+ 
++void KFileItemDelegate::setSelectionEmblemRect(QRect rect, int iconSize)
++{
++    const auto emblemSize = d->scaledEmblemSize(iconSize);
++
++    // With small icons, try to center the emblem on top of the icon
++    if (iconSize <= KIconLoader::SizeSmallMedium) {
++        d->emblemRect = QRect(rect.topLeft().x() + emblemSize / 4, rect.topLeft().y() + emblemSize / 4, emblemSize, emblemSize);
++    } else {
++        d->emblemRect = QRect(rect.topLeft().x(), rect.topLeft().y(), emblemSize, emblemSize);
++    }
++}
++
++QRect KFileItemDelegate::selectionEmblemRect() const
++{
++    return d->emblemRect;
++}
++
++KFileItem KFileItemDelegate::fileItem(const QModelIndex &index) const
++{
++    return d->fileItem(index);
++}
++
+ #include "moc_kfileitemdelegate.cpp"
+diff --git a/src/widgets/kfileitemdelegate.h b/src/widgets/kfileitemdelegate.h
+index b4dd7eb05..83ecfe405 100644
+--- a/src/widgets/kfileitemdelegate.h
++++ b/src/widgets/kfileitemdelegate.h
+@@ -9,6 +9,7 @@
+ #define KFILEITEMDELEGATE_H
+ 
+ #include "kiowidgets_export.h"
++#include <KFileItem>
+ #include <QAbstractItemDelegate>
+ #include <QTextOption>
+ 
+@@ -398,6 +399,18 @@ public:
+      */
+     bool eventFilter(QObject *object, QEvent *event) override;
+ 
++    /**
++     * @return The rectangle where selectionEmblem is being drawn
++     */
++    QRect selectionEmblemRect() const;
++
++    /**
++     * Set the rectangle where selectionEmblem should be drawn in.
++     */
++    void setSelectionEmblemRect(QRect rect, int iconSize);
++
++    KFileItem fileItem(const QModelIndex &index) const;
++
+ public Q_SLOTS:
+     /**
+      * Reimplemented from @ref QAbstractItemDelegate.
+@@ -414,6 +427,8 @@ private:
+     class Private;
+     std::unique_ptr<Private> const d; /// @internal
+     Q_DISABLE_COPY(KFileItemDelegate)
++
++    void drawSelectionEmblem(QStyleOptionViewItem option, QPainter *painter, const QModelIndex &index) const;
+ };
+ 
+ #endif // KFILEITEMDELEGATE_H
+-- 
+GitLab
+

--- End Message ---
--- Begin Message ---
Unblocked.

--- End Message ---

Reply to: