├── pics └── QRenamer.rc ├── installer ├── genInstaller.bat ├── packages │ └── main.component │ │ └── meta │ │ ├── package.xml │ │ ├── LGPL_EXCEPTION.txt │ │ └── installscript.qs ├── config │ └── config.xml └── readme.txt ├── js.qrc ├── .gitignore ├── js └── globalConstant.js ├── qml ├── BackButton.qml ├── filter │ ├── RemoveFilter.qml │ ├── Exclude.qml │ └── Include.qml ├── ButtonDelegate.qml ├── Miscellaneous.qml ├── Main.qml ├── FilterList.qml ├── qml.qrc ├── methods │ ├── AppendWords.qml │ ├── PrependWords.qml │ ├── ReplaceWords.qml │ ├── ReplaceSuffix.qml │ ├── RemoveWords.qml │ ├── InsertWords.qml │ ├── PrependZeroOnNum.qml │ ├── RegexGlobalModifier.qml │ ├── RegexUsingCaptures.qml │ └── RegexNumbering.qml ├── miscellaneous │ ├── SpanHeader.qml │ └── AddFolderRecursive.qml ├── ListStackView.qml ├── MethodsList.qml ├── TutorialSteps.qml ├── RainingParticle.qml └── StepLabel.qml ├── utility ├── initializer.cpp ├── quickCloseSignal.cpp ├── initializer.hpp ├── quickCloseSignal.hpp ├── stringUtility.hpp └── stringUtility.cpp ├── globalconst.hpp ├── readme.txt ├── main.cpp ├── help_pics.qrc ├── ui ├── numberStep.hpp ├── filterDialog.hpp ├── numberStep.cpp ├── filterDialog.cpp ├── numberstep.ui ├── MainWindow.hpp ├── filterDialog.ui ├── MainWindow.cpp └── MainWindow.ui ├── model ├── fileModelItem.hpp ├── fileModelItem.cpp ├── fileNameModel.hpp └── fileNameModel.cpp ├── workerThreads ├── addDirThread.hpp ├── scanDirThread.cpp ├── addDirThread.cpp ├── scanDirThread.hpp ├── renameFileThread.hpp └── renameFileThread.cpp ├── modelProxy ├── sortAndFilterFile.hpp └── sortAndFilterFile.cpp ├── LGPL_EXCEPTION.txt ├── QRenamer.pro └── pics.qrc /pics/QRenamer.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "macro_names.ico" -------------------------------------------------------------------------------- /installer/genInstaller.bat: -------------------------------------------------------------------------------- 1 | binarycreator --offline-only -c config/config.xml -p packages QRenamer -------------------------------------------------------------------------------- /js.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | js/globalConstant.js 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.autosave 3 | *.user 4 | Makefile 5 | *.Debug 6 | *.Release 7 | *.5pre1 8 | *.pro.user 9 | pics 10 | -------------------------------------------------------------------------------- /js/globalConstant.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | var global_const = { 4 | 5 | list_view_foot_height: function(){ 6 | return 40; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /qml/BackButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | 4 | Item { 5 | width: parent.width 6 | height :parent.height 7 | 8 | Button { 9 | anchors.fill: parent 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /utility/initializer.cpp: -------------------------------------------------------------------------------- 1 | #include "initializer.hpp" 2 | 3 | #include 4 | #include 5 | 6 | std::vector initialize_vector_index(int size) 7 | { 8 | std::vector result(size); 9 | std::iota(std::begin(result), std::end(result), 0); 10 | 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /globalconst.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GLOBALCONST_HPP 2 | #define GLOBALCONST_HPP 3 | 4 | enum class fileNameHeader{ 5 | Path, OldName, NewName, 6 | OldSuffix, NewSuffix, 7 | LastElem 8 | }; 9 | 10 | enum class filterType{ 11 | Exclude, Include 12 | }; 13 | 14 | enum class RenameType{ 15 | Base, Suffix, BaseAndSuffix 16 | }; 17 | 18 | #endif // GLOBALCONST_HPP 19 | 20 | -------------------------------------------------------------------------------- /utility/quickCloseSignal.cpp: -------------------------------------------------------------------------------- 1 | #include "quickCloseSignal.hpp" 2 | 3 | quickCloseSignal::quickCloseSignal(QWidget *parent) : 4 | QQuickWidget(parent) 5 | { 6 | 7 | } 8 | 9 | quickCloseSignal::~quickCloseSignal() 10 | { 11 | 12 | } 13 | 14 | void quickCloseSignal::closeEvent(QCloseEvent *event) 15 | { 16 | event->accept(); 17 | emit widget_close(); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /utility/initializer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INITIALIZER_HPP 2 | #define INITIALIZER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | std::vector generate_vector_index(int size, T initial_value = {}) 11 | { 12 | std::vector result(size); 13 | std::iota(std::begin(result), std::end(result), initial_value); 14 | 15 | return result; 16 | } 17 | 18 | #endif // INITIALIZER_HPP 19 | -------------------------------------------------------------------------------- /utility/quickCloseSignal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QUICKCLOSESIGNAL_H 2 | #define QUICKCLOSESIGNAL_H 3 | 4 | #include 5 | 6 | class quickCloseSignal : public QQuickWidget 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit quickCloseSignal(QWidget *parent = nullptr); 11 | ~quickCloseSignal(); 12 | 13 | signals: 14 | void widget_close(); 15 | 16 | protected: 17 | void closeEvent(QCloseEvent *event) override; 18 | }; 19 | 20 | #endif // QUICKCLOSESIGNAL_H 21 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | This app is not for sale or commercial purpose 2 | 3 | All of the icons are come from 4 | http://www.fatcow.com/free-icons 5 | 6 | You can download the icons use by this app from 7 | https://mega.nz/#!UslFiYQJ!-eNluUhjH5S2XBlpep5RdZZ8aGYJwqgWDBXvxh5dmOA 8 | and place it in the pics folder, this way the Qt resource 9 | system should be able to find the pics 10 | 11 | All of the icons are not for commercial use, please follow the license of http://www.fatcow.com/free-icons -------------------------------------------------------------------------------- /installer/packages/main.component/meta/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Main component 4 | Install Main component 5 | 1.0.0 6 | 2015-05-28 7 | 8 | 9 | 10 | true 11 | 12 | true 13 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication a(argc, argv); 11 | 12 | try{ 13 | MainWindow w; 14 | w.show(); 15 | 16 | return a.exec(); 17 | }catch(std::exception const &ex){ 18 | QMessageBox::critical(nullptr, QObject::tr("Error"), 19 | QObject::tr(ex.what())); 20 | 21 | return -1; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /help_pics.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | helps/pics/methods/appendWords00.png 4 | helps/pics/methods/selectFolder.png 5 | helps/pics/methods/appendWords01.png 6 | helps/pics/methods/renameFiles.png 7 | helps/pics/methods/prependWords00.png 8 | helps/pics/methods/prependWords01.png 9 | helps/pics/methods/replaceWords00.png 10 | helps/pics/methods/replaceWords01.png 11 | 12 | 13 | -------------------------------------------------------------------------------- /installer/config/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | QRenamer 4 | 1.0.0 5 | QRenamer 6 | Tham Ngap Wei 7 | 8 | macro_names 9 | macro_names 10 | QRenamer 11 | @HomeDir@/QRenamer 12 | maintenanceTool 13 | -------------------------------------------------------------------------------- /installer/readme.txt: -------------------------------------------------------------------------------- 1 | How to generate the installer for windows : 2 | 3 | 1 : download the pics from the mega link(https://mega.nz/#!UslFiYQJ!-eNluUhjH5S2XBlpep5RdZZ8aGYJwqgWDBXvxh5dmOA) 4 | unzip it and place the icon "macro_names.ico" under the folder "installer/config" 5 | 6 | 2 : Install the Qt installer(version 2.0 or above) 7 | 8 | 3 : setup the environment path of Qt installer(ex : C:\Qt\QtIFW2.0.1\bin) 9 | 10 | 4 : create a "data" folder under "installer/packages/main.component" 11 | 12 | 5 : copy the QRenamer.exe under the folder "data" 13 | 14 | 6 : open command prompt and enter the command "windeployqt QRenamer.exe"(make sure you setup the path before) 15 | 16 | 7 : double click the "genInstaller.bat" under the folder "installer" 17 | 18 | 8 : done -------------------------------------------------------------------------------- /ui/numberStep.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NUMBERSTEP_HPP 2 | #define NUMBERSTEP_HPP 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class numberStep; 8 | } 9 | 10 | class numberStep : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit numberStep(QWidget *parent = 0); 16 | ~numberStep(); 17 | 18 | int increment() const Q_DECL_NOEXCEPT; 19 | QString pad() const Q_DECL_NOEXCEPT; 20 | int reset() const Q_DECL_NOEXCEPT; 21 | int start() const Q_DECL_NOEXCEPT; 22 | 23 | signals: 24 | void ok_button_clicked(); 25 | 26 | private slots: 27 | void on_cancelPushButton_clicked(); 28 | 29 | void on_okPushButton_clicked(); 30 | 31 | void on_numPadLineEdit_textEdited(const QString &arg1); 32 | 33 | private: 34 | Ui::numberStep *ui; 35 | }; 36 | 37 | #endif // NUMBERSTEP_HPP 38 | -------------------------------------------------------------------------------- /model/fileModelItem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEMODELITEM_HPP 2 | #define FILEMODELITEM_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class fileModelItem 10 | { 11 | public: 12 | fileModelItem(); 13 | ~fileModelItem(); 14 | 15 | void add_new_file(); 16 | void add_new_file(QString const &input); 17 | 18 | void remove_at(int row); 19 | 20 | QStringList dir_paths_; 21 | QStringList insert_file_name_; 22 | QStringList new_base_name_; 23 | QStringList new_suffix_; 24 | QStringList old_file_name_complete_; 25 | QStringList old_suffix_; 26 | QStringList old_base_name_; 27 | 28 | std::vector source_indexes_; 29 | QVector match_regex_file_; 30 | }; 31 | 32 | #endif // FILEMODELITEM_HPP 33 | -------------------------------------------------------------------------------- /workerThreads/addDirThread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADDDIRTHREAD_HPP 2 | #define ADDDIRTHREAD_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class addDirThread : public QThread 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit addDirThread(QObject *parent = nullptr); 15 | addDirThread(addDirThread const&) = delete; 16 | addDirThread& operator=(addDirThread const&) = delete; 17 | 18 | QStringList get_old_file_name_complete() const; 19 | 20 | void set_dirs_to_scan(QStringList dirs); 21 | 22 | signals: 23 | void end(); 24 | void increment(int value); 25 | 26 | private: 27 | void run() override; 28 | 29 | private: 30 | QStringList old_file_name_complete_; 31 | 32 | QStringList dirs_to_scan_; 33 | }; 34 | 35 | #endif // ADDDIRTHREAD_HPP 36 | -------------------------------------------------------------------------------- /qml/filter/RemoveFilter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: filter_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the Remove filter icons"; 18 | img: "../pics/filter/removeFitler00.png" 19 | img_details: "
    20 |
  • The filter you apply before will be removed
  • 21 |
  • You cannot press on this icon if you have not apply any filter
  • 22 |
  • You can press the short cut \"F6\" to remove the filter too
  • 23 |
"} 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /qml/ButtonDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Item { 4 | id: root 5 | width: parent.width 6 | height: 88 7 | 8 | property alias text: textitem.text 9 | signal clicked 10 | 11 | Rectangle { 12 | anchors.fill: parent 13 | color: "#11ffffff" 14 | visible: mouse.pressed 15 | } 16 | 17 | Text { 18 | id: textitem 19 | color: "white" 20 | font.pixelSize: 32 21 | text: modelData 22 | anchors.verticalCenter: parent.verticalCenter 23 | anchors.left: parent.left 24 | anchors.leftMargin: 30 25 | } 26 | 27 | Rectangle { 28 | anchors.left: parent.left 29 | anchors.right: parent.right 30 | anchors.margins: 15 31 | height: 1 32 | color: "#424246" 33 | } 34 | 35 | MouseArea { 36 | id: mouse 37 | anchors.fill: parent 38 | onClicked: root.clicked() 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ui/filterDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILTERDIALOG_HPP 2 | #define FILTERDIALOG_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class filterDialog; 9 | } 10 | 11 | enum class fileNameHeader; 12 | enum class filterType; 13 | 14 | class filterDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit filterDialog(QWidget *parent = nullptr); 20 | ~filterDialog(); 21 | 22 | fileNameHeader get_filter_column() const Q_DECL_NOEXCEPT; 23 | QString get_filter_string() const Q_DECL_NOEXCEPT; 24 | filterType get_filter_type() const Q_DECL_NOEXCEPT; 25 | QRegularExpression::PatternOption get_filter_pattern() const Q_DECL_NOEXCEPT; 26 | 27 | signals: 28 | void ok(); 29 | 30 | private slots: 31 | void on_cancelPushButton_clicked(); 32 | 33 | void on_okPushButton_clicked(); 34 | 35 | private: 36 | Ui::filterDialog *ui; 37 | }; 38 | 39 | #endif // FILTERDIALOG_HPP 40 | -------------------------------------------------------------------------------- /workerThreads/scanDirThread.cpp: -------------------------------------------------------------------------------- 1 | #include "scanDirThread.hpp" 2 | 3 | #include 4 | 5 | scanDirThread::scanDirThread(QObject *parent) : 6 | QThread(parent) 7 | { 8 | } 9 | 10 | QStringList scanDirThread::get_result_dirs() const Q_DECL_NOEXCEPT 11 | { 12 | return result_dirs_; 13 | } 14 | 15 | /** 16 | * @brief setup the starting dir to scan 17 | * @param dir the starting dir to scan 18 | */ 19 | void scanDirThread::set_scan_dir(const QString dir) Q_DECL_NOEXCEPT 20 | { 21 | dir_ = dir; 22 | } 23 | 24 | void scanDirThread::run() 25 | { 26 | QDirIterator directories(dir_, 27 | QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); 28 | QStringList dirs; 29 | while(directories.hasNext()){ 30 | directories.next(); 31 | dirs.push_back(directories.filePath()); 32 | emit scan_dir(directories.filePath(), 2000); 33 | } 34 | 35 | result_dirs_ = dirs; 36 | emit end(); 37 | } 38 | -------------------------------------------------------------------------------- /modelProxy/sortAndFilterFile.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SORTANDFILTERFILE_HPP 2 | #define SORTANDFILTERFILE_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | enum class fileNameHeader; 9 | enum class filterType; 10 | 11 | class QRegularExpression; 12 | 13 | class sortAndFilterFile : public QSortFilterProxyModel 14 | { 15 | public: 16 | explicit sortAndFilterFile(QObject *parent = nullptr); 17 | ~sortAndFilterFile(); 18 | 19 | void set_filter_column(fileNameHeader header, bool invalidate = true); 20 | void set_filter_type(filterType type, bool invalidate = true); 21 | void set_regex(QRegularExpression reg, bool invalidate = true); 22 | 23 | protected: 24 | bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; 25 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; 26 | 27 | private: 28 | struct pimpl; 29 | std::unique_ptr impl_; 30 | }; 31 | 32 | #endif // SORTANDFILTERFILE_HPP 33 | -------------------------------------------------------------------------------- /workerThreads/addDirThread.cpp: -------------------------------------------------------------------------------- 1 | #include "addDirThread.hpp" 2 | 3 | #include 4 | 5 | addDirThread::addDirThread(QObject *parent) : 6 | QThread(parent) 7 | { 8 | } 9 | 10 | QStringList addDirThread::get_old_file_name_complete() const 11 | { 12 | return old_file_name_complete_; 13 | } 14 | 15 | void addDirThread::set_dirs_to_scan(const QStringList dirs) 16 | { 17 | dirs_to_scan_ = dirs; 18 | } 19 | 20 | void addDirThread::run() 21 | { 22 | QStringList old_file_name_complete; 23 | for(int i = 0, dir_size = dirs_to_scan_.size(); i != dir_size; ++i){ 24 | QDir directory(dirs_to_scan_[i]); 25 | auto const file_infos = directory.entryInfoList({}, QDir::Files); 26 | for(int j = 0, file_size = file_infos.size(); j != file_size; ++j){ 27 | old_file_name_complete.push_back(file_infos[j].absoluteFilePath()); 28 | } 29 | emit increment(i); 30 | } 31 | 32 | old_file_name_complete_ = old_file_name_complete; 33 | emit end(); 34 | } 35 | -------------------------------------------------------------------------------- /workerThreads/scanDirThread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SCANDIRTHREAD_HPP 2 | #define SCANDIRTHREAD_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class scanDirThread : public QThread 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit scanDirThread(QObject *parent = nullptr); 15 | scanDirThread(scanDirThread const&) = delete; 16 | scanDirThread& operator=(scanDirThread const&) = delete; 17 | 18 | QStringList get_result_dirs() const Q_DECL_NOEXCEPT; 19 | 20 | void set_scan_dir(QString dir) Q_DECL_NOEXCEPT; 21 | 22 | signals: 23 | /** 24 | * @brief scan_dir this signal will emit when dir was scanned 25 | * @param dir scanned directory 26 | * @param msec The longest time(milli seconds) to show the message 27 | */ 28 | void scan_dir(QString dir, int msec); 29 | void end(); 30 | 31 | private: 32 | void run() override; 33 | 34 | private: 35 | QString dir_; 36 | QStringList result_dirs_; 37 | }; 38 | 39 | #endif // SCANDIRTHREAD_HPP 40 | -------------------------------------------------------------------------------- /qml/Miscellaneous.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | import QtQuick.Layouts 1.1 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | gradient: Gradient { 10 | GradientStop { position: 0.0; color: "#f5baee" } 11 | GradientStop { position: 1.0; color: "#db44ca" } 12 | } 13 | 14 | ListModel { 15 | id: miscellaneous_page_model 16 | ListElement { 17 | title: "Add folder recursive" 18 | page: "qrc:/miscellaneous/AddFolderRecursive.qml" 19 | } 20 | ListElement { 21 | title: "Span header" 22 | page: "qrc:/miscellaneous/SpanHeader.qml" 23 | } 24 | ListElement { 25 | title: "Back" 26 | page: "qrc:/BackButton.qml" 27 | } 28 | } 29 | 30 | ListStackView{ 31 | id: miscellaneous_stack_view 32 | anchors.fill: parent 33 | list_view_model: miscellaneous_page_model 34 | parent_stack_view: menu_stack_view 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /qml/Main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | 4 | Rectangle{ 5 | width: 640 6 | height: 480 7 | 8 | gradient: Gradient { 9 | GradientStop { position: 0.0; color: "lightblue" } 10 | GradientStop { position: 1.0; color: "blue" } 11 | } 12 | 13 | ListModel { 14 | id: page_model 15 | ListElement { 16 | title: "Methods" 17 | page: "qrc:/MethodsList.qml" 18 | } 19 | ListElement { 20 | title: "Filter" 21 | page: "qrc:/FilterList.qml" 22 | } 23 | ListElement { 24 | title: "Miscellaneous" 25 | page: "qrc:/Miscellaneous.qml" 26 | } 27 | } 28 | 29 | ListStackView{ 30 | id: menu_stack_view 31 | anchors.fill: parent 32 | list_view_model: page_model 33 | focus: true 34 | should_disable_raining_particle: false 35 | } 36 | 37 | RainingParticle{ 38 | id: raining_particle 39 | anchors.fill: parent 40 | particle_source: "../pics/bullet_star.png" 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /qml/FilterList.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | import QtQuick.Layouts 1.1 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | gradient: Gradient { 10 | GradientStop { position: 0.0; color: "tomato" } 11 | GradientStop { position: 1.0; color: "coral" } 12 | } 13 | 14 | ListModel { 15 | id: filter_page_model 16 | ListElement { 17 | title: "Exclude" 18 | page: "qrc:/filter/Exclude.qml" 19 | } 20 | ListElement { 21 | title: "Include" 22 | page: "qrc:/filter/Include.qml" 23 | } 24 | ListElement { 25 | title: "Remove filter" 26 | page: "qrc:/filter/RemoveFilter.qml" 27 | } 28 | ListElement { 29 | title: "Back" 30 | page: "qrc:/BackButton.qml" 31 | } 32 | } 33 | 34 | ListStackView{ 35 | id: filter_stack_view 36 | anchors.fill: parent 37 | list_view_model: filter_page_model 38 | parent_stack_view: menu_stack_view 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /model/fileModelItem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fileModelItem.hpp" 4 | 5 | fileModelItem::fileModelItem() 6 | { 7 | 8 | } 9 | 10 | fileModelItem::~fileModelItem() 11 | { 12 | 13 | } 14 | 15 | void fileModelItem::add_new_file() 16 | { 17 | dir_paths_.push_back(""); 18 | old_suffix_.push_back(""); 19 | new_suffix_.push_back(""); 20 | old_base_name_.push_back(""); 21 | new_base_name_.push_back(""); 22 | old_file_name_complete_.push_back(""); 23 | } 24 | 25 | void fileModelItem::add_new_file(const QString &input) 26 | { 27 | QFileInfo const info(input); 28 | dir_paths_.push_back(info.absolutePath()); 29 | old_suffix_.push_back(info.suffix()); 30 | new_suffix_.push_back(old_suffix_.back()); 31 | old_base_name_.push_back(info.completeBaseName()); 32 | new_base_name_.push_back(old_base_name_.back()); 33 | old_file_name_complete_.push_back(input); 34 | } 35 | 36 | void fileModelItem::remove_at(int row) 37 | { 38 | dir_paths_.removeAt(row); 39 | old_suffix_.removeAt(row); 40 | new_suffix_.removeAt(row); 41 | new_base_name_.removeAt(row); 42 | old_base_name_.removeAt(row); 43 | old_file_name_complete_.removeAt(row); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /qml/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Main.qml 4 | MethodsList.qml 5 | ButtonDelegate.qml 6 | methods/AppendWords.qml 7 | BackButton.qml 8 | StepLabel.qml 9 | methods/PrependWords.qml 10 | methods/ReplaceWords.qml 11 | RainingParticle.qml 12 | methods/ReplaceSuffix.qml 13 | methods/RemoveWords.qml 14 | methods/InsertWords.qml 15 | methods/PrependZeroOnNum.qml 16 | methods/RegexUsingCaptures.qml 17 | methods/RegexNumbering.qml 18 | methods/RegexGlobalModifier.qml 19 | TutorialSteps.qml 20 | FilterList.qml 21 | ListStackView.qml 22 | filter/Exclude.qml 23 | filter/Include.qml 24 | filter/RemoveFilter.qml 25 | Miscellaneous.qml 26 | miscellaneous/AddFolderRecursive.qml 27 | miscellaneous/SpanHeader.qml 28 | 29 | 30 | -------------------------------------------------------------------------------- /LGPL_EXCEPTION.txt: -------------------------------------------------------------------------------- 1 | Digia Qt LGPL Exception version 1.1 2 | 3 | As an additional permission to the GNU Lesser General Public License version 4 | 2.1, the object code form of a "work that uses the Library" may incorporate 5 | material from a header file that is part of the Library. You may distribute 6 | such object code under terms of your choice, provided that: 7 | (i) the header files of the Library have not been modified; and 8 | (ii) the incorporated material is limited to numerical parameters, data 9 | structure layouts, accessors, macros, inline functions and 10 | templates; and 11 | (iii) you comply with the terms of Section 6 of the GNU Lesser General 12 | Public License version 2.1. 13 | 14 | Moreover, you may apply this exception to a modified version of the Library, 15 | provided that such modification does not involve copying material from the 16 | Library into the modified Library's header files unless such material is 17 | limited to (i) numerical parameters; (ii) data structure layouts; 18 | (iii) accessors; and (iv) small macros, templates and inline functions of 19 | five lines or less in length. 20 | 21 | Furthermore, you are not required to apply this additional permission to a 22 | modified version of the Library. 23 | -------------------------------------------------------------------------------- /qml/methods/AppendWords.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Append words"; 19 | img: "../pics/methods/appendWords00.png" 20 | img_details: "This tag allow users to append words."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : Append the words"; 25 | img: "../pics/methods/appendWords01.png" 26 | img_details: "Enter the words you want to append"} 27 | ListElement { help:"Step 4 : Rename the Files"; 28 | img: "../pics/methods/renameFiles.png" 29 | img_details: "Click on the ok button to rename the files, if anything wrong,
30 | you can always revert the change with the revert button"} 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /qml/methods/PrependWords.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Prepend words"; 19 | img: "../pics/methods/prependWords00.png" 20 | img_details: "This tag allow users to prepend words."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : prepend the words"; 25 | img: "../pics/methods/prependWords01.png" 26 | img_details: "Enter the words you want to prepend"} 27 | ListElement { help:"Step 4 : Rename the Files"; 28 | img: "../pics/methods/renameFiles.png" 29 | img_details: "Click on the ok button to rename the files, if anything wrong,
30 | you can always revert the change with the revert button"} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /installer/packages/main.component/meta/LGPL_EXCEPTION.txt: -------------------------------------------------------------------------------- 1 | Digia Qt LGPL Exception version 1.1 2 | 3 | As an additional permission to the GNU Lesser General Public License version 4 | 2.1, the object code form of a "work that uses the Library" may incorporate 5 | material from a header file that is part of the Library. You may distribute 6 | such object code under terms of your choice, provided that: 7 | (i) the header files of the Library have not been modified; and 8 | (ii) the incorporated material is limited to numerical parameters, data 9 | structure layouts, accessors, macros, inline functions and 10 | templates; and 11 | (iii) you comply with the terms of Section 6 of the GNU Lesser General 12 | Public License version 2.1. 13 | 14 | Moreover, you may apply this exception to a modified version of the Library, 15 | provided that such modification does not involve copying material from the 16 | Library into the modified Library's header files unless such material is 17 | limited to (i) numerical parameters; (ii) data structure layouts; 18 | (iii) accessors; and (iv) small macros, templates and inline functions of 19 | five lines or less in length. 20 | 21 | Furthermore, you are not required to apply this additional permission to a 22 | modified version of the Library. 23 | -------------------------------------------------------------------------------- /qml/methods/ReplaceWords.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Replace words"; 19 | img: "../pics/methods/replaceWords00.png" 20 | img_details: "This tag allow users to replace words."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : Replace the words"; 25 | img: "../pics/methods/replaceWords01.png" 26 | img_details: "Enter the words you want to replace"} 27 | ListElement { help:"Step 4 : Rename the Files"; 28 | img: "../pics/methods/renameFiles.png" 29 | img_details: "Click on the ok button to rename the files, if anything wrong,
30 | you can always revert the change with the revert button"} 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /qml/methods/ReplaceSuffix.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Replace suffix"; 19 | img: "../pics/methods/replaceSuffix00.png" 20 | img_details: "This tag allow users to replace suffix of files."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : Rename the suffix"; 25 | img: "../pics/methods/replaceSuffix01.png" 26 | img_details: "Enter the new suffix you want to replace"} 27 | ListElement { help:"Step 4 : Rename the Files"; 28 | img: "../pics/methods/renameFiles.png" 29 | img_details: "Click on the ok button to rename the files, if anything wrong,
30 | you can always revert the change with the revert button"} 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /utility/stringUtility.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRINGUTILITY_HPP 2 | #define STRINGUTILITY_HPP 3 | 4 | #include 5 | #include 6 | 7 | class QRegularExpression; 8 | 9 | QString prepend_symbol_on_match(QString const &old_file_name, 10 | int number_length, 11 | QRegularExpression const ®, 12 | QChar symbol); 13 | 14 | QStringList prepend_symbol_on_match(QStringList const &input, 15 | int number_length, 16 | QRegularExpression const ®, 17 | QChar symbol); 18 | 19 | QString prepend_symbol_on_nth_match(QString const &input, 20 | int number_length, 21 | int nth, 22 | QRegularExpression const ®, 23 | QChar symbol); 24 | 25 | QStringList prepend_symbol_on_nth_match(QStringList const &input, 26 | int number_length, 27 | int nth, 28 | QRegularExpression const ®, 29 | QChar symbol); 30 | 31 | #endif // STRINGUTILITY_HPP 32 | -------------------------------------------------------------------------------- /qml/filter/Exclude.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: filter_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Filter files"; 18 | img: "../pics/filter/filterSample.png" 19 | img_details: "Say you want to rename part of the files in the folder"} 20 | 21 | ListElement { help:"Step 1 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder"} 24 | 25 | ListElement { help:"Step 2 : Open filter"; 26 | img: "../pics/filter/filterSelect.png" 27 | img_details: "Open the filter dialog"} 28 | 29 | ListElement { help:"Step 3 : Exclude files"; 30 | img: "../pics/filter/filterExclude00.png" 31 | img_details: "Enter the key words you want to exclude(support regular expression)\n 32 | and select the Exclude circle"} 33 | 34 | ListElement { help:"Step 4 : Click ok"; 35 | img: "../pics/filter/filterExclude01.png" 36 | img_details: "Click on the ok button to exclude the files,you can\n 37 | press on F6 to remove the filter."} 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /qml/filter/Include.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: filter_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Filter files"; 18 | img: "../pics/filter/filterSample.png" 19 | img_details: "Say you want to rename part of the files in the folder"} 20 | 21 | ListElement { help:"Step 1 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder"} 24 | 25 | ListElement { help:"Step 2 : Open filter"; 26 | img: "../pics/filter/filterSelect.png" 27 | img_details: "Open the filter dialog"} 28 | 29 | ListElement { help:"Step 3 : Include files"; 30 | img: "../pics/filter/filterInclude00.png" 31 | img_details: "Enter the key words you want to include(support regular expression)\n 32 | and select the Include circle"} 33 | 34 | ListElement { help:"Step 4 : Click ok"; 35 | img: "../pics/filter/filterInclude01.png" 36 | img_details: "Click on the ok button to include the files,you can\n 37 | press on F6 to remove the filter."} 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /qml/miscellaneous/SpanHeader.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: miscellaneous_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"See the full contents of the Original name 18 | and New Name"; 19 | img: "../pics/miscellaneous/spanHeader00.png" 20 | img_details: "Say the Original name and 21 | New name are too long,
22 | you can not see the whole text, you can use the span function to adjust the space"} 23 | 24 | ListElement { help:"Step 1 : Press the Span header icon"; 25 | img: "../pics/miscellaneous/spanHeader01.png" 26 | img_details: "There are three ways to adjust the space 27 |
    28 |
  • Click on the Span header icon
  • 29 |
  • Press the shortcut button \"Ctrl+S\"
  • 30 |
  • Drag on the header
  • 31 |
"} 32 | 33 | ListElement { help:"Step 2 : Result after span"; 34 | img: "../pics/miscellaneous/spanHeader02.png" 35 | img_details: "You can see the space of the headers are long enough to show the contents"} 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /qml/miscellaneous/AddFolderRecursive.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: miscellaneous_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Adding a lot of folders"; 18 | img: "../pics/miscellaneous/addRecursiveFolder00.png" 19 | img_details: "Say you have a lot of folders want to add, it will be tedious 20 | if you have to add them one by one.
21 | If the folders have subfolders, things could go from bad to worse"} 22 | 23 | ListElement { help:"Step 1 : Press the add recursive folder icon"; 24 | img: "../pics/miscellaneous/addRecursiveFolder01.png" 25 | img_details: "You can add the folder recursive by clicking on the recursive icons
26 | or press the shortcut \"F3\""} 27 | 28 | ListElement { help:"Step 2 : Add the folders recursive"; 29 | img: "../pics/miscellaneous/addRecursiveFolder02.png" 30 | img_details: "Go into the folder you want to add recursively,
31 | then press the button Select Folder"} 32 | 33 | ListElement { help:"Step 3 : The results"; 34 | img: "../pics/miscellaneous/addRecursiveFolder03.png" 35 | img_details: "All of the files under the folder will be added"} 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /qml/methods/RemoveWords.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Remove words"; 19 | img: "../pics/methods/removeWords00.png" 20 | img_details: "This tag allow users to remove words."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : Select the position and range"; 25 | img: "../pics/methods/removeWords01.png" 26 | img_details: "Enter the position you want to start to remove the words,
27 | then select the range(how many characters you want to remove).
28 | Example : if the RemoveAt is 0, range is 5, then the file [shirobako_001] will become [bako_001].
29 | If the RemoveAt is 1, range is 3, then the file [shirobako_001] will become [sobako_001]"} 30 | ListElement { help:"Step 4 : Rename the Files"; 31 | img: "../pics/methods/renameFiles.png" 32 | img_details: "Click on the ok button to rename the files, if anything wrong,
33 | you can always revert the change with the revert button"} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /QRenamer.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-01-13T18:58:57 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += concurrent core gui quickwidgets quick 8 | 9 | CONFIG += c++17 10 | 11 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 12 | 13 | TARGET = QRenamer 14 | TEMPLATE = app 15 | 16 | win32{ 17 | RC_FILE = pics/QRenamer.rc 18 | } 19 | 20 | #Release:DESTDIR = ../../Qt_apps 21 | INCLUDEPATH += ui 22 | 23 | SOURCES += main.cpp\ 24 | ui/MainWindow.cpp \ 25 | utility/stringUtility.cpp \ 26 | workerThreads/renameFileThread.cpp \ 27 | workerThreads/addDirThread.cpp \ 28 | workerThreads/scanDirThread.cpp \ 29 | model/fileNameModel.cpp \ 30 | modelProxy/sortAndFilterFile.cpp \ 31 | ui/filterDialog.cpp \ 32 | ui/numberStep.cpp \ 33 | utility/quickCloseSignal.cpp \ 34 | model/fileModelItem.cpp 35 | 36 | HEADERS += ui/MainWindow.hpp \ 37 | utility/stringUtility.hpp \ 38 | workerThreads/renameFileThread.hpp \ 39 | workerThreads/addDirThread.hpp \ 40 | workerThreads/scanDirThread.hpp \ 41 | model/fileNameModel.hpp \ 42 | utility/initializer.hpp \ 43 | modelProxy/sortAndFilterFile.hpp \ 44 | ui/filterDialog.hpp \ 45 | globalconst.hpp \ 46 | ui/numberStep.hpp \ 47 | utility/quickCloseSignal.hpp \ 48 | model/fileModelItem.hpp 49 | 50 | FORMS += ui/MainWindow.ui \ 51 | ui/filterDialog.ui \ 52 | ui/numberstep.ui 53 | 54 | RESOURCES += \ 55 | pics.qrc \ 56 | qml/qml.qrc \ 57 | js.qrc 58 | -------------------------------------------------------------------------------- /qml/methods/InsertWords.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Insert words"; 19 | img: "../pics/methods/insertWords00.png" 20 | img_details: "This tag allow users to insert words."} 21 | ListElement { help:"Step 2 : Select the folder"; 22 | img: "../pics/methods/selectFolder.png" 23 | img_details: "This step will load the files in the folder."} 24 | ListElement { help:"Step 3 : Select the position to insert word"; 25 | img: "../pics/methods/insertWords01.png" 26 | img_details: "Enter the position you want to insert the words" 27 | } 28 | ListElement { help:"Step 4 : Enter the words want to insert"; 29 | img: "../pics/methods/insertWords02.png" 30 | img_details: "Enter the words you want to insert.
31 | Example : Remove At is 3, Insert Words is CowBoy, then [shirobako_001] will
32 | become [shiCowBoyrobako_001]" 33 | } 34 | ListElement { help:"Step 5 : Rename the Files"; 35 | img: "../pics/methods/renameFiles.png" 36 | img_details: "Click on the ok button to rename the files, if anything wrong,
37 | you can always revert the change with the revert button"} 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ui/numberStep.cpp: -------------------------------------------------------------------------------- 1 | #include "numberStep.hpp" 2 | #include "ui_numberstep.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | numberStep::numberStep(QWidget *parent) : 10 | QDialog(parent), 11 | ui(new Ui::numberStep) 12 | { 13 | ui->setupUi(this); 14 | } 15 | 16 | numberStep::~numberStep() 17 | { 18 | delete ui; 19 | } 20 | 21 | int numberStep::increment() const Q_DECL_NOEXCEPT 22 | { 23 | return ui->numIncSpinBox->value(); 24 | } 25 | 26 | QString numberStep::pad() const Q_DECL_NOEXCEPT 27 | { 28 | return ui->numPadLineEdit->text(); 29 | } 30 | 31 | int numberStep::reset() const Q_DECL_NOEXCEPT 32 | { 33 | return ui->numResetSpinBox->value(); 34 | } 35 | 36 | int numberStep::start() const Q_DECL_NOEXCEPT 37 | { 38 | return ui->numStartSpinBox->value(); 39 | } 40 | 41 | void numberStep::on_cancelPushButton_clicked() 42 | { 43 | close(); 44 | } 45 | 46 | void numberStep::on_okPushButton_clicked() 47 | { 48 | emit ok_button_clicked(); 49 | } 50 | 51 | void numberStep::on_numPadLineEdit_textEdited(const QString &arg1) 52 | { 53 | if(!arg1.isEmpty()){ 54 | auto const Target = arg1[0]; 55 | auto const Func = [Target](decltype(Target) a){ return a == Target; }; 56 | if(!std::all_of(std::begin(arg1), std::end(arg1), Func)){ 57 | QMessageBox::warning(this, tr("Padding error"), 58 | tr("All of the padding characters should be the same")); 59 | auto replace = arg1; 60 | replace.replace(arg1[arg1.size() - 1], Target); 61 | ui->numPadLineEdit->setText(replace); 62 | } 63 | }else{ 64 | QMessageBox::warning(this, tr("Padding error"), tr("Padding charaters should not be empty")); 65 | return; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ui/filterDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "filterDialog.hpp" 2 | #include "ui_filterDialog.h" 3 | 4 | #include "globalconst.hpp" 5 | 6 | filterDialog::filterDialog(QWidget *parent) : 7 | QDialog{parent}, 8 | ui{new Ui::filterDialog} 9 | { 10 | ui->setupUi(this); 11 | } 12 | 13 | filterDialog::~filterDialog() 14 | { 15 | delete ui; 16 | } 17 | 18 | fileNameHeader filterDialog::get_filter_column() const Q_DECL_NOEXCEPT 19 | { 20 | if(ui->newNameRadioButton->isChecked()){ 21 | return fileNameHeader::NewName; 22 | }else if(ui->originNameRadioButton->isChecked()){ 23 | return fileNameHeader::OldName; 24 | }else if(ui->newSuffixRadioButton->isChecked()){ 25 | return fileNameHeader::NewSuffix; 26 | }else if(ui->originSuffixRadioButton->isChecked()){ 27 | return fileNameHeader::OldSuffix; 28 | }else if(ui->pathRadioButton->isChecked()){ 29 | return fileNameHeader::Path; 30 | } 31 | 32 | return fileNameHeader::OldName; 33 | } 34 | 35 | QString filterDialog::get_filter_string() const Q_DECL_NOEXCEPT 36 | { 37 | return ui->FilterlineEdit->text(); 38 | } 39 | 40 | filterType filterDialog::get_filter_type() const Q_DECL_NOEXCEPT 41 | { 42 | if(ui->excludeRadioButton->isChecked()){ 43 | return filterType::Exclude; 44 | }else{ 45 | return filterType::Include; 46 | } 47 | } 48 | 49 | QRegularExpression::PatternOption 50 | filterDialog::get_filter_pattern() const Q_DECL_NOEXCEPT 51 | { 52 | return ui->icaseCheckBox->isChecked() ? 53 | QRegularExpression::CaseInsensitiveOption : 54 | QRegularExpression::NoPatternOption; 55 | } 56 | 57 | void filterDialog::on_cancelPushButton_clicked() 58 | { 59 | close(); 60 | } 61 | 62 | void filterDialog::on_okPushButton_clicked() 63 | { 64 | emit ok(); 65 | } 66 | -------------------------------------------------------------------------------- /qml/ListStackView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | 4 | FocusScope{ 5 | width: parent.width 6 | height: parent.height 7 | 8 | property alias list_view_model: list_view.model 9 | property variant parent_stack_view 10 | 11 | property bool should_disable_raining_particle: true 12 | 13 | function pop(){ 14 | stack_view.pop() 15 | } 16 | 17 | Item{ 18 | width: parent.width 19 | height: parent.height 20 | 21 | StackView { 22 | id: stack_view 23 | anchors.fill: parent 24 | focus: true 25 | 26 | Keys.onReleased: if ((event.key === Qt.Key_Backspace) 27 | && stack_view.depth > 1) { 28 | stack_view.pop(); 29 | raining_particle.emit_enabled = true 30 | event.accepted = true; 31 | } 32 | 33 | initialItem: Item { 34 | width: parent.width 35 | height: parent.height 36 | ListView { 37 | id: list_view 38 | anchors.fill: parent 39 | delegate: ButtonDelegate { 40 | text: title 41 | 42 | //onClicked: stack_view.push(Qt.resolvedUrl(page)) 43 | onClicked: { 44 | if(title != "Back"){ 45 | if(should_disable_raining_particle){ 46 | raining_particle.emit_enabled = false; 47 | } 48 | stack_view.push(Qt.resolvedUrl(page)); 49 | }else if(parent_stack_view){ 50 | parent_stack_view.pop(); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /workerThreads/renameFileThread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENAMEFILETHREAD_HPP 2 | #define RENAMEFILETHREAD_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class renameFileWorker; 12 | 13 | class renameFileThread : public QThread 14 | { 15 | Q_OBJECT 16 | public: 17 | using Map = std::pair; 18 | 19 | explicit renameFileThread(QObject *parent = nullptr); 20 | renameFileThread& operator=(renameFileThread const &) = delete; 21 | renameFileThread(renameFileThread const&) = delete; 22 | 23 | void enable_revert(bool value = true) Q_DECL_NOEXCEPT; 24 | 25 | QStringList get_error_message() const Q_DECL_NOEXCEPT; 26 | Map get_revert_map() const Q_DECL_NOEXCEPT; 27 | 28 | void set_dirs_to_scan(QStringList dirs) Q_DECL_NOEXCEPT; 29 | void set_new_file_names(QStringList *new_file_names) Q_DECL_NOEXCEPT; 30 | void set_new_suffix(QStringList suffix) Q_DECL_NOEXCEPT; 31 | void set_old_file_names(QStringList old_file_names) Q_DECL_NOEXCEPT; 32 | void set_old_suffix(QStringList suffix) Q_DECL_NOEXCEPT; 33 | void set_source_indexes(std::vector const &indexes) Q_DECL_NOEXCEPT; 34 | 35 | void rename(int Row); 36 | signals: 37 | void end(); 38 | void increment(int value); 39 | 40 | private: 41 | void run() override; 42 | 43 | private: 44 | QStringList dirs_to_scan_; 45 | bool enable_revert_; 46 | QStringList error_message_; 47 | QStringList *new_file_names_; 48 | QStringList new_suffix_; 49 | QStringList old_file_names_; 50 | QStringList old_suffix_; 51 | std::pair revert_map_; 52 | 53 | //if not revert, renameFileThread have to 54 | //build up the revert map and make sure the 55 | //indexes store the row number of source 56 | //but not the row number of proxy model 57 | //This is the reason why source_index_ exist 58 | std::vector const *source_indexes_; 59 | }; 60 | 61 | #endif // RENAMEFILETHREAD_HPP 62 | -------------------------------------------------------------------------------- /qml/MethodsList.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.3 3 | import QtQuick.Layouts 1.1 4 | import "methods" 5 | 6 | Rectangle { 7 | width: parent.width 8 | height: parent.height 9 | 10 | gradient: Gradient { 11 | GradientStop { position: 0.0; color: "darkgreen" } 12 | GradientStop { position: 0.33; color: "green" } 13 | GradientStop { position: 1.0; color: "lightgreen" } 14 | } 15 | 16 | ListModel { 17 | id: method_page_model 18 | ListElement { 19 | title: "Append words" 20 | page: "qrc:/methods/AppendWords.qml" 21 | } 22 | ListElement { 23 | title: "Insert words" 24 | page: "qrc:/methods/InsertWords.qml" 25 | } 26 | ListElement { 27 | title: "Prepend words" 28 | page: "qrc:/methods/PrependWords.qml" 29 | } 30 | ListElement { 31 | title: "Prepend zero on number" 32 | page: "qrc:/methods/PrependZeroOnNum.qml" 33 | } 34 | ListElement { 35 | title: "Regex--Global modifier" 36 | page: "qrc:/methods/RegexGlobalModifier.qml" 37 | } 38 | ListElement { 39 | title: "Regex--Numbering files" 40 | page: "qrc:/methods/RegexNumbering.qml" 41 | } 42 | ListElement { 43 | title: "Regex--Using captures" 44 | page: "qrc:/methods/RegexUsingCaptures.qml" 45 | } 46 | ListElement { 47 | title: "Remove words" 48 | page: "qrc:/methods/RemoveWords.qml" 49 | } 50 | ListElement { 51 | title: "Replace suffix" 52 | page: "qrc:/methods/ReplaceSuffix.qml" 53 | } 54 | ListElement { 55 | title: "Replace words" 56 | page: "qrc:/methods/ReplaceWords.qml" 57 | } 58 | ListElement { 59 | title: "Back" 60 | page: "qrc:/BackButton.qml" 61 | } 62 | } 63 | 64 | ListStackView{ 65 | id: method_stack_view 66 | anchors.fill: parent 67 | list_view_model: method_page_model 68 | parent_stack_view: menu_stack_view 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /qml/TutorialSteps.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | import "../js/globalConstant.js" as Global 5 | 6 | Item { 7 | width: parent.width 8 | height: parent.height 9 | 10 | property variant stack_view 11 | property alias model: list_view.model 12 | 13 | Rectangle{ 14 | anchors.fill: parent 15 | gradient: Gradient { 16 | GradientStop { position: 0.0; color: "#4a4a4a" } 17 | GradientStop { position: 1.0; color: "#2b2b2b" } 18 | } 19 | } 20 | 21 | ListView { 22 | id: list_view 23 | 24 | anchors.fill: parent 25 | 26 | delegate: StepLabel{ 27 | help_str: help 28 | img_src : img 29 | details : img_details 30 | } 31 | 32 | //model: tutorial //this model is come from the other methods(ex : AppendWords, PrependWords) 33 | footer: Rectangle{ 34 | id: foot 35 | height: Global.global_const.list_view_foot_height() 36 | width: list_view.width 37 | color: "silver" 38 | 39 | Text{ 40 | anchors.fill: parent 41 | text: "Back" 42 | horizontalAlignment: Text.AlignHCenter 43 | } 44 | 45 | MouseArea{ 46 | anchors.fill: parent 47 | onClicked: { 48 | parent.state = "press"; 49 | stack_view.pop(); 50 | } 51 | onReleased: { 52 | raining_particle.emit_enabled = true; 53 | parent.state = "" 54 | } 55 | } 56 | 57 | states: [ 58 | State { 59 | name: "press" 60 | 61 | PropertyChanges { target: foot; color: "blue"} 62 | } 63 | ] 64 | 65 | transitions: [ 66 | Transition { 67 | ColorAnimation { 68 | to: "blue" 69 | duration: 500 70 | } 71 | } 72 | ] 73 | } 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /installer/packages/main.component/meta/installscript.qs: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd. 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Installer Framework. 7 | ** 8 | ** $QT_BEGIN_LICENSE:LGPL$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see http://qt.io/terms-conditions. For further 15 | ** information use the contact form at http://www.qt.io/contact-us. 16 | ** 17 | ** GNU Lesser General Public License Usage 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser 19 | ** General Public License version 2.1 or version 3 as published by the Free 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 22 | ** following information to ensure the GNU Lesser General Public License 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 25 | ** 26 | ** As a special exception, The Qt Company gives you certain additional 27 | ** rights. These rights are described in The Qt Company LGPL Exception 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 29 | ** 30 | ** 31 | ** $QT_END_LICENSE$ 32 | ** 33 | **************************************************************************/ 34 | 35 | function Component() 36 | { 37 | // default constructor 38 | } 39 | 40 | Component.prototype.createOperations = function() 41 | { 42 | // call default implementation to actually install README.txt! 43 | component.createOperations(); 44 | 45 | if (systemInfo.productType === "windows") { 46 | component.addOperation("CreateShortcut", "@TargetDir@/QRenamer.exe", "@StartMenuDir@/QRenamer.lnk", 47 | "workingDirectory=@TargetDir@"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /qml/methods/PrependZeroOnNum.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Step 1 : Select the tag-- 18 | Prepend zero on number"; 19 | img: "../pics/methods/prependZeroOnNum00.png" 20 | img_details: "This tag allow users to prepend zero on number.
21 | ex : make [rockman_0] to [rockman_00]"} 22 | 23 | ListElement { help:"Step 2 : Select the folder"; 24 | img: "../pics/methods/selectFolder.png" 25 | img_details: "This step will load the files in the folder."} 26 | 27 | ListElement { help:"Step 3a-1 : Select how many 0 you want to
28 | prepend on the last number"; 29 | img: "../pics/methods/prependZeroOnNum01.png" 30 | img_details: "If you select 2(minimum is 1), [001_0] will become [001_00].
31 | If you select 3, [001_0] will become [001_000]"} 32 | 33 | ListElement { help:"Step 3b-1 : Change the box from 34 | last number to custom" 35 | img: "../pics/methods/prependZeroOnNum02.png" 36 | img_details: "This option will allow you to prepend 0 on specific number."} 37 | 38 | ListElement { help:"Step 3b-2 : Select the number you want to prepend zero" 39 | img: "../pics/methods/prependZeroOnNum03.png" 40 | img_details: "If you select Position as 1 on [001_0] and set the 41 | Number length as 4, the number will become [0001_0].
42 | If you select Position as 2 on [001_0] and set the 43 | Number length as 4, the number will become [001_0000].
"} 44 | 45 | ListElement { help:"Step 4 : Rename the Files"; 46 | img: "../pics/methods/renameFiles.png" 47 | img_details: "Click on the ok button to rename the files, if anything wrong,
48 | you can always revert the change with the revert button"} 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /qml/methods/RegexGlobalModifier.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Changing special characters to underscore" 18 | img: "../pics/methods/regexGlobalModifier00.png" 19 | img_details: "Lets say your files have some evil spaces(a right royal pain
20 | in some cases), unwanted characters, and you want to replace them.
21 | In this case, we want to replace each space or # character with an underscore"} 22 | 23 | ListElement { help:"Step 1 : Select the tag-- 24 | Regex" 25 | img: "../pics/methods/selectRegex.png" 26 | img_details: "This tag allow users to rename the files with regular expression."} 27 | 28 | ListElement { help:"Step 2 : Select the folder" 29 | img: "../pics/methods/selectFolder.png" 30 | img_details: "This step will load the files in the folder."} 31 | 32 | ListElement { help:"Step 3 : Captured the unwanted characters" 33 | img: "../pics/methods/regexGlobalModifier01.png" 34 | img_details: "Type the following regex in the Match field: [ #](match either a space or a # character)
"} 35 | 36 | ListElement { help:"Step 4 : Change the unwanted characters to \"_\"" 37 | img: "../pics/methods/regexGlobalModifier02.png" 38 | img_details: "Type the following regex in the Replace field: [ #] 39 |
    40 |
  • The files will be highlighted blue to indicate they match
  • 41 |
  • The space and # will be removed from the filename in the preview column.
  • 42 |
"} 43 | 44 | ListElement { help:"Step 5 : Rename the Files" 45 | img: "../pics/methods/renameFiles.png" 46 | img_details: "Click on the ok button to rename the files, if anything wrong,
47 | you can always revert the change with the revert button"} 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /qml/methods/RegexUsingCaptures.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import ".." 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Converting logfile dates(1)" 18 | img: "../pics/methods/regexUsingCaptures00.png" 19 | img_details: "Say you have a bunch of access logs with every day
20 | in the format: accesslog_dd_mm_yyyy.txt"} 21 | 22 | ListElement { help:"Converting logfile dates(2)" 23 | img: "../pics/methods/regexUsingCaptures01.png" 24 | img_details: "Using captures we can easily rename these files to a yyyy_mm_dd format"} 25 | 26 | ListElement { help:"Step 1 : Select the tag-- 27 | Regex" 28 | img: "../pics/methods/selectRegex.png" 29 | img_details: "This tag allow users to rename the files with regular expression."} 30 | 31 | ListElement { help:"Step 2 : Select the folder" 32 | img: "../pics/methods/selectFolder.png" 33 | img_details: "This step will load the files in the folder."} 34 | 35 | ListElement { help:"Step 3 : Captured the format(dd_mm_yyyy)" 36 | img: "../pics/methods/regexUsingCaptures02.png" 37 | img_details: "Type the following regex in the Match field: (\d+)_(\d+)_(\d+) 38 |
    39 |
  • The files will be highlighted blue to indicate they match
  • 40 |
  • The numbers will be removed from the filename in the preview column.
  • 41 |
"} 42 | 43 | ListElement { help:"Step 4 : Change the format" 44 | img: "../pics/methods/regexUsingCaptures03.png" 45 | img_details: "Type the following regex in the Replace field: \\3_\\2_\\1
46 | You will see the format of date change to yyyy_mm_dd"} 47 | 48 | ListElement { help:"Step 5 : Rename the Files" 49 | img: "../pics/methods/renameFiles.png" 50 | img_details: "Click on the ok button to rename the files, if anything wrong,
51 | you can always revert the change with the revert button"} 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /modelProxy/sortAndFilterFile.cpp: -------------------------------------------------------------------------------- 1 | #include "sortAndFilterFile.hpp" 2 | 3 | #include "globalconst.hpp" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | struct sortAndFilterFile::pimpl 11 | { 12 | pimpl(); 13 | 14 | fileNameHeader filter_column_; 15 | filterType filter_type_; 16 | QRegularExpression reg_; 17 | }; 18 | 19 | sortAndFilterFile::sortAndFilterFile(QObject *parent) 20 | : QSortFilterProxyModel(parent), 21 | impl_(new pimpl) 22 | { 23 | } 24 | 25 | sortAndFilterFile::~sortAndFilterFile() 26 | { 27 | 28 | } 29 | 30 | void sortAndFilterFile::set_filter_column(fileNameHeader header, bool invalidate) 31 | { 32 | impl_->filter_column_ = header; 33 | if(invalidate){ 34 | invalidateFilter(); 35 | } 36 | } 37 | 38 | void sortAndFilterFile::set_filter_type(filterType type, bool invalidate) 39 | { 40 | impl_->filter_type_ = type; 41 | if(invalidate){ 42 | invalidateFilter(); 43 | } 44 | } 45 | 46 | void sortAndFilterFile::set_regex(QRegularExpression reg, bool invalidate) 47 | { 48 | if(reg.isValid()){ 49 | impl_->reg_ = std::move(reg); 50 | if(invalidate){ 51 | invalidateFilter(); 52 | } 53 | } 54 | } 55 | 56 | bool sortAndFilterFile::filterAcceptsRow(int source_row, 57 | const QModelIndex &source_parent) const 58 | { 59 | if(impl_->filter_column_ == fileNameHeader::LastElem){ 60 | return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); 61 | } 62 | 63 | auto const Col = static_cast(impl_->filter_column_); 64 | auto const Index = sourceModel()->index(source_row, Col, source_parent); 65 | auto const Data = sourceModel()->data(Index).value(); 66 | if(impl_->reg_.match(Data).hasMatch()){ 67 | if(impl_->filter_type_ == filterType::Include){ 68 | return true; 69 | }else{ 70 | return false; 71 | } 72 | }else{ 73 | if(impl_->filter_type_ == filterType::Include){ 74 | return false; 75 | }else{ 76 | return true; 77 | } 78 | } 79 | } 80 | 81 | bool sortAndFilterFile::lessThan(const QModelIndex &left, const QModelIndex &right) const 82 | { 83 | auto const leftStr = sourceModel()->data(left).value(); 84 | auto const rightStr = sourceModel()->data(right).value(); 85 | 86 | return leftStr < rightStr; 87 | } 88 | 89 | 90 | sortAndFilterFile::pimpl::pimpl() : 91 | filter_column_(fileNameHeader::LastElem), 92 | filter_type_(filterType::Include) 93 | { 94 | 95 | } 96 | -------------------------------------------------------------------------------- /qml/methods/RegexNumbering.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "../" 4 | 5 | Rectangle { 6 | width: parent.width 7 | height: parent.height 8 | 9 | TutorialSteps{ 10 | anchors.fill: parent 11 | stack_view: method_stack_view 12 | model: tutorial 13 | } 14 | 15 | ListModel { 16 | id: tutorial 17 | ListElement { help:"Renaming photos" 18 | img: "../pics/methods/regexNumbering00.png" 19 | img_details: "If you have ever used a digital camera you’ll
20 | know they aren’t very creative when they name your files.
21 | With Cute Renamer, you can make these more descriptive"} 22 | 23 | ListElement { help:"Step 1 : Select the tag-- 24 | Regex" 25 | img: "../pics/methods/selectRegex.png" 26 | img_details: "This tag allow users to rename the files with regular expression."} 27 | 28 | ListElement { help:"Step 2 : Select the folder" 29 | img: "../pics/methods/selectFolder.png" 30 | img_details: "This step will load the files in the folder."} 31 | 32 | ListElement { help:"Step 3 : Captured the number" 33 | img: "../pics/methods/regexNumbering01.png" 34 | img_details: "Type the following regex in the Match field: \d+ 35 |
    36 |
  • The files will be highlighted blue to indicate they match
  • 37 |
  • The numbers will be removed from the filename in the preview column.
  • 38 |
"} 39 | 40 | ListElement { help:"Step 4 : Change the number" 41 | img: "../pics/methods/regexNumbering02.png" 42 | img_details: " 43 |
    44 |
  1. Type the following regex in the Replace field: $#
  2. 45 |
  3. Click on the Numbering circle
  4. 46 |
  5. The new name column will now contain the new filenames with the
    47 | default numbering settings
  6. 48 |
  7. Note the order is still the same as the original filenames
  8. 49 |
"} 50 | 51 | ListElement { help:"Step 5(option) : Configure the number sequence" 52 | img: "../pics/methods/regexNumbering03.png" 53 | img_details: "The defaults are (start at 1, pad to 3 digits,
54 | increment by 1, don’t ever reset the sequence)"} 55 | 56 | ListElement { help:"Step 6 : Rename the Files" 57 | img: "../pics/methods/renameFiles.png" 58 | img_details: "Click on the ok button to rename the files, if anything wrong,
59 | you can always revert the change with the revert button"} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /qml/RainingParticle.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Juergen Bocklage-Ryannel, Johan Thelin 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the editors nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | import QtQuick 2.0 29 | import QtQuick.Particles 2.0 30 | 31 | Item { 32 | id: root 33 | width: parent.width; height: parent.height 34 | 35 | property alias emit_enabled: emitter.enabled 36 | property alias particle_source: im_particle.source 37 | 38 | ParticleSystem { 39 | id: particleSystem 40 | } 41 | 42 | ImageParticle { 43 | id: im_particle 44 | system: particleSystem 45 | color: '#FFD700' 46 | opacity: 0.5 47 | colorVariation: 0.2 48 | rotation: 0 49 | rotationVariation: 45 50 | rotationVelocity: 15 51 | rotationVelocityVariation: 15 52 | entryEffect: ImageParticle.Scale 53 | } 54 | 55 | Emitter { 56 | id: emitter 57 | anchors.bottom: parent.top 58 | width: root.width; height: 1 59 | system: particleSystem 60 | lifeSpan: root.height * 10 + 50 61 | size: 32 62 | velocity: AngleDirection { 63 | angle: 90 64 | angleVariation: 15 65 | magnitude: root.height / 6 66 | magnitudeVariation: 50 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pics.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | pics/add.png 4 | pics/delete.png 5 | pics/folder.png 6 | pics/clipboard_empty.png 7 | pics/folders_explorer.png 8 | pics/setting_tools.png 9 | pics/spanHeader.png 10 | pics/revert.png 11 | pics/filter.png 12 | pics/help.png 13 | pics/num_step.png 14 | pics/accept.png 15 | pics/cancel.png 16 | pics/filter_clear.png 17 | pics/bullet_star.png 18 | pics/methods/appendWords00.png 19 | pics/methods/appendWords01.png 20 | pics/methods/prependWords00.png 21 | pics/methods/prependWords01.png 22 | pics/methods/renameFiles.png 23 | pics/methods/replaceWords00.png 24 | pics/methods/replaceWords01.png 25 | pics/methods/selectFolder.png 26 | pics/methods/replaceSuffix00.png 27 | pics/methods/replaceSuffix01.png 28 | pics/methods/removeWords00.png 29 | pics/methods/removeWords01.png 30 | pics/methods/insertWords00.png 31 | pics/methods/insertWords01.png 32 | pics/methods/insertWords02.png 33 | pics/methods/prependZeroOnNum00.png 34 | pics/methods/prependZeroOnNum01.png 35 | pics/methods/prependZeroOnNum03.png 36 | pics/methods/regexUsingCaptures00.png 37 | pics/methods/regexUsingCaptures01.png 38 | pics/methods/regexUsingCaptures02.png 39 | pics/methods/regexUsingCaptures03.png 40 | pics/methods/selectRegex.png 41 | pics/methods/prependZeroOnNum02.png 42 | pics/app_icons.png 43 | pics/methods/regexNumbering00.png 44 | pics/methods/regexNumbering01.png 45 | pics/methods/regexNumbering02.png 46 | pics/methods/regexNumbering03.png 47 | pics/methods/regexGlobalModifier00.png 48 | pics/methods/regexGlobalModifier01.png 49 | pics/methods/regexGlobalModifier02.png 50 | pics/cross_reference.png 51 | pics/filter/filterExclude00.png 52 | pics/filter/filterExclude01.png 53 | pics/filter/filterInclude00.png 54 | pics/filter/filterInclude01.png 55 | pics/filter/filterSample.png 56 | pics/filter/filterSelect.png 57 | pics/filter/removeFitler00.png 58 | pics/miscellaneous/addRecursiveFolder00.png 59 | pics/miscellaneous/addRecursiveFolder01.png 60 | pics/miscellaneous/addRecursiveFolder02.png 61 | pics/miscellaneous/addRecursiveFolder03.png 62 | pics/miscellaneous/spanHeader00.png 63 | pics/miscellaneous/spanHeader01.png 64 | pics/miscellaneous/spanHeader02.png 65 | pics/macro_names.png 66 | 67 | 68 | -------------------------------------------------------------------------------- /qml/StepLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "../js/globalConstant.js" as Global 4 | 5 | Rectangle { 6 | id : root 7 | width: parent.width 8 | height: 90 9 | 10 | property string details 11 | property string help_str 12 | property string img_src 13 | 14 | Rectangle { 15 | id: brief 16 | width: parent.width * 3 / 4 17 | height: parent.height 18 | 19 | anchors.left: parent.left 20 | anchors.right: parent.right 21 | anchors.top: parent.top 22 | 23 | color: "#333" 24 | border.color: Qt.lighter(color, 1.2) 25 | Text { 26 | id: help_text 27 | 28 | anchors.fill: parent 29 | font.pointSize: 12 30 | color: "white" 31 | text: help_str 32 | textFormat: Text.RichText 33 | } 34 | } 35 | 36 | Rectangle{ 37 | id: image 38 | 39 | width: parent.width / 4 40 | height: parent.height 41 | 42 | anchors.right: brief.right 43 | anchors.top: parent.top 44 | 45 | color: "black" 46 | 47 | Image { 48 | anchors.fill: parent 49 | 50 | fillMode: Image.PreserveAspectFit 51 | source: img_src 52 | asynchronous: true 53 | } 54 | } 55 | 56 | MouseArea { 57 | anchors.fill: parent 58 | onClicked: { 59 | if(parent.state.length == 0){ 60 | parent.state = "expanded" 61 | }else{ 62 | parent.state = "" 63 | } 64 | } 65 | } 66 | 67 | Item { 68 | id: detailsView 69 | 70 | anchors.top: image.bottom 71 | anchors.left: parent.left 72 | anchors.right: parent.right 73 | anchors.bottom: parent.bottom 74 | 75 | opacity: 0 76 | 77 | Rectangle { 78 | anchors.fill: parent 79 | 80 | gradient: Gradient { 81 | GradientStop { position: 0.0; color: "#fcd978" } 82 | GradientStop { position: 1.0; color: "#fccc4f" } 83 | } 84 | border.color: "black" 85 | border.width: 2 86 | 87 | Text { 88 | anchors.fill: parent 89 | anchors.margins: 5 90 | 91 | clip: true 92 | wrapMode: Text.WordWrap 93 | color: "black" 94 | font.pixelSize: 12 95 | textFormat: Text.RichText 96 | 97 | text: details 98 | } 99 | } 100 | } 101 | 102 | states: [ 103 | State { 104 | name: "expanded" 105 | 106 | PropertyChanges { target: root; height: list_view.height} 107 | PropertyChanges { target: image; width: list_view.width; height: list_view.height * 0.6; anchors.topMargin: 30 } 108 | PropertyChanges { target: detailsView; opacity: 1 } 109 | PropertyChanges { target: root.ListView.view; contentY: root.y; interactive: false } 110 | PropertyChanges { target: image; rotation: 360 } 111 | } 112 | ] 113 | 114 | transitions: [ 115 | Transition { 116 | ParallelAnimation{ 117 | NumberAnimation { 118 | duration: 200; 119 | properties: "height,width,rotation,anchors.topMargin,opacity,contentY" 120 | } 121 | } 122 | } 123 | ] 124 | } 125 | 126 | -------------------------------------------------------------------------------- /workerThreads/renameFileThread.cpp: -------------------------------------------------------------------------------- 1 | #include "renameFileThread.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | renameFileThread::renameFileThread(QObject *parent) : 10 | QThread(parent), 11 | enable_revert_(false), 12 | new_file_names_(nullptr), 13 | source_indexes_(nullptr) 14 | { 15 | } 16 | 17 | /** 18 | * @brief setup the flag to indicate it is rename the\n 19 | * files or try to revert the renamed files 20 | * 21 | * 22 | * After the rename/revert process finish, the flag will be\n 23 | * set to false again automatic 24 | * @param value true : try to revert the renamed files;else false 25 | */ 26 | void renameFileThread::enable_revert(bool value) Q_DECL_NOEXCEPT 27 | { 28 | enable_revert_ = value; 29 | } 30 | 31 | QStringList renameFileThread::get_error_message() const Q_DECL_NOEXCEPT 32 | { 33 | return error_message_; 34 | } 35 | 36 | renameFileThread::Map renameFileThread::get_revert_map() const Q_DECL_NOEXCEPT 37 | { 38 | return revert_map_; 39 | } 40 | 41 | void renameFileThread::set_dirs_to_scan(QStringList dirs) Q_DECL_NOEXCEPT 42 | { 43 | dirs_to_scan_ = dirs; 44 | } 45 | 46 | void renameFileThread::set_new_file_names(QStringList *new_file_names) Q_DECL_NOEXCEPT 47 | { 48 | new_file_names_ = new_file_names; 49 | } 50 | 51 | void renameFileThread::set_new_suffix(QStringList suffix) Q_DECL_NOEXCEPT 52 | { 53 | new_suffix_ = suffix; 54 | } 55 | 56 | void renameFileThread::set_old_file_names(QStringList old_file_names) Q_DECL_NOEXCEPT 57 | { 58 | old_file_names_ = old_file_names; 59 | } 60 | 61 | void renameFileThread::set_old_suffix(QStringList suffix) Q_DECL_NOEXCEPT 62 | { 63 | old_suffix_ = suffix; 64 | } 65 | 66 | void renameFileThread::set_source_indexes(std::vector const &indexes) Q_DECL_NOEXCEPT 67 | { 68 | source_indexes_ = &indexes; 69 | } 70 | 71 | void renameFileThread::rename(int Row) 72 | { 73 | auto const OldName = dirs_to_scan_[Row] + "/" + 74 | old_file_names_[Row] + "." + 75 | old_suffix_[Row]; 76 | if(QFile::exists(OldName)){ 77 | auto const NewName = dirs_to_scan_[Row] + "/" + 78 | (*new_file_names_)[Row] + "." + new_suffix_[Row]; 79 | if(!QFile::exists(NewName)){ 80 | if(OldName != NewName && QFile::rename(OldName, NewName)){ 81 | revert_map_.first.push_back(NewName); 82 | revert_map_.second.push_back(OldName); 83 | }else{ 84 | error_message_.push_back(NewName + " can not rename"); 85 | } 86 | } 87 | }else{ 88 | //(*new_file_names_)[Row] = old_file_names_[Row]; 89 | error_message_.push_back(OldName + " do not exist"); 90 | } 91 | } 92 | 93 | void renameFileThread::run() 94 | { 95 | error_message_.clear(); 96 | revert_map_.first.clear(); 97 | revert_map_.second.clear(); 98 | 99 | if(!enable_revert_){ 100 | for(decltype(source_indexes_->size()) i = 0; i 101 | != source_indexes_->size(); ++i){ 102 | rename((*source_indexes_)[i].row()); 103 | emit increment(static_cast(i)); 104 | } 105 | }else{ 106 | //if revert, the fileNameModel will setup 107 | //the mapping data into old_file_names and 108 | //new_file_names 109 | for(decltype(old_file_names_.size()) i = 0; 110 | i != old_file_names_.size(); ++i){ 111 | rename(i); 112 | emit increment(i); 113 | } 114 | enable_revert_ = false; 115 | } 116 | 117 | emit end(); 118 | } 119 | -------------------------------------------------------------------------------- /model/fileNameModel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILENAMEMODEL_HPP 2 | #define FILENAMEMODEL_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum class fileNameHeader; 9 | enum class RenameType; 10 | 11 | class addDirThread; 12 | class fileModelItem; 13 | class QRegularExpression; 14 | class renameFileThread; 15 | class scanDirThread; 16 | 17 | /** 18 | * @brief The model in charge of file names manipulation 19 | * 20 | * 21 | * This class use QtConcurrent to process some string \n 22 | * but haven't do any benchmark yet.The main\n 23 | * reason I use it is because I want to get familiar with\n 24 | * the api of QtConcurrent but not performance. 25 | */ 26 | class fileNameModel : public QAbstractTableModel 27 | { 28 | Q_OBJECT 29 | 30 | public: 31 | explicit fileNameModel(QObject *parent = nullptr); 32 | ~fileNameModel() override; 33 | 34 | void add_dirs_recursive(QString const &dir_path); 35 | void append_number_on_last_match(int length); 36 | void append_number_on_nth_match(int length, int position); 37 | void append_words(QString const &words); 38 | void change_name_with_increase_num(QString const &change_file_name_to, int zero_fill); 39 | 40 | int columnCount(const QModelIndex &parent) const override; 41 | void clear_all(); 42 | void clear_match_regex_file(); 43 | 44 | QVariant data(const QModelIndex &index, int role) const override; 45 | bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, 46 | const QModelIndex &parent) override; 47 | 48 | Qt::ItemFlags flags(const QModelIndex &index) const override; 49 | 50 | QStringList get_rename_error_message() const Q_DECL_NOEXCEPT; 51 | 52 | QVariant headerData(int section, Qt::Orientation orientation, int role) const override; 53 | 54 | void insert_at(int position, QString const &words); 55 | bool insertRows(int row, int count, const QModelIndex &parent) override; 56 | 57 | QStringList mimeTypes() const override; 58 | 59 | void numbering(int start, int increment, int reset, QString const &pad); 60 | 61 | void prepend_words(QString const &words); 62 | 63 | void remove_at(int position, int n); 64 | bool removeRow(int row, const QModelIndex &parent = QModelIndex()); 65 | bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; 66 | void rename_files(); 67 | void replace_regex(QRegularExpression const &re, QString const &after); 68 | void replace_suffix(QString const &from, QString const &to); 69 | void replace_text(QString const &from, QString const &to); 70 | void revert_rename_result(); 71 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 72 | 73 | bool setData(const QModelIndex &index, const QVariant &value, int role) override; 74 | void set_selected_index(std::vector &&indexes); 75 | 76 | void update_file(QStringList const &files); 77 | 78 | signals: 79 | void add_dirs_recursive_end(); 80 | void drop_data(QStringList file); 81 | void increment(int); 82 | void rename_file_end(); 83 | void scan_dir(QString dir, int msec); 84 | void set_size(int); 85 | void set_visible(bool); 86 | 87 | private slots: 88 | void add_dirs_recursive_end_slot(); 89 | void rename_file_finished(); 90 | void scan_dir_recursive_end_slot(); 91 | 92 | private: 93 | void insert_data(QStringList const &input, fileNameHeader header); 94 | void insert_rows_if_needed(int current_size, int previous_size); 95 | 96 | private: 97 | QScopedPointer item_; 98 | 99 | addDirThread *add_dirs_thread_; 100 | renameFileThread *rename_file_thread_; 101 | scanDirThread *scan_dir_thread_; 102 | }; 103 | 104 | #endif // FILENAMEMODEL_HPP 105 | -------------------------------------------------------------------------------- /ui/numberstep.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | numberStep 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Numbering 15 | 16 | 17 | 18 | :/pics/num_step.png:/pics/num_step.png 19 | 20 | 21 | Numbering 22 | 23 | 24 | Numbering 25 | 26 | 27 | 28 | 29 | 30 | 31 | 0 32 | 0 33 | 34 | 35 | 36 | Start 37 | 38 | 39 | 40 | 41 | 42 | 43 | 999999 44 | 45 | 46 | 47 | 48 | 49 | 50 | Pad 51 | 52 | 53 | 54 | 55 | 56 | 57 | 99999 58 | 59 | 60 | 61 | 62 | 63 | 64 | Reset 65 | 66 | 67 | 68 | 69 | 70 | 71 | 1 72 | 73 | 74 | 99999 75 | 76 | 77 | 78 | 79 | 80 | 81 | Inc 82 | 83 | 84 | 85 | 86 | 87 | 88 | 0 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 6 97 | 0 98 | 99 | 100 | 101 | Ok 102 | 103 | 104 | Ok 105 | 106 | 107 | 108 | 109 | 110 | 111 | :/pics/accept.png:/pics/accept.png 112 | 113 | 114 | Return 115 | 116 | 117 | 118 | 119 | 120 | 121 | Cancel 122 | 123 | 124 | Cancel 125 | 126 | 127 | 128 | 129 | 130 | 131 | :/pics/cancel.png:/pics/cancel.png 132 | 133 | 134 | Esc 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /ui/MainWindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_HPP 2 | #define MAINWINDOW_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Ui { 12 | class MainWindow; 13 | } 14 | 15 | class filterDialog; 16 | class fileNameModel; 17 | class numberStep; 18 | class quickCloseSignal; 19 | class QQuickWidget; 20 | class QTextBrowser; 21 | class sortAndFilterFile; 22 | 23 | class MainWindow : public QMainWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit MainWindow(QWidget *parent = nullptr); 29 | MainWindow(MainWindow const&) = delete; 30 | MainWindow& operator=(MainWindow const&) = delete; 31 | ~MainWindow(); 32 | 33 | protected: 34 | void closeEvent(QCloseEvent *event) override; 35 | 36 | private slots: 37 | void add_dir_recursive_finished(); 38 | 39 | void close_help_reader(); 40 | 41 | void drop_data(QStringList files); 42 | 43 | void enable_gui(); 44 | 45 | void filter(); 46 | 47 | void regex_replace_and_reset_view(); 48 | void rename_file_fisnished(); 49 | 50 | void show_status_bar(QString message, int msec); 51 | 52 | void on_numberComboBox_currentTextChanged(const QString&); 53 | 54 | void on_numberSpinBox_valueChanged(int); 55 | 56 | void on_positionSpinBox_valueChanged(int); 57 | 58 | void on_prependLineEdit_textChanged(const QString&); 59 | 60 | void on_renameFileButton_clicked(); 61 | 62 | void on_replaceFromLineEdit_textChanged(const QString&); 63 | 64 | void on_replaceTolineEdit_textChanged(const QString&); 65 | 66 | void on_replaceSuffixFromLineEdit_textChanged(const QString &arg1); 67 | 68 | void on_actionAddFolder_triggered(); 69 | 70 | void on_actionRemoveAll_triggered(); 71 | 72 | void on_actionAddFiles_triggered(); 73 | 74 | void on_actionDeleteFiles_triggered(); 75 | 76 | void on_appendLineEdit_textChanged(QString const&); 77 | 78 | void on_actionAddFolderRecursive_triggered(); 79 | 80 | void on_actionShowPath_triggered(bool); 81 | 82 | void on_actionShowSuffix_triggered(bool value); 83 | 84 | void on_actionRevert_triggered(bool); 85 | 86 | void on_replaceSuffixTolineEdit_textChanged(const QString &arg1); 87 | 88 | void on_policyTabWidget_currentChanged(int index); 89 | 90 | void on_actionFilter_triggered(bool checked); 91 | 92 | void on_regexMatchlineEdit_textChanged(const QString&); 93 | 94 | void on_regexReplacelineEdit_textChanged(const QString&); 95 | 96 | void on_icaseCheckBox_clicked(); 97 | 98 | void on_actionAboutQt_triggered(); 99 | 100 | void on_insertAtSpinBox_valueChanged(int arg1); 101 | 102 | void on_insertWordsLineEdit_textChanged(const QString&); 103 | 104 | void on_removeAtSpinBox_valueChanged(int arg1); 105 | 106 | void on_removeRanageSpinBox_valueChanged(int); 107 | 108 | void on_numStepToolButton_clicked(); 109 | 110 | void on_numRadioButton_clicked(bool checked); 111 | 112 | void on_actionRemovefilter_triggered(); 113 | 114 | void on_actionHelp_triggered(); 115 | 116 | void on_actionIcons_triggered(); 117 | 118 | void on_actionReference_triggered(); 119 | 120 | void on_lineEditChangeToFileName_textChanged(const QString &arg1); 121 | 122 | void on_spinBoxZeroFill_valueChanged(int arg1); 123 | 124 | private: 125 | void add_directory(QString const &dir); 126 | void add_files(); 127 | void append_words(); 128 | void build_table(); 129 | void build_connection(); 130 | void change_name_with_increase_num(); 131 | void enable_action_when_file_exist(bool value); 132 | void enable_delete_action(bool value); 133 | void enable_filter_action(bool value); 134 | void enable_remove_filter(bool value); 135 | void insert_words(); 136 | std::vector map_proxy_index_to_source_index() const; 137 | void open_directory(); 138 | void prepend_words(); 139 | void regex_replace(); 140 | void remove_words(); 141 | void replace_suffix(); 142 | void replace_text(); 143 | void show_suffix(bool enable); 144 | bool view_is_empty() const Q_DECL_NOEXCEPT; 145 | 146 | void update_new_file_names(); 147 | void update_number(); 148 | 149 | private: 150 | QString dir_path_; 151 | fileNameModel *file_model_; 152 | std::unique_ptr help_reader_; 153 | std::map> rename_policy_; 154 | sortAndFilterFile *sort_filter_file_; 155 | 156 | filterDialog *filter_; 157 | numberStep *num_step_; 158 | Ui::MainWindow *ui; 159 | }; 160 | 161 | #endif // MAINWINDOW_HPP 162 | -------------------------------------------------------------------------------- /ui/filterDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | filterDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 578 10 | 296 11 | 12 | 13 | 14 | Filter 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Filter 23 | 24 | 25 | Qt::AlignCenter 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Filter dialog 38 | 39 | 40 | Filter dialog 41 | 42 | 43 | Filter method 44 | 45 | 46 | 47 | 48 | 49 | Include 50 | 51 | 52 | true 53 | 54 | 55 | 56 | 57 | 58 | 59 | true 60 | 61 | 62 | Exclude 63 | 64 | 65 | 66 | 67 | 68 | 69 | Case insensitive 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | Filter contents 80 | 81 | 82 | 83 | 84 | 85 | Path 86 | 87 | 88 | 89 | 90 | 91 | 92 | Original name 93 | 94 | 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | New name 103 | 104 | 105 | 106 | 107 | 108 | 109 | Original suffix 110 | 111 | 112 | 113 | 114 | 115 | 116 | New suffix 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | :/pics/accept.png:/pics/accept.png 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | :/pics/cancel.png:/pics/cancel.png 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /utility/stringUtility.cpp: -------------------------------------------------------------------------------- 1 | #include "stringUtility.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief prepend the match string with specify symbol 9 | * @param input string need to prepend symbol 10 | * @param number_length the length of the number, will prepend symbol 11 | * if the length smaller than this number 12 | * @param symbol symbol prepand before the number 13 | * @return the name after alter 14 | * @example ex : number_length == 3, reg = (\\d+), symbol = '0' 15 | * then "1" == "001", "10" == "010" 16 | * ex : number_length == 2, reg = (\\d+), symbol = '0' 17 | * then "1" == "01", "10" == "10" 18 | */ 19 | QString prepend_symbol_on_match(QString const &input, int number_length, 20 | QRegularExpression const ®, 21 | QChar symbol) 22 | { 23 | auto new_file_name = input; 24 | auto match = reg.match(new_file_name); 25 | 26 | if(match.hasMatch()){ 27 | auto const Captured = match.captured(0); 28 | if(Captured.size() < number_length){ 29 | new_file_name.insert(match.capturedStart(0), 30 | QString(number_length - match.capturedLength(1), 31 | symbol)); 32 | } 33 | } 34 | 35 | return new_file_name; 36 | } 37 | 38 | /** 39 | * @brief prepend the match string with specify symbol 40 | * @param input string needed to rename 41 | * @param number_length the length of the number, will prepend symbol 42 | * if the length smaller than this number 43 | * @param symbol symbol prepand before the number 44 | * @return the name after alter 45 | * @example ex : number_length == 3, reg = (\\d+), symbol = '0' 46 | * then "1" == "001", "10" == "010" 47 | * ex : number_length == 2, reg = (\\d+), symbol = '0' 48 | * then "1" == "01", "10" == "10" 49 | */ 50 | QStringList prepend_symbol_on_match(QStringList const &input, int number_length, 51 | QRegularExpression const ®, QChar symbol) 52 | { 53 | QStringList new_file_names = input; 54 | for(int i = 0; i != new_file_names.size(); ++i){ 55 | new_file_names[i] = prepend_symbol_on_match(input[i], number_length, 56 | reg, symbol); 57 | } 58 | 59 | return new_file_names; 60 | } 61 | 62 | /** 63 | * @brief prepend symbol on nth match 64 | * @param input input string 65 | * @param number_length the length of the number, will prepend symbol 66 | * if the length smaller than this number 67 | * @param nth the number of match 68 | * @param reg the pattern want to find 69 | * @param symbol symbol prepand before the number 70 | * @return the input after symbol prepend if match the condition; if the reg 71 | * found the match pattern but the target length less than number_length, do nothing; 72 | * if reg do not find any match, return "the number of match is out of range 73 | * or found no match in this string" 74 | */ 75 | QString prepend_symbol_on_nth_match(QString const &input, int number_length, 76 | int nth, const QRegularExpression ®, QChar symbol) 77 | { 78 | auto it = reg.globalMatch(input); 79 | QRegularExpressionMatch match; 80 | int count = 0; 81 | 82 | while (it.hasNext()){ 83 | match = it.next(); 84 | if(count == nth){ 85 | if(match.hasMatch()){ 86 | QString new_file_name = input; 87 | if(!match.captured(1).isEmpty()){ 88 | if(match.capturedLength(0) < number_length){ 89 | new_file_name.insert(match.capturedStart(0), 90 | QString(number_length - match.capturedLength(0), 91 | symbol)); 92 | } 93 | } 94 | return new_file_name; 95 | }else{ 96 | break; 97 | } 98 | } 99 | ++count; 100 | } 101 | 102 | return "the number of match is out of range or found no match in this string"; 103 | } 104 | 105 | /** 106 | * @brief prepend symbol on nth match 107 | * @param input input string 108 | * @param number_length the length of the number, will prepend symbol 109 | * if the length smaller than this number 110 | * @param nth the number of match 111 | * @param reg the pattern want to find 112 | * @param symbol symbol prepand before the number 113 | * @return the input after symbol prepend if match the condition; if the reg 114 | * found the match pattern but the target length less than number_length, do nothing; 115 | * if reg do not find any match, return "the number of match is out of range 116 | * or not match in this string" 117 | */ 118 | QStringList prepend_symbol_on_nth_match(QStringList const &input, int number_length, 119 | int nth, const QRegularExpression ®, QChar symbol) 120 | { 121 | auto new_file_names = input; 122 | for(int i = 0; i != new_file_names.size(); ++i){ 123 | new_file_names[i] = prepend_symbol_on_nth_match(input[i], number_length, 124 | nth, reg, symbol); 125 | } 126 | 127 | return new_file_names; 128 | } 129 | -------------------------------------------------------------------------------- /model/fileNameModel.cpp: -------------------------------------------------------------------------------- 1 | #include "fileNameModel.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "globalconst.hpp" 13 | #include "../model/fileModelItem.hpp" 14 | #include "../utility/initializer.hpp" 15 | #include "../utility/stringUtility.hpp" 16 | #include "../workerThreads/addDirThread.hpp" 17 | #include "../workerThreads/renameFileThread.hpp" 18 | #include "../workerThreads/scanDirThread.hpp" 19 | 20 | fileNameModel::fileNameModel(QObject *parent) : 21 | QAbstractTableModel{parent}, 22 | item_{new fileModelItem}, 23 | add_dirs_thread_{new addDirThread{this}}, 24 | rename_file_thread_{new renameFileThread{this}}, 25 | scan_dir_thread_{new scanDirThread{this}} 26 | { 27 | connect(scan_dir_thread_, &scanDirThread::scan_dir, 28 | this, &fileNameModel::scan_dir); 29 | connect(scan_dir_thread_, &scanDirThread::end, 30 | this, &fileNameModel::scan_dir_recursive_end_slot); 31 | connect(add_dirs_thread_, &addDirThread::increment, 32 | this, &fileNameModel::increment); 33 | connect(add_dirs_thread_, &addDirThread::end, 34 | this, &fileNameModel::add_dirs_recursive_end_slot); 35 | 36 | connect(rename_file_thread_, &renameFileThread::end, 37 | this, &fileNameModel::rename_file_finished); 38 | connect(rename_file_thread_, &renameFileThread::increment, 39 | this, &fileNameModel::increment); 40 | } 41 | 42 | fileNameModel::~fileNameModel() 43 | { 44 | add_dirs_thread_->quit(); 45 | rename_file_thread_->quit(); 46 | scan_dir_thread_->quit(); 47 | 48 | add_dirs_thread_->wait(); 49 | rename_file_thread_->wait(); 50 | scan_dir_thread_->wait(); 51 | } 52 | 53 | void fileNameModel::add_dirs_recursive(const QString &dir_path) 54 | { 55 | scan_dir_thread_->set_scan_dir(dir_path); 56 | scan_dir_thread_->start(); 57 | } 58 | 59 | void fileNameModel::append_number_on_last_match(int length) 60 | { 61 | item_->new_base_name_ = prepend_symbol_on_match(item_->old_base_name_, 62 | length, 63 | //"(\\d+)[^\\d]*$" 64 | //"(\\d+)(?!.*\\d)" 65 | QRegularExpression("(\\d+)(?!.*\\d)"), 66 | '0'); 67 | } 68 | 69 | void fileNameModel::append_number_on_nth_match(int length, int position) 70 | { 71 | item_->new_base_name_ = prepend_symbol_on_nth_match(item_->old_base_name_, 72 | length, 73 | position, 74 | QRegularExpression("(\\d+)"), 75 | '0'); 76 | } 77 | 78 | void fileNameModel::append_words(QString const &words) 79 | { 80 | item_->new_base_name_ = item_->old_base_name_; 81 | QtConcurrent::blockingMap(item_->new_base_name_, [&words](QString &name) 82 | { 83 | name.append(words); 84 | }); 85 | } 86 | 87 | void fileNameModel::change_name_with_increase_num(const QString &change_file_name_to, int zero_fill) 88 | { 89 | item_->new_base_name_ = item_->old_base_name_; 90 | int index = 0; 91 | QtConcurrent::blockingMap(item_->new_base_name_, [=, &index](QString &name) 92 | { 93 | name = QString("%1_%2").arg(change_file_name_to).arg(index++, zero_fill, 10, QChar('0')); 94 | }); 95 | } 96 | 97 | int fileNameModel::columnCount(const QModelIndex&) const 98 | { 99 | return static_cast(fileNameHeader::LastElem); 100 | } 101 | 102 | void fileNameModel::clear_all() 103 | { 104 | removeRows(0, item_->old_base_name_.size(), {}); 105 | } 106 | 107 | QVariant fileNameModel::data(const QModelIndex &index, int role) const 108 | { 109 | switch(role){ 110 | case Qt::DisplayRole:{ 111 | switch (static_cast(index.column())){ 112 | case fileNameHeader::Path: { 113 | return item_->dir_paths_[index.row()]; 114 | } 115 | case fileNameHeader::OldName: { 116 | return item_->old_base_name_[index.row()]; 117 | } 118 | case fileNameHeader::NewName:{ 119 | return item_->new_base_name_[index.row()]; 120 | } 121 | case fileNameHeader::OldSuffix: { 122 | return item_->old_suffix_[index.row()]; 123 | } 124 | case fileNameHeader::NewSuffix: { 125 | return item_->new_suffix_[index.row()]; 126 | } 127 | default:{ 128 | return {}; 129 | } 130 | } 131 | } 132 | case Qt::ForegroundRole:{ 133 | switch (static_cast(index.column())){ 134 | //NewName and OldName should use the same color 135 | case fileNameHeader::NewName: 136 | case fileNameHeader::OldName:{ 137 | if(item_->new_base_name_[index.row()] == item_->old_base_name_[index.row()]){ 138 | return QBrush(Qt::red); 139 | }else if(item_->match_regex_file_[index.row()]){ 140 | return QBrush(Qt::blue); 141 | } 142 | } 143 | default:{ 144 | return {}; 145 | } 146 | } 147 | } 148 | } 149 | 150 | return {}; 151 | } 152 | 153 | bool fileNameModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int, 154 | int, const QModelIndex&) 155 | { 156 | if(action == Qt::CopyAction && data->hasUrls()){ 157 | auto const Urls = data->urls(); 158 | QStringList files; 159 | for(auto const &Url : Urls){ 160 | QFileInfo const Info(Url.toLocalFile()); 161 | if(Info.isFile()){ 162 | files += Info.absoluteFilePath(); 163 | }else if(Info.isDir()){ 164 | QDir directory(Info.absoluteFilePath()); 165 | files += directory.entryList(QDir::Files | QDir::NoDotAndDotDot); 166 | auto const AbsFilePath = Info.absoluteFilePath() + "/"; 167 | for(QString &file : files){ 168 | file.prepend(AbsFilePath); 169 | } 170 | } 171 | } 172 | if(!files.isEmpty()){ 173 | emit drop_data(files); 174 | return true; 175 | } 176 | } 177 | return false; 178 | } 179 | 180 | Qt::ItemFlags fileNameModel::flags(const QModelIndex &index) const 181 | { 182 | return QAbstractTableModel::flags(index) | Qt::ItemIsDropEnabled; 183 | } 184 | 185 | QStringList fileNameModel::get_rename_error_message() const Q_DECL_NOEXCEPT 186 | { 187 | return rename_file_thread_->get_error_message(); 188 | } 189 | 190 | QVariant fileNameModel::headerData(int section, Qt::Orientation orientation, int role) const 191 | { 192 | if(role == Qt::DisplayRole){ 193 | if(orientation == Qt::Horizontal){ 194 | switch(static_cast(section)){ 195 | case fileNameHeader::Path: { 196 | return QStringLiteral("Path"); 197 | } 198 | case fileNameHeader::OldName: { 199 | return QStringLiteral("Original name"); 200 | } 201 | case fileNameHeader::NewName: { 202 | return QStringLiteral("New name"); 203 | } 204 | case fileNameHeader::OldSuffix: { 205 | return QStringLiteral("Original suffix"); 206 | } 207 | case fileNameHeader::NewSuffix: { 208 | return QStringLiteral("New suffix"); 209 | } 210 | default:{ 211 | return {}; 212 | } 213 | } 214 | }else if(orientation == Qt::Vertical){ 215 | return section + 1; 216 | } 217 | } 218 | return {}; 219 | } 220 | 221 | void fileNameModel::insert_at(int position, const QString &words) 222 | { 223 | item_->new_base_name_ = item_->old_base_name_; 224 | QtConcurrent::blockingMap(item_->new_base_name_, [position, &words](QString &name) 225 | { 226 | name.insert(position, words); 227 | }); 228 | } 229 | 230 | /** 231 | * @brief insert rows into the model 232 | * @param row row number begin to index 233 | * @param count how many rows need to insert 234 | * @return always true 235 | */ 236 | bool fileNameModel::insertRows(int row, int count, const QModelIndex&) 237 | { 238 | if(item_->insert_file_name_.isEmpty()){ 239 | beginInsertRows({}, row, row + count - 1); 240 | for(int i = 0; i != count; ++i){ 241 | item_->add_new_file(); 242 | } 243 | endInsertRows(); 244 | }else if(item_->insert_file_name_.size() > row){ 245 | beginInsertRows(QModelIndex(), row, row + count - 1); 246 | for(int i = row, size = item_->insert_file_name_.size(); 247 | i != size; ++i){ 248 | item_->add_new_file(item_->insert_file_name_[i]); 249 | } 250 | endInsertRows(); 251 | } 252 | 253 | return true; 254 | } 255 | 256 | QStringList fileNameModel::mimeTypes() const 257 | { 258 | return QStringList()<<"text/uri-list"; 259 | } 260 | 261 | /** 262 | * @brief add number into the files based on the string "$#" 263 | * 264 | * 265 | * ex : llvm$# will replace the string as\n 266 | * llvm000, llvm001, llvm002... 267 | * @param start start number, should not be negative value 268 | * @param increment increment step, should not be negative value 269 | * @param reset reset to start when the number reach reset number.\n 270 | * Should not be negative value 271 | * @param pad padding string, must be the same characters.\n 272 | * ex : 000, ###, 111 and so on 273 | */ 274 | void fileNameModel::numbering(int start, int increment, 275 | int reset, const QString &pad) 276 | { 277 | auto const BaseSize = item_->new_base_name_.size(); 278 | auto const Symbol = !pad.isEmpty() ? pad[0] : QChar{'0'}; 279 | auto const PadSize = pad.size() > 0 ? pad.size() : 1; 280 | auto const InitValue = start; 281 | for(decltype(item_->new_base_name_.size()) i = 0; i != BaseSize; ++i){ 282 | auto const Number = QString("%1").arg(start, PadSize, 10, 283 | Symbol); 284 | item_->new_base_name_[i].replace("$#", Number); 285 | start += increment; 286 | if(reset != 0 && start > reset){ 287 | start = InitValue; 288 | } 289 | } 290 | } 291 | 292 | void fileNameModel::prepend_words(const QString &words) 293 | { 294 | item_->new_base_name_ = item_->old_base_name_; 295 | QtConcurrent::blockingMap(item_->new_base_name_, [&words](QString &name) 296 | { 297 | name.prepend(words); 298 | }); 299 | } 300 | 301 | void fileNameModel::remove_at(int position, int n) 302 | { 303 | item_->new_base_name_ = item_->old_base_name_; 304 | QtConcurrent::blockingMap(item_->new_base_name_, [position, n](QString &name) 305 | { 306 | name.remove(position, n); 307 | }); 308 | } 309 | 310 | bool fileNameModel::removeRow(int row, const QModelIndex&) 311 | { 312 | return removeRows(row, 1, {}); 313 | } 314 | 315 | bool fileNameModel::removeRows(int row, int count, const QModelIndex&) 316 | { 317 | beginRemoveRows({}, row, row + count - 1); 318 | for(int i = 0; i != count; ++i){ 319 | item_->remove_at(row); 320 | } 321 | endRemoveRows(); 322 | 323 | return true; 324 | } 325 | 326 | void fileNameModel::rename_files() 327 | { 328 | rename_file_thread_->set_dirs_to_scan(item_->dir_paths_); 329 | rename_file_thread_->set_old_suffix(item_->old_suffix_); 330 | rename_file_thread_->set_old_file_names(item_->old_base_name_); 331 | rename_file_thread_->set_new_file_names(&(item_->new_base_name_)); 332 | rename_file_thread_->set_new_suffix(item_->new_suffix_); 333 | rename_file_thread_->start(); 334 | } 335 | 336 | void fileNameModel::replace_regex(const QRegularExpression &re, const QString &after) 337 | { 338 | if(re.pattern().isEmpty()){ 339 | clear_match_regex_file(); 340 | item_->new_base_name_ = item_->old_base_name_; 341 | return; 342 | } 343 | item_->new_base_name_ = item_->old_base_name_; 344 | for(decltype(item_->new_base_name_.size()) i = 0; 345 | i != item_->new_base_name_.size(); ++i){ 346 | if(re.match(item_->new_base_name_[i]).hasMatch()){ 347 | item_->new_base_name_[i].replace(re, after); 348 | item_->match_regex_file_[i] = true; 349 | }else{ 350 | item_->match_regex_file_[i] = false; 351 | } 352 | } 353 | } 354 | 355 | void fileNameModel::replace_suffix(const QString &from, 356 | const QString &to) 357 | { 358 | item_->new_suffix_ = item_->old_suffix_; 359 | QtConcurrent::blockingMap(item_->new_suffix_, [&from, &to](QString &name) 360 | { 361 | name.replace(from, to); 362 | }); 363 | } 364 | 365 | void fileNameModel::replace_text(QString const &from, QString const &to) 366 | { 367 | item_->new_base_name_ = item_->old_base_name_; 368 | QtConcurrent::blockingMap(item_->new_base_name_, [&from, &to](QString &name) 369 | { 370 | name.replace(from, to); 371 | }); 372 | } 373 | 374 | void fileNameModel::revert_rename_result() 375 | { 376 | auto const RevertMap = rename_file_thread_->get_revert_map(); 377 | clear_all(); 378 | auto const &RenamedFile = RevertMap.first; 379 | auto const &RevertName = RevertMap.second; 380 | insert_rows_if_needed(RenamedFile.size(), 0); 381 | 382 | for(decltype(RenamedFile.size()) i = 0; i != RenamedFile.size(); ++i){ 383 | QFileInfo const info(RenamedFile[i]); 384 | item_->dir_paths_[i] = info.absolutePath(); 385 | item_->old_suffix_[i] = info.suffix(); 386 | item_->old_base_name_[i] = info.completeBaseName(); 387 | 388 | QFileInfo const new_info(RevertName[i]); 389 | item_->new_suffix_[i] = new_info.suffix(); 390 | item_->new_base_name_[i] = new_info.completeBaseName(); 391 | } 392 | 393 | rename_file_thread_->enable_revert(); 394 | item_->source_indexes_.clear(); 395 | rename_files(); 396 | } 397 | 398 | int fileNameModel::rowCount(const QModelIndex&) const 399 | { 400 | return item_->old_base_name_.size(); 401 | } 402 | 403 | bool fileNameModel::setData(const QModelIndex &index, const QVariant &value, int role) 404 | { 405 | if(!index.isValid() || role != Qt::DisplayRole){ 406 | return false; 407 | } 408 | 409 | switch(static_cast(index.column())){ 410 | case fileNameHeader::Path:{ 411 | item_->dir_paths_[index.row()] = value.value(); 412 | break; 413 | } 414 | case fileNameHeader::OldName:{ 415 | item_->old_base_name_[index.row()] = value.value(); 416 | break; 417 | } 418 | case fileNameHeader::NewName:{ 419 | item_->new_base_name_[index.row()] = value.value(); 420 | break; 421 | } 422 | case fileNameHeader::OldSuffix:{ 423 | item_->old_suffix_[index.row()] = value.value(); 424 | break; 425 | } 426 | case fileNameHeader::NewSuffix:{ 427 | item_->new_suffix_[index.row()] = value.value(); 428 | break; 429 | } 430 | default: 431 | break; 432 | } 433 | 434 | return true; 435 | } 436 | 437 | void fileNameModel::set_selected_index(std::vector &&indexes) 438 | { 439 | item_->source_indexes_ = std::move(indexes); 440 | rename_file_thread_->set_source_indexes(item_->source_indexes_); 441 | } 442 | 443 | void fileNameModel::update_file(const QStringList &files) 444 | { 445 | //make sure it provide strong exception guarantee 446 | if(!files.isEmpty()){ 447 | item_->insert_file_name_ = item_->old_file_name_complete_ + files; 448 | item_->insert_file_name_.removeDuplicates(); 449 | item_->match_regex_file_.clear(); 450 | item_->match_regex_file_.resize(item_->insert_file_name_.size()); 451 | std::fill(std::begin(item_->match_regex_file_), 452 | std::end(item_->match_regex_file_), false); 453 | insert_rows_if_needed(item_->insert_file_name_.size(), 454 | item_->dir_paths_.size()); 455 | item_->insert_file_name_.clear(); 456 | } 457 | } 458 | 459 | void fileNameModel::add_dirs_recursive_end_slot() 460 | { 461 | auto const Results = add_dirs_thread_->get_old_file_name_complete(); 462 | update_file(Results); 463 | 464 | emit add_dirs_recursive_end(); 465 | } 466 | 467 | void fileNameModel::rename_file_finished() 468 | { 469 | auto const NotRevert = !item_->source_indexes_.empty(); 470 | if(NotRevert){ 471 | for(auto const &Index : item_->source_indexes_){ 472 | auto const Row = Index.row(); 473 | item_->old_base_name_[Row] = item_->new_base_name_[Row]; 474 | item_->old_suffix_[Row] = item_->new_suffix_[Row]; 475 | } 476 | }else{ 477 | item_->old_base_name_ = item_->new_base_name_; 478 | item_->old_suffix_ = item_->new_suffix_; 479 | } 480 | emit rename_file_end(); 481 | } 482 | 483 | void fileNameModel::scan_dir_recursive_end_slot() 484 | { 485 | auto const dirs = scan_dir_thread_->get_result_dirs(); 486 | if(!dirs.empty()){ 487 | emit set_size(dirs.size()); 488 | emit set_visible(true); 489 | add_dirs_thread_->set_dirs_to_scan(dirs); 490 | add_dirs_thread_->start(); 491 | }else{ 492 | emit add_dirs_recursive_end(); 493 | } 494 | } 495 | 496 | void fileNameModel::clear_match_regex_file() 497 | { 498 | std::fill(std::begin(item_->match_regex_file_), 499 | std::end(item_->match_regex_file_), false); 500 | } 501 | 502 | void fileNameModel::insert_data(const QStringList &input, fileNameHeader column) 503 | { 504 | auto const Size = input.size(); 505 | insert_rows_if_needed(Size, item_->dir_paths_.size()); 506 | for(int i = 0; i != Size; ++i){ 507 | setData(index(i, static_cast(column)), input[i], Qt::DisplayRole); 508 | } 509 | } 510 | 511 | /** 512 | * @brief insert new rows if previous_size < current_size 513 | * @param current_size size of current data 514 | * @param previous_size size of previous data 515 | */ 516 | void fileNameModel::insert_rows_if_needed(int current_size, int previous_size) 517 | { 518 | if(previous_size < current_size){ 519 | insertRows(previous_size, current_size - previous_size, {}); 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /ui/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "filterDialog.hpp" 2 | #include "MainWindow.hpp" 3 | #include "numberStep.hpp" 4 | #include "ui_MainWindow.h" 5 | 6 | #include "globalconst.hpp" 7 | #include "model/fileNameModel.hpp" 8 | 9 | #include "modelProxy/sortAndFilterFile.hpp" 10 | 11 | #include "utility/quickCloseSignal.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace{ 25 | 26 | auto const Append = QStringLiteral("Append words"); 27 | auto const InsertWords = QStringLiteral("Insert words"); 28 | auto const Prepend = QStringLiteral("Prepend words"); 29 | auto const PrependZeroOnNumber = QStringLiteral("Prepend zero on number"); 30 | auto const RegexReplace = QStringLiteral("Regex"); 31 | auto const RemoveWords = QStringLiteral("Remove words"); 32 | auto const ReplaceBase = QStringLiteral("Replace words"); 33 | auto const ReplaceSuffix = QStringLiteral("Replace suffix"); 34 | QString const ChangeNameWithIncreaseNum = "Change name with increase num"; 35 | 36 | } 37 | 38 | MainWindow::MainWindow(QWidget *parent) : 39 | QMainWindow{parent}, 40 | file_model_{new fileNameModel{this}}, 41 | sort_filter_file_{new sortAndFilterFile{this}}, 42 | filter_{new filterDialog{this}}, 43 | num_step_{new numberStep{this}}, 44 | ui{new Ui::MainWindow} 45 | { 46 | ui->setupUi(this); 47 | 48 | QSettings setting{"ThamSoftWare", "QRenamer"}; 49 | restoreGeometry(setting.value("geometry").toByteArray()); 50 | 51 | build_connection(); 52 | build_table(); 53 | 54 | sort_filter_file_->setSourceModel(file_model_); 55 | sort_filter_file_->setDynamicSortFilter(false); 56 | ui->fileView->setSortingEnabled(true); 57 | ui->fileView->setModel(sort_filter_file_); 58 | 59 | ui->fileView->setSelectionBehavior(QAbstractItemView::SelectRows); 60 | ui->fileView->setSelectionMode(QAbstractItemView::ExtendedSelection); 61 | ui->fileView->setColumnHidden(static_cast(fileNameHeader::Path), true); 62 | show_suffix(false); 63 | 64 | ui->numberComboBox->addItems(QStringList()<<"last number"<<"custom"); 65 | ui->numberComboBox->setCurrentText(tr("last number")); 66 | 67 | ui->renameProgressBar->setVisible(false); 68 | ui->actionRevert->setEnabled(false); 69 | 70 | ui->numStepToolButton->setVisible(false); 71 | } 72 | 73 | MainWindow::~MainWindow() 74 | { 75 | delete ui; 76 | } 77 | 78 | void MainWindow::closeEvent(QCloseEvent *event) 79 | { 80 | QSettings setting{"ThamSoftWare", "QRenamer"}; 81 | setting.setValue("geometry", saveGeometry()); 82 | 83 | event->accept(); 84 | } 85 | 86 | void MainWindow::drop_data(QStringList files) 87 | { 88 | file_model_->update_file(files); 89 | update_new_file_names(); 90 | enable_action_when_file_exist(true); 91 | } 92 | 93 | void MainWindow::add_dir_recursive_finished() 94 | { 95 | setEnabled(true); 96 | ui->renameProgressBar->setVisible(false); 97 | update_new_file_names(); 98 | if(file_model_->rowCount()){ 99 | enable_action_when_file_exist(true); 100 | } 101 | } 102 | 103 | void MainWindow::close_help_reader() 104 | { 105 | help_reader_.reset(nullptr); 106 | } 107 | 108 | void MainWindow::enable_delete_action(bool value) 109 | { 110 | ui->actionDeleteFiles->setEnabled(value); 111 | ui->actionRemoveAll->setEnabled(value); 112 | } 113 | 114 | void MainWindow::enable_filter_action(bool value) 115 | { 116 | ui->actionFilter->setEnabled(value); 117 | } 118 | 119 | void MainWindow::enable_remove_filter(bool value) 120 | { 121 | ui->actionRemovefilter->setEnabled(value); 122 | } 123 | 124 | void MainWindow::insert_words() 125 | { 126 | file_model_->insert_at(ui->insertAtSpinBox->value(), 127 | ui->insertWordsLineEdit->text()); 128 | } 129 | 130 | void MainWindow::enable_gui() 131 | { 132 | setEnabled(true); 133 | ui->renameProgressBar->setVisible(false); 134 | } 135 | 136 | void MainWindow::filter() 137 | { 138 | sort_filter_file_->set_regex(QRegularExpression{filter_->get_filter_string(), 139 | filter_->get_filter_pattern()}, 140 | false); 141 | sort_filter_file_->set_filter_type(filter_->get_filter_type(), false); 142 | sort_filter_file_->set_filter_column(filter_->get_filter_column()); 143 | ui->fileView->selectAll(); 144 | ui->renameFileButton->setEnabled(!view_is_empty()); 145 | enable_remove_filter(true); 146 | } 147 | 148 | void MainWindow::regex_replace_and_reset_view() 149 | { 150 | regex_replace(); 151 | ui->fileView->reset(); 152 | } 153 | 154 | void MainWindow::add_directory(QString const &dir) 155 | { 156 | QDir directory(dir); 157 | auto file_name = directory.entryList(QDir::Files); 158 | auto const Dir = dir + "/"; 159 | QtConcurrent::blockingMap(file_name, [&Dir](QString &data) 160 | { 161 | data.prepend(Dir); 162 | }); 163 | 164 | if(!file_name.isEmpty()){ 165 | file_model_->update_file(file_name); 166 | update_new_file_names(); 167 | enable_action_when_file_exist(true); 168 | } 169 | } 170 | 171 | void MainWindow::add_files() 172 | { 173 | auto const &temp = QFileDialog::getOpenFileNames(this, tr("Open files"), 174 | dir_path_, QString(), nullptr, 175 | QFileDialog::DontResolveSymlinks 176 | ); 177 | if(!temp.isEmpty()){ 178 | dir_path_ = QFileInfo(temp[0]).absolutePath(); 179 | file_model_->update_file(temp); 180 | enable_action_when_file_exist(true); 181 | } 182 | } 183 | 184 | void MainWindow::append_words() 185 | { 186 | file_model_->append_words(ui->appendLineEdit->text()); 187 | } 188 | 189 | void MainWindow::build_connection() 190 | { 191 | connect(filter_, &filterDialog::ok, this, &MainWindow::filter); 192 | 193 | connect(file_model_, &fileNameModel::drop_data, 194 | this, &MainWindow::drop_data); 195 | connect(file_model_, &fileNameModel::scan_dir, 196 | this, &MainWindow::show_status_bar); 197 | connect(file_model_, &fileNameModel::set_size, 198 | ui->renameProgressBar, &QProgressBar::setMaximum); 199 | connect(file_model_, &fileNameModel::set_visible, 200 | ui->renameProgressBar, &QProgressBar::setVisible); 201 | connect(file_model_, &fileNameModel::add_dirs_recursive_end, 202 | this, &MainWindow::add_dir_recursive_finished); 203 | 204 | connect(file_model_, &fileNameModel::rename_file_end, 205 | this, &MainWindow::rename_file_fisnished); 206 | connect(file_model_, &fileNameModel::increment, 207 | ui->renameProgressBar, &QProgressBar::setValue); 208 | 209 | connect(num_step_, &numberStep::ok_button_clicked, this, 210 | &MainWindow::regex_replace_and_reset_view); 211 | } 212 | 213 | void MainWindow::change_name_with_increase_num() 214 | { 215 | file_model_->change_name_with_increase_num(ui->lineEditChangeToFileName->text(), 216 | ui->spinBoxZeroFill->value()); 217 | } 218 | 219 | void MainWindow::enable_action_when_file_exist(bool value) 220 | { 221 | enable_delete_action(value); 222 | enable_filter_action(value); 223 | ui->renameFileButton->setEnabled(value); 224 | } 225 | 226 | void MainWindow::build_table() 227 | { 228 | rename_policy_.emplace(Append, std::bind(&MainWindow::append_words, this)); 229 | rename_policy_.emplace(InsertWords, std::bind(&MainWindow::insert_words, this)); 230 | rename_policy_.emplace(Prepend, std::bind(&MainWindow::prepend_words, this)); 231 | rename_policy_.emplace(PrependZeroOnNumber, std::bind(&MainWindow::update_number, this)); 232 | rename_policy_.emplace(RegexReplace, std::bind(&MainWindow::regex_replace, this)); 233 | rename_policy_.emplace(RemoveWords, std::bind(&MainWindow::remove_words, this)); 234 | rename_policy_.emplace(ReplaceBase, std::bind(&MainWindow::replace_text, this)); 235 | rename_policy_.emplace(ReplaceSuffix, std::bind(&MainWindow::replace_suffix, this)); 236 | rename_policy_.emplace(ChangeNameWithIncreaseNum, std::bind(&MainWindow::change_name_with_increase_num, this)); 237 | } 238 | 239 | std::vector MainWindow::map_proxy_index_to_source_index() const 240 | { 241 | std::vector results; 242 | auto const Indexes = ui->fileView->selectionModel()->selectedRows(); 243 | for(auto const &Index : Indexes){ 244 | results.emplace_back(sort_filter_file_->mapToSource(Index)); 245 | } 246 | 247 | return results; 248 | } 249 | 250 | /** 251 | * @brief enable the MainWindow after the rename process end 252 | */ 253 | void MainWindow::rename_file_fisnished() 254 | { 255 | setEnabled(true); 256 | filter_->setEnabled(true); 257 | ui->renameProgressBar->setVisible(false); 258 | 259 | if(!file_model_->get_rename_error_message().isEmpty()){ 260 | QStringListModel model(file_model_->get_rename_error_message()); 261 | QTableView view; 262 | view.setWindowTitle("Error messages"); 263 | view.setModel(&model); 264 | view.horizontalHeader()->hide(); 265 | 266 | QEventLoop loop; 267 | view.resizeColumnsToContents(); 268 | view.resize(QSize{view.width(), view.height()}); 269 | view.show(); 270 | loop.exec(); 271 | } 272 | 273 | ui->fileView->reset(); 274 | if(file_model_->rowCount() != 0){ 275 | enable_action_when_file_exist(true); 276 | } 277 | } 278 | 279 | void MainWindow::show_status_bar(QString message, int msec) 280 | { 281 | statusBar()->showMessage(message, msec); 282 | } 283 | 284 | void MainWindow::open_directory() 285 | { 286 | QFileDialog dialog(nullptr, tr("Open Directory")); 287 | dialog.setAcceptMode(QFileDialog::AcceptOpen); 288 | dialog.setFileMode(QFileDialog::DirectoryOnly); 289 | dialog.setOption(QFileDialog::ShowDirsOnly); 290 | dialog.setOption(QFileDialog::DontResolveSymlinks); 291 | if(dialog.exec()){ 292 | dir_path_ = dialog.selectedFiles()[0]; 293 | }else{ 294 | dir_path_.clear(); 295 | } 296 | 297 | raise(); 298 | } 299 | 300 | void MainWindow::prepend_words() 301 | { 302 | file_model_->prepend_words(ui->prependLineEdit->text()); 303 | } 304 | 305 | void MainWindow::regex_replace() 306 | { 307 | auto const Option = ui->icaseCheckBox->isChecked() ? QRegularExpression::CaseInsensitiveOption : 308 | QRegularExpression::NoPatternOption; 309 | auto const Regex = QRegularExpression{ui->regexMatchlineEdit->text(), Option}; 310 | if(Regex.isValid()){ 311 | file_model_->replace_regex(Regex, 312 | ui->regexReplacelineEdit->text()); 313 | if(ui->numRadioButton->isChecked()){ 314 | file_model_->numbering(num_step_->start(), 315 | num_step_->increment(), 316 | num_step_->reset(), 317 | num_step_->pad()); 318 | } 319 | } 320 | } 321 | 322 | void MainWindow::remove_words() 323 | { 324 | file_model_->remove_at(ui->removeAtSpinBox->value(), 325 | ui->removeRanageSpinBox->value()); 326 | } 327 | 328 | void MainWindow::replace_text() 329 | { 330 | file_model_->replace_text(ui->replaceFromLineEdit->text(), 331 | ui->replaceTolineEdit->text()); 332 | } 333 | 334 | void MainWindow::update_new_file_names() 335 | { 336 | auto const RenamePolicy = ui->policyTabWidget->tabText(ui->policyTabWidget->currentIndex()); 337 | if(auto const It = rename_policy_.find(RenamePolicy); It != std::end(rename_policy_)){ 338 | It->second(); 339 | }else{ 340 | QMessageBox::warning(this, tr("Update fail"), 341 | tr("Do not support this function or under construct")); 342 | } 343 | } 344 | 345 | /** 346 | * @brief update the view of new files, remember to call 347 | * "update_old_name_view" if the old file names have changed 348 | */ 349 | void MainWindow::update_number() 350 | { 351 | if(ui->numberComboBox->currentText() == "last number"){ 352 | file_model_->append_number_on_last_match(ui->numberSpinBox->value()); 353 | }else{ 354 | file_model_->append_number_on_nth_match(ui->numberSpinBox->value(), 355 | ui->positionSpinBox->value() - 1); 356 | } 357 | } 358 | 359 | void MainWindow::on_actionAddFiles_triggered() 360 | { 361 | add_files(); 362 | 363 | update_new_file_names(); 364 | } 365 | 366 | void MainWindow::on_actionAddFolderRecursive_triggered() 367 | { 368 | open_directory(); 369 | if(!dir_path_.isEmpty()){ 370 | setEnabled(false); 371 | file_model_->add_dirs_recursive(dir_path_); 372 | } 373 | } 374 | 375 | void MainWindow::on_actionAddFolder_triggered() 376 | { 377 | open_directory(); 378 | if(!dir_path_.isEmpty()){ 379 | add_directory(dir_path_); 380 | } 381 | 382 | update_new_file_names(); 383 | } 384 | 385 | void MainWindow::on_actionDeleteFiles_triggered() 386 | { 387 | auto *const model = ui->fileView->selectionModel(); 388 | int offset = 0; 389 | for(auto const &var : model->selectedRows()){ 390 | int const Index = var.row() - offset; 391 | file_model_->removeRow(Index, {}); 392 | ++offset; 393 | } 394 | 395 | if(file_model_->rowCount() == 0){ 396 | enable_action_when_file_exist(false); 397 | } 398 | } 399 | 400 | /** 401 | * @brief remove the names from the name views(old and new) 402 | */ 403 | void MainWindow::on_actionRemoveAll_triggered() 404 | { 405 | file_model_->clear_all(); 406 | ui->fileView->reset(); 407 | enable_action_when_file_exist(false); 408 | } 409 | 410 | void MainWindow::on_appendLineEdit_textChanged(const QString&) 411 | { 412 | append_words(); 413 | ui->fileView->reset(); 414 | } 415 | 416 | void MainWindow::on_numberComboBox_currentTextChanged(QString const&) 417 | { 418 | if(ui->numberComboBox->currentText() == "last number"){ 419 | ui->positionSpinBox->setEnabled(false); 420 | }else{ 421 | ui->positionSpinBox->setEnabled(true); 422 | } 423 | 424 | update_number(); 425 | ui->fileView->reset(); 426 | } 427 | 428 | void MainWindow::on_numberSpinBox_valueChanged(int) 429 | { 430 | update_number(); 431 | ui->fileView->reset(); 432 | } 433 | 434 | void MainWindow::on_positionSpinBox_valueChanged(int) 435 | { 436 | on_numberSpinBox_valueChanged(0); 437 | } 438 | 439 | void MainWindow::on_prependLineEdit_textChanged(const QString&) 440 | { 441 | prepend_words(); 442 | ui->fileView->reset(); 443 | } 444 | 445 | /** 446 | * @brief rename the file in the other thread 447 | */ 448 | void MainWindow::on_renameFileButton_clicked() 449 | { 450 | setEnabled(false); 451 | filter_->setEnabled(false); 452 | 453 | ui->fileView->selectAll(); 454 | auto source_indexes = map_proxy_index_to_source_index(); 455 | ui->renameProgressBar->setVisible(true); 456 | ui->renameProgressBar->setRange(0, source_indexes.size()); 457 | ui->actionRevert->setEnabled(true); 458 | ui->fileView->clearSelection(); 459 | 460 | file_model_->set_selected_index(std::move(source_indexes)); 461 | file_model_->rename_files(); 462 | } 463 | 464 | void MainWindow::on_replaceFromLineEdit_textChanged(const QString&) 465 | { 466 | replace_text(); 467 | ui->fileView->reset(); 468 | } 469 | 470 | void MainWindow::on_replaceTolineEdit_textChanged(const QString&) 471 | { 472 | on_replaceFromLineEdit_textChanged(QStringLiteral("")); 473 | } 474 | 475 | void MainWindow::replace_suffix() 476 | { 477 | file_model_->replace_suffix(ui->replaceSuffixFromLineEdit->text(), 478 | ui->replaceSuffixTolineEdit->text()); 479 | } 480 | 481 | void MainWindow::on_replaceSuffixFromLineEdit_textChanged(const QString&) 482 | { 483 | replace_suffix(); 484 | ui->fileView->reset(); 485 | } 486 | 487 | void MainWindow::on_replaceSuffixTolineEdit_textChanged(const QString&) 488 | { 489 | on_replaceSuffixFromLineEdit_textChanged(QString()); 490 | } 491 | 492 | void MainWindow::show_suffix(bool enable) 493 | { 494 | ui->fileView->setColumnHidden(static_cast(fileNameHeader::OldSuffix), 495 | !enable); 496 | ui->fileView->setColumnHidden(static_cast(fileNameHeader::NewSuffix), 497 | !enable); 498 | } 499 | 500 | /** 501 | * @brief detect the view has item or not 502 | * 503 | * 504 | * The model may not be empty because filtering\n 505 | * would not remove the items from the model 506 | * @return true if the view do not show any item;else false 507 | */ 508 | bool MainWindow::view_is_empty() const Q_DECL_NOEXCEPT 509 | { 510 | ui->fileView->selectAll(); 511 | auto const IsEmpty = ui->fileView->selectionModel()->selectedRows().isEmpty(); 512 | ui->fileView->clearSelection(); 513 | 514 | return IsEmpty; 515 | } 516 | 517 | void MainWindow::on_actionShowPath_triggered(bool) 518 | { 519 | ui->fileView->setColumnHidden(static_cast(fileNameHeader::Path), 520 | !ui->actionShowPath->isChecked()); 521 | } 522 | 523 | void MainWindow::on_actionShowSuffix_triggered(bool value) 524 | { 525 | ui->actionShowSuffix->setChecked(value); 526 | show_suffix(value); 527 | } 528 | 529 | void MainWindow::on_policyTabWidget_currentChanged(int index) 530 | { 531 | auto const RenamePolicy = ui->policyTabWidget->tabText(index); 532 | if(RenamePolicy == ReplaceSuffix){ 533 | on_actionShowSuffix_triggered(true); 534 | }else{ 535 | on_actionShowSuffix_triggered(false); 536 | } 537 | file_model_->clear_match_regex_file(); 538 | update_new_file_names(); 539 | ui->fileView->reset(); 540 | } 541 | 542 | void MainWindow::on_actionRevert_triggered(bool) 543 | { 544 | ui->renameProgressBar->setRange(0, file_model_->rowCount()); 545 | ui->renameProgressBar->setVisible(true); 546 | ui->actionRevert->setEnabled(false); 547 | setEnabled(false); 548 | file_model_->revert_rename_result(); 549 | } 550 | 551 | void MainWindow::on_actionFilter_triggered(bool) 552 | { 553 | filter_->show(); 554 | } 555 | 556 | void MainWindow::on_regexMatchlineEdit_textChanged(const QString&) 557 | { 558 | regex_replace_and_reset_view(); 559 | } 560 | 561 | void MainWindow::on_regexReplacelineEdit_textChanged(const QString&) 562 | { 563 | on_regexMatchlineEdit_textChanged(""); 564 | } 565 | 566 | void MainWindow::on_icaseCheckBox_clicked() 567 | { 568 | on_regexMatchlineEdit_textChanged(""); 569 | } 570 | 571 | void MainWindow::on_actionAboutQt_triggered() 572 | { 573 | QMessageBox::aboutQt(this); 574 | } 575 | 576 | void MainWindow::on_insertAtSpinBox_valueChanged(int) 577 | { 578 | insert_words(); 579 | ui->fileView->reset(); 580 | } 581 | 582 | void MainWindow::on_insertWordsLineEdit_textChanged(const QString&) 583 | { 584 | on_insertAtSpinBox_valueChanged(0); 585 | } 586 | 587 | void MainWindow::on_removeAtSpinBox_valueChanged(int) 588 | { 589 | remove_words(); 590 | ui->fileView->reset(); 591 | } 592 | 593 | void MainWindow::on_removeRanageSpinBox_valueChanged(int) 594 | { 595 | on_removeAtSpinBox_valueChanged(0); 596 | } 597 | 598 | void MainWindow::on_numStepToolButton_clicked() 599 | { 600 | num_step_->exec(); 601 | } 602 | 603 | void MainWindow::on_numRadioButton_clicked(bool checked) 604 | { 605 | ui->numStepToolButton->setVisible(checked); 606 | regex_replace_and_reset_view(); 607 | } 608 | 609 | void MainWindow::on_actionRemovefilter_triggered() 610 | { 611 | sort_filter_file_->set_regex(QRegularExpression{""}, false); 612 | sort_filter_file_->set_filter_type(filterType::Include, false); 613 | sort_filter_file_->set_filter_column(fileNameHeader::OldName); 614 | enable_remove_filter(false); 615 | ui->renameFileButton->setEnabled(!view_is_empty()); 616 | } 617 | 618 | void MainWindow::on_actionHelp_triggered() 619 | { 620 | auto const LOCATION = QStringLiteral("qrc:/Main.qml"); 621 | 622 | help_reader_.reset(new quickCloseSignal); 623 | 624 | connect(help_reader_.get(), &quickCloseSignal::widget_close, this, 625 | &MainWindow::close_help_reader); 626 | 627 | help_reader_->setSource(QUrl(LOCATION)); 628 | help_reader_->setResizeMode(QQuickWidget::SizeRootObjectToView); 629 | help_reader_->setWindowTitle(tr("Help menu")); 630 | help_reader_->show(); 631 | } 632 | 633 | void MainWindow::on_actionIcons_triggered() 634 | { 635 | QMessageBox::information(this, tr("Source"), tr("The icons are come from http://www.fatcow.com/free-icons")); 636 | } 637 | 638 | void MainWindow::on_actionReference_triggered() 639 | { 640 | QMessageBox::information(this, tr("Reference"), 641 | tr("The regex functions of this app is reference to regexrenamer" 642 | "(http://regexrenamer.sourceforge.net/),\n" 643 | "many functions and tutorials are inspired by it")); 644 | } 645 | 646 | void MainWindow::on_lineEditChangeToFileName_textChanged(const QString&) 647 | { 648 | change_name_with_increase_num(); 649 | } 650 | 651 | void MainWindow::on_spinBoxZeroFill_valueChanged(int) 652 | { 653 | change_name_with_increase_num(); 654 | } 655 | -------------------------------------------------------------------------------- /ui/MainWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 876 10 | 474 11 | 12 | 13 | 14 | false 15 | 16 | 17 | QRenamer 18 | 19 | 20 | 21 | :/pics/macro_names.png:/pics/macro_names.png 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 0 30 | 0 31 | 32 | 33 | 34 | 8 35 | 36 | 37 | 38 | Prepend zero on number 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 0 51 | 0 52 | 53 | 54 | 55 | Number length 56 | 57 | 58 | Qt::AlignCenter 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 0 67 | 0 68 | 69 | 70 | 71 | 1 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 0 80 | 0 81 | 82 | 83 | 84 | Position 85 | 86 | 87 | Qt::AlignCenter 88 | 89 | 90 | 91 | 92 | 93 | 94 | false 95 | 96 | 97 | 98 | 0 99 | 0 100 | 101 | 102 | 103 | 1 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Prepend words 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | Prepend 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 0 130 | 0 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | Append words 142 | 143 | 144 | 145 | 146 | 147 | Append 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | Insert words 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 0 168 | 0 169 | 170 | 171 | 172 | Insert at 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 0 181 | 0 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 0 191 | 0 192 | 193 | 194 | 195 | Insert words 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 0 204 | 0 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | Remove words 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 0 225 | 0 226 | 227 | 228 | 229 | Remove at 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 0 245 | 0 246 | 247 | 248 | 249 | Range 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | Replace words 263 | 264 | 265 | 266 | 267 | 268 | From 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | To 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | Regex 290 | 291 | 292 | Replace names by regular expression 293 | 294 | 295 | 296 | 297 | 298 | 299 | 0 300 | 0 301 | 302 | 303 | 304 | Match 305 | 306 | 307 | 308 | 309 | 310 | Match 311 | 312 | 313 | 314 | 315 | 316 | 317 | \d+ 318 | 319 | 320 | 321 | 322 | 323 | 324 | Case insensitive 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 0 336 | 0 337 | 338 | 339 | 340 | Replace 341 | 342 | 343 | 344 | 345 | 346 | 347 | 0 348 | 0 349 | 350 | 351 | 352 | Replace 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 0 361 | 0 362 | 363 | 364 | 365 | $# 366 | 367 | 368 | 369 | 370 | 371 | 372 | Numbering 373 | 374 | 375 | true 376 | 377 | 378 | 379 | 380 | 381 | 382 | true 383 | 384 | 385 | Adjust the numbering step 386 | 387 | 388 | ... 389 | 390 | 391 | 392 | :/pics/num_step.png:/pics/num_step.png 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | Replace suffix 404 | 405 | 406 | 407 | 408 | 409 | From 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | To 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | Change name with increase num 431 | 432 | 433 | 434 | 435 | 436 | Change to file name 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | Zero fill 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | true 463 | 464 | 465 | QAbstractItemView::NoEditTriggers 466 | 467 | 468 | QAbstractItemView::DropOnly 469 | 470 | 471 | Qt::CopyAction 472 | 473 | 474 | QAbstractItemView::ExtendedSelection 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | false 484 | 485 | 486 | 487 | 488 | 489 | 490 | :/pics/accept.png:/pics/accept.png 491 | 492 | 493 | Return 494 | 495 | 496 | 497 | 498 | 499 | 500 | 24 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | toolBar 510 | 511 | 512 | false 513 | 514 | 515 | false 516 | 517 | 518 | TopToolBarArea 519 | 520 | 521 | false 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 0 539 | 0 540 | 876 541 | 21 542 | 543 | 544 | 545 | 546 | &View 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | &File 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | &Tools 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | &Help 573 | 574 | 575 | 576 | 577 | 578 | Info 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | true 593 | 594 | 595 | 596 | :/pics/add.png 597 | 598 | 599 | 600 | Add Files 601 | 602 | 603 | Add files 604 | 605 | 606 | F1 607 | 608 | 609 | 610 | 611 | false 612 | 613 | 614 | 615 | :/pics/delete.png:/pics/delete.png 616 | 617 | 618 | Delete files 619 | 620 | 621 | Delete selected files 622 | 623 | 624 | Del 625 | 626 | 627 | 628 | 629 | 630 | :/pics/folder.png:/pics/folder.png 631 | 632 | 633 | Add folder 634 | 635 | 636 | Add the files under specific folder 637 | 638 | 639 | F2 640 | 641 | 642 | 643 | 644 | false 645 | 646 | 647 | 648 | :/pics/clipboard_empty.png:/pics/clipboard_empty.png 649 | 650 | 651 | Remove all 652 | 653 | 654 | Remove all of the files 655 | 656 | 657 | Ctrl+X 658 | 659 | 660 | 661 | 662 | true 663 | 664 | 665 | 666 | :/pics/folders_explorer.png:/pics/folders_explorer.png 667 | 668 | 669 | Add folder recursive 670 | 671 | 672 | Add the files of the folder recursively 673 | 674 | 675 | F3 676 | 677 | 678 | 679 | 680 | true 681 | 682 | 683 | Show path 684 | 685 | 686 | Show the path of the file 687 | 688 | 689 | F7 690 | 691 | 692 | 693 | 694 | true 695 | 696 | 697 | Show suffix 698 | 699 | 700 | Show the suffix of the path 701 | 702 | 703 | F4 704 | 705 | 706 | 707 | 708 | 709 | :/pics/spanHeader.png:/pics/spanHeader.png 710 | 711 | 712 | Span header 713 | 714 | 715 | Span the header of the view 716 | 717 | 718 | Ctrl+S 719 | 720 | 721 | 722 | 723 | false 724 | 725 | 726 | 727 | :/pics/revert.png:/pics/revert.png 728 | 729 | 730 | Revert 731 | 732 | 733 | Revert the file you changed 734 | 735 | 736 | Ctrl+Z 737 | 738 | 739 | 740 | 741 | false 742 | 743 | 744 | 745 | :/pics/filter.png:/pics/filter.png 746 | 747 | 748 | Filter 749 | 750 | 751 | Filter the contents 752 | 753 | 754 | F5 755 | 756 | 757 | 758 | 759 | About Qt 760 | 761 | 762 | 763 | 764 | 765 | :/pics/help.png:/pics/help.png 766 | 767 | 768 | Help 769 | 770 | 771 | 772 | 773 | false 774 | 775 | 776 | 777 | :/pics/filter_clear.png:/pics/filter_clear.png 778 | 779 | 780 | Removefilter 781 | 782 | 783 | Remove filter 784 | 785 | 786 | F6 787 | 788 | 789 | 790 | 791 | 792 | :/pics/app_icons.png:/pics/app_icons.png 793 | 794 | 795 | Icons 796 | 797 | 798 | The link of the icons 799 | 800 | 801 | 802 | 803 | 804 | :/pics/cross_reference.png:/pics/cross_reference.png 805 | 806 | 807 | Reference 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | toolBar 818 | actionTriggered(QAction*) 819 | fileView 820 | resizeColumnsToContents() 821 | 822 | 823 | 171 824 | 41 825 | 826 | 827 | 179 828 | 247 829 | 830 | 831 | 832 | 833 | 834 | --------------------------------------------------------------------------------