diff --git a/ui/qtlib/src/lib.rs b/ui/qtlib/src/lib.rs index 3231861..75ddd82 100644 --- a/ui/qtlib/src/lib.rs +++ b/ui/qtlib/src/lib.rs @@ -1,6 +1,6 @@ use libc::{c_char}; use std::ffi::{CStr, CString}; -use zecpaperlib::paper; +use zecpaperlib::{pdf, paper}; /** * Call into rust to generate a paper wallet. Returns the paper wallet in JSON form. @@ -19,6 +19,30 @@ pub extern fn rust_generate_wallet(testnet: bool, zcount: u32, tcount: u32, entr return c_str.into_raw(); } +#[no_mangle] +pub extern fn rust_save_as_pdf(json: *const c_char, file: *const c_char)-> bool { + let json_str = unsafe { + assert!(!json.is_null()); + + CStr::from_ptr(json) + }; + + let file_str = unsafe { + assert!(!file.is_null()); + + CStr::from_ptr(file) + }; + + match pdf::save_to_pdf(json_str.to_str().unwrap(), file_str.to_str().unwrap()) { + Ok(_) => return true, + Err(e) => { + eprintln!("{}", e); + return false; + } + + } +} + /** * Callers that receive string return values from other functions should call this to return the string * back to rust, so it can be freed. Failure to call this function will result in a memory leak diff --git a/ui/qtlib/src/zecpaperrust.h b/ui/qtlib/src/zecpaperrust.h index a077130..6bcb3ad 100644 --- a/ui/qtlib/src/zecpaperrust.h +++ b/ui/qtlib/src/zecpaperrust.h @@ -7,6 +7,7 @@ extern "C"{ extern char * rust_generate_wallet(bool testnet, unsigned int zcount, unsigned int tcount, const char* entropy); extern void rust_free_string(char* s); +extern bool rust_save_as_pdf(const char* json, const char* filename); #ifdef __cplusplus } diff --git a/ui/src/mainwindow.cpp b/ui/src/mainwindow.cpp index 4975517..3c4dc5a 100644 --- a/ui/src/mainwindow.cpp +++ b/ui/src/mainwindow.cpp @@ -29,42 +29,106 @@ void AddWallet(QString address, QString pk, QWidget* scroll) { w.setupUi(g1); scroll->layout()->addWidget(g1); + // Setup fixed with fonts + const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); + w.qrAddress->setQrcodeString(address); - w.lblAddress->setText(SplitIntoLines(address, 44)); + w.lblAddress->setText(SplitIntoLines(address, 39)); + w.lblAddress->setFont(fixedFont); w.qrPrivateKey->setQrcodeString(pk); w.lblPrivateKey->setText(SplitIntoLines(pk, 59)); + w.lblPrivateKey->setFont(fixedFont); } -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ - ui->setupUi(this); - - // Setup fixed with fonts - // const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); - // ui->lblAddress->setFont(fixedFont); - // ui->lblPrivateKey->setFont(fixedFont); - +/** + * Generate wallets and return a JSON. + */ +QString Generate(int zaddrs, int taddrs, QString entropy) { // Call into rust to get the addresses - char* wallet = rust_generate_wallet(true, 1, 1, "entropy"); + char* wallet = rust_generate_wallet(false, zaddrs, taddrs, entropy.toStdString().c_str()); QString walletJson(wallet); rust_free_string(wallet); - auto json = QJsonDocument::fromJson(walletJson.toUtf8()); + return walletJson; +} + +void MainWindow::populateWallets() { + // First, get the numbers + int zaddrs = ui->txtzaddrs->text().toInt(); + int taddrs = ui->txttaddrs->text().toInt(); + + QString entropy = ui->txtEntropy->text(); + + currentWallets = Generate(zaddrs, taddrs, entropy); + + // Then, clear the Scroll area + auto children = ui->scroll->findChildren(); + for (int i=0; i < children.length(); i++) { + delete children[i]; + } + + // Then add the new wallets + auto json = QJsonDocument::fromJson(currentWallets.toUtf8()); for (int i=0; i < json.array().size(); i++) { auto addr = json.array()[i].toObject()["address"].toString(); auto pk = json.array()[i].toObject()["private_key"].toString(); AddWallet(addr, pk, ui->scroll); - } - + } } - +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + // First, set up the validators for the number fields + intValidator = new QIntValidator(0, 25); + ui->txttaddrs->setValidator(intValidator); + ui->txtzaddrs->setValidator(intValidator); + + // Wire up the generate button + QObject::connect(ui->btnGenerate, &QPushButton::clicked, [=]() { + this->populateWallets(); + }); + + // Save as PDF + QObject::connect(ui->btnSavePDF, &QPushButton::clicked, [=]() { + // Get a save file name + auto filename = QFileDialog::getSaveFileName(this, tr("Save as PDF"), "", tr("PDF Files (*.pdf)")); + if (!filename.isEmpty()) { + bool success = rust_save_as_pdf(this->currentWallets.toStdString().c_str(), filename.toStdString().c_str()); + if (success) { + QMessageBox::information(this, tr("Saved!"), tr("The wallets were saved to ") + filename); + } else { + QMessageBox::warning(this, tr("Failed to save file"), + tr("Couldn't save the file for some unknown reason")); + } + } + }); + + // Save as JSON + QObject::connect(ui->btnSaveJSON, &QPushButton::clicked, [=]() { + auto filename = QFileDialog::getSaveFileName(this, tr("Save as text"), "", tr("Text Files (*.txt)")); + if (!filename.isEmpty()) { + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) + { + QTextStream stream(&file); + stream << this->currentWallets << endl; + } + } + }); + + // Generate the default wallets + populateWallets(); + +} MainWindow::~MainWindow() { delete ui; + delete intValidator; } diff --git a/ui/src/mainwindow.h b/ui/src/mainwindow.h index 9353441..f3a5d73 100644 --- a/ui/src/mainwindow.h +++ b/ui/src/mainwindow.h @@ -2,6 +2,7 @@ #define MAINWINDOW_H #include +#include "precompiled.h" namespace Ui { class MainWindow; @@ -16,7 +17,13 @@ public: ~MainWindow(); private: + void populateWallets(); + + // The current JSON of the wallets. + QString currentWallets; + Ui::MainWindow *ui; + QIntValidator *intValidator; }; #endif // MAINWINDOW_H diff --git a/ui/src/mainwindow.ui b/ui/src/mainwindow.ui index 91eb270..4ee2896 100644 --- a/ui/src/mainwindow.ui +++ b/ui/src/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 805 - 609 + 927 + 930 @@ -15,86 +15,10 @@ - - - - Paper Wallet Generator - - - - - - Config - - - - - - - - - - - - Number of z addresses - - - - - - - Additional Entropy - - - - - - - Number of t addresses - - - - - - - - - - Generate from a single HD Key - - - true - - - - - - - - - - true - - - - - 0 - 0 - 761 - 274 - - - - - - - - - - + - Save + @@ -111,14 +35,14 @@ - + Save as PDF - + Save as JSON @@ -127,6 +51,81 @@ + + + + Config + + + + + + Additional Entropy + + + + + + + 0 + + + + + + + + + + Number of z addresses + + + + + + + 1 + + + + + + + Generate Wallets + + + + + + + Number of t addresses + + + + + + + + + + + + + true + + + + + 0 + 0 + 907 + 637 + + + + + + @@ -134,7 +133,7 @@ 0 0 - 805 + 927 22 @@ -150,6 +149,15 @@ + + txtzaddrs + txttaddrs + txtEntropy + btnGenerate + btnSavePDF + btnSaveJSON + scrollArea + diff --git a/ui/src/ui_mainwindow.h b/ui/src/ui_mainwindow.h index a8e1c05..0b0b2ae 100644 --- a/ui/src/ui_mainwindow.h +++ b/ui/src/ui_mainwindow.h @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -34,25 +33,23 @@ class Ui_MainWindow public: QWidget *centralWidget; QGridLayout *gridLayout; - QGroupBox *groupBox; - QGridLayout *gridLayout_2; + QGroupBox *Save; + QHBoxLayout *horizontalLayout; + QSpacerItem *horizontalSpacer; + QPushButton *btnSavePDF; + QPushButton *btnSaveJSON; QGroupBox *Config; QGridLayout *gridLayout_3; + QLabel *label_3; QLineEdit *txttaddrs; - QLineEdit *txtzaddrs; + QLineEdit *txtEntropy; QLabel *label; - QLabel *label_3; + QLineEdit *txtzaddrs; + QPushButton *btnGenerate; QLabel *label_2; - QLineEdit *lineEdit; - QCheckBox *checkBox; QScrollArea *scrollArea; QWidget *scroll; QVBoxLayout *verticalLayout; - QGroupBox *Save; - QHBoxLayout *horizontalLayout; - QSpacerItem *horizontalSpacer; - QPushButton *pushButton; - QPushButton *pushButton_2; QMenuBar *menuBar; QToolBar *mainToolBar; QStatusBar *statusBar; @@ -61,108 +58,99 @@ public: { if (MainWindow->objectName().isEmpty()) MainWindow->setObjectName(QString::fromUtf8("MainWindow")); - MainWindow->resize(805, 609); + MainWindow->resize(927, 930); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName(QString::fromUtf8("centralWidget")); gridLayout = new QGridLayout(centralWidget); gridLayout->setSpacing(6); gridLayout->setContentsMargins(11, 11, 11, 11); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); - groupBox = new QGroupBox(centralWidget); - groupBox->setObjectName(QString::fromUtf8("groupBox")); - gridLayout_2 = new QGridLayout(groupBox); - gridLayout_2->setSpacing(6); - gridLayout_2->setContentsMargins(11, 11, 11, 11); - gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); - Config = new QGroupBox(groupBox); + Save = new QGroupBox(centralWidget); + Save->setObjectName(QString::fromUtf8("Save")); + horizontalLayout = new QHBoxLayout(Save); + horizontalLayout->setSpacing(6); + horizontalLayout->setContentsMargins(11, 11, 11, 11); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + horizontalLayout->addItem(horizontalSpacer); + + btnSavePDF = new QPushButton(Save); + btnSavePDF->setObjectName(QString::fromUtf8("btnSavePDF")); + + horizontalLayout->addWidget(btnSavePDF); + + btnSaveJSON = new QPushButton(Save); + btnSaveJSON->setObjectName(QString::fromUtf8("btnSaveJSON")); + + horizontalLayout->addWidget(btnSaveJSON); + + + gridLayout->addWidget(Save, 2, 0, 1, 1); + + Config = new QGroupBox(centralWidget); Config->setObjectName(QString::fromUtf8("Config")); gridLayout_3 = new QGridLayout(Config); gridLayout_3->setSpacing(6); gridLayout_3->setContentsMargins(11, 11, 11, 11); gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3")); + label_3 = new QLabel(Config); + label_3->setObjectName(QString::fromUtf8("label_3")); + + gridLayout_3->addWidget(label_3, 1, 0, 1, 1); + txttaddrs = new QLineEdit(Config); txttaddrs->setObjectName(QString::fromUtf8("txttaddrs")); gridLayout_3->addWidget(txttaddrs, 0, 3, 1, 1); - txtzaddrs = new QLineEdit(Config); - txtzaddrs->setObjectName(QString::fromUtf8("txtzaddrs")); + txtEntropy = new QLineEdit(Config); + txtEntropy->setObjectName(QString::fromUtf8("txtEntropy")); - gridLayout_3->addWidget(txtzaddrs, 0, 1, 1, 1); + gridLayout_3->addWidget(txtEntropy, 1, 1, 1, 3); label = new QLabel(Config); label->setObjectName(QString::fromUtf8("label")); gridLayout_3->addWidget(label, 0, 0, 1, 1); - label_3 = new QLabel(Config); - label_3->setObjectName(QString::fromUtf8("label_3")); + txtzaddrs = new QLineEdit(Config); + txtzaddrs->setObjectName(QString::fromUtf8("txtzaddrs")); - gridLayout_3->addWidget(label_3, 1, 0, 1, 1); + gridLayout_3->addWidget(txtzaddrs, 0, 1, 1, 1); + + btnGenerate = new QPushButton(Config); + btnGenerate->setObjectName(QString::fromUtf8("btnGenerate")); + + gridLayout_3->addWidget(btnGenerate, 2, 0, 1, 1); label_2 = new QLabel(Config); label_2->setObjectName(QString::fromUtf8("label_2")); gridLayout_3->addWidget(label_2, 0, 2, 1, 1); - lineEdit = new QLineEdit(Config); - lineEdit->setObjectName(QString::fromUtf8("lineEdit")); - gridLayout_3->addWidget(lineEdit, 1, 1, 1, 3); + gridLayout->addWidget(Config, 0, 0, 1, 1); - checkBox = new QCheckBox(Config); - checkBox->setObjectName(QString::fromUtf8("checkBox")); - checkBox->setChecked(true); - - gridLayout_3->addWidget(checkBox, 2, 0, 1, 4); - - - gridLayout_2->addWidget(Config, 0, 0, 1, 1); - - scrollArea = new QScrollArea(groupBox); + scrollArea = new QScrollArea(centralWidget); scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setStyleSheet(QString::fromUtf8("")); scrollArea->setWidgetResizable(true); scroll = new QWidget(); scroll->setObjectName(QString::fromUtf8("scroll")); - scroll->setGeometry(QRect(0, 0, 761, 274)); + scroll->setGeometry(QRect(0, 0, 907, 637)); verticalLayout = new QVBoxLayout(scroll); verticalLayout->setSpacing(6); verticalLayout->setContentsMargins(11, 11, 11, 11); verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); scrollArea->setWidget(scroll); - gridLayout_2->addWidget(scrollArea, 1, 0, 1, 1); - - - gridLayout->addWidget(groupBox, 0, 0, 1, 1); - - Save = new QGroupBox(centralWidget); - Save->setObjectName(QString::fromUtf8("Save")); - horizontalLayout = new QHBoxLayout(Save); - horizontalLayout->setSpacing(6); - horizontalLayout->setContentsMargins(11, 11, 11, 11); - horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); - horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - - horizontalLayout->addItem(horizontalSpacer); - - pushButton = new QPushButton(Save); - pushButton->setObjectName(QString::fromUtf8("pushButton")); - - horizontalLayout->addWidget(pushButton); - - pushButton_2 = new QPushButton(Save); - pushButton_2->setObjectName(QString::fromUtf8("pushButton_2")); - - horizontalLayout->addWidget(pushButton_2); - - - gridLayout->addWidget(Save, 1, 0, 1, 1); + gridLayout->addWidget(scrollArea, 1, 0, 1, 1); MainWindow->setCentralWidget(centralWidget); menuBar = new QMenuBar(MainWindow); menuBar->setObjectName(QString::fromUtf8("menuBar")); - menuBar->setGeometry(QRect(0, 0, 805, 22)); + menuBar->setGeometry(QRect(0, 0, 927, 22)); MainWindow->setMenuBar(menuBar); mainToolBar = new QToolBar(MainWindow); mainToolBar->setObjectName(QString::fromUtf8("mainToolBar")); @@ -170,6 +158,12 @@ public: statusBar = new QStatusBar(MainWindow); statusBar->setObjectName(QString::fromUtf8("statusBar")); MainWindow->setStatusBar(statusBar); + QWidget::setTabOrder(txtzaddrs, txttaddrs); + QWidget::setTabOrder(txttaddrs, txtEntropy); + QWidget::setTabOrder(txtEntropy, btnGenerate); + QWidget::setTabOrder(btnGenerate, btnSavePDF); + QWidget::setTabOrder(btnSavePDF, btnSaveJSON); + QWidget::setTabOrder(btnSaveJSON, scrollArea); retranslateUi(MainWindow); @@ -179,15 +173,16 @@ public: void retranslateUi(QMainWindow *MainWindow) { MainWindow->setWindowTitle(QApplication::translate("MainWindow", "Zec Sapling Paper Wallet", nullptr)); - groupBox->setTitle(QApplication::translate("MainWindow", "Paper Wallet Generator", nullptr)); + Save->setTitle(QString()); + btnSavePDF->setText(QApplication::translate("MainWindow", "Save as PDF", nullptr)); + btnSaveJSON->setText(QApplication::translate("MainWindow", "Save as JSON", nullptr)); Config->setTitle(QApplication::translate("MainWindow", "Config", nullptr)); - label->setText(QApplication::translate("MainWindow", "Number of z addresses", nullptr)); label_3->setText(QApplication::translate("MainWindow", "Additional Entropy", nullptr)); + txttaddrs->setText(QApplication::translate("MainWindow", "0", nullptr)); + label->setText(QApplication::translate("MainWindow", "Number of z addresses", nullptr)); + txtzaddrs->setText(QApplication::translate("MainWindow", "1", nullptr)); + btnGenerate->setText(QApplication::translate("MainWindow", "Generate Wallets", nullptr)); label_2->setText(QApplication::translate("MainWindow", "Number of t addresses", nullptr)); - checkBox->setText(QApplication::translate("MainWindow", "Generate from a single HD Key", nullptr)); - Save->setTitle(QApplication::translate("MainWindow", "Save", nullptr)); - pushButton->setText(QApplication::translate("MainWindow", "Save as PDF", nullptr)); - pushButton_2->setText(QApplication::translate("MainWindow", "Save as JSON", nullptr)); } // retranslateUi };