GODT-1168 GODT-1169 Facelift: it has begun, qml artifacts for preview
This commit is contained in:
parent
b0ce46ca8a
commit
649195cc2b
|
@ -81,10 +81,25 @@ dependency-updates:
|
|||
|
||||
# Stage: BUILD
|
||||
|
||||
build-qml:
|
||||
tags:
|
||||
- small
|
||||
only:
|
||||
- branches
|
||||
stage: build
|
||||
artifacts:
|
||||
name: "bridge-qml-$CI_COMMIT_SHORT_SHA"
|
||||
expire_in: 1 day
|
||||
paths:
|
||||
- bridge_qml.tgz
|
||||
script:
|
||||
- cd internal/frontend/qml
|
||||
- tar -cvzf ../../../bridge_qml.tgz ./*
|
||||
|
||||
.build-base:
|
||||
stage: build
|
||||
only:
|
||||
- branches
|
||||
- manual
|
||||
before_script:
|
||||
- mkdir -p .cache/bin
|
||||
- export PATH=$(pwd)/.cache/bin:$PATH
|
||||
|
@ -118,26 +133,6 @@ build-linux-qa:
|
|||
paths:
|
||||
- bridge_*.tgz
|
||||
|
||||
build-ie-linux:
|
||||
extends: .build-base
|
||||
script:
|
||||
- make build-ie
|
||||
artifacts:
|
||||
name: "ie-linux-$CI_COMMIT_SHORT_SHA"
|
||||
paths:
|
||||
- ie_*.tgz
|
||||
|
||||
build-ie-linux-qa:
|
||||
extends: .build-base
|
||||
only:
|
||||
- web
|
||||
script:
|
||||
- BUILD_TAGS="build_qa" make build-ie
|
||||
artifacts:
|
||||
name: "ie-linux-qa-$CI_COMMIT_SHORT_SHA"
|
||||
paths:
|
||||
- ie_*.tgz
|
||||
|
||||
.build-darwin-base:
|
||||
extends: .build-base
|
||||
before_script:
|
||||
|
|
1
Makefile
1
Makefile
|
@ -230,7 +230,6 @@ test: gofiles
|
|||
./internal/cookies/... \
|
||||
./internal/crash/... \
|
||||
./internal/events/... \
|
||||
./internal/frontend/autoconfig/... \
|
||||
./internal/frontend/cli/... \
|
||||
./internal/imap/... \
|
||||
./internal/importexport/... \
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/app/base"
|
||||
"github.com/ProtonMail/proton-bridge/internal/app/ie"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
appName = "ProtonMail Import-Export app"
|
||||
appUsage = "Import and export messages to/from your ProtonMail account"
|
||||
configName = "importExport"
|
||||
updateURLName = "ie"
|
||||
keychainName = "import-export-app"
|
||||
cacheVersion = "c11"
|
||||
)
|
||||
|
||||
func main() {
|
||||
base, err := base.New(
|
||||
appName,
|
||||
appUsage,
|
||||
configName,
|
||||
updateURLName,
|
||||
keychainName,
|
||||
cacheVersion,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Failed to create app base")
|
||||
}
|
||||
// Other instance already running.
|
||||
if base == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ie.New(base).Run(os.Args); err != nil {
|
||||
logrus.WithError(err).Fatal("IE exited with error")
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Package ie implements the ie CLI application.
|
||||
package ie
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/api"
|
||||
"github.com/ProtonMail/proton-bridge/internal/app/base"
|
||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func New(b *base.Base) *cli.App {
|
||||
return b.NewApp(run)
|
||||
}
|
||||
|
||||
func run(b *base.Base, c *cli.Context) error {
|
||||
ie := importexport.New(b.Locations, b.Cache, b.CrashHandler, b.Listener, b.CM, b.Creds)
|
||||
|
||||
go func() {
|
||||
defer b.CrashHandler.HandlePanic()
|
||||
api.NewAPIServer(b.Settings, b.Listener).ListenAndServe()
|
||||
}()
|
||||
|
||||
var frontendMode string
|
||||
|
||||
switch {
|
||||
case c.Bool(base.FlagCLI):
|
||||
frontendMode = "cli"
|
||||
default:
|
||||
frontendMode = "qt"
|
||||
}
|
||||
|
||||
// We want to remove old versions if the app exits successfully.
|
||||
b.AddTeardownAction(b.Versioner.RemoveOldVersions)
|
||||
|
||||
// We want cookies to be saved to disk so they are loaded the next time.
|
||||
b.AddTeardownAction(b.CookieJar.PersistCookies)
|
||||
|
||||
f := frontend.NewImportExport(
|
||||
constants.Version,
|
||||
constants.BuildVersion,
|
||||
b.Name,
|
||||
frontendMode,
|
||||
b.CrashHandler,
|
||||
b.Locations,
|
||||
b.Settings,
|
||||
b.Listener,
|
||||
b.Updater,
|
||||
ie,
|
||||
b,
|
||||
)
|
||||
|
||||
// Watch for updates routine
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Hour)
|
||||
|
||||
for {
|
||||
checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
|
||||
return f.Loop()
|
||||
}
|
||||
|
||||
func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) { //nolint[unparam]
|
||||
log := logrus.WithField("pkg", "app/ie")
|
||||
version, err := u.Check()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("An error occurred while checking for updates")
|
||||
return
|
||||
}
|
||||
|
||||
f.WaitUntilFrontendIsReady()
|
||||
|
||||
// Update links in UI
|
||||
f.SetVersion(version)
|
||||
|
||||
if !u.IsUpdateApplicable(version) {
|
||||
log.Info("No need to update")
|
||||
return
|
||||
}
|
||||
|
||||
log.WithField("version", version.Version).Info("An update is available")
|
||||
|
||||
f.NotifyManualUpdate(version, u.CanInstall(version))
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// +build darwin
|
||||
|
||||
package autoconfig
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/internal/config/useragent"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/mobileconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
bigSurPreferncesPane = "/System/Library/PreferencePanes/Profiles.prefPane"
|
||||
)
|
||||
|
||||
func init() { //nolint[gochecknoinit]
|
||||
available = append(available, &appleMail{})
|
||||
}
|
||||
|
||||
type appleMail struct{}
|
||||
|
||||
func (c *appleMail) Name() string {
|
||||
return "Apple Mail"
|
||||
}
|
||||
|
||||
func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, addressIndex int) error {
|
||||
mc := prepareMobileConfig(imapPort, smtpPort, imapSSL, smtpSSL, user, addressIndex)
|
||||
|
||||
confPath, err := saveConfigTemporarily(mc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if useragent.IsBigSurOrNewer() {
|
||||
return exec.Command("open", bigSurPreferncesPane, confPath).Run() //nolint[gosec] G204: open command is safe, mobileconfig is generated by us
|
||||
}
|
||||
|
||||
return exec.Command("open", confPath).Run() //nolint[gosec] G204: open command is safe, mobileconfig is generated by us
|
||||
}
|
||||
|
||||
func prepareMobileConfig(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, addressIndex int) *mobileconfig.Config {
|
||||
var addresses string
|
||||
var displayName string
|
||||
|
||||
if user.IsCombinedAddressMode() {
|
||||
displayName = user.GetPrimaryAddress()
|
||||
addresses = strings.Join(user.GetAddresses(), ",")
|
||||
} else {
|
||||
for idx, address := range user.GetAddresses() {
|
||||
if idx == addressIndex {
|
||||
displayName = address
|
||||
break
|
||||
}
|
||||
}
|
||||
addresses = displayName
|
||||
}
|
||||
|
||||
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
|
||||
return &mobileconfig.Config{
|
||||
EmailAddress: addresses,
|
||||
DisplayName: displayName,
|
||||
Identifier: "protonmail " + displayName + timestamp,
|
||||
IMAP: &mobileconfig.IMAP{
|
||||
Hostname: bridge.Host,
|
||||
Port: imapPort,
|
||||
TLS: imapSSL,
|
||||
Username: displayName,
|
||||
Password: user.GetBridgePassword(),
|
||||
},
|
||||
SMTP: &mobileconfig.SMTP{
|
||||
Hostname: bridge.Host,
|
||||
Port: smtpPort,
|
||||
TLS: smtpSSL,
|
||||
Username: displayName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func saveConfigTemporarily(mc *mobileconfig.Config) (fname string, err error) {
|
||||
dir, err := ioutil.TempDir("", "protonmail-autoconfig")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure the temporary file is deleted.
|
||||
go (func() {
|
||||
<-time.After(10 * time.Minute)
|
||||
_ = os.RemoveAll(dir)
|
||||
})()
|
||||
|
||||
// Make sure the file is only readable for the current user.
|
||||
fname = filepath.Clean(filepath.Join(dir, "protonmail.mobileconfig"))
|
||||
f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mc.WriteOut(f); err != nil {
|
||||
_ = f.Close()
|
||||
return
|
||||
}
|
||||
_ = f.Close()
|
||||
|
||||
return
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Package autoconfig provides automatic config of IMAP and SMTP.
|
||||
// For now only for Apple Mail.
|
||||
package autoconfig
|
||||
|
||||
import "github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
|
||||
type AutoConfig interface {
|
||||
Name() string
|
||||
Configure(imapPort int, smtpPort int, imapSSl, smtpSSL bool, user types.User, addressIndex int) error
|
||||
}
|
||||
|
||||
var available []AutoConfig //nolint[gochecknoglobals]
|
||||
|
||||
func Available() []AutoConfig {
|
||||
return available
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
// completeUsernames is a helper to complete usernames as the user types.
|
||||
func (f *frontendCLI) completeUsernames(args []string) (usernames []string) {
|
||||
if len(args) > 1 {
|
||||
return
|
||||
}
|
||||
arg := ""
|
||||
if len(args) == 1 {
|
||||
arg = args[0]
|
||||
}
|
||||
for _, user := range f.ie.GetUsers() {
|
||||
if strings.HasPrefix(strings.ToLower(user.Username()), strings.ToLower(arg)) {
|
||||
usernames = append(usernames, user.Username())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// noAccountWrapper is a decorator for functions which need any account to be properly functional.
|
||||
func (f *frontendCLI) noAccountWrapper(callback func(*ishell.Context)) func(*ishell.Context) {
|
||||
return func(c *ishell.Context) {
|
||||
users := f.ie.GetUsers()
|
||||
if len(users) == 0 {
|
||||
f.Println("No active accounts. Please add account to continue.")
|
||||
} else {
|
||||
callback(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) askUserByIndexOrName(c *ishell.Context) types.User {
|
||||
user := f.getUserByIndexOrName("")
|
||||
if user != nil {
|
||||
return user
|
||||
}
|
||||
|
||||
numberOfAccounts := len(f.ie.GetUsers())
|
||||
indexRange := fmt.Sprintf("number between 0 and %d", numberOfAccounts-1)
|
||||
if len(c.Args) == 0 {
|
||||
f.Printf("Please choose %s or username.\n", indexRange)
|
||||
return nil
|
||||
}
|
||||
arg := c.Args[0]
|
||||
user = f.getUserByIndexOrName(arg)
|
||||
if user == nil {
|
||||
f.Printf("Wrong input '%s'. Choose %s or username.\n", bold(arg), indexRange)
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (f *frontendCLI) getUserByIndexOrName(arg string) types.User {
|
||||
users := f.ie.GetUsers()
|
||||
numberOfAccounts := len(users)
|
||||
if numberOfAccounts == 0 {
|
||||
return nil
|
||||
}
|
||||
if numberOfAccounts == 1 {
|
||||
return users[0]
|
||||
}
|
||||
if index, err := strconv.Atoi(arg); err == nil {
|
||||
if index < 0 || index >= numberOfAccounts {
|
||||
return nil
|
||||
}
|
||||
return users[index]
|
||||
}
|
||||
for _, user := range users {
|
||||
if user.Username() == arg {
|
||||
return user
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
func (f *frontendCLI) listAccounts(c *ishell.Context) {
|
||||
spacing := "%-2d: %-20s (%-15s, %-15s)\n"
|
||||
f.Printf(bold(strings.ReplaceAll(spacing, "d", "s")), "#", "account", "status", "address mode")
|
||||
for idx, user := range f.ie.GetUsers() {
|
||||
connected := "disconnected"
|
||||
if user.IsConnected() {
|
||||
connected = "connected"
|
||||
}
|
||||
mode := "split"
|
||||
if user.IsCombinedAddressMode() {
|
||||
mode = "combined"
|
||||
}
|
||||
f.Printf(spacing, idx, user.Username(), connected, mode)
|
||||
}
|
||||
f.Println()
|
||||
}
|
||||
|
||||
func (f *frontendCLI) loginAccount(c *ishell.Context) { // nolint[funlen]
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
loginName := ""
|
||||
if len(c.Args) > 0 {
|
||||
user := f.getUserByIndexOrName(c.Args[0])
|
||||
if user != nil {
|
||||
loginName = user.GetPrimaryAddress()
|
||||
}
|
||||
}
|
||||
|
||||
if loginName == "" {
|
||||
loginName = f.readStringInAttempts("Username", c.ReadLine, isNotEmpty)
|
||||
if loginName == "" {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
f.Println("Username:", loginName)
|
||||
}
|
||||
|
||||
password := f.readStringInAttempts("Password", c.ReadPassword, isNotEmpty)
|
||||
if password == "" {
|
||||
return
|
||||
}
|
||||
|
||||
f.Println("Authenticating ... ")
|
||||
client, auth, err := f.ie.Login(loginName, []byte(password))
|
||||
if err != nil {
|
||||
f.processAPIError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if auth.HasTwoFactor() {
|
||||
twoFactor := f.readStringInAttempts("Two factor code", c.ReadLine, isNotEmpty)
|
||||
if twoFactor == "" {
|
||||
return
|
||||
}
|
||||
|
||||
err = client.Auth2FA(context.Background(), twoFactor)
|
||||
if err != nil {
|
||||
f.processAPIError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mailboxPassword := password
|
||||
if auth.HasMailboxPassword() {
|
||||
mailboxPassword = f.readStringInAttempts("Mailbox password", c.ReadPassword, isNotEmpty)
|
||||
}
|
||||
if mailboxPassword == "" {
|
||||
return
|
||||
}
|
||||
|
||||
f.Println("Adding account ...")
|
||||
user, err := f.ie.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||
if err != nil {
|
||||
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
||||
f.Println("Adding account was unsuccessful:", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.Printf("Account %s was added successfully.\n", bold(user.Username()))
|
||||
}
|
||||
|
||||
func (f *frontendCLI) logoutAccount(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
if f.yesNoQuestion("Are you sure you want to logout account " + bold(user.Username())) {
|
||||
if err := user.Logout(); err != nil {
|
||||
f.printAndLogError("Logging out failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) deleteAccount(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
if f.yesNoQuestion("Are you sure you want to " + bold("remove account "+user.Username())) {
|
||||
clearCache := f.yesNoQuestion("Do you want to remove cache for this account")
|
||||
if err := f.ie.DeleteUser(user.ID(), clearCache); err != nil {
|
||||
f.printAndLogError("Cannot delete account: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) deleteAccounts(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
if !f.yesNoQuestion("Do you really want remove all accounts") {
|
||||
return
|
||||
}
|
||||
for _, user := range f.ie.GetUsers() {
|
||||
if err := f.ie.DeleteUser(user.ID(), false); err != nil {
|
||||
f.printAndLogError("Cannot delete account ", user.Username(), ": ", err)
|
||||
}
|
||||
}
|
||||
c.Println("Keychain cleared")
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Package cliie provides CLI interface of the Import-Export app.
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/internal/locations"
|
||||
"github.com/ProtonMail/proton-bridge/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||
|
||||
"github.com/abiosoft/ishell"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logrus.WithField("pkg", "frontend/cli-ie") //nolint[gochecknoglobals]
|
||||
)
|
||||
|
||||
type frontendCLI struct {
|
||||
*ishell.Shell
|
||||
|
||||
locations *locations.Locations
|
||||
eventListener listener.Listener
|
||||
updater types.Updater
|
||||
ie types.ImportExporter
|
||||
|
||||
restarter types.Restarter
|
||||
}
|
||||
|
||||
// New returns a new CLI frontend configured with the given options.
|
||||
func New( //nolint[funlen]
|
||||
panicHandler types.PanicHandler,
|
||||
|
||||
locations *locations.Locations,
|
||||
eventListener listener.Listener,
|
||||
updater types.Updater,
|
||||
ie types.ImportExporter,
|
||||
restarter types.Restarter,
|
||||
) *frontendCLI { //nolint[golint]
|
||||
fe := &frontendCLI{
|
||||
Shell: ishell.New(),
|
||||
|
||||
locations: locations,
|
||||
eventListener: eventListener,
|
||||
updater: updater,
|
||||
ie: ie,
|
||||
|
||||
restarter: restarter,
|
||||
}
|
||||
|
||||
// Clear commands.
|
||||
clearCmd := &ishell.Cmd{Name: "clear",
|
||||
Help: "remove stored accounts and preferences. (alias: cl)",
|
||||
Aliases: []string{"cl"},
|
||||
}
|
||||
clearCmd.AddCmd(&ishell.Cmd{Name: "accounts",
|
||||
Help: "remove all accounts from keychain. (aliases: a, k, keychain)",
|
||||
Aliases: []string{"a", "k", "keychain"},
|
||||
Func: fe.deleteAccounts,
|
||||
})
|
||||
fe.AddCmd(clearCmd)
|
||||
|
||||
// Check commands.
|
||||
checkCmd := &ishell.Cmd{Name: "check", Help: "check internet connection or new version."}
|
||||
checkCmd.AddCmd(&ishell.Cmd{Name: "updates",
|
||||
Help: "check for Import-Export updates. (aliases: u, v, version)",
|
||||
Aliases: []string{"u", "version", "v"},
|
||||
Func: fe.checkUpdates,
|
||||
})
|
||||
fe.AddCmd(checkCmd)
|
||||
|
||||
// Print info commands.
|
||||
fe.AddCmd(&ishell.Cmd{Name: "log-dir",
|
||||
Help: "print path to directory with logs. (aliases: log, logs)",
|
||||
Aliases: []string{"log", "logs"},
|
||||
Func: fe.printLogDir,
|
||||
})
|
||||
fe.AddCmd(&ishell.Cmd{Name: "manual",
|
||||
Help: "print URL with instructions. (alias: man)",
|
||||
Aliases: []string{"man"},
|
||||
Func: fe.printManual,
|
||||
})
|
||||
fe.AddCmd(&ishell.Cmd{Name: "credits",
|
||||
Help: "print used resources.",
|
||||
Func: fe.printCredits,
|
||||
})
|
||||
|
||||
// Account commands.
|
||||
fe.AddCmd(&ishell.Cmd{Name: "list",
|
||||
Help: "print the list of accounts. (aliases: l, ls)",
|
||||
Func: fe.noAccountWrapper(fe.listAccounts),
|
||||
Aliases: []string{"l", "ls"},
|
||||
})
|
||||
fe.AddCmd(&ishell.Cmd{Name: "login",
|
||||
Help: "login procedure to add or connect account. Optionally use index or account as parameter. (aliases: a, add, con, connect)",
|
||||
Func: fe.loginAccount,
|
||||
Aliases: []string{"add", "a", "con", "connect"},
|
||||
Completer: fe.completeUsernames,
|
||||
})
|
||||
fe.AddCmd(&ishell.Cmd{Name: "logout",
|
||||
Help: "disconnect the account. Use index or account name as parameter. (aliases: d, disconnect)",
|
||||
Func: fe.noAccountWrapper(fe.logoutAccount),
|
||||
Aliases: []string{"d", "disconnect"},
|
||||
Completer: fe.completeUsernames,
|
||||
})
|
||||
fe.AddCmd(&ishell.Cmd{Name: "delete",
|
||||
Help: "remove the account from keychain. Use index or account name as parameter. (aliases: del, rm, remove)",
|
||||
Func: fe.noAccountWrapper(fe.deleteAccount),
|
||||
Aliases: []string{"del", "rm", "remove"},
|
||||
Completer: fe.completeUsernames,
|
||||
})
|
||||
|
||||
// Import-Export commands.
|
||||
importCmd := &ishell.Cmd{Name: "import",
|
||||
Help: "import messages. (alias: imp)",
|
||||
Aliases: []string{"imp"},
|
||||
}
|
||||
importCmd.AddCmd(&ishell.Cmd{Name: "local",
|
||||
Help: "import local messages. (aliases: loc)",
|
||||
Func: fe.noAccountWrapper(fe.importLocalMessages),
|
||||
Aliases: []string{"loc"},
|
||||
})
|
||||
importCmd.AddCmd(&ishell.Cmd{Name: "remote",
|
||||
Help: "import remote messages. (aliases: rem)",
|
||||
Func: fe.noAccountWrapper(fe.importRemoteMessages),
|
||||
Aliases: []string{"rem"},
|
||||
})
|
||||
fe.AddCmd(importCmd)
|
||||
|
||||
exportCmd := &ishell.Cmd{Name: "export",
|
||||
Help: "export messages. (alias: exp)",
|
||||
Aliases: []string{"exp"},
|
||||
}
|
||||
exportCmd.AddCmd(&ishell.Cmd{Name: "eml",
|
||||
Help: "export messages to eml files.",
|
||||
Func: fe.noAccountWrapper(fe.exportMessagesToEML),
|
||||
})
|
||||
exportCmd.AddCmd(&ishell.Cmd{Name: "mbox",
|
||||
Help: "export messages to mbox files.",
|
||||
Func: fe.noAccountWrapper(fe.exportMessagesToMBOX),
|
||||
})
|
||||
fe.AddCmd(exportCmd)
|
||||
|
||||
// System commands.
|
||||
fe.AddCmd(&ishell.Cmd{Name: "restart",
|
||||
Help: "restart the Import-Export app.",
|
||||
Func: fe.restart,
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer panicHandler.HandlePanic()
|
||||
fe.watchEvents()
|
||||
}()
|
||||
return fe
|
||||
}
|
||||
|
||||
func (f *frontendCLI) watchEvents() {
|
||||
errorCh := f.eventListener.ProvideChannel(events.ErrorEvent)
|
||||
credentialsErrorCh := f.eventListener.ProvideChannel(events.CredentialsErrorEvent)
|
||||
internetOffCh := f.eventListener.ProvideChannel(events.InternetOffEvent)
|
||||
internetOnCh := f.eventListener.ProvideChannel(events.InternetOnEvent)
|
||||
addressChangedLogoutCh := f.eventListener.ProvideChannel(events.AddressChangedLogoutEvent)
|
||||
logoutCh := f.eventListener.ProvideChannel(events.LogoutEvent)
|
||||
certIssue := f.eventListener.ProvideChannel(events.TLSCertIssue)
|
||||
for {
|
||||
select {
|
||||
case errorDetails := <-errorCh:
|
||||
f.Println("Import-Export failed:", errorDetails)
|
||||
case <-credentialsErrorCh:
|
||||
f.notifyCredentialsError()
|
||||
case <-internetOffCh:
|
||||
f.notifyInternetOff()
|
||||
case <-internetOnCh:
|
||||
f.notifyInternetOn()
|
||||
case address := <-addressChangedLogoutCh:
|
||||
f.notifyLogout(address)
|
||||
case userID := <-logoutCh:
|
||||
user, err := f.ie.GetUser(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.notifyLogout(user.Username())
|
||||
case <-certIssue:
|
||||
f.notifyCertIssue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop starts the frontend loop with an interactive shell.
|
||||
func (f *frontendCLI) Loop() error {
|
||||
f.Print(`
|
||||
Welcome to ProtonMail Import-Export app interactive shell
|
||||
|
||||
WARNING: The CLI is an experimental feature and does not yet cover all functionality.
|
||||
`)
|
||||
f.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
|
||||
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
|
||||
}
|
||||
|
||||
func (f *frontendCLI) WaitUntilFrontendIsReady() {}
|
||||
func (f *frontendCLI) SetVersion(version updater.VersionInfo) {}
|
||||
func (f *frontendCLI) NotifySilentUpdateInstalled() {}
|
||||
func (f *frontendCLI) NotifySilentUpdateError(err error) {}
|
|
@ -1,232 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/internal/transfer"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
func (f *frontendCLI) importLocalMessages(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user, path := f.getUserAndPath(c, false)
|
||||
if user == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := f.ie.GetLocalImporter(user.Username(), user.GetPrimaryAddress(), path)
|
||||
f.transfer(t, err, false, true)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) importRemoteMessages(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
|
||||
username := f.readStringInAttempts("IMAP username", c.ReadLine, isNotEmpty)
|
||||
if username == "" {
|
||||
return
|
||||
}
|
||||
password := f.readStringInAttempts("IMAP password", c.ReadPassword, isNotEmpty)
|
||||
if password == "" {
|
||||
return
|
||||
}
|
||||
host := f.readStringInAttempts("IMAP host", c.ReadLine, isNotEmpty)
|
||||
if host == "" {
|
||||
return
|
||||
}
|
||||
port := f.readStringInAttempts("IMAP port", c.ReadLine, isNotEmpty)
|
||||
if port == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := f.ie.GetRemoteImporter(user.Username(), user.GetPrimaryAddress(), username, password, host, port)
|
||||
f.transfer(t, err, false, true)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) exportMessagesToEML(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user, path := f.getUserAndPath(c, true)
|
||||
if user == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := f.ie.GetEMLExporter(user.Username(), user.GetPrimaryAddress(), path)
|
||||
f.transfer(t, err, true, false)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) exportMessagesToMBOX(c *ishell.Context) {
|
||||
f.ShowPrompt(false)
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user, path := f.getUserAndPath(c, true)
|
||||
if user == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := f.ie.GetMBOXExporter(user.Username(), user.GetPrimaryAddress(), path)
|
||||
f.transfer(t, err, true, false)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) getUserAndPath(c *ishell.Context, createPath bool) (types.User, string) {
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
path := f.readStringInAttempts("Path of EML and MBOX files", c.ReadLine, isNotEmpty)
|
||||
if path == "" {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
if createPath {
|
||||
_ = os.Mkdir(path, os.ModePerm)
|
||||
}
|
||||
|
||||
return user, path
|
||||
}
|
||||
|
||||
func (f *frontendCLI) transfer(t *transfer.Transfer, err error, askSkipEncrypted bool, askGlobalMailbox bool) {
|
||||
if err != nil {
|
||||
f.printAndLogError("Failed to init transferrer: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if askSkipEncrypted {
|
||||
skipEncryptedMessages := f.yesNoQuestion("Skip encrypted messages")
|
||||
t.SetSkipEncryptedMessages(skipEncryptedMessages)
|
||||
}
|
||||
|
||||
if !f.setTransferRules(t) {
|
||||
return
|
||||
}
|
||||
|
||||
if askGlobalMailbox {
|
||||
if err := f.setTransferGlobalMailbox(t); err != nil {
|
||||
f.printAndLogError("Failed to create global mailbox: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
progress := t.Start()
|
||||
for range progress.GetUpdateChannel() {
|
||||
f.printTransferProgress(progress)
|
||||
}
|
||||
f.printTransferResult(progress)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) setTransferGlobalMailbox(t *transfer.Transfer) error {
|
||||
labelName := fmt.Sprintf("Imported %s", time.Now().Format("Jan-02-2006 15:04"))
|
||||
|
||||
useGlobalLabel := f.yesNoQuestion("Use global label " + labelName)
|
||||
if !useGlobalLabel {
|
||||
return nil
|
||||
}
|
||||
|
||||
globalMailbox, err := t.CreateTargetMailbox(transfer.Mailbox{
|
||||
Name: labelName,
|
||||
Color: pmapi.LabelColors[0],
|
||||
IsExclusive: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.SetGlobalMailbox(&globalMailbox)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *frontendCLI) setTransferRules(t *transfer.Transfer) bool {
|
||||
f.Println("Rules:")
|
||||
for _, rule := range t.GetRules() {
|
||||
if !rule.Active {
|
||||
continue
|
||||
}
|
||||
targets := strings.Join(rule.TargetMailboxNames(), ", ")
|
||||
if rule.HasTimeLimit() {
|
||||
f.Printf(" %-30s → %s (%s - %s)\n", rule.SourceMailbox.Name, targets, rule.FromDate(), rule.ToDate())
|
||||
} else {
|
||||
f.Printf(" %-30s → %s\n", rule.SourceMailbox.Name, targets)
|
||||
}
|
||||
}
|
||||
|
||||
return f.yesNoQuestion("Proceed")
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printTransferProgress(progress *transfer.Progress) {
|
||||
counts := progress.GetCounts()
|
||||
if counts.Total != 0 {
|
||||
f.Println(fmt.Sprintf(
|
||||
"Progress update: %d (%d / %d) / %d, skipped: %d, failed: %d",
|
||||
counts.Imported,
|
||||
counts.Exported,
|
||||
counts.Added,
|
||||
counts.Total,
|
||||
counts.Skipped,
|
||||
counts.Failed,
|
||||
))
|
||||
}
|
||||
|
||||
if progress.IsPaused() {
|
||||
f.Printf("Transfer is paused bacause %s", progress.PauseReason())
|
||||
if !f.yesNoQuestion("Continue (y) or stop (n)") {
|
||||
progress.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printTransferResult(progress *transfer.Progress) {
|
||||
err := progress.GetFatalError()
|
||||
if err != nil {
|
||||
f.Println("Transfer failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
statuses := progress.GetFailedMessages()
|
||||
if len(statuses) == 0 {
|
||||
f.Println("Transfer finished!")
|
||||
return
|
||||
}
|
||||
|
||||
f.Println("Transfer finished with errors:")
|
||||
for _, messageStatus := range statuses {
|
||||
f.Printf(
|
||||
" %-17s | %-30s | %-30s\n %s: %s\n",
|
||||
messageStatus.Time.Format("Jan 02 2006 15:04"),
|
||||
messageStatus.From,
|
||||
messageStatus.Subject,
|
||||
messageStatus.SourceID,
|
||||
messageStatus.GetErrorMessage(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
func (f *frontendCLI) restart(c *ishell.Context) {
|
||||
if f.yesNoQuestion("Are you sure you want to restart the Import-Export app") {
|
||||
f.Println("Restarting the Import-Export app...")
|
||||
f.restarter.SetToRestart()
|
||||
f.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printLogDir(c *ishell.Context) {
|
||||
if path, err := f.locations.ProvideLogsPath(); err != nil {
|
||||
f.Println("Failed to determine location of log files")
|
||||
} else {
|
||||
f.Println("Log files are stored in\n\n ", path)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printManual(c *ishell.Context) {
|
||||
f.Println("More instructions about the Import-Export app can be found at\n\n https://protonmail.com/support/categories/import-export/")
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
func (f *frontendCLI) checkUpdates(c *ishell.Context) {
|
||||
f.Println("Your version is up to date.")
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printCredits(c *ishell.Context) {
|
||||
for _, pkg := range strings.Split(importexport.Credits, ";") {
|
||||
f.Println(pkg)
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package cliie
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const (
|
||||
maxInputRepeat = 2
|
||||
)
|
||||
|
||||
var (
|
||||
bold = color.New(color.Bold).SprintFunc() //nolint[gochecknoglobals]
|
||||
)
|
||||
|
||||
func isNotEmpty(val string) bool {
|
||||
return val != ""
|
||||
}
|
||||
|
||||
func (f *frontendCLI) yesNoQuestion(question string) bool {
|
||||
f.Print(question, "? yes/"+bold("no")+": ")
|
||||
yes := "yes"
|
||||
answer := strings.ToLower(f.ReadLine())
|
||||
for i := 0; i < len(answer); i++ {
|
||||
if i >= len(yes) || answer[i] != yes[i] {
|
||||
return false // Everything else is false.
|
||||
}
|
||||
}
|
||||
return len(answer) > 0 // Empty is false.
|
||||
}
|
||||
|
||||
func (f *frontendCLI) readStringInAttempts(title string, readFunc func() string, isOK func(string) bool) (value string) {
|
||||
f.Printf("%s: ", title)
|
||||
value = readFunc()
|
||||
title = strings.ToLower(string(title[0])) + title[1:]
|
||||
for i := 0; !isOK(value); i++ {
|
||||
if i >= maxInputRepeat {
|
||||
f.Println("Too many attempts")
|
||||
return ""
|
||||
}
|
||||
f.Printf("Please fill %s: ", title)
|
||||
value = readFunc()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *frontendCLI) printAndLogError(args ...interface{}) {
|
||||
log.Error(args...)
|
||||
f.Println(args...)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) processAPIError(err error) {
|
||||
log.Warn("API error: ", err)
|
||||
switch err {
|
||||
case pmapi.ErrNoConnection:
|
||||
f.notifyInternetOff()
|
||||
case pmapi.ErrUpgradeApplication:
|
||||
f.notifyNeedUpgrade()
|
||||
default:
|
||||
f.Println("Server error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyInternetOff() {
|
||||
f.Println("Internet connection is not available.")
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyInternetOn() {
|
||||
f.Println("Internet connection is available again.")
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyLogout(address string) {
|
||||
f.Printf("Account %s is disconnected. Login to continue using this account with email client.", address)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyNeedUpgrade() {
|
||||
version, err := f.updater.Check()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to notify need upgrade")
|
||||
return
|
||||
}
|
||||
f.Println("Please download and install the newest version of application from", version.LandingPage)
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyCredentialsError() { // nolint[unused]
|
||||
// Print in 80-column width.
|
||||
f.Println("ProtonMail Import-Export app is not able to detect a supported password manager")
|
||||
f.Println("(pass, gnome-keyring). Please install and set up a supported password manager")
|
||||
f.Println("and restart the application.")
|
||||
}
|
||||
|
||||
func (f *frontendCLI) notifyCertIssue() {
|
||||
// Print in 80-column width.
|
||||
f.Println(`Connection security error: Your network connection to Proton services may
|
||||
be insecure.
|
||||
|
||||
Description:
|
||||
ProtonMail Import-Export was not able to establish a secure connection to Proton
|
||||
servers due to a TLS certificate error. This means your connection may
|
||||
potentially be insecure and susceptible to monitoring by third parties.
|
||||
|
||||
Recommendation:
|
||||
* If you trust your network operator, you can continue to use ProtonMail
|
||||
as usual.
|
||||
* If you don't trust your network operator, reconnect to ProtonMail over a VPN
|
||||
(such as ProtonVPN) which encrypts your Internet connection, or use
|
||||
a different network to access ProtonMail.
|
||||
`)
|
||||
}
|
|
@ -24,22 +24,12 @@ import (
|
|||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/internal/config/useragent"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/cli"
|
||||
cliie "github.com/ProtonMail/proton-bridge/internal/frontend/cli-ie"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/qt"
|
||||
qtie "github.com/ProtonMail/proton-bridge/internal/frontend/qt-ie"
|
||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
||||
"github.com/ProtonMail/proton-bridge/internal/locations"
|
||||
"github.com/ProtonMail/proton-bridge/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logrus.WithField("pkg", "frontend") // nolint[unused]
|
||||
)
|
||||
|
||||
// Frontend is an interface to be implemented by each frontend type (cli, gui, html).
|
||||
type Frontend interface {
|
||||
Loop() error
|
||||
NotifyManualUpdate(update updater.VersionInfo, canInstall bool)
|
||||
|
@ -68,42 +58,6 @@ func New(
|
|||
restarter types.Restarter,
|
||||
) Frontend {
|
||||
bridgeWrap := types.NewBridgeWrap(bridge)
|
||||
return newBridgeFrontend(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
frontendType,
|
||||
showWindowOnStart,
|
||||
panicHandler,
|
||||
locations,
|
||||
settings,
|
||||
eventListener,
|
||||
updater,
|
||||
userAgent,
|
||||
bridgeWrap,
|
||||
noEncConfirmator,
|
||||
autostart,
|
||||
restarter,
|
||||
)
|
||||
}
|
||||
|
||||
func newBridgeFrontend(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
frontendType string,
|
||||
showWindowOnStart bool,
|
||||
panicHandler types.PanicHandler,
|
||||
locations *locations.Locations,
|
||||
settings *settings.Settings,
|
||||
eventListener listener.Listener,
|
||||
updater types.Updater,
|
||||
userAgent *useragent.UserAgent,
|
||||
bridge types.Bridger,
|
||||
noEncConfirmator types.NoEncConfirmator,
|
||||
autostart *autostart.App,
|
||||
restarter types.Restarter,
|
||||
) Frontend {
|
||||
switch frontendType {
|
||||
case "cli":
|
||||
return cli.New(
|
||||
|
@ -112,94 +66,10 @@ func newBridgeFrontend(
|
|||
settings,
|
||||
eventListener,
|
||||
updater,
|
||||
bridge,
|
||||
bridgeWrap,
|
||||
restarter,
|
||||
)
|
||||
default:
|
||||
return qt.New(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
showWindowOnStart,
|
||||
panicHandler,
|
||||
locations,
|
||||
settings,
|
||||
eventListener,
|
||||
updater,
|
||||
userAgent,
|
||||
bridge,
|
||||
noEncConfirmator,
|
||||
autostart,
|
||||
restarter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NewImportExport returns initialized frontend based on `frontendType`, which can be `cli` or `qt`.
|
||||
func NewImportExport(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
frontendType string,
|
||||
panicHandler types.PanicHandler,
|
||||
locations *locations.Locations,
|
||||
settings *settings.Settings,
|
||||
eventListener listener.Listener,
|
||||
updater types.Updater,
|
||||
ie *importexport.ImportExport,
|
||||
restarter types.Restarter,
|
||||
) Frontend {
|
||||
ieWrap := types.NewImportExportWrap(ie)
|
||||
return newIEFrontend(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
frontendType,
|
||||
panicHandler,
|
||||
locations,
|
||||
settings,
|
||||
eventListener,
|
||||
updater,
|
||||
ieWrap,
|
||||
restarter,
|
||||
)
|
||||
}
|
||||
|
||||
func newIEFrontend(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
frontendType string,
|
||||
panicHandler types.PanicHandler,
|
||||
locations *locations.Locations,
|
||||
settings *settings.Settings,
|
||||
eventListener listener.Listener,
|
||||
updater types.Updater,
|
||||
ie types.ImportExporter,
|
||||
restarter types.Restarter,
|
||||
) Frontend {
|
||||
switch frontendType {
|
||||
case "cli":
|
||||
return cliie.New(
|
||||
panicHandler,
|
||||
locations,
|
||||
eventListener,
|
||||
updater,
|
||||
ie,
|
||||
restarter,
|
||||
)
|
||||
default:
|
||||
return qtie.New(
|
||||
version,
|
||||
buildVersion,
|
||||
programName,
|
||||
panicHandler,
|
||||
locations,
|
||||
settings,
|
||||
eventListener,
|
||||
updater,
|
||||
ie,
|
||||
restarter,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,430 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 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 int row_width: 50 * Style.px
|
||||
property int row_height: Style.accounts.heightAccount
|
||||
property var listalias : aliases.split(";")
|
||||
property int iAccount: index
|
||||
|
||||
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
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: mainaccRow.actionName
|
||||
Accessible.description: mainaccRow.actionName
|
||||
Accessible.onPressAction : mainaccRow.toggle_accountSettings()
|
||||
Accessible.ignored: root.state!="connected" || !root.enabled
|
||||
}
|
||||
}
|
||||
|
||||
// account name
|
||||
TextMetrics {
|
||||
id: accountMetrics
|
||||
font : accountName.font
|
||||
elide: Qt.ElideMiddle
|
||||
elideWidth: Style.accounts.elideWidth
|
||||
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 : Style.accounts.leftMargin2
|
||||
}
|
||||
text : qsTr("connected", "status of a listed logged-in account")
|
||||
iconText : Style.fa.circle_o
|
||||
textColor : Style.main.textGreen
|
||||
enabled : false
|
||||
Accessible.ignored: true
|
||||
}
|
||||
|
||||
// logout
|
||||
ClickIconText {
|
||||
id: logoutAccount
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : parent.left
|
||||
leftMargin : Style.accounts.leftMargin3
|
||||
}
|
||||
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
|
||||
right : parent.right
|
||||
rightMargin : Style.main.rightMargin
|
||||
}
|
||||
text : qsTr("Remove", "deletes an account from the account settings page")
|
||||
iconText : Style.fa.trash_o
|
||||
textColor : Style.main.text
|
||||
onClicked : {
|
||||
dialogGlobal.input=root.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
|
||||
|
||||
Rectangle {
|
||||
id: addressModeWrapper
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
visible : mainaccRow.state=="expanded"
|
||||
height : 2*Style.accounts.heightAddrRow/3
|
||||
color : Style.accounts.backgroundExpanded
|
||||
|
||||
ClickIconText {
|
||||
id: addressModeSwitch
|
||||
anchors {
|
||||
top : addressModeWrapper.top
|
||||
right : addressModeWrapper.right
|
||||
rightMargin : Style.main.rightMargin
|
||||
}
|
||||
textColor : Style.main.textBlue
|
||||
iconText : Style.fa.exchange
|
||||
iconOnRight : false
|
||||
text : isCombinedAddressMode ?
|
||||
qsTr("Switch to split addresses mode", "Text of button switching to mode with one configuration per each address.") :
|
||||
qsTr("Switch to combined addresses mode", "Text of button switching to mode with one configuration for all addresses.")
|
||||
|
||||
onClicked: {
|
||||
dialogGlobal.input=root.iAccount
|
||||
dialogGlobal.state="addressmode"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: combinedAddressConfig
|
||||
anchors {
|
||||
top : addressModeWrapper.top
|
||||
left : addressModeWrapper.left
|
||||
leftMargin : Style.accounts.leftMarginAddr+Style.main.leftMargin
|
||||
}
|
||||
visible : isCombinedAddressMode
|
||||
text : qsTr("Mailbox configuration", "Displays IMAP/SMTP settings information for a given account")
|
||||
iconText : Style.fa.gear
|
||||
textColor : Style.main.textBlue
|
||||
onClicked : {
|
||||
infoWin.showInfo(root.iAccount,0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeaterAddresses
|
||||
model: ["one", "two"]
|
||||
|
||||
Rectangle {
|
||||
id: addressRow
|
||||
visible: !isCombinedAddressMode
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height: Style.accounts.heightAddrRow
|
||||
color: Style.accounts.backgroundExpanded
|
||||
|
||||
// icon 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: 2*wrapAddr.width/3
|
||||
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
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: addressConfig
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
right: parent.right
|
||||
rightMargin: Style.main.rightMargin
|
||||
}
|
||||
text : qsTr("Address configuration", "Display the IMAP/SMTP configuration for address")
|
||||
iconText : Style.fa.gear
|
||||
textColor : Style.main.textBlue
|
||||
onClicked : infoWin.showInfo(root.iAccount,index)
|
||||
|
||||
Accessible.description: qsTr("Address configuration for %1", "Accessible text of button displaying the IMAP/SMTP configuration for address %1").arg(modelData)
|
||||
Accessible.ignored: !enabled
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clickSettings
|
||||
anchors.fill: wrapAddr
|
||||
onClicked : addressConfig.clicked()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onPressed: {
|
||||
wrapAddr.color = Qt.rgba(1,1,1,0.20)
|
||||
}
|
||||
onEntered: {
|
||||
wrapAddr.color = Qt.rgba(1,1,1,0.15)
|
||||
}
|
||||
onExited: {
|
||||
wrapAddr.color = Style.accounts.backgroundAddrRow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 : logoutAccount
|
||||
text : qsTr("Log out", "action to log out a connected account")
|
||||
onClicked : {
|
||||
mainaccRow.state="collapsed"
|
||||
dialogGlobal.input = root.iAccount
|
||||
dialogGlobal.state = "logout"
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) 2021 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 main menu
|
||||
|
||||
import QtQuick 2.8
|
||||
import BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "#aaff5577"
|
||||
anchors {
|
||||
left : tabbar.left
|
||||
right : tabbar.right
|
||||
top : tabbar.bottom
|
||||
bottom : parent.bottom
|
||||
}
|
||||
visible: false
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: toggle()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color : Style.menu.background
|
||||
radius : Style.menu.radius
|
||||
width : Style.menu.width
|
||||
height : Style.menu.height
|
||||
anchors {
|
||||
top : parent.top
|
||||
right : parent.right
|
||||
topMargin : Style.menu.topMargin
|
||||
rightMargin : Style.menu.rightMargin
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("About")
|
||||
color: Style.menu.text
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(){
|
||||
if (root.visible == false) {
|
||||
root.visible = true
|
||||
} else {
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: Style.main.width
|
||||
height: 3*Style.main.height/4
|
||||
color: "transparent"
|
||||
//color: "red"
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
clip : true
|
||||
model : go.credits.split(";")
|
||||
|
||||
delegate: AccessibleText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: modelData
|
||||
color: Style.main.text
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
}
|
||||
|
||||
footer: ButtonRounded {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Close", "close window")
|
||||
onClicked: dialogCredits.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
// Copyright (c) 2021 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 ProtonUI 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : ""
|
||||
isDialogBusy: false
|
||||
property string firstParagraph : qsTr("The Bridge is an application that runs on your computer in the background and seamlessly encrypts and decrypts your mail as it enters and leaves your computer.", "instructions that appear on welcome screen at first start")
|
||||
property string secondParagraph : qsTr("To add your ProtonMail account to the Bridge and <strong>generate your Bridge password</strong>, please see <a href=\"https://protonmail.com/bridge/install\">the installation guide</a> for detailed setup instructions.", "confirms and dismisses a notification (URL that leads to installation guide should stay intact)")
|
||||
|
||||
Column {
|
||||
id: dialogMessage
|
||||
property int heightInputs : welcome.height + middleSep.height + instructions.height + buttSep.height + buttonOkay.height + imageSep.height + logo.height
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-dialogMessage.heightInputs)/2 }
|
||||
|
||||
Text {
|
||||
id:welcome
|
||||
color: Style.main.text
|
||||
font.bold: true
|
||||
font.pointSize: 1.5*Style.main.fontSize*Style.pt
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Welcome to the", "welcome screen that appears on first start")
|
||||
}
|
||||
|
||||
Rectangle {id: imageSep; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator }
|
||||
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.spacing
|
||||
Image {
|
||||
id: logo
|
||||
anchors.bottom : pmbridge.baseline
|
||||
height : 2*Style.main.fontSize
|
||||
fillMode : Image.PreserveAspectFit
|
||||
mipmap : true
|
||||
source : "../ProtonUI/images/pm_logo.png"
|
||||
}
|
||||
AccessibleText {
|
||||
id:pmbridge
|
||||
color: Style.main.text
|
||||
font.bold: true
|
||||
font.pointSize: 2.2*Style.main.fontSize*Style.pt
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("ProtonMail Bridge", "app title")
|
||||
|
||||
Accessible.name: this.clearText(pmbridge.text)
|
||||
Accessible.description: this.clearText(welcome.text+ " " + pmbridge.text + ". " + root.firstParagraph + ". " + root.secondParagraph)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Rectangle { id:middleSep; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator }
|
||||
|
||||
|
||||
Text {
|
||||
id:instructions
|
||||
color: Style.main.text
|
||||
font.pointSize: Style.main.fontSize*Style.pt
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: root.width/1.5
|
||||
wrapMode: Text.Wrap
|
||||
textFormat: Text.RichText
|
||||
text: "<html><style>a { color: "+Style.main.textBlue+"; text-decoration: none;}</style><body>"+
|
||||
root.firstParagraph +
|
||||
"<br/><br/>"+
|
||||
root.secondParagraph +
|
||||
"</body></html>"
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { id:buttSep; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator }
|
||||
|
||||
|
||||
ButtonRounded {
|
||||
id:buttonOkay
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
onClicked : root.hide()
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
timer.interval : 3000
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
onTriggered: {
|
||||
}
|
||||
}
|
||||
|
||||
onShow : {
|
||||
pmbridge.Accessible.selected = true
|
||||
}
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Change default keychain dialog
|
||||
|
||||
import QtQuick 2.8
|
||||
import BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
import QtQuick.Controls 2.2 as QC
|
||||
import QtQuick.Layouts 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : "Change which keychain Bridge uses as default"
|
||||
subtitle : "Select which keychain is used (Bridge will automatically restart)"
|
||||
isDialogBusy: currentIndex==1
|
||||
|
||||
property var selectedKeychain
|
||||
|
||||
Connections {
|
||||
target: go.selectedKeychain
|
||||
onValueChanged: {
|
||||
console.debug("go.selectedKeychain == ", go.selectedKeychain)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: root.titleHeight + Style.dialog.heightSeparator
|
||||
Layout.maximumHeight: root.titleHeight + Style.dialog.heightSeparator
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
|
||||
Repeater {
|
||||
id: keychainRadioButtons
|
||||
model: go.availableKeychain
|
||||
QC.RadioButton {
|
||||
id: radioDelegate
|
||||
text: modelData
|
||||
checked: go.selectedKeychain === modelData
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.selectedKeychain = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: Style.dialog.heightSeparator
|
||||
Layout.maximumHeight: Style.dialog.heightSeparator
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
spacing: Style.dialog.spacing
|
||||
ButtonRounded {
|
||||
id:buttonNo
|
||||
color_main: Style.dialog.text
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Cancel", "dismisses current action")
|
||||
onClicked : root.hide()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonYes
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
onClicked : root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: root.titleHeight + Style.dialog.heightSeparator
|
||||
Layout.maximumHeight: root.titleHeight + Style.dialog.heightSeparator
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
|
||||
Text {
|
||||
id: answ
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width : parent.width/2
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
text : "Default keychain is now set to " + root.selectedKeychain +
|
||||
"\n\n" +
|
||||
qsTr("Settings will be applied after the next start.", "notification about setting being applied after next start") +
|
||||
"\n\n" +
|
||||
qsTr("Bridge will now restart.", "notification about restarting")
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: root.hide()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Enter"
|
||||
onActivated: root.confirmed()
|
||||
}
|
||||
|
||||
function confirmed() {
|
||||
if (selectedKeychain === go.selectedKeychain) {
|
||||
root.hide()
|
||||
return
|
||||
}
|
||||
|
||||
incrementCurrentIndex()
|
||||
timer.start()
|
||||
}
|
||||
|
||||
timer.interval : 5000
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
onTriggered: {
|
||||
// This action triggers restart on the backend side.
|
||||
go.selectedKeychain = selectedKeychain
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
import QtQuick.Controls 2.2 as QC
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : "Set IMAP & SMTP settings"
|
||||
subtitle : "Changes require reconfiguration of Mail client. (Bridge will automatically restart)"
|
||||
isDialogBusy: currentIndex==1
|
||||
|
||||
Column {
|
||||
id: dialogMessage
|
||||
property int heightInputs : imapPort.height + middleSep.height + smtpPort.height + buttonSep.height + buttonRow.height + secSMTPSep.height + securitySMTP.height
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-dialogMessage.heightInputs)/1.6 }
|
||||
|
||||
InputField {
|
||||
id: imapPort
|
||||
iconText : Style.fa.hashtag
|
||||
label : qsTr("IMAP port", "entry field to choose port used for the IMAP server")
|
||||
text : "undef"
|
||||
}
|
||||
|
||||
Rectangle { id:middleSep; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator }
|
||||
|
||||
InputField {
|
||||
id: smtpPort
|
||||
iconText : Style.fa.hashtag
|
||||
label : qsTr("SMTP port", "entry field to choose port used for the SMTP server")
|
||||
text : "undef"
|
||||
}
|
||||
|
||||
Rectangle { id:secSMTPSep; color : Style.transparent; width : Style.main.dummy; height : Style.dialog.heightSeparator }
|
||||
|
||||
// SSL button group
|
||||
Rectangle {
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
width : Style.dialog.widthInput
|
||||
height : securitySMTPLabel.height + securitySMTP.height
|
||||
color : "transparent"
|
||||
|
||||
AccessibleText {
|
||||
id: securitySMTPLabel
|
||||
anchors.left : parent.left
|
||||
text:qsTr("SMTP connection mode")
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
}
|
||||
|
||||
QC.ButtonGroup {
|
||||
buttons: securitySMTP.children
|
||||
}
|
||||
Row {
|
||||
id: securitySMTP
|
||||
spacing: Style.dialog.spacing
|
||||
anchors.top: securitySMTPLabel.bottom
|
||||
anchors.topMargin: Style.dialog.fontSize
|
||||
|
||||
CheckBoxLabel {
|
||||
id: securitySMTPSSL
|
||||
text: qsTr("SSL")
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
checked: true
|
||||
id: securitySMTPSTARTTLS
|
||||
text: qsTr("STARTTLS")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { id:buttonSep; 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.text
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Cancel", "dismisses current action")
|
||||
onClicked : root.hide()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonYes
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
onClicked : root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-answ.height)/2 }
|
||||
Text {
|
||||
id: answ
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width : parent.width/2
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
text : "IMAP: " + imapPort.text + "\nSMTP: " + smtpPort.text + "\nSMTP Connection Mode: " + getSelectedSSLMode() + "\n\n" +
|
||||
qsTr("Settings will be applied after the next start. You will need to reconfigure your email client(s).", "after user changes their ports they will see this notification to reconfigure their setup") +
|
||||
"\n\n" +
|
||||
qsTr("Bridge will now restart.", "after user changes their ports this appears to notify the user of restart")
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
function areInputsOK() {
|
||||
var isOK = true
|
||||
var imapUnchanged = false
|
||||
var secSMTPUnchanged = (securitySMTPSTARTTLS.checked == go.isSMTPSTARTTLS())
|
||||
root.warning.text = ""
|
||||
|
||||
if (imapPort.text!=go.getIMAPPort()) {
|
||||
if (go.isPortOpen(imapPort.text)!=0) {
|
||||
imapPort.rightIcon = Style.fa.exclamation_triangle
|
||||
root.warning.text = qsTr("Port number is not available.", "if the user changes one of their ports to a port that is occupied by another application")
|
||||
isOK=false
|
||||
} else {
|
||||
imapPort.rightIcon = Style.fa.check_circle
|
||||
}
|
||||
} else {
|
||||
imapPort.rightIcon = ""
|
||||
imapUnchanged = true
|
||||
}
|
||||
|
||||
if (smtpPort.text!=go.getSMTPPort()) {
|
||||
if (go.isPortOpen(smtpPort.text)!=0) {
|
||||
smtpPort.rightIcon = Style.fa.exclamation_triangle
|
||||
root.warning.text = qsTr("Port number is not available.", "if the user changes one of their ports to a port that is occupied by another application")
|
||||
isOK=false
|
||||
} else {
|
||||
smtpPort.rightIcon = Style.fa.check_circle
|
||||
}
|
||||
} else {
|
||||
smtpPort.rightIcon = ""
|
||||
if (imapUnchanged && secSMTPUnchanged) {
|
||||
root.warning.text = qsTr("Please change at least one port number or SMTP security.", "if the user tries to change IMAP/SMTP ports to the same ports as before")
|
||||
isOK=false
|
||||
}
|
||||
}
|
||||
|
||||
if (imapPort.text == smtpPort.text) {
|
||||
smtpPort.rightIcon = Style.fa.exclamation_triangle
|
||||
root.warning.text = qsTr("Port numbers must be different.", "if the user sets both the IMAP and SMTP ports to the same number")
|
||||
isOK=false
|
||||
}
|
||||
|
||||
root.warning.visible = !isOK
|
||||
return isOK
|
||||
}
|
||||
|
||||
function confirmed() {
|
||||
if (areInputsOK()) {
|
||||
incrementCurrentIndex()
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedSSLMode() {
|
||||
if (securitySMTPSTARTTLS.checked == true) {
|
||||
return "STARTTLS"
|
||||
} else {
|
||||
return "SSL"
|
||||
}
|
||||
}
|
||||
|
||||
onShow : {
|
||||
imapPort.text = go.getIMAPPort()
|
||||
smtpPort.text = go.getSMTPPort()
|
||||
if (go.isSMTPSTARTTLS()) {
|
||||
securitySMTPSTARTTLS.checked = true
|
||||
} else {
|
||||
securitySMTPSSL.checked = true
|
||||
}
|
||||
areInputsOK()
|
||||
root.warning.visible = false
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: root.hide()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Enter"
|
||||
onActivated: root.confirmed()
|
||||
}
|
||||
|
||||
timer.interval : 3000
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
onTriggered: {
|
||||
go.setPortsAndSecurity(imapPort.text, smtpPort.text, securitySMTPSTARTTLS.checked)
|
||||
go.setToRestart()
|
||||
Qt.quit()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: qsTr("Connection security error", "Title of modal explainning TLS issue")
|
||||
|
||||
property string par1Title : qsTr("Description:", "Title of paragraph describing the issue")
|
||||
property string par1Text : qsTr (
|
||||
"ProtonMail Bridge was not able to establish a secure connection to Proton servers due to a TLS certificate error. "+
|
||||
"This means your connection may potentially be insecure and susceptible to monitoring by third parties.",
|
||||
"A paragraph describing the issue"
|
||||
)
|
||||
|
||||
property string par2Title : qsTr("Recommendation:", "Title of paragraph describing recommended steps")
|
||||
property string par2Text : qsTr (
|
||||
"If you are on a corporate or public network, the network administrator may be monitoring or intercepting all traffic.",
|
||||
"A paragraph describing network issue"
|
||||
)
|
||||
property string par2ul1 : qsTr(
|
||||
"If you trust your network operator, you can continue to use ProtonMail as usual.",
|
||||
"A list item describing recomendation for trusted network"
|
||||
)
|
||||
|
||||
property string par2ul2 : qsTr(
|
||||
"If you don't trust your network operator, reconnect to ProtonMail over a VPN (such as ProtonVPN) "+
|
||||
"which encrypts your Internet connection, or use a different network to access ProtonMail.",
|
||||
"A list item describing recomendation for untrusted network"
|
||||
)
|
||||
property string par3Text : qsTr("Learn more on our knowledge base article","A paragraph describing where to find more information")
|
||||
property string kbArticleText : qsTr("What is TLS certificate error.", "Link text for knowledge base article")
|
||||
property string kbArticleLink : "https://protonmail.com/support/knowledge-base/"
|
||||
|
||||
|
||||
Item {
|
||||
AccessibleText {
|
||||
anchors.centerIn: parent
|
||||
color: Style.old.pm_white
|
||||
linkColor: color
|
||||
width: parent.width - 50 * Style.px
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: Style.main.fontSize*Style.pt
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
text: "<h3>"+par1Title+"</h3>"+
|
||||
par1Text+"<br>\n"+
|
||||
"<h3>"+par2Title+"</h3>"+
|
||||
par2Text+
|
||||
"<ul>"+
|
||||
"<li>"+par2ul1+"</li>"+
|
||||
"<li>"+par2ul2+"</li>"+
|
||||
"</ul>"+"<br>\n"+
|
||||
""
|
||||
//par3Text+
|
||||
//" <a href='"+kbArticleLink+"'>"+kbArticleText+"</a>\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,403 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 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
|
||||
visible: root.state != "toggleEarlyAccess"
|
||||
color_main: Style.dialog.text
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("No")
|
||||
onClicked : root.hide()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonYes
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
fa_icon: Style.fa.check
|
||||
text: root.state == "toggleEarlyAccess" ? qsTr("Ok") : 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.old.pm_white
|
||||
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 Bridge", "quits the application")
|
||||
question : qsTr("Are you sure you want to close the Bridge?", "asked when user tries to quit the application")
|
||||
note : ""
|
||||
answer : qsTr("Closing Bridge...", "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 Bridge and disconnect you from your email client(s).", "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 as well as cached email data for all accounts, and requires you to reconfigure your client.", "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: "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...", "displayed when the user changes between split and combined address mode")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "toggleAutoStart"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 1
|
||||
question : ""
|
||||
note : ""
|
||||
title : ""
|
||||
answer : {
|
||||
var msgTurnOn = qsTr("Turning on automatic start of Bridge...", "when the automatic start feature is selected")
|
||||
var msgTurnOff = qsTr("Turning off automatic start of Bridge...", "when the automatic start feature is deselected")
|
||||
return go.isAutoStart==false ? msgTurnOff : msgTurnOn
|
||||
}
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "toggleAllowProxy"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
question : {
|
||||
var questionTurnOn = qsTr("Do you want to allow alternative routing?")
|
||||
var questionTurnOff = qsTr("Do you want to disallow alternative routing?")
|
||||
return go.isProxyAllowed==false ? questionTurnOn : questionTurnOff
|
||||
}
|
||||
note : qsTr("In case Proton sites are blocked, this setting allows Bridge to try alternative network routing to reach Proton, which can be useful for bypassing firewalls or network issues. We recommend keeping this setting on for greater reliability.")
|
||||
title : {
|
||||
var titleTurnOn = qsTr("Allow alternative routing")
|
||||
var titleTurnOff = qsTr("Disallow alternative routing")
|
||||
return go.isProxyAllowed==false ? titleTurnOn : titleTurnOff
|
||||
}
|
||||
answer : {
|
||||
var msgTurnOn = qsTr("Allowing Bridge to use alternative routing to connect to Proton...", "when the allow proxy feature is selected")
|
||||
var msgTurnOff = qsTr("Disallowing Bridge to use alternative routing to connect to Proton...", "when the allow proxy feature is deselected")
|
||||
return go.isProxyAllowed==false ? msgTurnOn : msgTurnOff
|
||||
}
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "toggleEarlyAccessOn"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
question : qsTr("Do you want to be the first to get the latest updates? Please keep in mind that early versions may be less stable.")
|
||||
note : ""
|
||||
title : qsTr("Enable early access")
|
||||
answer : qsTr("Enabling early access...")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "toggleEarlyAccessOff"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
question : qsTr("Are you sure you want to leave early access? Please keep in mind this operation clears the cache and restarts Bridge.")
|
||||
note : qsTr("This will delete all of your stored preferences as well as cached email data for all accounts, and requires you to reconfigure your client.")
|
||||
title : qsTr("Disable early access")
|
||||
answer : qsTr("Disabling early access...")
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "noKeychain"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
currentIndex : 0
|
||||
note : qsTr(
|
||||
"%1 is not able to detected a supported password manager (pass, gnome-keyring). Please install and setup supported password manager and restart the application.",
|
||||
"Error message when no keychain is detected"
|
||||
).arg(go.programTitle)
|
||||
question : qsTr("Do you want to close application now?", "when no password manager found." )
|
||||
title : "No system password manager detected"
|
||||
answer : qsTr("Closing Bridge...", "displayed when user is quitting application")
|
||||
}
|
||||
},
|
||||
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.dialogChangePort .visible = false
|
||||
winMain.dialogCredits .visible = false
|
||||
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 == "toggleAllowProxy" ) { go.toggleAllowProxy () }
|
||||
if ( state == "toggleEarlyAccessOn" ) { go.toggleEarlyAccess () }
|
||||
if ( state == "toggleEarlyAccessOff" ) { go.toggleEarlyAccess () }
|
||||
if ( state == "quit" ) { Qt.quit () }
|
||||
if ( state == "instance exists" ) { Qt.quit () }
|
||||
if ( state == "noKeychain" ) { Qt.quit () }
|
||||
if ( state == "checkUpdates" ) { }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Enter) {
|
||||
root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 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: logs
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Logs", "title of button that takes user to logs directory")
|
||||
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", "title of button that takes user to bug report form")
|
||||
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: manual
|
||||
anchors.left: parent.left
|
||||
text: qsTr("Setup Guide", "title of button that opens setup and installation 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", "title of button to check for any app updates")
|
||||
leftIcon.text : Style.fa.refresh
|
||||
rightIcon.text : Style.fa.chevron_circle_right
|
||||
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
onClicked: {
|
||||
go.checkForUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom version notes
|
||||
Rectangle {
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
height: viewAccount.separatorNoAccount - 3.2*manual.height
|
||||
width: wrapper.width
|
||||
color : "transparent"
|
||||
AccessibleText {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
color: Style.main.textDisabled
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
text:
|
||||
"ProtonMail Bridge "+go.getBackendVersion()+"\n"+
|
||||
"© 2021 Proton Technologies AG"
|
||||
}
|
||||
}
|
||||
Row {
|
||||
anchors.left : parent.left
|
||||
|
||||
Rectangle { height: Style.dialog.spacing; width: (wrapper.width - credits.width - licenseFile.width - release.width - sepaCreditsRelease.width)/2; color: "transparent"}
|
||||
|
||||
ClickIconText {
|
||||
id:credits
|
||||
iconText : ""
|
||||
text : qsTr("Credits", "link to click on to view list of credited libraries")
|
||||
textColor : Style.main.textDisabled
|
||||
fontSize : Style.main.fontSize
|
||||
textUnderline : true
|
||||
onClicked : winMain.dialogCredits.show()
|
||||
}
|
||||
|
||||
Rectangle {id: sepaLicenseFile ; height: Style.dialog.spacing; width: Style.main.dummy; color: "transparent"}
|
||||
|
||||
ClickIconText {
|
||||
id:licenseFile
|
||||
iconText : ""
|
||||
text : qsTr("License", "link to click on to view license file")
|
||||
textColor : Style.main.textDisabled
|
||||
fontSize : Style.main.fontSize
|
||||
textUnderline : true
|
||||
onClicked : {
|
||||
go.openLicenseFile()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {id: sepaCreditsRelease ; height: Style.dialog.spacing; width: Style.main.dummy; color: "transparent"}
|
||||
|
||||
ClickIconText {
|
||||
id:release
|
||||
iconText : ""
|
||||
text : qsTr("Release notes", "link to click on to view release notes for this version of the app")
|
||||
textColor : Style.main.textDisabled
|
||||
fontSize : Style.main.fontSize
|
||||
textUnderline : true
|
||||
onClicked : gui.openReleaseNotes()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Window for imap and smtp settings
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Window {
|
||||
id:root
|
||||
width : Style.info.width
|
||||
height : Style.info.height
|
||||
minimumWidth : Style.info.width
|
||||
minimumHeight : Style.info.height
|
||||
maximumWidth : Style.info.width
|
||||
maximumHeight : Style.info.height
|
||||
color: "transparent"
|
||||
flags : Qt.Window | Qt.Dialog | Qt.FramelessWindowHint
|
||||
title : address
|
||||
|
||||
Accessible.role: Accessible.Window
|
||||
Accessible.name: qsTr("Configuration information for %1").arg(address)
|
||||
Accessible.description: Accessible.name
|
||||
|
||||
property QtObject accData : QtObject { // avoid null-pointer error
|
||||
property string account : "undef"
|
||||
property string aliases : "undef"
|
||||
property string hostname : "undef"
|
||||
property string password : "undef"
|
||||
property int portIMAP : 0
|
||||
property int portSMTP : 0
|
||||
}
|
||||
property string address : "undef"
|
||||
property int indexAccount : 0
|
||||
property int indexAddress : 0
|
||||
|
||||
WindowTitleBar {
|
||||
id: titleBar
|
||||
window: root
|
||||
}
|
||||
|
||||
Rectangle { // background
|
||||
color: Style.main.background
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : titleBar.bottom
|
||||
bottom : parent.bottom
|
||||
}
|
||||
border {
|
||||
width: Style.main.border
|
||||
color: Style.tabbar.background
|
||||
}
|
||||
}
|
||||
|
||||
// info content
|
||||
Column {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: titleBar.bottom
|
||||
leftMargin: Style.main.leftMargin
|
||||
topMargin: Style.info.topMargin
|
||||
}
|
||||
width : root.width - Style.main.leftMargin - Style.main.rightMargin
|
||||
|
||||
TextLabel { text: qsTr("IMAP SETTINGS", "title of the portion of the configuration screen that contains IMAP settings"); state: "heading" }
|
||||
Rectangle { width: parent.width; height: Style.info.topMargin; color: "#00000000"}
|
||||
Grid {
|
||||
columns: 2
|
||||
rowSpacing: Style.main.fontSize
|
||||
TextLabel { text: qsTr("Hostname", "in configuration screen, displays the server hostname (127.0.0.1)") + ":"} TextValue { text: root.accData.hostname }
|
||||
TextLabel { text: qsTr("Port", "in configuration screen, displays the server port (ex. 1025)") + ":"} TextValue { text: root.accData.portIMAP }
|
||||
TextLabel { text: qsTr("Username", "in configuration screen, displays the username to use with the desktop client") + ":"} TextValue { text: root.address }
|
||||
TextLabel { text: qsTr("Password", "in configuration screen, displays the Bridge password to use with the desktop client") + ":"} TextValue { text: root.accData.password }
|
||||
TextLabel { text: qsTr("Security", "in configuration screen, displays the IMAP security settings") + ":"} TextValue { text: "STARTTLS" }
|
||||
}
|
||||
Rectangle { width: Style.main.dummy; height: Style.main.fontSize; color: "#00000000"}
|
||||
Rectangle { width: Style.main.dummy; height: Style.info.topMargin; color: "#00000000"}
|
||||
|
||||
TextLabel { text: qsTr("SMTP SETTINGS", "title of the portion of the configuration screen that contains SMTP settings"); state: "heading" }
|
||||
Rectangle { width: Style.main.dummy; height: Style.info.topMargin; color: "#00000000"}
|
||||
Grid {
|
||||
columns: 2
|
||||
rowSpacing: Style.main.fontSize
|
||||
TextLabel { text: qsTr("Hostname", "in configuration screen, displays the server hostname (127.0.0.1)") + ":"} TextValue { text: root.accData.hostname }
|
||||
TextLabel { text: qsTr("Port", "in configuration screen, displays the server port (ex. 1025)") + ":"} TextValue { text: root.accData.portSMTP }
|
||||
TextLabel { text: qsTr("Username", "in configuration screen, displays the username to use with the desktop client") + ":"} TextValue { text: root.address }
|
||||
TextLabel { text: qsTr("Password", "in configuration screen, displays the Bridge password to use with the desktop client") + ":"} TextValue { text: root.accData.password }
|
||||
TextLabel { text: qsTr("Security", "in configuration screen, displays the SMTP security settings") + ":"} TextValue { text: go.isSMTPSTARTTLS() ? "STARTTLS" : "SSL" }
|
||||
}
|
||||
Rectangle { width: Style.main.dummy; height: Style.main.fontSize; color: "#00000000"}
|
||||
Rectangle { width: Style.main.dummy; height: Style.info.topMargin; color: "#00000000"}
|
||||
}
|
||||
|
||||
// apple mail button
|
||||
ButtonRounded{
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Style.info.topMargin
|
||||
}
|
||||
color_main : Style.main.textBlue
|
||||
isOpaque: false
|
||||
text: qsTr("Configure Apple Mail", "button on configuration screen to automatically configure Apple Mail")
|
||||
height: Style.main.fontSize*2
|
||||
width: 2*parent.width/3
|
||||
onClicked: {
|
||||
go.configureAppleMail(root.indexAccount, root.indexAddress)
|
||||
}
|
||||
visible: go.goos == "darwin"
|
||||
}
|
||||
|
||||
|
||||
function showInfo(iAccount, iAddress) {
|
||||
root.indexAccount = iAccount
|
||||
root.indexAddress = iAddress
|
||||
root.accData = accountsModel.get(iAccount)
|
||||
root.address = accData.aliases.split(";")[iAddress]
|
||||
root.show()
|
||||
root.raise()
|
||||
root.requestActivate()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
root.visible = false
|
||||
}
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 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 dialogChangePort : dialogChangePort
|
||||
property alias dialogCredits : dialogCredits
|
||||
property alias dialogTlsCert : dialogTlsCert
|
||||
property alias dialogUpdate : dialogUpdate
|
||||
property alias dialogFirstStart : dialogFirstStart
|
||||
property alias dialogGlobal : dialogGlobal
|
||||
property alias dialogConnectionTroubleshoot : dialogConnectionTroubleshoot
|
||||
property alias bubbleNote : bubbleNote
|
||||
property alias addAccountTip : addAccountTip
|
||||
property alias updateState : infoBar.state
|
||||
property alias tlsBarState : tlsBar.state
|
||||
property int heightContent : height-titleBar.height
|
||||
|
||||
// main window appeareance
|
||||
width : Style.main.width
|
||||
height : Style.main.height
|
||||
flags : Qt.Window | Qt.FramelessWindowHint
|
||||
color: go.goos=="windows" ? "black" : "transparent"
|
||||
title: go.programTitle
|
||||
minimumWidth: Style.main.width
|
||||
minimumHeight: Style.main.height
|
||||
maximumWidth: Style.main.width
|
||||
|
||||
property bool isOutdateVersion : root.updateState == "forceUpdate"
|
||||
|
||||
property bool activeContent :
|
||||
!dialogAddUser .visible &&
|
||||
!dialogChangePort .visible &&
|
||||
!dialogCredits .visible &&
|
||||
!dialogTlsCert .visible &&
|
||||
!dialogUpdate .visible &&
|
||||
!dialogFirstStart .visible &&
|
||||
!dialogGlobal .visible &&
|
||||
!bubbleNote .visible
|
||||
|
||||
Accessible.role: Accessible.Grouping
|
||||
Accessible.description: qsTr("Window %1").arg(title)
|
||||
Accessible.name: Accessible.description
|
||||
|
||||
|
||||
Component.onCompleted : {
|
||||
gui.winMain = root
|
||||
console.log("GraphicsInfo of", titleBar,
|
||||
"api" , titleBar.GraphicsInfo.api ,
|
||||
"majorVersion" , titleBar.GraphicsInfo.majorVersion ,
|
||||
"minorVersion" , titleBar.GraphicsInfo.minorVersion ,
|
||||
"profile" , titleBar.GraphicsInfo.profile ,
|
||||
"renderableType" , titleBar.GraphicsInfo.renderableType ,
|
||||
"shaderCompilationType" , titleBar.GraphicsInfo.shaderCompilationType ,
|
||||
"shaderSourceType" , titleBar.GraphicsInfo.shaderSourceType ,
|
||||
"shaderType" , titleBar.GraphicsInfo.shaderType)
|
||||
|
||||
tabbar.focusButton()
|
||||
}
|
||||
|
||||
WindowTitleBar {
|
||||
id: titleBar
|
||||
window: root
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top : titleBar.bottom
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
}
|
||||
color: Style.title.background
|
||||
}
|
||||
|
||||
TLSCertPinIssueBar {
|
||||
id: tlsBar
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : titleBar.bottom
|
||||
leftMargin: Style.main.border
|
||||
rightMargin: Style.main.border
|
||||
}
|
||||
enabled : root.activeContent
|
||||
}
|
||||
|
||||
InformationBar {
|
||||
id: infoBar
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : tlsBar.bottom
|
||||
leftMargin: Style.main.border
|
||||
rightMargin: Style.main.border
|
||||
}
|
||||
enabled : root.activeContent
|
||||
}
|
||||
|
||||
|
||||
TabLabels {
|
||||
id: tabbar
|
||||
currentIndex : 0
|
||||
enabled: root.activeContent
|
||||
anchors {
|
||||
top : infoBar.bottom
|
||||
right : parent.right
|
||||
left : parent.left
|
||||
leftMargin: Style.main.border
|
||||
rightMargin: Style.main.border
|
||||
}
|
||||
model: [
|
||||
{ "title" : qsTr("Accounts" , "title of tab that shows account list" ), "iconText": Style.fa.user_circle_o },
|
||||
{ "title" : qsTr("Settings" , "title of tab that allows user to change settings" ), "iconText": Style.fa.cog },
|
||||
{ "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: Style.main.border
|
||||
rightMargin: Style.main.border
|
||||
bottomMargin: Style.main.border
|
||||
}
|
||||
// attributes
|
||||
currentIndex : { return root.tabbar.currentIndex}
|
||||
clip : true
|
||||
// content
|
||||
AccountView {
|
||||
id: viewAccount
|
||||
onAddAccount: dialogAddUser.show()
|
||||
model: accountsModel
|
||||
delegate: AccountDelegate {
|
||||
row_width: viewContent.width
|
||||
}
|
||||
}
|
||||
|
||||
SettingsView { id: viewSettings; }
|
||||
HelpView { id: viewHelp; }
|
||||
}
|
||||
|
||||
|
||||
// Floating things
|
||||
|
||||
// Triangle
|
||||
Rectangle {
|
||||
id: tabtriangle
|
||||
visible: false
|
||||
property int margin : Style.main.leftMargin+ Style.tabbar.widthButton/2
|
||||
anchors {
|
||||
top : tabbar.bottom
|
||||
left : tabbar.left
|
||||
leftMargin : tabtriangle.margin - tabtriangle.width/2 + tabbar.currentIndex * tabbar.spacing
|
||||
}
|
||||
width: 2*Style.tabbar.heightTriangle
|
||||
height: Style.tabbar.heightTriangle
|
||||
color: "transparent"
|
||||
Canvas {
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.fillStyle = Style.tabbar.background
|
||||
ctx.moveTo(0 , 0)
|
||||
ctx.lineTo(width/2, height)
|
||||
ctx.lineTo(width , 0)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
DialogFirstStart {
|
||||
id: dialogFirstStart
|
||||
visible: go.isFirstStart && gui.isFirstWindow && !dialogGlobal.visible
|
||||
}
|
||||
|
||||
// Dialogs
|
||||
DialogPortChange {
|
||||
id: dialogChangePort
|
||||
}
|
||||
|
||||
DialogKeychainChange {
|
||||
id: dialogChangeKeychain
|
||||
}
|
||||
|
||||
DialogConnectionTroubleshoot {
|
||||
id: dialogConnectionTroubleshoot
|
||||
}
|
||||
|
||||
DialogAddUser {
|
||||
id: dialogAddUser
|
||||
onCreateAccount: Qt.openUrlExternally("https://protonmail.com/signup")
|
||||
}
|
||||
|
||||
DialogUpdate {
|
||||
id: dialogUpdate
|
||||
forceUpdate: root.isOutdateVersion
|
||||
}
|
||||
|
||||
|
||||
Dialog {
|
||||
id: dialogCredits
|
||||
title: qsTr("Credits", "link to click on to view list of credited libraries")
|
||||
Credits { }
|
||||
}
|
||||
|
||||
DialogTLSCertInfo {
|
||||
id: dialogTlsCert
|
||||
}
|
||||
|
||||
DialogYesNo {
|
||||
id: dialogGlobal
|
||||
question : ""
|
||||
answer : ""
|
||||
z: 100
|
||||
}
|
||||
|
||||
|
||||
// resize
|
||||
MouseArea {
|
||||
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)
|
||||
diff = root.height
|
||||
diff -= globPos.y
|
||||
}
|
||||
onMouseYChanged : {
|
||||
var globPos = mapToGlobal(mouse.x, mouse.y)
|
||||
root.height = Math.max(root.minimumHeight, globPos.y + 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
|
||||
// NOTE: In order to make an initial accounts load
|
||||
root.hide()
|
||||
gui.closeMainWindow()
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
Window {
|
||||
id:root
|
||||
width : Style.info.width
|
||||
height : Style.info.width/1.5
|
||||
minimumWidth : Style.info.width
|
||||
minimumHeight : Style.info.width/1.5
|
||||
maximumWidth : Style.info.width
|
||||
maximumHeight : Style.info.width/1.5
|
||||
color : Style.main.background
|
||||
flags : Qt.Window | Qt.Popup | Qt.FramelessWindowHint
|
||||
visible : false
|
||||
title : ""
|
||||
x: 10
|
||||
y: 10
|
||||
property string messageID: ""
|
||||
|
||||
// Drag and move
|
||||
MouseArea {
|
||||
property point diff: "0,0"
|
||||
property QtObject window: root
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
diff = Qt.point(window.x, window.y)
|
||||
var mousePos = mapToGlobal(mouse.x, mouse.y)
|
||||
diff.x -= mousePos.x
|
||||
diff.y -= mousePos.y
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
var currPos = mapToGlobal(mouse.x, mouse.y)
|
||||
window.x = currPos.x + diff.x
|
||||
window.y = currPos.y + diff.y
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
topPadding: Style.main.fontSize
|
||||
spacing: (root.height - (description.height + cancel.height + countDown.height + Style.main.fontSize))/3
|
||||
width: root.width
|
||||
|
||||
Text {
|
||||
id: description
|
||||
color : Style.main.text
|
||||
font.pointSize : Style.main.fontSize*Style.pt/1.2
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
horizontalAlignment : Text.AlignHCenter
|
||||
width : root.width - 2*Style.main.leftMargin
|
||||
wrapMode : Text.Wrap
|
||||
textFormat : Text.RichText
|
||||
|
||||
text: qsTr("The message with subject %1 has one or more recipients with no encryption settings. If you do not want to send this email click the cancel button.").arg("<h3>"+root.title+"</h3>")
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing : Style.dialog.spacing
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
ButtonRounded {
|
||||
id: sendAnyway
|
||||
onClicked : root.hide(true)
|
||||
height: Style.main.fontSize*2
|
||||
//width: Style.dialog.widthButton*1.3
|
||||
fa_icon: Style.fa.send
|
||||
text: qsTr("Send now", "Confirmation of sending unencrypted email.")
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: cancel
|
||||
onClicked : root.hide(false)
|
||||
height: Style.main.fontSize*2
|
||||
//width: Style.dialog.widthButton*1.3
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Cancel", "Cancel the sending of current email")
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: countDown
|
||||
color: Style.main.text
|
||||
font.pointSize : Style.main.fontSize*Style.pt/1.2
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
horizontalAlignment : Text.AlignHCenter
|
||||
width : root.width - 2*Style.main.leftMargin
|
||||
wrapMode : Text.Wrap
|
||||
textFormat : Text.RichText
|
||||
|
||||
text: qsTr("This popup will close after %1 and email will be sent unless you click the cancel button.").arg( "<b>" + timer.secLeft + "s</b>")
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
property var secLeft: 0
|
||||
interval: 1000 //ms
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
secLeft--
|
||||
if (secLeft <= 0) {
|
||||
root.hide(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hide(shouldSend) {
|
||||
root.visible = false
|
||||
timer.stop()
|
||||
go.saveOutgoingNoEncPopupCoord(root.x, root.y)
|
||||
go.shouldSendAnswer(root.messageID, shouldSend)
|
||||
}
|
||||
|
||||
function show(messageID, subject) {
|
||||
root.messageID = messageID
|
||||
root.title = subject
|
||||
root.visible = true
|
||||
timer.secLeft = 10
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
// Copyright (c) 2021 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 BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
import QtQuick.Controls 2.4
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// must have wrapper
|
||||
ScrollView {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
clip: true
|
||||
background: Rectangle {
|
||||
color: Style.main.background
|
||||
}
|
||||
|
||||
// horizontall scrollbar sometimes showes up when vertical scrollbar coveres content
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
// keeping vertical scrollbar allways visible when needed
|
||||
Connections {
|
||||
target: wrapper.ScrollBar.vertical
|
||||
onSizeChanged: {
|
||||
// ScrollBar.size == 0 at creating so no need to make it active
|
||||
if (wrapper.ScrollBar.vertical.size < 1.0 && wrapper.ScrollBar.vertical.size > 0 && !wrapper.ScrollBar.vertical.active) {
|
||||
wrapper.ScrollBar.vertical.active = true
|
||||
}
|
||||
}
|
||||
onActiveChanged: {
|
||||
wrapper.ScrollBar.vertical.active = true
|
||||
}
|
||||
}
|
||||
|
||||
// content
|
||||
Column {
|
||||
anchors.left : parent.left
|
||||
|
||||
ButtonIconText {
|
||||
id: cacheClear
|
||||
text: qsTr("Clear Cache", "button to clear cache in settings")
|
||||
leftIcon.text : Style.fa.times
|
||||
rightIcon {
|
||||
text : qsTr("Clear", "clickable link next to clear cache button in settings")
|
||||
color: Style.main.text
|
||||
font {
|
||||
family : cacheClear.font.family // use default font, not font-awesome
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogGlobal.state="clearCache"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: cacheKeychain
|
||||
text: qsTr("Clear Keychain", "button to clear keychain in settings")
|
||||
leftIcon.text : Style.fa.chain_broken
|
||||
rightIcon {
|
||||
text : qsTr("Clear", "clickable link next to clear keychain button in settings")
|
||||
color: Style.main.text
|
||||
font {
|
||||
family : cacheKeychain.font.family // use default font, not font-awesome
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogGlobal.state="clearChain"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: autoStart
|
||||
text: qsTr("Automatically start Bridge", "label for toggle that activates and disables the automatic start")
|
||||
leftIcon.text : Style.fa.rocket
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isAutoStart!=false ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isAutoStart!=false ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isAutoStart == false ?
|
||||
qsTr("Enable" , "Click to enable the automatic start of Bridge") :
|
||||
qsTr("Disable" , "Click to disable the automatic start of Bridge")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
go.toggleAutoStart()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: autoUpdates
|
||||
text: qsTr("Keep the application up to date", "label for toggle that activates and disables the automatic updates")
|
||||
leftIcon.text : Style.fa.download
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isAutoUpdate!=false ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isAutoUpdate!=false ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isAutoUpdate == false ?
|
||||
qsTr("Enable" , "Click to enable the automatic update of Bridge") :
|
||||
qsTr("Disable" , "Click to disable the automatic update of Bridge")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
go.toggleAutoUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: earlyAccess
|
||||
text: qsTr("Early access", "label for toggle that enables and disables early access")
|
||||
leftIcon.text : Style.fa.star
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isEarlyAccess!=false ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isEarlyAccess!=false ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isEarlyAccess == false ?
|
||||
qsTr("Enable" , "Click to enable early access") :
|
||||
qsTr("Disable" , "Click to disable early access")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
if (go.isEarlyAccess == true) {
|
||||
dialogGlobal.state="toggleEarlyAccessOff"
|
||||
dialogGlobal.show()
|
||||
} else {
|
||||
dialogGlobal.state="toggleEarlyAccessOn"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: advancedSettings
|
||||
property bool isAdvanced : !go.isDefaultPort
|
||||
text: qsTr("Advanced settings", "button to open the advanced settings list in the settings page")
|
||||
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
|
||||
}
|
||||
|
||||
Accessible.description: (
|
||||
isAdvanced ?
|
||||
qsTr("Hide", "Click to hide the advance settings") :
|
||||
qsTr("Show", "Click to show the advance settings")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
isAdvanced = !isAdvanced
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: changePort
|
||||
visible: advancedSettings.isAdvanced
|
||||
text: qsTr("Change IMAP & SMTP settings", "button to change IMAP and SMTP ports in settings")
|
||||
leftIcon.text : Style.fa.plug
|
||||
rightIcon {
|
||||
text : qsTr("Change", "clickable link next to change ports button in settings")
|
||||
color: Style.main.text
|
||||
font {
|
||||
family : changePort.font.family // use default font, not font-awesome
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogChangePort.show()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: reportNoEnc
|
||||
text: qsTr("Notification of outgoing email without encryption", "Button to set whether to report or send an email without encryption")
|
||||
visible: advancedSettings.isAdvanced
|
||||
leftIcon.text : Style.fa.ban
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isReportingOutgoingNoEnc ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isReportingOutgoingNoEnc ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isReportingOutgoingNoEnc == 0 ?
|
||||
qsTr("Enable" , "Click to report an email without encryption") :
|
||||
qsTr("Disable" , "Click to send without asking an email without encryption")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
go.toggleIsReportingOutgoingNoEnc()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: allowProxy
|
||||
visible: advancedSettings.isAdvanced
|
||||
text: qsTr("Allow alternative routing", "label for toggle that allows and disallows using a proxy")
|
||||
leftIcon.text : Style.fa.rocket
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isProxyAllowed!=false ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isProxyAllowed!=false ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isProxyAllowed == false ?
|
||||
qsTr("Enable" , "Click to allow alternative routing") :
|
||||
qsTr("Disable" , "Click to disallow alternative routing")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
dialogGlobal.state="toggleAllowProxy"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIconText {
|
||||
id: changeKeychain
|
||||
visible: advancedSettings.isAdvanced && (go.availableKeychain.length > 1)
|
||||
text: qsTr("Change keychain", "button to open dialog with default keychain selection")
|
||||
leftIcon.text : Style.fa.key
|
||||
rightIcon {
|
||||
text : qsTr("Change", "clickable link next to change keychain button in settings")
|
||||
color: Style.main.text
|
||||
font {
|
||||
family : changeKeychain.font.family // use default font, not font-awesome
|
||||
pointSize : Style.settings.fontSize * Style.pt
|
||||
underline : true
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dialogChangeKeychain.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
|
@ -1,15 +0,0 @@
|
|||
module BridgeUI
|
||||
AccountDelegate 1.0 AccountDelegate.qml
|
||||
Credits 1.0 Credits.qml
|
||||
DialogFirstStart 1.0 DialogFirstStart.qml
|
||||
DialogKeychainChange 1.0 DialogKeychainChange.qml
|
||||
DialogPortChange 1.0 DialogPortChange.qml
|
||||
DialogYesNo 1.0 DialogYesNo.qml
|
||||
DialogTLSCertInfo 1.0 DialogTLSCertInfo.qml
|
||||
HelpView 1.0 HelpView.qml
|
||||
InfoWindow 1.0 InfoWindow.qml
|
||||
MainWindow 1.0 MainWindow.qml
|
||||
ManualWindow 1.0 ManualWindow.qml
|
||||
OutgoingNoEncPopup 1.0 OutgoingNoEncPopup.qml
|
||||
SettingsView 1.0 SettingsView.qml
|
||||
StatusFooter 1.0 StatusFooter.qml
|
|
@ -1,378 +0,0 @@
|
|||
// Copyright (c) 2021 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 qml file
|
||||
|
||||
import QtQuick 2.8
|
||||
import BridgeUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
// All imports from dynamic must be loaded before
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Item {
|
||||
id: gui
|
||||
property MainWindow winMain
|
||||
property bool isFirstWindow: true
|
||||
property int warningFlags: 0
|
||||
|
||||
InfoWindow { id: infoWin }
|
||||
OutgoingNoEncPopup { id: outgoingNoEncPopup }
|
||||
BugReportWindow {
|
||||
id: bugreportWin
|
||||
clientVersion.visible : true
|
||||
|
||||
// pre-fill the form
|
||||
onPrefill : {
|
||||
userAddress.text=""
|
||||
if (accountsModel.count>0) {
|
||||
var addressList = accountsModel.get(0).aliases.split(";")
|
||||
if (addressList.length>0) {
|
||||
userAddress.text = addressList[0]
|
||||
}
|
||||
}
|
||||
clientVersion.text=go.getLastMailClient()
|
||||
}
|
||||
}
|
||||
|
||||
onWarningFlagsChanged : {
|
||||
if (gui.warningFlags==Style.okInfoBar) {
|
||||
go.normalSystray()
|
||||
return
|
||||
}
|
||||
|
||||
if ((gui.warningFlags & Style.errorInfoBar) == Style.errorInfoBar) {
|
||||
go.errorSystray()
|
||||
return
|
||||
}
|
||||
|
||||
go.highlightSystray()
|
||||
}
|
||||
|
||||
// Signals from Go
|
||||
Connections {
|
||||
target: go
|
||||
|
||||
onShowWindow : {
|
||||
gui.openMainWindow()
|
||||
}
|
||||
onShowHelp : {
|
||||
gui.openMainWindow(false)
|
||||
winMain.tabbar.currentIndex = 2
|
||||
winMain.showAndRise()
|
||||
}
|
||||
onShowQuit : {
|
||||
gui.openMainWindow(false)
|
||||
winMain.dialogGlobal.state="quit"
|
||||
winMain.dialogGlobal.show()
|
||||
winMain.showAndRise()
|
||||
}
|
||||
|
||||
onProcessFinished : {
|
||||
winMain.dialogGlobal.hide()
|
||||
winMain.dialogAddUser.hide()
|
||||
winMain.dialogChangePort.hide()
|
||||
infoWin.hide()
|
||||
}
|
||||
onOpenManual : Qt.openUrlExternally("http://protonmail.com/bridge")
|
||||
|
||||
onNotifyBubble : {
|
||||
gui.showBubble(tabIndex, message, true)
|
||||
}
|
||||
onSilentBubble : {
|
||||
gui.showBubble(tabIndex, message, false)
|
||||
}
|
||||
onBubbleClosed : {
|
||||
gui.warningFlags &= ~Style.warnBubbleMessage
|
||||
}
|
||||
|
||||
onSetConnectionStatus: {
|
||||
go.isConnectionOK = isAvailable
|
||||
gui.openMainWindow(false)
|
||||
if (go.isConnectionOK) {
|
||||
if( winMain.updateState=="noInternet") {
|
||||
go.updateState = "upToDate"
|
||||
}
|
||||
} else {
|
||||
go.updateState = "noInternet"
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateStateChanged : {
|
||||
// Update tray icon if needed
|
||||
switch (go.updateState) {
|
||||
case "internetCheck":
|
||||
break;
|
||||
case "noInternet" :
|
||||
gui.warningFlags |= Style.warnInfoBar
|
||||
break;
|
||||
case "oldVersion":
|
||||
gui.warningFlags |= Style.warnInfoBar
|
||||
break;
|
||||
case "forceUpdate":
|
||||
// Force update should presist once it happened and never be overwritten.
|
||||
// That means that tray icon should allways remain in error state.
|
||||
// But since we have only two sources of error icon in tray (force update
|
||||
// + installation fail) and both are unrecoverable and we do not ever remove
|
||||
// error flag from gui.warningFlags - it is ok to rely on gui.warningFlags and
|
||||
// not on winMain.updateState (which presist forceUpdate)
|
||||
gui.warningFlags |= Style.errorInfoBar
|
||||
break;
|
||||
case "upToDate":
|
||||
gui.warningFlags &= ~Style.warnInfoBar
|
||||
break;
|
||||
case "updateRestart":
|
||||
gui.warningFlags |= Style.warnInfoBar
|
||||
break;
|
||||
case "updateError":
|
||||
gui.warningFlags |= Style.errorInfoBar
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
// if main window is closed - most probably it is destroyed (see closeMainWindow())
|
||||
if (winMain == null) {
|
||||
return
|
||||
}
|
||||
// once app is outdated prevent from state change
|
||||
if (winMain.updateState != "forceUpdate") {
|
||||
winMain.updateState = go.updateState
|
||||
}
|
||||
}
|
||||
|
||||
onSetAddAccountWarning : winMain.dialogAddUser.setWarning(message, 0)
|
||||
|
||||
|
||||
onNotifyVersionIsTheLatest : {
|
||||
go.silentBubble(2,qsTr("You have the latest version!", "notification", -1))
|
||||
}
|
||||
|
||||
onNotifyManualUpdate: {
|
||||
go.updateState = "oldVersion"
|
||||
}
|
||||
|
||||
onNotifyManualUpdateRestartNeeded: {
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
go.updateState = "updateRestart"
|
||||
winMain.dialogUpdate.finished(false)
|
||||
|
||||
// after manual update - just retart immidiatly
|
||||
go.setToRestart()
|
||||
Qt.quit()
|
||||
}
|
||||
|
||||
onNotifyManualUpdateError: {
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
go.updateState = "updateError"
|
||||
winMain.dialogUpdate.finished(true)
|
||||
}
|
||||
|
||||
onNotifyForceUpdate : {
|
||||
go.updateState = "forceUpdate"
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
}
|
||||
|
||||
onNotifySilentUpdateRestartNeeded: {
|
||||
go.updateState = "updateRestart"
|
||||
}
|
||||
|
||||
onNotifySilentUpdateError: {
|
||||
go.updateState = "updateError"
|
||||
}
|
||||
|
||||
onNotifyLogout : {
|
||||
go.notifyBubble(0, qsTr("Account %1 has been disconnected. Please log in to continue to use the Bridge with this account.").arg(accname) )
|
||||
}
|
||||
|
||||
onNotifyAddressChanged : {
|
||||
go.notifyBubble(0, qsTr("The address list has been changed for account %1. You may need to reconfigure the settings in your email client.").arg(accname) )
|
||||
}
|
||||
|
||||
onNotifyAddressChangedLogout : {
|
||||
go.notifyBubble(0, qsTr("The address list has been changed for account %1. You have to reconfigure the settings in your email client.").arg(accname) )
|
||||
}
|
||||
|
||||
onNotifyPortIssue : { // busyPortIMAP , busyPortSMTP
|
||||
if (!busyPortIMAP && !busyPortSMTP) { // at least one must have issues to show warning
|
||||
return
|
||||
}
|
||||
gui.openMainWindow(false)
|
||||
winMain.tabbar.currentIndex=1
|
||||
go.isDefaultPort = false
|
||||
var text
|
||||
if (busyPortIMAP && busyPortSMTP) { // both have problems
|
||||
text = qsTr("The default ports used by Bridge for IMAP (%1) and SMTP (%2) are occupied by one or more other applications." , "the first part of notification text (two ports)").arg(go.getIMAPPort()).arg(go.getSMTPPort())
|
||||
text += " "
|
||||
text += qsTr("To change the ports for these servers, go to Settings -> Advanced Settings.", "the second part of notification text (two ports)")
|
||||
} else { // only one is occupied
|
||||
var server, port
|
||||
if (busyPortSMTP) {
|
||||
server = "SMTP"
|
||||
port = go.getSMTPPort()
|
||||
} else {
|
||||
server = "IMAP"
|
||||
port = go.getIMAPPort()
|
||||
}
|
||||
text = qsTr("The default port used by Bridge for %1 (%2) is occupied by another application.", "the first part of notification text (one port)").arg(server).arg(port)
|
||||
text += " "
|
||||
text += qsTr("To change the port for this server, go to Settings -> Advanced Settings.", "the second part of notification text (one port)")
|
||||
}
|
||||
go.notifyBubble(1, text )
|
||||
}
|
||||
|
||||
onNotifyKeychainRebuild : {
|
||||
go.notifyBubble(1, qsTr(
|
||||
"Your MacOS keychain is probably corrupted. Please consult the instructions in our <a href=\"https://protonmail.com/bridge/faq#c15\">FAQ</a>.",
|
||||
"notification message"
|
||||
))
|
||||
}
|
||||
|
||||
onNotifyHasNoKeychain : {
|
||||
gui.winMain.dialogGlobal.state="noKeychain"
|
||||
gui.winMain.dialogGlobal.show()
|
||||
}
|
||||
|
||||
onShowNoActiveKeyForRecipient : {
|
||||
go.notifyBubble(0, qsTr(
|
||||
"Key pinning is enabled for %1 but no active key is pinned. " +
|
||||
"You must pin the key in order to send a message to this address. " +
|
||||
"You can find instructions " +
|
||||
"<a href=\"https://protonmail.com/support/knowledge-base/key-pinning/\">here</a>."
|
||||
).arg(recipient))
|
||||
}
|
||||
|
||||
onFailedAutostartCode : {
|
||||
gui.openMainWindow(true)
|
||||
switch (code) {
|
||||
case "permission" : // linux+darwin
|
||||
case "85070005" : // windows
|
||||
go.notifyBubble(1, go.failedAutostartPerm)
|
||||
break
|
||||
case "81004003" : // windows
|
||||
go.notifyBubble(1, go.failedAutostart+" "+qsTr("Can not create instance.", "for autostart"))
|
||||
break
|
||||
case "" :
|
||||
default :
|
||||
go.notifyBubble(1, go.failedAutostart)
|
||||
}
|
||||
}
|
||||
|
||||
onShowOutgoingNoEncPopup : {
|
||||
outgoingNoEncPopup.show(messageID, subject)
|
||||
}
|
||||
|
||||
onSetOutgoingNoEncPopupCoord : {
|
||||
outgoingNoEncPopup.x = x
|
||||
outgoingNoEncPopup.y = y
|
||||
}
|
||||
|
||||
onShowCertIssue : {
|
||||
winMain.tlsBarState="notOK"
|
||||
}
|
||||
|
||||
onOpenReleaseNotesExternally: {
|
||||
Qt.openUrlExternally(go.updateReleaseNotesLink)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function openMainWindow(showAndRise) {
|
||||
// wait and check until font is loaded
|
||||
while(true){
|
||||
if (Style.fontawesome.status == FontLoader.Loading) continue
|
||||
if (Style.fontawesome.status != FontLoader.Ready) console.log("Error while loading font")
|
||||
break
|
||||
}
|
||||
|
||||
if (typeof(showAndRise)==='undefined') {
|
||||
showAndRise = true
|
||||
}
|
||||
if (gui.winMain == null) {
|
||||
gui.winMain = Qt.createQmlObject(
|
||||
'import BridgeUI 1.0; MainWindow {visible : false}',
|
||||
gui, "winMain"
|
||||
)
|
||||
}
|
||||
if (showAndRise) {
|
||||
gui.winMain.showAndRise()
|
||||
}
|
||||
|
||||
// restore update notification bar: trigger updateStateChanged
|
||||
var tmp = go.updateState
|
||||
go.updateState = ""
|
||||
go.updateState = tmp
|
||||
}
|
||||
|
||||
function closeMainWindow () {
|
||||
// Historical reasons: once upon a time there was a report about high GPU
|
||||
// usage on MacOS while bridge is closed. Legends say that destroying
|
||||
// MainWindow solved this.
|
||||
gui.winMain.hide()
|
||||
gui.winMain.destroy(5000)
|
||||
gui.winMain = null
|
||||
gui.isFirstWindow = false
|
||||
}
|
||||
|
||||
function showBubble(tabIndex, message, isWarning) {
|
||||
gui.openMainWindow(true)
|
||||
if (isWarning) {
|
||||
gui.warningFlags |= Style.warnBubbleMessage
|
||||
}
|
||||
winMain.bubbleNote.text = message
|
||||
winMain.bubbleNote.place(tabIndex)
|
||||
winMain.bubbleNote.show()
|
||||
}
|
||||
|
||||
function openReleaseNotes(){
|
||||
if (go.updateReleaseNotesLink == "") {
|
||||
go.checkAndOpenReleaseNotes()
|
||||
return
|
||||
}
|
||||
go.openReleaseNotesExternally()
|
||||
}
|
||||
|
||||
|
||||
// On start
|
||||
Component.onCompleted : {
|
||||
// set messages for translations
|
||||
go.wrongCredentials = qsTr("Incorrect username or password." , "notification", -1)
|
||||
go.wrongMailboxPassword = qsTr("Incorrect mailbox password." , "notification", -1)
|
||||
go.canNotReachAPI = qsTr("Cannot contact server, please check your internet connection." , "notification", -1)
|
||||
go.versionCheckFailed = qsTr("Version check was unsuccessful. Please try again later." , "notification", -1)
|
||||
go.credentialsNotRemoved = qsTr("Credentials could not be removed." , "notification", -1)
|
||||
go.failedAutostartPerm = qsTr("Unable to configure automatic start due to permissions settings - see <a href=\"https://protonmail.com/bridge/faq#c11\">FAQ</a> for details.", "notification", -1)
|
||||
go.failedAutostart = qsTr("Unable to configure automatic start." , "notification", -1)
|
||||
go.genericErrSeeLogs = qsTr("An error happened during procedure. See logs for more details." , "notification", -1)
|
||||
|
||||
go.guiIsReady()
|
||||
|
||||
// start window
|
||||
gui.openMainWindow(false)
|
||||
if (go.isShownOnStart) {
|
||||
gui.winMain.showAndRise()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,440 +0,0 @@
|
|||
// Copyright (c) 2021 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 qml file
|
||||
|
||||
import QtQuick 2.8
|
||||
import ImportExportUI 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
// All imports from dynamic must be loaded before
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Item {
|
||||
id: gui
|
||||
property alias winMain: winMain
|
||||
property bool isFirstWindow: true
|
||||
|
||||
property var locale : Qt.locale("en_US")
|
||||
property date netBday : new Date("1989-03-13T00:00:00")
|
||||
property var allYears : getYearList(1970,(new Date()).getFullYear())
|
||||
property var allMonths : getMonthList(1,12)
|
||||
property var allDays : getDayList(1,31)
|
||||
|
||||
property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"system","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceUpdate"}')
|
||||
|
||||
IEStyle{}
|
||||
|
||||
MainWindow {
|
||||
id: winMain
|
||||
|
||||
visible : true
|
||||
Component.onCompleted: {
|
||||
winMain.showAndRise()
|
||||
}
|
||||
}
|
||||
|
||||
BugReportWindow {
|
||||
id:bugreportWin
|
||||
clientVersion.visible: false
|
||||
onPrefill : {
|
||||
userAddress.text=""
|
||||
if (accountsModel.count>0) {
|
||||
var addressList = accountsModel.get(0).aliases.split(";")
|
||||
if (addressList.length>0) {
|
||||
userAddress.text = addressList[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Signals from Go
|
||||
Connections {
|
||||
target: go
|
||||
|
||||
onShowWindow : {
|
||||
winMain.showAndRise()
|
||||
}
|
||||
|
||||
onProcessFinished : {
|
||||
winMain.dialogAddUser.hide()
|
||||
winMain.dialogGlobal.hide()
|
||||
}
|
||||
onOpenManual : Qt.openUrlExternally("https://protonmail.com/support/categories/import-export/")
|
||||
|
||||
onNotifyBubble : {
|
||||
//go.highlightSystray()
|
||||
winMain.bubleNote.text = message
|
||||
winMain.bubleNote.place(tabIndex)
|
||||
winMain.bubleNote.show()
|
||||
winMain.showAndRise()
|
||||
}
|
||||
onBubbleClosed : {
|
||||
if (winMain.updateState=="uptodate") {
|
||||
//go.normalSystray()
|
||||
}
|
||||
}
|
||||
|
||||
onSetConnectionStatus: {
|
||||
go.isConnectionOK = isAvailable
|
||||
if (go.isConnectionOK) {
|
||||
if( winMain.updateState==gui.enums.statusNoInternet) {
|
||||
go.updateState = gui.enums.statusUpToDate
|
||||
}
|
||||
} else {
|
||||
go.updateState = gui.enums.statusNoInternet
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateStateChanged : {
|
||||
// once app is outdated prevent from state change
|
||||
if (winMain.updateState != "forceUpdate") {
|
||||
winMain.updateState = go.updateState
|
||||
}
|
||||
}
|
||||
|
||||
onSetAddAccountWarning : winMain.dialogAddUser.setWarning(message, 0)
|
||||
|
||||
onNotifyVersionIsTheLatest : {
|
||||
winMain.popupMessage.show(
|
||||
qsTr("You have the latest version!", "todo")
|
||||
)
|
||||
}
|
||||
|
||||
onNotifyError : {
|
||||
var sep = go.errorDescription.indexOf("\n") < 0 ? go.errorDescription.length : go.errorDescription.indexOf("\n")
|
||||
var name = go.errorDescription.slice(0, sep)
|
||||
var errorMessage = go.errorDescription.slice(sep)
|
||||
switch (errCode) {
|
||||
case gui.enums.errPMLoadFailed :
|
||||
winMain.popupMessage.show ( qsTr ( "Loading ProtonMail folders and labels was not successful." , "Error message" ) )
|
||||
winMain.dialogExport.hide()
|
||||
break
|
||||
case gui.enums.errLocalSourceLoadFailed :
|
||||
winMain.popupMessage.show(qsTr(
|
||||
"Loading local folder structure was not successful. "+
|
||||
"Folder does not contain valid MBOX or EML file.",
|
||||
"Error message when can not find correct files in folder."
|
||||
))
|
||||
winMain.dialogImport.hide()
|
||||
break
|
||||
case gui.enums.errRemoteSourceLoadFailed :
|
||||
winMain.popupMessage.show ( qsTr ( "Loading remote source structure was not successful." , "Error message" ) )
|
||||
winMain.dialogImport.hide()
|
||||
break
|
||||
case gui.enums.errWrongServerPathOrPort :
|
||||
winMain.popupMessage.show ( qsTr ( "Cannot contact server - incorrect server address and port." , "Error message" ) )
|
||||
winMain.dialogImport.decrementCurrentIndex()
|
||||
break
|
||||
case gui.enums.errWrongLoginOrPassword :
|
||||
winMain.popupMessage.show ( qsTr ( "Cannot authenticate - Incorrect email or password." , "Error message" ) )
|
||||
winMain.dialogImport.decrementCurrentIndex()
|
||||
break ;
|
||||
case gui.enums.errWrongAuthMethod :
|
||||
winMain.popupMessage.show ( qsTr ( "Cannot authenticate - Please use secured authentication method." , "Error message" ) )
|
||||
winMain.dialogImport.decrementCurrentIndex()
|
||||
break ;
|
||||
|
||||
|
||||
case gui.enums.errFillFolderName:
|
||||
winMain.popupMessage.show(qsTr (
|
||||
"Please fill the name.",
|
||||
"Error message when user did not fill the name of folder or label"
|
||||
))
|
||||
break
|
||||
case gui.enums.errCreateLabelFailed:
|
||||
winMain.popupMessage.show(qsTr(
|
||||
"Cannot create label with name \"%1\"\n%2",
|
||||
"Error message when it is not possible to create new label, arg1 folder name, arg2 error reason"
|
||||
).arg(name).arg(errorMessage))
|
||||
break
|
||||
case gui.enums.errCreateFolderFailed:
|
||||
winMain.popupMessage.show(qsTr(
|
||||
"Cannot create folder with name \"%1\"\n%2",
|
||||
"Error message when it is not possible to create new folder, arg1 folder name, arg2 error reason"
|
||||
).arg(name).arg(errorMessage))
|
||||
break
|
||||
|
||||
case gui.enums.errNothingToImport:
|
||||
winMain.popupMessage.show ( qsTr ( "No emails left to import after date range applied. Please, change the date range to continue." , "Error message" ) )
|
||||
winMain.dialogImport.decrementCurrentIndex()
|
||||
break
|
||||
|
||||
case gui.enums.errNoInternetWhileImport:
|
||||
case gui.enums.errNoInternet:
|
||||
go.setConnectionStatus(false)
|
||||
winMain.popupMessage.show ( go.canNotReachAPI )
|
||||
break
|
||||
|
||||
case gui.enums.errPMAPIMessageTooLarge:
|
||||
case gui.enums.errIMAPFetchFailed:
|
||||
case gui.enums.errEmailImportFailed :
|
||||
case gui.enums.errDraftImportFailed :
|
||||
case gui.enums.errDraftLabelFailed :
|
||||
case gui.enums.errEncryptMessageAttachment:
|
||||
case gui.enums.errEncryptMessage:
|
||||
//winMain.dialogImport.ask_retry_skip_cancel(name, errorMessage)
|
||||
console.log("Import error", errCode, go.errorDescription)
|
||||
winMain.popupMessage.show(qsTr("Error during import: \n%1\n please see log files for more details.", "message of generic error").arg(go.errorDescription))
|
||||
winMain.dialogImport.hide()
|
||||
break;
|
||||
|
||||
case gui.enums.errUnknownError : default:
|
||||
console.log("Unknown Error", errCode, go.errorDescription)
|
||||
winMain.popupMessage.show(qsTr("The program encounter an unknown error \n%1\n please see log files for more details.", "message of generic error").arg(go.errorDescription))
|
||||
winMain.dialogExport.hide()
|
||||
winMain.dialogImport.hide()
|
||||
winMain.dialogAddUser.hide()
|
||||
winMain.dialogGlobal.hide()
|
||||
}
|
||||
}
|
||||
|
||||
onNotifyManualUpdate: {
|
||||
go.updateState = "oldVersion"
|
||||
}
|
||||
|
||||
onNotifyManualUpdateRestartNeeded: {
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
go.updateState = "updateRestart"
|
||||
winMain.dialogUpdate.finished(false)
|
||||
|
||||
// after manual update - just retart immidiatly
|
||||
go.setToRestart()
|
||||
Qt.quit()
|
||||
}
|
||||
|
||||
onNotifyManualUpdateError: {
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
go.updateState = "updateError"
|
||||
winMain.dialogUpdate.finished(true)
|
||||
}
|
||||
|
||||
onNotifyForceUpdate : {
|
||||
go.updateState = "forceUpdate"
|
||||
if (!winMain.dialogUpdate.visible) {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
}
|
||||
|
||||
//onNotifySilentUpdateRestartNeeded: {
|
||||
// go.updateState = "updateRestart"
|
||||
//}
|
||||
//
|
||||
//onNotifySilentUpdateError: {
|
||||
// go.updateState = "updateError"
|
||||
//}
|
||||
|
||||
onNotifyLogout : {
|
||||
go.notifyBubble(0, qsTr("Account %1 has been disconnected. Please log in to continue to use the Import-Export app with this account.").arg(accname) )
|
||||
}
|
||||
|
||||
onNotifyAddressChanged : {
|
||||
go.notifyBubble(0, qsTr("The address list has been changed for account %1. You may need to reconfigure the settings in your email client.").arg(accname) )
|
||||
}
|
||||
|
||||
onNotifyAddressChangedLogout : {
|
||||
go.notifyBubble(0, qsTr("The address list has been changed for account %1. You have to reconfigure the settings in your email client.").arg(accname) )
|
||||
}
|
||||
|
||||
|
||||
onNotifyKeychainRebuild : {
|
||||
go.notifyBubble(1, qsTr(
|
||||
"Your MacOS keychain is probably corrupted. Please consult the instructions in our <a href=\"https://protonmail.com/bridge/faq#c15\">FAQ</a>.",
|
||||
"notification message"
|
||||
))
|
||||
}
|
||||
|
||||
onNotifyHasNoKeychain : {
|
||||
gui.winMain.dialogGlobal.state="noKeychain"
|
||||
gui.winMain.dialogGlobal.show()
|
||||
}
|
||||
|
||||
|
||||
onExportStructureLoadFinished: {
|
||||
if (okay) winMain.dialogExport.okay()
|
||||
else winMain.dialogExport.cancel()
|
||||
}
|
||||
onImportStructuresLoadFinished: {
|
||||
if (okay) winMain.dialogImport.okay()
|
||||
else winMain.dialogImport.cancel()
|
||||
}
|
||||
|
||||
onSimpleErrorHappen: {
|
||||
if (winMain.dialogImport.visible == true) {
|
||||
winMain.dialogImport.hide()
|
||||
}
|
||||
if (winMain.dialogExport.visible == true) {
|
||||
winMain.dialogExport.hide()
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateFinished : {
|
||||
winMain.dialogUpdate.finished(hasError)
|
||||
}
|
||||
|
||||
onOpenReleaseNotesExternally: {
|
||||
Qt.openUrlExternally(go.updateReleaseNotesLink)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function folderIcon(folderName, folderType) { // translations
|
||||
switch (folderName.toLowerCase()) {
|
||||
case "inbox" : return Style.fa.inbox
|
||||
case "sent" : return Style.fa.send
|
||||
case "spam" :
|
||||
case "junk" : return Style.fa.ban
|
||||
case "draft" : return Style.fa.file_o
|
||||
case "starred" : return Style.fa.star_o
|
||||
case "trash" : return Style.fa.trash_o
|
||||
case "archive" : return Style.fa.archive
|
||||
default: return folderType == gui.enums.folderTypeLabel ? Style.fa.tag : Style.fa.folder_open
|
||||
}
|
||||
return Style.fa.sticky_note_o
|
||||
}
|
||||
|
||||
function folderTypeTitle(folderType) { // translations
|
||||
if (folderType==gui.enums.folderTypeSystem ) return ""
|
||||
if (folderType==gui.enums.folderTypeLabel ) return qsTr("Labels" , "todo")
|
||||
if (folderType==gui.enums.folderTypeFolder ) return qsTr("Folders" , "todo")
|
||||
return "Undef"
|
||||
}
|
||||
|
||||
function isFolderEmpty() {
|
||||
return "true"
|
||||
}
|
||||
|
||||
function getUnixTime(dateString) {
|
||||
var d = new Date(dateString)
|
||||
var n = d.getTime()
|
||||
if (n != n) return -1
|
||||
return n
|
||||
}
|
||||
|
||||
function getYearList(minY,maxY) {
|
||||
var years = new Array()
|
||||
for (var i=0; i<=maxY-minY;i++) {
|
||||
years[i] = (maxY-i).toString()
|
||||
}
|
||||
//console.log("getYearList:", years)
|
||||
return years
|
||||
}
|
||||
|
||||
function getMonthList(minM,maxM) {
|
||||
var months = new Array()
|
||||
for (var i=0; i<=maxM-minM;i++) {
|
||||
var iMonth = new Date(1989,(i+minM-1),13)
|
||||
months[i] = iMonth.toLocaleString(gui.locale, "MMM")
|
||||
}
|
||||
//console.log("getMonthList:", months[0], months)
|
||||
return months
|
||||
}
|
||||
|
||||
function getDayList(minD,maxD) {
|
||||
var days = new Array()
|
||||
for (var i=0; i<=maxD-minD;i++) {
|
||||
days[i] = gui.prependZeros(i+minD,2)
|
||||
}
|
||||
return days
|
||||
}
|
||||
|
||||
function prependZeros(num,desiredLength) {
|
||||
var s = num+""
|
||||
while (s.length < desiredLength) s="0"+s
|
||||
return s
|
||||
}
|
||||
|
||||
function daysInMonth(year,month) {
|
||||
if (typeof(year) !== 'number') {
|
||||
year = parseInt(year)
|
||||
}
|
||||
if (typeof(month) !== 'number') {
|
||||
month = Date.fromLocaleDateString( gui.locale, "1970-"+month+"-10", "yyyy-MMM-dd").getMonth()+1
|
||||
}
|
||||
var maxDays = (new Date(year,month,0)).getDate()
|
||||
if (isNaN(maxDays)) maxDays = 0
|
||||
//console.log(" daysInMonth", year, month, maxDays)
|
||||
return maxDays
|
||||
}
|
||||
|
||||
function niceDateTime() {
|
||||
var stamp = new Date()
|
||||
var nice = getMonthList(stamp.getMonth()+1, stamp.getMonth()+1)[0]
|
||||
nice += "-" + getDayList(stamp.getDate(), stamp.getDate())[0]
|
||||
nice += "-" + getYearList(stamp.getFullYear(), stamp.getFullYear())[0]
|
||||
nice += " " + gui.prependZeros(stamp.getHours(),2)
|
||||
nice += ":" + gui.prependZeros(stamp.getMinutes(),2)
|
||||
return nice
|
||||
}
|
||||
|
||||
/*
|
||||
// Debug
|
||||
Connections {
|
||||
target: structureExternal
|
||||
|
||||
onDataChanged: {
|
||||
console.log("external data changed")
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
Connections {
|
||||
target: structurePM
|
||||
|
||||
onSelectedLabelsChanged: console.log("PM sel labels:", structurePM.selectedLabels)
|
||||
onSelectedFoldersChanged: console.log("PM sel folders:", structurePM.selectedFolders)
|
||||
onDataChanged: {
|
||||
console.log("PM data changed")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function openReleaseNotes(){
|
||||
if (go.updateReleaseNotesLink == "") {
|
||||
go.checkAndOpenReleaseNotes()
|
||||
return
|
||||
}
|
||||
go.openReleaseNotesExternally()
|
||||
}
|
||||
|
||||
|
||||
property string areYouSureYouWantToQuit : qsTr("There are incomplete processes - some items are not yet transferred. Do you really want to stop and quit?")
|
||||
// On start
|
||||
Component.onCompleted : {
|
||||
// set spell messages
|
||||
go.wrongCredentials = qsTr("Incorrect username or password." , "notification", -1)
|
||||
go.wrongMailboxPassword = qsTr("Incorrect mailbox password." , "notification", -1)
|
||||
go.canNotReachAPI = qsTr("Cannot contact server, please check your internet connection." , "notification", -1)
|
||||
go.versionCheckFailed = qsTr("Version check was unsuccessful. Please try again later." , "notification", -1)
|
||||
go.credentialsNotRemoved = qsTr("Credentials could not be removed." , "notification", -1)
|
||||
go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1)
|
||||
go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1)
|
||||
|
||||
|
||||
go.guiIsReady()
|
||||
|
||||
gui.allMonths = getMonthList(1,12)
|
||||
gui.allMonthsChanged()
|
||||
}
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
// Copyright (c) 2021 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.account = account
|
||||
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.account = account
|
||||
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.account = account
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) 2021 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: go.credits.split(";")
|
||||
|
||||
delegate: AccessibleText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: modelData
|
||||
color: Style.main.text
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
|
||||
footer: ButtonRounded {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Close", "close window")
|
||||
onClicked: dialogCredits.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
// Copyright (c) 2021 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
// Copyright (c) 2021 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 bool isMaxDateToday : false
|
||||
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
|
||||
onDownChanged: {
|
||||
if (root.isMaxDateToday){
|
||||
root.maxDate = new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
onDownChanged: {
|
||||
if (root.isMaxDateToday){
|
||||
root.maxDate = new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
onDownChanged: {
|
||||
if (root.isMaxDateToday){
|
||||
root.maxDate = new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
}
|
||||
var month = gui.allMonths.indexOf(monthInput.currentText)
|
||||
var year = parseInt(yearInput.currentText)
|
||||
var pickedDate = new Date(year, month, day)
|
||||
// Compensate automatic DST in windows
|
||||
if (pickedDate.getDate() != day) {
|
||||
pickedDate.setTime(pickedDate.getTime() + 60*60*1000) // add hour
|
||||
}
|
||||
currentUnix = pickedDate.getTime()
|
||||
}
|
||||
return new Date(Math.max(
|
||||
minDate.getTime(),
|
||||
Math.min(maxDate.getTime(), currentUnix)
|
||||
))
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
// Copyright (c) 2021 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 : transferRules
|
||||
property string sourceID : "-1"
|
||||
|
||||
property alias allDates : allDatesBox.checked
|
||||
property alias inputDateFrom : inputDateFrom
|
||||
property alias inputDateTo : inputDateTo
|
||||
|
||||
function getRange() {common.getRange()}
|
||||
function setRangeFromTo(from, to) {common.setRangeFromTo(from, to)}
|
||||
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
|
||||
isMaxDateToday: true
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) 2021 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 getRange() {common.getRange()}
|
||||
function applyRange() {common.applyRange()}
|
||||
*/
|
||||
|
||||
function resetRange() {
|
||||
inputDateFrom.setDate(gui.netBday.getTime())
|
||||
inputDateTo.setDate((new Date()).getTime())
|
||||
}
|
||||
|
||||
function setRangeFromTo(folderFrom, folderTo){ // unix time in seconds
|
||||
if ( folderFrom == 0 && folderTo ==0 ) {
|
||||
dateRange.allDates = true
|
||||
} else {
|
||||
dateRange.allDates = false
|
||||
inputDateFrom.setDate(folderFrom)
|
||||
inputDateTo.setDate(folderTo)
|
||||
}
|
||||
}
|
||||
|
||||
function getRange(){ // unix time in seconds
|
||||
//console.log(" ==== GET RANGE === ")
|
||||
//console.trace()
|
||||
var folderFrom = dateRange.structure.globalFromDate
|
||||
var folderTo = dateRange.structure.globalToDate
|
||||
|
||||
root.setRangeFromTo(folderFrom, 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)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
inputDateFrom.updateRange(gui.netBday)
|
||||
inputDateTo.updateRange(new Date())
|
||||
//getRange()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
// Copyright (c) 2021 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 : transferRules
|
||||
property string sourceID : ""
|
||||
property int sourceFromDate : 0
|
||||
property int sourceToDate : 0
|
||||
|
||||
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 : updateRange()
|
||||
onAboutToHide : dateRangeInput.applyRange()
|
||||
}
|
||||
|
||||
function updateRange() {
|
||||
dateRangeInput.setRangeFromTo(root.sourceFromDate, root.sourceToDate)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target:root
|
||||
onSourceFromDateChanged: root.updateRange()
|
||||
onSourceToDateChanged: root.updateRange()
|
||||
}
|
||||
}
|
|
@ -1,459 +0,0 @@
|
|||
// Copyright (c) 2021 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 account
|
||||
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
|
||||
}
|
||||
|
||||
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: transferRules != 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("Status:")
|
||||
font.pointSize: Style.main.iconSize * Style.pt
|
||||
color : Style.main.text
|
||||
}
|
||||
AccessibleText {
|
||||
anchors.baseline: statusLabel.baseline
|
||||
text : {
|
||||
if (progressbarExport.isFinished) return qsTr("finished")
|
||||
if (go.progressDescription == "") return qsTr("exporting")
|
||||
return 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) {
|
||||
if (go.progressDescription=="") return qsTr("Export finished","todo")
|
||||
else return qsTr("Export failed: %1").arg(go.progressDescription)
|
||||
}
|
||||
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 (transferRules.rowCount() == 0){
|
||||
errorPopup.show(qsTr("No emails found to export. Please try another address.", "todo"))
|
||||
return false
|
||||
}
|
||||
// at least one source selected
|
||||
/*
|
||||
if (!transferRules.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.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();
|
||||
// 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.getRange()
|
||||
//no break
|
||||
default:
|
||||
incrementCurrentIndex()
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
onShow: {
|
||||
if (winMain.updateState==gui.enums.statusNoInternet) {
|
||||
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.account, root.address)
|
||||
sourceFoldersInput.hasItems = (transferRules.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 : {
|
||||
errorPopup.hide()
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,353 +0,0 @@
|
|||
// Copyright (c) 2021 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 app.", "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
|
||||
// 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" ) { }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Enter) {
|
||||
root.confirmed()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
// Copyright (c) 2021 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 : transferRules
|
||||
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 {
|
||||
property variant modelData: model
|
||||
width : root.width - 5*root.border.width
|
||||
type : modelData.type
|
||||
folderIconColor : modelData.iconColor
|
||||
title : modelData.name
|
||||
isSelected : modelData.isActive
|
||||
onClicked : {
|
||||
//console.log("Clicked", folderId, isSelected)
|
||||
transferRules.setIsRuleActive(modelData.mboxID,!model.isActive)
|
||||
}
|
||||
}
|
||||
|
||||
section.property: "type"
|
||||
section.delegate: FolderRowButton {
|
||||
isSection : true
|
||||
width : root.width - 5*root.border.width
|
||||
title : gui.folderTypeTitle(section)
|
||||
isSelected : section == gui.enums.folderTypeLabel ? transferRules.isLabelGroupSelected : transferRules.isFolderGroupSelected
|
||||
onClicked : transferRules.setIsGroupActive(section,!isSelected)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) 2021 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
// Copyright (c) 2021 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 string folderIconColor : Style.main.textBlue
|
||||
|
||||
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=="" ? Style.main.textBlue : root.folderIconColor
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
// Copyright (c) 2021 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: {
|
||||
go.checkForUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
AccessibleText {
|
||||
id: aboutText
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
color: Style.main.textDisabled
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
text: "ProtonMail Import-Export app Version "+go.getBackendVersion()+"\n© 2021 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: licenseFile
|
||||
text : qsTr("License", "link to click on to open license file")
|
||||
color : Style.main.textDisabled
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
font.underline: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
go.openLicenseFile()
|
||||
}
|
||||
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 : gui.openReleaseNotes()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
// Copyright (c) 2021 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: isActive
|
||||
property string lastTargetFolder: "6" // Archive
|
||||
property string lastTargetLabels: "" // no flag by default
|
||||
|
||||
property string sourceID : mboxID
|
||||
property string sourceName : name
|
||||
|
||||
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(root.sourceName, 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 : root.sourceName
|
||||
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: root.sourceID
|
||||
targets: transferRules.targetFolders(root.sourceID)
|
||||
width: nameWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
enabled: root.isSourceSelected
|
||||
onDoNotImport: root.toggleImport()
|
||||
onImportToFolder: root.importToFolder(newTargetID)
|
||||
}
|
||||
|
||||
SelectLabelsMenu {
|
||||
sourceID: root.sourceID
|
||||
targets: transferRules.targetLabels(root.sourceID)
|
||||
width: nameWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
enabled: root.isSourceSelected
|
||||
onAddTargetLabel: { transferRules.addTargetID(sourceID, newTargetID) }
|
||||
onRemoveTargetLabel: { transferRules.removeTargetID(sourceID, newTargetID) }
|
||||
}
|
||||
|
||||
LabelIconList {
|
||||
colorList: labelColors=="" ? [] : labelColors.split(";")
|
||||
width: iconWidth
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
enabled: root.isSourceSelected
|
||||
}
|
||||
|
||||
DateRangeMenu {
|
||||
id: dateRangeMenu
|
||||
sourceID: root.sourceID
|
||||
sourceFromDate: fromDate
|
||||
sourceToDate: toDate
|
||||
|
||||
enabled: root.isSourceSelected
|
||||
anchors.verticalCenter : parent.verticalCenter
|
||||
|
||||
Component.onCompleted : dateRangeMenu.updateRange()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function importToFolder(newTargetID) {
|
||||
transferRules.addTargetID(root.sourceID,newTargetID)
|
||||
}
|
||||
|
||||
function toggleImport() {
|
||||
transferRules.setIsRuleActive(root.sourceID, !root.isSourceSelected)
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill : root
|
||||
hoverEnabled : !root.isHeader
|
||||
onEntered : { root.isHovered = true }
|
||||
onExited : { root.isHovered = false }
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright (c) 2021 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 : transferRules
|
||||
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.currentItem === null) 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
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (c) 2021 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 : transferRules
|
||||
property string sourceID : "-1"
|
||||
|
||||
property alias allDates : allDatesBox.checked
|
||||
property alias inputDateFrom : inputDateFrom
|
||||
property alias inputDateTo : inputDateTo
|
||||
|
||||
property alias labelWidth: label.width
|
||||
|
||||
function getRange() {common.getRange()}
|
||||
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
|
||||
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
|
||||
isMaxDateToday: true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
color: Style.main.text
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
InfoToolTip {
|
||||
info: qsTr( "When master import label is selected then all imported emails 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright (c) 2021 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 var colorList
|
||||
color: "transparent"
|
||||
|
||||
DelegateModel {
|
||||
id: selectedLabels
|
||||
model : colorList
|
||||
delegate : Text {
|
||||
text : metrics.text
|
||||
font : metrics.font
|
||||
color : modelData
|
||||
}
|
||||
}
|
||||
|
||||
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,root.colorList.length)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
// Copyright (c) 2021 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 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 == "forceUpdate"
|
||||
|
||||
property bool activeContent :
|
||||
!dialogAddUser .visible &&
|
||||
!dialogCredits .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
|
||||
forceUpdate: root.isOutdateVersion
|
||||
}
|
||||
|
||||
|
||||
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 { }
|
||||
}
|
||||
|
||||
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()
|
||||
onClickedCancel: popupMessage.hide()
|
||||
onClickedYes: {
|
||||
if (popupMessage.text == 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.buttonYes .visible = false
|
||||
popupMessage.buttonQuit .visible = true
|
||||
popupMessage.buttonCancel .visible = true
|
||||
popupMessage.show ( gui.areYouSureYouWantToQuit )
|
||||
return
|
||||
}
|
||||
|
||||
close.accepted=true
|
||||
go.processFinished()
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
}
|
|
@ -1,355 +0,0 @@
|
|||
// Copyright (c) 2021 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 sourceID
|
||||
property var targets
|
||||
property bool isFolderType: root.folderType == gui.enums.folderTypeFolder
|
||||
property bool below: true
|
||||
|
||||
signal doNotImport()
|
||||
signal importToFolder(string newTargetID)
|
||||
signal addTargetLabel(string newTargetID)
|
||||
signal removeTargetLabel(string newTargetID)
|
||||
|
||||
leftPadding: Style.dialog.spacing
|
||||
|
||||
onDownChanged : {
|
||||
root.below = popup.y>0
|
||||
}
|
||||
|
||||
contentItem : Row {
|
||||
id: boxText
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
family: Style.fontawesome.name
|
||||
}
|
||||
text: {
|
||||
if (view.currentIndex >= 0) {
|
||||
if (!root.isFolderType) {
|
||||
return Style.fa.tags + " "
|
||||
}
|
||||
var tgtIcon = view.currentItem.folderIcon
|
||||
var tgtColor = view.currentItem.folderColor
|
||||
if (tgtIcon != Style.fa.folder_open) {
|
||||
return tgtIcon + " "
|
||||
}
|
||||
return '<font color="'+tgtColor+'">'+ tgtIcon + "</font> "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
color: !root.enabled ? Style.main.textDisabled : ( root.down ? Style.main.background : Style.main.text )
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font {
|
||||
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: {
|
||||
if (view.currentIndex >= 0) {
|
||||
if (!root.isFolderType) return qsTr("Add/Remove labels")
|
||||
return view.currentItem.folderName
|
||||
}
|
||||
if (root.isFolderType) return qsTr("No folder selected")
|
||||
return qsTr("No labels selected")
|
||||
}
|
||||
|
||||
|
||||
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 row
|
||||
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 : isActive
|
||||
property string folderName: name
|
||||
property string folderIcon: gui.folderIcon(name,type)
|
||||
property string folderColor: (type == gui.enums.folderTypeLabel || type == gui.enums.folderTypeFolder) ? iconColor : root.popup.textColor
|
||||
|
||||
Text {
|
||||
id: targetIcon
|
||||
text: thisDelegate.folderIcon
|
||||
color : thisDelegate.folderColor
|
||||
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: thisDelegate.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
|
||||
root.popup.close()
|
||||
if (!isActive) {
|
||||
root.importToFolder(mboxID)
|
||||
}
|
||||
} else {
|
||||
if (isActive) {
|
||||
root.removeTargetLabel(mboxID)
|
||||
} else {
|
||||
root.addTargetLabel(mboxID)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
model : root.targets
|
||||
delegate : root.delegate
|
||||
|
||||
currentIndex: view.model.selectedIndex
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = name
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
}
|
||||
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
import QtQuick.Controls 2.4
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// must have wrapper
|
||||
ScrollView {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
background: Rectangle {
|
||||
color: Style.main.background
|
||||
}
|
||||
|
||||
// horizontall scrollbar sometimes showes up when vertical scrollbar coveres content
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
// keeping vertical scrollbar allways visible when needed
|
||||
Connections {
|
||||
target: wrapper.ScrollBar.vertical
|
||||
onSizeChanged: {
|
||||
// ScrollBar.size == 0 at creating so no need to make it active
|
||||
if (wrapper.ScrollBar.vertical.size < 1.0 && wrapper.ScrollBar.vertical.size > 0 && !wrapper.ScrollBar.vertical.active) {
|
||||
wrapper.ScrollBar.vertical.active = true
|
||||
}
|
||||
}
|
||||
onActiveChanged: {
|
||||
wrapper.ScrollBar.vertical.active = true
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
family : cacheKeychain.font.family // use default font, not font-awesome
|
||||
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: autoUpdates
|
||||
text: qsTr("Keep the application up to date", "label for toggle that activates and disables the automatic updates")
|
||||
leftIcon.text : Style.fa.download
|
||||
rightIcon {
|
||||
font.pointSize : Style.settings.toggleSize * Style.pt
|
||||
text : go.isAutoUpdate!=false ? Style.fa.toggle_on : Style.fa.toggle_off
|
||||
color : go.isAutoUpdate!=false ? Style.main.textBlue : Style.main.textDisabled
|
||||
}
|
||||
Accessible.description: (
|
||||
go.isAutoUpdate == false ?
|
||||
qsTr("Enable" , "Click to enable the automatic update of Bridge") :
|
||||
qsTr("Disable" , "Click to disable the automatic update of Bridge")
|
||||
) + " " + text
|
||||
onClicked: {
|
||||
go.toggleAutoUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
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
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2021 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.13
|
||||
import QtQuick.Window 2.15
|
||||
import Qt.labs.platform 1.0
|
||||
|
||||
QtObject {
|
||||
|
||||
property var _mainWindow: Window {
|
||||
id: mainWindow
|
||||
title: "window 1"
|
||||
visible: false
|
||||
}
|
||||
|
||||
property var _trayMenu: Window {
|
||||
id: trayMenu
|
||||
title: "window 2"
|
||||
visible: false
|
||||
flags: Qt.Dialog
|
||||
|
||||
width: 448
|
||||
}
|
||||
|
||||
property var _trayIcon: SystemTrayIcon {
|
||||
id: trayIcon
|
||||
visible: true
|
||||
iconSource: "./icons/rectangle-systray.png"
|
||||
onActivated: {
|
||||
switch (reason) {
|
||||
case SystemTrayIcon.Unknown:
|
||||
break;
|
||||
case SystemTrayIcon.Context:
|
||||
break
|
||||
case SystemTrayIcon.DoubleClick:
|
||||
break
|
||||
case SystemTrayIcon.Trigger:
|
||||
trayMenu.x = (Screen.desktopAvailableWidth - trayMenu.width) / 2
|
||||
trayMenu.visible = !trayMenu.visible
|
||||
break;
|
||||
case SystemTrayIcon.MiddleClick:
|
||||
mainWindow.visible = !mainWindow.visible
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// default options to make button accessible
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
Button {
|
||||
function clearText(value) {
|
||||
// remove font-awesome chars
|
||||
return value.replace(/[\uf000-\uf2e0]/g,'')
|
||||
}
|
||||
Accessible.onPressAction: clicked()
|
||||
Accessible.ignored: !enabled || !visible
|
||||
Accessible.name: clearText(text)
|
||||
Accessible.description: clearText(text)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// default options to make text accessible and selectable
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
TextEdit {
|
||||
function clearText(value) {
|
||||
// substitue the copyright symbol by the text and remove the font-awesome chars and HTML tags
|
||||
return value.replace(/\uf1f9/g,'Copyright').replace(/[\uf000-\uf2e0]/g,'').replace(/<[^>]+>/g,'')
|
||||
}
|
||||
|
||||
readOnly: true
|
||||
selectByKeyboard: true
|
||||
selectByMouse: true
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: clearText(text)
|
||||
Accessible.description: clearText(text)
|
||||
Accessible.focusable: true
|
||||
Accessible.ignored: !enabled || !visible || text == ""
|
||||
}
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// default options to make text accessible
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
Text {
|
||||
function clearText(value) {
|
||||
// substitue the copyright symbol by the text and remove the font-awesome chars and HTML tags
|
||||
return value.replace(/\uf1f9/g,'Copyright').replace(/[\uf000-\uf2e0]/g,'').replace(/<[^>]+>/g,'')
|
||||
}
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: clearText(text)
|
||||
Accessible.description: clearText(text)
|
||||
Accessible.focusable: true
|
||||
Accessible.ignored: !enabled || !visible || text == ""
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
// Copyright (c) 2021 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.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal addAccount()
|
||||
|
||||
property alias numAccounts : listAccounts.count
|
||||
property alias model : listAccounts.model
|
||||
property alias delegate : listAccounts.delegate
|
||||
property int separatorNoAccount : viewContent.height-Style.accounts.heightFooter
|
||||
property bool hasFooter : true
|
||||
|
||||
// must have wrapper
|
||||
Rectangle {
|
||||
id: wrapper
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.main.background
|
||||
|
||||
// content
|
||||
ListView {
|
||||
id: listAccounts
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : hasFooter ? addAccFooter.top : parent.bottom
|
||||
}
|
||||
orientation: ListView.Vertical
|
||||
clip: true
|
||||
cacheBuffer: 2500
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Style.main.rightMargin/4
|
||||
}
|
||||
width: Style.main.rightMargin/3
|
||||
Accessible.ignored: true
|
||||
}
|
||||
header: Rectangle {
|
||||
width : wrapper.width
|
||||
height : root.numAccounts!=0 ? Style.accounts.heightHeader : root.separatorNoAccount
|
||||
color : "transparent"
|
||||
AccessibleText { // Placeholder on empty
|
||||
anchors {
|
||||
centerIn: parent
|
||||
}
|
||||
visible: root.numAccounts==0
|
||||
text : qsTr("No accounts added", "displayed when there are no accounts added")
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
Text { // Account
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : Style.main.leftMargin
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
visible: root.numAccounts!=0
|
||||
font.bold : true
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
text : qsTr("ACCOUNT", "title of column that displays account name")
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
Text { // Status
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : viewContent.width/2
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
visible: root.numAccounts!=0
|
||||
font.bold : true
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
text : qsTr("STATUS", "title of column that displays connected or disconnected status")
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
Text { // Actions
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : 5.5*viewContent.width/8
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
visible: root.numAccounts!=0
|
||||
font.bold : true
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
text : qsTr("ACTIONS", "title of column that displays log out and log in actions for each account")
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
// line
|
||||
Rectangle {
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
}
|
||||
visible: root.numAccounts!=0
|
||||
color: Style.accounts.line
|
||||
height: Style.accounts.heightLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AddAccountBar {
|
||||
id: addAccFooter
|
||||
visible: hasFooter
|
||||
anchors {
|
||||
left : parent.left
|
||||
bottom : parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.SelectAll
|
||||
onActivated: root.addAccount()
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Bar with add account button and help
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
width : parent.width
|
||||
height : Style.accounts.heightFooter
|
||||
color: "transparent"
|
||||
Rectangle {
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
}
|
||||
height: Style.accounts.heightLine
|
||||
color: Style.accounts.line
|
||||
}
|
||||
ClickIconText {
|
||||
id: buttonAddAccount
|
||||
anchors {
|
||||
left : parent.left
|
||||
leftMargin : Style.main.leftMargin
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
textColor : Style.main.textBlue
|
||||
iconText : Style.fa.plus_circle
|
||||
text : qsTr("Add Account", "begins the flow to log in to an account that is not yet listed")
|
||||
textBold : true
|
||||
onClicked : root.addAccount()
|
||||
Accessible.description: {
|
||||
if (gui.winMain!=null) {
|
||||
return text + (gui.winMain.addAccountTip.visible? ", "+gui.winMain.addAccountTip.text : "")
|
||||
}
|
||||
return buttonAddAccount.text
|
||||
}
|
||||
}
|
||||
ClickIconText {
|
||||
id: buttonHelp
|
||||
anchors {
|
||||
right : parent.right
|
||||
rightMargin : Style.main.rightMargin
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
textColor : Style.main.textDisabled
|
||||
iconText : Style.fa.question_circle
|
||||
text : qsTr("Help", "directs the user to the online user guide")
|
||||
textBold : true
|
||||
onClicked : go.openManual()
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Notify user
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property int posx // x-coordinate of triangle
|
||||
property bool isTriangleBelow
|
||||
property string text
|
||||
property alias bubbleColor: bubble.color
|
||||
anchors {
|
||||
top : tabbar.bottom
|
||||
left : tabbar.left
|
||||
leftMargin : {
|
||||
// position of bubble calculated from posx
|
||||
return Math.max(
|
||||
Style.main.leftMargin, // keep minimal left margin
|
||||
Math.min(
|
||||
root.posx - root.width/2, // fit triangle in the middle if possible
|
||||
tabbar.width - root.width - Style.main.rightMargin // keep minimal right margin
|
||||
)
|
||||
)
|
||||
}
|
||||
topMargin: 0
|
||||
}
|
||||
height : triangle.height + bubble.height
|
||||
width : bubble.width
|
||||
color : "transparent"
|
||||
visible : false
|
||||
|
||||
|
||||
Rectangle {
|
||||
id : triangle
|
||||
anchors {
|
||||
top : root.isTriangleBelow ? undefined : root.top
|
||||
bottom : root.isTriangleBelow ? root.bottom : undefined
|
||||
bottomMargin : 1*Style.px
|
||||
left : root.left
|
||||
leftMargin : root.posx - triangle.width/2 - root.anchors.leftMargin
|
||||
}
|
||||
width: 2*Style.tabbar.heightTriangle+2
|
||||
height: Style.tabbar.heightTriangle
|
||||
color: "transparent"
|
||||
Canvas {
|
||||
anchors.fill: parent
|
||||
rotation: root.isTriangleBelow ? 180 : 0
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.fillStyle = bubble.color
|
||||
ctx.moveTo(0 , height)
|
||||
ctx.lineTo(width/2, 0)
|
||||
ctx.lineTo(width , height)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bubble
|
||||
anchors {
|
||||
top: root.top
|
||||
left: root.left
|
||||
topMargin: (root.isTriangleBelow ? 0 : triangle.height)
|
||||
}
|
||||
width : mainText.contentWidth + Style.main.leftMargin + Style.main.rightMargin
|
||||
height : 2*Style.main.fontSize
|
||||
radius : Style.bubble.radius
|
||||
color : Style.bubble.background
|
||||
|
||||
AccessibleText {
|
||||
id: mainText
|
||||
anchors {
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin : Style.main.fontSize
|
||||
}
|
||||
|
||||
text: "<html><style>a { color: "+Style.main.textBlue+";}</style>"+root.text+"<html>"
|
||||
width : Style.bubble.width - ( Style.main.leftMargin + Style.main.rightMargin )
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.WordWrap
|
||||
color: Style.bubble.text
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: mainText
|
||||
cursorShape: mainText.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
Accessible.name: qsTr("Message")
|
||||
Accessible.description: root.text
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: okButton
|
||||
visible: !root.isTriangleBelow
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
bottomMargin : Style.main.fontSize
|
||||
}
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
height: Style.main.fontSize*2
|
||||
color_main: Style.main.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
onClicked: hide()
|
||||
}
|
||||
}
|
||||
|
||||
function place(index) {
|
||||
if (index < 0) {
|
||||
// add accounts
|
||||
root.isTriangleBelow = true
|
||||
bubble.height = 3.25*Style.main.fontSize
|
||||
root.posx = 2*Style.main.leftMargin
|
||||
bubble.width = mainText.contentWidth - Style.main.leftMargin
|
||||
} else {
|
||||
root.isTriangleBelow = false
|
||||
bubble.height = (
|
||||
bubble.anchors.topMargin + // from top
|
||||
mainText.contentHeight + // the text content
|
||||
Style.main.fontSize + // gap between button
|
||||
okButton.height + okButton.anchors.bottomMargin // from bottom and button
|
||||
)
|
||||
if (index < 3) {
|
||||
// possition accordig to top tab
|
||||
var margin = Style.main.leftMargin + Style.tabbar.widthButton/2
|
||||
root.posx = margin + index*tabbar.spacing
|
||||
} else {
|
||||
// quit button
|
||||
root.posx = tabbar.width - 2*Style.main.rightMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show() {
|
||||
root.visible=true
|
||||
gui.winMain.activeContent = false
|
||||
}
|
||||
|
||||
function hide() {
|
||||
root.visible=false
|
||||
go.bubbleClosed()
|
||||
gui.winMain.activeContent = true
|
||||
gui.winMain.tabbar.focusButton()
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Window for sending a bug report
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Window {
|
||||
id:root
|
||||
property alias userAddress : userAddress
|
||||
property alias clientVersion : clientVersion
|
||||
|
||||
width : Style.bugreport.width
|
||||
height : Style.bugreport.height
|
||||
minimumWidth : Style.bugreport.width
|
||||
maximumWidth : Style.bugreport.width
|
||||
minimumHeight : Style.bugreport.height
|
||||
maximumHeight : Style.bugreport.height
|
||||
|
||||
property color inputBorderColor : Style.main.text
|
||||
|
||||
color : "transparent"
|
||||
flags : Qt.Window | Qt.Dialog | Qt.FramelessWindowHint
|
||||
title : "Bug report"
|
||||
visible : false
|
||||
|
||||
WindowTitleBar {
|
||||
id: titleBar
|
||||
window: root
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:background
|
||||
color: Style.main.background
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : titleBar.bottom
|
||||
bottom : parent.bottom
|
||||
}
|
||||
border {
|
||||
width: Style.main.border
|
||||
color: Style.tabbar.background
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id:content
|
||||
anchors {
|
||||
fill : parent
|
||||
leftMargin : Style.main.leftMargin
|
||||
rightMargin : Style.main.rightMargin
|
||||
bottomMargin : Style.main.rightMargin
|
||||
topMargin : Style.main.rightMargin + titleBar.height
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
// Description in flickable
|
||||
Flickable {
|
||||
id: descripWrapper
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: content.height - (
|
||||
(clientVersion.visible ? clientVersion.height + Style.dialog.fontSize : 0) +
|
||||
userAddress.height + Style.dialog.fontSize +
|
||||
securityNoteText.contentHeight + Style.dialog.fontSize +
|
||||
cancelButton.height + Style.dialog.fontSize
|
||||
)
|
||||
clip: true
|
||||
contentWidth : width
|
||||
contentHeight : height
|
||||
|
||||
TextArea.flickable: TextArea {
|
||||
id: description
|
||||
focus: true
|
||||
wrapMode: TextEdit.Wrap
|
||||
placeholderText: qsTr ("Please briefly describe the bug(s) you have encountered...", "bug report instructions")
|
||||
background : Rectangle {
|
||||
color : Style.dialog.background
|
||||
radius: Style.dialog.radiusButton
|
||||
border {
|
||||
color : root.inputBorderColor
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: FastBlur {
|
||||
anchors.fill: parent
|
||||
radius: 8 * Style.px
|
||||
}
|
||||
}
|
||||
color: Style.main.text
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
selectionColor: Style.main.textBlue
|
||||
selectByKeyboard: true
|
||||
selectByMouse: true
|
||||
KeyNavigation.tab: clientVersion
|
||||
KeyNavigation.priority: KeyNavigation.BeforeItem
|
||||
}
|
||||
|
||||
ScrollBar.vertical : ScrollBar{}
|
||||
}
|
||||
|
||||
// Client
|
||||
TextLabel {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: descripWrapper.bottom
|
||||
topMargin: Style.dialog.fontSize
|
||||
}
|
||||
visible: clientVersion.visible
|
||||
width: parent.width/2.618
|
||||
text: qsTr ("Email client:", "in the bug report form, which third-party email client is being used")
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: clientVersion
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: descripWrapper.bottom
|
||||
topMargin: Style.dialog.fontSize
|
||||
}
|
||||
placeholderText: qsTr("e.g. Thunderbird", "in the bug report form, placeholder text for email client")
|
||||
width: parent.width/1.618
|
||||
|
||||
color : Style.dialog.text
|
||||
selectionColor : Style.main.textBlue
|
||||
selectByMouse : true
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
padding : Style.dialog.radiusButton
|
||||
|
||||
background: Rectangle {
|
||||
color : Style.dialog.background
|
||||
radius: Style.dialog.radiusButton
|
||||
border {
|
||||
color : root.inputBorderColor
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: FastBlur {
|
||||
anchors.fill: parent
|
||||
radius: 8 * Style.px
|
||||
}
|
||||
}
|
||||
onAccepted: userAddress.focus = true
|
||||
}
|
||||
|
||||
// Address
|
||||
TextLabel {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: clientVersion.visible ? clientVersion.bottom : descripWrapper.bottom
|
||||
topMargin: Style.dialog.fontSize
|
||||
}
|
||||
color: Style.dialog.text
|
||||
width: parent.width/2.618
|
||||
text: qsTr ("Contact email:", "in the bug report form, an email to contact the user at")
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userAddress
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: clientVersion.visible ? clientVersion.bottom : descripWrapper.bottom
|
||||
topMargin: Style.dialog.fontSize
|
||||
}
|
||||
placeholderText: "benjerry@protonmail.com"
|
||||
width: parent.width/1.618
|
||||
|
||||
color : Style.dialog.text
|
||||
selectionColor : Style.main.textBlue
|
||||
selectByMouse : true
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
padding : Style.dialog.radiusButton
|
||||
|
||||
background: Rectangle {
|
||||
color : Style.dialog.background
|
||||
radius: Style.dialog.radiusButton
|
||||
border {
|
||||
color : root.inputBorderColor
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: FastBlur {
|
||||
anchors.fill: parent
|
||||
radius: 8 * Style.px
|
||||
}
|
||||
}
|
||||
onAccepted: root.submit()
|
||||
}
|
||||
|
||||
// Note
|
||||
Row {
|
||||
id: securityNote
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: userAddress.bottom
|
||||
topMargin: Style.dialog.fontSize
|
||||
}
|
||||
|
||||
Text {
|
||||
id: securityNoteIcon
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
color: Style.dialog.text
|
||||
text : Style.fa.exclamation_triangle
|
||||
}
|
||||
|
||||
AccessibleText {
|
||||
id: securityNoteText
|
||||
anchors {
|
||||
left: securityNoteIcon.right
|
||||
leftMargin: 5 * Style.pt
|
||||
right: parent.right
|
||||
}
|
||||
wrapMode: Text.Wrap
|
||||
color: Style.dialog.text
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
text:
|
||||
qsTr("Bug reports are not end-to-end encrypted!", "The first part of warning in bug report form") + " " +
|
||||
qsTr("Please do not send any sensitive information.", "The second part of warning in bug report form") + " " +
|
||||
qsTr("Contact us at security@protonmail.com for critical security issues.", "The third part of warning in bug report form")
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
ButtonRounded {
|
||||
id: cancelButton
|
||||
anchors {
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
}
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr ("Cancel", "dismisses current action")
|
||||
onClicked : root.hide()
|
||||
}
|
||||
ButtonRounded {
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
isOpaque: true
|
||||
color_main: "white"
|
||||
color_minor: Style.main.textBlue
|
||||
fa_icon: Style.fa.send
|
||||
text: qsTr ("Send", "button sends bug report")
|
||||
onClicked : root.submit()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: notification
|
||||
property bool isOK: true
|
||||
visible: false
|
||||
color: background.color
|
||||
anchors.fill: background
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
color: Style.dialog.text
|
||||
width: background.width*0.6180
|
||||
text: notification.isOK ?
|
||||
qsTr ( "Bug report successfully sent." , "notification message about bug sending" ) :
|
||||
qsTr ( "Unable to submit bug report." , "notification message about bug sending" )
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Style.dialog.titleSize * Style.pt
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: notificationTimer
|
||||
interval: 3000
|
||||
repeat: false
|
||||
onTriggered : {
|
||||
notification.visible=false
|
||||
if (notification.isOK) root.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submit(){
|
||||
if(root.areInputsOK()){
|
||||
root.notify(go.sendBug(description.text, clientVersion.text, userAddress.text ))
|
||||
}
|
||||
}
|
||||
|
||||
function isEmpty(input){
|
||||
if (input.text=="") {
|
||||
input.focus=true
|
||||
input.placeholderText = qsTr("Field required", "a field that must be filled in to submit form")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function areInputsOK() {
|
||||
var isOK = true
|
||||
if (isEmpty(userAddress)) { isOK=false }
|
||||
if (clientVersion.visible && isEmpty(clientVersion)) { isOK=false }
|
||||
if (isEmpty(description)) { isOK=false }
|
||||
return isOK
|
||||
}
|
||||
|
||||
function clear() {
|
||||
description.text = ""
|
||||
clientVersion.text = ""
|
||||
notification.visible = false
|
||||
}
|
||||
|
||||
signal prefill()
|
||||
|
||||
function notify(isOK){
|
||||
notification.isOK = isOK
|
||||
notification.visible = true
|
||||
notificationTimer.start()
|
||||
}
|
||||
|
||||
|
||||
function show() {
|
||||
prefill()
|
||||
description.focus=true
|
||||
root.visible=true
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clear()
|
||||
root.visible=false
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Button with full window width containing two icons (left and right) and text
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
AccessibleButton {
|
||||
id: root
|
||||
property alias leftIcon : leftIcon
|
||||
property alias rightIcon : rightIcon
|
||||
property alias main : mainText
|
||||
|
||||
// dimensions
|
||||
width : viewContent.width
|
||||
height : Style.main.heightRow
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: Style.main.leftMargin
|
||||
rightPadding: Style.main.rightMargin
|
||||
|
||||
background : Rectangle{
|
||||
color: Qt.lighter(Style.main.background, root.hovered || root.activeFocus ? ( root.pressed ? 1.2: 1.1) :1.0)
|
||||
// line
|
||||
Rectangle {
|
||||
anchors.bottom : parent.bottom
|
||||
width : parent.width
|
||||
height : Style.main.heightLine
|
||||
color : Style.main.line
|
||||
}
|
||||
// pointing cursor
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
cursorShape : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
contentItem : Rectangle {
|
||||
color: "transparent"
|
||||
// Icon left
|
||||
Text {
|
||||
id: leftIcon
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : parent.left
|
||||
}
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.settings.iconSize * Style.pt
|
||||
}
|
||||
color : Style.main.textBlue
|
||||
text : Style.fa.hashtag
|
||||
}
|
||||
|
||||
// Icon/Text right
|
||||
Text {
|
||||
id: rightIcon
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
right : parent.right
|
||||
}
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.settings.iconSize * Style.pt
|
||||
}
|
||||
color : Style.main.textBlue
|
||||
text : Style.fa.hashtag
|
||||
}
|
||||
|
||||
// Label
|
||||
Text {
|
||||
id: mainText
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : leftIcon.right
|
||||
leftMargin : leftIcon.text!="" ? Style.main.leftMargin : 0
|
||||
}
|
||||
font.pointSize : Style.settings.fontSize * Style.pt
|
||||
color : Style.main.text
|
||||
text : root.text
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Classic button with icon and text
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
AccessibleButton {
|
||||
id: root
|
||||
property string fa_icon : ""
|
||||
property color color_main : Style.dialog.text
|
||||
property color color_minor : "transparent"
|
||||
property bool isOpaque : false
|
||||
|
||||
text : "undef"
|
||||
state : root.hovered || root.activeFocus ? "hover" : "normal"
|
||||
width : Style.dialog.widthButton
|
||||
height : Style.dialog.heightButton
|
||||
scale : root.pressed ? 0.96 : 1.00
|
||||
|
||||
background: Rectangle {
|
||||
border {
|
||||
color : root.color_main
|
||||
width : root.isOpaque ? 0 : Style.dialog.borderButton
|
||||
}
|
||||
radius : Style.dialog.radiusButton
|
||||
color : root.isOpaque ? root.color_minor : "transparent"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
cursorShape : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Rectangle {
|
||||
color: "transparent"
|
||||
|
||||
Row {
|
||||
id: mainText
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
color : color_main
|
||||
text : root.fa_icon=="" ? "" : root.fa_icon + " "
|
||||
}
|
||||
|
||||
Text {
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
color : color_main
|
||||
text : root.text
|
||||
}
|
||||
}
|
||||
|
||||
Glow {
|
||||
id: mainTextEffect
|
||||
anchors.fill : mainText
|
||||
source: mainText
|
||||
color: color_main
|
||||
opacity: 0.33
|
||||
}
|
||||
}
|
||||
|
||||
states :[
|
||||
State {name: "normal"; PropertyChanges{ target: mainTextEffect; radius: 0 ; visible: false } },
|
||||
State {name: "hover" ; PropertyChanges{ target: mainTextEffect; radius: 3*Style.px ; visible: true } }
|
||||
]
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
|
||||
CheckBox {
|
||||
id: root
|
||||
spacing: Style.dialog.spacing
|
||||
padding: 0
|
||||
property color textColor : Style.main.text
|
||||
property color checkedColor : Style.main.textBlue
|
||||
property color uncheckedColor : Style.main.textInactive
|
||||
property string checkedSymbol : Style.fa.check_square_o
|
||||
property string uncheckedSymbol : Style.fa.square_o
|
||||
property alias symbolPointSize : symbol.font.pointSize
|
||||
background: Rectangle {
|
||||
color: Style.transparent
|
||||
}
|
||||
indicator: Text {
|
||||
id: symbol
|
||||
text : root.checked ? root.checkedSymbol : root.uncheckedSymbol
|
||||
color : root.checked ? root.checkedColor : root.uncheckedColor
|
||||
font {
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
id: label
|
||||
text : root.text
|
||||
color : root.textColor
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: Style.dialog.iconSize + root.spacing
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// No border button with icon
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
AccessibleButton {
|
||||
id: root
|
||||
|
||||
property string iconText : Style.fa.hashtag
|
||||
property color textColor : Style.main.text
|
||||
property int fontSize : Style.main.fontSize
|
||||
property int iconSize : Style.main.iconSize
|
||||
property int margin : iconText!="" ? Style.main.leftMarginButton : 0.0
|
||||
property bool iconOnRight : false
|
||||
property bool textBold : false
|
||||
property bool textUnderline : false
|
||||
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: root.text
|
||||
font: showText.font
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: metricsIcon
|
||||
text : root.iconText
|
||||
font : showIcon.font
|
||||
}
|
||||
|
||||
scale : root.pressed ? 0.96 : root.activeFocus ? 1.05 : 1.0
|
||||
height : Math.max(metrics.height, metricsIcon.height)
|
||||
width : metricsIcon.width*1.5 + margin + metrics.width + 4.0
|
||||
padding : 0.0
|
||||
|
||||
background : Rectangle {
|
||||
color: Style.transparent
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
cursorShape : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
contentItem : Rectangle {
|
||||
color: Style.transparent
|
||||
Text {
|
||||
id: showIcon
|
||||
anchors {
|
||||
left : iconOnRight ? showText.right : parent.left
|
||||
leftMargin : iconOnRight ? margin : 0
|
||||
verticalCenter : parent.verticalCenter
|
||||
}
|
||||
font {
|
||||
pointSize : iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
color : textColor
|
||||
text : root.iconText
|
||||
}
|
||||
|
||||
Text {
|
||||
id: showText
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : iconOnRight ? parent.left : showIcon.right
|
||||
leftMargin : iconOnRight ? 0 : margin
|
||||
}
|
||||
color : textColor
|
||||
font {
|
||||
pointSize : root.fontSize * Style.pt
|
||||
bold: root.textBold
|
||||
underline: root.textUnderline
|
||||
}
|
||||
text : root.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
// Copyright (c) 2021 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 adding new user
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Layouts 1.3
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
StackLayout {
|
||||
id: root
|
||||
property string title : "title"
|
||||
property string subtitle : ""
|
||||
property alias timer : timer
|
||||
property alias warning : warningText
|
||||
property bool isDialogBusy : false
|
||||
property real titleHeight : 2*titleText.anchors.topMargin + titleText.height + (warningText.visible ? warningText.anchors.topMargin + warningText.height : 0)
|
||||
property Item background : Rectangle {
|
||||
parent: root
|
||||
width: root.width
|
||||
height: root.height
|
||||
color : Style.dialog.background
|
||||
visible: root.visible
|
||||
z: -1
|
||||
|
||||
// Looks like StackLayout explicatly sets visible=false to all viasual children except selected.
|
||||
// We want this background to be also visible.
|
||||
onVisibleChanged: {
|
||||
if (visible != parent.visible) {
|
||||
visible = parent.visible
|
||||
}
|
||||
}
|
||||
|
||||
AccessibleText {
|
||||
id: titleText
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: Style.dialog.titleSize
|
||||
}
|
||||
font.pointSize : Style.dialog.titleSize * Style.pt
|
||||
color : Style.dialog.text
|
||||
text : root.title
|
||||
}
|
||||
|
||||
AccessibleText {
|
||||
id: subtitleText
|
||||
anchors {
|
||||
top: titleText.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
font.pointSize : Style.dialog.fontSize * Style.pt
|
||||
color : Style.dialog.text
|
||||
text : root.subtitle
|
||||
visible : root.subtitle != ""
|
||||
}
|
||||
|
||||
AccessibleText {
|
||||
id:warningText
|
||||
anchors {
|
||||
top: subtitleText.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
font {
|
||||
bold: true
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
text : ""
|
||||
color: Style.main.textBlue
|
||||
visible: false
|
||||
width: root.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// prevent any action below
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: Style.dialog.titleSize
|
||||
rightMargin: Style.dialog.titleSize
|
||||
}
|
||||
visible : !isDialogBusy
|
||||
iconText : Style.fa.times
|
||||
text : ""
|
||||
onClicked : root.hide()
|
||||
Accessible.description : qsTr("Close dialog %1", "Click to exit modal.").arg(root.title)
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Grouping
|
||||
Accessible.name: title
|
||||
Accessible.description: title
|
||||
Accessible.focusable: true
|
||||
|
||||
onVisibleChanged: {
|
||||
if (background.visible != visible) {
|
||||
background.visible = visible
|
||||
}
|
||||
}
|
||||
|
||||
visible : false
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : titleBar.bottom
|
||||
bottom : parent.bottom
|
||||
}
|
||||
currentIndex : 0
|
||||
|
||||
|
||||
signal show()
|
||||
signal hide()
|
||||
|
||||
function incrementCurrentIndex() {
|
||||
root.currentIndex++
|
||||
}
|
||||
|
||||
function decrementCurrentIndex() {
|
||||
root.currentIndex--
|
||||
}
|
||||
|
||||
onShow: {
|
||||
root.visible = true
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
|
||||
onHide: {
|
||||
root.timer.stop()
|
||||
root.currentIndex=0
|
||||
root.visible = false
|
||||
root.timer.stop()
|
||||
gui.winMain.tabbar.focusButton()
|
||||
}
|
||||
|
||||
// QTimer is recommeded solution for creating trheads : http://doc.qt.io/qt-5/qtquick-threading-example.html
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 300 // wait for transistion
|
||||
repeat: false
|
||||
}
|
||||
}
|
|
@ -1,465 +0,0 @@
|
|||
// Copyright (c) 2021 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 adding new user
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : ""
|
||||
|
||||
signal createAccount()
|
||||
|
||||
property alias inputPassword : inputPassword
|
||||
property alias input2FAuth : input2FAuth
|
||||
property alias inputPasswMailbox : inputPasswMailbox
|
||||
//
|
||||
property alias username : inputUsername.text
|
||||
property alias usernameElided : usernameMetrics.elidedText
|
||||
|
||||
isDialogBusy : currentIndex==waitingAuthIndex || currentIndex==addingAccIndex
|
||||
|
||||
property bool isFirstAccount: false
|
||||
|
||||
property color buttonOpaqueMain : "white"
|
||||
|
||||
|
||||
property int origin: 0
|
||||
property int nameAndPasswordIndex : 0
|
||||
property int waitingAuthIndex : 2
|
||||
property int twoFAIndex : 1
|
||||
property int mailboxIndex : 3
|
||||
property int addingAccIndex : 4
|
||||
property int newAccountIndex : 5
|
||||
|
||||
|
||||
signal cancel()
|
||||
signal okay()
|
||||
|
||||
TextMetrics {
|
||||
id: usernameMetrics
|
||||
font: dialogWaitingAuthText.font
|
||||
elideWidth : Style.dialog.widthInput
|
||||
elide : Qt.ElideMiddle
|
||||
text : root.username
|
||||
}
|
||||
|
||||
Column { // 0
|
||||
id: dialogNameAndPassword
|
||||
property int heightInputs : inputUsername.height + buttonRow.height + middleSep.height + inputPassword.height + middleSepPassw.height
|
||||
|
||||
Rectangle {
|
||||
id: topSep
|
||||
color : "transparent"
|
||||
width : Style.main.dummy
|
||||
// Hacky hack: +10 is to make title of Dialog bigger so longer error can fit just fine.
|
||||
height : root.height/2 + 10 - (dialogNameAndPassword.heightInputs)/2
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: inputUsername
|
||||
iconText : Style.fa.user_circle
|
||||
label : qsTr("Username", "enter username to add account")
|
||||
onAccepted : inputPassword.focusInput = true
|
||||
}
|
||||
|
||||
Rectangle { id: middleSepPassw; color : "transparent"; width : Style.main.dummy; height : Style.dialog.heightSeparator}
|
||||
|
||||
InputField {
|
||||
id: inputPassword
|
||||
label : qsTr("Password", "password entry field")
|
||||
iconText : Style.fa.lock
|
||||
isPassword : true
|
||||
onAccepted : root.okay()
|
||||
}
|
||||
|
||||
Rectangle { id: middleSep; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator }
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.fontSize
|
||||
ButtonRounded {
|
||||
id:buttonCancel
|
||||
fa_icon : Style.fa.times
|
||||
text : qsTr("Cancel", "dismisses current action")
|
||||
color_main : Style.dialog.text
|
||||
onClicked : root.cancel()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonNext
|
||||
fa_icon : Style.fa.check
|
||||
text : qsTr("Next", "navigate to next page in add account flow")
|
||||
color_main : buttonOpaqueMain
|
||||
color_minor : Style.dialog.textBlue
|
||||
isOpaque : true
|
||||
onClicked : root.okay()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color : "transparent"
|
||||
width : Style.main.dummy
|
||||
height : root.height - (topSep.height + dialogNameAndPassword.heightInputs + Style.main.bottomMargin + signUpForAccount.height)
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: signUpForAccount
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
fontSize : Style.dialog.fontSize
|
||||
iconSize : Style.dialog.fontSize
|
||||
iconText : "+"
|
||||
text : qsTr ("Sign Up for an Account", "takes user to web page where they can create a ProtonMail account")
|
||||
textBold : true
|
||||
textUnderline : true
|
||||
textColor : Style.dialog.text
|
||||
onClicked : root.createAccount()
|
||||
}
|
||||
}
|
||||
|
||||
Column { // 1
|
||||
id: dialog2FA
|
||||
property int heightInputs : buttonRowPassw.height + middleSep2FA.height + input2FAuth.height
|
||||
|
||||
Rectangle {
|
||||
color : "transparent"
|
||||
width : Style.main.dummy
|
||||
height : (root.height - dialog2FA.heightInputs)/2
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: input2FAuth
|
||||
label : qsTr("Two Factor Code", "two factor code entry field")
|
||||
iconText : Style.fa.lock
|
||||
onAccepted : root.okay()
|
||||
}
|
||||
|
||||
Rectangle { id: middleSep2FA; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator }
|
||||
|
||||
Row {
|
||||
id: buttonRowPassw
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.fontSize
|
||||
ButtonRounded {
|
||||
id: buttonBack
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Back", "navigate back in add account flow")
|
||||
color_main: Style.dialog.text
|
||||
onClicked : root.cancel()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonNextTwo
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Next", "navigate to next page in add account flow")
|
||||
color_main: buttonOpaqueMain
|
||||
color_minor: Style.dialog.textBlue
|
||||
isOpaque: true
|
||||
onClicked : root.okay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column { // 2
|
||||
id: dialogWaitingAuth
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height-dialogWaitingAuthText.height) /2 }
|
||||
Text {
|
||||
id: dialogWaitingAuthText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
text : qsTr("Logging in") +"\n" + root.usernameElided
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Column { // 3
|
||||
id: dialogMailboxPassword
|
||||
property int heightInputs : buttonRowMailbox.height + inputPasswMailbox.height + middleSepMailbox.height
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogMailboxPassword.heightInputs)/2}
|
||||
|
||||
InputField {
|
||||
id: inputPasswMailbox
|
||||
label : qsTr("Mailbox password for %1", "mailbox password entry field").arg(root.usernameElided)
|
||||
iconText : Style.fa.lock
|
||||
isPassword : true
|
||||
onAccepted : root.okay()
|
||||
}
|
||||
|
||||
Rectangle { id: middleSepMailbox; color : "transparent"; width : Style.main.dummy; height : 2*Style.dialog.heightSeparator }
|
||||
|
||||
Row {
|
||||
id: buttonRowMailbox
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.fontSize
|
||||
ButtonRounded {
|
||||
id: buttonBackBack
|
||||
fa_icon: Style.fa.times
|
||||
text: qsTr("Back", "navigate back in add account flow")
|
||||
color_main: Style.dialog.text
|
||||
onClicked : root.cancel()
|
||||
}
|
||||
ButtonRounded {
|
||||
id: buttonLogin
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Next", "navigate to next page in add account flow")
|
||||
color_main: buttonOpaqueMain
|
||||
color_minor: Style.dialog.textBlue
|
||||
isOpaque: true
|
||||
onClicked : root.okay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column { // 4
|
||||
id: dialogWaitingAccount
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogWaitingAccountText.height )/2 }
|
||||
|
||||
Text {
|
||||
id: dialogWaitingAccountText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
bold : true
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
text : qsTr("Adding account, please wait ...", "displayed after user has logged in, before new account is displayed")
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
|
||||
Column { // 5
|
||||
id: dialogFirstUserAdded
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : (root.height - dialogWaitingAccountText.height - okButton.height*2 )/2 }
|
||||
|
||||
Text {
|
||||
id: textFirstUser
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
bold : false
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
width: 2*root.width/3
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
text: "<html><style>a { font-weight: bold; text-decoration: none; color: white;}</style>"+
|
||||
qsTr("Now you need to configure your client(s) to use the Bridge. Instructions for configuring your client can be found at", "") +
|
||||
"<br/><a href=\"https://protonmail.com/bridge/clients\">https://protonmail.com/bridge/clients</a>.<html>"
|
||||
wrapMode: Text.Wrap
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink=="" ? Qt.PointingHandCursor : Qt.WaitCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { color : "transparent"; width : Style.main.dummy; height : okButton.height}
|
||||
|
||||
ButtonRounded{
|
||||
id: okButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color_main: buttonOpaqueMain
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
onClicked: root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
function clear_user() {
|
||||
inputUsername.text = ""
|
||||
inputUsername.rightIcon = ""
|
||||
}
|
||||
|
||||
function clear_passwd() {
|
||||
inputPassword.text = ""
|
||||
inputPassword.rightIcon = ""
|
||||
inputPassword.hidePasswordText()
|
||||
}
|
||||
|
||||
|
||||
function clear_2fa() {
|
||||
input2FAuth.text = ""
|
||||
input2FAuth.rightIcon = ""
|
||||
}
|
||||
|
||||
function clear_passwd_mailbox() {
|
||||
inputPasswMailbox.text = ""
|
||||
inputPasswMailbox.rightIcon = ""
|
||||
inputPasswMailbox.hidePasswordText()
|
||||
}
|
||||
|
||||
onCancel : {
|
||||
root.warning.visible=false
|
||||
if (currentIndex==0) {
|
||||
root.hide()
|
||||
} else {
|
||||
clear_passwd()
|
||||
clear_passwd_mailbox()
|
||||
currentIndex=0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function check_inputs() {
|
||||
var isOK = true
|
||||
switch (currentIndex) {
|
||||
case nameAndPasswordIndex :
|
||||
isOK &= inputUsername.checkNonEmpty()
|
||||
isOK &= inputPassword.checkNonEmpty()
|
||||
break
|
||||
case twoFAIndex :
|
||||
isOK &= input2FAuth.checkNonEmpty()
|
||||
break
|
||||
case mailboxIndex :
|
||||
isOK &= inputPasswMailbox.checkNonEmpty()
|
||||
break
|
||||
}
|
||||
if (isOK) {
|
||||
warning.visible = false
|
||||
warning.text= ""
|
||||
} else {
|
||||
setWarning(qsTr("Field required", "a field that must be filled in to submit form"),0)
|
||||
}
|
||||
return isOK
|
||||
}
|
||||
|
||||
function setWarning(msg, changeIndex) {
|
||||
// show message
|
||||
root.warning.text = msg
|
||||
root.warning.visible = true
|
||||
}
|
||||
|
||||
|
||||
onOkay : {
|
||||
var isOK = check_inputs()
|
||||
if (isOK) {
|
||||
root.origin = root.currentIndex
|
||||
switch (root.currentIndex) {
|
||||
case nameAndPasswordIndex:
|
||||
case twoFAIndex:
|
||||
root.currentIndex = waitingAuthIndex
|
||||
break;
|
||||
case mailboxIndex:
|
||||
root.currentIndex = addingAccIndex
|
||||
}
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
onShow: {
|
||||
root.title = qsTr ("Log in to your ProtonMail account", "displayed on screen when user enters username to begin adding account")
|
||||
root.warning.visible = false
|
||||
inputUsername.forceFocus()
|
||||
root.isFirstAccount = go.isFirstStart && accountsModel.count==0
|
||||
}
|
||||
|
||||
function startAgain() {
|
||||
clear_passwd()
|
||||
clear_2fa()
|
||||
clear_passwd_mailbox()
|
||||
root.currentIndex = nameAndPasswordIndex
|
||||
root.inputPassword.focusInput = true
|
||||
}
|
||||
|
||||
function finishLogin(){
|
||||
root.currentIndex = addingAccIndex
|
||||
var auth = go.addAccount(inputPasswMailbox.text)
|
||||
if (auth<0) {
|
||||
startAgain()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: timer
|
||||
|
||||
onTriggered : {
|
||||
timer.repeat = false
|
||||
switch (root.origin) {
|
||||
case nameAndPasswordIndex:
|
||||
var auth = go.login(inputUsername.text, inputPassword.text)
|
||||
if (auth < 0) {
|
||||
startAgain()
|
||||
break
|
||||
}
|
||||
if (auth == 1) {
|
||||
root.currentIndex = twoFAIndex
|
||||
root.input2FAuth.focusInput = true
|
||||
break
|
||||
}
|
||||
if (auth == 2) {
|
||||
root.currentIndex = mailboxIndex
|
||||
root.inputPasswMailbox.focusInput = true
|
||||
break
|
||||
}
|
||||
root.inputPasswMailbox.text = inputPassword.text
|
||||
root.finishLogin()
|
||||
break;
|
||||
case twoFAIndex:
|
||||
var auth = go.auth2FA(input2FAuth.text)
|
||||
if (auth < 0) {
|
||||
startAgain()
|
||||
break
|
||||
}
|
||||
if (auth == 1) {
|
||||
root.currentIndex = mailboxIndex
|
||||
root.inputPasswMailbox.focusInput = true
|
||||
break
|
||||
}
|
||||
root.inputPasswMailbox.text = inputPassword.text
|
||||
root.finishLogin()
|
||||
break;
|
||||
case mailboxIndex:
|
||||
root.finishLogin()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onHide: {
|
||||
// because hide slot is conneceted to processFinished it will update
|
||||
// the list evertyime `go` obejcet is finished
|
||||
clear_passwd()
|
||||
clear_passwd_mailbox()
|
||||
clear_2fa()
|
||||
clear_user()
|
||||
go.loadAccounts()
|
||||
if (root.isFirstAccount && accountsModel.count==1) {
|
||||
root.isFirstAccount=false
|
||||
root.currentIndex=5
|
||||
root.show()
|
||||
root.title=qsTr("Success, Account Added!", "shown after successful account addition")
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Enter) {
|
||||
root.okay()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright (c) 2021 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 ProtonUI 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title : qsTr(
|
||||
"Common connection problems and solutions",
|
||||
"Title of the network troubleshooting modal"
|
||||
)
|
||||
isDialogBusy: false // can close
|
||||
property var parContent : [
|
||||
[
|
||||
qsTr("Allow alternative routing" , "Paragraph title"),
|
||||
qsTr(
|
||||
"In case Proton sites are blocked, this setting allows Bridge "+
|
||||
"to try alternative network routing to reach Proton, which can "+
|
||||
"be useful for bypassing firewalls or network issues. We recommend "+
|
||||
"keeping this setting on for greater reliability. "+
|
||||
'<a href="https://protonmail.com/blog/anti-censorship-alternative-routing/">Learn more</a>'+
|
||||
" and "+
|
||||
'<a href="showProxy">enable here</a>'+
|
||||
".",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("No internet connection" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Please make sure that your internet connection is working.",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Internet Service Provider (ISP) problem" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Try connecting to Proton from a different network (or use "+
|
||||
'<a href="https://protonvpn.com/">ProtonVPN</a>'+
|
||||
" or "+
|
||||
'<a href="https://torproject.org/">Tor</a>'+
|
||||
").",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Government block" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Your country may be blocking access to Proton. Try using "+
|
||||
'<a href="https://protonvpn.com/">ProtonVPN</a>'+
|
||||
" (or any other VPN) or "+
|
||||
'<a href="https://torproject.org/">Tor</a>'+
|
||||
".",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Antivirus interference" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Temporarily disable or remove your antivirus software.",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Proxy/Firewall interference" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Disable any proxies or firewalls, or contact your network administrator.",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Still can’t find a solution" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Contact us directly through our "+
|
||||
'<a href="https://protonmail.com/support-form">support form</a>'+
|
||||
", email (support@protonmail.com), or "+
|
||||
'<a href="https://twitter.com/ProtonMail">Twitter</a>'+
|
||||
".",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
qsTr("Proton is down" , "Paragraph title"),
|
||||
qsTr(
|
||||
"Check "+
|
||||
'<a href="https://protonstatus.com/">Proton Status</a>'+
|
||||
" for our system status.",
|
||||
"Paragraph content"
|
||||
),
|
||||
],
|
||||
|
||||
]
|
||||
|
||||
Item {
|
||||
AccessibleText {
|
||||
anchors.centerIn: parent
|
||||
color: Style.old.pm_white
|
||||
linkColor: color
|
||||
width: parent.width - 50 * Style.px
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: Style.main.fontSize*Style.pt
|
||||
onLinkActivated: {
|
||||
if (link=="showProxy") {
|
||||
dialogGlobal.state= "toggleAllowProxy"
|
||||
dialogGlobal.show()
|
||||
} else {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
text: {
|
||||
var content=""
|
||||
for (var i=0; i<root.parContent.length; i++) {
|
||||
var par = root.parContent[i]
|
||||
content += "<p>"
|
||||
content += "<b>"+par[0]+":</b> "
|
||||
content += par[1]
|
||||
content += "</p>\n"
|
||||
}
|
||||
return content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// default options to make button accessible
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
property bool hasError : false
|
||||
property bool forceUpdate : false
|
||||
|
||||
signal cancel()
|
||||
signal okay()
|
||||
|
||||
title: forceUpdate ?
|
||||
qsTr("Update %1 now", "title of force update dialog").arg(go.programTitle):
|
||||
qsTr("Update to %1 %2", "title of normal update dialog").arg(go.programTitle).arg(go.updateVersion)
|
||||
|
||||
isDialogBusy: currentIndex==1 || forceUpdate
|
||||
|
||||
Rectangle { // 0: Release notes and confirm
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.transparent
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5*Style.dialog.spacing
|
||||
|
||||
AccessibleText {
|
||||
id:introduction
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Style.dialog.text
|
||||
linkColor: Style.dialog.textBlue
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
width: 2*root.width/3
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
text: {
|
||||
if (forceUpdate) {
|
||||
if (go.updateCanInstall) {
|
||||
return qsTr('You need to update this app to continue using it.<br>
|
||||
Update now or manually download the most recent version here:<br>
|
||||
<a href="%1">%1</a><br>
|
||||
<a href="https://protonmail.com/support/knowledge-base/update-required/">Learn why</a> you need to update',
|
||||
"Message for force-update").arg(go.updateLandingPage)
|
||||
} else {
|
||||
return qsTr('You need to update this app to continue using it.<br>
|
||||
Download the most recent version here:<br>
|
||||
<a href="%1">%1</a><br>
|
||||
<a href="https://protonmail.com/support/knowledge-base/update-required/">Learn why</a> you need to update',
|
||||
"Message for force-update").arg(go.updateLandingPage)
|
||||
}
|
||||
}
|
||||
|
||||
if (go.updateCanInstall) {
|
||||
return qsTr('Update to the newest version or download it from:<br>
|
||||
<a href="%1">%1</a><br>
|
||||
<a href="%2">View release notes</a>',
|
||||
"Message for manual update").arg(go.updateLandingPage).arg(go.updateReleaseNotesLink)
|
||||
} else {
|
||||
return qsTr('Update to the newest version from:<br>
|
||||
<a href="%1">%1</a><br>
|
||||
<a href="%2">View release notes</a>',
|
||||
"Message for manual update").arg(go.updateLandingPage).arg(go.updateReleaseNotesLink)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onLinkActivated : {
|
||||
console.log("clicked link:", link)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: introduction.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
id: autoUpdate
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Automatically update in the future", "Checkbox label for using autoupdates later on")
|
||||
checked: go.isAutoUpdate
|
||||
onToggled: go.toggleAutoUpdate()
|
||||
visible: !root.forceUpdate && (go.isAutoUpdate != undefined)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
ButtonRounded {
|
||||
fa_icon: Style.fa.times
|
||||
text: root.forceUpdate ? qsTr("Quit") : qsTr("Cancel")
|
||||
color_main: Style.dialog.text
|
||||
onClicked: root.forceUpdate ? Qt.quit() : root.cancel()
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
fa_icon: Style.fa.check
|
||||
text: qsTr("Update")
|
||||
visible: go.updateCanInstall
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
onClicked: root.okay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // 1: Installing update
|
||||
id: updateStatus
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.transparent
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
AccessibleText {
|
||||
color: Style.dialog.text
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: false
|
||||
}
|
||||
width: 2*root.width/3
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Updating...")
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: updateProgressBar
|
||||
width: 2*updateStatus.width/3
|
||||
height: Style.exporting.rowHeight
|
||||
//implicitWidth : 2*updateStatus.width/3
|
||||
//implicitHeight : Style.exporting.rowHeight
|
||||
indeterminate: true
|
||||
//value: 0.5
|
||||
//property int current: go.total * go.progress
|
||||
//property bool isFinished: finishedPartBar.width == progressbar.width
|
||||
background: Rectangle {
|
||||
radius : Style.exporting.boxRadius
|
||||
color : Style.exporting.progressBackground
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
clip: true
|
||||
Rectangle {
|
||||
id: progressIndicator
|
||||
width : updateProgressBar.indeterminate ? 50 : parent.width * updateProgressBar.visualPosition
|
||||
height : parent.height
|
||||
radius : Style.exporting.boxRadius
|
||||
gradient : Gradient {
|
||||
GradientStop { position: 0.00; color: Qt.lighter(Style.main.textBlue,1.1) }
|
||||
GradientStop { position: 0.66; color: Style.main.textBlue }
|
||||
GradientStop { position: 1.00; color: Qt.darker(Style.main.textBlue,1.1) }
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation { duration:300; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
running: updateProgressBar.visible && updateProgressBar.indeterminate
|
||||
loops: Animation.Infinite
|
||||
|
||||
SmoothedAnimation {
|
||||
target: progressIndicator
|
||||
property: "x"
|
||||
from: 0
|
||||
to: updateProgressBar.width - progressIndicator.width
|
||||
duration: 2000
|
||||
}
|
||||
|
||||
SmoothedAnimation {
|
||||
target: progressIndicator
|
||||
property: "x"
|
||||
from: updateProgressBar.width - progressIndicator.width
|
||||
to: 0
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
color: Style.main.background
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // 2: Something went wrong / All ok, closing bridge
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: Style.transparent
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5*Style.dialog.spacing
|
||||
|
||||
AccessibleText {
|
||||
color: Style.dialog.text
|
||||
linkColor: Style.dialog.textBlue
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
width: 2*root.width/3
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
text: !root.hasError ? qsTr('%1 will restart now to finish the update.', "message after successful update").arg(go.programTitle) :
|
||||
qsTr('<b>The update procedure was not successful!</b><br>Please follow the download link and update manually. <br><br><a href="%1">%1</a>').arg(go.updateLandingPage)
|
||||
|
||||
onLinkActivated : {
|
||||
console.log("clicked link:", link)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
ButtonRounded{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: root.hasError
|
||||
text: qsTr("Close")
|
||||
onClicked: root.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
root.hasError = false
|
||||
go.progress = 0.0
|
||||
go.progressDescription = "0"
|
||||
}
|
||||
|
||||
function finished(hasError) {
|
||||
root.hasError = hasError
|
||||
root.currentIndex = 2
|
||||
}
|
||||
|
||||
onShow: {
|
||||
root.clear()
|
||||
}
|
||||
|
||||
onHide: {
|
||||
root.clear()
|
||||
}
|
||||
|
||||
onOkay: {
|
||||
switch (root.currentIndex) {
|
||||
case 0:
|
||||
go.startManualUpdate()
|
||||
root.currentIndex = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onCancel: {
|
||||
root.hide()
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// one line input text field with label
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Dialogs 1.0
|
||||
import ProtonUI 1.0
|
||||
|
||||
Row {
|
||||
id: root
|
||||
spacing: Style.dialog.spacing
|
||||
|
||||
property string title : "title"
|
||||
|
||||
property alias path: inputPath.text
|
||||
property alias inputPath: inputPath
|
||||
property alias dialogVisible: pathDialog.visible
|
||||
|
||||
InputBox {
|
||||
id: inputPath
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
}
|
||||
spacing: Style.dialog.spacing
|
||||
field {
|
||||
height: browseButton.height
|
||||
width: root.width - root.spacing - browseButton.width
|
||||
}
|
||||
|
||||
label: title
|
||||
Component.onCompleted: sanitizePath(pathDialog.shortcuts.home)
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
id: browseButton
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Style.dialog.heightInput
|
||||
color_main: Style.main.textBlue
|
||||
fa_icon: Style.fa.folder_open
|
||||
text: qsTr("Browse", "click to look through directory for a file or folder")
|
||||
onClicked: pathDialog.visible = true
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: pathDialog
|
||||
title: root.title
|
||||
folder: shortcuts.home
|
||||
onAccepted: sanitizePath(pathDialog.fileUrl.toString())
|
||||
selectFolder: true
|
||||
}
|
||||
|
||||
function sanitizePath(path) {
|
||||
var pattern = "file://"
|
||||
if (go.goos=="windows") pattern+="/"
|
||||
inputPath.text = path.replace(pattern, "")
|
||||
}
|
||||
|
||||
function checkNonEmpty() {
|
||||
return inputPath.text != ""
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// on hover information
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
|
||||
Text { // info icon
|
||||
id:root
|
||||
property alias info : tip.text
|
||||
font {
|
||||
family: Style.fontawesome.name
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
}
|
||||
text: Style.fa.info_circle
|
||||
color: Style.main.textDisabled
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onEntered : tip.visible=true
|
||||
onExited : tip.visible=false
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
id: tip
|
||||
width: Style.bubble.width
|
||||
x: - 0.2*tip.width
|
||||
y: - tip.height
|
||||
|
||||
topPadding : Style.main.fontSize/2
|
||||
bottomPadding : Style.main.fontSize/2
|
||||
leftPadding : Style.bubble.widthPane + Style.dialog.spacing
|
||||
rightPadding: Style.dialog.spacing
|
||||
delay: 800
|
||||
|
||||
background : Rectangle {
|
||||
id: bck
|
||||
color: Style.bubble.paneBackground
|
||||
radius : Style.bubble.radius
|
||||
|
||||
|
||||
Text {
|
||||
id: icon
|
||||
color: Style.bubble.background
|
||||
text: Style.fa.info_circle
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
}
|
||||
anchors {
|
||||
verticalCenter : bck.verticalCenter
|
||||
left : bck.left
|
||||
leftMargin : (Style.bubble.widthPane - icon.width) / 2
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // right edge
|
||||
anchors {
|
||||
fill : bck
|
||||
leftMargin : Style.bubble.widthPane
|
||||
}
|
||||
radius: parent.radius
|
||||
color: Style.bubble.background
|
||||
}
|
||||
|
||||
Rectangle { // center background
|
||||
anchors {
|
||||
fill : parent
|
||||
leftMargin : Style.bubble.widthPane
|
||||
rightMargin : Style.bubble.widthPane
|
||||
}
|
||||
color: Style.bubble.background
|
||||
}
|
||||
}
|
||||
|
||||
contentItem : Text {
|
||||
text: tip.text
|
||||
color: Style.bubble.text
|
||||
wrapMode: Text.Wrap
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,371 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Important information under title bar
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var iTry: 0
|
||||
property var second: 1000 // convert millisecond to second
|
||||
property bool isVisible: true
|
||||
property var fontSize : 1.2 * Style.main.fontSize
|
||||
color : "black"
|
||||
state: "upToDate"
|
||||
|
||||
Row {
|
||||
id: messageRow
|
||||
anchors.centerIn: root
|
||||
visible: root.isVisible
|
||||
spacing: Style.main.leftMarginButton
|
||||
|
||||
AccessibleText {
|
||||
id: message
|
||||
font.pointSize: root.fontSize * Style.pt
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: linkText
|
||||
anchors.verticalCenter : message.verticalCenter
|
||||
iconText : " "
|
||||
fontSize : root.fontSize
|
||||
textUnderline: true
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: actionText
|
||||
anchors.verticalCenter : message.verticalCenter
|
||||
iconText : " "
|
||||
fontSize : root.fontSize
|
||||
textUnderline: true
|
||||
}
|
||||
Text {
|
||||
id: separatorText
|
||||
anchors.baseline : message.baseline
|
||||
color: Style.main.text
|
||||
font {
|
||||
pointSize : root.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
}
|
||||
ClickIconText {
|
||||
id: action2Text
|
||||
anchors.verticalCenter : message.verticalCenter
|
||||
iconText : ""
|
||||
fontSize : root.fontSize
|
||||
textUnderline: true
|
||||
}
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
id: closeSign
|
||||
anchors.verticalCenter : messageRow.verticalCenter
|
||||
anchors.right: root.right
|
||||
iconText : Style.fa.close
|
||||
fontSize : root.fontSize
|
||||
textUnderline: true
|
||||
}
|
||||
|
||||
onStateChanged : {
|
||||
switch (root.state) {
|
||||
case "internetCheck":
|
||||
break;
|
||||
case "noInternet" :
|
||||
break;
|
||||
case "oldVersion":
|
||||
break;
|
||||
case "forceUpdate":
|
||||
break;
|
||||
case "upToDate":
|
||||
break;
|
||||
case "updateRestart":
|
||||
break;
|
||||
case "updateError":
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "internetCheck"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textOrange
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.background
|
||||
text: qsTr("Checking connection. Please wait...", "displayed after user retries internet connection")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "noInternet"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textRed
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.line
|
||||
text: qsTr("Cannot contact server. Please wait...", "displayed when the app is disconnected from the internet or server has problems")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
text: "|"
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: true
|
||||
text: qsTr("Troubleshoot", "Show modal screen with additional tips for troubleshooting connection issues")
|
||||
onClicked: {
|
||||
dialogConnectionTroubleshoot.show()
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "oldVersion"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textBlue
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.background
|
||||
text: qsTr("Update available", "displayed in a notification when an app update is available")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: true
|
||||
text: qsTr("Release Notes", "display the release notes from the new version")
|
||||
onClicked: gui.openReleaseNotes()
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: true
|
||||
text: qsTr("Update", "click to update to a new version when one is available")
|
||||
onClicked: {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: true
|
||||
onClicked: {
|
||||
go.updateState = "upToDate"
|
||||
}
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "forceUpdate"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textRed
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.line
|
||||
text: qsTr("%1 is outdated.", "displayed in a notification when app is outdated").arg(go.programTitle)
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: true
|
||||
text: qsTr("Update", "click to update to a new version when one is available")
|
||||
onClicked: {
|
||||
winMain.dialogUpdate.show()
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "upToDate"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 0
|
||||
isVisible: false
|
||||
color: Style.main.textBlue
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.background
|
||||
text: ""
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "updateRestart"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textBlue
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.background
|
||||
text: qsTr("%1 update is ready", "displayed in a notification when an app update is installed and restart is needed").arg(go.programTitle)
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: true
|
||||
text: qsTr("Restart now", "click to restart application as new version was installed")
|
||||
onClicked: {
|
||||
go.setToRestart()
|
||||
Qt.quit()
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "updateError"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
isVisible: true
|
||||
color: Style.main.textRed
|
||||
}
|
||||
PropertyChanges {
|
||||
target: message
|
||||
color: Style.main.line
|
||||
text: qsTr("Sorry, %1 couldn't update.", "displayed in a notification when app failed to autoupdate").arg(go.programTitle)
|
||||
}
|
||||
PropertyChanges {
|
||||
target: linkText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: actionText
|
||||
visible: true
|
||||
text: qsTr("Please update manually", "click to open download page to update manally")
|
||||
onClicked: {
|
||||
Qt.openUrlExternally(go.updateLandingPage)
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: separatorText
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: action2Text
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeSign
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// one line input text field with label
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import ProtonUI 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Column {
|
||||
id: root
|
||||
property alias label: textlabel.text
|
||||
property alias placeholderText: inputField.placeholderText
|
||||
property alias echoMode: inputField.echoMode
|
||||
property alias text: inputField.text
|
||||
property alias field: inputField
|
||||
|
||||
signal accepted()
|
||||
|
||||
spacing: Style.dialog.heightSeparator
|
||||
|
||||
Text {
|
||||
id: textlabel
|
||||
font {
|
||||
pointSize: Style.dialog.fontSize * Style.pt
|
||||
bold: true
|
||||
}
|
||||
color: Style.dialog.text
|
||||
}
|
||||
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
width: Style.dialog.widthInput
|
||||
height: Style.dialog.heightButton
|
||||
selectByMouse : true
|
||||
selectionColor : Style.main.textBlue
|
||||
padding : Style.dialog.radiusButton
|
||||
color : Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
background: Rectangle {
|
||||
color : Style.dialog.background
|
||||
radius: Style.dialog.radiusButton
|
||||
border {
|
||||
color : Style.dialog.line
|
||||
width : Style.dialog.borderInput
|
||||
}
|
||||
layer.enabled: true
|
||||
layer.effect: FastBlur {
|
||||
anchors.fill: parent
|
||||
radius: 8 * Style.px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target : inputField
|
||||
onAccepted : root.accepted()
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// one line input text field with label
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
Column {
|
||||
id: root
|
||||
property alias focusInput : inputField.focus
|
||||
property alias label : textlabel.text
|
||||
property alias iconText : iconInput.text
|
||||
property alias placeholderText : inputField.placeholderText
|
||||
property alias text : inputField.text
|
||||
property bool isPassword : false
|
||||
property string rightIcon : ""
|
||||
|
||||
signal accepted()
|
||||
signal editingFinished()
|
||||
|
||||
spacing: Style.dialog.heightSeparator
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
|
||||
AccessibleText {
|
||||
id: textlabel
|
||||
anchors.left : parent.left
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color : Style.dialog.text
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: inputWrap
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
width : Style.dialog.widthInput
|
||||
height : Style.dialog.heightInput
|
||||
color : "transparent"
|
||||
|
||||
Text {
|
||||
id: iconInput
|
||||
anchors {
|
||||
top : parent.top
|
||||
left : parent.left
|
||||
}
|
||||
color : Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
text: "o"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
anchors {
|
||||
fill: inputWrap
|
||||
leftMargin : Style.dialog.iconSize+Style.dialog.fontSize
|
||||
bottomMargin : inputWrap.height - Style.dialog.iconSize
|
||||
}
|
||||
verticalAlignment : TextInput.AlignTop
|
||||
horizontalAlignment : TextInput.AlignLeft
|
||||
selectByMouse : true
|
||||
color : Style.dialog.text
|
||||
selectionColor : Style.main.textBlue
|
||||
font {
|
||||
pointSize : Style.dialog.fontSize * Style.pt
|
||||
}
|
||||
padding: 0
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color : "transparent"
|
||||
}
|
||||
Component.onCompleted : {
|
||||
if (isPassword) {
|
||||
echoMode = TextInput.Password
|
||||
} else {
|
||||
echoMode = TextInput.Normal
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.name: textlabel.text
|
||||
Accessible.description: textlabel.text
|
||||
}
|
||||
|
||||
Text {
|
||||
id: iconRight
|
||||
anchors {
|
||||
top : parent.top
|
||||
right : parent.right
|
||||
}
|
||||
color : Style.dialog.text
|
||||
font {
|
||||
pointSize : Style.dialog.iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
text: ( !isPassword ? "" : (
|
||||
inputField.echoMode == TextInput.Password ? Style.fa.eye : Style.fa.eye_slash
|
||||
)) + " " + rightIcon
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (isPassword) {
|
||||
if (inputField.echoMode == TextInput.Password) inputField.echoMode = TextInput.Normal
|
||||
else inputField.echoMode = TextInput.Password
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
bottom : parent.bottom
|
||||
}
|
||||
height: Math.max(Style.main.border,1)
|
||||
color: Style.dialog.text
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
inputField.text = ""
|
||||
rightIcon = ""
|
||||
}
|
||||
|
||||
function checkNonEmpty() {
|
||||
if (inputField.text == "") {
|
||||
rightIcon = Style.fa.exclamation_triangle
|
||||
root.placeholderText = ""
|
||||
inputField.focus = true
|
||||
return false
|
||||
} else {
|
||||
rightIcon = Style.fa.check_circle
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function hidePasswordText() {
|
||||
if (root.isPassword) inputField.echoMode = TextInput.Password
|
||||
}
|
||||
|
||||
function checkIsANumber(){
|
||||
if (/^\d+$/.test(inputField.text)) {
|
||||
rightIcon = Style.fa.check_circle
|
||||
return true
|
||||
}
|
||||
rightIcon = Style.fa.exclamation_triangle
|
||||
root.placeholderText = ""
|
||||
inputField.focus = true
|
||||
return false
|
||||
}
|
||||
|
||||
function forceFocus() {
|
||||
inputField.forceActiveFocus()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: inputField
|
||||
onAccepted: root.accepted()
|
||||
onEditingFinished: root.editingFinished()
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Enter) {
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright (c) 2021 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 ProtonUI 1.0
|
||||
|
||||
|
||||
// Main Window
|
||||
Window {
|
||||
id:winMain
|
||||
|
||||
// main window appeareance
|
||||
width : Style.main.width
|
||||
height : Style.main.height
|
||||
flags : Qt.Window | Qt.Dialog
|
||||
title: qsTr("ProtonMail Bridge", "app title")
|
||||
color : Style.main.background
|
||||
visible : true
|
||||
|
||||
Text {
|
||||
id: title
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: Style.main.topMargin
|
||||
}
|
||||
font{
|
||||
pointSize: Style.dialog.titleSize * Style.pt
|
||||
}
|
||||
color: Style.main.text
|
||||
text:
|
||||
"<span style='font-family: " + Style.fontawesome.name + "'>" + Style.fa.exclamation_triangle + "</span> " +
|
||||
qsTr ("Warning: Instance exists", "displayed when a version of the app is opened while another is already running")
|
||||
}
|
||||
|
||||
Text {
|
||||
id: message
|
||||
anchors.centerIn : parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Style.dialog.fontSize * Style.pt
|
||||
color: Style.main.text
|
||||
width: 2*parent.width/3
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("An instance of the ProtonMail Bridge is already running.", "displayed when a version of the app is opened while another is already running") + " " +
|
||||
qsTr("Please close the existing ProtonMail Bridge process before starting a new one.", "displayed when a version of the app is opened while another is already running")+ " " +
|
||||
qsTr("This program will close now.", "displayed when a version of the app is opened while another is already running")
|
||||
}
|
||||
|
||||
ButtonRounded {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Style.main.bottomMargin
|
||||
}
|
||||
text: qsTr("Okay", "confirms and dismisses a notification")
|
||||
color_main: Style.dialog.text
|
||||
color_minor: Style.main.textBlue
|
||||
isOpaque: true
|
||||
onClicked: Qt.quit()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Header of window with logo and buttons
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
// dimensions
|
||||
property Window parentWin
|
||||
property string title: "ProtonMail Bridge"
|
||||
property bool hasIcon : true
|
||||
anchors.top : parent.top
|
||||
anchors.right : parent.right
|
||||
width : Style.main.width
|
||||
height : Style.title.height
|
||||
// style
|
||||
color : Style.title.background
|
||||
|
||||
signal hideClicked()
|
||||
|
||||
// Drag to move : https://stackoverflow.com/a/18927884
|
||||
MouseArea {
|
||||
property variant clickPos: "1,1"
|
||||
anchors.fill: parent
|
||||
onPressed: {
|
||||
clickPos = Qt.point(mouse.x,mouse.y)
|
||||
}
|
||||
onPositionChanged: {
|
||||
var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
|
||||
parentWin.x += delta.x;
|
||||
parentWin.y += delta.y;
|
||||
}
|
||||
}
|
||||
|
||||
// logo
|
||||
Image {
|
||||
id: imgLogo
|
||||
height : Style.title.imgHeight
|
||||
fillMode : Image.PreserveAspectFit
|
||||
visible: root.hasIcon
|
||||
anchors {
|
||||
left : root.left
|
||||
leftMargin : Style.title.leftMargin
|
||||
verticalCenter : root.verticalCenter
|
||||
}
|
||||
//source : "qrc://logo.svg"
|
||||
source : "logo.svg"
|
||||
smooth : true
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: titleMetrics
|
||||
elideWidth: 2*root.width/3
|
||||
elide: Qt.ElideMiddle
|
||||
font: titleText.font
|
||||
text: root.title
|
||||
}
|
||||
|
||||
// Title
|
||||
Text {
|
||||
id: titleText
|
||||
anchors {
|
||||
left : hasIcon ? imgLogo.right : parent.left
|
||||
leftMargin : hasIcon ? Style.title.leftMargin : Style.main.leftMargin
|
||||
verticalCenter : root.verticalCenter
|
||||
}
|
||||
text : titleMetrics.elidedText
|
||||
color : Style.title.text
|
||||
font.pointSize : Style.title.fontSize * Style.pt
|
||||
}
|
||||
|
||||
// Underline Button
|
||||
Rectangle {
|
||||
id: buttonUndrLine
|
||||
anchors {
|
||||
verticalCenter : root.verticalCenter
|
||||
right : buttonCross.left
|
||||
rightMargin : 2*Style.title.fontSize
|
||||
}
|
||||
width : Style.title.fontSize
|
||||
height : Style.title.fontSize
|
||||
color : "transparent"
|
||||
Canvas {
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
var val = Style.title.fontSize
|
||||
var ctx = getContext("2d")
|
||||
ctx.strokeStyle = 'white'
|
||||
ctx.strokeWidth = 4
|
||||
ctx.moveTo(0 , val-1)
|
||||
ctx.lineTo(val, val-1)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.hideClicked()
|
||||
}
|
||||
}
|
||||
|
||||
// Cross Button
|
||||
Rectangle {
|
||||
id: buttonCross
|
||||
anchors {
|
||||
verticalCenter : root.verticalCenter
|
||||
right : root.right
|
||||
rightMargin : Style.main.rightMargin
|
||||
}
|
||||
width : Style.title.fontSize
|
||||
height : Style.title.fontSize
|
||||
color : "transparent"
|
||||
Canvas {
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
var val = Style.title.fontSize
|
||||
var ctx = getContext("2d")
|
||||
ctx.strokeStyle = 'white'
|
||||
ctx.strokeWidth = 4
|
||||
ctx.moveTo(0,0)
|
||||
ctx.lineTo(val,val)
|
||||
ctx.moveTo(val,0)
|
||||
ctx.lineTo(0,val)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.hideClicked()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright (c) 2021 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 message
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Controls 2.2
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: Style.transparent
|
||||
property alias text : message.text
|
||||
property alias checkbox : checkbox
|
||||
property alias buttonQuit : buttonQuit
|
||||
property alias buttonOkay : buttonOkay
|
||||
property alias buttonYes : buttonYes
|
||||
property alias buttonNo : buttonNo
|
||||
property alias buttonRetry : buttonRetry
|
||||
property alias buttonSkip : buttonSkip
|
||||
property alias buttonCancel : buttonCancel
|
||||
property alias msgWidth : backgroundInp.width
|
||||
property string msgID : ""
|
||||
visible: false
|
||||
|
||||
signal clickedOkay()
|
||||
signal clickedYes()
|
||||
signal clickedNo()
|
||||
signal clickedRetry()
|
||||
signal clickedSkip()
|
||||
signal clickedCancel()
|
||||
|
||||
MouseArea { // prevent action below
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: backgroundInp
|
||||
anchors.centerIn : root
|
||||
color : Style.errorDialog.background
|
||||
radius : Style.errorDialog.radius
|
||||
width : parent.width/3.
|
||||
height : contentInp.height
|
||||
|
||||
Column {
|
||||
id: contentInp
|
||||
anchors.horizontalCenter: backgroundInp.horizontalCenter
|
||||
spacing: Style.dialog.heightSeparator
|
||||
topPadding: Style.dialog.heightSeparator
|
||||
bottomPadding: Style.dialog.heightSeparator
|
||||
|
||||
AccessibleText {
|
||||
id: message
|
||||
font {
|
||||
pointSize : Style.errorDialog.fontSize * Style.pt
|
||||
bold : true
|
||||
}
|
||||
color: Style.errorDialog.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width : backgroundInp.width - 2*Style.main.rightMargin
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
CheckBoxLabel {
|
||||
id: checkbox
|
||||
text: ""
|
||||
checked: false
|
||||
visible: (text != "")
|
||||
textColor : Style.errorDialog.text
|
||||
checkedColor: Style.errorDialog.text
|
||||
uncheckedColor: Style.errorDialog.text
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.dialog.spacing
|
||||
anchors.horizontalCenter : parent.horizontalCenter
|
||||
|
||||
ButtonRounded { id : buttonQuit ; text : qsTr ( "Stop & quit", "" ) ; onClicked : root.clickedYes ( ) ; visible : false ; isOpaque : true ; color_main : Style.errorDialog.text ; color_minor : Style.dialog.textBlue ; }
|
||||
ButtonRounded { id : buttonNo ; text : qsTr ( "No" , "Button No" ) ; onClicked : root.clickedNo ( ) ; visible : false ; isOpaque : false ; color_main : Style.errorDialog.text ; color_minor : Style.transparent ; }
|
||||
ButtonRounded { id : buttonYes ; text : qsTr ( "Yes" , "Button Yes" ) ; onClicked : root.clickedYes ( ) ; visible : false ; isOpaque : true ; color_main : Style.errorDialog.text ; color_minor : Style.dialog.textBlue ; }
|
||||
ButtonRounded { id : buttonRetry ; text : qsTr ( "Retry" , "Button Retry" ) ; onClicked : root.clickedRetry ( ) ; visible : false ; isOpaque : false ; color_main : Style.errorDialog.text ; color_minor : Style.transparent ; }
|
||||
ButtonRounded { id : buttonSkip ; text : qsTr ( "Skip" , "Button Skip" ) ; onClicked : root.clickedSkip ( ) ; visible : false ; isOpaque : false ; color_main : Style.errorDialog.text ; color_minor : Style.transparent ; }
|
||||
ButtonRounded { id : buttonCancel ; text : qsTr ( "Cancel" , "Button Cancel" ) ; onClicked : root.clickedCancel ( ) ; visible : false ; isOpaque : true ; color_main : Style.errorDialog.text ; color_minor : Style.dialog.textBlue ; }
|
||||
ButtonRounded { id : buttonOkay ; text : qsTr ( "Okay" , "Button Okay" ) ; onClicked : root.clickedOkay ( ) ; visible : true ; isOpaque : true ; color_main : Style.errorDialog.text ; color_minor : Style.dialog.textBlue ; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(text) {
|
||||
root.text = text
|
||||
root.visible = true
|
||||
}
|
||||
|
||||
function hide() {
|
||||
root.visible=false
|
||||
|
||||
root .text = ""
|
||||
checkbox .text = ""
|
||||
|
||||
buttonNo .visible = false
|
||||
buttonYes .visible = false
|
||||
buttonRetry .visible = false
|
||||
buttonSkip .visible = false
|
||||
buttonCancel .visible = false
|
||||
buttonOkay .visible = true
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: Style.transparent
|
||||
|
||||
property color fillColor : Style.main.background
|
||||
property color strokeColor : Style.main.line
|
||||
property real strokeWidth : Style.dialog.borderInput
|
||||
property real radiusTopLeft : Style.dialog.radiusButton
|
||||
property real radiusBottomLeft : Style.dialog.radiusButton
|
||||
property real radiusTopRight : Style.dialog.radiusButton
|
||||
property real radiusBottomRight : Style.dialog.radiusButton
|
||||
|
||||
function paint() {
|
||||
canvas.requestPaint()
|
||||
}
|
||||
|
||||
onFillColorChanged : root.paint()
|
||||
onStrokeColorChanged : root.paint()
|
||||
onStrokeWidthChanged : root.paint()
|
||||
onRadiusTopLeftChanged : root.paint()
|
||||
onRadiusBottomLeftChanged : root.paint()
|
||||
onRadiusTopRightChanged : root.paint()
|
||||
onRadiusBottomRightChanged : root.paint()
|
||||
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: root
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = root.fillColor
|
||||
ctx.strokeStyle = root.strokeColor
|
||||
ctx.lineWidth = root.strokeWidth
|
||||
var dimensions = {
|
||||
x: ctx.lineWidth,
|
||||
y: ctx.lineWidth,
|
||||
w: canvas.width-2*ctx.lineWidth,
|
||||
h: canvas.height-2*ctx.lineWidth,
|
||||
}
|
||||
var radius = {
|
||||
tl: root.radiusTopLeft,
|
||||
tr: root.radiusTopRight,
|
||||
bl: root.radiusBottomLeft,
|
||||
br: root.radiusBottomRight,
|
||||
}
|
||||
|
||||
root.roundRect(
|
||||
ctx,
|
||||
dimensions,
|
||||
radius, true, true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// adapted from: https://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas/3368118#3368118
|
||||
function roundRect(ctx, dim, radius, fill, stroke) {
|
||||
if (typeof stroke == 'undefined') {
|
||||
stroke = true;
|
||||
}
|
||||
if (typeof radius === 'undefined') {
|
||||
radius = 5;
|
||||
}
|
||||
if (typeof radius === 'number') {
|
||||
radius = {tl: radius, tr: radius, br: radius, bl: radius};
|
||||
} else {
|
||||
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
|
||||
for (var side in defaultRadius) {
|
||||
radius[side] = radius[side] || defaultRadius[side];
|
||||
}
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(dim.x + radius.tl, dim.y);
|
||||
ctx.lineTo(dim.x + dim.w - radius.tr, dim.y);
|
||||
ctx.quadraticCurveTo(dim.x + dim.w, dim.y, dim.x + dim.w, dim.y + radius.tr);
|
||||
ctx.lineTo(dim.x + dim.w, dim.y + dim.h - radius.br);
|
||||
ctx.quadraticCurveTo(dim.x + dim.w, dim.y + dim.h, dim.x + dim.w - radius.br, dim.y + dim.h);
|
||||
ctx.lineTo(dim.x + radius.bl, dim.y + dim.h);
|
||||
ctx.quadraticCurveTo(dim.x, dim.y + dim.h, dim.x, dim.y + dim.h - radius.bl);
|
||||
ctx.lineTo(dim.x, dim.y + radius.tl);
|
||||
ctx.quadraticCurveTo(dim.x, dim.y, dim.x + radius.tl, dim.y);
|
||||
ctx.closePath();
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: root.paint()
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Important information under title bar
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 2.1
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
height: 0
|
||||
visible: state != "ok"
|
||||
state: "ok"
|
||||
color: "black"
|
||||
property var fontSize : 1.0 * Style.main.fontSize
|
||||
|
||||
Row {
|
||||
anchors.centerIn: root
|
||||
visible: root.visible
|
||||
spacing: Style.main.leftMarginButton
|
||||
|
||||
AccessibleText {
|
||||
id: message
|
||||
font.pointSize: root.fontSize * Style.pt
|
||||
|
||||
text: qsTr("Connection security error: Your network connection to Proton services may be insecure.", "message in bar showed when TLS Pinning fails")
|
||||
}
|
||||
|
||||
ClickIconText {
|
||||
anchors.verticalCenter : message.verticalCenter
|
||||
iconText : ""
|
||||
text : qsTr("Learn more", "This button opens TLS Pinning issue modal with more explanation")
|
||||
visible : root.visible
|
||||
onClicked : {
|
||||
winMain.dialogTlsCert.show()
|
||||
}
|
||||
fontSize : root.fontSize
|
||||
textUnderline: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "notOK"
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 2* Style.main.fontSize
|
||||
color: Style.main.textRed
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Button with text and icon for tabbar
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
import QtQuick.Controls 2.1
|
||||
|
||||
AccessibleButton {
|
||||
id: root
|
||||
property alias iconText : icon.text
|
||||
property alias title : titleText.text
|
||||
property color textColor : {
|
||||
if (root.state=="deactivated") {
|
||||
return Qt.lighter(Style.tabbar.textInactive, root.hovered || root.activeFocus ? 1.25 : 1.0)
|
||||
}
|
||||
if (root.state=="activated") {
|
||||
return Style.tabbar.text
|
||||
}
|
||||
}
|
||||
|
||||
text: root.title
|
||||
Accessible.description: root.title + " tab"
|
||||
|
||||
width : titleMetrics.width // Style.tabbar.widthButton
|
||||
height : Style.tabbar.heightButton
|
||||
padding: 0
|
||||
|
||||
background: Rectangle {
|
||||
color : Style.transparent
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
}
|
||||
|
||||
contentItem : Rectangle {
|
||||
color: "transparent"
|
||||
scale : root.pressed ? 0.96 : 1.00
|
||||
|
||||
Text {
|
||||
id: icon
|
||||
// dimenstions
|
||||
anchors {
|
||||
top : parent.top
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
// style
|
||||
color : root.textColor
|
||||
font {
|
||||
family : Style.fontawesome.name
|
||||
pointSize : Style.tabbar.iconSize * Style.pt
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: titleMetrics
|
||||
text : root.title
|
||||
font.pointSize : titleText.font.pointSize
|
||||
}
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
// dimenstions
|
||||
anchors {
|
||||
bottom : parent.bottom
|
||||
horizontalCenter : parent.horizontalCenter
|
||||
}
|
||||
// style
|
||||
color : root.textColor
|
||||
font {
|
||||
pointSize : Style.tabbar.fontSize * Style.pt
|
||||
bold : root.state=="activated"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "activated"
|
||||
},
|
||||
State {
|
||||
name: "deactivated"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// Tab labels
|
||||
|
||||
import QtQuick 2.8
|
||||
import ProtonUI 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
// attributes
|
||||
property alias model : tablist.model
|
||||
property alias currentIndex : tablist.currentIndex
|
||||
property int spacing : Style.tabbar.widthButton + Style.tabbar.spacingButton
|
||||
currentIndex: 0
|
||||
|
||||
// appereance
|
||||
height : Style.tabbar.height
|
||||
color : Style.tabbar.background
|
||||
|
||||
// content
|
||||
ListView {
|
||||
id: tablist
|
||||
// dimensions
|
||||
anchors {
|
||||
fill: root
|
||||
leftMargin : Style.tabbar.leftMargin
|
||||
rightMargin : Style.main.rightMargin
|
||||
bottomMargin : Style.tabbar.bottomMargin
|
||||
}
|
||||
spacing: Style.tabbar.spacingButton
|
||||
interactive : false
|
||||
// style
|
||||
orientation: Qt.Horizontal
|
||||
delegate: TabButton {
|
||||
anchors.bottom : parent.bottom
|
||||
title : modelData.title
|
||||
iconText : modelData.iconText
|
||||
state : index == tablist.currentIndex ? "activated" : "deactivated"
|
||||
onClicked : {
|
||||
tablist.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quit button
|
||||
TabButton {
|
||||
id: buttonQuit
|
||||
title : qsTr("Close Bridge", "quits the application")
|
||||
iconText : Style.fa.power_off
|
||||
state : "deactivated"
|
||||
visible : Style.tabbar.rightButton=="quit"
|
||||
anchors {
|
||||
right : root.right
|
||||
bottom : root.bottom
|
||||
rightMargin : Style.main.rightMargin
|
||||
bottomMargin : Style.tabbar.bottomMargin
|
||||
}
|
||||
|
||||
Accessible.description: buttonQuit.title
|
||||
|
||||
onClicked : {
|
||||
dialogGlobal.state = "quit"
|
||||
dialogGlobal.show()
|
||||
}
|
||||
}
|
||||
|
||||
// Add account
|
||||
TabButton {
|
||||
id: buttonAddAccount
|
||||
title : qsTr("Add account", "start the authentication to add account")
|
||||
iconText : Style.fa.plus_circle
|
||||
state : "deactivated"
|
||||
visible : Style.tabbar.rightButton=="add account"
|
||||
anchors {
|
||||
right : root.right
|
||||
bottom : root.bottom
|
||||
rightMargin : Style.main.rightMargin
|
||||
bottomMargin : Style.tabbar.bottomMargin
|
||||
}
|
||||
|
||||
Accessible.description: buttonAddAccount.title
|
||||
|
||||
onClicked : dialogAddUser.show()
|
||||
}
|
||||
|
||||
function focusButton() {
|
||||
tablist.currentItem.forceActiveFocus()
|
||||
tablist.currentItem.Accessible.focusedChanged(true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
|
||||
AccessibleText{
|
||||
id: root
|
||||
property bool hasCopyButton : false
|
||||
font.pointSize: Style.main.fontSize * Style.pt
|
||||
state: "label"
|
||||
|
||||
states : [
|
||||
State {
|
||||
name: "label"
|
||||
PropertyChanges {
|
||||
target : root
|
||||
font.bold : false
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "heading"
|
||||
PropertyChanges {
|
||||
target : root
|
||||
font.bold : true
|
||||
color : Style.main.textDisabled
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) 2021 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
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string text: "undef"
|
||||
width: copyIcon.width + valueText.width
|
||||
height: Math.max(copyIcon.height, valueText.contentHeight)
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
id: copyIcon
|
||||
width: Style.info.leftMarginIcon*2 + Style.info.iconSize
|
||||
height : Style.info.iconSize
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: root.top
|
||||
left: root.left
|
||||
}
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
font {
|
||||
pointSize : Style.info.iconSize * Style.pt
|
||||
family : Style.fontawesome.name
|
||||
}
|
||||
color : Style.main.textInactive
|
||||
text: Style.fa.copy
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked : {
|
||||
valueText.select(0, valueText.length)
|
||||
valueText.copy()
|
||||
valueText.deselect()
|
||||
}
|
||||
onPressed: copyIcon.scale = 0.90
|
||||
onReleased: copyIcon.scale = 1
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Copy %1 to clipboard", "Click to copy the value to system clipboard.").arg(root.text)
|
||||
Accessible.description: Accessible.name
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
id: valueText
|
||||
width: Style.info.widthValue
|
||||
height: Style.main.fontSize
|
||||
anchors {
|
||||
top: root.top
|
||||
left: copyIcon.right
|
||||
}
|
||||
font {
|
||||
pointSize: Style.main.fontSize * Style.pt
|
||||
}
|
||||
color: Style.main.text
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
selectByKeyboard: true
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: root.text
|
||||
selectionColor: Style.dialog.textBlue
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: root.text
|
||||
Accessible.description: Accessible.name
|
||||
}
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// simulating window title bar with different color
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick.Window 2.2
|
||||
import ProtonUI 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
height: visible ? (
|
||||
root.isDarwin ? Style.titleMacOS.height : Style.title.height
|
||||
) : 0
|
||||
color: "transparent"
|
||||
property bool isDarwin : (go.goos == "darwin")
|
||||
property QtObject window
|
||||
anchors {
|
||||
left : parent.left
|
||||
right : parent.right
|
||||
top : parent.top
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
property point diff: "0,0"
|
||||
anchors {
|
||||
top: root.top
|
||||
bottom: root.bottom
|
||||
left: root.left
|
||||
right: root.isDarwin ? root.right : iconRowWin.left
|
||||
}
|
||||
onPressed: {
|
||||
diff = Qt.point(window.x, window.y)
|
||||
var mousePos = mapToGlobal(mouse.x, mouse.y)
|
||||
diff.x -= mousePos.x
|
||||
diff.y -= mousePos.y
|
||||
}
|
||||
onPositionChanged: {
|
||||
var currPos = mapToGlobal(mouse.x, mouse.y)
|
||||
window.x = currPos.x + diff.x
|
||||
window.y = currPos.y + diff.y
|
||||
}
|
||||
}
|
||||
|
||||
// top background
|
||||
Rectangle {
|
||||
id: upperBackground
|
||||
anchors.fill: root
|
||||
color: (isDarwin? Style.titleMacOS.background : Style.title.background )
|
||||
radius: (isDarwin? Style.titleMacOS.radius : 0)
|
||||
border {
|
||||
width: Style.main.border
|
||||
color: Style.title.background
|
||||
}
|
||||
}
|
||||
// bottom background
|
||||
Rectangle {
|
||||
id: lowerBorder
|
||||
anchors {
|
||||
top: root.verticalCenter
|
||||
left: root.left
|
||||
right: root.right
|
||||
bottom: root.bottom
|
||||
}
|
||||
color: Style.title.background
|
||||
Rectangle {
|
||||
id: lowerBackground
|
||||
anchors{
|
||||
fill : parent
|
||||
leftMargin : Style.main.border
|
||||
rightMargin : Style.main.border
|
||||
}
|
||||
color: upperBackground.color
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Title
|
||||
TextMetrics {
|
||||
id: titleMetrics
|
||||
text : window.title
|
||||
font : isDarwin ? titleMac.font : titleWin.font
|
||||
elide: Qt.ElideMiddle
|
||||
elideWidth : window.width/2
|
||||
}
|
||||
Text {
|
||||
id: titleWin
|
||||
visible: !isDarwin
|
||||
anchors {
|
||||
baseline : logo.bottom
|
||||
left : logo.right
|
||||
leftMargin : Style.title.leftMargin/1.5
|
||||
}
|
||||
color : window.active ? Style.title.text : Style.main.textDisabled
|
||||
text : titleMetrics.elidedText
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
}
|
||||
Text {
|
||||
id: titleMac
|
||||
visible: isDarwin
|
||||
anchors {
|
||||
verticalCenter : parent.verticalCenter
|
||||
left : parent.left
|
||||
leftMargin : (parent.width-width)/2
|
||||
}
|
||||
color : window.active ? Style.title.text : Style.main.textDisabled
|
||||
text : titleMetrics.elidedText
|
||||
font.pointSize : Style.main.fontSize * Style.pt
|
||||
}
|
||||
|
||||
|
||||
// MACOS
|
||||
MouseArea {
|
||||
anchors.fill: iconRowMac
|
||||
property string beforeHover
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
beforeHover=iconRed.state
|
||||
//iconYellow.state="hover"
|
||||
iconRed.state="hover"
|
||||
}
|
||||
onExited: {
|
||||
//iconYellow.state=beforeHover
|
||||
iconRed.state=beforeHover
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: window
|
||||
onActiveChanged : {
|
||||
if (window.active) {
|
||||
//iconYellow.state="normal"
|
||||
iconRed.state="normal"
|
||||
} else {
|
||||
//iconYellow.state="disabled"
|
||||
iconRed.state="disabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: iconRowMac
|
||||
visible : isDarwin
|
||||
spacing : Style.titleMacOS.leftMargin
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
leftMargin : Style.title.leftMargin
|
||||
}
|
||||
Image {
|
||||
id: iconRed
|
||||
width : Style.titleMacOS.imgHeight
|
||||
height : Style.titleMacOS.imgHeight
|
||||
fillMode : Image.PreserveAspectFit
|
||||
smooth : true
|
||||
state : "normal"
|
||||
states: [
|
||||
State { name: "normal" ; PropertyChanges { target: iconRed ; source: "images/macos_red.png" } },
|
||||
State { name: "hover" ; PropertyChanges { target: iconRed ; source: "images/macos_red_hl.png" } },
|
||||
State { name: "pressed" ; PropertyChanges { target: iconRed ; source: "images/macos_red_dark.png" } },
|
||||
State { name: "disabled" ; PropertyChanges { target: iconRed ; source: "images/macos_gray.png" } }
|
||||
]
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
property string beforePressed : "normal"
|
||||
onClicked : {
|
||||
window.close()
|
||||
}
|
||||
onPressed: {
|
||||
beforePressed = parent.state
|
||||
parent.state="pressed"
|
||||
}
|
||||
onReleased: {
|
||||
parent.state=beforePressed
|
||||
}
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Close", "Close the window button")
|
||||
Accessible.description: Accessible.name
|
||||
Accessible.ignored: !parent.visible
|
||||
Accessible.onPressAction: {
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
id: iconYellow
|
||||
width : Style.titleMacOS.imgHeight
|
||||
height : Style.titleMacOS.imgHeight
|
||||
fillMode : Image.PreserveAspectFit
|
||||
smooth : true
|
||||
state : "disabled"
|
||||
states: [
|
||||
State { name: "normal" ; PropertyChanges { target: iconYellow ; source: "images/macos_yellow.png" } },
|
||||
State { name: "hover" ; PropertyChanges { target: iconYellow ; source: "images/macos_yellow_hl.png" } },
|
||||
State { name: "pressed" ; PropertyChanges { target: iconYellow ; source: "images/macos_yellow_dark.png" } },
|
||||
State { name: "disabled" ; PropertyChanges { target: iconYellow ; source: "images/macos_gray.png" } }
|
||||
]
|
||||
/*
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
property string beforePressed : "normal"
|
||||
onClicked : {
|
||||
window.visibility = Window.Minimized
|
||||
}
|
||||
onPressed: {
|
||||
beforePressed = parent.state
|
||||
parent.state="pressed"
|
||||
}
|
||||
onReleased: {
|
||||
parent.state=beforePressed
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Minimize", "Minimize the window button")
|
||||
Accessible.description: Accessible.name
|
||||
Accessible.ignored: !parent.visible
|
||||
Accessible.onPressAction: {
|
||||
window.visibility = Window.Minimized
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
Image {
|
||||
id: iconGreen
|
||||
width : Style.titleMacOS.imgHeight
|
||||
height : Style.titleMacOS.imgHeight
|
||||
fillMode : Image.PreserveAspectFit
|
||||
smooth : true
|
||||
source : "images/macos_gray.png"
|
||||
Component.onCompleted : {
|
||||
visible = false // (window.flags&Qt.Dialog) != Qt.Dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Windows
|
||||
Image {
|
||||
id: logo
|
||||
visible: !isDarwin
|
||||
anchors {
|
||||
left : parent.left
|
||||
verticalCenter : parent.verticalCenter
|
||||
leftMargin : Style.title.leftMargin
|
||||
}
|
||||
height : Style.title.fontSize-2*Style.px
|
||||
fillMode : Image.PreserveAspectFit
|
||||
mipmap : true
|
||||
source : "images/pm_logo.png"
|
||||
}
|
||||
|
||||
Row {
|
||||
id: iconRowWin
|
||||
visible: !isDarwin
|
||||
anchors {
|
||||
right : parent.right
|
||||
verticalCenter : root.verticalCenter
|
||||
}
|
||||
Rectangle {
|
||||
height : root.height
|
||||
width : 1.5*height
|
||||
color: Style.transparent
|
||||
Image {
|
||||
id: iconDash
|
||||
anchors.centerIn: parent
|
||||
height : iconTimes.height*0.90
|
||||
fillMode : Image.PreserveAspectFit
|
||||
mipmap : true
|
||||
source : "images/win10_Dash.png"
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
hoverEnabled : true
|
||||
onClicked : {
|
||||
window.visibility = Window.Minimized
|
||||
}
|
||||
onPressed: {
|
||||
parent.scale=0.92
|
||||
}
|
||||
onReleased: {
|
||||
parent.scale=1
|
||||
}
|
||||
onEntered: {
|
||||
parent.color= Qt.lighter(Style.title.background,1.2)
|
||||
}
|
||||
onExited: {
|
||||
parent.color=Style.transparent
|
||||
}
|
||||
|
||||
Accessible.role : Accessible.Button
|
||||
Accessible.name : qsTr("Minimize", "Minimize the window button")
|
||||
Accessible.description : Accessible.name
|
||||
Accessible.ignored : !parent.visible
|
||||
Accessible.onPressAction : {
|
||||
window.visibility = Window.Minimized
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
height : root.height
|
||||
width : 1.5*height
|
||||
color : Style.transparent
|
||||
Image {
|
||||
id: iconTimes
|
||||
anchors.centerIn : parent
|
||||
mipmap : true
|
||||
height : parent.height/1.5
|
||||
fillMode : Image.PreserveAspectFit
|
||||
source : "images/win10_Times.png"
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill : parent
|
||||
hoverEnabled : true
|
||||
onClicked : window.close()
|
||||
onPressed : {
|
||||
iconTimes.scale=0.92
|
||||
}
|
||||
onReleased: {
|
||||
parent.scale=1
|
||||
}
|
||||
onEntered: {
|
||||
parent.color=Style.main.textRed
|
||||
}
|
||||
onExited: {
|
||||
parent.color=Style.transparent
|
||||
}
|
||||
|
||||
Accessible.role : Accessible.Button
|
||||
Accessible.name : qsTr("Close", "Close the window button")
|
||||
Accessible.description : Accessible.name
|
||||
Accessible.ignored : !parent.visible
|
||||
Accessible.onPressAction : {
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
module ProtonUI
|
||||
singleton Style 1.0 Style.qml
|
||||
AccessibleButton 1.0 AccessibleButton.qml
|
||||
AccessibleText 1.0 AccessibleText.qml
|
||||
AccessibleSelectableText 1.0 AccessibleSelectableText.qml
|
||||
AccountView 1.0 AccountView.qml
|
||||
AddAccountBar 1.0 AddAccountBar.qml
|
||||
BubbleNote 1.0 BubbleNote.qml
|
||||
BugReportWindow 1.0 BugReportWindow.qml
|
||||
ButtonIconText 1.0 ButtonIconText.qml
|
||||
ButtonRounded 1.0 ButtonRounded.qml
|
||||
CheckBoxLabel 1.0 CheckBoxLabel.qml
|
||||
ClickIconText 1.0 ClickIconText.qml
|
||||
Dialog 1.0 Dialog.qml
|
||||
DialogAddUser 1.0 DialogAddUser.qml
|
||||
DialogUpdate 1.0 DialogUpdate.qml
|
||||
DialogConnectionTroubleshoot 1.0 DialogConnectionTroubleshoot.qml
|
||||
FileAndFolderSelect 1.0 FileAndFolderSelect.qml
|
||||
InfoToolTip 1.0 InfoToolTip.qml
|
||||
InformationBar 1.0 InformationBar.qml
|
||||
InputBox 1.0 InputBox.qml
|
||||
InputField 1.0 InputField.qml
|
||||
InstanceExistsWindow 1.0 InstanceExistsWindow.qml
|
||||
LogoHeader 1.0 LogoHeader.qml
|
||||
PopupMessage 1.0 PopupMessage.qml
|
||||
RoundedRectangle 1.0 RoundedRectangle.qml
|
||||
TabButton 1.0 TabButton.qml
|
||||
TabLabels 1.0 TabLabels.qml
|
||||
TextLabel 1.0 TextLabel.qml
|
||||
TextValue 1.0 TextValue.qml
|
||||
TLSCertPinIssueBar 1.0 TLSCertPinIssueBar.qml
|
||||
WindowTitleBar 1.0 WindowTitleBar.qml
|
|
@ -0,0 +1,22 @@
|
|||
/* File generated by Qt Creator */
|
||||
|
||||
import QmlProject 1.1
|
||||
|
||||
Project {
|
||||
mainFile: "./MainWindow.qml"
|
||||
|
||||
/* Include .qml, .js, and image files from current directory and subdirectories */
|
||||
QmlFiles {
|
||||
directory: "./"
|
||||
}
|
||||
JavaScriptFiles {
|
||||
directory: "./"
|
||||
}
|
||||
ImageFiles {
|
||||
directory: "./"
|
||||
}
|
||||
/* List of plugin directories passed to QML runtime */
|
||||
importPaths: [
|
||||
"./"
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue