KIO

batchrenamejob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2017 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "batchrenamejob.h"
9
10#include "copyjob.h"
11#include "job_p.h"
12
13#include <QMimeDatabase>
14#include <QTimer>
15
16#include <KLocalizedString>
17
18#include <set>
19
20using namespace KIO;
21
22class KIO::BatchRenameJobPrivate : public KIO::JobPrivate
23{
24public:
25 BatchRenameJobPrivate(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags)
26 : JobPrivate()
27 , m_srcList(src)
28 , m_newName(newName)
29 , m_index(index)
30 , m_placeHolder(placeHolder)
31 , m_listIterator(m_srcList.constBegin())
32 , m_allExtensionsDifferent(true)
33 , m_useIndex(true)
34 , m_appendIndex(false)
35 , m_flags(flags)
36 {
37 // There occur four cases when renaming multiple files,
38 // 1. All files have different extension and $newName contains a valid placeholder.
39 // 2. At least two files have same extension and $newName contains a valid placeholder.
40 // In these two cases the placeholder character will be replaced by an integer($index).
41 // 3. All files have different extension and new name contains an invalid placeholder
42 // (this means either $newName doesn't contain the placeholder or the placeholders
43 // are not in a connected sequence).
44 // In this case nothing is substituted and all files have the same $newName.
45 // 4. At least two files have same extension and $newName contains an invalid placeholder.
46 // In this case $index is appended to $newName.
47
48 // Check for extensions.
49 std::set<QString> extensions;
51 for (const QUrl &url : std::as_const(m_srcList)) {
52 const QString extension = db.suffixForFileName(url.path());
53 const auto [it, isInserted] = extensions.insert(extension);
54 if (!isInserted) {
55 m_allExtensionsDifferent = false;
56 break;
57 }
58 }
59
60 // Check for exactly one placeholder character or exactly one sequence of placeholders.
61 int pos = newName.indexOf(placeHolder);
62 if (pos != -1) {
63 while (pos < newName.size() && newName.at(pos) == placeHolder) {
64 pos++;
65 }
66 }
67 const bool validPlaceholder = (newName.indexOf(placeHolder, pos) == -1);
68
69 if (!validPlaceholder) {
70 if (!m_allExtensionsDifferent) {
71 m_appendIndex = true;
72 } else {
73 m_useIndex = false;
74 }
75 }
76 }
77
78 QList<QUrl> m_srcList;
79 QString m_newName;
80 int m_index;
81 QChar m_placeHolder;
82 QList<QUrl>::const_iterator m_listIterator;
83 bool m_allExtensionsDifferent;
84 bool m_useIndex;
85 bool m_appendIndex;
86 QUrl m_oldUrl;
87 QUrl m_newUrl; // for fileRenamed signal
88 const JobFlags m_flags;
89 QTimer m_reportTimer;
90
91 Q_DECLARE_PUBLIC(BatchRenameJob)
92
93 void slotStart();
94 void slotReport();
95
96 QString indexedName(const QString &name, int index, QChar placeHolder) const;
97
98 static inline BatchRenameJob *newJob(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags)
99 {
100 BatchRenameJob *job = new BatchRenameJob(*new BatchRenameJobPrivate(src, newName, index, placeHolder, flags));
102 if (!(flags & HideProgressInfo)) {
104 }
105 if (!(flags & NoPrivilegeExecution)) {
106 job->d_func()->m_privilegeExecutionEnabled = true;
107 job->d_func()->m_operationType = Rename;
108 }
109 return job;
110 }
111};
112
113BatchRenameJob::BatchRenameJob(BatchRenameJobPrivate &dd)
114 : Job(dd)
115{
117 connect(&d->m_reportTimer, &QTimer::timeout, this, [this]() {
118 d_func()->slotReport();
119 });
120 d->m_reportTimer.start(200);
121
122 QTimer::singleShot(0, this, [this] {
123 d_func()->slotStart();
124 });
125}
126
127BatchRenameJob::~BatchRenameJob()
128{
129}
130
131QString BatchRenameJobPrivate::indexedName(const QString &name, int index, QChar placeHolder) const
132{
133 if (!m_useIndex) {
134 return name;
135 }
136
137 QString newName = name;
138 QString indexString = QString::number(index);
139
140 if (m_appendIndex) {
141 newName.append(indexString);
142 return newName;
143 }
144
145 // Insert leading zeros if necessary
146 const int minIndexLength = name.count(placeHolder);
147 indexString.prepend(QString(minIndexLength - indexString.length(), QLatin1Char('0')));
148
149 // Replace the index placeholders by the indexString
150 const int placeHolderStart = newName.indexOf(placeHolder);
151 newName.replace(placeHolderStart, minIndexLength, indexString);
152
153 return newName;
154}
155
156void BatchRenameJobPrivate::slotStart()
157{
158 Q_Q(BatchRenameJob);
159
160 if (m_listIterator == m_srcList.constBegin()) { // emit total
161 q->setTotalAmount(KJob::Items, m_srcList.count());
162 }
163
164 if (m_listIterator != m_srcList.constEnd()) {
165 QString newName = indexedName(m_newName, m_index, m_placeHolder);
166 const QUrl oldUrl = *m_listIterator;
167 QMimeDatabase db;
168 const QString extension = db.suffixForFileName(oldUrl.path());
169 if (!extension.isEmpty()) {
170 newName += QLatin1Char('.') + extension;
171 }
172
173 m_oldUrl = oldUrl;
174 m_newUrl = oldUrl.adjusted(QUrl::RemoveFilename);
175 m_newUrl.setPath(m_newUrl.path() + KIO::encodeFileName(newName));
176
177 KIO::Job *job = KIO::moveAs(oldUrl, m_newUrl, KIO::HideProgressInfo);
178 job->setParentJob(q);
179 q->addSubjob(job);
180 } else {
181 m_reportTimer.stop();
182 slotReport();
183 q->emitResult();
184 }
185}
186
187void BatchRenameJobPrivate::slotReport()
188{
189 Q_Q(BatchRenameJob);
190
191 const auto processed = m_listIterator - m_srcList.constBegin();
192
193 q->setProcessedAmount(KJob::Items, processed);
194 q->emitPercent(processed, m_srcList.count());
195
196 emitRenaming(q, m_oldUrl, m_newUrl);
197}
198
199void BatchRenameJob::slotResult(KJob *job)
200{
202 if (job->error()) {
203 d->m_reportTimer.stop();
204 d->slotReport();
206 return;
207 }
208
209 removeSubjob(job);
210
211 Q_EMIT fileRenamed(*d->m_listIterator, d->m_newUrl);
212 ++d->m_listIterator;
213 ++d->m_index;
214 d->slotStart();
215}
216
217BatchRenameJob *KIO::batchRename(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, KIO::JobFlags flags)
218{
219 return BatchRenameJobPrivate::newJob(src, newName, index, placeHolder, flags);
220}
221
222#include "moc_batchrenamejob.cpp"
virtual void slotResult(KJob *job)
A KIO job that renames multiple files in one go.
KIOCORE_NO_EXPORT BatchRenameJob(BatchRenameJobPrivate &dd)
void fileRenamed(const QUrl &oldUrl, const QUrl &newUrl)
Signals that a file was renamed.
The base class for all jobs.
void setParentJob(Job *parentJob)
Set the parent Job.
Definition job.cpp:192
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition job.cpp:80
virtual void registerJob(KJob *job)
int error() const
void setUiDelegate(KJobUiDelegate *delegate)
A namespace for KIO globals.
KIOCORE_EXPORT BatchRenameJob * batchRename(const QList< QUrl > &src, const QString &newName, int index, QChar placeHolder, JobFlags flags=DefaultFlags)
Renames multiple files at once.
KIOCORE_EXPORT CopyJob * moveAs(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Moves a file or directory src to the given destination dest.
Definition copyjob.cpp:2669
KIOCORE_EXPORT KJobUiDelegate * createDefaultJobUiDelegate()
Convenience method: use default factory, if there's one, to create a delegate and return it.
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ NoPrivilegeExecution
When set, notifies the worker that application/job does not want privilege execution.
Definition job_base.h:276
KIOCORE_EXPORT KJobTrackerInterface * getJobTracker()
Returns the job tracker to be used by all KIO jobs (in which HideProgressInfo is not set)
KIOCORE_EXPORT QString encodeFileName(const QString &str)
Encodes (from the text displayed to the real filename) This translates '/' into a "unicode fraction s...
Definition global.cpp:111
QString name(StandardAction id)
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
QString suffixForFileName(const QString &fileName) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype count() const const
QString & append(QChar ch)
const QChar at(qsizetype position) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & prepend(QChar ch)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
void stop()
void timeout()
RemoveFilename
QUrl adjusted(FormattingOptions options) const const
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:09:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.