From 74ac6c27d7002feb45ed338f3282d3f306724ded Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 26 Apr 2012 16:35:13 +0200 Subject: [PATCH] Support moving entries with drag and drop. --- src/gui/EntryModel.cpp | 46 ++++++++++++++++++++++++++++++++++++++ src/gui/EntryModel.h | 4 ++++ src/gui/EntryView.cpp | 1 + src/gui/GroupModel.cpp | 50 +++++++++++++++++++++++++++--------------- src/gui/GroupView.cpp | 12 ++++++++++ src/gui/GroupView.h | 3 +++ 6 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/gui/EntryModel.cpp b/src/gui/EntryModel.cpp index f1f744f37..37a170285 100644 --- a/src/gui/EntryModel.cpp +++ b/src/gui/EntryModel.cpp @@ -17,6 +17,8 @@ #include "EntryModel.h" +#include + #include "core/Entry.h" #include "core/Group.h" @@ -24,6 +26,7 @@ EntryModel::EntryModel(QObject* parent) : QAbstractTableModel(parent) , m_group(0) { + setSupportedDragActions(Qt::MoveAction); } Entry* EntryModel::entryFromIndex(const QModelIndex& index) const @@ -106,6 +109,49 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro return QVariant(); } +Qt::DropActions EntryModel::supportedDropActions() const +{ + return 0; +} + +Qt::ItemFlags EntryModel::flags(const QModelIndex& modelIndex) const +{ + if (!modelIndex.isValid()) { + return Qt::NoItemFlags; + } + else { + return QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDragEnabled; + } +} + +QStringList EntryModel::mimeTypes() const +{ + QStringList types; + types << QLatin1String("application/x-keepassx-entry"); + return types; +} + +QMimeData* EntryModel::mimeData(const QModelIndexList& indexes) const +{ + if (indexes.isEmpty()) { + return 0; + } + + QMimeData* data = new QMimeData(); + QByteArray encoded; + QDataStream stream(&encoded, QIODevice::WriteOnly); + + for (int i = 0; i < indexes.size(); i++) { + if (!indexes[i].isValid()) { + continue; + } + stream << m_group->database()->uuid() << entryFromIndex(indexes[i])->uuid(); + } + + data->setData(mimeTypes().first(), encoded); + return data; +} + void EntryModel::entryAboutToAdd(Entry* entry) { Q_UNUSED(entry); diff --git a/src/gui/EntryModel.h b/src/gui/EntryModel.h index b65e533a1..a248a5d1c 100644 --- a/src/gui/EntryModel.h +++ b/src/gui/EntryModel.h @@ -35,6 +35,10 @@ public: int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + Qt::DropActions supportedDropActions() const; + Qt::ItemFlags flags(const QModelIndex& modelIndex) const; + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList& indexes) const; public Q_SLOTS: void setGroup(Group* group); diff --git a/src/gui/EntryView.cpp b/src/gui/EntryView.cpp index 234ef41a0..9985a1eb0 100644 --- a/src/gui/EntryView.cpp +++ b/src/gui/EntryView.cpp @@ -27,6 +27,7 @@ EntryView::EntryView(QWidget* parent) setUniformRowHeights(true); setRootIsDecorated(false); + setDragEnabled(true); connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(emitEntryActivated(const QModelIndex&))); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); diff --git a/src/gui/GroupModel.cpp b/src/gui/GroupModel.cpp index b1d8757cd..d9e1678e7 100644 --- a/src/gui/GroupModel.cpp +++ b/src/gui/GroupModel.cpp @@ -180,9 +180,10 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, // check if the format is supported QStringList types = mimeTypes(); - Q_ASSERT(!types.isEmpty()); - QString format = types.at(0); - if (!data->hasFormat(format)) { + Q_ASSERT(types.size() == 2); + bool isGroup = data->hasFormat(types.at(0)); + bool isEntry = data->hasFormat(types.at(1)); + if (!isGroup && !isEntry) { return false; } @@ -191,39 +192,52 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, } // decode and insert - QByteArray encoded = data->data(format); + QByteArray encoded = data->data(isGroup ? types.at(0) : types.at(1)); QDataStream stream(&encoded, QIODevice::ReadOnly); Uuid dbUuid; - Uuid groupUuid; - stream >> dbUuid >> groupUuid; + Uuid itemUuid; + stream >> dbUuid >> itemUuid; Database* db = Database::databaseByUuid(dbUuid); if (!db) { return false; } - Group* dragGroup = db->resolveGroup(groupUuid); - if (!dragGroup || !Tools::hasChild(db, dragGroup) || dragGroup == db->rootGroup()) { - return false; - } Group* parentGroup = groupFromIndex(parent); - if (dragGroup == parentGroup || Tools::hasChild(dragGroup, parentGroup)) { - return false; + if (isGroup) { + Group* dragGroup = db->resolveGroup(itemUuid); + if (!dragGroup || !Tools::hasChild(db, dragGroup) || dragGroup == db->rootGroup()) { + return false; + } + + if (dragGroup == parentGroup || Tools::hasChild(dragGroup, parentGroup)) { + return false; + } + + if (parentGroup == dragGroup->parent() && row > parentGroup->children().indexOf(dragGroup)) { + row--; + } + + dragGroup->setParent(parentGroup, row); + } + else { + Entry* dragEntry = db->resolveEntry(itemUuid); + if (!dragEntry || !Tools::hasChild(db, dragEntry) || row != -1) { + return false; + } + + dragEntry->setGroup(parentGroup); } - if (parentGroup == dragGroup->parent() && row > parentGroup->children().indexOf(dragGroup)) { - row--; - } - - dragGroup->setParent(parentGroup, row); return true; } QStringList GroupModel::mimeTypes() const { QStringList types; - types << QLatin1String("application/x-keepassx-group"); + types << "application/x-keepassx-group"; + types << "application/x-keepassx-entry"; return types; } diff --git a/src/gui/GroupView.cpp b/src/gui/GroupView.cpp index 972a33bd6..5f56cbfcd 100644 --- a/src/gui/GroupView.cpp +++ b/src/gui/GroupView.cpp @@ -18,6 +18,7 @@ #include "GroupView.h" #include +#include #include "core/Database.h" #include "core/Group.h" @@ -46,6 +47,17 @@ GroupView::GroupView(Database* db, QWidget* parent) setDropIndicatorShown(true); } +void GroupView::dragMoveEvent(QDragMoveEvent* event) +{ + QTreeView::dragMoveEvent(event); + + // entries may only be dropped on groups + if (event->isAccepted() && event->mimeData()->hasFormat("application/x-keepassx-entry") + && (dropIndicatorPosition() == AboveItem || dropIndicatorPosition() == BelowItem)) { + event->ignore(); + } +} + Group* GroupView::currentGroup() { return m_model->groupFromIndex(currentIndex()); diff --git a/src/gui/GroupView.h b/src/gui/GroupView.h index 54cf6bf29..9157e95f0 100644 --- a/src/gui/GroupView.h +++ b/src/gui/GroupView.h @@ -41,6 +41,9 @@ private Q_SLOTS: void emitGroupChanged(const QModelIndex& index); void emitGroupChanged(); +protected: + void dragMoveEvent(QDragMoveEvent* event); + private: void recInitExpanded(Group* group);