Changeset View
Changeset View
Standalone View
Standalone View
src/qt/addresstablemodel.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "addresstablemodel.h" | #include "addresstablemodel.h" | ||||
#include "guiutil.h" | #include "guiutil.h" | ||||
#include "walletmodel.h" | #include "walletmodel.h" | ||||
#include "dstencode.h" | #include "dstencode.h" | ||||
#include "interface/node.h" | |||||
#include "wallet/wallet.h" | #include "wallet/wallet.h" | ||||
#include <QDebug> | #include <QDebug> | ||||
#include <QFont> | #include <QFont> | ||||
const QString AddressTableModel::Send = "S"; | const QString AddressTableModel::Send = "S"; | ||||
const QString AddressTableModel::Receive = "R"; | const QString AddressTableModel::Receive = "R"; | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | else if (strPurpose == "unknown" || strPurpose == "") { | ||||
: AddressTableEntry::Sending); | : AddressTableEntry::Sending); | ||||
} | } | ||||
return addressType; | return addressType; | ||||
} | } | ||||
// Private implementation | // Private implementation | ||||
class AddressTablePriv { | class AddressTablePriv { | ||||
public: | public: | ||||
CWallet *wallet; | |||||
QList<AddressTableEntry> cachedAddressTable; | QList<AddressTableEntry> cachedAddressTable; | ||||
AddressTableModel *parent; | AddressTableModel *parent; | ||||
AddressTablePriv(CWallet *_wallet, AddressTableModel *_parent) | AddressTablePriv(AddressTableModel *_parent) : parent(_parent) {} | ||||
: wallet(_wallet), parent(_parent) {} | |||||
void refreshAddressTable() { | void refreshAddressTable(interface::Wallet &wallet) { | ||||
cachedAddressTable.clear(); | cachedAddressTable.clear(); | ||||
{ | { | ||||
LOCK(wallet->cs_wallet); | for (const auto &address : wallet.getAddresses()) { | ||||
for (const std::pair<CTxDestination, CAddressBookData> &item : | |||||
wallet->mapAddressBook) { | |||||
const CTxDestination &address = item.first; | |||||
bool fMine = IsMine(*wallet, address); | |||||
AddressTableEntry::Type addressType = translateTransactionType( | AddressTableEntry::Type addressType = translateTransactionType( | ||||
QString::fromStdString(item.second.purpose), fMine); | QString::fromStdString(address.purpose), address.is_mine); | ||||
const std::string &strName = item.second.name; | |||||
cachedAddressTable.append(AddressTableEntry( | cachedAddressTable.append(AddressTableEntry( | ||||
addressType, QString::fromStdString(strName), | addressType, QString::fromStdString(address.name), | ||||
QString::fromStdString(EncodeDestination(address)))); | QString::fromStdString(EncodeDestination(address.dest)))); | ||||
} | } | ||||
} | } | ||||
// qLowerBound() and qUpperBound() require our cachedAddressTable list | // qLowerBound() and qUpperBound() require our cachedAddressTable list | ||||
// to be sorted in asc order. Even though the map is already sorted this | // to be sorted in asc order. Even though the map is already sorted this | ||||
// re-sorting step is needed because the originating map is sorted by | // re-sorting step is needed because the originating map is sorted by | ||||
// binary address, not by base58() address. | // binary address, not by base58() address. | ||||
qSort(cachedAddressTable.begin(), cachedAddressTable.end(), | qSort(cachedAddressTable.begin(), cachedAddressTable.end(), | ||||
AddressTableEntryLessThan()); | AddressTableEntryLessThan()); | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | AddressTableEntry *index(int idx) { | ||||
if (idx >= 0 && idx < cachedAddressTable.size()) { | if (idx >= 0 && idx < cachedAddressTable.size()) { | ||||
return &cachedAddressTable[idx]; | return &cachedAddressTable[idx]; | ||||
} else { | } else { | ||||
return 0; | return 0; | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
AddressTableModel::AddressTableModel(CWallet *_wallet, WalletModel *parent) | AddressTableModel::AddressTableModel(WalletModel *parent) | ||||
: QAbstractTableModel(parent), walletModel(parent), wallet(_wallet), | : QAbstractTableModel(parent), walletModel(parent), priv(0) { | ||||
priv(0) { | |||||
columns << tr("Label") << tr("Address"); | columns << tr("Label") << tr("Address"); | ||||
priv = new AddressTablePriv(wallet, this); | priv = new AddressTablePriv(this); | ||||
priv->refreshAddressTable(); | priv->refreshAddressTable(parent->wallet()); | ||||
} | } | ||||
AddressTableModel::~AddressTableModel() { | AddressTableModel::~AddressTableModel() { | ||||
delete priv; | delete priv; | ||||
} | } | ||||
int AddressTableModel::rowCount(const QModelIndex &parent) const { | int AddressTableModel::rowCount(const QModelIndex &parent) const { | ||||
Q_UNUSED(parent); | Q_UNUSED(parent); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, | ||||
if (!index.isValid()) return false; | if (!index.isValid()) return false; | ||||
AddressTableEntry *rec = | AddressTableEntry *rec = | ||||
static_cast<AddressTableEntry *>(index.internalPointer()); | static_cast<AddressTableEntry *>(index.internalPointer()); | ||||
std::string strPurpose = | std::string strPurpose = | ||||
(rec->type == AddressTableEntry::Sending ? "send" : "receive"); | (rec->type == AddressTableEntry::Sending ? "send" : "receive"); | ||||
editStatus = OK; | editStatus = OK; | ||||
if (role == Qt::EditRole) { | if (role == Qt::EditRole) { | ||||
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ | CTxDestination curAddress = DecodeDestination( | ||||
CTxDestination curAddress = | rec->address.toStdString(), walletModel->getChainParams()); | ||||
DecodeDestination(rec->address.toStdString(), wallet->chainParams); | |||||
if (index.column() == Label) { | if (index.column() == Label) { | ||||
// Do nothing, if old label == new label | // Do nothing, if old label == new label | ||||
if (rec->label == value.toString()) { | if (rec->label == value.toString()) { | ||||
editStatus = NO_CHANGES; | editStatus = NO_CHANGES; | ||||
return false; | return false; | ||||
} | } | ||||
wallet->SetAddressBook(curAddress, value.toString().toStdString(), | walletModel->wallet().setAddressBook( | ||||
strPurpose); | curAddress, value.toString().toStdString(), strPurpose); | ||||
} else if (index.column() == Address) { | } else if (index.column() == Address) { | ||||
CTxDestination newAddress = DecodeDestination( | CTxDestination newAddress = DecodeDestination( | ||||
value.toString().toStdString(), wallet->chainParams); | value.toString().toStdString(), walletModel->getChainParams()); | ||||
// Refuse to set invalid address, set error status and return false | // Refuse to set invalid address, set error status and return false | ||||
if (boost::get<CNoDestination>(&newAddress)) { | if (boost::get<CNoDestination>(&newAddress)) { | ||||
editStatus = INVALID_ADDRESS; | editStatus = INVALID_ADDRESS; | ||||
return false; | return false; | ||||
} | } | ||||
// Do nothing, if old address == new address | // Do nothing, if old address == new address | ||||
else if (newAddress == curAddress) { | else if (newAddress == curAddress) { | ||||
editStatus = NO_CHANGES; | editStatus = NO_CHANGES; | ||||
return false; | return false; | ||||
} | } | ||||
// Check for duplicate addresses to prevent accidental deletion of | // Check for duplicate addresses to prevent accidental deletion of | ||||
// addresses, if you try to paste an existing address over another | // addresses, if you try to paste an existing address over another | ||||
// address (with a different label) | // address (with a different label) | ||||
else if (wallet->mapAddressBook.count(newAddress)) { | if (walletModel->wallet().getAddress(newAddress)) { | ||||
editStatus = DUPLICATE_ADDRESS; | editStatus = DUPLICATE_ADDRESS; | ||||
return false; | return false; | ||||
} | } | ||||
// Double-check that we're not overwriting a receiving address | // Double-check that we're not overwriting a receiving address | ||||
else if (rec->type == AddressTableEntry::Sending) { | else if (rec->type == AddressTableEntry::Sending) { | ||||
// Remove old entry | // Remove old entry | ||||
wallet->DelAddressBook(curAddress); | walletModel->wallet().delAddressBook(curAddress); | ||||
// Add new entry with new address | // Add new entry with new address | ||||
wallet->SetAddressBook(newAddress, rec->label.toStdString(), | walletModel->wallet().setAddressBook( | ||||
strPurpose); | newAddress, value.toString().toStdString(), strPurpose); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, | QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | QString AddressTableModel::addRow(const QString &type, const QString &label, | ||||
if (type == Send) { | if (type == Send) { | ||||
if (!walletModel->validateAddress(address)) { | if (!walletModel->validateAddress(address)) { | ||||
editStatus = INVALID_ADDRESS; | editStatus = INVALID_ADDRESS; | ||||
return QString(); | return QString(); | ||||
} | } | ||||
// Check for duplicate addresses | // Check for duplicate addresses | ||||
{ | { | ||||
LOCK(wallet->cs_wallet); | if (walletModel->wallet().getAddress(DecodeDestination( | ||||
if (wallet->mapAddressBook.count( | strAddress, walletModel->getChainParams()))) { | ||||
DecodeDestination(strAddress, wallet->chainParams))) { | |||||
editStatus = DUPLICATE_ADDRESS; | editStatus = DUPLICATE_ADDRESS; | ||||
return QString(); | return QString(); | ||||
} | } | ||||
} | } | ||||
} else if (type == Receive) { | } else if (type == Receive) { | ||||
// Generate a new address to associate with given label | // Generate a new address to associate with given label | ||||
CPubKey newKey; | CPubKey newKey; | ||||
if (!wallet->GetKeyFromPool(newKey)) { | if (!walletModel->wallet().getKeyFromPool(false /* internal */, | ||||
newKey)) { | |||||
WalletModel::UnlockContext ctx(walletModel->requestUnlock()); | WalletModel::UnlockContext ctx(walletModel->requestUnlock()); | ||||
if (!ctx.isValid()) { | if (!ctx.isValid()) { | ||||
// Unlock wallet failed or was cancelled | // Unlock wallet failed or was cancelled | ||||
editStatus = WALLET_UNLOCK_FAILURE; | editStatus = WALLET_UNLOCK_FAILURE; | ||||
return QString(); | return QString(); | ||||
} | } | ||||
if (!wallet->GetKeyFromPool(newKey)) { | if (!walletModel->wallet().getKeyFromPool(false /* internal */, | ||||
newKey)) { | |||||
editStatus = KEY_GENERATION_FAILURE; | editStatus = KEY_GENERATION_FAILURE; | ||||
return QString(); | return QString(); | ||||
} | } | ||||
} | } | ||||
strAddress = EncodeDestination(newKey.GetID()); | strAddress = EncodeDestination(newKey.GetID()); | ||||
} else { | } else { | ||||
return QString(); | return QString(); | ||||
} | } | ||||
// Add entry | // Add entry | ||||
wallet->SetAddressBook(DecodeDestination(strAddress, wallet->chainParams), | walletModel->wallet().setAddressBook( | ||||
strLabel, (type == Send ? "send" : "receive")); | DecodeDestination(strAddress, walletModel->getChainParams()), strLabel, | ||||
(type == Send ? "send" : "receive")); | |||||
return QString::fromStdString(strAddress); | return QString::fromStdString(strAddress); | ||||
} | } | ||||
bool AddressTableModel::removeRows(int row, int count, | bool AddressTableModel::removeRows(int row, int count, | ||||
const QModelIndex &parent) { | const QModelIndex &parent) { | ||||
Q_UNUSED(parent); | Q_UNUSED(parent); | ||||
AddressTableEntry *rec = priv->index(row); | AddressTableEntry *rec = priv->index(row); | ||||
if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { | if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { | ||||
// Can only remove one row at a time, and cannot remove rows not in | // Can only remove one row at a time, and cannot remove rows not in | ||||
// model. | // model. | ||||
// Also refuse to remove receiving addresses. | // Also refuse to remove receiving addresses. | ||||
return false; | return false; | ||||
} | } | ||||
wallet->DelAddressBook( | walletModel->wallet().delAddressBook(DecodeDestination( | ||||
DecodeDestination(rec->address.toStdString(), wallet->chainParams)); | rec->address.toStdString(), walletModel->getChainParams())); | ||||
return true; | return true; | ||||
} | } | ||||
/* Look up label for address in address book, if not found return empty string. | /* Look up label for address in address book, if not found return empty string. | ||||
*/ | */ | ||||
QString AddressTableModel::labelForAddress(const QString &address) const { | QString AddressTableModel::labelForAddress(const QString &address) const { | ||||
{ | { | ||||
LOCK(wallet->cs_wallet); | CTxDestination destination = DecodeDestination( | ||||
CTxDestination destination = | address.toStdString(), walletModel->getChainParams()); | ||||
DecodeDestination(address.toStdString(), wallet->chainParams); | std::string name; | ||||
std::map<CTxDestination, CAddressBookData>::iterator mi = | if (walletModel->wallet().getAddress(destination, &name)) { | ||||
wallet->mapAddressBook.find(destination); | return QString::fromStdString(name); | ||||
if (mi != wallet->mapAddressBook.end()) { | |||||
return QString::fromStdString(mi->second.name); | |||||
} | } | ||||
} | } | ||||
return QString(); | return QString(); | ||||
} | } | ||||
int AddressTableModel::lookupAddress(const QString &address) const { | int AddressTableModel::lookupAddress(const QString &address) const { | ||||
QModelIndexList lst = match(index(0, Address, QModelIndex()), Qt::EditRole, | QModelIndexList lst = match(index(0, Address, QModelIndex()), Qt::EditRole, | ||||
address, 1, Qt::MatchExactly); | address, 1, Qt::MatchExactly); | ||||
Show All 11 Lines |