diff options
40 files changed, 756 insertions, 691 deletions
diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index 4e904366..fa72ca3e 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -105,6 +105,7 @@ <file alias="publish.png">resource/lfs/icons/publish.png</file> <file alias="email-check.png">resource/lfs/icons/email-check.png</file> <file alias="email-open.png">resource/lfs/icons/email-open.png</file> + <file alias="export-email.png">resource/lfs/icons/export-email.png</file> </qresource> <qresource prefix="/test/key"> <file alias="pv1.key">resource/lfs/test/data/pv1.key</file> diff --git a/modules b/modules -Subproject 46d2b31ea023b3b1c886fb7b812c86ec3c4ab0c +Subproject 4b846455b7c232b7db63f95c6a1943ccbc7ce15 diff --git a/resource/lfs/icons/email.png b/resource/lfs/icons/email.png Binary files differindex f0517848..58c55528 100644 --- a/resource/lfs/icons/email.png +++ b/resource/lfs/icons/email.png diff --git a/resource/lfs/icons/export-email.png b/resource/lfs/icons/export-email.png Binary files differnew file mode 100644 index 00000000..fcf87bbe --- /dev/null +++ b/resource/lfs/icons/export-email.png diff --git a/resource/lfs/icons/misc_doc.png b/resource/lfs/icons/misc_doc.png Binary files differindex 57878ba3..25d2eb55 100644 --- a/resource/lfs/icons/misc_doc.png +++ b/resource/lfs/icons/misc_doc.png diff --git a/src/core/model/GpgSignResult.cpp b/src/core/model/GpgSignResult.cpp index 1819c22b..b7fd2e45 100644 --- a/src/core/model/GpgSignResult.cpp +++ b/src/core/model/GpgSignResult.cpp @@ -62,4 +62,9 @@ auto GpgSignResult::InvalidSigners() } return result; } + +auto GpgSignResult::HashAlgo() -> QString { + if (result_ref_->signatures == nullptr) return {}; + return gpgme_hash_algo_name(result_ref_->signatures->hash_algo); +} } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgSignResult.h b/src/core/model/GpgSignResult.h index a7356c65..3881c86f 100644 --- a/src/core/model/GpgSignResult.h +++ b/src/core/model/GpgSignResult.h @@ -39,6 +39,8 @@ class GPGFRONTEND_CORE_EXPORT GpgSignResult { auto GetRaw() -> gpgme_sign_result_t; + auto HashAlgo() -> QString; + auto InvalidSigners() -> std::vector<std::tuple<QString, GpgError>>; explicit GpgSignResult(gpgme_sign_result_t); diff --git a/src/sdk/GFSDKBasic.cpp b/src/sdk/GFSDKBasic.cpp index ed734bbe..1c7816d2 100644 --- a/src/sdk/GFSDKBasic.cpp +++ b/src/sdk/GFSDKBasic.cpp @@ -32,7 +32,7 @@ #include "core/function/SecureMemoryAllocator.h" #include "core/function/gpg/GpgCommandExecutor.h" #include "core/utils/BuildInfoUtils.h" -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" #include "ui/UIModuleManager.h" auto GFAllocateMemory(uint32_t size) -> void* { diff --git a/src/sdk/GFSDKExtra.cpp b/src/sdk/GFSDKExtra.cpp index 6a95a8f9..1c755389 100644 --- a/src/sdk/GFSDKExtra.cpp +++ b/src/sdk/GFSDKExtra.cpp @@ -31,7 +31,7 @@ #include <core/utils/BuildInfoUtils.h> #include <core/utils/CommonUtils.h> -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" auto GFCompareSoftwareVersion(const char *current_version, const char *latest_version) -> int { diff --git a/src/sdk/GFSDKGpg.cpp b/src/sdk/GFSDKGpg.cpp index 88162ffa..ae467b7b 100644 --- a/src/sdk/GFSDKGpg.cpp +++ b/src/sdk/GFSDKGpg.cpp @@ -24,4 +24,92 @@ * * SPDX-License-Identifier: GPL-3.0-or-later * - */
\ No newline at end of file + */ + +#include "GFSDKGpg.h" + +#include "GFSDKBasic.h" +#include "core/function/gpg/GpgBasicOperator.h" +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/function/gpg/GpgKeyImportExporter.h" +#include "core/model/DataObject.h" +#include "core/model/GpgSignResult.h" +#include "core/typedef/GpgTypedef.h" + +// +#include "core/utils/GpgUtils.h" +#include "private/GFSDKPrivat.h" + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgSignData(int channel, char** key_ids, + int key_ids_size, char* data, + int sign_mode, int ascii, + GFGpgSignResult** ps) -> int { + auto singer_ids = CharArrayToQList(key_ids, key_ids_size); + + GpgFrontend::KeyArgsList signer_keys; + for (const auto& signer_id : singer_ids) { + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey(signer_id); + if (key.IsGood()) signer_keys.push_back(key); + } + + if (signer_keys.empty()) return -1; + + auto in_buffer = GpgFrontend::GFBuffer(GFUnStrDup(data).toUtf8()); + + auto gpg_sign_mode = + sign_mode == 0 ? GPGME_SIG_MODE_NORMAL : GPGME_SIG_MODE_DETACH; + + auto [err, data_object] = + GpgFrontend::GpgBasicOperator::GetInstance(channel).SignSync( + signer_keys, in_buffer, gpg_sign_mode, ascii != 0); + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) return -1; + + auto result = + GpgFrontend::ExtractParams<GpgFrontend::GpgSignResult>(data_object, 0); + auto out_buffer = + GpgFrontend::ExtractParams<GpgFrontend::GFBuffer>(data_object, 1); + + *ps = + static_cast<GFGpgSignResult*>(GFAllocateMemory(sizeof(GFGpgSignResult))); + auto* s = *ps; + s->signature = GFStrDup(out_buffer.ConvertToQByteArray()); + s->hash_algo = GFStrDup(result.HashAlgo()); + return 0; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgPublicKey(int channel, char* key_id, + int ascii) -> char* { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey( + GFUnStrDup(key_id)); + + if (!key.IsGood()) return nullptr; + + auto [err, buffer] = + GpgFrontend::GpgKeyImportExporter::GetInstance(channel).ExportKey( + key, false, ascii != 0, true); + + if (GpgFrontend::CheckGpgError(err) != GPG_ERR_NO_ERROR) return nullptr; + + return GFStrDup(buffer.ConvertToQByteArray()); +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgKeyPrimaryUID(int channel, char* key_id, + GFGpgKeyUID** ps) -> int { + auto key = GpgFrontend::GpgKeyGetter::GetInstance(channel).GetKey( + GFUnStrDup(key_id)); + + if (!key.IsGood()) return -1; + + auto uids = key.GetUIDs(); + auto& primary_uid = uids->front(); + + *ps = static_cast<GFGpgKeyUID*>(GFAllocateMemory(sizeof(GFGpgKeyUID))); + + auto* s = *ps; + s->name = GFStrDup(primary_uid.GetName()); + s->email = GFStrDup(primary_uid.GetEmail()); + s->comment = GFStrDup(primary_uid.GetComment()); + return 0; +} diff --git a/src/sdk/GFSDKGpg.h b/src/sdk/GFSDKGpg.h index 1bbf5914..b7b40324 100644 --- a/src/sdk/GFSDKGpg.h +++ b/src/sdk/GFSDKGpg.h @@ -28,4 +28,52 @@ #pragma once -extern "C" {}
\ No newline at end of file +#include "GFSDKExport.h" + +extern "C" { + +struct GFGpgSignResult { + char* signature; + char* hash_algo; +}; + +struct GFGpgKeyUID { + char* name; + char* email; + char* comment; +}; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgSignData(int channel, char** key_ids, + int key_ids_size, char* data, + int sign_mode, int ascii, + GFGpgSignResult**) -> int; + +/** + * @brief + * + * @param key_id + * @param data + * @param mode + * @return const char* + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgPublicKey(int channel, char* key_id, + int ascii) -> char*; + +/** + * @brief + * + * @param channel + * @param key_id + * @return GpgKeyUID + */ +auto GPGFRONTEND_MODULE_SDK_EXPORT GFGpgKeyPrimaryUID(int channel, char* key_id, + GFGpgKeyUID**) -> int; +}
\ No newline at end of file diff --git a/src/sdk/GFSDKModule.cpp b/src/sdk/GFSDKModule.cpp index 5435b0a1..e76847d2 100644 --- a/src/sdk/GFSDKModule.cpp +++ b/src/sdk/GFSDKModule.cpp @@ -29,9 +29,9 @@ #include "GFSDKModule.h" #include <core/module/ModuleManager.h> -#include <sdk/private/CommonUtils.h> #include "GFSDKBasic.h" +#include "private/GFSDKPrivat.h" void GFModuleListenEvent(const char *module_id, const char *event_id) { return GpgFrontend::Module::ModuleManager::GetInstance().ListenEvent( diff --git a/src/sdk/GFSDKUI.cpp b/src/sdk/GFSDKUI.cpp index 54716209..cc08705c 100644 --- a/src/sdk/GFSDKUI.cpp +++ b/src/sdk/GFSDKUI.cpp @@ -31,9 +31,10 @@ #include <core/utils/CommonUtils.h> #include <QMap> +#include <QObject> #include <QString> -#include "sdk/private/CommonUtils.h" +#include "private/GFSDKPrivat.h" #include "ui/UIModuleManager.h" auto MetaDataArrayToQMap(MetaData** meta_data_array, @@ -53,7 +54,7 @@ auto MetaDataArrayToQMap(MetaData** meta_data_array, } auto GFUIMountEntry(const char* id, MetaData** meta_data_array, - int meta_data_array_size, EntryFactory factory) -> int { + int meta_data_array_size, QObjectFactory factory) -> int { if (id == nullptr || factory == nullptr) return -1; auto meta_data = MetaDataArrayToQMap(meta_data_array, meta_data_array_size); @@ -69,3 +70,76 @@ auto GFUIMountEntry(const char* id, MetaData** meta_data_array, return 0; } + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMainWindowPtr() -> void* { + return GpgFrontend::UI::UIModuleManager::GetInstance().GetQObject( + "main_window"); +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFUIShowDialog(void* dialog_raw_ptr, void* parent_raw_ptr) -> bool { + if (dialog_raw_ptr == nullptr) { + LOG_E() << "dialog raw ptr is nullptr"; + return false; + } + + auto* q_obj = static_cast<QObject*>(dialog_raw_ptr); + QPointer<QDialog> dialog = qobject_cast<QDialog*>(q_obj); + + if (dialog == nullptr) { + LOG_E() << "convert dialog raw ptr to qdialog failed"; + return false; + } + + QPointer<QWidget> parent = nullptr; + if (parent_raw_ptr != nullptr) { + auto* qp_obj = static_cast<QObject*>(parent_raw_ptr); + parent = qobject_cast<QWidget*>(qp_obj); + + if (parent == nullptr) { + LOG_E() << "convert parent raw ptr to qwidget failed"; + return false; + } + } + + auto* main_thread = QApplication::instance()->thread(); + + LOG_D() << "before entering into main thread, current thread id:" + << QThread::currentThreadId() + << ", dialog thread: " << dialog->thread() + << "main thread: " << main_thread; + + if (dialog->thread() != main_thread) { + LOG_E() << "dialog must be created on main thread"; + return false; + } + + QMetaObject::invokeMethod( + parent == nullptr ? QPointer<QObject>(QApplication::instance()) : parent, + [dialog, parent]() -> int { + LOG_D() << "show qdialog, current thread id:" + << QThread::currentThreadId(); + dialog->setParent(parent); + dialog->show(); + return 0; + }); + + return true; +} + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUICreateGUIObject(QObjectFactory factory, + void* data) -> void* { + QEventLoop loop; + void* object = nullptr; + + QMetaObject::invokeMethod(QApplication::instance(), [&]() -> int { + LOG_D() << "create gui object, current thread id:" + << QThread::currentThreadId(); + object = factory(data); + loop.quit(); + return 0; + }); + + loop.exec(); + return object; +} diff --git a/src/sdk/GFSDKUI.h b/src/sdk/GFSDKUI.h index bf4c3721..f9307cee 100644 --- a/src/sdk/GFSDKUI.h +++ b/src/sdk/GFSDKUI.h @@ -32,15 +32,24 @@ extern "C" { -using EntryFactory = void* (*)(const char*); +using QObjectFactory = void* (*)(void*); struct MetaData { const char* key; const char* value; }; -auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMountEntry(const char* id, - MetaData** meta_data_array, - int meta_data_array_size, - EntryFactory factory) -> int; +auto GPGFRONTEND_MODULE_SDK_EXPORT +GFUIMountEntry(const char* id, MetaData** meta_data_array, + int meta_data_array_size, QObjectFactory factory) -> int; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUICreateGUIObject(QObjectFactory factory, + void* data) -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIMainWindowPtr() -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIActiveWindowPtr() -> void*; + +auto GPGFRONTEND_MODULE_SDK_EXPORT GFUIShowDialog(void* dialog, + void* parent) -> bool; }
\ No newline at end of file diff --git a/src/sdk/private/CommonUtils.cpp b/src/sdk/private/GFSDKPrivat.cpp index 68afcb5f..71136535 100644 --- a/src/sdk/private/CommonUtils.cpp +++ b/src/sdk/private/GFSDKPrivat.cpp @@ -26,7 +26,7 @@ * */ -#include "CommonUtils.h" +#include "GFSDKPrivat.h" #include <core/utils/MemoryUtils.h> @@ -34,6 +34,8 @@ #include "GFSDKModule.h" +Q_LOGGING_CATEGORY(sdk, "sdk") + auto GFStrDup(const QString& str) -> char* { auto utf8_str = str.toUtf8(); auto* c_str = static_cast<char*>( @@ -58,9 +60,10 @@ auto CharArrayToQMap(char** char_array, int size) -> QMap<QString, QString> { QMap<QString, QString> map; for (int i = 0; i < size; i += 2) { QString const key = GFUnStrDup(char_array[i]); - QString const value = QString::fromUtf8(char_array[i + 1]); + QString const value = GFUnStrDup(char_array[i + 1]); map.insert(key, value); } + return map; } @@ -100,4 +103,32 @@ auto ConvertEventParamsToMap(GFModuleEventParam* params) } return param_map; -}
\ No newline at end of file +} + +auto CharArrayToQList(char** char_array, int size) -> QStringList { + QStringList list; + for (int i = 0; i < size; ++i) { + if (char_array[i] != nullptr) { + QString value = GFUnStrDup(char_array[i]); + list.append(value); + } + } + GpgFrontend::SecureFree(char_array); + return list; +} + +auto QListToCharArray(const QStringList& list) -> char** { + char** char_array = static_cast<char**>( + GpgFrontend::SecureMalloc(list.size() * sizeof(char*))); + + int index = 0; + for (const QString& item : list) { + QByteArray value = item.toUtf8(); + char_array[index] = + static_cast<char*>(GpgFrontend::SecureMalloc(value.size() + 1)); + std::strcpy(char_array[index], value.constData()); + index++; + } + + return char_array; +} diff --git a/src/sdk/private/CommonUtils.h b/src/sdk/private/GFSDKPrivat.h index 9539befe..7cfa2c76 100644 --- a/src/sdk/private/CommonUtils.h +++ b/src/sdk/private/GFSDKPrivat.h @@ -28,6 +28,15 @@ #pragma once +// declare logging category +Q_DECLARE_LOGGING_CATEGORY(sdk) + +#define LOG_D() qCDebug(sdk) +#define LOG_I() qCInfo(sdk) +#define LOG_W() qCWarning(sdk) +#define LOG_E() qCCritical(sdk) +#define LOG_F() qCFatal(sdk) + struct GFModuleEventParam; /** @@ -77,4 +86,22 @@ auto QMapToCharArray(const QMap<QString, QString> &map, int &size) -> char **; * @return QMap<QString, QString> */ auto ConvertEventParamsToMap(GFModuleEventParam *params) - -> QMap<QString, QString>;
\ No newline at end of file + -> QMap<QString, QString>; + +/** + * @brief + * + * @param char_array + * @param size + * @return QStringList + */ +auto CharArrayToQList(char **char_array, int size) -> QStringList; + +/** + * @brief + * + * @param list + * @param size + * @return char** + */ +auto QListToCharArray(const QStringList &list) -> char **;
\ No newline at end of file diff --git a/src/ui/GpgFrontendUIInit.cpp b/src/ui/GpgFrontendUIInit.cpp index 0baa1fbb..bc41f5f7 100644 --- a/src/ui/GpgFrontendUIInit.cpp +++ b/src/ui/GpgFrontendUIInit.cpp @@ -220,7 +220,7 @@ auto RunGpgFrontendUI(QApplication* app) -> int { auto main_window = SecureCreateUniqueObject<GpgFrontend::UI::MainWindow>(); // pre-check, if application need to restart - if (CommonUtils::GetInstance()->isApplicationNeedRestart()) { + if (CommonUtils::GetInstance()->IsApplicationNeedRestart()) { FLOG_D("application need to restart, before main window init."); return kDeepRestartCode; } diff --git a/src/ui/UIModuleManager.cpp b/src/ui/UIModuleManager.cpp index ed18f473..ba6172a4 100644 --- a/src/ui/UIModuleManager.cpp +++ b/src/ui/UIModuleManager.cpp @@ -61,7 +61,7 @@ auto UIModuleManager::DeclareMountPoint( auto UIModuleManager::MountEntry(const QString& id, QMap<QString, QString> meta_data, - EntryFactory factory) -> bool { + QObjectFactory factory) -> bool { if (id.isEmpty() || !mount_points_.contains(id)) return false; if (factory == nullptr) return false; @@ -82,7 +82,7 @@ auto UIModuleManager::QueryMountedEntries(QString id) -> QList<MountedUIEntry> { } auto MountedUIEntry::GetWidget() const -> QWidget* { - return qobject_cast<QWidget*>(static_cast<QObject*>(factory_(id_.toUtf8()))); + return qobject_cast<QWidget*>(static_cast<QObject*>(factory_(nullptr))); } auto MountedUIEntry::GetMetaDataByDefault( @@ -176,4 +176,15 @@ void UIModuleManager::TranslateAllModulesParams() { #endif } +auto UIModuleManager::RegisterQObject(const QString& id, QObject* p) -> bool { + if (id.isEmpty() || registered_qobjects_.contains(id)) return false; + + registered_qobjects_[id] = p; + return true; +} + +auto UIModuleManager::GetQObject(const QString& id) -> QObject* { + return registered_qobjects_.value(id, nullptr); +} + } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/UIModuleManager.h b/src/ui/UIModuleManager.h index 15f80030..7f23fc78 100644 --- a/src/ui/UIModuleManager.h +++ b/src/ui/UIModuleManager.h @@ -41,7 +41,7 @@ struct MountedUIEntry { QString id_; QMap<QString, QString> meta_data_; QMap<QString, QString> meta_data_translated_; - EntryFactory factory_; + QObjectFactory factory_; MountedUIEntry() = default; @@ -92,7 +92,7 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager * @return false */ auto MountEntry(const QString& id, QMap<QString, QString> meta_data, - EntryFactory factory) -> bool; + QObjectFactory factory) -> bool; /** * @brief @@ -113,6 +113,22 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager /** * @brief * + * @param id + * @return auto + */ + auto RegisterQObject(const QString& id, QObject*) -> bool; + + /** + * @brief + * + * @param id + * @return auto + */ + auto GetQObject(const QString& id) -> QObject*; + + /** + * @brief + * */ void RegisterAllModuleTranslators(); @@ -128,6 +144,7 @@ class GPGFRONTEND_UI_EXPORT UIModuleManager QMap<QString, ModuleTranslatorInfo> translator_data_readers_; QList<QTranslator*> registered_translators_; QList<QByteArray> read_translator_data_list_; + QMap<QString, QPointer<QObject>> registered_qobjects_; }; } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp index b8fc9961..d8c0059b 100644 --- a/src/ui/UserInterfaceUtils.cpp +++ b/src/ui/UserInterfaceUtils.cpp @@ -55,8 +55,8 @@ namespace GpgFrontend::UI { -std::unique_ptr<GpgFrontend::UI::CommonUtils> - GpgFrontend::UI::CommonUtils::instance_ = nullptr; +QScopedPointer<CommonUtils> CommonUtils::instance = + QScopedPointer<CommonUtils>(nullptr); void show_verify_details(QWidget *parent, int channel, InfoBoardWidget *info_board, GpgError error, @@ -121,10 +121,10 @@ void process_operation(QWidget *parent, const QString &waiting_title, } auto CommonUtils::GetInstance() -> CommonUtils * { - if (instance_ == nullptr) { - instance_ = std::make_unique<CommonUtils>(); + if (!instance) { + instance.reset(new CommonUtils()); } - return instance_.get(); + return instance.get(); } CommonUtils::CommonUtils() : QWidget(nullptr) { @@ -373,7 +373,7 @@ void CommonUtils::SlotExecuteGpgCommand( void CommonUtils::SlotImportKeyFromKeyServer( int channel, const KeyIdArgsList &key_ids, - const ImportCallbackFunctiopn &callback) { + const ImportCallbackFunction &callback) { auto target_keyserver = KeyServerSO(SettingsObject("key_server")).GetTargetServer(); if (target_keyserver.isEmpty()) { @@ -554,7 +554,7 @@ void CommonUtils::SlotRestartApplication(int code) { } } -auto CommonUtils::isApplicationNeedRestart() -> bool { +auto CommonUtils::IsApplicationNeedRestart() -> bool { return application_need_to_restart_at_once_; } diff --git a/src/ui/UserInterfaceUtils.h b/src/ui/UserInterfaceUtils.h index dab442dd..6aae75ba 100644 --- a/src/ui/UserInterfaceUtils.h +++ b/src/ui/UserInterfaceUtils.h @@ -89,7 +89,7 @@ class CommonUtils : public QWidget { * @brief * */ - using ImportCallbackFunctiopn = + using ImportCallbackFunction = std::function<void(const QString&, const QString&, size_t, size_t)>; /** @@ -131,7 +131,7 @@ class CommonUtils : public QWidget { * @brief * */ - auto isApplicationNeedRestart() -> bool; + auto IsApplicationNeedRestart() -> bool; /** * @brief @@ -238,7 +238,7 @@ class CommonUtils : public QWidget { */ static void SlotImportKeyFromKeyServer( int channel, const GpgFrontend::KeyIdArgsList& key_ids, - const GpgFrontend::UI::CommonUtils::ImportCallbackFunctiopn& callback); + const CommonUtils::ImportCallbackFunction& callback); /** * @brief @@ -282,7 +282,8 @@ class CommonUtils : public QWidget { std::shared_ptr<GpgImportInformation>); private: - static std::unique_ptr<CommonUtils> instance_; ///< + static QScopedPointer<CommonUtils> instance; ///< + bool application_need_to_restart_at_once_ = false; }; diff --git a/src/ui/main_window/GeneralMainWindow.cpp b/src/ui/main_window/GeneralMainWindow.cpp index 1cdb44e0..76eda3fc 100644 --- a/src/ui/main_window/GeneralMainWindow.cpp +++ b/src/ui/main_window/GeneralMainWindow.cpp @@ -29,6 +29,7 @@ #include "GeneralMainWindow.h" #include "core/model/SettingsObject.h" +#include "ui/UIModuleManager.h" #include "ui/struct/settings_object/AppearanceSO.h" #include "ui/struct/settings_object/WindowStateSO.h" @@ -36,9 +37,10 @@ namespace GpgFrontend::UI { class GeneralWindowState {}; -GpgFrontend::UI::GeneralMainWindow::GeneralMainWindow(QString name, +GpgFrontend::UI::GeneralMainWindow::GeneralMainWindow(QString id, QWidget *parent) - : QMainWindow(parent), name_(std::move(name)) { + : QMainWindow(parent), id_(std::move(id)) { + UIModuleManager::GetInstance().RegisterQObject(id_, this); slot_restore_settings(); } @@ -51,7 +53,7 @@ void GpgFrontend::UI::GeneralMainWindow::closeEvent(QCloseEvent *event) { void GpgFrontend::UI::GeneralMainWindow::slot_restore_settings() noexcept { try { - WindowStateSO window_state(SettingsObject(name_ + "_state")); + WindowStateSO window_state(SettingsObject(id_ + "_state")); if (!window_state.window_state_data.isEmpty()) { // state sets pos & size of dock-widgets @@ -112,13 +114,13 @@ void GpgFrontend::UI::GeneralMainWindow::slot_restore_settings() noexcept { icon_style_ = toolButtonStyle(); } catch (...) { - LOG_W() << "general main window: " << name_ << ", caught exception"; + LOG_W() << "general main window: " << id_ << ", caught exception"; } } void GpgFrontend::UI::GeneralMainWindow::slot_save_settings() noexcept { try { - SettingsObject general_windows_state(name_ + "_state"); + SettingsObject general_windows_state(id_ + "_state"); // update geo of current dialog size_ = this->size(); @@ -134,7 +136,7 @@ void GpgFrontend::UI::GeneralMainWindow::slot_save_settings() noexcept { general_windows_state.Store(window_state.Json()); } catch (...) { - LOG_W() << "general main window: " << name_ << ", caught exception"; + LOG_W() << "general main window: " << id_ << ", caught exception"; } } @@ -197,4 +199,6 @@ void GeneralMainWindow::update_rect_cache() { this->parent_rect_ = QRect{0, 0, 0, 0}; } } + +auto GeneralMainWindow::GetId() const -> QString { return id_; } } // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/main_window/GeneralMainWindow.h b/src/ui/main_window/GeneralMainWindow.h index 7ef1234f..d7d28930 100644 --- a/src/ui/main_window/GeneralMainWindow.h +++ b/src/ui/main_window/GeneralMainWindow.h @@ -41,13 +41,20 @@ class GeneralMainWindow : public QMainWindow { * * @param name */ - explicit GeneralMainWindow(QString name, QWidget* parent = nullptr); + explicit GeneralMainWindow(QString id, QWidget* parent = nullptr); /** * */ ~GeneralMainWindow() override; + /** + * @brief Get the Id object + * + * @return QString + */ + [[nodiscard]] auto GetId() const -> QString; + protected: /** * @@ -66,7 +73,7 @@ class GeneralMainWindow : public QMainWindow { */ void movePosition2CenterOfParent(); - QSize icon_size_{}; ///< + QSize icon_size_; ///< int font_size_{}; ///< Qt::ToolButtonStyle icon_style_; ///< @@ -88,9 +95,9 @@ class GeneralMainWindow : public QMainWindow { void update_rect_cache(); private: - QString name_; ///< - QPoint pos_; ///< - QSize size_; ///< + QString id_; ///< + QPoint pos_; ///< + QSize size_; ///< QRect rect_; QRect screen_rect_; QRect parent_rect_; diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index 47e7487b..1fcb14df 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -29,9 +29,7 @@ #include "MainWindow.h" #include "core/function/CacheManager.h" -#include "core/function/CoreSignalStation.h" #include "core/function/GlobalSettingStation.h" -#include "core/model/GpgPassphraseContext.h" #include "core/model/SettingsObject.h" #include "core/module/ModuleManager.h" #include "ui/UISignalStation.h" @@ -74,7 +72,7 @@ void MainWindow::Init() noexcept { attachment_dock_created_ = false; /* Variable containing if restart is needed */ - this->SlotSetRestartNeeded(false); + this->SlotSetRestartNeeded(0); // init menu bar this->setMenuBar(new QMenuBar()); diff --git a/src/ui/main_window/MainWindow.h b/src/ui/main_window/MainWindow.h index ef3fda13..6c6886d4 100644 --- a/src/ui/main_window/MainWindow.h +++ b/src/ui/main_window/MainWindow.h @@ -63,7 +63,6 @@ class MainWindow : public GeneralMainWindow { static constexpr OperationType kVerify = 1 << 3; static constexpr OperationType kEncryptAndSign = 1 << 4; static constexpr OperationType kDecryptAndVerify = 1 << 5; - static constexpr OperationType kVerifyEMail = 1 << 6; }; /** @@ -178,6 +177,12 @@ class MainWindow : public GeneralMainWindow { void SlotDecryptEML(); /** + * @brief + * + */ + void SlotSignEML(); + + /** * @details decrypt and verify the text of currently active textedit-page * with the currently checked keys */ @@ -299,18 +304,6 @@ class MainWindow : public GeneralMainWindow { */ void SlotGeneralDecryptVerify(bool); - /** - * @brief - * - */ - void SlotGeneralDecryptEMail(bool); - - /** - * @brief - * - */ - void SlotGeneralVerifyEMail(bool); - private slots: /** @@ -601,7 +594,6 @@ class MainWindow : public GeneralMainWindow { QMenu* key_menu_{}; ///< Submenu for key-operations QMenu* view_menu_{}; ///< Submenu for view operations QMenu* import_key_menu_{}; ///< Submenu for import operations - QMenu* email_menu_{}; ///< Submenu for email operations QToolBar* crypt_tool_bar_{}; ///< Toolbar holding crypt actions QToolBar* file_tool_bar_{}; ///< Toolbar holding file actions @@ -609,7 +601,6 @@ class MainWindow : public GeneralMainWindow { QToolBar* special_edit_tool_bar_{}; ///< Toolbar holding special edit actions QToolBar* key_tool_bar_{}; ///< Toolbar holding key operations - QToolBar* email_tool_bar_{}; QToolButton* import_button_{}; ///< Tool button for import dropdown menu in toolbar QDockWidget* key_list_dock_{}; ///< Encrypt Dock @@ -617,6 +608,7 @@ class MainWindow : public GeneralMainWindow { QDockWidget* info_board_dock_{}; QAction* new_tab_act_{}; ///< Action to create new tab + QAction* new_email_tab_act_{}; ///< Action to create email tab QAction* switch_tab_up_act_{}; ///< Action to switch tab up QAction* switch_tab_down_act_{}; ///< Action to switch tab down QAction* open_act_{}; ///< Action to open file @@ -681,9 +673,6 @@ class MainWindow : public GeneralMainWindow { QAction* import_key_from_clipboard_act_{}; ///< QAction* import_key_from_key_server_act_{}; ///< - QAction* verify_email_by_eml_data_act_{}; ///< - QAction* decrypt_email_by_eml_data_act_{}; - QLabel* status_bar_icon_{}; ///< KeyList* m_key_list_{}; ///< diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index c7579ff9..8bfb4289 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -689,4 +689,36 @@ void MainWindow::slot_decrypt_email_by_eml_data_result_helper( }); } +void MainWindow::SlotSignEML() { + if (edit_->TabCount() == 0 || edit_->CurEMailPage() == nullptr) return; + if (m_key_list_->GetCheckedKeys().isEmpty()) return; + + auto buffer = edit_->CurPlainText().toUtf8(); + + Module::TriggerEvent( + "EMAIL_EXPORT_EML_DATA", + { + {"body_data", QString::fromLatin1(buffer.toBase64())}, + {"channel", + QString::number(m_key_list_->GetCurrentGpgContextChannel())}, + {"sign_key", m_key_list_->GetCheckedKeys().front()}, + }, + [=](Module::EventIdentifier i, Module::Event::ListenerIdentifier ei, + Module::Event::Params p) { + LOG_D() << "EMAIL_DECRYPT_EML_DATA callback: " << i << ei; + if (p["ret"] != "0" || !p["err"].isEmpty()) { + LOG_E() << "An error occurred trying to decrypt email, " + << "error message: " << p["err"]; + + return; + } + + if (!p["eml_data"].isEmpty()) { + edit_->SlotSetText2CurEMailPage(p.value("eml_data", "")); + } + + LOG_E() << "mime or signature data is missing"; + }); +} + } // namespace GpgFrontend::UI diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index 105a2374..d99325ec 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -80,11 +80,6 @@ void MainWindow::slot_switch_menu_control_mode(int index) { decrypt_act_->setDisabled(disable); decrypt_verify_act_->setDisabled(disable); - if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_->setDisabled(disable); - decrypt_email_by_eml_data_act_->setDisabled(disable); - } - redo_act_->setDisabled(disable); undo_act_->setDisabled(disable); zoom_out_act_->setDisabled(disable); @@ -189,11 +184,6 @@ void MainWindow::SlotUpdateCryptoMenuStatus(unsigned int type) { decrypt_act_->setDisabled(true); decrypt_verify_act_->setDisabled(true); - if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_->setDisabled(true); - decrypt_email_by_eml_data_act_->setDisabled(true); - } - // gnupg operations if ((opera_type & MainWindow::OperationMenu::kVerify) != 0U) { verify_act_->setDisabled(false); @@ -213,12 +203,6 @@ void MainWindow::SlotUpdateCryptoMenuStatus(unsigned int type) { if ((opera_type & MainWindow::OperationMenu::kDecryptAndVerify) != 0U) { decrypt_verify_act_->setDisabled(false); } - - // email operations - if (Module::IsModuleActivate(kEmailModuleID) && - (opera_type & MainWindow::OperationMenu::kVerifyEMail) != 0U) { - verify_email_by_eml_data_act_->setDisabled(false); - } } void MainWindow::SlotGeneralEncrypt(bool) { @@ -254,6 +238,12 @@ void MainWindow::SlotGeneralDecrypt(bool) { } } } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotDecryptEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) { this->SlotDecrypt(); } @@ -267,6 +257,12 @@ void MainWindow::SlotGeneralSign(bool) { const auto file_info = QFileInfo(path); if (file_info.isFile()) this->SlotFileSign(path); } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotSignEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotSign(); } @@ -278,6 +274,12 @@ void MainWindow::SlotGeneralVerify(bool) { const auto file_info = QFileInfo(path); if (file_info.isFile()) this->SlotFileVerify(path); } + + if (edit_->CurEMailPage() != nullptr) { + this->SlotVerifyEML(); + return; + } + if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotVerify(); } @@ -314,22 +316,12 @@ void MainWindow::SlotGeneralDecryptVerify(bool) { } } } + if (edit_->SlotCurPageTextEdit() != nullptr) { this->SlotDecryptVerify(); } } -void MainWindow::SlotGeneralVerifyEMail(bool) { - if (edit_->SlotCurPageFileTreeView() != nullptr) { - const auto* file_tree_view = edit_->SlotCurPageFileTreeView(); - const auto path = file_tree_view->GetSelected(); - - const auto file_info = QFileInfo(path); - if (file_info.isFile()) this->SlotFileVerifyEML(path); - } - if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotVerifyEML(); -} - void MainWindow::slot_clean_gpg_password_cache(bool) { GpgFrontend::GpgAdvancedOperator::ClearGpgPasswordCache( [=](int err, DataObjectPtr) { @@ -376,15 +368,4 @@ void MainWindow::slot_restart_gpg_components(bool) { }); } -void MainWindow::SlotGeneralDecryptEMail(bool) { - // if (edit_->SlotCurPageFileTreeView() != nullptr) { - // const auto* file_tree_view = edit_->SlotCurPageFileTreeView(); - // const auto path = file_tree_view->GetSelected(); - - // const auto file_info = QFileInfo(path); - // if (file_info.isFile()) this->SlotFileVerify(path); - // } - if (edit_->SlotCurPageTextEdit() != nullptr) this->SlotDecryptEML(); -} - } // namespace GpgFrontend::UI diff --git a/src/ui/main_window/MainWindowUI.cpp b/src/ui/main_window/MainWindowUI.cpp index 9c2a5003..7728d0ae 100644 --- a/src/ui/main_window/MainWindowUI.cpp +++ b/src/ui/main_window/MainWindowUI.cpp @@ -260,17 +260,11 @@ void MainWindow::create_actions() { * E-Mail Menu */ if (Module::IsModuleActivate(kEmailModuleID)) { - verify_email_by_eml_data_act_ = create_action( - "verify_email_by_eml_data", tr("Verify E-Mail"), - ":/icons/email-check.png", tr("Verify RAW E-Mail Data (EML)")); - connect(verify_email_by_eml_data_act_, &QAction::triggered, this, - &MainWindow::SlotGeneralVerifyEMail); - - decrypt_email_by_eml_data_act_ = create_action( - "decrypt_email_by_eml_data", tr("Decrypt E-Mail"), - ":/icons/email-open.png", tr("Decrypt RAW E-Mail Data (EML)")); - connect(decrypt_email_by_eml_data_act_, &QAction::triggered, this, - &MainWindow::SlotGeneralDecryptEMail); + new_email_tab_act_ = + create_action("new_email_tab", tr("New E-Mail"), ":/icons/email.png", + tr("Create A New E-Mail Tab")); + connect(new_email_tab_act_, &QAction::triggered, edit_, + &TextEdit::SlotNewEMailTab); } /* @@ -407,6 +401,11 @@ void MainWindow::create_actions() { void MainWindow::create_menus() { file_menu_ = menuBar()->addMenu(tr("File")); file_menu_->addAction(new_tab_act_); + + if (Module::IsModuleActivate(kEmailModuleID)) { + file_menu_->addAction(new_email_tab_act_); + } + file_menu_->addAction(browser_act_); file_menu_->addAction(open_act_); file_menu_->addSeparator(); @@ -465,12 +464,6 @@ void MainWindow::create_menus() { advance_menu_->addAction(gnupg_controller_open_act_); advance_menu_->addAction(module_controller_open_act_); - if (Module::IsModuleActivate(kEmailModuleID)) { - email_menu_ = menuBar()->addMenu(tr("E-Mail")); - email_menu_->addAction(verify_email_by_eml_data_act_); - email_menu_->addAction(decrypt_email_by_eml_data_act_); - } - view_menu_ = menuBar()->addMenu(tr("View")); help_menu_ = menuBar()->addMenu(tr("Help")); @@ -494,6 +487,11 @@ void MainWindow::create_tool_bars() { file_tool_bar_ = addToolBar(tr("File")); file_tool_bar_->setObjectName("fileToolBar"); file_tool_bar_->addAction(new_tab_act_); + + if (Module::IsModuleActivate(kEmailModuleID)) { + file_tool_bar_->addAction(new_email_tab_act_); + } + file_tool_bar_->addAction(open_act_); file_tool_bar_->addAction(browser_act_); view_menu_->addAction(file_tool_bar_->toggleViewAction()); @@ -530,14 +528,6 @@ void MainWindow::create_tool_bars() { special_edit_tool_bar_->hide(); view_menu_->addAction(special_edit_tool_bar_->toggleViewAction()); - if (Module::IsModuleActivate(kEmailModuleID)) { - email_tool_bar_ = addToolBar(tr("E-Mail")); - email_tool_bar_->setObjectName("emailToolBar"); - email_tool_bar_->addAction(verify_email_by_eml_data_act_); - email_tool_bar_->addAction(decrypt_email_by_eml_data_act_); - view_menu_->addAction(email_tool_bar_->toggleViewAction()); - } - // Add dropdown menu for key import to keytoolbar import_button_ = new QToolButton(); import_button_->setMenu(import_key_menu_); diff --git a/src/ui/widgets/EMailEditorPage.cpp b/src/ui/widgets/EMailEditorPage.cpp new file mode 100644 index 00000000..e37695be --- /dev/null +++ b/src/ui/widgets/EMailEditorPage.cpp @@ -0,0 +1,43 @@ +/** + * 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 + * + */ + +#include "EMailEditorPage.h" + +#include "ui_PlainTextEditor.h" + +namespace GpgFrontend::UI { + +EMailEditorPage::EMailEditorPage() { + this->ui_->encodingLabel->setText("E-Mail"); +} + +EMailEditorPage::EMailEditorPage(const QString& file_path, QWidget* parent) + : PlainTextEditorPage(file_path, parent) { + this->ui_->encodingLabel->setText("E-Mail"); +} +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/EMailEditorPage.h b/src/ui/widgets/EMailEditorPage.h new file mode 100644 index 00000000..a5eb6f50 --- /dev/null +++ b/src/ui/widgets/EMailEditorPage.h @@ -0,0 +1,46 @@ +/** + * 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 <utility> + +#include "ui/GpgFrontendUI.h" +#include "ui/widgets/PlainTextEditorPage.h" + +class Ui_FilePage; + +namespace GpgFrontend::UI { +class EMailEditorPage : public PlainTextEditorPage { + Q_OBJECT + public: + EMailEditorPage(); + + EMailEditorPage(const QString& file_path, QWidget* parent); +}; +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index 4d9ce26a..ea80cb58 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -156,14 +156,11 @@ void FilePage::update_main_basical_opera_menu(const QString& selected_path) { } if (info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg" || - info.suffix() == "pgp" || info.suffix() == "asc")) { + info.suffix() == "pgp" || info.suffix() == "asc" || + info.suffix() == "eml")) { operation_type |= MainWindow::OperationMenu::kVerify; } - if (info.isFile() && (info.suffix() == "eml")) { - operation_type |= MainWindow::OperationMenu::kVerifyEMail; - } - emit SignalMainWindowlUpdateBasicalOperaMenu(operation_type); } } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 73328885..9498480e 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -328,6 +328,17 @@ auto KeyList::GetChecked() -> KeyIdArgsListPtr { return ret; } +auto KeyList::GetCheckedKeys() -> QStringList { + auto* key_table = qobject_cast<KeyTable*>(ui_->keyGroupTab->currentWidget()); + QStringList key_id_list; + for (int i = 0; i < key_table->GetRowCount(); i++) { + if (key_table->IsRowChecked(i)) { + key_id_list.append(key_table->GetKeyIdByRow(i)); + } + } + return key_id_list; +} + auto KeyList::GetAllPrivateKeys() -> KeyIdArgsListPtr { auto* key_table = qobject_cast<KeyTable*>(ui_->keyGroupTab->currentWidget()); auto ret = std::make_unique<KeyIdArgsList>(); diff --git a/src/ui/widgets/KeyList.h b/src/ui/widgets/KeyList.h index 18c9576e..4216eba8 100644 --- a/src/ui/widgets/KeyList.h +++ b/src/ui/widgets/KeyList.h @@ -145,6 +145,13 @@ class KeyList : public QWidget { auto GetChecked() -> KeyIdArgsListPtr; /** + * @brief Get the Checked Keys object + * + * @return QStringList + */ + auto GetCheckedKeys() -> QStringList; + + /** * @brief Get the Checked object * * @param key_table diff --git a/src/ui/widgets/PlainTextEditorPage.h b/src/ui/widgets/PlainTextEditorPage.h index c4423378..86ec0a56 100644 --- a/src/ui/widgets/PlainTextEditorPage.h +++ b/src/ui/widgets/PlainTextEditorPage.h @@ -115,8 +115,10 @@ class PlainTextEditorPage : public QWidget { */ void SignalUIBytesDisplayed(); - private: + protected: std::shared_ptr<Ui_PlainTextEditor> ui_; ///< + + private: QString full_file_path_; ///< The path to the file handled in the tab bool sign_marked_{}; ///< true, if the signed header is marked, false if not bool read_done_ = false; ///< diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 2b1f4766..1ea96fde 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -126,18 +126,31 @@ void TextEdit::SlotOpen() { } void TextEdit::SlotSave() { - if (tab_widget_->count() == 0 || SlotCurPageTextEdit() == 0) { + if (tab_widget_->count() == 0) { return; } - QString file_name = SlotCurPageTextEdit()->GetFilePath(); + if (CurEMailPage() != nullptr) { + QString file_name = CurEMailPage()->GetFilePath(); - if (file_name.isEmpty()) { - // QString docname = tabWidget->tabText(tabWidget->currentIndex()); - // docname.remove(0,2); - SlotSaveAs(); - } else { - saveFile(file_name); + if (file_name.isEmpty()) { + SlotSaveAsEML(); + } else { + saveEMLFile(file_name); + } + return; + } + + if (CurTextPage() != nullptr) { + QString file_name = SlotCurPageTextEdit()->GetFilePath(); + + if (file_name.isEmpty()) { + // QString docname = tabWidget->tabText(tabWidget->currentIndex()); + // docname.remove(0,2); + SlotSaveAs(); + } else { + saveFile(file_name); + } } } @@ -171,6 +184,38 @@ auto TextEdit::saveFile(const QString& file_name) -> bool { return false; } +auto TextEdit::saveEMLFile(const QString& file_name) -> bool { + if (file_name.isEmpty()) return false; + + PlainTextEditorPage* page = CurEMailPage(); + if (page == nullptr) return false; + + QFile file(file_name); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::warning( + this, tr("Warning"), + tr("Cannot read file %1:\n%2.").arg(file_name).arg(file.errorString())); + return false; + } + + QTextStream output_stream(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + output_stream + << page->GetTextPage()->toPlainText().replace("\n", "\r\n").toLatin1(); + QApplication::restoreOverrideCursor(); + QTextDocument* document = page->GetTextPage()->document(); + + document->setModified(false); + + int cur_index = tab_widget_->currentIndex(); + tab_widget_->setTabText(cur_index, stripped_name(file_name)); + page->SetFilePath(file_name); + page->NotifyFileSaved(); + + file.close(); + return true; +} + auto TextEdit::SlotSaveAs() -> bool { if (tab_widget_->count() == 0 || SlotCurPageTextEdit() == nullptr) { return true; @@ -187,6 +232,22 @@ auto TextEdit::SlotSaveAs() -> bool { return saveFile(QFileDialog::getSaveFileName(this, tr("Save file"), path)); } +auto TextEdit::SlotSaveAsEML() -> bool { + if (tab_widget_->count() == 0 || CurEMailPage() == nullptr) { + return true; + } + + PlainTextEditorPage* page = CurEMailPage(); + QString path; + if (!page->GetFilePath().isEmpty()) { + path = page->GetFilePath(); + } else { + path = tab_widget_->tabText(tab_widget_->currentIndex()).remove(0, 2); + } + + return saveEMLFile(QFileDialog::getSaveFileName(this, tr("Save file"), path)); +} + void TextEdit::SlotCloseTab() { slot_remove_tab(tab_widget_->currentIndex()); if (tab_widget_->count() != 0) { @@ -321,6 +382,13 @@ auto TextEdit::MaybeSaveAnyTab() -> bool { return false; } +void TextEdit::SlotSetText2CurEMailPage(const QString& text) { + if (CurTextPage() == nullptr) SlotNewEMailTab(); + auto* edit = CurTextPage()->GetTextPage(); + edit->clear(); + edit->appendPlainText(text); +} + void TextEdit::SlotAppendText2CurTextPage(const QString& text) { if (CurTextPage() == nullptr) SlotNewTab(); CurTextPage()->GetTextPage()->appendPlainText(text); @@ -554,4 +622,11 @@ auto TextEdit::CurPlainText() const -> QString { } auto TextEdit::TabWidget() const -> QTabWidget* { return tab_widget_; } + +auto TextEdit::CurEMailPage() const -> EMailEditorPage* { + return tab_widget_->CurEMailPage(); +} + +void TextEdit::SlotNewEMailTab() { tab_widget_->SlotNewEMailTab(); } + } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.h b/src/ui/widgets/TextEdit.h index 30540569..68b648e5 100644 --- a/src/ui/widgets/TextEdit.h +++ b/src/ui/widgets/TextEdit.h @@ -29,6 +29,7 @@ #pragma once #include "ui/dialog/QuitDialog.h" +#include "ui/widgets/EMailEditorPage.h" #include "ui/widgets/FilePage.h" #include "ui/widgets/PlainTextEditorPage.h" @@ -78,6 +79,13 @@ class TextEdit : public QWidget { /** * @brief * + * @return EMailEditorPage* + */ + [[nodiscard]] auto CurEMailPage() const -> EMailEditorPage*; + + /** + * @brief + * * @return FilePage* */ [[nodiscard]] auto CurFilePage() const -> FilePage*; @@ -161,6 +169,12 @@ class TextEdit : public QWidget { void SlotNewTab(); /** + * @details Adds a new tab with the title "untitled"+countpage+".eml" + * Sets the focus to the new tab. Increase Tab-Count by one + */ + void SlotNewEMailTab(); + + /** * @details * */ @@ -260,6 +274,21 @@ class TextEdit : public QWidget { */ void SlotAppendText2CurTextPage(const QString& text); + /** + * @brief + * + * @param text + */ + void SlotSetText2CurEMailPage(const QString& text); + + /** + * @brief + * + * @return true + * @return false + */ + auto SlotSaveAsEML() -> bool; + protected: /** * @brief Saves the content of currentTab to the file filename @@ -268,6 +297,13 @@ class TextEdit : public QWidget { */ auto saveFile(const QString& file_name) -> bool; + /** + * @brief + * + * @return auto + */ + auto saveEMLFile(const QString& file_name) -> bool; + private slots: /** diff --git a/src/ui/widgets/TextEditTabWidget.cpp b/src/ui/widgets/TextEditTabWidget.cpp index 97826f03..8007eb85 100644 --- a/src/ui/widgets/TextEditTabWidget.cpp +++ b/src/ui/widgets/TextEditTabWidget.cpp @@ -31,8 +31,9 @@ #include "core/function/GlobalSettingStation.h" #include "core/model/CacheObject.h" #include "ui/UISignalStation.h" +#include "ui/widgets/EMailEditorPage.h" +#include "ui/widgets/FilePage.h" #include "ui/widgets/PlainTextEditorPage.h" -#include "widgets/FilePage.h" namespace GpgFrontend::UI { @@ -80,6 +81,11 @@ void TextEditTabWidget::dropEvent(QDropEvent* event) { continue; } + if (file_info.suffix() == "eml") { + SlotOpenEMLFile(local_file); + return; + } + SlotOpenFile(local_file); } @@ -125,29 +131,58 @@ void TextEditTabWidget::SlotOpenFile(const QString& path) { file.close(); } -void TextEditTabWidget::SlotShowModified(bool changed) { + +void TextEditTabWidget::SlotOpenEMLFile(const QString& path) { + QFile file(path); + auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); + if (result) { + auto* page = new EMailEditorPage(path, this); + + connect(page->GetTextPage(), &QPlainTextEdit::textChanged, this, + &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage(), &QPlainTextEdit::selectionChanged, this, + &TextEditTabWidget::slot_save_status_to_cache_for_recovery); + + QApplication::setOverrideCursor(Qt::WaitCursor); + auto index = this->addTab(page, stripped_name(path)); + this->setTabIcon(index, QIcon(":/icons/email.png")); + this->setCurrentIndex(this->count() - 1); + QApplication::restoreOverrideCursor(); + page->GetTextPage()->setFocus(); + page->ReadFile(); + } else { + QMessageBox::warning( + this, tr("Warning"), + tr("Cannot read file %1:\n%2.").arg(path).arg(file.errorString())); + } + + file.close(); +} + +void TextEditTabWidget::SlotShowModified() { // get current tab int index = this->currentIndex(); - QString title = this->tabText(index); + QString title = this->tabText(index).trimmed(); - // if changed - if (!changed) { - this->setTabText(index, title.remove(0, 2)); - return; - } + if (title.startsWith("*")) return; // if doc is modified now, add leading * to title, // otherwise remove the leading * from the title if (CurTextPage()->GetTextPage()->document()->isModified()) { - this->setTabText(index, title.trimmed().prepend("* ")); + this->setTabText(index, title.prepend("* ")); } else { this->setTabText(index, title.remove(0, 2)); } } + auto TextEditTabWidget::CurTextPage() const -> PlainTextEditorPage* { return qobject_cast<PlainTextEditorPage*>(this->currentWidget()); } +auto TextEditTabWidget::CurEMailPage() const -> EMailEditorPage* { + return qobject_cast<EMailEditorPage*>(this->currentWidget()); +} + auto TextEditTabWidget::SlotCurPageTextEdit() -> PlainTextEditorPage* { auto* cur_page = qobject_cast<PlainTextEditorPage*>(this->currentWidget()); return cur_page; @@ -228,6 +263,22 @@ void TextEditTabWidget::SlotNewTab() { connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, this, &TextEditTabWidget::slot_save_status_to_cache_for_recovery); } + +void TextEditTabWidget::SlotNewEMailTab() { + QString header = tr("untitled") + QString::number(++count_page_) + ".eml"; + + auto* page = new EMailEditorPage(); + auto index = this->addTab(page, header); + this->setTabIcon(index, QIcon(":/icons/email.png")); + this->setCurrentIndex(this->count() - 1); + page->GetTextPage()->setFocus(); + + connect(page->GetTextPage(), &QPlainTextEdit::textChanged, this, + &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage(), &QPlainTextEdit::selectionChanged, this, + &TextEditTabWidget::slot_save_status_to_cache_for_recovery); +} + void TextEditTabWidget::SlotNewTabWithContent(QString title, const QString& content) { QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; diff --git a/src/ui/widgets/TextEditTabWidget.h b/src/ui/widgets/TextEditTabWidget.h index b0cafae2..13ed00f0 100644 --- a/src/ui/widgets/TextEditTabWidget.h +++ b/src/ui/widgets/TextEditTabWidget.h @@ -28,10 +28,11 @@ #pragma once -#include "widgets/FilePage.h" namespace GpgFrontend::UI { class PlainTextEditorPage; +class EMailEditorPage; +class FilePage; class TextEditTabWidget : public QTabWidget { Q_OBJECT @@ -49,6 +50,12 @@ class TextEditTabWidget : public QTabWidget { /** * @brief * + */ + void SlotNewEMailTab(); + + /** + * @brief + * * @param title * @param content */ @@ -62,6 +69,13 @@ class TextEditTabWidget : public QTabWidget { /** * @brief * + * @param path + */ + void SlotOpenEMLFile(const QString& path); + + /** + * @brief + * */ void SlotOpenDirectory(const QString& target_directory); @@ -69,7 +83,7 @@ class TextEditTabWidget : public QTabWidget { * @details put a * in front of current tabs title, if current textedit is * modified */ - void SlotShowModified(bool); + void SlotShowModified(); /** * @brief @@ -83,6 +97,13 @@ class TextEditTabWidget : public QTabWidget { * * @return PlainTextEditorPage* */ + [[nodiscard]] auto CurEMailPage() const -> EMailEditorPage*; + + /** + * @brief + * + * @return PlainTextEditorPage* + */ auto SlotCurPageTextEdit() -> PlainTextEditorPage*; /** diff --git a/ui/EmailListEditor.ui b/ui/EmailListEditor.ui deleted file mode 100644 index 5cc0ddef..00000000 --- a/ui/EmailListEditor.ui +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>EmailListEditorDialog</class> - <widget class="QDialog" name="EmailListEditorDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>618</width> - <height>498</height> - </rect> - </property> - <property name="windowTitle"> - <string>Email List Editor</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QLabel" name="titleLabel"> - <property name="text"> - <string>Email List:</string> - </property> - </widget> - </item> - <item> - <widget class="QListWidget" name="emaillistWidget"> - <property name="editTriggers"> - <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> - </property> - <property name="dragEnabled"> - <bool>true</bool> - </property> - <property name="isWrapping" stdset="0"> - <bool>false</bool> - </property> - <property name="spacing"> - <number>6</number> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="addEmailAddressButton"> - <property name="text"> - <string>Add An Email Address</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="tipsLabel"> - <property name="text"> - <string>Tips: You can double-click the email address in the edit list, or click the email to pop up the option menu.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - </layout> - <action name="actionDelete_Selected_Email_Address"> - <property name="text"> - <string>Delete Selected Email Address</string> - </property> - </action> - </widget> - <resources/> - <connections/> -</ui> diff --git a/ui/SendMailDialog.ui b/ui/SendMailDialog.ui deleted file mode 100644 index f68d2f45..00000000 --- a/ui/SendMailDialog.ui +++ /dev/null @@ -1,469 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SendMailDialog</class> - <widget class="QDialog" name="SendMailDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>995</width> - <height>760</height> - </rect> - </property> - <property name="cursor"> - <cursorShape>ArrowCursor</cursorShape> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::NoContextMenu</enum> - </property> - <property name="windowTitle"> - <string>New Message</string> - </property> - <property name="sizeGripEnabled"> - <bool>false</bool> - </property> - <property name="modal"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QWidget" name="horizontalWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="senderLabel"> - <property name="text"> - <string>Sender</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="senderEdit"/> - </item> - <item> - <widget class="Line" name="line"> - <property name="toolTipDuration"> - <number>0</number> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="lineWidth"> - <number>2</number> - </property> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="ccButton"> - <property name="text"> - <string>CC</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="bccButton"> - <property name="text"> - <string>BCC</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_4" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="recipientLabel"> - <property name="text"> - <string>Recipient(s)</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="recipientEdit"/> - </item> - <item> - <widget class="QPushButton" name="recipientsEditButton"> - <property name="text"> - <string>Edit Recipients(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="Line" name="line_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_5" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="subjectLabel"> - <property name="text"> - <string>Mail Subject</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="subjectEdit"/> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="horizontalWidget_6" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="gpgOperaLabel"> - <property name="text"> - <string>GPG Operations</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="senderKeySelectButton"> - <property name="text"> - <string>Select Sender GPG Key</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="recipientKeySelectButton"> - <property name="text"> - <string>Select Recipient(s) GPG Key</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="Line" name="line_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="ccInputWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="ccLabel"> - <property name="text"> - <string>CC</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="ccEdit"/> - </item> - <item> - <widget class="QPushButton" name="ccEditButton"> - <property name="text"> - <string>Edit CC(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QWidget" name="bccInputWidget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="bccLabel"> - <property name="text"> - <string>BCC</string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="bccEdit"/> - </item> - <item> - <widget class="QPushButton" name="bccEditButton"> - <property name="text"> - <string>Edit BCC(s)</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QLabel" name="tipsLabel"> - <property name="text"> - <string>Tips: You can fill in multiple email addresses, please separate them with ";".</string> - </property> - </widget> - </item> - <item> - <widget class="Line" name="line_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <widget class="QTextEdit" name="textEdit"/> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> - <widget class="QLabel" name="senderKeyLabel"> - <property name="text"> - <string>Sender GPG Key:</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="senderKeyValueLabel"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item alignment="Qt::AlignLeft|Qt::AlignVCenter"> - <widget class="QLabel" name="recipientKeysLabel"> - <property name="text"> - <string>Recipient(s) GPG Key:</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="recipientsKeyValueLabel"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QLabel" name="errorLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="Line" name="line_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <item> - <widget class="QCheckBox" name="contentEncryptCheckBox"> - <property name="text"> - <string>Encrypt content</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="attacSignatureCheckBox"> - <property name="text"> - <string>Attach signature</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="attachSenderPublickeyCheckBox"> - <property name="text"> - <string>Attach sender's public key</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item alignment="Qt::AlignRight"> - <widget class="QPushButton" name="sendMailButton"> - <property name="text"> - <string>Send Mail</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> |