add address/label autocomplete
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -910,6 +910,7 @@ MainWindow::~MainWindow()
|
|||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
delete rpc;
|
delete rpc;
|
||||||
|
delete labelCompleter;
|
||||||
|
|
||||||
delete loadingMovie;
|
delete loadingMovie;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user