Add natural sort of entry list

Introduce a third unsorted status that shows entries in the order they occur in the KDBX file.

* Add keyboard shortcut Ctrl+Alt+Up/Down to move entries up and down in sort order
* Add entry context menu icons to achieve movement up/down
* Only show menu icons when in natural sort order
* Add Material Design icons for moving up/down

* Add feature to track non-data changes and force a save on exit to ensure they are not lost when locking a database. This allows users to make entry movements and group expand/collapse operations and not lose that state.

Remove saveas
This commit is contained in:
Holger Böhnke 2020-05-21 21:43:00 -04:00 committed by Jonathan White
parent 43c82ccb09
commit eb198271ac
24 changed files with 500 additions and 11 deletions

View file

@ -612,3 +612,114 @@ void TestEntry::testIsRecycled()
db.recycleGroup(group1);
QVERIFY(entry1->isRecycled());
}
void TestEntry::testMove()
{
Database db;
Group* root = db.rootGroup();
QVERIFY(root);
Entry* entry0 = new Entry();
QVERIFY(entry0);
entry0->setGroup(root);
Entry* entry1 = new Entry();
QVERIFY(entry1);
entry1->setGroup(root);
Entry* entry2 = new Entry();
QVERIFY(entry2);
entry2->setGroup(root);
Entry* entry3 = new Entry();
QVERIFY(entry3);
entry3->setGroup(root);
// default order, straight
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveDown();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveDown();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveDown();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry0);
// no effect
entry0->moveDown();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry0);
entry0->moveUp();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveUp();
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveUp();
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
// no effect
entry0->moveUp();
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
entry2->moveUp();
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry3);
entry0->moveDown();
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry3);
entry3->moveUp();
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry1);
entry3->moveUp();
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry3);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry1);
entry2->moveDown();
QCOMPARE(root->entries().at(0), entry3);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry1);
entry1->moveUp();
QCOMPARE(root->entries().at(0), entry3);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry0);
}

View file

@ -38,6 +38,7 @@ private slots:
void testResolveNonIdPlaceholdersToUuid();
void testResolveClonedEntry();
void testIsRecycled();
void testMove();
};
#endif // KEEPASSX_TESTENTRY_H

View file

@ -55,6 +55,9 @@ void TestEntryModel::test()
EntryModel* model = new EntryModel(this);
QSignalSpy spyAboutToBeMoved(model, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)));
QSignalSpy spyMoved(model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)));
ModelTest* modelTest = new ModelTest(model, this);
model->setGroup(group1);
@ -79,6 +82,29 @@ void TestEntryModel::test()
Entry* entry3 = new Entry();
entry3->setGroup(group1);
QCOMPARE(spyAboutToBeMoved.count(), 0);
QCOMPARE(spyMoved.count(), 0);
entry1->moveDown();
QCOMPARE(spyAboutToBeMoved.count(), 1);
QCOMPARE(spyMoved.count(), 1);
entry1->moveDown();
QCOMPARE(spyAboutToBeMoved.count(), 2);
QCOMPARE(spyMoved.count(), 2);
entry1->moveDown();
QCOMPARE(spyAboutToBeMoved.count(), 2);
QCOMPARE(spyMoved.count(), 2);
entry3->moveUp();
QCOMPARE(spyAboutToBeMoved.count(), 3);
QCOMPARE(spyMoved.count(), 3);
entry3->moveUp();
QCOMPARE(spyAboutToBeMoved.count(), 3);
QCOMPARE(spyMoved.count(), 3);
QCOMPARE(spyAboutToAdd.count(), 1);
QCOMPARE(spyAdded.count(), 1);
QCOMPARE(spyAboutToRemove.count(), 0);

View file

@ -1208,3 +1208,114 @@ void TestGroup::testUsernamesRecursive()
QVERIFY(usernames.contains("Name2"));
QVERIFY(usernames.indexOf("Name2") < usernames.indexOf("Name1"));
}
void TestGroup::testMove()
{
Database database;
Group* root = database.rootGroup();
QVERIFY(root);
Entry* entry0 = new Entry();
QVERIFY(entry0);
entry0->setGroup(root);
Entry* entry1 = new Entry();
QVERIFY(entry1);
entry1->setGroup(root);
Entry* entry2 = new Entry();
QVERIFY(entry2);
entry2->setGroup(root);
Entry* entry3 = new Entry();
QVERIFY(entry3);
entry3->setGroup(root);
// default order, straight
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryDown(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryDown(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryDown(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry0);
// no effect
root->moveEntryDown(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry0);
root->moveEntryUp(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryUp(entry0);
QCOMPARE(root->entries().at(0), entry1);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryUp(entry0);
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
// no effect
root->moveEntryUp(entry0);
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry1);
QCOMPARE(root->entries().at(2), entry2);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryUp(entry2);
QCOMPARE(root->entries().at(0), entry0);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryDown(entry0);
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry3);
root->moveEntryUp(entry3);
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry0);
QCOMPARE(root->entries().at(2), entry3);
QCOMPARE(root->entries().at(3), entry1);
root->moveEntryUp(entry3);
QCOMPARE(root->entries().at(0), entry2);
QCOMPARE(root->entries().at(1), entry3);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry1);
root->moveEntryDown(entry2);
QCOMPARE(root->entries().at(0), entry3);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry0);
QCOMPARE(root->entries().at(3), entry1);
root->moveEntryUp(entry1);
QCOMPARE(root->entries().at(0), entry3);
QCOMPARE(root->entries().at(1), entry2);
QCOMPARE(root->entries().at(2), entry1);
QCOMPARE(root->entries().at(3), entry0);
}

View file

@ -49,6 +49,7 @@ private slots:
void testHierarchy();
void testApplyGroupIconRecursively();
void testUsernamesRecursive();
void testMove();
};
#endif // KEEPASSX_TESTGROUP_H