1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samirh78@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "deleteortrashjob.h"
9
10#include "fileundomanager.h"
11#include "widgetsaskuseractionhandler.h"
12#include <kio/copyjob.h>
13#include <kio/deletejob.h>
14#include <kio/emptytrashjob.h>
15#include <kio/job.h>
16#include <kio/jobuidelegatefactory.h>
17#include <kio_widgets_debug.h>
18
19#include <KJobWidgets>
20
21namespace KIO
22{
23
24using AskIface = AskUserActionInterface;
25
26class DeleteOrTrashJobPrivate
27{
28public:
29 DeleteOrTrashJobPrivate(const QList<QUrl> &urls, //
30 AskIface::DeletionType deletionType,
31 AskIface::ConfirmationType confirm,
32 QObject *parent,
33 DeleteOrTrashJob *qq)
34 : q(qq)
35 , m_urls(urls)
36 , m_delType(deletionType)
37 , m_confirm(confirm)
38 , m_parentWindow(qobject_cast<QWidget *>(o: parent))
39 {
40 // trashing an already trashed file is deleting it, BUG 459545
41 if (m_delType == AskIface::Trash && m_urls.first().scheme() == QStringLiteral("trash")) {
42 m_delType = AskIface::Delete;
43 }
44 }
45
46 void slotAskUser(bool allowDelete, const QList<QUrl> &urls, AskIface::DeletionType delType, QWidget *parentWindow);
47
48 DeleteOrTrashJob *q = nullptr;
49 QList<QUrl> m_urls;
50 AskIface::DeletionType m_delType;
51 AskIface::ConfirmationType m_confirm;
52 QWidget *m_parentWindow = nullptr;
53 QMetaObject::Connection m_handlerConnection;
54};
55
56void DeleteOrTrashJobPrivate::slotAskUser(bool allowDelete, const QList<QUrl> &urls, AskIface::DeletionType delType, QWidget *parentWindow)
57{
58 if (!allowDelete) {
59 return;
60 }
61
62 KIO::Job *job = nullptr;
63 switch (delType) {
64 case AskIface::Trash:
65 Q_ASSERT(!urls.isEmpty());
66 job = KIO::trash(src: urls);
67 using UndoMananger = KIO::FileUndoManager;
68 UndoMananger::self()->recordJob(op: UndoMananger::Trash, src: urls, dst: QUrl(QStringLiteral("trash:/")), job);
69 break;
70 case AskIface::DeleteInsteadOfTrash:
71 case AskIface::Delete:
72 Q_ASSERT(!urls.isEmpty());
73 job = KIO::del(src: urls);
74 break;
75 case AskIface::EmptyTrash:
76 job = KIO::emptyTrash();
77 break;
78 }
79
80 if (job) {
81 KJobWidgets::setWindow(job, widget: parentWindow);
82 // showErrorMessage() is used in slotResult() instead of AutoErrorHandling,
83 // because if Trashing fails (e.g. due to size constraints), we'll re-ask the
84 // user about deleting instead of Trashing, in which case we don't want to
85 // show the "File is too large to Trash" error message
86 job->uiDelegate()->setAutoErrorHandlingEnabled(false);
87 q->addSubjob(job);
88 }
89}
90
91DeleteOrTrashJob::DeleteOrTrashJob(const QList<QUrl> &urls, //
92 AskIface::DeletionType deletionType,
93 AskIface::ConfirmationType confirm,
94 QObject *parent)
95 : KCompositeJob(parent)
96 , d(new DeleteOrTrashJobPrivate{urls, deletionType, confirm, parent, this})
97{
98}
99
100DeleteOrTrashJob::~DeleteOrTrashJob() = default;
101
102void DeleteOrTrashJob::start()
103{
104 auto *askHandler = KIO::delegateExtension<AskIface *>(job: this);
105 if (!askHandler) {
106 auto *uiDelegate = new KJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled);
107 auto *widgetAskHandler = new WidgetsAskUserActionHandler(uiDelegate);
108 widgetAskHandler->setWindow(d->m_parentWindow);
109 setUiDelegate(uiDelegate);
110 askHandler = widgetAskHandler;
111 }
112
113 Q_ASSERT(askHandler);
114
115 auto askFunc = [this](bool allowDelete, //
116 const QList<QUrl> &urls,
117 AskIface::DeletionType deletionType,
118 QWidget *window) {
119 d->slotAskUser(allowDelete, urls, delType: deletionType, parentWindow: window);
120 };
121
122 // Make it a unique connection, as the same UI delegate could get re-used
123 // if e.g. Trashing failed and we're re-asking the user about deleting instead
124 // of Trashing
125 disconnect(d->m_handlerConnection);
126 d->m_handlerConnection = connect(sender: askHandler, signal: &AskIface::askUserDeleteResult, context: this, slot&: askFunc);
127 askHandler->askUserDelete(urls: d->m_urls, deletionType: d->m_delType, confirmationType: d->m_confirm, parent: d->m_parentWindow);
128}
129
130void DeleteOrTrashJob::slotResult(KJob *job)
131{
132 const int errCode = job->error();
133
134 if (errCode == KIO::ERR_TRASH_FILE_TOO_LARGE) {
135 removeSubjob(job);
136 d->m_delType = AskIface::DeleteInsteadOfTrash;
137 start();
138 return;
139 }
140
141 if (errCode) {
142 setError(errCode);
143 // We're a KJob, not a KIO::Job, so build the error string here
144 setErrorText(KIO::buildErrorString(errorCode: errCode, errorText: job->errorText()));
145 job->uiDelegate()->showErrorMessage();
146 }
147 emitResult();
148}
149
150} // namespace KIO
151
152#include "moc_deleteortrashjob.cpp"
153

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