diff options
author | saturneric <[email protected]> | 2025-01-26 18:17:20 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2025-01-26 18:18:10 +0000 |
commit | 92e1ed7b3fd5360278d41db087518b6c5af80b3e (patch) | |
tree | 577edfa58889d97a0b3ec1f2c045b9d6fe9cff70 | |
parent | fix: 'std::future' is defined in header '<future>' (diff) | |
download | GpgFrontend-92e1ed7b3fd5360278d41db087518b6c5af80b3e.tar.gz GpgFrontend-92e1ed7b3fd5360278d41db087518b6c5af80b3e.zip |
feat: allow file batch operations
23 files changed, 1434 insertions, 971 deletions
diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index 51f5b2b7..d82a47fa 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -108,6 +108,7 @@ <file alias="export-email.png">resource/lfs/icons/export-email.png</file> <file alias="warning-filling.png">resource/lfs/icons/warning-filling.png</file> <file alias="upgrade.png">resource/lfs/icons/upgrade.png</file> + <file alias="batch.png">resource/lfs/icons/batch.png</file> </qresource> <qresource prefix="/test/key"> <file alias="pv1.key">resource/lfs/test/data/pv1.key</file> diff --git a/resource/lfs/icons/batch.png b/resource/lfs/icons/batch.png Binary files differnew file mode 100644 index 00000000..ac3e77bd --- /dev/null +++ b/resource/lfs/icons/batch.png diff --git a/src/core/function/gpg/GpgFileOpera.cpp b/src/core/function/gpg/GpgFileOpera.cpp index 9dea413d..0a321b14 100644 --- a/src/core/function/gpg/GpgFileOpera.cpp +++ b/src/core/function/gpg/GpgFileOpera.cpp @@ -521,7 +521,7 @@ auto GpgFileOpera::EncryptFileSymmetricSync(const QString& in_path, bool ascii, "gpgme_op_encrypt_symmetric", "2.1.0"); } -void GpgFileOpera::EncryptDerectorySymmetric(const QString& in_path, bool ascii, +void GpgFileOpera::EncryptDirectorySymmetric(const QString& in_path, bool ascii, const QString& out_path, const GpgOperationCallback& cb) { auto ex = std::make_shared<GFDataExchanger>(kDataExchangerSize); @@ -548,7 +548,7 @@ void GpgFileOpera::EncryptDerectorySymmetric(const QString& in_path, bool ascii, }); } -auto GpgFileOpera::EncryptDerectorySymmetricSync( +auto GpgFileOpera::EncryptDirectorySymmetricSync( const QString& in_path, bool ascii, const QString& out_path) -> std::tuple<GpgError, DataObjectPtr> { auto ex = std::make_shared<GFDataExchanger>(kDataExchangerSize); diff --git a/src/core/function/gpg/GpgFileOpera.h b/src/core/function/gpg/GpgFileOpera.h index 939ddc2d..8bb08567 100644 --- a/src/core/function/gpg/GpgFileOpera.h +++ b/src/core/function/gpg/GpgFileOpera.h @@ -123,7 +123,7 @@ class GPGFRONTEND_CORE_EXPORT GpgFileOpera * @param out_path * @param cb */ - void EncryptDerectorySymmetric(const QString& in_path, bool ascii, + void EncryptDirectorySymmetric(const QString& in_path, bool ascii, const QString& out_path, const GpgOperationCallback& cb); @@ -134,7 +134,7 @@ class GPGFRONTEND_CORE_EXPORT GpgFileOpera * @param ascii * @param out_path */ - auto EncryptDerectorySymmetricSync(const QString& in_path, bool ascii, + auto EncryptDirectorySymmetricSync(const QString& in_path, bool ascii, const QString& out_path) -> std::tuple<GpgError, DataObjectPtr>; diff --git a/src/core/function/result_analyse/GpgResultAnalyse.h b/src/core/function/result_analyse/GpgResultAnalyse.h index ce47764c..fa84ddb4 100644 --- a/src/core/function/result_analyse/GpgResultAnalyse.h +++ b/src/core/function/result_analyse/GpgResultAnalyse.h @@ -27,10 +27,6 @@ */ #pragma once -#include <sstream> - -#include "core/typedef/GpgTypedef.h" - namespace GpgFrontend { class GPGFRONTEND_CORE_EXPORT GpgResultAnalyse : public QObject { diff --git a/src/ui/UISignalStation.h b/src/ui/UISignalStation.h index d89ef91b..dfafca93 100644 --- a/src/ui/UISignalStation.h +++ b/src/ui/UISignalStation.h @@ -75,7 +75,7 @@ class UISignalStation : public QObject { * @brief * */ - void SignalMainWindowlUpdateBasicalOperaMenu(unsigned int); + void SignalMainWindowUpdateBasicOperaMenu(unsigned int); /** * @brief diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index 33d72d8d..ea5bb3d9 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -216,6 +216,41 @@ void CommonUtils::WaitForOpera(QWidget *parent, looper.exec(); } +void CommonUtils::WaitForMultipleOperas( + QWidget *parent, const QString &waiting_dialog_title, + const QContainer<OperaWaitingCb> &operas) { + QEventLoop looper; + QPointer<WaitingDialog> const dialog = + new WaitingDialog(waiting_dialog_title, true, parent); + connect(dialog, &QDialog::finished, &looper, &QEventLoop::quit); + connect(dialog, &QDialog::finished, dialog, &QDialog::deleteLater); + dialog->show(); + + std::atomic<int> remaining_tasks(static_cast<int>(operas.size())); + const auto tasks_count = operas.size(); + + for (const auto &opera : operas) { + QTimer::singleShot(64, parent, [=, &remaining_tasks]() { + opera([dialog, &remaining_tasks, tasks_count]() { + if (dialog == nullptr) return; + + const auto pg_value = + static_cast<double>(tasks_count - remaining_tasks + 1) * 100.0 / + static_cast<double>(tasks_count); + emit dialog->SignalUpdateValue(static_cast<int>(pg_value)); + QCoreApplication::processEvents(); + + if (--remaining_tasks == 0) { + dialog->close(); + dialog->accept(); + } + }); + }); + } + + looper.exec(); +} + void CommonUtils::RaiseMessageBox(QWidget *parent, GpgError err) { GpgErrorDesc desc = DescribeGpgErrCode(err); GpgErrorCode err_code = CheckGpgError2ErrCode(err); @@ -326,7 +361,7 @@ void CommonUtils::SlotExecuteGpgCommand( const QStringList &arguments, const std::function<void(QProcess *)> &interact_func) { QEventLoop looper; - auto *dialog = new WaitingDialog(tr("Processing"), nullptr); + auto *dialog = new WaitingDialog(tr("Processing"), false); dialog->show(); auto *gpg_process = new QProcess(&looper); gpg_process->setProcessChannelMode(QProcess::MergedChannels); diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index 6aae75ba..81de99ed 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -44,7 +44,7 @@ class InfoBoardWidget; class TextEdit; using OperaWaitingHd = std::function<void()>; -using OperaWaitingCb = const std::function<void(OperaWaitingHd)>; +using OperaWaitingCb = std::function<void(OperaWaitingHd)>; /** * @brief @@ -116,6 +116,17 @@ class CommonUtils : public QWidget { /** * @brief * + * @param parent + * @param waiting_dialog_title + * @param operas + */ + static void WaitForMultipleOperas(QWidget* parent, + const QString& waiting_dialog_title, + const QContainer<OperaWaitingCb>& operas); + + /** + * @brief + * * @param err */ static void RaiseMessageBox(QWidget* parent, GpgError err); diff --git a/src/ui/dialog/WaitingDialog.cpp b/src/ui/dialog/WaitingDialog.cpp index da620ebf..6fdd3a70 100644 --- a/src/ui/dialog/WaitingDialog.cpp +++ b/src/ui/dialog/WaitingDialog.cpp @@ -32,15 +32,14 @@ namespace GpgFrontend::UI { -WaitingDialog::WaitingDialog(const QString& title, QWidget* parent) - : GeneralDialog("WaitingDialog", parent) { - auto* pb = new QProgressBar(); - pb->setRange(0, 0); - pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - pb->setTextVisible(false); +WaitingDialog::WaitingDialog(const QString& title, bool range, QWidget* parent) + : GeneralDialog("WaitingDialog", parent), pb_(new QProgressBar()) { + pb_->setRange(0, range ? 100 : 0); + pb_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + pb_->setTextVisible(false); auto* layout = new QVBoxLayout(); - layout->addWidget(pb); + layout->addWidget(pb_); this->setLayout(layout); this->setModal(true); @@ -50,8 +49,12 @@ WaitingDialog::WaitingDialog(const QString& title, QWidget* parent) this->setAttribute(Qt::WA_DeleteOnClose); this->setFixedSize(240, 42); + connect(this, &WaitingDialog::SignalUpdateValue, this, + &WaitingDialog::SlotUpdateValue); + this->movePosition2CenterOfParent(); this->show(); } +void WaitingDialog::SlotUpdateValue(int value) { pb_->setValue(value); } } // namespace GpgFrontend::UI diff --git a/src/ui/dialog/WaitingDialog.h b/src/ui/dialog/WaitingDialog.h index 6b0877fe..1fb9fe17 100644 --- a/src/ui/dialog/WaitingDialog.h +++ b/src/ui/dialog/WaitingDialog.h @@ -46,7 +46,28 @@ class WaitingDialog : public GeneralDialog { * @param title * @param parent */ - WaitingDialog(const QString& title, QWidget* parent); + explicit WaitingDialog(const QString& title, bool range, + QWidget* parent = nullptr); + + public slots: + + /** + * @brief max 100, min 0 + * + */ + void SlotUpdateValue(int value); + + signals: + + /** + * @brief + * + * @param value + */ + void SignalUpdateValue(int value); + + private: + QProgressBar* pb_; }; } // namespace GpgFrontend::UI diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index d191e268..f9d5d8be 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -104,7 +104,7 @@ void MainWindow::Init() noexcept { statusBar()->showMessage(message, timeout); }); connect(UISignalStation::GetInstance(), - &UISignalStation::SignalMainWindowlUpdateBasicalOperaMenu, this, + &UISignalStation::SignalMainWindowUpdateBasicOperaMenu, this, &MainWindow::SlotUpdateCryptoMenuStatus); connect(UISignalStation::GetInstance(), &UISignalStation::SignalMainWindowOpenFile, this, diff --git a/src/ui/main_window/MainWindow.h b/src/ui/main_window/MainWindow.h index f397b755..357a3ed5 100644 --- a/src/ui/main_window/MainWindow.h +++ b/src/ui/main_window/MainWindow.h @@ -28,7 +28,10 @@ #pragma once +#include "core/typedef/CoreTypedef.h" +#include "core/typedef/GpgTypedef.h" #include "ui/main_window/GeneralMainWindow.h" +#include "ui/struct/GpgOperaResult.h" namespace GpgFrontend { class GpgPassphraseContext; @@ -41,6 +44,8 @@ namespace GpgFrontend::UI { class KeyList; class TextEdit; class InfoBoardWidget; +struct GpgOperaContext; +struct GpgOperaContexts; /** * @brief @@ -209,41 +214,28 @@ class MainWindow : public GeneralMainWindow { /** * @details Open dialog for encrypting file. */ - void SlotFileEncrypt(const QString&); - - /** - * @brief - * - */ - void SlotDirectoryEncrypt(const QString&); - - /** - * @brief - * - * @param path - */ - void SlotFileDecrypt(const QString& path); + void SlotFileEncrypt(const QContainer<QString>& paths); /** * @brief * * @param path */ - void SlotArchiveDecrypt(const QString& path); + void SlotFileDecrypt(const QContainer<QString>& paths); /** * @brief * * @param path */ - void SlotFileSign(const QString& path); + void SlotFileSign(const QContainer<QString>& paths); /** * @brief * * @param path */ - void SlotFileVerify(const QString& path); + void SlotFileVerify(const QContainer<QString>& paths); /** * @brief @@ -257,28 +249,14 @@ class MainWindow : public GeneralMainWindow { * * @param path */ - void SlotFileEncryptSign(const QString& path); + void SlotFileEncryptSign(const QContainer<QString>& paths); /** * @brief * * @param path */ - void SlotDirectoryEncryptSign(const QString& path); - - /** - * @brief - * - * @param path - */ - void SlotFileDecryptVerify(const QString& path); - - /** - * @brief - * - * @param path - */ - void SlotArchiveDecryptVerify(const QString& path); + void SlotFileDecryptVerify(const QContainer<QString>& paths); /** * @details get value of member restartNeeded to needed. @@ -498,6 +476,14 @@ class MainWindow : public GeneralMainWindow { /** * @brief * + * @param opera_results + */ + void slot_result_analyse_show_helper( + const QContainer<GpgOperaResult>& opera_results); + + /** + * @brief + * * @param result_analyse */ void slot_eml_verify_show_helper(const QString& email_info, @@ -611,6 +597,135 @@ class MainWindow : public GeneralMainWindow { */ auto handle_module_error(QMap<QString, QString> p) -> bool; + /** + * @brief + * + * @param paths + * @return true + * @return false + */ + auto check_read_file_paths_helper(const QContainer<QString>& paths) -> bool; + + /** + * @brief + * + * @param paths + * @return true + * @return false + */ + auto check_write_file_paths_helper(const QContainer<QString>& paths) -> bool; + + /** + * @brief + * + * @param key_ids + * @param capability_check + * @param capability_err_string + * @return GpgKeyList + */ + auto check_keys_helper( + const KeyIdArgsList& key_ids, + const std::function<bool(const GpgKey&)>& capability_check, + const QString& capability_err_string) -> GpgKeyList; + + /** + * @brief + * + * @param context + * @return auto + */ + auto execute_operas_helper(const QString& task, + const QSharedPointer<GpgOperaContexts>& contexts); + + /** + * @brief + * + * @param context + */ + void build_operas_file_symmetric_encrypt( + QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_encrypt(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_directory_symmetric_encrypt( + QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_directory_encrypt(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_decrypt(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_archive_decrypt(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_sign(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_verify(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_encrypt_sign(QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_directory_encrypt_sign( + QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_file_decrypt_verify( + QSharedPointer<GpgOperaContext>& context); + + /** + * @brief + * + * @param context + */ + void build_operas_archive_decrypt_verify( + QSharedPointer<GpgOperaContext>& context); + TextEdit* edit_{}; ///< Tabwidget holding the edit-windows QMenu* file_menu_{}; ///< Submenu for file-operations QMenu* edit_menu_{}; ///< Submenu for text-operations @@ -651,8 +766,8 @@ class MainWindow : public GeneralMainWindow { QAction* sign_act_{}; ///< Action to sign text QAction* verify_act_{}; ///< Action to verify text QAction* import_key_from_edit_act_{}; ///< Action to import key from edit - QAction* clean_double_line_breaks_act_{}; ///< Action to remove double line - ///< breaks + QAction* clean_double_line_breaks_act_{}; ///< Action to remove double + ///< line breaks QAction* gnupg_controller_open_act_{}; ///< QAction* module_controller_open_act_{}; ///< @@ -660,8 +775,8 @@ class MainWindow : public GeneralMainWindow { QAction* reload_components_act_{}; ///< QAction* restart_components_act_{}; ///< - QAction* - append_selected_keys_act_{}; ///< Action to append selected keys to edit + QAction* append_selected_keys_act_{}; ///< Action to append selected keys + ///< to edit QAction* append_key_fingerprint_to_editor_act_{}; ///< QAction* append_key_create_date_to_editor_act_{}; ///< QAction* append_key_expire_date_to_editor_act_{}; ///< diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp index e2fe86bd..bfda5a8b 100644 --- a/src/ui/main_window/MainWindowFileSlotFunction.cpp +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -39,589 +39,747 @@ #include "core/utils/IOUtils.h" #include "ui/UserInterfaceUtils.h" #include "ui/dialog/SignersPicker.h" +#include "ui/struct/GpgOperaResult.h" +#include "ui/struct/GpgOperaResultContext.h" #include "ui/widgets/KeyList.h" namespace GpgFrontend::UI { -void MainWindow::SlotFileEncrypt(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot read from file: %1").arg(QFileInfo(path).fileName())); - return; +auto MainWindow::check_read_file_paths_helper(const QContainer<QString>& paths) + -> bool { + QStringList invalid_files; + for (const auto& path : paths) { + auto result = TargetFilePreCheck(path, true); + if (!std::get<0>(result)) { + invalid_files.append(path); + } } - bool const non_ascii_at_file_operation = - GlobalSettingStation::GetInstance() - .GetSettings() - .value("gnupg/non_ascii_at_file_operation", true) - .toBool(); - auto out_path = - SetExtensionOfOutputFile(path, kENCRYPT, !non_ascii_at_file_operation); + if (!invalid_files.empty()) { + QString error_file_names; + for (const auto& file_path : invalid_files) { + error_file_names += QFileInfo(file_path).fileName() + "\n"; + } + + QMessageBox::critical(this, tr("Error"), + tr("Cannot read from the following files:\n\n%1") + .arg(error_file_names.trimmed())); + return false; + } - if (QFile::exists(out_path)) { - auto out_file_name = tr("The target file %1 already exists, " - "do you need to overwrite it?") - .arg(out_path); - auto ret = QMessageBox::warning(this, tr("Warning"), out_file_name, - QMessageBox::Ok | QMessageBox::Cancel); + return true; +} - if (ret == QMessageBox::Cancel) return; +auto MainWindow::check_write_file_paths_helper( + const QContainer<QString>& o_paths) -> bool { + for (const auto& o_path : o_paths) { + if (QFile::exists(o_path)) { + auto out_file_name = tr("The target file %1 already exists, " + "do you need to overwrite it?") + .arg(QFileInfo(o_path).fileName()); + auto ret = QMessageBox::warning(this, tr("Warning"), out_file_name, + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return false; + } } - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; + QStringList invalid_output_files; + for (const auto& path : o_paths) { + auto result = TargetFilePreCheck(path, false); + if (!std::get<0>(result)) { + invalid_output_files.append(path); + } } - // check selected keys - auto key_ids = m_key_list_->GetChecked(); - if (key_ids.empty()) { - // Symmetric Encrypt - auto ret = QMessageBox::information( - this, tr("Symmetric Encryption"), - tr("No Key Selected. Do you want to encrypt with a " - "symmetric cipher using a passphrase?"), - QMessageBox::Ok | QMessageBox::Cancel); - if (ret == QMessageBox::Cancel) return; + if (!invalid_output_files.empty()) { + QString error_file_names; + for (const auto& file_path : invalid_output_files) { + error_file_names += QFileInfo(file_path).fileName() + "\n"; + } - CommonUtils::WaitForOpera( - this, tr("Symmetrically Encrypting"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptFileSymmetric( - path, !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgEncryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); - auto result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); + QMessageBox::critical(this, tr("Error"), + tr("Cannot write to the following files:\n\n%1") + .arg(error_file_names.trimmed())); + return false; + } - return; + return true; +} + +auto MainWindow::check_keys_helper( + const KeyIdArgsList& key_ids, + const std::function<bool(const GpgKey&)>& capability_check, + const QString& capability_err_string) -> GpgKeyList { + if (key_ids.empty()) { + QMessageBox::critical( + this, tr("No Key Checked"), + tr("Please check the key in the key toolbox on the right.")); + return {}; } - auto p_keys = + auto keys = GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) .GetKeys(key_ids); - assert(std::all_of(p_keys.begin(), p_keys.end(), + assert(std::all_of(keys.begin(), keys.end(), [](const auto& key) { return key.IsGood(); })); // check key abilities - for (const auto& key : p_keys) { - bool const key_can_encrypt = key.IsHasActualEncryptionCapability(); - - if (!key_can_encrypt) { - QMessageBox::critical( - nullptr, tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for encryption.") + - "<br/><br/>" + tr("For example the Following Key:") + " <br/>" + - key.GetUIDs()->front().GetUID()); - return; + for (const auto& key : keys) { + if (!capability_check(key)) { + QMessageBox::critical(nullptr, tr("Invalid KeyPair"), + capability_err_string + "<br/><br/>" + + tr("For example the Following Key:") + + " <br/>" + key.GetUIDs()->front().GetUID()); + return {}; } } - CommonUtils::WaitForOpera( - this, tr("Encrypting"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptFile( - {p_keys.begin(), p_keys.end()}, path, - !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgEncryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); - auto result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); + return keys; } -void MainWindow::SlotDirectoryEncrypt(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot read from file: %1").arg(QFileInfo(path).fileName())); - return; +auto MainWindow::execute_operas_helper( + const QString& task, const QSharedPointer<GpgOperaContexts>& contexts) { + CommonUtils::WaitForMultipleOperas(this, task, contexts->operas); + slot_result_analyse_show_helper(contexts->opera_results); +} + +void MainWindow::build_operas_file_symmetric_encrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptFileSymmetric( + path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgEncryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); + auto result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} + +void MainWindow::build_operas_file_encrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptFile( + context->keys, path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgEncryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); + auto result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); + } +} + +void MainWindow::build_operas_directory_symmetric_encrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptDirectorySymmetric( + path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (data_obj == nullptr || + !data_obj->Check<GpgEncryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); + auto result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); + } +} + +void MainWindow::build_operas_directory_encrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptDirectory( + context->keys, path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgEncryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); + auto result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); + } +} + +void MainWindow::SlotFileEncrypt(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); bool const non_ascii_at_file_operation = GlobalSettingStation::GetInstance() .GetSettings() .value("gnupg/non_ascii_at_file_operation", true) .toBool(); - auto out_path = SetExtensionOfOutputFileForArchive( - path, kENCRYPT, !non_ascii_at_file_operation); - if (QFile::exists(out_path)) { - auto out_file_name = tr("The target file %1 already exists, " - "do you need to overwrite it?") - .arg(out_path); - auto ret = QMessageBox::warning(this, tr("Warning"), out_file_name, - QMessageBox::Ok | QMessageBox::Cancel); + contexts->ascii = !non_ascii_at_file_operation; - if (ret == QMessageBox::Cancel) return; - } - - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } - - // check selected keys + bool is_symmetric = false; auto key_ids = m_key_list_->GetChecked(); - // symmetric encrypt - if (key_ids.empty()) { + + // Symmetric Encrypt + if (key_ids.isEmpty()) { auto ret = QMessageBox::information( this, tr("Symmetric Encryption"), tr("No Key Selected. Do you want to encrypt with a " "symmetric cipher using a passphrase?"), QMessageBox::Ok | QMessageBox::Cancel); if (ret == QMessageBox::Cancel) return; - - CommonUtils::WaitForOpera( - this, tr("Archiving & Symmetrically Encrypting"), - [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptDerectorySymmetric( - path, !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (data_obj == nullptr || - !data_obj->Check<GpgEncryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); - auto result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); - - return; + is_symmetric = true; + } else { + contexts->keys = check_keys_helper( + key_ids, + [](const GpgKey& key) { return key.IsHasActualEncryptionCapability(); }, + tr("The selected keypair cannot be used for encryption.")); + if (contexts->keys.empty()) return; + } + + if (!check_read_file_paths_helper(paths)) return; + + for (const auto& path : paths) { + QFileInfo info(path); + if (info.isDir()) { + contexts->GetContextPath(1).append(path); + contexts->GetContextOutPath(1).append( + SetExtensionOfOutputFileForArchive(path, kENCRYPT, contexts->ascii)); + } else { + contexts->GetContextPath(0).append(path); + contexts->GetContextOutPath(0).append( + SetExtensionOfOutputFile(path, kENCRYPT, contexts->ascii)); + } } - auto p_keys = - GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .GetKeys(key_ids); - assert(std::all_of(p_keys.begin(), p_keys.end(), - [](const auto& key) { return key.IsGood(); })); + if (!check_write_file_paths_helper(contexts->GetAllOutPath())) return; - // check key abilities - for (const auto& key : p_keys) { - bool const key_can_encrypt = key.IsHasActualEncryptionCapability(); - - if (!key_can_encrypt) { - QMessageBox::critical( - nullptr, tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for encryption.") + - "<br/><br/>" + tr("For example the Following Key:") + " <br/>" + - key.GetUIDs()->front().GetUID()); - return; + // Symmetric Encrypt + if (is_symmetric) { + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_symmetric_encrypt(f_context); + + auto d_context = contexts->GetContext(1); + if (d_context != nullptr) { + build_operas_directory_symmetric_encrypt(d_context); } + } else { + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_encrypt(f_context); + + auto d_context = contexts->GetContext(1); + if (d_context != nullptr) build_operas_directory_encrypt(d_context); } - CommonUtils::WaitForOpera( - this, tr("Archiving & Encrypting"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptDirectory( - {p_keys.begin(), p_keys.end()}, path, - !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgEncryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgEncryptResult>(data_obj, 0); - auto result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); + execute_operas_helper(tr("Encrypting"), contexts); } -void MainWindow::SlotFileDecrypt(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot read from file: %1").arg(QFileInfo(path).fileName())); - return; +/** + * @brief + * + * @param context + */ +void MainWindow::build_operas_file_decrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .DecryptFile( + path, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgDecryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgDecryptResult>(data_obj, 0); + auto result_analyse = GpgDecryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} - auto out_path = SetExtensionOfOutputFile(path, kDECRYPT, true); - if (QFileInfo(out_path).exists()) { - auto ret = QMessageBox::warning( - this, tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); - - if (ret == QMessageBox::Cancel) return; +void MainWindow::build_operas_archive_decrypt( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .DecryptArchive( + path, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgDecryptResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgDecryptResult>(data_obj, 0); + auto result_analyse = GpgDecryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } +void MainWindow::SlotFileDecrypt(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); - CommonUtils::WaitForOpera( - this, tr("Decrypting"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .DecryptFile( - path, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgDecryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgDecryptResult>(data_obj, 0); - auto result_analyse = GpgDecryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); -} + contexts->ascii = true; -void MainWindow::SlotArchiveDecrypt(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot read from file: %1").arg(path)); - return; - } + if (!check_read_file_paths_helper(paths)) return; - auto out_path = SetExtensionOfOutputFileForArchive(path, kDECRYPT, true); - if (QFileInfo(out_path).exists()) { - auto ret = QMessageBox::warning( - this, tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); + for (const auto& path : paths) { + QFileInfo info(path); + const auto extension = info.completeSuffix(); - if (ret == QMessageBox::Cancel) return; + if (extension == "tar.gpg" || extension == "tar.asc") { + contexts->GetContextPath(1).append(path); + contexts->GetContextOutPath(1).append( + SetExtensionOfOutputFileForArchive(path, kDECRYPT, contexts->ascii)); + } else { + contexts->GetContextPath(0).append(path); + contexts->GetContextOutPath(0).append( + SetExtensionOfOutputFile(path, kDECRYPT, contexts->ascii)); + } } - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } + if (!check_write_file_paths_helper(contexts->GetAllOutPath())) return; - CommonUtils::WaitForOpera( - this, tr("Decrypting & Extrating"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .DecryptArchive( - path, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgDecryptResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgDecryptResult>(data_obj, 0); - auto result_analyse = GpgDecryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); -} + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_decrypt(f_context); -void MainWindow::SlotFileSign(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot read from file: %1").arg(QFileInfo(path).fileName())); - return; + auto d_context = contexts->GetContext(1); + if (d_context != nullptr) { + build_operas_archive_decrypt(d_context); } - auto key_ids = m_key_list_->GetChecked(); - auto keys = - GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .GetKeys(key_ids); - assert(std::all_of(keys.begin(), keys.end(), - [](const auto& key) { return key.IsGood(); })); + execute_operas_helper(tr("Decrypting"), contexts); +} - if (keys.empty()) { - QMessageBox::critical( - this, tr("No Key Checked"), - tr("Please check the key in the key toolbox on the right.")); - return; +void MainWindow::build_operas_file_sign( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .SignFile( + context->keys, path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || !data_obj->Check<GpgSignResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgSignResult>(data_obj, 0); + auto result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + opera_results.append({result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} - for (const auto& key : keys) { - if (!key.IsHasActualSigningCapability()) { - QMessageBox::information( - this, tr("Invalid Operation"), - tr("The selected key contains a key that does not actually " - "have a sign usage.") + - "<br/><br/>" + tr("for example the Following Key:") + " <br/>" + - key.GetUIDs()->front().GetUID()); - return; - } - } +void MainWindow::SlotFileSign(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); bool const non_ascii_at_file_operation = GlobalSettingStation::GetInstance() .GetSettings() .value("gnupg/non_ascii_at_file_operation", true) .toBool(); - auto sig_file_path = - SetExtensionOfOutputFile(path, kSIGN, !non_ascii_at_file_operation); - if (QFileInfo(sig_file_path).exists()) { - auto ret = QMessageBox::warning(this, tr("Warning"), - tr("The signature file \"%1\" exists, " - "do you need to overwrite it?") - .arg(sig_file_path), - QMessageBox::Ok | QMessageBox::Cancel); + contexts->ascii = !non_ascii_at_file_operation; - if (ret == QMessageBox::Cancel) return; + auto key_ids = m_key_list_->GetChecked(); + + contexts->keys = check_keys_helper( + key_ids, + [](const GpgKey& key) { return key.IsHasActualSigningCapability(); }, + tr("The selected key contains a key that does not actually have a sign " + "usage.")); + if (contexts->keys.empty()) return; + + if (!check_read_file_paths_helper(paths)) return; + + for (const auto& path : paths) { + QFileInfo info(path); + contexts->GetContextPath(0).append(path); + contexts->GetContextOutPath(0).append( + SetExtensionOfOutputFile(path, kSIGN, contexts->ascii)); } - CommonUtils::WaitForOpera( - this, tr("Signing"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .SignFile({keys.begin(), keys.end()}, path, - !non_ascii_at_file_operation, sig_file_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgSignResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgSignResult>(data_obj, 0); - auto result_analyse = GpgSignResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - this->slot_refresh_current_file_view(); - }); - }); + if (!check_write_file_paths_helper(contexts->GetAllOutPath())) return; + + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_sign(f_context); + + execute_operas_helper(tr("Signing"), contexts); } -void MainWindow::SlotFileVerify(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot read from file: %1").arg(QFileInfo(path).fileName())); - return; +void MainWindow::build_operas_file_verify( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .VerifyFile( + o_path, path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgVerifyResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + + auto result = ExtractParams<GpgVerifyResult>(data_obj, 0); + auto result_analyse = GpgVerifyResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, result); + result_analyse.Analyse(); + + slot_result_analyse_show_helper(result_analyse); + + if (!result_analyse.GetUnknownSignatures().isEmpty() && + Module::IsModuleActivate(kKeyServerSyncModuleID)) { + slot_verifying_unknown_signature_helper(result_analyse); + } + + opera_results.append( + {result_analyse.GetStatus(), + result_analyse.GetResultReport(), + QFileInfo(path.isEmpty() ? o_path : path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} - auto file_info = QFileInfo(path); - QString sign_file_path = path; - QString data_file_path; +void MainWindow::SlotFileVerify(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); - bool const prossible_singleton_target = - file_info.suffix() == "gpg" || file_info.suffix() == "pgp"; - if (prossible_singleton_target) { - swap(data_file_path, sign_file_path); - } else { - data_file_path = file_info.path() + "/" + file_info.completeBaseName(); - } + if (!check_read_file_paths_helper(paths)) return; - auto data_file_info = QFileInfo(data_file_path); - if (!prossible_singleton_target && !data_file_info.exists()) { - bool ok; - QString const text = QInputDialog::getText( - this, tr("File to be Verified"), - tr("Please provide An ABSOLUTE Path \n" - "If Data And Signature is COMBINED within a single file, " - "KEEP THIS EMPTY: "), - QLineEdit::Normal, data_file_path, &ok); + for (const auto& path : paths) { + QFileInfo info(path); - if (!ok) return; + QString sign_file_path = path; + QString data_file_path; - data_file_path = text.isEmpty() ? data_file_path : text; - data_file_info = QFileInfo(data_file_path); - } + bool const possible_singleton_target = + info.suffix() == "gpg" || info.suffix() == "pgp"; + if (possible_singleton_target) { + swap(data_file_path, sign_file_path); + } else { + data_file_path = info.path() + "/" + info.completeBaseName(); + } - if (!data_file_info.isFile() || - (!sign_file_path.isEmpty() && !QFileInfo(sign_file_path).isFile())) { - QMessageBox::critical( - this, tr("Error"), - tr("Please select the appropriate origin file or signature file. " - "Ensure that both are in this directory.")); - return; - } + auto data_file_info = QFileInfo(data_file_path); + if (!possible_singleton_target && !data_file_info.exists()) { + bool ok; + QString const text = QInputDialog::getText( + this, tr("File to be Verified"), + tr("Please provide An ABSOLUTE Path \n" + "If Data And Signature is COMBINED within a single file, " + "KEEP THIS EMPTY: "), + QLineEdit::Normal, data_file_path, &ok); - CommonUtils::WaitForOpera( - this, tr("Verifying"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .VerifyFile( - data_file_path, sign_file_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgVerifyResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - - auto result = ExtractParams<GpgVerifyResult>(data_obj, 0); - auto result_analyse = GpgVerifyResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, result); - result_analyse.Analyse(); - - slot_result_analyse_show_helper(result_analyse); - - if (!result_analyse.GetUnknownSignatures().isEmpty() && - Module::IsModuleActivate(kKeyServerSyncModuleID)) { - slot_verifying_unknown_signature_helper(result_analyse); - } - - this->slot_refresh_current_file_view(); - }); - }); -} + if (!ok) return; -void MainWindow::SlotFileEncryptSign(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot read from file: %1").arg(path)); - return; + data_file_path = text.isEmpty() ? data_file_path : text; + data_file_info = QFileInfo(data_file_path); + } + + contexts->GetContextPath(0).append(sign_file_path); + contexts->GetContextOutPath(0).append(data_file_path); } - // check selected keys - auto key_ids = m_key_list_->GetChecked(); - auto p_keys = - GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .GetKeys(key_ids); - assert(std::all_of(p_keys.begin(), p_keys.end(), - [](const auto& key) { return key.IsGood(); })); + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_verify(f_context); - if (p_keys.empty()) { - QMessageBox::critical( - this, tr("No Key Checked"), - tr("Please check the key in the key toolbox on the right.")); - return; + execute_operas_helper(tr("Verifying"), contexts); +} + +void MainWindow::build_operas_file_encrypt_sign( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptSignFile( + context->keys, context->singer_keys, path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgEncryptResult, GpgSignResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + auto encrypt_result = + ExtractParams<GpgEncryptResult>(data_obj, 0); + auto sign_result = ExtractParams<GpgSignResult>(data_obj, 1); + + auto encrypt_result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + encrypt_result); + encrypt_result_analyse.Analyse(); + + auto sign_result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + sign_result); + sign_result_analyse.Analyse(); + + opera_results.append( + {std::min(encrypt_result_analyse.GetStatus(), + sign_result_analyse.GetStatus()), + encrypt_result_analyse.GetResultReport() + + sign_result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} - // check key abilities - for (const auto& key : p_keys) { - bool const key_can_encrypt = key.IsHasActualEncryptionCapability(); - - if (!key_can_encrypt) { - QMessageBox::critical( - nullptr, tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for encryption.") + - "<br/><br/>" + tr("For example the Following Key:") + " <br/>" + - key.GetUIDs()->front().GetUID()); - return; - } +void MainWindow::build_operas_directory_encrypt_sign( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .EncryptSignDirectory( + context->keys, context->singer_keys, path, context->ascii, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgEncryptResult, GpgSignResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + auto encrypt_result = + ExtractParams<GpgEncryptResult>(data_obj, 0); + auto sign_result = ExtractParams<GpgSignResult>(data_obj, 1); + + auto encrypt_result_analyse = GpgEncryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + encrypt_result); + encrypt_result_analyse.Analyse(); + + auto sign_result_analyse = GpgSignResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + sign_result); + sign_result_analyse.Analyse(); + + opera_results.append( + {std::min(encrypt_result_analyse.GetStatus(), + sign_result_analyse.GetStatus()), + encrypt_result_analyse.GetResultReport() + + sign_result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + }); + }; + + context->operas.push_back(opera); } +} + +void MainWindow::SlotFileEncryptSign(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); bool const non_ascii_at_file_operation = GlobalSettingStation::GetInstance() .GetSettings() .value("gnupg/non_ascii_at_file_operation", true) .toBool(); - auto out_path = SetExtensionOfOutputFile(path, kENCRYPT_SIGN, - !non_ascii_at_file_operation); - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } + contexts->ascii = !non_ascii_at_file_operation; - if (QFile::exists(out_path)) { - auto ret = QMessageBox::warning( - this, tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); + auto key_ids = m_key_list_->GetChecked(); - if (ret == QMessageBox::Cancel) return; - } + contexts->keys = check_keys_helper( + key_ids, + [](const GpgKey& key) { return key.IsHasActualEncryptionCapability(); }, + tr("The selected keypair cannot be used for encryption.")); + if (contexts->keys.empty()) return; auto* signers_picker = new SignersPicker(m_key_list_->GetCurrentGpgContextChannel(), this); @@ -633,350 +791,190 @@ void MainWindow::SlotFileEncryptSign(const QString& path) { if (!signers_picker->GetStatus()) return; auto signer_key_ids = signers_picker->GetCheckedSigners(); - auto p_signer_keys = + auto signer_keys = GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) .GetKeys(signer_key_ids); - assert(std::all_of(p_signer_keys.begin(), p_signer_keys.end(), + assert(std::all_of(signer_keys.begin(), signer_keys.end(), [](const auto& key) { return key.IsGood(); })); - CommonUtils::WaitForOpera( - this, tr("Encrypting and Signing"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptSignFile( - {p_keys.begin(), p_keys.end()}, - {p_signer_keys.begin(), p_signer_keys.end()}, path, - !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgEncryptResult, GpgSignResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - auto encrypt_result = - ExtractParams<GpgEncryptResult>(data_obj, 0); - auto sign_result = ExtractParams<GpgSignResult>(data_obj, 1); - - auto encrypt_result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - encrypt_result); - encrypt_result_analyse.Analyse(); - - auto sign_result_analyse = GpgSignResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - sign_result); - sign_result_analyse.Analyse(); - - slot_result_analyse_show_helper(encrypt_result_analyse, - sign_result_analyse); - - this->slot_refresh_current_file_view(); - }); - }); -} + contexts->singer_keys = signer_keys; -void MainWindow::SlotDirectoryEncryptSign(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot read from file: %1").arg(path)); - return; - } + if (!check_read_file_paths_helper(paths)) return; - // check selected keys - auto key_ids = m_key_list_->GetChecked(); - auto p_keys = - GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .GetKeys(key_ids); - assert(std::all_of(p_keys.begin(), p_keys.end(), - [](const auto& key) { return key.IsGood(); })); - - if (p_keys.empty()) { - QMessageBox::critical( - this, tr("No Key Checked"), - tr("Please check the key in the key toolbox on the right.")); - return; - } - - // check key abilities - for (const auto& key : p_keys) { - bool const key_can_encrypt = key.IsHasActualEncryptionCapability(); - - if (!key_can_encrypt) { - QMessageBox::critical( - nullptr, tr("Invalid KeyPair"), - tr("The selected keypair cannot be used for encryption.") + - "<br/><br/>" + tr("For example the Following Key:") + " <br/>" + - key.GetUIDs()->front().GetUID()); - return; + for (const auto& path : paths) { + QFileInfo info(path); + if (info.isDir()) { + contexts->GetContextPath(1).append(path); + contexts->GetContextOutPath(1).append( + SetExtensionOfOutputFileForArchive(path, kENCRYPT, contexts->ascii)); + } else { + contexts->GetContextPath(0).append(path); + contexts->GetContextOutPath(0).append( + SetExtensionOfOutputFile(path, kENCRYPT, contexts->ascii)); } } - bool const non_ascii_at_file_operation = - GlobalSettingStation::GetInstance() - .GetSettings() - .value("gnupg/non_ascii_at_file_operation", true) - .toBool(); - auto out_path = SetExtensionOfOutputFileForArchive( - path, kENCRYPT_SIGN, !non_ascii_at_file_operation); - - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } + if (!check_write_file_paths_helper(contexts->GetAllOutPath())) return; - if (QFile::exists(out_path)) { - auto ret = QMessageBox::warning( - this, tr("Warning"), - tr("The target file already exists, do you need to overwrite it?"), - QMessageBox::Ok | QMessageBox::Cancel); + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_encrypt_sign(f_context); - if (ret == QMessageBox::Cancel) return; - } - - auto* signers_picker = - new SignersPicker(m_key_list_->GetCurrentGpgContextChannel(), this); - QEventLoop loop; - connect(signers_picker, &SignersPicker::finished, &loop, &QEventLoop::quit); - loop.exec(); + auto d_context = contexts->GetContext(1); + if (d_context != nullptr) build_operas_directory_encrypt_sign(d_context); - // return when canceled - if (!signers_picker->GetStatus()) return; + execute_operas_helper(tr("Encrypting and Signing"), contexts); +} - auto signer_key_ids = signers_picker->GetCheckedSigners(); - auto p_signer_keys = - GpgKeyGetter::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .GetKeys(signer_key_ids); -#ifndef NDEBUG - for (const auto& key : p_signer_keys) { - assert(key.IsGood()); +void MainWindow::build_operas_file_decrypt_verify( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .DecryptVerifyFile( + path, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgDecryptResult, GpgVerifyResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + auto decrypt_result = + ExtractParams<GpgDecryptResult>(data_obj, 0); + auto verify_result = + ExtractParams<GpgVerifyResult>(data_obj, 1); + + auto decrypt_result_analyse = GpgDecryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + decrypt_result); + decrypt_result_analyse.Analyse(); + + auto verify_result_analyse = GpgVerifyResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + verify_result); + verify_result_analyse.Analyse(); + + opera_results.append( + {std::min(decrypt_result_analyse.GetStatus(), + verify_result_analyse.GetStatus()), + decrypt_result_analyse.GetResultReport() + + verify_result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + + if (!verify_result_analyse.GetUnknownSignatures().isEmpty() && + Module::IsModuleActivate(kKeyServerSyncModuleID)) { + slot_verifying_unknown_signature_helper( + verify_result_analyse); + } + }); + }; + + context->operas.push_back(opera); } -#endif - - CommonUtils::WaitForOpera( - this, tr("Archiving & Encrypting & Signing"), - [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .EncryptSignDirectory( - {p_keys.begin(), p_keys.end()}, - {p_signer_keys.begin(), p_signer_keys.end()}, path, - !non_ascii_at_file_operation, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgEncryptResult, GpgSignResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - auto encrypt_result = - ExtractParams<GpgEncryptResult>(data_obj, 0); - auto sign_result = ExtractParams<GpgSignResult>(data_obj, 1); - - auto encrypt_result_analyse = GpgEncryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - encrypt_result); - encrypt_result_analyse.Analyse(); - - auto sign_result_analyse = GpgSignResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - sign_result); - sign_result_analyse.Analyse(); - - slot_result_analyse_show_helper(encrypt_result_analyse, - sign_result_analyse); - - this->slot_refresh_current_file_view(); - }); - }); } -void MainWindow::SlotFileDecryptVerify(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot read from file: %1").arg(path)); - return; +void MainWindow::build_operas_archive_decrypt_verify( + QSharedPointer<GpgOperaContext>& context) { + assert(context->paths.size() == context->o_paths.size()); + + for (int i = 0; i < context->paths.size(); i++) { + const auto& path = context->paths[i]; + const auto& o_path = context->o_paths[i]; + + auto opera = [=, &opera_results = + context->opera_results](const OperaWaitingHd& op_hd) { + GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) + .DecryptVerifyArchive( + path, o_path, + [=, &opera_results](GpgError err, const DataObjectPtr& data_obj) { + // stop waiting + op_hd(); + + if (CheckGpgError(err) == GPG_ERR_USER_1 || + data_obj == nullptr || + !data_obj->Check<GpgDecryptResult, GpgVerifyResult>()) { + QMessageBox::critical(this, tr("Error"), + tr("Unknown error occurred")); + return; + } + auto decrypt_result = + ExtractParams<GpgDecryptResult>(data_obj, 0); + auto verify_result = + ExtractParams<GpgVerifyResult>(data_obj, 1); + + auto decrypt_result_analyse = GpgDecryptResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + decrypt_result); + decrypt_result_analyse.Analyse(); + + auto verify_result_analyse = GpgVerifyResultAnalyse( + m_key_list_->GetCurrentGpgContextChannel(), err, + verify_result); + verify_result_analyse.Analyse(); + + opera_results.append( + {std::min(decrypt_result_analyse.GetStatus(), + verify_result_analyse.GetStatus()), + decrypt_result_analyse.GetResultReport() + + verify_result_analyse.GetResultReport(), + QFileInfo(path).fileName()}); + + if (!verify_result_analyse.GetUnknownSignatures().isEmpty() && + Module::IsModuleActivate(kKeyServerSyncModuleID)) { + slot_verifying_unknown_signature_helper( + verify_result_analyse); + } + }); + }; + + context->operas.push_back(opera); } +} - auto out_path = SetExtensionOfOutputFile(path, kDECRYPT_VERIFY, true); - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } +void MainWindow::SlotFileDecryptVerify(const QContainer<QString>& paths) { + auto contexts = QSharedPointer<GpgOperaContexts>::create(); - if (QFile::exists(out_path)) { - auto ret = QMessageBox::warning(this, tr("Warning"), - tr("The output file %1 already exists, do " - "you need to overwrite it?") - .arg(out_path), - QMessageBox::Ok | QMessageBox::Cancel); + contexts->ascii = true; - if (ret == QMessageBox::Cancel) return; - } + if (!check_read_file_paths_helper(paths)) return; - CommonUtils::WaitForOpera( - this, tr("Decrypting and Verifying"), [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .DecryptVerifyFile( - path, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgDecryptResult, GpgVerifyResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - auto decrypt_result = - ExtractParams<GpgDecryptResult>(data_obj, 0); - auto verify_result = - ExtractParams<GpgVerifyResult>(data_obj, 1); - - auto decrypt_result_analyse = GpgDecryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - decrypt_result); - decrypt_result_analyse.Analyse(); - - auto verify_result_analyse = GpgVerifyResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - verify_result); - verify_result_analyse.Analyse(); - - slot_result_analyse_show_helper(decrypt_result_analyse, - verify_result_analyse); - - this->slot_refresh_current_file_view(); - - if (!verify_result_analyse.GetUnknownSignatures().isEmpty() && - Module::IsModuleActivate(kKeyServerSyncModuleID)) { - LOG_D() << "try to sync missing key info from server" - << verify_result_analyse.GetUnknownSignatures(); - - QString fingerprint_list; - for (const auto& fingerprint : - verify_result_analyse.GetUnknownSignatures()) { - fingerprint_list += fingerprint + "\n"; - } - - // Interaction with user - auto user_response = QMessageBox::question( - this, tr("Missing Keys"), - tr("Some signatures cannot be verified because the " - "corresponding keys are missing.\n\n" - "The following fingerprints are missing:\n%1\n\n" - "Would you like to fetch these keys from the key " - "server?") - .arg(fingerprint_list), - QMessageBox::Yes | QMessageBox::No); - - if (user_response == QMessageBox::Yes) { - CommonUtils::GetInstance() - ->ImportKeyByKeyServerSyncModule( - this, m_key_list_->GetCurrentGpgContextChannel(), - verify_result_analyse.GetUnknownSignatures()); - } else { - QMessageBox::information( - this, tr("Verification Incomplete"), - tr("Verification was incomplete due to missing " - "keys. You can manually import the keys later.")); - } - } - }); - }); -} + for (const auto& path : paths) { + QFileInfo info(path); + const auto extension = info.completeSuffix(); -void MainWindow::SlotArchiveDecryptVerify(const QString& path) { - auto check_result = TargetFilePreCheck(path, true); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot read from file: %1").arg(path)); - return; + if (extension == "tar.gpg" || extension == "tar.asc") { + contexts->GetContextPath(1).append(path); + contexts->GetContextOutPath(1).append( + SetExtensionOfOutputFileForArchive(path, kDECRYPT, contexts->ascii)); + } else { + contexts->GetContextPath(0).append(path); + contexts->GetContextOutPath(0).append( + SetExtensionOfOutputFile(path, kDECRYPT, contexts->ascii)); + } } - auto out_path = - SetExtensionOfOutputFileForArchive(path, kDECRYPT_VERIFY, true); - check_result = TargetFilePreCheck(out_path, false); - if (!std::get<0>(check_result)) { - QMessageBox::critical(this, tr("Error"), - tr("Cannot write to file: %1").arg(out_path)); - return; - } + if (!check_write_file_paths_helper(contexts->GetAllOutPath())) return; - if (QFile::exists(out_path)) { - auto ret = QMessageBox::warning(this, tr("Warning"), - tr("The output file %1 already exists, do " - "you need to overwrite it?") - .arg(out_path), - QMessageBox::Ok | QMessageBox::Cancel); + auto f_context = contexts->GetContext(0); + if (f_context != nullptr) build_operas_file_decrypt_verify(f_context); - if (ret == QMessageBox::Cancel) return; + auto d_context = contexts->GetContext(1); + if (d_context != nullptr) { + build_operas_archive_decrypt_verify(d_context); } - CommonUtils::WaitForOpera( - this, tr("Decrypting & Verifying & Extracting"), - [=](const OperaWaitingHd& op_hd) { - GpgFileOpera::GetInstance(m_key_list_->GetCurrentGpgContextChannel()) - .DecryptVerifyArchive( - path, out_path, - [=](GpgError err, const DataObjectPtr& data_obj) { - // stop waiting - op_hd(); - - if (CheckGpgError(err) == GPG_ERR_USER_1 || - data_obj == nullptr || - !data_obj->Check<GpgDecryptResult, GpgVerifyResult>()) { - QMessageBox::critical(this, tr("Error"), - tr("Unknown error occurred")); - return; - } - auto decrypt_result = - ExtractParams<GpgDecryptResult>(data_obj, 0); - auto verify_result = - ExtractParams<GpgVerifyResult>(data_obj, 1); - - auto decrypt_result_analyse = GpgDecryptResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - decrypt_result); - decrypt_result_analyse.Analyse(); - - auto verify_result_analyse = GpgVerifyResultAnalyse( - m_key_list_->GetCurrentGpgContextChannel(), err, - verify_result); - verify_result_analyse.Analyse(); - - slot_result_analyse_show_helper(decrypt_result_analyse, - verify_result_analyse); - - // pause this feature - // if (verify_result_analyse.GetStatus() == -2) { - // import_unknown_key_from_keyserver(this, - // verify_result_analyse); - // } - // pause this feature - // if (verify_result_analyse.GetStatus() >= 0) { - // show_verify_details(this, info_board_, err, - // verify_result); - // } - - this->slot_refresh_current_file_view(); - }); - }); -} + execute_operas_helper(tr("Decrypting and Verifying"), contexts); +}; void MainWindow::SlotFileVerifyEML(const QString& path) { auto check_result = TargetFilePreCheck(path, true); diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 03f51298..ee66aebb 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -48,6 +48,7 @@ #include "ui/dialog/import_export/KeyUploadDialog.h" #include "ui/dialog/keypair_details/KeyDetailsDialog.h" #include "ui/function/SetOwnerTrustLevel.h" +#include "ui/struct/GpgOperaResult.h" #include "ui/widgets/FindWidget.h" #include "ui/widgets/KeyList.h" #include "ui/widgets/TextEdit.h" @@ -464,13 +465,11 @@ void MainWindow::slot_verifying_unknown_signature_helper( auto user_response = QMessageBox::question(this, tr("Missing Keys"), tr("Some signatures cannot be verified because " - "the " - "corresponding keys are missing.\n\n" + "the corresponding keys are missing.\n\n" "The following fingerprints are " "missing:\n%1\n\n" "Would you like to fetch these keys from " - "the key " - "server?") + "the key server?") .arg(fingerprint_list), QMessageBox::Yes | QMessageBox::No); @@ -479,11 +478,11 @@ void MainWindow::slot_verifying_unknown_signature_helper( this, m_key_list_->GetCurrentGpgContextChannel(), result_analyse.GetUnknownSignatures()); } else { - QMessageBox::information(this, tr("Verification Incomplete"), - tr("Verification was incomplete due to " - "missing " - "keys. You can manually import the keys " - "later.")); + QMessageBox::information( + this, tr("Verification Incomplete"), + tr("Verification was incomplete due to " + "missing keys. You can manually import the keys " + "later.")); } } @@ -556,6 +555,73 @@ void MainWindow::slot_result_analyse_show_helper( result_analyse.GetResultReport()); } +void MainWindow::slot_result_analyse_show_helper( + const QContainer<GpgOperaResult>& opera_results) { + if (opera_results.empty()) { + slot_refresh_info_board(0, ""); + } + + int overall_status = 1; // Initialize to OK + QStringList report; + QStringList summary; + + QStringList failed_tags; + QStringList warning_tags; + + int success_count = 0; + int fail_count = 0; + int warn_count = 0; + + for (const auto& opera_result : opera_results) { + // Update overall status + overall_status = std::min(overall_status, opera_result.status); + + QString status_text; + if (opera_result.status < 0) { + status_text = tr("FAIL"); + failed_tags << opera_result.tag; + fail_count++; + } else if (opera_result.status > 0) { + status_text = tr("OK"); + success_count++; + } else { + status_text = tr("WARN"); + warning_tags << opera_result.tag; + warn_count++; + } + + // Append detailed report for each operation + report.append(QString("[ %1 ] %2\n\n%3\n") + .arg(status_text, opera_result.tag, opera_result.report)); + } + + // Prepare summary section + summary.append("# " + tr("Summary Report") + "\n\n"); + summary.append("- " + tr("Total Operations: %1\n").arg(opera_results.size())); + summary.append("- " + tr("Successful: %1\n").arg(success_count)); + summary.append("- " + tr("Warnings: %1\n").arg(warn_count)); + summary.append("- " + tr("Failures: %1\n").arg(fail_count)); + + if (!failed_tags.isEmpty()) { + summary.append("- " + + tr("Failed Objects: %1\n").arg(failed_tags.join(", "))); + } + + if (!warning_tags.isEmpty()) { + summary.append("- " + + tr("Warning Objects: %1\n").arg(warning_tags.join(", "))); + } + + // Display the final report in the info board + if (opera_results.size() == 1) { + slot_refresh_info_board(overall_status, report.join("")); + + } else { + slot_refresh_info_board(overall_status, + summary.join("") + "\n\n" + report.join("")); + } +} + void MainWindow::slot_refresh_info_board(int status, const QString& text) { info_board_->SlotReset(); diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index d3ba32b6..40df7dbc 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -208,14 +208,9 @@ void MainWindow::SlotUpdateCryptoMenuStatus(unsigned int type) { void MainWindow::SlotGeneralEncrypt(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); + const auto paths = file_tree_view->GetSelected(); - const auto file_info = QFileInfo(path); - if (file_info.isFile()) { - this->SlotFileEncrypt(path); - } else if (file_info.isDir()) { - this->SlotDirectoryEncrypt(path); - } + this->SlotFileEncrypt(paths); } if (edit_->CurEMailPage() != nullptr) { @@ -231,18 +226,9 @@ void MainWindow::SlotGeneralEncrypt(bool) { void MainWindow::SlotGeneralDecrypt(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); - - const auto file_info = QFileInfo(path); - if (file_info.isFile()) { - const QString extension = file_info.completeSuffix(); + const auto paths = file_tree_view->GetSelected(); - if (extension == "tar.gpg" || extension == "tar.asc") { - this->SlotArchiveDecrypt(path); - } else { - this->SlotFileDecrypt(path); - } - } + this->SlotFileDecrypt(paths); } if (edit_->CurEMailPage() != nullptr) { @@ -258,10 +244,9 @@ void MainWindow::SlotGeneralDecrypt(bool) { void MainWindow::SlotGeneralSign(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); + const auto paths = file_tree_view->GetSelected(); - const auto file_info = QFileInfo(path); - if (file_info.isFile()) this->SlotFileSign(path); + this->SlotFileSign(paths); } if (edit_->CurEMailPage() != nullptr) { @@ -275,10 +260,9 @@ void MainWindow::SlotGeneralSign(bool) { void MainWindow::SlotGeneralVerify(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); + const auto paths = file_tree_view->GetSelected(); - const auto file_info = QFileInfo(path); - if (file_info.isFile()) this->SlotFileVerify(path); + this->SlotFileVerify(paths); } if (edit_->CurEMailPage() != nullptr) { @@ -292,14 +276,9 @@ void MainWindow::SlotGeneralVerify(bool) { void MainWindow::SlotGeneralEncryptSign(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); + const auto paths = file_tree_view->GetSelected(); - const auto file_info = QFileInfo(path); - if (file_info.isFile()) { - this->SlotFileEncryptSign(path); - } else if (file_info.isDir()) { - this->SlotDirectoryEncryptSign(path); - } + this->SlotFileEncryptSign(paths); } if (edit_->CurEMailPage() != nullptr) { @@ -315,18 +294,9 @@ void MainWindow::SlotGeneralEncryptSign(bool) { void MainWindow::SlotGeneralDecryptVerify(bool) { if (edit_->CurPageFileTreeView() != nullptr) { const auto* file_tree_view = edit_->CurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); - - const auto file_info = QFileInfo(path); - if (file_info.isFile()) { - const QString extension = file_info.completeSuffix(); + const auto paths = file_tree_view->GetSelected(); - if (extension == "tar.gpg" || extension == "tar.asc") { - this->SlotArchiveDecryptVerify(path); - } else { - this->SlotFileDecryptVerify(path); - } - } + this->SlotFileDecryptVerify(paths); } if (edit_->CurEMailPage() != nullptr) { diff --git a/src/ui/struct/GpgOperaResult.h b/src/ui/struct/GpgOperaResult.h new file mode 100644 index 00000000..e3bfd61a --- /dev/null +++ b/src/ui/struct/GpgOperaResult.h @@ -0,0 +1,44 @@ +#include <utility> + +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +namespace GpgFrontend::UI { + +struct GpgOperaResult { + int status; + QString report; + QString tag; + + GpgOperaResult(int status, QString report, QString tag) + : status(status), report(std::move(report)), tag(std::move(tag)) {} +}; + +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/struct/GpgOperaResultContext.h b/src/ui/struct/GpgOperaResultContext.h new file mode 100644 index 00000000..f0073c7e --- /dev/null +++ b/src/ui/struct/GpgOperaResultContext.h @@ -0,0 +1,115 @@ +#include <utility> + +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include "ui/UserInterfaceUtils.h" +#include "ui/struct/GpgOperaResult.h" + +namespace GpgFrontend::UI { + +struct GpgOperaCategory { + QContainer<QString> paths; + QContainer<QString> o_paths; +}; + +struct GpgOperaContext { + QContainer<OperaWaitingCb>& operas; + QContainer<GpgOperaResult>& opera_results; + GpgKeyList& keys; + GpgKeyList& singer_keys; + QContainer<QString>& unknown_fprs; + bool ascii; + + QContainer<QString> paths; + QContainer<QString> o_paths; + + GpgOperaContext(QContainer<OperaWaitingCb>& operas, + QContainer<GpgOperaResult>& opera_results, GpgKeyList& keys, + GpgKeyList& singer_keys, QContainer<QString>& unknown_fprs) + : operas(operas), + opera_results(opera_results), + keys(keys), + singer_keys(singer_keys), + unknown_fprs(unknown_fprs) {} +}; + +struct GpgOperaContexts { + QContainer<OperaWaitingCb> operas; + QContainer<GpgOperaResult> opera_results; + GpgKeyList keys; + GpgKeyList singer_keys; + QContainer<QString> unknown_fprs; + bool ascii; + + QMap<int, GpgOperaCategory> categories; + + auto GetContextPath(int category) -> QContainer<QString>& { + if (!categories.contains(category)) categories[category] = {}; + return categories[category].paths; + } + + auto GetContextOutPath(int category) -> QContainer<QString>& { + if (!categories.contains(category)) categories[category] = {}; + return categories[category].o_paths; + } + + auto GetAllPath() -> QContainer<QString> { + QContainer<QString> res; + + for (auto& category : categories) { + res.append(category.paths); + } + return res; + } + + auto GetAllOutPath() -> QContainer<QString> { + QContainer<QString> res; + + for (auto& category : categories) { + res.append(category.o_paths); + } + return res; + } + + auto GetContext(int category) -> QSharedPointer<GpgOperaContext> { + if (GetContextPath(category).empty()) return nullptr; + + auto context = QSharedPointer<GpgOperaContext>::create( + operas, opera_results, keys, singer_keys, unknown_fprs); + context->ascii = ascii; + + context->paths = GetContextPath(category); + context->o_paths = GetContextOutPath(category); + return context; + } +}; + +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index 998b4245..5f6fbae0 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -73,6 +73,9 @@ FilePage::FilePage(QWidget* parent, const QString& target_path) option_popup_menu_->addAction(show_system_act); ui_->optionsButton->setMenu(option_popup_menu_); + connect(ui_->batchModeButton, &QToolButton::toggled, this, + [this](bool checked) { emit SignalSetBatchMode(checked); }); + connect(ui_->pathEdit, &QLineEdit::textChanged, [=]() { auto path = ui_->pathEdit->text(); auto dir = QDir(path); @@ -100,16 +103,16 @@ FilePage::FilePage(QWidget* parent, const QString& target_path) UISignalStation::GetInstance(), &UISignalStation::SignalMainWindowOpenFile); connect(file_tree_view_, &FileTreeView::SignalSelectedChanged, this, - &FilePage::update_main_basical_opera_menu); + &FilePage::update_main_basic_opera_menu); connect(this, &FilePage::SignalCurrentTabChanged, this, - [this]() { update_main_basical_opera_menu(GetSelected()); }); - connect(this, &FilePage::SignalMainWindowlUpdateBasicalOperaMenu, + [this]() { update_main_basic_opera_menu(GetSelected()); }); + connect(this, &FilePage::SignalMainWindowUpdateBasicOperaMenu, UISignalStation::GetInstance(), - &UISignalStation::SignalMainWindowlUpdateBasicalOperaMenu); + &UISignalStation::SignalMainWindowUpdateBasicOperaMenu); } -auto FilePage::GetSelected() const -> QString { - return file_tree_view_->GetSelectedPath(); +auto FilePage::GetSelected() const -> QContainer<QString> { + return file_tree_view_->GetSelectedPaths(); } void FilePage::SlotGoPath() { @@ -123,43 +126,71 @@ void FilePage::keyPressEvent(QKeyEvent* event) { } } -void FilePage::update_main_basical_opera_menu(const QString& selected_path) { +void FilePage::update_main_basic_opera_menu( + const QContainer<QString>& selected_paths) { + if (selected_paths.isEmpty()) { + emit SignalMainWindowUpdateBasicOperaMenu(MainWindow::OperationMenu::kNone); + return; + } + MainWindow::OperationMenu::OperationType operation_type = MainWindow::OperationMenu::kNone; - // abort... - if (selected_path.isEmpty()) return; + LOG_D() << "selected path size: " << selected_paths.size(); + LOG_D() << "selected paths: " << selected_paths; - QFileInfo const info(selected_path); + QContainer<QFileInfo> infos; - if ((info.isDir() || info.isFile()) && - (info.suffix() != "gpg" && info.suffix() != "pgp" && - info.suffix() != "sig" && info.suffix() != "asc")) { - operation_type |= MainWindow::OperationMenu::kEncrypt; + for (const auto& path : selected_paths) { + infos.append(QFileInfo(path)); } - if ((info.isDir() || info.isFile()) && - (info.suffix() != "gpg" && info.suffix() != "pgp" && - info.suffix() != "sig" && info.suffix() != "asc")) { - operation_type |= MainWindow::OperationMenu::kEncryptAndSign; - } + bool c_encr = + std::all_of(infos.cbegin(), infos.cend(), [](const QFileInfo& info) { + return (info.isDir() || info.isFile()) && + (info.suffix() != "gpg" && info.suffix() != "pgp" && + info.suffix() != "sig" && info.suffix() != "asc"); + }); - if (info.isFile() && (info.suffix() == "gpg" || info.suffix() == "pgp" || - info.suffix() == "asc")) { - operation_type |= MainWindow::OperationMenu::kDecrypt; - operation_type |= MainWindow::OperationMenu::kDecryptAndVerify; + if (c_encr) { + operation_type |= MainWindow::OperationMenu::kEncrypt | + MainWindow::OperationMenu::kEncryptAndSign; } - if (info.isFile() && (info.suffix() != "gpg" && info.suffix() != "pgp" && - info.suffix() != "sig" && info.suffix() != "asc")) { - operation_type |= MainWindow::OperationMenu::kSign; - } + bool c_decr = + std::all_of(infos.cbegin(), infos.cend(), [](const QFileInfo& info) { + return info.isFile() && + (info.suffix() == "gpg" || info.suffix() == "pgp" || + info.suffix() == "asc"); + }); - if (info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg" || - info.suffix() == "pgp" || info.suffix() == "asc")) { - operation_type |= MainWindow::OperationMenu::kVerify; + if (c_decr) { + operation_type |= MainWindow::OperationMenu::kDecrypt | + MainWindow::OperationMenu::kDecryptAndVerify; } - emit SignalMainWindowlUpdateBasicalOperaMenu(operation_type); + bool c_sign = + std::all_of(infos.cbegin(), infos.cend(), [](const QFileInfo& info) { + return info.isFile() && + (info.suffix() != "gpg" && info.suffix() != "pgp" && + info.suffix() != "sig" && info.suffix() != "asc"); + }); + + if (c_sign) operation_type |= MainWindow::OperationMenu::kSign; + + bool c_verify = + std::all_of(infos.cbegin(), infos.cend(), [](const QFileInfo& info) { + return info.isFile() && + (info.suffix() == "sig" || info.suffix() == "gpg" || + info.suffix() == "pgp" || info.suffix() == "asc"); + }); + + if (c_verify) operation_type |= MainWindow::OperationMenu::kVerify; + + emit SignalMainWindowUpdateBasicOperaMenu(operation_type); +} + +auto FilePage::IsBatchMode() const -> bool { + return ui_->batchModeButton->isChecked(); } } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/FilePage.h b/src/ui/widgets/FilePage.h index aaa61589..c6d9ec99 100644 --- a/src/ui/widgets/FilePage.h +++ b/src/ui/widgets/FilePage.h @@ -55,7 +55,15 @@ class FilePage : public QWidget { * * @return QString */ - [[nodiscard]] auto GetSelected() const -> QString; + [[nodiscard]] auto GetSelected() const -> QContainer<QString>; + + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto IsBatchMode() const -> bool; public slots: /** @@ -92,7 +100,13 @@ class FilePage : public QWidget { * * @param int */ - void SignalMainWindowlUpdateBasicalOperaMenu(unsigned int); + void SignalMainWindowUpdateBasicOperaMenu(unsigned int); + + /** + * @brief + * + */ + void SignalSetBatchMode(bool); protected: /** @@ -118,7 +132,7 @@ class FilePage : public QWidget { * @brief * */ - void update_main_basical_opera_menu(const QString&); + void update_main_basic_opera_menu(const QContainer<QString>&); }; } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/FileTreeView.cpp b/src/ui/widgets/FileTreeView.cpp index 500d9787..aa29d040 100644 --- a/src/ui/widgets/FileTreeView.cpp +++ b/src/ui/widgets/FileTreeView.cpp @@ -37,7 +37,7 @@ namespace GpgFrontend::UI { FileTreeView::FileTreeView(QWidget* parent, const QString& target_path) : QTreeView(parent) { - dir_model_ = new QFileSystemModel(); + dir_model_ = new QFileSystemModel(this); dir_model_->setRootPath(target_path.isEmpty() ? QDir::currentPath() : target_path); dir_model_->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); @@ -49,6 +49,7 @@ FileTreeView::FileTreeView(QWidget* parent, const QString& target_path) slot_create_popup_menu(); this->setContextMenuPolicy(Qt::CustomContextMenu); + this->setSelectionMode(QAbstractItemView::MultiSelection); connect(this, &QWidget::customContextMenuRequested, this, &FileTreeView::slot_show_custom_context_menu); @@ -64,16 +65,19 @@ void FileTreeView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { QTreeView::selectionChanged(selected, deselected); - if (!selected.indexes().empty()) { - selected_path_ = dir_model_->filePath(selected.indexes().first()); - emit SignalSelectedChanged(selected_path_); - } else { - selected_path_ = QString(); - if (!this->selectedIndexes().isEmpty()) { - selected_path_ = dir_model_->filePath(this->selectedIndexes().front()); - emit SignalSelectedChanged(selected_path_); + selected_paths_.clear(); + if (!this->selectedIndexes().isEmpty()) { + QSet<QString> paths; + for (const auto& index : this->selectedIndexes()) { + const auto path = dir_model_->filePath(index); + if (path == current_path_) continue; + + paths.insert(path); } + selected_paths_.append(paths.values()); } + + emit SignalSelectedChanged(selected_paths_); } void FileTreeView::SlotGoPath(const QString& target_path) { @@ -143,7 +147,9 @@ auto FileTreeView::GetPathByClickPoint(const QPoint& point) -> QString { return dir_model_->fileInfo(index).absoluteFilePath(); } -auto FileTreeView::GetSelectedPath() -> QString { return selected_path_; } +auto FileTreeView::GetSelectedPaths() -> QContainer<QString> { + return selected_paths_; +} auto FileTreeView::SlotDeleteSelectedItem() -> void { QModelIndex const index = this->currentIndex(); @@ -209,7 +215,9 @@ void FileTreeView::SlotTouch() { } void FileTreeView::SlotTouchBelowAtSelectedItem() { - auto root_path(selected_path_); + if (selected_paths_.size() != 1) return; + + auto root_path(selected_paths_.front()); if (root_path.isEmpty()) root_path = dir_model_->rootPath(); QString new_file_name; @@ -243,23 +251,29 @@ void FileTreeView::keyPressEvent(QKeyEvent* event) { } void FileTreeView::SlotOpenSelectedItemBySystemApplication() { - QFileInfo const info(selected_path_); + if (selected_paths_.size() != 1) return; + + auto selected_path = selected_paths_.front(); + QFileInfo const info(selected_path); if (info.isDir()) { const auto file_path = info.filePath().toUtf8(); - QDesktopServices::openUrl(QUrl::fromLocalFile(selected_path_)); + QDesktopServices::openUrl(QUrl::fromLocalFile(selected_path)); } else { - QDesktopServices::openUrl(QUrl::fromLocalFile(selected_path_)); + QDesktopServices::openUrl(QUrl::fromLocalFile(selected_path)); } } void FileTreeView::SlotRenameSelectedItem() { + if (selected_paths_.size() != 1) return; + bool ok; + auto selected_path = selected_paths_.front(); auto text = QInputDialog::getText(this, tr("Rename"), tr("New Filename"), QLineEdit::Normal, - QFileInfo(selected_path_).fileName(), &ok); + QFileInfo(selected_path).fileName(), &ok); if (ok && !text.isEmpty()) { - auto file_info = QFileInfo(selected_path_); + auto file_info = QFileInfo(selected_path); auto new_name_path = file_info.absolutePath() + "/" + text; if (!QDir().rename(file_info.absoluteFilePath(), new_name_path)) { @@ -282,8 +296,9 @@ void FileTreeView::slot_create_popup_menu() { action_open_file_ = new QAction(this); action_open_file_->setText(tr("Open")); - connect(action_open_file_, &QAction::triggered, this, - [this](bool) { emit SignalOpenFile(GetSelectedPath()); }); + connect(action_open_file_, &QAction::triggered, this, [this](bool) { + for (const auto& path : GetSelectedPaths()) emit SignalOpenFile(path); + }); action_rename_file_ = new QAction(this); action_rename_file_->setText(tr("Rename")); @@ -342,8 +357,11 @@ void FileTreeView::slot_create_popup_menu() { void FileTreeView::slot_show_custom_context_menu(const QPoint& point) { auto target_path = this->GetPathByClickPoint(point); - auto select_path = GetSelectedPath(); + auto select_paths = GetSelectedPaths(); + if (select_paths.size() != 1) return; + + auto select_path = select_paths.front(); if (target_path.isEmpty() && !select_path.isEmpty()) { target_path = select_path; } @@ -378,11 +396,14 @@ void FileTreeView::slot_show_custom_context_menu(const QPoint& point) { } void FileTreeView::slot_calculate_hash() { + if (GetSelectedPaths().empty()) return; + auto selected_path = GetSelectedPaths().front(); + CommonUtils::WaitForOpera( this->parentWidget(), tr("Calculating"), [=](const OperaWaitingHd& hd) { RunOperaAsync( [=](const DataObjectPtr& data_object) { - data_object->Swap({CalculateHash(this->GetSelectedPath())}); + data_object->Swap({CalculateHash(selected_path)}); return 0; }, [hd](int rtn, const DataObjectPtr& data_object) { diff --git a/src/ui/widgets/FileTreeView.h b/src/ui/widgets/FileTreeView.h index d99ff605..aa6c85dc 100644 --- a/src/ui/widgets/FileTreeView.h +++ b/src/ui/widgets/FileTreeView.h @@ -28,6 +28,8 @@ #pragma once +#include "core/typedef/CoreTypedef.h" + namespace GpgFrontend::UI { class FileTreeView : public QTreeView { @@ -53,7 +55,7 @@ class FileTreeView : public QTreeView { * * @return QString */ - auto GetSelectedPath() -> QString; + auto GetSelectedPaths() -> QContainer<QString>; /** * @brief Get the Path By Click Point object @@ -114,7 +116,7 @@ class FileTreeView : public QTreeView { * @brief * */ - void SignalSelectedChanged(const QString&); + void SignalSelectedChanged(const QContainer<QString>&); /** * @brief @@ -232,9 +234,9 @@ class FileTreeView : public QTreeView { void slot_adjust_column_widths(); private: - QFileSystemModel* dir_model_; ///< - QString current_path_; ///< - QString selected_path_; ///< + QFileSystemModel* dir_model_; ///< + QString current_path_; ///< + QContainer<QString> selected_paths_; ///< QMenu* popup_menu_; QMenu* new_item_action_menu_; diff --git a/src/ui/widgets/TextEditTabWidget.cpp b/src/ui/widgets/TextEditTabWidget.cpp index fbf1a3e7..69f95b1f 100644 --- a/src/ui/widgets/TextEditTabWidget.cpp +++ b/src/ui/widgets/TextEditTabWidget.cpp @@ -324,8 +324,8 @@ void TextEditTabWidget::slot_file_page_path_changed(const QString& path) { } this->setTabText(index, m_path); - emit UISignalStation::GetInstance() - -> SignalMainWindowlUpdateBasicalOperaMenu(0); + emit UISignalStation::GetInstance() -> SignalMainWindowUpdateBasicOperaMenu( + 0); } } // namespace GpgFrontend::UI diff --git a/ui/FilePage.ui b/ui/FilePage.ui index 5ee3058e..69143680 100644 --- a/ui/FilePage.ui +++ b/ui/FilePage.ui @@ -44,20 +44,20 @@ <item row="0" column="0"> <layout class="QVBoxLayout" name="verticalLayout"> <property name="sizeConstraint"> - <enum>QLayout::SetMaximumSize</enum> + <enum>QLayout::SizeConstraint::SetMaximumSize</enum> </property> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <property name="sizeConstraint"> - <enum>QLayout::SetMaximumSize</enum> + <enum>QLayout::SizeConstraint::SetMaximumSize</enum> </property> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0,0"> + <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0,0,0"> <property name="spacing"> <number>5</number> </property> <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> + <enum>QLayout::SizeConstraint::SetDefaultConstraint</enum> </property> <property name="leftMargin"> <number>5</number> @@ -142,6 +142,26 @@ </widget> </item> <item> + <widget class="QToolButton" name="batchModeButton"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../gpgfrontend.qrc"> + <normaloff>:/icons/batch.png</normaloff>:/icons/batch.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="popupMode"> + <enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum> + </property> + <property name="autoRaise"> + <bool>false</bool> + </property> + </widget> + </item> + <item> <widget class="QToolButton" name="optionsButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> @@ -157,10 +177,10 @@ <normaloff>:/icons/configure.png</normaloff>:/icons/configure.png</iconset> </property> <property name="popupMode"> - <enum>QToolButton::InstantPopup</enum> + <enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum> </property> <property name="toolButtonStyle"> - <enum>Qt::ToolButtonIconOnly</enum> + <enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum> </property> </widget> </item> |