From d3fbb291bd38366b2e03436a68c18bc62b437529 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 25 Apr 2012 00:22:55 +0200 Subject: [PATCH] Support moving groups with drag and drop. --- src/gui/GroupModel.cpp | 104 ++++++++++++++++++++++++++++++++++++++- src/gui/GroupModel.h | 6 +++ src/gui/GroupView.cpp | 4 ++ tests/TestGroupModel.cpp | 2 +- 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/gui/GroupModel.cpp b/src/gui/GroupModel.cpp index b8e878927..b1d8757cd 100644 --- a/src/gui/GroupModel.cpp +++ b/src/gui/GroupModel.cpp @@ -17,8 +17,11 @@ #include "GroupModel.h" +#include + #include "core/Database.h" #include "core/Group.h" +#include "core/Tools.h" GroupModel::GroupModel(Database* db, QObject* parent) : QAbstractItemModel(parent) @@ -148,6 +151,103 @@ Group* GroupModel::groupFromIndex(const QModelIndex& index) const return static_cast(index.internalPointer()); } +Qt::DropActions GroupModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +Qt::ItemFlags GroupModel::flags(const QModelIndex& modelIndex) const +{ + if (!modelIndex.isValid()) { + return Qt::NoItemFlags; + } + else if (modelIndex == index(0, 0)) { + return QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDropEnabled; + } + else { + return QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; + } +} + +bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, + int row, int column, const QModelIndex& parent) +{ + Q_UNUSED(column); + + if (!data || (action != Qt::MoveAction) || !parent.isValid()) { + return false; + } + + // check if the format is supported + QStringList types = mimeTypes(); + Q_ASSERT(!types.isEmpty()); + QString format = types.at(0); + if (!data->hasFormat(format)) { + return false; + } + + if (row > rowCount(parent)) { + row = rowCount(parent); + } + + // decode and insert + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + Uuid dbUuid; + Uuid groupUuid; + stream >> dbUuid >> groupUuid; + + 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 (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"); + return types; +} + +QMimeData* GroupModel::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_root->database()->uuid() << groupFromIndex(indexes[i])->uuid(); + } + + data->setData(mimeTypes().first(), encoded); + return data; +} + void GroupModel::groupDataChanged(Group* group) { QModelIndex ix = index(group); @@ -196,7 +296,9 @@ void GroupModel::groupAboutToMove(Group* group, Group* toGroup, int pos) pos++; } - beginMoveRows(oldParentIndex, oldPos, oldPos, newParentIndex, pos); + bool moveResult = beginMoveRows(oldParentIndex, oldPos, oldPos, newParentIndex, pos); + Q_UNUSED(moveResult); + Q_ASSERT(moveResult); } void GroupModel::groupMoved() diff --git a/src/gui/GroupModel.h b/src/gui/GroupModel.h index cf2eb72e4..9ed577f1d 100644 --- a/src/gui/GroupModel.h +++ b/src/gui/GroupModel.h @@ -38,6 +38,12 @@ public: QModelIndex parent(const QModelIndex& index) 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; + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, + const QModelIndex& parent); + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList& indexes) const; private: QModelIndex parent(Group* group) const; diff --git a/src/gui/GroupView.cpp b/src/gui/GroupView.cpp index 1ffe6da7b..972a33bd6 100644 --- a/src/gui/GroupView.cpp +++ b/src/gui/GroupView.cpp @@ -40,6 +40,10 @@ GroupView::GroupView(Database* db, QWidget* parent) // invoke later so the EntryView is connected QMetaObject::invokeMethod(this, "emitGroupChanged", Qt::QueuedConnection, Q_ARG(QModelIndex, m_model->index(0, 0))); + + setDragEnabled(true); + viewport()->setAcceptDrops(true); + setDropIndicatorShown(true); } Group* GroupView::currentGroup() diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp index eea0f7f5a..5daf2a611 100644 --- a/tests/TestGroupModel.cpp +++ b/tests/TestGroupModel.cpp @@ -91,7 +91,7 @@ void TestGroupModel::test() QSignalSpy spyMoved(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); Group* group2 = new Group(); - group1->setObjectName("group2"); + group2->setObjectName("group2"); group2->setName("group2"); group2->setParent(groupRoot); QModelIndex index2 = model->index(1, 0, indexRoot);