Control: tags -1 moreinfo On 2025-06-06 00:45:10 +0200, Aurélien COUDERC wrote: > Package: release.debian.org > Severity: normal > X-Debbugs-Cc: kf6-...@packages.debian.org, Debian Qt/KDE Maintainers > <debian-qt-...@lists.debian.org> > Control: affects -1 + src:kf6-kio > User: release.debian....@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.
This change removes a symbol. Why is it save to do that? Cheers > - 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 <couc...@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 <k...@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?= <ballog...@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 <k...@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 <mcla...@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 <al...@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 <k...@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 <k...@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 <akse...@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?= <efecif...@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 <akse...@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 <akse...@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 <akse...@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 <akse...@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 ¤t, > 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 ¤t, 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 <akse...@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 <akse...@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 > + -- Sebastian Ramacher