#include "addressbook.h" #include "ui_addressbook.h" #include "ui_mainwindow.h" #include "settings.h" #include "mainwindow.h" AddressBookModel::AddressBookModel(QTableView *parent) : QAbstractTableModel(parent) { headers << "Label" << "Address"; this->parent = parent; loadData(); } AddressBookModel::~AddressBookModel() { saveData(); } void AddressBookModel::saveData() { // Save column positions QSettings().setValue("addresstablegeometry", parent->horizontalHeader()->saveState()); } void AddressBookModel::loadData() { labels = AddressBook::getInstance()->getAllAddressLabels(); parent->horizontalHeader()->restoreState(QSettings().value("addresstablegeometry").toByteArray()); } void AddressBookModel::addNewLabel(QString label, QString addr) { //labels.push_back(QPair(label, addr)); AddressBook::getInstance()->addAddressLabel(label, addr); labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); layoutChanged(); } void AddressBookModel::removeItemAt(int row) { if (row >= labels.size()) return; AddressBook::getInstance()->removeAddressLabel(labels[row].first, labels[row].second); labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); 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(); return labels.at(row); } int AddressBookModel::rowCount(const QModelIndex&) const { return labels.size(); } int AddressBookModel::columnCount(const QModelIndex&) const { return headers.size(); } 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; } } return QVariant(); } QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { return headers.at(section); } return QVariant(); } //=============== // AddressBook //=============== void AddressBook::open(MainWindow* parent, QLineEdit* target) { QDialog d(parent); Ui_addressBook ab; ab.setupUi(&d); Settings::saveRestore(&d); QRegExpValidator v(QRegExp(Settings::labelRegExp), ab.label); ab.label->setValidator(&v); AddressBookModel model(ab.addresses); ab.addresses->setModel(&model); // If there is no target, the we'll call the button "Ok", else "Pick" if (target != nullptr) { 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 && Settings::isValidAddress(target->text())) { ab.addr->setText(target->text()); ab.label->setFocus(); } // Add new address button QObject::connect(ab.addNew, &QPushButton::clicked, [&] () { auto addr = ab.addr->text().trimmed(); if (!addr.isEmpty() && !ab.label->text().isEmpty()) { // Test if address is valid. if (!Settings::isValidAddress(addr)) { QMessageBox::critical(parent, QObject::tr("Address Format Error"), addr + QObject::tr(" doesn't seem to be a valid Zcash address."), QMessageBox::Ok); } else { model.addNewLabel(ab.label->text(), ab.addr->text()); } } }); 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 there's no target, then double-clicking does nothing. if (!target) return; if (index.row() < 0) return; QString lbl = model.itemAt(index.row()).first; QString addr = model.itemAt(index.row()).second; d.accept(); fnSetTargetLabelAddr(target, lbl, addr); }); // Right-Click ab.addresses->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ab.addresses, &QTableView::customContextMenuRequested, [&] (QPoint pos) { QModelIndex index = ab.addresses->indexAt(pos); 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", [&] () { d.accept(); fnSetTargetLabelAddr(target, lbl, addr); }); } menu.addAction(QObject::tr("Copy address"), [&] () { QGuiApplication::clipboard()->setText(addr); parent->ui->statusBar->showMessage(QObject::tr("Copied to clipboard"), 3 * 1000); }); menu.addAction(QObject::tr("Delete label"), [&] () { model.removeItemAt(index.row()); }); menu.exec(ab.addresses->viewport()->mapToGlobal(pos)); }); if (d.exec() == QDialog::Accepted && target != nullptr) { auto selection = ab.addresses->selectionModel(); if (selection->hasSelection()) { auto item = model.itemAt(selection->selectedRows().at(0).row()); fnSetTargetLabelAddr(target, item.first, item.second); } }; } //============= // AddressBook singleton class //============= AddressBook* AddressBook::getInstance() { if (!instance) instance = new AddressBook(); return instance; } AddressBook::AddressBook() { readFromStorage(); } void AddressBook::readFromStorage() { QFile file(AddressBook::writeableFile()); if (!file.exists()) { return; } allLabels.clear(); file.open(QIODevice::ReadOnly); QDataStream in(&file); // read the data serialized from the file QString version; in >> version >> allLabels; file.close(); } void AddressBook::writeToStorage() { QFile file(AddressBook::writeableFile()); file.open(QIODevice::ReadWrite | QIODevice::Truncate); QDataStream out(&file); // we will serialize the data into the file out << QString("v1") << allLabels; 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); } } // Add a new address/label to the database void AddressBook::addAddressLabel(QString label, QString address) { Q_ASSERT(Settings::isValidAddress(address)); allLabels.push_back(QPair(label, address)); writeToStorage(); } // Remove a new address/label from the database void AddressBook::removeAddressLabel(QString label, QString address) { // Iterate over the list and remove the label/address for (int i=0; i < allLabels.size(); i++) { if (allLabels[i].first == label && allLabels[i].second == address) { allLabels.removeAt(i); writeToStorage(); return; } } } void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabel) { // Iterate over the list and update the label/address for (int i = 0; i < allLabels.size(); i++) { if (allLabels[i].first == oldlabel && allLabels[i].second == address) { allLabels[i].first = newlabel; writeToStorage(); return; } } } // Read all addresses const QList>& AddressBook::getAllAddressLabels() { return allLabels; } // Get the label for an address QString AddressBook::getLabelForAddress(QString addr) { for (auto i : allLabels) { if (i.second == addr) return i.first; } return ""; } QString AddressBook::addLabelToAddress(QString addr) { QString label = AddressBook::getInstance()->getLabelForAddress(addr); if (!label.isEmpty()) return label + "/" + addr; else return addr; } QString AddressBook::addressFromAddressLabel(const QString& lblAddr) { return lblAddr.trimmed().split("/").last(); } AddressBook* AddressBook::instance = nullptr;