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->source() == NULL)
210 return; // drag event came from another application
212 if(event->mimeData()->hasFormat("application/x-keepassx-group")){
214 event->acceptProposedAction();
217 if(event->mimeData()->hasFormat("application/x-keepassx-entry")){
219 memcpy(&EntryDragItems,event->mimeData()->data("application/x-keepassx-entry").data(),sizeof(void*));
220 event->acceptProposedAction();
228 void KeepassGroupView::dragLeaveEvent ( QDragLeaveEvent * event ){
231 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
232 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
235 int RemoveLine=InsLinePos;
237 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
242 void KeepassGroupView::entryDropEvent( QDropEvent * event ){
243 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
249 if(Item->GroupHandle==((EntryViewItem*)(*EntryDragItems)[0])->EntryHandle->group())
251 for(int i=0;i<EntryDragItems->size();i++){
252 db->moveEntry(((EntryViewItem*)(*EntryDragItems)[i])->EntryHandle,Item->GroupHandle);
254 emit entriesDropped();
261 void KeepassGroupView::dropEvent( QDropEvent * event ){
263 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
264 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
267 if(DragType==EntryDrag){
268 entryDropEvent(event);
273 int RemoveLine=InsLinePos;
275 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
277 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
280 qDebug("Append at the end");
281 db->moveGroup(DragItem->GroupHandle,NULL,-1);
282 if(DragItem->parent()){
283 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
286 takeTopLevelItem(indexOfTopLevelItem(DragItem));
288 insertTopLevelItem(topLevelItemCount(),DragItem);
289 if(topLevelItemCount()>1){
290 if(topLevelItem(topLevelItemCount()-2)==SearchResultItem){
291 takeTopLevelItem(topLevelItemCount()-2);
292 insertTopLevelItem(topLevelItemCount(),SearchResultItem);
298 if (DragItem->GroupHandle==Item->GroupHandle)
301 QRect ItemRect=visualItemRect(Item);
302 if(event->pos().y()>ItemRect.y()+2 && event->pos().y()<ItemRect.y()+ItemRect.height()-2){
303 qDebug("Append as child of '%s'",((char*)Item->text(0).toUtf8().data()));
304 db->moveGroup(DragItem->GroupHandle,Item->GroupHandle,-1);
305 if(DragItem->parent()){
306 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
309 takeTopLevelItem(indexOfTopLevelItem(DragItem));
311 Item->insertChild(Item->childCount(),DragItem);
315 if(event->pos().y()>ItemRect.y()+2){
316 qDebug("Insert behind sibling '%s'",((char*)Item->text(0).toUtf8().data()));
317 if(DragItem->parent()){
318 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
321 takeTopLevelItem(indexOfTopLevelItem(DragItem));
324 int index=Item->parent()->indexOfChild(Item)+1;
325 db->moveGroup(DragItem->GroupHandle,((GroupViewItem*)Item->parent())->GroupHandle,index);
326 Item->parent()->insertChild(index,DragItem);
329 int index=indexOfTopLevelItem(Item)+1;
330 db->moveGroup(DragItem->GroupHandle,NULL,index);
331 insertTopLevelItem(index,DragItem);
336 qDebug("Insert before sibling '%s'",((char*)Item->text(0).toUtf8().data()));
337 if(DragItem->parent()){
338 DragItem->parent()->takeChild(DragItem->parent()->indexOfChild(DragItem));
341 takeTopLevelItem(indexOfTopLevelItem(DragItem));
344 int index=Item->parent()->indexOfChild(Item);
345 db->moveGroup(DragItem->GroupHandle,((GroupViewItem*)Item->parent())->GroupHandle,index);
346 Item->parent()->insertChild(index,DragItem);
349 int index=indexOfTopLevelItem(Item);
350 db->moveGroup(DragItem->GroupHandle,NULL,index);
351 insertTopLevelItem(index,DragItem);
362 void KeepassGroupView::entryDragMoveEvent(QDragMoveEvent* event){
364 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
367 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
368 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
374 if(Item==SearchResultItem){
376 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
377 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
383 if(LastHoverItem != Item){
385 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
386 LastHoverItem->setForeground(0,QBrush(QApplication::palette().color(QPalette::Text)));
388 Item->setBackgroundColor(0,QApplication::palette().color(QPalette::Highlight));
389 Item->setForeground(0,QBrush(QApplication::palette().color(QPalette::HighlightedText)));
392 event->acceptProposedAction();
397 void KeepassGroupView::dragMoveEvent(QDragMoveEvent* event){
398 if(DragType==EntryDrag){
399 entryDragMoveEvent(event);
403 GroupViewItem* Item=(GroupViewItem*)itemAt(event->pos());
406 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
410 int RemoveLine=InsLinePos;
412 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
414 event->acceptProposedAction();
417 if(Item==DragItem || Item==SearchResultItem){
421 if(!db->isParent(DragItem->GroupHandle,Item->GroupHandle)){
422 QRect ItemRect=visualItemRect(Item);
423 if(event->pos().y()>ItemRect.y()+2 && event->pos().y()<ItemRect.y()+ItemRect.height()-2){
425 int RemoveLine=InsLinePos;
427 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
429 if(LastHoverItem != Item){
431 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
433 Item->setBackgroundColor(0,QApplication::palette().color(QPalette::Highlight));
439 LastHoverItem->setBackgroundColor(0,QApplication::palette().color(QPalette::Base));
443 int RemoveLine=InsLinePos;
445 viewport()->update(QRegion(0,RemoveLine-2,viewport()->width(),4));
447 if(event->pos().y()>ItemRect.y()+2){
448 InsLinePos=ItemRect.y()+ItemRect.height();
451 InsLinePos=ItemRect.y();
453 InsLineStart=ItemRect.x();
454 viewport()->update(QRegion(0,InsLinePos-2,viewport()->width(),4));
456 event->acceptProposedAction();
464 void KeepassGroupView::paintEvent(QPaintEvent* event){
466 QTreeWidget::paintEvent(event);
467 if(InsLinePos != -1){
468 QPainter painter(viewport());
469 painter.setBrush(QBrush(QColor(0,0,0),Qt::Dense4Pattern));
470 painter.setPen(Qt::NoPen);
471 painter.drawRect(InsLineStart,InsLinePos-2,viewport()->width(),4);
476 void KeepassGroupView::mousePressEvent(QMouseEvent *event){
477 if (event->button() == Qt::LeftButton)
478 DragStartPos = event->pos();
479 QTreeWidget::mousePressEvent(event);
482 void KeepassGroupView::mouseMoveEvent(QMouseEvent *event){
483 if (!(event->buttons() & Qt::LeftButton))
485 if ((event->pos() - DragStartPos).manhattanLength()
486 < QApplication::startDragDistance())
489 DragItem=(GroupViewItem*)itemAt(event->pos());
492 if(DragItem==SearchResultItem){
493 qDebug("SearchGroup");
498 setCurrentItem(DragItem);
500 QDrag *drag = new QDrag(this);
501 QMimeData *mimeData = new QMimeData;
503 mimeData->setData("application/x-keepassx-group",QByteArray());
504 drag->setMimeData(mimeData);
506 EventOccurredBlock = true;
507 drag->exec(Qt::MoveAction);
508 EventOccurredBlock = false;
511 void KeepassGroupView::OnItemExpanded(QTreeWidgetItem* item){
512 static_cast<GroupViewItem*>(item)->GroupHandle->setExpanded(true);
515 void KeepassGroupView::OnItemCollapsed(QTreeWidgetItem* item){
516 static_cast<GroupViewItem*>(item)->GroupHandle->setExpanded(false);
519 void KeepassGroupView::OnSort() {
520 QHash<QTreeWidgetItem*,int> oldIndex;
521 for (int i=0; i<Items.size(); i++) {
522 if (Items[i]->parent())
523 oldIndex.insert(Items[i], Items[i]->parent()->indexOfChild(Items[i]));
525 oldIndex.insert(Items[i], invisibleRootItem()->indexOfChild(Items[i]));
528 sortItems(0, Qt::AscendingOrder);
530 bool modified = false;
531 QMutableHashIterator<QTreeWidgetItem*, int> i(oldIndex);
532 while (i.hasNext()) {
535 IGroupHandle* parent;
536 if (i.key()->parent()) {
537 newIndex = i.key()->parent()->indexOfChild(i.key());
538 parent = static_cast<GroupViewItem*>(i.key()->parent())->GroupHandle;
541 newIndex = invisibleRootItem()->indexOfChild(i.key());
545 if (newIndex != i.value()) {
546 db->moveGroup(static_cast<GroupViewItem*>(i.key())->GroupHandle, parent, newIndex);
557 GroupViewItem::GroupViewItem():QTreeWidgetItem(){
560 GroupViewItem::GroupViewItem(QTreeWidget *parent):QTreeWidgetItem(parent){
563 GroupViewItem::GroupViewItem(QTreeWidget *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
566 GroupViewItem::GroupViewItem(QTreeWidgetItem *parent):QTreeWidgetItem(parent){
569 GroupViewItem::GroupViewItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
572 bool GroupViewItem::operator<(const QTreeWidgetItem& other) const {
573 const GroupViewItem* otherItem = static_cast<const GroupViewItem*>(&other);
574 KeepassGroupView* groupView = static_cast<KeepassGroupView*>(treeWidget());
576 // Search result is always at the bottom
577 if (this == groupView->SearchResultItem)
579 if (otherItem == groupView->SearchResultItem)
582 // Backup group is always at the bottom but above search results
583 if (!parent() && text(0).compare("Backup", Qt::CaseInsensitive) == 0)
585 if (!other.parent() && other.text(0).compare("Backup", Qt::CaseInsensitive) == 0)
588 return QString::localeAwareCompare(text(0), other.text(0)) < 0;