KIO

renamefiledialog.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
4 SPDX-FileCopyrightText: 2020 Méven Car <meven.car@kdemail.net>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "renamefiledialog.h"
10
11#include <KGuiItem>
12#include <KIO/BatchRenameJob>
13#include <KIO/CopyJob>
14#include <KIO/FileUndoManager>
15#include <KJobUiDelegate>
16#include <KJobWidgets>
17#include <KLocalizedString>
18
19#include <QDialogButtonBox>
20#include <QHBoxLayout>
21#include <QLabel>
22#include <QLineEdit>
23#include <QMimeDatabase>
24#include <QPushButton>
25#include <QShowEvent>
26#include <QSpinBox>
27
28namespace KIO
29{
30class Q_DECL_HIDDEN RenameFileDialog::RenameFileDialogPrivate
31{
32public:
33 RenameFileDialogPrivate(const KFileItemList &items)
34 : lineEdit(nullptr)
35 , items(items)
36 , spinBox(nullptr)
37 , renameOneItem(false)
38 , allExtensionsDifferent(true)
39 {
40 }
41
42 QList<QUrl> renamedItems;
43 QLineEdit *lineEdit;
44 KFileItemList items;
45 QSpinBox *spinBox;
46 QPushButton *okButton;
47 bool renameOneItem;
48 bool allExtensionsDifferent;
49};
50
52 : QDialog(parent)
53 , d(new RenameFileDialogPrivate(items))
54{
55 setMinimumWidth(320);
56
57 const int itemCount = items.count();
58 Q_ASSERT(itemCount >= 1);
59 d->renameOneItem = (itemCount == 1);
60
61 setWindowTitle(d->renameOneItem ? i18nc("@title:window", "Rename Item") : i18nc("@title:window", "Rename Items"));
63 QVBoxLayout *mainLayout = new QVBoxLayout(this);
64 d->okButton = buttonBox->button(QDialogButtonBox::Ok);
65 d->okButton->setDefault(true);
67 connect(buttonBox, &QDialogButtonBox::accepted, this, &RenameFileDialog::slotAccepted);
70 d->okButton->setDefault(true);
71
72 KGuiItem::assign(d->okButton, KGuiItem(i18nc("@action:button", "&Rename"), QStringLiteral("dialog-ok-apply")));
73
74 QWidget *page = new QWidget(this);
75 mainLayout->addWidget(page);
76 mainLayout->addWidget(buttonBox);
77
78 QVBoxLayout *topLayout = new QVBoxLayout(page);
79
80 QLabel *editLabel = nullptr;
81 QString newName;
82 if (d->renameOneItem) {
83 newName = items.first().name();
84 editLabel = new QLabel(xi18nc("@label:textbox", "Rename the item <filename>%1</filename> to:", newName), page);
85 editLabel->setTextFormat(Qt::PlainText);
86 } else {
87 newName = i18nc("This a template for new filenames, # is replaced by a number later, must be the end character", "New name #");
88 editLabel = new QLabel(i18ncp("@label:textbox", "Rename the %1 selected item to:", "Rename the %1 selected items to:", itemCount), page);
89 }
90
91 d->lineEdit = new QLineEdit(page);
92 mainLayout->addWidget(d->lineEdit);
93 connect(d->lineEdit, &QLineEdit::textChanged, this, &RenameFileDialog::slotTextChanged);
94
95 int selectionLength = newName.length();
96 if (d->renameOneItem) {
97 // If the current item is a directory, select the whole file name.
98 if (!items.first().isDir()) {
100 const QString extension = db.suffixForFileName(items.first().name());
101 if (extension.length() > 0) {
102 // Don't select the extension
103 selectionLength -= extension.length() + 1;
104 }
105 }
106 } else {
107 // Don't select the # character
108 --selectionLength;
109 }
110
111 d->lineEdit->setText(newName);
112 d->lineEdit->setSelection(0, selectionLength);
113
114 topLayout->addWidget(editLabel);
115 topLayout->addWidget(d->lineEdit);
116
117 if (!d->renameOneItem) {
118 QMimeDatabase db;
119 QSet<QString> extensions;
120 for (const KFileItem &item : std::as_const(d->items)) {
121 const QString extension = db.suffixForFileName(item.name());
122
123 if (extensions.contains(extension)) {
124 d->allExtensionsDifferent = false;
125 break;
126 }
127
128 extensions.insert(extension);
129 }
130
131 QLabel *infoLabel = new QLabel(i18nc("@info", "# will be replaced by ascending numbers starting with:"), page);
132 mainLayout->addWidget(infoLabel);
133 d->spinBox = new QSpinBox(page);
134 d->spinBox->setMinimum(0);
135 d->spinBox->setMaximum(1'000'000'000);
136 d->spinBox->setSingleStep(1);
137 d->spinBox->setValue(1);
138 d->spinBox->setDisplayIntegerBase(10);
139
140 QHBoxLayout *horizontalLayout = new QHBoxLayout;
141 horizontalLayout->setContentsMargins(0, 0, 0, 0);
142 horizontalLayout->addWidget(infoLabel);
143 horizontalLayout->addWidget(d->spinBox);
144
145 topLayout->addLayout(horizontalLayout);
146 }
147
148 d->lineEdit->setFocus();
149}
150
151RenameFileDialog::~RenameFileDialog()
152{
153}
154
155void RenameFileDialog::slotAccepted()
156{
157 QWidget *widget = parentWidget();
158 if (!widget) {
159 widget = this;
160 }
161
162 const QList<QUrl> srcList = d->items.urlList();
163 const QString newName = d->lineEdit->text();
165 KIO::Job *job = nullptr;
166 if (d->renameOneItem) {
167 Q_ASSERT(d->items.count() == 1);
168 cmdType = KIO::FileUndoManager::Rename;
169 const QUrl oldUrl = d->items.constFirst().url();
170 QUrl newUrl = oldUrl.adjusted(QUrl::RemoveFilename);
171 newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName));
172 d->renamedItems << newUrl;
173 job = KIO::moveAs(oldUrl, newUrl, KIO::HideProgressInfo);
174 } else {
175 d->renamedItems.reserve(d->items.count());
177 job = KIO::batchRename(srcList, newName, d->spinBox->value(), QLatin1Char('#'));
178 connect(qobject_cast<KIO::BatchRenameJob *>(job), &KIO::BatchRenameJob::fileRenamed, this, &RenameFileDialog::slotFileRenamed);
179 }
180
181 KJobWidgets::setWindow(job, widget);
182 const QUrl parentUrl = srcList.first().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
183 KIO::FileUndoManager::self()->recordJob(cmdType, srcList, parentUrl, job);
184
185 connect(job, &KJob::result, this, &RenameFileDialog::slotResult);
187
188 accept();
189}
190
191void RenameFileDialog::slotTextChanged(const QString &newName)
192{
193 bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1String("."));
194 if (enable && !d->renameOneItem) {
195 const int count = newName.count(QLatin1Char('#'));
196 if (count == 0) {
197 // Renaming multiple files without '#' will only work if all extensions are different.
198 enable = d->allExtensionsDifferent;
199 } else {
200 // Ensure that the new name contains exactly one # (or a connected sequence of #'s)
201 const int first = newName.indexOf(QLatin1Char('#'));
202 const int last = newName.lastIndexOf(QLatin1Char('#'));
203 enable = (last - first + 1 == count);
204 }
205 }
206 d->okButton->setEnabled(enable);
207}
208
209void RenameFileDialog::slotFileRenamed(const QUrl &oldUrl, const QUrl &newUrl)
210{
211 Q_UNUSED(oldUrl)
212 d->renamedItems << newUrl;
213}
214
215void RenameFileDialog::slotResult(KJob *job)
216{
217 if (!job->error()) {
218 Q_EMIT renamingFinished(d->renamedItems);
219 } else {
220 Q_EMIT error(job);
221 }
222}
223
224} // namespace KIO
225
226#include "moc_renamefiledialog.cpp"
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition kfileitem.h:632
QList< QUrl > urlList() const
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
static void assign(QPushButton *button, const KGuiItem &item)
void fileRenamed(const QUrl &oldUrl, const QUrl &newUrl)
Signals that a file was renamed.
static FileUndoManager * self()
void recordJob(CommandType op, const QList< QUrl > &src, const QUrl &dst, KIO::Job *job)
Record this job while it's happening and add a command for it so that the user can undo it.
CommandType
The type of job.
@ BatchRename
Represents a KIO::batchRename() job. Used when renaming multiple files.
The base class for all jobs.
RenameFileDialog(const KFileItemList &items, QWidget *parent)
Constructs the Dialog to rename file(s)
int error() const
void result(KJob *job)
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
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
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
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
void setWindow(QObject *job, QWidget *widget)
void setShortcut(const QKeySequence &key)
void addLayout(QLayout *layout, int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void accept()
virtual void reject()
QPushButton * button(StandardButton which) const const
void setTextFormat(Qt::TextFormat)
void setContentsMargins(const QMargins &margins)
void setSelection(int start, int length)
void setText(const QString &)
void textChanged(const QString &text)
const T & constFirst() const const
qsizetype count() const const
T & first()
void reserve(qsizetype size)
QString suffixForFileName(const QString &fileName) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T qobject_cast(QObject *object)
void setDefault(bool)
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
void setDisplayIntegerBase(int base)
void setMaximum(int max)
void setMinimum(int min)
void setSingleStep(int val)
void setValue(int val)
qsizetype count() const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
Key_Return
PlainText
RemoveFilename
QUrl adjusted(FormattingOptions options) const const
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
QWidget(QWidget *parent, Qt::WindowFlags f)
void setEnabled(bool)
void setMinimumWidth(int minw)
QWidget * parentWidget() const const
void setFocus()
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:28 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.