Make it a slot
[kdepim.git] / kleopatra / mainwindow_desktop.cpp
blob7d51039a05ddc578e6acc27f9fe9e0785fbf8183
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"
57 // from libkdepim
58 #include "progresswidget/statusbarprogresswidget.h"
59 #include "progresswidget/progressdialog.h"
61 #include <KActionCollection>
62 #include <KLocale>
63 #include <KTabWidget>
64 #include <KStatusBar>
65 #include <KStandardAction>
66 #include <KAction>
67 #include <KAboutData>
68 #include <KMessageBox>
69 #include <KStandardGuiItem>
70 #include <KStandardDirs>
71 #include <KShortcutsDialog>
72 #include <KXMLGUIFactory>
73 #include <KEditToolBar>
74 #include <KAboutApplicationDialog>
75 #include <kdebug.h>
77 #include <QAbstractItemView>
78 #include <QFile>
79 #include <QToolBar>
80 #include <QWidgetAction>
81 #include <QApplication>
82 #include <QCloseEvent>
83 #include <QMenu>
84 #include <QTimer>
85 #include <QProcess>
86 #include <QPointer>
88 #include <kleo/cryptobackendfactory.h>
89 #include <ui/cryptoconfigdialog.h>
90 #include <kleo/cryptoconfig.h>
91 #include <kleo/stl_util.h>
93 #include <boost/bind.hpp>
94 #include <boost/shared_ptr.hpp>
96 #include <vector>
98 #ifdef Q_OS_WIN32
99 static const bool OS_WIN = true;
100 #else
101 static const bool OS_WIN = false;
102 #endif
104 using namespace Kleo;
105 using namespace Kleo::Commands;
106 using namespace boost;
107 using namespace GpgME;
109 namespace {
111 static const KAboutData * aboutGpg4WinData() {
112 static const AboutGpg4WinData data;
113 return &data;
118 static KGuiItem KStandardGuiItem_quit() {
119 static const QString app = KGlobal::mainComponent().aboutData()->programName();
120 KGuiItem item = KStandardGuiItem::quit();
121 item.setText( i18nc( "Quit [ApplicationName]", "&Quit %1", app ) );
122 return item;
125 static KGuiItem KStandardGuiItem_close() {
126 KGuiItem item = KStandardGuiItem::close();
127 item.setText( i18n("Only &Close Window" ) );
128 return item;
131 class MainWindow::Private {
132 friend class ::MainWindow;
133 MainWindow * const q;
135 public:
136 explicit Private( MainWindow * qq );
137 ~Private();
139 template <typename T>
140 void createAndStart() {
141 ( new T( this->currentView(), &this->controller ) )->start();
143 template <typename T>
144 void createAndStart( QAbstractItemView * view ) {
145 ( new T( view, &this->controller ) )->start();
147 template <typename T>
148 void createAndStart( const QStringList & a ) {
149 ( new T( a, this->currentView(), &this->controller ) )->start();
151 template <typename T>
152 void createAndStart( const QStringList & a, QAbstractItemView * view ) {
153 ( new T( a, view, &this->controller ) )->start();
156 void closeAndQuit() {
157 const QString app = KGlobal::mainComponent().aboutData()->programName();
158 const int rc = KMessageBox::questionYesNoCancel( q,
159 i18n("%1 may be used by other applications as a service.\n"
160 "You may instead want to close this window without exiting %1.", app ),
161 i18n("Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(),
162 "really-quit-" + app.toLower() );
163 if ( rc == KMessageBox::Cancel )
164 return;
165 if ( !q->close() )
166 return;
167 // WARNING: 'this' might be deleted at this point!
168 if ( rc == KMessageBox::No )
169 qApp->quit();
171 void configureToolbars() {
172 KEditToolBar dlg( q->factory() );
173 dlg.exec();
175 void editKeybindings() {
176 KShortcutsDialog::configure( q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed );
179 void selfTest() {
180 createAndStart<SelfTestCommand>();
182 void configureBackend();
184 void showHandbook();
186 void gnupgLogViewer() {
187 if( !QProcess::startDetached("kwatchgnupg" ) )
188 KMessageBox::error( q, i18n( "Could not start the GnuPG Log Viewer (kwatchgnupg). "
189 "Please check your installation." ),
190 i18n( "Error Starting KWatchGnuPG" ) );
193 void gnupgAdministrativeConsole() {
194 if( !QProcess::startDetached("kgpgconf" ) )
195 KMessageBox::error( q, i18n( "Could not start the GnuPG Administrative Console (kgpgconf). "
196 "Please check your installation." ),
197 i18n( "Error Starting KGpgConf" ) );
200 void slotConfigCommitted();
201 void slotContextMenuRequested( QAbstractItemView *, const QPoint & p ) {
202 if ( QMenu * const menu = qobject_cast<QMenu*>( q->factory()->container( "listview_popup", q ) ) )
203 menu->exec( p );
204 else
205 kDebug() << "no \"listview_popup\" <Menu> in kleopatra's ui.rc file";
208 void aboutGpg4Win() {
209 ( new KAboutApplicationDialog( aboutGpg4WinData(), KAboutApplicationDialog::HideKdeVersion|KAboutApplicationDialog::HideTranslators, q ) )->show();
212 private:
213 void setupActions();
215 QAbstractItemView * currentView() const {
216 return ui.tabWidget.currentView();
219 private:
220 Kleo::KeyListController controller;
221 bool firstShow : 1;
223 struct UI {
225 TabWidget tabWidget;
227 explicit UI( MainWindow * q )
228 : tabWidget( q )
230 KDAB_SET_OBJECT_NAME( tabWidget );
232 q->setCentralWidget(&tabWidget);
233 KPIM::ProgressDialog * progressDialog = new KPIM::ProgressDialog( q->statusBar(), q );
234 KDAB_SET_OBJECT_NAME( progressDialog );
235 progressDialog->hide();
236 KPIM::StatusbarProgressWidget * statusBarProgressWidget
237 = new KPIM::StatusbarProgressWidget( progressDialog, q->statusBar() );
238 KDAB_SET_OBJECT_NAME( statusBarProgressWidget );
239 q->statusBar()->addPermanentWidget( statusBarProgressWidget, 0 );
240 statusBarProgressWidget->show();
242 } ui;
245 MainWindow::Private::Private( MainWindow * qq )
246 : q( qq ),
247 controller( q ),
248 firstShow( true ),
249 ui( q )
251 KDAB_SET_OBJECT_NAME( controller );
253 AbstractKeyListModel * flatModel = AbstractKeyListModel::createFlatKeyListModel( q );
254 AbstractKeyListModel * hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel( q );
256 KDAB_SET_OBJECT_NAME( flatModel );
257 KDAB_SET_OBJECT_NAME( hierarchicalModel );
260 controller.setFlatModel( flatModel );
261 controller.setHierarchicalModel( hierarchicalModel );
262 controller.setTabWidget( &ui.tabWidget );
264 ui.tabWidget.setFlatModel( flatModel );
265 ui.tabWidget.setHierarchicalModel( hierarchicalModel );
267 setupActions();
269 connect( &controller, SIGNAL(message(QString,int)), q->statusBar(), SLOT(showMessage(QString,int)) );
270 connect( &controller, SIGNAL(contextMenuRequested(QAbstractItemView*,QPoint)),
271 q, SLOT(slotContextMenuRequested(QAbstractItemView*,QPoint)) );
273 q->createGUI( "kleopatra.rc" );
275 q->setAcceptDrops( true );
277 q->setAutoSaveSettings();
280 MainWindow::Private::~Private() {}
282 MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags )
283 : KXmlGuiWindow( parent, flags ), d( new Private( this ) )
287 MainWindow::~MainWindow() {}
290 void MainWindow::Private::setupActions() {
292 KActionCollection * const coll = q->actionCollection();
294 QWidgetAction * const searchBarAction = new QWidgetAction( q );
295 SearchBar * const searchBar = new SearchBar( q );
297 ui.tabWidget.connectSearchBar( searchBar );
299 searchBarAction->setDefaultWidget( searchBar );
300 coll->addAction( "key_search_bar", searchBarAction );
302 const action_data action_data[] = {
303 // most have been MOVED TO keylistcontroller.cpp
304 // Tools menu
305 #ifndef Q_OS_WIN
306 { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(),
307 "kwatchgnupg", q, SLOT(gnupgLogViewer()), QString(), false, true },
308 #endif
309 #if 0
310 { "tools_start_kgpgconf", i18n("GnuPG Administrative Console"), QString(),
311 "kgpgconf", q, SLOT(gnupgLogViewer()), QString(), false, true },
312 #endif
313 // most have been MOVED TO keylistcontroller.cpp
314 #if 0
315 { "configure_backend", i18n("Configure GnuPG Backend..."), QString(),
316 0, q, SLOT(configureBackend()), QString(), false, true },
317 #endif
318 // Settings menu
319 { "settings_self_test", i18n("Perform Self-Test"), QString(),
320 0, q, SLOT(selfTest()), QString(), false, true },
321 // Help menu
322 #ifdef Q_WS_WIN
323 { "help_about_gpg4win", i18n("About Gpg4win"), QString(),
324 "gpg4win-compact", q, SLOT(aboutGpg4Win()), QString(), false, true },
325 #endif
326 // most have been MOVED TO keylistcontroller.cpp
329 make_actions_from_data( action_data, /*sizeof action_data / sizeof *action_data,*/ coll );
331 if ( QAction * action = coll->action( "configure_backend" ) )
332 action->setMenuRole( QAction::NoRole ); //prevent Qt OS X heuristics for config* actions
334 KStandardAction::close( q, SLOT(close()), coll );
335 KStandardAction::quit( q, SLOT(closeAndQuit()), coll );
336 KStandardAction::configureToolbars( q, SLOT(configureToolbars()), coll );
337 KStandardAction::keyBindings( q, SLOT(editKeybindings()), coll );
338 KStandardAction::preferences( qApp, SLOT(openOrRaiseConfigDialog()), coll );
340 q->createStandardStatusBarAction();
341 q->setStandardToolBarMenuEnabled( true );
343 controller.createActions( coll );
345 ui.tabWidget.createActions( coll );
348 void MainWindow::Private::configureBackend() {
349 Kleo::CryptoConfig * const config = Kleo::CryptoBackendFactory::instance()->config();
350 if ( !config ) {
351 KMessageBox::error( q, i18n( "Could not configure the cryptography backend (gpgconf tool not found)" ), i18n( "Configuration Error" ) );
352 return;
355 Kleo::CryptoConfigDialog dlg( config );
357 const int result = dlg.exec();
359 // Forget all data parsed from gpgconf, so that we show updated information
360 // when reopening the configuration dialog.
361 config->clear();
363 if ( result == QDialog::Accepted ) {
364 #if 0
365 // Tell other apps (e.g. kmail) that the gpgconf data might have changed
366 QDBusMessage message =
367 QDBusMessage::createSignal(QString(), "org.kde.kleo.CryptoConfig", "changed");
368 QDBusConnection::sessionBus().send(message);
369 #endif
373 void MainWindow::Private::slotConfigCommitted() {
374 controller.updateConfig();
377 void MainWindow::closeEvent( QCloseEvent * e ) {
378 // KMainWindow::closeEvent() insists on quitting the application,
379 // so do not let it touch the event...
380 kDebug();
381 if ( d->controller.hasRunningCommands() ) {
382 if ( d->controller.shutdownWarningRequired() ) {
383 const int ret = KMessageBox::warningContinueCancel( this, i18n("There are still some background operations ongoing. "
384 "These will be terminated when closing the window. "
385 "Proceed?"),
386 i18n("Ongoing Background Tasks") );
387 if ( ret != KMessageBox::Continue ) {
388 e->ignore();
389 return;
392 d->controller.cancelCommands();
393 if ( d->controller.hasRunningCommands() ) {
394 // wait for them to be finished:
395 setEnabled( false );
396 QEventLoop ev;
397 QTimer::singleShot( 100, &ev, SLOT(quit()) );
398 connect( &d->controller, SIGNAL(commandsExecuting(bool)), &ev, SLOT(quit()) );
399 ev.exec();
400 kWarning( d->controller.hasRunningCommands() )
401 << "controller still has commands running, this may crash now...";
402 setEnabled( true );
405 d->ui.tabWidget.saveViews( KGlobal::config().data() );
406 saveMainWindowSettings( KConfigGroup( KGlobal::config(), autoSaveGroup() ) );
407 e->accept();
410 void MainWindow::showEvent( QShowEvent * e ) {
411 KXmlGuiWindow::showEvent( e );
412 if ( d->firstShow ) {
413 d->ui.tabWidget.loadViews( KGlobal::config().data() );
414 d->firstShow = false;
418 void MainWindow::importCertificatesFromFile( const QStringList & files ) {
419 if ( !files.empty() )
420 d->createAndStart<ImportCertificateFromFileCommand>( files );
424 static QStringList extract_local_files( const QMimeData * data ) {
425 const QList<QUrl> urls = data->urls();
426 // begin workaround KDE/Qt misinterpretation of text/uri-list
427 QList<QUrl>::const_iterator end = urls.end();
428 if ( urls.size() > 1 && !urls.back().isValid() )
429 --end;
430 // end workaround
431 QStringList result;
432 std::transform( urls.begin(), end,
433 std::back_inserter( result ),
434 boost::bind( &QUrl::toLocalFile, _1 ) );
435 result.erase( std::remove_if( result.begin(), result.end(),
436 boost::bind( &QString::isEmpty, _1 ) ), result.end() );
437 return result;
440 static bool can_decode_local_files( const QMimeData * data ) {
441 if ( !data )
442 return false;
443 return !extract_local_files( data ).empty();
446 void MainWindow::dragEnterEvent( QDragEnterEvent * e ) {
447 kDebug();
449 if ( can_decode_local_files( e->mimeData() ) )
450 e->acceptProposedAction();
453 void MainWindow::dropEvent( QDropEvent * e ) {
454 kDebug();
456 if ( !can_decode_local_files( e->mimeData() ) )
457 return;
459 e->setDropAction( Qt::CopyAction );
461 const QStringList files = extract_local_files( e->mimeData() );
463 const unsigned int classification = classify( files );
465 QMenu menu;
467 QAction * const signEncrypt = menu.addAction( i18n("Sign/Encrypt...") );
468 QAction * const decryptVerify = mayBeAnyMessageType( classification ) ? menu.addAction( i18n("Decrypt/Verify...") ) : 0 ;
469 if ( signEncrypt || decryptVerify )
470 menu.addSeparator();
472 QAction * const importCerts = mayBeAnyCertStoreType( classification ) ? menu.addAction( i18n("Import Certificates") ) : 0 ;
473 QAction * const importCRLs = mayBeCertificateRevocationList( classification ) ? menu.addAction( i18n("Import CRLs") ) : 0 ;
474 if ( importCerts || importCRLs )
475 menu.addSeparator();
477 if ( !signEncrypt && !decryptVerify && !importCerts && !importCRLs )
478 return;
480 menu.addAction( i18n("Cancel") );
482 const QAction * const chosen = menu.exec( mapToGlobal( e->pos() ) );
484 if ( !chosen )
485 return;
487 if ( chosen == signEncrypt )
488 d->createAndStart<SignEncryptFilesCommand>( files );
489 else if ( chosen == decryptVerify )
490 d->createAndStart<DecryptVerifyFilesCommand>( files );
491 else if ( chosen == importCerts )
492 d->createAndStart<ImportCertificateFromFileCommand>( files );
493 else if ( chosen == importCRLs )
494 d->createAndStart<ImportCrlCommand>( files );
496 e->accept();
499 #include "moc_mainwindow_desktop.cpp"