1 /***************************************************************************
2 * Copyright (C) 2005-2007 by Tarek Saidi *
3 * tarek.saidi@arcor.de *
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. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
21 #include "EntryView.h"
22 #include "GroupView.h"
23 #include "dialogs/EditGroupDlg.h"
26 #include <QHeaderView>
28 #define INSERT_AREA_WIDTH 4
30 KeepassGroupView::KeepassGroupView(QWidget* parent):QTreeWidget(parent){
32 ContextMenu=new QMenu(this);
33 ContextMenuSearchGroup=new QMenu(this);
34 connect(this,SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),this,SLOT(OnCurrentGroupChanged(QTreeWidgetItem*)));
35 connect(this,SIGNAL(itemExpanded(QTreeWidgetItem*)),this,SLOT(OnItemExpanded(QTreeWidgetItem*)));
36 connect(this,SIGNAL(itemCollapsed(QTreeWidgetItem*)),this,SLOT(OnItemCollapsed(QTreeWidgetItem*)));
39 void KeepassGroupView::createItems(){
43 QList<IGroupHandle*> groups=db->groups();
44 for(int i=0;i<groups.size();i++){
45 if(groups[i]->parent()==NULL){
46 Items.append(new GroupViewItem(this));
47 Items.back()->setText(0,groups[i]->title());
48 Items.back()->GroupHandle=groups[i];
49 addChildren(Items.back());
52 for(int i=0;i<Items.size();i++){
53 Items[i]->setIcon(0,db->icon(Items[i]->GroupHandle->image()));
54 Items[i]->setExpanded(Items[i]->GroupHandle->expanded());
56 SearchResultItem=new GroupViewItem();
60 void KeepassGroupView::retranslateUi() {
61 SearchResultItem->setText(0,tr("Search Results"));
64 void KeepassGroupView::updateIcons(){
65 for(int i=0;i<Items.size();i++){
66 Items[i]->setIcon(0,db->icon(Items[i]->GroupHandle->image()));
70 void KeepassGroupView::showSearchResults(){
71 if(topLevelItem(topLevelItemCount()-1)!=SearchResultItem){
72 addTopLevelItem(SearchResultItem);
74 setCurrentItem(SearchResultItem);
75 emit searchResultsSelected();
78 void KeepassGroupView::addChildren(GroupViewItem* item){
79 QList<IGroupHandle*>children=item->GroupHandle->children();
82 for(int i=0; i<children.size(); i++){
83 Items.push_back(new GroupViewItem(item));
84 Items.back()->setText(0,children[i]->title());
85 Items.back()->GroupHandle=children[i];
86 addChildren(Items.back());
90 void KeepassGroupView::OnDeleteGroup(){
91 if(config->askBeforeDelete()){
92 if(QMessageBox::question(this,tr("Delete?"),
93 tr("Are you sure you want to delete this group, all its child groups and all their entries?"),
94 QMessageBox::Yes | QMessageBox::No,QMessageBox::No) == QMessageBox::No)
97 GroupViewItem* item=(GroupViewItem*)currentItem();
99 db->deleteGroup(item->GroupHandle);
105 void KeepassGroupView::OnHideSearchResults(){
106 takeTopLevelItem(topLevelItemCount()-1);
109 void KeepassGroupView::OnNewGroup(){
111 CEditGroupDialog dlg(db,&NewGroup,parentWidget());
113 createGroup(NewGroup.Title, NewGroup.Image, NULL);
116 void KeepassGroupView::OnNewSubgroup(){
117 GroupViewItem* parent=(GroupViewItem*)currentItem();
119 CEditGroupDialog dlg(db,&NewGroup,parentWidget());
121 createGroup(NewGroup.Title, NewGroup.Image, parent);
124 void KeepassGroupView::createGroup(const QString& title, quint32 image, GroupViewItem* parent){
126 NewGroup.Title = title;
127 NewGroup.Image = image;
131 group=db->addGroup(&NewGroup,parent->GroupHandle);
132 Items.append(new GroupViewItem(parent));
135 if(topLevelItemCount()){
137 if(topLevelItem(topLevelItemCount()-i)==SearchResultItem)
139 if(title!="Backup" && topLevelItem(topLevelItemCount()-i)->text(0)=="Backup")
141 Items.append(new GroupViewItem(this,topLevelItem(topLevelItemCount()-i)));
144 Items.append(new GroupViewItem(this));
146 group = db->addGroup(&NewGroup,NULL);
149 Items.back()->GroupHandle = group;
150 Items.back()->setText(0, group->title());
151 Items.back()->setIcon(0, db->icon(group->image()));
155 void KeepassGroupView::OnEditGroup(){
156 GroupViewItem* item=(GroupViewItem*)currentItem();
157 CEditGroupDialog dlg(db,item->GroupHandle,parentWidget());
160 item->setIcon(0,db->icon(item->GroupHandle->image()));
161 item->setText(0,item->GroupHandle->title());
162 if(r==2)emit fileModified();
166 void KeepassGroupView::contextMenuEvent(QContextMenuEvent* e){
167 if(!(GroupViewItem*)itemAt(e->pos()))
171 if(currentItem()==SearchResultItem)
172 ContextMenuSearchGroup->popup(e->globalPos());
174 ContextMenu->popup(e->globalPos());
177 void KeepassGroupView::OnCurrentGroupChanged(QTreeWidgetItem* cur){
179 if(cur==SearchResultItem)
180 emit searchResultsSelected();
182 emit groupChanged(((GroupViewItem*)cur)->GroupHandle);
185 emit groupChanged(NULL);
189 void KeepassGroupView::setCurrentGroup(IGroupHandle* group){
192 for(i=0;i<Items.size();i++)
193 if(Items[i]->GroupHandle==group){found=true; break;}
195 setCurrentItem(Items[i]);
198 void KeepassGroupView::selectFirstGroup(){
202 setCurrentItem(Items[0]);
205 void KeepassGroupView::dragEnterEvent ( QDragEnterEvent * event ){
209 if(event->mimeData()->hasFormat("application/x-keepassx-group")){
211 event->acceptProposedAction();
214 if(event->mimeData()->hasFormat("application/x-keepassx-entry")){
216 memcpy(&EntryDragItems,event->mimeData()->data("application/x-keepassx-entry").data(),sizeof(void*));
217 event->acceptProposedAction();
225 void KeepassGroupView::dragLeaveEvent ( QDragLeaveEvent * event ){
228 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
229 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
232 int RemoveLine=InsLinePos;
234 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
239 void KeepassGroupView::entryDropEvent( QDropEvent * event ){
240 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
246 if(Item->GroupHandle==((EntryViewItem*)(*EntryDragItems)[0])->EntryHandle->group())
248 for(int i=0;i<EntryDragItems->size();i++){
249 db->moveEntry(((EntryViewItem*)(*EntryDragItems)[i])->EntryHandle,Item->GroupHandle);
251 emit entriesDropped();
258 void KeepassGroupView::dropEvent( QDropEvent * event ){
260 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
261 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
264 if(DragType==EntryDrag){
265 entryDropEvent(event);
270 int RemoveLine=InsLinePos;
272 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
274 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
277 qDebug("Append at the end");
278 db->moveGroup(DragItem->GroupHandle,NULL,-1);
279 if(DragItem->parent()){
280 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
283 takeTopLevelItem(indexOfTopLevelItem(DragItem));
285 insertTopLevelItem(topLevelItemCount(),DragItem);
286 if(topLevelItemCount()>1){
287 if(topLevelItem(topLevelItemCount()-2)==SearchResultItem){
288 takeTopLevelItem(topLevelItemCount()-2);
289 insertTopLevelItem(topLevelItemCount(),SearchResultItem);
295 if (DragItem->GroupHandle==Item->GroupHandle)
298 QRect ItemRect=visualItemRect(Item);
299 if(event->pos().y()>ItemRect.y()+2 && event->pos().y()<ItemRect.y()+ItemRect.height()-2){
300 qDebug("Append as child of '%s'",((char*)Item->text(0).toUtf8().data()));
301 db->moveGroup(DragItem->GroupHandle,Item->GroupHandle,-1);
302 if(DragItem->parent()){
303 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
306 takeTopLevelItem(indexOfTopLevelItem(DragItem));
308 Item->insertChild(Item->childCount(),DragItem);
312 if(event->pos().y()>ItemRect.y()+2){
313 qDebug("Insert behind sibling '%s'",((char*)Item->text(0).toUtf8().data()));
314 if(DragItem->parent()){
315 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
318 takeTopLevelItem(indexOfTopLevelItem(DragItem));
321 int index=Item->parent()->indexOfChild(Item)+1;
322 db->moveGroup(DragItem->GroupHandle,((GroupViewItem*)Item->parent())->GroupHandle,index);
323 Item->parent()->insertChild(index,DragItem);
326 int index=indexOfTopLevelItem(Item)+1;
327 db->moveGroup(DragItem->GroupHandle,NULL,index);
328 insertTopLevelItem(index,DragItem);
333 qDebug("Insert before sibling '%s'",((char*)Item->text(0).toUtf8().data()));
334 if(DragItem->parent()){
335 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
338 takeTopLevelItem(indexOfTopLevelItem(DragItem));
341 int index=Item->parent()->indexOfChild(Item);
342 db->moveGroup(DragItem->GroupHandle,((GroupViewItem*)Item->parent())->GroupHandle,index);
343 Item->parent()->insertChild(index,DragItem);
346 int index=indexOfTopLevelItem(Item);
347 db->moveGroup(DragItem->GroupHandle,NULL,index);
348 insertTopLevelItem(index,DragItem);
359 void KeepassGroupView::entryDragMoveEvent(QDragMoveEvent* event){
361 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
364 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
365 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
371 if(Item==SearchResultItem){
373 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
374 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
380 if(LastHoverItem != Item){
382 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
383 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
385 Item->setBackgroundColor(0,QApplication::palette().color(QPalette::Highlight));
386 Item->setForeground(0,QBrush(QApplication::palette().color(QPalette::HighlightedText)));
389 event->acceptProposedAction();
394 void KeepassGroupView::dragMoveEvent(QDragMoveEvent* event){
395 if(DragType==EntryDrag){
396 entryDragMoveEvent(event);
400 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
403 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
407 int RemoveLine=InsLinePos;
409 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
411 event->acceptProposedAction();
414 if(Item==DragItem || Item==SearchResultItem){
418 if(!db->isParent(DragItem->GroupHandle,Item->GroupHandle)){
419 QRect ItemRect=visualItemRect(Item);
420 if(event->pos().y()>ItemRect.y()+2 && event->pos().y()<ItemRect.y()+ItemRect.height()-2){
422 int RemoveLine=InsLinePos;
424 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
426 if(LastHoverItem != Item){
428 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
430 Item->setBackgroundColor(0,QApplication::palette().color(QPalette::Highlight));
436 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
440 int RemoveLine=InsLinePos;
442 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
444 if(event->pos().y()>ItemRect.y()+2){
445 InsLinePos=ItemRect.y()+ItemRect.height();
448 InsLinePos=ItemRect.y();
450 InsLineStart=ItemRect.x();
451 viewport()->update(QRegion(0,InsLinePos-2,viewport()->width(),4));
453 event->acceptProposedAction();
461 void KeepassGroupView::paintEvent(QPaintEvent* event){
463 QTreeWidget::paintEvent(event);
464 if(InsLinePos != -1){
465 QPainter painter(viewport());
466 painter.setBrush(QBrush(QColor(0,0,0),Qt::Dense4Pattern));
467 painter.setPen(Qt::NoPen);
468 painter.drawRect(InsLineStart,InsLinePos-2,viewport()->width(),4);
473 void KeepassGroupView::mousePressEvent(QMouseEvent *event){
474 if (event->button() == Qt::LeftButton)
475 DragStartPos = event->pos();
476 QTreeWidget::mousePressEvent(event);
479 void KeepassGroupView::mouseMoveEvent(QMouseEvent *event){
480 if (!(event->buttons() & Qt::LeftButton))
482 if ((event->pos() - DragStartPos).manhattanLength()
483 < QApplication::startDragDistance())
486 DragItem=(GroupViewItem*)itemAt(event->pos());
489 if(DragItem==SearchResultItem){
490 qDebug("SearchGroup");
495 setCurrentItem(DragItem);
497 QDrag *drag = new QDrag(this);
498 QMimeData *mimeData = new QMimeData;
500 mimeData->setData("application/x-keepassx-group",QByteArray());
501 drag->setMimeData(mimeData);
503 EventOccurredBlock = true;
504 drag->exec(Qt::MoveAction);
505 EventOccurredBlock = false;
508 void KeepassGroupView::OnItemExpanded(QTreeWidgetItem* item){
509 static_cast<GroupViewItem*>(item)->GroupHandle->setExpanded(true);
512 void KeepassGroupView::OnItemCollapsed(QTreeWidgetItem* item){
513 static_cast<GroupViewItem*>(item)->GroupHandle->setExpanded(false);
516 void KeepassGroupView::OnSort() {
517 QHash<QTreeWidgetItem*,int> oldIndex;
518 for (int i=0; i<Items.size(); i++) {
519 if (Items[i]->parent())
520 oldIndex.insert(Items[i], Items[i]->parent()->indexOfChild(Items[i]));
522 oldIndex.insert(Items[i], invisibleRootItem()->indexOfChild(Items[i]));
525 sortItems(0, Qt::AscendingOrder);
527 bool modified = false;
528 QMutableHashIterator<QTreeWidgetItem*, int> i(oldIndex);
529 while (i.hasNext()) {
532 IGroupHandle* parent;
533 if (i.key()->parent()) {
534 newIndex = i.key()->parent()->indexOfChild(i.key());
535 parent = static_cast<GroupViewItem*>(i.key()->parent())->GroupHandle;
538 newIndex = invisibleRootItem()->indexOfChild(i.key());
542 if (newIndex != i.value()) {
543 db->moveGroup(static_cast<GroupViewItem*>(i.key())->GroupHandle, parent, newIndex);
554 GroupViewItem::GroupViewItem():QTreeWidgetItem(){
557 GroupViewItem::GroupViewItem(QTreeWidget *parent):QTreeWidgetItem(parent){
560 GroupViewItem::GroupViewItem(QTreeWidget *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
563 GroupViewItem::GroupViewItem(QTreeWidgetItem *parent):QTreeWidgetItem(parent){
566 GroupViewItem::GroupViewItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
569 bool GroupViewItem::operator<(const QTreeWidgetItem& other) const {
570 const GroupViewItem* otherItem = static_cast<const GroupViewItem*>(&other);
571 KeepassGroupView* groupView = static_cast<KeepassGroupView*>(treeWidget());
573 // Search result is always at the bottom
574 if (this == groupView->SearchResultItem)
576 if (otherItem == groupView->SearchResultItem)
579 // Backup group is always at the bottom but above search results
580 if (!parent() && text(0).compare("Backup", Qt::CaseInsensitive) == 0)
582 if (!other.parent() && other.text(0).compare("Backup", Qt::CaseInsensitive) == 0)
585 return QString::localeAwareCompare(text(0), other.text(0)) < 0;