mirror of
https://github.com/monero-project/monero-gui
synced 2025-02-16 12:04:28 +01:00
Merge master -> release-v0.14.1
This commit is contained in:
commit
300a5afc21
19
.gitignore
vendored
19
.gitignore
vendored
@ -13,3 +13,22 @@ monero-wallet-gui_plugin_import.cpp
|
||||
monero-wallet-gui_qml_plugin_import.cpp
|
||||
*.qmlc
|
||||
*.jsc
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "src/qt/TailsOS.h"
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
|
||||
// default log path by OS (should be writable)
|
||||
@ -66,6 +67,9 @@ const QString getLogPath(const QString logPath)
|
||||
{
|
||||
const QFileInfo fi(logPath);
|
||||
|
||||
if(TailsOS::detect() && TailsOS::usePersistence)
|
||||
return QDir::homePath() + "/Persistent/Monero/logs/" + defaultLogName;
|
||||
|
||||
if(!logPath.isEmpty() && !fi.isDir())
|
||||
return fi.absoluteFilePath();
|
||||
else {
|
||||
|
@ -88,6 +88,7 @@ Rectangle {
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: appWindow.walletMode >= 2
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if(!appWindow.isMining) {
|
||||
@ -133,6 +134,7 @@ Rectangle {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: appWindow.walletMode >= 2
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if(!appWindow.isMining) {
|
||||
|
@ -1,287 +0,0 @@
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Window 2.0
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Item {
|
||||
id: root
|
||||
visible: false
|
||||
z: parent.z + 2
|
||||
|
||||
property bool isHidden: true
|
||||
property alias password: passwordInput1.text
|
||||
|
||||
// same signals as Dialog has
|
||||
signal accepted()
|
||||
signal rejected()
|
||||
signal closeCallback()
|
||||
|
||||
function open() {
|
||||
isHidden = true
|
||||
passwordInput1.echoMode = TextInput.Password;
|
||||
passwordInput2.echoMode = TextInput.Password;
|
||||
inactiveOverlay.visible = true
|
||||
leftPanel.enabled = false
|
||||
middlePanel.enabled = false
|
||||
titleBar.state = "essentials"
|
||||
root.visible = true;
|
||||
passwordInput1.text = "";
|
||||
passwordInput2.text = "";
|
||||
passwordInput1.focus = true
|
||||
}
|
||||
|
||||
function close() {
|
||||
inactiveOverlay.visible = false
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
titleBar.state = "default"
|
||||
root.visible = false;
|
||||
closeCallback();
|
||||
}
|
||||
|
||||
function toggleIsHidden() {
|
||||
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
isHidden = !isHidden;
|
||||
}
|
||||
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 480
|
||||
height: 360
|
||||
|
||||
ColumnLayout {
|
||||
z: inactiveOverlay.z + 1
|
||||
id: mainLayout
|
||||
spacing: 10
|
||||
anchors { fill: parent; margins: 35 }
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.maximumWidth: 400
|
||||
|
||||
Label {
|
||||
text: qsTr("Please enter new password") + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
TextField {
|
||||
id : passwordInput1
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
KeyNavigation.tab: passwordInput2
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Please confirm new password") + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
TextField {
|
||||
id : passwordInput2
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: okButton
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (passwordInput1.text === passwordInput2.text) {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "black"
|
||||
}
|
||||
|
||||
// Ok/Cancel buttons
|
||||
RowLayout {
|
||||
id: buttons
|
||||
spacing: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: cancelButton
|
||||
text: qsTr("Cancel") + translationManager.emptyString
|
||||
KeyNavigation.tab: passwordInput1
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
MoneroComponents.StandardButton {
|
||||
id: okButton
|
||||
text: qsTr("Continue") + translationManager.emptyString
|
||||
KeyNavigation.tab: cancelButton
|
||||
enabled: passwordInput1.text === passwordInput2.text
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Window 2.0
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Item {
|
||||
id: root
|
||||
visible: false
|
||||
z: parent.z + 2
|
||||
|
||||
property bool isHidden: true
|
||||
property alias passphrase: passphaseInput1.text
|
||||
property string walletName
|
||||
property string errorText
|
||||
|
||||
// same signals as Dialog has
|
||||
signal accepted()
|
||||
signal rejected()
|
||||
signal closeCallback()
|
||||
|
||||
function open(walletName, errorText) {
|
||||
isHidden = true
|
||||
passphaseInput1.echoMode = TextInput.Password;
|
||||
passphaseInput2.echoMode = TextInput.Password;
|
||||
|
||||
inactiveOverlay.visible = true
|
||||
|
||||
root.walletName = walletName ? walletName : ""
|
||||
root.errorText = errorText ? errorText : "";
|
||||
|
||||
leftPanel.enabled = false
|
||||
middlePanel.enabled = false
|
||||
titleBar.state = "essentials"
|
||||
|
||||
root.visible = true;
|
||||
passphaseInput1.text = "";
|
||||
passphaseInput2.text = "";
|
||||
passphaseInput1.focus = true
|
||||
}
|
||||
|
||||
function close() {
|
||||
inactiveOverlay.visible = false
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
titleBar.state = "default"
|
||||
root.visible = false;
|
||||
closeCallback();
|
||||
}
|
||||
|
||||
function toggleIsHidden() {
|
||||
passphaseInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
passphaseInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
isHidden = !isHidden;
|
||||
}
|
||||
|
||||
function showError(errorText) {
|
||||
open(root.walletName, errorText);
|
||||
}
|
||||
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 480
|
||||
height: 360
|
||||
|
||||
ColumnLayout {
|
||||
z: inactiveOverlay.z + 1
|
||||
id: mainLayout
|
||||
spacing: 10
|
||||
anchors { fill: parent; margins: 35 }
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.maximumWidth: 400
|
||||
|
||||
Label {
|
||||
text: (root.walletName.length > 0 ? qsTr("Please enter wallet device passphrase for: ") + root.walletName : qsTr("Please enter wallet device passphrase")) + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
font.pixelSize: 14
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.warningColor
|
||||
}
|
||||
|
||||
Label {
|
||||
text: root.errorText
|
||||
visible: root.errorText
|
||||
|
||||
color: MoneroComponents.Style.errorColor
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
TextField {
|
||||
id : passphaseInput1
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
KeyNavigation.tab: passphaseInput2
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Please re-enter") + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
TextField {
|
||||
id : passphaseInput2
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: okButton
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.dimmedFontColor
|
||||
selectedTextColor: MoneroComponents.Style.defaultFontColor
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (passphaseInput1.text === passphaseInput2.text) {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "black"
|
||||
}
|
||||
|
||||
// Ok/Cancel buttons
|
||||
RowLayout {
|
||||
id: buttons
|
||||
spacing: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: cancelButton
|
||||
text: qsTr("Cancel") + translationManager.emptyString
|
||||
KeyNavigation.tab: passphaseInput1
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
MoneroComponents.StandardButton {
|
||||
id: okButton
|
||||
text: qsTr("Continue") + translationManager.emptyString
|
||||
KeyNavigation.tab: cancelButton
|
||||
enabled: passphaseInput1.text === passphaseInput2.text
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -44,34 +44,63 @@ Item {
|
||||
z: parent.z + 2
|
||||
|
||||
property bool isHidden: true
|
||||
property alias password: passwordInput.text
|
||||
property alias password: passwordInput1.text
|
||||
property string walletName
|
||||
property string errorText
|
||||
property bool shiftIsPressed: false
|
||||
property bool isCapsLocksActive: false
|
||||
property bool backspaceIsPressed: false
|
||||
property bool passwordDialogMode
|
||||
property bool passphraseDialogMode
|
||||
property bool newPasswordDialogMode
|
||||
|
||||
// same signals as Dialog has
|
||||
signal accepted()
|
||||
signal acceptedNewPassword()
|
||||
signal acceptedPassphrase()
|
||||
signal rejected()
|
||||
signal rejectedNewPassword()
|
||||
signal rejectedPassphrase()
|
||||
signal closeCallback()
|
||||
|
||||
function open(walletName, errorText) {
|
||||
function _openInit(walletName, errorText) {
|
||||
isHidden = true
|
||||
passwordInput.echoMode = TextInput.Password
|
||||
passwordInput.text = ""
|
||||
passwordInput.forceActiveFocus();
|
||||
capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
passwordInput1.echoMode = TextInput.Password
|
||||
passwordInput2.echoMode = TextInput.Password
|
||||
passwordInput1.text = ""
|
||||
passwordInput2.text = ""
|
||||
passwordInput1.forceActiveFocus();
|
||||
inactiveOverlay.visible = true // draw appwindow inactive
|
||||
root.walletName = walletName ? walletName : ""
|
||||
errorTextLabel.text = errorText ? errorText : "";
|
||||
leftPanel.enabled = false
|
||||
middlePanel.enabled = false
|
||||
wizard.enabled = false
|
||||
titleBar.state = "essentials"
|
||||
root.visible = true;
|
||||
appWindow.hideBalanceForced = true;
|
||||
appWindow.updateBalance();
|
||||
}
|
||||
|
||||
function open(walletName, errorText) {
|
||||
passwordDialogMode = true;
|
||||
passphraseDialogMode = false;
|
||||
newPasswordDialogMode = false;
|
||||
_openInit(walletName, errorText);
|
||||
}
|
||||
|
||||
function openPassphraseDialog() {
|
||||
passwordDialogMode = false;
|
||||
passphraseDialogMode = true;
|
||||
newPasswordDialogMode = false;
|
||||
_openInit("", "");
|
||||
}
|
||||
|
||||
function openNewPasswordDialog() {
|
||||
passwordDialogMode = false;
|
||||
passphraseDialogMode = false;
|
||||
newPasswordDialogMode = true;
|
||||
_openInit("", "");
|
||||
}
|
||||
|
||||
function showError(errorText) {
|
||||
open(root.walletName, errorText);
|
||||
}
|
||||
@ -80,6 +109,7 @@ Item {
|
||||
inactiveOverlay.visible = false
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
wizard.enabled = true
|
||||
titleBar.state = "default"
|
||||
|
||||
root.visible = false;
|
||||
@ -88,6 +118,12 @@ Item {
|
||||
closeCallback();
|
||||
}
|
||||
|
||||
function toggleIsHidden() {
|
||||
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
isHidden = !isHidden;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
z: inactiveOverlay.z + 1
|
||||
id: mainLayout
|
||||
@ -102,7 +138,14 @@ Item {
|
||||
Layout.maximumWidth: 400
|
||||
|
||||
Label {
|
||||
text: (root.walletName.length > 0 ? qsTr("Please enter wallet password for: ") + root.walletName : qsTr("Please enter wallet password")) + translationManager.emptyString
|
||||
text: {
|
||||
if (newPasswordDialogMode) {
|
||||
return qsTr("Please enter new wallet password") + translationManager.emptyString;
|
||||
} else {
|
||||
var device = passwordDialogMode ? qsTr("wallet password") : qsTr("wallet device passphrase");
|
||||
return (root.walletName.length > 0 ? qsTr("Please enter %1 for: ").arg(device) + root.walletName : qsTr("Please enter %1").arg(device)) + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
@ -111,19 +154,41 @@ Item {
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString
|
||||
visible: passphraseDialogMode
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
font.pixelSize: 14
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.warningColor
|
||||
}
|
||||
|
||||
Label {
|
||||
id: errorTextLabel
|
||||
visible: root.errorText || text !== ""
|
||||
|
||||
color: MoneroComponents.Style.errorColor
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Label {
|
||||
id: capsLockTextLabel
|
||||
visible: false
|
||||
color: MoneroComponents.Style.errorColor
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
TextField {
|
||||
id : passwordInput
|
||||
id: passwordInput1
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
@ -131,24 +196,20 @@ Item {
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: okButton
|
||||
KeyNavigation.tab: {
|
||||
if (passwordDialogMode) {
|
||||
return okButton
|
||||
} else {
|
||||
return passwordInput2
|
||||
}
|
||||
}
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
|
||||
onTextChanged: {
|
||||
var letter = text[passwordInput.text.length - 1];
|
||||
isCapsLocksActive = Utils.isUpperLock(shiftIsPressed, letter);
|
||||
if(isCapsLocksActive && !backspaceIsPressed){
|
||||
errorTextLabel.text = qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
|
||||
}
|
||||
else{
|
||||
errorTextLabel.text = "";
|
||||
}
|
||||
}
|
||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
@ -177,8 +238,7 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
passwordInput.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
isHidden = !isHidden;
|
||||
toggleIsHidden();
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
@ -195,28 +255,129 @@ Item {
|
||||
Keys.enabled: root.visible
|
||||
Keys.onReturnPressed: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
if (passwordDialogMode) {
|
||||
root.accepted()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if(event.key === Qt.Key_Shift){
|
||||
shiftIsPressed = true;
|
||||
}
|
||||
if(event.key === Qt.Key_Backspace){
|
||||
backspaceIsPressed = true;
|
||||
if (passwordDialogMode) {
|
||||
root.rejected()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
Keys.onReleased: {
|
||||
if(event.key === Qt.Key_Shift){
|
||||
shiftIsPressed = false;
|
||||
}
|
||||
if(event.key === Qt.Key_Backspace){
|
||||
backspaceIsPressed =false;
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
visible: !passwordDialogMode
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: !passwordDialogMode
|
||||
text: newPasswordDialogMode ? qsTr("Please confirm new password") : qsTr("Please confirm wallet device passphrase") + translationManager.emptyString
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 16
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordInput2
|
||||
visible: !passwordDialogMode
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: okButton
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (passwordInput1.text === passwordInput2.text) {
|
||||
root.close()
|
||||
if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
Rectangle {
|
||||
visible: !passwordDialogMode
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 10
|
||||
opacity: 0
|
||||
color: "black"
|
||||
}
|
||||
|
||||
// Ok/Cancel buttons
|
||||
@ -230,10 +391,16 @@ Item {
|
||||
id: cancelButton
|
||||
small: true
|
||||
text: root.walletName.length > 0 ? qsTr("Change wallet") + translationManager.emptyString : qsTr("Cancel") + translationManager.emptyString
|
||||
KeyNavigation.tab: passwordInput
|
||||
KeyNavigation.tab: passwordInput1
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
if (passwordDialogMode) {
|
||||
root.rejected()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,13 +409,19 @@ Item {
|
||||
small: true
|
||||
text: qsTr("Continue") + translationManager.emptyString
|
||||
KeyNavigation.tab: cancelButton
|
||||
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
if (passwordDialogMode) {
|
||||
root.accepted()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
images/tails-grey.png
Normal file
BIN
images/tails-grey.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
30
installers/windows/Deterministic.md
Normal file
30
installers/windows/Deterministic.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Building the Installer Deterministically
|
||||
|
||||
This file contains info about building the Windows installer deterministically, i.e. how different people on different Windows machines or VMs can build it and arrive at a result that is bit-for-bit identical. This approach is also known as *reproducible builds*, see e.g. [this Wikipedia article](https://en.wikipedia.org/wiki/Reproducible_builds).
|
||||
|
||||
The steps to build the Windows installer deterministically by a group of people are the following (for some details about the build process in general see `README.md`):
|
||||
|
||||
* Agree on a particular version of Inno Setup, and everybody install that
|
||||
* Get the zip file for the Windows GUI wallet and unpack it, plus make sure / check that the file timestamps are preserved, i.e. upacked timestamp = timestamp in zip file
|
||||
* Build using Inno Setup and the `Monero.iss` script file
|
||||
* Success: All people arrive at a bit-for-bit identical installer .exe file, which they can verify by calculating and exchanging SHA256 hashes
|
||||
|
||||
Some background info why this process is relatively simple:
|
||||
|
||||
The tool used to build the Windows installer, Inno Setup, avoids many issues that make reproducible builds very challenging with many other compilers and similar tools: It does not store current date and time in the installer .exe file, and it does not seem to depend on the Windows version it runs on (tried with Windows 7 and two different editions of Windows 10), nor on the locale and display language.
|
||||
|
||||
So fortunately no complicated things as faked current system time or use of VMs with exactly prescribed versions of Windows are necessary.
|
||||
|
||||
The version of Inno Setup **is** important however: People wanting to reproducibly build the installer must agree on a particular version to use. This should not be hard to do however.
|
||||
|
||||
Also important are the **timestamps** of the source files because they go into the installer file, to be restored at install time.
|
||||
|
||||
You would think timestamp preservation is no problem when unpacking the zip archive with the files for the Windows GUI wallet from getmonero.org, but if you use the zip folder unpack functionality of the Windows 7 GUI, the files get the current date, **not** the file recorded in the zip file. (The Windows 10 GUI seems better here, and also the 7zip app.)
|
||||
|
||||
In any case, after unpacking, check the file dates in the `bin` directory where the installer script looks for them with the dates of the files in the zip file: They must be identical.
|
||||
|
||||
Note that the the following line in `Monero.iss` is also important regarding file timestamps:
|
||||
|
||||
TimeStampsInUTC=yes
|
||||
|
||||
Without this line the **timezone** of the machine used to build the installer would matter, with different timezones leading to different installer files.
|
@ -21,6 +21,8 @@ DisableWelcomePage=no
|
||||
LicenseFile=LICENSE
|
||||
AppPublisher=The Monero Developer Community
|
||||
AppPublisherURL=https://getmonero.org
|
||||
TimeStampsInUTC=yes
|
||||
CompressionThreads=1
|
||||
|
||||
UsedUserAreasWarning=no
|
||||
; The above directive silences the following compiler warning:
|
||||
@ -68,7 +70,7 @@ Source: "FinishImage.bmp"; Flags: dontcopy
|
||||
|
||||
; Monero GUI wallet exe and guide
|
||||
Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-GUI-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Monero CLI wallet
|
||||
Source: "bin\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
@ -12,13 +12,13 @@ function destinationsToAddress(destinations){
|
||||
}
|
||||
|
||||
function addressTruncate(address, range){
|
||||
if(typeof(address) === "undefined") return;
|
||||
if(typeof(address) === "undefined") return "";
|
||||
if(typeof(range) === "undefined") range = 8;
|
||||
return address.substring(0, range) + "..." + address.substring(address.length-range);
|
||||
}
|
||||
|
||||
function addressTruncatePretty(address, blocks){
|
||||
if(typeof(address) === "undefined") return;
|
||||
if(typeof(address) === "undefined") return "";
|
||||
if(typeof(blocks) === "undefined") blocks = 2;
|
||||
blocks = blocks <= 1 ? 1 : blocks >= 23 ? 23 : blocks;
|
||||
var ret = "";
|
||||
|
19
js/Utils.js
19
js/Utils.js
@ -112,25 +112,6 @@ function roundDownToNearestThousand(_num){
|
||||
return Math.floor(_num/1000.0)*1000
|
||||
}
|
||||
|
||||
function isAlpha(letter){ return letter.match(/^[A-Za-z0-9]+$/) !== null; }
|
||||
|
||||
function isLowerCaseChar(letter){ return letter === letter.toLowerCase(); }
|
||||
|
||||
function isUpperLock(shift, letter){
|
||||
if(!isAlpha((letter))) return false;
|
||||
if(shift) {
|
||||
if(isLowerCaseChar(letter))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
if(isLowerCaseChar(letter))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function qmlEach(item, properties, ignoredObjectNames, arr){
|
||||
// Traverse QML object tree and return components that match
|
||||
// via property names. Similar to jQuery("myclass").each(...
|
||||
|
@ -51,4 +51,12 @@ SCRIPT_DIR="\$(dirname "\$(test -L "\${BASH_SOURCE[0]}" && readlink "\${BASH_SOU
|
||||
"\$SCRIPT_DIR"/$GUI_EXEC "\$@"
|
||||
EOL
|
||||
|
||||
# Create start script
|
||||
cat > $TARGET/start-tails.AppImage <<EOL
|
||||
#!/bin/bash
|
||||
# Silly hack to provide a launcher that is double clickable
|
||||
bash ./start-gui.sh
|
||||
EOL
|
||||
|
||||
chmod +x $TARGET/start-gui.sh
|
||||
chmod +x $TARGET/start-tails.AppImage
|
||||
|
45
main.cpp
45
main.cpp
@ -64,8 +64,9 @@
|
||||
#include "MainApp.h"
|
||||
#include "qt/ipc.h"
|
||||
#include "qt/utils.h"
|
||||
#include "qt/mime.h"
|
||||
#include "src/qt/TailsOS.h"
|
||||
#include "src/qt/KeysFiles.h"
|
||||
#include "src/qt/MoneroSettings.h"
|
||||
#include "qt/prices.h"
|
||||
|
||||
// IOS exclusions
|
||||
@ -82,6 +83,7 @@ bool isAndroid = false;
|
||||
bool isWindows = false;
|
||||
bool isMac = false;
|
||||
bool isLinux = false;
|
||||
bool isTails = false;
|
||||
bool isDesktop = false;
|
||||
bool isOpenGL = true;
|
||||
|
||||
@ -101,6 +103,7 @@ int main(int argc, char *argv[])
|
||||
bool isWindows = true;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
bool isLinux = true;
|
||||
bool isTails = TailsOS::detect();
|
||||
#elif defined(Q_OS_MAC)
|
||||
bool isMac = true;
|
||||
#endif
|
||||
@ -122,25 +125,40 @@ int main(int argc, char *argv[])
|
||||
// qDebug() << "High DPI auto scaling - enabled";
|
||||
//#endif
|
||||
|
||||
MainApp app(argc, argv);
|
||||
|
||||
app.setApplicationName("monero-core");
|
||||
app.setOrganizationDomain("getmonero.org");
|
||||
app.setOrganizationName("monero-project");
|
||||
|
||||
// Ask to enable Tails OS persistence mode, it affects:
|
||||
// - Log file location
|
||||
// - QML Settings file location (monero-core.conf)
|
||||
// - Default wallets path
|
||||
// Target directory is: ~/Persistent/Monero
|
||||
if (isTails) {
|
||||
if (!TailsOS::detectDataPersistence())
|
||||
TailsOS::showDataPersistenceDisabledWarning();
|
||||
else
|
||||
TailsOS::askPersistence();
|
||||
}
|
||||
|
||||
QString moneroAccountsDir;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_IOS)
|
||||
QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
|
||||
#else
|
||||
QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
|
||||
#endif
|
||||
if (!moneroAccountsRootDir.empty()) {
|
||||
|
||||
if(isTails && TailsOS::usePersistence){
|
||||
moneroAccountsDir = QDir::homePath() + "/Persistent/Monero/wallets";
|
||||
} else if (!moneroAccountsRootDir.empty()) {
|
||||
moneroAccountsDir = moneroAccountsRootDir.at(0) + "/Monero/wallets";
|
||||
} else {
|
||||
qCritical() << "Error: accounts root directory could not be set";
|
||||
return 1;
|
||||
}
|
||||
|
||||
MainApp app(argc, argv);
|
||||
|
||||
app.setApplicationName("monero-core");
|
||||
app.setOrganizationDomain("getmonero.org");
|
||||
app.setOrganizationName("monero-project");
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico"));
|
||||
#endif
|
||||
@ -173,9 +191,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
qWarning().noquote() << "app startd" << "(log: " + logPath + ")";
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Desktop entry
|
||||
registerXdgMime(app);
|
||||
#endif
|
||||
|
||||
IPC *ipc = new IPC(&app);
|
||||
QStringList posArgs = parser.positionalArguments();
|
||||
@ -224,6 +241,9 @@ int main(int argc, char *argv[])
|
||||
// registering types for QML
|
||||
qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
|
||||
|
||||
// Temporary Qt.labs.settings replacement
|
||||
qmlRegisterType<MoneroSettings>("moneroComponents.Settings", 1, 0, "MoneroSettings");
|
||||
|
||||
qmlRegisterUncreatableType<Wallet>("moneroComponents.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly");
|
||||
|
||||
|
||||
@ -313,6 +333,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
engine.rootContext()->setContextProperty("walletLogPath", logPath);
|
||||
|
||||
engine.rootContext()->setContextProperty("tailsUsePersistence", TailsOS::usePersistence);
|
||||
|
||||
// Exclude daemon manager from IOS
|
||||
#ifndef Q_OS_IOS
|
||||
const QStringList arguments = (QStringList) QCoreApplication::arguments().at(0);
|
||||
@ -326,7 +348,7 @@ int main(int argc, char *argv[])
|
||||
engine.rootContext()->setContextProperty("isIOS", isIOS);
|
||||
engine.rootContext()->setContextProperty("isAndroid", isAndroid);
|
||||
engine.rootContext()->setContextProperty("isOpenGL", isOpenGL);
|
||||
engine.rootContext()->setContextProperty("isLinux", isLinux);
|
||||
engine.rootContext()->setContextProperty("isTails", isTails);
|
||||
|
||||
engine.rootContext()->setContextProperty("screenWidth", geo.width());
|
||||
engine.rootContext()->setContextProperty("screenHeight", geo.height());
|
||||
@ -350,6 +372,7 @@ int main(int argc, char *argv[])
|
||||
accountName = "My monero Account";
|
||||
|
||||
engine.rootContext()->setContextProperty("defaultAccountName", accountName);
|
||||
engine.rootContext()->setContextProperty("homePath", QDir::homePath());
|
||||
engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
|
||||
engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
|
||||
|
||||
|
116
main.qml
116
main.qml
@ -31,11 +31,11 @@ import QtQuick.Window 2.0
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import moneroComponents.Wallet 1.0
|
||||
import moneroComponents.PendingTransaction 1.0
|
||||
import moneroComponents.NetworkType 1.0
|
||||
import moneroComponents.Settings 1.0
|
||||
|
||||
import "components"
|
||||
import "components" as MoneroComponents
|
||||
@ -283,7 +283,7 @@ ApplicationWindow {
|
||||
titleBar.visible = persistentSettings.customDecorations;
|
||||
}
|
||||
|
||||
function closeWallet() {
|
||||
function closeWallet(callback) {
|
||||
|
||||
// Disconnect all listeners
|
||||
if (typeof currentWallet !== "undefined" && currentWallet !== null) {
|
||||
@ -306,8 +306,17 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
currentWallet = undefined;
|
||||
walletManager.closeWallet();
|
||||
|
||||
appWindow.showProcessingSplash(qsTr("Closing wallet..."));
|
||||
if (callback) {
|
||||
walletManager.closeWalletAsync(function() {
|
||||
hideProcessingSplash();
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
walletManager.closeWallet();
|
||||
hideProcessingSplash();
|
||||
}
|
||||
}
|
||||
|
||||
function connectWallet(wallet) {
|
||||
@ -558,26 +567,21 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function onWalletClosed(walletAddress) {
|
||||
hideProcessingSplash();
|
||||
console.log(">>> wallet closed: " + walletAddress)
|
||||
}
|
||||
|
||||
function onWalletPassphraseNeeded(){
|
||||
if(rootItem.state !== "normal") return;
|
||||
|
||||
hideProcessingSplash();
|
||||
|
||||
console.log(">>> wallet passphrase needed: ")
|
||||
passphraseDialog.onAcceptedCallback = function() {
|
||||
walletManager.onPassphraseEntered(passphraseDialog.passphrase);
|
||||
passwordDialog.onAcceptedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered(passwordDialog.password);
|
||||
this.onWalletOpening();
|
||||
}
|
||||
passphraseDialog.onRejectedCallback = function() {
|
||||
passwordDialog.onRejectedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered("", true);
|
||||
this.onWalletOpening();
|
||||
}
|
||||
passphraseDialog.open()
|
||||
passwordDialog.openPassphraseDialog()
|
||||
}
|
||||
|
||||
function onWalletUpdate() {
|
||||
@ -1105,27 +1109,30 @@ ApplicationWindow {
|
||||
console.log("Hiding processing splash")
|
||||
splash.close();
|
||||
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
titleBar.enabled = true
|
||||
inactiveOverlay.visible = false;
|
||||
if (!passwordDialog.visible) {
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
titleBar.enabled = true
|
||||
inactiveOverlay.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// close wallet and show wizard
|
||||
function showWizard(){
|
||||
clearMoneroCardLabelText();
|
||||
walletInitialized = false;
|
||||
closeWallet();
|
||||
currentWallet = undefined;
|
||||
wizard.restart();
|
||||
wizard.wizardState = "wizardHome";
|
||||
rootItem.state = "wizard"
|
||||
// reset balance
|
||||
leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0);
|
||||
fiatApiUpdateBalance(0, 0);
|
||||
// disable timers
|
||||
userInActivityTimer.running = false;
|
||||
simpleModeConnectionTimer.running = false;
|
||||
closeWallet(function() {
|
||||
currentWallet = undefined;
|
||||
wizard.restart();
|
||||
wizard.wizardState = "wizardHome";
|
||||
rootItem.state = "wizard"
|
||||
// reset balance
|
||||
leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0);
|
||||
fiatApiUpdateBalance(0, 0);
|
||||
// disable timers
|
||||
userInActivityTimer.running = false;
|
||||
simpleModeConnectionTimer.running = false;
|
||||
});
|
||||
}
|
||||
|
||||
function hideMenu() {
|
||||
@ -1289,7 +1296,6 @@ ApplicationWindow {
|
||||
y = (Screen.height - maxWindowHeight) / 2
|
||||
//
|
||||
walletManager.walletOpened.connect(onWalletOpened);
|
||||
walletManager.walletClosed.connect(onWalletClosed);
|
||||
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
|
||||
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
|
||||
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
|
||||
@ -1341,8 +1347,14 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
MoneroSettings {
|
||||
id: persistentSettings
|
||||
fileName: {
|
||||
if(isTails && tailsUsePersistence)
|
||||
return homePath + "/Persistent/Monero/monero-core.conf";
|
||||
return "";
|
||||
}
|
||||
|
||||
property string language
|
||||
property string locale
|
||||
property string account_name
|
||||
@ -1494,23 +1506,6 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
PassphraseDialog {
|
||||
id: passphraseDialog
|
||||
visible: false
|
||||
z: parent.z + 1
|
||||
anchors.fill: parent
|
||||
property var onAcceptedCallback
|
||||
property var onRejectedCallback
|
||||
onAccepted: {
|
||||
if (onAcceptedCallback)
|
||||
onAcceptedCallback();
|
||||
}
|
||||
onRejected: {
|
||||
if (onRejectedCallback)
|
||||
onRejectedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDialog {
|
||||
id: passwordDialog
|
||||
visible: false
|
||||
@ -1518,6 +1513,8 @@ ApplicationWindow {
|
||||
anchors.fill: parent
|
||||
property var onAcceptedCallback
|
||||
property var onRejectedCallback
|
||||
property var onAcceptedPassphraseCallback
|
||||
property var onRejectedPassphraseCallback
|
||||
onAccepted: {
|
||||
if (onAcceptedCallback)
|
||||
onAcceptedCallback();
|
||||
@ -1526,16 +1523,9 @@ ApplicationWindow {
|
||||
if (onRejectedCallback)
|
||||
onRejectedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
NewPasswordDialog {
|
||||
id: newPasswordDialog
|
||||
z: parent.z + 1
|
||||
visible:false
|
||||
anchors.fill: parent
|
||||
onAccepted: {
|
||||
if (currentWallet.setPassword(newPasswordDialog.password)) {
|
||||
appWindow.walletPassword = newPasswordDialog.password;
|
||||
onAcceptedNewPassword: {
|
||||
if (currentWallet.setPassword(passwordDialog.password)) {
|
||||
appWindow.walletPassword = passwordDialog.password;
|
||||
informationPopup.title = qsTr("Information") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Password changed successfully") + translationManager.emptyString;
|
||||
informationPopup.icon = StandardIcon.Information;
|
||||
@ -1547,7 +1537,14 @@ ApplicationWindow {
|
||||
informationPopup.onCloseCallback = null;
|
||||
informationPopup.open();
|
||||
}
|
||||
onRejected: {
|
||||
onRejectedNewPassword: {}
|
||||
onAcceptedPassphrase: {
|
||||
if (onAcceptedPassphraseCallback)
|
||||
onAcceptedPassphraseCallback();
|
||||
}
|
||||
onRejectedPassphrase: {
|
||||
if (onRejectedPassphraseCallback)
|
||||
onRejectedPassphraseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2131,8 +2128,8 @@ ApplicationWindow {
|
||||
console.log("close accepted");
|
||||
// Close wallet non async on exit
|
||||
daemonManager.exit();
|
||||
walletManager.closeWallet();
|
||||
Qt.quit();
|
||||
|
||||
closeWallet(Qt.quit);
|
||||
}
|
||||
|
||||
function onWalletCheckUpdatesComplete(update) {
|
||||
@ -2201,6 +2198,7 @@ ApplicationWindow {
|
||||
function checkInUserActivity() {
|
||||
if(rootItem.state !== "normal") return;
|
||||
if(!persistentSettings.lockOnUserInActivity) return;
|
||||
if(passwordDialog.visible) return;
|
||||
|
||||
// prompt password after X seconds of inactivity
|
||||
var epoch = Math.floor((new Date).getTime() / 1000);
|
||||
|
@ -1,6 +1,6 @@
|
||||
# qml components require at least QT 5.7.0
|
||||
lessThan (QT_MAJOR_VERSION, 5) | lessThan (QT_MINOR_VERSION, 7) {
|
||||
error("Can't build with Qt $${QT_VERSION}. Use at least Qt 5.7.0")
|
||||
# qml components require at least QT 5.9.0
|
||||
lessThan (QT_MAJOR_VERSION, 5) | lessThan (QT_MINOR_VERSION, 9) {
|
||||
error("Can't build with Qt $${QT_VERSION}. Use at least Qt 5.9.0")
|
||||
}
|
||||
|
||||
TEMPLATE = app
|
||||
@ -65,10 +65,12 @@ HEADERS += \
|
||||
MainApp.h \
|
||||
src/qt/FutureScheduler.h \
|
||||
src/qt/ipc.h \
|
||||
src/qt/mime.h \
|
||||
src/qt/KeysFiles.h \
|
||||
src/qt/utils.h \
|
||||
src/qt/prices.h
|
||||
src/qt/prices.h \
|
||||
src/qt/macoshelper.h \
|
||||
src/qt/MoneroSettings.h \
|
||||
src/qt/TailsOS.h
|
||||
|
||||
SOURCES += main.cpp \
|
||||
filter.cpp \
|
||||
@ -99,10 +101,11 @@ SOURCES += main.cpp \
|
||||
MainApp.cpp \
|
||||
src/qt/FutureScheduler.cpp \
|
||||
src/qt/ipc.cpp \
|
||||
src/qt/mime.cpp \
|
||||
src/qt/KeysFiles.cpp \
|
||||
src/qt/utils.cpp \
|
||||
src/qt/prices.cpp
|
||||
src/qt/prices.cpp \
|
||||
src/qt/MoneroSettings.cpp \
|
||||
src/qt/TailsOS.cpp
|
||||
|
||||
CONFIG(DISABLE_PASS_STRENGTH_METER) {
|
||||
HEADERS -= src/zxcvbn-c/zxcvbn.h
|
||||
@ -334,7 +337,8 @@ linux {
|
||||
-llmdb \
|
||||
-lsodium \
|
||||
-lhidapi-libusb \
|
||||
-lcrypto $$TREZOR_LINKER
|
||||
-lcrypto $$TREZOR_LINKER \
|
||||
-lX11
|
||||
|
||||
if(!android) {
|
||||
LIBS+= \
|
||||
@ -359,6 +363,8 @@ macx {
|
||||
# message("using static libraries")
|
||||
# LIBS+= -Wl,-Bstatic
|
||||
# }
|
||||
QT += macextras
|
||||
OBJECTIVE_SOURCES += src/qt/macoshelper.mm
|
||||
LIBS+= \
|
||||
-L/usr/local/lib \
|
||||
-L/usr/local/opt/openssl/lib \
|
||||
@ -372,6 +378,7 @@ macx {
|
||||
-lboost_chrono \
|
||||
-lboost_program_options \
|
||||
-framework CoreFoundation \
|
||||
-framework AppKit \
|
||||
-lhidapi \
|
||||
-lssl \
|
||||
-lsodium \
|
||||
|
35
oshelper.cpp
35
oshelper.cpp
@ -31,6 +31,20 @@
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#ifdef Q_OS_MAC
|
||||
#include "qt/macoshelper.h"
|
||||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <X11/XKBlib.h>
|
||||
#undef KeyPress
|
||||
#undef KeyRelease
|
||||
#undef FocusIn
|
||||
#undef FocusOut
|
||||
// #undef those Xlib #defines that conflict with QEvent::Type enum
|
||||
#endif
|
||||
|
||||
OSHelper::OSHelper(QObject *parent) : QObject(parent)
|
||||
{
|
||||
@ -58,6 +72,27 @@ bool OSHelper::removeTemporaryWallet(const QString &fileName) const
|
||||
return cache_deleted && address_deleted && keys_deleted;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/3006934
|
||||
bool OSHelper::isCapsLock() const
|
||||
{
|
||||
// platform dependent method of determining if CAPS LOCK is on
|
||||
#if defined(Q_OS_WIN32) // MS Windows version
|
||||
return GetKeyState(VK_CAPITAL) == 1;
|
||||
#elif defined(Q_OS_LINUX) // X11 version
|
||||
Display * d = XOpenDisplay((char*)0);
|
||||
bool caps_state = false;
|
||||
if (d) {
|
||||
unsigned n;
|
||||
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
|
||||
caps_state = (n & 0x01) == 1;
|
||||
}
|
||||
return caps_state;
|
||||
#elif defined(Q_OS_MAC)
|
||||
return MacOSHelper::isCapsLock();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
QString OSHelper::temporaryPath() const
|
||||
{
|
||||
return QDir::tempPath();
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
Q_INVOKABLE QString temporaryFilename() const;
|
||||
Q_INVOKABLE QString temporaryPath() const;
|
||||
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
|
||||
Q_INVOKABLE bool isCapsLock() const;
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -49,9 +49,9 @@ Rectangle {
|
||||
property var model
|
||||
property int sideMargin: 50
|
||||
property var initialized: false
|
||||
property int txMax: 5
|
||||
property int txMax: Math.max(5, ((appWindow.height - 250) / 60))
|
||||
property int txOffset: 0
|
||||
property int txPage: (txOffset / 5) + 1
|
||||
property int txPage: (txOffset / txMax) + 1
|
||||
property int txCount: 0
|
||||
property var sortSearchString: null
|
||||
property bool sortDirection: true // true = desc, false = asc
|
||||
@ -67,6 +67,8 @@ Rectangle {
|
||||
|
||||
color: "transparent"
|
||||
|
||||
onTxMaxChanged: root.updateDisplay(root.txOffset, root.txMax);
|
||||
|
||||
ColumnLayout {
|
||||
id: pageRoot
|
||||
anchors.topMargin: 40
|
||||
@ -1343,13 +1345,14 @@ Rectangle {
|
||||
root.updateDisplay(root.txOffset, root.txMax);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
function reset(keepDate) {
|
||||
root.txOffset = 0;
|
||||
root.txMax = 5;
|
||||
|
||||
if (typeof root.model !== 'undefined' && root.model != null) {
|
||||
root.model.dateFromFilter = "2014-04-18" // genesis block
|
||||
root.model.dateToFilter = "9999-09-09" // fix before september 9999
|
||||
if (!keepDate) {
|
||||
root.model.dateFromFilter = "2014-04-18" // genesis block
|
||||
root.model.dateToFilter = "9999-09-09" // fix before september 9999
|
||||
}
|
||||
// negative values disable filters here;
|
||||
root.model.amountFromFilter = -1;
|
||||
root.model.amountToFilter = -1;
|
||||
@ -1387,6 +1390,8 @@ Rectangle {
|
||||
txs.push(item);
|
||||
} else if(item.blockheight.toString().startsWith(root.sortSearchString)) {
|
||||
txs.push(item);
|
||||
} else if(item.tx_note.toLowerCase().indexOf(root.sortSearchString.toLowerCase()) !== -1) {
|
||||
txs.push(item);
|
||||
} else if (item.hash.startsWith(root.sortSearchString)){
|
||||
txs.push(item);
|
||||
}
|
||||
@ -1704,6 +1709,6 @@ Rectangle {
|
||||
|
||||
function onPageClosed(){
|
||||
root.initialized = false;
|
||||
root.reset();
|
||||
root.reset(true);
|
||||
}
|
||||
}
|
||||
|
@ -116,11 +116,11 @@ Rectangle {
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
Layout.fillWidth: true
|
||||
id: walletCreationHeight
|
||||
readOnly: true
|
||||
copyButton: true
|
||||
labelText: qsTr("Block #") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
text: currentWallet.walletCreationHeight
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,6 +261,7 @@ Rectangle {
|
||||
function onPageCompleted() {
|
||||
console.log("keys page loaded");
|
||||
|
||||
walletCreationHeight.text = currentWallet.walletCreationHeight
|
||||
secretViewKey.text = currentWallet.secretViewKey
|
||||
publicViewKey.text = currentWallet.publicViewKey
|
||||
secretSpendKey.text = (!currentWallet.viewOnly) ? currentWallet.secretSpendKey : ""
|
||||
|
@ -208,10 +208,11 @@ Rectangle {
|
||||
confirmationDialog.icon = StandardIcon.Question
|
||||
confirmationDialog.cancelText = qsTr("Cancel")
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
walletManager.closeWallet();
|
||||
walletManager.clearWalletCache(persistentSettings.wallet_path);
|
||||
walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword,
|
||||
persistentSettings.nettype, persistentSettings.kdfRounds);
|
||||
appWindow.closeWallet(function() {
|
||||
walletManager.clearWalletCache(persistentSettings.wallet_path);
|
||||
walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword,
|
||||
persistentSettings.nettype, persistentSettings.kdfRounds);
|
||||
});
|
||||
}
|
||||
|
||||
confirmationDialog.onRejectedCallback = null;
|
||||
@ -325,6 +326,41 @@ Rectangle {
|
||||
font.pixelSize: 14
|
||||
text: isOpenGL ? "OpenGL" : "Low graphics mode"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: isTails
|
||||
height: 1
|
||||
Layout.topMargin: 2
|
||||
Layout.bottomMargin: 2
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dividerColor
|
||||
opacity: MoneroComponents.Style.dividerOpacity
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: isTails
|
||||
height: 1
|
||||
Layout.topMargin: 2
|
||||
Layout.bottomMargin: 2
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dividerColor
|
||||
opacity: MoneroComponents.Style.dividerOpacity
|
||||
}
|
||||
|
||||
MoneroComponents.TextBlock {
|
||||
visible: isTails
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 14
|
||||
text: qsTr("Tails: ") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.TextBlock {
|
||||
visible: isTails
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
font.pixelSize: 14
|
||||
text: tailsUsePersistence ? qsTr("persistent") + translationManager.emptyString : qsTr("persistence disabled") + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy info to clipboard
|
||||
|
@ -339,7 +339,7 @@ Rectangle {
|
||||
onClicked: {
|
||||
passwordDialog.onAcceptedCallback = function() {
|
||||
if(appWindow.walletPassword === passwordDialog.password){
|
||||
newPasswordDialog.open()
|
||||
passwordDialog.openNewPasswordDialog()
|
||||
} else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Wrong password") + translationManager.emptyString;
|
||||
|
3
qml.qrc
3
qml.qrc
@ -96,9 +96,7 @@
|
||||
<file>pages/SharedRingDB.qml</file>
|
||||
<file>components/effects/ImageMask.qml</file>
|
||||
<file>components/IconButton.qml</file>
|
||||
<file>components/PassphraseDialog.qml</file>
|
||||
<file>components/PasswordDialog.qml</file>
|
||||
<file>components/NewPasswordDialog.qml</file>
|
||||
<file>components/InputDialog.qml</file>
|
||||
<file>components/ProcessingSplash.qml</file>
|
||||
<file>components/ProgressBar.qml</file>
|
||||
@ -253,5 +251,6 @@
|
||||
<file>images/copy.svg</file>
|
||||
<file>images/edit.svg</file>
|
||||
<file>images/arrow-right-in-circle-outline-medium-white.svg</file>
|
||||
<file>images/tails-grey.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -524,7 +524,8 @@ bool Wallet::submitTxFile(const QString &fileName) const
|
||||
void Wallet::commitTransactionAsync(PendingTransaction *t)
|
||||
{
|
||||
m_scheduler.run([this, t] {
|
||||
emit transactionCommitted(t->commit(), t, t->txid());
|
||||
auto txIdList = t->txid(); // retrieve before commit
|
||||
emit transactionCommitted(t->commit(), t, txIdList);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -351,7 +351,7 @@ signals:
|
||||
void walletCreationHeightChanged();
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void transactionCommitted(bool status, PendingTransaction *t, QStringList txid);
|
||||
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
||||
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
||||
|
||||
// emitted when transaction is created async
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
auto tmpPass = m_mgr->m_passphrase.toStdString();
|
||||
m_mgr->m_passphrase = QString::null;
|
||||
m_mgr->m_passphrase = QString();
|
||||
|
||||
return Monero::optional<std::string>(tmpPass);
|
||||
}
|
||||
@ -228,11 +228,11 @@ QString WalletManager::closeWallet()
|
||||
return result;
|
||||
}
|
||||
|
||||
void WalletManager::closeWalletAsync()
|
||||
void WalletManager::closeWalletAsync(const QJSValue& callback)
|
||||
{
|
||||
m_scheduler.run([this] {
|
||||
emit walletClosed(closeWallet());
|
||||
});
|
||||
return QJSValueList({closeWallet()});
|
||||
}, callback);
|
||||
}
|
||||
|
||||
bool WalletManager::walletExists(const QString &path) const
|
||||
|
@ -117,7 +117,7 @@ public:
|
||||
/*!
|
||||
* \brief closeWalletAsync - asynchronous version of "closeWallet"
|
||||
*/
|
||||
Q_INVOKABLE void closeWalletAsync();
|
||||
Q_INVOKABLE void closeWalletAsync(const QJSValue& callback);
|
||||
|
||||
//! checks is given filename is a wallet;
|
||||
Q_INVOKABLE bool walletExists(const QString &path) const;
|
||||
@ -192,7 +192,6 @@ signals:
|
||||
void walletPassphraseNeeded();
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void walletClosed(const QString &walletAddress);
|
||||
void checkUpdatesComplete(const QString &result) const;
|
||||
void miningStatus(bool isMining) const;
|
||||
|
||||
|
207
src/qt/MoneroSettings.cpp
Normal file
207
src/qt/MoneroSettings.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <QtCore>
|
||||
#include <QMetaObject>
|
||||
#include <QSettings>
|
||||
#include <QPointer>
|
||||
#include <QJSValue>
|
||||
#include <QHash>
|
||||
#include <QMetaProperty>
|
||||
|
||||
#include "src/qt/MoneroSettings.h"
|
||||
|
||||
/*!
|
||||
\qmlmodule moneroSettings 1.0
|
||||
\title Monero Settings QML Component
|
||||
\ingroup qmlmodules
|
||||
\brief Provides persistent platform-independent application settings.
|
||||
|
||||
This component was introduced in order to have control over where the
|
||||
configuration file is written. This is needed for Tails OS and
|
||||
portable installations.
|
||||
|
||||
For more information, see: https://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html and
|
||||
https://github.com/qt/qtdeclarative/blob/v5.12.0/src/imports/settings/qqmlsettings.cpp
|
||||
|
||||
To use this module, import the module with the following line:
|
||||
\code
|
||||
import moneroComponents.Settings 1.0
|
||||
\endcode
|
||||
|
||||
Usage:
|
||||
\code
|
||||
MoneroSettings { id: persistentSettings, property bool foo: true }
|
||||
\endcode
|
||||
|
||||
@TODO: Remove this QML component after migrating to Qt >= 5.12.0, as
|
||||
`Qt.labs.settings` provides the fileName via a Q_PROPERTY
|
||||
*/
|
||||
|
||||
|
||||
void MoneroSettings::load()
|
||||
{
|
||||
const QMetaObject *mo = this->metaObject();
|
||||
const int offset = mo->propertyOffset();
|
||||
const int count = mo->propertyCount();
|
||||
|
||||
for (int i = offset; i < count; ++i) {
|
||||
QMetaProperty property = mo->property(i);
|
||||
const QVariant previousValue = readProperty(property);
|
||||
const QVariant currentValue = this->m_settings->value(property.name(), previousValue);
|
||||
|
||||
if (!currentValue.isNull() && (!previousValue.isValid()
|
||||
|| (currentValue.canConvert(previousValue.type()) && previousValue != currentValue))) {
|
||||
property.write(this, currentValue);
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "QQmlSettings: load" << property.name() << "setting:" << currentValue << "default:" << previousValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ensure that a non-existent setting gets written
|
||||
// even if the property wouldn't change later
|
||||
if (!this->m_settings->contains(property.name()))
|
||||
this->_q_propertyChanged();
|
||||
|
||||
// setup change notifications on first load
|
||||
if (!this->m_initialized && property.hasNotifySignal()) {
|
||||
static const int propertyChangedIndex = mo->indexOfSlot("_q_propertyChanged()");
|
||||
int signalIndex = property.notifySignalIndex();
|
||||
QMetaObject::connect(this, signalIndex, this, propertyChangedIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoneroSettings::_q_propertyChanged()
|
||||
{
|
||||
// Called on QML property change
|
||||
const QMetaObject *mo = this->metaObject();
|
||||
const int offset = mo->propertyOffset();
|
||||
const int count = mo->propertyCount();
|
||||
for (int i = offset; i < count; ++i) {
|
||||
const QMetaProperty &property = mo->property(i);
|
||||
const QVariant value = readProperty(property);
|
||||
this->m_changedProperties.insert(property.name(), value);
|
||||
#ifdef QT_DEBUG
|
||||
//qDebug() << "QQmlSettings: cache" << property.name() << ":" << value;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (this->m_timerId != 0)
|
||||
this->killTimer(this->m_timerId);
|
||||
this->m_timerId = this->startTimer(settingsWriteDelay);
|
||||
}
|
||||
|
||||
QVariant MoneroSettings::readProperty(const QMetaProperty &property) const
|
||||
{
|
||||
QVariant var = property.read(this);
|
||||
if (var.userType() == qMetaTypeId<QJSValue>())
|
||||
var = var.value<QJSValue>().toVariant();
|
||||
return var;
|
||||
}
|
||||
|
||||
void MoneroSettings::init()
|
||||
{
|
||||
if (!this->m_initialized) {
|
||||
this->m_settings = this->m_fileName.isEmpty() ? new QSettings() : new QSettings(this->m_fileName, QSettings::IniFormat);
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "QQmlSettings: stored at" << this->m_settings->fileName();
|
||||
#endif
|
||||
this->load();
|
||||
this->m_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MoneroSettings::reset()
|
||||
{
|
||||
if (this->m_initialized && this->m_settings && !this->m_changedProperties.isEmpty())
|
||||
this->store();
|
||||
delete this->m_settings;
|
||||
}
|
||||
|
||||
void MoneroSettings::store()
|
||||
{
|
||||
QHash<const char *, QVariant>::const_iterator it = this->m_changedProperties.constBegin();
|
||||
|
||||
while (it != this->m_changedProperties.constEnd()) {
|
||||
this->m_settings->setValue(it.key(), it.value());
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
//qDebug() << "QQmlSettings: store" << it.key() << ":" << it.value();
|
||||
#endif
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
this->m_changedProperties.clear();
|
||||
}
|
||||
|
||||
void MoneroSettings::setFileName(const QString &fileName)
|
||||
{
|
||||
if (fileName != this->m_fileName) {
|
||||
this->reset();
|
||||
this->m_fileName = fileName;
|
||||
if (this->m_initialized)
|
||||
this->load();
|
||||
}
|
||||
}
|
||||
|
||||
QString MoneroSettings::fileName() const
|
||||
{
|
||||
return this->m_fileName;
|
||||
}
|
||||
|
||||
void MoneroSettings::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
if (event->timerId() == this->m_timerId) {
|
||||
killTimer(this->m_timerId);
|
||||
this->m_timerId = 0;
|
||||
this->store();
|
||||
}
|
||||
QObject::timerEvent(event);
|
||||
}
|
||||
|
||||
void MoneroSettings::componentComplete()
|
||||
{
|
||||
this->init();
|
||||
}
|
||||
|
||||
void MoneroSettings::classBegin()
|
||||
{
|
||||
}
|
||||
|
||||
MoneroSettings::MoneroSettings(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
81
src/qt/MoneroSettings.h
Normal file
81
src/qt/MoneroSettings.h
Normal file
@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#ifndef MONEROSETTINGS_H
|
||||
#define MONEROSETTINGS_H
|
||||
|
||||
#include <QtQml/qqmlparserstatus.h>
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <qsettings.h>
|
||||
|
||||
static const int settingsWriteDelay = 500; // ms
|
||||
|
||||
class MoneroSettings : public QObject, public QQmlParserStatus
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
Q_PROPERTY(QString fileName READ fileName WRITE setFileName FINAL)
|
||||
public:
|
||||
explicit MoneroSettings(QObject *parent = nullptr);
|
||||
|
||||
QString fileName() const;
|
||||
void setFileName(const QString &fileName);
|
||||
|
||||
public slots:
|
||||
void _q_propertyChanged();
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
void classBegin() override;
|
||||
void componentComplete() override;
|
||||
|
||||
private:
|
||||
QVariant readProperty(const QMetaProperty &property) const;
|
||||
void init();
|
||||
void reset();
|
||||
void load();
|
||||
void store();
|
||||
|
||||
QHash<const char *, QVariant> m_changedProperties;
|
||||
QSettings *m_settings;
|
||||
QString m_fileName = QString("");
|
||||
bool m_initialized = false;
|
||||
int m_timerId = 0;
|
||||
};
|
||||
|
||||
#endif // MONEROSETTINGS_H
|
100
src/qt/TailsOS.cpp
Normal file
100
src/qt/TailsOS.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include <QRegExp>
|
||||
#include <QMessageBox>
|
||||
#include <QPixmap>
|
||||
#include <QTranslator>
|
||||
|
||||
#include "TailsOS.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool TailsOS::usePersistence = false;
|
||||
QString TailsOS::tailsPathData = QString("/live/persistence/TailsData_unlocked/");
|
||||
|
||||
bool TailsOS::detect()
|
||||
{
|
||||
if (!fileExists("/etc/os-release"))
|
||||
return false;
|
||||
|
||||
QByteArray data = fileOpen("/etc/os-release");
|
||||
QRegularExpression re("TAILS_PRODUCT_NAME=\"Tails\"");
|
||||
QRegularExpressionMatch os_match = re.match(data);
|
||||
bool matched = os_match.hasMatch();
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
if (matched)
|
||||
qDebug() << "Tails OS detected";
|
||||
#endif
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
bool TailsOS::detectDataPersistence()
|
||||
{
|
||||
return QDir(QDir::homePath() + "/Persistent").exists();
|
||||
}
|
||||
|
||||
bool TailsOS::detectDotPersistence()
|
||||
{
|
||||
return QDir(tailsPathData + "dotfiles").exists();
|
||||
}
|
||||
|
||||
void TailsOS::showDataPersistenceDisabledWarning()
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(QObject::tr("Warning: persistence disabled"));
|
||||
msgBox.setWindowTitle(QObject::tr("Warning: persistence disabled"));
|
||||
msgBox.setInformativeText(
|
||||
QObject::tr("Monero GUI has detected that Tails persistence is "
|
||||
"currently disabled. Any configurations you make inside "
|
||||
"the Monero GUI will not be saved."
|
||||
"\n\n"
|
||||
"In addition, make sure to not save your wallet on the "
|
||||
"filesystem, as it will be lost at shutdown."
|
||||
"\n\n"
|
||||
"To enable Tails persistence, setup an encrypted volume "
|
||||
"and restart Tails. To gain a startup menu item, "
|
||||
"enable the Tails \"dotfiles\" feature."));
|
||||
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png"));
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
void TailsOS::askPersistence()
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(QObject::tr("Monero GUI"));
|
||||
msgBox.setText(QObject::tr("Use Tails persistence?"));
|
||||
msgBox.setInformativeText(
|
||||
QObject::tr("Persist wallet files and configuration on the encrypted volume?"
|
||||
"\n\n"
|
||||
"In addition, you can enable Tails dotfiles persistence "
|
||||
"to gain a start menu entry.\n"));
|
||||
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png"));
|
||||
TailsOS::usePersistence = (msgBox.exec() == QMessageBox::Yes);
|
||||
}
|
||||
|
||||
void TailsOS::persistXdgMime(QString filePath, QString data)
|
||||
{
|
||||
QFileInfo file(filePath);
|
||||
QString tailsPath = tailsPathData + "dotfiles/.local/share/applications/";
|
||||
|
||||
// write to persistent volume
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "Writing xdg mime: " << tailsPath + file.fileName();
|
||||
#endif
|
||||
|
||||
QDir().mkpath(tailsPath); // ensure directory exists
|
||||
fileWrite(tailsPath + file.fileName(), data);
|
||||
|
||||
// write to current session
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "Writing xdg mime: " << file.filePath();
|
||||
#endif
|
||||
|
||||
QDir().mkpath(file.path()); // ensure directory exists
|
||||
fileWrite(file.filePath(), data);
|
||||
}
|
23
src/qt/TailsOS.h
Normal file
23
src/qt/TailsOS.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef TAILSOS_H
|
||||
#define TAILSOS_H
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
|
||||
class TailsOS
|
||||
{
|
||||
public:
|
||||
TailsOS();
|
||||
static bool detect();
|
||||
static bool detectDataPersistence();
|
||||
static bool detectDotPersistence();
|
||||
|
||||
static void showDataPersistenceDisabledWarning();
|
||||
static void askPersistence();
|
||||
static void persistXdgMime(QString filePath, QString data);
|
||||
|
||||
static bool usePersistence;
|
||||
static QString tailsPathData;
|
||||
};
|
||||
|
||||
#endif // TAILSOS_H
|
@ -99,8 +99,8 @@ bool IPC::saveCommand(QString cmdString){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPC::saveCommand(const QUrl &url){;
|
||||
this->saveCommand(url.toString());
|
||||
bool IPC::saveCommand(const QUrl &url){
|
||||
return this->saveCommand(url.toString());
|
||||
}
|
||||
|
||||
void IPC::handleConnection(){
|
||||
|
@ -26,11 +26,15 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef MIME_H
|
||||
#define MIME_H
|
||||
#ifndef MACOSHELPER_H
|
||||
#define MACOSHELPER_H
|
||||
|
||||
#include <QApplication>
|
||||
class MacOSHelper
|
||||
{
|
||||
MacOSHelper() {}
|
||||
|
||||
void registerXdgMime(QApplication &app);
|
||||
public:
|
||||
static bool isCapsLock();
|
||||
};
|
||||
|
||||
#endif // MIME_H
|
||||
#endif //MACOSHELPER_H
|
@ -27,44 +27,23 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <QtCore>
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QtGui>
|
||||
#include <QtMac>
|
||||
#include "macoshelper.h"
|
||||
|
||||
#include "mime.h"
|
||||
#include "utils.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Availability.h>
|
||||
|
||||
void registerXdgMime(QApplication &app){
|
||||
// MacOS handled via Info.plist
|
||||
// Windows handled in the installer by rbrunner7
|
||||
|
||||
QString xdg = QString(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Monero GUI\n"
|
||||
"GenericName=Monero-GUI\n"
|
||||
"X-GNOME-FullName=Monero-GUI\n"
|
||||
"Comment=Monero GUI\n"
|
||||
"Keywords=Monero;\n"
|
||||
"Exec=%1 %u\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"Icon=monero\n"
|
||||
"Categories=Network;GNOME;Qt;\n"
|
||||
"MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n"
|
||||
"StartupNotify=true\n"
|
||||
"X-GNOME-Bugzilla-Bugzilla=GNOME\n"
|
||||
"X-GNOME-UsesNotifications=true\n"
|
||||
).arg(app.applicationFilePath());
|
||||
|
||||
QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||
QString filePath = QString("%1/monero-gui.desktop").arg(appPath);
|
||||
|
||||
qDebug() << QString("Writing %1").arg(filePath);
|
||||
QFile file(filePath);
|
||||
if(file.open(QIODevice::WriteOnly)){
|
||||
QTextStream out(&file); out << xdg << endl;
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
file.close();
|
||||
bool MacOSHelper::isCapsLock()
|
||||
{
|
||||
#ifdef __MAC_10_12
|
||||
NSUInteger flags = [NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
return (flags == NSEventModifierFlagCapsLock);
|
||||
#else
|
||||
NSUInteger flags = [NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
|
||||
return (flags & NSAlphaShiftKeyMask);
|
||||
#endif
|
||||
}
|
@ -27,15 +27,35 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <QtCore>
|
||||
#include <QApplication>
|
||||
|
||||
#include "src/qt/TailsOS.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool fileExists(QString path) {
|
||||
QFileInfo check_file(path);
|
||||
if (check_file.exists() && check_file.isFile())
|
||||
return check_file.exists() && check_file.isFile();
|
||||
}
|
||||
|
||||
QByteArray fileOpen(QString path) {
|
||||
QFile file(path);
|
||||
if(!file.open(QFile::ReadOnly | QFile::Text))
|
||||
return QByteArray();
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
return data;
|
||||
}
|
||||
|
||||
bool fileWrite(QString path, QString data) {
|
||||
QFile file(path);
|
||||
if(file.open(QIODevice::WriteOnly)){
|
||||
QTextStream out(&file); out << data << endl;
|
||||
file.close();
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString getAccountName(){
|
||||
@ -47,6 +67,53 @@ QString getAccountName(){
|
||||
return accountName;
|
||||
}
|
||||
|
||||
QString xdgMime(QApplication &app){
|
||||
return QString(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Monero GUI\n"
|
||||
"GenericName=Monero-GUI\n"
|
||||
"X-GNOME-FullName=Monero-GUI\n"
|
||||
"Comment=Monero GUI\n"
|
||||
"Keywords=Monero;\n"
|
||||
"Exec=%1 %u\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"Icon=monero\n"
|
||||
"Categories=Network;GNOME;Qt;\n"
|
||||
"MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n"
|
||||
"StartupNotify=true\n"
|
||||
"X-GNOME-Bugzilla-Bugzilla=GNOME\n"
|
||||
"X-GNOME-UsesNotifications=true\n"
|
||||
).arg(app.applicationFilePath());
|
||||
}
|
||||
|
||||
void registerXdgMime(QApplication &app){
|
||||
#ifdef Q_OS_LINUX
|
||||
// Register desktop entry
|
||||
// - MacOS handled via Info.plist
|
||||
// - Windows handled in the installer by rbrunner7
|
||||
// - Linux written to `QStandardPaths::ApplicationsLocation`
|
||||
// - Tails written to persistent dotfiles
|
||||
QString mime = xdgMime(app);
|
||||
QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||
QString filePath = QString("%1/monero-gui.desktop").arg(appPath);
|
||||
|
||||
if (TailsOS::detect() && TailsOS::detectDotPersistence() && TailsOS::usePersistence) {
|
||||
TailsOS::persistXdgMime(filePath, mime);
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo file(filePath);
|
||||
QDir().mkpath(file.path()); // ensure directory exists
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "Writing xdg mime: " << filePath;
|
||||
#endif
|
||||
|
||||
fileWrite(filePath, mime);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString randomUserAgent(){
|
||||
QStringList urand;
|
||||
urand << "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"
|
||||
|
@ -31,9 +31,14 @@
|
||||
|
||||
#include <QtCore>
|
||||
#include <QRegExp>
|
||||
#include <QApplication>
|
||||
|
||||
bool fileExists(QString path);
|
||||
QByteArray fileOpen(QString path);
|
||||
bool fileWrite(QString path, QString data);
|
||||
QString getAccountName();
|
||||
QString xdgMime(QApplication &app);
|
||||
void registerXdgMime(QApplication &app);
|
||||
const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)");
|
||||
QString randomUserAgent();
|
||||
|
||||
|
@ -498,15 +498,15 @@ Rectangle {
|
||||
splash.close()
|
||||
|
||||
console.log(">>> wallet passphrase needed: ");
|
||||
passphraseDialog.onAcceptedCallback = function() {
|
||||
walletManager.onPassphraseEntered(passphraseDialog.passphrase);
|
||||
passwordDialog.onAcceptedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered(passwordDialog.password);
|
||||
creatingWalletDeviceSplash();
|
||||
}
|
||||
passphraseDialog.onRejectedCallback = function() {
|
||||
passwordDialog.onRejectedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered("", true);
|
||||
creatingWalletDeviceSplash();
|
||||
}
|
||||
passphraseDialog.open()
|
||||
passwordDialog.openPassphraseDialog()
|
||||
}
|
||||
|
||||
function onDeviceButtonRequest(code){
|
||||
|
@ -151,7 +151,7 @@ Rectangle {
|
||||
labelFontSize: 14
|
||||
copyButton: false
|
||||
readOnly: true
|
||||
text: Utils.roundDownToNearestThousand(wizardController.m_wallet.walletCreationHeight)
|
||||
text: Utils.roundDownToNearestThousand(wizardController.m_wallet ? wizardController.m_wallet.walletCreationHeight : 0)
|
||||
}
|
||||
|
||||
MoneroComponents.WarningBox {
|
||||
|
@ -57,12 +57,6 @@ ColumnLayout {
|
||||
value: wizardController.language_language
|
||||
}
|
||||
|
||||
WizardSummaryItem {
|
||||
Layout.fillWidth: true
|
||||
header: qsTr("Wallet name") + translationManager.emptyString
|
||||
value: walletOptionsName
|
||||
}
|
||||
|
||||
WizardSummaryItem {
|
||||
Layout.fillWidth: true
|
||||
header: qsTr("Restore height") + translationManager.emptyString
|
||||
|
Loading…
Reference in New Issue
Block a user