diff --git a/README.md b/README.md index 80814b9..bacc211 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ Head over to the releases page and grab the latest binary. https://github.com/ad ### Linux Extract and run the binary ``` -tar -xvf zec-qt-wallet-v0.2.8.tar.gz -./zec-qt-wallet-v0.2.8/zec-qt-wallet +tar -xvf zec-qt-wallet-v0.2.9.tar.gz +./zec-qt-wallet-v0.2.9/zec-qt-wallet ``` ### Windows @@ -24,7 +24,7 @@ There is currently no official zcashd build for Windows so Windows users may eit Alternatively run zcashd inside [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10). -For all installations zcashd needs to run with RPC enabled (`server=1`, which is the default) and with a RPC username/password set. Add the following entries into `~/.zcash/zcash.conf` for Linux or` C:\Users\your-username\AppData\Roaming\Zcash\zcash.conf` on Windows replacing the default values with a strong password. zec-qt-wallet should detect these settings but if that fails you may edit the connection settings manually via the `File->Settings` menu. +For all installations zcashd needs to run with RPC enabled (`server=1`, which is the default) and with a RPC username/password set. Add the following entries into `~/.zcash/zcash.conf` for Linux or` C:\Users\your-username\AppData\Roaming\Zcash\zcash.conf` on Windows replacing the default values with a strong password. zec-qt-wallet should detect these settings but if that fails you may edit the connection settings manually via the `Edit->Settings` menu. ``` rpcuser=username @@ -41,7 +41,7 @@ https://z.cash/downloads/sprout-proving.key https://z.cash/downloads/sprout-verifying.key ``` -If you are running zcashd on WSL, then please set the connection parameters in the `File->Settings` menu. +If you are running zcashd on WSL, then please set the connection parameters in the `Edit->Settings` menu. ## Compiling from source zec-qt-wallet is written in C++ 14, and can be compiled with g++/clang++/visual c++. It also depends on Qt5, which you can get from here: https://www.qt.io/download @@ -49,6 +49,8 @@ zec-qt-wallet is written in C++ 14, and can be compiled with g++/clang++/visual ### Compiling on Linux ``` +sudo apt install libgl1-mesa-dev + git clone https://github.com/adityapk00/zec-qt-wallet.git cd zec-qt-wallet /path/to/qt5/bin/qmake zec-qt-wallet.pro CONFIG+=debug @@ -64,7 +66,7 @@ From the VS Tools command prompt ``` git clone https://github.com/adityapk00/zec-qt-wallet.git cd zec-qt-wallet -c:\Qt5\bin\qmake.exe zec-qt-wallet.pro CONFIG+=debug +c:\Qt5\bin\qmake.exe zec-qt-wallet.pro -spec win32-msvc CONFIG+=debug nmake debug\zec-qt-wallet.exe diff --git a/application.qrc b/application.qrc index 29f23df..0f7de37 100644 --- a/application.qrc +++ b/application.qrc @@ -3,7 +3,7 @@ res/Ubuntu-R.ttf - res/connected.png + res/connected.gif res/loading.gif res/icon.ico diff --git a/docs/screenshot-main.png b/docs/screenshot-main.png index 062037d..f7f37b6 100644 Binary files a/docs/screenshot-main.png and b/docs/screenshot-main.png differ diff --git a/res/connected.gif b/res/connected.gif new file mode 100644 index 0000000..a515ed4 Binary files /dev/null and b/res/connected.gif differ diff --git a/res/connected.png b/res/connected.png deleted file mode 100644 index 4feefae..0000000 Binary files a/res/connected.png and /dev/null differ diff --git a/src/addressbook.cpp b/src/addressbook.cpp index 27f98dc..873eac2 100644 --- a/src/addressbook.cpp +++ b/src/addressbook.cpp @@ -10,83 +10,56 @@ AddressBookModel::AddressBookModel(QTableView *parent) headers << "Label" << "Address"; this->parent = parent; - loadDataFromStorage(); + loadData(); } AddressBookModel::~AddressBookModel() { - if (labels != nullptr) - saveDataToStorage(); - - delete labels; + saveData(); } -void AddressBookModel::saveDataToStorage() { - QFile file(writeableFile()); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); - QDataStream out(&file); // we will serialize the data into the file - out << QString("v1") << *labels; - file.close(); +void AddressBookModel::saveData() { + AddressBook::writeToStorage(labels); // Save column positions QSettings().setValue("addresstablegeometry", parent->horizontalHeader()->saveState()); } -void AddressBookModel::loadDataFromStorage() { - QFile file(writeableFile()); - - delete labels; - labels = new QList>(); - - file.open(QIODevice::ReadOnly); - QDataStream in(&file); // read the data serialized from the file - QString version; - in >> version >> *labels; - - file.close(); +void AddressBookModel::loadData() { + labels = AddressBook::readFromStorage(); parent->horizontalHeader()->restoreState(QSettings().value("addresstablegeometry").toByteArray()); } void AddressBookModel::addNewLabel(QString label, QString addr) { - labels->push_back(QPair(label, addr)); + labels.push_back(QPair(label, addr)); + AddressBook::writeToStorage(labels); - dataChanged(index(0, 0), index(labels->size()-1, columnCount(index(0,0))-1)); + dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); layoutChanged(); } void AddressBookModel::removeItemAt(int row) { - if (row >= labels->size()) + if (row >= labels.size()) return; - labels->removeAt(row); - dataChanged(index(0, 0), index(labels->size()-1, columnCount(index(0,0))-1)); + labels.removeAt(row); + AddressBook::writeToStorage(labels); + + + dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); layoutChanged(); } QPair AddressBookModel::itemAt(int row) { - if (row >= labels->size()) return QPair(); + if (row >= labels.size()) return QPair(); - return labels->at(row); + return labels.at(row); } -QString AddressBookModel::writeableFile() { - auto filename = QStringLiteral("addresslabels.dat"); - - auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); - if (!dir.exists()) - QDir().mkpath(dir.absolutePath()); - - if (Settings::getInstance()->isTestnet()) { - return dir.filePath("testnet-" % filename); - } else { - return dir.filePath(filename); - } -} int AddressBookModel::rowCount(const QModelIndex&) const { - if (labels == nullptr) return 0; - return labels->size(); + return labels.size(); } int AddressBookModel::columnCount(const QModelIndex&) const { @@ -97,12 +70,12 @@ int AddressBookModel::columnCount(const QModelIndex&) const { QVariant AddressBookModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { switch(index.column()) { - case 0: return labels->at(index.row()).first; - case 1: return labels->at(index.row()).second; + case 0: return labels.at(index.row()).first; + case 1: return labels.at(index.row()).second; } } return QVariant(); -} +} QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -117,6 +90,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { QDialog d(parent); Ui_addressBook ab; ab.setupUi(&d); + Settings::saveRestore(&d); AddressBookModel model(ab.addresses); ab.addresses->setModel(&model); @@ -126,6 +100,9 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { ab.buttonBox->button(QDialogButtonBox::Ok)->setText("Pick"); } + // Connect the dialog's closing to updating the label address completor + QObject::connect(&d, &QDialog::finished, [=] (auto) { parent->updateLabelsAutoComplete(); }); + // If there is a target then make it the addr for the "Add to" button if (target != nullptr && Utils::isValidAddress(target->text())) { ab.addr->setText(target->text()); @@ -145,13 +122,18 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { } }); + auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr) { + target->setText(label % "/" % addr); + }; + // Double-Click picks the item QObject::connect(ab.addresses, &QTableView::doubleClicked, [&] (auto index) { if (index.row() < 0) return; + QString lbl = model.itemAt(index.row()).first; QString addr = model.itemAt(index.row()).second; d.accept(); - target->setText(addr); + fnSetTargetLabelAddr(target, lbl, addr); }); // Right-Click @@ -161,13 +143,15 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { if (index.row() < 0) return; + QString lbl = model.itemAt(index.row()).first; QString addr = model.itemAt(index.row()).second; QMenu menu(parent); if (target != nullptr) { menu.addAction("Pick", [&] () { - target->setText(addr); + d.accept(); + fnSetTargetLabelAddr(target, lbl, addr); }); } @@ -186,7 +170,46 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) { if (d.exec() == QDialog::Accepted && target != nullptr) { auto selection = ab.addresses->selectionModel(); if (selection->hasSelection()) { - target->setText(model.itemAt(selection->selectedRows().at(0).row()).second); + auto item = model.itemAt(selection->selectedRows().at(0).row()); + fnSetTargetLabelAddr(target, item.first, item.second); } }; +} + +QList> AddressBook::readFromStorage() { + QFile file(AddressBook::writeableFile()); + + QList> labels; + + file.open(QIODevice::ReadOnly); + QDataStream in(&file); // read the data serialized from the file + QString version; + in >> version >> labels; + + file.close(); + + return labels; +} + + +void AddressBook::writeToStorage(QList> labels) { + QFile file(AddressBook::writeableFile()); + file.open(QIODevice::ReadWrite | QIODevice::Truncate); + QDataStream out(&file); // we will serialize the data into the file + out << QString("v1") << labels; + file.close(); +} + +QString AddressBook::writeableFile() { + auto filename = QStringLiteral("addresslabels.dat"); + + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + if (!dir.exists()) + QDir().mkpath(dir.absolutePath()); + + if (Settings::getInstance()->isTestnet()) { + return dir.filePath("testnet-" % filename); + } else { + return dir.filePath(filename); + } } \ No newline at end of file diff --git a/src/addressbook.h b/src/addressbook.h index 5fe7666..9aa3f00 100644 --- a/src/addressbook.h +++ b/src/addressbook.h @@ -21,19 +21,22 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const; private: - void loadDataFromStorage(); - void saveDataToStorage(); - - QString writeableFile(); + void loadData(); + void saveData(); QTableView* parent; - QList>* labels = nullptr; + QList> labels; QStringList headers; }; class AddressBook { public: static void open(MainWindow* parent, QLineEdit* target = nullptr); + + static QList> readFromStorage(); + static void writeToStorage(QList> labels); + + static QString writeableFile(); }; #endif // ADDRESSBOOK_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 791a4bd..76fcd14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ int main(int argc, char *argv[]) qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false)); #endif - std::srand(std::time(nullptr)); + std::srand(static_cast(std::time(nullptr))); Settings::init(); Settings::getInstance()->setExecName(argv[0]); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 505f209..f02d711 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -109,6 +109,7 @@ void MainWindow::turnstileProgress() { Ui_TurnstileProgress progress; QDialog d(this); progress.setupUi(&d); + Settings::saveRestore(&d); QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); progress.msgIcon->setPixmap(icon.pixmap(64, 64)); @@ -189,6 +190,7 @@ void MainWindow::turnstileDoMigration(QString fromAddr) { Ui_Turnstile turnstile; QDialog d(this); turnstile.setupUi(&d); + Settings::saveRestore(&d); QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); turnstile.msgIcon->setPixmap(icon.pixmap(64, 64)); @@ -240,8 +242,7 @@ void MainWindow::turnstileDoMigration(QString fromAddr) { if (!fromAddr.isEmpty()) turnstile.migrateZaddList->setCurrentText(fromAddr); - fnUpdateSproutBalance(turnstile.migrateZaddList->currentText()); - + fnUpdateSproutBalance(turnstile.migrateZaddList->currentText()); // Combo box selection event QObject::connect(turnstile.migrateZaddList, &QComboBox::currentTextChanged, fnUpdateSproutBalance); @@ -349,6 +350,7 @@ void MainWindow::setupSettingsModal() { QDialog settingsDialog(this); Ui_Settings settings; settings.setupUi(&settingsDialog); + Settings::saveRestore(&settingsDialog); // Setup save sent check box QObject::connect(settings.chkSaveTxs, &QCheckBox::stateChanged, [=](auto checked) { @@ -412,7 +414,7 @@ void MainWindow::setupSettingsModal() { auto cl = new ConnectionLoader(this, rpc); cl->loadConnection(); } - }; + } }); } @@ -449,6 +451,10 @@ void MainWindow::postToZBoard() { QDialog d(this); Ui_zboard zb; zb.setupUi(&d); + Settings::saveRestore(&d); + + if (rpc->getConnection() == nullptr) + return; // Fill the from field with sapling addresses. for (auto i = rpc->getAllBalances()->keyBegin(); i != rpc->getAllBalances()->keyEnd(); i++) { @@ -457,6 +463,18 @@ void MainWindow::postToZBoard() { } } + QMap topics; + // Insert the main topic automatically + topics.insert("#Main_Area", Utils::getZboardAddr()); + zb.topicsList->addItem(topics.firstKey()); + // Then call the API to get topics, and if it returns successfully, then add the rest of the topics + rpc->getZboardTopics([&](QMap topicsMap) { + for (auto t : topicsMap.keys()) { + topics.insert(t, topicsMap[t]); + zb.topicsList->addItem(t); + } + }); + // Testnet warning if (Settings::getInstance()->isTestnet()) { zb.testnetWarning->setText("You are on testnet, your post won't actually appear on z-board.net"); @@ -465,10 +483,20 @@ void MainWindow::postToZBoard() { zb.testnetWarning->setText(""); } + QRegExpValidator v(QRegExp("^[a-zA-Z0-9_]{3,20}$"), zb.postAs); + zb.postAs->setValidator(&v); + zb.feeAmount->setText(Settings::getInstance()->getZECUSDDisplayFormat(Utils::getZboardAmount() + Utils::getMinerFee())); - QObject::connect(zb.memoTxt, &QPlainTextEdit::textChanged, [=] () { - QString txt = zb.memoTxt->toPlainText(); + auto fnBuildNameMemo = [=]() -> QString { + auto memo = zb.memoTxt->toPlainText().trimmed(); + if (!zb.postAs->text().trimmed().isEmpty()) + memo = zb.postAs->text().trimmed() + ":: " + memo; + return memo; + }; + + auto fnUpdateMemoSize = [=]() { + QString txt = fnBuildNameMemo(); zb.memoSize->setText(QString::number(txt.toUtf8().size()) + "/512"); if (txt.toUtf8().size() <= 512) { @@ -477,14 +505,26 @@ void MainWindow::postToZBoard() { zb.memoSize->setStyleSheet(""); } else { - // Overweight + // Overweight zb.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); zb.memoSize->setStyleSheet("color: red;"); } - - }); + + // Disallow blank memos + if (zb.memoTxt->toPlainText().trimmed().isEmpty()) { + zb.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } + else { + zb.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + } + }; + + // Memo text changed + QObject::connect(zb.memoTxt, &QPlainTextEdit::textChanged, fnUpdateMemoSize); + QObject::connect(zb.postAs, &QLineEdit::textChanged, fnUpdateMemoSize); zb.memoTxt->setFocus(); + fnUpdateMemoSize(); if (d.exec() == QDialog::Accepted) { // Create a transaction. @@ -501,7 +541,8 @@ void MainWindow::postToZBoard() { if (!zb.postAs->text().trimmed().isEmpty()) memo = zb.postAs->text().trimmed() + ":: " + memo; - tx.toAddrs.push_back(ToFields{ Utils::getZboardAddr(), Utils::getZboardAmount(), memo, memo.toUtf8().toHex() }); + auto toAddr = topics[zb.topicsList->currentText()]; + tx.toAddrs.push_back(ToFields{ toAddr, Utils::getZboardAmount(), memo, memo.toUtf8().toHex() }); tx.fee = Utils::getMinerFee(); json params = json::array(); @@ -520,7 +561,6 @@ void MainWindow::postToZBoard() { } void MainWindow::doImport(QList* keys) { - qDebug() << keys->size(); if (keys->isEmpty()) { delete keys; ui->statusBar->showMessage("Private key import rescan finished"); @@ -545,6 +585,7 @@ void MainWindow::importPrivKey() { QDialog d(this); Ui_PrivKey pui; pui.setupUi(&d); + Settings::saveRestore(&d); pui.buttonBox->button(QDialogButtonBox::Save)->setVisible(false); pui.helpLbl->setText(QString() % @@ -577,11 +618,14 @@ void MainWindow::exportAllKeys() { QDialog d(this); Ui_PrivKey pui; pui.setupUi(&d); - + + // Make the window big by default auto ps = this->geometry(); QMargins margin = QMargins() + 50; d.setGeometry(ps.marginsRemoved(margin)); + Settings::saveRestore(&d); + pui.privKeyTxt->setPlainText("Loading..."); pui.privKeyTxt->setReadOnly(true); pui.privKeyTxt->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap); @@ -706,9 +750,19 @@ void MainWindow::setupZcashdTab() { } void MainWindow::setupTransactionsTab() { + // Double click opens up memo if one exists + QObject::connect(ui->transactionsTable, &QTableView::doubleClicked, [=] (auto index) { + auto txModel = dynamic_cast(ui->transactionsTable->model()); + QString memo = txModel->getMemo(index.row()); + + if (!memo.isEmpty()) { + QMessageBox::information(this, "Memo", memo, QMessageBox::Ok); + } + }); + // Set up context menu on transactions tab ui->transactionsTable->setContextMenuPolicy(Qt::CustomContextMenu); - + // Table right click QObject::connect(ui->transactionsTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { QModelIndex index = ui->transactionsTable->indexAt(pos); if (index.row() < 0) return; @@ -877,6 +931,7 @@ MainWindow::~MainWindow() { delete ui; delete rpc; + delete labelCompleter; delete loadingMovie; } diff --git a/src/mainwindow.h b/src/mainwindow.h index e634a37..33d8f53 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -36,6 +36,8 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); + void updateLabelsAutoComplete(); + Ui::MainWindow* ui; QLabel* statusLabel; @@ -90,9 +92,10 @@ private: void restoreSavedStates(); - RPC* rpc = nullptr; + RPC* rpc = nullptr; + QCompleter* labelCompleter = nullptr; - QMovie* loadingMovie; + QMovie* loadingMovie; }; #endif // MAINWINDOW_H diff --git a/src/precompiled.h b/src/precompiled.h index e046d7e..916fb43 100644 --- a/src/precompiled.h +++ b/src/precompiled.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rpc.cpp b/src/rpc.cpp index 11ca9ae..a0aa2c5 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -470,7 +470,7 @@ void RPC::getInfoThenRefresh(bool force) { }; // Connected, so display checkmark. - QIcon i(":/icons/res/connected.png"); + QIcon i(":/icons/res/connected.gif"); main->statusIcon->setPixmap(i.pixmap(16, 16)); static int lastBlock = 0; @@ -899,3 +899,58 @@ void RPC::shutdownZcashd() { // Wait for the zcash process to exit. d.exec(); } + + +// Fetch the Z-board topics list +void RPC::getZboardTopics(std::function)> cb) { + if (conn == nullptr) + return noConnection(); + + QUrl cmcURL("http://z-board.net/listTopics"); + + QNetworkRequest req; + req.setUrl(cmcURL); + + QNetworkReply *reply = conn->restclient->get(req); + + QObject::connect(reply, &QNetworkReply::finished, [=] { + reply->deleteLater(); + + try { + if (reply->error() != QNetworkReply::NoError) { + auto parsed = json::parse(reply->readAll(), nullptr, false); + if (!parsed.is_discarded() && !parsed["error"]["message"].is_null()) { + qDebug() << QString::fromStdString(parsed["error"]["message"]); + } + else { + qDebug() << reply->errorString(); + } + return; + } + + auto all = reply->readAll(); + + auto parsed = json::parse(all, nullptr, false); + if (parsed.is_discarded()) { + return; + } + + QMap topics; + for (const json& item : parsed["topics"].get()) { + if (item.find("addr") == item.end() || item.find("topicName") == item.end()) + return; + + QString addr = QString::fromStdString(item["addr"].get()); + QString topic = QString::fromStdString(item["topicName"].get()); + + topics.insert(topic, addr); + } + + cb(topics); + } + catch (...) { + // If anything at all goes wrong, just set the price to 0 and move on. + qDebug() << QString("Caught something nasty"); + } + }); +} diff --git a/src/rpc.h b/src/rpc.h index 958a733..ae2b1ef 100644 --- a/src/rpc.h +++ b/src/rpc.h @@ -38,7 +38,9 @@ public: void refresh(bool force = false); void refreshAddresses(); + void refreshZECPrice(); + void getZboardTopics(std::function)> cb); void fillTxJsonParams(json& params, Tx tx); void sendZTransaction (json params, const std::function& cb); diff --git a/src/scripts/mkrelease.sh b/src/scripts/mkrelease.sh index aba3c0e..211dfd8 100755 --- a/src/scripts/mkrelease.sh +++ b/src/scripts/mkrelease.sh @@ -29,6 +29,7 @@ echo "[OK]" echo -n "Cleaning.........." rm -rf bin/* +rm -rf artifacts/* make distclean > /dev/null echo "[OK]" @@ -58,6 +59,7 @@ echo "[OK]" echo -n "Packaging........." mkdir bin/zec-qt-wallet-v$APP_VERSION > /dev/null +strip zec-qt-wallet cp zec-qt-wallet bin/zec-qt-wallet-v$APP_VERSION > /dev/null cp ../zcash/artifacts/zcashd bin/zec-qt-wallet-v$APP_VERSION > /dev/null cp README.md bin/zec-qt-wallet-v$APP_VERSION > /dev/null diff --git a/src/sendtab.cpp b/src/sendtab.cpp index 146f556..ae4e89e 100644 --- a/src/sendtab.cpp +++ b/src/sendtab.cpp @@ -45,6 +45,11 @@ void MainWindow::setupSendTab() { QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) { this->addressChanged(1, text); }); + + // This is the damnest thing ever. If we do AddressBook::readFromStorage() directly, the whole file + // doesn't get read. It needs to run in a timer after everything has finished to be able to read + // the file properly. + QTimer::singleShot(100, [=]() { updateLabelsAutoComplete(); }); // The first address book button QObject::connect(ui->AddressBook1, &QPushButton::clicked, [=] () { @@ -87,6 +92,25 @@ void MainWindow::setupSendTab() { }); } +void MainWindow::updateLabelsAutoComplete() { + QList list; + auto labels = AddressBook::readFromStorage(); + + std::transform(labels.begin(), labels.end(), std::back_inserter(list), [=] (auto la) -> QString { + return la.first % "/" % la.second; + }); + + delete labelCompleter; + labelCompleter = new QCompleter(list, this); + labelCompleter->setCaseSensitivity(Qt::CaseInsensitive); + + // Then, find all the address fields and update the completer. + QRegExp re("Address[0-9]+", Qt::CaseInsensitive); + for (auto target: ui->sendToWidgets->findChildren(re)) { + target->setCompleter(labelCompleter); + } +} + void MainWindow::setDefaultPayFrom() { auto findMax = [=] (QString startsWith) { double max_amt = 0; @@ -148,6 +172,7 @@ void MainWindow::addAddressSection() { QObject::connect(Address1, &QLineEdit::textChanged, [=] (auto text) { this->addressChanged(itemNumber, text); }); + Address1->setCompleter(labelCompleter); horizontalLayout_12->addWidget(Address1); @@ -350,6 +375,9 @@ Tx MainWindow::createTxFromSendPage() { int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that for (int i=0; i < totalItems; i++) { QString addr = ui->sendToWidgets->findChild(QString("Address") % QString::number(i+1))->text().trimmed(); + // Remove label if it exists + addr = addr.split("/").last(); + 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(); diff --git a/src/settings.cpp b/src/settings.cpp index 38fee88..8ae7502 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -120,3 +120,11 @@ QString Settings::getZECUSDDisplayFormat(double bal) { else return getZECDisplayFormat(bal); } + +void Settings::saveRestore(QDialog* d) { + d->restoreGeometry(QSettings().value(d->objectName() % "geometry").toByteArray()); + + QObject::connect(d, &QDialog::finished, [=](auto) { + QSettings().setValue(d->objectName() % "geometry", d->saveGeometry()); + }); +} diff --git a/src/settings.h b/src/settings.h index a19d9fe..b13586f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -16,6 +16,8 @@ public: static Settings* init(); static Settings* getInstance(); + static void saveRestore(QDialog* d); + Config getSettings(); void saveSettings(const QString& host, const QString& port, const QString& username, const QString& password); @@ -48,8 +50,7 @@ public: void setZECPrice(double p) { zecPrice = p; } double getZECPrice(); - - + QString getUSDFormat (double bal); QString getZECDisplayFormat (double bal); QString getZECUSDDisplayFormat(double bal); diff --git a/src/txid.ui b/src/txid.ui deleted file mode 100644 index 18d31ab..0000000 --- a/src/txid.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - - - Dialog - - - - 0 - 0 - 400 - 300 - - - - Dialog - - - - - 30 - 240 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/utils.cpp b/src/utils.cpp index bffe3e9..7f638fb 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -30,37 +30,8 @@ const QString Utils::getDevSproutAddr() { } // Get the dev fee address based on the transaction -const QString Utils::getDevAddr(Tx tx) { - auto testnetAddrLookup = [=] (const QString& addr) -> QString { - if (addr.startsWith("ztestsapling")) { - return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh"; - } else if (addr.startsWith("zt")) { - return getDevSproutAddr(); - } else { - return QString(); - } - }; - - if (Settings::getInstance()->isTestnet()) { - auto devAddr = testnetAddrLookup(tx.fromAddr); - if (!devAddr.isEmpty()) { - return devAddr; - } - - // t-Addr, find if it is going to a Sprout or Sapling address - for (const ToFields& to : tx.toAddrs) { - devAddr = testnetAddrLookup(to.addr); - if (!devAddr.isEmpty()) { - return devAddr; - } - } - - // If this is a t-Addr -> t-Addr transaction, use the Sapling address by default - return testnetAddrLookup("ztestsapling"); - } else { - // Mainnet doesn't have a fee yet! - return QString(); - } +const QString Utils::getDevAddr(Tx) { + return QString(); } @@ -82,7 +53,7 @@ QString Utils::getZboardAddr() { } double Utils::getDevFee() { if (Settings::getInstance()->isTestnet()) { - return 0.0001; + return 0; } else { return 0; } diff --git a/src/zboard.ui b/src/zboard.ui index 7b8e466..8a16047 100644 --- a/src/zboard.ui +++ b/src/zboard.ui @@ -82,7 +82,7 @@ - <html><head/><body><p>ZBoard: Fully anonymous and untraceable chat messages based on the ZCash blockchain. <a href="http://www.z-board.net/"><span style=" text-decoration: underline; color:#0000ff;">http://www.z-board.net/</span></a></p><p>Posting to ZBoard: #Main_Area</p></body></html> + <html><head/><body><p>ZBoard: Fully anonymous and untraceable chat messages based on the ZCash blockchain. <a href="http://www.z-board.net/"><span style=" text-decoration: underline; color:#0000ff;">http://www.z-board.net/</span></a></p></body></html> true @@ -115,6 +115,16 @@ + + + + + + + Posting to Board + + + diff --git a/zec-qt-wallet.pro b/zec-qt-wallet.pro index b62b9be..be1c031 100644 --- a/zec-qt-wallet.pro +++ b/zec-qt-wallet.pro @@ -13,7 +13,7 @@ PRECOMPILED_HEADER = src/precompiled.h greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = zec-qt-wallet -APP_VERSION=\\\"0.2.8\\\" +APP_VERSION=\\\"0.2.9\\\" TEMPLATE = app