Support moving entries with drag and drop.

This commit is contained in:
Felix Geyer 2012-04-26 16:35:13 +02:00
parent 79b6ff99e4
commit 74ac6c27d7
6 changed files with 98 additions and 18 deletions

View File

@ -17,6 +17,8 @@
#include "EntryModel.h" #include "EntryModel.h"
#include <QtCore/QMimeData>
#include "core/Entry.h" #include "core/Entry.h"
#include "core/Group.h" #include "core/Group.h"
@ -24,6 +26,7 @@ EntryModel::EntryModel(QObject* parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
, m_group(0) , m_group(0)
{ {
setSupportedDragActions(Qt::MoveAction);
} }
Entry* EntryModel::entryFromIndex(const QModelIndex& index) const Entry* EntryModel::entryFromIndex(const QModelIndex& index) const
@ -106,6 +109,49 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
return QVariant(); 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) void EntryModel::entryAboutToAdd(Entry* entry)
{ {
Q_UNUSED(entry); Q_UNUSED(entry);

View File

@ -35,6 +35,10 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, 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: public Q_SLOTS:
void setGroup(Group* group); void setGroup(Group* group);

View File

@ -27,6 +27,7 @@ EntryView::EntryView(QWidget* parent)
setUniformRowHeights(true); setUniformRowHeights(true);
setRootIsDecorated(false); setRootIsDecorated(false);
setDragEnabled(true);
connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(emitEntryActivated(const QModelIndex&))); connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(emitEntryActivated(const QModelIndex&)));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged()));

View File

@ -180,9 +180,10 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
// check if the format is supported // check if the format is supported
QStringList types = mimeTypes(); QStringList types = mimeTypes();
Q_ASSERT(!types.isEmpty()); Q_ASSERT(types.size() == 2);
QString format = types.at(0); bool isGroup = data->hasFormat(types.at(0));
if (!data->hasFormat(format)) { bool isEntry = data->hasFormat(types.at(1));
if (!isGroup && !isEntry) {
return false; return false;
} }
@ -191,23 +192,25 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
} }
// decode and insert // decode and insert
QByteArray encoded = data->data(format); QByteArray encoded = data->data(isGroup ? types.at(0) : types.at(1));
QDataStream stream(&encoded, QIODevice::ReadOnly); QDataStream stream(&encoded, QIODevice::ReadOnly);
Uuid dbUuid; Uuid dbUuid;
Uuid groupUuid; Uuid itemUuid;
stream >> dbUuid >> groupUuid; stream >> dbUuid >> itemUuid;
Database* db = Database::databaseByUuid(dbUuid); Database* db = Database::databaseByUuid(dbUuid);
if (!db) { if (!db) {
return false; return false;
} }
Group* dragGroup = db->resolveGroup(groupUuid);
Group* parentGroup = groupFromIndex(parent);
if (isGroup) {
Group* dragGroup = db->resolveGroup(itemUuid);
if (!dragGroup || !Tools::hasChild(db, dragGroup) || dragGroup == db->rootGroup()) { if (!dragGroup || !Tools::hasChild(db, dragGroup) || dragGroup == db->rootGroup()) {
return false; return false;
} }
Group* parentGroup = groupFromIndex(parent);
if (dragGroup == parentGroup || Tools::hasChild(dragGroup, parentGroup)) { if (dragGroup == parentGroup || Tools::hasChild(dragGroup, parentGroup)) {
return false; return false;
} }
@ -217,13 +220,24 @@ bool GroupModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
} }
dragGroup->setParent(parentGroup, row); dragGroup->setParent(parentGroup, row);
}
else {
Entry* dragEntry = db->resolveEntry(itemUuid);
if (!dragEntry || !Tools::hasChild(db, dragEntry) || row != -1) {
return false;
}
dragEntry->setGroup(parentGroup);
}
return true; return true;
} }
QStringList GroupModel::mimeTypes() const QStringList GroupModel::mimeTypes() const
{ {
QStringList types; QStringList types;
types << QLatin1String("application/x-keepassx-group"); types << "application/x-keepassx-group";
types << "application/x-keepassx-entry";
return types; return types;
} }

View File

@ -18,6 +18,7 @@
#include "GroupView.h" #include "GroupView.h"
#include <QtCore/QMetaObject> #include <QtCore/QMetaObject>
#include <QtGui/QDragMoveEvent>
#include "core/Database.h" #include "core/Database.h"
#include "core/Group.h" #include "core/Group.h"
@ -46,6 +47,17 @@ GroupView::GroupView(Database* db, QWidget* parent)
setDropIndicatorShown(true); 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() Group* GroupView::currentGroup()
{ {
return m_model->groupFromIndex(currentIndex()); return m_model->groupFromIndex(currentIndex());

View File

@ -41,6 +41,9 @@ private Q_SLOTS:
void emitGroupChanged(const QModelIndex& index); void emitGroupChanged(const QModelIndex& index);
void emitGroupChanged(); void emitGroupChanged();
protected:
void dragMoveEvent(QDragMoveEvent* event);
private: private:
void recInitExpanded(Group* group); void recInitExpanded(Group* group);