diff --git a/src/confirm.ui b/src/confirm.ui
index 5473136..ea6cce7 100644
--- a/src/confirm.ui
+++ b/src/confirm.ui
@@ -59,6 +59,13 @@
+ -
+
+
+ TextLabel
+
+
+
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 1594b1e..fdd0346 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -29,18 +29,23 @@ MainWindow::MainWindow(QWidget *parent) :
ui->statusBar->addPermanentWidget(loadingLabel);
loadingLabel->setVisible(false);
+ // Custom status bar menu
ui->statusBar->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->statusBar, &QStatusBar::customContextMenuRequested, [=](QPoint pos) {
auto msg = ui->statusBar->currentMessage();
- if (!msg.isEmpty() && msg.startsWith(Utils::txidStatusMessage)) {
- QMenu menu(this);
+ QMenu menu(this);
+
+ if (!msg.isEmpty() && msg.startsWith(Utils::txidStatusMessage)) {
menu.addAction("Copy txid", [=]() {
QGuiApplication::clipboard()->setText(msg.split(":")[1].trimmed());
});
- QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height());
- menu.exec(gpos);
}
-
+
+ menu.addAction("Refresh", [=]() {
+ rpc->refresh();
+ });
+ QPoint gpos(mapToGlobal(pos).x(), mapToGlobal(pos).y() + this->height() - ui->statusBar->height());
+ menu.exec(gpos);
});
statusLabel = new QLabel();
diff --git a/src/mainwindow.h b/src/mainwindow.h
index c64460f..4f2466a 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -7,8 +7,16 @@
class RPC;
class Settings;
+// Struct used to hold destination info when sending a Tx.
+struct ToFields {
+ QString addr;
+ double amount;
+ QString txtMemo;
+ QString encodedMemo;
+};
+
namespace Ui {
-class MainWindow;
+ class MainWindow;
}
class MainWindow : public QMainWindow
@@ -40,7 +48,9 @@ private:
void addAddressSection();
void maxAmountChecked(int checked);
- QString doSendTxValidations(QString fromAddr, QList> toAddrs);
+ void memoButtonClicked(int number);
+
+ QString doSendTxValidations(QString fromAddr, QList toAddrs);
void donate();
void importPrivKeys();
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 8c79e83..4b8a4c2 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -216,7 +216,7 @@
-
- Address Balance:
+ Address Balance
@@ -259,7 +259,22 @@
Send To
+
+ false
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
-
@@ -273,8 +288,8 @@
0
0
- 823
- 226
+ 841
+ 288
@@ -327,7 +342,7 @@
-
- Maximum Available
+ Max Available
@@ -344,8 +359,33 @@
+ -
+
+
+ true
+
+
+
+
+
+ Memo
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
@@ -413,47 +453,44 @@
-
-
-
- Fees
-
-
-
-
-
-
-
-
-
- Fee
-
-
-
- -
-
-
-
-
-
- true
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
+
+ -
+
+
+ Fee
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
-
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 51c600c..f6c88e9 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -30,7 +30,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
QObject::connect(timer, &QTimer::timeout, [=]() {
refresh();
});
- timer->start(10 * 1000);
+ timer->start(Utils::updateSpeed);
// Set up the timer to watch for tx status
txTimer = new QTimer(main);
@@ -38,7 +38,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
refreshTxStatus();
});
// Start at every 10s. When an operation is pending, this will change to every second
- txTimer->start(10 * 1000);
+ txTimer->start(Utils::updateSpeed);
}
RPC::~RPC() {
@@ -457,7 +457,7 @@ void RPC::refreshTxStatus(const QString& newOpid) {
main->loadingLabel->setVisible(false);
watchingOps.remove(id);
- txTimer->start(10 * 1000);
+ txTimer->start(Utils::updateSpeed);
// Refresh balances to show unconfirmed balances
refresh();
diff --git a/src/rpc.h b/src/rpc.h
index f4cf2a0..7f505a6 100644
--- a/src/rpc.h
+++ b/src/rpc.h
@@ -32,6 +32,7 @@ public:
void newZaddr (const std::function& cb);
void newTaddr (const std::function& cb);
+
private:
void doRPC (const json& payload, const std::function& cb);
void doSendRPC (const json& payload, const std::function& cb);
diff --git a/src/sendtab.cpp b/src/sendtab.cpp
index 4bfa568..1dbab2d 100644
--- a/src/sendtab.cpp
+++ b/src/sendtab.cpp
@@ -33,11 +33,19 @@ void MainWindow::setupSendTab() {
// Max available Checkbox
QObject::connect(ui->Max1, &QCheckBox::stateChanged, this, &MainWindow::maxAmountChecked);
+ // The first Memo button
+ QObject::connect(ui->MemoBtn1, &QPushButton::clicked, [=] () {
+ memoButtonClicked(1);
+ });
+
// Set up focus enter to set fees
QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=] (int pos) {
if (pos == 1) {
// Set the fees
ui->sendTxFees->setText("0.0001 " + Utils::getTokenName());
+
+ // Set focus to the first address box
+ ui->Address1->setFocus();
}
});
@@ -117,30 +125,78 @@ void MainWindow::addAddressSection() {
// Create the validator for send to/amount fields
auto amtValidator = new QDoubleValidator(0, 21000000, 8, Amount1);
Amount1->setValidator(amtValidator);
-
horizontalLayout_13->addWidget(Amount1);
- auto horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ auto horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_13->addItem(horizontalSpacer_4);
+
+
+ auto MemoBtn1 = new QPushButton(verticalGroupBox);
+ MemoBtn1->setObjectName(QString("MemoBtn") % QString::number(itemNumber));
+ MemoBtn1->setText("Memo");
+ // Connect Memo Clicked button
+ QObject::connect(MemoBtn1, &QPushButton::clicked, [=] () {
+ memoButtonClicked(itemNumber);
+ });
+ horizontalLayout_13->addWidget(MemoBtn1);
+
sendAddressLayout->addLayout(horizontalLayout_13);
+ auto MemoTxt1 = new QLabel(verticalGroupBox);
+ MemoTxt1->setObjectName(QString("MemoTxt") % QString::number(itemNumber));
+ QFont font1;
+ font1.setPointSize(10);
+ MemoTxt1->setFont(font1);
+ sendAddressLayout->addWidget(MemoTxt1);
+
ui->sendToLayout->insertWidget(itemNumber-1, verticalGroupBox);
+ // Set focus into the address
+ Address1->setFocus();
+
// Delay the call to scroll to allow the scroll window to adjust
QTimer::singleShot(10, [=] () {ui->sendToScrollArea->ensureWidgetVisible(ui->addAddressButton);});
}
+void MainWindow::memoButtonClicked(int number) {
+ // Memos can only be used with zAddrs. So check that first
+ auto addr = ui->sendToWidgets->findChild(QString("Address") + QString::number(number));
+ if (!addr->text().trimmed().startsWith("z")) {
+ QMessageBox msg(QMessageBox::Critical, "Memos can only be used with z Addresses",
+ "The Memo field can only be used with a z Address.\n" + addr->text() + "\ndoesn't look like a z Address",
+ QMessageBox::Ok, this);
+
+ msg.exec();
+ return;
+ }
+
+ auto memoTxt = ui->sendToWidgets->findChild(QString("MemoTxt") + QString::number(number));
+ // Get the current memo if it exists
+ QString currentMemo = memoTxt->text();
+
+ // Ref to see if the button was clicked
+ bool ok;
+ QString newMemo = QInputDialog::getText(this, "Memo",
+ "Please type a memo to include with the amount. The memo will be visible to the recepient",
+ QLineEdit::Normal, currentMemo, &ok);
+ if (ok) {
+ memoTxt->setText(newMemo);
+ }
+}
+
void MainWindow::removeExtraAddresses() {
// The last one is a spacer, so ignore that
int totalItems = ui->sendToWidgets->children().size() - 2;
- // Clear the first field
+ // Clear the first recepient fields
auto addr = ui->sendToWidgets->findChild(QString("Address1"));
addr->clear();
auto amt = ui->sendToWidgets->findChild(QString("Amount1"));
amt->clear();
auto max = ui->sendToWidgets->findChild(QString("Max1"));
max->setChecked(false);
+ auto memo = ui->sendToWidgets->findChild(QString("MemoTxt1"));
+ memo->clear();
// Start the deletion after the first item, since we want to keep 1 send field there all there
for (int i=1; i < totalItems; i++) {
@@ -179,6 +235,7 @@ void MainWindow::maxAmountChecked(int checked) {
}
}
+
void MainWindow::sendButton() {
auto fnSplitAddressForWrap = [=] (const QString& a) -> QString {
if (!a.startsWith("z")) return a;
@@ -191,13 +248,16 @@ void MainWindow::sendButton() {
// Gather the from / to addresses
QString fromAddr = ui->inputsCombo->currentText().split("(")[0].trimmed();
- QList> toAddrs;
+
+ 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 addr = ui->sendToWidgets->findChild(QString("Address") % QString::number(i+1))->text().trimmed();
+ double amt = ui->sendToWidgets->findChild(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble();
+ QString memo = ui->sendToWidgets->findChild(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
+
+ toAddrs.push_back( ToFields{addr, amt, memo, memo.toUtf8().toHex()} );
}
QString error = doSendTxValidations(fromAddr, toAddrs);
@@ -226,23 +286,27 @@ void MainWindow::sendButton() {
// Remove all existing address/amt qlabels
int totalConfirmAddrItems = confirm.sendToAddrs->children().size();
- for (int i = 0; i < totalConfirmAddrItems; i++) {
- auto addr = confirm.sendToAddrs->findChild(QString("Addr") % QString::number(i+1));
- auto amt = confirm.sendToAddrs->findChild(QString("Amt") % QString::number(i+1));
+ for (int i = 0; i < totalConfirmAddrItems / 3; i++) {
+ auto addr = confirm.sendToAddrs->findChild(QString("Addr") % QString::number(i+1));
+ auto amt = confirm.sendToAddrs->findChild(QString("Amt") % QString::number(i+1));
+ auto memo = confirm.sendToAddrs->findChild(QString("Memo") % QString::number(i+1));
+ delete memo;
delete addr;
delete amt;
}
- // For each addr/amt
- //std::for_each(toAddr.begin(), toAddr.end(), [&] (auto toAddr) {
+ // For each addr/amt/memo, construct the JSON and also build the confirm dialog box
for (int i=0; i < toAddrs.size(); i++) {
auto toAddr = toAddrs[i];
// Construct the JSON params
json rec = json::object();
- rec["address"] = toAddr.first.toStdString();
- rec["amount"] = toAddr.second;
+ rec["address"] = toAddr.addr.toStdString();
+ rec["amount"] = toAddr.amount;
+ if (toAddr.addr.startsWith("z"))
+ rec["memo"] = toAddr.encodedMemo.toStdString();
+
allRecepients.push_back(rec);
// Add new Address widgets instead of the same one.
@@ -250,14 +314,25 @@ void MainWindow::sendButton() {
auto Addr = new QLabel(confirm.sendToAddrs);
Addr->setObjectName(QString("Addr") % QString::number(i + 1));
Addr->setWordWrap(true);
- Addr->setText(fnSplitAddressForWrap(toAddr.first));
- confirm.gridLayout->addWidget(Addr, i, 0, 1, 1);
+ Addr->setText(fnSplitAddressForWrap(toAddr.addr));
+ confirm.gridLayout->addWidget(Addr, i*2, 0, 1, 1);
auto Amt = new QLabel(confirm.sendToAddrs);
Amt->setObjectName(QString("Amt") % QString::number(i + 1));
- Amt->setText(QString::number(toAddr.second, 'g', 8) % " " % Utils::getTokenName());
+ Amt->setText(QString::number(toAddr.amount, 'g', 8) % " " % Utils::getTokenName());
Amt->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);
- confirm.gridLayout->addWidget(Amt, i, 1, 1, 1);
+ confirm.gridLayout->addWidget(Amt, i*2, 1, 1, 1);
+
+ if (toAddr.addr.startsWith("z")) {
+ auto Memo = new QLabel(confirm.sendToAddrs);
+ Memo->setObjectName(QStringLiteral("Memo") % QString::number(i + 1));
+ Memo->setText(toAddr.txtMemo);
+ QFont font1;
+ font1.setPointSize(10);
+ Memo->setFont(font1);
+
+ confirm.gridLayout->addWidget(Memo, (i*2)+1, 0, 1, 2);
+ }
}
}
@@ -270,7 +345,7 @@ void MainWindow::sendButton() {
confirm.sendFrom->setText(fnSplitAddressForWrap(fromAddr));
// Fees in the confirm dialog
- confirm.feesLabel->setText("Fees: 0.0001 " % Utils::getTokenName());
+ confirm.feesLabel->setText("Fee 0.0001 " % Utils::getTokenName());
// Show the dialog and submit it if the user confirms
if (d.exec() == QDialog::Accepted) {
@@ -287,7 +362,7 @@ void MainWindow::sendButton() {
}
}
-QString MainWindow::doSendTxValidations(QString fromAddr, QList> toAddrs) {
+QString MainWindow::doSendTxValidations(QString fromAddr, QList toAddrs) {
// 1. Addresses are valid format.
QRegExp zcexp("^z[a-z0-9]{94}$", Qt::CaseInsensitive);
QRegExp zsexp("^z[a-z0-9]{77}$", Qt::CaseInsensitive);
@@ -304,8 +379,8 @@ QString MainWindow::doSendTxValidations(QString fromAddr, QListfirst))
- return QString("To Address ") % toAddr->first % " is Invalid";
+ if (!matchesAnyAddr(toAddr->addr))
+ return QString("Recipient Address ") % toAddr->addr % " is Invalid";
};
return QString();
diff --git a/src/ui_confirm.h b/src/ui_confirm.h
index ff1fb97..8c4ff29 100644
--- a/src/ui_confirm.h
+++ b/src/ui_confirm.h
@@ -33,6 +33,7 @@ public:
QGridLayout *gridLayout;
QLabel *Addr1;
QLabel *Amt1;
+ QLabel *Memo1;
QSpacerItem *verticalSpacer;
QFrame *line;
QLabel *feesLabel;
@@ -74,6 +75,11 @@ public:
gridLayout->addWidget(Amt1, 0, 1, 1, 1);
+ Memo1 = new QLabel(sendToAddrs);
+ Memo1->setObjectName(QStringLiteral("Memo1"));
+
+ gridLayout->addWidget(Memo1, 1, 0, 1, 2);
+
verticalLayout->addWidget(sendToAddrs);
@@ -116,6 +122,7 @@ public:
sendToAddrs->setTitle(QApplication::translate("confirm", "To", nullptr));
Addr1->setText(QApplication::translate("confirm", "TextLabel", nullptr));
Amt1->setText(QApplication::translate("confirm", "TextLabel", nullptr));
+ Memo1->setText(QApplication::translate("confirm", "TextLabel", nullptr));
feesLabel->setText(QString());
} // retranslateUi
diff --git a/src/ui_mainwindow.h b/src/ui_mainwindow.h
index 2070bea..e83719c 100644
--- a/src/ui_mainwindow.h
+++ b/src/ui_mainwindow.h
@@ -96,13 +96,13 @@ public:
QLineEdit *Amount1;
QCheckBox *Max1;
QSpacerItem *horizontalSpacer_4;
+ QPushButton *MemoBtn1;
+ QLabel *MemoTxt1;
QHBoxLayout *horizontalLayout_7;
QSpacerItem *horizontalSpacer_2;
QPushButton *addAddressButton;
QSpacerItem *horizontalSpacer_3;
QSpacerItem *verticalSpacer_2;
- QGroupBox *groupBox_7;
- QVBoxLayout *verticalLayout_10;
QHBoxLayout *horizontalLayout_14;
QLabel *label_7;
QLineEdit *sendTxFees;
@@ -342,17 +342,19 @@ public:
groupBox_3 = new QGroupBox(tab_2);
groupBox_3->setObjectName(QStringLiteral("groupBox_3"));
+ groupBox_3->setFlat(false);
verticalLayout_3 = new QVBoxLayout(groupBox_3);
verticalLayout_3->setSpacing(6);
verticalLayout_3->setContentsMargins(11, 11, 11, 11);
verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3"));
+ verticalLayout_3->setContentsMargins(0, 0, 0, 0);
sendToScrollArea = new QScrollArea(groupBox_3);
sendToScrollArea->setObjectName(QStringLiteral("sendToScrollArea"));
sendToScrollArea->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
sendToScrollArea->setWidgetResizable(true);
sendToWidgets = new QWidget();
sendToWidgets->setObjectName(QStringLiteral("sendToWidgets"));
- sendToWidgets->setGeometry(QRect(0, 0, 823, 226));
+ sendToWidgets->setGeometry(QRect(0, 0, 841, 288));
sendToLayout = new QVBoxLayout(sendToWidgets);
sendToLayout->setSpacing(6);
sendToLayout->setContentsMargins(11, 11, 11, 11);
@@ -402,9 +404,23 @@ public:
horizontalLayout_13->addItem(horizontalSpacer_4);
+ MemoBtn1 = new QPushButton(verticalGroupBox);
+ MemoBtn1->setObjectName(QStringLiteral("MemoBtn1"));
+ MemoBtn1->setEnabled(true);
+
+ horizontalLayout_13->addWidget(MemoBtn1);
+
sendAddressLayout->addLayout(horizontalLayout_13);
+ MemoTxt1 = new QLabel(verticalGroupBox);
+ MemoTxt1->setObjectName(QStringLiteral("MemoTxt1"));
+ QFont font1;
+ font1.setPointSize(10);
+ MemoTxt1->setFont(font1);
+
+ sendAddressLayout->addWidget(MemoTxt1);
+
sendToLayout->addWidget(verticalGroupBox);
@@ -439,22 +455,18 @@ public:
verticalLayout_4->addWidget(groupBox_3);
- groupBox_7 = new QGroupBox(tab_2);
- groupBox_7->setObjectName(QStringLiteral("groupBox_7"));
- verticalLayout_10 = new QVBoxLayout(groupBox_7);
- verticalLayout_10->setSpacing(6);
- verticalLayout_10->setContentsMargins(11, 11, 11, 11);
- verticalLayout_10->setObjectName(QStringLiteral("verticalLayout_10"));
horizontalLayout_14 = new QHBoxLayout();
horizontalLayout_14->setSpacing(6);
horizontalLayout_14->setObjectName(QStringLiteral("horizontalLayout_14"));
- label_7 = new QLabel(groupBox_7);
+ label_7 = new QLabel(tab_2);
label_7->setObjectName(QStringLiteral("label_7"));
horizontalLayout_14->addWidget(label_7);
- sendTxFees = new QLineEdit(groupBox_7);
+ sendTxFees = new QLineEdit(tab_2);
sendTxFees->setObjectName(QStringLiteral("sendTxFees"));
+ sizePolicy.setHeightForWidth(sendTxFees->sizePolicy().hasHeightForWidth());
+ sendTxFees->setSizePolicy(sizePolicy);
sendTxFees->setReadOnly(true);
horizontalLayout_14->addWidget(sendTxFees);
@@ -464,10 +476,7 @@ public:
horizontalLayout_14->addItem(horizontalSpacer_5);
- verticalLayout_10->addLayout(horizontalLayout_14);
-
-
- verticalLayout_4->addWidget(groupBox_7);
+ verticalLayout_4->addLayout(horizontalLayout_14);
horizontalLayout_6 = new QHBoxLayout();
horizontalLayout_6->setSpacing(6);
@@ -673,16 +682,20 @@ public:
groupBox_2->setTitle(QApplication::translate("MainWindow", "Address Balances", nullptr));
tabWidget->setTabText(tabWidget->indexOf(tab), QApplication::translate("MainWindow", "Balance", nullptr));
groupBox_4->setTitle(QApplication::translate("MainWindow", "Pay From", nullptr));
- label_5->setText(QApplication::translate("MainWindow", "Address Balance:", nullptr));
+ label_5->setText(QApplication::translate("MainWindow", "Address Balance", nullptr));
groupBox_3->setTitle(QApplication::translate("MainWindow", "Send To", nullptr));
verticalGroupBox->setTitle(QApplication::translate("MainWindow", "Recipient", nullptr));
label_4->setText(QApplication::translate("MainWindow", "Address", nullptr));
Address1->setPlaceholderText(QApplication::translate("MainWindow", "Address", nullptr));
label_6->setText(QApplication::translate("MainWindow", "Amount", nullptr));
Amount1->setPlaceholderText(QApplication::translate("MainWindow", "Amount", nullptr));
- Max1->setText(QApplication::translate("MainWindow", "Maximum Available", nullptr));
+ Max1->setText(QApplication::translate("MainWindow", "Max Available", nullptr));
+#ifndef QT_NO_TOOLTIP
+ MemoBtn1->setToolTip(QString());
+#endif // QT_NO_TOOLTIP
+ MemoBtn1->setText(QApplication::translate("MainWindow", "Memo", nullptr));
+ MemoTxt1->setText(QString());
addAddressButton->setText(QApplication::translate("MainWindow", "Add Address", nullptr));
- groupBox_7->setTitle(QApplication::translate("MainWindow", "Fees", nullptr));
label_7->setText(QApplication::translate("MainWindow", "Fee", nullptr));
sendTxFees->setText(QString());
sendTransactionButton->setText(QApplication::translate("MainWindow", "Send", nullptr));
diff --git a/src/utils.h b/src/utils.h
index db0b139..5408ae1 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -9,6 +9,8 @@ public:
static const QString txidStatusMessage;
static const QString getTokenName();
+
+ static const int updateSpeed = 20 * 1000; // 20 sec
private:
Utils() = delete;
};