diff --git a/src/kleopatra.rc b/src/kleopatra.rc index 39cee29f7..24d9509ca 100644 --- a/src/kleopatra.rc +++ b/src/kleopatra.rc @@ -1,127 +1,128 @@ &File &View + + &Certificates &Tools - - &Settings &Window &Help Main Toolbar + - + &Certificates diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c00f3cb51..33c5dfcb6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,672 +1,680 @@ /* -*- mode: c++; c-basic-offset:4 -*- mainwindow.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "mainwindow.h" #include "aboutdata.h" #include "view/padwidget.h" #include "view/searchbar.h" #include "view/tabwidget.h" #include "view/keylistcontroller.h" #include "view/keycacheoverlay.h" #include "view/smartcardwidget.h" #include "view/welcomewidget.h" #include "commands/selftestcommand.h" #include "commands/importcrlcommand.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/signencryptfilescommand.h" #include "utils/detail_p.h" #include #include "utils/action_data.h" #include "utils/filedialog.h" #include "utils/clipboardmenu.h" #include "dialogs/updatenotification.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; static KGuiItem KStandardGuiItem_quit() { static const QString app = KAboutData::applicationData().componentName(); KGuiItem item = KStandardGuiItem::quit(); item.setText(i18nc("Quit [ApplicationName]", "&Quit %1", app)); return item; } static KGuiItem KStandardGuiItem_close() { KGuiItem item = KStandardGuiItem::close(); item.setText(i18n("Only &Close Window")); return item; } static bool isQuitting = false; +namespace +{ +static const std::vector mainViewActionNames = { + QStringLiteral("view_certificate_overview"), + QStringLiteral("manage_smartcard"), + QStringLiteral("pad_view") +}; +} + class MainWindow::Private { friend class ::MainWindow; MainWindow *const q; public: explicit Private(MainWindow *qq); ~Private(); template void createAndStart() { (new T(this->currentView(), &this->controller))->start(); } template void createAndStart(QAbstractItemView *view) { (new T(view, &this->controller))->start(); } template void createAndStart(const QStringList &a) { (new T(a, this->currentView(), &this->controller))->start(); } template void createAndStart(const QStringList &a, QAbstractItemView *view) { (new T(a, view, &this->controller))->start(); } void closeAndQuit() { const QString app = KAboutData::applicationData().componentName(); const int rc = KMessageBox::questionYesNoCancel(q, i18n("%1 may be used by other applications as a service.\n" "You may instead want to close this window without exiting %1.", app), i18n("Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(), QLatin1String("really-quit-") + app.toLower()); if (rc == KMessageBox::Cancel) { return; } isQuitting = true; if (!q->close()) { return; } // WARNING: 'this' might be deleted at this point! if (rc == KMessageBox::No) { qApp->quit(); } } void configureToolbars() { KEditToolBar dlg(q->factory()); dlg.exec(); } void editKeybindings() { KShortcutsDialog::configure(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed); updateSearchBarClickMessage(); } void updateSearchBarClickMessage() { const QString shortcutStr = focusToClickSearchAction->shortcut().toString(); ui.searchBar->updateClickMessage(shortcutStr); } void updateStatusBar() { const auto complianceMode = Formatting::complianceMode(); if (complianceMode == QStringLiteral("de-vs")) { auto statusBar = new QStatusBar; q->setStatusBar(statusBar); auto statusLbl = new QLabel(Formatting::deVsString()); statusBar->insertPermanentWidget(0, statusLbl); } else { q->setStatusBar(nullptr); } } void selfTest() { createAndStart(); } void configureBackend(); void showHandbook(); void gnupgLogViewer() { if (!QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList())) KMessageBox::error(q, i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). " "Please check your installation."), i18n("Error Starting KWatchGnuPG")); } void forceUpdateCheck() { UpdateNotification::forceUpdateCheck(q); } void openCompendium() { QDir datadir(QCoreApplication::applicationDirPath() + QStringLiteral("/../share/gpg4win")); const auto path = datadir.filePath(i18nc("The Gpg4win compendium is only available" "at this point (24.7.2017) in german and english." "Please check with Gpg4win before translating this filename.", "gpg4win-compendium-en.pdf")); qCDebug(KLEOPATRA_LOG) << "Opening Compendium at:" << path; // The compendium is always installed. So this should work. Otherwise // we have debug output. QDesktopServices::openUrl(QUrl::fromLocalFile(path)); } void slotConfigCommitted(); void slotContextMenuRequested(QAbstractItemView *, const QPoint &p) { if (QMenu *const menu = qobject_cast(q->factory()->container(QStringLiteral("listview_popup"), q))) { menu->exec(p); } else { qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" in kleopatra's ui.rc file"; } } void slotFocusQuickSearch() { ui.searchBar->lineEdit()->setFocus(); } - void toggleSmartcardView() + void showView(const QString &actionName, QWidget *widget) { const auto coll = q->actionCollection(); if (coll) { - auto act = coll->action(QStringLiteral("pad_view")); - if (act) { - act->setChecked(false); + for ( const QString &name : mainViewActionNames ) { + if (auto action = coll->action(name)) { + action->setChecked(name == actionName); + } } } - if (ui.stackWidget->currentWidget() == ui.scWidget) { - ui.stackWidget->setCurrentWidget(ui.searchTab); - checkWelcomePage(); - return; - } - ui.stackWidget->setCurrentWidget(ui.scWidget); + ui.stackWidget->setCurrentWidget(widget); } - void togglePadView() + void showCertificateView() + { + showView(QStringLiteral("view_certificate_overview"), + KeyCache::instance()->keys().empty() ? ui.welcomeWidget : ui.searchTab); + } + + void showSmartcardView() + { + showView(QStringLiteral("manage_smartcard"), ui.scWidget); + } + + void showPadView() { - const auto coll = q->actionCollection(); - if (coll) { - auto act = coll->action(QStringLiteral("manage_smartcard")); - if (act) { - act->setChecked(false); - } - } - if (ui.stackWidget->currentWidget() == ui.padWidget) { - ui.stackWidget->setCurrentWidget(ui.searchTab); - checkWelcomePage(); - return; - } if (!ui.padWidget) { ui.padWidget = new PadWidget; ui.stackWidget->addWidget(ui.padWidget); } - ui.stackWidget->setCurrentWidget(ui.padWidget); + showView(QStringLiteral("pad_view"), ui.padWidget); ui.stackWidget->resize(ui.padWidget->sizeHint()); } private: void setupActions(); QAbstractItemView *currentView() const { return ui.tabWidget.currentView(); } - void checkWelcomePage() + void keyListingDone() { const auto curWidget = ui.stackWidget->currentWidget(); if (curWidget == ui.scWidget || curWidget == ui.padWidget) { return; } - if (KeyCache::instance()->keys().empty()) { - ui.stackWidget->setCurrentWidget(ui.welcomeWidget); - } else { - ui.stackWidget->setCurrentWidget(ui.searchTab); - } + showCertificateView(); } private: Kleo::KeyListController controller; bool firstShow : 1; struct UI { QWidget *searchTab; TabWidget tabWidget; SearchBar *searchBar; PadWidget *padWidget; SmartCardWidget *scWidget; WelcomeWidget *welcomeWidget; QStackedWidget *stackWidget; explicit UI(MainWindow *q); } ui; QAction *focusToClickSearchAction; ClipboardMenu *clipboadMenu; }; MainWindow::Private::UI::UI(MainWindow *q) : tabWidget(q), padWidget(nullptr) { KDAB_SET_OBJECT_NAME(tabWidget); searchTab = new QWidget; QVBoxLayout *vbox = new QVBoxLayout(searchTab); vbox->setSpacing(0); searchBar = new SearchBar; vbox->addWidget(searchBar); tabWidget.connectSearchBar(searchBar); vbox->addWidget(&tabWidget); QWidget *mainWidget = new QWidget; auto mainLayout = new QVBoxLayout(mainWidget); stackWidget = new QStackedWidget; mainLayout->addWidget(stackWidget); stackWidget->addWidget(searchTab); new KeyCacheOverlay(mainWidget, q); scWidget = new SmartCardWidget(); stackWidget->addWidget(scWidget); welcomeWidget = new WelcomeWidget(); stackWidget->addWidget(welcomeWidget); q->setCentralWidget(mainWidget); } MainWindow::Private::Private(MainWindow *qq) : q(qq), controller(q), firstShow(true), ui(q) { KDAB_SET_OBJECT_NAME(controller); AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q); AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q); KDAB_SET_OBJECT_NAME(flatModel); KDAB_SET_OBJECT_NAME(hierarchicalModel); controller.setFlatModel(flatModel); controller.setHierarchicalModel(hierarchicalModel); controller.setTabWidget(&ui.tabWidget); ui.tabWidget.setFlatModel(flatModel); ui.tabWidget.setHierarchicalModel(hierarchicalModel); - ui.stackWidget->setCurrentWidget(ui.searchTab); - setupActions(); + ui.stackWidget->setCurrentWidget(ui.searchTab); + if (auto action = q->actionCollection()->action(QStringLiteral("view_certificate_overview"))) { + action->setChecked(true); + } + connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView*,QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView*,QPoint))); - connect(ui.scWidget, &SmartCardWidget::backRequested, q, [this]() { - auto action = q->actionCollection()->action(QStringLiteral("manage_smartcard")); - Q_ASSERT(action); - action->setChecked(false); - }); - connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this] () {checkWelcomePage();}); + connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this] () {keyListingDone();}); q->createGUI(QStringLiteral("kleopatra.rc")); q->setAcceptDrops(true); // set default window size q->resize(QSize(1024, 500)); q->setAutoSaveSettings(); updateSearchBarClickMessage(); updateStatusBar(); } MainWindow::Private::~Private() {} MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : KXmlGuiWindow(parent, flags), d(new Private(this)) {} MainWindow::~MainWindow() {} void MainWindow::Private::setupActions() { KActionCollection *const coll = q->actionCollection(); const action_data action_data[] = { // most have been MOVED TO keylistcontroller.cpp // Tools menu #ifndef Q_OS_WIN { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(), "kwatchgnupg", q, SLOT(gnupgLogViewer()), QString(), false, true }, #endif #ifdef Q_OS_WIN { "help_check_updates", i18n("Check for updates"), QString(), "gpg4win-compact", q, SLOT(forceUpdateCheck()), QString(), false, true }, { "help_show_compendium", i18n("Gpg4win Compendium"), QString(), "gpg4win-compact", q, SLOT(openCompendium()), QString(), false, true }, #endif { - "pad_view", i18nc("Title for a generic data input / output area supporting text actions.", "Notepad"), - i18n("Switch to Pad view."), "edittext", q, SLOT(togglePadView()), QString(), true, true + "view_certificate_overview", i18nc("@action show certificate overview", "Certificates"), + i18n("Show certificate overview"), "view-certificate", q, SLOT(showCertificateView()), QString(), false, true + }, + { + "pad_view", i18nc("@action show input / output area for encrypting/signing resp. decrypting/verifying text", "Notepad"), + i18n("Show pad for encrypting/decrypting and signing/verifying text"), "note", q, SLOT(showPadView()), QString(), false, true }, // most have been MOVED TO keylistcontroller.cpp #if 0 { "configure_backend", i18n("Configure GnuPG Backend..."), QString(), 0, q, SLOT(configureBackend()), QString(), false, true }, #endif // Settings menu { "settings_self_test", i18n("Perform Self-Test"), QString(), nullptr, q, SLOT(selfTest()), QString(), false, true }, { - "manage_smartcard", i18n("Manage Smartcards"), - i18n("Edit or initialize a crypto hardware token."), "secure-card", q, SLOT(toggleSmartcardView()), QString(), true, true + "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"), + i18n("Show smartcard management"), "secure-card", q, SLOT(showSmartcardView()), QString(), false, true } // most have been MOVED TO keylistcontroller.cpp }; make_actions_from_data(action_data, /*sizeof action_data / sizeof *action_data,*/ coll); + for ( const QString &name : mainViewActionNames ) { + if (auto action = coll->action(name)) { + action->setCheckable(true); + } + } + if (QAction *action = coll->action(QStringLiteral("configure_backend"))) { action->setMenuRole(QAction::NoRole); //prevent Qt OS X heuristics for config* actions } KStandardAction::close(q, SLOT(close()), coll); KStandardAction::quit(q, SLOT(closeAndQuit()), coll); KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll); KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll); KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll); focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q); coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction); coll->setDefaultShortcut(focusToClickSearchAction, QKeySequence(Qt::ALT + Qt::Key_Q)); connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch())); clipboadMenu = new ClipboardMenu(q); clipboadMenu->setMainWindow(q); clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); clipboadMenu->clipboardMenu()->setDelayed(false); coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu()); q->setStandardToolBarMenuEnabled(true); controller.createActions(coll); ui.tabWidget.createActions(coll); } void MainWindow::Private::configureBackend() { QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig(); if (!config) { KMessageBox::error(q, i18n("Could not configure the cryptography backend (gpgconf tool not found)"), i18n("Configuration Error")); return; } Kleo::CryptoConfigDialog dlg(config); const int result = dlg.exec(); // Forget all data parsed from gpgconf, so that we show updated information // when reopening the configuration dialog. config->clear(); if (result == QDialog::Accepted) { #if 0 // Tell other apps (e.g. kmail) that the gpgconf data might have changed QDBusMessage message = QDBusMessage::createSignal(QString(), "org.kde.kleo.CryptoConfig", "changed"); QDBusConnection::sessionBus().send(message); #endif } } void MainWindow::Private::slotConfigCommitted() { controller.updateConfig(); updateStatusBar(); } void MainWindow::closeEvent(QCloseEvent *e) { // KMainWindow::closeEvent() insists on quitting the application, // so do not let it touch the event... qCDebug(KLEOPATRA_LOG); if (d->controller.hasRunningCommands()) { if (d->controller.shutdownWarningRequired()) { const int ret = KMessageBox::warningContinueCancel(this, i18n("There are still some background operations ongoing. " "These will be terminated when closing the window. " "Proceed?"), i18n("Ongoing Background Tasks")); if (ret != KMessageBox::Continue) { e->ignore(); return; } } d->controller.cancelCommands(); if (d->controller.hasRunningCommands()) { // wait for them to be finished: setEnabled(false); QEventLoop ev; QTimer::singleShot(100, &ev, &QEventLoop::quit); connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit); ev.exec(); if (d->controller.hasRunningCommands()) qCWarning(KLEOPATRA_LOG) << "controller still has commands running, this may crash now..."; setEnabled(true); } } if (isQuitting || qApp->isSavingSession()) { d->ui.tabWidget.saveViews(KSharedConfig::openConfig().data()); KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup())); saveMainWindowSettings(grp); e->accept(); } else { e->ignore(); hide(); } } void MainWindow::showEvent(QShowEvent *e) { KXmlGuiWindow::showEvent(e); if (d->firstShow) { d->ui.tabWidget.loadViews(KSharedConfig::openConfig().data()); d->firstShow = false; } if (!savedGeometry.isEmpty()) { restoreGeometry(savedGeometry); } } void MainWindow::hideEvent(QHideEvent *e) { savedGeometry = saveGeometry(); KXmlGuiWindow::hideEvent(e); } void MainWindow::importCertificatesFromFile(const QStringList &files) { if (!files.empty()) { d->createAndStart(files); } } static QStringList extract_local_files(const QMimeData *data) { const QList urls = data->urls(); // begin workaround KDE/Qt misinterpretation of text/uri-list QList::const_iterator end = urls.end(); if (urls.size() > 1 && !urls.back().isValid()) { --end; } // end workaround QStringList result; std::transform(urls.begin(), end, std::back_inserter(result), std::mem_fn(&QUrl::toLocalFile)); result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&QString::isEmpty)), result.end()); return result; } static bool can_decode_local_files(const QMimeData *data) { if (!data) { return false; } return !extract_local_files(data).empty(); } void MainWindow::dragEnterEvent(QDragEnterEvent *e) { qCDebug(KLEOPATRA_LOG); if (can_decode_local_files(e->mimeData())) { e->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent *e) { qCDebug(KLEOPATRA_LOG); if (!can_decode_local_files(e->mimeData())) { return; } e->setDropAction(Qt::CopyAction); const QStringList files = extract_local_files(e->mimeData()); const unsigned int classification = classify(files); QMenu menu; QAction *const signEncrypt = menu.addAction(i18n("Sign/Encrypt...")); QAction *const decryptVerify = mayBeAnyMessageType(classification) ? menu.addAction(i18n("Decrypt/Verify...")) : nullptr; if (signEncrypt || decryptVerify) { menu.addSeparator(); } QAction *const importCerts = mayBeAnyCertStoreType(classification) ? menu.addAction(i18n("Import Certificates")) : nullptr; QAction *const importCRLs = mayBeCertificateRevocationList(classification) ? menu.addAction(i18n("Import CRLs")) : nullptr; if (importCerts || importCRLs) { menu.addSeparator(); } if (!signEncrypt && !decryptVerify && !importCerts && !importCRLs) { return; } menu.addAction(i18n("Cancel")); const QAction *const chosen = menu.exec(mapToGlobal(e->pos())); if (!chosen) { return; } if (chosen == signEncrypt) { d->createAndStart(files); } else if (chosen == decryptVerify) { d->createAndStart(files); } else if (chosen == importCerts) { d->createAndStart(files); } else if (chosen == importCRLs) { d->createAndStart(files); } e->accept(); } void MainWindow::readProperties(const KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::readProperties(cg); setHidden(cg.readEntry("hidden", false)); } void MainWindow::saveProperties(KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::saveProperties(cg); cg.writeEntry("hidden", isHidden()); } #include "moc_mainwindow.cpp" diff --git a/src/mainwindow.h b/src/mainwindow.h index 4d80c4bff..fb71312e3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,59 +1,60 @@ /* -*- mode: c++; c-basic-offset:4 -*- mainwindow.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_MAINWINDOW_DESKTOP_H__ #define __KLEOPATRA_MAINWINDOW_DESKTOP_H__ #include #include class MainWindow : public KXmlGuiWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags f = {}); ~MainWindow() override; public Q_SLOTS: void importCertificatesFromFile(const QStringList &files); protected: QByteArray savedGeometry; void closeEvent(QCloseEvent *e) override; void showEvent(QShowEvent *e) override; void hideEvent(QHideEvent *e) override; void dragEnterEvent(QDragEnterEvent *) override; void dropEvent(QDropEvent *) override; void readProperties(const KConfigGroup &cg) override; void saveProperties(KConfigGroup &cg) override; private: class Private; kdtools::pimpl_ptr d; Q_PRIVATE_SLOT(d, void closeAndQuit()) Q_PRIVATE_SLOT(d, void selfTest()) Q_PRIVATE_SLOT(d, void configureBackend()) Q_PRIVATE_SLOT(d, void configureToolbars()) Q_PRIVATE_SLOT(d, void editKeybindings()) Q_PRIVATE_SLOT(d, void gnupgLogViewer()) Q_PRIVATE_SLOT(d, void slotConfigCommitted()) Q_PRIVATE_SLOT(d, void slotContextMenuRequested(QAbstractItemView *, QPoint)) Q_PRIVATE_SLOT(d, void slotFocusQuickSearch()) - Q_PRIVATE_SLOT(d, void togglePadView()) - Q_PRIVATE_SLOT(d, void toggleSmartcardView()) + Q_PRIVATE_SLOT(d, void showCertificateView()) + Q_PRIVATE_SLOT(d, void showPadView()) + Q_PRIVATE_SLOT(d, void showSmartcardView()) Q_PRIVATE_SLOT(d, void forceUpdateCheck()) Q_PRIVATE_SLOT(d, void openCompendium()) }; #endif /* __KLEOPATRA_MAINWINDOW_DESKTOP_H__ */ diff --git a/src/view/smartcardwidget.cpp b/src/view/smartcardwidget.cpp index c31509569..9089f391c 100644 --- a/src/view/smartcardwidget.cpp +++ b/src/view/smartcardwidget.cpp @@ -1,203 +1,194 @@ /* view/smartcardwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "smartcardwidget.h" #include "smartcard/readerstatus.h" #include "smartcard/openpgpcard.h" #include "smartcard/netkeycard.h" #include "smartcard/pivcard.h" #include "smartcard/utils.h" #include "view/pgpcardwidget.h" #include "view/netkeywidget.h" #include "view/pivcardwidget.h" #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::SmartCard; namespace { class PlaceHolderWidget: public QWidget { Q_OBJECT public: explicit PlaceHolderWidget(QWidget *parent = nullptr) : QWidget(parent) { auto lay = new QVBoxLayout; lay->addStretch(-1); const QStringList supported = QStringList() << QStringLiteral("OpenPGP v2.0 - v3.3") << QStringLiteral("Gnuk") << QStringLiteral("NetKey v3") << QStringLiteral("PIV"); lay->addWidget(new QLabel(QStringLiteral("\t\t

") + i18n("Please insert a compatible smartcard.") + QStringLiteral("

"), this)); lay->addSpacing(10); lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("
  • ") + supported.join(QLatin1String("
  • ")) + QStringLiteral("
"), this)); lay->addSpacing(10); lay->addWidget(new QLabel(i18n("Refresh the view (F5) to update the smartcard status."), this)); lay->addStretch(-1); auto hLay = new QHBoxLayout(this); hLay->addStretch(-1); hLay->addLayout(lay); hLay->addStretch(-1); lay->addStretch(-1); } }; } // namespace class SmartCardWidget::Private { public: Private(SmartCardWidget *qq); void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName); void cardRemoved(const std::string &serialNumber, const std::string &appName); private: template void cardAddedOrChanged(const std::string &serialNumber); private: SmartCardWidget *const q; QMap, QPointer > mCardWidgets; PlaceHolderWidget *mPlaceHolderWidget; QStackedWidget *mStack; QTabWidget *mTabWidget; }; SmartCardWidget::Private::Private(SmartCardWidget *qq) : q(qq) { - QPushButton *backBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("Back")); - QHBoxLayout *backH = new QHBoxLayout; - backH->addWidget(backBtn); - backH->addWidget(new QLabel(QStringLiteral("

") + i18n("Smartcard Management") + - QStringLiteral("

"))); - backH->addStretch(-1); - QVBoxLayout *vLay = new QVBoxLayout(q); - - connect(backBtn, &QPushButton::clicked, q, [this] () {Q_EMIT q->backRequested();}); - - vLay->addLayout(backH); + vLay->addWidget(new QLabel(QStringLiteral("

") + i18n("Smartcard Management") + + QStringLiteral("

"))); mStack = new QStackedWidget; vLay->addWidget(mStack); mPlaceHolderWidget = new PlaceHolderWidget; mStack->addWidget(mPlaceHolderWidget); mTabWidget = new QTabWidget; mStack->addWidget(mTabWidget); mStack->setCurrentWidget(mPlaceHolderWidget); connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); }); connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); }); connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, q, [this] (const std::string &serialNumber, const std::string &appName) { cardRemoved(serialNumber, appName); }); } void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName) { if (appName == SmartCard::NetKeyCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::OpenPGPCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::PIVCard::AppName) { cardAddedOrChanged(serialNumber); } else { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "App" << appName.c_str() << "is not supported"; } } namespace { static QString getCardLabel(const std::shared_ptr &card) { if (!card->cardHolder().isEmpty()) { return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard", "%1 - %2 - %3", displayAppName(card->appName()), card->cardHolder(), card->displaySerialNumber()); } else { return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber()); } } } template void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber) { const auto card = ReaderStatus::instance()->getCard(serialNumber); if (!card) { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found"; return; } W *cardWidget = dynamic_cast(mCardWidgets.value({serialNumber, C::AppName}).data()); if (!cardWidget) { cardWidget = new W; mCardWidgets.insert({serialNumber, C::AppName}, cardWidget); mTabWidget->addTab(cardWidget, getCardLabel(card)); if (mCardWidgets.size() == 1) { mStack->setCurrentWidget(mTabWidget); } } cardWidget->setCard(card.get()); } void SmartCardWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName) { QWidget * cardWidget = mCardWidgets.take({serialNumber, appName}); if (cardWidget) { const int index = mTabWidget->indexOf(cardWidget); if (index != -1) { mTabWidget->removeTab(index); } delete cardWidget; } if (mCardWidgets.empty()) { mStack->setCurrentWidget(mPlaceHolderWidget); } } SmartCardWidget::SmartCardWidget(QWidget *parent): QWidget(parent), d(new Private(this)) { } void SmartCardWidget::reload() { ReaderStatus::mutableInstance()->updateStatus(); } #include "smartcardwidget.moc" diff --git a/src/view/smartcardwidget.h b/src/view/smartcardwidget.h index a1e9fd72d..0605937e0 100644 --- a/src/view/smartcardwidget.h +++ b/src/view/smartcardwidget.h @@ -1,36 +1,33 @@ #ifndef VIEW_SMARTCARDWIDGET_H #define VIEW_SMARTCARDWIDGET_H /* view/smartcardwidget.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include namespace Kleo { /* SmartCardWidget a generic widget to interact with smartcards */ class SmartCardWidget: public QWidget { Q_OBJECT public: explicit SmartCardWidget(QWidget *parent = nullptr); public Q_SLOTS: void reload(); -Q_SIGNALS: - void backRequested(); - private: class Private; std::shared_ptr d; }; } // namespace Kleo #endif // VIEW_SMARTCARDWIDGET_H