tImplement Search - sailfish-safe - Sailfish frontend for safe(1)
 (HTM) git clone git://git.z3bra.org/sailfish-safe.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 6e1bec6bceed73deff7ee3f482ee54c32d549422
 (DIR) parent b4631ca0b35322a6ed2d170491e8c39a227c487a
 (HTM) Author: Daniel Vrátil <dvratil@kde.org>
       Date:   Thu, 14 Feb 2019 21:13:38 +0100
       
       Implement Search
       
       Diffstat:
         M harbour-passilic.pro                |       2 ++
         M qml/components/GlobalPullDownMenu.… |       4 ++++
         A qml/components/PasswordDelegate.qml |     129 +++++++++++++++++++++++++++++++
         M qml/harbour-passilic.qml            |       8 ++++++++
         M qml/pages/PasswordListPage.qml      |     105 ++-----------------------------
         A qml/pages/SearchPage.qml            |      43 ++++++++++++++++++++++++++++++
         M src/passwordfiltermodel.cpp         |       3 ++-
         M translations/harbour-passilic-zh_c… |      15 +++++++++++----
         M translations/harbour-passilic.ts    |      15 +++++++++++----
       
       9 files changed, 214 insertions(+), 110 deletions(-)
       ---
 (DIR) diff --git a/harbour-passilic.pro b/harbour-passilic.pro
       t@@ -47,6 +47,8 @@ DISTFILES += \
            rpm/harbour-passilic.yaml \
            translations/*.ts \
            harbour-passilic.desktop \
       +    qml/pages/SearchPage.qml \
       +    qml/components/PasswordDelegate.qml
        
        OTHER_FILES += \
            README.md
 (DIR) diff --git a/qml/components/GlobalPullDownMenu.qml b/qml/components/GlobalPullDownMenu.qml
       t@@ -24,4 +24,8 @@ PullDownMenu {
                text: qsTr("About")
                onClicked: app.pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"))
            }
       +    MenuItem {
       +        text: qsTr("Search")
       +        onClicked: app.pageStack.push(searchPage)
       +    }
        }
 (DIR) diff --git a/qml/components/PasswordDelegate.qml b/qml/components/PasswordDelegate.qml
       t@@ -0,0 +1,129 @@
       +/*
       + *   Copyright (C) 2019  Daniel Vrátil <dvratil@kde.org>
       + *
       + *   This program is free software: you can redistribute it and/or modify
       + *   it under the terms of the GNU General Public License as published by
       + *   the Free Software Foundation, either version 3 of the License, or
       + *   (at your option) any later version.
       + *
       + *   This program is distributed in the hope that it will be useful,
       + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
       + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       + *   GNU General Public License for more details.
       + *
       + *   You should have received a copy of the GNU General Public License
       + *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
       + */
       +
       +import QtQuick 2.2
       +import QtQml.Models 2.2
       +import Sailfish.Silica 1.0
       +import harbour.passilic 1.0
       +
       +ListItem {
       +    id: listItem
       +
       +    property var modelData
       +    property var password : null
       +
       +    signal folderSelected()
       +
       +    contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge
       +
       +    Row {
       +        anchors {
       +            fill: parent
       +            leftMargin: Theme.horizontalPageMargin
       +            rightMargin: Theme.horizontalPageMargin
       +            verticalCenter: parent.verticalCenter
       +            topMargin: Theme.paddingMedium
       +        }
       +
       +        Column {
       +            spacing: Theme.paddingSmall
       +            width: parent.width
       +
       +            Row {
       +                spacing: Theme.paddingMedium
       +
       +                Image {
       +                    anchors.verticalCenter: parent.verticalCenter
       +                    source:  "image://theme/"
       +                                + ((modelData.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock")
       +                                + "?"
       +                                + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor)
       +                    width: Theme.iconSizeSmall
       +                    height: width
       +                }
       +
       +                Label {
       +                    id: label
       +                    text: modelData.name
       +                }
       +            }
       +
       +            Row {
       +                visible: password !== null
       +                width: parent.width
       +
       +                Label {
       +                    id: errorLabel
       +
       +                    visible: password !== null && password.hasError
       +
       +                    text: password ? password.error : ""
       +                    font.pixelSize: Theme.fontSizeTiny
       +                }
       +
       +                Label {
       +                    id: okLabel
       +
       +                    visible: password !== null && password.valid
       +
       +                    text: qsTr("Password copied to clipboard")
       +                    font.pixelSize: Theme.fontSizeTiny
       +                }
       +            }
       +        }
       +    }
       +
       +    RemorseItem {
       +        id: remorse
       +
       +        cancelText: qsTr("Expire password")
       +
       +        // HACK: override RemorseItem._execute() to act as cancel when the timer expires
       +        function _execute(closeAfterExecute) {
       +            cancel()
       +        }
       +
       +        onCanceled: {
       +            if (listItem.password) {
       +                listItem.password.expirePassword();
       +            }
       +        }
       +    }
       +
       +    onClicked: {
       +        if (modelData.type === PasswordsModel.FolderEntry) {
       +            listItem.folderSelected()
       +        } else {
       +            modelData.password.requestPassword()
       +            var dialog = pageStack.push(Qt.resolvedUrl("../pages/PassphraseRequester.qml"),
       +                                        { "requester": modelData.password })
       +            dialog.done.connect(function() {
       +                listItem.password = modelData.password
       +                listItem.password.validChanged.connect(function() {
       +                    if (listItem.password.valid) {
       +                        remorse.execute(listItem, qsTr("Password will expire"),
       +                                        function() {
       +                                            if (listItem.password) {
       +                                                listItem.password.expirePassword();
       +                                            }
       +                                        }, listItem.password.defaultTimeout);
       +                    }
       +                });
       +            });
       +        }
       +    }
       +}
 (DIR) diff --git a/qml/harbour-passilic.qml b/qml/harbour-passilic.qml
       t@@ -47,6 +47,14 @@ ApplicationWindow
            }
        
            Component {
       +        id: searchPage
       +
       +        SearchPage {
       +            model: filterModel
       +        }
       +    }
       +
       +    Component {
                id: passwordsPage
        
                PasswordListPage {
 (DIR) diff --git a/qml/pages/PasswordListPage.qml b/qml/pages/PasswordListPage.qml
       t@@ -51,108 +51,11 @@ Page {
        
                    rootIndex: passwordListPage.rootIndex
        
       -            delegate: ListItem {
       -                id: listItem
       +            delegate: PasswordDelegate {
       +                modelData: model
        
       -                property var password : null
       -
       -                contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge
       -
       -                Row {
       -                    anchors {
       -                        fill: parent
       -                        leftMargin: Theme.horizontalPageMargin
       -                        rightMargin: Theme.horizontalPageMargin
       -                        verticalCenter: parent.verticalCenter
       -                        topMargin: Theme.paddingMedium
       -                    }
       -
       -                    Column {
       -                        spacing: Theme.paddingSmall
       -                        width: parent.width
       -
       -                        Row {
       -                            spacing: Theme.paddingMedium
       -
       -                            Image {
       -                                anchors.verticalCenter: parent.verticalCenter
       -                                source:  "image://theme/"
       -                                            + ((model.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock")
       -                                            + "?"
       -                                            + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor)
       -                                width: Theme.iconSizeSmall
       -                                height: width
       -                            }
       -
       -                            Label {
       -                                id: label
       -                                text: model.name
       -                            }
       -                        }
       -
       -                        Row {
       -                            visible: password !== null
       -                            width: parent.width
       -
       -                            Label {
       -                                id: errorLabel
       -
       -                                visible: password !== null && password.hasError
       -
       -                                text: password ? password.error : ""
       -                                font.pixelSize: Theme.fontSizeTiny
       -                            }
       -
       -                            Label {
       -                                id: okLabel
       -
       -                                visible: password !== null && password.valid
       -
       -                                text: qsTr("Password copied to clipboard")
       -                                font.pixelSize: Theme.fontSizeTiny
       -                            }
       -                        }
       -                    }
       -                }
       -
       -                RemorseItem {
       -                    id: remorse
       -
       -                    cancelText: qsTr("Expire password")
       -
       -                    // HACK: override RemorseItem._execute() to act as cancel when the timer expires
       -                    function _execute(closeAfterExecute) {
       -                        cancel()
       -                    }
       -
       -                    onCanceled: {
       -                        if (listItem.password) {
       -                            listItem.password.expirePassword();
       -                        }
       -                    }
       -                }
       -
       -                onClicked: {
       -                    if (model.type === PasswordsModel.FolderEntry) {
       -                        passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name);
       -                    } else {
       -                        model.password.requestPassword()
       -                        var dialog = pageStack.push(Qt.resolvedUrl("PassphraseRequester.qml"),
       -                                                    { "requester": model.password })
       -                        dialog.done.connect(function() {
       -                            listItem.password = model.password
       -                            listItem.password.validChanged.connect(function() {
       -                                if (listItem.password.valid) {
       -                                    remorse.execute(listItem, qsTr("Password will expire"),
       -                                                    function() {
       -                                                        if (listItem.password) {
       -                                                            listItem.password.expirePassword();
       -                                                        }
       -                                                    }, listItem.password.defaultTimeout);
       -                                }
       -                            });
       -                        });
       -                    }
       +                onFolderSelected: {
       +                    passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name);
                        }
                    }
                }
 (DIR) diff --git a/qml/pages/SearchPage.qml b/qml/pages/SearchPage.qml
       t@@ -0,0 +1,43 @@
       +/*
       + *   Copyright (C) 2019  Daniel Vrátil <dvratil@kde.org>
       + *
       + *   This program is free software: you can redistribute it and/or modify
       + *   it under the terms of the GNU General Public License as published by
       + *   the Free Software Foundation, either version 3 of the License, or
       + *   (at your option) any later version.
       + *
       + *   This program is distributed in the hope that it will be useful,
       + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
       + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       + *   GNU General Public License for more details.
       + *
       + *   You should have received a copy of the GNU General Public License
       + *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
       + */
       +
       +import QtQuick 2.2
       +import QtQml.Models 2.2
       +import Sailfish.Silica 1.0
       +import "../components"
       +
       +Page {
       +    id: searchPage
       +
       +    property alias model: listView.model
       +
       +    SilicaListView {
       +        id: listView
       +        anchors.fill: parent
       +
       +        header: SearchField {
       +            id: searchField
       +            width: parent.width
       +            focus: true
       +            onTextChanged: model.filter = text
       +        }
       +
       +        delegate: PasswordDelegate {
       +            modelData: model
       +        }
       +    }
       +}
 (DIR) diff --git a/src/passwordfiltermodel.cpp b/src/passwordfiltermodel.cpp
       t@@ -79,7 +79,8 @@ bool PasswordFilterModel::filterAcceptsRow(int source_row, const QModelIndex &so
            }
        
            if (mFilter.isEmpty()) {
       -        return true;
       +        // nothing matches an empty filter
       +        return false;
            }
        
            const auto path = sourceModel()->data(src_index, PasswordsModel::FullNameRole).toString();
 (DIR) diff --git a/translations/harbour-passilic-zh_cn.ts b/translations/harbour-passilic-zh_cn.ts
       t@@ -34,13 +34,13 @@
                <source>About</source>
                <translation type="unfinished"></translation>
            </message>
       -</context>
       -<context>
       -    <name>PasswordListPage</name>
            <message>
       -        <source>Passilic</source>
       +        <source>Search</source>
                <translation type="unfinished"></translation>
            </message>
       +</context>
       +<context>
       +    <name>PasswordDelegate</name>
            <message>
                <source>Password copied to clipboard</source>
                <translation type="unfinished"></translation>
       t@@ -55,6 +55,13 @@
            </message>
        </context>
        <context>
       +    <name>PasswordListPage</name>
       +    <message>
       +        <source>Passilic</source>
       +        <translation type="unfinished"></translation>
       +    </message>
       +</context>
       +<context>
            <name>PasswordProvider</name>
            <message>
                <source>Failed to decrypt password: GPG is not available</source>
 (DIR) diff --git a/translations/harbour-passilic.ts b/translations/harbour-passilic.ts
       t@@ -34,13 +34,13 @@
                <source>About</source>
                <translation type="unfinished"></translation>
            </message>
       -</context>
       -<context>
       -    <name>PasswordListPage</name>
            <message>
       -        <source>Passilic</source>
       +        <source>Search</source>
                <translation type="unfinished"></translation>
            </message>
       +</context>
       +<context>
       +    <name>PasswordDelegate</name>
            <message>
                <source>Password copied to clipboard</source>
                <translation type="unfinished"></translation>
       t@@ -55,6 +55,13 @@
            </message>
        </context>
        <context>
       +    <name>PasswordListPage</name>
       +    <message>
       +        <source>Passilic</source>
       +        <translation type="unfinished"></translation>
       +    </message>
       +</context>
       +<context>
            <name>PasswordProvider</name>
            <message>
                <source>Failed to decrypt password: GPG is not available</source>