Add untracked move action

This commit is contained in:
vuurvlieg 2024-03-29 22:44:03 +01:00
parent 22a1a82e5b
commit 86c6f684b2
4 changed files with 102 additions and 14 deletions

View File

@ -447,7 +447,7 @@ Qt::DropActions EntryModel::supportedDropActions() const
Qt::DropActions EntryModel::supportedDragActions() const
{
return (Qt::MoveAction | Qt::CopyAction);
return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;
}
Qt::ItemFlags EntryModel::flags(const QModelIndex& modelIndex) const

View File

@ -180,7 +180,7 @@ Group* GroupModel::groupFromIndex(const QModelIndex& index) const
Qt::DropActions GroupModel::supportedDropActions() const
{
return Qt::MoveAction | Qt::CopyAction;
return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;
}
Qt::ItemFlags GroupModel::flags(const QModelIndex& modelIndex) const
@ -206,7 +206,7 @@ bool GroupModel::dropMimeData(const QMimeData* data,
return true;
}
if (!data || (action != Qt::MoveAction && action != Qt::CopyAction) || !parent.isValid()) {
if (!data || !(action & (Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)) || !parent.isValid()) {
return false;
}
@ -261,11 +261,20 @@ bool GroupModel::dropMimeData(const QMimeData* data,
targetDb->metadata()->copyCustomIcons(group->customIconsRecursive(), sourceDb->metadata());
if (action == Qt::MoveAction) {
// For cross-db moves use a clone with new UUID but original CreationTime
// -- Tracked move
// A clone with new UUID but original CreationTime
group = dragGroup->clone(Entry::CloneFlags(Entry::CloneCopy & ~Entry::CloneResetCreationTime),
Group::CloneFlags(Group::CloneCopy & ~Group::CloneResetCreationTime));
// Remove the original from the sourceDb to allow this change to sync to other dbs
// Original UUID is marked as deleted to remove it from dbs that merge with this one
delete dragGroup;
} else if (action == Qt::LinkAction) {
// -- Untracked move
QList<DeletedObject> deletedObjects(sourceDb->deletedObjects());
// Exact copy of original
group = dragGroup->clone(Entry::CloneExactCopy, Group::CloneExactCopy);
delete dragGroup;
// Unmark UUID(s) as deleted by restoring the previous list
sourceDb->setDeletedObjects(deletedObjects);
} else {
group = dragGroup->clone(Entry::CloneCopy);
}
@ -302,12 +311,20 @@ bool GroupModel::dropMimeData(const QMimeData* data,
if (sourceDb != targetDb) {
targetDb->metadata()->copyCustomIcon(entry->iconUuid(), sourceDb->metadata());
// Reset the UUID when moving across db boundary
if (action == Qt::MoveAction) {
// For cross-db moves use a clone with new UUID but original CreationTime
// -- Tracked move
// A clone with new UUID but original CreationTime
entry = dragEntry->clone(Entry::CloneFlags(Entry::CloneCopy & ~Entry::CloneResetCreationTime));
// Remove the original from the sourceDb to allow this change to sync to other dbs
// Original UUID is marked as deleted to remove it from dbs that merge with this one
delete dragEntry;
} else if (action == Qt::LinkAction) {
// -- Untracked move
QList<DeletedObject> deletedObjects(sourceDb->deletedObjects());
// Exact copy of original
entry = dragEntry->clone(Entry::CloneExactCopy);
delete dragEntry;
// Unmark UUID as deleted by restoring the previous list
sourceDb->setDeletedObjects(deletedObjects);
} else {
entry = dragEntry->clone(Entry::CloneCopy);
}

View File

@ -24,11 +24,15 @@
#include "core/Config.h"
#include "core/Group.h"
#include "gui/group/GroupModel.h"
#include "gui/entry/EntryView.h"
#include "gui/DatabaseWidget.h"
GroupView::GroupView(Database* db, QWidget* parent)
: QTreeView(parent)
, m_model(new GroupModel(db, this))
, m_updatingExpanded(false)
, m_isDragEventSrcFromOtherDb(false)
, m_lastAcceptedDropAction(Qt::IgnoreAction)
{
QTreeView::setModel(m_model);
setHeaderHidden(true);
@ -73,20 +77,83 @@ void GroupView::changeDatabase(const QSharedPointer<Database>& newDb)
m_model->changeDatabase(newDb.data());
}
void GroupView::dragMoveEvent(QDragMoveEvent* event)
void GroupView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->keyboardModifiers() & Qt::ControlModifier) {
event->setDropAction(Qt::CopyAction);
} else {
event->setDropAction(Qt::MoveAction);
event->ignore(); // default to ignore
auto const eventSource = event->source();
// ignore events from other processes
if (!eventSource) {
return;
}
// ignore events with unsupported mime-types
auto supportedFormats = m_model->mimeTypes().toSet();
if (!supportedFormats.intersects(event->mimeData()->formats().toSet())) {
return;
}
auto firstAncestorOfTypeDatabaseWidget = [](QObject* object) -> DatabaseWidget* {
if (object) {
for (auto parent = object->parent(); parent; parent = parent->parent()) {
if (auto dbWidget = qobject_cast<DatabaseWidget*>(parent)) {
return dbWidget;
}
}
}
return nullptr;
};
m_isDragEventSrcFromOtherDb = false;
if (GroupView* view = qobject_cast<GroupView*>(eventSource)) {
m_isDragEventSrcFromOtherDb = view != this;
} else if (EntryView* view = qobject_cast<EntryView*>(eventSource)) {
auto targetDbWidget = firstAncestorOfTypeDatabaseWidget(this);
auto sourceDbWidget = firstAncestorOfTypeDatabaseWidget(view);
m_isDragEventSrcFromOtherDb = sourceDbWidget != targetDbWidget;
}
QTreeView::dragEnterEvent(event);
}
void GroupView::dragMoveEvent(QDragMoveEvent* event)
{
QTreeView::dragMoveEvent(event);
if (!event->isAccepted()) {
return;
}
// entries may only be dropped on groups
if (event->isAccepted() && event->mimeData()->hasFormat("application/x-keepassx-entry")
if (event->mimeData()->hasFormat("application/x-keepassx-entry")
&& (dropIndicatorPosition() == AboveItem || dropIndicatorPosition() == BelowItem)) {
event->ignore();
return;
}
// figure out which dropaction should be used
Qt::DropAction dropAction = Qt::MoveAction;
if (event->keyboardModifiers() & Qt::ControlModifier) {
dropAction = Qt::CopyAction;
} else if (event->keyboardModifiers() & Qt::AltModifier) {
dropAction = m_isDragEventSrcFromOtherDb ? Qt::LinkAction : Qt::IgnoreAction;
}
if (dropAction != Qt::IgnoreAction && event->possibleActions() & dropAction) {
event->setDropAction(dropAction);
m_lastAcceptedDropAction = event->dropAction();
} else {
event->ignore();
}
}
void GroupView::dropEvent(QDropEvent* event)
{
if (m_lastAcceptedDropAction != Qt::IgnoreAction) {
event->setDropAction(m_lastAcceptedDropAction);
QTreeView::dropEvent(event);
} else {
event->ignore();
}
}

View File

@ -48,7 +48,9 @@ private slots:
void contextMenuShortcutPressed();
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
private:
@ -56,6 +58,8 @@ private:
GroupModel* const m_model;
bool m_updatingExpanded;
bool m_isDragEventSrcFromOtherDb;
Qt::DropAction m_lastAcceptedDropAction;
};
#endif // KEEPASSX_GROUPVIEW_H