Fix layout
[kdepim.git] / kleopatra / mainwindow_desktop.cpp
blobc900c05d7de396ddb4887ba77979eecb89c9e9ed
1 /* -*- mode: c++; c-basic-offset:4 -*-
2 mainwindow_desktop.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2007 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include <config-kleopatra.h>
35 #include "mainwindow_desktop.h"
36 #include "aboutdata.h"
38 #include "models/keylistmodel.h"
39 #include "models/keylistsortfilterproxymodel.h"
41 #include "view/searchbar.h"
42 #include "view/tabwidget.h"
43 #include "view/keylistcontroller.h"
45 #include "commands/selftestcommand.h"
46 #include "commands/importcrlcommand.h"
47 #include "commands/importcertificatefromfilecommand.h"
48 #include "commands/decryptverifyfilescommand.h"
49 #include "commands/signencryptfilescommand.h"
51 #include "utils/detail_p.h"
52 #include "utils/gnupg-helper.h"
53 #include "utils/action_data.h"
54 #include "utils/classify.h"
55 #include "utils/filedialog.h"
56 #include "utils/clipboardmenu.h"
58 // from libkdepim
59 #include "libkdepim/statusbarprogresswidget.h"
60 #include "libkdepim/progressdialog.h"
61 #include <QStatusBar>
62 #include <KXMLGUIFactory>
63 #include <QApplication>
64 #include <KActionCollection>
65 #include <KLocalizedString>
66 #include <KStandardAction>
67 #include <QAction>
68 #include <KAboutData>
69 #include <KMessageBox>
70 #include <KStandardGuiItem>
71 #include <KShortcutsDialog>
72 #include <KEditToolBar>
73 #include <KAboutApplicationDialog>
74 #include "kleopatra_debug.h"
75 #include <QLineEdit>
76 #include <KActionMenu>
77 #include <KConfigGroup>
79 #include <QAbstractItemView>
80 #include <QApplication>
81 #include <QCloseEvent>
82 #include <QMenu>
83 #include <QTimer>
84 #include <QProcess>
85 #include <QVBoxLayout>
86 #include <QMimeData>
88 #include <Libkleo/CryptoBackendFactory>
89 #include <libkleo/cryptoconfigdialog.h>
90 #include <Libkleo/CryptoConfig>
91 #include <Libkleo/Stl_Util>
93 #include <boost/bind.hpp>
94 #include <boost/shared_ptr.hpp>
96 #include <vector>
97 #include <KSharedConfig>
99 using namespace Kleo;
100 using namespace Kleo::Commands;
101 using namespace boost;
102 using namespace GpgME;
104 static KGuiItem KStandardGuiItem_quit()
106 static const QString app = KAboutData::applicationData().componentName();
107 KGuiItem item = KStandardGuiItem::quit();
108 item.setText(i18nc("Quit [ApplicationName]", "&Quit %1", app));
109 return item;
112 static KGuiItem KStandardGuiItem_close()
114 KGuiItem item = KStandardGuiItem::close();
115 item.setText(i18n("Only &Close Window"));
116 return item;
119 static bool isQuitting = false;
121 class MainWindow::Private
123 friend class ::MainWindow;
124 MainWindow *const q;
126 public:
127 explicit Private(MainWindow *qq);
128 ~Private();
130 template <typename T>
131 void createAndStart()
133 (new T(this->currentView(), &this->controller))->start();
135 template <typename T>
136 void createAndStart(QAbstractItemView *view)
138 (new T(view, &this->controller))->start();
140 template <typename T>
141 void createAndStart(const QStringList &a)
143 (new T(a, this->currentView(), &this->controller))->start();
145 template <typename T>
146 void createAndStart(const QStringList &a, QAbstractItemView *view)
148 (new T(a, view, &this->controller))->start();
151 void closeAndQuit()
153 const QString app = KAboutData::applicationData().componentName();
154 const int rc = KMessageBox::questionYesNoCancel(q,
155 i18n("%1 may be used by other applications as a service.\n"
156 "You may instead want to close this window without exiting %1.", app),
157 i18n("Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(),
158 QLatin1String("really-quit-") + app.toLower());
159 if (rc == KMessageBox::Cancel) {
160 return;
162 isQuitting = true;
163 if (!q->close()) {
164 return;
166 // WARNING: 'this' might be deleted at this point!
167 if (rc == KMessageBox::No) {
168 qApp->quit();
171 void configureToolbars()
173 KEditToolBar dlg(q->factory());
174 dlg.exec();
176 void editKeybindings()
178 KShortcutsDialog::configure(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed);
179 updateSearchBarClickMessage();
182 void updateSearchBarClickMessage()
184 const QString shortcutStr = focusToClickSearchAction->shortcut().toString();
185 ui.searchBar->updateClickMessage(shortcutStr);
188 void selfTest()
190 createAndStart<SelfTestCommand>();
192 void configureBackend();
194 void showHandbook();
196 void gnupgLogViewer()
198 if (!QProcess::startDetached(QStringLiteral("kwatchgnupg")))
199 KMessageBox::error(q, i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). "
200 "Please check your installation."),
201 i18n("Error Starting KWatchGnuPG"));
204 void slotConfigCommitted();
205 void slotContextMenuRequested(QAbstractItemView *, const QPoint &p)
207 if (QMenu *const menu = qobject_cast<QMenu *>(q->factory()->container(QStringLiteral("listview_popup"), q))) {
208 menu->exec(p);
209 } else {
210 qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" <Menu> in kleopatra's ui.rc file";
214 void aboutGpg4Win()
216 #pragma message("port QT5")
218 //QT5 ( new KAboutApplicationDialog( aboutGpg4WinData(), KAboutApplicationDialog::HideKdeVersion|KAboutApplicationDialog::HideTranslators, q ) )->show();
220 void slotFocusQuickSearch()
222 ui.searchBar->lineEdit()->setFocus();
225 private:
226 void setupActions();
228 QAbstractItemView *currentView() const
230 return ui.tabWidget.currentView();
233 private:
234 Kleo::KeyListController controller;
235 bool firstShow : 1;
236 struct UI {
238 TabWidget tabWidget;
239 SearchBar *searchBar;
240 explicit UI(MainWindow *q);
241 } ui;
242 QAction *focusToClickSearchAction;
243 ClipboardMenu *clipboadMenu;
246 MainWindow::Private::UI::UI(MainWindow *q)
247 : tabWidget(q)
249 KDAB_SET_OBJECT_NAME(tabWidget);
251 QWidget *mainWidget = new QWidget;
252 QVBoxLayout *vbox = new QVBoxLayout;
253 vbox->setSpacing(0);
254 mainWidget->setLayout(vbox);
255 searchBar = new SearchBar;
256 vbox->addWidget(searchBar);
257 tabWidget.connectSearchBar(searchBar);
258 vbox->addWidget(&tabWidget);
260 q->setCentralWidget(mainWidget);
261 KPIM::ProgressDialog *progressDialog = new KPIM::ProgressDialog(q->statusBar(), q);
262 KDAB_SET_OBJECT_NAME(progressDialog);
263 progressDialog->hide();
264 KPIM::StatusbarProgressWidget *statusBarProgressWidget
265 = new KPIM::StatusbarProgressWidget(progressDialog, q->statusBar());
266 KDAB_SET_OBJECT_NAME(statusBarProgressWidget);
267 q->statusBar()->addPermanentWidget(statusBarProgressWidget, 0);
268 statusBarProgressWidget->show();
271 MainWindow::Private::Private(MainWindow *qq)
272 : q(qq),
273 controller(q),
274 firstShow(true),
275 ui(q)
277 KDAB_SET_OBJECT_NAME(controller);
279 AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q);
280 AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q);
282 KDAB_SET_OBJECT_NAME(flatModel);
283 KDAB_SET_OBJECT_NAME(hierarchicalModel);
285 controller.setFlatModel(flatModel);
286 controller.setHierarchicalModel(hierarchicalModel);
287 controller.setTabWidget(&ui.tabWidget);
289 ui.tabWidget.setFlatModel(flatModel);
290 ui.tabWidget.setHierarchicalModel(hierarchicalModel);
292 setupActions();
294 connect(&controller, &KeyListController::message, q->statusBar(), &QStatusBar::showMessage);
295 connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView*,QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView*,QPoint)));
297 q->createGUI(QStringLiteral("kleopatra.rc"));
299 q->setAcceptDrops(true);
301 q->setAutoSaveSettings();
302 updateSearchBarClickMessage();
305 MainWindow::Private::~Private() {}
307 MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags)
308 : KXmlGuiWindow(parent, flags), d(new Private(this))
312 MainWindow::~MainWindow() {}
314 void MainWindow::Private::setupActions()
317 KActionCollection *const coll = q->actionCollection();
319 const action_data action_data[] = {
320 // most have been MOVED TO keylistcontroller.cpp
321 // Tools menu
322 #ifndef Q_OS_WIN
324 "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(),
325 "kwatchgnupg", q, SLOT(gnupgLogViewer()), QString(), false, true
327 #endif
328 // most have been MOVED TO keylistcontroller.cpp
329 #if 0
331 "configure_backend", i18n("Configure GnuPG Backend..."), QString(),
332 0, q, SLOT(configureBackend()), QString(), false, true
334 #endif
335 // Settings menu
337 "settings_self_test", i18n("Perform Self-Test"), QString(),
338 0, q, SLOT(selfTest()), QString(), false, true
340 // Help menu
341 #ifdef Q_OS_WIN
343 "help_about_gpg4win", i18n("About Gpg4win"), QString(),
344 "gpg4win-compact", q, SLOT(aboutGpg4Win()), QString(), false, true
346 #endif
347 // most have been MOVED TO keylistcontroller.cpp
350 make_actions_from_data(action_data, /*sizeof action_data / sizeof *action_data,*/ coll);
352 if (QAction *action = coll->action(QStringLiteral("configure_backend"))) {
353 action->setMenuRole(QAction::NoRole); //prevent Qt OS X heuristics for config* actions
356 KStandardAction::close(q, SLOT(close()), coll);
357 KStandardAction::quit(q, SLOT(closeAndQuit()), coll);
358 KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll);
359 KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll);
360 KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll);
362 focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q);
363 focusToClickSearchAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Q));
364 coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction);
365 connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch()));
366 clipboadMenu = new ClipboardMenu(q);
367 clipboadMenu->setMainWindow(q);
368 clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste")));
369 clipboadMenu->clipboardMenu()->setDelayed(false);
370 coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu());
372 q->createStandardStatusBarAction();
373 q->setStandardToolBarMenuEnabled(true);
375 controller.createActions(coll);
377 ui.tabWidget.createActions(coll);
380 void MainWindow::Private::configureBackend()
382 Kleo::CryptoConfig *const config = Kleo::CryptoBackendFactory::instance()->config();
383 if (!config) {
384 KMessageBox::error(q, i18n("Could not configure the cryptography backend (gpgconf tool not found)"), i18n("Configuration Error"));
385 return;
388 Kleo::CryptoConfigDialog dlg(config);
390 const int result = dlg.exec();
392 // Forget all data parsed from gpgconf, so that we show updated information
393 // when reopening the configuration dialog.
394 config->clear();
396 if (result == QDialog::Accepted) {
397 #if 0
398 // Tell other apps (e.g. kmail) that the gpgconf data might have changed
399 QDBusMessage message =
400 QDBusMessage::createSignal(QString(), "org.kde.kleo.CryptoConfig", "changed");
401 QDBusConnection::sessionBus().send(message);
402 #endif
406 void MainWindow::Private::slotConfigCommitted()
408 controller.updateConfig();
411 void MainWindow::closeEvent(QCloseEvent *e)
413 // KMainWindow::closeEvent() insists on quitting the application,
414 // so do not let it touch the event...
415 qCDebug(KLEOPATRA_LOG);
416 if (d->controller.hasRunningCommands()) {
417 if (d->controller.shutdownWarningRequired()) {
418 const int ret = KMessageBox::warningContinueCancel(this, i18n("There are still some background operations ongoing. "
419 "These will be terminated when closing the window. "
420 "Proceed?"),
421 i18n("Ongoing Background Tasks"));
422 if (ret != KMessageBox::Continue) {
423 e->ignore();
424 return;
427 d->controller.cancelCommands();
428 if (d->controller.hasRunningCommands()) {
429 // wait for them to be finished:
430 setEnabled(false);
431 QEventLoop ev;
432 QTimer::singleShot(100, &ev, &QEventLoop::quit);
433 connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit);
434 ev.exec();
435 if (d->controller.hasRunningCommands())
436 qCWarning(KLEOPATRA_LOG)
437 << "controller still has commands running, this may crash now...";
438 setEnabled(true);
441 if (isQuitting || qApp->isSavingSession()) {
442 d->ui.tabWidget.saveViews(KSharedConfig::openConfig().data());
443 KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup()));
444 saveMainWindowSettings(grp);
445 e->accept();
446 } else {
447 e->ignore();
448 hide();
452 void MainWindow::showEvent(QShowEvent *e)
454 KXmlGuiWindow::showEvent(e);
455 if (d->firstShow) {
456 d->ui.tabWidget.loadViews(KSharedConfig::openConfig().data());
457 d->firstShow = false;
460 if (!savedGeometry.isEmpty()) {
461 restoreGeometry(savedGeometry);
466 void MainWindow::hideEvent(QHideEvent *e)
468 savedGeometry = saveGeometry();
469 KXmlGuiWindow::hideEvent(e);
472 void MainWindow::importCertificatesFromFile(const QStringList &files)
474 if (!files.empty()) {
475 d->createAndStart<ImportCertificateFromFileCommand>(files);
479 static QStringList extract_local_files(const QMimeData *data)
481 const QList<QUrl> urls = data->urls();
482 // begin workaround KDE/Qt misinterpretation of text/uri-list
483 QList<QUrl>::const_iterator end = urls.end();
484 if (urls.size() > 1 && !urls.back().isValid()) {
485 --end;
487 // end workaround
488 QStringList result;
489 std::transform(urls.begin(), end,
490 std::back_inserter(result),
491 boost::bind(&QUrl::toLocalFile, _1));
492 result.erase(std::remove_if(result.begin(), result.end(),
493 boost::bind(&QString::isEmpty, _1)), result.end());
494 return result;
497 static bool can_decode_local_files(const QMimeData *data)
499 if (!data) {
500 return false;
502 return !extract_local_files(data).empty();
505 void MainWindow::dragEnterEvent(QDragEnterEvent *e)
507 qCDebug(KLEOPATRA_LOG);
509 if (can_decode_local_files(e->mimeData())) {
510 e->acceptProposedAction();
514 void MainWindow::dropEvent(QDropEvent *e)
516 qCDebug(KLEOPATRA_LOG);
518 if (!can_decode_local_files(e->mimeData())) {
519 return;
522 e->setDropAction(Qt::CopyAction);
524 const QStringList files = extract_local_files(e->mimeData());
526 const unsigned int classification = classify(files);
528 QMenu menu;
530 QAction *const signEncrypt = menu.addAction(i18n("Sign/Encrypt..."));
531 QAction *const decryptVerify = mayBeAnyMessageType(classification) ? menu.addAction(i18n("Decrypt/Verify...")) : 0;
532 if (signEncrypt || decryptVerify) {
533 menu.addSeparator();
536 QAction *const importCerts = mayBeAnyCertStoreType(classification) ? menu.addAction(i18n("Import Certificates")) : 0;
537 QAction *const importCRLs = mayBeCertificateRevocationList(classification) ? menu.addAction(i18n("Import CRLs")) : 0;
538 if (importCerts || importCRLs) {
539 menu.addSeparator();
542 if (!signEncrypt && !decryptVerify && !importCerts && !importCRLs) {
543 return;
546 menu.addAction(i18n("Cancel"));
548 const QAction *const chosen = menu.exec(mapToGlobal(e->pos()));
550 if (!chosen) {
551 return;
554 if (chosen == signEncrypt) {
555 d->createAndStart<SignEncryptFilesCommand>(files);
556 } else if (chosen == decryptVerify) {
557 d->createAndStart<DecryptVerifyFilesCommand>(files);
558 } else if (chosen == importCerts) {
559 d->createAndStart<ImportCertificateFromFileCommand>(files);
560 } else if (chosen == importCRLs) {
561 d->createAndStart<ImportCrlCommand>(files);
564 e->accept();
567 void MainWindow::readProperties(const KConfigGroup &cg)
569 qCDebug(KLEOPATRA_LOG);
570 KXmlGuiWindow::readProperties(cg);
571 savedGeometry = cg.readEntry("savedGeometry", QByteArray());
572 if (!savedGeometry.isEmpty()) {
573 restoreGeometry(savedGeometry);
576 if (! cg.readEntry<bool>("hidden", false)) {
577 show();
581 void MainWindow::saveProperties(KConfigGroup &cg)
583 qCDebug(KLEOPATRA_LOG);
584 KXmlGuiWindow::saveProperties(cg);
585 cg.writeEntry("hidden", isHidden());
586 if (isHidden()) {
587 cg.writeEntry("savedGeometry", savedGeometry);
588 } else {
589 cg.writeEntry("savedGeometry", saveGeometry());
593 #include "moc_mainwindow_desktop.cpp"