X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fqt%2Faddresstablemodel.cpp;h=a5dc784e09fd3902fd65b3f066563d36f0fde511;hb=f8ea0dd6459856f2df18ca2ad532d49432a087dd;hp=eece092f0da545b23c6aa8f0892632e5015cb6b9;hpb=e8ef3da7133dd9fc411fa8b3cc8b8fc2f9c58a98;p=novacoin.git diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index eece092..a5dc784 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,7 +1,9 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "walletmodel.h" -#include "headers.h" +#include "wallet.h" +#include "base58.h" #include #include @@ -25,32 +27,96 @@ struct AddressTableEntry type(type), label(label), address(address) {} }; +struct AddressTableEntryLessThan +{ + bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const + { + return a.address < b.address; + } + bool operator()(const AddressTableEntry &a, const QString &b) const + { + return a.address < b; + } + bool operator()(const QString &a, const AddressTableEntry &b) const + { + return a < b.address; + } +}; + // Private implementation -struct AddressTablePriv +class AddressTablePriv { +public: CWallet *wallet; QList cachedAddressTable; + AddressTableModel *parent; - AddressTablePriv(CWallet *wallet): - wallet(wallet) {} + AddressTablePriv(CWallet *wallet, AddressTableModel *parent): + wallet(wallet), parent(parent) {} void refreshAddressTable() { cachedAddressTable.clear(); - - CRITICAL_BLOCK(wallet->cs_mapKeys) - CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, wallet->mapAddressBook) + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook) { - std::string strAddress = item.first; - std::string strName = item.second; - uint160 hash160; - bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + const CBitcoinAddress& address = item.first; + const std::string& strName = item.second; + bool fMine = IsMine(*wallet, address); cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, QString::fromStdString(strName), - QString::fromStdString(strAddress))); + QString::fromStdString(address.ToString()))); + } + } + // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); + } + + void updateEntry(const QString &address, const QString &label, bool isMine, int status) + { + // Find address / label in model + QList::iterator lower = qLowerBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + QList::iterator upper = qUpperBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + int lowerIndex = (lower - cachedAddressTable.begin()); + int upperIndex = (upper - cachedAddressTable.begin()); + bool inModel = (lower != upper); + AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n"); + break; + } + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); + parent->endInsertRows(); + break; + case CT_UPDATED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n"); + break; } + lower->type = newEntryType; + lower->label = label; + parent->emitDataChanged(lowerIndex); + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n"); + break; + } + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedAddressTable.erase(lower, upper); + parent->endRemoveRows(); + break; } } @@ -70,18 +136,13 @@ struct AddressTablePriv return 0; } } - - bool isDefaultAddress(const AddressTableEntry *rec) - { - return rec->address == QString::fromStdString(wallet->GetDefaultAddress()); - } }; -AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : - QAbstractTableModel(parent),wallet(wallet),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(wallet); + priv = new AddressTablePriv(wallet, this); priv->refreshAddressTable(); } @@ -114,11 +175,16 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const switch(index.column()) { case Label: - return rec->label; + if(rec->label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->label; + } case Address: return rec->address; - case IsDefaultAddress: - return priv->isDefaultAddress(rec); } } else if (role == Qt::FontRole) @@ -128,27 +194,8 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const { font = GUIUtil::bitcoinAddressFont(); } - if(priv->isDefaultAddress(rec)) - { - font.setBold(true); - } return font; } - else if (role == Qt::ForegroundRole) - { - // Show default address in alternative color - if(priv->isDefaultAddress(rec)) - { - return QColor(0,0,255); - } - } - else if (role == Qt::ToolTipRole) - { - if(priv->isDefaultAddress(rec)) - { - return tr("Default receiving address"); - } - } else if (role == TypeRole) { switch(rec->type) @@ -163,41 +210,60 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return QVariant(); } -bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) +bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; AddressTableEntry *rec = static_cast(index.internalPointer()); + editStatus = OK; + if(role == Qt::EditRole) { switch(index.column()) { case Label: - wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); - rec->label = value.toString(); + // Do nothing, if old label == new label + if(rec->label == value.toString()) + { + editStatus = NO_CHANGES; + return false; + } + wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()), value.toString().toStdString()); break; case Address: - // Double-check that we're not overwriting receiving address - if(rec->type == AddressTableEntry::Sending) + // Do nothing, if old address == new address + if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString())) { - // Remove old entry - wallet->EraseAddressBookName(rec->address.toStdString()); - // Add new entry with new address - wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); - - rec->address = value.toString(); + editStatus = NO_CHANGES; + return false; } - break; - case IsDefaultAddress: - if(value.toBool()) + // Refuse to set invalid address, set error status and return false + else if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; + return false; + } + // Check for duplicate addresses to prevent accidental deletion of addresses, if you try + // to paste an existing address over another address (with a different label) + else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()))) + { + editStatus = DUPLICATE_ADDRESS; + return false; + } + // Double-check that we're not overwriting a receiving address + else if(rec->type == AddressTableEntry::Sending) { - setDefaultAddress(rec->address); + { + LOCK(wallet->cs_wallet); + // Remove old entry + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString())); + // Add new entry with new address + wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()), rec->label.toStdString()); + } } break; } - emit dataChanged(index, index); - return true; } return false; @@ -215,7 +281,24 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const +{ + if(!index.isValid()) + return 0; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + // Can edit address and label for sending addresses, + // and only label for receiving addresses. + if(rec->type == AddressTableEntry::Sending || + (rec->type == AddressTableEntry::Receiving && index.column()==Label)) + { + retval |= Qt::ItemIsEditable; + } + return retval; +} + +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); AddressTableEntry *data = priv->index(row); @@ -229,51 +312,68 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa } } -void AddressTableModel::updateList() +void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status) { - // Update internal model from Bitcoin core - beginResetModel(); - priv->refreshAddressTable(); - endResetModel(); + // Update address book model from Bitcoin core + priv->updateEntry(address, label, isMine, status); } -QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault) +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); + editStatus = OK; + if(type == Send) { - // Check for duplicate - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } + // Check for duplicate addresses { - if(wallet->mapAddressBook.count(strAddress)) + LOCK(wallet->cs_wallet); + if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress))) { + editStatus = DUPLICATE_ADDRESS; return QString(); } } } else if(type == Receive) { - // Generate a new address to associate with given label, optionally - // set as default receiving address. - strAddress = PubKeyToAddress(wallet->GetKeyFromKeyPool()); - if(setAsDefault) + // Generate a new address to associate with given label + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + CPubKey newKey; + if(!wallet->GetKeyFromPool(newKey, true)) { - setDefaultAddress(QString::fromStdString(strAddress)); + editStatus = KEY_GENERATION_FAILURE; + return QString(); } + strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } else { return QString(); } - // Add entry and update list - wallet->SetAddressBookName(strAddress, strLabel); - updateList(); + + // Add entry + { + LOCK(wallet->cs_wallet); + wallet->SetAddressBookName(CBitcoinAddress(strAddress), strLabel); + } return QString::fromStdString(strAddress); } -bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) +bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); AddressTableEntry *rec = priv->index(row); @@ -283,22 +383,44 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - wallet->EraseAddressBookName(rec->address.toStdString()); - updateList(); + { + LOCK(wallet->cs_wallet); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString())); + } return true; } -QString AddressTableModel::getDefaultAddress() const +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const { - return QString::fromStdString(wallet->GetDefaultAddress()); + { + LOCK(wallet->cs_wallet); + CBitcoinAddress address_parsed(address.toStdString()); + std::map::iterator mi = wallet->mapAddressBook.find(address_parsed); + if (mi != wallet->mapAddressBook.end()) + { + return QString::fromStdString(mi->second); + } + } + return QString(); } -void AddressTableModel::setDefaultAddress(const QString &defaultAddress) +int AddressTableModel::lookupAddress(const QString &address) const { - wallet->SetDefaultAddress(defaultAddress.toStdString()); + QModelIndexList lst = match(index(0, Address, QModelIndex()), + Qt::EditRole, address, 1, Qt::MatchExactly); + if(lst.isEmpty()) + { + return -1; + } + else + { + return lst.at(0).row(); + } } -void AddressTableModel::update() +void AddressTableModel::emitDataChanged(int idx) { - emit defaultAddressChanged(getDefaultAddress()); + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); }