aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluigi1111 <luigi1111w@gmail.com>2018-01-26 14:29:40 -0500
committerluigi1111 <luigi1111w@gmail.com>2018-01-26 14:29:40 -0500
commitef6a6ed6519104f27777f8e508d8eeaf402cff30 (patch)
tree0f34b050893c914520d4f00fee0e3c5a14631332
parentb137f73440ccb1c848e88afbb3515d2ab771a7f4 (diff)
parenta6a7b56d730f9a0be9e7d24b0b589cb69cf5bd56 (diff)
downloadmonzero-gui-ef6a6ed6519104f27777f8e508d8eeaf402cff30.tar.gz
monzero-gui-ef6a6ed6519104f27777f8e508d8eeaf402cff30.tar.xz
monzero-gui-ef6a6ed6519104f27777f8e508d8eeaf402cff30.zip
Merge pull request #977
cee0474 Subaddresses minimal version: supports default account only a6a7b56 ditch payment ID from the Receive page
-rw-r--r--MiddlePanel.qml2
-rw-r--r--components/HistoryTable.qml39
-rw-r--r--components/IconButton.qml4
-rw-r--r--components/InputDialog.qml181
-rw-r--r--components/LineEdit.qml2
-rw-r--r--components/SubaddressTable.qml109
-rw-r--r--main.cpp8
-rw-r--r--main.qml45
-rw-r--r--monero-wallet-gui.pro4
-rw-r--r--pages/History.qml2
-rw-r--r--pages/Mining.qml2
-rw-r--r--pages/Receive.qml214
-rw-r--r--qml.qrc2
-rw-r--r--src/libwalletqt/PendingTransaction.cpp10
-rw-r--r--src/libwalletqt/PendingTransaction.h4
-rw-r--r--src/libwalletqt/Subaddress.cpp84
-rw-r--r--src/libwalletqt/Subaddress.h61
-rw-r--r--src/libwalletqt/TransactionHistory.cpp10
-rw-r--r--src/libwalletqt/TransactionHistory.h4
-rw-r--r--src/libwalletqt/TransactionInfo.cpp18
-rw-r--r--src/libwalletqt/TransactionInfo.h7
-rw-r--r--src/libwalletqt/Wallet.cpp92
-rw-r--r--src/libwalletqt/Wallet.h35
-rw-r--r--src/libwalletqt/WalletManager.cpp4
-rw-r--r--src/model/SubaddressModel.cpp89
-rw-r--r--src/model/SubaddressModel.h62
-rw-r--r--src/model/TransactionHistoryModel.cpp22
-rw-r--r--src/model/TransactionHistoryModel.h3
28 files changed, 939 insertions, 180 deletions
diff --git a/MiddlePanel.qml b/MiddlePanel.qml
index 845092a6..9c1096f6 100644
--- a/MiddlePanel.qml
+++ b/MiddlePanel.qml
@@ -112,7 +112,7 @@ Rectangle {
}, State {
name: "Receive"
PropertyChanges { target: root; currentView: receiveView }
- PropertyChanges { target: mainFlickable; contentHeight: minHeight }
+ PropertyChanges { target: mainFlickable; contentHeight: 1000 * scaleRatio }
}, State {
name: "TxKey"
PropertyChanges { target: root; currentView: txkeyView }
diff --git a/components/HistoryTable.qml b/components/HistoryTable.qml
index ba3971b5..633998ee 100644
--- a/components/HistoryTable.qml
+++ b/components/HistoryTable.qml
@@ -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
diff --git a/components/IconButton.qml b/components/IconButton.qml
index 042439f1..d927e296 100644
--- a/components/IconButton.qml
+++ b/components/IconButton.qml
@@ -30,6 +30,7 @@
import QtQuick 2.0
Item {
+ property alias image : buttonImage
property alias imageSource : buttonImage.source
signal clicked(var mouse)
@@ -53,7 +54,8 @@ Item {
MouseArea {
id: buttonArea
anchors.fill: parent
-
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
onPressed: {
buttonImage.x = buttonImage.x + 2
diff --git a/components/InputDialog.qml b/components/InputDialog.qml
new file mode 100644
index 00000000..8ab5b5d8
--- /dev/null
+++ b/components/InputDialog.qml
@@ -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()
+ }
+ }
+ }
+ }
+}
diff --git a/components/LineEdit.qml b/components/LineEdit.qml
index f7cb0612..4e34f241 100644
--- a/components/LineEdit.qml
+++ b/components/LineEdit.qml
@@ -37,6 +37,7 @@ Item {
property alias cursorPosition: input.cursorPosition
property alias echoMode: input.echoMode
property int fontSize: 18 * scaleRatio
+ property bool showBorder: true
property bool error: false
signal editingFinished()
signal accepted();
@@ -52,6 +53,7 @@ Item {
}
Rectangle {
+ visible: showBorder
anchors.fill: parent
anchors.bottomMargin: 1 * scaleRatio
color: "#DBDBDB"
diff --git a/components/SubaddressTable.qml b/components/SubaddressTable.qml
new file mode 100644
index 00000000..8d6b8d1d
--- /dev/null
+++ b/components/SubaddressTable.qml
@@ -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
+ }
+}
diff --git a/main.cpp b/main.cpp
index 3e15d373..43608815 100644
--- a/main.cpp
+++ b/main.cpp
@@ -50,6 +50,8 @@
#include "model/TransactionHistorySortFilterModel.h"
#include "AddressBook.h"
#include "model/AddressBookModel.h"
+#include "Subaddress.h"
+#include "model/SubaddressModel.h"
#include "wallet/api/wallet2_api.h"
#include "MainApp.h"
@@ -133,6 +135,12 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<AddressBook>("moneroComponents.AddressBook", 1, 0, "AddressBook",
"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<TransactionInfo::Direction>();
qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>();
diff --git a/main.qml b/main.qml
index 387d1b5d..01197161 100644
--- a/main.qml
+++ b/main.qml
@@ -310,8 +310,10 @@ ApplicationWindow {
}
function updateBalance() {
- middlePanel.unlockedBalanceText = leftPanel.unlockedBalanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.unlockedBalance);
- middlePanel.balanceText = leftPanel.balanceText = middlePanel.state === "Receive" ? qsTr("HIDDEN") : walletManager.displayAmount(currentWallet.balance);
+ if (!currentWallet)
+ 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){
@@ -329,7 +331,7 @@ ApplicationWindow {
}
// initialize transaction history once wallet is initialized first time;
if (!walletInitialized) {
- currentWallet.history.refresh()
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
walletInitialized = true
}
}
@@ -377,7 +379,7 @@ ApplicationWindow {
if(foundNewBlock) {
foundNewBlock = false;
console.log("New block found - updating history")
- currentWallet.history.refresh()
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
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");
}
@@ -453,6 +455,9 @@ ApplicationWindow {
console.log("Saving wallet after first refresh");
currentWallet.store()
isNewWallet = false
+
+ // Update History
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount);
}
// recovering from seed is finished after first refresh
@@ -463,7 +468,7 @@ ApplicationWindow {
// Update history on every refresh if it's empty
if(currentWallet.history.count == 0)
- currentWallet.history.refresh()
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
onWalletUpdate();
}
@@ -526,19 +531,21 @@ ApplicationWindow {
currentWallet.refresh()
console.log("Confirmed money found")
// history refresh is handled by walletUpdated
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount) // this will refresh model
+ currentWallet.subaddress.refresh(currentWallet.currentSubaddressAccount)
}
function onWalletUnconfirmedMoneyReceived(txId, amount) {
// refresh history
console.log("unconfirmed money found")
- currentWallet.history.refresh()
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
}
function onWalletMoneySent(txId, amount) {
// refresh transaction history here
console.log("money sent found")
currentWallet.refresh()
- currentWallet.history.refresh() // this will refresh model
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount) // this will refresh model
}
function walletsFound() {
@@ -584,8 +591,11 @@ ApplicationWindow {
// here we show confirmation popup;
transactionConfirmationPopup.title = qsTr("Confirmation") + translationManager.emptyString
- transactionConfirmationPopup.text = qsTr("Please confirm transaction:\n")
- + (address === "" ? "" : (qsTr("\nAddress: ") + address))
+ transactionConfirmationPopup.text = qsTr("Please confirm transaction:\n");
+ 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))
+ qsTr("\n\nAmount: ") + walletManager.displayAmount(transaction.amount)
+ 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 {
id: daemonManagerDialog
onRejected: {
diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro
index 221d4017..578ad0e7 100644
--- a/monero-wallet-gui.pro
+++ b/monero-wallet-gui.pro
@@ -35,6 +35,8 @@ HEADERS += \
src/QR-Code-generator/QrSegment.hpp \
src/model/AddressBookModel.h \
src/libwalletqt/AddressBook.h \
+ src/model/SubaddressModel.h \
+ src/libwalletqt/Subaddress.h \
src/zxcvbn-c/zxcvbn.h \
src/libwalletqt/UnsignedTransaction.h \
MainApp.h
@@ -58,6 +60,8 @@ SOURCES += main.cpp \
src/QR-Code-generator/QrSegment.cpp \
src/model/AddressBookModel.cpp \
src/libwalletqt/AddressBook.cpp \
+ src/model/SubaddressModel.cpp \
+ src/libwalletqt/Subaddress.cpp \
src/zxcvbn-c/zxcvbn.c \
src/libwalletqt/UnsignedTransaction.cpp \
MainApp.cpp
diff --git a/pages/History.qml b/pages/History.qml
index 9b184741..97162751 100644
--- a/pages/History.qml
+++ b/pages/History.qml
@@ -546,7 +546,7 @@ Rectangle {
function onPageCompleted() {
if(currentWallet != null && typeof currentWallet.history !== "undefined" ) {
- currentWallet.history.refresh()
+ currentWallet.history.refresh(currentWallet.currentSubaddressAccount)
table.addressBookModel = currentWallet ? currentWallet.addressBookModel : null
transactionTypeDropdown.update()
}
diff --git a/pages/Mining.qml b/pages/Mining.qml
index 0d87310f..a6bcb941 100644
--- a/pages/Mining.qml
+++ b/pages/Mining.qml
@@ -151,7 +151,7 @@ Rectangle {
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
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) {
update()
} else {
diff --git a/pages/Receive.qml b/pages/Receive.qml
index d31100c2..bea40e97 100644
--- a/pages/Receive.qml
+++ b/pages/Receive.qml
@@ -38,59 +38,26 @@ import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0
import moneroComponents.TransactionHistory 1.0
import moneroComponents.TransactionHistoryModel 1.0
+import moneroComponents.Subaddress 1.0
+import moneroComponents.SubaddressModel 1.0
Rectangle {
-
+ id: pageReceive
color: "#F0EEEE"
- property alias addressText : addressLine.text
- property alias paymentIdText : paymentIdLine.text
- property alias integratedAddressText : integratedAddressLine.text
property var model
+ property var current_address
+ property alias addressText : pageReceive.current_address
property string trackingLineText: ""
- function updatePaymentId(payment_id) {
- if (typeof appWindow.currentWallet === 'undefined' || appWindow.currentWallet == null)
- return
-
- // generate a new one if not given as argument
- if (typeof payment_id === 'undefined') {
- payment_id = appWindow.currentWallet.generatePaymentId()
- paymentIdLine.text = payment_id
- }
-
- if (payment_id.length > 0) {
- integratedAddressLine.text = appWindow.currentWallet.integratedAddress(payment_id)
- if (integratedAddressLine.text === "") {
- integratedAddressLine.text = qsTr("Invalid payment ID")
- paymentIdLine.error = true
- }
- else {
- paymentIdLine.error = false
- }
- }
- else {
- paymentIdLine.text = ""
- integratedAddressLine.text = ""
- paymentIdLine.error = false
- }
-
- update()
- }
-
function makeQRCodeString() {
var s = "monero:"
var nfields = 0
- s += addressLine.text
+ s += current_address;
var amount = amountLine.text.trim()
if (amount !== "") {
s += (nfields++ ? "&" : "?")
s += "tx_amount=" + amount
}
- var pid = paymentIdLine.text.trim().toLowerCase()
- if (pid !== "" && walletManager.paymentIdValid(pid)) {
- s += (nfields++ ? "&" : "?")
- s += "tx_payment_id=" + pid
- }
return s
}
@@ -118,13 +85,14 @@ Rectangle {
var count = model.rowCount()
var totalAmount = 0
var nTransactions = 0
- var list = ""
+ var list = []
var blockchainHeight = 0
for (var i = 0; i < count; ++i) {
var idx = model.index(i, 0)
var isout = model.data(idx, TransactionHistoryModel.TransactionIsOutRole);
- var payment_id = model.data(idx, TransactionHistoryModel.TransactionPaymentIdRole);
- if (!isout && payment_id == paymentIdLine.text) {
+ var subaddrAccount = model.data(idx, TransactionHistoryModel.TransactionSubaddrAccountRole);
+ var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
+ if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == table.currentIndex) {
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
totalAmount = walletManager.addi(totalAmount, amount)
nTransactions += 1
@@ -132,21 +100,25 @@ Rectangle {
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
var blockHeight = model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole);
if (blockHeight == 0) {
- list += qsTr("in the txpool: %1").arg(txid) + translationManager.emptyString
+ list.push(qsTr("in the txpool: %1").arg(txid) + translationManager.emptyString)
} else {
if (blockchainHeight == 0)
blockchainHeight = walletManager.blockchainHeight()
var confirmations = blockchainHeight - blockHeight - 1
var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
if (confirmations > 1) {
- list += qsTr("%2 confirmations: %3 (%1)").arg(txid).arg(confirmations).arg(displayAmount) + translationManager.emptyString
+ list.push(qsTr("%2 confirmations: %3 (%1)").arg(txid).arg(confirmations).arg(displayAmount) + translationManager.emptyString)
} else {
- list += qsTr("1 confirmation: %2 (%1)").arg(txid).arg(displayAmount) + translationManager.emptyString
+ list.push(qsTr("1 confirmation: %2 (%1)").arg(txid).arg(displayAmount) + translationManager.emptyString)
}
}
- list += "<br>"
}
}
+ // if there are too many txes, only show the first 3
+ if (list.length > 3) {
+ list.length = 3;
+ list.push("...");
+ }
if (nTransactions == 0) {
setTrackingLineText(qsTr("No transaction found yet...") + translationManager.emptyString)
@@ -165,7 +137,7 @@ Rectangle {
}
}
- setTrackingLineText(text + "<br>" + list)
+ setTrackingLineText(text + "<br>" + list.join("<br>"))
}
Clipboard { id: clipboard }
@@ -192,116 +164,68 @@ Rectangle {
id: addressRow
Label {
id: addressLabel
- text: qsTr("Address") + translationManager.emptyString
+ text: qsTr("Addresses") + translationManager.emptyString
width: mainLayout.labelWidth
}
- LineEdit {
- id: addressLine
- fontSize: mainLayout.lineEditFontSize
- placeholderText: qsTr("ReadOnly wallet address displayed here") + translationManager.emptyString;
- readOnly: true
- width: mainLayout.editWidth
+ Rectangle {
+ id: tableRect
Layout.fillWidth: true
- onTextChanged: cursorPosition = 0
-
- IconButton {
- imageSource: "../images/copyToClipboard.png"
- onClicked: {
- if (addressLine.text.length > 0) {
- console.log(addressLine.text + " copied to clipboard")
- clipboard.setText(addressLine.text)
- appWindow.showStatusMessage(qsTr("Address copied to clipboard"),3)
- }
+ Layout.preferredHeight: 200
+ color: "#FFFFFF"
+ Scroll {
+ id: flickableScroll
+ anchors.right: table.right
+ anchors.top: table.top
+ anchors.bottom: table.bottom
+ flickable: table
+ }
+ SubaddressTable {
+ id: table
+ anchors.fill: parent
+ onContentYChanged: flickableScroll.flickableContentYChanged()
+ onCurrentItemChanged: {
+ current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, table.currentIndex);
}
}
}
- }
-
- GridLayout {
- id: paymentIdRow
- columns:2
- Label {
- Layout.columnSpan: 2
- id: paymentIdLabel
- text: qsTr("Payment ID") + translationManager.emptyString
- width: mainLayout.labelWidth
- }
-
- LineEdit {
- id: paymentIdLine
- fontSize: mainLayout.lineEditFontSize
- placeholderText: qsTr("16 hexadecimal characters") + translationManager.emptyString;
- readOnly: false
- onTextChanged: updatePaymentId(paymentIdLine.text)
-
- width: mainLayout.editWidth
- Layout.fillWidth: true
-
- IconButton {
- imageSource: "../images/copyToClipboard.png"
+ RowLayout {
+ spacing: 20
+ StandardButton {
+ shadowReleasedColor: "#FF4304"
+ shadowPressedColor: "#B32D00"
+ releasedColor: "#FF6C3C"
+ pressedColor: "#FF4304"
+ text: qsTr("Create new address") + translationManager.emptyString;
onClicked: {
- if (paymentIdLine.text.length > 0) {
- clipboard.setText(paymentIdLine.text)
- appWindow.showStatusMessage(qsTr("Payment ID copied to clipboard"),3)
+ 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 {
- id: generatePaymentId
- shadowReleasedColor: "#FF4304"
- shadowPressedColor: "#B32D00"
- releasedColor: "#FF6C3C"
- pressedColor: "#FF4304"
- text: qsTr("Generate") + translationManager.emptyString;
- onClicked: updatePaymentId()
- }
-
- StandardButton {
- id: clearPaymentId
- enabled: !!paymentIdLine.text
- shadowReleasedColor: "#FF4304"
- shadowPressedColor: "#B32D00"
- releasedColor: "#FF6C3C"
- pressedColor: "#FF4304"
- text: qsTr("Clear") + translationManager.emptyString;
- onClicked: updatePaymentId("")
- }
- }
-
- ColumnLayout {
- id: integratedAddressRow
- Label {
- id: integratedAddressLabel
- text: qsTr("Integrated address") + translationManager.emptyString
- width: mainLayout.labelWidth
- }
-
-
- LineEdit {
-
- id: integratedAddressLine
- fontSize: mainLayout.lineEditFontSize
- placeholderText: qsTr("Generate payment ID for integrated address") + translationManager.emptyString
- readOnly: true
- width: mainLayout.editWidth
- Layout.fillWidth: true
-
- onTextChanged: cursorPosition = 0
-
- IconButton {
- imageSource: "../images/copyToClipboard.png"
+ StandardButton {
+ shadowReleasedColor: "#FF4304"
+ shadowPressedColor: "#B32D00"
+ releasedColor: "#FF6C3C"
+ pressedColor: "#FF4304"
+ enabled: table.currentIndex > 0
+ text: qsTr("Rename") + translationManager.emptyString;
onClicked: {
- if (integratedAddressLine.text.length > 0) {
- clipboard.setText(integratedAddressLine.text)
- appWindow.showStatusMessage(qsTr("Integrated address copied to clipboard"),3)
+ 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()
}
}
-
}
}
@@ -348,7 +272,6 @@ Rectangle {
trackingHowToUseDialog.title = qsTr("Tracking payments") + translationManager.emptyString;
trackingHowToUseDialog.text = qsTr(
"<p><font size='+2'>This is a simple sales tracker:</font></p>" +
- "<p>Click Generate to create a random payment id for a new customer</p> " +
"<p>Let your customer scan that QR code to make a payment (if that customer has software which " +
"supports QR code scanning).</p>" +
"<p>This page will automatically scan the blockchain and the tx pool " +
@@ -436,11 +359,12 @@ Rectangle {
function onPageCompleted() {
console.log("Receive page loaded");
+ table.model = currentWallet.subaddressModel;
if (appWindow.currentWallet) {
- if (addressLine.text.length === 0 || addressLine.text !== appWindow.currentWallet.address) {
- addressLine.text = appWindow.currentWallet.address
- }
+ current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
+ appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount)
+ table.currentIndex = 0
}
update()
diff --git a/qml.qrc b/qml.qrc
index 7ea33ba2..295b363b 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -51,6 +51,7 @@
<file>tabs/TweetsModel.qml</file>
<file>components/Scroll.qml</file>
<file>components/AddressBookTable.qml</file>
+ <file>components/SubaddressTable.qml</file>
<file>images/deleteIcon.png</file>
<file>images/moneroIcon.png</file>
<file>components/StandardDropdown.qml</file>
@@ -136,6 +137,7 @@
<file>components/IconButton.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>
<file>components/StandardDialog.qml</file>
diff --git a/src/libwalletqt/PendingTransaction.cpp b/src/libwalletqt/PendingTransaction.cpp
index bd621d6c..c6f1d069 100644
--- a/src/libwalletqt/PendingTransaction.cpp
+++ b/src/libwalletqt/PendingTransaction.cpp
@@ -50,6 +50,16 @@ quint64 PendingTransaction::txCount() const
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)
{
m_fileName = fileName;
diff --git a/src/libwalletqt/PendingTransaction.h b/src/libwalletqt/PendingTransaction.h
index 5aa94e0b..a73aab2e 100644
--- a/src/libwalletqt/PendingTransaction.h
+++ b/src/libwalletqt/PendingTransaction.h
@@ -2,6 +2,8 @@
#define PENDINGTRANSACTION_H
#include <QObject>
+#include <QList>
+#include <QVariant>
#include <wallet/api/wallet2_api.h>
@@ -19,6 +21,7 @@ class PendingTransaction : public QObject
Q_PROPERTY(quint64 fee READ fee)
Q_PROPERTY(QStringList txid READ txid)
Q_PROPERTY(quint64 txCount READ txCount)
+ Q_PROPERTY(QList<QVariant> subaddrIndices READ subaddrIndices)
public:
enum Status {
@@ -44,6 +47,7 @@ public:
quint64 fee() const;
QStringList txid() const;
quint64 txCount() const;
+ QList<QVariant> subaddrIndices() const;
Q_INVOKABLE void setFilename(const QString &fileName);
private:
diff --git a/src/libwalletqt/Subaddress.cpp b/src/libwalletqt/Subaddress.cpp
new file mode 100644
index 00000000..f9a798ce
--- /dev/null
+++ b/src/libwalletqt/Subaddress.cpp
@@ -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();
+}
diff --git a/src/libwalletqt/Subaddress.h b/src/libwalletqt/Subaddress.h
new file mode 100644
index 00000000..c43c11fd
--- /dev/null
+++ b/src/libwalletqt/Subaddress.h
@@ -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
diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp
index 40b3d3c4..243db642 100644
--- a/src/libwalletqt/TransactionHistory.cpp
+++ b/src/libwalletqt/TransactionHistory.cpp
@@ -22,7 +22,7 @@ TransactionInfo *TransactionHistory::transaction(int index)
// 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
emit refreshStarted();
@@ -37,6 +37,10 @@ QList<TransactionInfo *> TransactionHistory::getAll() const
TransactionHistory * parent = const_cast<TransactionHistory*>(this);
for (const auto i : m_pimpl->getAll()) {
TransactionInfo * ti = new TransactionInfo(i, parent);
+ if (ti->subaddrAccount() != accountIndex) {
+ delete ti;
+ continue;
+ }
m_tinfo.append(ti);
// looking for transactions timestamp scope
if (ti->timestamp() >= lastDateTime) {
@@ -69,12 +73,12 @@ QList<TransactionInfo *> TransactionHistory::getAll() const
return m_tinfo;
}
-void TransactionHistory::refresh()
+void TransactionHistory::refresh(quint32 accountIndex)
{
// rebuilding transaction list in wallet_api;
m_pimpl->refresh();
// copying list here and keep track on every item to avoid memleaks
- getAll();
+ getAll(accountIndex);
}
quint64 TransactionHistory::count() const
diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h
index 0c8bfc9d..ac0c4400 100644
--- a/src/libwalletqt/TransactionHistory.h
+++ b/src/libwalletqt/TransactionHistory.h
@@ -23,8 +23,8 @@ class TransactionHistory : public QObject
public:
Q_INVOKABLE TransactionInfo *transaction(int index);
// Q_INVOKABLE TransactionInfo * transaction(const QString &id);
- Q_INVOKABLE QList<TransactionInfo*> getAll() const;
- Q_INVOKABLE void refresh();
+ Q_INVOKABLE QList<TransactionInfo*> getAll(quint32 accountIndex) const;
+ Q_INVOKABLE void refresh(quint32 accountIndex);
quint64 count() const;
QDateTime firstDateTime() const;
QDateTime lastDateTime() const;
diff --git a/src/libwalletqt/TransactionInfo.cpp b/src/libwalletqt/TransactionInfo.cpp
index a787b151..72b56b6a 100644
--- a/src/libwalletqt/TransactionInfo.cpp
+++ b/src/libwalletqt/TransactionInfo.cpp
@@ -48,6 +48,24 @@ quint64 TransactionInfo::blockHeight() const
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
{
return m_pimpl->confirmations();
diff --git a/src/libwalletqt/TransactionInfo.h b/src/libwalletqt/TransactionInfo.h
index d957c455..1bb6cc56 100644
--- a/src/libwalletqt/TransactionInfo.h
+++ b/src/libwalletqt/TransactionInfo.h
@@ -4,6 +4,7 @@
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <QDateTime>
+#include <QSet>
class Transfer;
@@ -18,6 +19,9 @@ class TransactionInfo : public QObject
Q_PROPERTY(QString displayAmount READ displayAmount)
Q_PROPERTY(QString fee READ fee)
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 unlockTime READ unlockTime)
Q_PROPERTY(QString hash READ hash)
@@ -44,6 +48,9 @@ public:
QString displayAmount() const;
QString fee() const;
quint64 blockHeight() const;
+ QSet<quint32> subaddrIndex() const;
+ quint32 subaddrAccount() const;
+ QString label() const;
quint64 confirmations() const;
quint64 unlockTime() const;
//! transaction_id
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index b694a05f..4b18ccc8 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -3,9 +3,11 @@
#include "UnsignedTransaction.h"
#include "TransactionHistory.h"
#include "AddressBook.h"
+#include "Subaddress.h"
#include "model/TransactionHistoryModel.h"
#include "model/TransactionHistorySortFilterModel.h"
#include "model/AddressBookModel.h"
+#include "model/SubaddressModel.h"
#include "wallet/api/wallet2_api.h"
#include <QFile>
@@ -155,9 +157,9 @@ bool Wallet::setPassword(const QString &password)
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
@@ -241,14 +243,63 @@ bool Wallet::viewOnly() const
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
@@ -288,7 +339,8 @@ quint64 Wallet::daemonBlockChainTargetHeight() const
bool Wallet::refresh()
{
bool result = m_walletImpl->refresh();
- m_history->refresh();
+ m_history->refresh(currentSubaddressAccount());
+ m_subaddress->refresh(currentSubaddressAccount());
if (result)
emit updated();
return result;
@@ -324,9 +376,10 @@ PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QSt
quint64 amount, quint32 mixin_count,
PendingTransaction::Priority priority)
{
+ std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
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);
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,
quint32 mixin_count, PendingTransaction::Priority priority)
{
+ std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
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);
return result;
}
@@ -458,6 +512,18 @@ AddressBookModel *Wallet::addressBookModel() const
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
{
@@ -668,14 +734,18 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
, m_historyModel(nullptr)
, m_addressBook(nullptr)
, m_addressBookModel(nullptr)
+ , m_subaddress(nullptr)
+ , m_subaddressModel(nullptr)
, m_daemonBlockChainHeight(0)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
, m_daemonBlockChainTargetHeight(0)
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
+ , m_currentSubaddressAccount(0)
{
m_history = new TransactionHistory(m_walletImpl->history(), this);
m_addressBook = new AddressBook(m_walletImpl->addressBook(), this);
+ m_subaddress = new Subaddress(m_walletImpl->subaddress(), this);
m_walletImpl->setListener(new WalletListenerImpl(this));
m_connectionStatus = Wallet::ConnectionStatus_Disconnected;
// start cache timers
@@ -696,6 +766,10 @@ Wallet::~Wallet()
delete m_history;
m_history = NULL;
+ delete m_addressBook;
+ m_addressBook = NULL;
+ delete m_subaddress;
+ m_subaddress = NULL;
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
if(status() == Status_Critical)
qDebug("Not storing wallet cache");
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index 8c833f4e..c8e78339 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -20,6 +20,8 @@ class TransactionHistoryModel;
class TransactionHistorySortFilterModel;
class AddressBook;
class AddressBookModel;
+class Subaddress;
+class SubaddressModel;
class Wallet : public QObject
{
@@ -29,17 +31,17 @@ class Wallet : public QObject
Q_PROPERTY(Status status READ status)
Q_PROPERTY(bool testnet READ testnet)
// Q_PROPERTY(ConnectionStatus connected READ connected)
+ Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount)
Q_PROPERTY(bool synchronized READ synchronized)
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(QString paymentId READ paymentId WRITE setPaymentId)
Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged)
Q_PROPERTY(QString path READ path)
Q_PROPERTY(AddressBookModel * addressBookModel READ addressBookModel)
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(QString secretViewKey READ getSecretViewKey)
Q_PROPERTY(QString publicViewKey READ getPublicViewKey)
@@ -98,7 +100,7 @@ public:
Q_INVOKABLE bool setPassword(const QString &password);
//! returns wallet's public address
- QString address() const;
+ Q_INVOKABLE QString address(quint32 accountIndex, quint32 addressIndex) const;
//! returns wallet file's path
QString path() const;
@@ -126,10 +128,22 @@ public:
Q_INVOKABLE void setTrustedDaemon(bool arg);
//! returns balance
- Q_INVOKABLE quint64 balance() const;
+ Q_INVOKABLE quint64 balance(quint32 accountIndex) const;
+ Q_INVOKABLE quint64 balanceAll() const;
//! 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
Q_INVOKABLE bool viewOnly() const;
@@ -209,6 +223,12 @@ public:
//! returns adress book model
AddressBookModel *addressBookModel() const;
+ //! returns subaddress
+ Subaddress *subaddress();
+
+ //! returns subadress model
+ SubaddressModel *subaddressModel();
+
//! generate payment id
Q_INVOKABLE QString generatePaymentId() const;
@@ -302,8 +322,11 @@ private:
int m_connectionStatusTtl;
mutable QTime m_connectionStatusTime;
mutable bool m_initialized;
+ uint32_t m_currentSubaddressAccount;
AddressBook * m_addressBook;
mutable AddressBookModel * m_addressBookModel;
+ Subaddress * m_subaddress;
+ mutable SubaddressModel * m_subaddressModel;
QMutex m_connectionStatusMutex;
bool m_connectionStatusRunning;
QString m_daemonUsername;
diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp
index 3b33b8b7..73b0a006 100644
--- a/src/libwalletqt/WalletManager.cpp
+++ b/src/libwalletqt/WalletManager.cpp
@@ -49,7 +49,7 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
__PRETTY_FUNCTION__, qPrintable(path), 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);
// move wallet to the GUI thread. Otherwise it wont be emitting signals
@@ -110,7 +110,7 @@ QString WalletManager::closeWallet()
QMutexLocker locker(&m_mutex);
QString result;
if (m_currentWallet) {
- result = m_currentWallet->address();
+ result = m_currentWallet->address(0, 0);
delete m_currentWallet;
} else {
qCritical() << "Trying to close non existing wallet " << m_currentWallet;
diff --git a/src/model/SubaddressModel.cpp b/src/model/SubaddressModel.cpp
new file mode 100644
index 00000000..aa2b67db
--- /dev/null
+++ b/src/model/SubaddressModel.cpp
@@ -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;
+}
diff --git a/src/model/SubaddressModel.h b/src/model/SubaddressModel.h
new file mode 100644
index 00000000..b494e1f5
--- /dev/null
+++ b/src/model/SubaddressModel.h
@@ -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
diff --git a/src/model/TransactionHistoryModel.cpp b/src/model/TransactionHistoryModel.cpp
index 907c21e8..42dfcd24 100644
--- a/src/model/TransactionHistoryModel.cpp
+++ b/src/model/TransactionHistoryModel.cpp
@@ -83,6 +83,25 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
}
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:
result = tInfo->confirmations();
break;
@@ -133,6 +152,9 @@ QHash<int, QByteArray> TransactionHistoryModel::roleNames() const
roleNames.insert(TransactionAtomicAmountRole, "atomicAmount");
roleNames.insert(TransactionFeeRole, "fee");
roleNames.insert(TransactionBlockHeightRole, "blockHeight");
+ roleNames.insert(TransactionSubaddrIndexRole, "subaddrIndex");
+ roleNames.insert(TransactionSubaddrAccountRole, "subaddrAccount");
+ roleNames.insert(TransactionLabelRole, "label");
roleNames.insert(TransactionConfirmationsRole, "confirmations");
roleNames.insert(TransactionConfirmationsRequiredRole, "confirmationsRequired");
roleNames.insert(TransactionHashRole, "hash");
diff --git a/src/model/TransactionHistoryModel.h b/src/model/TransactionHistoryModel.h
index a2dbd967..cbbec8ad 100644
--- a/src/model/TransactionHistoryModel.h
+++ b/src/model/TransactionHistoryModel.h
@@ -25,6 +25,9 @@ public:
TransactionDisplayAmountRole,
TransactionFeeRole,
TransactionBlockHeightRole,
+ TransactionSubaddrIndexRole,
+ TransactionSubaddrAccountRole,
+ TransactionLabelRole,
TransactionConfirmationsRole,
TransactionConfirmationsRequiredRole,
TransactionHashRole,