Subaddresses minimal version: supports default account only

This commit is contained in:
kenshi84 2017-07-04 12:34:09 +09:00
parent 3b069ec049
commit cee0474e37
No known key found for this signature in database
GPG key ID: 085D092F1F43D51A
28 changed files with 934 additions and 56 deletions

View file

@ -112,7 +112,7 @@ Rectangle {
}, State { }, State {
name: "Receive" name: "Receive"
PropertyChanges { target: root; currentView: receiveView } PropertyChanges { target: root; currentView: receiveView }
PropertyChanges { target: mainFlickable; contentHeight: minHeight } PropertyChanges { target: mainFlickable; contentHeight: 1000 * scaleRatio }
}, State { }, State {
name: "TxKey" name: "TxKey"
PropertyChanges { target: root; currentView: txkeyView } PropertyChanges { target: root; currentView: txkeyView }

View file

@ -266,6 +266,45 @@ ListView {
} }
} }
Item { //separator
width: 100
height: 14
}
// -- "Received by" title
Text {
anchors.bottom: parent.bottom
font.family: "Arial"
font.pixelSize: 12
color: "#535353"
text: (isOut ? qsTr("Spent from:") : qsTr("Received by:")) + translationManager.emptyString
}
Item { //separator
width: 5
height: 14
}
// -- "Index" value
Text {
anchors.bottom: parent.bottom
font.family: "Arial"
font.pixelSize: 13
font.bold: true
color: "#545454"
text: "#" + subaddrIndex
}
Item { //separator
width: 5
height: 14
}
// -- "Label" value
Text {
anchors.bottom: parent.bottom
font.family: "Arial"
font.pixelSize: 13
color: "#545454"
text: label
elide: Text.ElideRight
width: detailsButton.x - x - 30
}
} }
// -- "Date", "Balance" and "Amound" section // -- "Date", "Balance" and "Amound" section

View file

@ -30,6 +30,7 @@
import QtQuick 2.0 import QtQuick 2.0
Item { Item {
property alias image : buttonImage
property alias imageSource : buttonImage.source property alias imageSource : buttonImage.source
signal clicked(var mouse) signal clicked(var mouse)
@ -53,7 +54,8 @@ Item {
MouseArea { MouseArea {
id: buttonArea id: buttonArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
buttonImage.x = buttonImage.x + 2 buttonImage.x = buttonImage.x + 2

181
components/InputDialog.qml Normal file
View file

@ -0,0 +1,181 @@
// Copyright (c) 2014-2018, 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.0
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import "../components" as MoneroComponents
Item {
id: root
visible: false
Rectangle {
id: bg
z: parent.z + 1
anchors.fill: parent
color: "white"
opacity: 0.9
}
property alias labelText: label.text
property alias inputText: input.text
// same signals as Dialog has
signal accepted()
signal rejected()
function open() {
leftPanel.enabled = false
middlePanel.enabled = false
titleBar.enabled = false
show()
root.visible = true;
input.focus = true;
input.text = "";
}
function close() {
leftPanel.enabled = true
middlePanel.enabled = true
titleBar.enabled = true
root.visible = false;
}
ColumnLayout {
z: bg.z + 1
id: mainLayout
spacing: 10
anchors { fill: parent; margins: 35 }
ColumnLayout {
id: column
//anchors {fill: parent; margins: 16 }
Layout.alignment: Qt.AlignHCenter
Label {
id: label
Layout.alignment: Qt.AlignHCenter
// Layout.columnSpan: 2
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 18 * scaleRatio
font.family: "Arial"
color: "#555555"
}
TextField {
id : input
focus: true
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
font.family: "Arial"
font.pixelSize: 32 * scaleRatio
// echoMode: TextInput.Password
KeyNavigation.tab: okButton
style: TextFieldStyle {
renderType: Text.NativeRendering
textColor: "#35B05A"
// passwordCharacter: ""
// no background
background: Rectangle {
radius: 0
border.width: 0
}
}
Keys.onReturnPressed: {
root.close()
root.accepted()
}
Keys.onEscapePressed: {
root.close()
root.rejected()
}
}
// underline
Rectangle {
height: 1
color: "#DBDBDB"
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
anchors.bottomMargin: 3
}
// padding
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
height: 10
opacity: 0
color: "black"
}
}
// Ok/Cancel buttons
RowLayout {
id: buttons
spacing: 60
Layout.alignment: Qt.AlignHCenter
MoneroComponents.StandardButton {
id: cancelButton
width: 120
fontSize: 14
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
text: qsTr("Cancel") + translationManager.emptyString
KeyNavigation.tab: input
onClicked: {
root.close()
root.rejected()
}
}
MoneroComponents.StandardButton {
id: okButton
width: 120
fontSize: 14
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
text: qsTr("Ok")
KeyNavigation.tab: cancelButton
onClicked: {
root.close()
root.accepted()
}
}
}
}
}

View file

@ -37,6 +37,7 @@ Item {
property alias cursorPosition: input.cursorPosition property alias cursorPosition: input.cursorPosition
property alias echoMode: input.echoMode property alias echoMode: input.echoMode
property int fontSize: 18 * scaleRatio property int fontSize: 18 * scaleRatio
property bool showBorder: true
property bool error: false property bool error: false
signal editingFinished() signal editingFinished()
signal accepted(); signal accepted();
@ -52,6 +53,7 @@ Item {
} }
Rectangle { Rectangle {
visible: showBorder
anchors.fill: parent anchors.fill: parent
anchors.bottomMargin: 1 * scaleRatio anchors.bottomMargin: 1 * scaleRatio
color: "#DBDBDB" color: "#DBDBDB"

View file

@ -0,0 +1,109 @@
// Copyright (c) 2014-2018, 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.0
import moneroComponents.Clipboard 1.0
ListView {
id: listView
clip: true
boundsBehavior: ListView.StopAtBounds
highlightMoveDuration: 0
delegate: Rectangle {
id: delegate
height: 64
width: listView.width
LineEdit {
id: addressLine
fontSize: 12
readOnly: true
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 5
onTextChanged: cursorPosition = 0
text: address
showBorder: false
IconButton {
id: clipboardButton
imageSource: "../images/copyToClipboard.png"
onClicked: {
console.log(addressLine.text + " copied to clipboard");
clipboard.setText(addressLine.text);
appWindow.showStatusMessage(qsTr("Address copied to clipboard"),3);
}
}
}
Text {
id: indexText
anchors.top: addressLine.bottom
anchors.left: parent.left
anchors.leftMargin: 20
font.family: "Arial"
font.bold: true
font.pixelSize: 12
color: "#444444"
text: "#" + index
}
Text {
id: labelText
anchors.top: addressLine.bottom
anchors.left: indexText.right
anchors.right: parent.right
anchors.leftMargin: 10
font.family: "Arial"
font.bold: true
font.pixelSize: 12
color: "#444444"
text: label
}
MouseArea {
z: 5
anchors.top: parent.top
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: clipboardButton.width
onClicked: listView.currentIndex = index
}
}
highlight: Rectangle {
height: 64
color: '#FF4304'
opacity: 0.2
z: 2
}
}

View file

@ -50,6 +50,8 @@
#include "model/TransactionHistorySortFilterModel.h" #include "model/TransactionHistorySortFilterModel.h"
#include "AddressBook.h" #include "AddressBook.h"
#include "model/AddressBookModel.h" #include "model/AddressBookModel.h"
#include "Subaddress.h"
#include "model/SubaddressModel.h"
#include "wallet/api/wallet2_api.h" #include "wallet/api/wallet2_api.h"
#include "MainApp.h" #include "MainApp.h"
@ -133,6 +135,12 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<AddressBook>("moneroComponents.AddressBook", 1, 0, "AddressBook", qmlRegisterUncreatableType<AddressBook>("moneroComponents.AddressBook", 1, 0, "AddressBook",
"AddressBook can't be instantiated directly"); "AddressBook can't be instantiated directly");
qmlRegisterUncreatableType<SubaddressModel>("moneroComponents.SubaddressModel", 1, 0, "SubaddressModel",
"SubaddressModel can't be instantiated directly");
qmlRegisterUncreatableType<Subaddress>("moneroComponents.Subaddress", 1, 0, "Subaddress",
"Subaddress can't be instantiated directly");
qRegisterMetaType<PendingTransaction::Priority>(); qRegisterMetaType<PendingTransaction::Priority>();
qRegisterMetaType<TransactionInfo::Direction>(); qRegisterMetaType<TransactionInfo::Direction>();
qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>(); qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>();

View file

@ -310,8 +310,10 @@ ApplicationWindow {
} }
function updateBalance() { function updateBalance() {
middlePanel.unlockedBalanceText = leftPanel.unlockedBalanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.unlockedBalance); if (!currentWallet)
middlePanel.balanceText = leftPanel.balanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.balance); return;
middlePanel.unlockedBalanceText = leftPanel.unlockedBalanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.unlockedBalance(currentWallet.currentSubaddressAccount));
middlePanel.balanceText = leftPanel.balanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.balance(currentWallet.currentSubaddressAccount));
} }
function onWalletConnectionStatusChanged(status){ function onWalletConnectionStatusChanged(status){
@ -329,7 +331,7 @@ ApplicationWindow {
} }
// initialize transaction history once wallet is initialized first time; // initialize transaction history once wallet is initialized first time;
if (!walletInitialized) { if (!walletInitialized) {
currentWallet.history.refresh() currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
walletInitialized = true walletInitialized = true
} }
} }
@ -377,7 +379,7 @@ ApplicationWindow {
if(foundNewBlock) { if(foundNewBlock) {
foundNewBlock = false; foundNewBlock = false;
console.log("New block found - updating history") console.log("New block found - updating history")
currentWallet.history.refresh() currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
timeToUnlock = currentWallet.history.minutesToUnlock timeToUnlock = currentWallet.history.minutesToUnlock
leftPanel.minutesToUnlockTxt = (timeToUnlock > 0)? (timeToUnlock == 20)? qsTr("Unlocked balance (waiting for block)") : qsTr("Unlocked balance (~%1 min)").arg(timeToUnlock) : qsTr("Unlocked balance"); leftPanel.minutesToUnlockTxt = (timeToUnlock > 0)? (timeToUnlock == 20)? qsTr("Unlocked balance (waiting for block)") : qsTr("Unlocked balance (~%1 min)").arg(timeToUnlock) : qsTr("Unlocked balance");
} }
@ -453,6 +455,9 @@ ApplicationWindow {
console.log("Saving wallet after first refresh"); console.log("Saving wallet after first refresh");
currentWallet.store() currentWallet.store()
isNewWallet = false isNewWallet = false
// Update History
currentWallet.history.refresh(currentWallet.currentSubaddressAccount);
} }
// recovering from seed is finished after first refresh // recovering from seed is finished after first refresh
@ -463,7 +468,7 @@ ApplicationWindow {
// Update history on every refresh if it's empty // Update history on every refresh if it's empty
if(currentWallet.history.count == 0) if(currentWallet.history.count == 0)
currentWallet.history.refresh() currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
onWalletUpdate(); onWalletUpdate();
} }
@ -526,19 +531,21 @@ ApplicationWindow {
currentWallet.refresh() currentWallet.refresh()
console.log("Confirmed money found") console.log("Confirmed money found")
// history refresh is handled by walletUpdated // history refresh is handled by walletUpdated
currentWallet.history.refresh(currentWallet.currentSubaddressAccount) // this will refresh model
currentWallet.subaddress.refresh(currentWallet.currentSubaddressAccount)
} }
function onWalletUnconfirmedMoneyReceived(txId, amount) { function onWalletUnconfirmedMoneyReceived(txId, amount) {
// refresh history // refresh history
console.log("unconfirmed money found") console.log("unconfirmed money found")
currentWallet.history.refresh() currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
} }
function onWalletMoneySent(txId, amount) { function onWalletMoneySent(txId, amount) {
// refresh transaction history here // refresh transaction history here
console.log("money sent found") console.log("money sent found")
currentWallet.refresh() currentWallet.refresh()
currentWallet.history.refresh() // this will refresh model currentWallet.history.refresh(currentWallet.currentSubaddressAccount) // this will refresh model
} }
function walletsFound() { function walletsFound() {
@ -584,8 +591,11 @@ ApplicationWindow {
// here we show confirmation popup; // here we show confirmation popup;
transactionConfirmationPopup.title = qsTr("Confirmation") + translationManager.emptyString transactionConfirmationPopup.title = qsTr("Confirmation") + translationManager.emptyString
transactionConfirmationPopup.text = qsTr("Please confirm transaction:\n") transactionConfirmationPopup.text = qsTr("Please confirm transaction:\n");
+ (address === "" ? "" : (qsTr("\nAddress: ") + address)) for (var i = 0; i < transaction.subaddrIndices.length; ++i)
transactionConfirmationPopup.text += qsTr("\nSpending address index: ") + transaction.subaddrIndices[i]
transactionConfirmationPopup.text +=
(address === "" ? "" : (qsTr("\n\nAddress: ") + address))
+ (paymentId === "" ? "" : (qsTr("\nPayment ID: ") + paymentId)) + (paymentId === "" ? "" : (qsTr("\nPayment ID: ") + paymentId))
+ qsTr("\n\nAmount: ") + walletManager.displayAmount(transaction.amount) + qsTr("\n\nAmount: ") + walletManager.displayAmount(transaction.amount)
+ qsTr("\nFee: ") + walletManager.displayAmount(transaction.fee) + qsTr("\nFee: ") + walletManager.displayAmount(transaction.fee)
@ -1185,6 +1195,23 @@ ApplicationWindow {
} }
} }
InputDialog {
id: inputDialog
visible: false
z: parent.z + 1
anchors.fill: parent
property var onAcceptedCallback
property var onRejectedCallback
onAccepted: {
if (onAcceptedCallback)
onAcceptedCallback()
}
onRejected: {
if (onRejectedCallback)
onRejectedCallback()
}
}
DaemonManagerDialog { DaemonManagerDialog {
id: daemonManagerDialog id: daemonManagerDialog
onRejected: { onRejected: {

View file

@ -35,6 +35,8 @@ HEADERS += \
src/QR-Code-generator/QrSegment.hpp \ src/QR-Code-generator/QrSegment.hpp \
src/model/AddressBookModel.h \ src/model/AddressBookModel.h \
src/libwalletqt/AddressBook.h \ src/libwalletqt/AddressBook.h \
src/model/SubaddressModel.h \
src/libwalletqt/Subaddress.h \
src/zxcvbn-c/zxcvbn.h \ src/zxcvbn-c/zxcvbn.h \
src/libwalletqt/UnsignedTransaction.h \ src/libwalletqt/UnsignedTransaction.h \
MainApp.h MainApp.h
@ -58,6 +60,8 @@ SOURCES += main.cpp \
src/QR-Code-generator/QrSegment.cpp \ src/QR-Code-generator/QrSegment.cpp \
src/model/AddressBookModel.cpp \ src/model/AddressBookModel.cpp \
src/libwalletqt/AddressBook.cpp \ src/libwalletqt/AddressBook.cpp \
src/model/SubaddressModel.cpp \
src/libwalletqt/Subaddress.cpp \
src/zxcvbn-c/zxcvbn.c \ src/zxcvbn-c/zxcvbn.c \
src/libwalletqt/UnsignedTransaction.cpp \ src/libwalletqt/UnsignedTransaction.cpp \
MainApp.cpp MainApp.cpp

View file

@ -546,7 +546,7 @@ Rectangle {
function onPageCompleted() { function onPageCompleted() {
if(currentWallet != null && typeof currentWallet.history !== "undefined" ) { if(currentWallet != null && typeof currentWallet.history !== "undefined" ) {
currentWallet.history.refresh() currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
table.addressBookModel = currentWallet ? currentWallet.addressBookModel : null table.addressBookModel = currentWallet ? currentWallet.addressBookModel : null
transactionTypeDropdown.update() transactionTypeDropdown.update()
} }

View file

@ -151,7 +151,7 @@ Rectangle {
releasedColor: "#FF6C3C" releasedColor: "#FF6C3C"
pressedColor: "#FF4304" pressedColor: "#FF4304"
onClicked: { onClicked: {
var success = walletManager.startMining(appWindow.currentWallet.address, soloMinerThreadsLine.text, persistentSettings.allow_background_mining, persistentSettings.miningIgnoreBattery) var success = walletManager.startMining(appWindow.currentWallet.address(0, 0), soloMinerThreadsLine.text, persistentSettings.allow_background_mining, persistentSettings.miningIgnoreBattery)
if (success) { if (success) {
update() update()
} else { } else {

View file

@ -38,14 +38,17 @@ import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0 import moneroComponents.WalletManager 1.0
import moneroComponents.TransactionHistory 1.0 import moneroComponents.TransactionHistory 1.0
import moneroComponents.TransactionHistoryModel 1.0 import moneroComponents.TransactionHistoryModel 1.0
import moneroComponents.Subaddress 1.0
import moneroComponents.SubaddressModel 1.0
Rectangle { Rectangle {
id: pageReceive
color: "#F0EEEE" color: "#F0EEEE"
property alias addressText : addressLine.text property var model
property var current_address
property alias addressText : pageReceive.current_address
property alias paymentIdText : paymentIdLine.text property alias paymentIdText : paymentIdLine.text
property alias integratedAddressText : integratedAddressLine.text property alias integratedAddressText : integratedAddressLine.text
property var model
property string trackingLineText: "" property string trackingLineText: ""
function updatePaymentId(payment_id) { function updatePaymentId(payment_id) {
@ -80,7 +83,7 @@ Rectangle {
function makeQRCodeString() { function makeQRCodeString() {
var s = "monero:" var s = "monero:"
var nfields = 0 var nfields = 0
s += addressLine.text s += current_address;
var amount = amountLine.text.trim() var amount = amountLine.text.trim()
if (amount !== "") { if (amount !== "") {
s += (nfields++ ? "&" : "?") s += (nfields++ ? "&" : "?")
@ -192,27 +195,66 @@ Rectangle {
id: addressRow id: addressRow
Label { Label {
id: addressLabel id: addressLabel
text: qsTr("Address") + translationManager.emptyString text: qsTr("Addresses") + translationManager.emptyString
width: mainLayout.labelWidth width: mainLayout.labelWidth
} }
LineEdit { Rectangle {
id: addressLine id: tableRect
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("ReadOnly wallet address displayed here") + translationManager.emptyString;
readOnly: true
width: mainLayout.editWidth
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: cursorPosition = 0 Layout.preferredHeight: 200
color: "#FFFFFF"
IconButton { Scroll {
imageSource: "../images/copyToClipboard.png" id: flickableScroll
onClicked: { anchors.right: table.right
if (addressLine.text.length > 0) { anchors.top: table.top
console.log(addressLine.text + " copied to clipboard") anchors.bottom: table.bottom
clipboard.setText(addressLine.text) flickable: table
appWindow.showStatusMessage(qsTr("Address copied to clipboard"),3)
} }
SubaddressTable {
id: table
anchors.fill: parent
onContentYChanged: flickableScroll.flickableContentYChanged()
onCurrentItemChanged: {
current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, table.currentIndex);
}
}
}
RowLayout {
spacing: 20
StandardButton {
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
text: qsTr("Create new address") + translationManager.emptyString;
onClicked: {
inputDialog.labelText = qsTr("Set the label of the new address:") + translationManager.emptyString
inputDialog.inputText = qsTr("(Untitled)")
inputDialog.onAcceptedCallback = function() {
appWindow.currentWallet.subaddress.addRow(appWindow.currentWallet.currentSubaddressAccount, inputDialog.inputText)
table.currentIndex = appWindow.currentWallet.numSubaddresses() - 1
}
inputDialog.onRejectedCallback = null;
inputDialog.open()
}
}
StandardButton {
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
enabled: table.currentIndex > 0
text: qsTr("Rename") + translationManager.emptyString;
onClicked: {
inputDialog.labelText = qsTr("Set the label of the selected address:") + translationManager.emptyString
inputDialog.inputText = appWindow.currentWallet.getSubaddressLabel(appWindow.currentWallet.currentSubaddressAccount, table.currentIndex)
inputDialog.onAcceptedCallback = function() {
appWindow.currentWallet.subaddress.setLabel(appWindow.currentWallet.currentSubaddressAccount, table.currentIndex, inputDialog.inputText)
}
inputDialog.onRejectedCallback = null;
inputDialog.open()
} }
} }
} }
@ -436,11 +478,12 @@ Rectangle {
function onPageCompleted() { function onPageCompleted() {
console.log("Receive page loaded"); console.log("Receive page loaded");
table.model = currentWallet.subaddressModel;
if (appWindow.currentWallet) { if (appWindow.currentWallet) {
if (addressLine.text.length === 0 || addressLine.text !== appWindow.currentWallet.address) { current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
addressLine.text = appWindow.currentWallet.address appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount)
} table.currentIndex = 0
} }
update() update()

View file

@ -51,6 +51,7 @@
<file>tabs/TweetsModel.qml</file> <file>tabs/TweetsModel.qml</file>
<file>components/Scroll.qml</file> <file>components/Scroll.qml</file>
<file>components/AddressBookTable.qml</file> <file>components/AddressBookTable.qml</file>
<file>components/SubaddressTable.qml</file>
<file>images/deleteIcon.png</file> <file>images/deleteIcon.png</file>
<file>images/moneroIcon.png</file> <file>images/moneroIcon.png</file>
<file>components/StandardDropdown.qml</file> <file>components/StandardDropdown.qml</file>
@ -136,6 +137,7 @@
<file>components/IconButton.qml</file> <file>components/IconButton.qml</file>
<file>components/PasswordDialog.qml</file> <file>components/PasswordDialog.qml</file>
<file>components/NewPasswordDialog.qml</file> <file>components/NewPasswordDialog.qml</file>
<file>components/InputDialog.qml</file>
<file>components/ProcessingSplash.qml</file> <file>components/ProcessingSplash.qml</file>
<file>components/ProgressBar.qml</file> <file>components/ProgressBar.qml</file>
<file>components/StandardDialog.qml</file> <file>components/StandardDialog.qml</file>

View file

@ -50,6 +50,16 @@ quint64 PendingTransaction::txCount() const
return m_pimpl->txCount(); return m_pimpl->txCount();
} }
QList<QVariant> PendingTransaction::subaddrIndices() const
{
std::vector<std::set<uint32_t>> subaddrIndices = m_pimpl->subaddrIndices();
QList<QVariant> result;
for (const auto& x : subaddrIndices)
for (uint32_t i : x)
result.push_back(i);
return result;
}
void PendingTransaction::setFilename(const QString &fileName) void PendingTransaction::setFilename(const QString &fileName)
{ {
m_fileName = fileName; m_fileName = fileName;

View file

@ -2,6 +2,8 @@
#define PENDINGTRANSACTION_H #define PENDINGTRANSACTION_H
#include <QObject> #include <QObject>
#include <QList>
#include <QVariant>
#include <wallet/api/wallet2_api.h> #include <wallet/api/wallet2_api.h>
@ -19,6 +21,7 @@ class PendingTransaction : public QObject
Q_PROPERTY(quint64 fee READ fee) Q_PROPERTY(quint64 fee READ fee)
Q_PROPERTY(QStringList txid READ txid) Q_PROPERTY(QStringList txid READ txid)
Q_PROPERTY(quint64 txCount READ txCount) Q_PROPERTY(quint64 txCount READ txCount)
Q_PROPERTY(QList<QVariant> subaddrIndices READ subaddrIndices)
public: public:
enum Status { enum Status {
@ -44,6 +47,7 @@ public:
quint64 fee() const; quint64 fee() const;
QStringList txid() const; QStringList txid() const;
quint64 txCount() const; quint64 txCount() const;
QList<QVariant> subaddrIndices() const;
Q_INVOKABLE void setFilename(const QString &fileName); Q_INVOKABLE void setFilename(const QString &fileName);
private: private:

View file

@ -0,0 +1,84 @@
// Copyright (c) 2018, 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 "Subaddress.h"
#include <QDebug>
Subaddress::Subaddress(Monero::Subaddress *subaddressImpl, QObject *parent)
: QObject(parent), m_subaddressImpl(subaddressImpl)
{
qDebug(__FUNCTION__);
getAll();
}
QList<Monero::SubaddressRow*> Subaddress::getAll(bool update) const
{
qDebug(__FUNCTION__);
emit refreshStarted();
if(update)
m_rows.clear();
if (m_rows.empty()){
for (auto &row: m_subaddressImpl->getAll()) {
m_rows.append(row);
}
}
emit refreshFinished();
return m_rows;
}
Monero::SubaddressRow * Subaddress::getRow(int index) const
{
return m_rows.at(index);
}
void Subaddress::addRow(quint32 accountIndex, const QString &label) const
{
m_subaddressImpl->addRow(accountIndex, label.toStdString());
getAll(true);
}
void Subaddress::setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const
{
m_subaddressImpl->setLabel(accountIndex, addressIndex, label.toStdString());
getAll(true);
}
void Subaddress::refresh(quint32 accountIndex) const
{
m_subaddressImpl->refresh(accountIndex);
getAll(true);
}
quint64 Subaddress::count() const
{
return m_rows.size();
}

View file

@ -0,0 +1,61 @@
// Copyright (c) 2018, 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 SUBADDRESS_H
#define SUBADDRESS_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <QList>
#include <QDateTime>
class Subaddress : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QList<Monero::SubaddressRow*> getAll(bool update = false) const;
Q_INVOKABLE Monero::SubaddressRow * getRow(int index) const;
Q_INVOKABLE void addRow(quint32 accountIndex, const QString &label) const;
Q_INVOKABLE void setLabel(quint32 accountIndex, quint32 addressIndex, const QString &label) const;
Q_INVOKABLE void refresh(quint32 accountIndex) const;
quint64 count() const;
signals:
void refreshStarted() const;
void refreshFinished() const;
public slots:
private:
explicit Subaddress(Monero::Subaddress * subaddressImpl, QObject *parent);
friend class Wallet;
Monero::Subaddress * m_subaddressImpl;
mutable QList<Monero::SubaddressRow*> m_rows;
};
#endif // SUBADDRESS_H

View file

@ -22,7 +22,7 @@ TransactionInfo *TransactionHistory::transaction(int index)
// return nullptr; // return nullptr;
//} //}
QList<TransactionInfo *> TransactionHistory::getAll() const QList<TransactionInfo *> TransactionHistory::getAll(quint32 accountIndex) const
{ {
// XXX this invalidates previously saved history that might be used by model // XXX this invalidates previously saved history that might be used by model
emit refreshStarted(); emit refreshStarted();
@ -37,6 +37,10 @@ QList<TransactionInfo *> TransactionHistory::getAll() const
TransactionHistory * parent = const_cast<TransactionHistory*>(this); TransactionHistory * parent = const_cast<TransactionHistory*>(this);
for (const auto i : m_pimpl->getAll()) { for (const auto i : m_pimpl->getAll()) {
TransactionInfo * ti = new TransactionInfo(i, parent); TransactionInfo * ti = new TransactionInfo(i, parent);
if (ti->subaddrAccount() != accountIndex) {
delete ti;
continue;
}
m_tinfo.append(ti); m_tinfo.append(ti);
// looking for transactions timestamp scope // looking for transactions timestamp scope
if (ti->timestamp() >= lastDateTime) { if (ti->timestamp() >= lastDateTime) {
@ -69,12 +73,12 @@ QList<TransactionInfo *> TransactionHistory::getAll() const
return m_tinfo; return m_tinfo;
} }
void TransactionHistory::refresh() void TransactionHistory::refresh(quint32 accountIndex)
{ {
// rebuilding transaction list in wallet_api; // rebuilding transaction list in wallet_api;
m_pimpl->refresh(); m_pimpl->refresh();
// copying list here and keep track on every item to avoid memleaks // copying list here and keep track on every item to avoid memleaks
getAll(); getAll(accountIndex);
} }
quint64 TransactionHistory::count() const quint64 TransactionHistory::count() const

View file

@ -23,8 +23,8 @@ class TransactionHistory : public QObject
public: public:
Q_INVOKABLE TransactionInfo *transaction(int index); Q_INVOKABLE TransactionInfo *transaction(int index);
// Q_INVOKABLE TransactionInfo * transaction(const QString &id); // Q_INVOKABLE TransactionInfo * transaction(const QString &id);
Q_INVOKABLE QList<TransactionInfo*> getAll() const; Q_INVOKABLE QList<TransactionInfo*> getAll(quint32 accountIndex) const;
Q_INVOKABLE void refresh(); Q_INVOKABLE void refresh(quint32 accountIndex);
quint64 count() const; quint64 count() const;
QDateTime firstDateTime() const; QDateTime firstDateTime() const;
QDateTime lastDateTime() const; QDateTime lastDateTime() const;

View file

@ -48,6 +48,24 @@ quint64 TransactionInfo::blockHeight() const
return m_pimpl->blockHeight(); return m_pimpl->blockHeight();
} }
QSet<quint32> TransactionInfo::subaddrIndex() const
{
QSet<quint32> result;
for (uint32_t i : m_pimpl->subaddrIndex())
result.insert(i);
return result;
}
quint32 TransactionInfo::subaddrAccount() const
{
return m_pimpl->subaddrAccount();
}
QString TransactionInfo::label() const
{
return QString::fromStdString(m_pimpl->label());
}
quint64 TransactionInfo::confirmations() const quint64 TransactionInfo::confirmations() const
{ {
return m_pimpl->confirmations(); return m_pimpl->confirmations();

View file

@ -4,6 +4,7 @@
#include <wallet/api/wallet2_api.h> #include <wallet/api/wallet2_api.h>
#include <QObject> #include <QObject>
#include <QDateTime> #include <QDateTime>
#include <QSet>
class Transfer; class Transfer;
@ -18,6 +19,9 @@ class TransactionInfo : public QObject
Q_PROPERTY(QString displayAmount READ displayAmount) Q_PROPERTY(QString displayAmount READ displayAmount)
Q_PROPERTY(QString fee READ fee) Q_PROPERTY(QString fee READ fee)
Q_PROPERTY(quint64 blockHeight READ blockHeight) Q_PROPERTY(quint64 blockHeight READ blockHeight)
Q_PROPERTY(QSet<quint32> subaddrIndex READ subaddrIndex)
Q_PROPERTY(quint32 subaddrAccount READ subaddrAccount)
Q_PROPERTY(QString label READ label)
Q_PROPERTY(quint64 confirmations READ confirmations) Q_PROPERTY(quint64 confirmations READ confirmations)
Q_PROPERTY(quint64 unlockTime READ unlockTime) Q_PROPERTY(quint64 unlockTime READ unlockTime)
Q_PROPERTY(QString hash READ hash) Q_PROPERTY(QString hash READ hash)
@ -44,6 +48,9 @@ public:
QString displayAmount() const; QString displayAmount() const;
QString fee() const; QString fee() const;
quint64 blockHeight() const; quint64 blockHeight() const;
QSet<quint32> subaddrIndex() const;
quint32 subaddrAccount() const;
QString label() const;
quint64 confirmations() const; quint64 confirmations() const;
quint64 unlockTime() const; quint64 unlockTime() const;
//! transaction_id //! transaction_id

View file

@ -3,9 +3,11 @@
#include "UnsignedTransaction.h" #include "UnsignedTransaction.h"
#include "TransactionHistory.h" #include "TransactionHistory.h"
#include "AddressBook.h" #include "AddressBook.h"
#include "Subaddress.h"
#include "model/TransactionHistoryModel.h" #include "model/TransactionHistoryModel.h"
#include "model/TransactionHistorySortFilterModel.h" #include "model/TransactionHistorySortFilterModel.h"
#include "model/AddressBookModel.h" #include "model/AddressBookModel.h"
#include "model/SubaddressModel.h"
#include "wallet/api/wallet2_api.h" #include "wallet/api/wallet2_api.h"
#include <QFile> #include <QFile>
@ -155,9 +157,9 @@ bool Wallet::setPassword(const QString &password)
return m_walletImpl->setPassword(password.toStdString()); return m_walletImpl->setPassword(password.toStdString());
} }
QString Wallet::address() const QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const
{ {
return QString::fromStdString(m_walletImpl->address()); return QString::fromStdString(m_walletImpl->address(accountIndex, addressIndex));
} }
QString Wallet::path() const QString Wallet::path() const
@ -241,14 +243,63 @@ bool Wallet::viewOnly() const
return m_walletImpl->watchOnly(); return m_walletImpl->watchOnly();
} }
quint64 Wallet::balance() const quint64 Wallet::balance(quint32 accountIndex) const
{ {
return m_walletImpl->balance(); return m_walletImpl->balance(accountIndex);
} }
quint64 Wallet::unlockedBalance() const quint64 Wallet::balanceAll() const
{ {
return m_walletImpl->unlockedBalance(); return m_walletImpl->balanceAll();
}
quint64 Wallet::unlockedBalance(quint32 accountIndex) const
{
return m_walletImpl->unlockedBalance(accountIndex);
}
quint64 Wallet::unlockedBalanceAll() const
{
return m_walletImpl->unlockedBalanceAll();
}
quint32 Wallet::currentSubaddressAccount() const
{
return m_currentSubaddressAccount;
}
void Wallet::switchSubaddressAccount(quint32 accountIndex)
{
if (accountIndex < numSubaddressAccounts())
{
m_currentSubaddressAccount = accountIndex;
m_subaddress->refresh(m_currentSubaddressAccount);
m_history->refresh(m_currentSubaddressAccount);
}
}
void Wallet::addSubaddressAccount(const QString& label)
{
m_walletImpl->addSubaddressAccount(label.toStdString());
switchSubaddressAccount(numSubaddressAccounts() - 1);
}
quint32 Wallet::numSubaddressAccounts() const
{
return m_walletImpl->numSubaddressAccounts();
}
quint32 Wallet::numSubaddresses(quint32 accountIndex) const
{
return m_walletImpl->numSubaddresses(accountIndex);
}
void Wallet::addSubaddress(const QString& label)
{
m_walletImpl->addSubaddress(currentSubaddressAccount(), label.toStdString());
}
QString Wallet::getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const
{
return QString::fromStdString(m_walletImpl->getSubaddressLabel(accountIndex, addressIndex));
}
void Wallet::setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label)
{
m_walletImpl->setSubaddressLabel(accountIndex, addressIndex, label.toStdString());
} }
quint64 Wallet::blockChainHeight() const quint64 Wallet::blockChainHeight() const
@ -288,7 +339,8 @@ quint64 Wallet::daemonBlockChainTargetHeight() const
bool Wallet::refresh() bool Wallet::refresh()
{ {
bool result = m_walletImpl->refresh(); bool result = m_walletImpl->refresh();
m_history->refresh(); m_history->refresh(currentSubaddressAccount());
m_subaddress->refresh(currentSubaddressAccount());
if (result) if (result)
emit updated(); emit updated();
return result; return result;
@ -324,9 +376,10 @@ PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QSt
quint64 amount, quint32 mixin_count, quint64 amount, quint32 mixin_count,
PendingTransaction::Priority priority) PendingTransaction::Priority priority)
{ {
std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction( Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count, dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority)); static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
PendingTransaction * result = new PendingTransaction(ptImpl,0); PendingTransaction * result = new PendingTransaction(ptImpl,0);
return result; return result;
} }
@ -351,9 +404,10 @@ void Wallet::createTransactionAsync(const QString &dst_addr, const QString &paym
PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id, PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id,
quint32 mixin_count, PendingTransaction::Priority priority) quint32 mixin_count, PendingTransaction::Priority priority)
{ {
std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction( Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
dst_addr.toStdString(), payment_id.toStdString(), Monero::optional<uint64_t>(), mixin_count, dst_addr.toStdString(), payment_id.toStdString(), Monero::optional<uint64_t>(), mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority)); static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
PendingTransaction * result = new PendingTransaction(ptImpl, this); PendingTransaction * result = new PendingTransaction(ptImpl, this);
return result; return result;
} }
@ -458,6 +512,18 @@ AddressBookModel *Wallet::addressBookModel() const
return m_addressBookModel; return m_addressBookModel;
} }
Subaddress *Wallet::subaddress()
{
return m_subaddress;
}
SubaddressModel *Wallet::subaddressModel()
{
if (!m_subaddressModel) {
m_subaddressModel = new SubaddressModel(this, m_subaddress);
}
return m_subaddressModel;
}
QString Wallet::generatePaymentId() const QString Wallet::generatePaymentId() const
{ {
@ -668,14 +734,18 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
, m_historyModel(nullptr) , m_historyModel(nullptr)
, m_addressBook(nullptr) , m_addressBook(nullptr)
, m_addressBookModel(nullptr) , m_addressBookModel(nullptr)
, m_subaddress(nullptr)
, m_subaddressModel(nullptr)
, m_daemonBlockChainHeight(0) , m_daemonBlockChainHeight(0)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS) , m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
, m_daemonBlockChainTargetHeight(0) , m_daemonBlockChainTargetHeight(0)
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS) , m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS) , m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
, m_currentSubaddressAccount(0)
{ {
m_history = new TransactionHistory(m_walletImpl->history(), this); m_history = new TransactionHistory(m_walletImpl->history(), this);
m_addressBook = new AddressBook(m_walletImpl->addressBook(), this); m_addressBook = new AddressBook(m_walletImpl->addressBook(), this);
m_subaddress = new Subaddress(m_walletImpl->subaddress(), this);
m_walletImpl->setListener(new WalletListenerImpl(this)); m_walletImpl->setListener(new WalletListenerImpl(this));
m_connectionStatus = Wallet::ConnectionStatus_Disconnected; m_connectionStatus = Wallet::ConnectionStatus_Disconnected;
// start cache timers // start cache timers
@ -696,6 +766,10 @@ Wallet::~Wallet()
delete m_history; delete m_history;
m_history = NULL; m_history = NULL;
delete m_addressBook;
m_addressBook = NULL;
delete m_subaddress;
m_subaddress = NULL;
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl); //Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
if(status() == Status_Critical) if(status() == Status_Critical)
qDebug("Not storing wallet cache"); qDebug("Not storing wallet cache");

View file

@ -20,6 +20,8 @@ class TransactionHistoryModel;
class TransactionHistorySortFilterModel; class TransactionHistorySortFilterModel;
class AddressBook; class AddressBook;
class AddressBookModel; class AddressBookModel;
class Subaddress;
class SubaddressModel;
class Wallet : public QObject class Wallet : public QObject
{ {
@ -29,17 +31,17 @@ class Wallet : public QObject
Q_PROPERTY(Status status READ status) Q_PROPERTY(Status status READ status)
Q_PROPERTY(bool testnet READ testnet) Q_PROPERTY(bool testnet READ testnet)
// Q_PROPERTY(ConnectionStatus connected READ connected) // Q_PROPERTY(ConnectionStatus connected READ connected)
Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount)
Q_PROPERTY(bool synchronized READ synchronized) Q_PROPERTY(bool synchronized READ synchronized)
Q_PROPERTY(QString errorString READ errorString) Q_PROPERTY(QString errorString READ errorString)
Q_PROPERTY(QString address READ address)
Q_PROPERTY(quint64 balance READ balance)
Q_PROPERTY(quint64 unlockedBalance READ unlockedBalance)
Q_PROPERTY(TransactionHistory * history READ history) Q_PROPERTY(TransactionHistory * history READ history)
Q_PROPERTY(QString paymentId READ paymentId WRITE setPaymentId) Q_PROPERTY(QString paymentId READ paymentId WRITE setPaymentId)
Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged) Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged)
Q_PROPERTY(QString path READ path) Q_PROPERTY(QString path READ path)
Q_PROPERTY(AddressBookModel * addressBookModel READ addressBookModel) Q_PROPERTY(AddressBookModel * addressBookModel READ addressBookModel)
Q_PROPERTY(AddressBook * addressBook READ addressBook) Q_PROPERTY(AddressBook * addressBook READ addressBook)
Q_PROPERTY(SubaddressModel * subaddressModel READ subaddressModel)
Q_PROPERTY(Subaddress * subaddress READ subaddress)
Q_PROPERTY(bool viewOnly READ viewOnly) Q_PROPERTY(bool viewOnly READ viewOnly)
Q_PROPERTY(QString secretViewKey READ getSecretViewKey) Q_PROPERTY(QString secretViewKey READ getSecretViewKey)
Q_PROPERTY(QString publicViewKey READ getPublicViewKey) Q_PROPERTY(QString publicViewKey READ getPublicViewKey)
@ -98,7 +100,7 @@ public:
Q_INVOKABLE bool setPassword(const QString &password); Q_INVOKABLE bool setPassword(const QString &password);
//! returns wallet's public address //! returns wallet's public address
QString address() const; Q_INVOKABLE QString address(quint32 accountIndex, quint32 addressIndex) const;
//! returns wallet file's path //! returns wallet file's path
QString path() const; QString path() const;
@ -126,10 +128,22 @@ public:
Q_INVOKABLE void setTrustedDaemon(bool arg); Q_INVOKABLE void setTrustedDaemon(bool arg);
//! returns balance //! returns balance
Q_INVOKABLE quint64 balance() const; Q_INVOKABLE quint64 balance(quint32 accountIndex) const;
Q_INVOKABLE quint64 balanceAll() const;
//! returns unlocked balance //! returns unlocked balance
Q_INVOKABLE quint64 unlockedBalance() const; Q_INVOKABLE quint64 unlockedBalance(quint32 accountIndex) const;
Q_INVOKABLE quint64 unlockedBalanceAll() const;
//! account/address management
quint32 currentSubaddressAccount() const;
Q_INVOKABLE void switchSubaddressAccount(quint32 accountIndex);
Q_INVOKABLE void addSubaddressAccount(const QString& label);
Q_INVOKABLE quint32 numSubaddressAccounts() const;
Q_INVOKABLE quint32 numSubaddresses(quint32 accountIndex) const;
Q_INVOKABLE void addSubaddress(const QString& label);
Q_INVOKABLE QString getSubaddressLabel(quint32 accountIndex, quint32 addressIndex) const;
Q_INVOKABLE void setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, const QString &label);
//! returns if view only wallet //! returns if view only wallet
Q_INVOKABLE bool viewOnly() const; Q_INVOKABLE bool viewOnly() const;
@ -209,6 +223,12 @@ public:
//! returns adress book model //! returns adress book model
AddressBookModel *addressBookModel() const; AddressBookModel *addressBookModel() const;
//! returns subaddress
Subaddress *subaddress();
//! returns subadress model
SubaddressModel *subaddressModel();
//! generate payment id //! generate payment id
Q_INVOKABLE QString generatePaymentId() const; Q_INVOKABLE QString generatePaymentId() const;
@ -302,8 +322,11 @@ private:
int m_connectionStatusTtl; int m_connectionStatusTtl;
mutable QTime m_connectionStatusTime; mutable QTime m_connectionStatusTime;
mutable bool m_initialized; mutable bool m_initialized;
uint32_t m_currentSubaddressAccount;
AddressBook * m_addressBook; AddressBook * m_addressBook;
mutable AddressBookModel * m_addressBookModel; mutable AddressBookModel * m_addressBookModel;
Subaddress * m_subaddress;
mutable SubaddressModel * m_subaddressModel;
QMutex m_connectionStatusMutex; QMutex m_connectionStatusMutex;
bool m_connectionStatusRunning; bool m_connectionStatusRunning;
QString m_daemonUsername; QString m_daemonUsername;

View file

@ -49,7 +49,7 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
__PRETTY_FUNCTION__, qPrintable(path), testnet); __PRETTY_FUNCTION__, qPrintable(path), testnet);
Monero::Wallet * w = m_pimpl->openWallet(path.toStdString(), password.toStdString(), testnet); Monero::Wallet * w = m_pimpl->openWallet(path.toStdString(), password.toStdString(), testnet);
qDebug("%s: opened wallet: %s, status: %d", __PRETTY_FUNCTION__, w->address().c_str(), w->status()); qDebug("%s: opened wallet: %s, status: %d", __PRETTY_FUNCTION__, w->address(0, 0).c_str(), w->status());
m_currentWallet = new Wallet(w); m_currentWallet = new Wallet(w);
// move wallet to the GUI thread. Otherwise it wont be emitting signals // move wallet to the GUI thread. Otherwise it wont be emitting signals
@ -110,7 +110,7 @@ QString WalletManager::closeWallet()
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QString result; QString result;
if (m_currentWallet) { if (m_currentWallet) {
result = m_currentWallet->address(); result = m_currentWallet->address(0, 0);
delete m_currentWallet; delete m_currentWallet;
} else { } else {
qCritical() << "Trying to close non existing wallet " << m_currentWallet; qCritical() << "Trying to close non existing wallet " << m_currentWallet;

View file

@ -0,0 +1,89 @@
// Copyright (c) 2018, 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 "SubaddressModel.h"
#include "Subaddress.h"
#include <QDebug>
#include <QHash>
#include <wallet/api/wallet2_api.h>
SubaddressModel::SubaddressModel(QObject *parent, Subaddress *subaddress)
: QAbstractListModel(parent), m_subaddress(subaddress)
{
qDebug(__FUNCTION__);
connect(m_subaddress,SIGNAL(refreshStarted()),this,SLOT(startReset()));
connect(m_subaddress,SIGNAL(refreshFinished()),this,SLOT(endReset()));
}
void SubaddressModel::startReset(){
qDebug(__FUNCTION__);
beginResetModel();
}
void SubaddressModel::endReset(){
qDebug(__FUNCTION__);
endResetModel();
}
int SubaddressModel::rowCount(const QModelIndex &parent) const
{
return m_subaddress->count();
}
QVariant SubaddressModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || (unsigned)index.row() >= m_subaddress->count())
return {};
Monero::SubaddressRow * sr = m_subaddress->getRow(index.row());
if (!sr)
return {};
QVariant result = "";
switch (role) {
case SubaddressAddressRole:
result = QString::fromStdString(sr->getAddress());
break;
case SubaddressLabelRole:
result = index.row() == 0 ? tr("Primary address") : QString::fromStdString(sr->getLabel());
break;
}
return result;
}
QHash<int, QByteArray> SubaddressModel::roleNames() const
{
static QHash<int, QByteArray> roleNames;
if (roleNames.empty())
{
roleNames.insert(SubaddressAddressRole, "address");
roleNames.insert(SubaddressLabelRole, "label");
}
return roleNames;
}

View file

@ -0,0 +1,62 @@
// Copyright (c) 2018, 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 SUBADDRESSMODEL_H
#define SUBADDRESSMODEL_H
#include <QAbstractListModel>
class Subaddress;
class SubaddressModel : public QAbstractListModel
{
Q_OBJECT
public:
enum SubaddressRowRole {
SubaddressRole = Qt::UserRole + 1, // for the SubaddressRow object;
SubaddressAddressRole,
SubaddressLabelRole,
};
Q_ENUM(SubaddressRowRole)
SubaddressModel(QObject *parent, Subaddress *subaddress);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
public slots:
void startReset();
void endReset();
private:
Subaddress *m_subaddress;
};
#endif // SUBADDRESSMODEL_H

View file

@ -83,6 +83,25 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
} }
break; break;
case TransactionSubaddrIndexRole:
{
QString str = QString{""};
bool first = true;
for (quint32 i : tInfo->subaddrIndex()) {
if (!first)
str += QString{","};
first = false;
str += QString::number(i);
}
result = str;
}
break;
case TransactionSubaddrAccountRole:
result = tInfo->subaddrAccount();
break;
case TransactionLabelRole:
result = tInfo->subaddrIndex().size() == 1 && *tInfo->subaddrIndex().begin() == 0 ? tr("Primary address") : tInfo->label();
break;
case TransactionConfirmationsRole: case TransactionConfirmationsRole:
result = tInfo->confirmations(); result = tInfo->confirmations();
break; break;
@ -133,6 +152,9 @@ QHash<int, QByteArray> TransactionHistoryModel::roleNames() const
roleNames.insert(TransactionAtomicAmountRole, "atomicAmount"); roleNames.insert(TransactionAtomicAmountRole, "atomicAmount");
roleNames.insert(TransactionFeeRole, "fee"); roleNames.insert(TransactionFeeRole, "fee");
roleNames.insert(TransactionBlockHeightRole, "blockHeight"); roleNames.insert(TransactionBlockHeightRole, "blockHeight");
roleNames.insert(TransactionSubaddrIndexRole, "subaddrIndex");
roleNames.insert(TransactionSubaddrAccountRole, "subaddrAccount");
roleNames.insert(TransactionLabelRole, "label");
roleNames.insert(TransactionConfirmationsRole, "confirmations"); roleNames.insert(TransactionConfirmationsRole, "confirmations");
roleNames.insert(TransactionConfirmationsRequiredRole, "confirmationsRequired"); roleNames.insert(TransactionConfirmationsRequiredRole, "confirmationsRequired");
roleNames.insert(TransactionHashRole, "hash"); roleNames.insert(TransactionHashRole, "hash");

View file

@ -25,6 +25,9 @@ public:
TransactionDisplayAmountRole, TransactionDisplayAmountRole,
TransactionFeeRole, TransactionFeeRole,
TransactionBlockHeightRole, TransactionBlockHeightRole,
TransactionSubaddrIndexRole,
TransactionSubaddrAccountRole,
TransactionLabelRole,
TransactionConfirmationsRole, TransactionConfirmationsRole,
TransactionConfirmationsRequiredRole, TransactionConfirmationsRequiredRole,
TransactionHashRole, TransactionHashRole,