From d0f3cd7f9c910a72545b2333d81dda4155412c3d Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Tue, 14 Jul 2026 12:07:50 +0300 Subject: [PATCH] New signing dialog IB-7974 Signed-off-by: Raul Metsma --- client/Application.cpp | 1 + client/CDocSupport.cpp | 7 +- client/CMakeLists.txt | 3 +- client/CheckConnection.cpp | 2 +- client/MainWindow.cpp | 58 +- client/MainWindow.h | 7 +- client/Utils.h | 1 + client/common_enums.h | 6 +- client/dialogs/MobileDialog.cpp | 105 --- client/dialogs/MobileDialog.h | 40 - client/dialogs/MobileDialog.ui | 318 -------- client/dialogs/MobileProgress.ui | 179 ----- client/dialogs/SigningDialog.cpp | 283 +++++++ client/dialogs/SigningDialog.h | 34 + client/dialogs/SigningDialog.ui | 710 ++++++++++++++++++ client/dialogs/SmartIDDialog.cpp | 98 --- client/dialogs/SmartIDDialog.h | 39 - client/dialogs/SmartIDDialog.ui | 355 --------- client/{dialogs => signer}/MobileProgress.cpp | 79 +- client/{dialogs => signer}/MobileProgress.h | 14 +- client/{ => signer}/QSigner.cpp | 0 client/{ => signer}/QSigner.h | 0 .../{dialogs => signer}/SmartIDProgress.cpp | 96 +-- client/{dialogs => signer}/SmartIDProgress.h | 6 +- client/translations/en.ts | 175 ++--- client/translations/et.ts | 177 ++--- client/widgets/CardListItem.cpp | 82 ++ client/widgets/CardListItem.h | 33 + client/widgets/CardListItem.ui | 153 ++++ client/widgets/ContainerPage.cpp | 120 +-- client/widgets/ContainerPage.h | 8 +- client/widgets/MainAction.cpp | 154 +--- client/widgets/MainAction.h | 15 +- client/widgets/MainAction.ui | 130 ---- client/widgets/ShrinkingStack.h | 17 + 35 files changed, 1618 insertions(+), 1887 deletions(-) delete mode 100644 client/dialogs/MobileDialog.cpp delete mode 100644 client/dialogs/MobileDialog.h delete mode 100644 client/dialogs/MobileDialog.ui delete mode 100644 client/dialogs/MobileProgress.ui create mode 100644 client/dialogs/SigningDialog.cpp create mode 100644 client/dialogs/SigningDialog.h create mode 100644 client/dialogs/SigningDialog.ui delete mode 100644 client/dialogs/SmartIDDialog.cpp delete mode 100644 client/dialogs/SmartIDDialog.h delete mode 100644 client/dialogs/SmartIDDialog.ui rename client/{dialogs => signer}/MobileProgress.cpp (85%) rename client/{dialogs => signer}/MobileProgress.h (81%) rename client/{ => signer}/QSigner.cpp (100%) rename client/{ => signer}/QSigner.h (100%) rename client/{dialogs => signer}/SmartIDProgress.cpp (83%) rename client/{dialogs => signer}/SmartIDProgress.h (93%) create mode 100644 client/widgets/CardListItem.cpp create mode 100644 client/widgets/CardListItem.h create mode 100644 client/widgets/CardListItem.ui delete mode 100644 client/widgets/MainAction.ui create mode 100644 client/widgets/ShrinkingStack.h diff --git a/client/Application.cpp b/client/Application.cpp index 75f246650..66781ff28 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -432,6 +432,7 @@ Application::Application( int &argc, char **argv ) // Clear obsolete registriy settings Settings::CDOC2_NOTIFICATION.clear(); + Settings::MOBILEID_ORDER.clear(); #ifndef Q_OS_DARWIN Settings::DEFAULT_DIR.clear(); #endif diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index f50a6c294..727566adf 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -201,12 +201,13 @@ DDCryptoBackend::getLastErrorStr(libcdoc::result_t code) const static bool checkConnection() { - if(CheckConnection().check()) { + CheckConnection check; + if(check.check()) { return true; } - return dispatchToMain([] { + return dispatchToMain([check] { FadeInNotification::error(Application::mainWindow()->findChild(QStringLiteral("topBar")), - QCoreApplication::translate("MainWindow", "Check internet connection")); + check.errorString()); return false; }); } diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 475f57341..668a58961 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(libcdoc EXCLUDE_FROM_ALL) file(GLOB WIDGETS CONFIGURE_DEPENDS "dialogs/*.cpp" "dialogs/*.h" "dialogs/*.ui" "effects/*.cpp" "effects/*.h" "effects/*.ui" + "signer/*.cpp" "signer/*.h" "widgets/*.cpp" "widgets/*.h" "widgets/*.ui" ) add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE @@ -66,8 +67,6 @@ add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE QPCSC.h QPKCS11.cpp QPKCS11.h - QSigner.cpp - QSigner.h QSmartCard.cpp QSmartCard_p.h QSmartCard.h diff --git a/client/CheckConnection.cpp b/client/CheckConnection.cpp index ee6da2d30..ee8cd7d48 100644 --- a/client/CheckConnection.cpp +++ b/client/CheckConnection.cpp @@ -57,7 +57,7 @@ QString CheckConnection::errorString() const case QNetworkReply::ProxyAuthenticationRequiredError: return QCoreApplication::translate("CheckConnection", "Check proxy username and password"); default: - return QCoreApplication::translate("CheckConnection", "Cannot connect to certificate status service!"); + return QCoreApplication::translate("CheckConnection", "Check internet connection"); } } diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index f5840bea2..6eee521e6 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -26,17 +26,15 @@ #include "DigiDoc.h" #include "QCryptoBackend.h" #include "QPCSC.h" -#include "QSigner.h" #include "Settings.h" #include "SslCertificate.h" #include "TokenData.h" #include "effects/FadeInNotification.h" #include "effects/Overlay.h" #include "dialogs/FileDialog.h" -#include "dialogs/MobileProgress.h" #include "dialogs/RoleAddressDialog.h" #include "dialogs/SettingsDialog.h" -#include "dialogs/SmartIDProgress.h" +#include "dialogs/SigningDialog.h" #include "dialogs/WaitDialog.h" #include "dialogs/WarningDialog.h" #include "widgets/CardPopup.h" @@ -218,7 +216,6 @@ void MainWindow::mouseReleaseEvent(QMouseEvent *event) { if(auto *cardPopup = findChild()) cardPopup->deleteLater(); - ui->signContainerPage->clearPopups(); QWidget::mouseReleaseEvent(event); } @@ -283,30 +280,12 @@ void MainWindow::navigateToPage( Pages page, const QStringList &files, bool crea selectPage(page); } -void MainWindow::onSignAction(int action, const QString &idCode, const QString &info2) +void MainWindow::onSignAction(int action) { switch(action) { case SignatureAdd: - case SignatureToken: - sign([this](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { - QSigner signer(qApp->cryptoManager()->tokensign()); - return digiDoc->sign(city, state, zip, country, role, &signer); - }); - break; - case SignatureMobile: - sign([this, idCode, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { - MobileProgress m(this); - return m.init(idCode, info2) && - digiDoc->sign(city, state, zip, country, role, &m); - }); - break; - case SignatureSmartID: - sign([this, idCode, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { - SmartIDProgress s(this); - return s.init(info2, idCode, digiDoc->fileName()) && - digiDoc->sign(city, state, zip, country, role, &s); - }); + sign(); break; case ClearSignatureWarning: ui->signature->warningIcon(false); @@ -360,7 +339,7 @@ void MainWindow::convertToCDoc() FadeInNotification::success(ui->topBar, tr("Converted to crypto container!")); } -void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString &/*phone*/) +void MainWindow::onCryptoAction(int action) { switch(action) { @@ -551,12 +530,11 @@ void MainWindow::showSettings(int page) dlg.exec(); } -template -void MainWindow::sign(F &&sign) +void MainWindow::sign() { - if(!CheckConnection().check()) + if(CheckConnection check; !check.check()) { - FadeInNotification::error(ui->topBar, tr("Check internet connection")); + FadeInNotification::error(ui->topBar, check.errorString()); return; } @@ -564,28 +542,34 @@ void MainWindow::sign(F &&sign) if(RoleAddressDialog(this).get(city, country, state, zip, role) == QDialog::Rejected) return; - WaitDialogHolder waitDialog(this, tr("Signing")); if(digiDoc->isPDF()) { - QString wrappedFile = digiDoc->fileName(); - if(!wrap(wrappedFile, true)) + QString pdfFile = digiDoc->fileName(); + if(!wrap(pdfFile, true)) return; - if(!sign(city, state, zip, country, role)) + SigningDialog dlg(digiDoc.get(), ui->signContainerPage); + dlg.setRoleAddress(city, country, state, zip, role); + if(dlg.exec() != QDialog::Accepted) { digiDoc.reset(); - openFiles({std::move(wrappedFile)}); + openFiles({std::move(pdfFile)}); return; } } - else if(!sign(city, state, zip, country, role)) - return; + else + { + SigningDialog dlg(digiDoc.get(), ui->signContainerPage); + dlg.setRoleAddress(city, country, state, zip, role); + if(dlg.exec() != QDialog::Accepted) + return; + } + WaitDialogHolder waitDialog(this, tr("Signing")); if(!digiDoc->save()) return; ui->signContainerPage->transition(digiDoc.get()); - FadeInNotification::success(ui->topBar, tr("The container has been successfully signed!")); } diff --git a/client/MainWindow.h b/client/MainWindow.h index 39de44426..f0ee5ab6d 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -64,12 +64,11 @@ class MainWindow final : public QWidget void changePinClicked(QSmartCardData::PinType type, QSmartCard::PinAction action); void convertToCDoc(); void navigateToPage(Pages page, const QStringList &files = QStringList(), bool create = true); - void onCryptoAction(int action, const QString &id, const QString &phone); - void onSignAction(int action, const QString &idCode, const QString &info2); + void onCryptoAction(int action); + void onSignAction(int action); void openContainer(bool signature); void resetDigiDoc(std::unique_ptr &&doc); - template - void sign(F &&sign); + void sign(); void updateSelector(); void updateMyEID(const TokenData &t); void updateMyEid(const QSmartCardData &data); diff --git a/client/Utils.h b/client/Utils.h index c8c317fe7..035a953cd 100644 --- a/client/Utils.h +++ b/client/Utils.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include diff --git a/client/common_enums.h b/client/common_enums.h index a6f4b2342..f90d1d387 100644 --- a/client/common_enums.h +++ b/client/common_enums.h @@ -42,18 +42,14 @@ enum Actions : unsigned char { ContainerEncrypt, EncryptContainer, + EncryptLT, EncryptContainerSuccess, DecryptContainer, - DecryptToken, DecryptContainerSuccess, SignatureAdd, - SignatureMobile, - SignatureSmartID, - SignatureToken, ClearSignatureWarning, ClearCryptoWarning, - EncryptLT }; } diff --git a/client/dialogs/MobileDialog.cpp b/client/dialogs/MobileDialog.cpp deleted file mode 100644 index b19606d01..000000000 --- a/client/dialogs/MobileDialog.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "MobileDialog.h" -#include "ui_MobileDialog.h" - -#include "IKValidator.h" -#include "Settings.h" -#include "effects/Overlay.h" - -MobileDialog::MobileDialog(QWidget *parent) - : QDialog(parent) - , ui(new Ui::MobileDialog) -{ - static const QStringList countryCodes {QStringLiteral("372"), QStringLiteral("370")}; - new Overlay(this); - - ui->setupUi(this); - setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint); -#ifdef Q_OS_WIN - ui->buttonLayout->setDirection(QBoxLayout::RightToLeft); -#endif - - // Mobile - ui->idCode->setValidator(new NumberValidator(ui->idCode)); - ui->idCode->setText(Settings::MOBILEID_CODE); - ui->idCode->setAttribute(Qt::WA_MacShowFocusRect, false); - ui->errorCode->hide(); - ui->phoneNo->setValidator(new NumberValidator(ui->phoneNo)); - ui->phoneNo->setText(Settings::MOBILEID_NUMBER); - ui->phoneNo->setAttribute(Qt::WA_MacShowFocusRect, false); - ui->phoneNo->setFocus(); - ui->errorPhone->hide(); - ui->cbRemember->setChecked(Settings::MOBILEID_REMEMBER); - ui->cbRemember->setAttribute(Qt::WA_MacShowFocusRect, false); - auto saveSettings = [this] { - bool checked = ui->cbRemember->isChecked(); - Settings::MOBILEID_REMEMBER = checked; - Settings::MOBILEID_CODE = checked ? ui->idCode->text() : QString(); - Settings::MOBILEID_NUMBER = checked ? ui->phoneNo->text() : QString(); - }; - auto setError = [](LineEdit *input, QLabel *error, const QString &msg) { - input->setLabel(msg.isEmpty() ? QString() : QStringLiteral("error")); - error->setText(msg); - error->setHidden(msg.isEmpty()); - }; - connect(ui->idCode, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); - connect(ui->idCode, &QLineEdit::textEdited, this, saveSettings); - connect(ui->idCode, &QLineEdit::textEdited, ui->errorCode, [this, setError] { - setError(ui->idCode, ui->errorCode, {}); - }); - connect(ui->phoneNo, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); - connect(ui->phoneNo, &QLineEdit::textEdited, this, saveSettings); - connect(ui->phoneNo, &QLineEdit::textEdited, ui->errorPhone, [this, setError] { - setError(ui->phoneNo, ui->errorPhone, {}); - }); - connect(ui->cbRemember, &QCheckBox::clicked, this, saveSettings); - connect(ui->sign, &QPushButton::clicked, this, [this, setError] { - if(!IKValidator::isValid(idCode())) - setError(ui->idCode, ui->errorCode, tr("Personal code is not valid")); - else - setError(ui->idCode, ui->errorCode, {}); - if(phoneNo().size() < 8 || countryCodes.contains(phoneNo())) - setError(ui->phoneNo, ui->errorPhone, tr("Phone number is not entered")); - else if(!countryCodes.contains(phoneNo().left(3))) - setError(ui->phoneNo, ui->errorPhone, tr("Invalid country code")); - else - setError(ui->phoneNo, ui->errorPhone, {}); - if(ui->errorCode->text().isEmpty() && ui->errorPhone->text().isEmpty()) - accept(); - }); - connect(ui->cancel, &QPushButton::clicked, this, &MobileDialog::reject); - connect(this, &MobileDialog::finished, this, &MobileDialog::close); -} - -MobileDialog::~MobileDialog() -{ - delete ui; -} - -QString MobileDialog::idCode() -{ - return ui->idCode->text(); -} - -QString MobileDialog::phoneNo() -{ - return ui->phoneNo->text(); -} diff --git a/client/dialogs/MobileDialog.h b/client/dialogs/MobileDialog.h deleted file mode 100644 index 7875dba7d..000000000 --- a/client/dialogs/MobileDialog.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include - -namespace Ui { class MobileDialog; } - -class MobileDialog final : public QDialog -{ - Q_OBJECT - -public: - explicit MobileDialog(QWidget *parent = nullptr); - ~MobileDialog() final; - - QString idCode(); - QString phoneNo(); - -private: - Ui::MobileDialog *ui; -}; - diff --git a/client/dialogs/MobileDialog.ui b/client/dialogs/MobileDialog.ui deleted file mode 100644 index 5fb98339b..000000000 --- a/client/dialogs/MobileDialog.ui +++ /dev/null @@ -1,318 +0,0 @@ - - - MobileDialog - - - Qt::WindowModal - - - - 0 - 0 - 430 - 481 - - - - Mobile-ID - - - QWidget { -color: #07142A; -font-family: Roboto, Helvetica; -font-size: 14px; -} -#MobileDialog { -background-color: #FFFFFF; -border-radius: 4px; -} -#label { -color: #003168; -font-size: 20px; -font-weight: 700; -} -#errorCode, #errorPhone { -color: #AD2A45; -} -QLineEdit { -padding: 10px 14px; -border: 1px solid #C4CBD8; -border-radius: 4px; -background-color: white; -placeholder-text-color: #607496; -font-size: 16px; -} -QLineEdit[label="error"] { -border-color: #BE7884; -} -QCheckBox { -spacing: 8px; -border-right: none; /*Workaround for right padding*/ -} -QCheckBox:disabled { -color: #C4CBD8; -} -QCheckBox::indicator { -width: 16px; -height: 16px; -} -QCheckBox::indicator:unchecked { -image: url(:/images/icon_checkbox.svg); -} -QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus { -image: url(:/images/icon_checkbox_active.svg); -} -QCheckBox::indicator:unchecked:disabled { -image: url(:/images/icon_checkbox_disabled.svg); -} -QCheckBox::indicator:checked { -image: url(:/images/icon_checkbox_check.svg); -} -QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus { -image: url(:/images/icon_checkbox_check_active.svg); -} -QCheckBox::indicator:checked:disabled { -image: url(:/images/icon_checkbox_check_disabled.svg); -} -QPushButton { -padding: 12px 12px; -border-radius: 4px; -border: 1px solid #AD2A45; -color: #AD2A45; -font-weight: 700; -} -QPushButton:hover, QPushButton:focus { -background-color: #F5EBED; -} -QPushButton:pressed { -background-color: #E1C1C6; -} -QPushButton:default { -color: #ffffff; -border-color: #2F70B6; -background-color: #2F70B6; -} -QPushButton:default:hover, QPushButton:default:focus { -border-color: #2B66A6; -background-color: #2B66A6; -} -QPushButton:default:pressed { -border-color: #215081; -background-color: #215081; -} -QPushButton:default:disabled { -border-color: #82A9D3; -background-color: #82A9D3; -} - - - - 40 - - - QLayout::SetDefaultConstraint - - - 40 - - - 32 - - - 40 - - - 32 - - - - - - 350 - 0 - - - - Qt::TabFocus - - - Enter your phone number to sign with mobile-ID - - - Qt::AlignCenter - - - true - - - - - - - 24 - - - - - 6 - - - - - - 0 - 20 - - - - Country code and phone number - - - phoneNo - - - - - - - 37254321 - - - - - - - - - - - 0 - 20 - - - - Qt::TabFocus - - - - - - - - - 6 - - - - - - 0 - 20 - - - - Personal code - - - idCode - - - - - - - 47101010033 - - - - - - - - - - - 0 - 20 - - - - Qt::TabFocus - - - - - - - - - - 0 - 17 - - - - Remember me - - - - - - - - - 0 - - - - - PointingHandCursor - - - Cancel - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - PointingHandCursor - - - Sign - - - true - - - - - - - - - - LineEdit - QLineEdit -
widgets/LineEdit.h
-
-
- - -
diff --git a/client/dialogs/MobileProgress.ui b/client/dialogs/MobileProgress.ui deleted file mode 100644 index b9edf5b73..000000000 --- a/client/dialogs/MobileProgress.ui +++ /dev/null @@ -1,179 +0,0 @@ - - - MobileProgress - - - Qt::WindowModal - - - - 0 - 0 - 432 - 322 - - - - QWidget { -color: #07142A; -font-family: Roboto, Helvetica; -font-size: 14px; -} -#MobileProgress { -background-color: #FFFFFF; -border-radius: 4px; -} -#code { -color: #003168; -font-size: 32px; -font-weight: 700; -} -QPushButton { -padding: 12px 12px; -border-radius: 4px; -border: 1px solid #AD2A45; -color: #AD2A45; -font-weight: 700; -} -QPushButton:hover, QPushButton:focus { -background-color: #F5EBED; -} -QPushButton:pressed { -background-color: #E1C1C6; -} -QPushButton:default { -color: #ffffff; -border-color: #2F70B6; -background-color: #2F70B6; -} -QPushButton:default:hover, QPushButton:default:focus { -border-color: #2B66A6; -background-color: #2B66A6; -} -QPushButton:default:pressed { -border-color: #215081; -background-color: #215081; -} -QPushButton:default:disabled { -border-color: #82A9D3; -background-color: #82A9D3; -} - - - - 30 - - - QLayout::SetFixedSize - - - 40 - - - 32 - - - 40 - - - 32 - - - - - 24 - - - - - - 350 - 0 - - - - Control code: - - - Qt::AlignCenter - - - - - - - Qt::TabFocus - - - 1234 - - - Qt::AlignCenter - - - - - - - Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code. - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - 0 - 40 - - - - 75 - - - 0 - - - Qt::AlignCenter - - - false - - - %v sec - - - - - - - - - - - PointingHandCursor - - - Cancel - - - false - - - - - - - - diff --git a/client/dialogs/SigningDialog.cpp b/client/dialogs/SigningDialog.cpp new file mode 100644 index 000000000..c7a41934f --- /dev/null +++ b/client/dialogs/SigningDialog.cpp @@ -0,0 +1,283 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "SigningDialog.h" +#include "ui_SigningDialog.h" + +#include "Application.h" +#include "DigiDoc.h" +#include "IKValidator.h" +#include "QPCSC.h" +#include "QCryptoBackend.h" +#include "Settings.h" +#include "SslCertificate.h" +#include "dialogs/WarningDialog.h" +#include "effects/Overlay.h" +#include "signer/QSigner.h" +#include "signer/MobileProgress.h" +#include "signer/SmartIDProgress.h" +#include "widgets/CardListItem.h" +#include "widgets/NoCardInfo.h" + +#include + +SigningDialog::SigningDialog(DigiDoc *doc, QWidget *parent) + : QDialog(parent) + , digiDoc(doc) + , ui(new Ui::SigningDialog) +{ + static const QStringList countryCodes {QStringLiteral("372"), QStringLiteral("370")}; + new Overlay(this); + ui->setupUi(this); + ui->progressPage->hide(); + setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint); +#ifdef Q_OS_WIN + ui->buttonLayout->setDirection(QBoxLayout::RightToLeft); +#endif + + // Tab bar + ui->tabs->addTab(tr("ID-card")); + ui->tabs->addTab(tr("Mobile-ID")); + ui->tabs->addTab(tr("Smart-ID")); + connect(ui->tabs, &QTabBar::currentChanged, ui->tabStack, &QStackedWidget::setCurrentIndex); + + // ID-card tab + updateCards(); + connect(qApp->cryptoManager(), &QCryptoManager::cacheChanged, this, &SigningDialog::updateCards); + connect(ui->tabs, &QTabBar::currentChanged, this, [this](int index) { + if(index == IDCard) + updateCards(); + else + ui->sign->setEnabled(true); + }); + + auto setError = [](LineEdit *input, QLabel *error, const QString &msg) { + input->setLabel(msg.isEmpty() ? QString() : QStringLiteral("error")); + error->setText(msg); + error->setHidden(msg.isEmpty()); + }; + + // Mobile-ID tab + ui->idCodeMID->setValidator(new NumberValidator(ui->idCodeMID)); + ui->idCodeMID->setText(Settings::MOBILEID_CODE); + ui->idCodeMID->setAttribute(Qt::WA_MacShowFocusRect, false); + ui->errorCodeMID->hide(); + ui->phoneNo->setValidator(new NumberValidator(ui->phoneNo)); + ui->phoneNo->setText(Settings::MOBILEID_NUMBER); + ui->phoneNo->setAttribute(Qt::WA_MacShowFocusRect, false); + ui->errorPhone->hide(); + ui->cbRememberMID->setChecked(Settings::MOBILEID_REMEMBER); + ui->cbRememberMID->setAttribute(Qt::WA_MacShowFocusRect, false); + auto saveMIDSettings = [this] { + bool checked = ui->cbRememberMID->isChecked(); + Settings::MOBILEID_REMEMBER = checked; + Settings::MOBILEID_CODE = checked ? ui->idCodeMID->text() : QString(); + Settings::MOBILEID_NUMBER = checked ? ui->phoneNo->text() : QString(); + }; + connect(ui->idCodeMID, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); + connect(ui->idCodeMID, &QLineEdit::textEdited, this, saveMIDSettings); + connect(ui->idCodeMID, &QLineEdit::textEdited, this, [this, setError] { + setError(ui->idCodeMID, ui->errorCodeMID, {}); + }); + connect(ui->phoneNo, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); + connect(ui->phoneNo, &QLineEdit::textEdited, this, saveMIDSettings); + connect(ui->phoneNo, &QLineEdit::textEdited, this, [this, setError] { + setError(ui->phoneNo, ui->errorPhone, {}); + }); + connect(ui->cbRememberMID, &QCheckBox::clicked, this, saveMIDSettings); + + // Smart-ID tab + static const QString &EE = Settings::SMARTID_COUNTRY_LIST.first(); + auto *ik = new NumberValidator(ui->idCodeSID); + ui->idCodeSID->setValidator(Settings::SMARTID_COUNTRY == EE ? ik : nullptr); + ui->idCodeSID->setText(Settings::SMARTID_CODE); + ui->idCodeSID->setAttribute(Qt::WA_MacShowFocusRect, false); + ui->errorCodeSID->hide(); + ui->cbRememberSID->setAttribute(Qt::WA_MacShowFocusRect, false); + for(int i = 0, count = Settings::SMARTID_COUNTRY_LIST.size(); i < count; ++i) + ui->idCountry->setItemData(i, Settings::SMARTID_COUNTRY_LIST[i]); + ui->idCountry->setCurrentIndex(ui->idCountry->findData(Settings::SMARTID_COUNTRY)); + ui->cbRememberSID->setChecked(Settings::SMARTID_REMEMBER); + auto saveSIDSettings = [this] { + bool checked = ui->cbRememberSID->isChecked(); + Settings::SMARTID_REMEMBER = checked; + Settings::SMARTID_CODE = checked ? ui->idCodeSID->text() : QString(); + Settings::SMARTID_COUNTRY = checked ? ui->idCountry->currentData().toString() : Settings::SMARTID_COUNTRY_LIST.first(); + }; + connect(ui->idCodeSID, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); + connect(ui->idCodeSID, &QLineEdit::textEdited, this, saveSIDSettings); + connect(ui->idCodeSID, &QLineEdit::textEdited, this, [this, setError] { + setError(ui->idCodeSID, ui->errorCodeSID, {}); + }); + connect(ui->idCountry, &QComboBox::currentTextChanged, this, [this, ik, saveSIDSettings] { + static const QString &EE2 = Settings::SMARTID_COUNTRY_LIST.first(); + ui->idCodeSID->setValidator(ui->idCountry->currentData().toString() == EE2 ? ik : nullptr); + saveSIDSettings(); + }); + connect(ui->cbRememberSID, &QCheckBox::clicked, this, saveSIDSettings); + + // Sign button — validates per-tab then delegates to performSign() + connect(ui->sign, &QPushButton::clicked, this, [this, setError] { + switch(currentMethod()) { + case IDCard: + if(performSign()) accept(); + else reject(); + break; + case MobileID: { + const QString code = ui->idCodeMID->text(); + const QString phone = ui->phoneNo->text(); + if(!IKValidator::isValid(code)) + setError(ui->idCodeMID, ui->errorCodeMID, tr("Personal code is not valid")); + else + setError(ui->idCodeMID, ui->errorCodeMID, {}); + if(phone.size() < 8 || countryCodes.contains(phone)) + setError(ui->phoneNo, ui->errorPhone, tr("Phone number is not entered")); + else if(!countryCodes.contains(phone.left(3))) + setError(ui->phoneNo, ui->errorPhone, tr("Invalid country code")); + else + setError(ui->phoneNo, ui->errorPhone, {}); + if(ui->errorCodeMID->text().isEmpty() && ui->errorPhone->text().isEmpty()) { + if(performSign()) accept(); + else reject(); + } + break; + } + case SmartID: + if(ui->idCodeSID->validator() && !IKValidator::isValid(ui->idCodeSID->text())) + setError(ui->idCodeSID, ui->errorCodeSID, tr("Personal code is not valid")); + else { + setError(ui->idCodeSID, ui->errorCodeSID, {}); + if(performSign()) accept(); + else reject(); + } + break; + } + }); + connect(ui->cancel, &QPushButton::clicked, this, &QDialog::reject); + connect(this, &QDialog::finished, this, &QDialog::close); +} + +SigningDialog::~SigningDialog() +{ + delete ui; +} + +void SigningDialog::setRoleAddress(const QString &city, const QString &country, + const QString &state, const QString &zip, const QString &role) +{ + m_city = city; + m_country = country; + m_state = state; + m_zip = zip; + m_role = role; +} + +SigningDialog::Method SigningDialog::currentMethod() const +{ + return static_cast(ui->tabs->currentIndex()); +} + +QString SigningDialog::currentCode() const +{ + switch(currentMethod()) { + case IDCard: + return SslCertificate(m_selectedToken.cert()).personalCode(); + case MobileID: + return ui->idCodeMID->text(); + case SmartID: + return ui->idCodeSID->text(); + } + return {}; +} + +void SigningDialog::updateCards() +{ + qDeleteAll(ui->cardListContainer->findChildren()); + + // Fall back to global selection if our local token was removed + const QList tokens = qApp->cryptoManager()->cache(); + bool localValid = std::any_of(tokens.cbegin(), tokens.cend(), + [this](const TokenData &t) { return t == m_selectedToken; }); + if(!localValid) + m_selectedToken = qApp->cryptoManager()->tokensign(); + + bool any = false; + bool selectedUsable = false; + for(const TokenData &token : tokens) { + SslCertificate c(token.cert()); + if(!c.keyUsage().contains(SslCertificate::NonRepudiation)) + continue; + bool usable = c.isValid() && !token.data(QStringLiteral("blocked")).toBool(); + auto *w = new CardListItem(ui->cardListContainer); + w->update(token, token.card() == m_selectedToken.card(), usable); + connect(w, &CardListItem::selected, this, [this](const TokenData &t) { + m_selectedToken = t; + updateCards(); + }); + ui->cardListContainer->layout()->addWidget(w); + any = true; + if(token == m_selectedToken) + selectedUsable = usable; + } + + if(!any) { + if(!QPCSC::instance().serviceRunning()) + ui->noCard->update(NoCardInfo::NoPCSC); + else if(QPCSC::instance().readers().isEmpty()) + ui->noCard->update(NoCardInfo::NoReader); + else + ui->noCard->update(NoCardInfo::NoCard); + } + ui->noCard->setVisible(!any); + ui->cardListContainer->setVisible(any); + if(currentMethod() == IDCard) + ui->sign->setEnabled(selectedUsable); +} + +bool SigningDialog::performSign() +{ + // Warn if this personal code has already signed the document + const QString code = currentCode(); + if(!code.isEmpty()) { + const bool alreadySigned = std::any_of( + digiDoc->signatures().cbegin(), digiDoc->signatures().cend(), + [&code](const DigiDocSignature &sig) { + return SslCertificate(sig.cert()).personalCode() == code; + }); + if(alreadySigned) { + auto *dlg = WarningDialog::create(this) + ->withTitle(tr("The document has already been signed by you")) + ->addButton(tr("Continue signing"), QMessageBox::Ok); + if(dlg->exec() != QMessageBox::Ok) + return false; + } + } + + auto showPage = [this](QWidget *page) + { + ui->formPage->setVisible(page == ui->formPage); + ui->progressPage->setVisible(page == ui->progressPage); + }; + + switch(currentMethod()) { + case IDCard: { + QSigner signer(m_selectedToken); + return digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &signer); + } + case MobileID: { + SigningProgressUI pui { ui->progressInfo, ui->progressCode, ui->progressLabel, ui->progressBar, ui->progressCancel, this }; + showPage(ui->progressPage); + MobileProgress m(pui); + return m.init(ui->idCodeMID->text(), ui->phoneNo->text()) && + digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &m); + } + case SmartID: { + SigningProgressUI pui { ui->progressInfo, ui->progressCode, ui->progressLabel, ui->progressBar, ui->progressCancel, this }; + showPage(ui->progressPage); + SmartIDProgress s(pui); + return s.init(ui->idCountry->currentData().toString(), ui->idCodeSID->text(), digiDoc->fileName()) && + digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &s); + } + } + return false; +} diff --git a/client/dialogs/SigningDialog.h b/client/dialogs/SigningDialog.h new file mode 100644 index 000000000..624d81816 --- /dev/null +++ b/client/dialogs/SigningDialog.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once +#include + +#include "TokenData.h" + +class DigiDoc; +namespace Ui { class SigningDialog; } + +class SigningDialog final : public QDialog +{ + Q_OBJECT +public: + enum Method { IDCard, MobileID, SmartID }; + + explicit SigningDialog(DigiDoc *doc, QWidget *parent = nullptr); + ~SigningDialog() final; + + void setRoleAddress(const QString &city, const QString &country, + const QString &state, const QString &zip, const QString &role); + +private: + void updateCards(); + bool performSign(); + Method currentMethod() const; + QString currentCode() const; + + DigiDoc *digiDoc; + Ui::SigningDialog *ui; + TokenData m_selectedToken; + QString m_role, m_city, m_state, m_country, m_zip; +}; diff --git a/client/dialogs/SigningDialog.ui b/client/dialogs/SigningDialog.ui new file mode 100644 index 000000000..b5f6636d1 --- /dev/null +++ b/client/dialogs/SigningDialog.ui @@ -0,0 +1,710 @@ + + + + + SigningDialog + + + Qt::WindowModal + + + + 0 + 0 + 380 + 756 + + + + Sign document + + + QWidget { +color: #07142A; +font-family: Roboto, Helvetica; +font-size: 14px; +} +#SigningDialog { +background-color: #FFFFFF; +border-radius: 4px; +} +#signingTitle { +color: #003168; +font-size: 20px; +font-weight: 700; +} +#progressCode { +color: #003168; +font-size: 32px; +font-weight: 700; +} +QProgressBar { +background-color: #F3F5F7; +border: 1px solid #C4CBD8; +border-radius: 4px; +min-height: 6px; +max-height: 6px; +margin: 15px 5px; +} +QProgressBar::chunk { +border-radius: 3px; +background-color: #2F70B6; +} +#errorCodeMID, #errorPhone, #errorCodeSID, #errorCountry { +color: #AD2A45; +} +QLineEdit, QComboBox { +padding: 10px 14px; +border: 1px solid #C4CBD8; +border-radius: 4px; +background-color: #FFFFFF; +placeholder-text-color: #607496; +font-size: 16px; +} +QLineEdit[label="error"] { +border-color: #BE7884; +} +QComboBox QWidget#popup { +background-color: transparent; +} +QComboBox QWidget#content { +border: 1px solid #C4CBD8; +border-radius: 4px; +background-color: #FFFFFF; +} +QComboBox QPushButton { +margin: 3px; +padding: 0px 12px 0px 4px; +border: 0px; +color: #07142A; +text-align: left; +font-weight: normal; +font-size: 16px; +qproperty-iconSize: 14px 9px; +qproperty-layoutDirection: RightToLeft; +} +QComboBox QPushButton#selected { +qproperty-icon: url(:/images/arrow_up.svg); +} +QComboBox QPushButton:hover#selected, QComboBox QPushButton:focus#selected { +qproperty-icon: url(:/images/arrow_up_white.svg); +} +QComboBox::drop-down { +background-color: #FFFFFF; +width: 25px; +} +QComboBox::down-arrow { +image: url(:/images/arrow_down.svg); +} +QComboBox::down-arrow:on { +top: 1px; +left: 1px; +} +QCheckBox { +spacing: 8px; +border: none; +} +QCheckBox:disabled { +color: #C4CBD8; +} +QCheckBox::indicator { +width: 16px; +height: 16px; +} +QCheckBox::indicator:unchecked { +image: url(:/images/icon_checkbox.svg); +} +QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus { +image: url(:/images/icon_checkbox_active.svg); +} +QCheckBox::indicator:unchecked:disabled { +image: url(:/images/icon_checkbox_disabled.svg); +} +QCheckBox::indicator:checked { +image: url(:/images/icon_checkbox_check.svg); +} +QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus { +image: url(:/images/icon_checkbox_check_active.svg); +} +QCheckBox::indicator:checked:disabled { +image: url(:/images/icon_checkbox_check_disabled.svg); +} +QPushButton { +padding: 12px 12px; +border-radius: 4px; +border: 1px solid #AD2A45; +color: #AD2A45; +font-weight: 700; +} +QPushButton:hover, QPushButton:focus { +background-color: #F5EBED; +} +QPushButton:pressed { +background-color: #E1C1C6; +} +QPushButton:default { +color: #ffffff; +border-color: #2F70B6; +background-color: #2F70B6; +} +QPushButton:default:hover, QPushButton:default:focus { +border-color: #2B66A6; +background-color: #2B66A6; +} +QPushButton:default:pressed { +border-color: #215081; +background-color: #215081; +} +QPushButton:default:disabled { +border-color: #82A9D3; +background-color: #82A9D3; +} +QTabBar::tab { +padding: 11px 19px; +color: #003168; +border: none; +font-weight: 700; +background-color: #FFFFFF; +border: 1px solid #003168; +border-width: 1px 0px 1px 1px; +} +QTabBar::tab:first { +border-top-left-radius: 4px; +border-bottom-left-radius: 4px; +} +QTabBar::tab:last { +border-top-right-radius: 4px; +border-bottom-right-radius: 4px; +border-right-width: 1px; +} +QTabBar::tab:selected { +background-color: #003168; +color: #FFFFFF; +} +QTabBar::tab:hover:!selected { +background-color: #EDF1F6; +} + + + + 0 + + + QLayout::SetFixedSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 40 + + + 40 + + + 32 + + + 40 + + + 32 + + + + + + 300 + 0 + + + + Choose signing method + + + Qt::AlignCenter + + + + + + + 16 + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 8 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + 24 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + + + + 0 + 20 + + + + Country code and phone number + + + phoneNo + + + + + + + 37254321 + + + + + + + + 0 + 20 + + + + Qt::TabFocus + + + + + + + + + 6 + + + + + + 0 + 20 + + + + Personal code + + + idCodeMID + + + + + + + 47101010033 + + + + + + + + 0 + 20 + + + + Qt::TabFocus + + + + + + + + + + 0 + 17 + + + + Remember me + + + + + + + + + 24 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + + + + 0 + 20 + + + + Country + + + idCountry + + + + + + + + Estonia + + + + + Lithuania + + + + + Latvia + + + + + + + + + + 6 + + + + + + 0 + 20 + + + + Personal code + + + idCodeSID + + + + + + + 47101010033 + + + + + + + + 0 + 20 + + + + Qt::TabFocus + + + + + + + + + + 0 + 17 + + + + Remember me + + + + + + + + + + + + + 0 + + + + + PointingHandCursor + + + Cancel + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + PointingHandCursor + + + Sign + + + true + + + + + + + + + + + + + 24 + + + 40 + + + 32 + + + 40 + + + 32 + + + + + + 300 + 0 + + + + Control code: + + + Qt::AlignCenter + + + + + + + Qt::TabFocus + + + Qt::AlignCenter + + + + + + + Qt::AlignCenter + + + true + + + true + + + + + + + + 0 + 40 + + + + 75 + + + 0 + + + false + + + + + + + PointingHandCursor + + + Cancel + + + false + + + + + + + + + + + ShrinkingStack + QStackedWidget +
widgets/ShrinkingStack.h
+ 1 +
+ + ComboBox + QComboBox +
widgets/ComboBox.h
+
+ + LineEdit + QLineEdit +
widgets/LineEdit.h
+
+ + NoCardInfo + QWidget +
widgets/NoCardInfo.h
+ 1 +
+ + QTabBar + QWidget +
qtabbar.h
+
+
+ + +
diff --git a/client/dialogs/SmartIDDialog.cpp b/client/dialogs/SmartIDDialog.cpp deleted file mode 100644 index eea9c1251..000000000 --- a/client/dialogs/SmartIDDialog.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "SmartIDDialog.h" -#include "ui_SmartIDDialog.h" - -#include "IKValidator.h" -#include "Settings.h" -#include "effects/Overlay.h" - -SmartIDDialog::SmartIDDialog(QWidget *parent) - : QDialog(parent) - , ui(new Ui::SmartIDDialog) -{ - static const QString &EE = Settings::SMARTID_COUNTRY_LIST.first(); - new Overlay(this); - - ui->setupUi(this); - setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint); -#ifdef Q_OS_WIN - ui->buttonLayout->setDirection(QBoxLayout::RightToLeft); -#endif - - ui->idCode->setAttribute(Qt::WA_MacShowFocusRect, false); - ui->idCode->setFocus(); - ui->cbRemember->setAttribute(Qt::WA_MacShowFocusRect, false); - ui->errorCode->hide(); - - auto *ik = new NumberValidator(ui->idCode); - ui->idCode->setValidator(Settings::SMARTID_COUNTRY == EE ? ik : nullptr); - ui->idCode->setText(Settings::SMARTID_CODE); - for(int i = 0, count = Settings::SMARTID_COUNTRY_LIST.size(); i < count; ++i) - ui->idCountry->setItemData(i, Settings::SMARTID_COUNTRY_LIST[i]); - ui->idCountry->setCurrentIndex(ui->idCountry->findData(Settings::SMARTID_COUNTRY)); - ui->cbRemember->setChecked(Settings::SMARTID_REMEMBER); - auto saveSettings = [this]{ - bool checked = ui->cbRemember->isChecked(); - Settings::SMARTID_REMEMBER = checked; - Settings::SMARTID_CODE = checked ? idCode() : QString(); - Settings::SMARTID_COUNTRY = checked ? country() : EE; - }; - auto setError = [](LineEdit *input, QLabel *error, const QString &msg) { - input->setLabel(msg.isEmpty() ? QString() : QStringLiteral("error")); - error->setText(msg); - error->setHidden(msg.isEmpty()); - }; - connect(ui->idCode, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); - connect(ui->idCode, &QLineEdit::textEdited, this, saveSettings); - connect(ui->idCode, &QLineEdit::textEdited, ui->errorCode, [this, setError] { - setError(ui->idCode, ui->errorCode, {}); - }); - connect(ui->idCountry, &QComboBox::currentTextChanged, this, [this, ik, saveSettings] { - ui->idCode->setValidator(country() == EE ? ik : nullptr); - saveSettings(); - }); - connect(ui->cbRemember, &QCheckBox::clicked, this, saveSettings); - connect(ui->cancel, &QPushButton::clicked, this, &QDialog::reject); - connect(ui->sign, &QPushButton::clicked, this, [this, setError] { - if(ui->idCode->validator() && !IKValidator::isValid(idCode())) - setError(ui->idCode, ui->errorCode, tr("Personal code is not valid")); - else - { - setError(ui->idCode, ui->errorCode, {}); - accept(); - } - }); -} - -SmartIDDialog::~SmartIDDialog() -{ - delete ui; -} - -QString SmartIDDialog::country() const -{ - return ui->idCountry->currentData().toString(); -} - -QString SmartIDDialog::idCode() const -{ - return ui->idCode->text(); -} diff --git a/client/dialogs/SmartIDDialog.h b/client/dialogs/SmartIDDialog.h deleted file mode 100644 index 45cb090fe..000000000 --- a/client/dialogs/SmartIDDialog.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include - -namespace Ui { class SmartIDDialog; } - -class SmartIDDialog final : public QDialog -{ - Q_OBJECT - -public: - explicit SmartIDDialog(QWidget *parent = nullptr); - ~SmartIDDialog() final; - - QString country() const; - QString idCode() const; - -private: - Ui::SmartIDDialog *ui; -}; diff --git a/client/dialogs/SmartIDDialog.ui b/client/dialogs/SmartIDDialog.ui deleted file mode 100644 index 2afbc4495..000000000 --- a/client/dialogs/SmartIDDialog.ui +++ /dev/null @@ -1,355 +0,0 @@ - - - SmartIDDialog - - - Qt::WindowModal - - - - 0 - 0 - 430 - 455 - - - - Smart-ID - - - QWidget { -color: #07142A; -font-family: Roboto, Helvetica; -font-size: 14px; -} -#SmartIDDialog { -background-color: #FFFFFF; -border-radius: 4px; -} -#label { -color: #003168; -font-size: 20px; -font-weight: 700; -} -#errorCode, #errorCountry { -color: #AD2A45; -} -QLineEdit, QComboBox { -padding: 10px 14px; -border: 1px solid #C4CBD8; -border-radius: 4px; -background-color: #FFFFFF; -placeholder-text-color: #607496; -font-size: 16px; -} -QLineEdit[label="error"] { -border-color: #BE7884; -} -QComboBox QWidget#popup { -background-color: transparent; -} -QComboBox QWidget#content { -border: 1px solid #C4CBD8; -border-radius: 4px; -background-color: #FFFFFF; -} -QComboBox QPushButton { -margin: 3px; -padding: 0px 12px 0px 4px; -border: 0px; -color: #07142A; -text-align: left; -font-weight: normal; -font-size: 16px; -qproperty-iconSize: 14px 9px; -qproperty-layoutDirection: RightToLeft; -} -QComboBox QPushButton#selected { -qproperty-icon: url(:/images/arrow_up.svg); -} -QComboBox QPushButton:hover#selected, QComboBox QPushButton:focus#selected { -qproperty-icon: url(:/images/arrow_up_white.svg); -} -QComboBox::drop-down { -background-color: #FFFFFF; -width: 25px; -} -QComboBox::down-arrow { -image: url(:/images/arrow_down.svg); -} -QComboBox::down-arrow:on { -top: 1px; -left: 1px; -} -QCheckBox { -spacing: 8px; -border: none; /*Workaround for right padding*/ -} -QCheckBox:disabled { -color: #C4CBD8; -} -QCheckBox::indicator { -width: 16px; -height: 16px; -} -QCheckBox::indicator:unchecked { -image: url(:/images/icon_checkbox.svg); -} -QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus { -image: url(:/images/icon_checkbox_active.svg); -} -QCheckBox::indicator:unchecked:disabled { -image: url(:/images/icon_checkbox_disabled.svg); -} -QCheckBox::indicator:checked { -image: url(:/images/icon_checkbox_check.svg); -} -QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus { -image: url(:/images/icon_checkbox_check_active.svg); -} -QCheckBox::indicator:checked:disabled { -image: url(:/images/icon_checkbox_check_disabled.svg); -} -QPushButton { -padding: 12px 12px; -border-radius: 4px; -border: 1px solid #AD2A45; -color: #AD2A45; -font-weight: 700; -} -QPushButton:hover, QPushButton:focus { -background-color: #F5EBED; -} -QPushButton:pressed { -background-color: #E1C1C6; -} -QPushButton:default { -color: #ffffff; -border-color: #2F70B6; -background-color: #2F70B6; -} -QPushButton:default:hover, QPushButton:default:focus { -border-color: #2B66A6; -background-color: #2B66A6; -} -QPushButton:default:pressed { -border-color: #215081; -background-color: #215081; -} -QPushButton:default:disabled { -border-color: #82A9D3; -background-color: #82A9D3; -} - - - - 40 - - - QLayout::SetFixedSize - - - 40 - - - 32 - - - 40 - - - 32 - - - - - - 350 - 0 - - - - Qt::TabFocus - - - Enter your personal code to sign with Smart-ID - - - Qt::AlignCenter - - - true - - - - - - - 24 - - - - - 6 - - - - - - 0 - 20 - - - - Country - - - idCountry - - - - - - - - Estonia - - - - - Lithuania - - - - - Latvia - - - - - - - - - - 6 - - - - - - 0 - 20 - - - - Personal code - - - idCode - - - - - - - 47101010033 - - - - - - - - - - - 0 - 20 - - - - Qt::TabFocus - - - - - - - - - - 0 - 17 - - - - Remember me - - - - - - - - - 0 - - - - - PointingHandCursor - - - Cancel - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - PointingHandCursor - - - Sign - - - true - - - - - - - - - - ComboBox - QComboBox -
widgets/ComboBox.h
-
- - LineEdit - QLineEdit -
widgets/LineEdit.h
-
-
- - -
diff --git a/client/dialogs/MobileProgress.cpp b/client/signer/MobileProgress.cpp similarity index 85% rename from client/dialogs/MobileProgress.cpp rename to client/signer/MobileProgress.cpp index d78131636..8c3d9fafd 100644 --- a/client/dialogs/MobileProgress.cpp +++ b/client/signer/MobileProgress.cpp @@ -18,7 +18,6 @@ */ #include "MobileProgress.h" -#include "ui_MobileProgress.h" #include "Application.h" #include "CheckConnection.h" @@ -38,18 +37,27 @@ #include #include #include +#include +#include +#include +#include Q_LOGGING_CATEGORY(MIDLog,"RIA.MID") using namespace digidoc; -class MobileProgress::Private final: public QDialog, public Ui::MobileProgress +class MobileProgress::Private final: public QObject { Q_OBJECT public: - using QDialog::QDialog; - void reject() final { l.exit(QDialog::Rejected); } - QTimeLine *statusTimer{}; + explicit Private(const SigningProgressUI &pui, QObject *parent = nullptr) + : QObject(parent) + , ui(pui) + {} + + const SigningProgressUI &ui; + + QTimeLine *statusTimer {}; QNetworkAccessManager *manager {}; QNetworkRequest req; QString ssid, cell, sessionID; @@ -62,38 +70,25 @@ class MobileProgress::Private final: public QDialog, public Ui::MobileProgress QString URL = !UUID.isNull() && useCustomUUID ? Settings::MID_SK_URL : Settings::MID_PROXY_URL; }; -MobileProgress::MobileProgress(QWidget *parent) - : d(new Private(parent)) +MobileProgress::MobileProgress(const SigningProgressUI &pui) + : d(new Private(pui)) { const_cast(MIDLog()).setEnabled(QtDebugMsg, QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); - d->setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint); - d->setupUi(d); - d->code->setBuddy(d->signProgressBar); - d->code->clear(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) -const auto styleSheet = R"(QProgressBar { -background-color: #d3d3d3; -border-style: solid; -border-radius: 3px; -min-height: 6px; -max-height: 6px; -margin: 15px 5px; -} -QProgressBar::chunk { -border-style: solid; -border-radius: 3px; -background-color: #007aff; -})"; - d->signProgressBar->setStyleSheet(styleSheet); -#endif - QObject::connect(d->cancel, &QPushButton::clicked, d, &QDialog::reject); + d->ui.bar->setMaximum(75); + d->ui.bar->setValue(0); + d->ui.code->setBuddy(d->ui.bar); + d->ui.code->clear(); + d->ui.info->clear(); + QObject::connect(pui.cancel, &QAbstractButton::clicked, [this]() { + d->l.exit(QDialog::Rejected); + }); - d->statusTimer = new QTimeLine(d->signProgressBar->maximum() * 1000, d); + d->statusTimer = new QTimeLine(d->ui.bar->maximum() * 1000, d); d->statusTimer->setEasingCurve(QEasingCurve::Linear); - d->statusTimer->setFrameRange(d->signProgressBar->minimum(), d->signProgressBar->maximum()); - QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->signProgressBar, &QProgressBar::setValue); - QObject::connect(d->statusTimer, &QTimeLine::finished, d, &QDialog::reject); + d->statusTimer->setFrameRange(d->ui.bar->minimum(), d->ui.bar->maximum()); + QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->ui.bar, &QProgressBar::setValue); + QObject::connect(d->statusTimer, &QTimeLine::finished, pui.cancel, &QAbstractButton::click); d->req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); d->manager = CheckConnection::setupNAM(d->req); @@ -103,8 +98,7 @@ background-color: #007aff; auto returnError = [=, this](const QString &err, const QString &details = {}) { qCWarning(MIDLog) << err; d->statusTimer->stop(); - d->hide(); - auto *dlg = WarningDialog::create(d->parentWidget())->withText(err)->withDetails(details) + auto *dlg = WarningDialog::create(d->ui.errorParent)->withText(err)->withDetails(details) ->withTitle(QCoreApplication::translate("DigiDoc", "Failed to sign container")); QObject::connect(dlg, &WarningDialog::finished, &d->l, &QEventLoop::exit); dlg->open(); @@ -169,7 +163,6 @@ background-color: #007aff; try { QByteArray b64 = QByteArray::fromBase64(result.value(QLatin1String("cert")).toString().toUtf8()); d->cert = X509Cert((const unsigned char*)b64.constData(), size_t(b64.size()), X509Cert::Der); - d->hide(); d->l.exit(QDialog::Accepted); } catch(const Exception &e) { returnError(tr("Failed to parse certificate: ") + QString::fromStdString(e.msg())); @@ -186,7 +179,6 @@ background-color: #007aff; QByteArray b64 = QByteArray::fromBase64( result.value(QLatin1String("signature")).toObject().value(QLatin1String("value")).toString().toUtf8()); d->signature.assign(b64.cbegin(), b64.cend()); - d->hide(); d->l.exit(QDialog::Accepted); } else if(endResult == QLatin1String("NOT_MID_CLIENT") || endResult == QLatin1String("NOT_FOUND") || endResult == QLatin1String("NOT_ACTIVE")) @@ -227,7 +219,7 @@ bool MobileProgress::init(const QString &ssid, const QString &cell) { if(!d->UUID.isEmpty() && QUuid(d->UUID).isNull()) { - WarningDialog::create(d->parentWidget()) + WarningDialog::create(d->ui.errorParent) ->withText(tr("Failed to send request. Check your %1 service access settings.").arg(tr("mobile-ID"))) ->withTitle(QCoreApplication::translate("DigiDoc", "Failed to sign container")) ->open(); @@ -235,8 +227,8 @@ bool MobileProgress::init(const QString &ssid, const QString &cell) } d->ssid = ssid; d->cell = '+' + cell; - d->info->setText(tr("Signing in process")); - d->code->setAccessibleName(d->info->text()); + d->ui.info->setText(tr("Signing in process")); + d->ui.code->setAccessibleName(d->ui.info->text()); d->sessionID.clear(); QByteArray data = QJsonDocument(QJsonObject::fromVariantHash(QVariantHash{ {"relyingPartyUUID", d->UUID.isEmpty() ? QStringLiteral("00000000-0000-0000-0000-000000000000") : d->UUID}, @@ -268,9 +260,9 @@ std::vector MobileProgress::sign(const std::string &method, const else throw Exception(__FILE__, __LINE__, "Unsupported digest method"); - d->code->setText(QStringLiteral("%1").arg((digest.front() >> 2) << 7 | (digest.back() & 0x7F), 4, 10, QChar('0'))); - d->info->setText(tr("Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code.")); - d->code->setAccessibleName(QStringLiteral("%1 %2. %3").arg(d->label->text(), d->code->text(), d->info->text())); + d->ui.code->setText(QStringLiteral("%1").arg((digest.front() >> 2) << 7 | (digest.back() & 0x7F), 4, 10, QChar('0'))); + d->ui.info->setText(tr("Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code.")); + d->ui.code->setAccessibleName(QStringLiteral("%1 %2. %3").arg(d->ui.label->text(), d->ui.code->text(), d->ui.info->text())); QByteArray data = QJsonDocument(QJsonObject::fromVariantHash({ {"relyingPartyUUID", d->UUID.isEmpty() ? QStringLiteral("00000000-0000-0000-0000-000000000000") : d->UUID}, @@ -289,9 +281,6 @@ std::vector MobileProgress::sign(const std::string &method, const qCDebug(MIDLog).noquote() << d->req.url() << data; d->manager->post(d->req, data); d->statusTimer->start(); - d->adjustSize(); - WaitDialogHider hider; - d->show(); switch(d->l.exec()) { case QDialog::Accepted: return d->signature; diff --git a/client/dialogs/MobileProgress.h b/client/signer/MobileProgress.h similarity index 81% rename from client/dialogs/MobileProgress.h rename to client/signer/MobileProgress.h index d8db2e232..00c6a5bbb 100644 --- a/client/dialogs/MobileProgress.h +++ b/client/signer/MobileProgress.h @@ -23,13 +23,25 @@ #include +class QAbstractButton; +class QLabel; +class QProgressBar; class QWidget; +struct SigningProgressUI { + QLabel *info {}; + QLabel *code {}; + QLabel *label {}; + QProgressBar *bar {}; + QAbstractButton *cancel {}; + QWidget *errorParent {}; +}; + class MobileProgress final: public digidoc::Signer { Q_DECLARE_TR_FUNCTIONS(MobileProgress) public: - explicit MobileProgress(QWidget *parent = nullptr); + explicit MobileProgress(const SigningProgressUI &pui); ~MobileProgress() final; bool init(const QString &ssid, const QString &cell); diff --git a/client/QSigner.cpp b/client/signer/QSigner.cpp similarity index 100% rename from client/QSigner.cpp rename to client/signer/QSigner.cpp diff --git a/client/QSigner.h b/client/signer/QSigner.h similarity index 100% rename from client/QSigner.h rename to client/signer/QSigner.h diff --git a/client/dialogs/SmartIDProgress.cpp b/client/signer/SmartIDProgress.cpp similarity index 83% rename from client/dialogs/SmartIDProgress.cpp rename to client/signer/SmartIDProgress.cpp index 97afb60fd..cb4748e1e 100644 --- a/client/dialogs/SmartIDProgress.cpp +++ b/client/signer/SmartIDProgress.cpp @@ -18,7 +18,6 @@ */ #include "SmartIDProgress.h" -#include "ui_MobileProgress.h" #include "Application.h" #include "CheckConnection.h" @@ -39,26 +38,27 @@ #include #include #include - -#include +#include +#include +#include +#include Q_LOGGING_CATEGORY(SIDLog,"RIA.SmartID") using namespace digidoc; -class SmartIDProgress::Private final: public QDialog, public Ui::MobileProgress +class SmartIDProgress::Private final: public QObject { Q_OBJECT public: - using QDialog::QDialog; - void reject() final { l.exit(QDialog::Rejected); } - void setVisible(bool visible) final { - if(visible && !hider) hider = std::make_unique(); - QDialog::setVisible(visible); - if(!visible && hider) hider.reset(); - } - QTimer *timer{}; - QTimeLine *statusTimer{}; + explicit Private(const SigningProgressUI &pui, QObject *parent = nullptr) + : QObject(parent) + , ui(pui) + {} + + const SigningProgressUI &ui; + + QTimeLine *statusTimer {}; QNetworkAccessManager *manager {}; QNetworkRequest req; QString documentNumber, sessionID, fileName; @@ -69,43 +69,26 @@ class SmartIDProgress::Private final: public QDialog, public Ui::MobileProgress QString UUID = useCustomUUID ? Settings::SID_UUID : QString(); QString NAME = Settings::SID_NAME; QString URL = !UUID.isNull() && useCustomUUID ? Settings::SID_SK_URL : Settings::SID_PROXY_URL; - std::unique_ptr hider; }; - - -SmartIDProgress::SmartIDProgress(QWidget *parent) - : d(new Private(parent)) +SmartIDProgress::SmartIDProgress(const SigningProgressUI &pui) + : d(new Private(pui)) { const_cast(SIDLog()).setEnabled(QtDebugMsg, QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); - d->setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint); - d->setupUi(d); - d->signProgressBar->setMaximum(100); - d->code->setBuddy(d->signProgressBar); - d->code->clear(); -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) -const auto styleSheet = R"(QProgressBar { -background-color: #d3d3d3;; -border-style: solid; -border-radius: 3px; -min-height: 6px; -max-height: 6px; -margin: 15px 5px; -} -QProgressBar::chunk { -border-style: solid; -border-radius: 3px; -background-color: #007aff; -})"; - d->signProgressBar->setStyleSheet(styleSheet); -#endif - QObject::connect(d->cancel, &QPushButton::clicked, d, &QDialog::reject); + d->ui.bar->setMaximum(100); + d->ui.bar->setValue(0); + d->ui.code->setBuddy(d->ui.bar); + d->ui.code->clear(); + d->ui.info->clear(); + QObject::connect(pui.cancel, &QAbstractButton::clicked, [this] { + d->l.exit(QDialog::Rejected); + }); - d->statusTimer = new QTimeLine(d->signProgressBar->maximum() * 1000, d); + d->statusTimer = new QTimeLine(d->ui.bar->maximum() * 1000, d); d->statusTimer->setEasingCurve(QEasingCurve::Linear); - d->statusTimer->setFrameRange(d->signProgressBar->minimum(), d->signProgressBar->maximum()); - QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->signProgressBar, &QProgressBar::setValue); + d->statusTimer->setFrameRange(d->ui.bar->minimum(), d->ui.bar->maximum()); + QObject::connect(d->statusTimer, &QTimeLine::frameChanged, d->ui.bar, &QProgressBar::setValue); d->req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); d->manager = CheckConnection::setupNAM(d->req); @@ -115,10 +98,7 @@ background-color: #007aff; auto returnError = [=, this](const QString &err, const QString &details = {}) { qCWarning(SIDLog) << err; d->statusTimer->stop(); - delete d->timer; - d->timer = nullptr; - d->hide(); - auto *dlg = WarningDialog::create(d->parentWidget())->withText(err)->withDetails(details) + auto *dlg = WarningDialog::create(d->ui.errorParent)->withText(err)->withDetails(details) ->withTitle(QCoreApplication::translate("DigiDoc", "Failed to sign container")); QObject::connect(dlg, &WarningDialog::finished, &d->l, &QEventLoop::exit); dlg->open(); @@ -203,7 +183,6 @@ background-color: #007aff; QByteArray b64 = QByteArray::fromBase64( result.value(QLatin1String("signature")).toObject().value(QLatin1String("value")).toString().toUtf8()); d->signature.assign(b64.cbegin(), b64.cend()); - d->hide(); d->l.exit(QDialog::Accepted); } else if(result.contains(QLatin1String("cert"))) @@ -212,7 +191,6 @@ background-color: #007aff; QByteArray b64 = QByteArray::fromBase64( result.value(QLatin1String("cert")).toObject().value(QLatin1String("value")).toString().toUtf8()); d->cert = X509Cert((const unsigned char*)b64.constData(), size_t(b64.size()), X509Cert::Der); - d->hide(); d->l.exit(QDialog::Accepted); } catch(const Exception &e) { returnError(tr("Failed to parse certificate: ") + QString::fromStdString(e.msg())); @@ -240,7 +218,7 @@ bool SmartIDProgress::init(const QString &country, const QString &idCode, const { if(!d->UUID.isEmpty() && QUuid(d->UUID).isNull()) { - WarningDialog::create(d->parentWidget()) + WarningDialog::create(d->ui.errorParent) ->withText(tr("Failed to send request. Check your %1 service access settings.").arg(tr("Smart-ID"))) ->withTitle(QCoreApplication::translate("DigiDoc", "Failed to sign container")) ->open(); @@ -262,15 +240,9 @@ bool SmartIDProgress::init(const QString &country, const QString &idCode, const d->req.setUrl(QUrl(QStringLiteral("%1/certificatechoice/etsi/PNO%2-%3").arg(d->URL, country, idCode))); qCDebug(SIDLog).noquote() << d->req.url() << data; d->manager->post(d->req, data); - d->info->setText(tr("Open the Smart-ID application on your smart device and confirm device for signing.")); - d->code->setAccessibleName(d->info->text()); + d->ui.info->setText(tr("Open the Smart-ID application on your smart device and confirm device for signing.")); + d->ui.code->setAccessibleName(d->ui.info->text()); d->statusTimer->start(); - d->adjustSize(); - d->timer = new QTimer(d); - d->timer->setSingleShot(true); - QObject::connect(d->timer, &QTimer::timeout, d, &SmartIDProgress::Private::show); - using namespace std::chrono; - d->timer->start(3s); return d->l.exec() == QDialog::Accepted; } @@ -291,9 +263,9 @@ std::vector SmartIDProgress::sign(const std::string &method, cons QByteArray codeDiest = QCryptographicHash::hash(QByteArray::fromRawData((const char*)digest.data(), int(digest.size())), QCryptographicHash::Sha256); uint code = codeDiest.right(2).toHex().toUInt(nullptr, 16) % 10000; - d->code->setText(QStringLiteral("%1").arg(code, 4, 10, QChar('0'))); - d->info->setText(tr("Make sure control code matches with one in phone screen and enter Smart-ID PIN2-code.")); - d->code->setAccessibleName(QStringLiteral("%1 %2. %3").arg(d->label->text(), d->code->text(), d->info->text())); + d->ui.code->setText(QStringLiteral("%1").arg(code, 4, 10, QChar('0'))); + d->ui.info->setText(tr("Make sure control code matches with one in phone screen and enter Smart-ID PIN2-code.")); + d->ui.code->setAccessibleName(QStringLiteral("%1 %2. %3").arg(d->ui.label->text(), d->ui.code->text(), d->ui.info->text())); QJsonObject req{ {"relyingPartyUUID", (d->UUID.isEmpty() ? QStringLiteral("00000000-0000-0000-0000-000000000000") : d->UUID)}, @@ -313,8 +285,6 @@ std::vector SmartIDProgress::sign(const std::string &method, cons qCDebug(SIDLog).noquote() << d->req.url() << data; d->manager->post(d->req, data); d->statusTimer->start(); - d->adjustSize(); - d->show(); switch(d->l.exec()) { case QDialog::Accepted: return d->signature; diff --git a/client/dialogs/SmartIDProgress.h b/client/signer/SmartIDProgress.h similarity index 93% rename from client/dialogs/SmartIDProgress.h rename to client/signer/SmartIDProgress.h index 18202fd5e..80d371aec 100644 --- a/client/dialogs/SmartIDProgress.h +++ b/client/signer/SmartIDProgress.h @@ -19,17 +19,17 @@ #pragma once +#include "MobileProgress.h" + #include #include -class QWidget; - class SmartIDProgress final: public digidoc::Signer { Q_DECLARE_TR_FUNCTIONS(MobileProgress) public: - explicit SmartIDProgress(QWidget *parent = nullptr); + explicit SmartIDProgress(const SigningProgressUI &pui); ~SmartIDProgress() final; digidoc::X509Cert cert() const final; bool init(const QString &country, const QString &idCode, const QString &fileName); diff --git a/client/translations/en.ts b/client/translations/en.ts index 380932ab7..8f3767aaa 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -256,6 +256,17 @@ Added file(s) exceeds the maximum size limit of the container (∼120MB). <a href='https://www.id.ee/en/article/encrypting-large-120-mb-files/'>Read more about it</a> + + CardListItem + + Issuer + Issuer + + + Valid to + Valid to + + CardWidget @@ -464,8 +475,8 @@ Check proxy username and password - Cannot connect to certificate status service! - Cannot connect to certificate status service! + Check internet connection + Check internet connection @@ -485,10 +496,6 @@ Container: Container: - - The document has already been signed by you - The document has already been signed by you - DigiDoc4 Client DigiDoc4 Client @@ -561,10 +568,6 @@ Sign Sign - - Continue signing - Continue signing - Encrypting Encrypting @@ -1355,29 +1358,6 @@ LDAP server is unavailable. MainAction - - Token selection - accessible - Token selection - - - Sign with -Mobile-ID - Sign with -Mobile-ID - - - Sign with -Smart-ID - Sign with -Smart-ID - - - Sign with -E-Seal - Sign with -E-Seal - Encrypt Encrypt @@ -1387,10 +1367,8 @@ E-Seal Decrypt - Decrypt with -ID-Card - Decrypt with -ID-Card + Sign + Sign Encrypt @@ -1398,12 +1376,6 @@ long-term Encrypt long-term - - Sign with -ID-Card - Sign with -ID-Card - MainWindow @@ -1491,10 +1463,6 @@ ID-Card Signing Signing - - Check internet connection - Check internet connection - Load file from disk for signing or verifying accessible @@ -1569,49 +1537,6 @@ ID-Card Continue - - MobileDialog - - Enter your phone number to sign with mobile-ID - <b>Enter your phone number to sign<br/>with mobile-ID</b> - - - Remember me - Remember me - - - Personal code is not valid - Personal code is not valid - - - Phone number is not entered - Phone number is not entered - - - Cancel - Cancel - - - Sign - Sign - - - Country code and phone number - Country code and phone number - - - Personal code - Personal code - - - Invalid country code - Invalid country code - - - Mobile-ID - Mobile-ID - - MobileProgress @@ -1638,22 +1563,10 @@ ID-Card Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code. Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code. - - Control code: - Control code: - SSL handshake failed. Check the proxy settings of your computer or software upgrades. SSL handshake failed. Check the proxy settings of your computer or software upgrades. - - %v sec - %v sec - - - Cancel - Cancel - %1 service has encountered technical errors. Please try again later. %1 service has encountered technical errors. Please try again later. @@ -2789,22 +2702,26 @@ Additional licenses and components - SmartIDDialog + SigningDialog - Personal code - Personal code + Sign document + Sign document - Enter your personal code to sign with Smart-ID - Enter your personal code to sign with Smart-ID + Choose signing method + Choose signing method - Remember me - Remember me + Country code and phone number + Country code and phone number - Personal code is not valid - Personal code is not valid + Personal code + Personal code + + + Remember me + Remember me Country @@ -2830,6 +2747,42 @@ Additional licenses and components Sign Sign + + Control code: + Control code: + + + ID-card + ID-card + + + Mobile-ID + Mobile-ID + + + Smart-ID + Smart-ID + + + Personal code is not valid + Personal code is not valid + + + Phone number is not entered + Phone number is not entered + + + Invalid country code + Invalid country code + + + The document has already been signed by you + The document has already been signed by you + + + Continue signing + Continue signing + SslCertificate diff --git a/client/translations/et.ts b/client/translations/et.ts index 990f151bb..2e0dc0a67 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -256,6 +256,17 @@ Lisatud fail(id) ületab turvaümbriku maksimaalset suurust (~120MB). <a href='https://www.id.ee/artikkel/suuremahuliste-120-mb-failide-krupteerimine/'>Loe täpsemalt siit</a> + + CardListItem + + Issuer + Väljaandja + + + Valid to + Kehtib kuni + + CardWidget @@ -464,8 +475,8 @@ Kontrolli proksi kasutajanime ja parooli - Cannot connect to certificate status service! - Kehtivuskinnitusteenus ei ole kättesaadav! + Check internet connection + Kontrolli internetiühendust @@ -485,10 +496,6 @@ Container: Ümbrik: - - The document has already been signed by you - Dokument on Sinu poolt juba allkirjastatud - DigiDoc4 Client DigiDoc4 klient @@ -561,10 +568,6 @@ Sign Allkirjasta - - Continue signing - Jätka allkirjastamisega - Encrypting Krüpteerin @@ -1355,29 +1358,6 @@ LDAP serveriga ei saa ühendust. MainAction - - Token selection - accessible - Vali vahend - - - Sign with -Mobile-ID - Allkirjasta -Mobiil-ID’ga - - - Sign with -Smart-ID - Allkirjasta -Smart-ID’ga - - - Sign with -E-Seal - Allkirjasta -E-templiga - Encrypt Krüpteeri @@ -1387,10 +1367,8 @@ E-templiga Dekrüpteeri - Decrypt with -ID-Card - Dekrüpteeri -ID-kaardiga + Sign + Allkirjasta Encrypt @@ -1398,12 +1376,6 @@ long-term Krüpteeri säilitamiseks - - Sign with -ID-Card - Allkirjasta -ID-kaardiga - MainWindow @@ -1491,10 +1463,6 @@ ID-kaardiga Signing Allkirjastamine - - Check internet connection - Kontrolli internetiühendust - Load file from disk for signing or verifying accessible @@ -1569,49 +1537,6 @@ ID-kaardiga Edasi - - MobileDialog - - Enter your phone number to sign with mobile-ID - <b>Sisesta oma telefoninumber<br/>mobiil-IDga allkirjastamiseks</b> - - - Remember me - Pea mind meeles - - - Personal code is not valid - Isikukood pole kehtiv - - - Phone number is not entered - Telefoninumber pole sisestatud - - - Cancel - Katkesta - - - Sign - Allkirjastan - - - Country code and phone number - Riigikood ja telefoninumber - - - Personal code - Isikukood - - - Invalid country code - Vigane riigikood - - - Mobile-ID - Mobiil-ID - - MobileProgress @@ -1638,22 +1563,10 @@ ID-kaardiga Make sure control code matches with one in phone screen and enter mobile-ID PIN2-code. Veendu kontrollkoodi õigsuses ja sisesta telefonil mobiil-ID PIN2-kood. - - Control code: - Kontrollkood: - SSL handshake failed. Check the proxy settings of your computer or software upgrades. SSL ühenduskanali loomine ebaõnnestus. Kontrolli arvuti puhverserveri seadeid või tarkvara uuendusi. - - %v sec - %v sek - - - Cancel - Katkesta - %1 service has encountered technical errors. Please try again later. %1 teenuses esinevad tehnilised tõrked. Palun proovi mõne aja pärast uuesti. @@ -2789,22 +2702,26 @@ Täiendavad litsentsid ja komponendid - SmartIDDialog + SigningDialog - Personal code - Isikukood + Sign document + Allkirjasta dokument - Enter your personal code to sign with Smart-ID - Sisesta oma isikukood Smart-IDga allkirjastamiseks + Choose signing method + Vali allkirjastamise meetod - Remember me - Pea mind meeles + Country code and phone number + Riigikood ja telefoninumber - Personal code is not valid - Isikukood pole kehtiv + Personal code + Isikukood + + + Remember me + Pea mind meeles Country @@ -2828,7 +2745,43 @@ Täiendavad litsentsid ja komponendid Sign - Allkirjastan + Allkirjasta + + + Control code: + Kontrollkood: + + + ID-card + ID-kaart + + + Mobile-ID + Mobiil-ID + + + Smart-ID + Smart-ID + + + Personal code is not valid + Isikukood pole kehtiv + + + Phone number is not entered + Telefoninumber pole sisestatud + + + Invalid country code + Vigane riigikood + + + The document has already been signed by you + Dokument on Sinu poolt juba allkirjastatud + + + Continue signing + Jätka allkirjastamisega diff --git a/client/widgets/CardListItem.cpp b/client/widgets/CardListItem.cpp new file mode 100644 index 000000000..355c4d9d4 --- /dev/null +++ b/client/widgets/CardListItem.cpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "CardListItem.h" +#include "ui_CardListItem.h" + +#include "SslCertificate.h" + +#include +#include +#include + +CardListItem::CardListItem(QWidget *parent) + : QAbstractButton(parent) + , ui(new Ui::CardListItem) +{ + ui->setupUi(this); + ui->cardIcon->load(QStringLiteral(":/images/icon_Minu_eID_hover.svg")); + setAutoExclusive(true); + connect(this, &QAbstractButton::toggled, this, [this](bool checked) { + if(checked) + emit selected(t); + }); +} + +CardListItem::~CardListItem() +{ + delete ui; +} + +TokenData CardListItem::token() const +{ + return t; +} + +void CardListItem::paintEvent(QPaintEvent*) +{ + QStyleOptionButton opt; + opt.initFrom(this); + if(isChecked()) + opt.state |= QStyle::State_On; + if(isDown()) + opt.state |= QStyle::State_Sunken; + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void CardListItem::changeEvent(QEvent *ev) +{ + QAbstractButton::changeEvent(ev); + if(ev->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + update(t, isChecked(), isCheckable()); + } +} + +void CardListItem::update(const TokenData &token, bool selected, bool usable) +{ + t = token; + SslCertificate c(t.cert()); + QString name = !c.subjectInfo("GN").isEmpty() || !c.subjectInfo("SN").isEmpty() ? + c.toString(QStringLiteral("GN SN")) : c.toString(QStringLiteral("CN")); + ui->cardName->setText(QStringLiteral("%1, %2").arg(name, c.personalCode()).toHtmlEscaped()); + ui->cardName->setAccessibleName(ui->cardName->text().toLower()); + ui->cardIssuer->setText(QStringLiteral("%1: %2").arg(tr("Issuer"), c.issuerInfo(QSslCertificate::CommonName))); + ui->cardValidUntil->setText(QStringLiteral("%1: %2").arg(tr("Valid to"), + c.expiryDate().toLocalTime().toString(QStringLiteral("dd.MM.yyyy")))); + + qint64 leftDays = std::max(0, QDateTime::currentDateTime().daysTo(c.expiryDate().toLocalTime())); + if(!usable || !c.isValid()) + ui->cardValidUntil->setLabel(QStringLiteral("error")); + else if(leftDays <= 105) + ui->cardValidUntil->setLabel(QStringLiteral("warning")); + else + ui->cardValidUntil->setLabel(QString()); + + setCheckable(usable); + setEnabled(usable); + setCursor(usable ? Qt::PointingHandCursor : Qt::ArrowCursor); + setChecked(usable && selected); +} diff --git a/client/widgets/CardListItem.h b/client/widgets/CardListItem.h new file mode 100644 index 000000000..757a84d0f --- /dev/null +++ b/client/widgets/CardListItem.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "TokenData.h" + +#include + +namespace Ui { class CardListItem; } + +class CardListItem final: public QAbstractButton +{ + Q_OBJECT + +public: + explicit CardListItem(QWidget *parent = nullptr); + ~CardListItem() final; + + TokenData token() const; + void update(const TokenData &token, bool selected, bool usable); + +signals: + void selected(const TokenData &token); + +protected: + void paintEvent(QPaintEvent *ev) final; + void changeEvent(QEvent *ev) final; + +private: + Ui::CardListItem *ui; + TokenData t; +}; diff --git a/client/widgets/CardListItem.ui b/client/widgets/CardListItem.ui new file mode 100644 index 000000000..4a64c6431 --- /dev/null +++ b/client/widgets/CardListItem.ui @@ -0,0 +1,153 @@ + + + + + CardListItem + + + + 0 + 0 + 420 + 114 + + + + #CardListItem { +border: 1px solid #E7EAEF; +border-radius: 5px; +} +#CardListItem:hover, #CardListItem:focus { +border-color: #EAF1F8; +background-color: #EAF1F8; +} +#CardListItem:checked { +border-color: #003168; +background-color: #EAF1F8; +} +#cardName { +color: #07142A; +font-family: Roboto, Helvetica; +font-size: 14px; +font-weight: 700; +} +#cardIssuer { +color: #07142A; +font-family: Roboto, Helvetica; +font-size: 12px; +font-weight: 400; +} +#cardValidUntil { +color: #091A36; +font-family: Roboto, Helvetica; +font-size: 12px; +font-weight: 400; +padding: 2px 8px; +border-radius: 8px; +background-color: #F3F5F7; +} +#cardValidUntil[label="error"] { +color: #AD2A45; +background-color: #F5EBED; +} +#cardValidUntil[label="warning"] { +color: #83612D; +background-color: #FDF7EE; +} +#cardValidUntil[label="good"] { +color: #1A641B; +background-color: #EAF8EA; +} + + + + 16 + + + 16 + + + 16 + + + 16 + + + 11 + + + 2 + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + Qt::FocusPolicy::TabFocus + + + MARI MAASIKAS, 48405050123 + + + Qt::NoTextInteraction + + + + + + + Qt::FocusPolicy::TabFocus + + + Issuer: ESTEID-SK 2023 + + + Qt::NoTextInteraction + + + + + + + Qt::FocusPolicy::TabFocus + + + Valid to: 02.02.2030 + + + Qt::NoTextInteraction + + + + + + + + Label + QLabel +
widgets/Label.h
+
+ + QSvgWidget + QWidget +
QSvgWidget
+ 1 +
+
+ + +
diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 6b5d36628..96c9aaa3d 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -30,9 +30,7 @@ #include "TokenData.h" #include "dialogs/AddRecipients.h" #include "dialogs/FileDialog.h" -#include "dialogs/MobileDialog.h" #include "dialogs/PasswordDialog.h" -#include "dialogs/SmartIDDialog.h" #include "dialogs/WarningDialog.h" #include "widgets/AddressItem.h" #include "widgets/MainAction.h" @@ -67,7 +65,7 @@ ContainerPage::ContainerPage(QWidget *parent) connect(btn, &QAbstractButton::clicked, this, [this,code] { emit action(code); }); }; - connect(mainAction, &MainAction::action, this, &ContainerPage::handleAction); + connect(mainAction, &MainAction::action, this, &ContainerPage::action); connect(ui->cancel, &QPushButton::clicked, this, [this] { window()->setWindowFilePath({}); window()->setWindowTitle(tr("DigiDoc4 Client")); @@ -105,10 +103,7 @@ ContainerPage::~ContainerPage() void ContainerPage::cardChanged(const SslCertificate &cert, bool isBlocked) { emit ui->rightPane->idChanged(cert); - isSeal = cert.type() & SslCertificate::TempelType; - isExpired = !cert.isValid(); this->isBlocked = isBlocked; - idCode = cert.personalCode(); emit certChanged(cert); } @@ -124,11 +119,6 @@ void ContainerPage::clear(int code) emit action(code); } -void ContainerPage::clearPopups() -{ - mainAction->hideDropdown(); -} - void ContainerPage::elideFileName() { ui->containerFile->setText(QStringLiteral("%1") @@ -150,52 +140,6 @@ bool ContainerPage::eventFilter(QObject *o, QEvent *e) return QWidget::eventFilter(o, e); } -void ContainerPage::handleAction(int type) -{ - QString code; - QString info2; - switch(type) - { - case SignatureAdd: - case SignatureToken: - code = idCode; - break; - case SignatureMobile: - { - MobileDialog dlg(this); - if(dlg.exec() != QDialog::Accepted) - return; - code = dlg.idCode(); - info2 = dlg.phoneNo(); - break; - } - case SignatureSmartID: - { - SmartIDDialog dlg(this); - if(dlg.exec() != QDialog::Accepted) - return; - code = dlg.idCode(); - info2 = dlg.country(); - break; - } - default: - emit action(type, code, info2); - return; - } - if(auto items = ui->rightPane->findChildren(); - std::any_of(items.cbegin(), items.cend(), [code](auto *signatureItem) { - return signatureItem->isSelfSigned(code); - })) - { - auto *dlg = WarningDialog::create(this) - ->withTitle(tr("The document has already been signed by you")) - ->addButton(tr("Continue signing"), QMessageBox::Ok); - if(dlg->exec() != QMessageBox::Ok) - return; - } - emit action(type, code, info2); -} - void ContainerPage::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) @@ -212,7 +156,7 @@ void ContainerPage::decrypt(CryptoDoc *container, const libcdoc::Lock *lock, con if (!container->decrypt(lock, secret)) return; transition(container, QSslCertificate{}); - emit action(DecryptContainerSuccess, {}, {}); + emit action(DecryptContainerSuccess); } template @@ -245,7 +189,7 @@ void ContainerPage::encrypt(CryptoDoc *container, bool longTerm) if(!container->encrypt(container->fileName(), {}, {})) return; transition(container, qApp->cryptoManager()->tokenauth().cert()); - emit action(EncryptContainerSuccess, {}, {}); + emit action(EncryptContainerSuccess); return; } @@ -257,7 +201,7 @@ void ContainerPage::encrypt(CryptoDoc *container, bool longTerm) if(!container->encrypt(container->fileName(), p.label(), p.secret())) return; transition(container, QSslCertificate{}); - emit action(EncryptContainerSuccess, {}, {}); + emit action(EncryptContainerSuccess); } void ContainerPage::setHeader(const QString &file) @@ -268,33 +212,17 @@ void ContainerPage::setHeader(const QString &file) elideFileName(); } -void ContainerPage::showMainAction(const QList &actions) -{ - mainAction->showActions(actions); - bool isSignCard = actions.contains(SignatureAdd) || actions.contains(SignatureToken); - bool isSignMobile = !isSignCard && (actions.contains(SignatureMobile) || actions.contains(SignatureSmartID)); - bool isEncrypt = actions.contains(EncryptContainer) && !ui->rightPane->findChildren().isEmpty(); - bool isEncryptLT = actions.contains(EncryptLT); - bool isDecrypt = !isBlocked && (actions.contains(DecryptContainer) || actions.contains(DecryptToken)); - mainAction->setButtonEnabled(isSupported && - (isEncrypt || isEncryptLT || isDecrypt || isSignMobile || (isSignCard && !isBlocked && !isExpired))); - ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); - ui->navigationArea->layout()->invalidate(); -} - void ContainerPage::showEncryptAction(CryptoDoc *container) { if (!container->keys().empty()) { - mainAction->showActions({EncryptContainer}); - mainAction->setButtonEnabled(true); + mainAction->showAction(EncryptContainer); + mainAction->setEnabled(true); + } else if (container->supportsSymmetricKeys()) { + mainAction->showAction(EncryptLT); + mainAction->setEnabled(true); } else { - if (container->supportsSymmetricKeys()) { - mainAction->showActions({EncryptLT}); - mainAction->setButtonEnabled(true); - } else { - mainAction->showActions({EncryptContainer}); - mainAction->setButtonEnabled(false); - } + mainAction->showAction(EncryptContainer); + mainAction->setEnabled(false); } ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); ui->navigationArea->layout()->invalidate(); @@ -306,14 +234,14 @@ void ContainerPage::showSigningButton() { mainAction->hide(); ui->mainActionSpacer->changeSize(1, 20, QSizePolicy::Fixed); - ui->navigationArea->layout()->invalidate(); } - else if(idCode.isEmpty()) - showMainAction({ SignatureMobile, SignatureSmartID }); - else if(isSeal) - showMainAction({ SignatureToken, SignatureMobile, SignatureSmartID }); else - showMainAction({ SignatureAdd, SignatureMobile, SignatureSmartID }); + { + mainAction->showAction(SignatureAdd); + mainAction->setEnabled(isSupported); + ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); + } + ui->navigationArea->layout()->invalidate(); } void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert) @@ -373,7 +301,6 @@ void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert case EncryptLT: encrypt(container, true); break; - case DecryptToken: case DecryptContainer: decrypt(container, nullptr, {}); break; @@ -416,10 +343,6 @@ void ContainerPage::transition(DigiDoc* container) connect(ui->leftPane, &ItemList::removed, container, [this, container](int index) { deleteConfirm(container, index); }); - disconnect(this, &ContainerPage::certChanged, container, nullptr); - connect(this, &ContainerPage::certChanged, container, [this](const SslCertificate &) { - showSigningButton(); - }); disconnect(ui->summary, &QAbstractButton::clicked, container, nullptr); connect(ui->summary, &QAbstractButton::clicked, container, [this,container] { #ifdef Q_OS_WIN @@ -531,15 +454,16 @@ void ContainerPage::transition(DigiDoc* container) } } - showSigningButton(); - ui->leftPane->setModel(container->documentModel()); updatePanes(container->state(), nullptr); } void ContainerPage::updateDecryptionButton() { - showMainAction({ isSeal ? DecryptToken : DecryptContainer }); + mainAction->showAction(DecryptContainer); + mainAction->setEnabled(isSupported && !isBlocked); + ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); + ui->navigationArea->layout()->invalidate(); } void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container) @@ -568,6 +492,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc ui->changeLocation->show(); ui->rightPane->init(ItemList::ItemSignature, QT_TRANSLATE_NOOP("ItemList", "Container is not signed")); ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); + showSigningButton(); setButtonsVisible({ ui->saveAs, ui->email }, true); ui->extend->hide(); break; @@ -577,6 +502,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc ui->changeLocation->hide(); ui->rightPane->init(ItemList::ItemSignature, QT_TRANSLATE_NOOP("ItemList", "Container signatures")); ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); + showSigningButton(); setButtonsVisible({ ui->saveAs, ui->email, ui->extend }, true); break; case UnencryptedContainer: diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index e301dfae7..8519c1dec 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -45,14 +45,13 @@ class ContainerPage final : public QWidget void cardChanged(const SslCertificate &cert, bool isBlocked = false); void tokenChanged(const TokenData &token); - void clearPopups(); void setHeader(const QString &file); void togglePrinting(bool enable); void transition(CryptoDoc *container, const QSslCertificate &cert); void transition(DigiDoc* container); Q_SIGNALS: - void action(int code, const QString &idCode = {}, const QString &info2 = {}); + void action(int code); void addFiles(const QStringList &files); void certChanged(const SslCertificate &cert); void warning(const WarningText &warningText); @@ -66,23 +65,18 @@ class ContainerPage final : public QWidget void elideFileName(); void encrypt(CryptoDoc *container, bool longTerm); bool eventFilter(QObject *o, QEvent *e) final; - void showMainAction(const QList &actions); void showEncryptAction(CryptoDoc *container); void showSigningButton(); - void handleAction(int type); void updateDecryptionButton(); void updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container); void translateLabels(); Ui::ContainerPage *ui; MainAction *mainAction {}; - QString idCode; QString fileName; const char *cancelText = QT_TR_NOOP("Cancel"); const char *convertText = QT_TR_NOOP("Encrypt"); bool isSupported = false; - bool isSeal = false; - bool isExpired = false; bool isBlocked = false; }; diff --git a/client/widgets/MainAction.cpp b/client/widgets/MainAction.cpp index d4f92f216..708685207 100644 --- a/client/widgets/MainAction.cpp +++ b/client/widgets/MainAction.cpp @@ -18,48 +18,37 @@ */ #include "MainAction.h" -#include "ui_MainAction.h" -#include "Settings.h" -#include -#include -#include +#include using namespace ria::qdigidoc4; -class MainAction::Private: public Ui::MainAction -{ -public: - QList actions; - QList list; -}; - MainAction::MainAction(QWidget *parent) - : QWidget(parent) - , ui(new Private) + : QPushButton(parent) { - ui->setupUi(this); - ui->otherCards->hide(); - ui->otherCards->installEventFilter(this); + setFixedSize(QSize(200, 65)); + setCursor(QCursor(Qt::CursorShape::PointingHandCursor)); + setStyleSheet(QString::fromUtf8(R"(QPushButton { +border: 0px; +color: #ffffff; +background-color: #2F70B6; +font-family: Roboto, Helvetica; +font-size: 16px; +font-weight: 700; +border-top-left-radius: 4px; +} +QPushButton:hover, QPushButton:focus { +background-color: #2B66A6; +} +QPushButton:pressed { +background-color: #215081; +} +QPushButton:disabled { +background-color: #82A9D3; +})")); parent->installEventFilter(this); move(parent->width() - width(), parent->height() - height()); - - connect(ui->mainAction, &QPushButton::clicked, this, [&]{ - if (ui->actions.value(0) == Actions::SignatureMobile) - Settings::MOBILEID_ORDER = true; - if (ui->actions.value(0) == Actions::SignatureSmartID) - Settings::MOBILEID_ORDER = false; - }); - connect(ui->mainAction, &QPushButton::clicked, this, [this]{ emit action(ui->actions.value(0)); }); - connect(ui->mainAction, &QPushButton::clicked, this, &MainAction::hideDropdown); - connect(ui->otherCards, &QToolButton::clicked, this, &MainAction::showDropdown); - adjustSize(); -} - -MainAction::~MainAction() -{ - hideDropdown(); - delete ui; + connect(this, &QPushButton::clicked, this, [this]{ emit action(_action); }); } void MainAction::changeEvent(QEvent* event) @@ -69,104 +58,27 @@ void MainAction::changeEvent(QEvent* event) QWidget::changeEvent(event); } -void MainAction::hideDropdown() -{ - for(QPushButton *other: ui->list) - other->deleteLater(); - ui->list.clear(); - setStyleSheet(QStringLiteral("QPushButton { border-top-left-radius: 4px; }")); -} - bool MainAction::eventFilter(QObject *watched, QEvent *event) { - switch(event->type()) - { - case QEvent::Resize: - if(watched == parentWidget()) - { - move(parentWidget()->width() - width(), parentWidget()->height() - height()); - QWidget* prev = this; - for(QPushButton *other: std::as_const(ui->list)) - { - other->move(prev->pos() + QPoint(0, -height() - 1)); - prev = other; - } - } - break; - default: break; - } + if(event->type() == QEvent::Resize && watched == parentWidget()) + move(parentWidget()->width() - width(), parentWidget()->height() - height()); return QWidget::eventFilter(watched, event); } - -QString MainAction::label(Actions action) -{ - switch(action) - { - case SignatureMobile: return tr("Sign with\nMobile-ID"); - case SignatureSmartID: return tr("Sign with\nSmart-ID"); - case SignatureToken: return tr("Sign with\nE-Seal"); - case EncryptContainer: return tr("Encrypt"); - case EncryptLT: return tr("Encrypt\nlong-term"); - case DecryptContainer: return tr("Decrypt with\nID-Card"); - case DecryptToken: return tr("Decrypt"); - default: return tr("Sign with\nID-Card"); - } -} - -void MainAction::setButtonEnabled(bool enabled) -{ - ui->mainAction->setEnabled(enabled); -} - -void MainAction::showActions(QList actions) +void MainAction::showAction(Actions action) { - if(actions.size() == 2 && - std::all_of(actions.cbegin(), actions.cend(), [] (Actions action) { - return action == SignatureMobile || action == SignatureSmartID; - }) && - !Settings::MOBILEID_ORDER) - std::reverse(actions.begin(), actions.end()); - ui->actions = std::move(actions); + _action = action; update(); - ui->otherCards->setVisible(ui->actions.size() > 1); show(); } -void MainAction::showDropdown() +void MainAction::update() { - if(ui->actions.size() > 1 && ui->list.isEmpty()) + switch(_action) { - QWidget* prev = this; - for(auto i = std::next(ui->actions.cbegin()); i != ui->actions.cend(); ++i) - { - auto *other = new QPushButton(label(*i), parentWidget()); - other->setCursor(ui->mainAction->cursor()); - other->resize(size()); - other->move(prev->pos() + QPoint(0, -height() - 1)); - prev = other; - other->show(); - other->setStyleSheet(ui->mainAction->styleSheet() + - (i + 1 == ui->actions.cend() ? QStringLiteral("\nQPushButton { border-top-left-radius: 4px; }") : QString())); - connect(other, &QPushButton::clicked, this, [i, this]{ - hideDropdown(); - if (*i == Actions::SignatureMobile) - Settings::MOBILEID_ORDER = true; - if (*i == Actions::SignatureSmartID) - Settings::MOBILEID_ORDER = false; - emit action(*i); - }); - ui->list.push_back(other); - } - setStyleSheet({}); + case EncryptContainer: return setText(tr("Encrypt")); + case EncryptLT: return setText(tr("Encrypt\nlong-term")); + case DecryptContainer: return setText(tr("Decrypt")); + default: return setText(tr("Sign")); } - else - hideDropdown(); -} - -void MainAction::update() -{ - hideDropdown(); - if(!ui->actions.isEmpty()) - ui->mainAction->setText(label(ui->actions[0])); } diff --git a/client/widgets/MainAction.h b/client/widgets/MainAction.h index b558e15b1..ac56446e3 100644 --- a/client/widgets/MainAction.h +++ b/client/widgets/MainAction.h @@ -21,19 +21,16 @@ #include "common_enums.h" -#include +#include -class MainAction final : public QWidget +class MainAction final : public QPushButton { Q_OBJECT public: explicit MainAction(QWidget *parent); - ~MainAction() final; - void hideDropdown(); - void setButtonEnabled(bool enabled); - void showActions(QList actions); + void showAction(ria::qdigidoc4::Actions action); signals: void action(ria::qdigidoc4::Actions action); @@ -41,11 +38,7 @@ class MainAction final : public QWidget private: void changeEvent(QEvent* event) override; bool eventFilter(QObject *watched, QEvent *event) override; - void showDropdown(); void update(); - static QString label(ria::qdigidoc4::Actions action); - - class Private; - Private *ui; + ria::qdigidoc4::Actions _action; }; diff --git a/client/widgets/MainAction.ui b/client/widgets/MainAction.ui deleted file mode 100644 index 8d3ce0ab4..000000000 --- a/client/widgets/MainAction.ui +++ /dev/null @@ -1,130 +0,0 @@ - - - MainAction - - - - 0 - 0 - 200 - 65 - - - - - 200 - 65 - - - - QPushButton { border-top-left-radius: 4px; } - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 65 - - - - - 16777215 - 65 - - - - PointingHandCursor - - - QPushButton { -border: 0px; -color: #ffffff; -background-color: #2F70B6; -font-family: Roboto, Helvetica; -font-size: 16px; -font-weight: 700; -} -QPushButton:hover, QPushButton:focus { -background-color: #2B66A6; -} -QPushButton:pressed { -background-color: #215081; -} -QPushButton:disabled { -background-color: #82A9D3; -} - - - Allkirjasta -ID-Kaardiga - - - - - - - - 34 - 65 - - - - - 34 - 65 - - - - PointingHandCursor - - - Token selection - - - QToolButton { -border: 0px solid #E7EAEF; -border-width: 0px 0px 0px 1px; -background-color: #2F70B6; -} -QToolButton:hover, QToolButton:focus { -background-color: #2B66A6; -} -QToolButton:pressed { -background-color: #215081; -} - - - - :/images/arrow_up_white.svg - - - - - 14 - 8 - - - - - - - - - diff --git a/client/widgets/ShrinkingStack.h b/client/widgets/ShrinkingStack.h new file mode 100644 index 000000000..e4e471f1b --- /dev/null +++ b/client/widgets/ShrinkingStack.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once +#include + +class ShrinkingStack final : public QStackedWidget +{ +public: + using QStackedWidget::QStackedWidget; + QSize sizeHint() const final { + return currentWidget() ? currentWidget()->sizeHint() : QStackedWidget::sizeHint(); + } + QSize minimumSizeHint() const final { + return currentWidget() ? currentWidget()->minimumSizeHint() : QStackedWidget::minimumSizeHint(); + } +};