From b77216db4b6d941afb03325e0843509621a283da Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 18 Dec 2017 21:00:05 +0100 Subject: We no longer need the kpackage structure --- components/accounts/contents/ui/AccountWizard.qml | 93 ---- .../accounts/contents/ui/AccountWizardPage.qml | 154 ------ components/accounts/contents/ui/main.qml | 30 -- components/accounts/metadata.desktop | 8 - components/accounts/qml/AccountWizard.qml | 93 ++++ components/accounts/qml/AccountWizardPage.qml | 155 ++++++ components/accounts/qml/main.qml | 30 ++ components/kube/contents/ui/AccountsView.qml | 142 ------ .../kube/contents/ui/AddresseeListEditor.qml | 138 ------ components/kube/contents/ui/ComposerView.qml | 529 --------------------- components/kube/contents/ui/Kube.qml | 412 ---------------- components/kube/contents/ui/LogView.qml | 329 ------------- components/kube/contents/ui/LoginView.qml | 52 -- components/kube/contents/ui/MailView.qml | 120 ----- components/kube/contents/ui/PeopleView.qml | 38 -- components/kube/contents/ui/main.qml | 24 - components/kube/metadata.desktop | 8 - components/kube/qml/AccountsView.qml | 142 ++++++ components/kube/qml/AddresseeListEditor.qml | 138 ++++++ components/kube/qml/ComposerView.qml | 529 +++++++++++++++++++++ components/kube/qml/Kube.qml | 413 ++++++++++++++++ components/kube/qml/LogView.qml | 329 +++++++++++++ components/kube/qml/LoginView.qml | 52 ++ components/kube/qml/MailView.qml | 120 +++++ components/kube/qml/PeopleView.qml | 38 ++ components/kube/qml/main.qml | 24 + components/mailviewer/contents/ui/ErrorPart.qml | 41 -- components/mailviewer/contents/ui/HtmlContent.qml | 81 ---- .../mailviewer/contents/ui/MailDataModel.qml | 159 ------- components/mailviewer/contents/ui/MailPart.qml | 70 --- components/mailviewer/contents/ui/MailViewer.qml | 44 -- components/mailviewer/contents/ui/TextContent.qml | 60 --- components/mailviewer/contents/ui/main.qml | 30 -- components/mailviewer/metadata.desktop | 8 - components/mailviewer/qml/ErrorPart.qml | 41 ++ components/mailviewer/qml/HtmlContent.qml | 81 ++++ components/mailviewer/qml/MailDataModel.qml | 159 +++++++ components/mailviewer/qml/MailPart.qml | 70 +++ components/mailviewer/qml/MailViewer.qml | 44 ++ components/mailviewer/qml/TextContent.qml | 60 +++ components/mailviewer/qml/main.qml | 30 ++ 41 files changed, 2548 insertions(+), 2570 deletions(-) delete mode 100644 components/accounts/contents/ui/AccountWizard.qml delete mode 100644 components/accounts/contents/ui/AccountWizardPage.qml delete mode 100644 components/accounts/contents/ui/main.qml delete mode 100644 components/accounts/metadata.desktop create mode 100644 components/accounts/qml/AccountWizard.qml create mode 100644 components/accounts/qml/AccountWizardPage.qml create mode 100644 components/accounts/qml/main.qml delete mode 100644 components/kube/contents/ui/AccountsView.qml delete mode 100644 components/kube/contents/ui/AddresseeListEditor.qml delete mode 100644 components/kube/contents/ui/ComposerView.qml delete mode 100644 components/kube/contents/ui/Kube.qml delete mode 100644 components/kube/contents/ui/LogView.qml delete mode 100644 components/kube/contents/ui/LoginView.qml delete mode 100644 components/kube/contents/ui/MailView.qml delete mode 100644 components/kube/contents/ui/PeopleView.qml delete mode 100644 components/kube/contents/ui/main.qml delete mode 100644 components/kube/metadata.desktop create mode 100644 components/kube/qml/AccountsView.qml create mode 100644 components/kube/qml/AddresseeListEditor.qml create mode 100644 components/kube/qml/ComposerView.qml create mode 100644 components/kube/qml/Kube.qml create mode 100644 components/kube/qml/LogView.qml create mode 100644 components/kube/qml/LoginView.qml create mode 100644 components/kube/qml/MailView.qml create mode 100644 components/kube/qml/PeopleView.qml create mode 100644 components/kube/qml/main.qml delete mode 100644 components/mailviewer/contents/ui/ErrorPart.qml delete mode 100644 components/mailviewer/contents/ui/HtmlContent.qml delete mode 100644 components/mailviewer/contents/ui/MailDataModel.qml delete mode 100644 components/mailviewer/contents/ui/MailPart.qml delete mode 100644 components/mailviewer/contents/ui/MailViewer.qml delete mode 100644 components/mailviewer/contents/ui/TextContent.qml delete mode 100644 components/mailviewer/contents/ui/main.qml delete mode 100644 components/mailviewer/metadata.desktop create mode 100644 components/mailviewer/qml/ErrorPart.qml create mode 100644 components/mailviewer/qml/HtmlContent.qml create mode 100644 components/mailviewer/qml/MailDataModel.qml create mode 100644 components/mailviewer/qml/MailPart.qml create mode 100644 components/mailviewer/qml/MailViewer.qml create mode 100644 components/mailviewer/qml/TextContent.qml create mode 100644 components/mailviewer/qml/main.qml diff --git a/components/accounts/contents/ui/AccountWizard.qml b/components/accounts/contents/ui/AccountWizard.qml deleted file mode 100644 index 4a11ca05..00000000 --- a/components/accounts/contents/ui/AccountWizard.qml +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2016 Michael Bohlender, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.7 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 as Controls2 -import org.kube.framework 1.0 as Kube - -Kube.Popup { - id: root - objectName: "accountWizard" - - property bool requireSetup: false - property var availableAccountPlugins: [] - - modal: true - closePolicy: requireSetup ? Controls2.Popup.NoAutoClose : Controls2.Popup.CloseOnEscape | Controls2.Popup.CloseOnPressOutside - - clip: true - - Controls2.StackView { - id: stack - anchors.fill: parent - Component.onCompleted: { - //If we only have one account type we skip the selection - if (root.availableAccountPlugins.length == 1) { - stack.push(wizardPage.createObject(app, {accountType: root.availableAccountPlugins[0]})) - } else { - stack.push(mainView.createObject(app)) - } - } - onCurrentItemChanged: { - if (!!currentItem) { - currentItem.forceActiveFocus() - } - } - } - - Component { - id: mainView - - FocusScope { - Kube.Heading { - id: heading - text: qsTr("Select your new account type") - color: Kube.Colors.highlightColor - } - - ColumnLayout { - anchors.centerIn: parent - width: parent.width * 0.4 - - spacing: Kube.Units.largeSpacing - - Repeater { - model: root.availableAccountPlugins - delegate: Kube.Button { - Layout.fillWidth: true - text: modelData - onClicked: stack.push(wizardPage.createObject(app, {accountType:modelData})) - } - } - } - } - } - - Component { - id: wizardPage - AccountWizardPage { - focus: true - requireSetup: root.requireSetup - onDone: { - root.close() - Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) - } - } - } -} diff --git a/components/accounts/contents/ui/AccountWizardPage.qml b/components/accounts/contents/ui/AccountWizardPage.qml deleted file mode 100644 index 18001419..00000000 --- a/components/accounts/contents/ui/AccountWizardPage.qml +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2016 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.7 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.4 as Controls -import QtQuick.Controls 2.0 as Controls2 -import org.kube.framework 1.0 as Kube - - -FocusScope { - id: root - property string accountType - signal done() - - property bool isFirstView: root.Controls2.StackView.index == 0 - property bool requireSetup: false - - function save() { - if (loader.item.valid) { - loader.item.save() - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": loader.item.accountIdentifier}); - root.done() - } else { - console.warn("Invalid settings."); - } - } - - Kube.AccountFactory { - id: accountFactory - accountType: root.accountType - } - - Controls.ToolButton { - id: backButton - iconName: Kube.Icons.goBack - tooltip: "go back" - visible: !root.isFirstView - onClicked: { - stack.pop() - } - } - - Keys.onReturnPressed: save() - - //Item to avoid anchors conflict with stack - Item { - anchors{ - top: backButton.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - } - - Kube.Heading { - id: heading - text: loader.item.heading - color: Kube.Colors.highlightColor - } - - Kube.Label { - id: subHeadline - - anchors { - left: heading.left - top: heading.bottom - } - - width: parent.width - text: loader.item.subheadline - color: Kube.Colors.disabledTextColor - wrapMode: Text.Wrap - } - - Item { - id: accountEdit - anchors { - top:subHeadline.bottom - left: parent.left - right: parent.right - topMargin: Kube.Units.largeSpacing * 2 - } - - Loader { - id: loader - anchors.fill: parent - focus: true - source: accountFactory.uiPath - } - } - - Item { - id: spacer - Layout.fillHeight: true - anchors { - top:accountEdit.bottom - bottom: footer.top - left: parent.left - right: parent.right - } - } - - //This is where we should place the account wizard ui - Item { - id: footer - - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - topMargin: Kube.Units.largeSpacing * 2 - } - - Kube.Button { - anchors { - left: parent.left - bottom: parent.bottom - } - visible: !root.requireSetup - - text: qsTr("Discard") - onClicked: { - root.done() - } - } - - Kube.PositiveButton { - anchors { - right: parent.right - bottom: parent.bottom - } - - text: qsTr("Save") - onClicked: save() - } - } - } -} diff --git a/components/accounts/contents/ui/main.qml b/components/accounts/contents/ui/main.qml deleted file mode 100644 index e15bca8f..00000000 --- a/components/accounts/contents/ui/main.qml +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2016 Michael Bohlender, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.7 -import QtQuick.Controls 2.0 as Controls2 - -Controls2.ApplicationWindow { - id: app - height: 900 - width: 1500 - - AccountWizard { - visible: true - } -} diff --git a/components/accounts/metadata.desktop b/components/accounts/metadata.desktop deleted file mode 100644 index d93813d0..00000000 --- a/components/accounts/metadata.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Name=Kube Accounts -X-KDE-PluginInfo-Name=org.kube.components.accounts -Exec=kpackagelauncherqml -a org.kube.components.accounts -X-Plasma-MainScript=ui/main.qml -X-KDE-ServiceTypes=KPackage/GenericQML -Icon=kmail2 -Type=Service diff --git a/components/accounts/qml/AccountWizard.qml b/components/accounts/qml/AccountWizard.qml new file mode 100644 index 00000000..4a11ca05 --- /dev/null +++ b/components/accounts/qml/AccountWizard.qml @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Michael Bohlender, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 as Controls2 +import org.kube.framework 1.0 as Kube + +Kube.Popup { + id: root + objectName: "accountWizard" + + property bool requireSetup: false + property var availableAccountPlugins: [] + + modal: true + closePolicy: requireSetup ? Controls2.Popup.NoAutoClose : Controls2.Popup.CloseOnEscape | Controls2.Popup.CloseOnPressOutside + + clip: true + + Controls2.StackView { + id: stack + anchors.fill: parent + Component.onCompleted: { + //If we only have one account type we skip the selection + if (root.availableAccountPlugins.length == 1) { + stack.push(wizardPage.createObject(app, {accountType: root.availableAccountPlugins[0]})) + } else { + stack.push(mainView.createObject(app)) + } + } + onCurrentItemChanged: { + if (!!currentItem) { + currentItem.forceActiveFocus() + } + } + } + + Component { + id: mainView + + FocusScope { + Kube.Heading { + id: heading + text: qsTr("Select your new account type") + color: Kube.Colors.highlightColor + } + + ColumnLayout { + anchors.centerIn: parent + width: parent.width * 0.4 + + spacing: Kube.Units.largeSpacing + + Repeater { + model: root.availableAccountPlugins + delegate: Kube.Button { + Layout.fillWidth: true + text: modelData + onClicked: stack.push(wizardPage.createObject(app, {accountType:modelData})) + } + } + } + } + } + + Component { + id: wizardPage + AccountWizardPage { + focus: true + requireSetup: root.requireSetup + onDone: { + root.close() + Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + } + } + } +} diff --git a/components/accounts/qml/AccountWizardPage.qml b/components/accounts/qml/AccountWizardPage.qml new file mode 100644 index 00000000..9f11ac36 --- /dev/null +++ b/components/accounts/qml/AccountWizardPage.qml @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.4 as Controls +import QtQuick.Controls 2.0 as Controls2 +import org.kube.framework 1.0 as Kube + + +FocusScope { + id: root + property string accountType + signal done() + + property bool isFirstView: root.Controls2.StackView.index == 0 + property bool requireSetup: false + + function save() { + if (loader.item.valid) { + loader.item.save() + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": loader.item.accountIdentifier}); + root.done() + } else { + console.warn("Invalid settings."); + } + } + + //accountType -> uiPath + Kube.AccountFactory { + id: accountFactory + accountType: root.accountType + } + + Controls.ToolButton { + id: backButton + iconName: Kube.Icons.goBack + tooltip: "go back" + visible: !root.isFirstView + onClicked: { + stack.pop() + } + } + + Keys.onReturnPressed: save() + + //Item to avoid anchors conflict with stack + Item { + anchors{ + top: backButton.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + Kube.Heading { + id: heading + text: loader.item.heading + color: Kube.Colors.highlightColor + } + + Kube.Label { + id: subHeadline + + anchors { + left: heading.left + top: heading.bottom + } + + width: parent.width + text: loader.item.subheadline + color: Kube.Colors.disabledTextColor + wrapMode: Text.Wrap + } + + Item { + id: accountEdit + anchors { + top:subHeadline.bottom + left: parent.left + right: parent.right + topMargin: Kube.Units.largeSpacing * 2 + } + + Loader { + id: loader + anchors.fill: parent + focus: true + source: accountFactory.uiPath + } + } + + Item { + id: spacer + Layout.fillHeight: true + anchors { + top:accountEdit.bottom + bottom: footer.top + left: parent.left + right: parent.right + } + } + + //This is where we should place the account wizard ui + Item { + id: footer + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + topMargin: Kube.Units.largeSpacing * 2 + } + + Kube.Button { + anchors { + left: parent.left + bottom: parent.bottom + } + visible: !root.requireSetup + + text: qsTr("Discard") + onClicked: { + root.done() + } + } + + Kube.PositiveButton { + anchors { + right: parent.right + bottom: parent.bottom + } + + text: qsTr("Save") + onClicked: save() + } + } + } +} diff --git a/components/accounts/qml/main.qml b/components/accounts/qml/main.qml new file mode 100644 index 00000000..e15bca8f --- /dev/null +++ b/components/accounts/qml/main.qml @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Michael Bohlender, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.0 as Controls2 + +Controls2.ApplicationWindow { + id: app + height: 900 + width: 1500 + + AccountWizard { + visible: true + } +} diff --git a/components/kube/contents/ui/AccountsView.qml b/components/kube/contents/ui/AccountsView.qml deleted file mode 100644 index df88e1f7..00000000 --- a/components/kube/contents/ui/AccountsView.qml +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.4 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.3 as Controls -import QtQuick.Controls 2.0 -import org.kube.framework 1.0 as Kube -import org.kube.components.accounts 1.0 as KubeAccounts - -FocusScope { - id: root - //Defines whether more than one account is supported. - property bool singleAccountMode: false - //Defines available account types. - property var availableAccountPlugins: ["kolabnow", "imap", "maildir", "gmail"] - - Controls.SplitView { - height: parent.height - width: parent.width - - Item { - id: accountList - width: Kube.Units.gridUnit * 12 - Layout.fillHeight: true - visible: !root.singleAccountMode - - Kube.PositiveButton { - id: newAccountButton - anchors { - top: parent.top - left: parent.left - right: parent.right - margins: Kube.Units.largeSpacing - } - text: qsTr("New Account") - - onClicked: accountWizard.open() - } - - Kube.ListView { - id: listView - - anchors { - top: newAccountButton.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - topMargin: Kube.Units.largeSpacing - } - - clip: true - - model: Kube.AccountsModel {} - - onCurrentItemChanged: { - if (currentItem) { - edit.accountId = currentItem.currentData.accountId - } - } - - delegate: Kube.ListDelegate { - id: delegateRoot - - Kube.Label { - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: Kube.Units.largeSpacing - } - width: parent.width - Kube.Units.largeSpacing * 2 - - text: model.name - color: delegateRoot.textColor - } - } - } - } - - Item { - height: parent.height - width: Kube.Units.gridUnit * 20 - Layout.fillWidth: true - - Kube.EditAccount { - id: edit - anchors { - fill: parent - bottomMargin: Kube.Units.largeSpacing - } - - canRemove: !root.singleAccountMode - - Component.onCompleted: { - //We don't have any accounts setup if accountId is empty, so we trigger the accountWizard - //FIXME: this assumes we load accounts synchronously, which we do right now. - if (accountId == "") { - //Require the setup to be completed since it's the first account - accountWizard.requireSetup = true - //Launch account wizard - accountWizard.open() - } - } - } - } - } - - onActiveFocusChanged: { - if (activeFocus && accountWizard.visible) { - accountWizard.forceActiveFocus() - } - } - - //BEGIN AccountWizard - KubeAccounts.AccountWizard { - id: accountWizard - - parent: ApplicationWindow.overlay - height: app.height - width: app.width - app.sidebarWidth - x: app.sidebarWidth - y: 0 - - availableAccountPlugins: root.availableAccountPlugins - } - //END AccountWizard -} diff --git a/components/kube/contents/ui/AddresseeListEditor.qml b/components/kube/contents/ui/AddresseeListEditor.qml deleted file mode 100644 index 8f9862e7..00000000 --- a/components/kube/contents/ui/AddresseeListEditor.qml +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 - -import org.kube.framework 1.0 as Kube - -FocusScope { - id: root - property variant controller - property variant completer - property bool encrypt: false - - implicitHeight: listView.height + lineEdit.height - height: implicitHeight - - Column { - anchors.fill: parent - - spacing: Kube.Units.smallSpacing - - ListView { - id: listView - anchors { - left: parent.left - right: parent.right - } - height: contentHeight - spacing: Kube.Units.smallSpacing - model: controller.model - delegate: Rectangle { - height: Kube.Units.gridUnit + Kube.Units.smallSpacing * 2 //smallSpacing for padding - width: parent.width - color: Kube.Colors.buttonColor - Row { - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: removeButton.left - margins: Kube.Units.smallSpacing - } - spacing: Kube.Units.smallSpacing - Kube.Label { - id: label - anchors { - top: parent.top - } - text: model.name - elide: Text.ElideRight - } - Kube.Icon { - anchors { - top: parent.top - } - height: Kube.Units.gridUnit - width: height - visible: root.encrypt - iconName: model.keyFound ? Kube.Icons.secure: Kube.Icons.insecure - } - } - Kube.IconButton { - id: removeButton - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - margins: Kube.Units.smallSpacing - } - height: Kube.Units.gridUnit - width: height - onClicked: root.controller.remove(model.id) - padding: 0 - iconName: Kube.Icons.remove - } - } - } - - FocusScope { - height: Kube.Units.gridUnit * Kube.Units.smallSpacing * 2 - width: parent.width - focus: true - - Kube.TextButton { - id: button - text: "+ " + qsTr("Add recipient") - textColor: Kube.Colors.highlightColor - focus: true - onClicked: { - lineEdit.visible = true - lineEdit.forceActiveFocus() - } - } - - Kube.AutocompleteLineEdit { - id: lineEdit - anchors { - left: parent.left - right: parent.right - } - visible: false - - placeholderText: "+ " + qsTr("Add recipient") - model: root.completer.model - onSearchTermChanged: root.completer.searchString = searchTerm - onAccepted: { - root.controller.add({name: text}); - clear() - visible = false - button.forceActiveFocus() - } - onAborted: { - clear() - visible = false - button.forceActiveFocus() - } - } - } - } -} diff --git a/components/kube/contents/ui/ComposerView.qml b/components/kube/contents/ui/ComposerView.qml deleted file mode 100644 index 2e0267d5..00000000 --- a/components/kube/contents/ui/ComposerView.qml +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Controls 1.3 -import QtQuick.Controls 2.0 as Controls2 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.0 as Dialogs - -import org.kube.framework 1.0 as Kube - -Kube.View { - id: root - - property bool newMessage: false - property bool loadAsDraft: false - property variant message: {} - property variant recipients: [] - - resources: [ - Kube.ComposerController { - id: composerController - htmlBody: html.checked - sign: signCheckbox.checked - encrypt: encryptCheckbox.checked - onDone: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) - - property bool foundAllKeys: to.foundAllKeys && cc.foundAllKeys && bcc.foundAllKeys - - sendAction.enabled: composerController.accountId && composerController.subject && (!composerController.encrypt || composerController.foundAllKeys) && (!composerController.sign && !composerController.encrypt || composerController.foundPersonalKeys) - saveAsDraftAction.enabled: composerController.accountId - } - ] - - Component.onCompleted: loadMessage(root.message, root.loadAsDraft) - - Controls2.StackView.onActivated: { - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "mail", "specialPurpose": "drafts"}) - //For autocompletion - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "contacts"}) - } - - function loadMessage(message, loadAsDraft) { - if (message) { - composerController.loadMessage(message, loadAsDraft) - //Forward focus for replies directly - if (!loadAsDraft) { - subject.forceActiveFocus() - } - } else if (newMessage) { - composerController.clear() - if (root.recipients) { - for (var i = 0; i < root.recipients.length; ++i) { - composerController.to.add({name: root.recipients[i]}) - } - } - subject.forceActiveFocus() - } - } - - function closeFirstSplitIfNecessary() { - //Move the view forward - if (root.currentIndex == 0) { - listView.currentIndex = -1 - root.incrementCurrentIndex() - } - } - - //Drafts - Rectangle { - - anchors { - top: parent.top - bottom: parent.bottom - } - - width: Kube.Units.gridUnit * 10 - Layout.minimumWidth: Kube.Units.gridUnit * 5 - - color: Kube.Colors.textColor - - ColumnLayout { - - anchors { - fill: parent - margins: Kube.Units.largeSpacing - } - - spacing: Kube.Units.largeSpacing - - Kube.PositiveButton { - anchors { - left: parent.left - right: parent.right - } - focus: true - text: qsTr("New Email") - onClicked: { - composerController.clear() - subject.forceActiveFocus() - } - } - - Kube.Label{ - text: qsTr("Drafts") - color: Kube.Colors.highlightedTextColor - } - - Kube.ListView { - id: listView - activeFocusOnTab: true - - anchors { - left: parent.left - right: parent.right - } - - Layout.fillHeight: true - clip: true - - //BEGIN keyboard nav - onActiveFocusChanged: { - if (activeFocus && currentIndex < 0) { - currentIndex = 0 - } - } - Keys.onDownPressed: { - listView.incrementCurrentIndex() - } - Keys.onUpPressed: { - listView.decrementCurrentIndex() - } - //END keyboard nav - - onCurrentItemChanged: { - if (currentItem) { - root.loadMessage(currentItem.currentData.domainObject, true) - } - } - - model: Kube.MailListModel { - id: mailListModel - showDrafts: true - } - - delegate: Kube.ListDelegate { - id: delegateRoot - - color: Kube.Colors.textColor - border.width: 0 - - Item { - id: content - - anchors { - fill: parent - margins: Kube.Units.smallSpacing - } - - Kube.Label { - width: content.width - text: model.subject == "" ? "no subject" : model.subject - color: Kube.Colors.highlightedTextColor - maximumLineCount: 2 - wrapMode: Text.WrapAnywhere - elide: Text.ElideRight - } - - Kube.Label { - anchors { - right: parent.right - bottom: parent.bottom - } - text: Qt.formatDateTime(model.date, "dd MMM yyyy") - font.italic: true - color: Kube.Colors.disabledTextColor - font.pointSize: Kube.Units.smallFontSize - visible: !delegateRoot.hovered - } - } - Row { - id: buttons - - anchors { - right: parent.right - bottom: parent.bottom - margins: Kube.Units.smallSpacing - } - - visible: delegateRoot.hovered - spacing: Kube.Units.smallSpacing - opacity: 0.7 - - Kube.IconButton { - id: deleteButton - activeFocusOnTab: true - iconName: Kube.Icons.moveToTrash - visible: enabled - enabled: !!model.mail - onClicked: Kube.Fabric.postMessage(Kube.Messages.moveToTrash, {"mail": model.mail}) - } - } - } - } - } - } - - //Content - Rectangle { - Layout.fillWidth: true - Layout.minimumWidth: Kube.Units.gridUnit * 5 - anchors { - top: parent.top - bottom: parent.bottom - } - color: Kube.Colors.backgroundColor - - ColumnLayout { - anchors { - fill: parent - margins: Kube.Units.largeSpacing - leftMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 - rightMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 - } - - spacing: Kube.Units.smallSpacing - - Kube.TextField { - id: subject - Layout.fillWidth: true - activeFocusOnTab: true - - placeholderText: qsTr("Enter Subject...") - text: composerController.subject - onTextChanged: composerController.subject = text; - onActiveFocusChanged: { - if (activeFocus) { - closeFirstSplitIfNecessary() - } - } - } - - Flow { - id: attachments - - Layout.fillWidth: true - layoutDirection: Qt.RightToLeft - spacing: Kube.Units.smallSpacing - clip: true - - Repeater { - model: composerController.attachments.model - delegate: Kube.AttachmentDelegate { - name: model.filename - icon: model.iconname - clip: true - actionIcon: Kube.Icons.remove - onExecute: composerController.attachments.remove(model.id) - } - } - } - - RowLayout { - - spacing: Kube.Units.largeSpacing - - Kube.Switch { - id: html - text: checked ? qsTr("plain") : qsTr("html") - focusPolicy: Qt.TabFocus - focus: false - checked: composerController.htmlBody - } - - Row { - visible: html.checked - spacing: 1 - - Kube.IconButton { - iconName: Kube.Icons.bold - checkable: true - checked: textEditor.bold - onClicked: textEditor.bold = !textEditor.bold - focusPolicy: Qt.TabFocus - focus: false - } - Kube.IconButton { - iconName: Kube.Icons.italic - checkable: true - checked: textEditor.italic - onClicked: textEditor.italic = !textEditor.italic - focusPolicy: Qt.TabFocus - focus: false - } - Kube.IconButton { - iconName: Kube.Icons.underline - checkable: true - checked: textEditor.underline - onClicked: textEditor.underline = !textEditor.underline - focusPolicy: Qt.TabFocus - focus: false - } - } - - Item { - height: 1 - Layout.fillWidth: true - } - - Kube.Button { - text: qsTr("Attach file") - - onClicked: { - fileDialogComponent.createObject(parent) - } - - Component { - id: fileDialogComponent - Dialogs.FileDialog { - id: fileDialog - visible: true - title: "Choose a file to attach" - selectFolder: false - onAccepted: { - composerController.attachments.add({url: fileDialog.fileUrl}) - } - } - } - } - } - - Kube.TextEditor { - id: textEditor - - Layout.fillWidth: true - Layout.fillHeight: true - htmlEnabled: html.checked - - onActiveFocusChanged: closeFirstSplitIfNecessary() - Keys.onEscapePressed: recipients.forceActiveFocus() - initialText: composerController.body - onTextChanged: composerController.body = text; - } - } - } - - //Recepients - FocusScope { - id: recipients - anchors { - top: parent.top - bottom: parent.bottom - } - width: Kube.Units.gridUnit * 15 - activeFocusOnTab: true - - //background - Rectangle { - anchors.fill: parent - color: Kube.Colors.backgroundColor - - Rectangle { - height: parent.height - width: 1 - color: Kube.Colors.buttonColor - } - } - - //Content - ColumnLayout { - anchors { - fill: parent - margins: Kube.Units.largeSpacing - } - - spacing: Kube.Units.largeSpacing - ColumnLayout { - Layout.maximumWidth: parent.width - Layout.fillWidth: true - Layout.fillHeight: true - - Kube.Label { - text: qsTr("Sending Email to:") - } - - AddresseeListEditor { - Layout.preferredHeight: implicitHeight - Layout.fillWidth: true - focus: true - activeFocusOnTab: true - encrypt: composerController.encrypt - controller: composerController.to - completer: composerController.recipientCompleter - } - - Kube.Label { - text: qsTr("Sending Copy to (CC):") - } - AddresseeListEditor { - id: cc - Layout.preferredHeight: cc.implicitHeight - Layout.fillWidth: true - activeFocusOnTab: true - encrypt: composerController.encrypt - controller: composerController.cc - completer: composerController.recipientCompleter - } - - Kube.Label { - text: qsTr("Sending Secret Copy to (Bcc):") - } - AddresseeListEditor { - id: bcc - Layout.preferredHeight: bcc.implicitHeight - Layout.fillWidth: true - activeFocusOnTab: true - encrypt: composerController.encrypt - controller: composerController.bcc - completer: composerController.recipientCompleter - } - Item { - width: parent.width - Layout.fillHeight: true - } - } - - RowLayout { - enabled: composerController.foundPersonalKeys - Kube.CheckBox { - id: encryptCheckbox - checked: composerController.encrypt - } - Kube.Label { - text: qsTr("encrypt") - } - } - - RowLayout { - enabled: composerController.foundPersonalKeys - Kube.CheckBox { - id: signCheckbox - checked: composerController.sign - } - Kube.Label { - text: qsTr("sign") - } - } - Kube.Label { - visible: !composerController.foundPersonalKeys - Layout.maximumWidth: parent.width - text: qsTr("Encryption is not available because your personal key has not been found.") - wrapMode: Text.Wrap - } - - RowLayout { - Layout.maximumWidth: parent.width - width: parent.width - height: Kube.Units.gridUnit - - Kube.Button { - width: saveDraftButton.width - text: qsTr("Discard") - onClicked: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) - } - - Kube.Button { - id: saveDraftButton - - text: qsTr("Save as Draft") - enabled: composerController.saveAsDraftAction.enabled - onClicked: { - composerController.saveAsDraftAction.execute() - } - } - } - - ColumnLayout { - Layout.maximumWidth: parent.width - Layout.fillWidth: true - Kube.Label { - id: fromLabel - text: qsTr("You are sending this from:") - } - - Kube.ComboBox { - id: identityCombo - - width: parent.width - Kube.Units.largeSpacing * 2 - - model: composerController.identitySelector.model - textRole: "address" - Layout.fillWidth: true - onCurrentIndexChanged: { - composerController.identitySelector.currentIndex = currentIndex - } - } - } - - Kube.PositiveButton { - id: sendButton - - width: parent.width - - text: qsTr("Send") - enabled: composerController.sendAction.enabled - onClicked: { - composerController.sendAction.execute() - } - } - } - }//FocusScope -} diff --git a/components/kube/contents/ui/Kube.qml b/components/kube/contents/ui/Kube.qml deleted file mode 100644 index c73d5bfa..00000000 --- a/components/kube/contents/ui/Kube.qml +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Layouts 1.3 -import QtQuick.Window 2.0 - -import QtQuick.Controls 2.0 as Controls2 -import org.kube.framework 1.0 as Kube - -Controls2.ApplicationWindow { - id: app - - property int sidebarWidth: Kube.Units.gridUnit + Kube.Units.largeSpacing - - height: Screen.desktopAvailableHeight * 0.8 - width: Screen.desktopAvailableWidth * 0.8 - visible: true - - //Application default font - font.family: Kube.Font.fontFamily - - //Application context - property variant currentFolder - onCurrentFolderChanged: { - if (!!currentFolder) { - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": currentFolder}) - } - } - property variant currentAccount - onCurrentAccountChanged: { - if (!!currentAccount) { - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": currentAccount}) - } - } - - Kube.AccountFactory { - id: accountFactory - accountId: !!app.currentAccount ? app.currentAccount : "" - } - - //Interval sync - Timer { - id: intervalSync - //5min - interval: 300000 - running: !!app.currentFolder - repeat: true - onTriggered: Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) - } - - Kube.StartupCheck { - id: startupCheck - } - - //Listener - Kube.Listener { - filter: Kube.Messages.accountSelection - onMessageReceived: app.currentAccount = message.account - } - - Kube.Listener { - filter: Kube.Messages.folderSelection - onMessageReceived: app.currentFolder = message.folder - } - - Kube.Listener { - filter: Kube.Messages.notification - onMessageReceived: { - notificationPopup.notify(message.message); - } - } - - Kube.Listener { - filter: Kube.Messages.reply - onMessageReceived: { - kubeViews.openComposerWithMail(message.mail, false) - } - } - - Kube.Listener { - filter: Kube.Messages.edit - onMessageReceived: { - kubeViews.openComposerWithMail(message.mail, true) - } - } - - Kube.Listener { - filter: Kube.Messages.compose - onMessageReceived: kubeViews.openComposer(true, message.recipients) - } - - Kube.Listener { - filter: Kube.Messages.requestLogin - onMessageReceived: kubeViews.setLoginView() - } - - Kube.Listener { - filter: Kube.Messages.requestAccountsConfiguration - onMessageReceived: kubeViews.setAccountsView() - } - - //BEGIN Shortcuts - Shortcut { - sequence: StandardKey.Quit - onActivated: Qt.quit() - } - Shortcut { - onActivated: Kube.Fabric.postMessage(Kube.Messages.search, {}) - sequence: StandardKey.Find - } - Shortcut { - onActivated: { - Kube.Fabric.postMessage(Kube.Messages.unlockKeyring, {accountId: app.currentAccount}) - } - sequence: "Ctrl+l" - } - Shortcut { - id: syncShortcut - sequence: StandardKey.Refresh - onActivated: !!app.currentFolder ? Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) : Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": app.currentAccount}) - } - //END Shortcuts - - //BEGIN background - Rectangle { - anchors.fill: parent - - color: Kube.Colors.backgroundColor - } - //END background - - //BEGIN Main content - RowLayout { - id: mainContent - spacing: 0 - anchors.fill: parent - - Rectangle { - id: sideBar - - anchors { - top: mainContent.top - bottom: mainContent.bottom - } - width: app.sidebarWidth - color: Kube.Colors.textColor - - Rectangle { - anchors.right: parent.right - width: 1 - height: parent.height - color: Kube.Colors.viewBackgroundColor - opacity: 0.3 - } - - Controls2.ButtonGroup { id: viewButtonGroup } - - Column { - anchors { - top: parent.top - topMargin: Kube.Units.smallSpacing - horizontalCenter: parent.horizontalCenter - } - - spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing - - Kube.IconButton { - id: composerButton - iconName: Kube.Icons.edit_inverted - onClicked: kubeViews.openComposer(false, []) - activeFocusOnTab: true - checkable: true - Controls2.ButtonGroup.group: viewButtonGroup - tooltip: qsTr("composer") - } - - Kube.IconButton { - id: mailButton - iconName: Kube.Icons.mail_inverted - onClicked: kubeViews.setMailView() - activeFocusOnTab: true - checkable: true - checked: true - Controls2.ButtonGroup.group: viewButtonGroup - tooltip: qsTr("mails") - } - - Kube.IconButton { - id: peopleButton - iconName: Kube.Icons.user_inverted - onClicked: kubeViews.setPeopleView() - activeFocusOnTab: true - checkable: true - Controls2.ButtonGroup.group: viewButtonGroup - tooltip: qsTr("people") - } - } - Column { - anchors { - bottom: parent.bottom - bottomMargin: Kube.Units.smallSpacing - horizontalCenter: parent.horizontalCenter - } - - spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing - Kube.Outbox { - height: Kube.Units.gridUnit * 1.5 - width: height - - Kube.ToolTip { - text: qsTr("outbox") - visible: parent.hovered - } - } - - Kube.IconButton { - id: logButton - iconName: Kube.Icons.info_inverted - onClicked: kubeViews.setLogView() - activeFocusOnTab: true - checkable: true - alert: logView.pendingError - Controls2.ButtonGroup.group: viewButtonGroup - tooltip: qsTr("logview") - } - - Kube.IconButton { - id: accountsButton - iconName: Kube.Icons.menu_inverted - onClicked: kubeViews.setAccountsView() - activeFocusOnTab: true - checkable: true - Controls2.ButtonGroup.group: viewButtonGroup - tooltip: qsTr("settings") - } - } - } - Controls2.StackView { - id: kubeViews - - anchors { - top: mainContent.top - bottom: mainContent.bottom - } - Layout.fillWidth: true - - function loginIfNecessary() - { - if (!!app.currentAccount && !Kube.Keyring.isUnlocked(app.currentAccount)) { - if (accountFactory.requiresKeyring) { - setLoginView() - } else { - Kube.Keyring.unlock(app.currentAccount) - } - } - } - - Kube.Listener { - filter: Kube.Messages.componentDone - onMessageReceived: { - //Return to the mailview if we try to pop everything off - if (kubeViews.depth == 1) { - kubeViews.setMailView() - } else { - kubeViews.pop(Controls2.StackView.Immediate) - } - kubeViews.loginIfNecessary() - } - } - - onCurrentItemChanged: { - if (currentItem) { - currentItem.forceActiveFocus() - } - } - - Component.onCompleted: { - //Setup the initial item stack - if (!currentItem) { - setMailView() - if (startupCheck.noAccount) { - setAccountsView() - } else { - loginIfNecessary() - } - } - } - - ///Replace the current view (we can't go back to the old view, and we destroy the old view) - function replaceView(view) { - if (currentItem != view) { - kubeViews.replace(null, view, {}, Controls2.StackView.Immediate) - } - } - - ///Push a new view on the stack (the old view remains, and we can go back once done) - function pushView(view, properties) { - kubeViews.push(view, properties, Controls2.StackView.Immediate) - } - - //TODO replacing here while a composer is open is destructive - function setPeopleView() { - replaceView(peopleView) - } - - function setMailView() { - replaceView(mailView) - } - - function setAccountsView() { - pushView(accountsView, {}) - } - - function setLogView() { - replaceView(logView) - } - - function setLoginView() { - if (currentItem != loginView) { - pushView(loginView, {accountId: currentAccount}) - } - } - - function openComposer(newMessage, recipients) { - pushView(composerView, {newMessage: newMessage, recipients: recipients}) - } - - function openComposerWithMail(mail, openAsDraft) { - pushView(composerView, {message: mail, loadAsDraft: openAsDraft}) - } - - - //These items are not visible until pushed onto the stack, so we keep them in resources instead of items - resources: [ - //Not components so we maintain state - MailView { - id: mailView - anchors.fill: parent - Controls2.StackView.onActivated: mailButton.checked = true - Controls2.StackView.onDeactivated: mailButton.checked = false - }, - PeopleView { - id: peopleView - anchors.fill: parent - Controls2.StackView.onActivated: peopleButton.checked = true - Controls2.StackView.onDeactivated: peopleButton.checked = false - }, - //Not a component because otherwise we can't log stuff - LogView { - id: logView - anchors.fill: parent - Controls2.StackView.onActivated: logButton.checked = true - Controls2.StackView.onDeactivated: logButton.checked = false - } - ] - //A component so it's always destroyed when we're done - Component { - id: composerView - ComposerView { - anchors.fill: parent - Controls2.StackView.onActivated: composerButton.checked = true - Controls2.StackView.onDeactivated: composerButton.checked = false - } - } - Component { - id: accountsView - AccountsView { - anchors.fill: parent - Controls2.StackView.onActivated: accountsButton.checked = true - Controls2.StackView.onDeactivated: accountsButton.checked = false - } - } - Component { - id: loginView - LoginView { - anchors.fill: parent - } - } - } - } - //END Main content - - //BEGIN Notification - Kube.NotificationPopup { - id: notificationPopup - - anchors { - left: parent.left - leftMargin: app.sidebarWidth - 3 // so it does not align with the border - bottom: parent.bottom - bottomMargin: Kube.Units.gridUnit * 4 - } - } - //END Notification -} diff --git a/components/kube/contents/ui/LogView.qml b/components/kube/contents/ui/LogView.qml deleted file mode 100644 index 3b0aa2d5..00000000 --- a/components/kube/contents/ui/LogView.qml +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.4 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.3 as Controls -import QtQuick.Controls 2.0 as Controls2 -import org.kube.framework 1.0 as Kube - -Controls.SplitView { - id: root - - property bool pendingError: false; - - Controls2.StackView.onActivated: { - root.pendingError = false; - //Always select the latest notification - listView.currentIndex = 0 - } - - Item { - id: accountList - width: parent.width/3 - Layout.fillHeight: true - - Kube.Listener { - filter: Kube.Messages.notification - onMessageReceived: { - if (message.type == Kube.Notifications.error) { - root.pendingError = true - } - var error = {timestamp: new Date(), message: message.message, details: message.details, resource: message.resource} - if (logModel.count > 0) { - var lastEntry = logModel.get(0) - //Merge if we get an entry of the same subtype - if (lastEntry.subtype && lastEntry.subtype == message.subtype) { - logModel.set(0, {type: message.type, subtype: message.subtype, errors: [error].concat(lastEntry.errors)}) - return - } - } - logModel.insert(0, {type: message.type, subtype: message.subtype, errors: [error]}) - } - } - - Kube.Label { - anchors.centerIn: parent - visible: listView.count == 0 - text: qsTr("Nothing here...") - } - - Kube.ListView { - id: listView - anchors { - fill: parent - } - - clip: true - - model: ListModel { - id: logModel - objectName: "logModel" - } - - onCurrentItemChanged: { - var error = currentItem.currentData.errors.get(0) - if (!!error.resource) { - details.resourceId = error.resource - } - details.message = error.message + "\n" + error.details - details.timestamp = error.timestamp - if (!!currentItem.currentData.subtype) { - details.subtype = currentItem.currentData.subtype - } else { - details.subtype = "" - } - } - - delegate: Kube.ListDelegate { - border.color: Kube.Colors.buttonColor - border.width: 1 - Kube.Label { - id: description - anchors { - top: parent.top - topMargin: Kube.Units.smallSpacing - left: parent.left - leftMargin: Kube.Units.largeSpacing - } - height: Kube.Units.gridUnit - width: parent.width - Kube.Units.largeSpacing * 2 - text: model.type == Kube.Notifications.error ? qsTr("Error") : qsTr("Info") - } - - Kube.Label { - id: message - anchors { - topMargin: Kube.Units.smallSpacing - top: description.bottom - left: parent.left - leftMargin: Kube.Units.largeSpacing - } - height: Kube.Units.gridUnit - width: parent.width - Kube.Units.largeSpacing * 2 - maximumLineCount: 1 - elide: Text.ElideRight - color: Kube.Colors.disabledTextColor - text: model.errors.get(0).message - } - - Kube.Label { - id: date - - anchors { - right: parent.right - bottom: parent.bottom - rightMargin: Kube.Units.smallSpacing - } - text: Qt.formatDateTime(model.errors.get(0).timestamp, " hh:mm:ss dd MMM yyyy") - font.italic: true - color: Kube.Colors.disabledTextColor - font.pointSize: Kube.Units.smallFontSize - } - } - } - } - Item { - id: details - property string subtype: "" - property date timestamp - property string message: "" - property string resourceId: "" - - Kube.ModelIndexRetriever { - id: retriever - model: Kube.AccountsModel { - resourceId: details.resourceId - } - } - - Loader { - id: detailsLoader - visible: message != "" - clip: true - anchors { - fill: parent - margins: Kube.Units.largeSpacing - } - property date timestamp: details.timestamp - property string message: details.message - property string resourceId: details.resourceId - property string accountId: retriever.currentData ? retriever.currentData.accountId : "" - property string accountName: retriever.currentData ? retriever.currentData.name : "" - - function getComponent(subtype) { - if (subtype == Kube.Notifications.loginError) { - return loginErrorComponent - } - if (subtype == Kube.Notifications.hostNotFoundError) { - return hostNotFoundErrorComponent - } - if (subtype == Kube.Notifications.connectionError) { - return hostNotFoundErrorComponent - } - return detailsComponent - } - - sourceComponent: getComponent(details.subtype) - } - } - - Component { - id: detailsComponent - Rectangle { - color: Kube.Colors.viewBackgroundColor - GridLayout { - id: gridLayout - Layout.minimumWidth: 0 - anchors { - top: parent.top - left: parent.left - right: parent.right - } - columns: 2 - Kube.Label { - text: qsTr("Account:") - visible: accountName - } - Kube.Label { - Layout.fillWidth: true - text: accountName - visible: accountName - elide: Text.ElideRight - } - Kube.Label { - text: qsTr("Account Id:") - visible: accountId - } - Kube.Label { - text: accountId - visible: accountId - Layout.fillWidth: true - elide: Text.ElideRight - } - Kube.Label { - text: qsTr("Resource Id:") - visible: resourceId - } - Kube.Label { - text: resourceId - visible: resourceId - Layout.fillWidth: true - elide: Text.ElideRight - } - Kube.Label { - text: qsTr("Timestamp:") - } - Kube.Label { - text: Qt.formatDateTime(timestamp, " hh:mm:ss dd MMM yyyy") - Layout.fillWidth: true - elide: Text.ElideRight - } - Kube.Label { - text: qsTr("Message:") - Layout.alignment: Qt.AlignTop - } - Kube.Label { - text: message - Layout.fillWidth: true - wrapMode: Text.Wrap - } - Item { - Layout.columnSpan: 2 - Layout.fillHeight: true - Layout.fillWidth: true - } - } - - Kube.SelectableItem { - layout: gridLayout - } - } - } - - Component { - id: loginErrorComponent - Item { - Column { - anchors { - top: parent.top - left: parent.left - right: parent.right - } - spacing: Kube.Units.largeSpacing - Column { - Kube.Heading { - id: heading - text: qsTr("Failed to login") - color: Kube.Colors.warningColor - } - - Kube.Label { - id: subHeadline - text: accountName + ": " + qsTr("Please check your credentials.") - color: Kube.Colors.disabledTextColor - wrapMode: Text.Wrap - } - } - Kube.Button { - text: qsTr("Change Password") - onClicked: { - Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) - Kube.Fabric.postMessage(Kube.Messages.requestLogin, {}) - } - } - } - } - } - - Component { - id: hostNotFoundErrorComponent - Item { - Column { - anchors { - top: parent.top - left: parent.left - right: parent.right - } - spacing: Kube.Units.largeSpacing - Column { - Kube.Heading { - id: heading - text: qsTr("Host not found") - color: Kube.Colors.warningColor - } - - Kube.Label { - id: subHeadline - text: accountName + ": " + qsTr("Please check your network connection and settings.") - color: Kube.Colors.disabledTextColor - wrapMode: Text.Wrap - } - } - Kube.Button { - text: qsTr("Account settings") - onClicked: { - Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) - Kube.Fabric.postMessage(Kube.Messages.requestAccountsConfiguration, {}) - } - } - } - } - } -} diff --git a/components/kube/contents/ui/LoginView.qml b/components/kube/contents/ui/LoginView.qml deleted file mode 100644 index dbbed11c..00000000 --- a/components/kube/contents/ui/LoginView.qml +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.4 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import org.kube.framework 1.0 as Kube - -FocusScope { - id: root - property alias accountId: login.accountId - onActiveFocusChanged: { - if (activeFocus) { - popup.forceActiveFocus() - } - } - - Kube.Popup { - id: popup - visible: true - parent: ApplicationWindow.overlay - height: app.height - width: app.width - app.sidebarWidth - x: app.sidebarWidth - y: 0 - modal: true - closePolicy: Popup.NoAutoClose - Kube.LoginAccount { - id: login - anchors { - fill: parent - bottomMargin: Kube.Units.largeSpacing - } - } - } -} diff --git a/components/kube/contents/ui/MailView.qml b/components/kube/contents/ui/MailView.qml deleted file mode 100644 index 8b2b0caf..00000000 --- a/components/kube/contents/ui/MailView.qml +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Controls 1.3 -import QtQuick.Controls 2.0 as Controls2 -import QtQuick.Layouts 1.1 - -import org.kube.framework 1.0 as Kube - -FocusScope { - SplitView { - anchors.fill: parent - Rectangle { - width: Kube.Units.gridUnit * 10 - Layout.fillHeight: parent.height - color: Kube.Colors.textColor - - Kube.PositiveButton { - id: newMailButton - - anchors { - top: parent.top - left: parent.left - right: parent.right - margins: Kube.Units.largeSpacing - } - focus: true - text: qsTr("New Email") - onClicked: Kube.Fabric.postMessage(Kube.Messages.compose, {}) - } - - Kube.InlineAccountSwitcher { - id: accountFolderview - activeFocusOnTab: true - anchors { - top: newMailButton.bottom - topMargin: Kube.Units.largeSpacing - bottom: statusBarContainer.top - left: newMailButton.left - right: parent.right - } - } - - Item { - id: statusBarContainer - anchors { - topMargin: Kube.Units.smallSpacing - bottom: parent.bottom - left: parent.left - right: parent.right - } - height: childrenRect.height - - Rectangle { - id: border - visible: statusBar.visible - anchors { - right: parent.right - left: parent.left - margins: Kube.Units.smallSpacing - } - height: 1 - color: Kube.Colors.viewBackgroundColor - opacity: 0.3 - } - Kube.StatusBar { - id: statusBar - accountId: accountFolderview.currentAccount - height: Kube.Units.gridUnit * 2 - anchors { - top: border.bottom - left: statusBarContainer.left - right: statusBarContainer.right - } - } - } - } - - Rectangle { - width: Kube.Units.gridUnit * 18 - Layout.fillHeight: parent.height - - color: "transparent" - border.width: 1 - border.color: Kube.Colors.buttonColor - - Kube.MailListView { - id: mailListView - anchors.fill: parent - activeFocusOnTab: true - Layout.minimumWidth: Kube.Units.gridUnit * 10 - } - } - - Kube.ConversationView { - id: mailView - Layout.fillWidth: true - Layout.fillHeight: parent.height - activeFocusOnTab: true - } - } -} diff --git a/components/kube/contents/ui/PeopleView.qml b/components/kube/contents/ui/PeopleView.qml deleted file mode 100644 index 3f1b9261..00000000 --- a/components/kube/contents/ui/PeopleView.qml +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import org.kube.framework 1.0 as Kube - -Item { - - StackView.onActivated: { - Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "contacts"}) - } - - Kube.People { - id: people - anchors { - fill: parent - margins: Kube.Units.smallSpacing - } - } -} diff --git a/components/kube/contents/ui/main.qml b/components/kube/contents/ui/main.qml deleted file mode 100644 index 4391ebe5..00000000 --- a/components/kube/contents/ui/main.qml +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.7 -import org.kube.components.kube 1.0 as KubeComponent - -KubeComponent.Kube { - -} diff --git a/components/kube/metadata.desktop b/components/kube/metadata.desktop deleted file mode 100644 index 581bbdf4..00000000 --- a/components/kube/metadata.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Name=Kube -X-KDE-PluginInfo-Name=org.kube.components.kube -Exec=kpackagelauncherqml -a org.kube.components.kube -X-Plasma-MainScript=ui/main.qml -X-KDE-ServiceTypes=KPackage/GenericQML -Icon=kmail2 -Type=Service diff --git a/components/kube/qml/AccountsView.qml b/components/kube/qml/AccountsView.qml new file mode 100644 index 00000000..df88e1f7 --- /dev/null +++ b/components/kube/qml/AccountsView.qml @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.3 as Controls +import QtQuick.Controls 2.0 +import org.kube.framework 1.0 as Kube +import org.kube.components.accounts 1.0 as KubeAccounts + +FocusScope { + id: root + //Defines whether more than one account is supported. + property bool singleAccountMode: false + //Defines available account types. + property var availableAccountPlugins: ["kolabnow", "imap", "maildir", "gmail"] + + Controls.SplitView { + height: parent.height + width: parent.width + + Item { + id: accountList + width: Kube.Units.gridUnit * 12 + Layout.fillHeight: true + visible: !root.singleAccountMode + + Kube.PositiveButton { + id: newAccountButton + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: Kube.Units.largeSpacing + } + text: qsTr("New Account") + + onClicked: accountWizard.open() + } + + Kube.ListView { + id: listView + + anchors { + top: newAccountButton.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + topMargin: Kube.Units.largeSpacing + } + + clip: true + + model: Kube.AccountsModel {} + + onCurrentItemChanged: { + if (currentItem) { + edit.accountId = currentItem.currentData.accountId + } + } + + delegate: Kube.ListDelegate { + id: delegateRoot + + Kube.Label { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: Kube.Units.largeSpacing + } + width: parent.width - Kube.Units.largeSpacing * 2 + + text: model.name + color: delegateRoot.textColor + } + } + } + } + + Item { + height: parent.height + width: Kube.Units.gridUnit * 20 + Layout.fillWidth: true + + Kube.EditAccount { + id: edit + anchors { + fill: parent + bottomMargin: Kube.Units.largeSpacing + } + + canRemove: !root.singleAccountMode + + Component.onCompleted: { + //We don't have any accounts setup if accountId is empty, so we trigger the accountWizard + //FIXME: this assumes we load accounts synchronously, which we do right now. + if (accountId == "") { + //Require the setup to be completed since it's the first account + accountWizard.requireSetup = true + //Launch account wizard + accountWizard.open() + } + } + } + } + } + + onActiveFocusChanged: { + if (activeFocus && accountWizard.visible) { + accountWizard.forceActiveFocus() + } + } + + //BEGIN AccountWizard + KubeAccounts.AccountWizard { + id: accountWizard + + parent: ApplicationWindow.overlay + height: app.height + width: app.width - app.sidebarWidth + x: app.sidebarWidth + y: 0 + + availableAccountPlugins: root.availableAccountPlugins + } + //END AccountWizard +} diff --git a/components/kube/qml/AddresseeListEditor.qml b/components/kube/qml/AddresseeListEditor.qml new file mode 100644 index 00000000..8f9862e7 --- /dev/null +++ b/components/kube/qml/AddresseeListEditor.qml @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +import QtQuick 2.7 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.1 + +import org.kube.framework 1.0 as Kube + +FocusScope { + id: root + property variant controller + property variant completer + property bool encrypt: false + + implicitHeight: listView.height + lineEdit.height + height: implicitHeight + + Column { + anchors.fill: parent + + spacing: Kube.Units.smallSpacing + + ListView { + id: listView + anchors { + left: parent.left + right: parent.right + } + height: contentHeight + spacing: Kube.Units.smallSpacing + model: controller.model + delegate: Rectangle { + height: Kube.Units.gridUnit + Kube.Units.smallSpacing * 2 //smallSpacing for padding + width: parent.width + color: Kube.Colors.buttonColor + Row { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: removeButton.left + margins: Kube.Units.smallSpacing + } + spacing: Kube.Units.smallSpacing + Kube.Label { + id: label + anchors { + top: parent.top + } + text: model.name + elide: Text.ElideRight + } + Kube.Icon { + anchors { + top: parent.top + } + height: Kube.Units.gridUnit + width: height + visible: root.encrypt + iconName: model.keyFound ? Kube.Icons.secure: Kube.Icons.insecure + } + } + Kube.IconButton { + id: removeButton + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + margins: Kube.Units.smallSpacing + } + height: Kube.Units.gridUnit + width: height + onClicked: root.controller.remove(model.id) + padding: 0 + iconName: Kube.Icons.remove + } + } + } + + FocusScope { + height: Kube.Units.gridUnit * Kube.Units.smallSpacing * 2 + width: parent.width + focus: true + + Kube.TextButton { + id: button + text: "+ " + qsTr("Add recipient") + textColor: Kube.Colors.highlightColor + focus: true + onClicked: { + lineEdit.visible = true + lineEdit.forceActiveFocus() + } + } + + Kube.AutocompleteLineEdit { + id: lineEdit + anchors { + left: parent.left + right: parent.right + } + visible: false + + placeholderText: "+ " + qsTr("Add recipient") + model: root.completer.model + onSearchTermChanged: root.completer.searchString = searchTerm + onAccepted: { + root.controller.add({name: text}); + clear() + visible = false + button.forceActiveFocus() + } + onAborted: { + clear() + visible = false + button.forceActiveFocus() + } + } + } + } +} diff --git a/components/kube/qml/ComposerView.qml b/components/kube/qml/ComposerView.qml new file mode 100644 index 00000000..2e0267d5 --- /dev/null +++ b/components/kube/qml/ComposerView.qml @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +import QtQuick 2.7 +import QtQuick.Controls 1.3 +import QtQuick.Controls 2.0 as Controls2 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.0 as Dialogs + +import org.kube.framework 1.0 as Kube + +Kube.View { + id: root + + property bool newMessage: false + property bool loadAsDraft: false + property variant message: {} + property variant recipients: [] + + resources: [ + Kube.ComposerController { + id: composerController + htmlBody: html.checked + sign: signCheckbox.checked + encrypt: encryptCheckbox.checked + onDone: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + + property bool foundAllKeys: to.foundAllKeys && cc.foundAllKeys && bcc.foundAllKeys + + sendAction.enabled: composerController.accountId && composerController.subject && (!composerController.encrypt || composerController.foundAllKeys) && (!composerController.sign && !composerController.encrypt || composerController.foundPersonalKeys) + saveAsDraftAction.enabled: composerController.accountId + } + ] + + Component.onCompleted: loadMessage(root.message, root.loadAsDraft) + + Controls2.StackView.onActivated: { + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "mail", "specialPurpose": "drafts"}) + //For autocompletion + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "contacts"}) + } + + function loadMessage(message, loadAsDraft) { + if (message) { + composerController.loadMessage(message, loadAsDraft) + //Forward focus for replies directly + if (!loadAsDraft) { + subject.forceActiveFocus() + } + } else if (newMessage) { + composerController.clear() + if (root.recipients) { + for (var i = 0; i < root.recipients.length; ++i) { + composerController.to.add({name: root.recipients[i]}) + } + } + subject.forceActiveFocus() + } + } + + function closeFirstSplitIfNecessary() { + //Move the view forward + if (root.currentIndex == 0) { + listView.currentIndex = -1 + root.incrementCurrentIndex() + } + } + + //Drafts + Rectangle { + + anchors { + top: parent.top + bottom: parent.bottom + } + + width: Kube.Units.gridUnit * 10 + Layout.minimumWidth: Kube.Units.gridUnit * 5 + + color: Kube.Colors.textColor + + ColumnLayout { + + anchors { + fill: parent + margins: Kube.Units.largeSpacing + } + + spacing: Kube.Units.largeSpacing + + Kube.PositiveButton { + anchors { + left: parent.left + right: parent.right + } + focus: true + text: qsTr("New Email") + onClicked: { + composerController.clear() + subject.forceActiveFocus() + } + } + + Kube.Label{ + text: qsTr("Drafts") + color: Kube.Colors.highlightedTextColor + } + + Kube.ListView { + id: listView + activeFocusOnTab: true + + anchors { + left: parent.left + right: parent.right + } + + Layout.fillHeight: true + clip: true + + //BEGIN keyboard nav + onActiveFocusChanged: { + if (activeFocus && currentIndex < 0) { + currentIndex = 0 + } + } + Keys.onDownPressed: { + listView.incrementCurrentIndex() + } + Keys.onUpPressed: { + listView.decrementCurrentIndex() + } + //END keyboard nav + + onCurrentItemChanged: { + if (currentItem) { + root.loadMessage(currentItem.currentData.domainObject, true) + } + } + + model: Kube.MailListModel { + id: mailListModel + showDrafts: true + } + + delegate: Kube.ListDelegate { + id: delegateRoot + + color: Kube.Colors.textColor + border.width: 0 + + Item { + id: content + + anchors { + fill: parent + margins: Kube.Units.smallSpacing + } + + Kube.Label { + width: content.width + text: model.subject == "" ? "no subject" : model.subject + color: Kube.Colors.highlightedTextColor + maximumLineCount: 2 + wrapMode: Text.WrapAnywhere + elide: Text.ElideRight + } + + Kube.Label { + anchors { + right: parent.right + bottom: parent.bottom + } + text: Qt.formatDateTime(model.date, "dd MMM yyyy") + font.italic: true + color: Kube.Colors.disabledTextColor + font.pointSize: Kube.Units.smallFontSize + visible: !delegateRoot.hovered + } + } + Row { + id: buttons + + anchors { + right: parent.right + bottom: parent.bottom + margins: Kube.Units.smallSpacing + } + + visible: delegateRoot.hovered + spacing: Kube.Units.smallSpacing + opacity: 0.7 + + Kube.IconButton { + id: deleteButton + activeFocusOnTab: true + iconName: Kube.Icons.moveToTrash + visible: enabled + enabled: !!model.mail + onClicked: Kube.Fabric.postMessage(Kube.Messages.moveToTrash, {"mail": model.mail}) + } + } + } + } + } + } + + //Content + Rectangle { + Layout.fillWidth: true + Layout.minimumWidth: Kube.Units.gridUnit * 5 + anchors { + top: parent.top + bottom: parent.bottom + } + color: Kube.Colors.backgroundColor + + ColumnLayout { + anchors { + fill: parent + margins: Kube.Units.largeSpacing + leftMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 + rightMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 + } + + spacing: Kube.Units.smallSpacing + + Kube.TextField { + id: subject + Layout.fillWidth: true + activeFocusOnTab: true + + placeholderText: qsTr("Enter Subject...") + text: composerController.subject + onTextChanged: composerController.subject = text; + onActiveFocusChanged: { + if (activeFocus) { + closeFirstSplitIfNecessary() + } + } + } + + Flow { + id: attachments + + Layout.fillWidth: true + layoutDirection: Qt.RightToLeft + spacing: Kube.Units.smallSpacing + clip: true + + Repeater { + model: composerController.attachments.model + delegate: Kube.AttachmentDelegate { + name: model.filename + icon: model.iconname + clip: true + actionIcon: Kube.Icons.remove + onExecute: composerController.attachments.remove(model.id) + } + } + } + + RowLayout { + + spacing: Kube.Units.largeSpacing + + Kube.Switch { + id: html + text: checked ? qsTr("plain") : qsTr("html") + focusPolicy: Qt.TabFocus + focus: false + checked: composerController.htmlBody + } + + Row { + visible: html.checked + spacing: 1 + + Kube.IconButton { + iconName: Kube.Icons.bold + checkable: true + checked: textEditor.bold + onClicked: textEditor.bold = !textEditor.bold + focusPolicy: Qt.TabFocus + focus: false + } + Kube.IconButton { + iconName: Kube.Icons.italic + checkable: true + checked: textEditor.italic + onClicked: textEditor.italic = !textEditor.italic + focusPolicy: Qt.TabFocus + focus: false + } + Kube.IconButton { + iconName: Kube.Icons.underline + checkable: true + checked: textEditor.underline + onClicked: textEditor.underline = !textEditor.underline + focusPolicy: Qt.TabFocus + focus: false + } + } + + Item { + height: 1 + Layout.fillWidth: true + } + + Kube.Button { + text: qsTr("Attach file") + + onClicked: { + fileDialogComponent.createObject(parent) + } + + Component { + id: fileDialogComponent + Dialogs.FileDialog { + id: fileDialog + visible: true + title: "Choose a file to attach" + selectFolder: false + onAccepted: { + composerController.attachments.add({url: fileDialog.fileUrl}) + } + } + } + } + } + + Kube.TextEditor { + id: textEditor + + Layout.fillWidth: true + Layout.fillHeight: true + htmlEnabled: html.checked + + onActiveFocusChanged: closeFirstSplitIfNecessary() + Keys.onEscapePressed: recipients.forceActiveFocus() + initialText: composerController.body + onTextChanged: composerController.body = text; + } + } + } + + //Recepients + FocusScope { + id: recipients + anchors { + top: parent.top + bottom: parent.bottom + } + width: Kube.Units.gridUnit * 15 + activeFocusOnTab: true + + //background + Rectangle { + anchors.fill: parent + color: Kube.Colors.backgroundColor + + Rectangle { + height: parent.height + width: 1 + color: Kube.Colors.buttonColor + } + } + + //Content + ColumnLayout { + anchors { + fill: parent + margins: Kube.Units.largeSpacing + } + + spacing: Kube.Units.largeSpacing + ColumnLayout { + Layout.maximumWidth: parent.width + Layout.fillWidth: true + Layout.fillHeight: true + + Kube.Label { + text: qsTr("Sending Email to:") + } + + AddresseeListEditor { + Layout.preferredHeight: implicitHeight + Layout.fillWidth: true + focus: true + activeFocusOnTab: true + encrypt: composerController.encrypt + controller: composerController.to + completer: composerController.recipientCompleter + } + + Kube.Label { + text: qsTr("Sending Copy to (CC):") + } + AddresseeListEditor { + id: cc + Layout.preferredHeight: cc.implicitHeight + Layout.fillWidth: true + activeFocusOnTab: true + encrypt: composerController.encrypt + controller: composerController.cc + completer: composerController.recipientCompleter + } + + Kube.Label { + text: qsTr("Sending Secret Copy to (Bcc):") + } + AddresseeListEditor { + id: bcc + Layout.preferredHeight: bcc.implicitHeight + Layout.fillWidth: true + activeFocusOnTab: true + encrypt: composerController.encrypt + controller: composerController.bcc + completer: composerController.recipientCompleter + } + Item { + width: parent.width + Layout.fillHeight: true + } + } + + RowLayout { + enabled: composerController.foundPersonalKeys + Kube.CheckBox { + id: encryptCheckbox + checked: composerController.encrypt + } + Kube.Label { + text: qsTr("encrypt") + } + } + + RowLayout { + enabled: composerController.foundPersonalKeys + Kube.CheckBox { + id: signCheckbox + checked: composerController.sign + } + Kube.Label { + text: qsTr("sign") + } + } + Kube.Label { + visible: !composerController.foundPersonalKeys + Layout.maximumWidth: parent.width + text: qsTr("Encryption is not available because your personal key has not been found.") + wrapMode: Text.Wrap + } + + RowLayout { + Layout.maximumWidth: parent.width + width: parent.width + height: Kube.Units.gridUnit + + Kube.Button { + width: saveDraftButton.width + text: qsTr("Discard") + onClicked: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + } + + Kube.Button { + id: saveDraftButton + + text: qsTr("Save as Draft") + enabled: composerController.saveAsDraftAction.enabled + onClicked: { + composerController.saveAsDraftAction.execute() + } + } + } + + ColumnLayout { + Layout.maximumWidth: parent.width + Layout.fillWidth: true + Kube.Label { + id: fromLabel + text: qsTr("You are sending this from:") + } + + Kube.ComboBox { + id: identityCombo + + width: parent.width - Kube.Units.largeSpacing * 2 + + model: composerController.identitySelector.model + textRole: "address" + Layout.fillWidth: true + onCurrentIndexChanged: { + composerController.identitySelector.currentIndex = currentIndex + } + } + } + + Kube.PositiveButton { + id: sendButton + + width: parent.width + + text: qsTr("Send") + enabled: composerController.sendAction.enabled + onClicked: { + composerController.sendAction.execute() + } + } + } + }//FocusScope +} diff --git a/components/kube/qml/Kube.qml b/components/kube/qml/Kube.qml new file mode 100644 index 00000000..79b18a2f --- /dev/null +++ b/components/kube/qml/Kube.qml @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.0 + +import QtQuick.Controls 2.0 as Controls2 +import org.kube.framework 1.0 as Kube + +Controls2.ApplicationWindow { + id: app + + property int sidebarWidth: Kube.Units.gridUnit + Kube.Units.largeSpacing + + height: Screen.desktopAvailableHeight * 0.8 + width: Screen.desktopAvailableWidth * 0.8 + visible: true + + //Application default font + font.family: Kube.Font.fontFamily + + //Application context + property variant currentFolder + onCurrentFolderChanged: { + if (!!currentFolder) { + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": currentFolder}) + } + } + property variant currentAccount + onCurrentAccountChanged: { + if (!!currentAccount) { + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": currentAccount}) + } + } + + //accountId -> requiresKeyring + Kube.AccountFactory { + id: accountFactory + accountId: !!app.currentAccount ? app.currentAccount : "" + } + + //Interval sync + Timer { + id: intervalSync + //5min + interval: 300000 + running: !!app.currentFolder + repeat: true + onTriggered: Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) + } + + Kube.StartupCheck { + id: startupCheck + } + + //Listener + Kube.Listener { + filter: Kube.Messages.accountSelection + onMessageReceived: app.currentAccount = message.account + } + + Kube.Listener { + filter: Kube.Messages.folderSelection + onMessageReceived: app.currentFolder = message.folder + } + + Kube.Listener { + filter: Kube.Messages.notification + onMessageReceived: { + notificationPopup.notify(message.message); + } + } + + Kube.Listener { + filter: Kube.Messages.reply + onMessageReceived: { + kubeViews.openComposerWithMail(message.mail, false) + } + } + + Kube.Listener { + filter: Kube.Messages.edit + onMessageReceived: { + kubeViews.openComposerWithMail(message.mail, true) + } + } + + Kube.Listener { + filter: Kube.Messages.compose + onMessageReceived: kubeViews.openComposer(true, message.recipients) + } + + Kube.Listener { + filter: Kube.Messages.requestLogin + onMessageReceived: kubeViews.setLoginView() + } + + Kube.Listener { + filter: Kube.Messages.requestAccountsConfiguration + onMessageReceived: kubeViews.setAccountsView() + } + + //BEGIN Shortcuts + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + Shortcut { + onActivated: Kube.Fabric.postMessage(Kube.Messages.search, {}) + sequence: StandardKey.Find + } + Shortcut { + onActivated: { + Kube.Fabric.postMessage(Kube.Messages.unlockKeyring, {accountId: app.currentAccount}) + } + sequence: "Ctrl+l" + } + Shortcut { + id: syncShortcut + sequence: StandardKey.Refresh + onActivated: !!app.currentFolder ? Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": app.currentFolder}) : Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": app.currentAccount}) + } + //END Shortcuts + + //BEGIN background + Rectangle { + anchors.fill: parent + + color: Kube.Colors.backgroundColor + } + //END background + + //BEGIN Main content + RowLayout { + id: mainContent + spacing: 0 + anchors.fill: parent + + Rectangle { + id: sideBar + + anchors { + top: mainContent.top + bottom: mainContent.bottom + } + width: app.sidebarWidth + color: Kube.Colors.textColor + + Rectangle { + anchors.right: parent.right + width: 1 + height: parent.height + color: Kube.Colors.viewBackgroundColor + opacity: 0.3 + } + + Controls2.ButtonGroup { id: viewButtonGroup } + + Column { + anchors { + top: parent.top + topMargin: Kube.Units.smallSpacing + horizontalCenter: parent.horizontalCenter + } + + spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing + + Kube.IconButton { + id: composerButton + iconName: Kube.Icons.edit_inverted + onClicked: kubeViews.openComposer(false, []) + activeFocusOnTab: true + checkable: true + Controls2.ButtonGroup.group: viewButtonGroup + tooltip: qsTr("composer") + } + + Kube.IconButton { + id: mailButton + iconName: Kube.Icons.mail_inverted + onClicked: kubeViews.setMailView() + activeFocusOnTab: true + checkable: true + checked: true + Controls2.ButtonGroup.group: viewButtonGroup + tooltip: qsTr("mails") + } + + Kube.IconButton { + id: peopleButton + iconName: Kube.Icons.user_inverted + onClicked: kubeViews.setPeopleView() + activeFocusOnTab: true + checkable: true + Controls2.ButtonGroup.group: viewButtonGroup + tooltip: qsTr("people") + } + } + Column { + anchors { + bottom: parent.bottom + bottomMargin: Kube.Units.smallSpacing + horizontalCenter: parent.horizontalCenter + } + + spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing + Kube.Outbox { + height: Kube.Units.gridUnit * 1.5 + width: height + + Kube.ToolTip { + text: qsTr("outbox") + visible: parent.hovered + } + } + + Kube.IconButton { + id: logButton + iconName: Kube.Icons.info_inverted + onClicked: kubeViews.setLogView() + activeFocusOnTab: true + checkable: true + alert: logView.pendingError + Controls2.ButtonGroup.group: viewButtonGroup + tooltip: qsTr("logview") + } + + Kube.IconButton { + id: accountsButton + iconName: Kube.Icons.menu_inverted + onClicked: kubeViews.setAccountsView() + activeFocusOnTab: true + checkable: true + Controls2.ButtonGroup.group: viewButtonGroup + tooltip: qsTr("settings") + } + } + } + Controls2.StackView { + id: kubeViews + + anchors { + top: mainContent.top + bottom: mainContent.bottom + } + Layout.fillWidth: true + + function loginIfNecessary() + { + if (!!app.currentAccount && !Kube.Keyring.isUnlocked(app.currentAccount)) { + if (accountFactory.requiresKeyring) { + setLoginView() + } else { + Kube.Keyring.unlock(app.currentAccount) + } + } + } + + Kube.Listener { + filter: Kube.Messages.componentDone + onMessageReceived: { + //Return to the mailview if we try to pop everything off + if (kubeViews.depth == 1) { + kubeViews.setMailView() + } else { + kubeViews.pop(Controls2.StackView.Immediate) + } + kubeViews.loginIfNecessary() + } + } + + onCurrentItemChanged: { + if (currentItem) { + currentItem.forceActiveFocus() + } + } + + Component.onCompleted: { + //Setup the initial item stack + if (!currentItem) { + setMailView() + if (startupCheck.noAccount) { + setAccountsView() + } else { + loginIfNecessary() + } + } + } + + ///Replace the current view (we can't go back to the old view, and we destroy the old view) + function replaceView(view) { + if (currentItem != view) { + kubeViews.replace(null, view, {}, Controls2.StackView.Immediate) + } + } + + ///Push a new view on the stack (the old view remains, and we can go back once done) + function pushView(view, properties) { + kubeViews.push(view, properties, Controls2.StackView.Immediate) + } + + //TODO replacing here while a composer is open is destructive + function setPeopleView() { + replaceView(peopleView) + } + + function setMailView() { + replaceView(mailView) + } + + function setAccountsView() { + pushView(accountsView, {}) + } + + function setLogView() { + replaceView(logView) + } + + function setLoginView() { + if (currentItem != loginView) { + pushView(loginView, {accountId: currentAccount}) + } + } + + function openComposer(newMessage, recipients) { + pushView(composerView, {newMessage: newMessage, recipients: recipients}) + } + + function openComposerWithMail(mail, openAsDraft) { + pushView(composerView, {message: mail, loadAsDraft: openAsDraft}) + } + + + //These items are not visible until pushed onto the stack, so we keep them in resources instead of items + resources: [ + //Not components so we maintain state + MailView { + id: mailView + anchors.fill: parent + Controls2.StackView.onActivated: mailButton.checked = true + Controls2.StackView.onDeactivated: mailButton.checked = false + }, + PeopleView { + id: peopleView + anchors.fill: parent + Controls2.StackView.onActivated: peopleButton.checked = true + Controls2.StackView.onDeactivated: peopleButton.checked = false + }, + //Not a component because otherwise we can't log stuff + LogView { + id: logView + anchors.fill: parent + Controls2.StackView.onActivated: logButton.checked = true + Controls2.StackView.onDeactivated: logButton.checked = false + } + ] + //A component so it's always destroyed when we're done + Component { + id: composerView + ComposerView { + anchors.fill: parent + Controls2.StackView.onActivated: composerButton.checked = true + Controls2.StackView.onDeactivated: composerButton.checked = false + } + } + Component { + id: accountsView + AccountsView { + anchors.fill: parent + Controls2.StackView.onActivated: accountsButton.checked = true + Controls2.StackView.onDeactivated: accountsButton.checked = false + } + } + Component { + id: loginView + LoginView { + anchors.fill: parent + } + } + } + } + //END Main content + + //BEGIN Notification + Kube.NotificationPopup { + id: notificationPopup + + anchors { + left: parent.left + leftMargin: app.sidebarWidth - 3 // so it does not align with the border + bottom: parent.bottom + bottomMargin: Kube.Units.gridUnit * 4 + } + } + //END Notification +} diff --git a/components/kube/qml/LogView.qml b/components/kube/qml/LogView.qml new file mode 100644 index 00000000..3b0aa2d5 --- /dev/null +++ b/components/kube/qml/LogView.qml @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.3 as Controls +import QtQuick.Controls 2.0 as Controls2 +import org.kube.framework 1.0 as Kube + +Controls.SplitView { + id: root + + property bool pendingError: false; + + Controls2.StackView.onActivated: { + root.pendingError = false; + //Always select the latest notification + listView.currentIndex = 0 + } + + Item { + id: accountList + width: parent.width/3 + Layout.fillHeight: true + + Kube.Listener { + filter: Kube.Messages.notification + onMessageReceived: { + if (message.type == Kube.Notifications.error) { + root.pendingError = true + } + var error = {timestamp: new Date(), message: message.message, details: message.details, resource: message.resource} + if (logModel.count > 0) { + var lastEntry = logModel.get(0) + //Merge if we get an entry of the same subtype + if (lastEntry.subtype && lastEntry.subtype == message.subtype) { + logModel.set(0, {type: message.type, subtype: message.subtype, errors: [error].concat(lastEntry.errors)}) + return + } + } + logModel.insert(0, {type: message.type, subtype: message.subtype, errors: [error]}) + } + } + + Kube.Label { + anchors.centerIn: parent + visible: listView.count == 0 + text: qsTr("Nothing here...") + } + + Kube.ListView { + id: listView + anchors { + fill: parent + } + + clip: true + + model: ListModel { + id: logModel + objectName: "logModel" + } + + onCurrentItemChanged: { + var error = currentItem.currentData.errors.get(0) + if (!!error.resource) { + details.resourceId = error.resource + } + details.message = error.message + "\n" + error.details + details.timestamp = error.timestamp + if (!!currentItem.currentData.subtype) { + details.subtype = currentItem.currentData.subtype + } else { + details.subtype = "" + } + } + + delegate: Kube.ListDelegate { + border.color: Kube.Colors.buttonColor + border.width: 1 + Kube.Label { + id: description + anchors { + top: parent.top + topMargin: Kube.Units.smallSpacing + left: parent.left + leftMargin: Kube.Units.largeSpacing + } + height: Kube.Units.gridUnit + width: parent.width - Kube.Units.largeSpacing * 2 + text: model.type == Kube.Notifications.error ? qsTr("Error") : qsTr("Info") + } + + Kube.Label { + id: message + anchors { + topMargin: Kube.Units.smallSpacing + top: description.bottom + left: parent.left + leftMargin: Kube.Units.largeSpacing + } + height: Kube.Units.gridUnit + width: parent.width - Kube.Units.largeSpacing * 2 + maximumLineCount: 1 + elide: Text.ElideRight + color: Kube.Colors.disabledTextColor + text: model.errors.get(0).message + } + + Kube.Label { + id: date + + anchors { + right: parent.right + bottom: parent.bottom + rightMargin: Kube.Units.smallSpacing + } + text: Qt.formatDateTime(model.errors.get(0).timestamp, " hh:mm:ss dd MMM yyyy") + font.italic: true + color: Kube.Colors.disabledTextColor + font.pointSize: Kube.Units.smallFontSize + } + } + } + } + Item { + id: details + property string subtype: "" + property date timestamp + property string message: "" + property string resourceId: "" + + Kube.ModelIndexRetriever { + id: retriever + model: Kube.AccountsModel { + resourceId: details.resourceId + } + } + + Loader { + id: detailsLoader + visible: message != "" + clip: true + anchors { + fill: parent + margins: Kube.Units.largeSpacing + } + property date timestamp: details.timestamp + property string message: details.message + property string resourceId: details.resourceId + property string accountId: retriever.currentData ? retriever.currentData.accountId : "" + property string accountName: retriever.currentData ? retriever.currentData.name : "" + + function getComponent(subtype) { + if (subtype == Kube.Notifications.loginError) { + return loginErrorComponent + } + if (subtype == Kube.Notifications.hostNotFoundError) { + return hostNotFoundErrorComponent + } + if (subtype == Kube.Notifications.connectionError) { + return hostNotFoundErrorComponent + } + return detailsComponent + } + + sourceComponent: getComponent(details.subtype) + } + } + + Component { + id: detailsComponent + Rectangle { + color: Kube.Colors.viewBackgroundColor + GridLayout { + id: gridLayout + Layout.minimumWidth: 0 + anchors { + top: parent.top + left: parent.left + right: parent.right + } + columns: 2 + Kube.Label { + text: qsTr("Account:") + visible: accountName + } + Kube.Label { + Layout.fillWidth: true + text: accountName + visible: accountName + elide: Text.ElideRight + } + Kube.Label { + text: qsTr("Account Id:") + visible: accountId + } + Kube.Label { + text: accountId + visible: accountId + Layout.fillWidth: true + elide: Text.ElideRight + } + Kube.Label { + text: qsTr("Resource Id:") + visible: resourceId + } + Kube.Label { + text: resourceId + visible: resourceId + Layout.fillWidth: true + elide: Text.ElideRight + } + Kube.Label { + text: qsTr("Timestamp:") + } + Kube.Label { + text: Qt.formatDateTime(timestamp, " hh:mm:ss dd MMM yyyy") + Layout.fillWidth: true + elide: Text.ElideRight + } + Kube.Label { + text: qsTr("Message:") + Layout.alignment: Qt.AlignTop + } + Kube.Label { + text: message + Layout.fillWidth: true + wrapMode: Text.Wrap + } + Item { + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + Kube.SelectableItem { + layout: gridLayout + } + } + } + + Component { + id: loginErrorComponent + Item { + Column { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + spacing: Kube.Units.largeSpacing + Column { + Kube.Heading { + id: heading + text: qsTr("Failed to login") + color: Kube.Colors.warningColor + } + + Kube.Label { + id: subHeadline + text: accountName + ": " + qsTr("Please check your credentials.") + color: Kube.Colors.disabledTextColor + wrapMode: Text.Wrap + } + } + Kube.Button { + text: qsTr("Change Password") + onClicked: { + Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + Kube.Fabric.postMessage(Kube.Messages.requestLogin, {}) + } + } + } + } + } + + Component { + id: hostNotFoundErrorComponent + Item { + Column { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + spacing: Kube.Units.largeSpacing + Column { + Kube.Heading { + id: heading + text: qsTr("Host not found") + color: Kube.Colors.warningColor + } + + Kube.Label { + id: subHeadline + text: accountName + ": " + qsTr("Please check your network connection and settings.") + color: Kube.Colors.disabledTextColor + wrapMode: Text.Wrap + } + } + Kube.Button { + text: qsTr("Account settings") + onClicked: { + Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + Kube.Fabric.postMessage(Kube.Messages.requestAccountsConfiguration, {}) + } + } + } + } + } +} diff --git a/components/kube/qml/LoginView.qml b/components/kube/qml/LoginView.qml new file mode 100644 index 00000000..dbbed11c --- /dev/null +++ b/components/kube/qml/LoginView.qml @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import org.kube.framework 1.0 as Kube + +FocusScope { + id: root + property alias accountId: login.accountId + onActiveFocusChanged: { + if (activeFocus) { + popup.forceActiveFocus() + } + } + + Kube.Popup { + id: popup + visible: true + parent: ApplicationWindow.overlay + height: app.height + width: app.width - app.sidebarWidth + x: app.sidebarWidth + y: 0 + modal: true + closePolicy: Popup.NoAutoClose + Kube.LoginAccount { + id: login + anchors { + fill: parent + bottomMargin: Kube.Units.largeSpacing + } + } + } +} diff --git a/components/kube/qml/MailView.qml b/components/kube/qml/MailView.qml new file mode 100644 index 00000000..8b2b0caf --- /dev/null +++ b/components/kube/qml/MailView.qml @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +import QtQuick 2.7 +import QtQuick.Controls 1.3 +import QtQuick.Controls 2.0 as Controls2 +import QtQuick.Layouts 1.1 + +import org.kube.framework 1.0 as Kube + +FocusScope { + SplitView { + anchors.fill: parent + Rectangle { + width: Kube.Units.gridUnit * 10 + Layout.fillHeight: parent.height + color: Kube.Colors.textColor + + Kube.PositiveButton { + id: newMailButton + + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: Kube.Units.largeSpacing + } + focus: true + text: qsTr("New Email") + onClicked: Kube.Fabric.postMessage(Kube.Messages.compose, {}) + } + + Kube.InlineAccountSwitcher { + id: accountFolderview + activeFocusOnTab: true + anchors { + top: newMailButton.bottom + topMargin: Kube.Units.largeSpacing + bottom: statusBarContainer.top + left: newMailButton.left + right: parent.right + } + } + + Item { + id: statusBarContainer + anchors { + topMargin: Kube.Units.smallSpacing + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: childrenRect.height + + Rectangle { + id: border + visible: statusBar.visible + anchors { + right: parent.right + left: parent.left + margins: Kube.Units.smallSpacing + } + height: 1 + color: Kube.Colors.viewBackgroundColor + opacity: 0.3 + } + Kube.StatusBar { + id: statusBar + accountId: accountFolderview.currentAccount + height: Kube.Units.gridUnit * 2 + anchors { + top: border.bottom + left: statusBarContainer.left + right: statusBarContainer.right + } + } + } + } + + Rectangle { + width: Kube.Units.gridUnit * 18 + Layout.fillHeight: parent.height + + color: "transparent" + border.width: 1 + border.color: Kube.Colors.buttonColor + + Kube.MailListView { + id: mailListView + anchors.fill: parent + activeFocusOnTab: true + Layout.minimumWidth: Kube.Units.gridUnit * 10 + } + } + + Kube.ConversationView { + id: mailView + Layout.fillWidth: true + Layout.fillHeight: parent.height + activeFocusOnTab: true + } + } +} diff --git a/components/kube/qml/PeopleView.qml b/components/kube/qml/PeopleView.qml new file mode 100644 index 00000000..3f1b9261 --- /dev/null +++ b/components/kube/qml/PeopleView.qml @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import org.kube.framework 1.0 as Kube + +Item { + + StackView.onActivated: { + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "contacts"}) + } + + Kube.People { + id: people + anchors { + fill: parent + margins: Kube.Units.smallSpacing + } + } +} diff --git a/components/kube/qml/main.qml b/components/kube/qml/main.qml new file mode 100644 index 00000000..4391ebe5 --- /dev/null +++ b/components/kube/qml/main.qml @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import org.kube.components.kube 1.0 as KubeComponent + +KubeComponent.Kube { + +} diff --git a/components/mailviewer/contents/ui/ErrorPart.qml b/components/mailviewer/contents/ui/ErrorPart.qml deleted file mode 100644 index 0e5d9b8e..00000000 --- a/components/mailviewer/contents/ui/ErrorPart.qml +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - Copyright (C) 2017 Christian Mollekopf, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.4 - -Item { - id: root - property variant errorType - property string errorString - height: partListView.height - width: parent.width - - Column { - id: partListView - anchors { - top: parent.top - left: parent.left - } - width: parent.width - spacing: 5 - Text { - text: qsTr("An error occured: %1").arg(errorString) - } - } -} diff --git a/components/mailviewer/contents/ui/HtmlContent.qml b/components/mailviewer/contents/ui/HtmlContent.qml deleted file mode 100644 index 3ac1bb38..00000000 --- a/components/mailviewer/contents/ui/HtmlContent.qml +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtWebEngine 1.4 - -import org.kube.framework 1.0 as Kube - -Item { - id: root - property string content - //We have to give it a minimum size so the html content starts to expand - property int contentHeight: 10; - - height: contentHeight - width: partColumn.width - - WebEngineView { - id: htmlView - anchors.fill: parent - Component.onCompleted: loadHtml(content, "file:///") - onContentsSizeChanged: { - //Resizing pixel by pixel causes some mails to grow indefinitely - if (contentsSize.height >= root.contentHeight + 5) { - root.contentHeight = contentsSize.height - } - } - onLoadingChanged: { - if (loadRequest.status == WebEngineLoadRequest.LoadFailedStatus) { - console.warn("Failed to load html content.") - console.warn("Error is ", loadRequest.errorString) - } - } - settings { - webGLEnabled: false - touchIconsEnabled: false - spatialNavigationEnabled: false - screenCaptureEnabled: false - pluginsEnabled: false - localStorageEnabled: false - localContentCanAccessRemoteUrls: false - localContentCanAccessFileUrls: false - linksIncludedInFocusChain: false - javascriptEnabled: false - javascriptCanOpenWindows: false - javascriptCanAccessClipboard: false - hyperlinkAuditingEnabled: false - fullScreenSupportEnabled: false - errorPageEnabled: false - //defaultTextEncoding: ??? - autoLoadImages: true - autoLoadIconsForPage: false - accelerated2dCanvasEnabled: false - //The webview should not steal focus - focusOnNavigationEnabled: false - } - profile: Kube.WebEngineProfile - onContextMenuRequested: function(request) { - request.accepted = true - } - } - onContentChanged: { - htmlView.loadHtml(content, "file:///"); - } -} diff --git a/components/mailviewer/contents/ui/MailDataModel.qml b/components/mailviewer/contents/ui/MailDataModel.qml deleted file mode 100644 index ec7f8e74..00000000 --- a/components/mailviewer/contents/ui/MailDataModel.qml +++ /dev/null @@ -1,159 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.4 -import QtQml.Models 2.2 -import org.kube.framework 1.0 as Kube - -DelegateModel { - id: root - - delegate: Item { - id: partColumn - - width: parent.width - height: childrenRect.height - - function getColor(securityLevel) - { - if (securityLevel == "good") { - return Kube.Colors.positiveColor - } - if (securityLevel == "bad") { - return Kube.Colors.negativeColor - } - if (securityLevel == "notsogood") { - return Kube.Colors.warningColor - } - return Kube.Colors.lightgrey - } - - function getDetails(signatureDetails) - { - var details = ""; - if (signatureDetails.noSignaturesFound) { - details += qsTr("This message has been signed but we failed to validate the signature.") + "\n" - } else if (!signatureDetails.signatureIsGood) { - details += qsTr("This message is signed but the signature is invalid.") + "\n" - } else if (signatureDetails.keyMissing) { - details += qsTr("This message has been signed using the key %1.").arg(signatureDetails.keyId) + "\n"; - details += qsTr("The key details are not available.") + "\n"; - return details; - } else { - details += qsTr("This message has been signed using the key %1 by %2.").arg(signatureDetails.keyId).arg(signatureDetails.signer) + "\n"; - if (signatureDetails.keyRevoked) { - details += qsTr("The key was revoked.") + "\n" - } - if (signatureDetails.keyExpired) { - details += qsTr("The key has expired.") + "\n" - } - if (signatureDetails.keyIsTrusted) { - details += qsTr("You are trusting this key.") + "\n" - } - } - return details - } - - Column { - id: buttons - anchors.left: parent.left - anchors.top: parent.top - anchors.rightMargin: Kube.Units.smallSpacing - spacing: Kube.Units.smallSpacing - Kube.IconButton { - id: encryptedButton - width: Kube.Units.gridUnit - height: width - iconName: Kube.Icons.secure - color: getColor(model.securityLevel) - backgroundOpacity: 0.5 - visible: model.encrypted - tooltip: qsTr("This message is encrypted."); - //FIXME make text copyable - // Kube.SelectableItem { - // visualParent: encryptedButton - // text: parent.tooltip - // } - } - Kube.IconButton { - id: signedButton - width: Kube.Units.gridUnit - height: width - iconName: Kube.Icons.signed - color: getColor(model.securityLevel) - backgroundOpacity: 0.5 - visible: model.signed - tooltip: getDetails(model.signatureDetails) - } - } - Rectangle { - id: border - visible: encryptedButton.hovered || signedButton.hovered - anchors.topMargin: Kube.Units.smallSpacing - anchors.top: buttons.bottom - anchors.bottom: partLoader.bottom - anchors.right: buttons.right - width: Kube.Units.smallSpacing - color: getColor(model.securityLevel) - opacity: 0.5 - } - - Loader { - id: partLoader - anchors { - top: parent.top - left: buttons.right - leftMargin: Kube.Units.smallSpacing - right: parent.right - } - height: item ? item.contentHeight : 0 - width: parent.width - } - Component.onCompleted: { - switch (model.type) { - case "plain": - partLoader.setSource("TextContent.qml", - {"content": model.content, - "embedded": model.embedded, - "type": model.type - }) - break - case "html": - partLoader.setSource("HtmlContent.qml", - {"content": model.content, - }) - break; - case "error": - partLoader.setSource("ErrorPart.qml", - { - "errorType": model.errorType, - "errorString": model.errorString, - }) - break; - case "encapsulated": - partLoader.setSource("MailPart.qml", - {"rootIndex": root.modelIndex(index), - "model": root.model, - "sender": model.sender, - "date": model.date - }) - break; - } - } - } -} diff --git a/components/mailviewer/contents/ui/MailPart.qml b/components/mailviewer/contents/ui/MailPart.qml deleted file mode 100644 index e570ebaa..00000000 --- a/components/mailviewer/contents/ui/MailPart.qml +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.4 - -import org.kube.framework 1.0 as Kube - -Item { - id: root - property alias rootIndex: visualModel.rootIndex - property alias model: visualModel.model - property variant sender - property variant date - height: childrenRect.height - - Rectangle { - id: border - anchors { - top: parent.top - left: parent.left - leftMargin: Kube.Units.smallSpacing - } - color: Kube.Colors.lightgrey - height: partListView.height + sender.height - width: Kube.Units.smallSpacing - } - - Text { - id: sender - anchors { - top: parent.top - left: border.right - leftMargin: Kube.Units.smallSpacing - } - - text: qsTr("sent by %1 on %2").arg(root.sender).arg(root.date) - color: "grey" - } - ListView { - id: partListView - anchors { - top: sender.bottom - left: border.right - margins: Kube.Units.smallSpacing - leftMargin: Kube.Units.smallSpacing - } - model: MailDataModel { - id: visualModel - } - spacing: 7 - height: contentHeight - width: parent.width - Kube.Units.smallSpacing * 3 - interactive: false - } -} diff --git a/components/mailviewer/contents/ui/MailViewer.qml b/components/mailviewer/contents/ui/MailViewer.qml deleted file mode 100644 index 9031ec17..00000000 --- a/components/mailviewer/contents/ui/MailViewer.qml +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.4 - -Item { - id: root - property alias rootIndex: visualModel.rootIndex - property alias model: visualModel.model - height: partListView.height - - MailDataModel { - id: visualModel - } - - ListView { - id: partListView - model: visualModel - anchors { - top: parent.top - left: parent.left - margins: 5 - } - spacing: 5 - height: contentHeight - width: parent.width - 10 - interactive: false - } -} diff --git a/components/mailviewer/contents/ui/TextContent.qml b/components/mailviewer/contents/ui/TextContent.qml deleted file mode 100644 index 5d8dbc90..00000000 --- a/components/mailviewer/contents/ui/TextContent.qml +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2016 Michael Bohlender, - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.7 - -import org.kube.framework 1.0 as Kube - -Item { - id: root - - property string content - property bool embedded: true - property string type - - height: textEdit.height - - TextEdit { - id: textEdit - - anchors { - top: parent.top - left: parent.left - right: parent.right - } - - selectionColor: Kube.Colors.highlightColor - - readOnly: true - selectByMouse: true - - text: content - wrapMode: TextEdit.Wrap - textFormat: Text.RichText - - font.family: Kube.Font.fontFamily - color: embedded ? Kube.Colors.disabledTextColor : Kube.Colors.textColor - onLinkActivated: Qt.openUrlExternally(link) - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text - cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor - } - } -} diff --git a/components/mailviewer/contents/ui/main.qml b/components/mailviewer/contents/ui/main.qml deleted file mode 100644 index e69dd5a2..00000000 --- a/components/mailviewer/contents/ui/main.qml +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2017 Christian Mollekopf, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -import QtQuick 2.7 -import QtQuick.Controls 2.0 as Controls2 - -Controls2.ApplicationWindow { - id: app - height: 900 - width: 1500 - - MailViewer { - visible: true - } -} diff --git a/components/mailviewer/metadata.desktop b/components/mailviewer/metadata.desktop deleted file mode 100644 index c573ff89..00000000 --- a/components/mailviewer/metadata.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Name=Kube Mailviewer -X-KDE-PluginInfo-Name=org.kube.components.mailviewer -Exec=kpackagelauncherqml -a org.kube.components.mailviewer -X-Plasma-MainScript=ui/main.qml -X-KDE-ServiceTypes=KPackage/GenericQML -Icon=kmail2 -Type=Service diff --git a/components/mailviewer/qml/ErrorPart.qml b/components/mailviewer/qml/ErrorPart.qml new file mode 100644 index 00000000..0e5d9b8e --- /dev/null +++ b/components/mailviewer/qml/ErrorPart.qml @@ -0,0 +1,41 @@ +/* + Copyright (C) 2016 Michael Bohlender, + Copyright (C) 2017 Christian Mollekopf, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 + +Item { + id: root + property variant errorType + property string errorString + height: partListView.height + width: parent.width + + Column { + id: partListView + anchors { + top: parent.top + left: parent.left + } + width: parent.width + spacing: 5 + Text { + text: qsTr("An error occured: %1").arg(errorString) + } + } +} diff --git a/components/mailviewer/qml/HtmlContent.qml b/components/mailviewer/qml/HtmlContent.qml new file mode 100644 index 00000000..3ac1bb38 --- /dev/null +++ b/components/mailviewer/qml/HtmlContent.qml @@ -0,0 +1,81 @@ +/* + Copyright (C) 2016 Michael Bohlender, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtWebEngine 1.4 + +import org.kube.framework 1.0 as Kube + +Item { + id: root + property string content + //We have to give it a minimum size so the html content starts to expand + property int contentHeight: 10; + + height: contentHeight + width: partColumn.width + + WebEngineView { + id: htmlView + anchors.fill: parent + Component.onCompleted: loadHtml(content, "file:///") + onContentsSizeChanged: { + //Resizing pixel by pixel causes some mails to grow indefinitely + if (contentsSize.height >= root.contentHeight + 5) { + root.contentHeight = contentsSize.height + } + } + onLoadingChanged: { + if (loadRequest.status == WebEngineLoadRequest.LoadFailedStatus) { + console.warn("Failed to load html content.") + console.warn("Error is ", loadRequest.errorString) + } + } + settings { + webGLEnabled: false + touchIconsEnabled: false + spatialNavigationEnabled: false + screenCaptureEnabled: false + pluginsEnabled: false + localStorageEnabled: false + localContentCanAccessRemoteUrls: false + localContentCanAccessFileUrls: false + linksIncludedInFocusChain: false + javascriptEnabled: false + javascriptCanOpenWindows: false + javascriptCanAccessClipboard: false + hyperlinkAuditingEnabled: false + fullScreenSupportEnabled: false + errorPageEnabled: false + //defaultTextEncoding: ??? + autoLoadImages: true + autoLoadIconsForPage: false + accelerated2dCanvasEnabled: false + //The webview should not steal focus + focusOnNavigationEnabled: false + } + profile: Kube.WebEngineProfile + onContextMenuRequested: function(request) { + request.accepted = true + } + } + onContentChanged: { + htmlView.loadHtml(content, "file:///"); + } +} diff --git a/components/mailviewer/qml/MailDataModel.qml b/components/mailviewer/qml/MailDataModel.qml new file mode 100644 index 00000000..ec7f8e74 --- /dev/null +++ b/components/mailviewer/qml/MailDataModel.qml @@ -0,0 +1,159 @@ +/* + Copyright (C) 2016 Michael Bohlender, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 +import QtQml.Models 2.2 +import org.kube.framework 1.0 as Kube + +DelegateModel { + id: root + + delegate: Item { + id: partColumn + + width: parent.width + height: childrenRect.height + + function getColor(securityLevel) + { + if (securityLevel == "good") { + return Kube.Colors.positiveColor + } + if (securityLevel == "bad") { + return Kube.Colors.negativeColor + } + if (securityLevel == "notsogood") { + return Kube.Colors.warningColor + } + return Kube.Colors.lightgrey + } + + function getDetails(signatureDetails) + { + var details = ""; + if (signatureDetails.noSignaturesFound) { + details += qsTr("This message has been signed but we failed to validate the signature.") + "\n" + } else if (!signatureDetails.signatureIsGood) { + details += qsTr("This message is signed but the signature is invalid.") + "\n" + } else if (signatureDetails.keyMissing) { + details += qsTr("This message has been signed using the key %1.").arg(signatureDetails.keyId) + "\n"; + details += qsTr("The key details are not available.") + "\n"; + return details; + } else { + details += qsTr("This message has been signed using the key %1 by %2.").arg(signatureDetails.keyId).arg(signatureDetails.signer) + "\n"; + if (signatureDetails.keyRevoked) { + details += qsTr("The key was revoked.") + "\n" + } + if (signatureDetails.keyExpired) { + details += qsTr("The key has expired.") + "\n" + } + if (signatureDetails.keyIsTrusted) { + details += qsTr("You are trusting this key.") + "\n" + } + } + return details + } + + Column { + id: buttons + anchors.left: parent.left + anchors.top: parent.top + anchors.rightMargin: Kube.Units.smallSpacing + spacing: Kube.Units.smallSpacing + Kube.IconButton { + id: encryptedButton + width: Kube.Units.gridUnit + height: width + iconName: Kube.Icons.secure + color: getColor(model.securityLevel) + backgroundOpacity: 0.5 + visible: model.encrypted + tooltip: qsTr("This message is encrypted."); + //FIXME make text copyable + // Kube.SelectableItem { + // visualParent: encryptedButton + // text: parent.tooltip + // } + } + Kube.IconButton { + id: signedButton + width: Kube.Units.gridUnit + height: width + iconName: Kube.Icons.signed + color: getColor(model.securityLevel) + backgroundOpacity: 0.5 + visible: model.signed + tooltip: getDetails(model.signatureDetails) + } + } + Rectangle { + id: border + visible: encryptedButton.hovered || signedButton.hovered + anchors.topMargin: Kube.Units.smallSpacing + anchors.top: buttons.bottom + anchors.bottom: partLoader.bottom + anchors.right: buttons.right + width: Kube.Units.smallSpacing + color: getColor(model.securityLevel) + opacity: 0.5 + } + + Loader { + id: partLoader + anchors { + top: parent.top + left: buttons.right + leftMargin: Kube.Units.smallSpacing + right: parent.right + } + height: item ? item.contentHeight : 0 + width: parent.width + } + Component.onCompleted: { + switch (model.type) { + case "plain": + partLoader.setSource("TextContent.qml", + {"content": model.content, + "embedded": model.embedded, + "type": model.type + }) + break + case "html": + partLoader.setSource("HtmlContent.qml", + {"content": model.content, + }) + break; + case "error": + partLoader.setSource("ErrorPart.qml", + { + "errorType": model.errorType, + "errorString": model.errorString, + }) + break; + case "encapsulated": + partLoader.setSource("MailPart.qml", + {"rootIndex": root.modelIndex(index), + "model": root.model, + "sender": model.sender, + "date": model.date + }) + break; + } + } + } +} diff --git a/components/mailviewer/qml/MailPart.qml b/components/mailviewer/qml/MailPart.qml new file mode 100644 index 00000000..e570ebaa --- /dev/null +++ b/components/mailviewer/qml/MailPart.qml @@ -0,0 +1,70 @@ +/* + Copyright (C) 2016 Michael Bohlender, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 + +import org.kube.framework 1.0 as Kube + +Item { + id: root + property alias rootIndex: visualModel.rootIndex + property alias model: visualModel.model + property variant sender + property variant date + height: childrenRect.height + + Rectangle { + id: border + anchors { + top: parent.top + left: parent.left + leftMargin: Kube.Units.smallSpacing + } + color: Kube.Colors.lightgrey + height: partListView.height + sender.height + width: Kube.Units.smallSpacing + } + + Text { + id: sender + anchors { + top: parent.top + left: border.right + leftMargin: Kube.Units.smallSpacing + } + + text: qsTr("sent by %1 on %2").arg(root.sender).arg(root.date) + color: "grey" + } + ListView { + id: partListView + anchors { + top: sender.bottom + left: border.right + margins: Kube.Units.smallSpacing + leftMargin: Kube.Units.smallSpacing + } + model: MailDataModel { + id: visualModel + } + spacing: 7 + height: contentHeight + width: parent.width - Kube.Units.smallSpacing * 3 + interactive: false + } +} diff --git a/components/mailviewer/qml/MailViewer.qml b/components/mailviewer/qml/MailViewer.qml new file mode 100644 index 00000000..9031ec17 --- /dev/null +++ b/components/mailviewer/qml/MailViewer.qml @@ -0,0 +1,44 @@ +/* + Copyright (C) 2016 Michael Bohlender, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 + +Item { + id: root + property alias rootIndex: visualModel.rootIndex + property alias model: visualModel.model + height: partListView.height + + MailDataModel { + id: visualModel + } + + ListView { + id: partListView + model: visualModel + anchors { + top: parent.top + left: parent.left + margins: 5 + } + spacing: 5 + height: contentHeight + width: parent.width - 10 + interactive: false + } +} diff --git a/components/mailviewer/qml/TextContent.qml b/components/mailviewer/qml/TextContent.qml new file mode 100644 index 00000000..5d8dbc90 --- /dev/null +++ b/components/mailviewer/qml/TextContent.qml @@ -0,0 +1,60 @@ +/* + Copyright (C) 2016 Michael Bohlender, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.7 + +import org.kube.framework 1.0 as Kube + +Item { + id: root + + property string content + property bool embedded: true + property string type + + height: textEdit.height + + TextEdit { + id: textEdit + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + selectionColor: Kube.Colors.highlightColor + + readOnly: true + selectByMouse: true + + text: content + wrapMode: TextEdit.Wrap + textFormat: Text.RichText + + font.family: Kube.Font.fontFamily + color: embedded ? Kube.Colors.disabledTextColor : Kube.Colors.textColor + onLinkActivated: Qt.openUrlExternally(link) + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } +} diff --git a/components/mailviewer/qml/main.qml b/components/mailviewer/qml/main.qml new file mode 100644 index 00000000..e69dd5a2 --- /dev/null +++ b/components/mailviewer/qml/main.qml @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.0 as Controls2 + +Controls2.ApplicationWindow { + id: app + height: 900 + width: 1500 + + MailViewer { + visible: true + } +} -- cgit v1.2.3