File indexing completed on 2023-09-24 04:08:27
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2017 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "batchrenamejob.h" 0009 0010 #include "copyjob.h" 0011 #include "job_p.h" 0012 0013 #include <QMimeDatabase> 0014 #include <QTimer> 0015 0016 #include <KLocalizedString> 0017 0018 #include <set> 0019 0020 using namespace KIO; 0021 0022 class KIO::BatchRenameJobPrivate : public KIO::JobPrivate 0023 { 0024 public: 0025 BatchRenameJobPrivate(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags) 0026 : JobPrivate() 0027 , m_srcList(src) 0028 , m_newName(newName) 0029 , m_index(index) 0030 , m_placeHolder(placeHolder) 0031 , m_listIterator(m_srcList.constBegin()) 0032 , m_allExtensionsDifferent(true) 0033 , m_useIndex(true) 0034 , m_appendIndex(false) 0035 , m_flags(flags) 0036 { 0037 // There occur four cases when renaming multiple files, 0038 // 1. All files have different extension and $newName contains a valid placeholder. 0039 // 2. At least two files have same extension and $newName contains a valid placeholder. 0040 // In these two cases the placeholder character will be replaced by an integer($index). 0041 // 3. All files have different extension and new name contains an invalid placeholder 0042 // (this means either $newName doesn't contain the placeholder or the placeholders 0043 // are not in a connected sequence). 0044 // In this case nothing is substituted and all files have the same $newName. 0045 // 4. At least two files have same extension and $newName contains an invalid placeholder. 0046 // In this case $index is appended to $newName. 0047 0048 // Check for extensions. 0049 std::set<QString> extensions; 0050 QMimeDatabase db; 0051 for (const QUrl &url : std::as_const(m_srcList)) { 0052 const QString extension = db.suffixForFileName(url.path()); 0053 const auto [it, isInserted] = extensions.insert(extension); 0054 if (!isInserted) { 0055 m_allExtensionsDifferent = false; 0056 break; 0057 } 0058 } 0059 0060 // Check for exactly one placeholder character or exactly one sequence of placeholders. 0061 int pos = newName.indexOf(placeHolder); 0062 if (pos != -1) { 0063 while (pos < newName.size() && newName.at(pos) == placeHolder) { 0064 pos++; 0065 } 0066 } 0067 const bool validPlaceholder = (newName.indexOf(placeHolder, pos) == -1); 0068 0069 if (!validPlaceholder) { 0070 if (!m_allExtensionsDifferent) { 0071 m_appendIndex = true; 0072 } else { 0073 m_useIndex = false; 0074 } 0075 } 0076 } 0077 0078 QList<QUrl> m_srcList; 0079 QString m_newName; 0080 int m_index; 0081 QChar m_placeHolder; 0082 QList<QUrl>::const_iterator m_listIterator; 0083 bool m_allExtensionsDifferent; 0084 bool m_useIndex; 0085 bool m_appendIndex; 0086 QUrl m_oldUrl; 0087 QUrl m_newUrl; // for fileRenamed signal 0088 const JobFlags m_flags; 0089 QTimer m_reportTimer; 0090 0091 Q_DECLARE_PUBLIC(BatchRenameJob) 0092 0093 void slotStart(); 0094 void slotReport(); 0095 0096 QString indexedName(const QString &name, int index, QChar placeHolder) const; 0097 0098 static inline BatchRenameJob *newJob(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags) 0099 { 0100 BatchRenameJob *job = new BatchRenameJob(*new BatchRenameJobPrivate(src, newName, index, placeHolder, flags)); 0101 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0102 if (!(flags & HideProgressInfo)) { 0103 KIO::getJobTracker()->registerJob(job); 0104 } 0105 if (!(flags & NoPrivilegeExecution)) { 0106 job->d_func()->m_privilegeExecutionEnabled = true; 0107 job->d_func()->m_operationType = Rename; 0108 } 0109 return job; 0110 } 0111 }; 0112 0113 BatchRenameJob::BatchRenameJob(BatchRenameJobPrivate &dd) 0114 : Job(dd) 0115 { 0116 Q_D(BatchRenameJob); 0117 connect(&d->m_reportTimer, &QTimer::timeout, this, [this]() { 0118 d_func()->slotReport(); 0119 }); 0120 d->m_reportTimer.start(200); 0121 0122 QTimer::singleShot(0, this, [this] { 0123 d_func()->slotStart(); 0124 }); 0125 } 0126 0127 BatchRenameJob::~BatchRenameJob() 0128 { 0129 } 0130 0131 QString BatchRenameJobPrivate::indexedName(const QString &name, int index, QChar placeHolder) const 0132 { 0133 if (!m_useIndex) { 0134 return name; 0135 } 0136 0137 QString newName = name; 0138 QString indexString = QString::number(index); 0139 0140 if (m_appendIndex) { 0141 newName.append(indexString); 0142 return newName; 0143 } 0144 0145 // Insert leading zeros if necessary 0146 const int minIndexLength = name.count(placeHolder); 0147 indexString.prepend(QString(minIndexLength - indexString.length(), QLatin1Char('0'))); 0148 0149 // Replace the index placeholders by the indexString 0150 const int placeHolderStart = newName.indexOf(placeHolder); 0151 newName.replace(placeHolderStart, minIndexLength, indexString); 0152 0153 return newName; 0154 } 0155 0156 void BatchRenameJobPrivate::slotStart() 0157 { 0158 Q_Q(BatchRenameJob); 0159 0160 if (m_listIterator == m_srcList.constBegin()) { // emit total 0161 q->setTotalAmount(KJob::Items, m_srcList.count()); 0162 } 0163 0164 if (m_listIterator != m_srcList.constEnd()) { 0165 QString newName = indexedName(m_newName, m_index, m_placeHolder); 0166 const QUrl oldUrl = *m_listIterator; 0167 QMimeDatabase db; 0168 const QString extension = db.suffixForFileName(oldUrl.path()); 0169 if (!extension.isEmpty()) { 0170 newName += QLatin1Char('.') + extension; 0171 } 0172 0173 m_oldUrl = oldUrl; 0174 m_newUrl = oldUrl.adjusted(QUrl::RemoveFilename); 0175 m_newUrl.setPath(m_newUrl.path() + KIO::encodeFileName(newName)); 0176 0177 KIO::Job *job = KIO::moveAs(oldUrl, m_newUrl, KIO::HideProgressInfo); 0178 job->setParentJob(q); 0179 q->addSubjob(job); 0180 } else { 0181 m_reportTimer.stop(); 0182 slotReport(); 0183 q->emitResult(); 0184 } 0185 } 0186 0187 void BatchRenameJobPrivate::slotReport() 0188 { 0189 Q_Q(BatchRenameJob); 0190 0191 const auto processed = m_listIterator - m_srcList.constBegin(); 0192 0193 q->setProcessedAmount(KJob::Items, processed); 0194 q->emitPercent(processed, m_srcList.count()); 0195 0196 emitRenaming(q, m_oldUrl, m_newUrl); 0197 } 0198 0199 void BatchRenameJob::slotResult(KJob *job) 0200 { 0201 Q_D(BatchRenameJob); 0202 if (job->error()) { 0203 d->m_reportTimer.stop(); 0204 d->slotReport(); 0205 KIO::Job::slotResult(job); 0206 return; 0207 } 0208 0209 removeSubjob(job); 0210 0211 Q_EMIT fileRenamed(*d->m_listIterator, d->m_newUrl); 0212 ++d->m_listIterator; 0213 ++d->m_index; 0214 d->slotStart(); 0215 } 0216 0217 BatchRenameJob *KIO::batchRename(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, KIO::JobFlags flags) 0218 { 0219 return BatchRenameJobPrivate::newJob(src, newName, index, placeHolder, flags); 0220 } 0221 0222 #include "moc_batchrenamejob.cpp"