merge
This commit is contained in:
12
README.md
12
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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<file>res/Ubuntu-R.ttf</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons">
|
||||
<file>res/connected.png</file>
|
||||
<file>res/connected.gif</file>
|
||||
<file>res/loading.gif</file>
|
||||
<file>res/icon.ico</file>
|
||||
</qresource>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 72 KiB |
BIN
res/connected.gif
Normal file
BIN
res/connected.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.3 KiB |
@@ -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<QPair<QString, QString>>();
|
||||
|
||||
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<QString, QString>(label, addr));
|
||||
labels.push_back(QPair<QString, QString>(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<QString, QString> AddressBookModel::itemAt(int row) {
|
||||
if (row >= labels->size()) return QPair<QString, QString>();
|
||||
if (row >= labels.size()) return QPair<QString, QString>();
|
||||
|
||||
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<QPair<QString, QString>> AddressBook::readFromStorage() {
|
||||
QFile file(AddressBook::writeableFile());
|
||||
|
||||
QList<QPair<QString, QString>> 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<QPair<QString, QString>> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<QPair<QString, QString>>* labels = nullptr;
|
||||
QList<QPair<QString, QString>> labels;
|
||||
QStringList headers;
|
||||
};
|
||||
|
||||
class AddressBook {
|
||||
public:
|
||||
static void open(MainWindow* parent, QLineEdit* target = nullptr);
|
||||
|
||||
static QList<QPair<QString, QString>> readFromStorage();
|
||||
static void writeToStorage(QList<QPair<QString, QString>> labels);
|
||||
|
||||
static QString writeableFile();
|
||||
};
|
||||
|
||||
#endif // ADDRESSBOOK_H
|
||||
@@ -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<unsigned int>(std::time(nullptr)));
|
||||
Settings::init();
|
||||
Settings::getInstance()->setExecName(argv[0]);
|
||||
|
||||
|
||||
@@ -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<QString, QString> 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<QString, QString> 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<QString>* 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<TxTableModel *>(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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <QPair>
|
||||
#include <QDir>
|
||||
#include <QMenu>
|
||||
#include <QCompleter>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QSettings>
|
||||
|
||||
57
src/rpc.cpp
57
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<void(QMap<QString, QString>)> 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<QString, QString> topics;
|
||||
for (const json& item : parsed["topics"].get<json::array_t>()) {
|
||||
if (item.find("addr") == item.end() || item.find("topicName") == item.end())
|
||||
return;
|
||||
|
||||
QString addr = QString::fromStdString(item["addr"].get<json::string_t>());
|
||||
QString topic = QString::fromStdString(item["topicName"].get<json::string_t>());
|
||||
|
||||
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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ public:
|
||||
void refresh(bool force = false);
|
||||
|
||||
void refreshAddresses();
|
||||
|
||||
void refreshZECPrice();
|
||||
void getZboardTopics(std::function<void(QMap<QString, QString>)> cb);
|
||||
|
||||
void fillTxJsonParams(json& params, Tx tx);
|
||||
void sendZTransaction (json params, const std::function<void(json)>& cb);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<QString> 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<QLineEdit *>(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<QLineEdit*>(QString("Address") % QString::number(i+1))->text().trimmed();
|
||||
// Remove label if it exists
|
||||
addr = addr.split("/").last();
|
||||
|
||||
double amt = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble();
|
||||
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
|
||||
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
71
src/txid.ui
71
src/txid.ui
@@ -1,71 +0,0 @@
|
||||
<ui version="4.0" >
|
||||
<author></author>
|
||||
<comment></comment>
|
||||
<exportmacro></exportmacro>
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>240</y>
|
||||
<width>341</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</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>Dialog</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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -115,6 +115,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="topicsList"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Posting to Board</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user