diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7a12921..ae03159 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" #include "addressbook.h" +#include "viewalladdresses.h" #include "ui_mainwindow.h" #include "ui_mobileappconnector.h" #include "ui_addressbook.h" @@ -9,6 +10,7 @@ #include "ui_settings.h" #include "ui_turnstile.h" #include "ui_turnstileprogress.h" +#include "ui_viewalladdresses.h" #include "rpc.h" #include "balancestablemodel.h" #include "settings.h" @@ -425,15 +427,15 @@ void MainWindow::setupStatusBar() { if (!msg.isEmpty() && msg.startsWith(Settings::txidStatusMessage)) { auto txid = msg.split(":")[1].trimmed(); - menu.addAction("Copy txid", [=]() { + menu.addAction(tr("Copy txid"), [=]() { QGuiApplication::clipboard()->setText(txid); }); - menu.addAction("View tx on block explorer", [=]() { + menu.addAction(tr("View tx on block explorer"), [=]() { Settings::openTxInExplorer(txid); }); } - menu.addAction("Refresh", [=]() { + menu.addAction(tr("Refresh"), [=]() { rpc->refresh(true); }); QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height()); @@ -980,7 +982,7 @@ void MainWindow::exportKeys(QString addr) { Settings::saveRestore(&d); - pui.privKeyTxt->setPlainText(tr("Loading...")); + pui.privKeyTxt->setPlainText(tr("This might take several minutes. Loading...")); pui.privKeyTxt->setReadOnly(true); pui.privKeyTxt->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap); @@ -1306,7 +1308,45 @@ void MainWindow::setupReceiveTab() { }); // View all addresses goes to "View all private keys" - QObject::connect(ui->btnViewAllAddresses, &QPushButton::clicked, this, &MainWindow::exportAllKeys); + QObject::connect(ui->btnViewAllAddresses, &QPushButton::clicked, [=] () { + // If there's no RPC, return + if (!getRPC()) + return; + + QDialog d(this); + Ui_ViewAddressesDialog viewaddrs; + viewaddrs.setupUi(&d); + Settings::saveRestore(&d); + Settings::saveRestoreTableHeader(viewaddrs.tblAddresses, &d, "viewalladdressestable"); + + ViewAllAddressesModel model(viewaddrs.tblAddresses, *getRPC()->getAllTAddresses(), getRPC()); + viewaddrs.tblAddresses->setModel(&model); + + QObject::connect(viewaddrs.btnExportAll, &QPushButton::clicked, this, &MainWindow::exportAllKeys); + + viewaddrs.tblAddresses->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(viewaddrs.tblAddresses, &QTableView::customContextMenuRequested, [=] (QPoint pos) { + QModelIndex index = viewaddrs.tblAddresses->indexAt(pos); + if (index.row() < 0) return; + + index = index.sibling(index.row(), 0); + QString addr = viewaddrs.tblAddresses->model()->data(index).toString(); + + QMenu menu(this); + menu.addAction(tr("Export Private Key"), [=] () { + if (addr.isEmpty()) + return; + + this->exportKeys(addr); + }); + menu.addAction(tr("Copy Address"), [=]() { + QGuiApplication::clipboard()->setText(addr); + }); + menu.exec(viewaddrs.tblAddresses->viewport()->mapToGlobal(pos)); + }); + + d.exec(); + }); QObject::connect(ui->rdioZSAddr, &QRadioButton::toggled, addZAddrsToComboList(true)); @@ -1423,9 +1463,11 @@ void MainWindow::updateTAddrCombo(bool checked) { auto utxos = this->rpc->getUTXOs(); ui->listReceiveAddresses->clear(); - // Maintain a set of addresses so we don't duplicate any. + // Maintain a set of addresses so we don't duplicate any, because we'll be adding + // t addresses multiple times QSet addrs; + // 1. Add all t addresses that have a balance std::for_each(utxos->begin(), utxos->end(), [=, &addrs](auto& utxo) { auto addr = utxo.address; if (Settings::isTAddress(addr) && !addrs.contains(addr)) { @@ -1436,17 +1478,30 @@ void MainWindow::updateTAddrCombo(bool checked) { } }); + // 2. Add all t addresses that have a label auto allTaddrs = this->rpc->getAllTAddresses(); QSet labels; for (auto p : AddressBook::getInstance()->getAllAddressLabels()) { labels.insert(p.second); } - std::for_each(allTaddrs->begin(), allTaddrs->end(), [=] (auto& taddr) { + std::for_each(allTaddrs->begin(), allTaddrs->end(), [=, &addrs] (auto& taddr) { // If the address is in the address book, add it. if (labels.contains(taddr) && !addrs.contains(taddr)) { + addrs.insert(taddr); ui->listReceiveAddresses->addItem(taddr, 0); } }); + + // 3. Add all t-addresses. We won't add more than 20 total t-addresses, + // since it will overwhelm the dropdown + for (int i=0; addrs.size() < 20 && i < allTaddrs->size(); i++) { + auto addr = allTaddrs->at(i); + if (!addrs.contains(addr)) { + addrs.insert(addr); + // Balance is zero since it has not been previously added + ui->listReceiveAddresses->addItem(addr, 0); + } + } } }; diff --git a/src/settings.cpp b/src/settings.cpp index b251765..66bdef7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -176,6 +176,14 @@ void Settings::saveRestore(QDialog* d) { }); } +void Settings::saveRestoreTableHeader(QTableView* table, QDialog* d, QString tablename) { + table->horizontalHeader()->restoreState(QSettings().value(tablename).toByteArray()); + + QObject::connect(d, &QDialog::finished, [=](auto) { + QSettings().setValue(tablename, table->horizontalHeader()->saveState()); + }); +} + void Settings::openAddressInExplorer(QString address) { QString url; if (Settings::getInstance()->isTestnet()) { diff --git a/src/settings.h b/src/settings.h index 245ae29..612fe4b 100644 --- a/src/settings.h +++ b/src/settings.h @@ -82,6 +82,7 @@ public: static const QString txidStatusMessage; static void saveRestore(QDialog* d); + static void saveRestoreTableHeader(QTableView* table, QDialog* d, QString tablename) ; static void openAddressInExplorer(QString address); static void openTxInExplorer(QString txid); diff --git a/src/viewalladdresses.cpp b/src/viewalladdresses.cpp new file mode 100644 index 0000000..7b7af7d --- /dev/null +++ b/src/viewalladdresses.cpp @@ -0,0 +1,38 @@ +#include "viewalladdresses.h" +#include "settings.h" + +ViewAllAddressesModel::ViewAllAddressesModel(QTableView *parent, QList taddrs, RPC* rpc) + : QAbstractTableModel(parent) { + headers << tr("Address") << tr("Balance (%1)").arg(Settings::getTokenName()); + addresses = taddrs; + this->rpc = rpc; +} + + +int ViewAllAddressesModel::rowCount(const QModelIndex&) const { + return addresses.size(); +} + +int ViewAllAddressesModel::columnCount(const QModelIndex&) const { + return headers.size(); +} + +QVariant ViewAllAddressesModel::data(const QModelIndex &index, int role) const { + QString address = addresses.at(index.row()); + if (role == Qt::DisplayRole) { + switch(index.column()) { + case 0: return address; + case 1: return rpc->getAllBalances()->value(address, 0.0); + } + } + return QVariant(); +} + + +QVariant ViewAllAddressesModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + return headers.at(section); + } + + return QVariant(); +} diff --git a/src/viewalladdresses.h b/src/viewalladdresses.h new file mode 100644 index 0000000..4e9e3d4 --- /dev/null +++ b/src/viewalladdresses.h @@ -0,0 +1,24 @@ +#ifndef VIEWALLADDRESSES_H +#define VIEWALLADDRESSES_H + +#include "precompiled.h" +#include "rpc.h" + +class ViewAllAddressesModel : public QAbstractTableModel { + +public: + ViewAllAddressesModel(QTableView* parent, QList taddrs, RPC* rpc); + ~ViewAllAddressesModel() = default; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList addresses; + QStringList headers; + RPC* rpc; +}; + +#endif \ No newline at end of file diff --git a/src/viewalladdresses.ui b/src/viewalladdresses.ui new file mode 100644 index 0000000..fae5ecf --- /dev/null +++ b/src/viewalladdresses.ui @@ -0,0 +1,81 @@ + + + ViewAddressesDialog + + + + 0 + 0 + 400 + 300 + + + + All Addresses + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + Export All Keys + + + + + + + QAbstractItemView::SingleSelection + + + true + + + + + + + + + buttonBox + accepted() + ViewAddressesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ViewAddressesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/zec-qt-wallet.pro b/zec-qt-wallet.pro index c439afd..493e303 100644 --- a/zec-qt-wallet.pro +++ b/zec-qt-wallet.pro @@ -57,7 +57,8 @@ SOURCES += \ src/mobileappconnector.cpp \ src/recurring.cpp \ src/requestdialog.cpp \ - src/memoedit.cpp + src/memoedit.cpp \ + src/viewalladdresses.cpp HEADERS += \ src/mainwindow.h \ @@ -82,7 +83,8 @@ HEADERS += \ src/mobileappconnector.h \ src/recurring.h \ src/requestdialog.h \ - src/memoedit.h + src/memoedit.h \ + src/viewalladdresses.h FORMS += \ src/mainwindow.ui \ @@ -95,6 +97,7 @@ FORMS += \ src/turnstileprogress.ui \ src/privkey.ui \ src/memodialog.ui \ + src/viewalladdresses.ui \ src/connection.ui \ src/zboard.ui \ src/addressbook.ui \