From 8633b96646d1a3ae3dbeb440c62855f829db4604 Mon Sep 17 00:00:00 2001
From: cat
Date: Sat, 26 Nov 2022 23:25:35 +0500
Subject: [PATCH] Add option to read tags from command's output.
---
src/global_enums.cpp | 2 +
src/global_enums.h | 16 +++
src/settings_dialog.cpp | 11 ++
src/tagger.cpp | 121 +++++++++++-----------
src/tagger.h | 3 +
src/window.cpp | 100 ++++++++++++++++--
src/window.h | 3 +
ui/settings.ui | 198 +++++++++++++++++++++---------------
util/command_placeholders.h | 1 +
9 files changed, 308 insertions(+), 147 deletions(-)
diff --git a/src/global_enums.cpp b/src/global_enums.cpp
index 327c978..d00ce76 100644
--- a/src/global_enums.cpp
+++ b/src/global_enums.cpp
@@ -46,12 +46,14 @@ struct MetaTypeRegistrator
REGISTER_METATYPE_STREAM_OPERATORS(GlobalEnums::ViewMode)
REGISTER_METATYPE_STREAM_OPERATORS(GlobalEnums::SortQueueBy)
REGISTER_METATYPE_STREAM_OPERATORS(GlobalEnums::EditMode)
+ REGISTER_METATYPE_STREAM_OPERATORS(GlobalEnums::CommandOutputMode)
}
};
static const MetaTypeRegistrator _;
IMPLEMENT_ENUM_STREAM_OPERATORS(GlobalEnums::ViewMode)
IMPLEMENT_ENUM_STREAM_OPERATORS(GlobalEnums::SortQueueBy)
IMPLEMENT_ENUM_STREAM_OPERATORS(GlobalEnums::EditMode)
+IMPLEMENT_ENUM_STREAM_OPERATORS(GlobalEnums::CommandOutputMode)
GlobalEnums::EditMode GlobalEnums::next_edit_mode(EditMode current)
diff --git a/src/global_enums.h b/src/global_enums.h
index bf5e861..f5752e0 100644
--- a/src/global_enums.h
+++ b/src/global_enums.h
@@ -79,11 +79,24 @@ class GlobalEnums
*/
static EditMode next_edit_mode(EditMode current);
+
+ /*!
+ * \brief Specifies command output mode.
+ */
+ enum class CommandOutputMode {
+ Ignore, ///< Command output is ignored, and the process is detached.
+ Replace, ///< Command output replaces current tags
+ Append, ///< Command output appended to current tags
+ Prepend ///< Command output prepended to current tags
+ };
+ Q_ENUM(CommandOutputMode);
+
GlobalEnums() = delete;
};
ENUM_STREAM_OPERATORS(GlobalEnums::ViewMode)
ENUM_STREAM_OPERATORS(GlobalEnums::SortQueueBy)
ENUM_STREAM_OPERATORS(GlobalEnums::EditMode)
+ENUM_STREAM_OPERATORS(GlobalEnums::CommandOutputMode)
/// Alias for \ref GlobalEnums::ViewMode enumeration
@@ -95,4 +108,7 @@ using SortQueueBy = GlobalEnums::SortQueueBy;
/// Alias for \ref GlobalEnums::EditMode enumeration
using EditMode = GlobalEnums::EditMode;
+/// Alias for \ref GlobalEnums::CommandOutputMode enumeration
+using CommandOutputMode = GlobalEnums::CommandOutputMode;
+
#endif // GLOBAL_ENUMS_H
diff --git a/src/settings_dialog.cpp b/src/settings_dialog.cpp
index 4260bca..34276fc 100644
--- a/src/settings_dialog.cpp
+++ b/src/settings_dialog.cpp
@@ -9,6 +9,7 @@
#include "ui_settings.h"
#include "util/misc.h"
#include "util/project_info.h"
+#include "global_enums.h"
#include "util/command_placeholders.h"
#include
#include
@@ -307,6 +308,7 @@ void SettingsDialog::reset()
m_dmpr->setModel(m_cmdl);
m_dmpr->addMapping(ui->cmdExecutableEdit, 2);
m_dmpr->addMapping(ui->cmdArgsEdit, 3);
+ m_dmpr->addMapping(ui->cmdOutputMode, 4, "currentIndex");
m_dmpr->toFirst();
auto fmd = new HideColumnsFilter(m_cmdl);
@@ -332,6 +334,8 @@ void SettingsDialog::reset()
}
disable_widgets(disabled);
});
+
+ ui->cmdView->selectRow(0);
}
void SettingsDialog::resetModel()
@@ -371,6 +375,11 @@ void SettingsDialog::resetModel()
path.removeFirst();
m_cmdl->setItem(i, 3, new QStandardItem(join_args(path)));
}
+
+ auto mode = st.value(SETT_COMMAND_MODE).value();
+ auto item = new QStandardItem();
+ item->setData(static_cast(mode), Qt::DisplayRole);
+ m_cmdl->setItem(i, 4, item);
}
st.endArray();
}
@@ -442,10 +451,12 @@ void SettingsDialog::apply()
auto exec_path = m_cmdl->data(m_cmdl->index(i,2)).toString();
auto args = parse_arguments(m_cmdl->data(m_cmdl->index(i,3)).toString());
args.prepend(exec_path);
+ auto mode = m_cmdl->data(m_cmdl->index(i, 4)).toInt();
settings.setValue(SETT_COMMAND_NAME, name);
settings.setValue(SETT_COMMAND_HOTKEY, hotkey);
settings.setValue(SETT_COMMAND_CMD, args);
+ settings.setValue(SETT_COMMAND_MODE, QVariant::fromValue(static_cast(mode)));
}
settings.endArray();
emit updated();
diff --git a/src/tagger.cpp b/src/tagger.cpp
index beaf65c..1f60191 100644
--- a/src/tagger.cpp
+++ b/src/tagger.cpp
@@ -267,6 +267,69 @@ QString Tagger::text() const
return m_input.text();
}
+void Tagger::addTags(QString tags, int *tag_count)
+{
+ TagEditState state;
+ auto options = TagParser::FixOptions::from_settings();
+ options.sort = true;
+
+ // Autofix imageboard tags before comparing and assigning
+ util::replace_special(tags);
+ auto fixed_tags_list = m_input.tag_parser().fixTags(state, tags, options);
+ tags = util::join(fixed_tags_list);
+ if (tag_count) {
+ *tag_count = fixed_tags_list.size();
+ }
+
+ util::replace_special(tags);
+ auto current_tags = m_input.tags();
+
+ if (current_tags != tags) {
+
+ if (!util::is_hex_string(current_tags)) {
+
+ auto current_list = m_input.tags_list();
+ // sort current tags in case they were being edited and unsorted.
+ // imageboard tags are already sorted.
+ current_list.sort();
+
+ QString added_tags_str, removed_tags_str;
+ getTagDifference(current_list, fixed_tags_list, added_tags_str, removed_tags_str, true);
+
+ // Ask user what to do with tags
+ QMessageBox mbox(QMessageBox::Question,
+ tr("Fetched tags mismatch"),
+ tr("Imageboard tags do not match current tags:"
+ "
"
+ "%1
"
+ "%2%3"
+ "Please choose what to do:
").arg(tags)
+ .arg(added_tags_str)
+ .arg(removed_tags_str),
+ QMessageBox::Save|QMessageBox::SaveAll);
+
+ mbox.addButton(QMessageBox::Cancel);
+ mbox.setButtonText(QMessageBox::Save, tr("Use only imageboard tags"));
+ mbox.setButtonText(QMessageBox::SaveAll, tr("Merge tags"));
+
+ int res = mbox.exec();
+
+ if (res == QMessageBox::Save) {
+ m_input.setTags(tags);
+ }
+ if (res == QMessageBox::SaveAll) {
+ current_tags.append(' ');
+ current_tags.append(tags);
+ m_input.setTags(current_tags);
+ }
+ } else {
+ // if there was only hash filename to begin with,
+ // just use the imageboard tags without asking
+ setText(tags);
+ }
+ }
+}
+
void Tagger::resetText()
{
setText(QFileInfo(m_file_queue.current()).completeBaseName());
@@ -334,63 +397,7 @@ void Tagger::tagsFetched(QString file, QString tags)
// Current file might have already changed since reply came
if (currentFile() == file) {
- TagEditState state;
- auto options = TagParser::FixOptions::from_settings();
- options.sort = true;
-
- // Autofix imageboard tags before comparing and assigning
- util::replace_special(tags);
- auto fixed_tags_list = m_input.tag_parser().fixTags(state, tags, options);
- tags = util::join(fixed_tags_list);
- processed_tags_count = fixed_tags_list.size();
-
- util::replace_special(tags);
- auto current_tags = m_input.tags();
-
- if (current_tags != tags) {
-
- if (!util::is_hex_string(current_tags)) {
-
- auto current_list = m_input.tags_list();
- // sort current tags in case they were being edited and unsorted.
- // imageboard tags are already sorted.
- current_list.sort();
-
- QString added_tags_str, removed_tags_str;
- getTagDifference(current_list, fixed_tags_list, added_tags_str, removed_tags_str, true);
-
- // Ask user what to do with tags
- QMessageBox mbox(QMessageBox::Question,
- tr("Fetched tags mismatch"),
- tr("Imageboard tags do not match current tags:"
- "
"
- "%1
"
- "%2%3"
- "Please choose what to do:
").arg(tags)
- .arg(added_tags_str)
- .arg(removed_tags_str),
- QMessageBox::Save|QMessageBox::SaveAll);
-
- mbox.addButton(QMessageBox::Cancel);
- mbox.setButtonText(QMessageBox::Save, tr("Use only imageboard tags"));
- mbox.setButtonText(QMessageBox::SaveAll, tr("Merge tags"));
-
- int res = mbox.exec();
-
- if (res == QMessageBox::Save) {
- m_input.setTags(tags);
- }
- if (res == QMessageBox::SaveAll) {
- current_tags.append(' ');
- current_tags.append(tags);
- m_input.setTags(current_tags);
- }
- } else {
- // if there was only hash filename to begin with,
- // just use the imageboard tags without asking
- setText(tags);
- }
- }
+ addTags(tags, &processed_tags_count);
}
TaggerStatistics::instance().tagsFetched(overall_tags_count, processed_tags_count);
diff --git a/src/tagger.h b/src/tagger.h
index cb16213..2eaf857 100644
--- a/src/tagger.h
+++ b/src/tagger.h
@@ -138,6 +138,9 @@ class Tagger : public QWidget
/// Current tag input text.
QString text() const;
+ /// Ask user to add tags (maybe replacing current tags?)
+ void addTags(QString tags, int* tag_count=nullptr);
+
/// Undo tag input changes and set original tags.
void resetText();
diff --git a/src/window.cpp b/src/window.cpp
index 7e68bb9..e01c175 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -465,6 +465,17 @@ void Window::showFileHashingProgress(QString file, int value)
statusBar()->showMessage(tr("Calculating file hash... %1% complete").arg(value));
}
+void Window::showCommandExecutionProgress(QString command_name)
+{
+#ifdef Q_OS_WIN32
+ auto progress = m_win_taskbar_button.progress();
+ progress->setVisible(true);
+ progress->setMaximum(0);
+ progress->setValue(0);
+#endif
+ statusBar()->showMessage(tr("Running %1...").arg(command_name));
+}
+
void Window::hideUploadProgress()
{
#ifdef Q_OS_WIN32
@@ -974,6 +985,7 @@ void Window::createCommands()
auto name = settings.value(SETT_COMMAND_NAME).toString();
auto cmd = settings.value(SETT_COMMAND_CMD).toStringList();
auto hkey = settings.value(SETT_COMMAND_HOTKEY).toString();
+ auto mode = settings.value(SETT_COMMAND_MODE).value();
if(name == CMD_SEPARATOR) {
menu_commands.addSeparator();
@@ -998,7 +1010,7 @@ void Window::createCommands()
action->setShortcut(hkey);
}
- connect(action, &QAction::triggered, this, [this,name,binary,cmd]()
+ connect(action, &QAction::triggered, this, [this,name,binary,cmd,mode]()
{
auto cmd_tmp = cmd; // NOTE: separate copy is needed instead of mutable lambda
auto to_native = [](const auto& path)
@@ -1026,13 +1038,85 @@ void Window::createCommands()
to_native(remove_ext(m_tagger.currentFileName())));
}
- auto success = QProcess::startDetached(binary, cmd_tmp, m_tagger.currentDir());
- pdbg << "QProcess::startDetached(" << binary << "," << cmd_tmp << ") =>" << success;
- if(!success) {
- QMessageBox::critical(this,
- tr("Failed to start command"),
- tr("Failed to launch command %1:
Could not start %2
.
")
- .arg(name, binary));
+ if (mode == CommandOutputMode::Ignore) {
+ bool success = QProcess::startDetached(binary, cmd_tmp, m_tagger.currentDir());
+ pdbg << "QProcess::startDetached(" << binary << "," << cmd_tmp << ") =>" << success;
+ if(!success) {
+ QMessageBox::critical(this,
+ tr("Failed to start command"),
+ tr("Failed to launch command %1:
Could not start %2
.
")
+ .arg(name, binary));
+ }
+ } else {
+
+ auto proc = new QProcess{this};
+ proc->setProgram(binary);
+ proc->setArguments(cmd_tmp);
+ proc->setWorkingDirectory(m_tagger.currentDir());
+
+ showCommandExecutionProgress(name);
+
+ connect(&m_tagger,
+ &Tagger::fileOpened,
+ proc,
+ [this, proc](const QString&)
+ {
+ if (proc->state() != QProcess::NotRunning) {
+ disconnect(proc, nullptr, this, nullptr);
+ proc->terminate();
+ }
+ });
+
+ connect(proc,
+ QOverload::of(&QProcess::finished),
+ this,
+ [this, name, binary, mode](int exit_code, QProcess::ExitStatus exit_status)
+ {
+ Q_UNUSED(exit_code);
+
+ if (exit_status != QProcess::NormalExit) {
+ QMessageBox::critical(this,
+ tr("Failed to start command"),
+ tr("Failed to launch command %1:
Could not start %2
.
")
+ .arg(name, binary));
+
+
+ hideUploadProgress();
+ return;
+ }
+
+ auto proc = qobject_cast(sender());
+ Q_ASSERT(proc);
+
+ auto tags_data = proc->readAllStandardOutput();
+
+ QTextStream in{tags_data};
+ in.setCodec("UTF-8");
+
+ auto tags = in.readAll().replace('\n', ' ').trimmed();
+ if (!tags.isEmpty()) {
+ auto current_tags = m_tagger.text();
+
+ switch(mode) {
+ case CommandOutputMode::Replace:
+ m_tagger.addTags(tags);
+ break;
+ case CommandOutputMode::Append:
+ m_tagger.setText(current_tags + " " + tags);
+ break;
+ case CommandOutputMode::Prepend:
+ m_tagger.setText(tags + " " + current_tags);
+ break;
+ default:
+ break;
+ }
+ }
+ hideUploadProgress();
+ proc->deleteLater();
+ }, Qt::QueuedConnection);
+
+ proc->start(QProcess::ReadOnly);
+
}
});
menu_commands.setEnabled(true);
diff --git a/src/window.h b/src/window.h
index a61fd86..77bee01 100644
--- a/src/window.h
+++ b/src/window.h
@@ -49,6 +49,9 @@ public slots:
/// Display file hashing progress.
void showFileHashingProgress(QString file, int value);
+ /// Display command execution progress.
+ void showCommandExecutionProgress(QString command_name);
+
/// Hide current upload progress
void hideUploadProgress();
diff --git a/ui/settings.ui b/ui/settings.ui
index b895787..661af3c 100644
--- a/ui/settings.ui
+++ b/ui/settings.ui
@@ -587,6 +587,122 @@ In Main View
+ -
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+ &Browse...
+
+
+
+ -
+
+
+ Arguments are used to pass information to launched program, such as files to open.
+
+
+
+ -
+
+
+ Arguments are used to pass information to launched program, such as files to open.
+
+
+ Arguments are used to pass information to launched program, such as files to open.
+
+
+ &Arguments
+
+
+ cmdArgsEdit
+
+
+
+ -
+
+
+ E&xecutable
+
+
+ cmdExecutableEdit
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 22
+
+
+
+ <html><head/><body><p>Available substitutions: </p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{path} - Path of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{dir} - Directory of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{fullname} - Name of current file</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{basename} - Name of current file without extension</li></ul></body></html>
+
+
+ <html><head/><body><p>Available substitutions: </p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{path} - Path of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{dir} - Directory of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{fullname} - Name of current file</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{basename} - Name of current file without extension</li></ul></body></html>
+
+
+ Placeholders
+
+
+ QToolButton::InstantPopup
+
+
+ Qt::ToolButtonTextOnly
+
+
+
+ -
+
+
-
+
+ Ignore
+
+
+ -
+
+ Replace tags
+
+
+ -
+
+ Append tags
+
+
+ -
+
+ Prepend tags
+
+
+
+
+ -
+
+
+ &Output
+
+
+ cmdOutputMode
+
+
+
+
+
-
-
@@ -690,88 +806,6 @@ In Main View
- -
-
-
-
-
-
- true
-
-
-
- -
-
-
- &Browse...
-
-
-
- -
-
-
- E&xecutable
-
-
- cmdExecutableEdit
-
-
-
- -
-
-
- Arguments are used to pass information to launched program, such as files to open.
-
-
- Arguments are used to pass information to launched program, such as files to open.
-
-
- &Arguments
-
-
- cmdArgsEdit
-
-
-
- -
-
-
- Arguments are used to pass information to launched program, such as files to open.
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 22
-
-
-
- <html><head/><body><p>Available substitutions: </p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{path} - Path of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{dir} - Directory of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{fullname} - Name of current file</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{basename} - Name of current file without extension</li></ul></body></html>
-
-
- <html><head/><body><p>Available substitutions: </p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{path} - Path of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{dir} - Directory of current file</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{fullname} - Name of current file</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{basename} - Name of current file without extension</li></ul></body></html>
-
-
- Placeholders
-
-
- QToolButton::InstantPopup
-
-
- Qt::ToolButtonTextOnly
-
-
-
-
-
diff --git a/util/command_placeholders.h b/util/command_placeholders.h
index 7bc13c8..727f1ac 100644
--- a/util/command_placeholders.h
+++ b/util/command_placeholders.h
@@ -12,6 +12,7 @@
#define SETT_COMMAND_NAME QStringLiteral("display_name")
#define SETT_COMMAND_CMD QStringLiteral("command")
#define SETT_COMMAND_HOTKEY QStringLiteral("hotkey")
+#define SETT_COMMAND_MODE QStringLiteral("mode")
#define CMD_SEPARATOR QStringLiteral("__separator__")