File indexing completed on 2024-06-16 10:06:10

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samirh78@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "deleteortrashjob.h"
0009 
0010 #include "fileundomanager.h"
0011 #include "widgetsaskuseractionhandler.h"
0012 #include <kio/copyjob.h>
0013 #include <kio/deletejob.h>
0014 #include <kio/emptytrashjob.h>
0015 #include <kio/job.h>
0016 #include <kio/jobuidelegatefactory.h>
0017 #include <kio_widgets_debug.h>
0018 
0019 #include <KJobWidgets>
0020 
0021 namespace KIO
0022 {
0023 
0024 using AskIface = AskUserActionInterface;
0025 
0026 class DeleteOrTrashJobPrivate
0027 {
0028 public:
0029     DeleteOrTrashJobPrivate(const QList<QUrl> &urls, //
0030                             AskIface::DeletionType deletionType,
0031                             AskIface::ConfirmationType confirm,
0032                             QObject *parent,
0033                             DeleteOrTrashJob *qq)
0034         : q(qq)
0035         , m_urls(urls)
0036         , m_delType(deletionType)
0037         , m_confirm(confirm)
0038         , m_parentWindow(qobject_cast<QWidget *>(parent))
0039     {
0040         // trashing an already trashed file is deleting it, BUG 459545
0041         if (m_delType == AskIface::Trash && m_urls.first().scheme() == QStringLiteral("trash")) {
0042             m_delType = AskIface::Delete;
0043         }
0044     }
0045 
0046     void slotAskUser(bool allowDelete, const QList<QUrl> &urls, AskIface::DeletionType delType, QWidget *parentWindow);
0047 
0048     DeleteOrTrashJob *q = nullptr;
0049     QList<QUrl> m_urls;
0050     AskIface::DeletionType m_delType;
0051     AskIface::ConfirmationType m_confirm;
0052     QWidget *m_parentWindow = nullptr;
0053     QMetaObject::Connection m_handlerConnection;
0054 };
0055 
0056 void DeleteOrTrashJobPrivate::slotAskUser(bool allowDelete, const QList<QUrl> &urls, AskIface::DeletionType delType, QWidget *parentWindow)
0057 {
0058     if (!allowDelete) {
0059         return;
0060     }
0061 
0062     KIO::Job *job = nullptr;
0063     switch (delType) {
0064     case AskIface::Trash:
0065         Q_ASSERT(!urls.isEmpty());
0066         job = KIO::trash(urls);
0067         using UndoMananger = KIO::FileUndoManager;
0068         UndoMananger::self()->recordJob(UndoMananger::Trash, urls, QUrl(QStringLiteral("trash:/")), job);
0069         break;
0070     case AskIface::DeleteInsteadOfTrash:
0071     case AskIface::Delete:
0072         Q_ASSERT(!urls.isEmpty());
0073         job = KIO::del(urls);
0074         break;
0075     case AskIface::EmptyTrash:
0076         job = KIO::emptyTrash();
0077         break;
0078     }
0079 
0080     if (job) {
0081         KJobWidgets::setWindow(job, parentWindow);
0082         // showErrorMessage() is used in slotResult() instead of AutoErrorHandling,
0083         // because if Trashing fails (e.g. due to size constraints), we'll re-ask the
0084         // user about deleting instead of Trashing, in which case we don't want to
0085         // show the "File is too large to Trash" error message
0086         job->uiDelegate()->setAutoErrorHandlingEnabled(false);
0087         q->addSubjob(job);
0088     }
0089 }
0090 
0091 DeleteOrTrashJob::DeleteOrTrashJob(const QList<QUrl> &urls, //
0092                                    AskIface::DeletionType deletionType,
0093                                    AskIface::ConfirmationType confirm,
0094                                    QObject *parent)
0095     : KCompositeJob(parent)
0096     , d(new DeleteOrTrashJobPrivate{urls, deletionType, confirm, parent, this})
0097 {
0098 }
0099 
0100 DeleteOrTrashJob::~DeleteOrTrashJob() = default;
0101 
0102 void DeleteOrTrashJob::start()
0103 {
0104     auto *askHandler = KIO::delegateExtension<AskIface *>(this);
0105     if (!askHandler) {
0106         auto *uiDelegate = new KJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled);
0107         auto *widgetAskHandler = new WidgetsAskUserActionHandler(uiDelegate);
0108         widgetAskHandler->setWindow(d->m_parentWindow);
0109         setUiDelegate(uiDelegate);
0110         askHandler = widgetAskHandler;
0111     }
0112 
0113     Q_ASSERT(askHandler);
0114 
0115     auto askFunc = [this](bool allowDelete, //
0116                           const QList<QUrl> &urls,
0117                           AskIface::DeletionType deletionType,
0118                           QWidget *window) {
0119         d->slotAskUser(allowDelete, urls, deletionType, window);
0120     };
0121 
0122     // Make it a unique connection, as the same UI delegate could get re-used
0123     // if e.g. Trashing failed and we're re-asking the user about deleting instead
0124     // of Trashing
0125     disconnect(d->m_handlerConnection);
0126     d->m_handlerConnection = connect(askHandler, &AskIface::askUserDeleteResult, this, askFunc);
0127     askHandler->askUserDelete(d->m_urls, d->m_delType, d->m_confirm, d->m_parentWindow);
0128 }
0129 
0130 void DeleteOrTrashJob::slotResult(KJob *job)
0131 {
0132     const int errCode = job->error();
0133 
0134     if (errCode == KIO::ERR_TRASH_FILE_TOO_LARGE) {
0135         removeSubjob(job);
0136         d->m_delType = AskIface::DeleteInsteadOfTrash;
0137         start();
0138         return;
0139     }
0140 
0141     if (errCode) {
0142         setError(errCode);
0143         // We're a KJob, not a KIO::Job, so build the error string here
0144         setErrorText(KIO::buildErrorString(errCode, job->errorText()));
0145         job->uiDelegate()->showErrorMessage();
0146     }
0147     emitResult();
0148 }
0149 
0150 } // namespace KIO
0151 
0152 #include "moc_deleteortrashjob.cpp"