Shared GUI for Bridge and Import/Export
This commit is contained in:
432
internal/frontend/qml/ImportExportUI/AccountDelegate.qml
Normal file
432
internal/frontend/qml/ImportExportUI/AccountDelegate.qml
Normal file
@ -0,0 +1,432 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
// NOTE: Keep the Column so the height and width is inherited from content
|
||||
Column {
|
||||
id: root
|
||||
state: status
|
||||
anchors.left: parent.left
|
||||
|
||||
property real row_width: 50 * Style.px
|
||||
property int row_height: Style.accounts.heightAccount
|
||||
property var listalias : aliases.split(";")
|
||||
property int iAccount: index
|
||||
|
||||
property real spacingLastButtons: (row_width - exportAccount.anchors.leftMargin -Style.main.rightMargin - exportAccount.width - logoutAccount.width - deleteAccount.width)/2
|
||||
|
||||
Accessible.role: go.goos=="windows" ? Accessible.Grouping : Accessible.Row
|
||||
Accessible.name: qsTr("Account %1, status %2", "Accessible text describing account row with arguments: account name and status (connected/disconnected), resp.").arg(account).arg(statusMark.text)
|
||||
Accessible.description: Accessible.name
|
||||
Accessible.ignored: !enabled || !visible
|
||||
|
||||
// Main row
|
||||
Rectangle {
|
||||
id: mainaccRow
|
||||
anchors.left: parent.left
|
||||
width : row_width
|
||||
height : row_height
|
||||
state: { return isExpanded ? "expanded" : "collapsed" }
|
||||
color: Style.main.background
|
||||
|
||||
property string actionName : (
|
||||
isExpanded ?
|
||||
qsTr("Collapse row for account %2", "Accessible text of button showing additional configuration of account") :
|
||||
qsTr("Expand row for account %2", "Accessible text of button hiding additional configuration of account")
|
||||
). arg(account)
|
||||
|
||||
|
||||
// override by other buttons
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
if (root.state=="connected") {
|
||||
mainaccRow.toggle_accountSettings()
|
||||
}
|
||||
}
|
||||
cursorShape : root.state == "connected" ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
if (mainaccRow.state=="collapsed") {
|
||||
mainaccRow.color = Qt.lighter(Style.main.background,1.1)
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (mainaccRow.state=="collapsed") {
|
||||
mainaccRow.color = Style.main.background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// toggle down/up icon
|
||||
Text {
|
||||
id: toggleIcon
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
leftMargin : Style.main.leftMargin
|
||||
}
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : Style.accounts.sizeChevron * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
text: Style.fa.chevron_down
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
if (root.state=="connected") {
|
||||
mainaccRow.toggle_accountSettings()
|
||||
}
|
||||
}
|
||||
cursorShape : root.state == "connected" ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: mainaccRow.actionName
|
||||
Accessible.description: mainaccRow.actionName
|
||||
Accessible.onPressAction : {
|
||||
if (root.state=="connected") {
|
||||
mainaccRow.toggle_accountSettings()
|
||||
}
|
||||
}
|
||||
Accessible.ignored: root.state!="connected" || !root.enabled
|
||||
}
|
||||
}
|
||||
|
||||
// account name
|
||||
TextMetrics {
|
||||
id: accountMetrics
|
||||
font : accountName.font
|
||||
elide: Qt.ElideMiddle
|
||||
elideWidth: (
|
||||
statusMark.anchors.leftMargin
|
||||
- toggleIcon.anchors.leftMargin
|
||||
)
|
||||
text: account
|
||||
}
|
||||
Text {
|
||||
id: accountName
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : toggleIcon.left
|
||||
leftMargin : Style.main.leftMargin
|
||||
}
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : (Style.main.fontSize+2*Style.px) * Style.pt
|
||||
}
|
||||
text: accountMetrics.elidedText
|
||||
}
|
||||
|
||||
// status
|
||||
ClickIconText {
|
||||
id: statusMark
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : parent.left
|
||||
leftMargin : row_width/2
|
||||
}
|
||||
text : qsTr("connected", "status of a listed logged-in account")
|
||||
iconText : Style.fa.circle_o
|
||||
textColor : Style.main.textGreen
|
||||
enabled : false
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
||||
// export
|
||||
ClickIconText {
|
||||
id: exportAccount
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : parent.left
|
||||
leftMargin : 5.5*row_width/8
|
||||
}
|
||||
text : qsTr("Export All", "todo")
|
||||
iconText : Style.fa.floppy_o
|
||||
textBold : true
|
||||
textColor : Style.main.textBlue
|
||||
onClicked: {
|
||||
dialogExport.currentIndex = 0
|
||||
dialogExport.address = account
|
||||
dialogExport.show()
|
||||
}
|
||||
}
|
||||
|
||||
// logout
|
||||
ClickIconText {
|
||||
id: logoutAccount
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : exportAccount.right
|
||||
leftMargin : root.spacingLastButtons
|
||||
}
|
||||
text : qsTr("Log out", "action to log out a connected account")
|
||||
iconText : Style.fa.power_off
|
||||
textBold : true
|
||||
textColor : Style.main.textBlue
|
||||
}
|
||||
|
||||
// remove
|
||||
ClickIconText {
|
||||
id: deleteAccount
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : logoutAccount.right
|
||||
leftMargin : root.spacingLastButtons
|
||||
}
|
||||
text : qsTr("Remove", "deletes an account from the account settings page")
|
||||
iconText : Style.fa.trash_o
|
||||
textColor : Style.main.text
|
||||
onClicked : {
|
||||
dialogGlobal.input=iAccount
|
||||
dialogGlobal.state="deleteUser"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// functions
|
||||
function toggle_accountSettings() {
|
||||
if (root.state=="connected") {
|
||||
if (mainaccRow.state=="collapsed" ) {
|
||||
mainaccRow.state="expanded"
|
||||
} else {
|
||||
mainaccRow.state="collapsed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "collapsed"
|
||||
PropertyChanges { target : toggleIcon ; text : root.state=="connected" ? Style.fa.chevron_down : " " }
|
||||
PropertyChanges { target : accountName ; font.bold : false }
|
||||
PropertyChanges { target : mainaccRow ; color : Style.main.background }
|
||||
PropertyChanges { target : addressList ; visible : false }
|
||||
},
|
||||
State {
|
||||
name: "expanded"
|
||||
PropertyChanges { target : toggleIcon ; text : Style.fa.chevron_up }
|
||||
PropertyChanges { target : accountName ; font.bold : true }
|
||||
PropertyChanges { target : mainaccRow ; color : Style.accounts.backgroundExpanded }
|
||||
PropertyChanges { target : addressList ; visible : true }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// List of adresses
|
||||
Column {
|
||||
id: addressList
|
||||
anchors.left : parent.left
|
||||
width: row_width
|
||||
visible: false
|
||||
property alias model : repeaterAddresses.model
|
||||
|
||||
Repeater {
|
||||
id: repeaterAddresses
|
||||
model: ["one", "two"]
|
||||
|
||||
Rectangle {
|
||||
id: addressRow
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height: Style.accounts.heightAddrRow
|
||||
color: Style.accounts.backgroundExpanded
|
||||
|
||||
// iconText level down
|
||||
Text {
|
||||
id: levelDown
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : Style.accounts.leftMarginAddr
|
||||
verticalCenter : wrapAddr.verticalCenter
|
||||
}
|
||||
text : Style.fa.level_up
|
||||
font.family : Style.fontawesome.name
|
||||
color : Style.main.textDisabled
|
||||
rotation : 90
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: wrapAddr
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : levelDown.right
|
||||
right : parent.right
|
||||
leftMargin : Style.main.leftMargin
|
||||
rightMargin : Style.main.rightMargin
|
||||
}
|
||||
height: Style.accounts.heightAddr
|
||||
border {
|
||||
width : Style.main.border
|
||||
color : Style.main.line
|
||||
}
|
||||
color: Style.accounts.backgroundAddrRow
|
||||
|
||||
TextMetrics {
|
||||
id: addressMetrics
|
||||
font: address.font
|
||||
elideWidth: (
|
||||
wrapAddr.width
|
||||
- address.anchors.leftMargin
|
||||
- 2*exportAlias.width
|
||||
- 3*exportAlias.anchors.rightMargin
|
||||
)
|
||||
elide: Qt.ElideMiddle
|
||||
text: modelData
|
||||
}
|
||||
|
||||
Text {
|
||||
id: address
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: Style.main.leftMargin
|
||||
}
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
color: Style.main.text
|
||||
text: addressMetrics.elidedText
|
||||
}
|
||||
|
||||
// export
|
||||
ClickIconText {
|
||||
id: exportAlias
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: importAlias.left
|
||||
rightMargin: Style.main.rightMargin
|
||||
}
|
||||
text: qsTr("Export", "todo")
|
||||
iconText: Style.fa.floppy_o
|
||||
textBold: true
|
||||
textColor: Style.main.textBlue
|
||||
onClicked: {
|
||||
dialogExport.address = listalias[index]
|
||||
dialogExport.show()
|
||||
}
|
||||
}
|
||||
|
||||
// import
|
||||
ClickIconText {
|
||||
id: importAlias
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
rightMargin: Style.main.rightMargin
|
||||
}
|
||||
text: qsTr("Import", "todo")
|
||||
iconText: Style.fa.upload
|
||||
textBold: true
|
||||
textColor: enabled ? Style.main.textBlue : Style.main.textDisabled
|
||||
onClicked: {
|
||||
dialogImport.address = listalias[index]
|
||||
dialogImport.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// line
|
||||
Rectangle {
|
||||
id: line
|
||||
color: Style.accounts.line
|
||||
height: Style.accounts.heightLine
|
||||
width: root.row_width
|
||||
}
|
||||
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "connected"
|
||||
PropertyChanges {
|
||||
target : addressList
|
||||
model : listalias
|
||||
}
|
||||
PropertyChanges {
|
||||
target : toggleIcon
|
||||
color : Style.main.text
|
||||
}
|
||||
PropertyChanges {
|
||||
target : accountName
|
||||
color : Style.main.text
|
||||
}
|
||||
PropertyChanges {
|
||||
target : statusMark
|
||||
textColor : Style.main.textGreen
|
||||
text : qsTr("connected", "status of a listed logged-in account")
|
||||
iconText : Style.fa.circle
|
||||
}
|
||||
PropertyChanges {
|
||||
target: exportAccount
|
||||
visible: true
|
||||
}
|
||||
PropertyChanges {
|
||||
target : logoutAccount
|
||||
text : qsTr("Log out", "action to log out a connected account")
|
||||
onClicked : {
|
||||
mainaccRow.state="collapsed"
|
||||
dialogGlobal.state = "logout"
|
||||
dialogGlobal.input = root.iAccount
|
||||
dialogGlobal.show()
|
||||
dialogGlobal.confirmed()
|
||||
}
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "disconnected"
|
||||
PropertyChanges {
|
||||
target : addressList
|
||||
model : 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target : toggleIcon
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
PropertyChanges {
|
||||
target : accountName
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
PropertyChanges {
|
||||
target : statusMark
|
||||
textColor : Style.main.textDisabled
|
||||
text : qsTr("disconnected", "status of a listed logged-out account")
|
||||
iconText : Style.fa.circle_o
|
||||
}
|
||||
PropertyChanges {
|
||||
target : logoutAccount
|
||||
text : qsTr("Log in", "action to log in a disconnected account")
|
||||
onClicked : {
|
||||
dialogAddUser.username = root.listalias[0]
|
||||
dialogAddUser.show()
|
||||
dialogAddUser.inputPassword.focusInput = true
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: exportAccount
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
92
internal/frontend/qml/ImportExportUI/Credits.qml
Normal file
92
internal/frontend/qml/ImportExportUI/Credits.qml
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// credits
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: Style.main.width
|
||||
height: root.parent.height - 6*Style.dialog.titleSize
|
||||
color: "transparent"
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
model: [
|
||||
"github.com/0xAX/notificator" ,
|
||||
"github.com/abiosoft/ishell" ,
|
||||
"github.com/allan-simon/go-singleinstance" ,
|
||||
"github.com/andybalholm/cascadia" ,
|
||||
"github.com/bgentry/speakeasy" ,
|
||||
"github.com/boltdb/bolt" ,
|
||||
"github.com/docker/docker-credential-helpers" ,
|
||||
"github.com/emersion/go-imap" ,
|
||||
"github.com/emersion/go-imap-appendlimit" ,
|
||||
"github.com/emersion/go-imap-idle" ,
|
||||
"github.com/emersion/go-imap-move" ,
|
||||
"github.com/emersion/go-imap-quota" ,
|
||||
"github.com/emersion/go-imap-specialuse" ,
|
||||
"github.com/emersion/go-smtp" ,
|
||||
"github.com/emersion/go-textwrapper" ,
|
||||
"github.com/fsnotify/fsnotify" ,
|
||||
"github.com/jaytaylor/html2text" ,
|
||||
"github.com/jhillyerd/go.enmime" ,
|
||||
"github.com/k0kubun/pp" ,
|
||||
"github.com/kardianos/osext" ,
|
||||
"github.com/keybase/go-keychain" ,
|
||||
"github.com/mattn/go-colorable" ,
|
||||
"github.com/pkg/browser" ,
|
||||
"github.com/shibukawa/localsocket" ,
|
||||
"github.com/shibukawa/tobubus" ,
|
||||
"github.com/shirou/gopsutil" ,
|
||||
"github.com/sirupsen/logrus" ,
|
||||
"github.com/skratchdot/open-golang/open" ,
|
||||
"github.com/therecipe/qt" ,
|
||||
"github.com/thomasf/systray" ,
|
||||
"github.com/ugorji/go/codec" ,
|
||||
"github.com/urfave/cli" ,
|
||||
"" ,
|
||||
"Font Awesome 4.7.0",
|
||||
"" ,
|
||||
"The Qt Company - Qt 5.9.1 LGPLv3" ,
|
||||
"" ,
|
||||
]
|
||||
|
||||
delegate: Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: modelData
|
||||
color: Style.main.text
|
||||
}
|
||||
|
||||
footer: ButtonRounded {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Close"
|
||||
onClicked: {
|
||||
root.parent.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
220
internal/frontend/qml/ImportExportUI/DateBox.qml
Normal file
220
internal/frontend/qml/ImportExportUI/DateBox.qml
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for year / month / day
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQml.Models 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
ComboBox {
|
||||
id: root
|
||||
|
||||
property string placeholderText : "none"
|
||||
property var dropDownStyle : Style.dropDownLight
|
||||
property real radius : Style.dialog.radiusButton
|
||||
property bool below : true
|
||||
|
||||
onDownChanged : {
|
||||
root.below = popup.y>0
|
||||
}
|
||||
|
||||
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
|
||||
spacing : Style.dialog.spacing
|
||||
height : Style.dialog.heightInput
|
||||
width : 10*Style.px
|
||||
|
||||
function updateWidth() {
|
||||
// make the width according to localization ( especially for Months)
|
||||
var max = 10*Style.px
|
||||
if (root.model === undefined) return
|
||||
for (var i=-1; i<root.model.length; ++i){
|
||||
metrics.text = i<0 ? root.placeholderText : root.model[i]+"MM" // "M" for extra space
|
||||
max = Math.max(max, metrics.width)
|
||||
}
|
||||
root.width = root.spacing + max + root.spacing + indicatorIcon.width + root.spacing
|
||||
//console.log("width updated", root.placeholderText, root.width)
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
font: root.font
|
||||
text: placeholderText
|
||||
}
|
||||
|
||||
|
||||
indicator: Text {
|
||||
id: indicatorIcon
|
||||
color: root.enabled ? dropDownStyle.highlight : dropDownStyle.inactive
|
||||
text: root.down ? Style.fa.chevron_up : Style.fa.chevron_down
|
||||
font.family: Style.fontawesome.name
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: root.spacing
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
id: boxItem
|
||||
leftPadding: root.spacing
|
||||
rightPadding: root.spacing
|
||||
|
||||
text : enabled && root.currentIndex>=0 ? root.displayText : placeholderText
|
||||
font : root.font
|
||||
color : root.enabled ? dropDownStyle.text : dropDownStyle.inactive
|
||||
verticalAlignment : Text.AlignVCenter
|
||||
elide : Text.ElideRight
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Style.transparent
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.down ? root.popup.close() : root.popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DelegateModel { // FIXME QML DelegateModel: Error creating delegate
|
||||
id: filteredData
|
||||
model: root.model
|
||||
filterOnGroup: "filtered"
|
||||
groups: DelegateModelGroup {
|
||||
id: filtered
|
||||
name: "filtered"
|
||||
includeByDefault: true
|
||||
}
|
||||
delegate: root.delegate
|
||||
}
|
||||
|
||||
function filterItems(minIndex,maxIndex) {
|
||||
// filter
|
||||
var rowCount = filteredData.items.count
|
||||
if (rowCount<=0) return
|
||||
//console.log(" filter", root.placeholderText, rowCount, minIndex, maxIndex)
|
||||
for (var iItem = 0; iItem < rowCount; iItem++) {
|
||||
var entry = filteredData.items.get(iItem);
|
||||
entry.inFiltered = ( iItem >= minIndex && iItem <= maxIndex )
|
||||
//console.log(" inserted ", iItem, rowCount, entry.model.modelData, entry.inFiltered )
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: thisItem
|
||||
width : view.width
|
||||
height : Style.dialog.heightInput
|
||||
leftPadding : root.spacing
|
||||
rightPadding : root.spacing
|
||||
topPadding : 0
|
||||
bottomPadding : 0
|
||||
|
||||
property int index : {
|
||||
//console.log( "index: ", thisItem.DelegateModel.itemsIndex )
|
||||
return thisItem.DelegateModel.itemsIndex
|
||||
}
|
||||
|
||||
onClicked : {
|
||||
//console.log("thisItem click", thisItem.index)
|
||||
root.currentIndex = thisItem.index
|
||||
root.activated(thisItem.index)
|
||||
root.popup.close()
|
||||
}
|
||||
|
||||
|
||||
contentItem: Text {
|
||||
text: modelData
|
||||
color: dropDownStyle.text
|
||||
font: root.font
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: thisItem.hovered ? dropDownStyle.highlight : dropDownStyle.background
|
||||
Text {
|
||||
anchors{
|
||||
right: parent.right
|
||||
rightMargin: root.spacing
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
}
|
||||
text: root.currentIndex == thisItem.index ? Style.fa.check : ""
|
||||
color: thisItem.hovered ? dropDownStyle.text : dropDownStyle.highlight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Style.dialog.borderInput
|
||||
color: dropDownStyle.separator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: root.height
|
||||
x: -background.strokeWidth
|
||||
width: root.width + 2*background.strokeWidth
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
|
||||
topPadding: background.radiusTopLeft + 2*background.strokeWidth
|
||||
bottomPadding: background.radiusBottomLeft + 2*background.strokeWidth
|
||||
leftPadding: 2*background.strokeWidth
|
||||
rightPadding: 2*background.strokeWidth
|
||||
|
||||
contentItem: ListView {
|
||||
id: view
|
||||
clip: true
|
||||
implicitHeight: winMain.height/3
|
||||
model: filteredData // if you want to slide down to position: popup.visible ? root.delegateModel : null
|
||||
currentIndex: root.currentIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
background: RoundedRectangle {
|
||||
radiusTopLeft : root.below ? 0 : root.radius
|
||||
radiusBottomLeft : !root.below ? 0 : root.radius
|
||||
radiusTopRight : radiusTopLeft
|
||||
radiusBottomRight : radiusBottomLeft
|
||||
fillColor : dropDownStyle.background
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//console.log(" box ", label)
|
||||
root.updateWidth()
|
||||
root.filterItems(0,model.length-1)
|
||||
}
|
||||
|
||||
onModelChanged :{
|
||||
//console.log("model changed", root.placeholderText)
|
||||
root.updateWidth()
|
||||
root.filterItems(0,model.length-1)
|
||||
}
|
||||
}
|
||||
|
||||
243
internal/frontend/qml/ImportExportUI/DateInput.qml
Normal file
243
internal/frontend/qml/ImportExportUI/DateInput.qml
Normal file
@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for date
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
width : row.width + (root.label == "" ? 0 : textlabel.width)
|
||||
height : row.height
|
||||
color : Style.transparent
|
||||
|
||||
property alias label : textlabel.text
|
||||
property string metricsLabel : root.label
|
||||
property var dropDownStyle : Style.dropDownLight
|
||||
|
||||
// dates
|
||||
property date currentDate : new Date() // default now
|
||||
property date minDate : new Date(0) // default epoch start
|
||||
property date maxDate : new Date() // default now
|
||||
property int unix : Math.floor(currentDate.getTime()/1000)
|
||||
|
||||
onMinDateChanged: {
|
||||
if (isNaN(minDate.getTime()) || minDate.getTime() > maxDate.getTime()) {
|
||||
minDate = new Date(0)
|
||||
}
|
||||
//console.log(" minDate changed:", root.label, minDate.toDateString())
|
||||
updateRange()
|
||||
}
|
||||
onMaxDateChanged: {
|
||||
if (isNaN(maxDate.getTime()) || minDate.getTime() > maxDate.getTime()) {
|
||||
maxDate = new Date()
|
||||
}
|
||||
//console.log(" maxDate changed:", root.label, maxDate.toDateString())
|
||||
updateRange()
|
||||
}
|
||||
|
||||
RoundedRectangle {
|
||||
id: background
|
||||
anchors.fill : row
|
||||
strokeColor : dropDownStyle.line
|
||||
strokeWidth : Style.dialog.borderInput
|
||||
fillColor : dropDownStyle.background
|
||||
radiusTopLeft : row.children[0].down && !row.children[0].below ? 0 : Style.dialog.radiusButton
|
||||
radiusBottomLeft : row.children[0].down && row.children[0].below ? 0 : Style.dialog.radiusButton
|
||||
radiusTopRight : row.children[row.children.length-1].down && !row.children[row.children.length-1].below ? 0 : Style.dialog.radiusButton
|
||||
radiusBottomRight : row.children[row.children.length-1].down && row.children[row.children.length-1].below ? 0 : Style.dialog.radiusButton
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: textMetrics
|
||||
text: root.metricsLabel+"M"
|
||||
font: textlabel.font
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textlabel
|
||||
anchors {
|
||||
left : root.left
|
||||
verticalCenter : root.verticalCenter
|
||||
}
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: dropDownStyle.labelBold
|
||||
}
|
||||
color: dropDownStyle.text
|
||||
width: textMetrics.width
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
anchors {
|
||||
left : root.label=="" ? root.left : textlabel.right
|
||||
bottom : root.bottom
|
||||
}
|
||||
padding : Style.dialog.borderInput
|
||||
|
||||
DateBox {
|
||||
id: monthInput
|
||||
placeholderText: qsTr("Month")
|
||||
enabled: !allDates
|
||||
model: gui.allMonths
|
||||
onActivated: updateRange()
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
dropDownStyle: root.dropDownStyle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Style.dialog.borderInput
|
||||
height: monthInput.height
|
||||
color: dropDownStyle.line
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DateBox {
|
||||
id: dayInput
|
||||
placeholderText: qsTr("Day")
|
||||
enabled: !allDates
|
||||
model: gui.allDays
|
||||
onActivated: updateRange()
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
dropDownStyle: root.dropDownStyle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Style.dialog.borderInput
|
||||
height: monthInput.height
|
||||
color: dropDownStyle.line
|
||||
}
|
||||
|
||||
DateBox {
|
||||
id: yearInput
|
||||
placeholderText: qsTr("Year")
|
||||
enabled: !allDates
|
||||
model: gui.allYears
|
||||
onActivated: updateRange()
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
dropDownStyle: root.dropDownStyle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setDate(d) {
|
||||
//console.trace()
|
||||
//console.log( " setDate ", label, d)
|
||||
if (isNaN(d = parseInt(d))) return
|
||||
var newUnix = Math.min(maxDate.getTime(), d*1000) // seconds to ms
|
||||
newUnix = Math.max(minDate.getTime(), newUnix)
|
||||
root.updateRange(new Date(newUnix))
|
||||
//console.log( " set ", currentDate.getTime())
|
||||
}
|
||||
|
||||
|
||||
function updateRange(curr) {
|
||||
if (curr === undefined || isNaN(curr.getTime())) curr = root.getCurrentDate()
|
||||
//console.log( " update", label, curr, curr.getTime())
|
||||
//console.trace()
|
||||
if (isNaN(curr.getTime())) return // shouldn't happen
|
||||
// full system date range
|
||||
var firstYear = parseInt(gui.allYears[0])
|
||||
var firstDay = parseInt(gui.allDays[0])
|
||||
if ( isNaN(firstYear) || isNaN(firstDay) ) return
|
||||
// get minimal and maximal available year, month, day
|
||||
// NOTE: The order is important!!!
|
||||
var minYear = minDate.getFullYear()
|
||||
var maxYear = maxDate.getFullYear()
|
||||
var minMonth = (curr.getFullYear() == minYear ? minDate.getMonth() : 0 )
|
||||
var maxMonth = (curr.getFullYear() == maxYear ? maxDate.getMonth() : 11 )
|
||||
var minDay = (
|
||||
curr.getFullYear() == minYear &&
|
||||
curr.getMonth() == minMonth ?
|
||||
minDate.getDate() : firstDay
|
||||
)
|
||||
var maxDay = (
|
||||
curr.getFullYear() == maxYear &&
|
||||
curr.getMonth() == maxMonth ?
|
||||
maxDate.getDate() : gui.daysInMonth(curr.getFullYear(), curr.getMonth()+1)
|
||||
)
|
||||
|
||||
//console.log("update ranges: ", root.label, minYear, maxYear, minMonth+1, maxMonth+1, minDay, maxDay)
|
||||
//console.log("update indexes: ", root.label, firstYear-minYear, firstYear-maxYear, minMonth, maxMonth, minDay-firstDay, maxDay-firstDay)
|
||||
|
||||
|
||||
yearInput.filterItems(firstYear-maxYear, firstYear-minYear)
|
||||
monthInput.filterItems(minMonth,maxMonth) // getMonth() is index not a month (i.e. Jan==0)
|
||||
dayInput.filterItems(minDay-1,maxDay-1)
|
||||
|
||||
// keep ordering from model not from filter
|
||||
yearInput .currentIndex = firstYear - curr.getFullYear()
|
||||
monthInput .currentIndex = curr.getMonth() // getMonth() is index not a month (i.e. Jan==0)
|
||||
dayInput .currentIndex = curr.getDate()-firstDay
|
||||
|
||||
/*
|
||||
console.log(
|
||||
"update current indexes: ", root.label,
|
||||
curr.getFullYear() , '->' , yearInput.currentIndex ,
|
||||
gui.allMonths[curr.getMonth()] , '->' , monthInput.currentIndex ,
|
||||
curr.getDate() , '->' , dayInput.currentIndex
|
||||
)
|
||||
*/
|
||||
|
||||
// test if current date changed
|
||||
if (
|
||||
yearInput.currentText == root.currentDate.getFullYear() &&
|
||||
monthInput.currentText == root.currentDate.toLocaleString(gui.locale, "MMM") &&
|
||||
dayInput.currentText == gui.prependZeros(root.currentDate.getDate(),2)
|
||||
) {
|
||||
//console.log(" currentDate NOT changed", label, root.currentDate.toDateString())
|
||||
return
|
||||
}
|
||||
|
||||
root.currentDate = root.getCurrentDate()
|
||||
// console.log(" currentDate changed", label, root.currentDate.toDateString())
|
||||
}
|
||||
|
||||
// get current date from selected
|
||||
function getCurrentDate() {
|
||||
if (isNaN(root.currentDate.getTime())) { // wrong current ?
|
||||
console.log("!WARNING! Wrong current date format", root.currentDate)
|
||||
root.currentDate = new Date(0)
|
||||
}
|
||||
var currentString = ""
|
||||
var currentUnix = root.currentDate.getTime()
|
||||
if (
|
||||
yearInput.currentText != "" &&
|
||||
yearInput.currentText != yearInput.placeholderText &&
|
||||
monthInput.currentText != "" &&
|
||||
monthInput.currentText != monthInput.placeholderText
|
||||
) {
|
||||
var day = gui.daysInMonth(yearInput.currentText, monthInput.currentText)
|
||||
if (!isNaN(parseInt(dayInput.currentText))) {
|
||||
day = Math.min(day, parseInt(dayInput.currentText))
|
||||
}
|
||||
currentString = [ yearInput.currentText, monthInput.currentText, day].join("-")
|
||||
currentUnix = Date.fromLocaleDateString( locale, currentString, "yyyy-MMM-d").getTime()
|
||||
}
|
||||
return new Date(Math.max(
|
||||
minDate.getTime(),
|
||||
Math.min(maxDate.getTime(), currentUnix)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
121
internal/frontend/qml/ImportExportUI/DateRange.qml
Normal file
121
internal/frontend/qml/ImportExportUI/DateRange.qml
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for date range
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
Column {
|
||||
id: dateRange
|
||||
|
||||
property var structure : structureExternal
|
||||
property string sourceID : structureExternal.getID ( -1 )
|
||||
|
||||
property alias allDates : allDatesBox.checked
|
||||
property alias inputDateFrom : inputDateFrom
|
||||
property alias inputDateTo : inputDateTo
|
||||
|
||||
function setRange() {common.setRange()}
|
||||
function applyRange() {common.applyRange()}
|
||||
|
||||
property var dropDownStyle : Style.dropDownLight
|
||||
property var isDark : dropDownStyle.background == Style.dropDownDark.background
|
||||
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
DateRangeFunctions {id:common}
|
||||
|
||||
DateInput {
|
||||
id: inputDateFrom
|
||||
label: qsTr("From:")
|
||||
currentDate: gui.netBday
|
||||
maxDate: inputDateTo.currentDate
|
||||
dropDownStyle: dateRange.dropDownStyle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: inputDateTo.width
|
||||
height: Style.dialog.borderInput / 2
|
||||
color: isDark ? dropDownStyle.separator : Style.transparent
|
||||
}
|
||||
|
||||
DateInput {
|
||||
id: inputDateTo
|
||||
label: qsTr("To:")
|
||||
metricsLabel: inputDateFrom.label
|
||||
currentDate: new Date() // now
|
||||
minDate: inputDateFrom.currentDate
|
||||
dropDownStyle: dateRange.dropDownStyle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: inputDateTo.width
|
||||
height: Style.dialog.borderInput
|
||||
color: isDark ? dropDownStyle.separator : Style.transparent
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
id: allDatesBox
|
||||
text : qsTr("All dates")
|
||||
anchors.right : inputDateTo.right
|
||||
checkedSymbol : Style.fa.toggle_on
|
||||
uncheckedSymbol : Style.fa.toggle_off
|
||||
uncheckedColor : Style.main.textDisabled
|
||||
textColor : dropDownStyle.text
|
||||
symbolPointSize : Style.dialog.iconSize * Style.pt * 1.1
|
||||
spacing : Style.dialog.spacing*2
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: allDatesBox.checkedSymbol
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: allDatesBox.symbolPointSize
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: allDatesBox.checked ? dotBackground.color : Style.exporting.sliderBackground
|
||||
width: metrics.width*0.9
|
||||
height: metrics.height*0.6
|
||||
radius: height/2
|
||||
z: -1
|
||||
|
||||
anchors {
|
||||
left: allDatesBox.left
|
||||
verticalCenter: allDatesBox.verticalCenter
|
||||
leftMargin: 0.05 * metrics.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dotBackground
|
||||
color : Style.exporting.background
|
||||
height : parent.height
|
||||
width : height
|
||||
radius : height/2
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
internal/frontend/qml/ImportExportUI/DateRangeFunctions.qml
Normal file
81
internal/frontend/qml/ImportExportUI/DateRangeFunctions.qml
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for date range
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
/*
|
||||
NOTE: need to be in obejct with
|
||||
id: dateRange
|
||||
|
||||
property var structure : structureExternal
|
||||
property string sourceID : structureExternal.getID ( -1 )
|
||||
|
||||
property alias allDates : allDatesBox.checked
|
||||
property alias inputDateFrom : inputDateFrom
|
||||
property alias inputDateTo : inputDateTo
|
||||
|
||||
function setRange() {common.setRange()}
|
||||
function applyRange() {common.applyRange()}
|
||||
*/
|
||||
|
||||
function resetRange() {
|
||||
inputDateFrom.setDate(gui.netBday.getTime())
|
||||
inputDateTo.setDate((new Date()).getTime())
|
||||
}
|
||||
|
||||
function setRange(){ // unix time in seconds
|
||||
var folderFrom = dateRange.structure.getFrom(dateRange.sourceID)
|
||||
if (folderFrom===undefined) folderFrom = 0
|
||||
var folderTo = dateRange.structure.getTo(dateRange.sourceID)
|
||||
if (folderTo===undefined) folderTo = 0
|
||||
if ( folderFrom == 0 && folderTo ==0 ) {
|
||||
dateRange.allDates = true
|
||||
} else {
|
||||
dateRange.allDates = false
|
||||
inputDateFrom.setDate(folderFrom)
|
||||
inputDateTo.setDate(folderTo)
|
||||
}
|
||||
}
|
||||
|
||||
function applyRange(){ // unix time is seconds
|
||||
if (dateRange.allDates) structure.setFromToDate(dateRange.sourceID, 0, 0)
|
||||
else {
|
||||
var endOfDay = new Date(inputDateTo.unix*1000)
|
||||
endOfDay.setHours(23,59,59,999)
|
||||
var endOfDayUnix = parseInt(endOfDay.getTime()/1000)
|
||||
structure.setFromToDate(dateRange.sourceID, inputDateFrom.unix, endOfDayUnix)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: dateRange
|
||||
onStructureChanged: setRange()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
inputDateFrom.updateRange(gui.netBday)
|
||||
inputDateTo.updateRange(new Date())
|
||||
setRange()
|
||||
}
|
||||
}
|
||||
|
||||
151
internal/frontend/qml/ImportExportUI/DateRangeMenu.qml
Normal file
151
internal/frontend/qml/ImportExportUI/DateRangeMenu.qml
Normal file
@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of import folder and their target
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id:root
|
||||
|
||||
width : icon.width + indicator.width + 3*padding
|
||||
height : icon.height + 3*padding
|
||||
|
||||
property real padding : Style.dialog.spacing
|
||||
property bool down : popup.visible
|
||||
|
||||
property var structure : structureExternal
|
||||
property string sourceID : structureExternal.getID(-1)
|
||||
|
||||
color: Style.transparent
|
||||
|
||||
RoundedRectangle {
|
||||
anchors.fill: parent
|
||||
radiusTopLeft: root.down ? 0 : Style.dialog.radiusButton
|
||||
fillColor: root.down ? Style.main.textBlue : Style.transparent
|
||||
}
|
||||
|
||||
Text {
|
||||
id: icon
|
||||
text: Style.fa.calendar_o
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : root.padding
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
|
||||
color: root.enabled ? (
|
||||
root.down ? Style.main.background : Style.main.text
|
||||
) : Style.main.textDisabled
|
||||
|
||||
font.family : Style.fontawesome.name
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
verticalCenter: parent.bottom
|
||||
horizontalCenter: parent.right
|
||||
}
|
||||
|
||||
color : !root.down && root.enabled ? Style.main.textRed : icon.color
|
||||
text : Style.fa.exclamation_circle
|
||||
visible : !dateRangeInput.allDates
|
||||
font.pointSize : root.padding * Style.pt * 1.5
|
||||
font.family : Style.fontawesome.name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
id: indicator
|
||||
anchors {
|
||||
right : parent.right
|
||||
rightMargin : root.padding
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
|
||||
text : root.down ? Style.fa.chevron_up : Style.fa.chevron_down
|
||||
color : !root.down && root.enabled ? Style.main.textBlue : icon.color
|
||||
font.family : Style.fontawesome.name
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
onClicked: {
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: popup
|
||||
|
||||
x : -width
|
||||
modal : true
|
||||
clip : true
|
||||
|
||||
topPadding : 0
|
||||
|
||||
background: RoundedRectangle {
|
||||
fillColor : Style.bubble.paneBackground
|
||||
strokeColor : fillColor
|
||||
radiusTopRight: 0
|
||||
|
||||
RoundedRectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: Style.dialog.heightInput
|
||||
fillColor: Style.dropDownDark.highlight
|
||||
strokeColor: fillColor
|
||||
radiusTopRight: 0
|
||||
radiusBottomLeft: 0
|
||||
radiusBottomRight: 0
|
||||
}
|
||||
}
|
||||
|
||||
contentItem : Column {
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
text : qsTr("Import date range")
|
||||
font.bold : Style.dropDownDark.labelBold
|
||||
color : Style.dropDownDark.text
|
||||
height : Style.dialog.heightInput
|
||||
verticalAlignment : Text.AlignVCenter
|
||||
}
|
||||
|
||||
DateRange {
|
||||
id: dateRangeInput
|
||||
allDates: true
|
||||
structure: root.structure
|
||||
sourceID: root.sourceID
|
||||
dropDownStyle: Style.dropDownDark
|
||||
}
|
||||
}
|
||||
|
||||
onAboutToShow : dateRangeInput.setRange()
|
||||
onAboutToHide : dateRangeInput.applyRange()
|
||||
}
|
||||
}
|
||||
457
internal/frontend/qml/ImportExportUI/DialogExport.qml
Normal file
457
internal/frontend/qml/ImportExportUI/DialogExport.qml
Normal file
@ -0,0 +1,457 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Export dialog
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
// TODO
|
||||
// - make ErrorDialog module
|
||||
// - map decision to error code : ask (default), skip ()
|
||||
// - what happens when import fails ? heuristic to find mail where to start from
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
enum Page {
|
||||
LoadingStructure = 0, Options, Progress
|
||||
}
|
||||
|
||||
title : set_title()
|
||||
|
||||
property string address
|
||||
property alias finish: finish
|
||||
|
||||
property string msgClearUnfished: qsTr ("Remove already exported files.")
|
||||
|
||||
isDialogBusy : true // currentIndex == 0 || currentIndex == 3
|
||||
|
||||
signal cancel()
|
||||
signal okay()
|
||||
|
||||
|
||||
Rectangle { // 0
|
||||
id: dialogLoading
|
||||
width: root.width
|
||||
height: root.height
|
||||
color: Style.transparent
|
||||
|
||||
Text {
|
||||
anchors.centerIn : dialogLoading
|
||||
font.pointSize: Style.dialog.titleSize * Style.pt
|
||||
color: Style.dialog.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Loading folders and labels for", "todo") +"\n" + address
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // 1
|
||||
id: dialogInput
|
||||
width: root.width
|
||||
height: root.height
|
||||
color: Style.transparent
|
||||
|
||||
Row {
|
||||
id: inputRow
|
||||
anchors {
|
||||
topMargin : root.titleHeight
|
||||
top : parent.top
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
spacing: 3*Style.main.leftMargin
|
||||
property real columnWidth : (root.width - Style.main.leftMargin - inputRow.spacing - Style.main.rightMargin) / 2
|
||||
property real columnHeight : root.height - root.titleHeight - Style.main.leftMargin
|
||||
|
||||
|
||||
ExportStructure {
|
||||
id: sourceFoldersInput
|
||||
width : inputRow.columnWidth
|
||||
height : inputRow.columnHeight
|
||||
title : qsTr("From: %1", "todo").arg(address)
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: (inputRow.columnHeight - dateRangeInput.height - outputFormatInput.height - outputPathInput.height - buttonRow.height - infotipEncrypted.height) / 4
|
||||
|
||||
DateRange{
|
||||
id: dateRangeInput
|
||||
structure: structurePM
|
||||
sourceID: structurePM.getID(-1)
|
||||
}
|
||||
|
||||
OutputFormat {
|
||||
id: outputFormatInput
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.dialog.spacing
|
||||
CheckBoxLabel {
|
||||
id: exportEncrypted
|
||||
text: qsTr("Export emails that cannot be decrypted as ciphertext")
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Style.dialog.fontSize/1.8
|
||||
}
|
||||
}
|
||||
|
||||
InfoToolTip {
|
||||
id: infotipEncrypted
|
||||
anchors {
|
||||
verticalCenter: exportEncrypted.verticalCenter
|
||||
}
|
||||
info: qsTr("Checking this option will export all emails that cannot be decrypted in ciphertext. If this option is not checked, these emails will not be exported", "todo")
|
||||
}
|
||||
}
|
||||
|
||||
FileAndFolderSelect {
|
||||
id: outputPathInput
|
||||
title: qsTr("Select location of export:", "todo")
|
||||
width : inputRow.columnWidth // stretch folder input
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors.right : parent.right
|
||||
spacing : Style.dialog.rightMargin
|
||||
|
||||
ButtonRounded {
|
||||
id:buttonCancel
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Cancel")
|
||||
color_main: Style.main.textBlue
|
||||
onClicked : root.cancel()
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: buttonNext
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Export","todo")
|
||||
enabled: structurePM != 0
|
||||
color_main: Style.dialog.background
|
||||
color_minor: enabled ? Style.dialog.textBlue : Style.main.textDisabled
|
||||
isOpaque: true
|
||||
onClicked : root.okay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // 2
|
||||
id: progressStatus
|
||||
width: root.width
|
||||
height: root.height
|
||||
color: "transparent"
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
bottom: progressbarExport.top
|
||||
bottomMargin: Style.dialog.heightSeparator
|
||||
left: progressbarExport.left
|
||||
}
|
||||
spacing: Style.main.rightMargin
|
||||
AccessibleText {
|
||||
id: statusLabel
|
||||
text : qsTr("Exporting to:")
|
||||
font.pointSize: Style.main.iconSize * Style.pt
|
||||
color : Style.main.text
|
||||
}
|
||||
AccessibleText {
|
||||
anchors.baseline: statusLabel.baseline
|
||||
text : go.progressDescription == gui.enums.progressInit ? outputPathInput.path : go.progressDescription
|
||||
elide: Text.ElideMiddle
|
||||
width: progressbarExport.width - parent.spacing - statusLabel.width
|
||||
font.pointSize: Style.dialog.textSize * Style.pt
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: progressbarExport
|
||||
implicitWidth : 2*progressStatus.width/3
|
||||
implicitHeight : Style.exporting.rowHeight
|
||||
value: go.progress
|
||||
property int current: go.total * go.progress
|
||||
property bool isFinished: finishedPartBar.width == progressbarExport.width
|
||||
anchors {
|
||||
centerIn: parent
|
||||
}
|
||||
background: Rectangle {
|
||||
radius : Style.exporting.boxRadius
|
||||
color : Style.exporting.progressBackground
|
||||
}
|
||||
contentItem: Item {
|
||||
Rectangle {
|
||||
id: finishedPartBar
|
||||
width : parent.width * progressbarExport.visualPosition
|
||||
height : parent.height
|
||||
radius : Style.exporting.boxRadius
|
||||
gradient : Gradient {
|
||||
GradientStop { position: 0.00; color: Qt.lighter(Style.exporting.progressStatus,1.1) }
|
||||
GradientStop { position: 0.66; color: Style.exporting.progressStatus }
|
||||
GradientStop { position: 1.00; color: Qt.darker(Style.exporting.progressStatus,1.1) }
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation { duration:800; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (progressbarExport.isFinished) return qsTr("Export finished","todo")
|
||||
if (
|
||||
go.progressDescription == gui.enums.progressInit ||
|
||||
(go.progress==0 && go.description=="")
|
||||
) {
|
||||
if (go.total>1) return qsTr("Estimating the total number of messages (%1)","todo").arg(go.total)
|
||||
else return qsTr("Estimating the total number of messages","todo")
|
||||
}
|
||||
var msg = qsTr("Exporting message %1 of %2 (%3%)","todo")
|
||||
if (pauseButton.paused) msg = qsTr("Exporting paused at message %1 of %2 (%3%)","todo")
|
||||
return msg.arg(progressbarExport.current).arg(go.total).arg(Math.floor(go.progress*100))
|
||||
}
|
||||
color: Style.main.background
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
top: progressbarExport.bottom
|
||||
topMargin : Style.dialog.heightSeparator
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
spacing: Style.dialog.rightMargin
|
||||
|
||||
ButtonRounded {
|
||||
id: pauseButton
|
||||
property bool paused : false
|
||||
fa_icon : paused ? Style.fa.play : Style.fa.pause
|
||||
text : paused ? qsTr("Resume") : qsTr("Pause")
|
||||
color_main : Style.dialog.textBlue
|
||||
onClicked : {
|
||||
if (paused) {
|
||||
if (winMain.updateState == gui.enums.statusNoInternet) {
|
||||
go.notifyError(gui.enums.errNoInternet)
|
||||
return
|
||||
}
|
||||
go.resumeProcess()
|
||||
} else {
|
||||
go.pauseProcess()
|
||||
}
|
||||
paused = !paused
|
||||
pauseButton.focus=false
|
||||
}
|
||||
visible : !progressbarExport.isFinished
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
fa_icon : Style.fa.times
|
||||
text : qsTr("Cancel")
|
||||
color_main : Style.dialog.textBlue
|
||||
visible : !progressbarExport.isFinished
|
||||
onClicked : root.ask_cancel_progress()
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: finish
|
||||
fa_icon : Style.fa.check
|
||||
text : qsTr("Okay","todo")
|
||||
color_main : Style.dialog.background
|
||||
color_minor : Style.dialog.textBlue
|
||||
isOpaque : true
|
||||
visible : progressbarExport.isFinished
|
||||
onClicked : root.okay()
|
||||
}
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: buttonHelp
|
||||
anchors {
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
rightMargin : Style.main.rightMargin
|
||||
bottomMargin : Style.main.rightMargin
|
||||
}
|
||||
textColor : Style.main.textDisabled
|
||||
iconText : Style.fa.question_circle
|
||||
text : qsTr("Help", "directs the user to the online user guide")
|
||||
textBold : true
|
||||
onClicked : Qt.openUrlExternally("https://protonmail.com/support/categories/import-export/")
|
||||
}
|
||||
}
|
||||
|
||||
PopupMessage {
|
||||
id: errorPopup
|
||||
width: root.width
|
||||
height: root.height
|
||||
}
|
||||
|
||||
function check_inputs() {
|
||||
if (currentIndex == 1) {
|
||||
// at least one email to export
|
||||
if (structurePM.rowCount() == 0){
|
||||
errorPopup.show(qsTr("No emails found to export. Please try another address.", "todo"))
|
||||
return false
|
||||
}
|
||||
// at least one source selected
|
||||
if (!structurePM.atLeastOneSelected) {
|
||||
errorPopup.show(qsTr("Please select at least one item to export.", "todo"))
|
||||
return false
|
||||
}
|
||||
// check path
|
||||
var folderCheck = go.checkPathStatus(outputPathInput.path)
|
||||
switch (folderCheck) {
|
||||
case gui.enums.pathEmptyPath:
|
||||
errorPopup.show(qsTr("Missing export path. Please select an output folder."))
|
||||
break;
|
||||
case gui.enums.pathWrongPath:
|
||||
errorPopup.show(qsTr("Folder '%1' not found. Please select an output folder.").arg(outputPathInput.path))
|
||||
break;
|
||||
case gui.enums.pathOK | gui.enums.pathNotADir:
|
||||
errorPopup.show(qsTr("File '%1' is not a folder. Please select an output folder.").arg(outputPathInput.path))
|
||||
break;
|
||||
case gui.enums.pathWrongPermissions:
|
||||
errorPopup.show(qsTr("Cannot access folder '%1'. Please check folder permissions.").arg(outputPathInput.path))
|
||||
break;
|
||||
}
|
||||
if (
|
||||
(folderCheck&gui.enums.pathOK)==0 ||
|
||||
(folderCheck&gui.enums.pathNotADir)==gui.enums.pathNotADir
|
||||
) return false
|
||||
if (winMain.updateState == gui.enums.statusNoInternet) {
|
||||
errorPopup.show(qsTr("Please check your internet connection."))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function set_title() {
|
||||
switch(root.currentIndex){
|
||||
case 1 : return qsTr("Select what you'd like to export:")
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
|
||||
function clear_status() {
|
||||
go.progress=0.0
|
||||
go.total=0.0
|
||||
go.progressDescription=gui.enums.progressInit
|
||||
}
|
||||
|
||||
function ask_cancel_progress(){
|
||||
errorPopup.buttonYes.visible = true
|
||||
errorPopup.buttonNo.visible = true
|
||||
errorPopup.buttonOkay.visible = false
|
||||
errorPopup.checkbox.text = root.msgClearUnfished
|
||||
errorPopup.show ("Are you sure you want to cancel this export?")
|
||||
}
|
||||
|
||||
|
||||
onCancel : {
|
||||
switch (root.currentIndex) {
|
||||
case 0 :
|
||||
case 1 : root.hide(); break;
|
||||
case 2 : // progress bar
|
||||
go.cancelProcess (
|
||||
errorPopup.checkbox.text == root.msgClearUnfished &&
|
||||
errorPopup.checkbox.checked
|
||||
);
|
||||
// no break
|
||||
default:
|
||||
root.clear_status()
|
||||
root.currentIndex=1
|
||||
}
|
||||
}
|
||||
|
||||
onOkay : {
|
||||
var isOK = check_inputs()
|
||||
if (!isOK) return
|
||||
timer.interval= currentIndex==1 ? 1 : 300
|
||||
switch (root.currentIndex) {
|
||||
case 2: // progress
|
||||
root.clear_status()
|
||||
root.hide()
|
||||
break
|
||||
case 0: // loading structure
|
||||
dateRangeInput.setRange()
|
||||
//no break
|
||||
default:
|
||||
incrementCurrentIndex()
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
onShow: {
|
||||
if (winMain.updateState==gui.enums.statusNoInternet) {
|
||||
go.checkInternet()
|
||||
if (winMain.updateState==gui.enums.statusNoInternet) {
|
||||
go.notifyError(gui.enums.errNoInternet)
|
||||
root.hide()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
root.clear_status()
|
||||
root.currentIndex=0
|
||||
timer.interval = 300
|
||||
timer.start()
|
||||
dateRangeInput.allDates = true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
onTriggered : {
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
go.loadStructureForExport(root.address)
|
||||
sourceFoldersInput.hasItems = (structurePM.rowCount() > 0)
|
||||
break
|
||||
case 2:
|
||||
dateRangeInput.applyRange()
|
||||
go.startExport(
|
||||
outputPathInput.path,
|
||||
root.address,
|
||||
outputFormatInput.checkedText,
|
||||
exportEncrypted.checked
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: errorPopup
|
||||
|
||||
onClickedOkay : errorPopup.hide()
|
||||
onClickedYes : {
|
||||
root.cancel()
|
||||
errorPopup.hide()
|
||||
}
|
||||
onClickedNo : {
|
||||
go.resumeProcess()
|
||||
errorPopup.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
1027
internal/frontend/qml/ImportExportUI/DialogImport.qml
Normal file
1027
internal/frontend/qml/ImportExportUI/DialogImport.qml
Normal file
File diff suppressed because it is too large
Load Diff
354
internal/frontend/qml/ImportExportUI/DialogYesNo.qml
Normal file
354
internal/frontend/qml/ImportExportUI/DialogYesNo.qml
Normal file
@ -0,0 +1,354 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Dialog with Yes/No buttons
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : ""
|
||||
|
||||
property string input
|
||||
|
||||
property alias question : msg.text
|
||||
property alias note : noteText.text
|
||||
property alias answer : answ.text
|
||||
property alias buttonYes : buttonYes
|
||||
property alias buttonNo : buttonNo
|
||||
|
||||
isDialogBusy: currentIndex==1
|
||||
|
||||
signal confirmed()
|
||||
|
||||
Column {
|
||||
id: dialogMessage
|
||||
property int heightInputs : msg.height+
|
||||
middleSep.height+
|
||||
buttonRow.height +
|
||||
(checkboxSep.visible ? checkboxSep.height : 0 ) +
|
||||
(noteSep.visible ? noteSep.height : 0 ) +
|
||||
(checkBoxWrapper.visible ? checkBoxWrapper.height : 0 ) +
|
||||
(root.note!="" ? noteText.height : 0 )
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-dialogMessage.heightInputs)/2 }
|
||||
|
||||
AccessibleText {
|
||||
id:noteText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: false
|
||||
}
|
||||
width: 2*root.width/3
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
Rectangle { id: noteSep; visible: note!=""; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator}
|
||||
|
||||
AccessibleText {
|
||||
id: msg
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: true
|
||||
}
|
||||
width: 2*parent.width/3
|
||||
text : ""
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Rectangle { id: checkboxSep; visible: checkBoxWrapper.visible; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator}
|
||||
Row {
|
||||
id: checkBoxWrapper
|
||||
property bool isChecked : false
|
||||
visible: root.state=="deleteUser"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
function toggle() {
|
||||
checkBoxWrapper.isChecked = !checkBoxWrapper.isChecked
|
||||
}
|
||||
|
||||
Text {
|
||||
id: checkbox
|
||||
font {
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
text: checkBoxWrapper.isChecked ? Style.fa.check_square_o : Style.fa.square_o
|
||||
color: checkBoxWrapper.isChecked ? Style.main.textBlue : Style.main.text
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: checkBoxWrapper.toggle()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: checkBoxNote
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
text: qsTr("Additionally delete all stored preferences and data", "when removing an account, this extra preference additionally deletes all cached data")
|
||||
color: Style.main.text
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: checkBoxWrapper.toggle()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
Accessible.role: Accessible.CheckBox
|
||||
Accessible.checked: checkBoxWrapper.isChecked
|
||||
Accessible.name: checkBoxNote.text
|
||||
Accessible.description: checkBoxNote.text
|
||||
Accessible.ignored: checkBoxNote.text == ""
|
||||
Accessible.onToggleAction: checkBoxWrapper.toggle()
|
||||
Accessible.onPressAction: checkBoxWrapper.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { id: middleSep; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator }
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.spacing
|
||||
ButtonRounded {
|
||||
id:buttonNo
|
||||
color_main : Style.dialog.textBlue
|
||||
fa_icon : Style.fa.times
|
||||
text : qsTr("No")
|
||||
onClicked : root.hide()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonYes
|
||||
color_main : Style.dialog.background
|
||||
color_minor : Style.dialog.textBlue
|
||||
isOpaque : true
|
||||
fa_icon : Style.fa.check
|
||||
text : qsTr("Yes")
|
||||
onClicked : {
|
||||
currentIndex=1
|
||||
root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-answ.height)/2 }
|
||||
AccessibleText {
|
||||
id: answ
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
width: 3*parent.width/4
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text : qsTr("Waiting...", "in general this displays between screens when processing data takes a long time")
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
states : [
|
||||
State {
|
||||
name: "quit"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
title : qsTr("Close ImportExport", "quits the application")
|
||||
question : qsTr("Are you sure you want to close the ImportExport?", "asked when user tries to quit the application")
|
||||
note : ""
|
||||
answer : qsTr("Closing ImportExport...", "displayed when user is quitting application")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "logout"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
title : qsTr("Logout", "title of page that displays during account logout")
|
||||
question : ""
|
||||
note : ""
|
||||
answer : qsTr("Logging out...", "displays during account logout")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "deleteUser"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
title : qsTr("Delete account", "title of page that displays during account deletion")
|
||||
question : qsTr("Are you sure you want to remove this account?", "displays during account deletion")
|
||||
note : ""
|
||||
answer : qsTr("Deleting ...", "displays during account deletion")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "clearChain"
|
||||
PropertyChanges {
|
||||
target : root
|
||||
currentIndex : 0
|
||||
title : qsTr("Clear keychain", "title of page that displays during keychain clearing")
|
||||
question : qsTr("Are you sure you want to clear your keychain?", "displays during keychain clearing")
|
||||
note : qsTr("This will remove all accounts that you have added to the Import-Export tool.", "displays during keychain clearing")
|
||||
answer : qsTr("Clearing the keychain ...", "displays during keychain clearing")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "clearCache"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
title : qsTr("Clear cache", "title of page that displays during cache clearing")
|
||||
question : qsTr("Are you sure you want to clear your local cache?", "displays during cache clearing")
|
||||
note : qsTr("This will delete all of your stored preferences.", "displays during cache clearing")
|
||||
answer : qsTr("Clearing the cache ...", "displays during cache clearing")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "checkUpdates"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
title : ""
|
||||
question : ""
|
||||
note : ""
|
||||
answer : qsTr("Checking for updates ...", "displays if user clicks the Check for Updates button in the Help tab")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "internetCheck"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
title : ""
|
||||
question : ""
|
||||
note : ""
|
||||
answer : qsTr("Contacting server...", "displays if user clicks the Check for Updates button in the Help tab")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "addressmode"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
title : ""
|
||||
question : qsTr("Do you want to continue?", "asked when the user changes between split and combined address mode")
|
||||
note : qsTr("Changing between split and combined address mode will require you to delete your account(s) from your email client and begin the setup process from scratch.", "displayed when the user changes between split and combined address mode")
|
||||
answer : qsTr("Configuring address mode for ", "displayed when the user changes between split and combined address mode") + root.input
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "toggleAutoStart"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
question : ""
|
||||
note : ""
|
||||
title : ""
|
||||
answer : {
|
||||
var msgTurnOn = qsTr("Turning on automatic start of ImportExport...", "when the automatic start feature is selected")
|
||||
var msgTurnOff = qsTr("Turning off automatic start of ImportExport...", "when the automatic start feature is deselected")
|
||||
return go.isAutoStart==0 ? msgTurnOff : msgTurnOn
|
||||
}
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "undef";
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
question : ""
|
||||
note : ""
|
||||
title : ""
|
||||
answer : ""
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: root.hide()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Enter"
|
||||
onActivated: root.confirmed()
|
||||
}
|
||||
|
||||
onHide: {
|
||||
checkBoxWrapper.isChecked = false
|
||||
state = "undef"
|
||||
}
|
||||
|
||||
onShow: {
|
||||
// hide all other dialogs
|
||||
winMain.dialogAddUser .visible = false
|
||||
winMain.dialogCredits .visible = false
|
||||
//winMain.dialogVersionInfo .visible = false
|
||||
// dialogFirstStart should reappear again after closing global
|
||||
root.visible = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
onConfirmed : {
|
||||
if (state == "quit" || state == "instance exists" ) {
|
||||
timer.interval = 1000
|
||||
} else {
|
||||
timer.interval = 300
|
||||
}
|
||||
answ.forceActiveFocus()
|
||||
timer.start()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
onTriggered: {
|
||||
if ( state == "addressmode" ) { go.switchAddressMode (input) }
|
||||
if ( state == "clearChain" ) { go.clearKeychain () }
|
||||
if ( state == "clearCache" ) { go.clearCache () }
|
||||
if ( state == "deleteUser" ) { go.deleteAccount (input, checkBoxWrapper.isChecked) }
|
||||
if ( state == "logout" ) { go.logoutAccount (input) }
|
||||
if ( state == "toggleAutoStart" ) { go.toggleAutoStart () }
|
||||
if ( state == "quit" ) { Qt.quit () }
|
||||
if ( state == "instance exists" ) { Qt.quit () }
|
||||
if ( state == "checkUpdates" ) { go.runCheckVersion (true) }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Enter) {
|
||||
root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
||||
151
internal/frontend/qml/ImportExportUI/ExportStructure.qml
Normal file
151
internal/frontend/qml/ImportExportUI/ExportStructure.qml
Normal file
@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of export folders / labels
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color : Style.exporting.background
|
||||
radius : Style.exporting.boxRadius
|
||||
border {
|
||||
color : Style.exporting.line
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
property bool hasItems: true
|
||||
|
||||
|
||||
Text { // placeholder
|
||||
visible: !root.hasItems
|
||||
anchors.centerIn: parent
|
||||
color: Style.main.textDisabled
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: qsTr("No emails found for this address.","todo")
|
||||
}
|
||||
|
||||
|
||||
property string title : ""
|
||||
|
||||
TextMetrics {
|
||||
id: titleMetrics
|
||||
text: root.title
|
||||
elide: Qt.ElideMiddle
|
||||
elideWidth: root.width - 4*Style.exporting.leftMargin
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: header
|
||||
anchors {
|
||||
top: root.top
|
||||
left: root.left
|
||||
}
|
||||
width : root.width
|
||||
height : Style.dialog.fontSize*3
|
||||
color : Style.transparent
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
color : Style.exporting.line
|
||||
height : Style.dialog.borderInput
|
||||
width : parent.width
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : 2*Style.exporting.leftMargin
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
color: Style.dialog.text
|
||||
font: titleMetrics.font
|
||||
text: titleMetrics.elidedText
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
clip : true
|
||||
orientation : ListView.Vertical
|
||||
boundsBehavior : Flickable.StopAtBounds
|
||||
model : structurePM
|
||||
cacheBuffer : 10000
|
||||
|
||||
anchors {
|
||||
left : root.left
|
||||
right : root.right
|
||||
bottom : root.bottom
|
||||
top : header.bottom
|
||||
margins : Style.dialog.borderInput
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
/*
|
||||
policy: ScrollBar.AsNeeded
|
||||
background : Rectangle {
|
||||
color : Style.exporting.sliderBackground
|
||||
radius : Style.exporting.boxRadius
|
||||
}
|
||||
contentItem : Rectangle {
|
||||
color : Style.exporting.sliderForeground
|
||||
radius : Style.exporting.boxRadius
|
||||
implicitWidth : Style.main.rightMargin / 3
|
||||
}
|
||||
*/
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Style.main.rightMargin/4
|
||||
}
|
||||
width: Style.main.rightMargin/3
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
||||
delegate: FolderRowButton {
|
||||
width : root.width - 5*root.border.width
|
||||
type : folderType
|
||||
color : folderColor
|
||||
title : folderName
|
||||
isSelected : isFolderSelected
|
||||
onClicked : {
|
||||
//console.log("Clicked", folderId, isSelected)
|
||||
structurePM.setFolderSelection(folderId,!isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
section.property: "folderType"
|
||||
section.delegate: FolderRowButton {
|
||||
isSection : true
|
||||
width : root.width - 5*root.border.width
|
||||
title : gui.folderTypeTitle(section)
|
||||
isSelected : {
|
||||
//console.log("section selected changed: ", section)
|
||||
return section == gui.enums.folderTypeLabel ? structurePM.selectedLabels : structurePM.selectedFolders
|
||||
}
|
||||
onClicked : structurePM.selectType(section,!isSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
55
internal/frontend/qml/ImportExportUI/FilterStructure.qml
Normal file
55
internal/frontend/qml/ImportExportUI/FilterStructure.qml
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Filter only selected folders or labels
|
||||
import QtQuick 2.8
|
||||
import QtQml.Models 2.2
|
||||
|
||||
|
||||
DelegateModel {
|
||||
id: root
|
||||
model : structurePM
|
||||
//filterOnGroup : root.folderType
|
||||
//delegate : root.delegate
|
||||
groups : [
|
||||
DelegateModelGroup {name: gui.enums.folderTypeFolder ; includeByDefault: false},
|
||||
DelegateModelGroup {name: gui.enums.folderTypeLabel ; includeByDefault: false}
|
||||
]
|
||||
|
||||
function updateFilter() {
|
||||
//console.log("FilterModelDelegate::UpdateFilter")
|
||||
// filter
|
||||
var rowCount = root.items.count;
|
||||
for (var iItem = 0; iItem < rowCount; iItem++) {
|
||||
var entry = root.items.get(iItem);
|
||||
entry.inLabel = (
|
||||
root.filterOnGroup == gui.enums.folderTypeLabel &&
|
||||
entry.model.folderType == gui.enums.folderTypeLabel
|
||||
)
|
||||
entry.inFolder = (
|
||||
root.filterOnGroup == gui.enums.folderTypeFolder &&
|
||||
entry.model.folderType != gui.enums.folderTypeLabel
|
||||
)
|
||||
/*
|
||||
if (entry.inFolder && entry.model.folderId == selectedIDs) {
|
||||
view.currentIndex = iItem
|
||||
}
|
||||
*/
|
||||
//console.log("::::update filter:::::", iItem, entry.model.folderName, entry.inFolder, entry.inLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
99
internal/frontend/qml/ImportExportUI/FolderRowButton.qml
Normal file
99
internal/frontend/qml/ImportExportUI/FolderRowButton.qml
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Checkbox row for folder selection
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
AccessibleButton {
|
||||
id: root
|
||||
|
||||
property bool isSection : false
|
||||
property bool isSelected : false
|
||||
property string title : "N/A"
|
||||
property string type : ""
|
||||
property color color : "black"
|
||||
|
||||
height : Style.exporting.rowHeight
|
||||
padding : 0.0
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: isSection ? Style.exporting.background : Style.exporting.rowBackground
|
||||
Rectangle { // line
|
||||
anchors.bottom : parent.bottom
|
||||
height : Style.dialog.borderInput
|
||||
width : parent.width
|
||||
color : Style.exporting.background
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Rectangle {
|
||||
color: "transparent"
|
||||
id: content
|
||||
Text {
|
||||
id: checkbox
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : content.left
|
||||
leftMargin : Style.exporting.leftMargin * (root.type == gui.enums.folderTypeSystem ? 1.0 : 2.0)
|
||||
}
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
color : isSelected ? Style.main.text : Style.main.textInactive
|
||||
text : (isSelected ? Style.fa.check_square_o : Style.fa.square_o )
|
||||
}
|
||||
|
||||
Text { // icon
|
||||
id: folderIcon
|
||||
visible: !isSection
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : checkbox.left
|
||||
leftMargin : Style.dialog.fontSize + Style.exporting.leftMargin
|
||||
}
|
||||
color : root.type==gui.enums.folderTypeSystem ? Style.main.textBlue : root.color
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
text : {
|
||||
return gui.folderIcon(root.title.toLowerCase(), root.type)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.title
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : isSection ? checkbox.left : folderIcon.left
|
||||
leftMargin : Style.dialog.fontSize + Style.exporting.leftMargin
|
||||
}
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold: isSection
|
||||
}
|
||||
color: Style.exporting.text
|
||||
}
|
||||
}
|
||||
}
|
||||
129
internal/frontend/qml/ImportExportUI/HelpView.qml
Normal file
129
internal/frontend/qml/ImportExportUI/HelpView.qml
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List the settings
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// must have wrapper
|
||||
Rectangle {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.main.background
|
||||
|
||||
// content
|
||||
Column {
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
|
||||
|
||||
ButtonIconText {
|
||||
id: manual
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Setup Guide")
|
||||
leftIcon.text : Style.fa.book
|
||||
rightIcon.text : Style.fa.chevron_circle_right
|
||||
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
onClicked: go.openManual()
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: updates
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Check for Updates")
|
||||
leftIcon.text : Style.fa.refresh
|
||||
rightIcon.text : Style.fa.chevron_circle_right
|
||||
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
onClicked: {
|
||||
dialogGlobal.state="checkUpdates"
|
||||
dialogGlobal.show()
|
||||
dialogGlobal.confirmed()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
height: Math.max (
|
||||
aboutText.height +
|
||||
Style.main.fontSize,
|
||||
wrapper.height - (
|
||||
2*manual.height +
|
||||
creditsLink.height +
|
||||
Style.main.fontSize
|
||||
)
|
||||
)
|
||||
width: wrapper.width
|
||||
color : Style.transparent
|
||||
Text {
|
||||
id: aboutText
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
color: Style.main.textDisabled
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
font.family : Style.fontawesome.name
|
||||
text: "ProtonMail Import-Export Version "+go.getBackendVersion()+"\n"+Style.fa.copyright + " 2020 Proton Technologies AG"
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
spacing : Style.main.dummy
|
||||
|
||||
Text {
|
||||
id: creditsLink
|
||||
text : qsTr("Credits", "link to click on to view list of credited libraries")
|
||||
color : Style.main.textDisabled
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
font.underline: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
winMain.dialogCredits.show()
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
id: releaseNotes
|
||||
text : qsTr("Release notes", "link to click on to view release notes for this version of the app")
|
||||
color : Style.main.textDisabled
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
font.underline: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
go.getLocalVersionInfo()
|
||||
winMain.dialogVersionInfo.show()
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
106
internal/frontend/qml/ImportExportUI/IEStyle.qml
Normal file
106
internal/frontend/qml/ImportExportUI/IEStyle.qml
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Adjust Bridge Style
|
||||
|
||||
import QtQuick 2.8
|
||||
import ImportExportUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
Item {
|
||||
Component.onCompleted : {
|
||||
//Style.refdpi = go.goos == "darwin" ? 86.0 : 96.0
|
||||
Style.pt = go.goos == "darwin" ? 93/Style.dpi : 80/Style.dpi
|
||||
|
||||
Style.main.background = "#fff"
|
||||
Style.main.text = "#505061"
|
||||
Style.main.textInactive = "#686876"
|
||||
Style.main.line = "#dddddd"
|
||||
Style.main.width = 884 * Style.px
|
||||
Style.main.height = 422 * Style.px
|
||||
Style.main.leftMargin = 25 * Style.px
|
||||
Style.main.rightMargin = 25 * Style.px
|
||||
|
||||
Style.title.background = Style.main.text
|
||||
Style.title.text = Style.main.background
|
||||
|
||||
Style.tabbar.background = "#3D3A47"
|
||||
Style.tabbar.rightButton = "add account"
|
||||
Style.tabbar.spacingButton = 45*Style.px
|
||||
|
||||
Style.accounts.backgroundExpanded = "#fafafa"
|
||||
Style.accounts.backgroundAddrRow = "#fff"
|
||||
Style.accounts.leftMargin2 = Style.main.width/2
|
||||
Style.accounts.leftMargin3 = 5.5*Style.main.width/8
|
||||
|
||||
|
||||
Style.dialog.background = "#fff"
|
||||
Style.dialog.text = Style.main.text
|
||||
Style.dialog.line = "#e2e2e2"
|
||||
Style.dialog.fontSize = 12 * Style.px
|
||||
Style.dialog.heightInput = 2.2*Style.dialog.fontSize
|
||||
Style.dialog.heightButton = Style.dialog.heightInput
|
||||
Style.dialog.borderInput = 1 * Style.px
|
||||
|
||||
Style.bubble.background = "#595966"
|
||||
Style.bubble.paneBackground = "#454553"
|
||||
Style.bubble.text = "#fff"
|
||||
Style.bubble.width = 310 * Style.px
|
||||
Style.bubble.widthPane = 36 * Style.px
|
||||
Style.bubble.iconSize = 14 * Style.px
|
||||
|
||||
|
||||
// colors:
|
||||
// text: #515061
|
||||
// tick: #686876
|
||||
// blue icon: #9396cc
|
||||
// row bck: #f8f8f9
|
||||
// line: #ddddde or #e2e2e2
|
||||
//
|
||||
// slider bg: #e6e6e6
|
||||
// slider fg: #515061
|
||||
// info icon: #c3c3c8
|
||||
// input border: #ebebeb
|
||||
//
|
||||
// bubble color: #595966
|
||||
// bubble pane: #454553
|
||||
// bubble text: #fff
|
||||
//
|
||||
// indent folder
|
||||
//
|
||||
// Dimensions:
|
||||
// full width: 882px
|
||||
// leftMargin: 25px
|
||||
// rightMargin: 25px
|
||||
// rightMargin: 25px
|
||||
// middleSeparator: 69px
|
||||
// width folders: 416px or (width - separators) /2
|
||||
// width output: 346px or (width - separators) /2
|
||||
//
|
||||
// height from top to input begin: 78px
|
||||
// heightSeparator: 27px
|
||||
// height folder input: 26px
|
||||
//
|
||||
// buble width: 309px
|
||||
// buble left pane icon: 14px
|
||||
// buble left pane width: 36px or (2.5 icon width)
|
||||
// buble height: 46px
|
||||
// buble arrow height: 12px
|
||||
// buble arrow width: 14px
|
||||
// buble radius: 3-4px
|
||||
}
|
||||
}
|
||||
164
internal/frontend/qml/ImportExportUI/ImportDelegate.qml
Normal file
164
internal/frontend/qml/ImportExportUI/ImportDelegate.qml
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of import folder and their target
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: Style.importing.rowBackground
|
||||
height: 40
|
||||
width: 300
|
||||
property real leftMargin1 : folderIcon.x - root.x
|
||||
property real leftMargin2 : selectFolder.x - root.x
|
||||
property real nameWidth : {
|
||||
var available = root.width
|
||||
available -= rowPlacement.children.length * rowPlacement.spacing // spacing between places
|
||||
available -= 3*rowPlacement.leftPadding // left, and 2x right
|
||||
available -= folderIcon.width
|
||||
available -= arrowIcon.width
|
||||
available -= dateRangeMenu.width
|
||||
return available/3.3 // source folder label, target folder menu, target labels menu, and 0.3x label list
|
||||
}
|
||||
property real iconWidth : nameWidth*0.3
|
||||
|
||||
property bool isSourceSelected: targetFolderID!=""
|
||||
property string lastTargetFolder: "6" // Archive
|
||||
property string lastTargetLabels: "" // no flag by default
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: line
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
}
|
||||
height : Style.main.border * 2
|
||||
color : Style.importing.rowLine
|
||||
}
|
||||
|
||||
Row {
|
||||
id: rowPlacement
|
||||
spacing: Style.dialog.spacing
|
||||
leftPadding: Style.dialog.spacing*2
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
|
||||
CheckBoxLabel {
|
||||
id: checkBox
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
checked: root.isSourceSelected
|
||||
|
||||
onClicked: root.toggleImport()
|
||||
}
|
||||
|
||||
Text {
|
||||
id: folderIcon
|
||||
text : gui.folderIcon(folderName, gui.enums.folderTypeFolder)
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
color: root.isSourceSelected ? Style.main.text : Style.main.textDisabled
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text : folderName
|
||||
width: nameWidth
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
color: folderIcon.color
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
|
||||
Text {
|
||||
id: arrowIcon
|
||||
text : Style.fa.arrow_right
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
color: Style.main.text
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
SelectFolderMenu {
|
||||
id: selectFolder
|
||||
sourceID: folderId
|
||||
selectedIDs: targetFolderID
|
||||
width: nameWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
onDoNotImport: root.toggleImport()
|
||||
onImportToFolder: root.importToFolder(newTargetID)
|
||||
}
|
||||
|
||||
SelectLabelsMenu {
|
||||
sourceID: folderId
|
||||
selectedIDs: targetLabelIDs
|
||||
width: nameWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
enabled: root.isSourceSelected
|
||||
}
|
||||
|
||||
LabelIconList {
|
||||
selectedIDs: targetLabelIDs
|
||||
width: iconWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
enabled: root.isSourceSelected
|
||||
}
|
||||
|
||||
DateRangeMenu {
|
||||
id: dateRangeMenu
|
||||
sourceID: folderId
|
||||
|
||||
enabled: root.isSourceSelected
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function importToFolder(newTargetID) {
|
||||
if (root.isSourceSelected) {
|
||||
structureExternal.setTargetFolderID(folderId,newTargetID)
|
||||
} else {
|
||||
lastTargetFolder = newTargetID
|
||||
toggleImport()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleImport() {
|
||||
if (root.isSourceSelected) {
|
||||
lastTargetFolder = targetFolderID
|
||||
lastTargetLabels = targetLabelIDs
|
||||
structureExternal.setTargetFolderID(folderId,"")
|
||||
return Qt.Unchecked
|
||||
} else {
|
||||
structureExternal.setTargetFolderID(folderId,lastTargetFolder)
|
||||
var labelsSplit = lastTargetLabels.split(";")
|
||||
for (var labelIndex in labelsSplit) {
|
||||
var labelID = labelsSplit[labelIndex]
|
||||
structureExternal.addTargetLabelID(folderId,labelID)
|
||||
}
|
||||
return Qt.Checked
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
216
internal/frontend/qml/ImportExportUI/ImportReport.qml
Normal file
216
internal/frontend/qml/ImportExportUI/ImportReport.qml
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Import report modal
|
||||
import QtQuick 2.11
|
||||
import QtQuick.Controls 2.4
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "#aa101021"
|
||||
visible: false
|
||||
|
||||
MouseArea { // disable bellow
|
||||
anchors.fill: root
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:background
|
||||
color: Style.main.background
|
||||
anchors {
|
||||
fill : root
|
||||
topMargin : Style.main.rightMargin
|
||||
leftMargin : 2*Style.main.rightMargin
|
||||
rightMargin : 2*Style.main.rightMargin
|
||||
bottomMargin : 2.5*Style.main.rightMargin
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
anchors {
|
||||
top : parent.top
|
||||
right : parent.right
|
||||
margins : .5* Style.main.rightMargin
|
||||
}
|
||||
iconText : Style.fa.times
|
||||
text : ""
|
||||
textColor : Style.main.textBlue
|
||||
onClicked : root.hide()
|
||||
Accessible.description : qsTr("Close dialog %1", "Click to exit modal.").arg(title.text)
|
||||
}
|
||||
|
||||
Text {
|
||||
id: title
|
||||
text : qsTr("List of errors")
|
||||
font {
|
||||
pointSize: Style.dialog.titleSize * Style.pt
|
||||
}
|
||||
anchors {
|
||||
top : parent.top
|
||||
topMargin : 0.5*Style.main.rightMargin
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: errorView
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : title.bottom
|
||||
bottom : detailBtn.top
|
||||
margins : Style.main.rightMargin
|
||||
}
|
||||
|
||||
clip : true
|
||||
flickableDirection : Flickable.HorizontalAndVerticalFlick
|
||||
contentWidth : errorView.rWall
|
||||
boundsBehavior : Flickable.StopAtBounds
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
anchors {
|
||||
right : parent.right
|
||||
top : parent.top
|
||||
rightMargin : Style.main.rightMargin/4
|
||||
topMargin : Style.main.rightMargin
|
||||
}
|
||||
width: Style.main.rightMargin/3
|
||||
Accessible.ignored: true
|
||||
}
|
||||
ScrollBar.horizontal: ScrollBar {
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
right : parent.right
|
||||
bottomMargin : Style.main.rightMargin/4
|
||||
rightMargin : Style.main.rightMargin
|
||||
}
|
||||
height: Style.main.rightMargin/3
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
||||
|
||||
|
||||
property real rW1 : 150 *Style.px
|
||||
property real rW2 : 150 *Style.px
|
||||
property real rW3 : 100 *Style.px
|
||||
property real rW4 : 150 *Style.px
|
||||
property real rW5 : 550 *Style.px
|
||||
property real rWall : errorView.rW1+errorView.rW2+errorView.rW3+errorView.rW4+errorView.rW5
|
||||
property real pH : .5*Style.main.rightMargin
|
||||
|
||||
model : errorList
|
||||
delegate : Rectangle {
|
||||
width : Math.max(errorView.width, row.width)
|
||||
height : row.height
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
spacing : errorView.pH
|
||||
leftPadding : errorView.pH
|
||||
rightPadding : errorView.pH
|
||||
topPadding : errorView.pH
|
||||
bottomPadding : errorView.pH
|
||||
|
||||
ImportReportCell { width : errorView.rW1; text : mailSubject }
|
||||
ImportReportCell { width : errorView.rW2; text : mailDate }
|
||||
ImportReportCell { width : errorView.rW3; text : inputFolder }
|
||||
ImportReportCell { width : errorView.rW4; text : mailFrom }
|
||||
ImportReportCell { width : errorView.rW5; text : errorMessage }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color : Style.main.line
|
||||
height : .8*Style.px
|
||||
width : parent.width
|
||||
anchors.left : parent.left
|
||||
anchors.bottom : parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
header: Rectangle {
|
||||
height : viewHeader.height
|
||||
width : Math.max(errorView.width, viewHeader.width)
|
||||
color : Style.accounts.backgroundExpanded
|
||||
z : 2
|
||||
|
||||
Row {
|
||||
id: viewHeader
|
||||
|
||||
spacing : errorView.pH
|
||||
leftPadding : errorView.pH
|
||||
rightPadding : errorView.pH
|
||||
topPadding : .5*errorView.pH
|
||||
bottomPadding : .5*errorView.pH
|
||||
|
||||
ImportReportCell { width : errorView.rW1 ; text : qsTr ( "SUBJECT" ); isHeader: true }
|
||||
ImportReportCell { width : errorView.rW2 ; text : qsTr ( "DATE/TIME" ); isHeader: true }
|
||||
ImportReportCell { width : errorView.rW3 ; text : qsTr ( "FOLDER" ); isHeader: true }
|
||||
ImportReportCell { width : errorView.rW4 ; text : qsTr ( "FROM" ); isHeader: true }
|
||||
ImportReportCell { width : errorView.rW5 ; text : qsTr ( "ERROR" ); isHeader: true }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color : Style.main.line
|
||||
height : .8*Style.px
|
||||
width : parent.width
|
||||
anchors.left : parent.left
|
||||
anchors.bottom : parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors{
|
||||
fill : errorView
|
||||
margins : -radius
|
||||
}
|
||||
radius : 2* Style.px
|
||||
color : Style.transparent
|
||||
border {
|
||||
width : Style.px
|
||||
color : Style.main.line
|
||||
}
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: detailBtn
|
||||
fa_icon : Style.fa.file_text
|
||||
text : qsTr("Detailed file")
|
||||
color_main : Style.dialog.textBlue
|
||||
onClicked : go.importLogFileName == "" ? go.openLogs() : go.openReport()
|
||||
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
bottomMargin : 0.5*Style.main.rightMargin
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function show() {
|
||||
root.visible = true
|
||||
}
|
||||
|
||||
function hide() {
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
67
internal/frontend/qml/ImportExportUI/ImportReportCell.qml
Normal file
67
internal/frontend/qml/ImportExportUI/ImportReportCell.qml
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Import report modal
|
||||
import QtQuick 2.11
|
||||
import QtQuick.Controls 2.4
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property alias text : cellText.text
|
||||
property bool isHeader : false
|
||||
property bool isHovered : false
|
||||
property bool isWider : cellText.contentWidth > root.width
|
||||
|
||||
width : 20*Style.px
|
||||
height : cellText.height
|
||||
z : root.isHovered ? 3 : 1
|
||||
color : Style.transparent
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
fill : cellText
|
||||
margins : -2*Style.px
|
||||
}
|
||||
color : root.isWider ? Style.main.background : Style.transparent
|
||||
border {
|
||||
color : root.isWider ? Style.main.textDisabled : Style.transparent
|
||||
width : Style.px
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: cellText
|
||||
color : root.isHeader ? Style.main.textDisabled : Style.main.text
|
||||
elide : root.isHovered ? Text.ElideNone : Text.ElideRight
|
||||
width : root.isHovered ? cellText.contentWidth : root.width
|
||||
font {
|
||||
pointSize : Style.main.textSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill : root
|
||||
hoverEnabled : !root.isHeader
|
||||
onEntered : { root.isHovered = true }
|
||||
onExited : { root.isHovered = false }
|
||||
}
|
||||
}
|
||||
89
internal/frontend/qml/ImportExportUI/ImportSourceButton.qml
Normal file
89
internal/frontend/qml/ImportExportUI/ImportSourceButton.qml
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Export dialog
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
width : 200
|
||||
height : icon.height + 4*tag.height
|
||||
scale : pressed ? 0.95 : 1.0
|
||||
|
||||
property string iconText : Style.fa.ban
|
||||
|
||||
background: Rectangle { color: "transparent" }
|
||||
|
||||
contentItem: Rectangle {
|
||||
id: wrapper
|
||||
color: "transparent"
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
anchors {
|
||||
bottom : wrapper.bottom
|
||||
bottomMargin : tag.height*2.5
|
||||
horizontalCenter : wrapper.horizontalCenter
|
||||
}
|
||||
fillMode : Image.PreserveAspectFit
|
||||
width : Style.main.fontSize * 7
|
||||
mipmap : true
|
||||
source : "images/"+iconText+".png"
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.dialog.spacing
|
||||
anchors {
|
||||
bottom : wrapper.bottom
|
||||
horizontalCenter : wrapper.horizontalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: tag
|
||||
|
||||
text : Style.fa.plus_circle
|
||||
color : Qt.lighter( Style.dialog.textBlue, root.enabled ? 1.0 : 1.5)
|
||||
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.main.fontSize * Style.pt * 1.2
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text : root.text
|
||||
color: tag.color
|
||||
|
||||
font {
|
||||
family : tag.font.family
|
||||
pointSize : tag.font.pointSize
|
||||
weight : Font.DemiBold
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
149
internal/frontend/qml/ImportExportUI/ImportStructure.qml
Normal file
149
internal/frontend/qml/ImportExportUI/ImportStructure.qml
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of import folder and their target
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string titleFrom
|
||||
property string titleTo
|
||||
property bool hasItems: true
|
||||
|
||||
color : Style.transparent
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: root
|
||||
radius : Style.dialog.radiusButton
|
||||
color : Style.transparent
|
||||
border {
|
||||
color : Style.main.line
|
||||
width : 1.5*Style.dialog.borderInput
|
||||
}
|
||||
|
||||
|
||||
Text { // placeholder
|
||||
visible: !root.hasItems
|
||||
anchors.centerIn: parent
|
||||
color: Style.main.textDisabled
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: qsTr("No emails found for this source.","todo")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : parent.top
|
||||
bottom : parent.bottom
|
||||
|
||||
leftMargin : Style.main.leftMargin
|
||||
rightMargin : Style.main.leftMargin
|
||||
topMargin : Style.main.topMargin
|
||||
bottomMargin : Style.main.bottomMargin
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
clip : true
|
||||
orientation : ListView.Vertical
|
||||
boundsBehavior : Flickable.StopAtBounds
|
||||
model : structureExternal
|
||||
cacheBuffer : 10000
|
||||
delegate : ImportDelegate {
|
||||
width: root.width
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: titleBox.bottom
|
||||
bottom: root.bottom
|
||||
left: root.left
|
||||
right: root.right
|
||||
margins : Style.dialog.borderInput
|
||||
bottomMargin: Style.dialog.radiusButton
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Style.main.rightMargin/4
|
||||
}
|
||||
width: Style.main.rightMargin/3
|
||||
Accessible.ignored: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBox
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: Style.main.fontSize *2
|
||||
color : Style.transparent
|
||||
|
||||
Text {
|
||||
id: textTitleFrom
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: {
|
||||
if (listview.currentIndex<0) return 0
|
||||
else return listview.currentItem.leftMargin1
|
||||
}
|
||||
}
|
||||
text: "<b>"+qsTr("From:")+"</b> " + root.titleFrom
|
||||
color: Style.main.text
|
||||
width: listview.currentItem === null ? 0 : (listview.currentItem.leftMargin2 - listview.currentItem.leftMargin1 - Style.dialog.spacing)
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textTitleTo
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: {
|
||||
if (listview.currentIndex<0) return root.width/3
|
||||
else return listview.currentItem.leftMargin2
|
||||
}
|
||||
}
|
||||
text: "<b>"+qsTr("To:")+"</b> " + root.titleTo
|
||||
color: Style.main.text
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: line
|
||||
anchors {
|
||||
left : titleBox.left
|
||||
right : titleBox.right
|
||||
top : titleBox.bottom
|
||||
}
|
||||
height: Style.dialog.borderInput
|
||||
color: Style.main.line
|
||||
}
|
||||
}
|
||||
128
internal/frontend/qml/ImportExportUI/InlineDateRange.qml
Normal file
128
internal/frontend/qml/ImportExportUI/InlineDateRange.qml
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for date range
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: dateRange
|
||||
|
||||
property var structure : structureExternal
|
||||
property string sourceID : structureExternal.getID ( -1 )
|
||||
|
||||
property alias allDates : allDatesBox.checked
|
||||
property alias inputDateFrom : inputDateFrom
|
||||
property alias inputDateTo : inputDateTo
|
||||
|
||||
property alias labelWidth: label.width
|
||||
|
||||
function setRange() {common.setRange()}
|
||||
function applyRange() {common.applyRange()}
|
||||
|
||||
DateRangeFunctions {id:common}
|
||||
|
||||
spacing: Style.dialog.spacing*2
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text : qsTr("Date range")
|
||||
font {
|
||||
bold: true
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
color: Style.main.text
|
||||
}
|
||||
|
||||
DateInput {
|
||||
id: inputDateFrom
|
||||
label: ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
currentDate: new Date(0) // default epoch start
|
||||
maxDate: inputDateTo.currentDate
|
||||
}
|
||||
|
||||
Text {
|
||||
text : Style.fa.arrows_h
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Style.main.text
|
||||
font.family: Style.fontawesome.name
|
||||
}
|
||||
|
||||
DateInput {
|
||||
id: inputDateTo
|
||||
label: ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
currentDate: new Date() // default now
|
||||
minDate: inputDateFrom.currentDate
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
id: allDatesBox
|
||||
text : qsTr("All dates")
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
checkedSymbol : Style.fa.toggle_on
|
||||
uncheckedSymbol : Style.fa.toggle_off
|
||||
uncheckedColor : Style.main.textDisabled
|
||||
symbolPointSize : Style.dialog.iconSize * Style.pt * 1.1
|
||||
spacing : Style.dialog.spacing*2
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: allDatesBox.checkedSymbol
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: allDatesBox.symbolPointSize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
color: allDatesBox.checked ? dotBackground.color : Style.exporting.sliderBackground
|
||||
width: metrics.width*0.9
|
||||
height: metrics.height*0.6
|
||||
radius: height/2
|
||||
z: -1
|
||||
|
||||
anchors {
|
||||
left: allDatesBox.left
|
||||
verticalCenter: allDatesBox.verticalCenter
|
||||
leftMargin: 0.05 * metrics.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dotBackground
|
||||
color : Style.exporting.background
|
||||
height : parent.height
|
||||
width : height
|
||||
radius : height/2
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
227
internal/frontend/qml/ImportExportUI/InlineLabelSelect.qml
Normal file
227
internal/frontend/qml/ImportExportUI/InlineLabelSelect.qml
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// input for date range
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: root
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
property alias labelWidth : label.width
|
||||
|
||||
property string labelName : ""
|
||||
property string labelColor : ""
|
||||
property alias labelSelected : masterLabelCheckbox.checked
|
||||
|
||||
Text {
|
||||
id: label
|
||||
text : qsTr("Add import label")
|
||||
font {
|
||||
bold: true
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
color: Style.main.text
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
InfoToolTip {
|
||||
info: qsTr( "When master import lablel is selected then all imported email will have this label.", "Tooltip text for master import label")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
id: masterLabelCheckbox
|
||||
text : ""
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
checkedSymbol : Style.fa.toggle_on
|
||||
uncheckedSymbol : Style.fa.toggle_off
|
||||
uncheckedColor : Style.main.textDisabled
|
||||
symbolPointSize : Style.dialog.iconSize * Style.pt * 1.1
|
||||
spacing : Style.dialog.spacing*2
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: masterLabelCheckbox.checkedSymbol
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: masterLabelCheckbox.symbolPointSize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
color: parent.checked ? dotBackground.color : Style.exporting.sliderBackground
|
||||
width: metrics.width*0.9
|
||||
height: metrics.height*0.6
|
||||
radius: height/2
|
||||
z: -1
|
||||
|
||||
anchors {
|
||||
left: masterLabelCheckbox.left
|
||||
verticalCenter: masterLabelCheckbox.verticalCenter
|
||||
leftMargin: 0.05 * metrics.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dotBackground
|
||||
color : Style.exporting.background
|
||||
height : parent.height
|
||||
width : height
|
||||
radius : height/2
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// label
|
||||
color : Style.transparent
|
||||
radius : Style.dialog.radiusButton
|
||||
border {
|
||||
color : Style.dialog.line
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
|
||||
scale: area.pressed ? 0.95 : 1
|
||||
|
||||
width: content.width
|
||||
height: content.height
|
||||
|
||||
|
||||
Row {
|
||||
id: content
|
||||
|
||||
spacing : Style.dialog.spacing
|
||||
padding : Style.dialog.spacing
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// label icon color
|
||||
Text {
|
||||
text: Style.fa.tag
|
||||
color: root.labelSelected ? root.labelColor : Style.dialog.line
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id:labelMetrics
|
||||
text: root.labelName
|
||||
elide: Text.ElideRight
|
||||
elideWidth:gui.winMain.width*0.303
|
||||
|
||||
font {
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
family: Style.fontawesome.name
|
||||
}
|
||||
}
|
||||
|
||||
// label text
|
||||
Text {
|
||||
text: labelMetrics.elidedText
|
||||
color: root.labelSelected ? Style.dialog.text : Style.dialog.line
|
||||
font: labelMetrics.font
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// edit icon
|
||||
Text {
|
||||
text: Style.fa.edit
|
||||
color: root.labelSelected ? Style.main.textBlue : Style.dialog.line
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: area
|
||||
anchors.fill: parent
|
||||
enabled: root.labelSelected
|
||||
onClicked : {
|
||||
if (!root.labelSelected) return
|
||||
// NOTE: "createLater" is hack
|
||||
winMain.popupFolderEdit.show(root.labelName, "createLater", root.labelColor, gui.enums.folderTypeLabel, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reset(){
|
||||
labelColor = go.leastUsedColor()
|
||||
labelName = qsTr("Imported", "default name of global label followed by date") + " " + gui.niceDateTime()
|
||||
labelSelected=true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: winMain.popupFolderEdit
|
||||
|
||||
onEdited : {
|
||||
if (newName!="") root.labelName = newName
|
||||
if (newColor!="") root.labelColor = newColor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SelectLabelsMenu {
|
||||
id: labelMenu
|
||||
width : winMain.width/5
|
||||
sourceID : root.sourceID
|
||||
selectedIDs : root.structure.getTargetLabelIDs(root.sourceID)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
LabelIconList {
|
||||
id: iconList
|
||||
selectedIDs : root.structure.getTargetLabelIDs(root.sourceID)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: structureExternal
|
||||
onDataChanged: {
|
||||
iconList.selectedIDs = root.structure.getTargetLabelIDs(root.sourceID)
|
||||
labelMenu.selectedIDs = root.structure.getTargetLabelIDs(root.sourceID)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: structurePM
|
||||
onDataChanged:{
|
||||
iconList.selectedIDs = root.structure.getTargetLabelIDs(root.sourceID)
|
||||
labelMenu.selectedIDs = root.structure.getTargetLabelIDs(root.sourceID)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
96
internal/frontend/qml/ImportExportUI/LabelIconList.qml
Normal file
96
internal/frontend/qml/ImportExportUI/LabelIconList.qml
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of icons for selected folders
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQml.Models 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: Style.main.fontSize * 2
|
||||
height: metrics.height
|
||||
property string selectedIDs : ""
|
||||
color: "transparent"
|
||||
|
||||
|
||||
|
||||
DelegateModel {
|
||||
id: selectedLabels
|
||||
filterOnGroup: "selected"
|
||||
groups: DelegateModelGroup {
|
||||
id: selected
|
||||
name: "selected"
|
||||
includeByDefault: true
|
||||
}
|
||||
model : structurePM
|
||||
delegate : Text {
|
||||
text : metrics.text
|
||||
font : metrics.font
|
||||
color : folderColor===undefined ? "#000": folderColor
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilter() {
|
||||
var selected = root.selectedIDs.split(";")
|
||||
var rowCount = selectedLabels.items.count
|
||||
//console.log(" log ::", root.selectedIDs, rowCount, selectedLabels.model)
|
||||
// filter
|
||||
for (var iItem = 0; iItem < rowCount; iItem++) {
|
||||
var entry = selectedLabels.items.get(iItem);
|
||||
//console.log(" log filter ", iItem, rowCount, entry.model.folderId, entry.model.folderType, selected[iSel], entry.inSelected )
|
||||
for (var iSel in selected) {
|
||||
entry.inSelected = (
|
||||
entry.model.folderType == gui.enums.folderTypeLabel &&
|
||||
entry.model.folderId == selected[iSel]
|
||||
)
|
||||
if (entry.inSelected) break // found match, skip rest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: Style.fa.tag
|
||||
font {
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
family: Style.fontawesome.name
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left : root.left
|
||||
spacing : {
|
||||
var n = Math.max(2,selectedLabels.count)
|
||||
var tagWidth = Math.max(1.0,metrics.width)
|
||||
var space = Math.min(1*Style.px, (root.width - n*tagWidth)/(n-1)) // not more than 1px
|
||||
space = Math.max(space,-tagWidth) // not less than tag width
|
||||
return space
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: selectedLabels
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: root.updateFilter()
|
||||
onSelectedIDsChanged: root.updateFilter()
|
||||
Connections { target: structurePM; onDataChanged:root.updateFilter() }
|
||||
}
|
||||
|
||||
473
internal/frontend/qml/ImportExportUI/MainWindow.qml
Normal file
473
internal/frontend/qml/ImportExportUI/MainWindow.qml
Normal file
@ -0,0 +1,473 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// This is main window
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.3
|
||||
import ImportExportUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
// Main Window
|
||||
Window {
|
||||
id : root
|
||||
property alias tabbar : tabbar
|
||||
property alias viewContent : viewContent
|
||||
property alias viewAccount : viewAccount
|
||||
property alias dialogAddUser : dialogAddUser
|
||||
property alias dialogGlobal : dialogGlobal
|
||||
property alias dialogCredits : dialogCredits
|
||||
property alias dialogVersionInfo : dialogVersionInfo
|
||||
property alias dialogUpdate : dialogUpdate
|
||||
property alias popupMessage : popupMessage
|
||||
property alias popupFolderEdit : popupFolderEdit
|
||||
property alias updateState : infoBar.state
|
||||
property alias dialogExport : dialogExport
|
||||
property alias dialogImport : dialogImport
|
||||
property alias addAccountTip : addAccountTip
|
||||
property int heightContent : height-titleBar.height
|
||||
|
||||
property real innerWindowBorder : go.goos=="darwin" ? 0 : Style.main.border
|
||||
|
||||
// main window appearance
|
||||
width : Style.main.width
|
||||
height : Style.main.height
|
||||
flags : go.goos=="darwin" ? Qt.Window : Qt.Window | Qt.FramelessWindowHint
|
||||
color: go.goos=="windows" ? Style.main.background : Style.transparent
|
||||
title: go.programTitle
|
||||
|
||||
minimumWidth : Style.main.width
|
||||
minimumHeight : Style.main.height
|
||||
|
||||
property bool isOutdateVersion : root.updateState == "forceUpgrade"
|
||||
|
||||
property bool activeContent :
|
||||
!dialogAddUser .visible &&
|
||||
!dialogCredits .visible &&
|
||||
!dialogVersionInfo .visible &&
|
||||
!dialogGlobal .visible &&
|
||||
!dialogUpdate .visible &&
|
||||
!dialogImport .visible &&
|
||||
!dialogExport .visible &&
|
||||
!popupFolderEdit .visible &&
|
||||
!popupMessage .visible
|
||||
|
||||
Accessible.role: Accessible.Grouping
|
||||
Accessible.description: qsTr("Window %1").arg(title)
|
||||
Accessible.name: Accessible.description
|
||||
|
||||
WindowTitleBar {
|
||||
id: titleBar
|
||||
window: root
|
||||
visible: go.goos!="darwin"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top : titleBar.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
}
|
||||
color: Style.title.background
|
||||
}
|
||||
|
||||
InformationBar {
|
||||
id: infoBar
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : titleBar.bottom
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
}
|
||||
|
||||
TabLabels {
|
||||
id: tabbar
|
||||
currentIndex : 0
|
||||
enabled: root.activeContent
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
right : parent.right
|
||||
left : parent.left
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
model: [
|
||||
{ "title" : qsTr("Import/Export" , "title of tab that shows account list" ), "iconText": Style.fa.home },
|
||||
{ "title" : qsTr("Settings" , "title of tab that allows user to change settings" ), "iconText": Style.fa.cogs },
|
||||
{ "title" : qsTr("Help" , "title of tab that shows the help menu" ), "iconText": Style.fa.life_ring }
|
||||
]
|
||||
}
|
||||
|
||||
// Content of tabs
|
||||
StackLayout {
|
||||
id: viewContent
|
||||
enabled: root.activeContent
|
||||
// dimensions
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : tabbar.bottom
|
||||
bottom : parent.bottom
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
bottomMargin: innerWindowBorder
|
||||
}
|
||||
// attributes
|
||||
currentIndex : { return root.tabbar.currentIndex}
|
||||
clip : true
|
||||
// content
|
||||
AccountView {
|
||||
id : viewAccount
|
||||
onAddAccount : dialogAddUser.show()
|
||||
model : accountsModel
|
||||
hasFooter : false
|
||||
delegate : AccountDelegate {
|
||||
row_width : viewContent.width
|
||||
}
|
||||
}
|
||||
SettingsView { id: viewSettings; }
|
||||
HelpView { id: viewHelp; }
|
||||
}
|
||||
|
||||
|
||||
// Bubble prevent action
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: titleBar.bottom
|
||||
bottom: parent.bottom
|
||||
}
|
||||
visible: bubbleNote.visible
|
||||
color: "#aa222222"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
BubbleNote {
|
||||
id : bubbleNote
|
||||
visible : false
|
||||
Component.onCompleted : {
|
||||
bubbleNote.place(0)
|
||||
}
|
||||
}
|
||||
|
||||
BubbleNote {
|
||||
id:addAccountTip
|
||||
anchors.topMargin: viewAccount.separatorNoAccount - 2*Style.main.fontSize
|
||||
text : qsTr("Click here to start", "on first launch, this is displayed above the Add Account button to tell the user what to do first")
|
||||
state: (go.isFirstStart && viewAccount.numAccounts==0 && root.viewContent.currentIndex==0) ? "Visible" : "Invisible"
|
||||
bubbleColor: Style.main.textBlue
|
||||
|
||||
Component.onCompleted : {
|
||||
addAccountTip.place(-1)
|
||||
}
|
||||
enabled: false
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "Visible"
|
||||
// hack: opacity 100% makes buttons dialog windows quit wrong color
|
||||
PropertyChanges{target: addAccountTip; opacity: 0.999; visible: true}
|
||||
},
|
||||
State {
|
||||
name: "Invisible"
|
||||
PropertyChanges{target: addAccountTip; opacity: 0.0; visible: false}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "Visible"
|
||||
to: "Invisible"
|
||||
|
||||
SequentialAnimation{
|
||||
NumberAnimation {
|
||||
target: addAccountTip
|
||||
property: "opacity"
|
||||
duration: 0
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: addAccountTip
|
||||
property: "visible"
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "Invisible"
|
||||
to: "Visible"
|
||||
SequentialAnimation{
|
||||
NumberAnimation {
|
||||
target: addAccountTip
|
||||
property: "visible"
|
||||
duration: 300
|
||||
}
|
||||
NumberAnimation {
|
||||
target: addAccountTip
|
||||
property: "opacity"
|
||||
duration: 500
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Dialogs
|
||||
|
||||
DialogAddUser {
|
||||
id: dialogAddUser
|
||||
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
bottomMargin: innerWindowBorder
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
|
||||
onCreateAccount: Qt.openUrlExternally("https://protonmail.com/signup")
|
||||
}
|
||||
|
||||
DialogUpdate {
|
||||
id: dialogUpdate
|
||||
|
||||
title: root.isOutdateVersion ?
|
||||
qsTr("%1 is outdated", "title of outdate dialog").arg(go.programTitle):
|
||||
qsTr("%1 update to %2", "title of update dialog").arg(go.programTitle).arg(go.newversion)
|
||||
introductionText: {
|
||||
if (root.isOutdateVersion) {
|
||||
if (go.goos=="linux") {
|
||||
return qsTr('You are using an outdated version of our software.<br>
|
||||
Please dowload and install the latest version to continue using %1.<br><br>
|
||||
<a href="%2">%2</a>',
|
||||
"Message for force-update in Linux").arg(go.programTitle).arg(go.landingPage)
|
||||
} else {
|
||||
return qsTr('You are using an outdated version of our software.<br>
|
||||
Please dowload and install the latest version to continue using %1.<br><br>
|
||||
You can continue with update or download and install the new version manually from<br><br>
|
||||
<a href="%2">%2</a>',
|
||||
"Message for force-update in Win/Mac").arg(go.programTitle).arg(go.landingPage)
|
||||
}
|
||||
} else {
|
||||
if (go.goos=="linux") {
|
||||
return qsTr('New version of %1 is available.<br>
|
||||
Check <a href="%2">release notes</a> to learn what is new in %3.<br>
|
||||
Use your package manager to update or download and install new version manually from<br><br>
|
||||
<a href="%4">%4</a>',
|
||||
"Message for update in Linux").arg(go.programTitle).arg("releaseNotes").arg(go.newversion).arg(go.landingPage)
|
||||
} else {
|
||||
return qsTr('New version of %1 is available.<br>
|
||||
Check <a href="%2">release notes</a> to learn what is new in %3.<br>
|
||||
You can continue with update or download and install new version manually from<br><br>
|
||||
<a href="%4">%4</a>',
|
||||
"Message for update in Win/Mac").arg(go.programTitle).arg("releaseNotes").arg(go.newversion).arg(go.landingPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DialogExport {
|
||||
id: dialogExport
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
bottomMargin: innerWindowBorder
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DialogImport {
|
||||
id: dialogImport
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
bottomMargin: innerWindowBorder
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: dialogCredits
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
bottomMargin: innerWindowBorder
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
|
||||
title: qsTr("Credits", "title for list of credited libraries")
|
||||
|
||||
Credits { }
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: dialogVersionInfo
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
bottomMargin: innerWindowBorder
|
||||
leftMargin: innerWindowBorder
|
||||
rightMargin: innerWindowBorder
|
||||
}
|
||||
property bool checkVersion : false
|
||||
title: qsTr("Information about", "title of release notes page") + " v" + go.newversion
|
||||
VersionInfo { }
|
||||
onShow : {
|
||||
// Hide information bar with olde version
|
||||
if ( infoBar.state=="oldVersion" ) {
|
||||
infoBar.state="upToDate"
|
||||
dialogVersionInfo.checkVersion = true
|
||||
}
|
||||
}
|
||||
onHide : {
|
||||
// Reload current version based on online status
|
||||
if (dialogVersionInfo.checkVersion) go.runCheckVersion(false)
|
||||
dialogVersionInfo.checkVersion = false
|
||||
}
|
||||
}
|
||||
|
||||
DialogYesNo {
|
||||
id: dialogGlobal
|
||||
question : ""
|
||||
answer : ""
|
||||
z: 100
|
||||
}
|
||||
|
||||
PopupEditFolder {
|
||||
id: popupFolderEdit
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: infoBar.bottom
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
// Popup
|
||||
PopupMessage {
|
||||
id: popupMessage
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : infoBar.bottom
|
||||
bottom : parent.bottom
|
||||
}
|
||||
|
||||
onClickedNo: popupMessage.hide()
|
||||
onClickedOkay: popupMessage.hide()
|
||||
onClickedYes: {
|
||||
if (popupMessage.message == gui.areYouSureYouWantToQuit) Qt.quit()
|
||||
}
|
||||
}
|
||||
|
||||
// resize
|
||||
MouseArea { // bottom
|
||||
id: resizeBottom
|
||||
property int diff: 0
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
cursorShape: Qt.SizeVerCursor
|
||||
height: Style.main.fontSize
|
||||
onPressed: {
|
||||
var globPos = mapToGlobal(mouse.x, mouse.y)
|
||||
resizeBottom.diff = root.height
|
||||
resizeBottom.diff -= globPos.y
|
||||
}
|
||||
onMouseYChanged : {
|
||||
var globPos = mapToGlobal(mouse.x, mouse.y)
|
||||
root.height = Math.max(root.minimumHeight, globPos.y + resizeBottom.diff)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // right
|
||||
id: resizeRight
|
||||
property int diff: 0
|
||||
anchors {
|
||||
top : titleBar.bottom
|
||||
bottom : parent.bottom
|
||||
right : parent.right
|
||||
}
|
||||
cursorShape: Qt.SizeHorCursor
|
||||
width: Style.main.fontSize/2
|
||||
onPressed: {
|
||||
var globPos = mapToGlobal(mouse.x, mouse.y)
|
||||
resizeRight.diff = root.width
|
||||
resizeRight.diff -= globPos.x
|
||||
}
|
||||
onMouseXChanged : {
|
||||
var globPos = mapToGlobal(mouse.x, mouse.y)
|
||||
root.width = Math.max(root.minimumWidth, globPos.x + resizeRight.diff)
|
||||
}
|
||||
}
|
||||
|
||||
function showAndRise(){
|
||||
go.loadAccounts()
|
||||
root.show()
|
||||
root.raise()
|
||||
if (!root.active) {
|
||||
root.requestActivate()
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle window
|
||||
function toggle() {
|
||||
go.loadAccounts()
|
||||
if (root.visible) {
|
||||
if (!root.active) {
|
||||
root.raise()
|
||||
root.requestActivate()
|
||||
} else {
|
||||
root.hide()
|
||||
}
|
||||
} else {
|
||||
root.show()
|
||||
root.raise()
|
||||
}
|
||||
}
|
||||
|
||||
onClosing : {
|
||||
close.accepted=false
|
||||
if (
|
||||
(dialogImport.visible && dialogImport.currentIndex == 4 && go.progress!=1) ||
|
||||
(dialogExport.visible && dialogExport.currentIndex == 2 && go.progress!=1)
|
||||
) {
|
||||
popupMessage.buttonOkay .visible = false
|
||||
popupMessage.buttonNo .visible = true
|
||||
popupMessage.buttonYes .visible = true
|
||||
popupMessage.show ( gui.areYouSureYouWantToQuit )
|
||||
return
|
||||
}
|
||||
|
||||
close.accepted=true
|
||||
go.processFinished()
|
||||
}
|
||||
}
|
||||
84
internal/frontend/qml/ImportExportUI/OutputFormat.qml
Normal file
84
internal/frontend/qml/ImportExportUI/OutputFormat.qml
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Column {
|
||||
spacing: Style.dialog.spacing
|
||||
property string checkedText : group.checkedButton.text
|
||||
|
||||
Text {
|
||||
id: formatLabel
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: true
|
||||
}
|
||||
color: Style.dialog.text
|
||||
text: qsTr("Select format of exported email:")
|
||||
|
||||
InfoToolTip {
|
||||
info: qsTr("MBOX exports one file for each folder", "todo") + "\n" + qsTr("EML exports one file for each email", "todo")
|
||||
anchors {
|
||||
left: parent.right
|
||||
leftMargin: Style.dialog.spacing
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing : Style.main.leftMargin
|
||||
ButtonGroup {
|
||||
id: group
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [ "MBOX", "EML" ]
|
||||
delegate : RadioButton {
|
||||
id: radioDelegate
|
||||
checked: modelData=="MBOX"
|
||||
width: 5*Style.dialog.fontSize // hack due to bold
|
||||
text: modelData
|
||||
ButtonGroup.group: group
|
||||
spacing: Style.main.spacing
|
||||
indicator: Text {
|
||||
text : radioDelegate.checked ? Style.fa.check_circle : Style.fa.circle_o
|
||||
color : radioDelegate.checked ? Style.main.textBlue : Style.main.textInactive
|
||||
font {
|
||||
pointSize: Style.dialog.iconSize * Style.pt
|
||||
family: Style.fontawesome.name
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
contentItem: Text {
|
||||
text: radioDelegate.text
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: checked
|
||||
}
|
||||
horizontalAlignment : Text.AlignHCenter
|
||||
verticalAlignment : Text.AlignVCenter
|
||||
leftPadding: Style.dialog.iconSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
311
internal/frontend/qml/ImportExportUI/PopupEditFolder.qml
Normal file
311
internal/frontend/qml/ImportExportUI/PopupEditFolder.qml
Normal file
@ -0,0 +1,311 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// popup to edit folders or labels
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import ImportExportUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
visible: false
|
||||
color: "#aa223344"
|
||||
|
||||
property string folderType : gui.enums.folderTypeFolder
|
||||
property bool isFolder : folderType == gui.enums.folderTypeFolder
|
||||
property bool isNew : currentId == ""
|
||||
property bool isCreateLater : currentId == "createLater" // NOTE: "createLater" is hack because folder id should be base64 string
|
||||
|
||||
property string currentName : ""
|
||||
property string currentId : ""
|
||||
property string currentColor : ""
|
||||
|
||||
property string sourceID : ""
|
||||
property string selectedColor : colorList[0]
|
||||
|
||||
property color textColor : Style.main.background
|
||||
property color backColor : Style.bubble.paneBackground
|
||||
|
||||
signal edited(string newName, string newColor)
|
||||
|
||||
|
||||
|
||||
|
||||
property var colorList : [ "#7272a7", "#8989ac", "#cf5858", "#cf7e7e", "#c26cc7", "#c793ca", "#7569d1", "#9b94d1", "#69a9d1", "#a8c4d5", "#5ec7b7", "#97c9c1", "#72bb75", "#9db99f", "#c3d261", "#c6cd97", "#e6c04c", "#e7d292", "#e6984c", "#dfb286" ]
|
||||
|
||||
MouseArea { // prevent action below aka modal: true
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:background
|
||||
|
||||
anchors {
|
||||
fill: root
|
||||
leftMargin: winMain.width/6
|
||||
topMargin: winMain.height/6
|
||||
rightMargin: anchors.leftMargin
|
||||
bottomMargin: anchors.topMargin
|
||||
}
|
||||
|
||||
color: backColor
|
||||
radius: Style.errorDialog.radius
|
||||
}
|
||||
|
||||
|
||||
Column { // content
|
||||
anchors {
|
||||
top : background.top
|
||||
horizontalCenter : background.horizontalCenter
|
||||
}
|
||||
|
||||
topPadding : Style.main.topMargin
|
||||
bottomPadding : topPadding
|
||||
spacing : (background.height - title.height - inputField.height - view.height - buttonRow.height - topPadding - bottomPadding) / children.length
|
||||
|
||||
Text {
|
||||
id: title
|
||||
|
||||
font.pointSize: Style.dialog.titleSize * Style.pt
|
||||
color: textColor
|
||||
|
||||
text: {
|
||||
if ( root.isFolder && root.isNew ) return qsTr ( "Create new folder" )
|
||||
if ( !root.isFolder && root.isNew ) return qsTr ( "Create new label" )
|
||||
if ( root.isFolder && !root.isNew ) return qsTr ( "Edit folder %1" ) .arg( root.currentName )
|
||||
if ( !root.isFolder && !root.isNew ) return qsTr ( "Edit label %1" ) .arg( root.currentName )
|
||||
}
|
||||
|
||||
width : parent.width
|
||||
elide : Text.ElideRight
|
||||
|
||||
horizontalAlignment : Text.AlignHCenter
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.bottom
|
||||
topMargin: Style.dialog.spacing
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
color: textColor
|
||||
height: Style.main.borderInput
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
width : parent.width
|
||||
height : Style.dialog.button
|
||||
rightPadding : Style.dialog.spacing
|
||||
leftPadding : height + rightPadding
|
||||
bottomPadding : rightPadding
|
||||
topPadding : rightPadding
|
||||
selectByMouse : true
|
||||
color : textColor
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
|
||||
background: Rectangle {
|
||||
color: backColor
|
||||
border {
|
||||
color: textColor
|
||||
width: Style.dialog.borderInput
|
||||
}
|
||||
|
||||
radius : Style.dialog.radiusButton
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.dialog.titleSize * Style.pt
|
||||
}
|
||||
|
||||
text : folderType == gui.enums.folderTypeFolder ? Style.fa.folder : Style.fa.tag
|
||||
color : root.selectedColor
|
||||
width : parent.height
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: parent.height
|
||||
}
|
||||
width: parent.border.width/2
|
||||
height: parent.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GridView {
|
||||
id: view
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
model : colorList
|
||||
cellWidth : 2*Style.dialog.titleSize
|
||||
cellHeight : cellWidth
|
||||
width : 10*cellWidth
|
||||
height : 2*cellHeight
|
||||
|
||||
delegate: Rectangle {
|
||||
width: view.cellWidth*0.8
|
||||
height: width
|
||||
radius: width/2
|
||||
color: modelData
|
||||
|
||||
border {
|
||||
color: indicator.visible ? textColor : modelData
|
||||
width: 2*Style.px
|
||||
}
|
||||
|
||||
Text {
|
||||
id: indicator
|
||||
anchors.centerIn : parent
|
||||
text: Style.fa.check
|
||||
color: textColor
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize: Style.dialog.titleSize * Style.pt
|
||||
}
|
||||
visible: modelData == root.selectedColor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
root.selectedColor = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
spacing: Style.main.leftMargin
|
||||
|
||||
ButtonRounded {
|
||||
text: "Cancel"
|
||||
color_main : textColor
|
||||
onClicked :{
|
||||
root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
text: "Okay"
|
||||
color_main: Style.dialog.background
|
||||
color_minor: Style.dialog.textBlue
|
||||
isOpaque: true
|
||||
onClicked :{
|
||||
root.okay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
root.visible=false
|
||||
root.currentId = ""
|
||||
root.currentName = ""
|
||||
root.currentColor = ""
|
||||
root.folderType = ""
|
||||
root.sourceID = ""
|
||||
inputField.text = ""
|
||||
}
|
||||
|
||||
function show(currentName, currentId, currentColor, folderType, sourceID) {
|
||||
root.currentId = currentId
|
||||
root.currentName = currentName
|
||||
root.currentColor = currentColor=="" ? go.leastUsedColor() : currentColor
|
||||
root.selectedColor = root.currentColor
|
||||
root.folderType = folderType
|
||||
root.sourceID = sourceID
|
||||
|
||||
inputField.text = currentName
|
||||
root.visible=true
|
||||
//console.log(title.text , root.currentName, root.currentId, root.currentColor, root.folderType, root.sourceID)
|
||||
}
|
||||
|
||||
function okay() {
|
||||
// check inpupts
|
||||
if (inputField.text == "") {
|
||||
go.notifyError(gui.enums.errFillFolderName)
|
||||
return
|
||||
}
|
||||
if (colorList.indexOf(root.selectedColor)<0) {
|
||||
go.notifyError(gui.enums.errSelectFolderColor)
|
||||
return
|
||||
}
|
||||
var isLabel = root.folderType == gui.enums.folderTypeLabel
|
||||
if (!isLabel && !root.isFolder){
|
||||
console.log("Unknown folder type: ", root.folderType)
|
||||
go.notifyError(gui.enums.errUpdateLabelFailed)
|
||||
root.hide()
|
||||
return
|
||||
}
|
||||
|
||||
if (winMain.dialogImport.address == "") {
|
||||
console.log("Unknown address", winMain.dialogImport.address)
|
||||
go.onNotifyError(gui.enums.errUpdateLabelFailed)
|
||||
root.hide()
|
||||
}
|
||||
|
||||
if (root.isCreateLater) {
|
||||
root.edited(inputField.text, root.selectedColor)
|
||||
root.hide()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// TODO send request (as timer)
|
||||
if (root.isNew) {
|
||||
var isOK = go.createLabelOrFolder(winMain.dialogImport.address, inputField.text, root.selectedColor, isLabel, root.sourceID)
|
||||
if (isOK) {
|
||||
root.hide()
|
||||
}
|
||||
} else {
|
||||
// TODO: check there was some change
|
||||
go.updateLabelOrFolder(winMain.dialogImport.address, root.currentId, inputField.text, root.selectedColor)
|
||||
}
|
||||
|
||||
// waiting for finish
|
||||
// TODO: waiting wheel of doom
|
||||
// TODO: on close add source to sourceID
|
||||
}
|
||||
}
|
||||
362
internal/frontend/qml/ImportExportUI/SelectFolderMenu.qml
Normal file
362
internal/frontend/qml/ImportExportUI/SelectFolderMenu.qml
Normal file
@ -0,0 +1,362 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// This is global combo box which can be adjusted to choose folder target, folder label or global label
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
ComboBox {
|
||||
id: root
|
||||
//fixme rounded
|
||||
height: Style.main.fontSize*2 //fixme
|
||||
property string folderType: gui.enums.folderTypeFolder
|
||||
property string selectedIDs
|
||||
property string sourceID
|
||||
property bool isFolderType: root.folderType == gui.enums.folderTypeFolder
|
||||
property bool hasTarget: root.selectedIDs != ""
|
||||
property bool below: true
|
||||
|
||||
signal doNotImport()
|
||||
signal importToFolder(string newTargetID)
|
||||
|
||||
leftPadding: Style.dialog.spacing
|
||||
|
||||
onDownChanged : {
|
||||
if (root.down) view.model.updateFilter()
|
||||
root.below = popup.y>0
|
||||
}
|
||||
|
||||
contentItem : Text {
|
||||
id: boxText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold: root.down
|
||||
}
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.StyledText
|
||||
|
||||
text : root.displayText
|
||||
color: !root.enabled ? Style.main.textDisabled : ( root.down ? Style.main.background : Style.main.text )
|
||||
}
|
||||
|
||||
displayText: {
|
||||
//console.trace()
|
||||
//console.log("updatebox", view.currentIndex, root.hasTarget, root.selectedIDs, root.sourceID, root.folderType)
|
||||
if (!root.hasTarget) {
|
||||
if (root.isFolderType) return qsTr("Do not import")
|
||||
return qsTr("No labels selected")
|
||||
}
|
||||
if (!root.isFolderType) return Style.fa.tags + " " + qsTr("Add/Remove labels")
|
||||
|
||||
// We know here that it has a target and this is folder dropdown so we must find the first folder
|
||||
var selSplit = root.selectedIDs.split(";")
|
||||
for (var selIndex in selSplit) {
|
||||
var selectedID = selSplit[selIndex]
|
||||
var selectedType = structurePM.getType(selectedID)
|
||||
if (selectedType == gui.enums.folderTypeLabel) continue; // skip type::labele
|
||||
var selectedName = structurePM.getName(selectedID)
|
||||
if (selectedName == "") continue; // empty name seems like wrong ID
|
||||
var icon = gui.folderIcon(selectedName, selectedType)
|
||||
if (selectedType == gui.enums.folderTypeSystem) {
|
||||
return icon + " " + selectedName
|
||||
}
|
||||
var iconColor = structurePM.getColor(selectedID)
|
||||
return '<font color="'+iconColor+'">'+ icon + "</font> " + selectedName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
background : RoundedRectangle {
|
||||
fillColor : root.down ? Style.main.textBlue : Style.transparent
|
||||
strokeColor : root.down ? fillColor : Style.main.line
|
||||
radiusTopLeft : root.down && !root.below ? 0 : Style.dialog.radiusButton
|
||||
radiusBottomLeft : root.down && root.below ? 0 : Style.dialog.radiusButton
|
||||
radiusTopRight : radiusTopLeft
|
||||
radiusBottomRight : radiusBottomLeft
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
if (root.down) root.popup.close()
|
||||
else root.popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indicator : Text {
|
||||
text: (root.down && root.below) || (!root.down && !root.below) ? Style.fa.chevron_up : Style.fa.chevron_down
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
rightMargin: Style.dialog.spacing
|
||||
}
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
color: root.enabled && !root.down ? Style.main.textBlue : root.contentItem.color
|
||||
}
|
||||
|
||||
// Popup objects
|
||||
delegate: Rectangle {
|
||||
id: thisDelegate
|
||||
|
||||
height : Style.main.fontSize * 2
|
||||
width : selectNone.width
|
||||
|
||||
property bool isHovered: area.containsMouse
|
||||
|
||||
color: isHovered ? root.popup.hoverColor : root.popup.backColor
|
||||
|
||||
|
||||
property bool isSelected : {
|
||||
var selected = root.selectedIDs.split(";")
|
||||
for (var iSel in selected) {
|
||||
var sel = selected[iSel]
|
||||
if (folderId == sel){
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Text {
|
||||
id: targetIcon
|
||||
text: gui.folderIcon(folderName,folderType)
|
||||
color : folderType != gui.enums.folderTypeSystem ? folderColor : root.popup.textColor
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: root.leftPadding
|
||||
}
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: targetName
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: targetIcon.right
|
||||
right: parent.right
|
||||
leftMargin: Style.dialog.spacing
|
||||
rightMargin: Style.dialog.spacing
|
||||
}
|
||||
|
||||
text: folderName
|
||||
color : root.popup.textColor
|
||||
elide: Text.ElideRight
|
||||
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: targetIndicator
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
text : thisDelegate.isSelected ? Style.fa.check_square : Style.fa.square_o
|
||||
visible : thisDelegate.isSelected || !root.isFolderType
|
||||
color : root.popup.textColor
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: line
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height : Style.main.lineWidth
|
||||
color : Style.main.line
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: area
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
//console.log(" click delegate")
|
||||
if (root.isFolderType) { // don't update if selected
|
||||
if (!thisDelegate.isSelected) {
|
||||
root.importToFolder(folderId)
|
||||
}
|
||||
root.popup.close()
|
||||
}
|
||||
if (root.folderType==gui.enums.folderTypeLabel) {
|
||||
if (thisDelegate.isSelected) {
|
||||
structureExternal.removeTargetLabelID(sourceID,folderId)
|
||||
} else {
|
||||
structureExternal.addTargetLabelID(sourceID,folderId)
|
||||
}
|
||||
}
|
||||
}
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
popup : Popup {
|
||||
y: root.height
|
||||
width: root.width
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
|
||||
padding: Style.dialog.spacing
|
||||
|
||||
property var textColor : Style.main.background
|
||||
property var backColor : Style.main.text
|
||||
property var hoverColor : Style.main.textBlue
|
||||
|
||||
contentItem : Column {
|
||||
// header
|
||||
Rectangle {
|
||||
id: selectNone
|
||||
width: root.popup.width - 2*root.popup.padding
|
||||
//height: root.isFolderType ? 2* Style.main.fontSize : 0
|
||||
height: 2*Style.main.fontSize
|
||||
color: area.containsMouse ? root.popup.hoverColor : root.popup.backColor
|
||||
visible : root.isFolderType
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : Style.dialog.spacing
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
text: root.isFolderType ? qsTr("Do not import") : ""
|
||||
color: root.popup.textColor
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: line
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height : Style.dialog.borderInput
|
||||
color : Style.main.line
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: area
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
//console.log(" click no set")
|
||||
root.doNotImport()
|
||||
root.popup.close()
|
||||
}
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
// scroll area
|
||||
Rectangle {
|
||||
width: selectNone.width
|
||||
height: winMain.height/4
|
||||
color: root.popup.backColor
|
||||
|
||||
ListView {
|
||||
id: view
|
||||
|
||||
clip : true
|
||||
anchors.fill : parent
|
||||
|
||||
section.property : "sectionName"
|
||||
section.delegate : Text{text: sectionName}
|
||||
|
||||
model : FilterStructure {
|
||||
filterOnGroup : root.folderType
|
||||
delegate : root.delegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// footer
|
||||
Rectangle {
|
||||
id: addFolderOrLabel
|
||||
width: selectNone.width
|
||||
height: addButton.height + 3*Style.dialog.spacing
|
||||
color: root.popup.backColor
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height : Style.dialog.borderInput
|
||||
color : Style.main.line
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: addButton
|
||||
anchors.centerIn: addFolderOrLabel
|
||||
width: parent.width * 0.681
|
||||
|
||||
fa_icon : Style.fa.plus_circle
|
||||
text : root.isFolderType ? qsTr("Create new folder") : qsTr("Create new label")
|
||||
color_main : root.popup.textColor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
|
||||
onClicked : {
|
||||
//console.log("click", addButton.text)
|
||||
var newName = ""
|
||||
if ( typeof folderName !== 'undefined' && !structurePM.hasFolderWithName (folderName) ) {
|
||||
newName = folderName
|
||||
}
|
||||
winMain.popupFolderEdit.show(newName, "", "", root.folderType, sourceID)
|
||||
root.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background : RoundedRectangle {
|
||||
strokeColor : root.popup.backColor
|
||||
fillColor : root.popup.backColor
|
||||
radiusTopLeft : root.below ? 0 : Style.dialog.radiusButton
|
||||
radiusBottomLeft : !root.below ? 0 : Style.dialog.radiusButton
|
||||
radiusTopRight : radiusTopLeft
|
||||
radiusBottomRight : radiusBottomLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
internal/frontend/qml/ImportExportUI/SelectLabelsMenu.qml
Normal file
29
internal/frontend/qml/ImportExportUI/SelectLabelsMenu.qml
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List of import folder and their target
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
SelectFolderMenu {
|
||||
id: root
|
||||
folderType: gui.enums.folderTypeLabel
|
||||
}
|
||||
|
||||
|
||||
148
internal/frontend/qml/ImportExportUI/SettingsView.qml
Normal file
148
internal/frontend/qml/ImportExportUI/SettingsView.qml
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// List the settings
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// must have wrapper
|
||||
Rectangle {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.main.background
|
||||
|
||||
// content
|
||||
Column {
|
||||
anchors.left : parent.left
|
||||
|
||||
ButtonIconText {
|
||||
id: cacheKeychain
|
||||
text: qsTr("Clear Keychain")
|
||||
leftIcon.text : Style.fa.chain_broken
|
||||
rightIcon {
|
||||
text : qsTr("Clear")
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogGlobal.state="clearChain"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: logs
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Logs")
|
||||
leftIcon.text : Style.fa.align_justify
|
||||
rightIcon.text : Style.fa.chevron_circle_right
|
||||
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
onClicked: go.openLogs()
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: bugreport
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Report Bug")
|
||||
leftIcon.text : Style.fa.bug
|
||||
rightIcon.text : Style.fa.chevron_circle_right
|
||||
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
onClicked: bugreportWin.show()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
ButtonIconText {
|
||||
id: cacheClear
|
||||
text: qsTr("Clear Cache")
|
||||
leftIcon.text : Style.fa.times
|
||||
rightIcon {
|
||||
text : qsTr("Clear")
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogGlobal.state="clearCache"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ButtonIconText {
|
||||
id: autoStart
|
||||
text: qsTr("Automatically Start Bridge")
|
||||
leftIcon.text : Style.fa.rocket
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isAutoStart!=0 ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isAutoStart!=0 ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
onClicked: {
|
||||
go.toggleAutoStart()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: advancedSettings
|
||||
property bool isAdvanced : !go.isDefaultPort
|
||||
text: qsTr("Advanced settings")
|
||||
leftIcon.text : Style.fa.cogs
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : isAdvanced!=0 ? Style.fa.chevron_circle_up : Style.fa.chevron_circle_right
|
||||
color : isAdvanced!=0 ? Style.main.textDisabled : Style.main.textBlue
|
||||
}
|
||||
onClicked: {
|
||||
isAdvanced = !isAdvanced
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: changePort
|
||||
visible: advancedSettings.isAdvanced
|
||||
text: qsTr("Change SMTP/IMAP Ports")
|
||||
leftIcon.text : Style.fa.plug
|
||||
rightIcon {
|
||||
text : qsTr("Change")
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogChangePort.show()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
115
internal/frontend/qml/ImportExportUI/VersionInfo.qml
Normal file
115
internal/frontend/qml/ImportExportUI/VersionInfo.qml
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2020 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge 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.
|
||||
//
|
||||
// ProtonMail Bridge 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// credits
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import ImportExportUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
Rectangle {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: 2*Style.main.width/3
|
||||
height: Style.main.height - 6*Style.dialog.titleSize
|
||||
color: "transparent"
|
||||
|
||||
Flickable {
|
||||
anchors.fill : wrapper
|
||||
contentWidth : wrapper.width
|
||||
contentHeight : content.height
|
||||
flickableDirection : Flickable.VerticalFlick
|
||||
clip : true
|
||||
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: wrapper.width
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
visible: go.changelog != ""
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
font.bold: true
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
color: Style.main.text
|
||||
text: qsTr("Release notes:")
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Style.main.leftMargin
|
||||
}
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
width: wrapper.width - anchors.leftMargin
|
||||
wrapMode: Text.Wrap
|
||||
color: Style.main.text
|
||||
text: go.changelog
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: go.bugfixes != ""
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
font.bold: true
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
color: Style.main.text
|
||||
text: qsTr("Fixed bugs:")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
anchors.fill: parent
|
||||
model: go.bugfixes.split(";")
|
||||
|
||||
Text {
|
||||
visible: go.bugfixes!=""
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Style.main.leftMargin
|
||||
}
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
width: wrapper.width - anchors.leftMargin
|
||||
wrapMode: Text.Wrap
|
||||
color: Style.main.text
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{id:spacer; color:"transparent"; width:10; height: buttonClose.height}
|
||||
|
||||
|
||||
ButtonRounded {
|
||||
id: buttonClose
|
||||
anchors.horizontalCenter: content.horizontalCenter
|
||||
text: "Close"
|
||||
onClicked: {
|
||||
root.parent.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
internal/frontend/qml/ImportExportUI/qmldir
Normal file
31
internal/frontend/qml/ImportExportUI/qmldir
Normal file
@ -0,0 +1,31 @@
|
||||
module ImportExportUI
|
||||
AccountDelegate 1.0 AccountDelegate.qml
|
||||
Credits 1.0 Credits.qml
|
||||
DateBox 1.0 DateBox.qml
|
||||
DateInput 1.0 DateInput.qml
|
||||
DateRangeMenu 1.0 DateRangeMenu.qml
|
||||
DateRange 1.0 DateRange.qml
|
||||
DateRangeFunctions 1.0 DateRangeFunctions.qml
|
||||
DialogExport 1.0 DialogExport.qml
|
||||
DialogImport 1.0 DialogImport.qml
|
||||
DialogYesNo 1.0 DialogYesNo.qml
|
||||
ExportStructure 1.0 ExportStructure.qml
|
||||
FilterStructure 1.0 FilterStructure.qml
|
||||
FolderRowButton 1.0 FolderRowButton.qml
|
||||
HelpView 1.0 HelpView.qml
|
||||
IEStyle 1.0 IEStyle.qml
|
||||
ImportDelegate 1.0 ImportDelegate.qml
|
||||
ImportSourceButton 1.0 ImportSourceButton.qml
|
||||
ImportStructure 1.0 ImportStructure.qml
|
||||
ImportReport 1.0 ImportReport.qml
|
||||
ImportReportCell 1.0 ImportReportCell.qml
|
||||
InlineDateRange 1.0 InlineDateRange.qml
|
||||
InlineLabelSelect 1.0 InlineLabelSelect.qml
|
||||
LabelIconList 1.0 LabelIconList.qml
|
||||
MainWindow 1.0 MainWindow.qml
|
||||
OutputFormat 1.0 OutputFormat.qml
|
||||
PopupEditFolder 1.0 PopupEditFolder.qml
|
||||
SelectFolderMenu 1.0 SelectFolderMenu.qml
|
||||
SelectLabelsMenu 1.0 SelectLabelsMenu.qml
|
||||
SettingsView 1.0 SettingsView.qml
|
||||
VersionInfo 1.0 VersionInfo.qml
|
||||
Reference in New Issue
Block a user