LCOV - code coverage report
Current view: top level - src/qt - addresstablemodel.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 180 0.0 %
Date: 2015-10-12 22:39:14 Functions: 0 26 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2013 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include "addresstablemodel.h"
       6             : 
       7             : #include "guiutil.h"
       8             : #include "walletmodel.h"
       9             : 
      10             : #include "base58.h"
      11             : #include "wallet/wallet.h"
      12             : 
      13             : #include <boost/foreach.hpp>
      14             : 
      15             : #include <QFont>
      16             : #include <QDebug>
      17             : 
      18           0 : const QString AddressTableModel::Send = "S";
      19           0 : const QString AddressTableModel::Receive = "R";
      20             : 
      21           0 : struct AddressTableEntry
      22             : {
      23             :     enum Type {
      24             :         Sending,
      25             :         Receiving,
      26             :         Hidden /* QSortFilterProxyModel will filter these out */
      27             :     };
      28             : 
      29             :     Type type;
      30             :     QString label;
      31             :     QString address;
      32             : 
      33             :     AddressTableEntry() {}
      34           0 :     AddressTableEntry(Type type, const QString &label, const QString &address):
      35           0 :         type(type), label(label), address(address) {}
      36             : };
      37             : 
      38             : struct AddressTableEntryLessThan
      39             : {
      40           0 :     bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
      41             :     {
      42           0 :         return a.address < b.address;
      43             :     }
      44           0 :     bool operator()(const AddressTableEntry &a, const QString &b) const
      45             :     {
      46           0 :         return a.address < b;
      47             :     }
      48           0 :     bool operator()(const QString &a, const AddressTableEntry &b) const
      49             :     {
      50           0 :         return a < b.address;
      51             :     }
      52             : };
      53             : 
      54             : /* Determine address type from address purpose */
      55           0 : static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
      56             : {
      57           0 :     AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
      58             :     // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
      59           0 :     if (strPurpose == "send")
      60             :         addressType = AddressTableEntry::Sending;
      61           0 :     else if (strPurpose == "receive")
      62             :         addressType = AddressTableEntry::Receiving;
      63           0 :     else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
      64           0 :         addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
      65           0 :     return addressType;
      66             : }
      67             : 
      68             : // Private implementation
      69           0 : class AddressTablePriv
      70             : {
      71             : public:
      72             :     CWallet *wallet;
      73             :     QList<AddressTableEntry> cachedAddressTable;
      74             :     AddressTableModel *parent;
      75             : 
      76           0 :     AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
      77           0 :         wallet(wallet), parent(parent) {}
      78             : 
      79           0 :     void refreshAddressTable()
      80             :     {
      81           0 :         cachedAddressTable.clear();
      82             :         {
      83           0 :             LOCK(wallet->cs_wallet);
      84           0 :             BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
      85             :             {
      86           0 :                 const CBitcoinAddress& address = item.first;
      87           0 :                 bool fMine = IsMine(*wallet, address.Get());
      88             :                 AddressTableEntry::Type addressType = translateTransactionType(
      89           0 :                         QString::fromStdString(item.second.purpose), fMine);
      90           0 :                 const std::string& strName = item.second.name;
      91             :                 cachedAddressTable.append(AddressTableEntry(addressType,
      92             :                                   QString::fromStdString(strName),
      93           0 :                                   QString::fromStdString(address.ToString())));
      94             :             }
      95             :         }
      96             :         // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
      97             :         // Even though the map is already sorted this re-sorting step is needed because the originating map
      98             :         // is sorted by binary address, not by base58() address.
      99           0 :         qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
     100           0 :     }
     101             : 
     102           0 :     void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
     103             :     {
     104             :         // Find address / label in model
     105             :         QList<AddressTableEntry>::iterator lower = qLowerBound(
     106           0 :             cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
     107             :         QList<AddressTableEntry>::iterator upper = qUpperBound(
     108           0 :             cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
     109           0 :         int lowerIndex = (lower - cachedAddressTable.begin());
     110           0 :         int upperIndex = (upper - cachedAddressTable.begin());
     111           0 :         bool inModel = (lower != upper);
     112           0 :         AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
     113             : 
     114           0 :         switch(status)
     115             :         {
     116             :         case CT_NEW:
     117           0 :             if(inModel)
     118             :             {
     119           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
     120           0 :                 break;
     121             :             }
     122           0 :             parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
     123           0 :             cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
     124           0 :             parent->endInsertRows();
     125             :             break;
     126             :         case CT_UPDATED:
     127           0 :             if(!inModel)
     128             :             {
     129           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
     130           0 :                 break;
     131             :             }
     132           0 :             lower->type = newEntryType;
     133           0 :             lower->label = label;
     134           0 :             parent->emitDataChanged(lowerIndex);
     135             :             break;
     136             :         case CT_DELETED:
     137           0 :             if(!inModel)
     138             :             {
     139           0 :                 qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
     140           0 :                 break;
     141             :             }
     142           0 :             parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
     143           0 :             cachedAddressTable.erase(lower, upper);
     144           0 :             parent->endRemoveRows();
     145             :             break;
     146             :         }
     147           0 :     }
     148             : 
     149             :     int size()
     150             :     {
     151           0 :         return cachedAddressTable.size();
     152             :     }
     153             : 
     154           0 :     AddressTableEntry *index(int idx)
     155             :     {
     156           0 :         if(idx >= 0 && idx < cachedAddressTable.size())
     157             :         {
     158           0 :             return &cachedAddressTable[idx];
     159             :         }
     160             :         else
     161             :         {
     162             :             return 0;
     163             :         }
     164             :     }
     165             : };
     166             : 
     167           0 : AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
     168           0 :     QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
     169             : {
     170           0 :     columns << tr("Label") << tr("Address");
     171           0 :     priv = new AddressTablePriv(wallet, this);
     172           0 :     priv->refreshAddressTable();
     173           0 : }
     174             : 
     175           0 : AddressTableModel::~AddressTableModel()
     176             : {
     177           0 :     delete priv;
     178           0 : }
     179             : 
     180           0 : int AddressTableModel::rowCount(const QModelIndex &parent) const
     181             : {
     182             :     Q_UNUSED(parent);
     183           0 :     return priv->size();
     184             : }
     185             : 
     186           0 : int AddressTableModel::columnCount(const QModelIndex &parent) const
     187             : {
     188             :     Q_UNUSED(parent);
     189           0 :     return columns.length();
     190             : }
     191             : 
     192           0 : QVariant AddressTableModel::data(const QModelIndex &index, int role) const
     193             : {
     194           0 :     if(!index.isValid())
     195             :         return QVariant();
     196             : 
     197           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     198             : 
     199           0 :     if(role == Qt::DisplayRole || role == Qt::EditRole)
     200             :     {
     201           0 :         switch(index.column())
     202             :         {
     203             :         case Label:
     204           0 :             if(rec->label.isEmpty() && role == Qt::DisplayRole)
     205             :             {
     206           0 :                 return tr("(no label)");
     207             :             }
     208             :             else
     209             :             {
     210           0 :                 return rec->label;
     211             :             }
     212             :         case Address:
     213           0 :             return rec->address;
     214             :         }
     215             :     }
     216           0 :     else if (role == Qt::FontRole)
     217             :     {
     218           0 :         QFont font;
     219           0 :         if(index.column() == Address)
     220             :         {
     221           0 :             font = GUIUtil::bitcoinAddressFont();
     222             :         }
     223           0 :         return font;
     224             :     }
     225           0 :     else if (role == TypeRole)
     226             :     {
     227           0 :         switch(rec->type)
     228             :         {
     229             :         case AddressTableEntry::Sending:
     230           0 :             return Send;
     231             :         case AddressTableEntry::Receiving:
     232           0 :             return Receive;
     233             :         default: break;
     234             :         }
     235             :     }
     236             :     return QVariant();
     237             : }
     238             : 
     239           0 : bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
     240             : {
     241           0 :     if(!index.isValid())
     242             :         return false;
     243           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     244           0 :     std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
     245           0 :     editStatus = OK;
     246             : 
     247           0 :     if(role == Qt::EditRole)
     248             :     {
     249           0 :         LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
     250           0 :         CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
     251           0 :         if(index.column() == Label)
     252             :         {
     253             :             // Do nothing, if old label == new label
     254           0 :             if(rec->label == value.toString())
     255             :             {
     256           0 :                 editStatus = NO_CHANGES;
     257           0 :                 return false;
     258             :             }
     259           0 :             wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
     260           0 :         } else if(index.column() == Address) {
     261           0 :             CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
     262             :             // Refuse to set invalid address, set error status and return false
     263           0 :             if(boost::get<CNoDestination>(&newAddress))
     264             :             {
     265           0 :                 editStatus = INVALID_ADDRESS;
     266           0 :                 return false;
     267             :             }
     268             :             // Do nothing, if old address == new address
     269           0 :             else if(newAddress == curAddress)
     270             :             {
     271           0 :                 editStatus = NO_CHANGES;
     272           0 :                 return false;
     273             :             }
     274             :             // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
     275             :             // to paste an existing address over another address (with a different label)
     276           0 :             else if(wallet->mapAddressBook.count(newAddress))
     277             :             {
     278           0 :                 editStatus = DUPLICATE_ADDRESS;
     279           0 :                 return false;
     280             :             }
     281             :             // Double-check that we're not overwriting a receiving address
     282           0 :             else if(rec->type == AddressTableEntry::Sending)
     283             :             {
     284             :                 // Remove old entry
     285           0 :                 wallet->DelAddressBook(curAddress);
     286             :                 // Add new entry with new address
     287           0 :                 wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
     288             :             }
     289             :         }
     290             :         return true;
     291             :     }
     292             :     return false;
     293             : }
     294             : 
     295           0 : QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
     296             : {
     297           0 :     if(orientation == Qt::Horizontal)
     298             :     {
     299           0 :         if(role == Qt::DisplayRole && section < columns.size())
     300             :         {
     301           0 :             return columns[section];
     302             :         }
     303             :     }
     304             :     return QVariant();
     305             : }
     306             : 
     307           0 : Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
     308             : {
     309           0 :     if(!index.isValid())
     310             :         return 0;
     311           0 :     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
     312             : 
     313             :     Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
     314             :     // Can edit address and label for sending addresses,
     315             :     // and only label for receiving addresses.
     316           0 :     if(rec->type == AddressTableEntry::Sending ||
     317           0 :       (rec->type == AddressTableEntry::Receiving && index.column()==Label))
     318             :     {
     319             :         retval |= Qt::ItemIsEditable;
     320             :     }
     321             :     return retval;
     322             : }
     323             : 
     324           0 : QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
     325             : {
     326             :     Q_UNUSED(parent);
     327           0 :     AddressTableEntry *data = priv->index(row);
     328           0 :     if(data)
     329             :     {
     330           0 :         return createIndex(row, column, priv->index(row));
     331             :     }
     332             :     else
     333             :     {
     334             :         return QModelIndex();
     335             :     }
     336             : }
     337             : 
     338           0 : void AddressTableModel::updateEntry(const QString &address,
     339             :         const QString &label, bool isMine, const QString &purpose, int status)
     340             : {
     341             :     // Update address book model from Bitcoin core
     342           0 :     priv->updateEntry(address, label, isMine, purpose, status);
     343           0 : }
     344             : 
     345           0 : QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
     346             : {
     347           0 :     std::string strLabel = label.toStdString();
     348           0 :     std::string strAddress = address.toStdString();
     349             : 
     350           0 :     editStatus = OK;
     351             : 
     352           0 :     if(type == Send)
     353             :     {
     354           0 :         if(!walletModel->validateAddress(address))
     355             :         {
     356           0 :             editStatus = INVALID_ADDRESS;
     357             :             return QString();
     358             :         }
     359             :         // Check for duplicate addresses
     360             :         {
     361           0 :             LOCK(wallet->cs_wallet);
     362           0 :             if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
     363             :             {
     364           0 :                 editStatus = DUPLICATE_ADDRESS;
     365             :                 return QString();
     366             :             }
     367             :         }
     368             :     }
     369           0 :     else if(type == Receive)
     370             :     {
     371             :         // Generate a new address to associate with given label
     372             :         CPubKey newKey;
     373           0 :         if(!wallet->GetKeyFromPool(newKey))
     374             :         {
     375           0 :             WalletModel::UnlockContext ctx(walletModel->requestUnlock());
     376           0 :             if(!ctx.isValid())
     377             :             {
     378             :                 // Unlock wallet failed or was cancelled
     379           0 :                 editStatus = WALLET_UNLOCK_FAILURE;
     380           0 :                 return QString();
     381             :             }
     382           0 :             if(!wallet->GetKeyFromPool(newKey))
     383             :             {
     384           0 :                 editStatus = KEY_GENERATION_FAILURE;
     385             :                 return QString();
     386           0 :             }
     387             :         }
     388           0 :         strAddress = CBitcoinAddress(newKey.GetID()).ToString();
     389             :     }
     390             :     else
     391             :     {
     392             :         return QString();
     393             :     }
     394             : 
     395             :     // Add entry
     396             :     {
     397           0 :         LOCK(wallet->cs_wallet);
     398             :         wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
     399           0 :                                (type == Send ? "send" : "receive"));
     400             :     }
     401             :     return QString::fromStdString(strAddress);
     402             : }
     403             : 
     404           0 : bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
     405             : {
     406             :     Q_UNUSED(parent);
     407           0 :     AddressTableEntry *rec = priv->index(row);
     408           0 :     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
     409             :     {
     410             :         // Can only remove one row at a time, and cannot remove rows not in model.
     411             :         // Also refuse to remove receiving addresses.
     412             :         return false;
     413             :     }
     414             :     {
     415           0 :         LOCK(wallet->cs_wallet);
     416           0 :         wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
     417             :     }
     418           0 :     return true;
     419             : }
     420             : 
     421             : /* Look up label for address in address book, if not found return empty string.
     422             :  */
     423           0 : QString AddressTableModel::labelForAddress(const QString &address) const
     424             : {
     425             :     {
     426           0 :         LOCK(wallet->cs_wallet);
     427           0 :         CBitcoinAddress address_parsed(address.toStdString());
     428           0 :         std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
     429           0 :         if (mi != wallet->mapAddressBook.end())
     430             :         {
     431           0 :             return QString::fromStdString(mi->second.name);
     432             :         }
     433             :     }
     434             :     return QString();
     435             : }
     436             : 
     437           0 : int AddressTableModel::lookupAddress(const QString &address) const
     438             : {
     439           0 :     QModelIndexList lst = match(index(0, Address, QModelIndex()),
     440           0 :                                 Qt::EditRole, address, 1, Qt::MatchExactly);
     441           0 :     if(lst.isEmpty())
     442             :     {
     443             :         return -1;
     444             :     }
     445             :     else
     446             :     {
     447           0 :         return lst.at(0).row();
     448           0 :     }
     449             : }
     450             : 
     451           0 : void AddressTableModel::emitDataChanged(int idx)
     452             : {
     453           0 :     Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
     454           0 : }

Generated by: LCOV version 1.11