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