mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
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:
parent
43c82ccb09
commit
eb198271ac
2
COPYING
2
COPYING
@ -155,6 +155,8 @@ Files: share/icons/application/scalable/actions/application-exit.svg
|
|||||||
share/icons/application/scalable/actions/help-about.svg
|
share/icons/application/scalable/actions/help-about.svg
|
||||||
share/icons/application/scalable/actions/key-enter.svg
|
share/icons/application/scalable/actions/key-enter.svg
|
||||||
share/icons/application/scalable/actions/message-close.svg
|
share/icons/application/scalable/actions/message-close.svg
|
||||||
|
share/icons/application/scalable/actions/move-down.svg
|
||||||
|
share/icons/application/scalable/actions/move-up.svg
|
||||||
share/icons/application/scalable/actions/paperclip.svg
|
share/icons/application/scalable/actions/paperclip.svg
|
||||||
share/icons/application/scalable/actions/password-copy.svg
|
share/icons/application/scalable/actions/password-copy.svg
|
||||||
share/icons/application/scalable/actions/password-generate.svg
|
share/icons/application/scalable/actions/password-generate.svg
|
||||||
|
1
share/icons/application/scalable/actions/move-down.svg
Normal file
1
share/icons/application/scalable/actions/move-down.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z" /></svg>
|
After Width: | Height: | Size: 413 B |
1
share/icons/application/scalable/actions/move-up.svg
Normal file
1
share/icons/application/scalable/actions/move-up.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41,18.41L6,17L12,11L18,17L16.59,18.41L12,13.83L7.41,18.41M7.41,12.41L6,11L12,5L18,11L16.59,12.41L12,7.83L7.41,12.41Z" /></svg>
|
After Width: | Height: | Size: 414 B |
@ -43,6 +43,8 @@
|
|||||||
<file>application/scalable/actions/key-enter.svg</file>
|
<file>application/scalable/actions/key-enter.svg</file>
|
||||||
<file>application/scalable/actions/keyboard-shortcuts.svg</file>
|
<file>application/scalable/actions/keyboard-shortcuts.svg</file>
|
||||||
<file>application/scalable/actions/message-close.svg</file>
|
<file>application/scalable/actions/message-close.svg</file>
|
||||||
|
<file>application/scalable/actions/move-down.svg</file>
|
||||||
|
<file>application/scalable/actions/move-up.svg</file>
|
||||||
<file>application/scalable/actions/object-locked.svg</file>
|
<file>application/scalable/actions/object-locked.svg</file>
|
||||||
<file>application/scalable/actions/object-unlocked.svg</file>
|
<file>application/scalable/actions/object-unlocked.svg</file>
|
||||||
<file>application/scalable/actions/paperclip.svg</file>
|
<file>application/scalable/actions/paperclip.svg</file>
|
||||||
|
@ -836,9 +836,9 @@ void Database::setEmitModified(bool value)
|
|||||||
m_emitModified = value;
|
m_emitModified = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::isModified() const
|
bool Database::isModified(bool includeNonDataChanges) const
|
||||||
{
|
{
|
||||||
return m_modified;
|
return m_modified || (includeNonDataChanges && m_hasNonDataChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::markAsModified()
|
void Database::markAsModified()
|
||||||
@ -855,11 +855,17 @@ void Database::markAsClean()
|
|||||||
bool emitSignal = m_modified;
|
bool emitSignal = m_modified;
|
||||||
m_modified = false;
|
m_modified = false;
|
||||||
m_modifiedTimer.stop();
|
m_modifiedTimer.stop();
|
||||||
|
m_hasNonDataChange = false;
|
||||||
if (emitSignal) {
|
if (emitSignal) {
|
||||||
emit databaseSaved();
|
emit databaseSaved();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::markNonDataChange()
|
||||||
|
{
|
||||||
|
m_hasNonDataChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param uuid UUID of the database
|
* @param uuid UUID of the database
|
||||||
* @return pointer to the database or nullptr if no such database exists
|
* @return pointer to the database or nullptr if no such database exists
|
||||||
|
@ -81,7 +81,7 @@ public:
|
|||||||
void releaseData();
|
void releaseData();
|
||||||
|
|
||||||
bool isInitialized() const;
|
bool isInitialized() const;
|
||||||
bool isModified() const;
|
bool isModified(bool includeNonDataChanges = false) const;
|
||||||
void setEmitModified(bool value);
|
void setEmitModified(bool value);
|
||||||
bool isReadOnly() const;
|
bool isReadOnly() const;
|
||||||
void setReadOnly(bool readOnly);
|
void setReadOnly(bool readOnly);
|
||||||
@ -138,6 +138,7 @@ public slots:
|
|||||||
void markAsModified();
|
void markAsModified();
|
||||||
void markAsClean();
|
void markAsClean();
|
||||||
void updateCommonUsernames(int topN = 10);
|
void updateCommonUsernames(int topN = 10);
|
||||||
|
void markNonDataChange();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void filePathChanged(const QString& oldPath, const QString& newPath);
|
void filePathChanged(const QString& oldPath, const QString& newPath);
|
||||||
@ -210,6 +211,7 @@ private:
|
|||||||
QPointer<FileWatcher> m_fileWatcher;
|
QPointer<FileWatcher> m_fileWatcher;
|
||||||
bool m_modified = false;
|
bool m_modified = false;
|
||||||
bool m_emitModified;
|
bool m_emitModified;
|
||||||
|
bool m_hasNonDataChange = false;
|
||||||
QString m_keyError;
|
QString m_keyError;
|
||||||
|
|
||||||
QList<QString> m_commonUsernames;
|
QList<QString> m_commonUsernames;
|
||||||
|
@ -1066,6 +1066,20 @@ QString Entry::referenceFieldValue(EntryReferenceType referenceType) const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entry::moveUp()
|
||||||
|
{
|
||||||
|
if (m_group) {
|
||||||
|
m_group->moveEntryUp(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::moveDown()
|
||||||
|
{
|
||||||
|
if (m_group) {
|
||||||
|
m_group->moveEntryDown(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Group* Entry::group()
|
Group* Entry::group()
|
||||||
{
|
{
|
||||||
return m_group;
|
return m_group;
|
||||||
|
@ -233,6 +233,9 @@ public:
|
|||||||
void beginUpdate();
|
void beginUpdate();
|
||||||
bool endUpdate();
|
bool endUpdate();
|
||||||
|
|
||||||
|
void moveUp();
|
||||||
|
void moveDown();
|
||||||
|
|
||||||
Group* group();
|
Group* group();
|
||||||
const Group* group() const;
|
const Group* group() const;
|
||||||
void setGroup(Group* group);
|
void setGroup(Group* group);
|
||||||
|
@ -48,6 +48,7 @@ Group::Group()
|
|||||||
|
|
||||||
connect(m_customData, SIGNAL(customDataModified()), this, SIGNAL(groupModified()));
|
connect(m_customData, SIGNAL(customDataModified()), this, SIGNAL(groupModified()));
|
||||||
connect(this, SIGNAL(groupModified()), SLOT(updateTimeinfo()));
|
connect(this, SIGNAL(groupModified()), SLOT(updateTimeinfo()));
|
||||||
|
connect(this, SIGNAL(groupNonDataChange()), SLOT(updateTimeinfo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Group::~Group()
|
Group::~Group()
|
||||||
@ -364,11 +365,11 @@ void Group::setExpanded(bool expanded)
|
|||||||
{
|
{
|
||||||
if (m_data.isExpanded != expanded) {
|
if (m_data.isExpanded != expanded) {
|
||||||
m_data.isExpanded = expanded;
|
m_data.isExpanded = expanded;
|
||||||
if (!config()->get(Config::TrackNonDataChanges).toBool()) {
|
if (config()->get(Config::TrackNonDataChanges).toBool()) {
|
||||||
updateTimeinfo();
|
emit groupModified();
|
||||||
return;
|
} else {
|
||||||
|
emit groupNonDataChange();
|
||||||
}
|
}
|
||||||
emit groupModified();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,6 +965,40 @@ void Group::removeEntry(Entry* entry)
|
|||||||
emit entryRemoved(entry);
|
emit entryRemoved(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Group::moveEntryUp(Entry* entry)
|
||||||
|
{
|
||||||
|
int row = m_entries.indexOf(entry);
|
||||||
|
if (row <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit entryAboutToMoveUp(row);
|
||||||
|
m_entries.move(row, row - 1);
|
||||||
|
emit entryMovedUp();
|
||||||
|
if (config()->get(Config::TrackNonDataChanges).toBool()) {
|
||||||
|
emit groupModified();
|
||||||
|
} else {
|
||||||
|
emit groupNonDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Group::moveEntryDown(Entry* entry)
|
||||||
|
{
|
||||||
|
int row = m_entries.indexOf(entry);
|
||||||
|
if (row >= m_entries.size() - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit entryAboutToMoveDown(row);
|
||||||
|
m_entries.move(row, row + 1);
|
||||||
|
emit entryMovedDown();
|
||||||
|
if (config()->get(Config::TrackNonDataChanges).toBool()) {
|
||||||
|
emit groupModified();
|
||||||
|
} else {
|
||||||
|
emit groupNonDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Group::connectDatabaseSignalsRecursive(Database* db)
|
void Group::connectDatabaseSignalsRecursive(Database* db)
|
||||||
{
|
{
|
||||||
if (m_db) {
|
if (m_db) {
|
||||||
@ -989,6 +1024,7 @@ void Group::connectDatabaseSignalsRecursive(Database* db)
|
|||||||
connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
|
connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
|
||||||
connect(this, SIGNAL(groupMoved()), db, SIGNAL(groupMoved()));
|
connect(this, SIGNAL(groupMoved()), db, SIGNAL(groupMoved()));
|
||||||
connect(this, SIGNAL(groupModified()), db, SLOT(markAsModified()));
|
connect(this, SIGNAL(groupModified()), db, SLOT(markAsModified()));
|
||||||
|
connect(this, SIGNAL(groupNonDataChange()), db, SLOT(markNonDataChange()));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +167,8 @@ public:
|
|||||||
|
|
||||||
void addEntry(Entry* entry);
|
void addEntry(Entry* entry);
|
||||||
void removeEntry(Entry* entry);
|
void removeEntry(Entry* entry);
|
||||||
|
void moveEntryUp(Entry* entry);
|
||||||
|
void moveEntryDown(Entry* entry);
|
||||||
|
|
||||||
void applyGroupIconOnCreateTo(Entry* entry);
|
void applyGroupIconOnCreateTo(Entry* entry);
|
||||||
void applyGroupIconTo(Entry* entry);
|
void applyGroupIconTo(Entry* entry);
|
||||||
@ -185,10 +187,15 @@ signals:
|
|||||||
void aboutToMove(Group* group, Group* toGroup, int index);
|
void aboutToMove(Group* group, Group* toGroup, int index);
|
||||||
void groupMoved();
|
void groupMoved();
|
||||||
void groupModified();
|
void groupModified();
|
||||||
|
void groupNonDataChange();
|
||||||
void entryAboutToAdd(Entry* entry);
|
void entryAboutToAdd(Entry* entry);
|
||||||
void entryAdded(Entry* entry);
|
void entryAdded(Entry* entry);
|
||||||
void entryAboutToRemove(Entry* entry);
|
void entryAboutToRemove(Entry* entry);
|
||||||
void entryRemoved(Entry* entry);
|
void entryRemoved(Entry* entry);
|
||||||
|
void entryAboutToMoveUp(int row);
|
||||||
|
void entryMovedUp();
|
||||||
|
void entryAboutToMoveDown(int row);
|
||||||
|
void entryMovedDown();
|
||||||
void entryDataChanged(Entry* entry);
|
void entryDataChanged(Entry* entry);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -279,6 +279,11 @@ bool DatabaseWidget::isSaving() const
|
|||||||
return m_db->isSaving();
|
return m_db->isSaving();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DatabaseWidget::isSorted() const
|
||||||
|
{
|
||||||
|
return m_entryView->isSorted();
|
||||||
|
}
|
||||||
|
|
||||||
bool DatabaseWidget::isSearchActive() const
|
bool DatabaseWidget::isSearchActive() const
|
||||||
{
|
{
|
||||||
return m_entryView->inSearchMode();
|
return m_entryView->inSearchMode();
|
||||||
@ -645,6 +650,24 @@ void DatabaseWidget::focusOnGroups()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::moveEntryUp()
|
||||||
|
{
|
||||||
|
auto currentEntry = currentSelectedEntry();
|
||||||
|
if (currentEntry) {
|
||||||
|
currentEntry->moveUp();
|
||||||
|
m_entryView->setCurrentEntry(currentEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::moveEntryDown()
|
||||||
|
{
|
||||||
|
auto currentEntry = currentSelectedEntry();
|
||||||
|
if (currentEntry) {
|
||||||
|
currentEntry->moveDown();
|
||||||
|
m_entryView->setCurrentEntry(currentEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DatabaseWidget::copyTitle()
|
void DatabaseWidget::copyTitle()
|
||||||
{
|
{
|
||||||
auto currentEntry = currentSelectedEntry();
|
auto currentEntry = currentSelectedEntry();
|
||||||
@ -1510,7 +1533,7 @@ bool DatabaseWidget::lock()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_db->isModified()) {
|
if (m_db->isModified(true)) {
|
||||||
bool saved = false;
|
bool saved = false;
|
||||||
// Attempt to save on exit, but don't block locking if it fails
|
// Attempt to save on exit, but don't block locking if it fails
|
||||||
if (config()->get(Config::AutoSaveOnExit).toBool()
|
if (config()->get(Config::AutoSaveOnExit).toBool()
|
||||||
@ -1594,7 +1617,7 @@ void DatabaseWidget::reloadDatabaseFile()
|
|||||||
QString error;
|
QString error;
|
||||||
auto db = QSharedPointer<Database>::create(m_db->filePath());
|
auto db = QSharedPointer<Database>::create(m_db->filePath());
|
||||||
if (db->open(database()->key(), &error)) {
|
if (db->open(database()->key(), &error)) {
|
||||||
if (m_db->isModified()) {
|
if (m_db->isModified(true)) {
|
||||||
// Ask if we want to merge changes into new database
|
// Ask if we want to merge changes into new database
|
||||||
auto result = MessageBox::question(
|
auto result = MessageBox::question(
|
||||||
this,
|
this,
|
||||||
@ -1641,6 +1664,11 @@ int DatabaseWidget::numberOfSelectedEntries() const
|
|||||||
return m_entryView->numberOfSelectedEntries();
|
return m_entryView->numberOfSelectedEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int DatabaseWidget::currentEntryIndex() const
|
||||||
|
{
|
||||||
|
return m_entryView->currentEntryIndex();
|
||||||
|
}
|
||||||
|
|
||||||
QStringList DatabaseWidget::customEntryAttributes() const
|
QStringList DatabaseWidget::customEntryAttributes() const
|
||||||
{
|
{
|
||||||
Entry* entry = m_entryView->currentEntry();
|
Entry* entry = m_entryView->currentEntry();
|
||||||
|
@ -83,6 +83,7 @@ public:
|
|||||||
DatabaseWidget::Mode currentMode() const;
|
DatabaseWidget::Mode currentMode() const;
|
||||||
bool isLocked() const;
|
bool isLocked() const;
|
||||||
bool isSaving() const;
|
bool isSaving() const;
|
||||||
|
bool isSorted() const;
|
||||||
bool isSearchActive() const;
|
bool isSearchActive() const;
|
||||||
bool isEntryViewActive() const;
|
bool isEntryViewActive() const;
|
||||||
bool isEntryEditActive() const;
|
bool isEntryEditActive() const;
|
||||||
@ -99,6 +100,7 @@ public:
|
|||||||
bool isGroupSelected() const;
|
bool isGroupSelected() const;
|
||||||
bool isRecycleBinSelected() const;
|
bool isRecycleBinSelected() const;
|
||||||
int numberOfSelectedEntries() const;
|
int numberOfSelectedEntries() const;
|
||||||
|
int currentEntryIndex() const;
|
||||||
|
|
||||||
QStringList customEntryAttributes() const;
|
QStringList customEntryAttributes() const;
|
||||||
bool isEditWidgetModified() const;
|
bool isEditWidgetModified() const;
|
||||||
@ -167,6 +169,8 @@ public slots:
|
|||||||
void deleteEntries(QList<Entry*> entries);
|
void deleteEntries(QList<Entry*> entries);
|
||||||
void focusOnEntries();
|
void focusOnEntries();
|
||||||
void focusOnGroups();
|
void focusOnGroups();
|
||||||
|
void moveEntryUp();
|
||||||
|
void moveEntryDown();
|
||||||
void copyTitle();
|
void copyTitle();
|
||||||
void copyUsername();
|
void copyUsername();
|
||||||
void copyPassword();
|
void copyPassword();
|
||||||
|
@ -128,6 +128,9 @@ MainWindow::MainWindow()
|
|||||||
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
|
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
|
||||||
m_entryContextMenu->addAction(m_ui->actionEntryNew);
|
m_entryContextMenu->addAction(m_ui->actionEntryNew);
|
||||||
m_entryContextMenu->addSeparator();
|
m_entryContextMenu->addSeparator();
|
||||||
|
m_entryContextMenu->addAction(m_ui->actionEntryMoveUp);
|
||||||
|
m_entryContextMenu->addAction(m_ui->actionEntryMoveDown);
|
||||||
|
m_entryContextMenu->addSeparator();
|
||||||
m_entryContextMenu->addAction(m_ui->actionEntryOpenUrl);
|
m_entryContextMenu->addAction(m_ui->actionEntryOpenUrl);
|
||||||
m_entryContextMenu->addAction(m_ui->actionEntryDownloadIcon);
|
m_entryContextMenu->addAction(m_ui->actionEntryDownloadIcon);
|
||||||
|
|
||||||
@ -236,6 +239,8 @@ MainWindow::MainWindow()
|
|||||||
m_ui->actionEntryTotp->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
|
m_ui->actionEntryTotp->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
|
||||||
m_ui->actionEntryDownloadIcon->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
|
m_ui->actionEntryDownloadIcon->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
|
||||||
m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T);
|
m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T);
|
||||||
|
m_ui->actionEntryMoveUp->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Up);
|
||||||
|
m_ui->actionEntryMoveDown->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Down);
|
||||||
m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B);
|
m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B);
|
||||||
m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C);
|
m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C);
|
||||||
m_ui->actionEntryAutoType->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
|
m_ui->actionEntryAutoType->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
|
||||||
@ -254,6 +259,8 @@ MainWindow::MainWindow()
|
|||||||
m_ui->actionEntryTotp->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryTotp->setShortcutVisibleInContextMenu(true);
|
||||||
m_ui->actionEntryDownloadIcon->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryDownloadIcon->setShortcutVisibleInContextMenu(true);
|
||||||
m_ui->actionEntryCopyTotp->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryCopyTotp->setShortcutVisibleInContextMenu(true);
|
||||||
|
m_ui->actionEntryMoveUp->setShortcutVisibleInContextMenu(true);
|
||||||
|
m_ui->actionEntryMoveDown->setShortcutVisibleInContextMenu(true);
|
||||||
m_ui->actionEntryCopyUsername->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryCopyUsername->setShortcutVisibleInContextMenu(true);
|
||||||
m_ui->actionEntryCopyPassword->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryCopyPassword->setShortcutVisibleInContextMenu(true);
|
||||||
m_ui->actionEntryAutoType->setShortcutVisibleInContextMenu(true);
|
m_ui->actionEntryAutoType->setShortcutVisibleInContextMenu(true);
|
||||||
@ -336,6 +343,8 @@ MainWindow::MainWindow()
|
|||||||
m_ui->actionEntryEdit->setIcon(resources()->icon("entry-edit"));
|
m_ui->actionEntryEdit->setIcon(resources()->icon("entry-edit"));
|
||||||
m_ui->actionEntryDelete->setIcon(resources()->icon("entry-delete"));
|
m_ui->actionEntryDelete->setIcon(resources()->icon("entry-delete"));
|
||||||
m_ui->actionEntryAutoType->setIcon(resources()->icon("auto-type"));
|
m_ui->actionEntryAutoType->setIcon(resources()->icon("auto-type"));
|
||||||
|
m_ui->actionEntryMoveUp->setIcon(resources()->icon("move-up"));
|
||||||
|
m_ui->actionEntryMoveDown->setIcon(resources()->icon("move-down"));
|
||||||
m_ui->actionEntryCopyUsername->setIcon(resources()->icon("username-copy"));
|
m_ui->actionEntryCopyUsername->setIcon(resources()->icon("username-copy"));
|
||||||
m_ui->actionEntryCopyPassword->setIcon(resources()->icon("password-copy"));
|
m_ui->actionEntryCopyPassword->setIcon(resources()->icon("password-copy"));
|
||||||
m_ui->actionEntryCopyURL->setIcon(resources()->icon("url-copy"));
|
m_ui->actionEntryCopyURL->setIcon(resources()->icon("url-copy"));
|
||||||
@ -420,6 +429,8 @@ MainWindow::MainWindow()
|
|||||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp()));
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode()));
|
m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle()));
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle()));
|
||||||
|
m_actionMultiplexer.connect(m_ui->actionEntryMoveUp, SIGNAL(triggered()), SLOT(moveEntryUp()));
|
||||||
|
m_actionMultiplexer.connect(m_ui->actionEntryMoveDown, SIGNAL(triggered()), SLOT(moveEntryDown()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername()));
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword()));
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyURL, SIGNAL(triggered()), SLOT(copyURL()));
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyURL, SIGNAL(triggered()), SLOT(copyURL()));
|
||||||
@ -662,11 +673,19 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
|||||||
bool currentGroupHasChildren = dbWidget->currentGroup()->hasChildren();
|
bool currentGroupHasChildren = dbWidget->currentGroup()->hasChildren();
|
||||||
bool currentGroupHasEntries = !dbWidget->currentGroup()->entries().isEmpty();
|
bool currentGroupHasEntries = !dbWidget->currentGroup()->entries().isEmpty();
|
||||||
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
|
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
|
||||||
|
bool sorted = dbWidget->isSorted();
|
||||||
|
int entryIndex = dbWidget->currentEntryIndex();
|
||||||
|
int numEntries = dbWidget->currentGroup()->entries().size();
|
||||||
|
|
||||||
m_ui->actionEntryNew->setEnabled(true);
|
m_ui->actionEntryNew->setEnabled(true);
|
||||||
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
|
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
|
||||||
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
|
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
|
||||||
m_ui->actionEntryDelete->setEnabled(entriesSelected);
|
m_ui->actionEntryDelete->setEnabled(entriesSelected);
|
||||||
|
m_ui->actionEntryMoveUp->setVisible(!sorted);
|
||||||
|
m_ui->actionEntryMoveDown->setVisible(!sorted);
|
||||||
|
m_ui->actionEntryMoveUp->setEnabled(singleEntrySelected && !sorted && entryIndex > 0);
|
||||||
|
m_ui->actionEntryMoveDown->setEnabled(singleEntrySelected && !sorted && entryIndex >= 0
|
||||||
|
&& entryIndex < numEntries - 1);
|
||||||
m_ui->actionEntryCopyTitle->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTitle());
|
m_ui->actionEntryCopyTitle->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTitle());
|
||||||
m_ui->actionEntryCopyUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
|
m_ui->actionEntryCopyUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
|
||||||
// NOTE: Copy password is enabled even if the selected entry's password is blank to prevent Ctrl+C
|
// NOTE: Copy password is enabled even if the selected entry's password is blank to prevent Ctrl+C
|
||||||
|
@ -309,6 +309,9 @@
|
|||||||
<addaction name="actionEntryClone"/>
|
<addaction name="actionEntryClone"/>
|
||||||
<addaction name="actionEntryDelete"/>
|
<addaction name="actionEntryDelete"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionEntryMoveUp"/>
|
||||||
|
<addaction name="actionEntryMoveDown"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionEntryCopyUsername"/>
|
<addaction name="actionEntryCopyUsername"/>
|
||||||
<addaction name="actionEntryCopyPassword"/>
|
<addaction name="actionEntryCopyPassword"/>
|
||||||
<addaction name="menuEntryCopyAttribute"/>
|
<addaction name="menuEntryCopyAttribute"/>
|
||||||
@ -578,6 +581,28 @@
|
|||||||
<string>&Clone Entry…</string>
|
<string>&Clone Entry…</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionEntryMoveUp">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move u&p</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Move entry one step up</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionEntryMoveDown">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move do&wn</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Move entry one step down</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionEntryCopyUsername">
|
<action name="actionEntryCopyUsername">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
@ -497,10 +497,41 @@ void EntryModel::entryRemoved()
|
|||||||
if (m_group) {
|
if (m_group) {
|
||||||
m_entries = m_group->entries();
|
m_entries = m_group->entries();
|
||||||
}
|
}
|
||||||
|
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntryModel::entryAboutToMoveUp(int row)
|
||||||
|
{
|
||||||
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), row - 1);
|
||||||
|
if (m_group) {
|
||||||
|
m_entries.move(row, row - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryModel::entryMovedUp()
|
||||||
|
{
|
||||||
|
if (m_group) {
|
||||||
|
m_entries = m_group->entries();
|
||||||
|
}
|
||||||
|
endMoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryModel::entryAboutToMoveDown(int row)
|
||||||
|
{
|
||||||
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), row + 2);
|
||||||
|
if (m_group) {
|
||||||
|
m_entries.move(row, row + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryModel::entryMovedDown()
|
||||||
|
{
|
||||||
|
if (m_group) {
|
||||||
|
m_entries = m_group->entries();
|
||||||
|
}
|
||||||
|
endMoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
void EntryModel::entryDataChanged(Entry* entry)
|
void EntryModel::entryDataChanged(Entry* entry)
|
||||||
{
|
{
|
||||||
int row = m_entries.indexOf(entry);
|
int row = m_entries.indexOf(entry);
|
||||||
@ -524,6 +555,10 @@ void EntryModel::makeConnections(const Group* group)
|
|||||||
connect(group, SIGNAL(entryAdded(Entry*)), SLOT(entryAdded(Entry*)));
|
connect(group, SIGNAL(entryAdded(Entry*)), SLOT(entryAdded(Entry*)));
|
||||||
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*)));
|
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*)));
|
||||||
connect(group, SIGNAL(entryRemoved(Entry*)), SLOT(entryRemoved()));
|
connect(group, SIGNAL(entryRemoved(Entry*)), SLOT(entryRemoved()));
|
||||||
|
connect(group, SIGNAL(entryAboutToMoveUp(int)), SLOT(entryAboutToMoveUp(int)));
|
||||||
|
connect(group, SIGNAL(entryMovedUp()), SLOT(entryMovedUp()));
|
||||||
|
connect(group, SIGNAL(entryAboutToMoveDown(int)), SLOT(entryAboutToMoveDown(int)));
|
||||||
|
connect(group, SIGNAL(entryMovedDown()), SLOT(entryMovedDown()));
|
||||||
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
|
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,10 @@ private slots:
|
|||||||
void entryAdded(Entry* entry);
|
void entryAdded(Entry* entry);
|
||||||
void entryAboutToRemove(Entry* entry);
|
void entryAboutToRemove(Entry* entry);
|
||||||
void entryRemoved();
|
void entryRemoved();
|
||||||
|
void entryAboutToMoveUp(int row);
|
||||||
|
void entryMovedUp();
|
||||||
|
void entryAboutToMoveDown(int row);
|
||||||
|
void entryMovedDown();
|
||||||
void entryDataChanged(Entry* entry);
|
void entryDataChanged(Entry* entry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -30,6 +30,8 @@ EntryView::EntryView(QWidget* parent)
|
|||||||
: QTreeView(parent)
|
: QTreeView(parent)
|
||||||
, m_model(new EntryModel(this))
|
, m_model(new EntryModel(this))
|
||||||
, m_sortModel(new SortFilterHideProxyModel(this))
|
, m_sortModel(new SortFilterHideProxyModel(this))
|
||||||
|
, m_lastIndex(-1)
|
||||||
|
, m_lastOrder(Qt::AscendingOrder)
|
||||||
, m_inSearchMode(false)
|
, m_inSearchMode(false)
|
||||||
{
|
{
|
||||||
m_sortModel->setSourceModel(m_model);
|
m_sortModel->setSourceModel(m_model);
|
||||||
@ -120,7 +122,7 @@ EntryView::EntryView(QWidget* parent)
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SIGNAL(viewStateChanged()));
|
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SLOT(sortIndicatorChanged(int,Qt::SortOrder)));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +134,31 @@ void EntryView::contextMenuShortcutPressed()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntryView::sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
|
||||||
|
{
|
||||||
|
int oldIndex = m_lastIndex;
|
||||||
|
m_lastIndex = logicalIndex;
|
||||||
|
Qt::SortOrder oldOrder = m_lastOrder;
|
||||||
|
m_lastOrder = order;
|
||||||
|
|
||||||
|
if (oldIndex == logicalIndex // same index
|
||||||
|
&& oldOrder == Qt::DescendingOrder // old order is descending
|
||||||
|
&& order == Qt::AscendingOrder) // new order is ascending
|
||||||
|
{
|
||||||
|
// a change from descending to ascending on the same column occurred
|
||||||
|
// this sets the header into no sort order
|
||||||
|
header()->setSortIndicator(-1, Qt::AscendingOrder);
|
||||||
|
// do not emit any signals, header()->setSortIndicator recursively calls this
|
||||||
|
// function and the signals are emitted in the else part
|
||||||
|
} else {
|
||||||
|
// call emitEntrySelectionChanged even though the selection did not really change
|
||||||
|
// this triggers the evaluation of the menu activation and anyway, the position
|
||||||
|
// of the selected entry within the widget did change
|
||||||
|
emitEntrySelectionChanged();
|
||||||
|
emit viewStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntryView::keyPressEvent(QKeyEvent* event)
|
void EntryView::keyPressEvent(QKeyEvent* event)
|
||||||
{
|
{
|
||||||
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
||||||
@ -211,6 +238,11 @@ bool EntryView::inSearchMode()
|
|||||||
return m_inSearchMode;
|
return m_inSearchMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntryView::isSorted()
|
||||||
|
{
|
||||||
|
return header()->sortIndicatorSection() != -1;
|
||||||
|
}
|
||||||
|
|
||||||
void EntryView::emitEntryActivated(const QModelIndex& index)
|
void EntryView::emitEntryActivated(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
Entry* entry = entryFromIndex(index);
|
Entry* entry = entryFromIndex(index);
|
||||||
@ -258,6 +290,17 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EntryView::currentEntryIndex()
|
||||||
|
{
|
||||||
|
QModelIndexList list = selectionModel()->selectedRows();
|
||||||
|
if (list.size() == 1) {
|
||||||
|
auto index = m_sortModel->mapToSource(list.first());
|
||||||
|
return index.row();
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current state of 'Hide Usernames' setting (NOTE: just pass-through for
|
* Get current state of 'Hide Usernames' setting (NOTE: just pass-through for
|
||||||
* m_model)
|
* m_model)
|
||||||
|
@ -39,7 +39,9 @@ public:
|
|||||||
Entry* currentEntry();
|
Entry* currentEntry();
|
||||||
void setCurrentEntry(Entry* entry);
|
void setCurrentEntry(Entry* entry);
|
||||||
Entry* entryFromIndex(const QModelIndex& index);
|
Entry* entryFromIndex(const QModelIndex& index);
|
||||||
|
int currentEntryIndex();
|
||||||
bool inSearchMode();
|
bool inSearchMode();
|
||||||
|
bool isSorted();
|
||||||
int numberOfSelectedEntries();
|
int numberOfSelectedEntries();
|
||||||
void setFirstEntryActive();
|
void setFirstEntryActive();
|
||||||
bool isUsernamesHidden() const;
|
bool isUsernamesHidden() const;
|
||||||
@ -74,12 +76,15 @@ private slots:
|
|||||||
void fitColumnsToContents();
|
void fitColumnsToContents();
|
||||||
void resetViewToDefaults();
|
void resetViewToDefaults();
|
||||||
void contextMenuShortcutPressed();
|
void contextMenuShortcutPressed();
|
||||||
|
void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resetFixedColumns();
|
void resetFixedColumns();
|
||||||
|
|
||||||
EntryModel* const m_model;
|
EntryModel* const m_model;
|
||||||
SortFilterHideProxyModel* const m_sortModel;
|
SortFilterHideProxyModel* const m_sortModel;
|
||||||
|
int m_lastIndex;
|
||||||
|
Qt::SortOrder m_lastOrder;
|
||||||
bool m_inSearchMode;
|
bool m_inSearchMode;
|
||||||
bool m_columnsNeedRelayout = true;
|
bool m_columnsNeedRelayout = true;
|
||||||
|
|
||||||
|
@ -612,3 +612,114 @@ void TestEntry::testIsRecycled()
|
|||||||
db.recycleGroup(group1);
|
db.recycleGroup(group1);
|
||||||
QVERIFY(entry1->isRecycled());
|
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);
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ private slots:
|
|||||||
void testResolveNonIdPlaceholdersToUuid();
|
void testResolveNonIdPlaceholdersToUuid();
|
||||||
void testResolveClonedEntry();
|
void testResolveClonedEntry();
|
||||||
void testIsRecycled();
|
void testIsRecycled();
|
||||||
|
void testMove();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTENTRY_H
|
#endif // KEEPASSX_TESTENTRY_H
|
||||||
|
@ -55,6 +55,9 @@ void TestEntryModel::test()
|
|||||||
|
|
||||||
EntryModel* model = new EntryModel(this);
|
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);
|
ModelTest* modelTest = new ModelTest(model, this);
|
||||||
|
|
||||||
model->setGroup(group1);
|
model->setGroup(group1);
|
||||||
@ -79,6 +82,29 @@ void TestEntryModel::test()
|
|||||||
Entry* entry3 = new Entry();
|
Entry* entry3 = new Entry();
|
||||||
entry3->setGroup(group1);
|
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(spyAboutToAdd.count(), 1);
|
||||||
QCOMPARE(spyAdded.count(), 1);
|
QCOMPARE(spyAdded.count(), 1);
|
||||||
QCOMPARE(spyAboutToRemove.count(), 0);
|
QCOMPARE(spyAboutToRemove.count(), 0);
|
||||||
|
@ -1208,3 +1208,114 @@ void TestGroup::testUsernamesRecursive()
|
|||||||
QVERIFY(usernames.contains("Name2"));
|
QVERIFY(usernames.contains("Name2"));
|
||||||
QVERIFY(usernames.indexOf("Name2") < usernames.indexOf("Name1"));
|
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);
|
||||||
|
}
|
||||||
|
@ -49,6 +49,7 @@ private slots:
|
|||||||
void testHierarchy();
|
void testHierarchy();
|
||||||
void testApplyGroupIconRecursively();
|
void testApplyGroupIconRecursively();
|
||||||
void testUsernamesRecursive();
|
void testUsernamesRecursive();
|
||||||
|
void testMove();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTGROUP_H
|
#endif // KEEPASSX_TESTGROUP_H
|
||||||
|
@ -107,6 +107,8 @@ map() {
|
|||||||
key-enter) echo keyboard-variant ;;
|
key-enter) echo keyboard-variant ;;
|
||||||
keyboard-shortcuts) echo apple-keyboard-command ;;
|
keyboard-shortcuts) echo apple-keyboard-command ;;
|
||||||
message-close) echo close ;;
|
message-close) echo close ;;
|
||||||
|
move-down) echo chevron-double-down ;;
|
||||||
|
move-up) echo chevron-double-up ;;
|
||||||
object-locked) echo lock-outline ;;
|
object-locked) echo lock-outline ;;
|
||||||
object-unlocked) echo lock-open-variant-outline ;;
|
object-unlocked) echo lock-open-variant-outline ;;
|
||||||
paperclip) echo paperclip ;;
|
paperclip) echo paperclip ;;
|
||||||
|
Loading…
Reference in New Issue
Block a user