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