Pimcommon

renamefiledialog.cpp
1/*
2 * SPDX-FileCopyrightText: 2011-2024 Laurent Montel <montel@kde.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6#include "renamefiledialog.h"
7#include <KIO/StatJob>
8#include <KJobWidgets>
9#include <KLocalizedString>
10#include <KMessageBox>
11#include <KSeparator>
12#include <KStandardGuiItem>
13#include <QLineEdit>
14#include <QPushButton>
15
16#include <QCheckBox>
17#include <QFileInfo>
18#include <QHBoxLayout>
19#include <QLabel>
20#include <QVBoxLayout>
21
22using namespace PimCommon;
23
24class Q_DECL_HIDDEN PimCommon::RenameFileDialog::RenameFileDialogPrivate
25{
26public:
27 RenameFileDialogPrivate(const QUrl &_url, RenameFileDialog *qq)
28 : url(_url)
29 , q(qq)
30 {
31 }
32
33 [[nodiscard]] QString suggestName(const QUrl &baseURL, const QString &oldName);
34
35 const QUrl url;
36 QCheckBox *applyAll = nullptr;
37 QPushButton *renameBtn = nullptr;
38 QPushButton *suggestNewNameBtn = nullptr;
39 QLineEdit *nameEdit = nullptr;
40 RenameFileDialog *const q;
41};
42
43QString PimCommon::RenameFileDialog::RenameFileDialogPrivate::suggestName(const QUrl &baseURL, const QString &oldName)
44{
45 QString dotSuffix;
46 QString suggestedName;
47 QString basename = oldName;
48 const QChar spacer(QLatin1Char(' '));
49
50 // ignore dots at the beginning, that way "..aFile.tar.gz" will become "..aFile 1.tar.gz" instead of " 1..aFile.tar.gz"
51 int index = basename.indexOf(QLatin1Char('.'));
52 int continuous = 0;
53 while (continuous == index) {
54 index = basename.indexOf(QLatin1Char('.'), index + 1);
55 ++continuous;
56 }
57
58 if (index != -1) {
59 dotSuffix = basename.mid(index);
60 basename.truncate(index);
61 }
62
63 const int pos = basename.lastIndexOf(spacer);
64
65 if (pos != -1) {
66 const QString tmp = basename.mid(pos + 1);
67 bool ok;
68 const int number = tmp.toInt(&ok);
69
70 if (!ok) { // ok there is no number
71 suggestedName = basename + spacer + QLatin1Char('1') + dotSuffix;
72 } else {
73 // yes there's already a number behind the spacer so increment it by one
74 basename.replace(pos + 1, tmp.length(), QString::number(number + 1));
75 suggestedName = basename + dotSuffix;
76 }
77 } else { // no spacer yet
78 suggestedName = basename + spacer + QLatin1Char('1') + dotSuffix;
79 }
80
81 // Check if suggested name already exists
82 bool exists = false;
83 // TODO: network transparency. However, using NetAccess from a modal dialog
84 // could be a problem, no? (given that it uses a modal widget itself....)
85 if (baseURL.isLocalFile()) {
86 exists = QFileInfo::exists(baseURL.toLocalFile() + QLatin1Char('/') + suggestedName);
87 }
88
89 if (exists) { // already exists -> recurse
90 return suggestName(baseURL, suggestedName);
91 } else {
92 return suggestedName;
93 }
94}
95
96RenameFileDialog::RenameFileDialog(const QUrl &url, bool multiFiles, QWidget *parent)
97 : QDialog(parent)
98 , d(new RenameFileDialogPrivate(url, this))
99{
100 setWindowTitle(i18nc("@title:window", "File Already Exists"));
101 auto pLayout = new QVBoxLayout(this);
102
103 auto label = new QLabel(xi18n("A file named <filename>%1</filename> already exists. Do you want to overwrite it?", url.fileName()), this);
104 pLayout->addWidget(label);
105
106 auto renameLayout = new QHBoxLayout();
107 pLayout->addLayout(renameLayout);
108
109 d->nameEdit = new QLineEdit(this);
110 renameLayout->addWidget(d->nameEdit);
111 d->nameEdit->setClearButtonEnabled(true);
112 d->nameEdit->setText(url.fileName());
113 d->suggestNewNameBtn = new QPushButton(i18n("Suggest New &Name"), this);
114 renameLayout->addWidget(d->suggestNewNameBtn);
115 connect(d->suggestNewNameBtn, &QPushButton::clicked, this, &RenameFileDialog::slotSuggestNewNamePressed);
116
117 auto overWrite = new QPushButton(this);
118 KStandardGuiItem::assign(overWrite, KStandardGuiItem::Overwrite);
119 connect(overWrite, &QPushButton::clicked, this, &RenameFileDialog::slotOverwritePressed);
120
121 auto ignore = new QPushButton(i18n("&Ignore"), this);
122 connect(ignore, &QPushButton::clicked, this, &RenameFileDialog::slotIgnorePressed);
123
124 d->renameBtn = new QPushButton(i18n("&Rename"), this);
125 connect(d->renameBtn, &QPushButton::clicked, this, &RenameFileDialog::slotRenamePressed);
126
127 auto separator = new KSeparator(this);
128 pLayout->addWidget(separator);
129
130 auto layout = new QHBoxLayout();
131 pLayout->addLayout(layout);
132
133 if (multiFiles) {
134 d->applyAll = new QCheckBox(i18n("Appl&y to All"), this);
135 connect(d->applyAll, &QCheckBox::clicked, this, &RenameFileDialog::slotApplyAllPressed);
136 layout->addWidget(d->applyAll);
137 slotApplyAllPressed();
138 }
139 layout->addWidget(d->renameBtn);
140 layout->addWidget(overWrite);
141 layout->addWidget(ignore);
142}
143
144RenameFileDialog::~RenameFileDialog() = default;
145
146void RenameFileDialog::slotOverwritePressed()
147{
148 if (d->applyAll && d->applyAll->isChecked()) {
149 done(RENAMEFILE_OVERWRITEALL);
150 } else {
151 done(RENAMEFILE_OVERWRITE);
152 }
153}
154
155void RenameFileDialog::slotIgnorePressed()
156{
157 if (d->applyAll && d->applyAll->isChecked()) {
158 done(RENAMEFILE_IGNOREALL);
159 } else {
160 done(RENAMEFILE_IGNORE);
161 }
162}
163
164void RenameFileDialog::slotRenamePressed()
165{
166 if (d->nameEdit->text().isEmpty()) {
167 return;
168 }
169
170 bool fileExists = false;
171 if (newName().isLocalFile()) {
172 fileExists = QFile::exists(newName().path());
173 } else {
174 auto job = KIO::stat(newName(), KIO::StatJob::DestinationSide, KIO::StatBasic);
175 KJobWidgets::setWindow(job, this);
176 fileExists = job->exec();
177 }
178
179 if (fileExists) {
180 KMessageBox::error(this, i18n("This filename \"%1\" already exists.", newName().toDisplayString(QUrl::PreferLocalFile)), i18n("File already exists"));
181 return;
182 }
183 done(RENAMEFILE_RENAME);
184}
185
186void RenameFileDialog::slotApplyAllPressed()
187{
188 const bool enabled(!d->applyAll->isChecked());
189 d->nameEdit->setEnabled(enabled);
190 d->suggestNewNameBtn->setEnabled(enabled);
191 d->renameBtn->setEnabled(enabled);
192}
193
194void RenameFileDialog::slotSuggestNewNamePressed()
195{
196 if (d->nameEdit->text().isEmpty()) {
197 return;
198 }
199
200 auto destDirectory = d->url.adjusted(QUrl::RemoveFilename);
201 d->nameEdit->setText(d->suggestName(destDirectory, d->nameEdit->text()));
202}
203
204QUrl RenameFileDialog::newName() const
205{
206 const QString fileName = d->nameEdit->text();
207
208 auto newDest = d->url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
209 newDest.setPath(newDest.path() + QLatin1Char('/') + KIO::encodeFileName(fileName));
210
211 return newDest;
212}
213
214#include "moc_renamefiledialog.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString xi18n(const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
bool fileExists(const QUrl &path)
KCOREADDONS_EXPORT QString suggestName(const QUrl &baseURL, const QString &oldName)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
KIOCORE_EXPORT QString encodeFileName(const QString &str)
void setWindow(QObject *job, QWidget *widget)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString label(StandardShortcut id)
folderdialogacltab.h
void clicked(bool checked)
bool exists() const const
bool exists() const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
int toInt(bool *ok, int base) const const
void truncate(qsizetype position)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
PreferLocalFile
QString fileName(ComponentFormattingOptions options) const const
bool isLocalFile() const const
QString toLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.