diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index ab2a875b..82f3f3b5 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,135 +1,136 @@ if(POLICY CMP0028) cmake_policy(SET CMP0028 OLD) endif() remove_definitions(-DQT_NO_CAST_FROM_ASCII) include(ECMAddTests) add_subdirectory(http) add_subdirectory(kcookiejar) find_package(Qt5Widgets REQUIRED) ########### unittests ############### find_package(Qt5Concurrent ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) ecm_add_tests( kacltest.cpp listdirtest.cpp kmountpointtest.cpp upurltest.cpp dataprotocoltest.cpp jobtest.cpp jobremotetest.cpp kfileitemtest.cpp kprotocolinfotest.cpp ktcpsockettest.cpp globaltest.cpp mkpathjobtest.cpp threadtest.cpp udsentrytest.cpp udsentry_benchmark.cpp deletejobtest.cpp urlutiltest.cpp + batchrenamejobtest.cpp NAME_PREFIX "kiocore-" LINK_LIBRARIES KF5::KIOCore KF5::I18n Qt5::Test Qt5::Network ) target_link_libraries(threadtest Qt5::Concurrent) ecm_add_test( http_jobtest.cpp httpserver_p.cpp TEST_NAME http_jobtest NAME_PREFIX "kiocore-" LINK_LIBRARIES KF5::KIOCore KF5::I18n Qt5::Test Qt5::Network ) if(UNIX) ecm_add_tests( klocalsockettest.cpp klocalsocketservertest.cpp NAME_PREFIX "kiocore-" LINK_LIBRARIES KF5::KIOCore KF5::I18n Qt5::Test Qt5::Network ) endif() if (TARGET KF5::KIOGui) ecm_add_tests( favicontest.cpp NAME_PREFIX "kiogui-" LINK_LIBRARIES KF5::KIOCore KF5::KIOGui Qt5::Test ) target_link_libraries(favicontest Qt5::Concurrent) endif() if (TARGET KF5::KIOWidgets) ecm_add_tests( clipboardupdatertest.cpp dropjobtest.cpp kdynamicjobtrackernowidgetstest.cpp krununittest.cpp kdirlistertest.cpp kdirmodeltest.cpp kfileitemactionstest.cpp fileundomanagertest.cpp kurifiltertest.cpp kurlcompletiontest.cpp jobguitest.cpp pastetest.cpp accessmanagertest.cpp kurifiltersearchprovideractionstest.cpp NAME_PREFIX "kiowidgets-" LINK_LIBRARIES KF5::KIOCore KF5::KIOWidgets Qt5::Test ) set_target_properties(krununittest PROPERTIES COMPILE_FLAGS "-DCMAKE_INSTALL_FULL_LIBEXECDIR_KF5=\"\\\"${CMAKE_INSTALL_FULL_LIBEXECDIR_KF5}\\\"\"") # Same as accessmanagertest, but using QNetworkAccessManager, to make sure we # behave the same ecm_add_test( accessmanagertest.cpp TEST_NAME accessmanagertest-qnam NAME_PREFIX "kiowidgets-" LINK_LIBRARIES KF5::KIOCore KF5::KIOWidgets Qt5::Test ) set_target_properties(accessmanagertest-qnam PROPERTIES COMPILE_FLAGS "-DUSE_QNAM") # Same as kurlcompletiontest, but with immediate return, and results posted by thread later ecm_add_test( kurlcompletiontest.cpp TEST_NAME kurlcompletiontest-nowait NAME_PREFIX "kiowidgets-" LINK_LIBRARIES KF5::KIOCore KF5::KIOWidgets Qt5::Test ) set_target_properties(kurlcompletiontest-nowait PROPERTIES COMPILE_FLAGS "-DNO_WAIT") endif() if (TARGET KF5::KIOFileWidgets) find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/src/filewidgets ${CMAKE_BINARY_DIR}/src/filewidgets) ecm_add_tests( kurlnavigatortest.cpp kurlcomboboxtest.cpp kdiroperatortest.cpp kfilewidgettest.cpp knewfilemenutest.cpp kfilecopytomenutest.cpp kfileplacesmodeltest.cpp kfileplacesviewtest.cpp kurlrequestertest.cpp NAME_PREFIX "kiofilewidgets-" LINK_LIBRARIES KF5::KIOFileWidgets KF5::KIOWidgets KF5::XmlGui KF5::Bookmarks Qt5::Test KF5::I18n ) set_tests_properties(kiofilewidgets-kfileplacesmodeltest PROPERTIES RUN_SERIAL TRUE) set_tests_properties(kiofilewidgets-kfileplacesviewtest PROPERTIES RUN_SERIAL TRUE) endif() # this should be done by cmake, see bug 371721 if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND Qt5Core_VERSION VERSION_GREATER 5.8.0) set_property(TARGET jobtest APPEND PROPERTY AUTOMOC_MOC_OPTIONS --include ${CMAKE_BINARY_DIR}/src/core/moc_predefs.h) endif() diff --git a/autotests/batchrenamejobtest.cpp b/autotests/batchrenamejobtest.cpp new file mode 100644 index 00000000..fc128863 --- /dev/null +++ b/autotests/batchrenamejobtest.cpp @@ -0,0 +1,147 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include +#include + +#include + +#include "kiotesthelper.h" + +class BatchRenameJobTest : public QObject +{ + Q_OBJECT + +private: + void createTestFiles(const QStringList &fileList) + { + foreach (const QString &filename, fileList) { + createTestFile(m_homeDir + filename); + } + } + + bool checkFileExistence(const QStringList &fileList) + { + foreach (const QString &filename, fileList) { + const QString filePath = m_homeDir + filename; + if (!QFile::exists(filePath)) { + return false; + } + } + return true; + } + + QList createUrlList(const QStringList &fileList) + { + QList srcList; + srcList.reserve(fileList.count()); + foreach (const QString &filename, fileList) { + const QString filePath = m_homeDir + filename; + srcList.append(QUrl::fromLocalFile(filePath)); + } + return srcList; + } + +private Q_SLOTS: + void initTestCase() + { + QStandardPaths::enableTestMode(true); + + // To avoid a runtime dependency on klauncher + qputenv("KDE_FORK_SLAVES", "yes"); + + cleanupTestCase(); + + // Create temporary home directory + m_homeDir = homeTmpDir(); + + } + + void cleanupTestCase() + { + QDir(homeTmpDir()).removeRecursively(); + } + + void batchRenameJobTest_data() + { + QTest::addColumn("oldFilenames"); + QTest::addColumn("baseName"); + QTest::addColumn("index"); + QTest::addColumn("indexPlaceholder"); + QTest::addColumn("newFilenames"); + + QTest::newRow("different-extensions-single-placeholder") << (QStringList() << "old_file_without_extension" + << "old_file.txt" + << "old_file.zip") + << "#-new_name" << 1 << QChar('#') + << (QStringList() << "1-new_name" + << "2-new_name.txt" + << "3-new_name.zip"); + + QTest::newRow("same-extensions-placeholder-sequence") << (QStringList() << "first_source.cpp" + << "second_source.cpp" + << "third_source.java") + << "new_source###" << 8 << QChar('#') + << (QStringList() << "new_source008.cpp" + << "new_source009.cpp" + << "new_source010.java"); + + QTest::newRow("different-extensions-invalid-placeholder") << (QStringList() << "audio.mp3" + << "video.mp4" + << "movie.mkv") + << "me#d#ia" << 0 << QChar('#') + << (QStringList() << "me#d#ia.mp3" + << "me#d#ia.mp4" + << "me#d#ia.mkv"); + + QTest::newRow("same-extensions-invalid-placeholder") << (QStringList() << "random_headerfile.h" + << "another_headerfile.h" + << "random_sourcefile.c") + << "##file#" << 4 << QChar('#') + << (QStringList() << "##file#4.h" + << "##file#5.h" + << "##file#6.c"); + + } + + void batchRenameJobTest() + { + QFETCH(QStringList, oldFilenames); + QFETCH(QString, baseName); + QFETCH(int, index); + QFETCH(QChar, indexPlaceholder); + QFETCH(QStringList, newFilenames); + createTestFiles(oldFilenames); + QVERIFY(checkFileExistence(oldFilenames)); + KIO::Job *job = KIO::batchRename(createUrlList(oldFilenames), baseName, index, indexPlaceholder); + job->setUiDelegate(nullptr); + QSignalSpy spy(job, SIGNAL(fileRenamed(QUrl, QUrl))); + QVERIFY2(job->exec(), qPrintable(job->errorString())); + QCOMPARE(spy.count(), oldFilenames.count()); + QVERIFY(!checkFileExistence(oldFilenames)); + QVERIFY(checkFileExistence(newFilenames)); + } + +private: + QString m_homeDir; +}; + +QTEST_MAIN(BatchRenameJobTest) + +#include "batchrenamejobtest.moc" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a3dc3bc7..4901609b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,287 +1,289 @@ project(KIOCore) include (ConfigureChecks.cmake) configure_file(config-kiocore.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiocore.h ) configure_file(config-kmountpoint.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kmountpoint.h) # KSSL_HAVE_SSL only used in kssl/ksslsettings.cpp, but currently ifdefed out #find_package(OpenSSL) #set_package_properties(OpenSSL PROPERTIES DESCRIPTION "Support for secure network communications (SSL and TLS)" # URL "http://openssl.org" # TYPE RECOMMENDED # PURPOSE "KDE uses OpenSSL for the bulk of secure communications, including secure web browsing via HTTPS" # ) #if(OPENSSL_FOUND) # set(KSSL_HAVE_SSL 1) # include_directories(${OPENSSL_INCLUDE_DIR}) #endif() set(kiocore_SRCS idleslave.cpp klocalsocket.cpp connectionbackend.cpp connection.cpp connectionserver.cpp krecentdocument.cpp kfileitemlistproperties.cpp tcpslavebase.cpp directorysizejob.cpp forwardingslavebase.cpp chmodjob.cpp kdiskfreespaceinfo.cpp usernotificationhandler.cpp ksambasharedata.cpp ksambashare.cpp knfsshare.cpp kfileitem.cpp davjob.cpp deletejob.cpp copyjob.cpp filejob.cpp mkdirjob.cpp mkpathjob.cpp kpasswdserverloop.cpp kpasswdserverclient.cpp kremoteencoding.cpp sessiondata.cpp slavebase.cpp dataslave.cpp dataprotocol.cpp desktopexecparser.cpp emptytrashjob.cpp authinfo.cpp slaveinterface.cpp slave.cpp job_error.cpp job.cpp filecopyjob.cpp listjob.cpp mimetypejob.cpp multigetjob.cpp restorejob.cpp simplejob.cpp specialjob.cpp statjob.cpp storedtransferjob.cpp transferjob.cpp filesystemfreespacejob.cpp scheduler.cpp slaveconfig.cpp kprotocolmanager.cpp hostinfo.cpp kdirnotify.cpp kurlauthorized.cpp kacl.cpp udsentry.cpp global.cpp metadata.cpp kprotocolinfo.cpp kprotocolinfofactory.cpp jobtracker.cpp jobuidelegateextension.cpp jobuidelegatefactory.cpp kmountpoint.cpp kcoredirlister.cpp faviconscache.cpp ksslcertificatemanager.cpp ktcpsocket.cpp kssl/ksslsettings.cpp kiocoredebug.cpp kioglobal_p.cpp + batchrenamejob.cpp ) if (UNIX) set(kiocore_SRCS ${kiocore_SRCS} klocalsocket_unix.cpp kioglobal_p_unix.cpp ) endif() if (WIN32) set(kiocore_SRCS ${kiocore_SRCS} klocalsocket_win.cpp kioglobal_p_win.cpp ) endif() qt5_add_dbus_interface(kiocore_SRCS org.kde.KSlaveLauncher.xml klauncher_interface) set_source_files_properties(org.kde.KPasswdServer.xml PROPERTIES INCLUDE authinfo.h ) qt5_add_dbus_interface(kiocore_SRCS org.kde.KPasswdServer.xml kpasswdserver_interface) install(FILES org.kde.KDirNotify.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KDirNotify.xml) install(FILES org.kde.KPasswdServer.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KPasswdServer.xml) install(FILES org.kde.KSlaveLauncher.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KSlaveLauncher.xml) add_library(KF5KIOCore ${kiocore_SRCS}) generate_export_header(KF5KIOCore BASE_NAME KIOCore) add_library(KF5::KIOCore ALIAS KF5KIOCore) target_include_directories(KF5KIOCore PUBLIC "$" # kio_version.h "$" ) target_include_directories(KF5KIOCore INTERFACE "$") target_link_libraries(KF5KIOCore PUBLIC KF5::CoreAddons # KJob KF5::Service PRIVATE Qt5::Network Qt5::Concurrent # QtConcurrentRun in hostinfo.cpp Qt5::Xml # davjob.cpp uses QDom Qt5::DBus KF5::I18n KF5::DBusAddons # KDEInitInterface ) if(ACL_FOUND) target_link_libraries(KF5KIOCore PRIVATE ${ACL_LIBS}) endif() set_target_properties(KF5KIOCore PROPERTIES VERSION ${KIO_VERSION_STRING} SOVERSION ${KIO_SOVERSION} EXPORT_NAME KIOCore ) # this should be done by cmake, see bug 371721 if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND Qt5Core_VERSION VERSION_GREATER 5.8.0) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h COMMAND "${CMAKE_CXX_COMPILER}" "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp" > ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h ) set_property(TARGET KF5KIOCore APPEND PROPERTY AUTOMOC_MOC_OPTIONS --include ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h) set_property(TARGET KF5KIOCore APPEND PROPERTY AUTOGEN_TARGET_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h) endif() # Headers prefixed with KIO/ ecm_generate_headers(KIOCore_CamelCase_HEADERS HEADER_NAMES IdleSlave ConnectionServer TCPSlaveBase DirectorySizeJob ForwardingSlaveBase Job # ### should forward to job_base.h, not job.h... JobTracker Global ChmodJob DeleteJob CopyJob EmptyTrashJob FileJob MkdirJob MkpathJob SlaveBase SlaveConfig HostInfo MetaData UDSEntry JobUiDelegateExtension JobUiDelegateFactory SlaveInterface Slave FileCopyJob ListJob MimetypeJob MultiGetJob RestoreJob SimpleJob SpecialJob StatJob StoredTransferJob TransferJob Scheduler AuthInfo DavJob DesktopExecParser FileSystemFreeSpaceJob + BatchRenameJob PREFIX KIO REQUIRED_HEADERS KIO_namespaced_HEADERS ) # Create local forwarding header for kio/job_base.h set(REGULAR_HEADER_NAME ${CMAKE_CURRENT_BINARY_DIR}/kio/job_base.h) if (NOT EXISTS ${REGULAR_HEADER_NAME}) file(WRITE ${REGULAR_HEADER_NAME} "#include \"${CMAKE_CURRENT_SOURCE_DIR}/job_base.h\"\n") endif() install(TARGETS KF5KIOCore EXPORT KF5KIOTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) list(APPEND KIO_namespaced_HEADERS http_slave_defaults.h ioslave_defaults.h job_base.h jobclasses.h ) install(FILES ${KIO_namespaced_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore/kio COMPONENT Devel ) # Headers not prefixed with KIO/ ecm_generate_headers(KIOCore_HEADERS HEADER_NAMES KACL KUrlAuthorized KCoreDirLister KRemoteEncoding KDirNotify KDiskFreeSpaceInfo KFileItem KFileItemListProperties KMountPoint KNFSShare KSambaShare KSambaShareData KPasswdServerClient KProtocolInfo KProtocolManager KRecentDocument KSslCertificateManager KTcpSocket REQUIRED_HEADERS KIOCore_HEADERS ) ecm_generate_headers(KIOCore_HEADERS HEADER_NAMES KSSLSettings RELATIVE kssl REQUIRED_HEADERS KIOCore_HEADERS ) install(FILES ${KIOCore_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore/KIO COMPONENT Devel) install(FILES ksslcertificatemanager_p.h ${KIOCore_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kiocore_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore COMPONENT Devel) install(FILES accept-languages.codes DESTINATION ${KDE_INSTALL_CONFDIR}) # make available to ecm_add_qch in parent folder set(KIOCore_QCH_SOURCES ${KIOCore_HEADERS} ${KIO_namespaced_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KIOCore LIB_NAME KF5KIOCore DEPS "KCoreAddons KService" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/core/batchrenamejob.cpp b/src/core/batchrenamejob.cpp new file mode 100644 index 00000000..92c73209 --- /dev/null +++ b/src/core/batchrenamejob.cpp @@ -0,0 +1,209 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "batchrenamejob.h" + +#include "job_p.h" +#include "copyjob.h" + +#include +#include +#include +#include + +using namespace KIO; + +class KIO::BatchRenameJobPrivate : public KIO::JobPrivate +{ +public: + BatchRenameJobPrivate(const QList &src, const QString &newName, + int index, QChar placeHolder, JobFlags flags) + : JobPrivate(), + m_srcList(src), + m_newName(newName), + m_index(index), + m_placeHolder(placeHolder), + m_listIterator(m_srcList.constBegin()), + m_allExtensionsDifferent(true), + m_useIndex(true), + m_appendIndex(false), + m_flags(flags) + { + // There occur four cases when renaming multiple files, + // 1. All files have different extension and $newName contains a valid placeholder. + // 2. At least two files have same extension and $newName contains a valid placeholder. + // In these two cases the placeholder character will be replaced by an integer($index). + // 3. All files have different extension and new name contains an invalid placeholder + // (this means either $newName doesn't contain the placeholder or the placeholders + // are not in a connected sequence). + // In this case nothing is substituted and all files have the same $newName. + // 4. At least two files have same extension and $newName contains an invalid placeholder. + // In this case $index is appended to $newName. + + + // Check for extensions. + QSet extensions; + QMimeDatabase db; + foreach (const QUrl &url, m_srcList) { + const QString extension = db.suffixForFileName(url.toDisplayString().toLower()); + if (extensions.contains(extension)) { + m_allExtensionsDifferent = false; + break; + } + + extensions.insert(extension); + } + + // Check for exactly one placeholder character or exactly one sequence of placeholders. + int pos = newName.indexOf(placeHolder); + if (pos != -1) { + while (pos < newName.size() && newName.at(pos) == placeHolder) { + pos++; + } + } + const bool validPlaceholder = (newName.indexOf(placeHolder, pos) == -1); + + if (!validPlaceholder) { + if (!m_allExtensionsDifferent) { + m_appendIndex = true; + } else { + m_useIndex = false; + } + } + } + + QList m_srcList; + QString m_newName; + int m_index; + QChar m_placeHolder; + QList::const_iterator m_listIterator; + bool m_allExtensionsDifferent; + bool m_useIndex; + bool m_appendIndex; + QUrl m_newUrl; // for fileRenamed signal + const JobFlags m_flags; + + Q_DECLARE_PUBLIC(BatchRenameJob) + + void slotStart(); + + QString indexedName(const QString& name, int index, QChar placeHolder) const; + + static inline BatchRenameJob *newJob(const QList &src, const QString &newName, + int index, QChar placeHolder, JobFlags flags) + { + BatchRenameJob *job = new BatchRenameJob(*new BatchRenameJobPrivate(src, newName, index, placeHolder, flags)); + job->setUiDelegate(KIO::createDefaultJobUiDelegate()); + if (!(flags & HideProgressInfo)) { + KIO::getJobTracker()->registerJob(job); + } + return job; + } + +}; + +BatchRenameJob::BatchRenameJob(BatchRenameJobPrivate &dd) + : Job(dd) +{ + QTimer::singleShot(0, this, SLOT(slotStart())); +} + +BatchRenameJob::~BatchRenameJob() +{ +} + +QString BatchRenameJobPrivate::indexedName(const QString& name, int index, QChar placeHolder) const +{ + if (!m_useIndex) { + return name; + } + + QString newName = name; + QString indexString = QString::number(index); + + if (m_appendIndex) { + newName.append(indexString); + return newName; + } + + // Insert leading zeros if necessary + const int minIndexLength = name.count(placeHolder); + indexString.prepend(QString(minIndexLength - indexString.length(), QLatin1Char('0'))); + + // Replace the index placeholders by the indexString + const int placeHolderStart = newName.indexOf(placeHolder); + newName.replace(placeHolderStart, minIndexLength, indexString); + + return newName; +} + +void BatchRenameJobPrivate::slotStart() +{ + Q_Q(BatchRenameJob); + + if (m_listIterator == m_srcList.constBegin()) { // emit total + q->setTotalAmount(KJob::Files, m_srcList.count()); + } + + if (m_listIterator != m_srcList.constEnd()) { + QString newName = indexedName(m_newName, m_index, m_placeHolder); + const QUrl oldUrl = *m_listIterator; + QMimeDatabase db; + const QString extension = db.suffixForFileName(oldUrl.path().toLower()); + if (!extension.isEmpty()) { + newName.append(QLatin1Char('.')); + newName.append(extension); + } + + m_newUrl = oldUrl.adjusted(QUrl::RemoveFilename); + m_newUrl.setPath(m_newUrl.path() + KIO::encodeFileName(newName)); + + KIO::Job * job = KIO::moveAs(oldUrl, m_newUrl, KIO::HideProgressInfo); + q->addSubjob(job); + q->setProcessedAmount(KJob::Files, q->processedAmount(KJob::Files) + 1); + } else { + q->emitResult(); + } +} + +void BatchRenameJob::slotResult(KJob *job) +{ + Q_D(BatchRenameJob); + if (job->error()) { + KIO::Job::slotResult(job); + return; + } + + removeSubjob(job); + + emit fileRenamed(*d->m_listIterator, d->m_newUrl); + ++d->m_listIterator; + ++d->m_index; + emitPercent(d->m_listIterator - d->m_srcList.constBegin(), d->m_srcList.count()); + d->slotStart(); +} + +BatchRenameJob * KIO::batchRename(const QList &src, const QString &newName, + int index, QChar placeHolder, KIO::JobFlags flags) +{ + return BatchRenameJobPrivate::newJob(src, newName, index, placeHolder, flags); +} + + +#include "moc_batchrenamejob.cpp" diff --git a/src/core/batchrenamejob.h b/src/core/batchrenamejob.h new file mode 100644 index 00000000..48d1596d --- /dev/null +++ b/src/core/batchrenamejob.h @@ -0,0 +1,89 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef BATCHRENAMEJOB_H +#define BATCHRENAMEJOB_H + +#include "kiocore_export.h" +#include "job_base.h" + +namespace KIO +{ + +class BatchRenameJobPrivate; + +/** + * @class KIO::BatchRenameJob batchrenamejob.h + * + * A KIO job that renames multiple files in one go. + * + * @since 5.42 + */ +class KIOCORE_EXPORT BatchRenameJob : public Job +{ + Q_OBJECT + +public: + virtual ~BatchRenameJob(); + +Q_SIGNALS: + /** + * Signals that a file was renamed. + */ + void fileRenamed(const QUrl &oldUrl, const QUrl &newUrl); + +protected Q_SLOTS: + void slotResult(KJob *job) Q_DECL_OVERRIDE; + +protected: + /// @internal + BatchRenameJob(BatchRenameJobPrivate &dd); + +private: + Q_PRIVATE_SLOT(d_func(), void slotStart()) + Q_DECLARE_PRIVATE(BatchRenameJob) +}; + +/** + * Renames multiple files at once. + * + * The new filename is obtained by replacing the characters represented by + * @p placeHolder by the index @p index. + * E.g. Calling batchRename({"file:///Test.jpg"}, "Test #" 12, '#') renames + * the file to "Test 12.jpg". A connected sequence of placeholders results in + * leading zeros. batchRename({"file:///Test.jpg"}, "Test ####" 12, '#') renames + * the file to "Test 0012.jpg". And if no placeholder is there then @p index is + * appended to @p newName. Calling batchRename({"file:///Test.jpg"}, "NewTest" 12, '#') + * renames the file to "NewTest12.jpg". + * + * @param src The list of items to rename. + * @param newName The base name to use in all new filenames. + * @param index The integer(incremented after renaming a file) to add to the base name. + * @param placeHolder The character(s) which @p index will replace. + * + * @return A pointer to the job handling the operation. + * @since 5.42 + */ +KIOCORE_EXPORT BatchRenameJob *batchRename(const QList &src, const QString &newName, + int index, QChar placeHolder, + JobFlags flags = DefaultFlags); + +} + +#endif