1 #include "addresstablemodel.h"
4 #include "walletmodel.h"
11 const QString AddressTableModel::Send = "S";
12 const QString AddressTableModel::Receive = "R";
14 struct AddressTableEntry
25 AddressTableEntry() {}
26 AddressTableEntry(Type type, const QString &label, const QString &address):
27 type(type), label(label), address(address) {}
30 struct AddressTableEntryLessThan
32 bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
34 return a.address < b.address;
36 bool operator()(const AddressTableEntry &a, const QString &b) const
40 bool operator()(const QString &a, const AddressTableEntry &b) const
46 // Private implementation
47 class AddressTablePriv
51 QList<AddressTableEntry> cachedAddressTable;
52 AddressTableModel *parent;
54 AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
55 wallet(wallet), parent(parent) {}
57 void refreshAddressTable()
59 cachedAddressTable.clear();
61 LOCK(wallet->cs_wallet);
62 BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
64 const CBitcoinAddress& address = item.first;
66 AddressTableEntry::Type addressType;
67 const std::string& strPurpose = item.second.purpose;
68 if (strPurpose == "send") addressType = AddressTableEntry::Sending;
69 else if (strPurpose == "receive") addressType = AddressTableEntry::Receiving;
70 else if (strPurpose == "unknown") {
71 bool fMine = IsMine(*wallet, address.Get());
72 addressType = (fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
74 else continue; // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
76 const std::string& strName = item.second.name;
77 cachedAddressTable.append(AddressTableEntry(addressType,
78 QString::fromStdString(strName),
79 QString::fromStdString(address.ToString())));
82 // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
83 qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
86 void updateEntry(const QString &address, const QString &label, bool isMine, int status)
88 // Find address / label in model
89 QList<AddressTableEntry>::iterator lower = qLowerBound(
90 cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
91 QList<AddressTableEntry>::iterator upper = qUpperBound(
92 cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
93 int lowerIndex = (lower - cachedAddressTable.begin());
94 int upperIndex = (upper - cachedAddressTable.begin());
95 bool inModel = (lower != upper);
96 AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending;
103 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
106 parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
107 cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
108 parent->endInsertRows();
113 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
116 lower->type = newEntryType;
117 lower->label = label;
118 parent->emitDataChanged(lowerIndex);
123 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
126 parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
127 cachedAddressTable.erase(lower, upper);
128 parent->endRemoveRows();
135 return cachedAddressTable.size();
138 AddressTableEntry *index(int idx)
140 if(idx >= 0 && idx < cachedAddressTable.size())
142 return &cachedAddressTable[idx];
151 AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
152 QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
154 columns << tr("Label") << tr("Address");
155 priv = new AddressTablePriv(wallet, this);
156 priv->refreshAddressTable();
159 AddressTableModel::~AddressTableModel()
164 int AddressTableModel::rowCount(const QModelIndex &parent) const
170 int AddressTableModel::columnCount(const QModelIndex &parent) const
173 return columns.length();
176 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
181 AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
183 if(role == Qt::DisplayRole || role == Qt::EditRole)
185 switch(index.column())
188 if(rec->label.isEmpty() && role == Qt::DisplayRole)
190 return tr("(no label)");
200 else if (role == Qt::FontRole)
203 if(index.column() == Address)
205 font = GUIUtil::bitcoinAddressFont();
209 else if (role == TypeRole)
213 case AddressTableEntry::Sending:
215 case AddressTableEntry::Receiving:
223 bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
227 AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
228 std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
231 if(role == Qt::EditRole)
233 switch(index.column())
236 // Do nothing, if old label == new label
237 if(rec->label == value.toString())
239 editStatus = NO_CHANGES;
242 wallet->SetAddressBook(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString(), strPurpose);
245 // Do nothing, if old address == new address
246 if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString()))
248 editStatus = NO_CHANGES;
251 // Refuse to set invalid address, set error status and return false
252 else if(!walletModel->validateAddress(value.toString()))
254 editStatus = INVALID_ADDRESS;
257 // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
258 // to paste an existing address over another address (with a different label)
259 else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get()))
261 editStatus = DUPLICATE_ADDRESS;
264 // Double-check that we're not overwriting a receiving address
265 else if(rec->type == AddressTableEntry::Sending)
268 LOCK(wallet->cs_wallet);
270 wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
271 // Add new entry with new address
272 wallet->SetAddressBook(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString(), strPurpose);
282 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
284 if(orientation == Qt::Horizontal)
286 if(role == Qt::DisplayRole)
288 return columns[section];
294 Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
298 AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
300 Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
301 // Can edit address and label for sending addresses,
302 // and only label for receiving addresses.
303 if(rec->type == AddressTableEntry::Sending ||
304 (rec->type == AddressTableEntry::Receiving && index.column()==Label))
306 retval |= Qt::ItemIsEditable;
311 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
314 AddressTableEntry *data = priv->index(row);
317 return createIndex(row, column, priv->index(row));
321 return QModelIndex();
325 void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
327 // Update address book model from Bitcoin core
328 priv->updateEntry(address, label, isMine, status);
331 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
333 std::string strLabel = label.toStdString();
334 std::string strAddress = address.toStdString();
340 if(!walletModel->validateAddress(address))
342 editStatus = INVALID_ADDRESS;
345 // Check for duplicate addresses
347 LOCK(wallet->cs_wallet);
348 if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
350 editStatus = DUPLICATE_ADDRESS;
355 else if(type == Receive)
357 // Generate a new address to associate with given label
358 WalletModel::UnlockContext ctx(walletModel->requestUnlock());
361 // Unlock wallet failed or was cancelled
362 editStatus = WALLET_UNLOCK_FAILURE;
366 if(!wallet->GetKeyFromPool(newKey, true))
368 editStatus = KEY_GENERATION_FAILURE;
371 strAddress = CBitcoinAddress(newKey.GetID()).ToString();
380 LOCK(wallet->cs_wallet);
381 wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
382 (type == Send ? "send" : "receive"));
384 return QString::fromStdString(strAddress);
387 bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
390 AddressTableEntry *rec = priv->index(row);
391 if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
393 // Can only remove one row at a time, and cannot remove rows not in model.
394 // Also refuse to remove receiving addresses.
398 LOCK(wallet->cs_wallet);
399 wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
404 /* Look up label for address in address book, if not found return empty string.
406 QString AddressTableModel::labelForAddress(const QString &address) const
409 LOCK(wallet->cs_wallet);
410 CBitcoinAddress address_parsed(address.toStdString());
411 std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
412 if (mi != wallet->mapAddressBook.end())
414 return QString::fromStdString(mi->second.name);
420 int AddressTableModel::lookupAddress(const QString &address) const
422 QModelIndexList lst = match(index(0, Address, QModelIndex()),
423 Qt::EditRole, address, 1, Qt::MatchExactly);
430 return lst.at(0).row();
434 void AddressTableModel::emitDataChanged(int idx)
436 emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));