Quick GUI: Allow filtering entries

So far all operations on entries are disabled when
in filtered state and switching between normal and
filtered view invalidates the stack.
This commit is contained in:
Martchus 2018-09-16 21:37:30 +02:00
parent c3775775d1
commit fa050a422a
8 changed files with 223 additions and 54 deletions

View File

@ -1,6 +1,8 @@
#include "./entryfiltermodel.h" #include "./entryfiltermodel.h"
#include "./entrymodel.h" #include "./entrymodel.h"
#include <cassert>
namespace QtGui { namespace QtGui {
/*! /*!
@ -15,9 +17,43 @@ namespace QtGui {
*/ */
EntryFilterModel::EntryFilterModel(QObject *parent) EntryFilterModel::EntryFilterModel(QObject *parent)
: QSortFilterProxyModel(parent) : QSortFilterProxyModel(parent)
, m_sourceModel(nullptr)
{ {
} }
bool EntryFilterModel::isNode(const QModelIndex &parent) const
{
return m_sourceModel->isNode(mapToSource(parent));
}
void EntryFilterModel::setSourceModel(QAbstractItemModel *sourceModel)
{
if (!sourceModel) {
QSortFilterProxyModel::setSourceModel(sourceModel);
m_sourceModel = nullptr;
return;
}
auto *const entryModel = qobject_cast<EntryModel *>(sourceModel);
assert(entryModel);
QSortFilterProxyModel::setSourceModel(sourceModel);
m_sourceModel = entryModel;
}
void EntryFilterModel::setInsertTypeToNode()
{
if (m_sourceModel) {
m_sourceModel->setInsertTypeToNode();
}
}
void EntryFilterModel::setInsertTypeToAccount()
{
if (m_sourceModel) {
m_sourceModel->setInsertTypeToAccount();
}
}
bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
// just use default implementation // just use default implementation

View File

@ -5,17 +5,26 @@
namespace QtGui { namespace QtGui {
class EntryModel;
class EntryFilterModel : public QSortFilterProxyModel { class EntryFilterModel : public QSortFilterProxyModel {
Q_OBJECT Q_OBJECT
public: public:
explicit EntryFilterModel(QObject *parent = nullptr); explicit EntryFilterModel(QObject *parent = nullptr);
void setSourceModel(QAbstractItemModel *sourceModel) override;
Q_INVOKABLE bool isNode(const QModelIndex &parent) const;
Q_INVOKABLE void setInsertTypeToNode();
Q_INVOKABLE void setInsertTypeToAccount();
protected: protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private: private:
bool hasAcceptedChildren(const QModelIndex &index) const; bool hasAcceptedChildren(const QModelIndex &index) const;
EntryModel *m_sourceModel;
}; };
} // namespace QtGui } // namespace QtGui
#endif // ENTRYFILTERMODEL_H #endif // ENTRYFILTERMODEL_H

View File

@ -41,7 +41,7 @@ public:
explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr); explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr);
#endif #endif
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const override;
Io::NodeEntry *rootEntry(); Io::NodeEntry *rootEntry();
void setRootEntry(Io::NodeEntry *entry); void setRootEntry(Io::NodeEntry *entry);
Q_INVOKABLE Io::Entry *entry(const QModelIndex &index); Q_INVOKABLE Io::Entry *entry(const QModelIndex &index);
@ -49,26 +49,27 @@ public:
Q_INVOKABLE bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries); Q_INVOKABLE bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries);
Io::EntryType insertType() const; Io::EntryType insertType() const;
void setInsertType(Io::EntryType type); void setInsertType(Io::EntryType type);
QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex index(Io::Entry *entry) const; QModelIndex index(Io::Entry *entry) const;
QModelIndex parent(const QModelIndex &child) const; QModelIndex parent(const QModelIndex &child) const override;
bool hasChildren(const QModelIndex &parent) const; bool hasChildren(const QModelIndex &parent) const override;
Q_INVOKABLE bool isNode(const QModelIndex &parent) const; Q_INVOKABLE bool isNode(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QMap<int, QVariant> itemData(const QModelIndex &index) const; QMap<int, QVariant> itemData(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role); bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles); bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); Q_INVOKABLE bool moveRows(
QStringList mimeTypes() const; const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
QMimeData *mimeData(const QModelIndexList &indexes) const; QStringList mimeTypes() const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); QMimeData *mimeData(const QModelIndexList &indexes) const override;
Qt::DropActions supportedDropActions() const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDropActions() const override;
Q_INVOKABLE void setInsertTypeToNode(); Q_INVOKABLE void setInsertTypeToNode();
Q_INVOKABLE void setInsertTypeToAccount(); Q_INVOKABLE void setInsertTypeToAccount();

View File

@ -53,25 +53,26 @@ public:
explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr); explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr);
#endif #endif
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const override;
Io::AccountEntry *accountEntry(); Io::AccountEntry *accountEntry();
const Io::AccountEntry *accountEntry() const; const Io::AccountEntry *accountEntry() const;
void setAccountEntry(Io::AccountEntry *entry); void setAccountEntry(Io::AccountEntry *entry);
std::vector<Io::Field> *fields(); std::vector<Io::Field> *fields();
PasswordVisibility passwordVisibility() const; PasswordVisibility passwordVisibility() const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QMap<int, QVariant> itemData(const QModelIndex &index) const; QMap<int, QVariant> itemData(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role); bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); Q_INVOKABLE bool moveRows(
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
QStringList mimeTypes() const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
QMimeData *mimeData(const QModelIndexList &indices) const; QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indices) const override;
Q_INVOKABLE const Io::Field *field(std::size_t row) const; Q_INVOKABLE const Io::Field *field(std::size_t row) const;
public Q_SLOTS: public Q_SLOTS:

View File

@ -11,18 +11,24 @@ Kirigami.ScrollablePage {
property alias rootIndex: delegateModel.rootIndex property alias rootIndex: delegateModel.rootIndex
Layout.fillWidth: true Layout.fillWidth: true
title: entryModel.data(rootIndex) title: {
var currentEntryName = entryModel.data(rootIndex)
return currentEntryName ? currentEntryName : ""
}
actions { actions {
main: Kirigami.Action { main: Kirigami.Action {
iconName: "list-add" iconName: "list-add"
text: qsTr("Add account") text: qsTr("Add account")
visible: !nativeInterface.hasEntryFilter
enabled: !nativeInterface.hasEntryFilter
onTriggered: insertEntry("Account") onTriggered: insertEntry("Account")
shortcut: "Ctrl+A" shortcut: "Ctrl+A"
} }
left: Kirigami.Action { left: Kirigami.Action {
iconName: "edit-paste" iconName: "edit-paste"
text: qsTr("Paste account") text: qsTr("Paste account")
enabled: nativeInterface.canPaste visible: !nativeInterface.hasEntryFilter
enabled: nativeInterface.canPaste && !nativeInterface.hasEntryFilter
onTriggered: { onTriggered: {
var pastedEntries = nativeInterface.pasteEntries(rootIndex) var pastedEntries = nativeInterface.pasteEntries(rootIndex)
if (pastedEntries.length < 1) { if (pastedEntries.length < 1) {
@ -38,6 +44,8 @@ Kirigami.ScrollablePage {
right: Kirigami.Action { right: Kirigami.Action {
iconName: "folder-add" iconName: "folder-add"
text: qsTr("Add category") text: qsTr("Add category")
visible: !nativeInterface.hasEntryFilter
enabled: !nativeInterface.hasEntryFilter
onTriggered: insertEntry("Node") onTriggered: insertEntry("Node")
shortcut: "Ctrl+Shift+A" shortcut: "Ctrl+Shift+A"
} }
@ -127,6 +135,7 @@ Kirigami.ScrollablePage {
Kirigami.ListItemDragHandle { Kirigami.ListItemDragHandle {
listItem: listItem listItem: listItem
listView: entriesListView listView: entriesListView
enabled: !nativeInterface.hasEntryFilter
// FIXME: not sure why newIndex + 1 is required to be able to move a row at the end // FIXME: not sure why newIndex + 1 is required to be able to move a row at the end
onMoveRequested: entryModel.moveRows( onMoveRequested: entryModel.moveRows(
rootIndex, oldIndex, 1, rootIndex, rootIndex, oldIndex, 1, rootIndex,
@ -167,6 +176,7 @@ Kirigami.ScrollablePage {
Controls.MenuItem { Controls.MenuItem {
icon.name: "edit-cut" icon.name: "edit-cut"
text: qsTr("Cut") text: qsTr("Cut")
enabled: !nativeInterface.hasEntryFilter
onTriggered: { onTriggered: {
nativeInterface.cutEntry( nativeInterface.cutEntry(
entryModel.index(index, 0, entryModel.index(index, 0,
@ -177,12 +187,14 @@ Kirigami.ScrollablePage {
Controls.MenuItem { Controls.MenuItem {
icon.name: "edit-delete" icon.name: "edit-delete"
text: qsTr("Delete") text: qsTr("Delete")
enabled: !nativeInterface.hasEntryFilter
onTriggered: confirmDeletionDialog.confirmDeletion( onTriggered: confirmDeletionDialog.confirmDeletion(
model.name, index) model.name, index)
} }
Controls.MenuItem { Controls.MenuItem {
icon.name: "edit-rename" icon.name: "edit-rename"
text: qsTr("Rename") text: qsTr("Rename")
enabled: !nativeInterface.hasEntryFilter
onTriggered: renameDialog.renameEntry(model.name, onTriggered: renameDialog.renameEntry(model.name,
index) index)
} }
@ -193,6 +205,7 @@ Kirigami.ScrollablePage {
Kirigami.Action { Kirigami.Action {
iconName: "edit-cut" iconName: "edit-cut"
text: qsTr("Cut") text: qsTr("Cut")
enabled: !nativeInterface.hasEntryFilter
onTriggered: { onTriggered: {
nativeInterface.cutEntry(entryModel.index(index, 0, nativeInterface.cutEntry(entryModel.index(index, 0,
rootIndex)) rootIndex))
@ -203,6 +216,7 @@ Kirigami.ScrollablePage {
Kirigami.Action { Kirigami.Action {
iconName: "edit-delete" iconName: "edit-delete"
text: qsTr("Delete") text: qsTr("Delete")
enabled: !nativeInterface.hasEntryFilter
onTriggered: confirmDeletionDialog.confirmDeletion( onTriggered: confirmDeletionDialog.confirmDeletion(
model.name, index) model.name, index)
shortcut: StandardKey.Delete shortcut: StandardKey.Delete
@ -210,6 +224,7 @@ Kirigami.ScrollablePage {
Kirigami.Action { Kirigami.Action {
iconName: "edit-rename" iconName: "edit-rename"
text: qsTr("Rename") text: qsTr("Rename")
enabled: !nativeInterface.hasEntryFilter
onTriggered: renameDialog.renameEntry(model.name, index) onTriggered: renameDialog.renameEntry(model.name, index)
shortcut: "F2" shortcut: "F2"
} }

View File

@ -1,4 +1,5 @@
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Templates 2.0 as T2
import QtQuick.Controls 2.1 as Controls import QtQuick.Controls 2.1 as Controls
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
@ -15,15 +16,6 @@ Kirigami.ApplicationWindow {
minimumHeight: 0 minimumHeight: 0
preferredHeight: Kirigami.Units.gridUnit * 2.3 preferredHeight: Kirigami.Units.gridUnit * 2.3
maximumHeight: Kirigami.Units.gridUnit * 3 maximumHeight: Kirigami.Units.gridUnit * 3
/*
Controls.TextField {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
placeholderText: qsTr("Filter")
width: Kirigami.Units.gridUnit * 8
}
*/
} }
globalDrawer: Kirigami.GlobalDrawer { globalDrawer: Kirigami.GlobalDrawer {
id: leftMenu id: leftMenu
@ -37,6 +29,35 @@ Kirigami.ApplicationWindow {
topContent: ColumnLayout { topContent: ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Item {
Layout.preferredHeight: 4
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: filterTextField.implicitHeight
enabled: nativeInterface.fileOpen
Controls.TextField {
id: filterTextField
anchors.fill: parent
placeholderText: qsTr("Filter")
onTextChanged: nativeInterface.entryFilter = text
}
Kirigami.Icon {
source: "edit-clear"
anchors.right: parent.right
anchors.rightMargin: 6
anchors.verticalCenter: parent.verticalCenter
width: Kirigami.Units.iconSizes.small
height: Kirigami.Units.iconSizes.small
visible: filterTextField.text.length !== 0
MouseArea {
anchors.fill: parent
onClicked: filterTextField.text = ""
}
}
}
Controls.MenuSeparator { Controls.MenuSeparator {
padding: 0 padding: 0
topPadding: 8 topPadding: 8
@ -104,6 +125,7 @@ Kirigami.ApplicationWindow {
shortcut: StandardKey.Open shortcut: StandardKey.Open
}, },
Kirigami.Action { Kirigami.Action {
id: recentlyOpenedAction
text: qsTr("Recently opened ...") text: qsTr("Recently opened ...")
iconName: "document-open-recent" iconName: "document-open-recent"
children: createRecentlyOpenedActions( children: createRecentlyOpenedActions(
@ -210,9 +232,7 @@ Kirigami.ApplicationWindow {
nativeInterface.fileName)) nativeInterface.fileName))
return return
} }
var entryModel = nativeInterface.entryModel initStack()
var rootIndex = entryModel.index(0, 0)
pushStackEntry(entryModel, rootIndex)
showPassiveNotification(qsTr("%1 opened").arg( showPassiveNotification(qsTr("%1 opened").arg(
nativeInterface.fileName)) nativeInterface.fileName))
leftMenu.close() leftMenu.close()
@ -231,6 +251,11 @@ Kirigami.ApplicationWindow {
} }
} }
onEntryAboutToBeRemoved: { onEntryAboutToBeRemoved: {
// get the filter entry index
if (nativeInterface.hasEntryFilter) {
removedIndex = nativeInterface.filterEntryIndex(removedIndex)
}
// remove all possibly open stack pages of the removed entry and its children // remove all possibly open stack pages of the removed entry and its children
for (var i = pageStack.depth - 1; i >= 0; --i) { for (var i = pageStack.depth - 1; i >= 0; --i) {
var stackPage = pageStack.get(i) var stackPage = pageStack.get(i)
@ -243,6 +268,12 @@ Kirigami.ApplicationWindow {
} }
} }
} }
onHasEntryFilterChanged: {
if (nativeInterface.fileOpen) {
pageStack.clear()
initStack()
}
}
} }
Component { Component {
@ -285,6 +316,12 @@ Kirigami.ApplicationWindow {
onActivated: leftMenu.visible = !leftMenu.visible onActivated: leftMenu.visible = !leftMenu.visible
} }
function initStack() {
var entryModel = nativeInterface.hasEntryFilter ? nativeInterface.entryFilterModel : nativeInterface.entryModel
var rootIndex = entryModel.index(0, 0)
pushStackEntry(entryModel, rootIndex)
}
function clearStack() { function clearStack() {
pageStack.pop(lastEntriesPage = root.pageStack.initialPage, pageStack.pop(lastEntriesPage = root.pageStack.initialPage,
Controls.StackView.Immediate) Controls.StackView.Immediate)

View File

@ -309,15 +309,41 @@ void Controller::handleRecentFilesChanged()
m_settings.setValue(QStringLiteral("recententries"), m_recentFiles); m_settings.setValue(QStringLiteral("recententries"), m_recentFiles);
} }
QStringList Controller::pasteEntries(const QModelIndex &destinationParent, int row) QStringList Controller::pasteEntries(const QModelIndex &destinationParentMaybeFromFilterModel, int row)
{ {
if (m_cutEntries.isEmpty() || !m_entryModel.isNode(destinationParent)) { // skip if no entries have been cut
if (m_cutEntries.isEmpty()) {
return QStringList(); return QStringList();
} }
// determine destinationParent and row in the source model
QModelIndex destinationParent;
if (destinationParentMaybeFromFilterModel.model() == &m_entryFilterModel) {
if (row < 0) {
row = m_entryFilterModel.rowCount(destinationParentMaybeFromFilterModel);
}
const auto destinationIndexInFilter = m_entryFilterModel.index(row, 0, destinationParentMaybeFromFilterModel);
if (destinationIndexInFilter.isValid()) {
const auto destinationIndex = m_entryFilterModel.mapToSource(destinationIndexInFilter);
destinationParent = destinationIndex.parent();
row = destinationIndex.row();
} else {
destinationParent = m_entryFilterModel.mapToSource(destinationParentMaybeFromFilterModel);
row = -1;
}
} else {
destinationParent = destinationParentMaybeFromFilterModel;
}
if (row < 0) { if (row < 0) {
row = m_entryModel.rowCount(destinationParent); row = m_entryModel.rowCount(destinationParent);
} }
// skip if destination is no node
if (!m_entryModel.isNode(destinationParent)) {
return QStringList();
}
// move the entries
QStringList successfullyMovedEntries; QStringList successfullyMovedEntries;
successfullyMovedEntries.reserve(m_cutEntries.size()); successfullyMovedEntries.reserve(m_cutEntries.size());
for (const QPersistentModelIndex &cutIndex : m_cutEntries) { for (const QPersistentModelIndex &cutIndex : m_cutEntries) {
@ -401,4 +427,17 @@ void Controller::setUseNativeFileDialog(bool useNativeFileDialog)
m_settings.setValue(QStringLiteral("usenativefiledialog"), m_useNativeFileDialog); m_settings.setValue(QStringLiteral("usenativefiledialog"), m_useNativeFileDialog);
} }
void Controller::setEntryFilter(const QString &filter)
{
const auto previousFilter(m_entryFilterModel.filterRegExp().pattern());
if (filter == previousFilter) {
return;
}
m_entryFilterModel.setFilterRegExp(filter);
emit entryFilterChanged(filter);
if (previousFilter.isEmpty() != filter.isEmpty()) {
emit hasEntryFilterChanged(!filter.isEmpty());
}
}
} // namespace QtGui } // namespace QtGui

View File

@ -34,6 +34,8 @@ class Controller : public QObject {
Q_PROPERTY(QStringList recentFiles READ recentFiles RESET clearRecentFiles NOTIFY recentFilesChanged) Q_PROPERTY(QStringList recentFiles READ recentFiles RESET clearRecentFiles NOTIFY recentFilesChanged)
Q_PROPERTY(bool useNativeFileDialog READ useNativeFileDialog WRITE setUseNativeFileDialog NOTIFY useNativeFileDialogChanged) Q_PROPERTY(bool useNativeFileDialog READ useNativeFileDialog WRITE setUseNativeFileDialog NOTIFY useNativeFileDialogChanged)
Q_PROPERTY(bool supportsNativeFileDialog READ supportsNativeFileDialog NOTIFY supportsNativeFileDialogChanged) Q_PROPERTY(bool supportsNativeFileDialog READ supportsNativeFileDialog NOTIFY supportsNativeFileDialogChanged)
Q_PROPERTY(QString entryFilter READ entryFilter WRITE setEntryFilter NOTIFY entryFilterChanged)
Q_PROPERTY(bool hasEntryFilter READ hasEntryFilter NOTIFY hasEntryFilterChanged)
public: public:
explicit Controller(QSettings &settings, const QString &filePath = QString(), QObject *parent = nullptr); explicit Controller(QSettings &settings, const QString &filePath = QString(), QObject *parent = nullptr);
@ -53,12 +55,12 @@ public:
Io::AccountEntry *currentAccount(); Io::AccountEntry *currentAccount();
void setCurrentAccount(Io::AccountEntry *entry); void setCurrentAccount(Io::AccountEntry *entry);
QModelIndex currentAccountIndex() const; QModelIndex currentAccountIndex() const;
void setCurrentAccountIndex(const QModelIndex &accountIndex); void setCurrentAccountIndex(const QModelIndex &accountIndexMaybeFromFilterModel);
bool hasCurrentAccount() const; bool hasCurrentAccount() const;
const QList<QPersistentModelIndex> &cutEntries() const; const QList<QPersistentModelIndex> &cutEntries() const;
void setCutEntries(const QList<QPersistentModelIndex> &cutEntries); void setCutEntries(const QList<QPersistentModelIndex> &cutEntries);
QString currentAccountName() const; QString currentAccountName() const;
Q_INVOKABLE void cutEntry(const QModelIndex &entryIndex); Q_INVOKABLE void cutEntry(const QModelIndex &entryIndexMaybeFromFilterModel);
Q_INVOKABLE QStringList pasteEntries(const QModelIndex &destinationParent, int row = -1); Q_INVOKABLE QStringList pasteEntries(const QModelIndex &destinationParent, int row = -1);
Q_INVOKABLE bool copyToClipboard(const QString &text) const; Q_INVOKABLE bool copyToClipboard(const QString &text) const;
bool canPaste() const; bool canPaste() const;
@ -67,6 +69,10 @@ public:
bool useNativeFileDialog() const; bool useNativeFileDialog() const;
void setUseNativeFileDialog(bool useNativeFileDialog); void setUseNativeFileDialog(bool useNativeFileDialog);
bool supportsNativeFileDialog() const; bool supportsNativeFileDialog() const;
Q_INVOKABLE QModelIndex filterEntryIndex(const QModelIndex &entryIndex) const;
QString entryFilter() const;
void setEntryFilter(const QString &filter);
bool hasEntryFilter() const;
public slots: public slots:
void init(); void init();
@ -99,6 +105,8 @@ signals:
void useNativeFileDialogChanged(bool useNativeFileDialog); void useNativeFileDialogChanged(bool useNativeFileDialog);
void supportsNativeFileDialogChanged(); void supportsNativeFileDialogChanged();
void entryAboutToBeRemoved(const QModelIndex &removedIndex); void entryAboutToBeRemoved(const QModelIndex &removedIndex);
void entryFilterChanged(const QString &newFilter);
void hasEntryFilterChanged(bool hasEntryFilter);
private slots: private slots:
void handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last); void handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last);
@ -109,6 +117,7 @@ private:
void updateWindowTitle(); void updateWindowTitle();
void setFileOpen(bool fileOpen); void setFileOpen(bool fileOpen);
void emitIoError(const QString &when); void emitIoError(const QString &when);
QModelIndex ensureSourceEntryIndex(const QModelIndex &entryIndexMaybeFromFilterModel) const;
QSettings &m_settings; QSettings &m_settings;
QString m_filePath; QString m_filePath;
@ -127,6 +136,12 @@ private:
bool m_useNativeFileDialog; bool m_useNativeFileDialog;
}; };
inline QModelIndex Controller::ensureSourceEntryIndex(const QModelIndex &entryIndexMaybeFromFilterModel) const
{
return entryIndexMaybeFromFilterModel.model() == &m_entryFilterModel ? m_entryFilterModel.mapToSource(entryIndexMaybeFromFilterModel)
: entryIndexMaybeFromFilterModel;
}
inline const QString &Controller::filePath() const inline const QString &Controller::filePath() const
{ {
return m_filePath; return m_filePath;
@ -193,8 +208,9 @@ inline QModelIndex Controller::currentAccountIndex() const
return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex(); return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex();
} }
inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndex) inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndexMaybeFromFilterModel)
{ {
const auto accountIndex = ensureSourceEntryIndex(accountIndexMaybeFromFilterModel);
m_fieldModel.setAccountEntry(m_entryModel.isNode(accountIndex) ? nullptr : static_cast<Io::AccountEntry *>(m_entryModel.entry(accountIndex))); m_fieldModel.setAccountEntry(m_entryModel.isNode(accountIndex) ? nullptr : static_cast<Io::AccountEntry *>(m_entryModel.entry(accountIndex)));
emit currentAccountChanged(); emit currentAccountChanged();
} }
@ -221,7 +237,7 @@ inline QString Controller::currentAccountName() const
inline void Controller::cutEntry(const QModelIndex &entryIndex) inline void Controller::cutEntry(const QModelIndex &entryIndex)
{ {
cutEntriesChanged(m_cutEntries << QPersistentModelIndex(entryIndex)); cutEntriesChanged(m_cutEntries << QPersistentModelIndex(ensureSourceEntryIndex(entryIndex)));
} }
inline bool Controller::canPaste() const inline bool Controller::canPaste() const
@ -248,6 +264,21 @@ inline bool Controller::supportsNativeFileDialog() const
#endif #endif
} }
inline QModelIndex Controller::filterEntryIndex(const QModelIndex &entryIndex) const
{
return m_entryFilterModel.mapFromSource(entryIndex);
}
inline QString Controller::entryFilter() const
{
return m_entryFilterModel.filterRegExp().pattern();
}
inline bool Controller::hasEntryFilter() const
{
return !m_entryFilterModel.filterRegExp().isEmpty();
}
} // namespace QtGui } // namespace QtGui
#endif // QT_QUICK_GUI_CONTROLLER_H #endif // QT_QUICK_GUI_CONTROLLER_H