diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -124,6 +124,7 @@ kfileplacesmodeltest.cpp kfileplacesviewtest.cpp kurlrequestertest.cpp + batchrenametypestest.cpp NAME_PREFIX "kiofilewidgets-" LINK_LIBRARIES KF5::KIOFileWidgets KF5::KIOWidgets KF5::XmlGui KF5::Bookmarks Qt5::Test KF5::I18n ) diff --git a/autotests/batchrenametypestest.h b/autotests/batchrenametypestest.h new file mode 100644 --- /dev/null +++ b/autotests/batchrenametypestest.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_BATCHRENAMETYPESTEST_H +#define KIO_BATCHRENAMETYPESTEST_H + +#include + +class BatchRenameTypesTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testMacroReplace_data(); + void testMacroReplace(); + + void testFindReplace_data(); + void testFindReplace(); + + void testCaptureGroups_data(); + void testCaptureGroups(); +}; + +#endif diff --git a/autotests/batchrenametypestest.cpp b/autotests/batchrenametypestest.cpp new file mode 100644 --- /dev/null +++ b/autotests/batchrenametypestest.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +typedef QHash RenameTypeInput; +Q_DECLARE_METATYPE(RenameTypeInput) + +void BatchRenameTypesTest::testMacroReplace_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expected"); + + RenameTypeInput params; + params["macroReplace.sequence"] = 15; + params["macroReplace.padding"] = 3; + params["timestamp"] = QDateTime::fromTime_t(1522504691); // 2018-03-31 15:58:11 CET + params["name"] = "file.ext"; + params["baseName"] = "file"; + params["extension"] = ".ext"; + + params["macroReplace.text"] = "NewName {index} - (%y) %Y%M%D (Renamed from {name})"; + QTest::newRow("Macro replace with sequence, date and name") + << params << "NewName 015 - (18) 20180331 (Renamed from file.ext)"; + + params["macroReplace.text"] = "{baseName}_%H%m%S{suffix}"; + QTest::newRow("Macro replace with time, basename and extension") + << params << "file_155811.ext"; + + params["macroReplace.text"] = "hello"; + QTest::newRow("Macro replace with no placeholders") + << params << "hello"; +} + +void BatchRenameTypesTest::testMacroReplace() +{ + QFETCH(RenameTypeInput, input); + QFETCH(QString, expected); + + QCOMPARE(KIO::BatchRenameTypes::macroRenamer(input), expected); +} + +void BatchRenameTypesTest::testFindReplace_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expected"); + + RenameTypeInput params; + params["name"] = "Filename_for_find_and_replace.tar.gz"; + params["findReplace.find"] = "find_"; + params["findReplace.replace"] = ""; + params["findReplace.useRegex"] = false; + params["findReplace.caseInsensitive"] = false; + + QTest::newRow("Find & Replace no regex case sensitive") + << params << "Filename_for_and_replace.tar.gz"; + + params["findReplace.find"] = "FIND_"; + QTest::newRow("Find & Replace no regex case sensitive, should not replace anything") + << params << "Filename_for_find_and_replace.tar.gz"; + + params["findReplace.find"] = "FIND_"; + params["findReplace.caseInsensitive"] = true; + QTest::newRow("Find & Replace no regex case insensitive") + << params << "Filename_for_and_replace.tar.gz"; + + params["findReplace.useRegex"] = true; + params["findReplace.caseInsensitive"] = false; + params["findReplace.find"] = "[^a-z]"; + QTest::newRow("Find & Replace regex, case sensitive. Remove all chars except a-z") + << params << "ilenameforfindandreplacetargz"; + + params["findReplace.useRegex"] = true; + params["findReplace.caseInsensitive"] = true; + params["findReplace.find"] = "[^a-z]"; + QTest::newRow("Find & Replace regex, case insensitive. Remove all non alpha chars") + << params << "Filenameforfindandreplacetargz"; +} + +void BatchRenameTypesTest::testFindReplace() +{ + QFETCH(RenameTypeInput, input); + QFETCH(QString, expected); + + QCOMPARE(KIO::BatchRenameTypes::findReplaceRenamer(input), expected); +} + +void BatchRenameTypesTest::testCaptureGroups_data() +{ + QTest::addColumn>("input"); + QTest::addColumn("expected"); + + RenameTypeInput params; + params["name"] = "myfavpodcast1x03-Episode Title(192kbps).mp3"; + params["newName.find"] = R"(.+(\d)x(\d+)-(.+?)\()"; + params["newName.replace"] = R"(Favourite Podcast S0\1E\2 - \3.mp3)"; + + QTest::addRow("Capture groups") << params << "Favourite Podcast S01E03 - Episode Title.mp3"; +} + +void BatchRenameTypesTest::testCaptureGroups() +{ + QFETCH(RenameTypeInput, input); + QFETCH(QString, expected); + + QCOMPARE(KIO::BatchRenameTypes::captureRegexGroups(input), expected); +} + +QTEST_MAIN(BatchRenameTypesTest) + +#include "batchrenametypestest.moc" diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -61,7 +61,12 @@ executablefileopendialog.cpp dndpopupmenuplugin.cpp kurifiltersearchprovideractions.cpp -) + rename/batchrenamedialog.cpp + rename/batchrenamedialogmodel_p.cpp + rename/batchrenametypes_p.cpp + rename/filenameutils_p.cpp + rename/batchrenamevar_p.h + rename/batchrenamevar_p.cpp) if (WIN32) list(APPEND kiowidgets_SRCS krun_win.cpp @@ -145,6 +150,7 @@ KShellCompletion KUrlCompletion KUriFilter + rename/BatchRenameDialog REQUIRED_HEADERS KIOWidgets_HEADERS ) diff --git a/src/widgets/rename/batchrenamedialog.h b/src/widgets/rename/batchrenamedialog.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialog.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef BatchRenameDialog_H +#define BatchRenameDialog_H + +#include "kiowidgets_export.h" +#include "batchrenamedialogmodel_p.h" +#include "batchrenametypes_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace KIO +{ + +class KIOWIDGETS_EXPORT BatchRenameDialog : public QDialog +{ + Q_OBJECT + +public: + BatchRenameDialog ( QWidget *parent, const KFileItemList &items ); + +Q_SIGNALS: + void renamingFinished ( const QList &urls ); + +private: + void onEditValueChanged ( const QString &value ); + void btnOkClicked(); + void updateStatus ( const QString &message, KMessageWidget::MessageType messageType = KMessageWidget::Information ); + void performPreviewRename ( RenamerFunction *callback ); + void performItemsRename(); + void renameItem ( const KFileItem &item, const QString &newName ); + void updatePreview(); + void createInsertTokenMenu(); + void updateCapturedGroupsLabel(); + + QAction *createInsertTokenAction ( const QString &text, const QString &token ); + QLineEdit *createLineEdit(); + + QList> m_itemsToBeRenamed; + QList m_renamedItems; + + KMessageWidget *m_messageWidget; + QTimer *m_timer; + QPushButton *m_btnOk; + BatchRenameDialogModel *model; + QTableView *m_previewTable; + QTabWidget *m_tabs; + + QMenu *m_insertTokenMenu; + QLineEdit *m_macroReplaceEdit; + QSpinBox *m_IdStartFrom; + QSpinBox *m_IdPaddingLength; + + QLineEdit *m_findEdit; + QLineEdit *m_replaceEdit; + QCheckBox *m_useRegex; + QCheckBox *m_caseInsensitive; + + QLineEdit *m_patternFind; + QLineEdit *m_newNameFromMatches; + QLabel *m_capturedGroupsText; +}; +} + +#endif diff --git a/src/widgets/rename/batchrenamedialog.cpp b/src/widgets/rename/batchrenamedialog.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialog.cpp @@ -0,0 +1,376 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "batchrenamedialog.h" +#include "filenameutils_p.h" +#include "batchrenamevar_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KIO +{ + +BatchRenameDialog::BatchRenameDialog(QWidget *parent, const KFileItemList &items) : QDialog(parent) +{ + auto *mainLayout = new QVBoxLayout(); + setWindowTitle(i18nc("@title:window", "Rename Items")); + setMinimumWidth(500); + setLayout(mainLayout); + + model = new BatchRenameDialogModel(this, items); + + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + m_timer->setInterval(500); + connect(m_timer, &QTimer::timeout, this, &BatchRenameDialog::updatePreview); + + m_previewTable = new QTableView(); + m_previewTable->setModel(model); + m_previewTable->horizontalHeader()->setStretchLastSection(true); + m_previewTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_previewTable->verticalHeader()->hide(); + m_previewTable->setColumnHidden(BatchRenameDialogModel::Valid, true); + m_previewTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mainLayout->addWidget(m_previewTable); + + const auto valueChangedIntCallback = [this](int) { + onEditValueChanged(QString()); + }; + + // Create tab widget + m_tabs = new QTabWidget(this); + + QWidget *tabRenameSequential = new QWidget(m_tabs); + auto layoutRenameSequential = new QVBoxLayout; + layoutRenameSequential->setAlignment(Qt::AlignTop); + tabRenameSequential->setLayout(layoutRenameSequential); + m_tabs->addTab(tabRenameSequential, i18nc("@label", "Rename")); + + QWidget *tabRenameFindReplace = new QWidget(m_tabs); + m_tabs->addTab(tabRenameFindReplace, i18nc("@label", "Find && Replace")); + auto layoutRenameFindReplace = new QVBoxLayout; + layoutRenameFindReplace->setAlignment(Qt::AlignTop); + tabRenameFindReplace->setLayout(layoutRenameFindReplace); + + QWidget *tabNewNameFromMatches = new QWidget(m_tabs); + m_tabs->addTab(tabNewNameFromMatches, i18nc("@label", "Capture groups")); + auto layoutNewNameFromMatches = new QVBoxLayout; + layoutNewNameFromMatches->setAlignment(Qt::AlignTop); + tabNewNameFromMatches->setLayout(layoutNewNameFromMatches); + + mainLayout->addWidget(m_tabs); + + // Rename Sequential + m_macroReplaceEdit = createLineEdit(); + m_macroReplaceEdit->setText(i18nc("@label:button", "New Name {index}")); + + createInsertTokenMenu(); + auto btnInsertToken = new QPushButton(this); + btnInsertToken->setText(i18nc("@label", "Insert token")); + btnInsertToken->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); + connect(btnInsertToken, &QPushButton::clicked, [this]() { + m_insertTokenMenu->exec(QCursor::pos()); + }); + + auto layoutEditInsertToken = new QHBoxLayout(this); + layoutEditInsertToken->addWidget(m_macroReplaceEdit); + layoutEditInsertToken->addWidget(btnInsertToken); + + layoutRenameSequential->addWidget(new QLabel(i18ncp("@label:textbox", "Rename the %1 selected item to:", + "Rename the %1 selected items to:", items.count()), this)); + layoutRenameSequential->addLayout(layoutEditInsertToken); + + + m_IdStartFrom = new QSpinBox(this); + m_IdStartFrom->setKeyboardTracking(true); + connect(m_IdStartFrom, static_cast(&QSpinBox::valueChanged), valueChangedIntCallback); + m_IdStartFrom->setRange(1, 1000000); + + layoutRenameSequential->addWidget( + new QLabel(i18nc("@info", "{index} will be replaced by ascending numbers starting with:"), this)); + layoutRenameSequential->addWidget(m_IdStartFrom); + + m_IdPaddingLength = new QSpinBox(this); + m_IdPaddingLength->setKeyboardTracking(true); + connect(m_IdPaddingLength, static_cast(&QSpinBox::valueChanged), valueChangedIntCallback); + m_IdPaddingLength->setRange(1, 1000000); + + layoutRenameSequential->addWidget(new QLabel(i18nc("@info", "Pad numbers using a length of:"), this)); + layoutRenameSequential->addWidget(m_IdPaddingLength); + + // Find & Replace tab + m_findEdit = createLineEdit(); + m_replaceEdit = createLineEdit(); + m_useRegex = new QCheckBox(i18nc("@label", "Use Regular Expressions"), this); + connect(m_useRegex, &QCheckBox::stateChanged, valueChangedIntCallback); + m_caseInsensitive = new QCheckBox(i18nc("@label", "Case insensitive"), this); + connect(m_caseInsensitive, &QCheckBox::stateChanged, valueChangedIntCallback); + + layoutRenameFindReplace->addWidget(new QLabel(i18nc("@label", "Find:"), this)); + layoutRenameFindReplace->addWidget(m_findEdit); + + layoutRenameFindReplace->addWidget(new QLabel(i18nc("@label", "Replace:"), this)); + layoutRenameFindReplace->addWidget(m_replaceEdit); + + layoutRenameFindReplace->addItem(new QSpacerItem(0, 10)); + layoutRenameFindReplace->addWidget(m_useRegex); + layoutRenameFindReplace->addWidget(m_caseInsensitive); + + // New name from matches + m_patternFind = createLineEdit(); + m_newNameFromMatches = createLineEdit(); + + layoutNewNameFromMatches->addWidget(new QLabel(i18nc("@label", "Find patterns:"), this)); + layoutNewNameFromMatches->addWidget(m_patternFind); + + layoutNewNameFromMatches->addWidget(new QLabel(i18nc("@label", "New name:"), this)); + layoutNewNameFromMatches->addWidget(m_newNameFromMatches); + + m_capturedGroupsText = new QLabel(this); + m_capturedGroupsText->setWordWrap(true); + layoutNewNameFromMatches->addWidget(m_capturedGroupsText); + + m_messageWidget = new KMessageWidget(this); + m_messageWidget->setCloseButtonVisible(false); // hiding,showing of the message will be handled by this form. + m_messageWidget->hide(); + mainLayout->addWidget(m_messageWidget); + + // Button Bar + auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help); + m_btnOk = buttonBox->button(QDialogButtonBox::Ok); + + KGuiItem::assign(m_btnOk, KGuiItem(i18nc("@action:button", "&Rename"), QStringLiteral("dialog-ok-apply"))); + + m_btnOk->setEnabled(false); + connect(m_btnOk, &QPushButton::clicked, this, &BatchRenameDialog::btnOkClicked); + connect(buttonBox, &QDialogButtonBox::rejected, this, &BatchRenameDialog::reject); + + mainLayout->addWidget(buttonBox); + + m_macroReplaceEdit->setFocus(); + updatePreview(); + connect(m_tabs, &QTabWidget::currentChanged, valueChangedIntCallback); +} + +void BatchRenameDialog::performPreviewRename(RenamerFunction *callback) +{ + QStringList generatedNames; + QHash params; + bool allItemsOk = true; + m_itemsToBeRenamed.clear(); + + params.insert(QStringLiteral("timestamp"), QDateTime::currentDateTime()); + + params.insert(QStringLiteral("macroReplace.text"), m_macroReplaceEdit->text()); + params.insert(QStringLiteral("macroReplace.padding"), m_IdPaddingLength->value()); + + params.insert(QStringLiteral("findReplace.find"), m_findEdit->text()); + params.insert(QStringLiteral("findReplace.replace"), m_replaceEdit->text()); + params.insert(QStringLiteral("findReplace.useRegex"), m_useRegex->isChecked()); + params.insert(QStringLiteral("findReplace.caseInsensitive"), m_caseInsensitive->isChecked()); + + params.insert(QStringLiteral("newName.find"), m_patternFind->text()); + params.insert(QStringLiteral("newName.replace"), m_newNameFromMatches->text()); + + + for (int i = 0; i < model->rowCount(); i++) { + const QString itemName = model->data(model->index(i, BatchRenameDialogModel::Name), Qt::DisplayRole).toString(); + const KFileItem itemModel = model->fileItem(i); + const QDir itemDir(itemModel.url().adjusted(QUrl::RemoveFilename).adjusted(QUrl::RemoveScheme).toString()); + + params.insert(QStringLiteral("name"), itemName); + params.insert(QStringLiteral("baseName"), KIO::FileNameUtils::getName(itemName)); + params.insert(QStringLiteral("extension"), KIO::FileNameUtils::getSuffix(itemName)); + params.insert(QStringLiteral("macroReplace.sequence"), i + m_IdStartFrom->value()); + + const QString newName = KIO::encodeFileName(callback(params)); + + bool exists = (itemDir.exists(newName) || generatedNames.contains(newName)); + bool valid = (newName == itemName) || !(newName.isEmpty() || exists); + allItemsOk = allItemsOk && valid; + generatedNames.append(newName); + + if (newName != itemName && !exists) { + m_itemsToBeRenamed.append(QPair(itemModel, newName)); + } + + model->setData(model->index(i, BatchRenameDialogModel::Valid), valid, Qt::DisplayRole); + model->setData(model->index(i, BatchRenameDialogModel::NewName), newName, Qt::DisplayRole); + } + + const int itemsToBeRenamed = m_itemsToBeRenamed.length(); + m_btnOk->setEnabled(allItemsOk && itemsToBeRenamed > 0); + m_previewTable->repaint(); + + if (!allItemsOk) { + updateStatus(i18nc("@info:status", "Renaming cannot continue while there are file name clashes"), + KMessageWidget::Error); + } + + if (itemsToBeRenamed == 0) { + updateStatus(i18nc("@info:status", "No items to be renamed"), KMessageWidget::Information); + } + + if (allItemsOk && itemsToBeRenamed > 0) { + updateStatus(QString()); + } +} + +void BatchRenameDialog::renameItem(const KFileItem &item, const QString &newName) +{ + const QUrl newUrl = QUrl::fromLocalFile(item.url().adjusted(QUrl::RemoveFilename).path() + newName); + + auto *job = KIO::moveAs(item.url(), newUrl, KIO::HideProgressInfo); + KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {item.url()}, newUrl, job); + + if (!job->error()) { + m_renamedItems << newUrl; + } +} + +void BatchRenameDialog::performItemsRename() +{ + for (const auto &pair: qAsConst(m_itemsToBeRenamed)) { + renameItem(pair.first, pair.second); + } +} + +void BatchRenameDialog::updatePreview() +{ + switch (m_tabs->currentIndex()) { + case 1: + performPreviewRename(BatchRenameTypes::findReplaceRenamer); + break; + case 2: + performPreviewRename(BatchRenameTypes::captureRegexGroups); + updateCapturedGroupsLabel(); + break; + + default: + performPreviewRename(BatchRenameTypes::macroRenamer); + } +} + +void BatchRenameDialog::btnOkClicked() +{ + performItemsRename(); + emit renamingFinished(m_renamedItems); + accept(); +} + +void BatchRenameDialog::onEditValueChanged(const QString &value) +{ + Q_UNUSED(value); + + m_timer->start(); +} + +QLineEdit *BatchRenameDialog::createLineEdit() +{ + auto edit = new QLineEdit(this); + connect(edit, &QLineEdit::textChanged, this, &BatchRenameDialog::onEditValueChanged); + + return edit; +} + +void BatchRenameDialog::updateStatus(const QString &message, KMessageWidget::MessageType messageType) +{ + // no actual status update. + if (m_messageWidget->isVisible() && m_messageWidget->text() == message) { + return; + } + + // if the message is empty, hide the widget + if (message.isEmpty()) { + m_messageWidget->animatedHide(); + + return; + } + + m_messageWidget->setMessageType(messageType); + m_messageWidget->setText(message); + m_messageWidget->animatedShow(); +} + +QAction *BatchRenameDialog::createInsertTokenAction(const QString &text, const QString &token) +{ + auto result = new QAction(text, this); + connect(result, &QAction::triggered, [this, token]() { + m_macroReplaceEdit->setText(m_macroReplaceEdit->text() + token); + }); + + return result; +} + +void BatchRenameDialog::createInsertTokenMenu() +{ + m_insertTokenMenu = new QMenu(this); + + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Index"), BatchRenameVar::index())); + m_insertTokenMenu->addSeparator(); + + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "File name"), BatchRenameVar::fullName())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Base name"), BatchRenameVar::baseName())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Extension"), BatchRenameVar::suffix())); + m_insertTokenMenu->addSeparator(); + + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Year (4 digits)"), BatchRenameVar::dateYear4Digit())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Year (2 digits)"), BatchRenameVar::dateYear2Digit())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Month"), BatchRenameVar::dateMonth())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Day"), BatchRenameVar::dateDay())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Hour"), BatchRenameVar::dateHour())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Minute"), BatchRenameVar::dateMinute())); + m_insertTokenMenu->addAction(createInsertTokenAction(i18nc("@label", "Second"), BatchRenameVar::dateSecond())); +} + +void BatchRenameDialog::updateCapturedGroupsLabel() +{ + QString groups; + const auto capturedGroups = BatchRenameTypes::firstCapturedGroups(); + + for (int i = 0; i < capturedGroups.length(); i++) { + if (capturedGroups[i].isEmpty()) { + continue; + } + + groups.append(QStringLiteral("\\%1: %2
").arg(i).arg(capturedGroups[i])); + } + + m_capturedGroupsText->setText(QStringLiteral("%1:

%2") + .arg(i18nc("@label", "Captured groups from the first row")).arg(groups)); + BatchRenameTypes::clearCapturedGroups(); +} +} diff --git a/src/widgets/rename/batchrenamedialogmodel.h b/src/widgets/rename/batchrenamedialogmodel.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialogmodel.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_BATCHRENAMEDIALOGMODEL +#define KIO_BATCHRENAMEDIALOGMODEL + +#include +#include "KFileItem" + +namespace KIO +{ + +struct BatchRenameDialogModelData +{ + KFileItem item; + QString newName; + bool valid; +}; + + +class BatchRenameDialogModel : public QAbstractTableModel +{ + Q_OBJECT + +private: + QList *itemData; + +public: + enum DataColumn + { + Name = 0, + NewName = 1, + Valid = 2, + }; + + explicit BatchRenameDialogModel(QObject *parent, const KFileItemList &items); + ~BatchRenameDialogModel() override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + KFileItem fileItem(int index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; +}; +} + +#endif diff --git a/src/widgets/rename/batchrenamedialogmodel.cpp b/src/widgets/rename/batchrenamedialogmodel.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialogmodel.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include "batchrenamedialogmodel.h" + +namespace KIO +{ + +int BatchRenameDialogModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + if (parent.isValid()) { + return 0; + } + + return itemData->count(); +} + +int BatchRenameDialogModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 3; +} + +QVariant BatchRenameDialogModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (!(role == Qt::DisplayRole && orientation == Qt::Horizontal)) { + return QVariant(); + } + + switch (section) { + case 0: + return i18nc("@label", "Name"); + case 1: + return i18nc("@label", "New name"); + + default: + return QVariant(); + } +} + +QVariant BatchRenameDialogModel::data(const QModelIndex &index, int role) const +{ + const auto item = itemData->at(index.row()); + + if (role == Qt::DecorationRole && index.column() == BatchRenameDialogModel::NewName) { + return QIcon::fromTheme(item.valid ? QStringLiteral("dialog-ok-apply") : QStringLiteral("dialog-close")); + } + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case BatchRenameDialogModel::Name: + return item.item.url().fileName(); + + case BatchRenameDialogModel::NewName: + return item.newName.isEmpty() ? i18nc("@label", "(Empty name)") : item.newName; + + case BatchRenameDialogModel::Valid: + return item.valid; + + default: + return QVariant(); + } + } + + return QVariant(); +} + +BatchRenameDialogModel::BatchRenameDialogModel(QObject *parent, const KFileItemList &items) : QAbstractTableModel(parent) +{ + itemData = new QList; + + for (const KFileItem &item: items) { + itemData->append(BatchRenameDialogModelData {item, QString(), true}); + } +} + +bool BatchRenameDialogModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::DisplayRole) { + return false; + } + + switch (index.column()) { + case 1: + (*itemData)[index.row()].newName = value.toString(); + break; + case 2: + (*itemData)[index.row()].valid = value.toBool(); + break; + + default: + return false; + } + + emit dataChanged(index, index); + return true; +} + +KFileItem BatchRenameDialogModel::fileItem(int index) const +{ + return itemData->at(index).item; +} + +BatchRenameDialogModel::~BatchRenameDialogModel() +{ + delete itemData; +} +} diff --git a/src/widgets/rename/batchrenamedialogmodel_p.h b/src/widgets/rename/batchrenamedialogmodel_p.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialogmodel_p.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_BATCHRENAMEDIALOGMODEL +#define KIO_BATCHRENAMEDIALOGMODEL + +#include +#include "KFileItem" + +namespace KIO +{ + +struct BatchRenameDialogModelData +{ + KFileItem item; + QString newName; + bool valid; +}; + + +class BatchRenameDialogModel : public QAbstractTableModel +{ + Q_OBJECT + +private: + QList *itemData; + +public: + enum DataColumn + { + Name = 0, + NewName = 1, + Valid = 2, + }; + + explicit BatchRenameDialogModel(QObject *parent, const KFileItemList &items); + ~BatchRenameDialogModel() override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + KFileItem fileItem(int index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; +}; +} + +#endif diff --git a/src/widgets/rename/batchrenamedialogmodel_p.cpp b/src/widgets/rename/batchrenamedialogmodel_p.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamedialogmodel_p.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include "batchrenamedialogmodel_p.h" + +namespace KIO +{ + +int BatchRenameDialogModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + if (parent.isValid()) { + return 0; + } + + return itemData->count(); +} + +int BatchRenameDialogModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 3; +} + +QVariant BatchRenameDialogModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (!(role == Qt::DisplayRole && orientation == Qt::Horizontal)) { + return QVariant(); + } + + switch (section) { + case 0: + return i18nc("@label", "Name"); + case 1: + return i18nc("@label", "New name"); + + default: + return QVariant(); + } +} + +QVariant BatchRenameDialogModel::data(const QModelIndex &index, int role) const +{ + const auto item = itemData->at(index.row()); + + if (role == Qt::DecorationRole && index.column() == BatchRenameDialogModel::NewName) { + return QIcon::fromTheme(item.valid ? QStringLiteral("dialog-ok-apply") : QStringLiteral("dialog-close")); + } + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case BatchRenameDialogModel::Name: + return item.item.url().fileName(); + + case BatchRenameDialogModel::NewName: + return item.newName.isEmpty() ? i18nc("@label", "(Empty name)") : item.newName; + + case BatchRenameDialogModel::Valid: + return item.valid; + + default: + return QVariant(); + } + } + + return QVariant(); +} + +BatchRenameDialogModel::BatchRenameDialogModel(QObject *parent, const KFileItemList &items) : QAbstractTableModel(parent) +{ + itemData = new QList; + + for (const KFileItem &item: items) { + itemData->append(BatchRenameDialogModelData {item, QString(), true}); + } +} + +bool BatchRenameDialogModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::DisplayRole) { + return false; + } + + switch (index.column()) { + case 1: + (*itemData)[index.row()].newName = value.toString(); + break; + case 2: + (*itemData)[index.row()].valid = value.toBool(); + break; + + default: + return false; + } + + emit dataChanged(index, index); + return true; +} + +KFileItem BatchRenameDialogModel::fileItem(int index) const +{ + return itemData->at(index).item; +} + +BatchRenameDialogModel::~BatchRenameDialogModel() +{ + delete itemData; +} +} diff --git a/src/widgets/rename/batchrenametypes.h b/src/widgets/rename/batchrenametypes.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenametypes.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_RENAMETYPES +#define KIO_RENAMETYPES + +#include "kiowidgets_export.h" +#include +#include + +namespace KIO +{ + +typedef QString RenamerFunction(const QHash &); + +class KIOWIDGETS_EXPORT BatchRenameTypes +{ +private: + static QStringList capturedGroups; +public: + static QStringList firstCapturedGroups(); + static void clearCapturedGroups(); + + static QString macroRenamer(const QHash ¶ms); + static QString findReplaceRenamer(const QHash ¶ms); + static QString captureRegexGroups(const QHash ¶ms); +}; +} + +#endif diff --git a/src/widgets/rename/batchrenametypes.cpp b/src/widgets/rename/batchrenametypes.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenametypes.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "batchrenametypes.h" + +#include +#include +#include +#include +#include +#include +#include "batchrenamevar.h" + +namespace KIO +{ + +QString BatchRenameTypes::macroRenamer(const QHash ¶ms) +{ + QString sequenceFormatted = params[QStringLiteral("macroReplace.sequence")] + .toString().rightJustified(params[QStringLiteral("macroReplace.padding")].toInt(), QLatin1Char('0')); + const QDateTime timestamp = params[QStringLiteral("timestamp")].toDateTime(); + + return params["macroReplace.text"].toString() + .replace(BatchRenameVar::dateYear4Digit(), timestamp.toString(QStringLiteral("yyyy"))) + .replace(BatchRenameVar::dateYear2Digit(), timestamp.toString(QStringLiteral("yy"))) + .replace(BatchRenameVar::dateMonth(), timestamp.toString(QStringLiteral("MM"))) + .replace(BatchRenameVar::dateDay(), timestamp.toString(QStringLiteral("dd"))) + .replace(BatchRenameVar::dateHour(), timestamp.toString(QStringLiteral("hh"))) + .replace(BatchRenameVar::dateMinute(), timestamp.toString(QStringLiteral("mm"))) + .replace(BatchRenameVar::dateSecond(), timestamp.toString(QStringLiteral("ss"))) + .replace(BatchRenameVar::fullName(), params[QStringLiteral("name")].toString()) + .replace(BatchRenameVar::baseName(), params[QStringLiteral("baseName")].toString()) + .replace(BatchRenameVar::suffix(), params[QStringLiteral("extension")].toString()) + .replace(BatchRenameVar::index(), sequenceFormatted); +} + +QString BatchRenameTypes::findReplaceRenamer(const QHash ¶ms) +{ + const QString find = params[QStringLiteral("findReplace.find")].toString(); + const QString replace = params[QStringLiteral("findReplace.replace")].toString(); + const bool caseInsensitive = params[QStringLiteral("findReplace.caseInsensitive")].toBool(); + + if (!params[QStringLiteral("findReplace.useRegex")].toBool()) { + return params[QStringLiteral("name")].toString().replace( + find, replace, caseInsensitive ? Qt::CaseInsensitive : Qt::CaseSensitive); + } + + auto opt = caseInsensitive + ? QRegularExpression::CaseInsensitiveOption + : QRegularExpression::NoPatternOption; + return params[QStringLiteral("name")].toString().replace(QRegularExpression(find, opt), replace); +} + +QString BatchRenameTypes::captureRegexGroups(const QHash ¶ms) +{ + const auto find = params[QStringLiteral("newName.find")].toString(); + auto replace = params[QStringLiteral("newName.replace")].toString(); + + QRegularExpression re(QStringLiteral("(\\\\(\\d))")); + QRegularExpressionMatchIterator match = re.globalMatch(replace); + + QRegularExpression exp(find); + QRegularExpressionMatch findMatches = exp.match(params[QStringLiteral("name")].toString()); + + if (capturedGroups.isEmpty()) { + capturedGroups = findMatches.capturedTexts(); + } + + while (match.hasNext()) { + auto m = match.next(); + auto num = m.captured(2); + + replace = replace.replace(QStringLiteral("\\") + num, findMatches.captured(num.toInt())); + } + + return replace; +} + +QStringList BatchRenameTypes::firstCapturedGroups() +{ + return capturedGroups; +} + +void BatchRenameTypes::clearCapturedGroups() +{ + capturedGroups.clear(); +} + +QStringList BatchRenameTypes::capturedGroups; + +} diff --git a/src/widgets/rename/batchrenametypes_p.h b/src/widgets/rename/batchrenametypes_p.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenametypes_p.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_RENAMETYPES +#define KIO_RENAMETYPES + +#include "kiowidgets_export.h" +#include +#include + +namespace KIO +{ + +typedef QString RenamerFunction(const QHash &); + +class KIOWIDGETS_EXPORT BatchRenameTypes +{ +private: + static QStringList capturedGroups; +public: + static QStringList firstCapturedGroups(); + static void clearCapturedGroups(); + + static QString macroRenamer(const QHash ¶ms); + static QString findReplaceRenamer(const QHash ¶ms); + static QString captureRegexGroups(const QHash ¶ms); +}; +} + +#endif diff --git a/src/widgets/rename/batchrenametypes_p.cpp b/src/widgets/rename/batchrenametypes_p.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenametypes_p.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "batchrenametypes_p.h" + +#include +#include +#include +#include +#include +#include +#include "batchrenamevar_p.h" + +namespace KIO +{ + +QString BatchRenameTypes::macroRenamer(const QHash ¶ms) +{ + QString sequenceFormatted = params[QStringLiteral("macroReplace.sequence")] + .toString().rightJustified(params[QStringLiteral("macroReplace.padding")].toInt(), QLatin1Char('0')); + const QDateTime timestamp = params[QStringLiteral("timestamp")].toDateTime(); + + return params["macroReplace.text"].toString() + .replace(BatchRenameVar::dateYear4Digit(), timestamp.toString(QStringLiteral("yyyy"))) + .replace(BatchRenameVar::dateYear2Digit(), timestamp.toString(QStringLiteral("yy"))) + .replace(BatchRenameVar::dateMonth(), timestamp.toString(QStringLiteral("MM"))) + .replace(BatchRenameVar::dateDay(), timestamp.toString(QStringLiteral("dd"))) + .replace(BatchRenameVar::dateHour(), timestamp.toString(QStringLiteral("hh"))) + .replace(BatchRenameVar::dateMinute(), timestamp.toString(QStringLiteral("mm"))) + .replace(BatchRenameVar::dateSecond(), timestamp.toString(QStringLiteral("ss"))) + .replace(BatchRenameVar::fullName(), params[QStringLiteral("name")].toString()) + .replace(BatchRenameVar::baseName(), params[QStringLiteral("baseName")].toString()) + .replace(BatchRenameVar::suffix(), params[QStringLiteral("extension")].toString()) + .replace(BatchRenameVar::index(), sequenceFormatted); +} + +QString BatchRenameTypes::findReplaceRenamer(const QHash ¶ms) +{ + const QString find = params[QStringLiteral("findReplace.find")].toString(); + const QString replace = params[QStringLiteral("findReplace.replace")].toString(); + const bool caseInsensitive = params[QStringLiteral("findReplace.caseInsensitive")].toBool(); + + if (!params[QStringLiteral("findReplace.useRegex")].toBool()) { + return params[QStringLiteral("name")].toString().replace( + find, replace, caseInsensitive ? Qt::CaseInsensitive : Qt::CaseSensitive); + } + + auto opt = caseInsensitive + ? QRegularExpression::CaseInsensitiveOption + : QRegularExpression::NoPatternOption; + return params[QStringLiteral("name")].toString().replace(QRegularExpression(find, opt), replace); +} + +QString BatchRenameTypes::captureRegexGroups(const QHash ¶ms) +{ + const auto find = params[QStringLiteral("newName.find")].toString(); + auto replace = params[QStringLiteral("newName.replace")].toString(); + + QRegularExpression re(QStringLiteral("(\\\\(\\d))")); + QRegularExpressionMatchIterator match = re.globalMatch(replace); + + QRegularExpression exp(find); + QRegularExpressionMatch findMatches = exp.match(params[QStringLiteral("name")].toString()); + + if (capturedGroups.isEmpty()) { + capturedGroups = findMatches.capturedTexts(); + } + + while (match.hasNext()) { + auto m = match.next(); + auto num = m.captured(2); + + replace = replace.replace(QStringLiteral("\\") + num, findMatches.captured(num.toInt())); + } + + return replace; +} + +QStringList BatchRenameTypes::firstCapturedGroups() +{ + return capturedGroups; +} + +void BatchRenameTypes::clearCapturedGroups() +{ + capturedGroups.clear(); +} + +QStringList BatchRenameTypes::capturedGroups; + +} diff --git a/src/widgets/rename/batchrenamevar.h b/src/widgets/rename/batchrenamevar.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamevar.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + +#ifndef KIO_BATCHRENAMEVAR_H +#define KIO_BATCHRENAMEVAR_H + + +#include +#include + +namespace BatchRenameVar +{ + +QString index(); +QString baseName(); +QString fullName(); +QString suffix(); + +QString dateYear4Digit(); +QString dateYear2Digit(); +QString dateMonth(); +QString dateDay(); +QString dateHour(); +QString dateMinute(); +QString dateSecond(); + +} +#endif diff --git a/src/widgets/rename/batchrenamevar.cpp b/src/widgets/rename/batchrenamevar.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamevar.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + + +#include +#include +#include "batchrenamevar.h" + +namespace BatchRenameVar +{ + +QString index() +{ + return QStringLiteral("{index}"); +} + +QString baseName() +{ + return QStringLiteral("{baseName}"); +} + + +QString fullName() +{ + return QStringLiteral("{name}"); +} + +QString suffix() +{ + return QStringLiteral("{suffix}"); +} + +QString dateYear4Digit() +{ + return QStringLiteral("%Y"); +} + +QString dateYear2Digit() +{ + return QStringLiteral("%y"); +} + +QString dateMonth() +{ + return QStringLiteral("%M"); +} + +QString dateDay() +{ + return QStringLiteral("%D"); +} + +QString dateHour() +{ + return QStringLiteral("%H"); +} + +QString dateMinute() +{ + return QStringLiteral("%m"); +} + +QString dateSecond() +{ + return QStringLiteral("%S"); +} +} diff --git a/src/widgets/rename/batchrenamevar_p.h b/src/widgets/rename/batchrenamevar_p.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamevar_p.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + +#ifndef KIO_BATCHRENAMEVAR_H +#define KIO_BATCHRENAMEVAR_H + + +#include +#include + +namespace BatchRenameVar +{ + +QString index(); +QString baseName(); +QString fullName(); +QString suffix(); + +QString dateYear4Digit(); +QString dateYear2Digit(); +QString dateMonth(); +QString dateDay(); +QString dateHour(); +QString dateMinute(); +QString dateSecond(); + +} +#endif diff --git a/src/widgets/rename/batchrenamevar_p.cpp b/src/widgets/rename/batchrenamevar_p.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/batchrenamevar_p.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + + +#include +#include +#include "batchrenamevar_p.h" + +namespace BatchRenameVar +{ + +QString index() +{ + return QStringLiteral("{index}"); +} + +QString baseName() +{ + return QStringLiteral("{baseName}"); +} + + +QString fullName() +{ + return QStringLiteral("{name}"); +} + +QString suffix() +{ + return QStringLiteral("{suffix}"); +} + +QString dateYear4Digit() +{ + return QStringLiteral("%Y"); +} + +QString dateYear2Digit() +{ + return QStringLiteral("%y"); +} + +QString dateMonth() +{ + return QStringLiteral("%M"); +} + +QString dateDay() +{ + return QStringLiteral("%D"); +} + +QString dateHour() +{ + return QStringLiteral("%H"); +} + +QString dateMinute() +{ + return QStringLiteral("%m"); +} + +QString dateSecond() +{ + return QStringLiteral("%S"); +} +} diff --git a/src/widgets/rename/filenameutils.h b/src/widgets/rename/filenameutils.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/filenameutils.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_FILENAMEUTILS +#define KIO_FILENAMEUTILS + +#include "kiowidgets_export.h" +#include + +namespace KIO +{ +namespace FileNameUtils +{ + +/** + * Returns the extension of a file. If file is not a known type, it will return only the last suffix. + * (file.unknown.ext will return .ext) + */ +QString getSuffix(const QString &file); + +QString getName(const QString &file); + +} +} +#endif diff --git a/src/widgets/rename/filenameutils.cpp b/src/widgets/rename/filenameutils.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/filenameutils.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include +#include "filenameutils.h" + +namespace KIO +{ +namespace FileNameUtils +{ + +QString getSuffix(const QString &file) +{ + QString extension = QMimeDatabase().suffixForFileName(file); + + if (!extension.isEmpty()) { + return extension.prepend(QLatin1Char('.')); + } + + QString suffix = QFileInfo(file).suffix(); + + if (!suffix.isEmpty()) { + suffix.prepend(QLatin1Char('.')); + } + + return suffix; +} + +QString getName(const QString &file) +{ + return file.left(file.length() - getSuffix(file).length()); +} + +} +} diff --git a/src/widgets/rename/filenameutils_p.h b/src/widgets/rename/filenameutils_p.h new file mode 100644 --- /dev/null +++ b/src/widgets/rename/filenameutils_p.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KIO_FILENAMEUTILS +#define KIO_FILENAMEUTILS + +#include "kiowidgets_export.h" +#include + +namespace KIO +{ +namespace FileNameUtils +{ + +/** + * Returns the extension of a file. If file is not a known type, it will return only the last suffix. + * (file.unknown.ext will return .ext) + */ +QString getSuffix(const QString &file); + +QString getName(const QString &file); + +} +} +#endif diff --git a/src/widgets/rename/filenameutils_p.cpp b/src/widgets/rename/filenameutils_p.cpp new file mode 100644 --- /dev/null +++ b/src/widgets/rename/filenameutils_p.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include +#include "filenameutils_p.h" + +namespace KIO +{ +namespace FileNameUtils +{ + +QString getSuffix(const QString &file) +{ + QString extension = QMimeDatabase().suffixForFileName(file); + + if (!extension.isEmpty()) { + return extension.prepend(QLatin1Char('.')); + } + + QString suffix = QFileInfo(file).suffix(); + + if (!suffix.isEmpty()) { + suffix.prepend(QLatin1Char('.')); + } + + return suffix; +} + +QString getName(const QString &file) +{ + return file.left(file.length() - getSuffix(file).length()); +} + +} +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ ksycocaupdatetest udsentrybenchmark kurlnavigatortest_gui + batchrenamedialogtest_gui kprotocolinfo_dumper kfilewidgettest_gui runapplication diff --git a/tests/batchrenamedialogtest_gui.cpp b/tests/batchrenamedialogtest_gui.cpp new file mode 100644 --- /dev/null +++ b/tests/batchrenamedialogtest_gui.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2018 by Emirald Mateli * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include "rename/batchrenamedialog.h" + +int main(int argc, char **argv) +{ + QApplication::setApplicationName(QStringLiteral("batchrenamedialogtest")); + QApplication app(argc, argv); + + KFileItem e1(QUrl::fromLocalFile("~/Podcast S01E01 - Hello")); + KFileItem e2(QUrl::fromLocalFile("~/Podcast S01E02 - World")); + + auto dlg = new KIO::BatchRenameDialog(nullptr, KFileItemList({e1, e2})); + dlg->show(); + + return QApplication::exec(); +} +