File indexing completed on 2024-05-12 05:20:02
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 mainwindow.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "aboutdata.h" 0013 #include "kleopatraapplication.h" 0014 #include "mainwindow.h" 0015 #include "settings.h" 0016 0017 #include <interfaces/focusfirstchild.h> 0018 0019 #include "view/keycacheoverlay.h" 0020 #include "view/keylistcontroller.h" 0021 #include "view/padwidget.h" 0022 #include "view/searchbar.h" 0023 #include "view/smartcardwidget.h" 0024 #include "view/tabwidget.h" 0025 #include "view/welcomewidget.h" 0026 0027 #include "commands/decryptverifyfilescommand.h" 0028 #include "commands/importcertificatefromfilecommand.h" 0029 #include "commands/importcrlcommand.h" 0030 #include "commands/selftestcommand.h" 0031 #include "commands/signencryptfilescommand.h" 0032 0033 #include "utils/action_data.h" 0034 #include "utils/clipboardmenu.h" 0035 #include "utils/detail_p.h" 0036 #include "utils/filedialog.h" 0037 #include "utils/gui-helper.h" 0038 #include "utils/keyexportdraghandler.h" 0039 0040 #include <Libkleo/GnuPG> 0041 0042 #include "dialogs/updatenotification.h" 0043 0044 // needed for GPGME_VERSION_NUMBER 0045 #include <gpgme.h> 0046 0047 #include "kleopatra_debug.h" 0048 #include <KAboutData> 0049 #include <KActionCollection> 0050 #include <KActionMenu> 0051 #include <KColorScheme> 0052 #include <KConfigDialog> 0053 #include <KConfigGroup> 0054 #include <KEditToolBar> 0055 #include <KLocalizedString> 0056 #include <KMessageBox> 0057 #include <KShortcutsDialog> 0058 #include <KStandardAction> 0059 #include <KStandardGuiItem> 0060 #include <KToolBar> 0061 #include <KXMLGUIFactory> 0062 #include <QAction> 0063 #include <QApplication> 0064 #include <QLineEdit> 0065 #include <QSize> 0066 0067 #include <QAbstractItemView> 0068 #include <QCloseEvent> 0069 #include <QDesktopServices> 0070 #include <QDir> 0071 #include <QLabel> 0072 #include <QMenu> 0073 #include <QMimeData> 0074 #include <QPixmap> 0075 #include <QProcess> 0076 #include <QSettings> 0077 #include <QStackedWidget> 0078 #include <QStatusBar> 0079 #include <QTimer> 0080 #include <QVBoxLayout> 0081 0082 #include <Libkleo/Classify> 0083 #include <Libkleo/Compliance> 0084 #include <Libkleo/DocAction> 0085 #include <Libkleo/Formatting> 0086 #include <Libkleo/GnuPG> 0087 #include <Libkleo/KeyCache> 0088 #include <Libkleo/KeyListModel> 0089 #include <Libkleo/KeyListSortFilterProxyModel> 0090 #include <Libkleo/Stl_Util> 0091 #include <Libkleo/SystemInfo> 0092 0093 #include <KSharedConfig> 0094 0095 #ifdef Q_OS_UNIX 0096 #include <KWaylandExtras> 0097 #endif 0098 0099 #include <chrono> 0100 #include <vector> 0101 using namespace std::chrono_literals; 0102 0103 using namespace Kleo; 0104 using namespace Kleo::Commands; 0105 using namespace GpgME; 0106 0107 static KGuiItem KStandardGuiItem_quit() 0108 { 0109 static const QString app = KAboutData::applicationData().displayName(); 0110 KGuiItem item = KStandardGuiItem::quit(); 0111 item.setText(xi18nc("@action:button", "&Quit <application>%1</application>", app)); 0112 return item; 0113 } 0114 0115 static KGuiItem KStandardGuiItem_close() 0116 { 0117 KGuiItem item = KStandardGuiItem::close(); 0118 item.setText(i18nc("@action:button", "Only &Close Window")); 0119 return item; 0120 } 0121 0122 static bool isQuitting = false; 0123 0124 namespace 0125 { 0126 static const std::vector<QString> mainViewActionNames = { 0127 QStringLiteral("view_certificate_overview"), 0128 QStringLiteral("manage_smartcard"), 0129 QStringLiteral("pad_view"), 0130 }; 0131 0132 class CertificateView : public QWidget, public FocusFirstChild 0133 { 0134 Q_OBJECT 0135 public: 0136 CertificateView(QWidget *parent = nullptr) 0137 : QWidget{parent} 0138 , ui{this} 0139 { 0140 } 0141 0142 SearchBar *searchBar() const 0143 { 0144 return ui.searchBar; 0145 } 0146 0147 TabWidget *tabWidget() const 0148 { 0149 return ui.tabWidget; 0150 } 0151 0152 void focusFirstChild(Qt::FocusReason reason) override 0153 { 0154 ui.searchBar->lineEdit()->setFocus(reason); 0155 } 0156 0157 private: 0158 struct UI { 0159 TabWidget *tabWidget = nullptr; 0160 SearchBar *searchBar = nullptr; 0161 explicit UI(CertificateView *q) 0162 { 0163 auto vbox = new QVBoxLayout{q}; 0164 vbox->setSpacing(0); 0165 0166 searchBar = new SearchBar{q}; 0167 vbox->addWidget(searchBar); 0168 tabWidget = new TabWidget{q}; 0169 vbox->addWidget(tabWidget); 0170 0171 tabWidget->connectSearchBar(searchBar); 0172 } 0173 } ui; 0174 }; 0175 0176 } 0177 0178 class MainWindow::Private 0179 { 0180 friend class ::MainWindow; 0181 MainWindow *const q; 0182 0183 public: 0184 explicit Private(MainWindow *qq); 0185 ~Private(); 0186 0187 template<typename T> 0188 void createAndStart() 0189 { 0190 (new T(this->currentView(), &this->controller))->start(); 0191 } 0192 template<typename T> 0193 void createAndStart(QAbstractItemView *view) 0194 { 0195 (new T(view, &this->controller))->start(); 0196 } 0197 template<typename T> 0198 void createAndStart(const QStringList &a) 0199 { 0200 (new T(a, this->currentView(), &this->controller))->start(); 0201 } 0202 template<typename T> 0203 void createAndStart(const QStringList &a, QAbstractItemView *view) 0204 { 0205 (new T(a, view, &this->controller))->start(); 0206 } 0207 0208 void closeAndQuit() 0209 { 0210 const QString app = KAboutData::applicationData().displayName(); 0211 const int rc = KMessageBox::questionTwoActionsCancel(q, 0212 xi18n("<application>%1</application> may be used by other applications as a service.<nl/>" 0213 "You may instead want to close this window without exiting <application>%1</application>.", 0214 app), 0215 i18nc("@title:window", "Really Quit?"), 0216 KStandardGuiItem_close(), 0217 KStandardGuiItem_quit(), 0218 KStandardGuiItem::cancel(), 0219 QLatin1StringView("really-quit-") + app.toLower()); 0220 if (rc == KMessageBox::Cancel) { 0221 return; 0222 } 0223 isQuitting = true; 0224 if (!q->close()) { 0225 return; 0226 } 0227 // WARNING: 'this' might be deleted at this point! 0228 if (rc == KMessageBox::ButtonCode::SecondaryAction) { 0229 qApp->quit(); 0230 } 0231 } 0232 void configureToolbars() 0233 { 0234 KEditToolBar dlg(q->factory()); 0235 dlg.exec(); 0236 } 0237 void editKeybindings() 0238 { 0239 KShortcutsDialog::showDialog(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, q); 0240 updateSearchBarClickMessage(); 0241 } 0242 0243 void updateSearchBarClickMessage() 0244 { 0245 const QString shortcutStr = focusToClickSearchAction->shortcut().toString(); 0246 ui.searchTab->searchBar()->updateClickMessage(shortcutStr); 0247 } 0248 0249 void updateStatusBar() 0250 { 0251 auto statusBar = std::make_unique<QStatusBar>(); 0252 auto settings = KleopatraApplication::instance()->distributionSettings(); 0253 bool showStatusbar = false; 0254 if (settings) { 0255 const QString statusline = settings->value(QStringLiteral("statusline"), {}).toString(); 0256 if (!statusline.isEmpty()) { 0257 auto customStatusLbl = new QLabel(statusline); 0258 statusBar->insertWidget(0, customStatusLbl); 0259 showStatusbar = true; 0260 } 0261 } 0262 if (DeVSCompliance::isActive()) { 0263 auto statusLbl = std::make_unique<QLabel>(DeVSCompliance::name()); 0264 if (!SystemInfo::isHighContrastModeActive()) { 0265 const auto color = KColorScheme(QPalette::Active, KColorScheme::View) 0266 .foreground(DeVSCompliance::isCompliant() ? KColorScheme::NormalText : KColorScheme::NegativeText) 0267 .color(); 0268 const auto background = KColorScheme(QPalette::Active, KColorScheme::View) 0269 .background(DeVSCompliance::isCompliant() ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground) 0270 .color(); 0271 statusLbl->setStyleSheet(QStringLiteral("QLabel { color: %1; background-color: %2; }").arg(color.name()).arg(background.name())); 0272 } 0273 statusBar->insertPermanentWidget(0, statusLbl.release()); 0274 showStatusbar = true; 0275 } 0276 0277 if (showStatusbar) { 0278 q->setStatusBar(statusBar.release()); // QMainWindow takes ownership 0279 } else { 0280 q->setStatusBar(nullptr); 0281 } 0282 } 0283 0284 void selfTest() 0285 { 0286 createAndStart<SelfTestCommand>(); 0287 } 0288 0289 void configureGroups() 0290 { 0291 // open groups config dialog as independent top-level window 0292 KleopatraApplication::instance()->openOrRaiseGroupsConfigDialog(nullptr); 0293 } 0294 0295 void showHandbook(); 0296 0297 void gnupgLogViewer() 0298 { 0299 // Warning: Don't assume that the program needs to be in PATH. On Windows, it will also be found next to the calling process. 0300 if (!QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList())) 0301 KMessageBox::error(q, 0302 i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). " 0303 "Please check your installation."), 0304 i18n("Error Starting KWatchGnuPG")); 0305 } 0306 0307 void forceUpdateCheck() 0308 { 0309 UpdateNotification::forceUpdateCheck(q); 0310 } 0311 0312 void slotConfigCommitted(); 0313 void slotContextMenuRequested(QAbstractItemView *, const QPoint &p) 0314 { 0315 if (auto const menu = qobject_cast<QMenu *>(q->factory()->container(QStringLiteral("listview_popup"), q))) { 0316 menu->exec(p); 0317 } else { 0318 qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" <Menu> in kleopatra's ui.rc file"; 0319 } 0320 } 0321 0322 void slotFocusQuickSearch() 0323 { 0324 ui.searchTab->searchBar()->lineEdit()->setFocus(); 0325 } 0326 0327 void showView(const QString &actionName, QWidget *widget) 0328 { 0329 const auto coll = q->actionCollection(); 0330 if (coll) { 0331 for (const QString &name : mainViewActionNames) { 0332 if (auto action = coll->action(name)) { 0333 action->setChecked(name == actionName); 0334 } 0335 } 0336 } 0337 ui.stackWidget->setCurrentWidget(widget); 0338 if (auto ffci = dynamic_cast<Kleo::FocusFirstChild *>(widget)) { 0339 ffci->focusFirstChild(Qt::TabFocusReason); 0340 } 0341 } 0342 0343 void showCertificateView() 0344 { 0345 if (KeyCache::instance()->keys().empty()) { 0346 showView(QStringLiteral("view_certificate_overview"), ui.welcomeWidget); 0347 } else { 0348 showView(QStringLiteral("view_certificate_overview"), ui.searchTab); 0349 } 0350 } 0351 0352 void showSmartcardView() 0353 { 0354 showView(QStringLiteral("manage_smartcard"), ui.scWidget); 0355 } 0356 0357 void showPadView() 0358 { 0359 if (!ui.padWidget) { 0360 ui.padWidget = new PadWidget; 0361 ui.stackWidget->addWidget(ui.padWidget); 0362 } 0363 showView(QStringLiteral("pad_view"), ui.padWidget); 0364 ui.stackWidget->resize(ui.padWidget->sizeHint()); 0365 } 0366 0367 void restartDaemons() 0368 { 0369 Kleo::killDaemons(); 0370 } 0371 0372 private: 0373 void setupActions(); 0374 0375 QAbstractItemView *currentView() const 0376 { 0377 return ui.searchTab->tabWidget()->currentView(); 0378 } 0379 0380 void keyListingDone() 0381 { 0382 const auto curWidget = ui.stackWidget->currentWidget(); 0383 if (curWidget == ui.scWidget || curWidget == ui.padWidget) { 0384 return; 0385 } 0386 showCertificateView(); 0387 } 0388 0389 private: 0390 Kleo::KeyListController controller; 0391 bool firstShow : 1; 0392 struct UI { 0393 CertificateView *searchTab = nullptr; 0394 PadWidget *padWidget = nullptr; 0395 SmartCardWidget *scWidget = nullptr; 0396 WelcomeWidget *welcomeWidget = nullptr; 0397 QStackedWidget *stackWidget = nullptr; 0398 explicit UI(MainWindow *q); 0399 } ui; 0400 QAction *focusToClickSearchAction = nullptr; 0401 ClipboardMenu *clipboadMenu = nullptr; 0402 }; 0403 0404 MainWindow::Private::UI::UI(MainWindow *q) 0405 : padWidget(nullptr) 0406 { 0407 auto mainWidget = new QWidget{q}; 0408 auto mainLayout = new QVBoxLayout(mainWidget); 0409 mainLayout->setContentsMargins({}); 0410 stackWidget = new QStackedWidget{q}; 0411 0412 searchTab = new CertificateView{q}; 0413 stackWidget->addWidget(searchTab); 0414 0415 new KeyCacheOverlay(mainWidget, q); 0416 0417 scWidget = new SmartCardWidget{q}; 0418 stackWidget->addWidget(scWidget); 0419 0420 welcomeWidget = new WelcomeWidget{q}; 0421 stackWidget->addWidget(welcomeWidget); 0422 0423 mainLayout->addWidget(stackWidget); 0424 0425 q->setCentralWidget(mainWidget); 0426 } 0427 0428 MainWindow::Private::Private(MainWindow *qq) 0429 : q(qq) 0430 , controller(q) 0431 , firstShow(true) 0432 , ui(q) 0433 { 0434 KDAB_SET_OBJECT_NAME(controller); 0435 0436 AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q); 0437 AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q); 0438 0439 KDAB_SET_OBJECT_NAME(flatModel); 0440 KDAB_SET_OBJECT_NAME(hierarchicalModel); 0441 0442 #if GPGME_VERSION_NUMBER >= 0x011800 // 1.24.0 0443 auto keyExportDragHandler = std::make_shared<KeyExportDragHandler>(); 0444 flatModel->setDragHandler(keyExportDragHandler); 0445 hierarchicalModel->setDragHandler(keyExportDragHandler); 0446 #endif 0447 0448 controller.setFlatModel(flatModel); 0449 controller.setHierarchicalModel(hierarchicalModel); 0450 controller.setTabWidget(ui.searchTab->tabWidget()); 0451 0452 ui.searchTab->tabWidget()->setFlatModel(flatModel); 0453 ui.searchTab->tabWidget()->setHierarchicalModel(hierarchicalModel); 0454 0455 #ifdef Q_OS_UNIX 0456 connect(KWaylandExtras::self(), &KWaylandExtras::windowExported, q, [this](const auto &window, const auto &token) { 0457 if (window == q->windowHandle()) { 0458 qputenv("PINENTRY_GEOM_HINT", QUrl::toPercentEncoding(token)); 0459 } 0460 }); 0461 q->exportWindow(); 0462 #endif 0463 0464 setupActions(); 0465 0466 ui.stackWidget->setCurrentWidget(ui.searchTab); 0467 if (auto action = q->actionCollection()->action(QStringLiteral("view_certificate_overview"))) { 0468 action->setChecked(true); 0469 } 0470 0471 connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView *, QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView *, QPoint))); 0472 connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this]() { 0473 keyListingDone(); 0474 }); 0475 0476 q->createGUI(QStringLiteral("kleopatra.rc")); 0477 0478 // make toolbar buttons accessible by keyboard 0479 auto toolbar = q->findChild<KToolBar *>(); 0480 if (toolbar) { 0481 auto toolbarButtons = toolbar->findChildren<QToolButton *>(); 0482 for (auto b : toolbarButtons) { 0483 b->setFocusPolicy(Qt::TabFocus); 0484 } 0485 // move toolbar and its child widgets before the central widget in the tab order; 0486 // this is necessary to make Shift+Tab work as expected 0487 forceSetTabOrder(q, toolbar); 0488 auto toolbarChildren = toolbar->findChildren<QWidget *>(); 0489 std::for_each(std::rbegin(toolbarChildren), std::rend(toolbarChildren), [toolbar](auto w) { 0490 forceSetTabOrder(toolbar, w); 0491 }); 0492 } 0493 0494 if (auto action = q->actionCollection()->action(QStringLiteral("help_whats_this"))) { 0495 delete action; 0496 } 0497 0498 q->setAcceptDrops(true); 0499 0500 // set default window size 0501 q->resize(QSize(1024, 500)); 0502 q->setAutoSaveSettings(); 0503 0504 updateSearchBarClickMessage(); 0505 updateStatusBar(); 0506 0507 if (KeyCache::instance()->initialized()) { 0508 keyListingDone(); 0509 } 0510 0511 // delay setting the models to use the key cache so that the UI (including 0512 // the "Loading certificate cache..." overlay) is shown before the 0513 // blocking key cache initialization happens 0514 QMetaObject::invokeMethod( 0515 q, 0516 [flatModel, hierarchicalModel]() { 0517 flatModel->useKeyCache(true, KeyList::AllKeys); 0518 hierarchicalModel->useKeyCache(true, KeyList::AllKeys); 0519 }, 0520 Qt::QueuedConnection); 0521 } 0522 0523 MainWindow::Private::~Private() 0524 { 0525 } 0526 0527 MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) 0528 : KXmlGuiWindow(parent, flags) 0529 , d(new Private(this)) 0530 { 0531 } 0532 0533 MainWindow::~MainWindow() 0534 { 0535 } 0536 0537 void MainWindow::Private::setupActions() 0538 { 0539 KActionCollection *const coll = q->actionCollection(); 0540 0541 const std::vector<action_data> action_data = { 0542 // see keylistcontroller.cpp for more actions 0543 // Tools menu 0544 #ifndef Q_OS_WIN 0545 { 0546 "tools_start_kwatchgnupg", 0547 i18n("GnuPG Log Viewer"), 0548 QString(), 0549 "kwatchgnupg", 0550 q, 0551 [this](bool) { 0552 gnupgLogViewer(); 0553 }, 0554 QString(), 0555 }, 0556 #endif 0557 { 0558 "tools_restart_backend", 0559 i18nc("@action:inmenu", "Restart Background Processes"), 0560 i18nc("@info:tooltip", "Restart the background processes, e.g. after making changes to the configuration."), 0561 "view-refresh", 0562 q, 0563 [this](bool) { 0564 restartDaemons(); 0565 }, 0566 {}, 0567 }, 0568 // Help menu 0569 #ifdef Q_OS_WIN 0570 { 0571 "help_check_updates", 0572 i18n("Check for updates"), 0573 QString(), 0574 "gpg4win-compact", 0575 q, 0576 [this](bool) { 0577 forceUpdateCheck(); 0578 }, 0579 QString(), 0580 }, 0581 #endif 0582 // View menu 0583 { 0584 "view_certificate_overview", 0585 i18nc("@action show certificate overview", "Certificates"), 0586 i18n("Show certificate overview"), 0587 "view-certificate", 0588 q, 0589 [this](bool) { 0590 showCertificateView(); 0591 }, 0592 QString(), 0593 }, 0594 { 0595 "pad_view", 0596 i18nc("@action show input / output area for encrypting/signing resp. decrypting/verifying text", "Notepad"), 0597 i18n("Show pad for encrypting/decrypting and signing/verifying text"), 0598 "note", 0599 q, 0600 [this](bool) { 0601 showPadView(); 0602 }, 0603 QString(), 0604 }, 0605 { 0606 "manage_smartcard", 0607 i18nc("@action show smartcard management view", "Smartcards"), 0608 i18n("Show smartcard management"), 0609 "auth-sim-locked", 0610 q, 0611 [this](bool) { 0612 showSmartcardView(); 0613 }, 0614 QString(), 0615 }, 0616 // Settings menu 0617 { 0618 "settings_self_test", 0619 i18n("Perform Self-Test"), 0620 QString(), 0621 nullptr, 0622 q, 0623 [this](bool) { 0624 selfTest(); 0625 }, 0626 QString(), 0627 }, 0628 { 0629 "configure_groups", 0630 i18n("Configure Groups..."), 0631 QString(), 0632 "group", 0633 q, 0634 [this](bool) { 0635 configureGroups(); 0636 }, 0637 QString(), 0638 }}; 0639 0640 make_actions_from_data(action_data, coll); 0641 0642 if (!Settings().groupsEnabled()) { 0643 if (auto action = coll->action(QStringLiteral("configure_groups"))) { 0644 delete action; 0645 } 0646 } 0647 0648 for (const QString &name : mainViewActionNames) { 0649 if (auto action = coll->action(name)) { 0650 action->setCheckable(true); 0651 } 0652 } 0653 0654 KStandardAction::close(q, SLOT(close()), coll); 0655 KStandardAction::quit(q, SLOT(closeAndQuit()), coll); 0656 KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll); 0657 KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll); 0658 KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll); 0659 0660 focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q); 0661 coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction); 0662 coll->setDefaultShortcut(focusToClickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q)); 0663 connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch())); 0664 clipboadMenu = new ClipboardMenu(q); 0665 clipboadMenu->setMainWindow(q); 0666 clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); 0667 clipboadMenu->clipboardMenu()->setPopupMode(QToolButton::InstantPopup); 0668 coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu()); 0669 0670 /* Add additional help actions for documentation */ 0671 const auto compendium = new DocAction(QIcon::fromTheme(QStringLiteral("gpg4win-compact")), 0672 i18n("Gpg4win Compendium"), 0673 i18nc("The Gpg4win compendium is only available" 0674 "at this point (24.7.2017) in german and english." 0675 "Please check with Gpg4win before translating this filename.", 0676 "gpg4win-compendium-en.pdf"), 0677 QStringLiteral("../share/gpg4win"), 0678 coll); 0679 coll->addAction(QStringLiteral("help_doc_compendium"), compendium); 0680 0681 /* Documentation centered around the german approved VS-NfD mode for official 0682 * RESTRICTED communication. This is only available in some distributions with 0683 * the focus on official communications. */ 0684 const auto quickguide = 0685 new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0686 i18n("&Quickguide"), 0687 i18nc("Only available in German and English. Leave to English for other languages.", "encrypt_and_sign_gnupgvsd_en.pdf"), 0688 QStringLiteral("../share/doc/gnupg-vsd"), 0689 coll); 0690 coll->addAction(QStringLiteral("help_doc_quickguide"), quickguide); 0691 0692 const auto symguide = 0693 new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0694 i18n("&Password-based encryption"), 0695 i18nc("Only available in German and English. Leave to English for other languages.", "symmetric_encryption_gnupgvsd_en.pdf"), 0696 QStringLiteral("../share/doc/gnupg-vsd"), 0697 coll); 0698 coll->addAction(QStringLiteral("help_doc_symenc"), symguide); 0699 0700 const auto groups = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0701 i18n("&Group configuration"), 0702 i18nc("Only available in German and English. Leave to English for other languages.", "groupfeature_gnupgvsd_en.pdf"), 0703 QStringLiteral("../share/doc/gnupg-vsd"), 0704 coll); 0705 coll->addAction(QStringLiteral("help_doc_groups"), groups); 0706 0707 #ifdef Q_OS_WIN 0708 const auto gpgol = 0709 new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0710 i18n("&Mail encryption in Outlook"), 0711 i18nc("Only available in German and English. Leave to English for other languages. Only shown on Windows.", "gpgol_outlook_addin_en.pdf"), 0712 QStringLiteral("../share/doc/gnupg-vsd"), 0713 coll); 0714 coll->addAction(QStringLiteral("help_doc_gpgol"), gpgol); 0715 #endif 0716 0717 /* The submenu with advanced topics */ 0718 const auto certmngmnt = 0719 new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0720 i18n("&Certification Management"), 0721 i18nc("Only available in German and English. Leave to English for other languages.", "certification_management_gnupgvsd_en.pdf"), 0722 QStringLiteral("../share/doc/gnupg-vsd"), 0723 coll); 0724 coll->addAction(QStringLiteral("help_doc_cert_management"), certmngmnt); 0725 0726 const auto smartcard = 0727 new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0728 i18n("&Smartcard setup"), 0729 i18nc("Only available in German and English. Leave to English for other languages.", "smartcard_setup_gnupgvsd_en.pdf"), 0730 QStringLiteral("../share/doc/gnupg-vsd"), 0731 coll); 0732 coll->addAction(QStringLiteral("help_doc_smartcard"), smartcard); 0733 0734 const auto man_gnupg = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), 0735 i18n("GnuPG Command&line"), 0736 QStringLiteral("gnupg_manual_en.pdf"), 0737 QStringLiteral("../share/doc/gnupg-vsd"), 0738 coll); 0739 coll->addAction(QStringLiteral("help_doc_gnupg"), man_gnupg); 0740 0741 /* The secops */ 0742 const auto vsa10573 = 0743 new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), 0744 i18n("SecOps VSA-10573"), 0745 i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10573-ENG_secops-20220207.pdf"), 0746 QStringLiteral("../share/doc/gnupg-vsd"), 0747 coll); 0748 coll->addAction(QStringLiteral("help_doc_vsa10573"), vsa10573); 0749 0750 const auto vsa10584 = 0751 new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), 0752 i18n("SecOps VSA-10584"), 0753 i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10584-ENG_secops-20220207.pdf"), 0754 QStringLiteral("../share/doc/gnupg-vsd"), 0755 coll); 0756 coll->addAction(QStringLiteral("help_doc_vsa10584"), vsa10584); 0757 0758 q->setStandardToolBarMenuEnabled(true); 0759 0760 controller.createActions(coll); 0761 0762 ui.searchTab->tabWidget()->createActions(coll); 0763 } 0764 0765 void MainWindow::Private::slotConfigCommitted() 0766 { 0767 controller.updateConfig(); 0768 updateStatusBar(); 0769 } 0770 0771 void MainWindow::closeEvent(QCloseEvent *e) 0772 { 0773 // KMainWindow::closeEvent() insists on quitting the application, 0774 // so do not let it touch the event... 0775 qCDebug(KLEOPATRA_LOG); 0776 if (d->controller.hasRunningCommands()) { 0777 if (d->controller.shutdownWarningRequired()) { 0778 const int ret = KMessageBox::warningContinueCancel(this, 0779 i18n("There are still some background operations ongoing. " 0780 "These will be terminated when closing the window. " 0781 "Proceed?"), 0782 i18n("Ongoing Background Tasks")); 0783 if (ret != KMessageBox::Continue) { 0784 e->ignore(); 0785 return; 0786 } 0787 } 0788 d->controller.cancelCommands(); 0789 if (d->controller.hasRunningCommands()) { 0790 // wait for them to be finished: 0791 setEnabled(false); 0792 QEventLoop ev; 0793 QTimer::singleShot(100ms, &ev, &QEventLoop::quit); 0794 connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit); 0795 ev.exec(); 0796 if (d->controller.hasRunningCommands()) 0797 qCWarning(KLEOPATRA_LOG) << "controller still has commands running, this may crash now..."; 0798 setEnabled(true); 0799 } 0800 } 0801 unexportWindow(); 0802 if (isQuitting || qApp->isSavingSession()) { 0803 d->ui.searchTab->tabWidget()->saveViews(KSharedConfig::openConfig().data()); 0804 KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup())); 0805 saveMainWindowSettings(grp); 0806 e->accept(); 0807 } else { 0808 e->ignore(); 0809 hide(); 0810 } 0811 } 0812 0813 void MainWindow::showEvent(QShowEvent *e) 0814 { 0815 KXmlGuiWindow::showEvent(e); 0816 if (d->firstShow) { 0817 d->ui.searchTab->tabWidget()->loadViews(KSharedConfig::openConfig().data()); 0818 d->firstShow = false; 0819 } 0820 0821 if (!savedGeometry.isEmpty()) { 0822 restoreGeometry(savedGeometry); 0823 } 0824 } 0825 0826 void MainWindow::hideEvent(QHideEvent *e) 0827 { 0828 savedGeometry = saveGeometry(); 0829 KXmlGuiWindow::hideEvent(e); 0830 } 0831 0832 void MainWindow::importCertificatesFromFile(const QStringList &files) 0833 { 0834 if (!files.empty()) { 0835 d->createAndStart<ImportCertificateFromFileCommand>(files); 0836 } 0837 } 0838 0839 static QStringList extract_local_files(const QMimeData *data) 0840 { 0841 const QList<QUrl> urls = data->urls(); 0842 // begin workaround KDE/Qt misinterpretation of text/uri-list 0843 QList<QUrl>::const_iterator end = urls.end(); 0844 if (urls.size() > 1 && !urls.back().isValid()) { 0845 --end; 0846 } 0847 // end workaround 0848 QStringList result; 0849 std::transform(urls.begin(), end, std::back_inserter(result), std::mem_fn(&QUrl::toLocalFile)); 0850 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&QString::isEmpty)), result.end()); 0851 return result; 0852 } 0853 0854 static bool can_decode_local_files(const QMimeData *data) 0855 { 0856 if (!data) { 0857 return false; 0858 } 0859 return !extract_local_files(data).empty(); 0860 } 0861 0862 void MainWindow::dragEnterEvent(QDragEnterEvent *e) 0863 { 0864 qCDebug(KLEOPATRA_LOG); 0865 0866 if (can_decode_local_files(e->mimeData())) { 0867 e->acceptProposedAction(); 0868 } 0869 } 0870 0871 void MainWindow::dropEvent(QDropEvent *e) 0872 { 0873 qCDebug(KLEOPATRA_LOG); 0874 0875 if (!can_decode_local_files(e->mimeData())) { 0876 return; 0877 } 0878 0879 e->setDropAction(Qt::CopyAction); 0880 0881 const QStringList files = extract_local_files(e->mimeData()); 0882 0883 KleopatraApplication::instance()->handleFiles(files); 0884 0885 e->accept(); 0886 } 0887 0888 void MainWindow::readProperties(const KConfigGroup &cg) 0889 { 0890 qCDebug(KLEOPATRA_LOG); 0891 KXmlGuiWindow::readProperties(cg); 0892 setHidden(cg.readEntry("hidden", false)); 0893 } 0894 0895 void MainWindow::saveProperties(KConfigGroup &cg) 0896 { 0897 qCDebug(KLEOPATRA_LOG); 0898 KXmlGuiWindow::saveProperties(cg); 0899 cg.writeEntry("hidden", isHidden()); 0900 } 0901 0902 void MainWindow::exportWindow() 0903 { 0904 #ifdef Q_OS_UNIX 0905 (void)winId(); // Ensures that windowHandle() returns the window 0906 KWaylandExtras::self()->exportWindow(windowHandle()); 0907 #endif 0908 } 0909 0910 void MainWindow::unexportWindow() 0911 { 0912 #ifdef Q_OS_UNIX 0913 KWaylandExtras::self()->unexportWindow(windowHandle()); 0914 #endif 0915 } 0916 0917 #include "mainwindow.moc" 0918 #include "moc_mainwindow.cpp"