2018-05-20 01:54:08 +02:00
|
|
|
import QtQuick 2.7
|
|
|
|
import QtQuick.Controls 2.1 as Controls
|
|
|
|
import QtQuick.Layouts 1.2
|
|
|
|
import QtQuick.Dialogs 1.3
|
|
|
|
import org.kde.kirigami 2.4 as Kirigami
|
2015-04-22 19:30:09 +02:00
|
|
|
|
2018-05-20 01:54:08 +02:00
|
|
|
Kirigami.ApplicationWindow {
|
2015-04-22 19:30:09 +02:00
|
|
|
id: root
|
2018-06-14 23:19:28 +02:00
|
|
|
property alias showPasswordsOnFocus: showPasswordsOnFocusSwitch.checked
|
2018-05-20 01:54:08 +02:00
|
|
|
|
|
|
|
header: Kirigami.ApplicationHeader {
|
2018-09-09 00:57:02 +02:00
|
|
|
backButtonEnabled: false
|
2018-09-08 20:00:43 +02:00
|
|
|
minimumHeight: 0
|
|
|
|
preferredHeight: Kirigami.Units.gridUnit * 2.3
|
|
|
|
maximumHeight: Kirigami.Units.gridUnit * 3
|
|
|
|
|
2018-09-09 00:57:02 +02:00
|
|
|
/*
|
2018-09-08 20:00:43 +02:00
|
|
|
Controls.TextField {
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
placeholderText: qsTr("Filter")
|
|
|
|
width: Kirigami.Units.gridUnit * 8
|
|
|
|
}
|
2018-09-09 00:57:02 +02:00
|
|
|
*/
|
2018-05-20 01:54:08 +02:00
|
|
|
}
|
|
|
|
globalDrawer: Kirigami.GlobalDrawer {
|
|
|
|
id: leftMenu
|
2018-09-10 19:53:14 +02:00
|
|
|
property bool showNoPasswordWarning: nativeInterface.fileOpen
|
|
|
|
&& !nativeInterface.passwordSet
|
|
|
|
|
2018-09-02 19:08:03 +02:00
|
|
|
title: qsTr("Password Manager")
|
2018-09-09 00:57:02 +02:00
|
|
|
titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager-black.svg"
|
2018-09-10 19:57:58 +02:00
|
|
|
visible: true
|
2018-09-10 20:43:28 +02:00
|
|
|
resetMenuOnTriggered: false
|
2018-06-14 23:19:28 +02:00
|
|
|
topContent: ColumnLayout {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
Controls.MenuSeparator {
|
|
|
|
padding: 0
|
|
|
|
topPadding: 8
|
|
|
|
bottomPadding: 0
|
|
|
|
Layout.fillWidth: true
|
|
|
|
}
|
|
|
|
Controls.Label {
|
2018-09-08 20:00:43 +02:00
|
|
|
id: fileNameLabel
|
2018-06-14 23:19:28 +02:00
|
|
|
padding: 8
|
|
|
|
wrapMode: Controls.Label.Wrap
|
|
|
|
fontSizeMode: Text.HorizontalFit
|
|
|
|
minimumPixelSize: 10
|
|
|
|
font.pixelSize: 20
|
|
|
|
Layout.fillWidth: true
|
2018-06-14 23:46:18 +02:00
|
|
|
text: nativeInterface.fileOpen ? nativeInterface.fileName : qsTr(
|
|
|
|
"No file opened")
|
2018-09-08 20:00:43 +02:00
|
|
|
MouseArea {
|
|
|
|
id: fileNameMouseArea
|
|
|
|
anchors.fill: parent
|
|
|
|
hoverEnabled: true
|
|
|
|
}
|
|
|
|
Controls.ToolTip {
|
|
|
|
z: 1000
|
|
|
|
text: nativeInterface.filePath
|
|
|
|
visible: text ? fileNameMouseArea.containsMouse : false
|
|
|
|
delay: Qt.styleHints.mousePressAndHoldInterval
|
|
|
|
onAboutToShow: {
|
|
|
|
x = fileNameMouseArea.mouseX + 10
|
|
|
|
y = fileNameMouseArea.mouseY + 10
|
|
|
|
}
|
|
|
|
}
|
2018-06-14 23:19:28 +02:00
|
|
|
}
|
2018-09-10 19:53:14 +02:00
|
|
|
RowLayout {
|
|
|
|
visible: leftMenu.showNoPasswordWarning
|
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.preferredWidth: 2
|
|
|
|
}
|
|
|
|
Kirigami.Icon {
|
|
|
|
source: "emblem-warning"
|
|
|
|
width: Kirigami.Units.iconSizes.small
|
|
|
|
height: Kirigami.Units.iconSizes.small
|
|
|
|
}
|
|
|
|
Controls.Label {
|
|
|
|
text: qsTr("No password set\nFile will be saved unencrypted!")
|
|
|
|
font.bold: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Item {
|
|
|
|
visible: leftMenu.showNoPasswordWarning
|
|
|
|
height: 4
|
|
|
|
}
|
2018-06-14 23:19:28 +02:00
|
|
|
}
|
2018-05-20 01:54:08 +02:00
|
|
|
actions: [
|
|
|
|
Kirigami.Action {
|
|
|
|
text: qsTr("Create new file")
|
|
|
|
iconName: "document-new"
|
|
|
|
onTriggered: fileDialog.createNew()
|
|
|
|
},
|
|
|
|
Kirigami.Action {
|
|
|
|
text: qsTr("Open existing file")
|
|
|
|
iconName: "document-open"
|
|
|
|
onTriggered: fileDialog.openExisting()
|
|
|
|
},
|
2018-06-16 15:07:46 +02:00
|
|
|
Kirigami.Action {
|
|
|
|
text: qsTr("Recently opened ...")
|
|
|
|
iconName: "document-open-recent"
|
2018-06-16 15:25:57 +02:00
|
|
|
children: createFileActions(nativeInterface.recentFiles)
|
2018-06-16 15:07:46 +02:00
|
|
|
},
|
2018-05-20 01:54:08 +02:00
|
|
|
Kirigami.Action {
|
2018-09-10 19:57:58 +02:00
|
|
|
text: qsTr("Save modifications")
|
2018-05-20 01:54:08 +02:00
|
|
|
enabled: nativeInterface.fileOpen
|
|
|
|
iconName: "document-save"
|
|
|
|
onTriggered: nativeInterface.save()
|
|
|
|
},
|
|
|
|
Kirigami.Action {
|
2018-09-10 19:57:58 +02:00
|
|
|
text: nativeInterface.passwordSet ? qsTr("Change password") : qsTr(
|
|
|
|
"Add password")
|
2018-05-20 01:54:08 +02:00
|
|
|
enabled: nativeInterface.fileOpen
|
2018-09-09 00:57:02 +02:00
|
|
|
iconName: "document-encrypt"
|
2018-05-20 01:54:08 +02:00
|
|
|
onTriggered: enterPasswordDialog.askForNewPassword(
|
|
|
|
"Change password for " + nativeInterface.filePath)
|
|
|
|
},
|
|
|
|
Kirigami.Action {
|
|
|
|
text: "Close file"
|
|
|
|
enabled: nativeInterface.fileOpen
|
|
|
|
iconName: "document-close"
|
|
|
|
onTriggered: nativeInterface.close()
|
2015-04-22 19:30:09 +02:00
|
|
|
}
|
2018-05-20 01:54:08 +02:00
|
|
|
]
|
2018-09-04 00:52:43 +02:00
|
|
|
Controls.Switch {
|
|
|
|
text: qsTr("Use native file dialog")
|
|
|
|
checked: nativeInterface.useNativeFileDialog
|
|
|
|
visible: nativeInterface.supportsNativeFileDialog
|
|
|
|
onCheckedChanged: nativeInterface.useNativeFileDialog = checked
|
|
|
|
}
|
2018-06-14 23:19:28 +02:00
|
|
|
Controls.Switch {
|
|
|
|
id: showPasswordsOnFocusSwitch
|
|
|
|
text: qsTr("Show passwords on focus")
|
|
|
|
checked: true
|
|
|
|
}
|
2015-04-22 19:30:09 +02:00
|
|
|
}
|
2018-06-12 22:02:37 +02:00
|
|
|
contextDrawer: Kirigami.ContextDrawer {
|
|
|
|
id: contextDrawer
|
|
|
|
}
|
2018-06-16 15:15:25 +02:00
|
|
|
Component.onCompleted: nativeInterface.init()
|
2018-06-14 23:46:18 +02:00
|
|
|
|
|
|
|
PasswordDialog {
|
|
|
|
id: enterPasswordDialog
|
2018-09-10 19:57:58 +02:00
|
|
|
onAboutToShow: leftMenu.close()
|
|
|
|
onRejected: {
|
|
|
|
if (!nativeInterface.fileOpen) {
|
|
|
|
leftMenu.open()
|
|
|
|
}
|
|
|
|
}
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FileDialog {
|
|
|
|
id: fileDialog
|
|
|
|
title: selectExisting ? qsTr("Select an existing file") : qsTr(
|
|
|
|
"Select path for new file")
|
|
|
|
onAccepted: {
|
|
|
|
if (fileUrls.length < 1) {
|
|
|
|
return
|
|
|
|
}
|
2018-09-04 00:52:43 +02:00
|
|
|
nativeInterface.handleFileSelectionAccepted(fileUrls[0],
|
|
|
|
this.selectExisting)
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
2018-09-04 00:52:43 +02:00
|
|
|
onRejected: nativeInterface.handleFileSelectionCanceled()
|
2018-06-14 23:46:18 +02:00
|
|
|
|
2018-09-04 00:52:43 +02:00
|
|
|
function show() {
|
|
|
|
if (nativeInterface.showNativeFileDialog(this.selectExisting)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// fallback to the Qt Quick file dialog if a native implementation is not available
|
|
|
|
this.open()
|
|
|
|
}
|
2018-06-14 23:46:18 +02:00
|
|
|
function openExisting() {
|
|
|
|
this.selectExisting = true
|
2018-09-04 00:52:43 +02:00
|
|
|
this.show()
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
|
|
|
function createNew() {
|
|
|
|
this.selectExisting = false
|
2018-09-04 00:52:43 +02:00
|
|
|
this.show()
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: nativeInterface
|
|
|
|
onFileError: {
|
|
|
|
showPassiveNotification(errorMessage)
|
|
|
|
}
|
|
|
|
onPasswordRequired: {
|
|
|
|
enterPasswordDialog.askForExistingPassword(
|
|
|
|
qsTr("Password required to open %1").arg(
|
|
|
|
nativeInterface.filePath))
|
|
|
|
leftMenu.resetMenu()
|
|
|
|
}
|
|
|
|
onFileOpenChanged: {
|
|
|
|
clearStack()
|
|
|
|
if (!nativeInterface.fileOpen) {
|
|
|
|
showPassiveNotification(qsTr("%1 closed").arg(
|
|
|
|
nativeInterface.fileName))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var entryModel = nativeInterface.entryModel
|
|
|
|
var rootIndex = entryModel.index(0, 0)
|
|
|
|
pushStackEntry(entryModel, rootIndex)
|
|
|
|
showPassiveNotification(qsTr("%1 opened").arg(
|
|
|
|
nativeInterface.fileName))
|
2018-09-10 19:57:58 +02:00
|
|
|
leftMenu.close()
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
|
|
|
onFileSaved: {
|
|
|
|
showPassiveNotification(qsTr("%1 saved").arg(
|
|
|
|
nativeInterface.fileName))
|
|
|
|
}
|
2018-09-04 00:52:43 +02:00
|
|
|
onNewNotification: {
|
|
|
|
showPassiveNotification(message)
|
|
|
|
}
|
2018-06-14 23:46:18 +02:00
|
|
|
}
|
|
|
|
|
2018-06-16 15:07:46 +02:00
|
|
|
Component {
|
2018-06-16 15:25:57 +02:00
|
|
|
id: fileActionComponent
|
2018-06-16 15:07:46 +02:00
|
|
|
Kirigami.Action {
|
|
|
|
property string filePath
|
|
|
|
text: filePath.substring(filePath.lastIndexOf('/') + 1)
|
|
|
|
onTriggered: nativeInterface.load(filePath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-16 15:15:25 +02:00
|
|
|
Component {
|
|
|
|
id: entriesComponent
|
|
|
|
EntriesPage {
|
|
|
|
main: root
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-09 01:49:32 +02:00
|
|
|
Component {
|
|
|
|
id: fieldsComponent
|
|
|
|
FieldsPage {
|
|
|
|
main: root
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 23:46:18 +02:00
|
|
|
function clearStack() {
|
|
|
|
pageStack.pop(root.pageStack.initialPage, Controls.StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
|
|
|
function pushStackEntry(entryModel, rootIndex) {
|
|
|
|
pageStack.push(entriesComponent.createObject(root, {
|
2018-09-04 00:52:43 +02:00
|
|
|
"entryModel": entryModel,
|
|
|
|
"rootIndex": rootIndex,
|
|
|
|
"title": entryModel.data(
|
|
|
|
rootIndex)
|
2018-06-14 23:46:18 +02:00
|
|
|
}))
|
|
|
|
}
|
2018-06-16 15:07:46 +02:00
|
|
|
|
2018-09-09 01:49:32 +02:00
|
|
|
function pushAccountEdit() {
|
|
|
|
pageStack.push(fieldsComponent.createObject(root))
|
|
|
|
}
|
|
|
|
|
2018-06-16 15:25:57 +02:00
|
|
|
function createFileActions(files) {
|
|
|
|
return files.map(function (filePath) {
|
|
|
|
return this.createObject(root, {
|
2018-09-04 00:52:43 +02:00
|
|
|
"filePath": filePath
|
2018-06-16 15:25:57 +02:00
|
|
|
})
|
|
|
|
}, fileActionComponent)
|
2018-06-16 15:07:46 +02:00
|
|
|
}
|
2015-04-22 19:30:09 +02:00
|
|
|
}
|