Quick GUI: Hide entries and their children when deleted

This commit is contained in:
Martchus 2018-09-10 22:46:04 +02:00
parent 55eddd3fed
commit 316f1ee2a7
4 changed files with 108 additions and 9 deletions

View File

@ -11,7 +11,7 @@ Kirigami.ScrollablePage {
property alias rootIndex: delegateModel.rootIndex
Layout.fillWidth: true
title: "?"
title: entryModel.data(rootIndex)
actions {
main: Kirigami.Action {
iconName: "list-add"

View File

@ -7,6 +7,8 @@ import org.kde.kirigami 2.4 as Kirigami
Kirigami.ApplicationWindow {
id: root
property alias showPasswordsOnFocus: showPasswordsOnFocusSwitch.checked
property var fieldsPage: undefined
property var lastEntriesPage: undefined
header: Kirigami.ApplicationHeader {
backButtonEnabled: false
@ -220,6 +222,25 @@ Kirigami.ApplicationWindow {
onNewNotification: {
showPassiveNotification(message)
}
onCurrentAccountChanged: {
// remove the fields page if the current account has been removed
if (!nativeInterface.hasCurrentAccount) {
pageStack.pop(lastEntriesPage)
}
}
onEntryAboutToBeRemoved: {
// remove all possibly open stack pages of the removed entry and its children
for (var i = pageStack.depth - 1; i >= 0; --i) {
var stackPage = pageStack.get(i)
if (!stackPage) {
continue
}
if (stackPage.rootIndex === removedIndex) {
pageStack.pop(lastEntriesPage = pageStack.get(i - 1))
return
}
}
}
}
Component {
@ -251,20 +272,27 @@ Kirigami.ApplicationWindow {
}
function clearStack() {
pageStack.pop(root.pageStack.initialPage, Controls.StackView.Immediate)
pageStack.pop(lastEntriesPage = root.pageStack.initialPage,
Controls.StackView.Immediate)
}
function pushStackEntry(entryModel, rootIndex) {
pageStack.push(entriesComponent.createObject(root, {
"entryModel": entryModel,
"rootIndex": rootIndex,
"title": entryModel.data(
rootIndex)
}))
pageStack.push(lastEntriesPage = entriesComponent.createObject(root, {
"entryModel": entryModel,
"rootIndex": rootIndex
}))
}
function pushAccountEdit() {
pageStack.push(fieldsComponent.createObject(root))
// lazy-initialize fieldsPage
if (!fieldsPage) {
fieldsPage = fieldsComponent.createObject(root)
}
// remove fieldsPage if already shown to prevent warning
if (pageStack.get(pageStack.depth - 1) === fieldsPage) {
pageStack.pop(lastEntriesPage)
}
pageStack.push(fieldsPage)
}
function createFileActions(files) {

View File

@ -23,6 +23,7 @@
#include <QSettings>
#include <QStringBuilder>
#include <cassert>
#include <stdexcept>
using namespace std;
@ -40,6 +41,7 @@ Controller::Controller(QSettings &settings, const QString &filePath, QObject *pa
, m_useNativeFileDialog(false)
{
m_entryFilterModel.setSourceModel(&m_entryModel);
connect(&m_entryModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &Controller::handleEntriesRemoved);
// share settings with main window
m_settings.beginGroup(QStringLiteral("mainwindow"));
@ -258,6 +260,50 @@ void Controller::handleFileSelectionCanceled()
emit newNotification(tr("Canceled file selection"));
}
void Controller::handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last)
{
// handle deletion of root (currently the view doesn't allow this)
const auto *const parentEntry = m_entryModel.entry(parentIndex);
if (!parentEntry) {
emit entryAboutToBeRemoved(m_entryModel.index(0, 0, QModelIndex()));
setCurrentAccount(nullptr);
return;
}
// assert arguments
assert(parentEntry->type() == EntryType::Node);
const auto &childEntries = static_cast<const NodeEntry *>(parentEntry)->children();
assert(first >= 0 && static_cast<size_t>(first) < childEntries.size());
assert(last >= 0 && static_cast<size_t>(last) < childEntries.size());
// iterate from first to last of the deleted entries
const auto *const currentAccount = this->currentAccount();
for (; first <= last; ++first) {
// inform view about deletion
emit entryAboutToBeRemoved(m_entryModel.index(first, 0, parentIndex));
// unset current account if it is under the deleted node
if (!currentAccount) {
continue;
}
const auto *const childEntry = childEntries[static_cast<size_t>(first)];
switch (childEntry->type()) {
case EntryType::Account:
if (currentAccount == static_cast<const AccountEntry *>(childEntry)) {
setCurrentAccount(nullptr);
}
break;
case EntryType::Node:
// FIXME: remove const_cast in passwordfile v4
if (currentAccount->isIndirectChildOf(static_cast<NodeEntry *>(const_cast<Entry *>(childEntry)))) {
setCurrentAccount(nullptr);
}
break;
default:;
}
}
}
QStringList Controller::pasteEntries(const QModelIndex &destinationParent, int row)
{
if (m_cutEntries.isEmpty() || !m_entryModel.isNode(destinationParent)) {

View File

@ -25,8 +25,10 @@ class Controller : public QObject {
Q_PROPERTY(EntryModel *entryModel READ entryModel NOTIFY entryModelChanged)
Q_PROPERTY(EntryFilterModel *entryFilterModel READ entryFilterModel NOTIFY entryFilterModelChanged)
Q_PROPERTY(FieldModel *fieldModel READ fieldModel NOTIFY fieldModelChanged)
Q_PROPERTY(Io::AccountEntry *currentAccount READ currentAccount WRITE setCurrentAccount NOTIFY currentAccountChanged)
Q_PROPERTY(QModelIndex currentAccountIndex READ currentAccountIndex WRITE setCurrentAccountIndex NOTIFY currentAccountChanged)
Q_PROPERTY(QString currentAccountName READ currentAccountName NOTIFY currentAccountChanged)
Q_PROPERTY(bool hasCurrentAccount READ hasCurrentAccount NOTIFY currentAccountChanged)
Q_PROPERTY(QList<QPersistentModelIndex> cutEntries READ cutEntries WRITE setCutEntries NOTIFY cutEntriesChanged)
Q_PROPERTY(bool canPaste READ canPaste NOTIFY cutEntriesChanged)
Q_PROPERTY(QStringList recentFiles READ recentFiles NOTIFY recentFilesChanged)
@ -48,8 +50,11 @@ public:
EntryModel *entryModel();
EntryFilterModel *entryFilterModel();
FieldModel *fieldModel();
Io::AccountEntry *currentAccount();
void setCurrentAccount(Io::AccountEntry *entry);
QModelIndex currentAccountIndex() const;
void setCurrentAccountIndex(const QModelIndex &accountIndex);
bool hasCurrentAccount() const;
const QList<QPersistentModelIndex> &cutEntries() const;
void setCutEntries(const QList<QPersistentModelIndex> &cutEntries);
QString currentAccountName() const;
@ -92,6 +97,10 @@ signals:
void newNotification(const QString &message);
void useNativeFileDialogChanged(bool useNativeFileDialog);
void supportsNativeFileDialogChanged();
void entryAboutToBeRemoved(const QModelIndex &removedIndex);
private slots:
void handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last);
private:
void resetFileStatus();
@ -166,6 +175,17 @@ inline FieldModel *Controller::fieldModel()
return &m_fieldModel;
}
inline Io::AccountEntry *Controller::currentAccount()
{
return m_fieldModel.accountEntry();
}
inline void Controller::setCurrentAccount(Io::AccountEntry *entry)
{
m_fieldModel.setAccountEntry(entry);
emit currentAccountChanged();
}
inline QModelIndex Controller::currentAccountIndex() const
{
return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex();
@ -177,6 +197,11 @@ inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndex)
emit currentAccountChanged();
}
inline bool Controller::hasCurrentAccount() const
{
return m_fieldModel.accountEntry() != nullptr;
}
inline const QList<QPersistentModelIndex> &Controller::cutEntries() const
{
return m_cutEntries;