Initial commit
[keepassx] / src / lib / EntryView.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2007 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 <QHeaderView>
22 #include <QClipboard>
23 #include <QProcess>
24 #include <algorithm>
25 #include "lib/AutoType.h"
26 #include "lib/EntryView.h"
27 #include "dialogs/EditEntryDlg.h"
28
29 #define NUM_COLUMNS 11
30
31 // just for the lessThan funtion
32 /*QList<EntryViewItem*>* pItems;
33 KeepassEntryView* pEntryView;*/
34
35 KeepassEntryView::KeepassEntryView(QWidget* parent) : QTreeWidget(parent) {
36         ViewMode=Normal;
37         AutoResizeColumns = true;
38         header()->setResizeMode(QHeaderView::Interactive);
39         header()->setStretchLastSection(false);
40         header()->setClickable(true);
41         header()->setCascadingSectionResizes(true);
42         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
43         retranslateColumns();
44         restoreHeaderView();
45         
46         connect(header(), SIGNAL(sectionResized(int,int,int)), SLOT(resizeColumns()));
47         connect(this,SIGNAL(itemSelectionChanged()), SLOT(OnItemsChanged()));
48         connect(&ClipboardTimer, SIGNAL(timeout()), SLOT(OnClipboardTimeOut()));
49         connect(this, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(OnEntryActivated(QTreeWidgetItem*,int)));
50         connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(OnEntryDblClicked(QTreeWidgetItem*,int)));
51         Clipboard=QApplication::clipboard();
52         ContextMenu=new QMenu(this);
53         setAlternatingRowColors(config->alternatingRowColors());
54
55         /*pItems=&Items;
56         pEntryView=this;*/
57 }
58
59 KeepassEntryView::~KeepassEntryView(){
60         saveHeaderView();
61 }
62
63 void KeepassEntryView::retranslateColumns() {
64         setHeaderLabels( QStringList() << tr("Title") << tr("Username") << tr("URL") << tr("Password") << tr("Comments")
65                 << tr("Expires") << tr("Creation") << tr("Last Change") << tr("Last Access") << tr("Attachment") << tr("Group") );
66 }
67
68 bool KeepassEntryView::columnVisible(int col) {
69         return !header()->isSectionHidden(col);
70 }
71
72 void KeepassEntryView::setColumnVisible(int col, bool visible) {
73         if (columnVisible(col) == visible)
74                 return; // nothing to do
75         
76         header()->setSectionHidden(col, !visible);
77         if (visible)
78                 header()->resizeSection(col, columnSizes[col]);
79 }
80
81 void KeepassEntryView::saveHeaderView() {
82         QBitArray columns(NUM_COLUMNS);
83         QList<int> columnOrder;
84         int columnSort = header()->sortIndicatorSection();
85         Qt::SortOrder columnSortOrder = header()->sortIndicatorOrder();
86         
87         for (int i=0; i<NUM_COLUMNS; ++i) {
88                 columns.setBit(i, columnVisible(i));
89                 columnOrder << header()->visualIndex(i);
90         }
91         
92         if (ViewMode == Normal) {
93                 config->setColumns(columns);
94                 config->setColumnOrder(columnOrder);
95                 config->setColumnSizes(columnSizes);
96                 config->setColumnSort(columnSort);
97                 config->setColumnSortOrder(columnSortOrder);
98         }
99         else {
100                 config->setSearchColumns(columns);
101                 config->setSearchColumnOrder(columnOrder);
102                 config->setSearchColumnSizes(columnSizes);
103                 config->setSearchColumnSort(columnSort);
104                 config->setSearchColumnSortOrder(columnSortOrder);
105         }
106 }
107
108 void KeepassEntryView::restoreHeaderView() {
109         AutoResizeColumns = false;
110         
111         QBitArray columns;
112         QList<int> columnOrder;
113         int columnSort;
114         Qt::SortOrder columnSortOrder;
115         
116         if (ViewMode == Normal) {
117                 columns = config->columns();
118                 columnOrder = config->columnOrder();
119                 columnSizes = config->columnSizes();
120                 columnSort = config->columnSort();
121                 columnSortOrder = config->columnSortOrder();
122                 columns[10] = 0; // just to be sure
123         }
124         else {
125                 columns = config->searchColumns();
126                 columnOrder = config->searchColumnOrder();
127                 columnSizes = config->searchColumnSizes();
128                 columnSort = config->searchColumnSort();
129                 columnSortOrder = config->searchColumnSortOrder();
130         }
131         
132         // compatibility with KeePassX <= 0.4.0 (100 = column hidden)
133         int lastVisibleIndex = -1;
134         for (int i=0; i<NUM_COLUMNS; ++i) {
135                 if (columnOrder[i]!=100 && columnOrder[i]>lastVisibleIndex)
136                         lastVisibleIndex = columnOrder[i];
137         }
138         
139         QMap<int,int> order; // key=visual index; value=logical index
140         for (int i=0; i<NUM_COLUMNS; ++i) {
141                 if (columnOrder[i] == 100)
142                         columnOrder[i] = ++lastVisibleIndex;
143                 
144                 order.insert(columnOrder[i], i);
145                 setColumnVisible(i, false); // initally hide all columns
146                 if (columnSizes[i] < header()->minimumSectionSize())
147                         columnSizes[i] = header()->minimumSectionSize();
148         }
149         
150         for (QMap<int,int>::const_iterator i = order.constBegin(); i != order.constEnd(); ++i) {
151                 header()->moveSection(header()->visualIndex(i.value()), NUM_COLUMNS-1);
152                 header()->resizeSection(i.value(), columnSizes[i.value()]);
153                 setColumnVisible(i.value(), columns.testBit(i.value()));
154         }
155         
156         header()->setSortIndicator(columnSort, columnSortOrder);
157         
158         AutoResizeColumns = true;
159         
160         resizeColumns();
161 }
162
163 void KeepassEntryView::resizeColumns() {
164         if (!AutoResizeColumns)
165                 return;
166         
167         AutoResizeColumns = false;
168         
169         int w = viewport()->width();
170         int sum = 0;
171         
172         for (int i=0; i<NUM_COLUMNS; ++i) {
173                 if (columnVisible(i))
174                         sum += header()->sectionSize(i);
175         }
176         
177         double stretch = (double)w / (double)sum;
178         
179         for (int i=0; i<NUM_COLUMNS; ++i) {
180                 if (columnVisible(i) && header()->sectionSize(i)!=0) {
181                         int size = qRound(header()->sectionSize(i) * stretch);
182                         header()->resizeSection(i, size);
183                         columnSizes[i] = size;
184                 }
185                 else {
186                         columnSizes[i] = qRound(columnSizes[i] * stretch);
187                 }
188         }
189         
190         AutoResizeColumns = true;
191 }
192
193 void KeepassEntryView::OnGroupChanged(IGroupHandle* group){
194         CurrentGroup=group;
195         showGroup(group);
196 }
197
198 void KeepassEntryView::OnShowSearchResults(){
199         CurrentGroup=NULL;
200         showSearchResults();
201 }
202
203 void KeepassEntryView::OnItemsChanged(){
204         switch(selectedItems().size()){
205                 case 0: emit selectionChanged(NONE);
206                                 break;
207                 case 1: emit selectionChanged(SINGLE);
208                                 break;
209                 default:emit selectionChanged(MULTIPLE);
210         }
211 }
212
213 void KeepassEntryView::OnSaveAttachment(){
214         if (selectedItems().size() == 0) return;
215         CEditEntryDlg::saveAttachment(((EntryViewItem*)selectedItems().first())->EntryHandle,this);
216 }
217
218 void KeepassEntryView::OnCloneEntry(){
219         QList<QTreeWidgetItem*> entries=selectedItems();
220         for(int i=0; i<entries.size();i++){
221                 Items.append(new EntryViewItem(this));
222                 Items.back()->EntryHandle=
223                         db->cloneEntry(((EntryViewItem*)entries[i])->EntryHandle);
224                 updateEntry(Items.back());
225         }
226         if (header()->isSortIndicatorShown())
227                 sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
228         emit fileModified();
229 }
230
231 void KeepassEntryView::OnDeleteEntry(){
232         QList<QTreeWidgetItem*> entries=selectedItems();
233         
234         if(config->askBeforeDelete()){
235                 QString text;
236                 if(entries.size()==1)
237                         text=tr("Are you sure you want to delete this entry?");
238                 else
239                         text=tr("Are you sure you want to delete these %1 entries?").arg(entries.size());
240                 if(QMessageBox::question(this,tr("Delete?"),text,QMessageBox::Yes | QMessageBox::No,QMessageBox::No)==QMessageBox::No)
241                         return;
242         }
243         
244         bool backup = false;
245         IGroupHandle* bGroup = NULL;
246         if (config->backup() && ((EntryViewItem*)entries[0])->EntryHandle->group() != (bGroup=db->backupGroup()))
247                 backup = true;
248         if (backup && !bGroup) {
249                 emit requestCreateGroup("Backup", 4, NULL);
250                 bGroup = db->backupGroup();
251         }
252         for(int i=0; i<entries.size();i++){
253                 IEntryHandle* entryHandle = ((EntryViewItem*)entries[i])->EntryHandle;
254                 if (backup && bGroup){
255                         db->moveEntry(entryHandle, bGroup);
256                         QDateTime now = QDateTime::currentDateTime();
257                         entryHandle->setLastAccess(now);
258                         entryHandle->setLastMod(now);
259                 }
260                 else{
261                         db->deleteEntry(entryHandle);
262                 }
263                 Items.removeAt(Items.indexOf((EntryViewItem*)entries[i]));
264                 delete entries[i];
265         }
266         emit fileModified();
267 }
268
269 QString KeepassEntryView::columnString(IEntryHandle* entry, int col, bool forceClearText) {
270         switch (col) {
271                 case 0:
272                         return entry->title();
273                 case 1:
274                         if (config->hideUsernames() && !forceClearText)
275                                 return "******";
276                         else
277                                 return entry->username();
278                 case 2:
279                         return entry->url();
280                 case 3:
281                 {
282                         if (config->hidePasswords() && !forceClearText) {
283                                 return "******";
284                         }
285                         else {
286                                 SecString password = entry->password();
287                                 password.unlock();
288                                 return password.string();
289                         }
290                 }
291                 case 4:
292                 {
293                         QString comment = entry->comment();
294                         int toPos = comment.indexOf(QRegExp("[\\r\\n]"));
295                         if (toPos == -1)
296                                 return comment;
297                         else
298                                 return comment.left(toPos);
299                 }
300                 case 5:
301                         return entry->expire().dateToString(Qt::SystemLocaleDate);
302                 case 6:
303                         return entry->creation().dateToString(Qt::SystemLocaleDate);
304                 case 7:
305                         return entry->lastMod().dateToString(Qt::SystemLocaleDate);
306                 case 8:
307                         return entry->lastAccess().dateToString(Qt::SystemLocaleDate);
308                 case 9:
309                         return entry->binaryDesc();
310                 case 10:
311                         return entry->group()->title();
312                 default:
313                         Q_ASSERT(false);
314                         return QString();
315         }
316 }
317
318 void KeepassEntryView::updateEntry(EntryViewItem* item){
319         IEntryHandle* entry = item->EntryHandle;
320         
321         int cols = NUM_COLUMNS - 1;
322         if (ViewMode == ShowSearchResults) {
323                 item->setIcon(10, db->icon(entry->group()->image()));
324                 ++cols;
325         }
326         
327         for (int i=0; i<cols; ++i) {
328                 item->setText(i, columnString(entry, i));
329         }
330         item->setIcon(0, db->icon(entry->image()));
331 }
332
333 void KeepassEntryView::editEntry(EntryViewItem* item){
334         IEntryHandle* handle = item->EntryHandle;
335         CEntry old = handle->data();
336         
337         CEditEntryDlg dlg(db,handle,this,true);
338         int result = dlg.exec();
339         switch(result){
340                 case 0: //canceled or no changes
341                         break;
342                 case 1: //modifications but same group
343                         updateEntry(item);
344                         emit fileModified();
345                         break;
346                 //entry moved to another group
347                 case 2: //modified
348                 case 3: //not modified
349                         Items.removeAll(item);
350                         delete item;
351                         emit fileModified();
352                         break;
353         }
354         
355         IGroupHandle* bGroup;
356         if ((result==1 || result==2) && config->backup() && handle->group() != (bGroup=db->backupGroup())){
357                 old.LastAccess = QDateTime::currentDateTime();
358                 old.LastMod = old.LastAccess;
359                 if (bGroup==NULL)
360                         emit requestCreateGroup("Backup", 4, NULL);
361                 if ((bGroup = db->backupGroup())!=NULL)
362                         db->addEntry(&old, bGroup);
363         }
364         
365         if (result == 1)
366                 OnItemsChanged();
367 }
368
369
370 void KeepassEntryView::OnNewEntry(){
371         IEntryHandle* NewEntry = NULL;
372         if (!CurrentGroup){ // We must be viewing search results. Add the new entry to the first group.
373                 if (db->groups().size() > 0)
374                         NewEntry=db->newEntry(db->sortedGroups()[0]);
375                 else{
376                         QMessageBox::critical(NULL,tr("Error"),tr("At least one group must exist before adding an entry."),tr("OK"));
377                 }
378         }
379         else
380                 NewEntry=db->newEntry(CurrentGroup);
381         CEditEntryDlg dlg(db,NewEntry,this,true);
382         if(!dlg.exec()){
383                 db->deleteLastEntry();
384         }
385         else{
386                 Items.append(new EntryViewItem(this));
387                 Items.back()->EntryHandle=NewEntry;
388                 updateEntry(Items.back());
389                 emit fileModified();
390                 if (header()->isSortIndicatorShown())
391                         sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
392         }
393
394 }
395
396 void KeepassEntryView::OnEntryActivated(QTreeWidgetItem* item, int Column){
397         Q_UNUSED(item);
398         
399         switch (Column){
400                 case 1:
401                         OnUsernameToClipboard();
402                         break;
403                 case 2:
404                         OnEditOpenUrl();
405                         break;
406                 case 3:
407                         OnPasswordToClipboard();
408                         break;
409         }
410 }
411
412 void KeepassEntryView::OnEntryDblClicked(QTreeWidgetItem* item, int Column){
413         if (Column == 0)
414                 editEntry((EntryViewItem*)item);
415 }
416
417 void KeepassEntryView::OnEditEntry(){
418         if (selectedItems().size() == 0) return;
419         editEntry((EntryViewItem*)selectedItems().first());
420 }
421
422 void KeepassEntryView::OnEditOpenUrl(){
423         if (selectedItems().size() == 0) return;
424         openBrowser( ((EntryViewItem*)selectedItems().first())->EntryHandle );
425 }
426
427 void KeepassEntryView::OnEditCopyUrl(){
428         if (selectedItems().size() == 0) return;
429         QString url = ((EntryViewItem*)selectedItems().first())->EntryHandle->url();
430         if (url.trimmed().isEmpty()) return;
431         if (url.startsWith("cmd://") && url.length()>6)
432                 url = url.right(url.length()-6);
433         
434         Clipboard->setText(url,  QClipboard::Clipboard);
435         if(Clipboard->supportsSelection()){
436                 Clipboard->setText(url, QClipboard::Selection);
437         }
438 }
439
440 void KeepassEntryView::OnUsernameToClipboard(){
441         if (selectedItems().size() == 0) return;
442         QString username = ((EntryViewItem*)selectedItems().first())->EntryHandle->username();
443         if (username.trimmed().isEmpty()) return;
444         Clipboard->setText(username,  QClipboard::Clipboard);
445         if(Clipboard->supportsSelection()){
446                 Clipboard->setText(username, QClipboard::Selection);
447         }
448         
449         if (config->clipboardTimeOut()!=0) {
450                 ClipboardTimer.setSingleShot(true);
451                 ClipboardTimer.start(config->clipboardTimeOut()*1000);
452         }
453 }
454
455 void KeepassEntryView::OnPasswordToClipboard(){
456         if (selectedItems().size() == 0) return;
457         SecString password;
458         password=((EntryViewItem*)selectedItems().first())->EntryHandle->password();
459         password.unlock();
460         if (password.string().isEmpty()) return;
461         Clipboard->setText(password.string(), QClipboard::Clipboard);
462         if(Clipboard->supportsSelection()){
463                 Clipboard->setText(password.string(), QClipboard::Selection);
464         }
465         
466         if (config->clipboardTimeOut()!=0) {
467                 ClipboardTimer.setSingleShot(true);
468                 ClipboardTimer.start(config->clipboardTimeOut()*1000);
469         }
470 }
471
472 void KeepassEntryView::OnClipboardTimeOut(){
473         Clipboard->clear(QClipboard::Clipboard);
474         if(Clipboard->supportsSelection()){
475                 Clipboard->clear(QClipboard::Selection);
476         }
477 #ifdef Q_WS_X11
478         QProcess::startDetached("dcop klipper klipper clearClipboardHistory");
479         QProcess::startDetached("dbus-send --type=method_call --dest=org.kde.klipper /klipper "
480                 "org.kde.klipper.klipper.clearClipboardHistory");
481 #endif
482 }
483
484
485 void KeepassEntryView::contextMenuEvent(QContextMenuEvent* e){
486         if(itemAt(e->pos())){
487                 EntryViewItem* item=(EntryViewItem*)itemAt(e->pos());
488                 if(!selectedItems().size()){
489                         setItemSelected(item,true);
490                 }
491                 else{
492                                 if(!isItemSelected(item)){
493                                         while(selectedItems().size()){
494                                                 setItemSelected(selectedItems().first(),false);
495                                         }
496                                         setItemSelected(item,true);
497                                 }
498                         }
499         }
500         else{
501                 while (selectedItems().size())
502                         setItemSelected(selectedItems().first(),false);
503         }
504         e->accept();
505         ContextMenu->popup(e->globalPos());
506 }
507
508 void KeepassEntryView::resizeEvent(QResizeEvent* e){
509         resizeColumns();
510         QTreeWidget::resizeEvent(e);
511 }
512
513
514 void KeepassEntryView::showSearchResults(){
515         if(ViewMode == Normal){
516                 saveHeaderView();
517                 ViewMode = ShowSearchResults;
518                 restoreHeaderView();
519                 emit viewModeChanged(true);
520         }
521         clear();
522         Items.clear();
523         createItems(SearchResults);
524 }
525
526
527 void KeepassEntryView::showGroup(IGroupHandle* group){
528         if(ViewMode == ShowSearchResults){
529                 saveHeaderView();
530                 ViewMode = Normal;
531                 restoreHeaderView();
532                 emit viewModeChanged(false);
533         }
534         clear();
535         Items.clear();
536         if(group==NULL)return;
537         QList<IEntryHandle*>entries=db->entries(group);
538         createItems(entries);
539 }
540
541 void KeepassEntryView::createItems(QList<IEntryHandle*>& entries){
542         for (int i=0; i<entries.size(); ++i) {
543                 if (!entries[i]->isValid())
544                         continue;
545                 
546                 EntryViewItem* item = new EntryViewItem(this);
547                 Items.push_back(item);
548                 Items.back()->EntryHandle = entries[i];
549                 
550                 updateEntry(item);
551         }
552 }
553
554 void KeepassEntryView::updateIcons(){
555         for(int i=0;i<Items.size();i++){
556                 Items[i]->setIcon(0,db->icon(Items[i]->EntryHandle->image()));
557         }
558 }
559
560 void KeepassEntryView::refreshItems(){
561         for (int i=0;i<Items.size();i++)
562                 updateEntry(Items.at(i));
563 }
564
565 void KeepassEntryView::mousePressEvent(QMouseEvent *event){
566         //save event position - maybe this is the start of a drag
567         if (event->button() == Qt::LeftButton)
568                 DragStartPos = event->pos();
569         QTreeWidget::mousePressEvent(event);
570 }
571
572 void KeepassEntryView::mouseMoveEvent(QMouseEvent *event){
573         if (!(event->buttons() & Qt::LeftButton))
574                 return;
575         if ((event->pos() - DragStartPos).manhattanLength() < QApplication::startDragDistance())
576                 return;
577
578         DragItems.clear();
579         EntryViewItem* DragStartItem=(EntryViewItem*)itemAt(DragStartPos);
580         if(!DragStartItem){
581                 while(selectedItems().size()){
582                         setItemSelected(selectedItems().first(),false);
583                 }
584                 return;
585         }
586         if(selectedItems().isEmpty()){
587                         setItemSelected(DragStartItem,true);
588         }
589         else{
590                 bool AlreadySelected=false;
591                 for(int i=0;i<selectedItems().size();i++){
592                         if(selectedItems()[i]==DragStartItem){
593                                 AlreadySelected=true;
594                                 break;
595                         }
596                 }
597                 if(!AlreadySelected){
598                         while(selectedItems().size()){
599                                 setItemSelected(selectedItems().first(),false);
600                         }
601                         setItemSelected(DragStartItem,true);
602                 }
603         }
604
605         DragItems=selectedItems();
606         QDrag *drag = new QDrag(this);
607         QMimeData *mimeData = new QMimeData;
608         void* pDragItems=&DragItems;
609         if (header()->logicalIndexAt(event->pos()) != -1) {
610                 mimeData->setText(columnStringView(DragStartItem, header()->logicalIndexAt(event->pos()), true));
611         }
612         mimeData->setData("application/x-keepassx-entry",QByteArray((char*)&pDragItems,sizeof(void*)));
613         drag->setMimeData(mimeData);
614         EventOccurredBlock = true;
615         drag->exec(Qt::MoveAction);
616         EventOccurredBlock = false;
617 }
618
619 void KeepassEntryView::removeDragItems(){
620         for(int i=0;i<DragItems.size();i++){
621                 for(int j=0;j<Items.size();j++){
622                         if(Items[j]==DragItems[i]){
623                                 Items.removeAt(j);
624                                 j--;
625                                 delete DragItems[i];
626                         }
627                 }
628         }
629 }
630
631 #ifdef AUTOTYPE
632 void KeepassEntryView::OnAutoType(){
633         if (selectedItems().size() == 0) return;
634         autoType->perform(((EntryViewItem*)selectedItems().first())->EntryHandle);
635 }
636 #endif
637
638 void KeepassEntryView::paintEvent(QPaintEvent * event){
639 QTreeWidget::paintEvent(event);
640 }
641
642
643 EntryViewItem::EntryViewItem(QTreeWidget *parent):QTreeWidgetItem(parent){
644
645 }
646
647 EntryViewItem::EntryViewItem(QTreeWidget *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
648
649 }
650
651 EntryViewItem::EntryViewItem(QTreeWidgetItem *parent):QTreeWidgetItem(parent){
652
653 }
654
655 EntryViewItem::EntryViewItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
656
657 }
658
659
660 bool EntryViewItem::operator<(const QTreeWidgetItem& other) const{
661         int SortCol = treeWidget()->header()->sortIndicatorSection();
662         int ListIndex = ((KeepassEntryView*)treeWidget())->header()->logicalIndex(SortCol);
663         
664         int comp = compare(other, SortCol, ListIndex);
665         if (comp!=0)
666                 return (comp<0);
667         else {
668                 int visibleCols = treeWidget()->header()->count() - treeWidget()->header()->hiddenSectionCount();
669                 int ListIndexOrg = ListIndex;
670                 for (int i=0; i<visibleCols; i++){
671                         SortCol = treeWidget()->header()->logicalIndex(i);
672                         ListIndex = ((KeepassEntryView*)treeWidget())->header()->logicalIndex(SortCol);
673                         if (ListIndex==ListIndexOrg || ListIndex==3) // sort or password column
674                                 continue;
675                         
676                         comp = compare(other, SortCol, ListIndex);
677                         if (comp!=0)
678                                 return (comp<0);
679                 }
680                 return true; // entries are equal
681         }
682 }
683
684 int EntryViewItem::compare(const QTreeWidgetItem& other, int col, int index) const{
685         if (index < 5 || index > 8){ //columns with string values (Title, Username, Password, URL, Comment, Group)
686                 return QString::localeAwareCompare(text(col),other.text(col));
687         }
688         
689         KpxDateTime DateThis;
690         KpxDateTime DateOther;
691
692         switch (index){
693                 case 5:
694                         DateThis=EntryHandle->expire();
695                         DateOther=((EntryViewItem&)other).EntryHandle->expire();
696                         break;
697                 case 6:
698                         DateThis=EntryHandle->creation();
699                         DateOther=((EntryViewItem&)other).EntryHandle->creation();
700                         break;
701                 case 7:
702                         DateThis=EntryHandle->lastMod();
703                         DateOther=((EntryViewItem&)other).EntryHandle->lastMod();
704                         break;
705                 case 8:
706                         DateThis=EntryHandle->lastAccess();
707                         DateOther=((EntryViewItem&)other).EntryHandle->lastAccess();
708                         break;
709                 default:
710                         Q_ASSERT(false);
711         }
712         
713         if (DateThis==DateOther)
714                 return 0;
715         else if (DateThis < DateOther)
716                 return -1;
717         else
718                 return 1;
719 }
720
721 void KeepassEntryView::setCurrentEntry(IEntryHandle* entry){
722         bool found=false;
723         int i;
724         for(i=0;i<Items.size();i++)
725                 if(Items.at(i)->EntryHandle==entry){found=true; break;}
726         if(!found)return;
727         setCurrentItem(Items.at(i));
728 }