diff options
Diffstat (limited to '')
-rw-r--r-- | .github/workflows/debug.yml | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | gpgfrontend.qrc | 1 | ||||
-rw-r--r-- | resource/icons/ssh-key.png | bin | 0 -> 6510 bytes | |||
-rw-r--r-- | src/gpg/function/GpgKeyImportExportor.cpp | 28 | ||||
-rw-r--r-- | src/gpg/function/GpgKeyImportExportor.h | 5 | ||||
-rwxr-xr-x | src/ui/KeyMgmt.cpp | 52 | ||||
-rwxr-xr-x | src/ui/KeyMgmt.h | 3 | ||||
-rw-r--r-- | src/ui/keypair_details/KeyPairDetailTab.cpp | 106 | ||||
-rw-r--r-- | src/ui/keypair_details/KeyPairDetailTab.h | 5 | ||||
-rw-r--r-- | src/ui/keypair_details/KeyPairSubkeyTab.cpp | 27 | ||||
-rw-r--r-- | src/ui/widgets/FilePage.cpp | 64 | ||||
-rw-r--r-- | src/ui/widgets/FilePage.h | 7 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.cpp | 67 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.h | 12 | ||||
-rw-r--r-- | ui/VerifyDetails.ui | 150 |
16 files changed, 447 insertions, 88 deletions
diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index 2a0f6319..b468761c 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -2,12 +2,13 @@ name: Debug Build & Package on: push: - branches: [ develop ] + branches: [ develop, develop_ui ] paths-ignore: - '**/README.md' - '**/README_CN.md' - 'resource/ts/**' - 'docs/**' + - '**.md' pull_request: branches: [ develop ] paths-ignore: @@ -15,6 +16,7 @@ on: - '**/README_CN.md' - 'resource/ts/**' - 'docs/**' + - '**.md' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d759f6a..377f627c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(GpgFrontend VERSION 2.0.1 LANGUAGES CXX) +project(GpgFrontend VERSION 2.0.2 LANGUAGES CXX) message(STATUS "GpgFrontend Build Configuration Started CMAKE Version ${CMAKE_VERSION}") @@ -274,7 +274,7 @@ if (QT5_ENV_SUPPORT) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) - + set(CMAKE_AUTORCC_OPTIONS "--compress;9") set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${CMAKE_SOURCE_DIR}/ui) message(STATUS "CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS}") diff --git a/gpgfrontend.qrc b/gpgfrontend.qrc index 1f8953a6..c2589b83 100644 --- a/gpgfrontend.qrc +++ b/gpgfrontend.qrc @@ -47,6 +47,7 @@ <file alias="misc_doc.png">resource/icons/misc_doc.png</file> <file alias="quote.png">resource/icons/quote.png</file> <file alias="signature.png">resource/icons/signature.png</file> + <file alias="ssh-key.png">resource/icons/ssh-key.png</file> <file alias="statusbar_icon.png">resource/icons/statusbar_icon.png</file> <file alias="txt.png">resource/icons/txt.png</file> <file alias="undo.png">resource/icons/undo.png</file> diff --git a/resource/icons/ssh-key.png b/resource/icons/ssh-key.png Binary files differnew file mode 100644 index 00000000..c3563e43 --- /dev/null +++ b/resource/icons/ssh-key.png diff --git a/src/gpg/function/GpgKeyImportExportor.cpp b/src/gpg/function/GpgKeyImportExportor.cpp index d8812839..89d3b002 100644 --- a/src/gpg/function/GpgKeyImportExportor.cpp +++ b/src/gpg/function/GpgKeyImportExportor.cpp @@ -130,3 +130,31 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKey( std::swap(out_buffer, temp_out_buffer); return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; } + +bool GpgFrontend::GpgKeyImportExportor::ExportKeyOpenSSH( + const GpgFrontend::GpgKey& key, + GpgFrontend::ByteArrayPtr& out_buffer) const { + GpgData data_out; + auto err = + gpgme_op_export(ctx, key.id().c_str(), GPGME_EXPORT_MODE_SSH, data_out); + + DLOG(INFO) << "read_bytes" << gpgme_data_seek(data_out, 0, SEEK_END); + + auto temp_out_buffer = data_out.Read2Buffer(); + std::swap(out_buffer, temp_out_buffer); + return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; +} + +bool GpgFrontend::GpgKeyImportExportor::ExportSecretKeyShortest( + const GpgFrontend::GpgKey& key, + GpgFrontend::ByteArrayPtr& out_buffer) const { + GpgData data_out; + auto err = gpgme_op_export(ctx, key.id().c_str(), GPGME_EXPORT_MODE_MINIMAL, + data_out); + + DLOG(INFO) << "read_bytes" << gpgme_data_seek(data_out, 0, SEEK_END); + + auto temp_out_buffer = data_out.Read2Buffer(); + std::swap(out_buffer, temp_out_buffer); + return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; +} diff --git a/src/gpg/function/GpgKeyImportExportor.h b/src/gpg/function/GpgKeyImportExportor.h index 35a237ba..ad43d539 100644 --- a/src/gpg/function/GpgKeyImportExportor.h +++ b/src/gpg/function/GpgKeyImportExportor.h @@ -90,8 +90,13 @@ class GpgKeyImportExportor bool ExportKey(const GpgKey& key, ByteArrayPtr& out_buffer) const; + bool ExportKeyOpenSSH(const GpgKey& key, ByteArrayPtr& out_buffer) const; + bool ExportSecretKey(const GpgKey& key, ByteArrayPtr& outBuffer) const; + bool ExportSecretKeyShortest(const GpgKey& key, + ByteArrayPtr& outBuffer) const; + private: GpgContext& ctx = GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index aa6df120..f28e6587 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -207,6 +207,13 @@ void KeyMgmt::createActions() { connect(exportKeyToFileAct, SIGNAL(triggered()), this, SLOT(slotExportKeyToFile())); + exportKeyAsOpenSSHFormat = new QAction(_("Export As OpenSSH"), this); + exportKeyAsOpenSSHFormat->setIcon(QIcon(":ssh-key.png")); + exportKeyAsOpenSSHFormat->setToolTip( + _("Export Selected Key(s) As OpenSSH Format to File")); + connect(exportKeyAsOpenSSHFormat, SIGNAL(triggered()), this, + SLOT(slotExportAsOpenSSHFormat())); + deleteSelectedKeysAct = new QAction(_("Delete Selected Key(s)"), this); deleteSelectedKeysAct->setToolTip(_("Delete the Selected keys")); connect(deleteSelectedKeysAct, SIGNAL(triggered()), this, @@ -240,6 +247,7 @@ void KeyMgmt::createMenus() { importKeyMenu->addAction(importKeyFromKeyServerAct); keyMenu->addAction(exportKeyToFileAct); keyMenu->addAction(exportKeyToClipboardAct); + keyMenu->addAction(exportKeyAsOpenSSHFormat); keyMenu->addSeparator(); keyMenu->addAction(deleteCheckedKeysAct); } @@ -273,6 +281,7 @@ void KeyMgmt::createToolBars() { keyToolBar->addSeparator(); keyToolBar->addAction(exportKeyToFileAct); keyToolBar->addAction(exportKeyToClipboardAct); + keyToolBar->addAction(exportKeyAsOpenSSHFormat); } void KeyMgmt::slotDeleteSelectedKeys() { @@ -446,4 +455,47 @@ void KeyMgmt::slotSaveWindowState() { GlobalSettingStation::GetInstance().Sync(); } +void KeyMgmt::slotExportAsOpenSSHFormat() { + ByteArrayPtr key_export_data = nullptr; + auto keys_checked = mKeyList->getChecked(); + + if (keys_checked->empty()) { + QMessageBox::critical(nullptr, _("Error"), _("No Key Checked.")); + return; + } + + auto key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); + if (!GpgKeyImportExportor::GetInstance().ExportKeyOpenSSH(key, + key_export_data)) { + QMessageBox::critical(nullptr, _("Error"), + _("An error occur in exporting.")); + return; + } + + if (key_export_data->empty()) { + QMessageBox::critical( + nullptr, _("Error"), + _("This key may not be able to export as OpenSSH format. Please check " + "the key-size of the subkey(s) used to sign.")); + return; + } + + key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); + if (!key.good()) { + QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); + return; + } + QString fileString = QString::fromStdString(key.name() + " " + key.email() + + "(" + key.id() + ").pub"); + + QString file_name = QFileDialog::getSaveFileName( + this, _("Export OpenSSH Key To File"), fileString, + QString(_("OpenSSH Public Key Files")) + " (*.pub);;All Files (*)"); + + if (!file_name.isEmpty()) { + write_buffer_to_file(file_name.toStdString(), *key_export_data); + emit signalStatusBarChanged(QString(_("key(s) exported"))); + } +} + } // namespace GpgFrontend::UI diff --git a/src/ui/KeyMgmt.h b/src/ui/KeyMgmt.h index bf1c9b5a..7edb1b5c 100755 --- a/src/ui/KeyMgmt.h +++ b/src/ui/KeyMgmt.h @@ -48,6 +48,8 @@ class KeyMgmt : public QMainWindow { void slotExportKeyToClipboard(); + void slotExportAsOpenSSHFormat(); + void slotDeleteSelectedKeys(); void slotDeleteCheckedKeys(); @@ -80,6 +82,7 @@ class KeyMgmt : public QMainWindow { QMenu* importKeyMenu{}; QAction* openKeyFileAct{}; QAction* exportKeyToFileAct{}; + QAction* exportKeyAsOpenSSHFormat{}; QAction* exportKeyToClipboardAct{}; QAction* deleteCheckedKeysAct{}; QAction* deleteSelectedKeysAct{}; diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index de16ccf7..3de83e38 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -81,15 +81,24 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) vboxKD->addWidget(new QLabel(QString(_("Master Key Existence")) + ": "), 8, 0); - vboxKD->addWidget(keyidVarLabel, 0, 1); - vboxKD->addWidget(algorithmVarLabel, 1, 1); - vboxKD->addWidget(keySizeVarLabel, 2, 1); - vboxKD->addWidget(usageVarLabel, 3, 1); - vboxKD->addWidget(actualUsageVarLabel, 4, 1); - vboxKD->addWidget(createdVarLabel, 5, 1); - vboxKD->addWidget(expireVarLabel, 6, 1); - vboxKD->addWidget(lastUpdateVarLabel, 7, 1); - vboxKD->addWidget(masterKeyExistVarLabel, 8, 1); + vboxKD->addWidget(keyidVarLabel, 0, 1, 1, 1); + vboxKD->addWidget(algorithmVarLabel, 1, 1, 1, 2); + vboxKD->addWidget(keySizeVarLabel, 2, 1, 1, 2); + vboxKD->addWidget(usageVarLabel, 3, 1, 1, 2); + vboxKD->addWidget(actualUsageVarLabel, 4, 1, 1, 2); + vboxKD->addWidget(createdVarLabel, 5, 1, 1, 2); + vboxKD->addWidget(expireVarLabel, 6, 1, 1, 2); + vboxKD->addWidget(lastUpdateVarLabel, 7, 1, 1, 2); + vboxKD->addWidget(masterKeyExistVarLabel, 8, 1, 1, 2); + + auto* copyKeyIdButton = new QPushButton(_("Copy")); + copyKeyIdButton->setFlat(true); + vboxKD->addWidget(copyKeyIdButton, 0, 2); + connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() { + QString fpr = keyidVarLabel->text().trimmed(); + QClipboard* cb = QApplication::clipboard(); + cb->setText(fpr); + }); ownerBox->setLayout(vboxOD); mvbox->addWidget(ownerBox); @@ -119,6 +128,9 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) mvbox->addStretch(); mvbox->addWidget(fingerprintBox); + // Set Menu + createOperaMenu(); + auto* opera_key_box = new QGroupBox(_("Operations")); auto* vbox_p_k = new QVBoxLayout(); @@ -131,10 +143,9 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) SLOT(slotExportPublicKey())); if (mKey.is_private_key()) { - auto* export_private_button = - new QPushButton(_("Export Private Key (Include Subkey)")); - connect(export_private_button, SIGNAL(clicked()), this, - SLOT(slotExportPrivateKey())); + auto* export_private_button = new QPushButton(_("Export Private Key")); + export_private_button->setStyleSheet("text-align:center;"); + export_private_button->setMenu(secretKeyExportOperaMenu); export_h_box_layout->addWidget(export_private_button); if (mKey.has_master_key()) { @@ -157,10 +168,6 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) auto* key_server_opera_button = new QPushButton(_("Key Server Operation (Pubkey)")); key_server_opera_button->setStyleSheet("text-align:center;"); - connect(key_server_opera_button, SIGNAL(clicked()), this, - SLOT(slotModifyEditDatetime())); - // Set Menu - createKeyServerOperaMenu(); key_server_opera_button->setMenu(keyServerOperaMenu); advance_h_box_layout->addWidget(key_server_opera_button); @@ -230,6 +237,48 @@ void KeyPairDetailTab::slotExportPublicKey() { } } +void KeyPairDetailTab::slotExportShortPrivateKey() { + // Show a information box with explanation about private key + int ret = QMessageBox::information( + this, _("Exporting short private Key"), + "<h3>" + QString(_("You are about to export your")) + + "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + + _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + + _("Do you REALLY want to export your PRIVATE KEY in a Minimum " + "Size?") + + "<br />" + + _("For OpenPGP keys it removes all signatures except for the latest " + "self-signatures."), + QMessageBox::Cancel | QMessageBox::Ok); + + // export key, if ok was clicked + if (ret == QMessageBox::Ok) { + ByteArrayPtr keyArray = nullptr; + + if (!GpgKeyImportExportor::GetInstance().ExportSecretKeyShortest( + mKey, keyArray)) { + QMessageBox::critical( + this, _("Error"), + _("An error occurred during the export operation.")); + return; + } + auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_short_secret.asc"; + auto fileName = + QFileDialog::getSaveFileName( + this, _("Export Key To File"), QString::fromStdString(fileString), + QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") + .toStdString(); + + if (!write_buffer_to_file(fileName, *keyArray)) { + QMessageBox::critical( + this, _("Export Error"), + QString(_("Couldn't open %1 for writing")).arg(fileName.c_str())); + return; + } + } +} + void KeyPairDetailTab::slotExportPrivateKey() { // Show a information box with explanation about private key int ret = QMessageBox::information( @@ -250,8 +299,8 @@ void KeyPairDetailTab::slotExportPrivateKey() { _("An error occurred during the export operation.")); return; } - auto fileString = - mKey.name() + " " + mKey.email() + "(" + mKey.id() + ")_secret.asc"; + auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_full_secret.asc"; auto fileName = QFileDialog::getSaveFileName( this, _("Export Key To File"), QString::fromStdString(fileString), @@ -370,7 +419,7 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { } } -void KeyPairDetailTab::createKeyServerOperaMenu() { +void KeyPairDetailTab::createOperaMenu() { keyServerOperaMenu = new QMenu(this); auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this); @@ -384,6 +433,21 @@ void KeyPairDetailTab::createKeyServerOperaMenu() { keyServerOperaMenu->addAction(uploadKeyPair); keyServerOperaMenu->addAction(updateKeyPair); + + secretKeyExportOperaMenu = new QMenu(this); + + auto* exportFullSecretKey = new QAction(_("Export Full Secret Key"), this); + connect(exportFullSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportPrivateKey())); + if (!mKey.is_private_key()) exportFullSecretKey->setDisabled(true); + + auto* exportShortestSecretKey = + new QAction(_("Export Shortest Secret Key"), this); + connect(exportShortestSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportShortPrivateKey())); + + secretKeyExportOperaMenu->addAction(exportFullSecretKey); + secretKeyExportOperaMenu->addAction(exportShortestSecretKey); } void KeyPairDetailTab::slotUploadKeyToServer() { @@ -403,7 +467,7 @@ void KeyPairDetailTab::slotUpdateKeyFromServer() { } void KeyPairDetailTab::slotGenRevokeCert() { - auto literal = QStringLiteral("%1 (*.rev)").arg(_("Revocation Certificates")); + auto literal = QString("%1 (*.rev)").arg(_("Revocation Certificates")); QString m_output_file_name; QFileDialog dialog(this, "Generate revocation certificate", QString(), diff --git a/src/ui/keypair_details/KeyPairDetailTab.h b/src/ui/keypair_details/KeyPairDetailTab.h index 68ca1ebd..3e2f2298 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.h +++ b/src/ui/keypair_details/KeyPairDetailTab.h @@ -36,7 +36,7 @@ namespace GpgFrontend::UI { class KeyPairDetailTab : public QWidget { Q_OBJECT - void createKeyServerOperaMenu(); + void createOperaMenu(); private slots: @@ -45,6 +45,8 @@ class KeyPairDetailTab : public QWidget { */ void slotExportPrivateKey(); + void slotExportShortPrivateKey(); + void slotExportPublicKey(); /** @@ -95,6 +97,7 @@ class KeyPairDetailTab : public QWidget { QLabel* expLabel; QMenu* keyServerOperaMenu{}; + QMenu* secretKeyExportOperaMenu{}; public: explicit KeyPairDetailTab(const std::string& key_id, diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp index 93e07875..cb71c09f 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp @@ -64,7 +64,7 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) subkeyDetailLayout->addWidget(new QLabel(QString(_("Usage")) + ": "), 3, 0); subkeyDetailLayout->addWidget(new QLabel(QString(_("Expires On")) + ": "), 4, 0); - subkeyDetailLayout->addWidget(new QLabel(QString(_("Last Update")) + ": "), 5, + subkeyDetailLayout->addWidget(new QLabel(QString(_("Create Date")) + ": "), 5, 0); subkeyDetailLayout->addWidget(new QLabel(QString(_("Existence")) + ": "), 6, 0); @@ -80,14 +80,23 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) masterKeyExistVarLabel = new QLabel(); fingerPrintVarLabel = new QLabel(); - subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1); - subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1); - subkeyDetailLayout->addWidget(expireVarLabel, 4, 1); - subkeyDetailLayout->addWidget(algorithmVarLabel, 1, 1); - subkeyDetailLayout->addWidget(createdVarLabel, 5, 1); - subkeyDetailLayout->addWidget(usageVarLabel, 3, 1); - subkeyDetailLayout->addWidget(masterKeyExistVarLabel, 6, 1); - subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1); + subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1, 1, 1); + subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1, 1, 2); + subkeyDetailLayout->addWidget(expireVarLabel, 4, 1, 1, 2); + subkeyDetailLayout->addWidget(algorithmVarLabel, 1, 1, 1, 2); + subkeyDetailLayout->addWidget(createdVarLabel, 5, 1, 1, 2); + subkeyDetailLayout->addWidget(usageVarLabel, 3, 1, 1, 2); + subkeyDetailLayout->addWidget(masterKeyExistVarLabel, 6, 1, 1, 2); + subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1, 1, 2); + + auto* copyKeyIdButton = new QPushButton(_("Copy")); + copyKeyIdButton->setFlat(true); + subkeyDetailLayout->addWidget(copyKeyIdButton, 0, 2); + connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() { + QString fpr = keyidVarLabel->text().trimmed(); + QClipboard* cb = QApplication::clipboard(); + cb->setText(fpr); + }); listBox->setLayout(subkeyListLayout); listBox->setContentsMargins(0, 12, 0, 0); diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index f73d9104..0cd7c1a8 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -32,11 +32,13 @@ #include "ui/MainWindow.h" #include "ui/SignalStation.h" +#include "ui_FilePage.h" namespace GpgFrontend::UI { -FilePage::FilePage(QWidget* parent) : QWidget(parent), Ui_FilePage() { - setupUi(this); +FilePage::FilePage(QWidget* parent) + : QWidget(parent), ui(std::make_shared<Ui_FilePage>()) { + ui->setupUi(this); firstParent = parent; @@ -44,35 +46,37 @@ FilePage::FilePage(QWidget* parent) : QWidget(parent), Ui_FilePage() { dirModel->setRootPath(QDir::currentPath()); dirModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - fileTreeView->setModel(dirModel); - fileTreeView->setColumnWidth(0, 320); - fileTreeView->sortByColumn(0, Qt::AscendingOrder); + ui->fileTreeView->setModel(dirModel); + ui->fileTreeView->setColumnWidth(0, 320); + ui->fileTreeView->sortByColumn(0, Qt::AscendingOrder); mPath = boost::filesystem::path(dirModel->rootPath().toStdString()); createPopupMenu(); - connect(upPathButton, &QPushButton::clicked, this, &FilePage::slotUpLevel); - connect(refreshButton, &QPushButton::clicked, this, &FilePage::slotGoPath); - optionsButton->setMenu(optionPopUpMenu); + connect(ui->upPathButton, &QPushButton::clicked, this, + &FilePage::slotUpLevel); + connect(ui->refreshButton, &QPushButton::clicked, this, + &FilePage::slotGoPath); + ui->optionsButton->setMenu(optionPopUpMenu); - pathEdit->setText(dirModel->rootPath()); + ui->pathEdit->setText(dirModel->rootPath()); pathEditCompleter = new QCompleter(this); pathCompleteModel = new QStringListModel(); pathEditCompleter->setModel(pathCompleteModel); pathEditCompleter->setCaseSensitivity(Qt::CaseInsensitive); pathEditCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); - pathEdit->setCompleter(pathEditCompleter); + ui->pathEdit->setCompleter(pathEditCompleter); - connect(fileTreeView, &QTreeView::clicked, this, + connect(ui->fileTreeView, &QTreeView::clicked, this, &FilePage::fileTreeViewItemClicked); - connect(fileTreeView, &QTreeView::doubleClicked, this, + connect(ui->fileTreeView, &QTreeView::doubleClicked, this, &FilePage::fileTreeViewItemDoubleClicked); - connect(fileTreeView, &QTreeView::customContextMenuRequested, this, + connect(ui->fileTreeView, &QTreeView::customContextMenuRequested, this, &FilePage::onCustomContextMenu); - connect(pathEdit, &QLineEdit::textChanged, [=]() { - auto path = pathEdit->text(); + connect(ui->pathEdit, &QLineEdit::textChanged, [=]() { + auto path = ui->pathEdit->text(); auto dir = QDir(path); if (path.endsWith("/") && dir.isReadable()) { auto dir_list = dir.entryInfoList(QDir::AllEntries); @@ -99,7 +103,7 @@ void FilePage::fileTreeViewItemClicked(const QModelIndex& index) { } void FilePage::slotUpLevel() { - QModelIndex currentRoot = fileTreeView->rootIndex(); + QModelIndex currentRoot = ui->fileTreeView->rootIndex(); auto utf8_path = dirModel->fileInfo(currentRoot).absoluteFilePath().toStdString(); @@ -117,7 +121,7 @@ void FilePage::slotUpLevel() { if (mPath.has_parent_path() && !mPath.parent_path().empty()) { mPath = mPath.parent_path(); LOG(INFO) << "parent path" << mPath; - pathEdit->setText(mPath.string().c_str()); + ui->pathEdit->setText(mPath.string().c_str()); this->slotGoPath(); } } @@ -127,7 +131,7 @@ void FilePage::fileTreeViewItemDoubleClicked(const QModelIndex& index) { if (file_info.isFile()) { slotOpenItem(); } else { - pathEdit->setText(file_info.filePath()); + ui->pathEdit->setText(file_info.filePath()); slotGoPath(); } } @@ -137,7 +141,7 @@ QString FilePage::getSelected() const { } void FilePage::slotGoPath() { - const auto path_edit = pathEdit->text().toStdString(); + const auto path_edit = ui->pathEdit->text().toStdString(); #ifdef WINDOWS std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; @@ -152,12 +156,12 @@ void FilePage::slotGoPath() { if (fileInfo.isDir() && fileInfo.isReadable() && fileInfo.isExecutable()) { mPath = boost::filesystem::path(fileInfo.filePath().toStdString()); LOG(INFO) << "set path" << mPath; - fileTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); + ui->fileTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); dirModel->setRootPath(fileInfo.filePath()); for (int i = 1; i < dirModel->columnCount(); ++i) { - fileTreeView->resizeColumnToContents(i); + ui->fileTreeView->resizeColumnToContents(i); } - pathEdit->setText(mPath.generic_path().string().c_str()); + ui->pathEdit->setText(mPath.generic_path().string().c_str()); } else { QMessageBox::critical( this, _("Error"), @@ -240,7 +244,7 @@ void FilePage::createPopupMenu() { } void FilePage::onCustomContextMenu(const QPoint& point) { - QModelIndex index = fileTreeView->indexAt(point); + QModelIndex index = ui->fileTreeView->indexAt(point); selectedPath = boost::filesystem::path( dirModel->fileInfo(index).absoluteFilePath().toStdString()); LOG(INFO) << "right click" << selectedPath; @@ -272,7 +276,7 @@ void FilePage::onCustomContextMenu(const QPoint& point) { verifyItemAct->setEnabled(false); hashCalculateAct->setEnabled(false); } - popUpMenu->exec(fileTreeView->viewport()->mapToGlobal(point)); + popUpMenu->exec(ui->fileTreeView->viewport()->mapToGlobal(point)); } void FilePage::slotOpenItem() { @@ -281,7 +285,7 @@ void FilePage::slotOpenItem() { if (info.isReadable() && info.isExecutable()) { const auto file_path = info.filePath().toStdString(); LOG(INFO) << "set path" << file_path; - pathEdit->setText(info.filePath()); + ui->pathEdit->setText(info.filePath()); slotGoPath(); } else { QMessageBox::critical(this, _("Error"), @@ -325,8 +329,8 @@ void FilePage::slotRenameItem() { } void FilePage::slotDeleteItem() { - QModelIndex index = fileTreeView->currentIndex(); - QVariant data = fileTreeView->model()->data(index); + QModelIndex index = ui->fileTreeView->currentIndex(); + QVariant data = ui->fileTreeView->model()->data(index); auto ret = QMessageBox::warning(this, _("Warning"), _("Are you sure you want to delete it?"), @@ -412,7 +416,7 @@ void FilePage::slotCalculateHash() { } void FilePage::slotMkdir() { - auto index = fileTreeView->rootIndex(); + auto index = ui->fileTreeView->rootIndex(); QString new_dir_name; bool ok; @@ -450,10 +454,10 @@ void FilePage::slotCreateEmptyFile() { void FilePage::keyPressEvent(QKeyEvent* event) { LOG(INFO) << "Key Press" << event->key(); - if (pathEdit->hasFocus() && + if (ui->pathEdit->hasFocus() && (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) { slotGoPath(); - } else if (fileTreeView->currentIndex().isValid()) { + } else if (ui->fileTreeView->currentIndex().isValid()) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) slotOpenItem(); else if (event->key() == Qt::Key_Delete || diff --git a/src/ui/widgets/FilePage.h b/src/ui/widgets/FilePage.h index d9492f6e..03caf36b 100644 --- a/src/ui/widgets/FilePage.h +++ b/src/ui/widgets/FilePage.h @@ -29,11 +29,12 @@ #include "ui/GpgFrontendUI.h" #include "ui/widgets/InfoBoardWidget.h" -#include "ui_FilePage.h" + +class Ui_FilePage; namespace GpgFrontend::UI { -class FilePage : public QWidget, private Ui_FilePage { +class FilePage : public QWidget { Q_OBJECT public: explicit FilePage(QWidget* parent = nullptr); @@ -75,6 +76,8 @@ class FilePage : public QWidget, private Ui_FilePage { private: void createPopupMenu(); + std::shared_ptr<Ui_FilePage> ui; + QFileSystemModel* dirModel; QCompleter* pathEditCompleter; QStringListModel* pathCompleteModel; diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index 7bb47bb6..1b7dbda0 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -31,14 +31,21 @@ namespace GpgFrontend::UI { InfoBoardWidget::InfoBoardWidget(QWidget* parent) - : QWidget(parent), Ui_InfoBoard() { - setupUi(this); - - actionButtonLayout->addStretch(); - actionLabel->setText(_("Actions Menu")); - copyButton->setText(_("Copy")); - saveButton->setText(_("Save")); - clearButton->setText(_("Clear")); + : QWidget(parent), ui(std::make_shared<Ui_InfoBoard>()) { + ui->setupUi(this); + + ui->actionButtonLayout->addStretch(); + ui->actionLabel->setText(_("InfoBoard's Actions Menu")); + ui->copyButton->setText(_("Copy")); + ui->saveButton->setText(_("Save")); + ui->clearButton->setText(_("Clear")); + + connect(ui->copyButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotCopy); + connect(ui->saveButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotSave); + connect(ui->clearButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotReset); connect(SignalStation::GetInstance(), &SignalStation::signalRefreshInfoBoard, this, &InfoBoardWidget::slotRefresh); @@ -47,7 +54,7 @@ InfoBoardWidget::InfoBoardWidget(QWidget* parent) void InfoBoardWidget::setInfoBoard(const QString& text, InfoBoardStatus verifyLabelStatus) { QString color; - infoBoard->clear(); + ui->infoBoard->clear(); switch (verifyLabelStatus) { case INFO_ERROR_OK: color = "#008000"; @@ -61,12 +68,12 @@ void InfoBoardWidget::setInfoBoard(const QString& text, default: break; } - infoBoard->append(text); + ui->infoBoard->append(text); - infoBoard->setAutoFillBackground(true); - QPalette status = infoBoard->palette(); + ui->infoBoard->setAutoFillBackground(true); + QPalette status = ui->infoBoard->palette(); status.setColor(QPalette::Text, color); - infoBoard->setPalette(status); + ui->infoBoard->setPalette(status); auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); @@ -78,13 +85,13 @@ void InfoBoardWidget::setInfoBoard(const QString& text, } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("info_font_size"); } - infoBoard->setFont(QFont("Times", info_font_size)); + ui->infoBoard->setFont(QFont("Times", info_font_size)); } void InfoBoardWidget::slotRefresh(const QString& text, InfoBoardStatus status) { - infoBoard->clear(); + ui->infoBoard->clear(); setInfoBoard(text, status); - infoBoard->verticalScrollBar()->setValue(0); + ui->infoBoard->verticalScrollBar()->setValue(0); } void InfoBoardWidget::associateTextEdit(QTextEdit* edit) { @@ -115,10 +122,10 @@ void InfoBoardWidget::addOptionalAction(const QString& name, auto actionButton = new QPushButton(name); auto layout = new QHBoxLayout(); layout->setContentsMargins(5, 0, 5, 0); - infoBoard->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + ui->infoBoard->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // set margin from surroundings layout->addWidget(actionButton); - actionButtonLayout->addLayout(layout); + ui->actionButtonLayout->addLayout(layout); connect(actionButton, &QPushButton::clicked, this, [=]() { action(); }); } @@ -127,11 +134,11 @@ void InfoBoardWidget::addOptionalAction(const QString& name, */ void InfoBoardWidget::resetOptionActionsMenu() { // skip stretch - deleteWidgetsInLayout(actionButtonLayout, 1); + deleteWidgetsInLayout(ui->actionButtonLayout, 1); } void InfoBoardWidget::slotReset() { - this->infoBoard->clear(); + ui->infoBoard->clear(); resetOptionActionsMenu(); } @@ -153,4 +160,24 @@ void InfoBoardWidget::deleteWidgetsInLayout(QLayout* layout, int start_index) { } } +void InfoBoardWidget::slotCopy() { + auto* clipboard = QGuiApplication::clipboard(); + clipboard->setText(ui->infoBoard->toPlainText()); +} + +void InfoBoardWidget::slotSave() { + auto file_path = QFileDialog::getSaveFileName( + this, _("Save Information Board's Content"), {}, tr("Text (*.txt)")); + LOG(INFO) << "file path" << file_path.toStdString(); + QFile file(file_path); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + file.write(ui->infoBoard->toPlainText().toUtf8()); + } else { + QMessageBox::critical( + this, _("Error"), + _("The file path is not exists, unprivileged or unreachable.")); + } + file.close(); +} + } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/InfoBoardWidget.h b/src/ui/widgets/InfoBoardWidget.h index 05a3b345..8d37be6c 100644 --- a/src/ui/widgets/InfoBoardWidget.h +++ b/src/ui/widgets/InfoBoardWidget.h @@ -28,7 +28,8 @@ #include "EditorPage.h" #include "gpg/result_analyse/VerifyResultAnalyse.h" #include "ui/details/VerifyDetailsDialog.h" -#include "ui_InfoBoard.h" + +class Ui_InfoBoard; namespace GpgFrontend::UI { @@ -45,7 +46,7 @@ typedef enum { /** * @brief Class for handling the verifylabel shown at buttom of a textedit-page */ -class InfoBoardWidget : public QWidget, private Ui_InfoBoard { +class InfoBoardWidget : public QWidget { Q_OBJECT public: /** @@ -82,7 +83,14 @@ class InfoBoardWidget : public QWidget, private Ui_InfoBoard { */ void slotRefresh(const QString& text, InfoBoardStatus status); + private slots: + + void slotCopy(); + + void slotSave(); + private: + std::shared_ptr<Ui_InfoBoard> ui; QTextEdit* mTextPage{nullptr}; /** TextEdit associated to the notification */ QTabWidget* mTabWidget{nullptr}; diff --git a/ui/VerifyDetails.ui b/ui/VerifyDetails.ui new file mode 100644 index 00000000..3f88984d --- /dev/null +++ b/ui/VerifyDetails.ui @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VerifyDetailsDialog</class> + <widget class="QDialog" name="VerifyDetailsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>635</width> + <height>829</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item alignment="Qt::AlignTop"> + <widget class="QWidget" name="horizontalWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item alignment="Qt::AlignTop"> + <widget class="QLabel" name="titleLabel"> + <property name="text"> + <string>Verify Details</string> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item alignment="Qt::AlignTop"> + <widget class="QWidget" name="verticalWidget_2" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item alignment="Qt::AlignTop"> + <widget class="QWidget" name="horizontalWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item alignment="Qt::AlignLeft|Qt::AlignTop"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Date: </string> + </property> + </widget> + </item> + <item alignment="Qt::AlignTop"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="horizontalWidget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item alignment="Qt::AlignLeft"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Status: </string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Signer(s) List: </string> + </property> + </widget> + </item> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Tab 1</string> + </attribute> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Tab 2</string> + </attribute> + </widget> + </widget> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |