s/PasswordEchoOnEdit/Password/
[pinentry-qt.git] / pinentrydialog.cpp
1 /*
2    pinentrydialog.cpp - A (not yet) secure Qt 4 dialog for PIN entry.
3
4    Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB)
5    Copyright 2007 Ingo Klöcker
6
7    Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
8
9    This program is free software; you can redistribute it and/or
10    modify it under the terms of the GNU General Public License as
11    published by the Free Software Foundation; either version 2 of the
12    License, or (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful, but
15    WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24 #include "pinentrydialog.h"
25 #include <QGridLayout>
26
27 #include <QProgressBar>
28 #include <QApplication>
29 #include <QStyle>
30 #include <QPainter>
31 #include <QPushButton>
32 #include <QDialogButtonBox>
33 #include <QKeyEvent>
34 #include <QLabel>
35 #include <QPalette>
36
37 #ifdef Q_WS_WIN
38 #include <windows.h>
39 #endif
40
41 #if defined(Q_WS_WIN) && !defined(HAVE_W32CE_SYSTEM)
42 void SetForegroundWindowEx( HWND hWnd )
43 {
44    //Attach foreground window thread to our thread
45    const DWORD ForeGroundID = GetWindowThreadProcessId(::GetForegroundWindow(),NULL);
46    const DWORD CurrentID   = GetCurrentThreadId();
47  
48    AttachThreadInput ( ForeGroundID, CurrentID, TRUE );
49    //Do our stuff here
50    HWND hLastActivePopupWnd = GetLastActivePopup( hWnd );
51    SetForegroundWindow( hLastActivePopupWnd );
52  
53    //Detach the attached thread
54    AttachThreadInput ( ForeGroundID, CurrentID, FALSE );
55 }// End SetForegroundWindowEx
56 #endif
57
58 void raiseWindow( QWidget* w )
59 {
60 #ifdef HAVE_W32CE_SYSTEM
61     SetForegroundWindow( w->winId() );
62 #elif defined(Q_WS_WIN)
63     SetForegroundWindowEx( w->winId() );
64 #endif
65     w->raise(); 
66     w->activateWindow();
67 }
68
69 QPixmap icon( QStyle::StandardPixmap which )
70 {
71     QPixmap pm = qApp->windowIcon().pixmap( 48, 48 );
72    
73     if ( which != QStyle::SP_CustomBase ) {
74         const QIcon ic = qApp->style()->standardIcon( which );
75         QPainter painter( &pm );
76         const int emblemSize = 22;
77         painter.drawPixmap( pm.width()-emblemSize, 0,
78                             ic.pixmap( emblemSize, emblemSize ) );
79     }
80
81     return pm;
82 }
83
84 PinEntryDialog::PinEntryDialog( QWidget* parent, const char* name, bool modal,
85                                 bool enable_quality_bar )
86   : QDialog( parent ), _grabbed( false )
87 {
88   setWindowFlags( windowFlags() & ~Qt::WindowContextHelpButtonHint );
89
90   if ( modal ) {
91     setWindowModality( Qt::ApplicationModal );
92   }
93
94   _icon = new QLabel( this );
95   _icon->setPixmap( icon() );
96
97   _error = new QLabel( this );
98   _error->setWordWrap(true);
99   QPalette pal;
100   pal.setColor( QPalette::WindowText, Qt::red );
101   _error->setPalette( pal );
102   _error->hide();
103
104   _desc = new QLabel( this );
105   _desc->setWordWrap(true);
106   _desc->hide();
107
108   _prompt = new QLabel( this );
109   _prompt->hide();
110
111   _edit = new QSecureLineEdit( this );
112   _edit->setMaxLength( 256 );
113   /* FIXME: For unknown reasons PasswordEchoOnEdit does not work.  The
114      asterisk are only displayed after changing the focus.  Tested
115      with Qt 4.6.3.  */
116   _edit->setEchoMode( QLineEdit::Password );
117
118   _prompt->setBuddy( _edit );
119
120   if (enable_quality_bar)
121   {
122     _quality_bar_label = new QLabel( this );
123     _quality_bar_label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
124     _quality_bar = new QProgressBar( this );
125     _quality_bar->setAlignment( Qt::AlignCenter );
126     _have_quality_bar = true;
127   }
128   else
129     _have_quality_bar = false;
130
131   QDialogButtonBox* const buttons = new QDialogButtonBox( this );
132   buttons->setStandardButtons( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
133   _ok = buttons->button( QDialogButtonBox::Ok );
134   _cancel = buttons->button( QDialogButtonBox::Cancel );
135
136   _ok->setDefault(true);
137
138   if ( style()->styleHint( QStyle::SH_DialogButtonBox_ButtonsHaveIcons ) )
139     {
140       _ok->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) );
141       _cancel->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) );
142     }
143  
144   connect( buttons, SIGNAL(accepted()), this, SLOT(accept()) );
145   connect( buttons, SIGNAL(rejected()), this, SLOT(reject()) );
146   connect( _edit, SIGNAL( textChanged(const QString &) ),
147            this, SLOT( updateQuality(const QString &) ) );
148
149   _edit->setFocus();
150
151   QGridLayout* const grid = new QGridLayout( this );
152   grid->addWidget( _icon, 0, 0, 5, 1, Qt::AlignTop|Qt::AlignLeft );
153   grid->addWidget( _error, 1, 1, 1, 2 );
154   grid->addWidget( _desc,  2, 1, 1, 2 );
155   //grid->addItem( new QSpacerItem( 0, _edit->height() / 10, QSizePolicy::Minimum, QSizePolicy::Fixed ), 1, 1 );
156   grid->addWidget( _prompt, 3, 1 );
157   grid->addWidget( _edit, 3, 2 );
158   if( enable_quality_bar )
159   {
160     grid->addWidget( _quality_bar_label, 4, 1 );
161     grid->addWidget( _quality_bar, 4, 2 );
162   }
163   grid->addWidget( buttons, 5, 0, 1, 3 );
164
165   grid->setSizeConstraint( QLayout::SetFixedSize );
166 }
167
168 void PinEntryDialog::hideEvent( QHideEvent* ev )
169 {
170   if ( !_pinentry_info || _pinentry_info->grab )
171     _edit->releaseKeyboard();
172   _grabbed = false;
173   QDialog::hideEvent( ev );
174 }
175
176 void PinEntryDialog::showEvent( QShowEvent* event )
177 {
178     QDialog::showEvent( event );
179     raiseWindow( this );
180 }
181
182 void PinEntryDialog::setDescription( const QString& txt )
183 {
184   _desc->setVisible( !txt.isEmpty() );
185   _desc->setText( txt );
186   _icon->setPixmap( icon() );
187   setError( QString::null );
188 }
189
190 QString PinEntryDialog::description() const
191 {
192   return _desc->text();
193 }
194
195 void PinEntryDialog::setError( const QString& txt )
196 {
197   if( !txt.isNull() )_icon->setPixmap( icon( QStyle::SP_MessageBoxCritical ) );
198   _error->setText( txt );
199   _error->setVisible( !txt.isEmpty() );
200 }
201
202 QString PinEntryDialog::error() const
203 {
204   return _error->text();
205 }
206
207 void PinEntryDialog::setPin( const secqstring & txt )
208 {
209     _edit->setText( txt );
210 }
211
212 secqstring PinEntryDialog::pin() const
213 {
214     return _edit->text();
215 }
216
217 void PinEntryDialog::setPrompt( const QString& txt )
218 {
219   _prompt->setText( txt );
220   _prompt->setVisible( !txt.isEmpty() );
221 }
222
223 QString PinEntryDialog::prompt() const
224 {
225   return _prompt->text();
226 }
227
228 void PinEntryDialog::setOkText( const QString& txt )
229 {
230   _ok->setText( txt );
231   _ok->setVisible( !txt.isEmpty() );
232 }
233
234 void PinEntryDialog::setCancelText( const QString& txt )
235 {
236   _cancel->setText( txt );
237   _cancel->setVisible( !txt.isEmpty() );
238 }
239
240 void PinEntryDialog::setQualityBar( const QString& txt )
241 {
242   if (_have_quality_bar)
243     _quality_bar_label->setText( txt );
244 }
245
246 void PinEntryDialog::setQualityBarTT( const QString& txt )
247 {
248   if (_have_quality_bar)
249     _quality_bar->setToolTip( txt );
250 }
251
252 void PinEntryDialog::updateQuality(const secqstring & txt )
253 {
254   int length;
255   int percent;
256   QPalette pal;
257
258   if (!_have_quality_bar || !_pinentry_info)
259     return;
260   secstring pinStr(txt.toUtf8());
261   const char* pin = pinStr.c_str();
262   // The Qt3 version called ::secmem_free (pin) here, but from other usage of secstring,
263   // it seems like this is not needed anymore - 16 Mar. 2009 13:15 -- Jesper K. Pedersen
264   length = strlen (pin);
265   percent = length? pinentry_inq_quality (_pinentry_info, pin, length) : 0;
266   if (!length)
267     {
268       _quality_bar->reset ();
269     }
270   else
271     {
272       pal = _quality_bar->palette ();
273       if (percent < 0)
274         {
275           pal.setColor (QPalette::Highlight, QColor("red"));
276           percent = -percent;
277         }
278       else
279         {
280           pal.setColor (QPalette::Highlight, QColor("green"));
281         }
282       _quality_bar->setPalette (pal);
283       _quality_bar->setValue (percent);
284     }
285 }
286
287 void PinEntryDialog::setPinentryInfo(pinentry_t peinfo)
288 {
289     _pinentry_info = peinfo;
290 }
291
292 void PinEntryDialog::paintEvent( QPaintEvent* event )
293 {
294   // Grab keyboard. It might be a little weird to do it here, but it works!
295   // Previously this code was in showEvent, but that did not work in Qt4.
296   QDialog::paintEvent( event );
297   if ( !_grabbed && ( !_pinentry_info || _pinentry_info->grab ) ) {
298     _edit->grabKeyboard();
299     _grabbed = true;
300   }
301
302 }
303
304 #include "pinentrydialog.moc"