Browse Source

add address/label autocomplete

import_zecw
Aditya Kulkarni 6 years ago
parent
commit
b60d65ee82
  1. 124
      src/addressbook.cpp
  2. 13
      src/addressbook.h
  3. 1
      src/mainwindow.cpp
  4. 7
      src/mainwindow.h
  5. 1
      src/precompiled.h
  6. 28
      src/sendtab.cpp
  7. 57
      src/utils.cpp

124
src/addressbook.cpp

@ -10,83 +10,56 @@ AddressBookModel::AddressBookModel(QTableView *parent)
headers << "Label" << "Address"; headers << "Label" << "Address";
this->parent = parent; this->parent = parent;
loadDataFromStorage(); loadData();
} }
AddressBookModel::~AddressBookModel() { AddressBookModel::~AddressBookModel() {
if (labels != nullptr) saveData();
saveDataToStorage();
delete labels;
} }
void AddressBookModel::saveDataToStorage() { void AddressBookModel::saveData() {
QFile file(writeableFile()); AddressBook::writeToStorage(labels);
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
QDataStream out(&file); // we will serialize the data into the file
out << QString("v1") << *labels;
file.close();
// Save column positions // Save column positions
QSettings().setValue("addresstablegeometry", parent->horizontalHeader()->saveState()); QSettings().setValue("addresstablegeometry", parent->horizontalHeader()->saveState());
} }
void AddressBookModel::loadDataFromStorage() { void AddressBookModel::loadData() {
QFile file(writeableFile()); labels = AddressBook::readFromStorage();
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();
parent->horizontalHeader()->restoreState(QSettings().value("addresstablegeometry").toByteArray()); parent->horizontalHeader()->restoreState(QSettings().value("addresstablegeometry").toByteArray());
} }
void AddressBookModel::addNewLabel(QString label, QString addr) { 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(); layoutChanged();
} }
void AddressBookModel::removeItemAt(int row) { void AddressBookModel::removeItemAt(int row) {
if (row >= labels->size()) if (row >= labels.size())
return; 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(); layoutChanged();
} }
QPair<QString, QString> AddressBookModel::itemAt(int row) { 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 { int AddressBookModel::rowCount(const QModelIndex&) const {
if (labels == nullptr) return 0; return labels.size();
return labels->size();
} }
int AddressBookModel::columnCount(const QModelIndex&) const { 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 { QVariant AddressBookModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
switch(index.column()) { switch(index.column()) {
case 0: return labels->at(index.row()).first; case 0: return labels.at(index.row()).first;
case 1: return labels->at(index.row()).second; case 1: return labels.at(index.row()).second;
} }
} }
return QVariant(); return QVariant();
} }
QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const {
@ -127,6 +100,9 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) {
ab.buttonBox->button(QDialogButtonBox::Ok)->setText("Pick"); 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 there is a target then make it the addr for the "Add to" button
if (target != nullptr && Utils::isValidAddress(target->text())) { if (target != nullptr && Utils::isValidAddress(target->text())) {
ab.addr->setText(target->text()); ab.addr->setText(target->text());
@ -146,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 // Double-Click picks the item
QObject::connect(ab.addresses, &QTableView::doubleClicked, [&] (auto index) { QObject::connect(ab.addresses, &QTableView::doubleClicked, [&] (auto index) {
if (index.row() < 0) return; if (index.row() < 0) return;
QString lbl = model.itemAt(index.row()).first;
QString addr = model.itemAt(index.row()).second; QString addr = model.itemAt(index.row()).second;
d.accept(); d.accept();
target->setText(addr); fnSetTargetLabelAddr(target, lbl, addr);
}); });
// Right-Click // Right-Click
@ -162,13 +143,15 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) {
if (index.row() < 0) return; if (index.row() < 0) return;
QString lbl = model.itemAt(index.row()).first;
QString addr = model.itemAt(index.row()).second; QString addr = model.itemAt(index.row()).second;
QMenu menu(parent); QMenu menu(parent);
if (target != nullptr) { if (target != nullptr) {
menu.addAction("Pick", [&] () { menu.addAction("Pick", [&] () {
target->setText(addr); d.accept();
fnSetTargetLabelAddr(target, lbl, addr);
}); });
} }
@ -187,7 +170,46 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) {
if (d.exec() == QDialog::Accepted && target != nullptr) { if (d.exec() == QDialog::Accepted && target != nullptr) {
auto selection = ab.addresses->selectionModel(); auto selection = ab.addresses->selectionModel();
if (selection->hasSelection()) { 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);
}
} }

13
src/addressbook.h

@ -21,19 +21,22 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const;
private: private:
void loadDataFromStorage(); void loadData();
void saveDataToStorage(); void saveData();
QString writeableFile();
QTableView* parent; QTableView* parent;
QList<QPair<QString, QString>>* labels = nullptr; QList<QPair<QString, QString>> labels;
QStringList headers; QStringList headers;
}; };
class AddressBook { class AddressBook {
public: public:
static void open(MainWindow* parent, QLineEdit* target = nullptr); 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 #endif // ADDRESSBOOK_H

1
src/mainwindow.cpp

@ -910,6 +910,7 @@ MainWindow::~MainWindow()
{ {
delete ui; delete ui;
delete rpc; delete rpc;
delete labelCompleter;
delete loadingMovie; delete loadingMovie;
} }

7
src/mainwindow.h

@ -36,6 +36,8 @@ public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
void updateLabelsAutoComplete();
Ui::MainWindow* ui; Ui::MainWindow* ui;
QLabel* statusLabel; QLabel* statusLabel;
@ -89,9 +91,10 @@ private:
void restoreSavedStates(); void restoreSavedStates();
RPC* rpc = nullptr; RPC* rpc = nullptr;
QCompleter* labelCompleter = nullptr;
QMovie* loadingMovie; QMovie* loadingMovie;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

1
src/precompiled.h

@ -22,6 +22,7 @@
#include <QPair> #include <QPair>
#include <QDir> #include <QDir>
#include <QMenu> #include <QMenu>
#include <QCompleter>
#include <QDateTime> #include <QDateTime>
#include <QTimer> #include <QTimer>
#include <QSettings> #include <QSettings>

28
src/sendtab.cpp

@ -45,6 +45,11 @@ void MainWindow::setupSendTab() {
QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) { QObject::connect(ui->Address1, &QLineEdit::textChanged, [=] (auto text) {
this->addressChanged(1, 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 // The first address book button
QObject::connect(ui->AddressBook1, &QPushButton::clicked, [=] () { 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() { void MainWindow::setDefaultPayFrom() {
auto findMax = [=] (QString startsWith) { auto findMax = [=] (QString startsWith) {
double max_amt = 0; double max_amt = 0;
@ -148,6 +172,7 @@ void MainWindow::addAddressSection() {
QObject::connect(Address1, &QLineEdit::textChanged, [=] (auto text) { QObject::connect(Address1, &QLineEdit::textChanged, [=] (auto text) {
this->addressChanged(itemNumber, text); this->addressChanged(itemNumber, text);
}); });
Address1->setCompleter(labelCompleter);
horizontalLayout_12->addWidget(Address1); 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 int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that
for (int i=0; i < totalItems; i++) { for (int i=0; i < totalItems; i++) {
QString addr = ui->sendToWidgets->findChild<QLineEdit*>(QString("Address") % QString::number(i+1))->text().trimmed(); 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(); 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(); QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();

57
src/utils.cpp

@ -31,36 +31,37 @@ const QString Utils::getDevSproutAddr() {
// Get the dev fee address based on the transaction // Get the dev fee address based on the transaction
const QString Utils::getDevAddr(Tx tx) { const QString Utils::getDevAddr(Tx tx) {
auto testnetAddrLookup = [=] (const QString& addr) -> QString { return QString();
if (addr.startsWith("ztestsapling")) { // auto testnetAddrLookup = [=] (const QString& addr) -> QString {
return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh"; // if (addr.startsWith("ztestsapling")) {
} else if (addr.startsWith("zt")) { // return "ztestsapling1kdp74adyfsmm9838jaupgfyx3npgw8ut63stjjx757pc248cuc0ymzphqeux60c64qe5qt68ygh";
return getDevSproutAddr(); // } else if (addr.startsWith("zt")) {
} else { // return getDevSproutAddr();
return QString(); // } else {
} // return QString();
}; // }
// };
if (Settings::getInstance()->isTestnet()) { // if (Settings::getInstance()->isTestnet()) {
auto devAddr = testnetAddrLookup(tx.fromAddr); // auto devAddr = testnetAddrLookup(tx.fromAddr);
if (!devAddr.isEmpty()) { // if (!devAddr.isEmpty()) {
return devAddr; // return devAddr;
} // }
// t-Addr, find if it is going to a Sprout or Sapling address // // t-Addr, find if it is going to a Sprout or Sapling address
for (const ToFields& to : tx.toAddrs) { // for (const ToFields& to : tx.toAddrs) {
devAddr = testnetAddrLookup(to.addr); // devAddr = testnetAddrLookup(to.addr);
if (!devAddr.isEmpty()) { // if (!devAddr.isEmpty()) {
return devAddr; // return devAddr;
} // }
} // }
// If this is a t-Addr -> t-Addr transaction, use the Sapling address by default // // If this is a t-Addr -> t-Addr transaction, use the Sapling address by default
return testnetAddrLookup("ztestsapling"); // return testnetAddrLookup("ztestsapling");
} else { // } else {
// Mainnet doesn't have a fee yet! // // Mainnet doesn't have a fee yet!
return QString(); // return QString();
} // }
} }
@ -82,7 +83,7 @@ QString Utils::getZboardAddr() {
} }
double Utils::getDevFee() { double Utils::getDevFee() {
if (Settings::getInstance()->isTestnet()) { if (Settings::getInstance()->isTestnet()) {
return 0.0001; return 0;
} else { } else {
return 0; return 0;
} }

Loading…
Cancel
Save