diff --git a/LeftPanel.qml b/LeftPanel.qml index 425c955c..fce19dec 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -61,6 +61,7 @@ Rectangle { signal signClicked() signal keysClicked() signal merchantClicked() + signal accountClicked() function selectItem(pos) { menuColumn.previousButton.checked = false @@ -76,7 +77,7 @@ Rectangle { else if(pos === "Settings") menuColumn.previousButton = settingsButton else if(pos === "Advanced") menuColumn.previousButton = advancedButton else if(pos === "Keys") menuColumn.previousButton = keysButton - + else if(pos === "Account") menuColumn.previousButton = accountButton menuColumn.previousButton.checked = true } @@ -285,6 +286,8 @@ Rectangle { anchors.leftMargin: 20 anchors.top: parent.top anchors.topMargin: 60 + elide: Text.ElideRight + textWidth: 238 } Item { //separator anchors.left: parent.left @@ -331,6 +334,30 @@ Rectangle { height: 1 } + // ------------- Account tab --------------- + MoneroComponents.MenuButton { + id: accountButton + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Account") + translationManager.emptyString + symbol: qsTr("T") + translationManager.emptyString + dotColor: "#44AAFF" + onClicked: { + parent.previousButton.checked = false + parent.previousButton = accountButton + panel.accountClicked() + } + } + + Rectangle { + visible: accountButton.present + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + color: "#313131" + height: 1 + } + // ------------- Transfer tab --------------- MoneroComponents.MenuButton { id: transferButton diff --git a/MiddlePanel.qml b/MiddlePanel.qml index 4ed21c58..df0aa1b3 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -67,7 +67,7 @@ Rectangle { property Mining miningView: Mining { } property AddressBook addressBookView: AddressBook { } property Keys keysView: Keys { } - + property Account accountView: Account { } signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description) signal sweepUnmixableClicked() @@ -160,7 +160,11 @@ Rectangle { name: "Keys" PropertyChanges { target: root; currentView: keysView } PropertyChanges { target: mainFlickable; contentHeight: keysView.keysHeight } - } + }, State { + name: "Account" + PropertyChanges { target: root; currentView: accountView } + PropertyChanges { target: mainFlickable; contentHeight: minHeight } + } ] // color stripe at the top diff --git a/components/Label.qml b/components/Label.qml index 1edc14e8..05c65da6 100644 --- a/components/Label.qml +++ b/components/Label.qml @@ -44,6 +44,8 @@ Item { property alias wrapMode: label.wrapMode property alias horizontalAlignment: label.horizontalAlignment property alias hoveredLink: label.hoveredLink + property alias elide: label.elide + property alias textWidth: label.width signal linkActivated() height: label.height * scaleRatio width: label.width * scaleRatio diff --git a/images/plus-in-circle-medium-white.png b/images/plus-in-circle-medium-white.png new file mode 100755 index 00000000..ddc548fb Binary files /dev/null and b/images/plus-in-circle-medium-white.png differ diff --git a/images/plus-in-circle-medium-white@2x.png b/images/plus-in-circle-medium-white@2x.png new file mode 100755 index 00000000..c4c3af90 Binary files /dev/null and b/images/plus-in-circle-medium-white@2x.png differ diff --git a/main.cpp b/main.cpp index 43b1f5a6..b47d1191 100644 --- a/main.cpp +++ b/main.cpp @@ -55,6 +55,8 @@ #include "model/AddressBookModel.h" #include "Subaddress.h" #include "model/SubaddressModel.h" +#include "SubaddressAccount.h" +#include "model/SubaddressAccountModel.h" #include "wallet/api/wallet2_api.h" #include "Logger.h" #include "MainApp.h" @@ -208,6 +210,12 @@ int main(int argc, char *argv[]) qmlRegisterUncreatableType("moneroComponents.Subaddress", 1, 0, "Subaddress", "Subaddress can't be instantiated directly"); + qmlRegisterUncreatableType("moneroComponents.SubaddressAccountModel", 1, 0, "SubaddressAccountModel", + "SubaddressAccountModel can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.SubaddressAccount", 1, 0, "SubaddressAccount", + "SubaddressAccount can't be instantiated directly"); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/main.qml b/main.qml index df84d763..7de16518 100644 --- a/main.qml +++ b/main.qml @@ -86,10 +86,11 @@ ApplicationWindow { // true if wallet ever synchronized property bool walletInitialized : false - // Current selected address / subaddress (Receive page) + // Current selected address / subaddress / (Receive/Account page) property var current_address property var current_address_label: "Primary" property int current_subaddress_table_index: 0 + property int current_subaddress_account_table_index: 0 function altKeyReleased() { ctrlPressed = false; } @@ -117,6 +118,7 @@ ApplicationWindow { else if(seq === "Ctrl+E") middlePanel.state = "Settings" else if(seq === "Ctrl+Y") leftPanel.keysClicked() else if(seq === "Ctrl+D") middlePanel.state = "Advanced" + else if(seq === "Ctrl+T") middlePanel.state = "Account" else if(seq === "Ctrl+Tab" || seq === "Alt+Tab") { /* if(middlePanel.state === "Transfer") middlePanel.state = "Receive" @@ -128,7 +130,8 @@ ApplicationWindow { else if(middlePanel.state === "Mining") middlePanel.state = "Sign" else if(middlePanel.state === "Sign") middlePanel.state = "Settings" */ - if(middlePanel.state === "Settings") middlePanel.state = "Transfer" + if(middlePanel.state === "Settings") middlePanel.state = "Account" + else if(middlePanel.state === "Account") middlePanel.state = "Transfer" else if(middlePanel.state === "Transfer") middlePanel.state = "AddressBook" else if(middlePanel.state === "AddressBook") middlePanel.state = "Receive" else if(middlePanel.state === "Receive") middlePanel.state = "History" @@ -156,7 +159,8 @@ ApplicationWindow { else if(middlePanel.state === "History") middlePanel.state = "Receive" else if(middlePanel.state === "Receive") middlePanel.state = "AddressBook" else if(middlePanel.state === "AddressBook") middlePanel.state = "Transfer" - else if(middlePanel.state === "Transfer") middlePanel.state = "Settings" + else if(middlePanel.state === "Transfer") middlePanel.state = "Account" + else if(middlePanel.state === "Account") middlePanel.state = "Settings" } if (middlePanel.state !== "Advanced") updateBalance(); @@ -368,6 +372,9 @@ ApplicationWindow { leftPanel.unlockedBalanceText = balance_unlocked; middlePanel.balanceText = balance; leftPanel.balanceText = balance; + + var accountLabel = currentWallet.getSubaddressLabel(currentWallet.currentSubaddressAccount, 0); + leftPanel.balanceLabelText = qsTr("Balance (#%1%2)").arg(currentWallet.currentSubaddressAccount).arg(accountLabel === "" ? "" : (" – " + accountLabel)); } function onWalletConnectionStatusChanged(status){ @@ -1435,6 +1442,15 @@ ApplicationWindow { } onKeysClicked: Utils.showSeedPage(); + + onAccountClicked: { + middlePanel.state = "Account"; + middlePanel.flickable.contentY = 0; + if(isMobile) { + hideMenu(); + } + updateBalance(); + } } RightPanel { diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 2662c5c9..15405d29 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -50,6 +50,8 @@ HEADERS += \ src/libwalletqt/AddressBook.h \ src/model/SubaddressModel.h \ src/libwalletqt/Subaddress.h \ + src/model/SubaddressAccountModel.h \ + src/libwalletqt/SubaddressAccount.h \ src/zxcvbn-c/zxcvbn.h \ src/libwalletqt/UnsignedTransaction.h \ Logger.h \ @@ -76,6 +78,8 @@ SOURCES += main.cpp \ src/libwalletqt/AddressBook.cpp \ src/model/SubaddressModel.cpp \ src/libwalletqt/Subaddress.cpp \ + src/model/SubaddressAccountModel.cpp \ + src/libwalletqt/SubaddressAccount.cpp \ src/zxcvbn-c/zxcvbn.c \ src/libwalletqt/UnsignedTransaction.cpp \ Logger.cpp \ diff --git a/pages/Account.qml b/pages/Account.qml new file mode 100644 index 00000000..ba06a81c --- /dev/null +++ b/pages/Account.qml @@ -0,0 +1,352 @@ +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick 2.0 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 + +import "../components" as MoneroComponents +import moneroComponents.Clipboard 1.0 +import moneroComponents.Wallet 1.0 +import moneroComponents.WalletManager 1.0 +import moneroComponents.TransactionHistory 1.0 +import moneroComponents.TransactionHistoryModel 1.0 +import "../js/TxUtils.js" as TxUtils + +Rectangle { + id: pageAccount + color: "transparent" + property var model + property alias accountHeight: mainLayout.height + property bool selectAndSend: false + + function renameSubaddressAccountLabel(_index){ + inputDialog.labelText = qsTr("Set the label of the selected account:") + translationManager.emptyString; + inputDialog.inputText = appWindow.currentWallet.getSubaddressLabel(_index, 0); + inputDialog.onAcceptedCallback = function() { + appWindow.currentWallet.subaddressAccount.setLabel(_index, inputDialog.inputText) + } + inputDialog.onRejectedCallback = null; + inputDialog.open() + } + + Clipboard { id: clipboard } + + /* main layout */ + ColumnLayout { + id: mainLayout + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio + anchors.topMargin: 40 * scaleRatio + + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + + spacing: 20 * scaleRatio + + ColumnLayout { + id: balanceRow + visible: !selectAndSend + spacing: 0 + + MoneroComponents.LabelSubheader { + Layout.fillWidth: true + textFormat: Text.RichText + text: qsTr("Balance All") + } + + RowLayout { + Layout.topMargin: 22 * scaleRatio + Text { + text: qsTr("Total balance: ") + Layout.fillWidth: true + color: "#757575" + font.pixelSize: 14 + font.family: MoneroComponents.Style.fontRegular.name + } + Text { + id: balanceAll + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 + color: MoneroComponents.Style.white + MouseArea { + hoverEnabled: true + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onEntered: { + parent.color = MoneroComponents.Style.orange + } + onExited: { + parent.color = MoneroComponents.Style.white + } + onClicked: { + console.log("Copied to clipboard"); + clipboard.setText(parent.text); + appWindow.showStatusMessage(qsTr("Copied to clipboard"),3) + } + } + } + } + + RowLayout { + Layout.topMargin: 10 * scaleRatio + Text { + text: qsTr("Total unlocked balance: ") + Layout.fillWidth: true + color: "#757575" + font.pixelSize: 14 + font.family: MoneroComponents.Style.fontRegular.name + } + Text { + id: unlockedBalanceAll + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 + color: MoneroComponents.Style.white + MouseArea { + hoverEnabled: true + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onEntered: { + parent.color = MoneroComponents.Style.orange + } + onExited: { + parent.color = MoneroComponents.Style.white + } + onClicked: { + console.log("Copied to clipboard"); + clipboard.setText(parent.text); + appWindow.showStatusMessage(qsTr("Copied to clipboard"),3) + } + } + } + } + } + + ColumnLayout { + id: addressRow + spacing: 0 + + MoneroComponents.LabelSubheader { + Layout.fillWidth: true + textFormat: Text.RichText + text: qsTr("Accounts") + } + + ColumnLayout { + id: subaddressAccountListRow + property int subaddressAccountListItemHeight: 50 * scaleRatio + Layout.topMargin: 6 * scaleRatio + Layout.fillWidth: true + Layout.minimumWidth: 240 + Layout.preferredHeight: subaddressAccountListItemHeight * subaddressAccountListView.count + visible: subaddressAccountListView.count >= 1 + + ListView { + id: subaddressAccountListView + Layout.fillWidth: true + anchors.fill: parent + clip: true + boundsBehavior: ListView.StopAtBounds + delegate: Rectangle { + id: tableItem2 + height: subaddressAccountListRow.subaddressAccountListItemHeight + width: parent.width + Layout.fillWidth: true + color: "transparent" + Rectangle { + anchors.right: parent.right + anchors.left: parent.left + anchors.top: parent.top + height: 1 + color: "#404040" + visible: index !== 0 + } + + Rectangle { + anchors.fill: parent + anchors.topMargin: 5 * scaleRatio + anchors.rightMargin: 80 * scaleRatio + color: "transparent" + + MoneroComponents.Label { + id: idLabel + color: index === appWindow.current_subaddress_account_table_index ? "white" : "#757575" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 6 * scaleRatio + fontSize: 14 * scaleRatio + fontBold: true + text: "#" + index + } + + MoneroComponents.Label { + id: nameLabel + color: "#a5a5a5" + anchors.verticalCenter: parent.verticalCenter + anchors.left: idLabel.right + anchors.leftMargin: 6 * scaleRatio + fontSize: 14 * scaleRatio + fontBold: true + text: label + elide: Text.ElideRight + textWidth: addressLabel.x - nameLabel.x - 1 + } + + MoneroComponents.Label { + id: addressLabel + color: "white" + anchors.verticalCenter: parent.verticalCenter + anchors.left: balanceLabel.left + anchors.leftMargin: (mainLayout.width < 510 ? -70 : -125) * scaleRatio + fontSize: 14 * scaleRatio + fontBold: true + text: TxUtils.addressTruncate(address, mainLayout.width < 510 ? 3 : 6) + } + + MoneroComponents.Label { + id: balanceLabel + color: "white" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.right + anchors.leftMargin: (mainLayout.width < 510 ? -120 : -180) * scaleRatio + fontSize: 14 * scaleRatio + fontBold: true + text: qsTr("Balance: ") + balance + elide: mainLayout.width < 510 ? Text.ElideRight : Text.ElideNone + textWidth: 120 + } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + hoverEnabled: true + onEntered: { + tableItem2.color = "#26FFFFFF" + } + onExited: { + tableItem2.color = "transparent" + } + onClicked: { + if (index == subaddressAccountListView.currentIndex && selectAndSend) { + appWindow.showPageRequest("Transfer"); + } + subaddressAccountListView.currentIndex = index; + } + } + } + + MoneroComponents.IconButton { + id: renameButton + imageSource: "../images/editIcon.png" + anchors.right: parent.right + anchors.rightMargin: 30 * scaleRatio + anchors.topMargin: 1 * scaleRatio + + onClicked: { + renameSubaddressAccountLabel(index); + } + } + + MoneroComponents.IconButton { + id: copyButton + imageSource: "../images/dropdownCopy.png" + anchors.right: parent.right + + onClicked: { + console.log("Address copied to clipboard"); + clipboard.setText(address); + appWindow.showStatusMessage(qsTr("Address copied to clipboard"),3); + } + } + } + onCurrentItemChanged: { + // reset global vars + appWindow.current_subaddress_account_table_index = subaddressAccountListView.currentIndex; + appWindow.currentWallet.switchSubaddressAccount(appWindow.current_subaddress_account_table_index); + appWindow.onWalletUpdate(); + } + + onCurrentIndexChanged: { + if (selectAndSend) { + appWindow.showPageRequest("Transfer"); + } + } + } + } + + Rectangle { + color: "#404040" + Layout.fillWidth: true + height: 1 + } + + MoneroComponents.CheckBox { + id: addNewAccountCheckbox + visible: !selectAndSend + border: false + checkedIcon: "qrc:///images/plus-in-circle-medium-white.png" + uncheckedIcon: "qrc:///images/plus-in-circle-medium-white.png" + fontSize: 14 * scaleRatio + iconOnTheLeft: true + Layout.fillWidth: true + Layout.topMargin: 10 * scaleRatio + text: qsTr("Create new account") + translationManager.emptyString; + onClicked: { + inputDialog.labelText = qsTr("Set the label of the new account:") + translationManager.emptyString + inputDialog.inputText = qsTr("(Untitled)") + inputDialog.onAcceptedCallback = function() { + appWindow.currentWallet.subaddressAccount.addRow(inputDialog.inputText) + appWindow.currentWallet.switchSubaddressAccount(appWindow.currentWallet.numSubaddressAccounts() - 1) + current_subaddress_account_table_index = appWindow.currentWallet.numSubaddressAccounts() - 1 + appWindow.onWalletUpdate(); + } + inputDialog.onRejectedCallback = null; + inputDialog.open() + } + } + } + } + + function onPageCompleted() { + console.log("account"); + if (appWindow.currentWallet !== undefined) { + appWindow.currentWallet.subaddressAccount.refresh(); + subaddressAccountListView.model = appWindow.currentWallet.subaddressAccountModel; + appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount) + + balanceAll.text = walletManager.displayAmount(appWindow.currentWallet.balanceAll()) + unlockedBalanceAll.text = walletManager.displayAmount(appWindow.currentWallet.unlockedBalanceAll()) + } + } + + function onPageClosed() { + selectAndSend = false; + } +} diff --git a/pages/Transfer.qml b/pages/Transfer.qml index 51d3d6c3..dcfa2073 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -137,7 +137,13 @@ Rectangle { id: amountLine Layout.fillWidth: true inlineIcon: true - labelText: qsTr("Amount") + translationManager.emptyString + labelText: qsTr("\ + Amount ( Change account )") + + translationManager.emptyString + onLabelLinkActivated: { + middlePanel.accountView.selectAndSend = true; + appWindow.showPageRequest("Account") + } placeholderText: qsTr("") + translationManager.emptyString width: 100 * scaleRatio fontBold: true diff --git a/qml.qrc b/qml.qrc index c3b0316c..1a234299 100644 --- a/qml.qrc +++ b/qml.qrc @@ -17,6 +17,7 @@ images/whatIsIcon@2x.png images/lockIcon.png components/MenuButton.qml + pages/Account.qml pages/Transfer.qml pages/History.qml pages/AddressBook.qml @@ -232,6 +233,8 @@ images/eyeHide@2x.png images/eyeShow.png images/eyeShow@2x.png + images/plus-in-circle-medium-white.png + images/plus-in-circle-medium-white@2x.png pages/merchant/Merchant.qml pages/merchant/MerchantCheckbox.qml pages/merchant/MerchantTrackingList.qml diff --git a/src/libwalletqt/SubaddressAccount.cpp b/src/libwalletqt/SubaddressAccount.cpp new file mode 100644 index 00000000..83557141 --- /dev/null +++ b/src/libwalletqt/SubaddressAccount.cpp @@ -0,0 +1,56 @@ +#include "SubaddressAccount.h" +#include + +SubaddressAccount::SubaddressAccount(Monero::SubaddressAccount *subaddressAccountImpl, QObject *parent) + : QObject(parent), m_subaddressAccountImpl(subaddressAccountImpl) +{ + qDebug(__FUNCTION__); + getAll(); +} + +QList SubaddressAccount::getAll(bool update) const +{ + qDebug(__FUNCTION__); + + emit refreshStarted(); + + if(update) + m_rows.clear(); + + if (m_rows.empty()){ + for (auto &row: m_subaddressAccountImpl->getAll()) { + m_rows.append(row); + } + } + + emit refreshFinished(); + return m_rows; +} + +Monero::SubaddressAccountRow * SubaddressAccount::getRow(int index) const +{ + return m_rows.at(index); +} + +void SubaddressAccount::addRow(const QString &label) const +{ + m_subaddressAccountImpl->addRow(label.toStdString()); + getAll(true); +} + +void SubaddressAccount::setLabel(quint32 accountIndex, const QString &label) const +{ + m_subaddressAccountImpl->setLabel(accountIndex, label.toStdString()); + getAll(true); +} + +void SubaddressAccount::refresh() const +{ + m_subaddressAccountImpl->refresh(); + getAll(true); +} + +quint64 SubaddressAccount::count() const +{ + return m_rows.size(); +} diff --git a/src/libwalletqt/SubaddressAccount.h b/src/libwalletqt/SubaddressAccount.h new file mode 100644 index 00000000..ee40c22e --- /dev/null +++ b/src/libwalletqt/SubaddressAccount.h @@ -0,0 +1,33 @@ +#ifndef SUBADDRESSACCOUNT_H +#define SUBADDRESSACCOUNT_H + +#include +#include +#include +#include + +class SubaddressAccount : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE QList getAll(bool update = false) const; + Q_INVOKABLE Monero::SubaddressAccountRow * getRow(int index) const; + Q_INVOKABLE void addRow(const QString &label) const; + Q_INVOKABLE void setLabel(quint32 accountIndex, const QString &label) const; + Q_INVOKABLE void refresh() const; + quint64 count() const; + +signals: + void refreshStarted() const; + void refreshFinished() const; + +public slots: + +private: + explicit SubaddressAccount(Monero::SubaddressAccount * subaddressAccountImpl, QObject *parent); + friend class Wallet; + Monero::SubaddressAccount * m_subaddressAccountImpl; + mutable QList m_rows; +}; + +#endif // SUBADDRESSACCOUNT_H diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 85761e0b..e488b635 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -4,10 +4,12 @@ #include "TransactionHistory.h" #include "AddressBook.h" #include "Subaddress.h" +#include "SubaddressAccount.h" #include "model/TransactionHistoryModel.h" #include "model/TransactionHistorySortFilterModel.h" #include "model/AddressBookModel.h" #include "model/SubaddressModel.h" +#include "model/SubaddressAccountModel.h" #include "wallet/api/wallet2_api.h" #include @@ -357,6 +359,7 @@ bool Wallet::refresh() bool result = m_walletImpl->refresh(); m_history->refresh(currentSubaddressAccount()); m_subaddress->refresh(currentSubaddressAccount()); + m_subaddressAccount->getAll(true); if (result) emit updated(); return result; @@ -543,6 +546,20 @@ SubaddressModel *Wallet::subaddressModel() return m_subaddressModel; } +SubaddressAccount *Wallet::subaddressAccount() const +{ + return m_subaddressAccount; +} + +SubaddressAccountModel *Wallet::subaddressAccountModel() const +{ + if (!m_subaddressAccountModel) { + Wallet * w = const_cast(this); + m_subaddressAccountModel = new SubaddressAccountModel(w,m_subaddressAccount); + } + return m_subaddressAccountModel; +} + QString Wallet::generatePaymentId() const { return QString::fromStdString(Monero::Wallet::genPaymentId()); @@ -858,6 +875,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_addressBookModel(nullptr) , m_subaddress(nullptr) , m_subaddressModel(nullptr) + , m_subaddressAccount(nullptr) + , m_subaddressAccountModel(nullptr) , m_daemonBlockChainHeight(0) , m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS) , m_daemonBlockChainTargetHeight(0) @@ -868,6 +887,7 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) 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_subaddressAccount = new SubaddressAccount(m_walletImpl->subaddressAccount(), this); m_walletListener = new WalletListenerImpl(this); m_walletImpl->setListener(m_walletListener); m_connectionStatus = Wallet::ConnectionStatus_Disconnected; @@ -893,6 +913,8 @@ Wallet::~Wallet() m_addressBook = NULL; delete m_subaddress; m_subaddress = NULL; + delete m_subaddressAccount; + m_subaddressAccount = 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 8197e567..e3357528 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -24,6 +24,8 @@ class AddressBook; class AddressBookModel; class Subaddress; class SubaddressModel; +class SubaddressAccount; +class SubaddressAccountModel; class Wallet : public QObject { @@ -44,6 +46,8 @@ class Wallet : public QObject Q_PROPERTY(AddressBook * addressBook READ addressBook) Q_PROPERTY(SubaddressModel * subaddressModel READ subaddressModel) Q_PROPERTY(Subaddress * subaddress READ subaddress) + Q_PROPERTY(SubaddressAccountModel * subaddressAccountModel READ subaddressAccountModel) + Q_PROPERTY(SubaddressAccount * subaddressAccount READ subaddressAccount) Q_PROPERTY(bool viewOnly READ viewOnly) Q_PROPERTY(QString secretViewKey READ getSecretViewKey) Q_PROPERTY(QString publicViewKey READ getPublicViewKey) @@ -234,6 +238,12 @@ public: //! returns subadress model SubaddressModel *subaddressModel(); + //! returns subaddress account + SubaddressAccount *subaddressAccount() const; + + //! returns subadress account model + SubaddressAccountModel *subaddressAccountModel() const; + //! generate payment id Q_INVOKABLE QString generatePaymentId() const; @@ -348,6 +358,8 @@ private: mutable AddressBookModel * m_addressBookModel; Subaddress * m_subaddress; mutable SubaddressModel * m_subaddressModel; + SubaddressAccount * m_subaddressAccount; + mutable SubaddressAccountModel * m_subaddressAccountModel; QMutex m_connectionStatusMutex; bool m_connectionStatusRunning; QString m_daemonUsername; diff --git a/src/model/SubaddressAccountModel.cpp b/src/model/SubaddressAccountModel.cpp new file mode 100644 index 00000000..fa2ce0b2 --- /dev/null +++ b/src/model/SubaddressAccountModel.cpp @@ -0,0 +1,66 @@ +#include "SubaddressAccountModel.h" +#include "SubaddressAccount.h" +#include +#include +#include + +SubaddressAccountModel::SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount) + : QAbstractListModel(parent), m_subaddressAccount(subaddressAccount) +{ + qDebug(__FUNCTION__); + connect(m_subaddressAccount,SIGNAL(refreshStarted()),this,SLOT(startReset())); + connect(m_subaddressAccount,SIGNAL(refreshFinished()),this,SLOT(endReset())); +} + +void SubaddressAccountModel::startReset(){ + qDebug("SubaddressAccountModel::startReset"); + beginResetModel(); +} +void SubaddressAccountModel::endReset(){ + qDebug("SubaddressAccountModel::endReset"); + endResetModel(); +} + +int SubaddressAccountModel::rowCount(const QModelIndex &parent) const +{ + return m_subaddressAccount->count(); +} + +QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || (unsigned)index.row() >= m_subaddressAccount->count()) + return {}; + + Monero::SubaddressAccountRow * sr = m_subaddressAccount->getRow(index.row()); + + QVariant result = ""; + switch (role) { + case SubaddressAccountAddressRole: + result = QString::fromStdString(sr->getAddress()); + break; + case SubaddressAccountLabelRole: + result = QString::fromStdString(sr->getLabel()); + break; + case SubaddressAccountBalanceRole: + result = QString::fromStdString(sr->getBalance()); + break; + case SubaddressAccountUnlockedBalanceRole: + result = QString::fromStdString(sr->getUnlockedBalance()); + break; + } + + return result; +} + +QHash SubaddressAccountModel::roleNames() const +{ + static QHash roleNames; + if (roleNames.empty()) + { + roleNames.insert(SubaddressAccountAddressRole, "address"); + roleNames.insert(SubaddressAccountLabelRole, "label"); + roleNames.insert(SubaddressAccountBalanceRole, "balance"); + roleNames.insert(SubaddressAccountUnlockedBalanceRole, "unlockedBalance"); + } + return roleNames; +} diff --git a/src/model/SubaddressAccountModel.h b/src/model/SubaddressAccountModel.h new file mode 100644 index 00000000..e78a3f84 --- /dev/null +++ b/src/model/SubaddressAccountModel.h @@ -0,0 +1,36 @@ +#ifndef SUBADDRESSACCOUNTMODEL_H +#define SUBADDRESSACCOUNTMODEL_H + +#include + +class SubaddressAccount; + +class SubaddressAccountModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum SubaddressAccountRowRole { + SubaddressAccountRole = Qt::UserRole + 1, // for the SubaddressAccountRow object; + SubaddressAccountAddressRole, + SubaddressAccountLabelRole, + SubaddressAccountBalanceRole, + SubaddressAccountUnlockedBalanceRole, + }; + Q_ENUM(SubaddressAccountRowRole) + + SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + +public slots: + void startReset(); + void endReset(); + +private: + SubaddressAccount *m_subaddressAccount; +}; + +#endif // SUBADDRESSACCOUNTMODEL_H