File indexing completed on 2024-12-08 03:40:42
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"