Release 0.4.3-3maemo with patches to disable menus/actions, add ScrollArea and fix...
[keepassx] / src / dialogs / EditEntryDlg.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2006 by Tarek Saidi                                *
3  *   tarek.saidi@arcor.de                                                  *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; version 2 of the License.               *
8
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include <QFileDialog>
22 #include "SelectIconDlg.h"
23 #include "PasswordGenDlg.h"
24 #include "EditEntryDlg.h"
25 #include "CalendarDlg.h"
26
27 #ifdef GLOBAL_AUTOTYPE
28 #include "TargetWindowDlg.h"
29 #endif
30
31 CEditEntryDlg::CEditEntryDlg(IDatabase* _db, IEntryHandle* _entry,QWidget* parent, bool newEntry)
32 : QDialog(parent)
33 {
34         Q_ASSERT(_db);
35         Q_ASSERT(_entry);
36         entry=_entry;
37         db=_db;
38         pNewEntry=newEntry;
39         setupUi(this);
40         ModFlag=false;
41         QMenu *ExpirePresetsMenu=new QMenu();
42         
43         setGeometry( config->dialogGeometry(this) );
44
45         connect(Edit_Title, SIGNAL(textChanged(const QString&)), this, SLOT( OnTitleTextChanged(const QString&)));
46         connect(Edit_Password_w, SIGNAL(editingFinished()), this, SLOT(OnPasswordwLostFocus()));
47         connect(Edit_Password_w, SIGNAL(textChanged(const QString&)), this, SLOT( OnPasswordwTextChanged()));
48         connect(Edit_Password, SIGNAL(textChanged(const QString&)), this, SLOT( OnPasswordTextChanged()));
49         connect(ButtonEchoMode, SIGNAL(clicked()), this, SLOT( ChangeEchoMode()));
50         connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT( OnButtonCancel()));
51         connect(ButtonOpenAttachment, SIGNAL(clicked()), this, SLOT( OnNewAttachment()));
52         connect(ButtonDeleteAttachment, SIGNAL(clicked()), this, SLOT( OnDeleteAttachment()));
53         connect(ButtonSaveAttachment, SIGNAL(clicked()), this, SLOT( OnSaveAttachment()));
54         connect(ButtonGenPw, SIGNAL(clicked()), this, SLOT( OnButtonGenPw()));
55         connect(buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()),this,SLOT(OnButtonOK()));
56         connect(CheckBox_ExpiresNever,SIGNAL(stateChanged(int)),this,SLOT(OnCheckBoxExpiresNeverChanged(int)));
57         connect(Button_Icons,SIGNAL(clicked()),this,SLOT(OnButtonIcons()));
58         connect(ExpirePresetsMenu,SIGNAL(triggered(QAction*)),this,SLOT(OnExpirePreset(QAction*)));
59         connect(ButtonExpirePresets,SIGNAL(triggered(QAction*)),this,SLOT(OnCalendar(QAction*)));
60         connect(this, SIGNAL(finished(int)), this, SLOT(OnClose()));
61
62         // QAction::data() contains the time until expiration in days.
63         ExpirePresetsMenu->addAction(tr("Today"))->setData(0);
64         ExpirePresetsMenu->addSeparator();
65         ExpirePresetsMenu->addAction(tr("1 Week"))->setData(7);
66         ExpirePresetsMenu->addAction(tr("2 Weeks"))->setData(14);
67         ExpirePresetsMenu->addAction(tr("3 Weeks"))->setData(21);
68         ExpirePresetsMenu->addSeparator();
69         ExpirePresetsMenu->addAction(tr("1 Month"))->setData(30);
70         ExpirePresetsMenu->addAction(tr("3 Months"))->setData(90);
71         ExpirePresetsMenu->addAction(tr("6 Months"))->setData(180);
72         ExpirePresetsMenu->addSeparator();
73         ExpirePresetsMenu->addAction(tr("1 Year"))->setData(365);
74         ButtonExpirePresets->setMenu(ExpirePresetsMenu);
75         QAction* actionCalendar = new QAction(tr("Calendar..."),ButtonExpirePresets);
76         actionCalendar->setData(-1);
77         ButtonExpirePresets->setDefaultAction(actionCalendar);
78
79         IconIndex = entry->image();
80         Button_Icons->setIcon(db->icon(IconIndex));
81
82         ButtonOpenAttachment->setIcon(getIcon("fileopen"));
83         ButtonDeleteAttachment->setIcon(getIcon("filedelete"));
84         ButtonSaveAttachment->setIcon(getIcon("filesave"));
85         ButtonExpirePresets->setIcon(getIcon("clock"));
86
87     OnTitleTextChanged(entry->title());
88         setWindowIcon(db->icon(IconIndex));
89         Edit_Title->setText(entry->title());
90         Edit_UserName->setText(entry->username());
91         Edit_URL->setText(entry->url());
92         SecString Password=entry->password();
93         Password.unlock();
94         Edit_Password->setText(Password.string());
95         Edit_Password_w->setText(Password.string());
96         Password.lock();
97         if(!config->showPasswords())
98                 ChangeEchoMode();
99     else
100         ButtonEchoMode->setIcon(getIcon("pwd_show"));
101
102     // MX-COMMENT: This call is not needed. Both Passwords fields will always have the same value
103     OnPasswordwLostFocus();
104         int bits=(Password.length()*8);
105         Label_Bits->setText(tr("%1 Bit").arg(QString::number(bits)));
106         if(bits>128)
107                 bits=128;
108         Progress_Quali->setValue(100*bits/128);
109         Edit_Attachment->setText(entry->binaryDesc());
110         Edit_Comment->setPlainText(entry->comment());
111         InitGroupComboBox();
112
113         if(!entry->binarySize()){
114                 ButtonSaveAttachment->setDisabled(true);
115                 ButtonDeleteAttachment->setDisabled(true);
116                 Label_AttachmentSize->setText("");
117         }
118         else
119         Label_AttachmentSize->setText(entry->friendlySize());
120
121         if(entry->expire()==Date_Never){
122                 DateTime_Expire->setDisabled(true);
123                 CheckBox_ExpiresNever->setChecked(true);
124         }
125         else{
126                 DateTime_Expire->setDateTime(entry->expire());
127         }
128         
129 #ifdef AUTOTYPE
130         QToolButton* buttonTools = new QToolButton(buttonBox);
131         buttonTools->setText("Tools");
132         buttonTools->setPopupMode(QToolButton::InstantPopup);
133         QMenu* menuTools = new QMenu(buttonTools);
134         connect(menuTools->addAction("Auto-Type: Customize Sequence"), SIGNAL(triggered(bool)), SLOT(OnCustomizeSequence()));
135 #ifdef GLOBAL_AUTOTYPE
136         connect(menuTools->addAction("Auto-Type: Select target window"), SIGNAL(triggered(bool)), SLOT(OnSelectTarget()));
137 #endif
138         buttonTools->setMenu(menuTools);
139         buttonBox->addButton(buttonTools, QDialogButtonBox::ResetRole); // ResetRole: workaround to display button on the left
140 #endif
141 }
142
143 CEditEntryDlg::~CEditEntryDlg(){
144 }
145
146 void CEditEntryDlg::resizeEvent(QResizeEvent *event){
147         createBanner(&BannerPixmap,getPixmap("keepassx"),pNewEntry?tr("New Entry"):tr("Edit Entry"),width());
148         QDialog::resizeEvent(event);
149 }
150
151
152
153 void CEditEntryDlg::paintEvent(QPaintEvent *event){
154         QDialog::paintEvent(event);
155         QPainter painter(this);
156         painter.setClipRegion(event->region());
157         painter.drawPixmap(QPoint(0,0),BannerPixmap);
158 }
159
160 void CEditEntryDlg::InitGroupComboBox(){
161         QString Space;
162         groups=db->sortedGroups();
163         for(int i=0;i<groups.size();i++){
164                 Space.fill(' ', 2 * (groups[i]->level()));
165                 Combo_Group->insertItem(i,db->icon(groups[i]->image()),Space+groups[i]->title());
166                 if(groups[i]==entry->group()){
167                         Combo_Group->setCurrentIndex(i);
168                         GroupIndex=i;
169                 }
170         }
171 }
172
173 void CEditEntryDlg::OnButtonOK()
174 {
175         bool EntryMoved=false;
176         if(QString::compare(Edit_Password->text(),Edit_Password_w->text())!=0){
177                 QMessageBox::warning(NULL,tr("Warning"),tr("Password and password repetition are not equal.\nPlease check your input."),tr("OK"));
178                 return;
179         }
180
181         if(CheckBox_ExpiresNever->checkState()==Qt::Checked){
182                 DateTime_Expire->setDateTime(Date_Never);
183         }
184
185         if(DateTime_Expire->dateTime()!=entry->expire())
186                 ModFlag=true;
187         if(entry->title()!=Edit_Title->text())
188                 ModFlag=true;
189         if(entry->username()!=Edit_UserName->text())
190                 ModFlag=true;
191         if(entry->url()!=Edit_URL->text())
192                 ModFlag=true;
193         if(entry->comment()!=Edit_Comment->toPlainText())
194                 ModFlag=true;
195         SecString pw=entry->password();
196         pw.unlock();
197         QString password=pw.string();
198         if(password!=Edit_Password->text())
199                 ModFlag=true;
200         pw.lock();
201         password.fill('X');
202         if(entry->image()!=IconIndex)
203                 ModFlag=true;
204
205         if(ModFlag){
206                 QDateTime now = QDateTime::currentDateTime();
207                 entry->setExpire(DateTime_Expire->dateTime());
208                 entry->setLastAccess(now);
209                 entry->setLastMod(now);
210                 entry->setTitle(Edit_Title->text());
211                 entry->setUsername(Edit_UserName->text());
212                 entry->setUrl(Edit_URL->text());
213                 SecString pw;
214                 QString password=Edit_Password->text();
215                 pw.setString(password,true);
216                 entry->setPassword(pw);
217                 entry->setComment(Edit_Comment->toPlainText());
218                 entry->setImage(IconIndex);
219         }
220         if(Combo_Group->currentIndex()!=GroupIndex){
221                 db->moveEntry(entry,groups[Combo_Group->currentIndex()]);
222                 EntryMoved=true;
223         }
224
225         if(EntryMoved)
226                 done(ModFlag?2:3);
227         else if (ModFlag)
228                 done(1);
229         else
230                 done(0);
231 }
232
233 void CEditEntryDlg::OnButtonCancel()
234 {
235         entry->setLastAccess(QDateTime::currentDateTime());
236         done(0);
237 }
238
239 void CEditEntryDlg::ChangeEchoMode()
240 {
241         if(Edit_Password->echoMode()==QLineEdit::Normal){
242                 Edit_Password->setEchoMode(QLineEdit::Password);
243                 Edit_Password_w->setEchoMode(QLineEdit::Password);
244                 ButtonEchoMode->setIcon(getIcon("pwd_hide"));
245         }
246         else
247         {
248                 Edit_Password->setEchoMode(QLineEdit::Normal);
249                 Edit_Password_w->setEchoMode(QLineEdit::Normal);
250                 ButtonEchoMode->setIcon(getIcon("pwd_show"));
251         }
252 }
253
254 void CEditEntryDlg::OnTitleTextChanged(const QString& txt)
255 {
256         setWindowTitle((txt=="") ? tr("[Untitled Entry]") : txt);
257 }
258
259 void CEditEntryDlg::OnPasswordTextChanged()
260 {
261         Edit_Password_w->setText(QString());
262         int bits=(Edit_Password->text().length()*8);
263         Label_Bits->setText(QString::number(bits)+" Bit");
264         if(bits>128)bits=128;
265         Progress_Quali->setValue(100*bits/128);
266 }
267
268 void CEditEntryDlg::OnPasswordwTextChanged()
269 {
270         if(QString::compare(Edit_Password_w->text(),Edit_Password->text().mid(0,(Edit_Password_w->text().length())))!=0){
271                 QPalette palette;
272                 palette.setColor(Edit_Password_w->backgroundRole(),QColor(255,125,125));
273                 Edit_Password_w->setPalette(palette);
274         }
275         else {
276                 Edit_Password_w->setPalette(QApplication::palette());
277         }
278 }
279
280 void CEditEntryDlg::OnPasswordwLostFocus()
281 {
282         if (QString::compare(Edit_Password_w->text(),Edit_Password->text())!=0){
283                 QPalette palette;
284                 palette.setColor(Edit_Password_w->backgroundRole(),QColor(255,125,125));
285                 Edit_Password_w->setPalette(palette);
286         }
287         else {
288                 Edit_Password_w->setPalette(QApplication::palette ());
289         }
290 }
291
292 void CEditEntryDlg::OnNewAttachment()
293 {
294         QString filename=QFileDialog::getOpenFileName(this,tr("Add Attachment..."),QDir::homePath());
295         if(filename=="")return;
296         QFile file(filename);
297         if(!file.open(QIODevice::ReadOnly)){
298                 file.close();
299                 QMessageBox::warning(NULL,tr("Error"),tr("Could not open file."),tr("OK"));
300                 return;
301         }
302         ModFlag=true;
303         entry->setBinary(file.readAll());
304         file.close();
305         QFileInfo info(filename);
306         entry->setBinaryDesc(info.fileName());
307         Edit_Attachment->setText(entry->binaryDesc());
308         Label_AttachmentSize->setText(entry->friendlySize());
309         ButtonOpenAttachment->setEnabled(true);
310         ButtonSaveAttachment->setEnabled(true);
311         ButtonDeleteAttachment->setEnabled(true);
312 }
313
314 void CEditEntryDlg::OnSaveAttachment(){
315         saveAttachment(entry,this);
316 }
317
318 void CEditEntryDlg::saveAttachment(IEntryHandle* pEntry, QWidget* ParentWidget)
319 {
320         if(!pEntry->binarySize()){
321                 QMessageBox::information(NULL,tr("Error"),tr("The chosen entry has no attachment or it is empty."),tr("OK"));
322                 return;
323         }
324         QFileDialog FileDlg(ParentWidget,tr("Save Attachment..."),QDir::homePath());
325         FileDlg.selectFile(pEntry->binaryDesc());
326         FileDlg.setAcceptMode(QFileDialog::AcceptSave);
327         if(!FileDlg.exec())return;
328         QString filename=FileDlg.selectedFiles()[0];
329         QFile file(filename);
330         if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
331                 QMessageBox::critical(NULL,tr("Error"),tr("Could not open file."),tr("OK"));
332                 return;
333         }
334
335         int r=file.write(pEntry->binary());
336         if(r==-1){
337                 file.close();
338                 QMessageBox::critical(NULL,tr("Error"),tr("Error while writing the file."),tr("OK"));
339                 return;
340         }
341         if(r!=pEntry->binarySize()){
342                 file.close();
343                 QMessageBox::critical(NULL,tr("Error"),tr("Error while writing the file."),tr("OK"));
344                 return;
345         }
346         file.close();
347 }
348
349 void CEditEntryDlg::OnDeleteAttachment()
350 {
351         int r=QMessageBox::warning(this,tr("Delete Attachment?"),
352                         tr("You are about to delete the attachment of this entry.\nAre you sure?"),
353                         tr("Yes"),tr("No, Cancel"),NULL,1,1);
354         if(r==0){
355                 ModFlag=true;
356                 entry->setBinary(QByteArray());
357                 entry->setBinaryDesc("");
358                 Edit_Attachment->setText("");
359                 Label_AttachmentSize->setText("");
360                 ButtonOpenAttachment->setEnabled(true);
361                 ButtonSaveAttachment->setDisabled(true);
362                 ButtonDeleteAttachment->setDisabled(true);
363         }
364 }
365
366 void CEditEntryDlg::OnButtonGenPw()
367 {
368         CGenPwDialog dlg(this,false);
369         if(dlg.exec()){
370                 Edit_Password->setText(dlg.Edit_dest->text());
371                 Edit_Password_w->setText(dlg.Edit_dest->text());
372                 ModFlag=true;
373         }
374 }
375
376
377 void CEditEntryDlg::OnCheckBoxExpiresNeverChanged(int state){
378         if(state==Qt::Unchecked)
379                 DateTime_Expire->setDisabled(false);
380         else
381                 DateTime_Expire->setDisabled(true);
382 }
383
384 void CEditEntryDlg::OnButtonIcons(){
385         // CSelectIconDlg dlg(db,Combo_IconPicker->currentIndex(),this);
386         CSelectIconDlg dlg(db, IconIndex, this);
387         int r=dlg.exec();
388         if (r!=-1)
389         {
390                 IconIndex=r;
391                 Button_Icons->setIcon(db->icon(IconIndex));
392                 setWindowIcon(db->icon(IconIndex));
393         }
394 }
395
396 void CEditEntryDlg::OnExpirePreset(QAction* action){
397         CheckBox_ExpiresNever->setChecked(false);
398         int days = action->data().toInt();
399         switch (days){
400                 case 30:
401                 case 90:
402                 case 180:
403                         DateTime_Expire->setDate(QDate::currentDate().addMonths(days/30));
404                         break;
405                 case 365:
406                         DateTime_Expire->setDate(QDate::currentDate().addYears(1));
407                         break;
408                 default:
409                         DateTime_Expire->setDate(QDate::currentDate().addDays(days));
410         }
411         DateTime_Expire->setTime(QTime(0,0,0));
412 }
413
414 void CEditEntryDlg::OnCalendar(QAction* action){
415         if (action->data().toInt()!=-1)
416                 return;
417         
418         CalendarDialog dlg(this);
419         if(dlg.exec()==QDialog::Accepted){
420                 CheckBox_ExpiresNever->setChecked(false);
421                 DateTime_Expire->setDate(dlg.calendarWidget->selectedDate());
422                 DateTime_Expire->setTime(QTime(0,0,0));
423         }
424 }
425
426 void CEditEntryDlg::OnClose(){
427         config->setDialogGeometry(this);
428 }
429
430
431 void CEditEntryDlg::OnCustomizeSequence(){
432 #ifdef AUTOTYPE
433         QString text = Edit_Comment->toPlainText();
434         if (!text.isEmpty())
435                 text.append("\n");
436         Edit_Comment->setPlainText(text.append("Auto-Type: {USERNAME}{TAB}{PASSWORD}{ENTER}"));
437 #endif
438 }
439
440 void CEditEntryDlg::OnSelectTarget(){
441 #ifdef GLOBAL_AUTOTYPE
442         TargetWindowDlg dlg(this);
443         if (dlg.exec() && !dlg.windowTitle().isEmpty()){
444                 QString text = Edit_Comment->toPlainText();
445                 if (!text.isEmpty())
446                         text.append("\n");
447                 Edit_Comment->setPlainText(text.append("Auto-Type-Window: "+dlg.windowTitle()));
448         }
449 #endif
450 }