diff --git a/CMakeLists.txt b/CMakeLists.txt index 587c016..78e1f2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.12) list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -project(mediacopier VERSION 2.1.0) +project(MediaCopier VERSION 2.1.1) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/mediacopier-cli/source/config.cpp b/mediacopier-cli/source/config.cpp index 28ee0b1..b4e3104 100644 --- a/mediacopier-cli/source/config.cpp +++ b/mediacopier-cli/source/config.cpp @@ -16,13 +16,12 @@ #include "config.hpp" -#include +#include +#include #include #include -#include - #include #include @@ -96,3 +95,31 @@ void Config::finalize() noexcept pattern = DEFAULT_PATTERN; } } + +bool Config::validate() const noexcept +{ + bool result = false; + if (pattern.empty()) { + spdlog::error("No pattern specified!"); + result = true; + } + try { + if (fs::absolute(outputDir).empty() || !fs::is_directory(inputDir)) { + spdlog::error("Input directory does not exist!"); + result = true; + } + } catch(const fs::filesystem_error& err) { + spdlog::error("Failed to validate input directory path: " + std::string{err.what()}); + result = true; + } + try { + if (fs::absolute(outputDir).empty()) { + spdlog::error("No output directory specified"); + result = true; + } + } catch(const fs::filesystem_error& err) { + spdlog::error("Failed to validate output directory path: " + std::string{err.what()}); + result = true; + } + return result; +} diff --git a/mediacopier-cli/source/config.hpp b/mediacopier-cli/source/config.hpp index 8e16a26..e5d5ec6 100644 --- a/mediacopier-cli/source/config.hpp +++ b/mediacopier-cli/source/config.hpp @@ -32,6 +32,7 @@ struct Config void loadPersistentConfig(); void storePersistentConfig() const; void finalize() noexcept; + bool validate() const noexcept; Command cmd = Command::Copy; std::filesystem::path inputDir; diff --git a/mediacopier-cli/source/main.cpp b/mediacopier-cli/source/main.cpp index 20e4a1d..86bdfd5 100644 --- a/mediacopier-cli/source/main.cpp +++ b/mediacopier-cli/source/main.cpp @@ -60,7 +60,7 @@ auto exec(const Config& config) -> void }); spdlog::info("Checking input directory.."); - auto reg = mc::FileRegister{config.outputDir, config.pattern}; + auto fileRegister = mc::FileRegister{config.outputDir, config.pattern}; std::optional dest; for (auto file : valid_media_files(config.inputDir)) { @@ -69,7 +69,7 @@ auto exec(const Config& config) -> void break; } try { - if ((dest = reg.add(file)).has_value()) { + if ((dest = fileRegister.add(file)).has_value()) { spdlog::info("Processing: {} -> {}", file->path().string(), dest.value().string()); @@ -82,7 +82,7 @@ auto exec(const Config& config) -> void } spdlog::info("Removing duplicates in destination directory.."); - reg.removeDuplicates(); + fileRegister.removeDuplicates(); spdlog::info("Done"); std::signal(SIGINT, SIG_DFL); @@ -98,6 +98,11 @@ int main(int argc, char *argv[]) config.loadPersistentConfig(); config.finalize(); + if (config.validate()) { + spdlog::error("Invalid configuration"); + return 1; + } + switch (config.cmd) { case Config::Command::Copy: exec(config); diff --git a/mediacopier-lib/source/operation_simulate.cpp b/mediacopier-lib/source/operation_simulate.cpp index 535bef2..b015922 100644 --- a/mediacopier-lib/source/operation_simulate.cpp +++ b/mediacopier-lib/source/operation_simulate.cpp @@ -20,25 +20,31 @@ #include #include +#include +#include + namespace mediacopier { auto FileOperationSimulate::dumpFilePaths(const AbstractFileInfo& file) const -> void { - spdlog::info(m_destination.string() + " (from " + file.path().string() + ")"); + spdlog::info("{} -> {}", file.path().string(), m_destination.string()); } auto FileOperationSimulate::visit(const FileInfoImage& file) -> void { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); dumpFilePaths(file); } auto FileOperationSimulate::visit(const FileInfoImageJpeg& file) -> void { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); dumpFilePaths(file); } auto FileOperationSimulate::visit(const FileInfoVideo& file) -> void { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); dumpFilePaths(file); } diff --git a/mediacopier-qt/lang/README.md b/mediacopier-qt/lang/README.md new file mode 100644 index 0000000..609d6b3 --- /dev/null +++ b/mediacopier-qt/lang/README.md @@ -0,0 +1,5 @@ +You can update the translations via + +```sh +linguist-qt5 mediacopier-qt/lang/lang_de.ts +``` diff --git a/mediacopier-qt/lang/lang_de.ts b/mediacopier-qt/lang/lang_de.ts index 0edaecf..46bb3ff 100644 --- a/mediacopier-qt/lang/lang_de.ts +++ b/mediacopier-qt/lang/lang_de.ts @@ -5,21 +5,14 @@ Command - Copy Kopieren - - + Move Verschieben - - - Simulate - Simulation - KMediaCopierJob @@ -45,28 +38,34 @@ MediaCopierParamWidget - + Operation - + Input Quellverzeichnis - + Pattern Schema - + Output Zielverzeichnis - - + + Update + Ändern + Ändern + + + + ... @@ -74,12 +73,12 @@ QObject - + Source folder Quellverzeichnis - + Destination folder Zielverzeichnis diff --git a/mediacopier-qt/source/config.cpp b/mediacopier-qt/source/config.cpp index 50eb806..ece0d4e 100644 --- a/mediacopier-qt/source/config.cpp +++ b/mediacopier-qt/source/config.cpp @@ -24,19 +24,16 @@ namespace fs = std::filesystem; static constexpr const char* CONFIG_FILE = ".mediacopier"; +static constexpr const char* DEFAULT_PATTERN = "%Y/%W/IMG_%Y%m%d_%H%M%S"; static const std::map commands = { - {"copy", Config::Command::COPY_JPEG}, - {"move", Config::Command::MOVE_JPEG}, - {"sim", Config::Command::SIMULATE} + {"copy", Config::Command::COPY}, + {"move", Config::Command::MOVE} }; static const std::map commandStrings = { {Config::Command::COPY, QT_TRANSLATE_NOOP("Command", "Copy")}, - {Config::Command::COPY_JPEG, QT_TRANSLATE_NOOP("Command", "Copy")}, - {Config::Command::MOVE, QT_TRANSLATE_NOOP("Command", "Move")}, - {Config::Command::MOVE_JPEG, QT_TRANSLATE_NOOP("Command", "Move")}, - {Config::Command::SIMULATE, QT_TRANSLATE_NOOP("Command", "Simulate")} + {Config::Command::MOVE, QT_TRANSLATE_NOOP("Command", "Move")} }; Config::Config(const QApplication& app) @@ -45,52 +42,38 @@ Config::Config(const QApplication& app) parser.setApplicationDescription( app.applicationName() + - ", Copyright (C) 2020-2023 Patrick Ziegler"); - + ", Copyright (C) 2020-2024 Patrick Ziegler"); + parser.addPositionalArgument( + "CMD", "Available commands: copy (default), move", "[CMD"); parser.addPositionalArgument( "SRC", "Input directory", "[SRC"); - parser.addPositionalArgument( - "DST", "Output directory", "[DST]]"); - - QCommandLineOption optCommand( - "c", "Available commands: copy (default), move, sim", - "command"); - - QCommandLineOption optPattern( - "f", "Pattern to be used for creating new filenames", - "pattern"); - + "DST", "Output directory", "[DST]]]"); QCommandLineOption optSlimGui( "slim-gui", "Pattern to be used for creating new filenames"); - QCommandLineOption optNoGui( "no-gui", "Pattern to be used for creating new filenames"); - parser.addOptions({optCommand, optPattern, optSlimGui, optNoGui}); + parser.addOptions({optSlimGui, optNoGui}); parser.addVersionOption(); parser.addHelpOption(); parser.process(app); - if (parser.positionalArguments().length() > 0) - setInputDir(parser.positionalArguments().at(0)); - - if (parser.positionalArguments().length() > 1) - setOutputDir(parser.positionalArguments().at(1)); - - readConfigFile(); - - if (parser.isSet("f")) - setPattern(parser.value("f")); - - if (parser.isSet("c")) - setCommand(parser.value("c")); - - if (parser.isSet("slim-gui")) - m_ui = UI::SlimGui; + if (parser.positionalArguments().length() > 0) { + setCommand(parser.positionalArguments().at(0)); + } + if (parser.positionalArguments().length() > 1) { + setInputDir(parser.positionalArguments().at(1)); + } + if (parser.positionalArguments().length() > 2) { + setOutputDir(parser.positionalArguments().at(2)); + readConfigFile(); + } + if (parser.isSet("slim-gui")) { + m_guiType = GuiType::Slim; + } - if (parser.isSet("no-gui")) - m_ui = UI::NoGui; + m_pattern = DEFAULT_PATTERN; } bool Config::readConfigFile() noexcept @@ -141,6 +124,11 @@ void Config::setPattern(const QString& pattern) m_pattern = pattern.toStdString(); } +void Config::resetPattern() +{ + m_pattern = DEFAULT_PATTERN; +} + void Config::setInputDir(const QString& inputDir) { m_inputDir = inputDir.toStdString(); diff --git a/mediacopier-qt/source/config.hpp b/mediacopier-qt/source/config.hpp index 66604a8..5d2998d 100644 --- a/mediacopier-qt/source/config.hpp +++ b/mediacopier-qt/source/config.hpp @@ -24,16 +24,12 @@ class Config { public: enum class Command { COPY, - MOVE, - COPY_JPEG, - MOVE_JPEG, - SIMULATE + MOVE }; - enum class UI { - FullGui, - SlimGui, - NoGui + enum class GuiType { + Full, + Slim, }; Config(const QApplication& app); @@ -44,21 +40,22 @@ class Config { void setCommand(const Command& command); void setCommand(const QString& command); void setPattern(const QString& pattern); + void resetPattern(); void setInputDir(const QString& inputDir); void setOutputDir(const QString& outputDir); static const QString commandString(const Command& command); const Command& command() const { return m_command; } - const UI& ui() const { return m_ui; } + const GuiType& guiType() const { return m_guiType; } const std::string& pattern() const { return m_pattern; } const std::filesystem::path& inputDir() const { return m_inputDir; } const std::filesystem::path& outputDir() const { return m_outputDir; } private: - Command m_command = Command::COPY_JPEG; - UI m_ui = UI::FullGui; - std::string m_pattern = "%Y/%W/IMG_%Y%m%d_%H%M%S"; + Command m_command = Command::COPY; + GuiType m_guiType = GuiType::Full; + std::string m_pattern; std::filesystem::path m_inputDir; std::filesystem::path m_outputDir; }; diff --git a/mediacopier-qt/source/gui/MediaCopierDialogFull.cpp b/mediacopier-qt/source/gui/MediaCopierDialogFull.cpp index 2359b34..9f91c0c 100644 --- a/mediacopier-qt/source/gui/MediaCopierDialogFull.cpp +++ b/mediacopier-qt/source/gui/MediaCopierDialogFull.cpp @@ -56,16 +56,19 @@ void MediaCopierDialogFull::init(std::shared_ptr config, QApplication& a fsm = std::make_unique(); auto s1 = new QState(); // waiting for input - auto s2 = new QState(); // executing operation - auto s3 = new QState(); // aborting operation - auto s4 = new QFinalState(); // closing dialog + auto s2 = new QState(); // checking parameters + auto s3 = new QState(); // executing operation + auto s4 = new QState(); // aborting operation + auto s5 = new QFinalState(); // closing dialog s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::accepted, s2); - s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s4); - s2->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s3); - s2->addTransition(this, &MediaCopierDialogFull::rejected, s3); - s2->addTransition(this, &MediaCopierDialogFull::operationDone, s1); + s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s5); + s2->addTransition(ui->param, &MediaCopierParamWidget::validParameters, s3); + s2->addTransition(ui->param, &MediaCopierParamWidget::invalidParameters, s1); + s3->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s4); + s3->addTransition(this, &MediaCopierDialogFull::rejected, s4); s3->addTransition(this, &MediaCopierDialogFull::operationDone, s1); + s4->addTransition(this, &MediaCopierDialogFull::operationDone, s1); auto m_btnOk = ui->dialogButtonBox->button(QDialogButtonBox::Ok); auto m_btnCancel = ui->dialogButtonBox->button(QDialogButtonBox::Cancel); @@ -73,17 +76,22 @@ void MediaCopierDialogFull::init(std::shared_ptr config, QApplication& a s1->assignProperty(m_btnOk, "enabled", true); s1->assignProperty(m_btnCancel, "enabled", true); s2->assignProperty(m_btnOk, "enabled", false); - s3->assignProperty(m_btnCancel, "enabled", false); + s2->assignProperty(m_btnCancel, "enabled", false); + s3->assignProperty(m_btnOk, "enabled", false); + s3->assignProperty(m_btnCancel, "enabled", true); + s4->assignProperty(m_btnCancel, "enabled", false); QObject::connect(s1, &QState::entered, this, &MediaCopierDialogFull::awaitOperation); - QObject::connect(s2, &QState::entered, this, &MediaCopierDialogFull::startOperation); - QObject::connect(s3, &QState::entered, this, &MediaCopierDialogFull::cancelOperation); + QObject::connect(s2, &QState::entered, ui->param, &MediaCopierParamWidget::validate); + QObject::connect(s3, &QState::entered, this, &MediaCopierDialogFull::startOperation); + QObject::connect(s4, &QState::entered, this, &MediaCopierDialogFull::cancelOperation); QObject::connect(fsm.get(), &QStateMachine::finished, this, &MediaCopierDialogFull::close); fsm->addState(s1); fsm->addState(s2); fsm->addState(s3); fsm->addState(s4); + fsm->addState(s5); fsm->setInitialState(s1); fsm->start(); } @@ -96,6 +104,7 @@ void MediaCopierDialogFull::aboutToQuit() void MediaCopierDialogFull::startOperation() { + this->ui->log->clear(); worker = std::make_shared(*config); QObject::connect(worker.get(), &Worker::status, this->ui->log, &MediaCopierLogWidget::update); QObject::connect(worker.get(), &Worker::finished, this, &MediaCopierDialogFull::operationDone); diff --git a/mediacopier-qt/source/gui/MediaCopierDialogSlim.cpp b/mediacopier-qt/source/gui/MediaCopierDialogSlim.cpp index 482eb7d..c24fe47 100644 --- a/mediacopier-qt/source/gui/MediaCopierDialogSlim.cpp +++ b/mediacopier-qt/source/gui/MediaCopierDialogSlim.cpp @@ -55,20 +55,25 @@ void MediaCopierDialogSlim::init(std::shared_ptr config, QApplication& a fsm = std::make_unique(); auto s1 = new QState(); // waiting for input - auto s2 = new QState(); // executing operation - auto s3 = new QFinalState(); // closing dialog + auto s2 = new QState(); // checking parameters + auto s3 = new QState(); // executing operation + auto s4 = new QFinalState(); // closing dialog s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::accepted, s2); - s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s3); - s2->addTransition(this, &MediaCopierDialogSlim::operationDone, s3); - - QObject::connect(s2, &QState::entered, this, &MediaCopierDialogSlim::hide); - QObject::connect(s2, &QState::entered, this, &MediaCopierDialogSlim::startOperation); + s1->addTransition(ui->dialogButtonBox, &QDialogButtonBox::rejected, s4); + s2->addTransition(ui->param, &MediaCopierParamWidget::validParameters, s3); + s2->addTransition(ui->param, &MediaCopierParamWidget::invalidParameters, s1); + s3->addTransition(this, &MediaCopierDialogSlim::operationDone, s4); + + QObject::connect(s2, &QState::entered, ui->param, &MediaCopierParamWidget::validate); + QObject::connect(s3, &QState::entered, this, &MediaCopierDialogSlim::hide); + QObject::connect(s3, &QState::entered, this, &MediaCopierDialogSlim::startOperation); QObject::connect(fsm.get(), &QStateMachine::finished, this, &MediaCopierDialogSlim::close); fsm->addState(s1); fsm->addState(s2); fsm->addState(s3); + fsm->addState(s4); fsm->setInitialState(s1); fsm->start(); } diff --git a/mediacopier-qt/source/gui/MediaCopierLogWidget.cpp b/mediacopier-qt/source/gui/MediaCopierLogWidget.cpp index 75e67ea..1a54967 100644 --- a/mediacopier-qt/source/gui/MediaCopierLogWidget.cpp +++ b/mediacopier-qt/source/gui/MediaCopierLogWidget.cpp @@ -29,7 +29,10 @@ MediaCopierLogWidget::MediaCopierLogWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MediaCopierLogWidget) { ui->setupUi(this); - + ui->logText->setPlainText( + QString("%1 v%2") + .arg(mediacopier::MEDIACOPIER_PROJECT_NAME) + .arg(mediacopier::MEDIACOPIER_VERSION)); auto sink = std::make_shared(ui->logText, "appendPlainText"); spdlog::default_logger()->sinks().push_back(std::move(sink)); } @@ -39,6 +42,12 @@ MediaCopierLogWidget::~MediaCopierLogWidget() delete ui; } +void MediaCopierLogWidget::clear() +{ + ui->logProgressBar->setValue(0); + ui->logText->clear(); +} + void MediaCopierLogWidget::update(Status info) { ui->logProgressBar->setMaximum(info.fileCount()); diff --git a/mediacopier-qt/source/gui/MediaCopierLogWidget.hpp b/mediacopier-qt/source/gui/MediaCopierLogWidget.hpp index 8c39645..63229a1 100644 --- a/mediacopier-qt/source/gui/MediaCopierLogWidget.hpp +++ b/mediacopier-qt/source/gui/MediaCopierLogWidget.hpp @@ -33,6 +33,7 @@ class MediaCopierLogWidget : public QWidget public: explicit MediaCopierLogWidget(QWidget *parent=nullptr); ~MediaCopierLogWidget(); + void clear(); public Q_SLOTS: void update(Status info); diff --git a/mediacopier-qt/source/gui/MediaCopierParamWidget.cpp b/mediacopier-qt/source/gui/MediaCopierParamWidget.cpp index ca78826..467f764 100644 --- a/mediacopier-qt/source/gui/MediaCopierParamWidget.cpp +++ b/mediacopier-qt/source/gui/MediaCopierParamWidget.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -33,9 +34,8 @@ const auto ask_for_directory = [](const QString& title) -> QString }; static const QList commands = { - Config::Command::COPY_JPEG, - Config::Command::MOVE_JPEG, - Config::Command::SIMULATE + Config::Command::COPY, + Config::Command::MOVE }; MediaCopierParamWidget::MediaCopierParamWidget(QWidget *parent) : @@ -47,6 +47,7 @@ MediaCopierParamWidget::MediaCopierParamWidget(QWidget *parent) : Q_FOREACH(Config::Command item, commands) { ui->paramCommand->addItem(Config::commandString(item)); } + ui->paramPattern->setEnabled(false); } MediaCopierParamWidget::~MediaCopierParamWidget() @@ -56,9 +57,17 @@ MediaCopierParamWidget::~MediaCopierParamWidget() void MediaCopierParamWidget::init(std::shared_ptr config) { - this->config = std::move(config); + m_config = std::move(config); - syncConfig(); + ui->dirsInputDirText->setText(QString::fromStdString(m_config->inputDir().string())); + ui->dirsOutputDirText->setText(QString::fromStdString(m_config->outputDir().string())); + ui->paramPattern->setText(m_config->pattern().c_str()); + for (int i = 0; i < commands.length(); ++i) { + if (commands.at(i) == m_config->command()) { + ui->paramCommand->setCurrentIndex(i); + break; + } + } connect(ui->dirsInputDirButton, &QToolButton::clicked, this, &MediaCopierParamWidget::onOpenInputDirClicked); @@ -66,6 +75,9 @@ void MediaCopierParamWidget::init(std::shared_ptr config) connect(ui->dirsOutputDirButton, &QToolButton::clicked, this, &MediaCopierParamWidget::onOpenOutputDirClicked); + connect(ui->paramPatternUpdate, &QCheckBox::clicked, + this, &MediaCopierParamWidget::onPatternUpdateClicked); + connect(ui->dirsInputDirText, &QLineEdit::textChanged, this, &MediaCopierParamWidget::onInputDirChanged); @@ -79,17 +91,39 @@ void MediaCopierParamWidget::init(std::shared_ptr config) this, &MediaCopierParamWidget::onCommandChanged); } -void MediaCopierParamWidget::syncConfig() +void MediaCopierParamWidget::validate() { - ui->dirsInputDirText->setText(QString::fromStdString(config->inputDir().string())); - ui->dirsOutputDirText->setText(QString::fromStdString(config->outputDir().string())); - ui->paramPattern->setText(config->pattern().c_str()); + bool result = true; - for (int i = 0; i < commands.length(); ++i) { - if (commands.at(i) == config->command()) { - ui->paramCommand->setCurrentIndex(i); - break; - } + QPalette errorPalette; + errorPalette.setColor(QPalette::Base, QColor(255, 145, 145)); + errorPalette.setColor(QPalette::Text, Qt::white); + + if (ui->dirsInputDirText->text().isEmpty() || !QDir(ui->dirsInputDirText->text()).exists()) { + ui->dirsInputDirText->setPalette(errorPalette); + result = false; + } else { + ui->dirsInputDirText->setPalette(this->style()->standardPalette()); + } + + if (ui->dirsOutputDirText->text().isEmpty() || !QDir(ui->dirsOutputDirText->text()).makeAbsolute()) { + ui->dirsOutputDirText->setPalette(errorPalette); + result = false; + } else { + ui->dirsOutputDirText->setPalette(this->style()->standardPalette()); + } + + if (ui->paramPattern->text().isEmpty()) { + ui->paramPattern->setPalette(errorPalette); + result = false; + } else { + ui->paramPattern->setPalette(this->style()->standardPalette()); + } + + if (result) { + validParameters(); + } else { + invalidParameters(); } } @@ -103,28 +137,43 @@ void MediaCopierParamWidget::onOpenOutputDirClicked() ui->dirsOutputDirText->setText(ask_for_directory(QObject::tr("Destination folder"))); } +void MediaCopierParamWidget::onPatternUpdateClicked(bool checked) +{ + ui->paramPattern->setEnabled(checked); + if (!checked) { + m_config->resetPattern(); + m_config->readConfigFile(); + ui->paramPattern->setText(m_config->pattern().c_str()); + } +} + void MediaCopierParamWidget::onInputDirChanged(const QString& text) { - config->setInputDir(text); - spdlog::debug("Changed input dir to " + config->outputDir().string()); + m_config->setInputDir(text); + spdlog::debug("Changed input dir to " + m_config->outputDir().string()); } void MediaCopierParamWidget::onOutputDirChanged(const QString& text) { - config->setOutputDir(text); - spdlog::debug("Changed output dir to " + config->outputDir().string()); - config->readConfigFile(); - syncConfig(); + m_config->setOutputDir(text); + spdlog::debug("Changed output dir to " + m_config->outputDir().string()); + if (!ui->paramPattern->isEnabled()) { + m_config->resetPattern(); + if (QDir(text).exists()) { + m_config->readConfigFile(); + } + } + ui->paramPattern->setText(m_config->pattern().c_str()); } void MediaCopierParamWidget::onPatternChanged(const QString& text) { - config->setPattern(text); - spdlog::debug("Changed pattern to " + config->pattern()); + m_config->setPattern(text); + spdlog::debug("Changed pattern to " + m_config->pattern()); } void MediaCopierParamWidget::onCommandChanged(int index) { - config->setCommand(commands.at(index)); + m_config->setCommand(commands.at(index)); spdlog::debug("Changed command to " + Config::commandString(commands.at(index)).toStdString()); } diff --git a/mediacopier-qt/source/gui/MediaCopierParamWidget.hpp b/mediacopier-qt/source/gui/MediaCopierParamWidget.hpp index 0ef63fe..77e93a9 100644 --- a/mediacopier-qt/source/gui/MediaCopierParamWidget.hpp +++ b/mediacopier-qt/source/gui/MediaCopierParamWidget.hpp @@ -32,13 +32,16 @@ class MediaCopierParamWidget : public QWidget explicit MediaCopierParamWidget(QWidget *parent=nullptr); ~MediaCopierParamWidget(); void init(std::shared_ptr config); + void validate(); -private: - void syncConfig(); +Q_SIGNALS: + void validParameters(); + void invalidParameters(); private Q_SLOTS: void onOpenInputDirClicked(); void onOpenOutputDirClicked(); + void onPatternUpdateClicked(bool checked); void onInputDirChanged(const QString& text); void onOutputDirChanged(const QString& text); void onPatternChanged(const QString& text); @@ -46,5 +49,5 @@ private Q_SLOTS: private: Ui::MediaCopierParamWidget *ui; - std::shared_ptr config; + std::shared_ptr m_config; }; diff --git a/mediacopier-qt/source/gui/MediaCopierParamWidget.ui b/mediacopier-qt/source/gui/MediaCopierParamWidget.ui index 96ac463..5d4d124 100644 --- a/mediacopier-qt/source/gui/MediaCopierParamWidget.ui +++ b/mediacopier-qt/source/gui/MediaCopierParamWidget.ui @@ -7,8 +7,8 @@ 0 0 - 160 - 150 + 187 + 122 @@ -24,33 +24,39 @@ 0 - - + + - Operation + Input Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + + + + - Input - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + ... - - + + - Pattern + ... - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + QToolButton::DelayedPopup + + + Qt::NoArrow @@ -64,42 +70,79 @@ - - - - - + + - ... + Pattern + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - - + + - ... - - - QToolButton::DelayedPopup + Operation - - Qt::NoArrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + 0 + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Update + + + + - - - @@ -107,6 +150,7 @@ dirsInputDirButton dirsOutputDirText dirsOutputDirButton + paramPatternUpdate paramPattern paramCommand diff --git a/mediacopier-qt/source/kde/mediacopier.desktop.in b/mediacopier-qt/source/kde/mediacopier.desktop.in index 89bda29..887c74d 100644 --- a/mediacopier-qt/source/kde/mediacopier.desktop.in +++ b/mediacopier-qt/source/kde/mediacopier.desktop.in @@ -12,10 +12,10 @@ X-KDE-Priority=TopLevel Name=Copy media files to ... Name[de]=Medien kopieren nach ... Icon=folder-copy -Exec=@CMAKE_INSTALL_PREFIX@/bin/@EXECUTABLE_NAME@ --slim-gui -c copy "%U" +Exec=@CMAKE_INSTALL_PREFIX@/bin/@EXECUTABLE_NAME@ --slim-gui copy "%U" [Desktop Action mediacopier-move] Name=Move media files to ... Name[de]=Medien verschieben nach ... Icon=folder-move -Exec=@CMAKE_INSTALL_PREFIX@/bin/@EXECUTABLE_NAME@ --slim-gui -c move "%U" +Exec=@CMAKE_INSTALL_PREFIX@/bin/@EXECUTABLE_NAME@ --slim-gui move "%U" diff --git a/mediacopier-qt/source/main.cpp b/mediacopier-qt/source/main.cpp index e295873..f3ec7ea 100644 --- a/mediacopier-qt/source/main.cpp +++ b/mediacopier-qt/source/main.cpp @@ -16,7 +16,6 @@ #include "gui/MediaCopierDialogFull.hpp" #include "gui/MediaCopierDialogSlim.hpp" -#include "worker.hpp" #include @@ -34,21 +33,6 @@ int run_gui(QApplication& app, std::shared_ptr config) return app.exec(); } -int run_cli(QApplication& app, std::shared_ptr config) -{ - Worker worker{*config}; - worker.start(); - return app.exec(); -} - -using UiFuncType = std::function)>; - -static const std::map uiFuncMap = { - {Config::UI::FullGui, &run_gui}, - {Config::UI::SlimGui, &run_gui}, - {Config::UI::NoGui, &run_cli}, -}; - int main(int argc, char *argv[]) { try { @@ -62,8 +46,13 @@ int main(int argc, char *argv[]) app.setApplicationVersion(mediacopier::MEDIACOPIER_VERSION); auto config = std::make_shared(app); - return uiFuncMap.at(config->ui())(app, config); + switch (config->guiType()) { + case Config::GuiType::Full: + return run_gui(app, config); + case Config::GuiType::Slim: + return run_gui(app, config); + } } catch (const std::exception& err) { spdlog::error(err.what()); return 1; diff --git a/mediacopier-qt/source/worker.cpp b/mediacopier-qt/source/worker.cpp index 7b6e074..4768f4f 100644 --- a/mediacopier-qt/source/worker.cpp +++ b/mediacopier-qt/source/worker.cpp @@ -23,11 +23,8 @@ #include #include -#include #include -#include #include -#include #include #include @@ -52,7 +49,7 @@ static volatile std::atomic operationSuspended(false); static constexpr const unsigned int DEFAULT_WAIT_MS = 200; -auto check_operation_state() +auto is_operation_cancelled() { while (true) { if (operationCancelled.load()) { @@ -100,14 +97,6 @@ auto execute(const fs::path& dest, mc::FileInfoPtr file) -> void typedef void (*ExecFuncPtr)(const fs::path&, mc::FileInfoPtr); -static const std::map execFuncMap = { - {Config::Command::COPY, &execute}, - {Config::Command::COPY_JPEG, &execute}, - {Config::Command::MOVE, &execute}, - {Config::Command::MOVE_JPEG, &execute}, - {Config::Command::SIMULATE, &execute} -}; - } // namespace Worker::Worker(Config config) : m_config{std::move(config)} @@ -174,37 +163,45 @@ void Worker::exec() m_fileCount = valid_media_file_count(m_config.inputDir()); m_progress = 0; - auto reg = mc::FileRegister{m_config.outputDir(), m_config.pattern()}; - auto execute = execFuncMap.at(m_config.command()); - std::optional dest; + auto fileRegister = mc::FileRegister{m_config.outputDir(), m_config.pattern()}; - try { - for (auto file : valid_media_files(m_config.inputDir())) { - if (check_operation_state()) { - spdlog::info("Operation was cancelled.."); - break; - } - ++m_progress; - if ((dest = reg.add(file)).has_value()) { + ExecFuncPtr execute; + switch (m_config.command()) { + case Config::Command::COPY: + execute = &::execute; + break; + case Config::Command::MOVE: + execute = &::execute; + break; + } + + std::optional dest; + for (auto file : valid_media_files(m_config.inputDir())) { + if (is_operation_cancelled()) { + spdlog::info("Operation was cancelled.."); + break; + } + ++m_progress; + try { + if ((dest = fileRegister.add(file)).has_value()) { Q_EMIT status({m_config.command(), file->path(), dest.value(), m_fileCount, m_progress}); execute(dest.value(), file); } else { Q_EMIT status({m_config.command(), file->path(), "", m_fileCount, m_progress}); } + } catch (const std::exception& err) { + spdlog::error(err.what()); } - } catch (const std::exception& err) { - spdlog::error(err.what()); } - spdlog::info("Removing duplicates.."); - reg.removeDuplicates(); + spdlog::info("Removing duplicates in destination directory.."); + fileRegister.removeDuplicates(); spdlog::info("Writing config.."); m_config.writeConfigFile(); - std::signal(SIGINT, SIG_DFL); - spdlog::info("Done"); + std::signal(SIGINT, SIG_DFL); Q_EMIT execDone(); }