X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fqt%2Faddresstablemodel.cpp;h=a5dc784e09fd3902fd65b3f066563d36f0fde511;hb=db1eaec482383385156b3e7cb6b2baa9454975f0;hp=0558bfa45356838a2c26b9ffda50b56632b508a8;hpb=f5927f5b32c9e28033fb7a00dd43f3162781c1d0;p=novacoin.git diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 0558bfa..a5dc784 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,6 +1,9 @@ #include "addresstablemodel.h" #include "guiutil.h" -#include "main.h" +#include "walletmodel.h" + +#include "wallet.h" +#include "base58.h" #include #include @@ -22,40 +25,98 @@ struct AddressTableEntry AddressTableEntry() {} AddressTableEntry(Type type, const QString &label, const QString &address): type(type), label(label), address(address) {} +}; - bool isDefaultAddress() const +struct AddressTableEntryLessThan +{ + bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const { - std::vector vchPubKey; - if (CWalletDB("r").ReadDefaultKey(vchPubKey)) - { - return address == QString::fromStdString(PubKeyToAddress(vchPubKey)); - } - return false; + 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, AddressTableModel *parent): + wallet(wallet), parent(parent) {} void refreshAddressTable() { cachedAddressTable.clear(); - - CRITICAL_BLOCK(cs_mapKeys) - CRITICAL_BLOCK(cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, 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; } } @@ -77,11 +138,11 @@ struct AddressTablePriv } }; -AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(); + priv = new AddressTablePriv(wallet, this); priv->refreshAddressTable(); } @@ -114,7 +175,14 @@ 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; } @@ -126,27 +194,8 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const { font = GUIUtil::bitcoinAddressFont(); } - if(rec->isDefaultAddress()) - { - font.setBold(true); - } return font; } - else if (role == Qt::ForegroundRole) - { - // Show default address in alternative color - if(rec->isDefaultAddress()) - { - return QColor(0,0,255); - } - } - else if (role == Qt::ToolTipRole) - { - if(rec->isDefaultAddress()) - { - return tr("Default receiving address"); - } - } else if (role == TypeRole) { switch(rec->type) @@ -161,35 +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: - 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 - CWalletDB().EraseName(rec->address.toStdString()); - // Add new entry with new address - SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); - - rec->address = value.toString(); + editStatus = NO_CHANGES; + return false; + } + // 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) + { + { + 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; @@ -207,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); @@ -221,12 +312,10 @@ 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) @@ -234,13 +323,21 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); + editStatus = OK; + if(type == Send) { - // Check for duplicate - CRITICAL_BLOCK(cs_mapAddressBook) + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } + // Check for duplicate addresses { - if(mapAddressBook.count(strAddress)) + LOCK(wallet->cs_wallet); + if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress))) { + editStatus = DUPLICATE_ADDRESS; return QString(); } } @@ -248,19 +345,35 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con else if(type == Receive) { // Generate a new address to associate with given label - strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + 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)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } else { return QString(); } - // Add entry and update list - 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); @@ -270,7 +383,44 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - CWalletDB().EraseName(rec->address.toStdString()); - updateList(); + { + LOCK(wallet->cs_wallet); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString())); + } return true; } + +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const +{ + { + 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(); +} + +int AddressTableModel::lookupAddress(const QString &address) const +{ + 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::emitDataChanged(int idx) +{ + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); +}