From 50a77307f477fd63a722a1a3fff9bafc38c7f37a Mon Sep 17 00:00:00 2001
From: tobtoht <thotbot@protonmail.com>
Date: Sat, 22 May 2021 15:42:26 +0200
Subject: [PATCH] Account switcher

---
 src/appcontext.cpp                     |   7 ++
 src/assets.qrc                         |   1 +
 src/assets/images/change_account.png   | Bin 0 -> 1939 bytes
 src/dialog/AccountSwitcherDialog.cpp   | 107 +++++++++++++++++++++
 src/dialog/AccountSwitcherDialog.h     |  41 +++++++++
 src/dialog/AccountSwitcherDialog.ui    |  88 ++++++++++++++++++
 src/libwalletqt/Coins.cpp              |   4 +
 src/libwalletqt/SubaddressAccount.cpp  |   5 +
 src/libwalletqt/SubaddressAccount.h    |   1 +
 src/libwalletqt/TransactionHistory.cpp |   1 -
 src/libwalletqt/TransactionHistory.h   |   1 +
 src/libwalletqt/Wallet.cpp             |   2 +
 src/mainwindow.cpp                     |  14 ++-
 src/mainwindow.h                       |   2 +
 src/mainwindow.ui                      |  25 +++++
 src/model/SubaddressAccountModel.cpp   | 123 +++++++++++++++++++------
 src/model/SubaddressAccountModel.h     |  29 ++++--
 src/model/SubaddressModel.cpp          |  12 ++-
 src/model/SubaddressModel.h            |   2 +
 19 files changed, 421 insertions(+), 44 deletions(-)
 create mode 100644 src/assets/images/change_account.png
 create mode 100644 src/dialog/AccountSwitcherDialog.cpp
 create mode 100644 src/dialog/AccountSwitcherDialog.h
 create mode 100644 src/dialog/AccountSwitcherDialog.ui

diff --git a/src/appcontext.cpp b/src/appcontext.cpp
index 6ae6b9f..96b4563 100644
--- a/src/appcontext.cpp
+++ b/src/appcontext.cpp
@@ -41,6 +41,9 @@ AppContext::AppContext(Wallet *wallet)
     connect(this->wallet.get(), &Wallet::connectionStatusChanged, [this]{
         this->nodes->autoConnect();
     });
+    connect(this->wallet.get(), &Wallet::currentSubaddressAccountChanged, [this]{
+        this->updateBalance();
+    });
 
     connect(this, &AppContext::createTransactionError, this, &AppContext::onCreateTransactionError);
 
@@ -54,6 +57,10 @@ AppContext::AppContext(Wallet *wallet)
 
     // force trigger preferredFiat signal for history model
     this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
+
+    connect(this->wallet->history(), &TransactionHistory::txNoteChanged, [this]{
+        this->wallet->history()->refresh(this->wallet->currentSubaddressAccount());
+    });
 }
 
 // ################## Transaction creation ##################
diff --git a/src/assets.qrc b/src/assets.qrc
index 8f7488a..9956d8b 100644
--- a/src/assets.qrc
+++ b/src/assets.qrc
@@ -19,6 +19,7 @@
     <file>assets/images/bitcoin.png</file>
     <file>assets/images/camera_dark.png</file>
     <file>assets/images/camera_white.png</file>
+    <file>assets/images/change_account.png</file>
     <file>assets/images/clock1.png</file>
     <file>assets/images/clock2.png</file>
     <file>assets/images/clock3.png</file>
diff --git a/src/assets/images/change_account.png b/src/assets/images/change_account.png
new file mode 100644
index 0000000000000000000000000000000000000000..f24a4ba220b3284a6ec57e84f44b60ce333810a1
GIT binary patch
literal 1939
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
Z<G*z*J*qF3T-yKu002ovPDHLkV1hg7#BTrq

literal 0
HcmV?d00001

diff --git a/src/dialog/AccountSwitcherDialog.cpp b/src/dialog/AccountSwitcherDialog.cpp
new file mode 100644
index 0000000..dfa66db
--- /dev/null
+++ b/src/dialog/AccountSwitcherDialog.cpp
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#include "AccountSwitcherDialog.h"
+#include "ui_AccountSwitcherDialog.h"
+
+#include "libwalletqt/SubaddressAccount.h"
+#include "utils/Icons.h"
+#include <QMenu>
+
+AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer<AppContext> ctx, QWidget *parent)
+    : QDialog(parent)
+    , ui(new Ui::AccountSwitcherDialog)
+    , m_ctx(std::move(ctx))
+    , m_model(m_ctx->wallet->subaddressAccountModel())
+{
+    ui->setupUi(this);
+
+    ui->accounts->setContextMenuPolicy(Qt::CustomContextMenu);
+
+    m_ctx->wallet->subaddressAccount()->refresh();
+    ui->accounts->setModel(m_ctx->wallet->subaddressAccountModel());
+    ui->accounts->setSelectionMode(QAbstractItemView::SingleSelection);
+    ui->accounts->setSelectionBehavior(QAbstractItemView::SelectRows);
+    ui->accounts->hideColumn(SubaddressAccountModel::Column::Address);
+    ui->accounts->hideColumn(SubaddressAccountModel::Column::UnlockedBalance);
+    ui->accounts->setColumnWidth(SubaddressAccountModel::Column::Label, 200);
+    ui->accounts->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+    ui->accounts->header()->setSectionResizeMode(SubaddressAccountModel::Label, QHeaderView::Stretch);
+
+    connect(ui->accounts->selectionModel(), &QItemSelectionModel::currentChanged, this, &AccountSwitcherDialog::switchAccount);
+    connect(ui->accounts, &QTreeView::customContextMenuRequested, this, &AccountSwitcherDialog::showContextMenu);
+    connect(ui->accounts, &QTreeView::doubleClicked, [this](QModelIndex index){
+        if (!m_model) return;
+        if (!(m_model->flags(index) & Qt::ItemIsEditable)) {
+            this->switchAccount();
+        }
+    });
+
+    connect(ui->btn_newAccount, &QPushButton::clicked, [this]{
+       m_ctx->wallet->addSubaddressAccount("New account");
+       m_ctx->wallet->subaddressAccount()->refresh();
+    });
+
+    connect(m_ctx->wallet.get(), &Wallet::currentSubaddressAccountChanged, this, &AccountSwitcherDialog::updateSelection);
+    connect(m_ctx->wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection);
+
+    this->updateSelection();
+}
+
+void AccountSwitcherDialog::switchAccount() {
+    QModelIndex index = ui->accounts->currentIndex();
+    m_ctx->wallet->switchSubaddressAccount(index.row());
+}
+
+void AccountSwitcherDialog::copyLabel() {
+    auto row = this->currentEntry();
+    if (!row)
+        return;
+
+    Utils::copyToClipboard(QString::fromStdString(row->getLabel()));
+}
+
+void AccountSwitcherDialog::copyBalance() {
+    auto row = this->currentEntry();
+    if (!row)
+        return;
+
+    Utils::copyToClipboard(QString::fromStdString(row->getBalance()));
+}
+
+void AccountSwitcherDialog::editLabel() {
+    QModelIndex index = ui->accounts->currentIndex().siblingAtColumn(m_ctx->wallet->subaddressAccountModel()->Column::Label);
+    ui->accounts->setCurrentIndex(index);
+    ui->accounts->edit(index);
+}
+
+void AccountSwitcherDialog::updateSelection() {
+    qDebug() << "test";
+    QModelIndex index = m_model->index(m_ctx->wallet->currentSubaddressAccount(), 0);
+    ui->accounts->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
+}
+
+void AccountSwitcherDialog::showContextMenu(const QPoint &point) {
+    QModelIndex index = ui->accounts->currentIndex();
+    if (!index.isValid()) {
+        return;
+    }
+
+    auto *menu = new QMenu(ui->accounts);
+
+    menu->addAction("Switch to account", this, &AccountSwitcherDialog::switchAccount);
+    menu->addAction("Copy label", this, &AccountSwitcherDialog::copyLabel);
+    menu->addAction("Copy balance", this, &AccountSwitcherDialog::copyBalance);
+    menu->addAction("Edit label", this, &AccountSwitcherDialog::editLabel);
+
+    menu->popup(ui->accounts->viewport()->mapToGlobal(point));
+}
+
+Monero::SubaddressAccountRow* AccountSwitcherDialog::currentEntry() {
+    QModelIndex index = ui->accounts->currentIndex();
+    return m_ctx->wallet->subaddressAccountModel()->entryFromIndex(index);
+}
+
+AccountSwitcherDialog::~AccountSwitcherDialog() {
+    delete ui;
+}
diff --git a/src/dialog/AccountSwitcherDialog.h b/src/dialog/AccountSwitcherDialog.h
new file mode 100644
index 0000000..59cc79b
--- /dev/null
+++ b/src/dialog/AccountSwitcherDialog.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2020-2021, The Monero Project.
+
+#ifndef FEATHER_ACCOUNTSWITCHERDIALOG_H
+#define FEATHER_ACCOUNTSWITCHERDIALOG_H
+
+#include <QDialog>
+#include "appcontext.h"
+
+#include "model/SubaddressAccountModel.h"
+
+namespace Ui {
+    class AccountSwitcherDialog;
+}
+
+class AccountSwitcherDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit AccountSwitcherDialog(QSharedPointer<AppContext> ctx, QWidget *parent = nullptr);
+    ~AccountSwitcherDialog() override;
+
+private slots:
+    void showContextMenu(const QPoint& point);
+    void updateSelection();
+
+private:
+    void switchAccount();
+    void copyLabel();
+    void copyBalance();
+    void editLabel();
+
+    Monero::SubaddressAccountRow* currentEntry();
+
+    Ui::AccountSwitcherDialog *ui;
+    QSharedPointer<AppContext> m_ctx;
+    SubaddressAccountModel *m_model;
+};
+
+#endif //FEATHER_ACCOUNTSWITCHERDIALOG_H
diff --git a/src/dialog/AccountSwitcherDialog.ui b/src/dialog/AccountSwitcherDialog.ui
new file mode 100644
index 0000000..d332d8e
--- /dev/null
+++ b/src/dialog/AccountSwitcherDialog.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AccountSwitcherDialog</class>
+ <widget class="QDialog" name="AccountSwitcherDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>521</width>
+    <height>275</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Accounts</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeView" name="accounts">
+     <property name="focusPolicy">
+      <enum>Qt::NoFocus</enum>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <attribute name="headerStretchLastSection">
+      <bool>false</bool>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="btn_newAccount">
+       <property name="text">
+        <string>New account</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AccountSwitcherDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AccountSwitcherDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/libwalletqt/Coins.cpp b/src/libwalletqt/Coins.cpp
index c275cab..d0440db 100644
--- a/src/libwalletqt/Coins.cpp
+++ b/src/libwalletqt/Coins.cpp
@@ -41,6 +41,10 @@ void Coins::refresh(quint32 accountIndex)
 
         m_pimpl->refresh();
         for (const auto i : m_pimpl->getAll()) {
+            if (i->subaddrAccount() != accountIndex) {
+                continue;
+            }
+
             m_tinfo.append(new CoinsInfo(i, this));
         }
     }
diff --git a/src/libwalletqt/SubaddressAccount.cpp b/src/libwalletqt/SubaddressAccount.cpp
index db885e8..5aa304a 100644
--- a/src/libwalletqt/SubaddressAccount.cpp
+++ b/src/libwalletqt/SubaddressAccount.cpp
@@ -62,3 +62,8 @@ quint64 SubaddressAccount::count() const
 
     return m_rows.size();
 }
+
+Monero::SubaddressAccountRow* SubaddressAccount::row(int index) const
+{
+    return m_rows.value(index);
+}
\ No newline at end of file
diff --git a/src/libwalletqt/SubaddressAccount.h b/src/libwalletqt/SubaddressAccount.h
index e2db11e..7cc380e 100644
--- a/src/libwalletqt/SubaddressAccount.h
+++ b/src/libwalletqt/SubaddressAccount.h
@@ -22,6 +22,7 @@ public:
     Q_INVOKABLE void setLabel(quint32 accountIndex, const QString &label) const;
     Q_INVOKABLE void refresh() const;
     quint64 count() const;
+    Monero::SubaddressAccountRow* row(int index) const;
 
 signals:
     void refreshStarted() const;
diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp
index 42d2349..e355275 100644
--- a/src/libwalletqt/TransactionHistory.cpp
+++ b/src/libwalletqt/TransactionHistory.cpp
@@ -104,7 +104,6 @@ void TransactionHistory::refresh(quint32 accountIndex)
 void TransactionHistory::setTxNote(const QString &txid, const QString &note)
 {
     m_pimpl->setTxNote(txid.toStdString(), note.toStdString());
-    this->refresh(0); // todo: get actual account index
     emit txNoteChanged();
 }
 
diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h
index c00a387..15b0ba2 100644
--- a/src/libwalletqt/TransactionHistory.h
+++ b/src/libwalletqt/TransactionHistory.h
@@ -60,6 +60,7 @@ private:
     // history contains locked transfers
     mutable bool m_locked;
 
+    quint32 lastAccountIndex = 0;
 };
 
 #endif // TRANSACTIONHISTORY_H
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index 6edffa2..29bd6c9 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -375,6 +375,8 @@ void Wallet::switchSubaddressAccount(quint32 accountIndex)
         }
         m_subaddress->refresh(m_currentSubaddressAccount);
         m_history->refresh(m_currentSubaddressAccount);
+        m_coins->refresh(m_currentSubaddressAccount);
+        this->subaddressModel()->setCurrentSubaddressAcount(m_currentSubaddressAccount);
         emit currentSubaddressAccountChanged();
     }
 }
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index a535881..7be6943 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -20,6 +20,7 @@
 #include "dialog/balancedialog.h"
 #include "dialog/WalletCacheDebugDialog.h"
 #include "dialog/UpdateDialog.h"
+#include "dialog/AccountSwitcherDialog.h"
 #include "constants.h"
 #include "libwalletqt/AddressBook.h"
 #include "utils/AsyncTask.h"
@@ -128,6 +129,10 @@ void MainWindow::initStatusBar() {
     connect(m_statusBtnConnectionStatusIndicator, &StatusBarButton::clicked, this, &MainWindow::showConnectionStatusDialog);
     this->statusBar()->addPermanentWidget(m_statusBtnConnectionStatusIndicator);
 
+    m_statusAccountSwitcher = new StatusBarButton(icons()->icon("change_account.png"), "Account switcher", this);
+    connect(m_statusAccountSwitcher, &StatusBarButton::clicked, this, &MainWindow::showAccountSwitcherDialog);
+    this->statusBar()->addPermanentWidget(m_statusAccountSwitcher);
+
     m_statusBtnPassword = new StatusBarButton(icons()->icon("lock.svg"), "Password", this);
     connect(m_statusBtnPassword, &StatusBarButton::clicked, this, &MainWindow::showPasswordDialog);
     this->statusBar()->addPermanentWidget(m_statusBtnPassword);
@@ -212,6 +217,7 @@ void MainWindow::initMenu() {
 
     // [Wallet]
     connect(ui->actionInformation,  &QAction::triggered, this, &MainWindow::showWalletInfoDialog);
+    connect(ui->actionAccount,      &QAction::triggered, this, &MainWindow::showAccountSwitcherDialog);
     connect(ui->actionPassword,     &QAction::triggered, this, &MainWindow::showPasswordDialog);
     connect(ui->actionSeed,         &QAction::triggered, this, &MainWindow::showSeedDialog);
     connect(ui->actionKeys,         &QAction::triggered, this, &MainWindow::showKeysDialog);
@@ -439,12 +445,13 @@ void MainWindow::onWalletOpened() {
         this->setStatusText("Wallet opened - Searching for node");
 
     // receive page
-    m_ctx->wallet->subaddress()->refresh( m_ctx->wallet->currentSubaddressAccount());
+    m_ctx->wallet->subaddress()->refresh(m_ctx->wallet->currentSubaddressAccount());
     if (m_ctx->wallet->subaddress()->count() == 1) {
         for (int i = 0; i < 10; i++) {
             m_ctx->wallet->subaddress()->addRow(m_ctx->wallet->currentSubaddressAccount(), "");
         }
     }
+    m_ctx->wallet->subaddressModel()->setCurrentSubaddressAcount(m_ctx->wallet->currentSubaddressAccount());
 
     // history page
     m_ctx->wallet->history()->refresh(m_ctx->wallet->currentSubaddressAccount());
@@ -903,6 +910,11 @@ void MainWindow::showWalletCacheDebugDialog() {
     dialog.exec();
 }
 
+void MainWindow::showAccountSwitcherDialog() {
+    AccountSwitcherDialog dialog{m_ctx, this};
+    dialog.exec();
+}
+
 void MainWindow::showNodeExhaustedMessage() {
     // Spawning dialogs inside a lambda can cause system freezes on linux so we have to do it this way ¯\_(ツ)_/¯
 
diff --git a/src/mainwindow.h b/src/mainwindow.h
index e59e4ae..4d14251 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -155,6 +155,7 @@ private slots:
     void showKeysDialog();
     void showViewOnlyDialog();
     void showWalletCacheDebugDialog();
+    void showAccountSwitcherDialog();
 
     void donateButtonClicked();
     void showCalcWindow();
@@ -230,6 +231,7 @@ private:
     ClickableLabel *m_statusLabelBalance;
     QLabel *m_statusLabelStatus;
     QLabel *m_statusLabelNetStats;
+    StatusBarButton *m_statusAccountSwitcher;
     StatusBarButton *m_statusBtnConnectionStatusIndicator;
     StatusBarButton *m_statusBtnPassword;
     StatusBarButton *m_statusBtnPreferences;
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 5d503c8..4ecec6b 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -403,6 +403,7 @@
     <addaction name="actionInformation"/>
     <addaction name="menuAdvanced"/>
     <addaction name="separator"/>
+    <addaction name="actionAccount"/>
     <addaction name="actionPassword"/>
     <addaction name="actionSeed"/>
     <addaction name="actionKeys"/>
@@ -742,6 +743,30 @@
     <string>t</string>
    </property>
   </action>
+  <action name="actiond">
+   <property name="text">
+    <string>d</string>
+   </property>
+  </action>
+  <action name="actionPrimary_account">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Primary account</string>
+   </property>
+  </action>
+  <action name="actionAccount">
+   <property name="text">
+    <string>Account</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
diff --git a/src/model/SubaddressAccountModel.cpp b/src/model/SubaddressAccountModel.cpp
index 8a1f75f..53e9109 100644
--- a/src/model/SubaddressAccountModel.cpp
+++ b/src/model/SubaddressAccountModel.cpp
@@ -7,7 +7,8 @@
 #include <QDebug>
 
 SubaddressAccountModel::SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount)
-    : QAbstractListModel(parent), m_subaddressAccount(subaddressAccount)
+    : QAbstractTableModel(parent)
+    , m_subaddressAccount(subaddressAccount)
 {
     connect(m_subaddressAccount, &SubaddressAccount::refreshStarted, this, &SubaddressAccountModel::startReset);
     connect(m_subaddressAccount, &SubaddressAccount::refreshFinished, this, &SubaddressAccountModel::endReset);
@@ -16,13 +17,26 @@ SubaddressAccountModel::SubaddressAccountModel(QObject *parent, SubaddressAccoun
 void SubaddressAccountModel::startReset(){
     beginResetModel();
 }
+
 void SubaddressAccountModel::endReset(){
     endResetModel();
 }
 
-int SubaddressAccountModel::rowCount(const QModelIndex &) const
+int SubaddressAccountModel::rowCount(const QModelIndex &parent) const
 {
-    return m_subaddressAccount->count();
+    if (parent.isValid()) {
+        return 0;
+    } else {
+        return m_subaddressAccount->count();
+    }
+}
+
+int SubaddressAccountModel::columnCount(const QModelIndex &parent) const
+{
+    if (parent.isValid()) {
+        return 0;
+    }
+    return Column::COUNT;
 }
 
 QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const
@@ -32,24 +46,12 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const
 
     QVariant result;
 
-    bool found = m_subaddressAccount->getRow(index.row(), [&result, &role](const Monero::SubaddressAccountRow &row) {
-        switch (role) {
-        case SubaddressAccountAddressRole:
-            result = QString::fromStdString(row.getAddress());
-            break;
-        case SubaddressAccountLabelRole:
-            result = QString::fromStdString(row.getLabel());
-            break;
-        case SubaddressAccountBalanceRole:
-            result = QString::fromStdString(row.getBalance());
-            break;
-        case SubaddressAccountUnlockedBalanceRole:
-            result = QString::fromStdString(row.getUnlockedBalance());
-            break;
-        default:
-            qCritical() << "Unimplemented role" << role;
+    bool found = m_subaddressAccount->getRow(index.row(), [this, &index, &result, &role](const Monero::SubaddressAccountRow &row) {
+        if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
+            result = parseSubaddressAccountRow(row, index, role);
         }
     });
+
     if (!found) {
         qCritical("%s: internal error: invalid index %d", __FUNCTION__, index.row());
     }
@@ -57,15 +59,80 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const
     return result;
 }
 
-QHash<int, QByteArray> SubaddressAccountModel::roleNames() const
+QVariant SubaddressAccountModel::parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row,
+                                                           const QModelIndex &index, int role) const
 {
-    static QHash<int, QByteArray> roleNames;
-    if (roleNames.empty())
-    {
-        roleNames.insert(SubaddressAccountAddressRole, "address");
-        roleNames.insert(SubaddressAccountLabelRole, "label");
-        roleNames.insert(SubaddressAccountBalanceRole, "balance");
-        roleNames.insert(SubaddressAccountUnlockedBalanceRole, "unlockedBalance");
+    switch (index.column()) {
+        case Number:
+            return QString("#%1").arg(QString::number(index.row()));
+        case Address:
+            return QString::fromStdString(row.getAddress());
+        case Label:
+            return QString::fromStdString(row.getLabel());
+        case Balance:
+            return QString::fromStdString(row.getBalance());
+        case UnlockedBalance:
+            return QString::fromStdString(row.getUnlockedBalance());
+        default:
+            return QVariant();
     }
-    return roleNames;
 }
+
+QVariant SubaddressAccountModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (role != Qt::DisplayRole) {
+        return QVariant();
+    }
+    if (orientation == Qt::Horizontal)
+    {
+        switch (section) {
+            case Address:
+                return QString("Address");
+            case Label:
+                return QString("Label");
+            case Balance:
+                return QString("Balance");
+            case UnlockedBalance:
+                return QString("Spendable balance");
+            default:
+                return QVariant();
+        }
+    }
+    return QVariant();
+}
+
+bool SubaddressAccountModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+    if (index.isValid() && role == Qt::EditRole) {
+        const int row = index.row();
+
+        switch (index.column()) {
+            case Label:
+                m_subaddressAccount->setLabel(row, value.toString());
+                break;
+            default:
+                return false;
+        }
+        emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
+
+        return true;
+    }
+    return false;
+}
+
+
+Qt::ItemFlags SubaddressAccountModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return Qt::ItemIsEnabled;
+
+    if (index.column() == Label)
+        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
+
+    return QAbstractTableModel::flags(index);
+}
+
+Monero::SubaddressAccountRow* SubaddressAccountModel::entryFromIndex(const QModelIndex &index) const {
+    Q_ASSERT(index.isValid() && index.row() < m_subaddressAccount->count());
+    return m_subaddressAccount->row(index.row());
+}
\ No newline at end of file
diff --git a/src/model/SubaddressAccountModel.h b/src/model/SubaddressAccountModel.h
index cbc7019..b49f5ba 100644
--- a/src/model/SubaddressAccountModel.h
+++ b/src/model/SubaddressAccountModel.h
@@ -4,35 +4,44 @@
 #ifndef SUBADDRESSACCOUNTMODEL_H
 #define SUBADDRESSACCOUNTMODEL_H
 
-#include <QAbstractListModel>
+#include "Subaddress.h"
+#include <QAbstractTableModel>
 
 class SubaddressAccount;
 
-class SubaddressAccountModel : public QAbstractListModel
+class SubaddressAccountModel : public QAbstractTableModel
 {
     Q_OBJECT
 
 public:
-    enum SubaddressAccountRowRole {
-        SubaddressAccountRole = Qt::UserRole + 1, // for the SubaddressAccountRow object;
-        SubaddressAccountAddressRole,
-        SubaddressAccountLabelRole,
-        SubaddressAccountBalanceRole,
-        SubaddressAccountUnlockedBalanceRole,
+    enum Column {
+        Number,
+        Address,
+        Label,
+        Balance,
+        UnlockedBalance,
+        COUNT
     };
-    Q_ENUM(SubaddressAccountRowRole)
 
     SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount);
 
     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
-    QHash<int, QByteArray> roleNames() const  override;
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+    Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+    bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+
+    Monero::SubaddressAccountRow* entryFromIndex(const QModelIndex &index) const;
 
 public slots:
     void startReset();
     void endReset();
 
 private:
+    QVariant parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row, const QModelIndex &index, int role) const;
+
     SubaddressAccount *m_subaddressAccount;
 };
 
diff --git a/src/model/SubaddressModel.cpp b/src/model/SubaddressModel.cpp
index 7b170b3..bad56e3 100644
--- a/src/model/SubaddressModel.cpp
+++ b/src/model/SubaddressModel.cpp
@@ -11,9 +11,9 @@
 #include <QBrush>
 
 SubaddressModel::SubaddressModel(QObject *parent, Subaddress *subaddress)
-    : QAbstractTableModel(parent),
-    m_subaddress(subaddress),
-    m_showFullAddresses(false)
+    : QAbstractTableModel(parent)
+    , m_subaddress(subaddress)
+    , m_showFullAddresses(false)
 {
     connect(m_subaddress, &Subaddress::refreshStarted, this, &SubaddressModel::startReset);
     connect(m_subaddress, &Subaddress::refreshFinished, this, &SubaddressModel::endReset);
@@ -143,7 +143,7 @@ bool SubaddressModel::setData(const QModelIndex &index, const QVariant &value, i
 
         switch (index.column()) {
             case Label:
-                m_subaddress->setLabel(0, row, value.toString()); // Todo: get actual account index
+                m_subaddress->setLabel(m_currentSubaddressAcount, row, value.toString());
                 break;
             default:
                 return false;
@@ -180,6 +180,10 @@ int SubaddressModel::unusedLookahead() const {
     return m_subaddress->unusedLookahead();
 }
 
+void SubaddressModel::setCurrentSubaddressAcount(quint32 accountIndex) {
+    m_currentSubaddressAcount = accountIndex;
+}
+
 Monero::SubaddressRow* SubaddressModel::entryFromIndex(const QModelIndex &index) const {
     Q_ASSERT(index.isValid() && index.row() < m_subaddress->count());
     return m_subaddress->row(index.row());
diff --git a/src/model/SubaddressModel.h b/src/model/SubaddressModel.h
index 0b8eb0c..3056c02 100644
--- a/src/model/SubaddressModel.h
+++ b/src/model/SubaddressModel.h
@@ -40,6 +40,7 @@ public:
 
     Monero::SubaddressRow* entryFromIndex(const QModelIndex &index) const;
 
+    void setCurrentSubaddressAcount(quint32 accountIndex);
     int unusedLookahead() const;
 
 public slots:
@@ -51,6 +52,7 @@ private:
     QVariant parseSubaddressRow(const Monero::SubaddressRow &subaddress, const QModelIndex &index, int role) const;
 
     bool m_showFullAddresses;
+    quint32 m_currentSubaddressAcount;
 };
 
 #endif // SUBADDRESSMODEL_H