From 44554f7ff45fa077260abe4f66167ebe0b120afd Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Mon, 15 Oct 2018 14:59:48 -0700 Subject: [PATCH] Fix ZEC double precision display, add validations --- src/balancestablemodel.cpp | 2 +- src/main.cpp | 12 +++--- src/mainwindow.cpp | 17 ++++++++- src/mainwindow.h | 4 ++ src/mainwindow.ui | 20 ++++++---- src/precompiled.h | 1 + src/rpc.cpp | 21 ++++++----- src/sendtab.cpp | 77 +++++++++++++++++++++++++++++++------- src/txtablemodel.cpp | 2 +- src/ui_mainwindow.h | 13 +++++-- 10 files changed, 128 insertions(+), 41 deletions(-) diff --git a/src/balancestablemodel.cpp b/src/balancestablemodel.cpp index 11de043..246d028 100644 --- a/src/balancestablemodel.cpp +++ b/src/balancestablemodel.cpp @@ -18,7 +18,7 @@ void BalancesTableModel::setNewData(const QMap* balances, delete modeldata; modeldata = new QList>(); std::for_each(balances->constKeyValueBegin(), balances->constKeyValueEnd(), [=] (auto it) { - modeldata->push_back(std::make_tuple(it.first, QString::number(it.second, 'f'))); + modeldata->push_back(std::make_tuple(it.first, QString::number(it.second, 'g', 8))); }); // And then update the data diff --git a/src/main.cpp b/src/main.cpp index 61f1ff5..5ff9e64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,13 @@ int main(int argc, char *argv[]) { + QApplication a(argc, argv); + + #ifdef Q_OS_LINUX + QFontDatabase::addApplicationFont(":/fonts/res/Ubuntu-R.ttf"); + qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false)); + #endif + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); @@ -10,11 +17,6 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("adityapk.com"); QCoreApplication::setApplicationName("zcash-qt-wallet"); - QApplication a(argc, argv); - #ifdef Q_OS_LINUX - QFontDatabase::addApplicationFont(":/fonts/res/Ubuntu-R.ttf"); - qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false)); - #endif MainWindow w; w.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8a8211a..ddb287b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -70,7 +70,10 @@ MainWindow::MainWindow(QWidget *parent) : }); // Set up exit action - QObject::connect(ui->actionExit, &QAction::triggered, [=] { this->close(); }); + QObject::connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close); + + // Set up donate action + QObject::connect(ui->actionDonate, &QAction::triggered, this, &MainWindow::donate); // Set up about action QObject::connect(ui->actionAbout, &QAction::triggered, [=] () { @@ -97,6 +100,18 @@ MainWindow::MainWindow(QWidget *parent) : rpc->refresh(); } +void MainWindow::donate() { + // Set up a donation to me :) + ui->Address1->setText("zcEgrceTwvoiFdEvPWcsJHAMrpLsprMF6aRJiQa3fan5ZphyXLPuHghnEPrEPRoEVzUy65GnMVyCTRdkT6BYBepnXh6NBYs"); + ui->Address1->setCursorPosition(0); + ui->Amount1->setText("0.01"); + + ui->statusBar->showMessage("Donate 0.01 ZEC to support zcash-qt-wallet"); + + // And switch to the send tab. + ui->tabWidget->setCurrentIndex(1); +} + void MainWindow::setupBalancesTab() { ui->unconfirmedWarning->setVisible(false); diff --git a/src/mainwindow.h b/src/mainwindow.h index d94fe5d..81ea8a9 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -42,6 +42,10 @@ private: void addAddressSection(); void maxAmountChecked(int checked); + QString doSendTxValidations(QString fromAddr, QList> toAddrs); + + void donate(); + RPC* rpc; Settings* settings; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 3346874..667a924 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 838 - 595 + 889 + 603 @@ -18,7 +18,7 @@ - 1 + 0 @@ -247,8 +247,8 @@ 0 0 - 774 - 276 + 772 + 218 @@ -610,8 +610,8 @@ 0 0 - 838 - 21 + 889 + 22 @@ -626,6 +626,7 @@ Help + @@ -647,6 +648,11 @@ Settings + + + Donate + + diff --git a/src/precompiled.h b/src/precompiled.h index 2d18169..1d4bc7e 100644 --- a/src/precompiled.h +++ b/src/precompiled.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc.cpp b/src/rpc.cpp index 4c6c572..dfbb474 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -341,7 +341,7 @@ void RPC::refreshBalances() { UnspentOutput( qsAddr, QString::fromStdString(it["txid"]), - QString::number(it["amount"].get(), 'f', 8), + QString::number(it["amount"].get(), 'g', 8), confirmations ) ); @@ -361,7 +361,7 @@ void RPC::refreshBalances() { ui->inputsCombo->clear(); auto i = allBalances->constBegin(); while (i != allBalances->constEnd()) { - QString item = i.key() % "(" % QString::number(i.value(), 'f') % " ZEC)"; + QString item = i.key() % "(" % QString::number(i.value(), 'g', 8) % " ZEC)"; ui->inputsCombo->addItem(item); if (item.startsWith(lastFromAddr)) ui->inputsCombo->setCurrentText(item); @@ -417,13 +417,7 @@ void RPC::refreshTxStatus(const QString& newOpid) { }; doRPC(payload, [=] (const json& reply) { - // If there is some op that we are watching, then show the loading bar, otherwise hide it - if (watchingOps.isEmpty()) { - main->loadingLabel->setVisible(false); - } else { - main->loadingLabel->setVisible(true); - main->loadingLabel->setToolTip(QString::number(watchingOps.size()) + " tx computing"); - } + int numExecuting = 0; // There's an array for each item in the status for (auto& it : reply.get()) { @@ -462,8 +456,17 @@ void RPC::refreshTxStatus(const QString& newOpid) { } else if (status == "executing") { // If the operation is executing, then watch every second. txTimer->start(1 * 1000); + numExecuting++; } } } + + // If there is some op that we are watching, then show the loading bar, otherwise hide it + if (numExecuting == 0) { + main->loadingLabel->setVisible(false); + } else { + main->loadingLabel->setVisible(true); + main->loadingLabel->setToolTip(QString::number(numExecuting) + " tx computing"); + } }); } diff --git a/src/sendtab.cpp b/src/sendtab.cpp index 35ac97a..8027b4d 100644 --- a/src/sendtab.cpp +++ b/src/sendtab.cpp @@ -66,7 +66,7 @@ void MainWindow::setDefaultPayFrom() { void MainWindow::inputComboTextChanged(const QString& text) { auto bal = rpc->getAllBalances()->value(text.split("(")[0].trimmed()); - auto balFmt = QString::number(bal, 'f', 8) + " ZEC"; + auto balFmt = QString::number(bal, 'g', 8) + " ZEC"; ui->sendAddressBalance->setText(balFmt); } @@ -163,7 +163,7 @@ void MainWindow::maxAmountChecked(int checked) { auto maxamount = rpc->getAllBalances()->value(addr) - sumAllAmounts; maxamount = (maxamount < 0) ? 0 : maxamount; - ui->Amount1->setText(QString::number(maxamount, 'f')); + ui->Amount1->setText(QString::number(maxamount, 'g', 8)); } else if (checked == Qt::Unchecked) { // Just remove the readonly part, don't change the content ui->Amount1->setReadOnly(false); @@ -179,6 +179,34 @@ void MainWindow::sendButton() { return splitted; }; + // Gather the from / to addresses + QString fromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed(); + + QList> toAddrs; + // For each addr/amt in the sendTo tab + int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that + for (int i=0; i < totalItems; i++) { + auto addr = ui->sendToWidgets->findChild(QString("Address") % QString::number(i+1))->text().trimmed(); + auto amt = ui->sendToWidgets->findChild(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble(); + toAddrs.push_back(QPair(addr, amt)); + } + + QString error = doSendTxValidations(fromAddr, toAddrs); + if (!error.isEmpty()) { + // Something went wrong, so show an error and exit + QMessageBox msg( + QMessageBox::Critical, + "Transaction Error", + error, + QMessageBox::Ok, + this + ); + + msg.exec(); + // abort the Tx + return; + } + // Get all the addresses and amounts json allRecepients = json::array(); @@ -197,16 +225,15 @@ void MainWindow::sendButton() { delete amt; } - // For each addr/amt in the sendTo tab - int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that - for (int i=0; i < totalItems; i++) { - auto addr = ui->sendToWidgets->findChild(QString("Address") % QString::number(i+1)); - auto amt = ui->sendToWidgets->findChild(QString("Amount") % QString::number(i+1)); + // For each addr/amt + //std::for_each(toAddr.begin(), toAddr.end(), [&] (auto toAddr) { + for (int i=0; i < toAddrs.size(); i++) { + auto toAddr = toAddrs[i]; // Construct the JSON params json rec = json::object(); - rec["address"] = addr->text().toStdString(); - rec["amount"] = amt->text().toDouble(); + rec["address"] = toAddr.first.toStdString(); + rec["amount"] = toAddr.second; allRecepients.push_back(rec); // Add new Address widgets instead of the same one. @@ -214,24 +241,24 @@ void MainWindow::sendButton() { auto Addr = new QLabel(confirm.sendToAddrs); Addr->setObjectName(QString("Addr") % QString::number(i + 1)); Addr->setWordWrap(true); - Addr->setText(fnSplitAddressForWrap(addr->text())); + Addr->setText(fnSplitAddressForWrap(toAddr.first)); confirm.gridLayout->addWidget(Addr, i, 0, 1, 1); auto Amt = new QLabel(confirm.sendToAddrs); Amt->setObjectName(QString("Amt") % QString::number(i + 1)); - Amt->setText(amt->text() % " ZEC"); + Amt->setText(QString::number(toAddr.second, 'g', 8) % " ZEC"); Amt->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); confirm.gridLayout->addWidget(Amt, i, 1, 1, 1); } } + // Add sender json params = json::array(); - params.push_back(ui->inputsCombo->currentText().split("(")[0].trimmed().toStdString()); + params.push_back(fromAddr.toStdString()); params.push_back(allRecepients); // And show it in the confirm dialog - auto fromAddr = ui->inputsCombo->currentText().split("(")[0]; confirm.sendFrom->setText(fnSplitAddressForWrap(fromAddr)); // Show the dialog and submit it if the user confirms @@ -249,8 +276,32 @@ void MainWindow::sendButton() { } } +QString MainWindow::doSendTxValidations(QString fromAddr, QList> toAddrs) { + // 1. Addresses are valid format. + QRegExp zcexp("^zc[a-z0-9]{93}$", Qt::CaseInsensitive); + QRegExp zsexp("^zc[a-z0-9]{76}$", Qt::CaseInsensitive); + QRegExp texp("^t[a-z0-9]{34}$", Qt::CaseInsensitive); + + auto matchesAnyAddr = [&] (QString addr) { + return zcexp.exactMatch(addr) || + texp.exactMatch(addr) || + zsexp.exactMatch(addr); + }; + + + if (!matchesAnyAddr(fromAddr)) return QString("From Address is Invalid"); + + for (auto toAddr = toAddrs.begin(); toAddr != toAddrs.end(); toAddr++) { + if (!matchesAnyAddr(toAddr->first)) + return QString("To Address ") % toAddr->first % " is Invalid"; + }; + + return QString(); +} + void MainWindow::cancelButton() { removeExtraAddresses(); // Back to the balances tab ui->tabWidget->setCurrentIndex(0); } + diff --git a/src/txtablemodel.cpp b/src/txtablemodel.cpp index 8f9796a..1c1c6bb 100644 --- a/src/txtablemodel.cpp +++ b/src/txtablemodel.cpp @@ -53,7 +53,7 @@ void TxTableModel::setNewData(QList* data) { case 0: return modeldata->at(index.row()).type; case 1: return modeldata->at(index.row()).address; case 2: return modeldata->at(index.row()).datetime; - case 3: return QVariant(QString::number(modeldata->at(index.row()).amount, 'f') % " ZEC"); + case 3: return QVariant(QString::number(modeldata->at(index.row()).amount, 'g', 8) % " ZEC"); } } diff --git a/src/ui_mainwindow.h b/src/ui_mainwindow.h index 0362494..e8c54bf 100644 --- a/src/ui_mainwindow.h +++ b/src/ui_mainwindow.h @@ -43,6 +43,7 @@ public: QAction *actionExit; QAction *actionAbout; QAction *actionSettings; + QAction *actionDonate; QWidget *centralWidget; QGridLayout *gridLayout_3; QTabWidget *tabWidget; @@ -134,13 +135,15 @@ public: { if (MainWindow->objectName().isEmpty()) MainWindow->setObjectName(QStringLiteral("MainWindow")); - MainWindow->resize(838, 595); + MainWindow->resize(889, 603); actionExit = new QAction(MainWindow); actionExit->setObjectName(QStringLiteral("actionExit")); actionAbout = new QAction(MainWindow); actionAbout->setObjectName(QStringLiteral("actionAbout")); actionSettings = new QAction(MainWindow); actionSettings->setObjectName(QStringLiteral("actionSettings")); + actionDonate = new QAction(MainWindow); + actionDonate->setObjectName(QStringLiteral("actionDonate")); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName(QStringLiteral("centralWidget")); gridLayout_3 = new QGridLayout(centralWidget); @@ -327,7 +330,7 @@ public: sendToScrollArea->setWidgetResizable(true); sendToWidgets = new QWidget(); sendToWidgets->setObjectName(QStringLiteral("sendToWidgets")); - sendToWidgets->setGeometry(QRect(0, 0, 774, 276)); + sendToWidgets->setGeometry(QRect(0, 0, 772, 218)); sendToLayout = new QVBoxLayout(sendToWidgets); sendToLayout->setSpacing(6); sendToLayout->setContentsMargins(11, 11, 11, 11); @@ -586,7 +589,7 @@ public: MainWindow->setCentralWidget(centralWidget); menuBar = new QMenuBar(MainWindow); menuBar->setObjectName(QStringLiteral("menuBar")); - menuBar->setGeometry(QRect(0, 0, 838, 21)); + menuBar->setGeometry(QRect(0, 0, 889, 22)); menuBalance = new QMenu(menuBar); menuBalance->setObjectName(QStringLiteral("menuBalance")); menuHelp = new QMenu(menuBar); @@ -618,11 +621,12 @@ public: menuBalance->addAction(actionSettings); menuBalance->addSeparator(); menuBalance->addAction(actionExit); + menuHelp->addAction(actionDonate); menuHelp->addAction(actionAbout); retranslateUi(MainWindow); - tabWidget->setCurrentIndex(1); + tabWidget->setCurrentIndex(0); QMetaObject::connectSlotsByName(MainWindow); @@ -634,6 +638,7 @@ public: actionExit->setText(QApplication::translate("MainWindow", "Exit", nullptr)); actionAbout->setText(QApplication::translate("MainWindow", "About", nullptr)); actionSettings->setText(QApplication::translate("MainWindow", "Settings", nullptr)); + actionDonate->setText(QApplication::translate("MainWindow", "Donate", nullptr)); groupBox->setTitle(QApplication::translate("MainWindow", "Summary", nullptr)); label->setText(QApplication::translate("MainWindow", "Shielded", nullptr)); balSheilded->setText(QString());