1 /* -*- mode: c++; c-basic-offset:4 -*-
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
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"
58 #include "progresswidget/statusbarprogresswidget.h"
59 #include "progresswidget/progressdialog.h"
61 #include <KActionCollection>
65 #include <KStandardAction>
68 #include <KMessageBox>
69 #include <KStandardGuiItem>
70 #include <KStandardDirs>
71 #include <KShortcutsDialog>
72 #include <KXMLGUIFactory>
73 #include <KEditToolBar>
74 #include <KAboutApplicationDialog>
77 #include <QAbstractItemView>
80 #include <QWidgetAction>
81 #include <QApplication>
82 #include <QCloseEvent>
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>
99 static const bool OS_WIN
= true;
101 static const bool OS_WIN
= false;
104 using namespace Kleo
;
105 using namespace Kleo::Commands
;
106 using namespace boost
;
107 using namespace GpgME
;
111 static const KAboutData
* aboutGpg4WinData() {
112 static const AboutGpg4WinData 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
) );
125 static KGuiItem
KStandardGuiItem_close() {
126 KGuiItem item
= KStandardGuiItem::close();
127 item
.setText( i18n("Only &Close Window" ) );
131 class MainWindow::Private
{
132 friend class ::MainWindow
;
133 MainWindow
* const q
;
136 explicit Private( MainWindow
* qq
);
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
)
167 // WARNING: 'this' might be deleted at this point!
168 if ( rc
== KMessageBox::No
)
171 void configureToolbars() {
172 KEditToolBar
dlg( q
->factory() );
175 void editKeybindings() {
176 KShortcutsDialog::configure( q
->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed
);
180 createAndStart
<SelfTestCommand
>();
182 void configureBackend();
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
) ) )
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();
215 QAbstractItemView
* currentView() const {
216 return ui
.tabWidget
.currentView();
220 Kleo::KeyListController controller
;
227 explicit UI( MainWindow
* 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();
245 MainWindow::Private::Private( MainWindow
* qq
)
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
);
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
306 { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(),
307 "kwatchgnupg", q
, SLOT(gnupgLogViewer()), QString(), false, true },
310 { "tools_start_kgpgconf", i18n("GnuPG Administrative Console"), QString(),
311 "kgpgconf", q
, SLOT(gnupgLogViewer()), QString(), false, true },
313 // most have been MOVED TO keylistcontroller.cpp
315 { "configure_backend", i18n("Configure GnuPG Backend..."), QString(),
316 0, q
, SLOT(configureBackend()), QString(), false, true },
319 { "settings_self_test", i18n("Perform Self-Test"), QString(),
320 0, q
, SLOT(selfTest()), QString(), false, true },
323 { "help_about_gpg4win", i18n("About Gpg4win"), QString(),
324 "gpg4win-compact", q
, SLOT(aboutGpg4Win()), QString(), false, true },
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();
351 KMessageBox::error( q
, i18n( "Could not configure the cryptography backend (gpgconf tool not found)" ), i18n( "Configuration Error" ) );
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.
363 if ( result
== QDialog::Accepted
) {
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
);
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...
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. "
386 i18n("Ongoing Background Tasks") );
387 if ( ret
!= KMessageBox::Continue
) {
392 d
->controller
.cancelCommands();
393 if ( d
->controller
.hasRunningCommands() ) {
394 // wait for them to be finished:
397 QTimer::singleShot( 100, &ev
, SLOT(quit()) );
398 connect( &d
->controller
, SIGNAL(commandsExecuting(bool)), &ev
, SLOT(quit()) );
400 kWarning( d
->controller
.hasRunningCommands() )
401 << "controller still has commands running, this may crash now...";
405 d
->ui
.tabWidget
.saveViews( KGlobal::config().data() );
406 saveMainWindowSettings( KConfigGroup( KGlobal::config(), autoSaveGroup() ) );
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() )
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() );
440 static bool can_decode_local_files( const QMimeData
* data
) {
443 return !extract_local_files( data
).empty();
446 void MainWindow::dragEnterEvent( QDragEnterEvent
* e
) {
449 if ( can_decode_local_files( e
->mimeData() ) )
450 e
->acceptProposedAction();
453 void MainWindow::dropEvent( QDropEvent
* e
) {
456 if ( !can_decode_local_files( e
->mimeData() ) )
459 e
->setDropAction( Qt::CopyAction
);
461 const QStringList files
= extract_local_files( e
->mimeData() );
463 const unsigned int classification
= classify( files
);
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
)
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
)
477 if ( !signEncrypt
&& !decryptVerify
&& !importCerts
&& !importCRLs
)
480 menu
.addAction( i18n("Cancel") );
482 const QAction
* const chosen
= menu
.exec( mapToGlobal( e
->pos() ) );
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
);
499 #include "moc_mainwindow_desktop.cpp"