Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
addresstablemodel.cpp
Go to the documentation of this file.
1 #include "addresstablemodel.h"
2 
3 #include "guiutil.h"
4 #include "walletmodel.h"
5 
6 #include "wallet.h"
7 #include "base58.h"
8 
9 #include <QFont>
10 
11 const QString AddressTableModel::Send = "S";
12 const QString AddressTableModel::Receive = "R";
13 
15 {
16  enum Type {
19  };
20 
22  QString label;
23  QString address;
24 
26  AddressTableEntry(Type type, const QString &label, const QString &address):
27  type(type), label(label), address(address) {}
28 };
29 
31 {
32  bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
33  {
34  return a.address < b.address;
35  }
36  bool operator()(const AddressTableEntry &a, const QString &b) const
37  {
38  return a.address < b;
39  }
40  bool operator()(const QString &a, const AddressTableEntry &b) const
41  {
42  return a < b.address;
43  }
44 };
45 
46 // Private implementation
48 {
49 public:
51  QList<AddressTableEntry> cachedAddressTable;
53 
55  wallet(wallet), parent(parent) {}
56 
58  {
59  cachedAddressTable.clear();
60  {
61  LOCK(wallet->cs_wallet);
62  BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& item, wallet->mapAddressBook)
63  {
64  const CBitcoinAddress& address = item.first;
65  const std::string& strName = item.second;
66  bool fMine = IsMine(*wallet, address.Get());
68  QString::fromStdString(strName),
69  QString::fromStdString(address.ToString())));
70  }
71  }
72  // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
73  qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
74  }
75 
76  void updateEntry(const QString &address, const QString &label, bool isMine, int status)
77  {
78  // Find address / label in model
79  QList<AddressTableEntry>::iterator lower = qLowerBound(
80  cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
81  QList<AddressTableEntry>::iterator upper = qUpperBound(
82  cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
83  int lowerIndex = (lower - cachedAddressTable.begin());
84  int upperIndex = (upper - cachedAddressTable.begin());
85  bool inModel = (lower != upper);
87 
88  switch(status)
89  {
90  case CT_NEW:
91  if(inModel)
92  {
93  OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
94  break;
95  }
96  parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
97  cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
98  parent->endInsertRows();
99  break;
100  case CT_UPDATED:
101  if(!inModel)
102  {
103  OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
104  break;
105  }
106  lower->type = newEntryType;
107  lower->label = label;
108  parent->emitDataChanged(lowerIndex);
109  break;
110  case CT_DELETED:
111  if(!inModel)
112  {
113  OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
114  break;
115  }
116  parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
117  cachedAddressTable.erase(lower, upper);
118  parent->endRemoveRows();
119  break;
120  }
121  }
122 
123  int size()
124  {
125  return cachedAddressTable.size();
126  }
127 
129  {
130  if(idx >= 0 && idx < cachedAddressTable.size())
131  {
132  return &cachedAddressTable[idx];
133  }
134  else
135  {
136  return 0;
137  }
138  }
139 };
140 
142  QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
143 {
144  columns << tr("Label") << tr("Address");
145  priv = new AddressTablePriv(wallet, this);
147 }
148 
150 {
151  delete priv;
152 }
153 
154 int AddressTableModel::rowCount(const QModelIndex &parent) const
155 {
156  Q_UNUSED(parent);
157  return priv->size();
158 }
159 
160 int AddressTableModel::columnCount(const QModelIndex &parent) const
161 {
162  Q_UNUSED(parent);
163  return columns.length();
164 }
165 
166 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
167 {
168  if(!index.isValid())
169  return QVariant();
170 
171  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
172 
173  if(role == Qt::DisplayRole || role == Qt::EditRole)
174  {
175  switch(index.column())
176  {
177  case Label:
178  if(rec->label.isEmpty() && role == Qt::DisplayRole)
179  {
180  return tr("(no label)");
181  }
182  else
183  {
184  return rec->label;
185  }
186  case Address:
187  return rec->address;
188  }
189  }
190  else if (role == Qt::FontRole)
191  {
192  QFont font;
193  if(index.column() == Address)
194  {
196  }
197  return font;
198  }
199  else if (role == TypeRole)
200  {
201  switch(rec->type)
202  {
204  return Send;
206  return Receive;
207  default: break;
208  }
209  }
210  return QVariant();
211 }
212 
213 bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
214 {
215  if(!index.isValid())
216  return false;
217  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
218 
219  editStatus = OK;
220 
221  if(role == Qt::EditRole)
222  {
223  switch(index.column())
224  {
225  case Label:
226  // Do nothing, if old label == new label
227  if(rec->label == value.toString())
228  {
230  return false;
231  }
232  wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString());
233  break;
234  case Address:
235  // Do nothing, if old address == new address
236  if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString()))
237  {
239  return false;
240  }
241  // Refuse to set invalid address, set error status and return false
242  else if(!walletModel->validateAddress(value.toString()))
243  {
245  return false;
246  }
247  // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
248  // to paste an existing address over another address (with a different label)
249  else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get()))
250  {
252  return false;
253  }
254  // Double-check that we're not overwriting a receiving address
255  else if(rec->type == AddressTableEntry::Sending)
256  {
257  {
259  // Remove old entry
260  wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
261  // Add new entry with new address
262  wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString());
263  }
264  }
265  break;
266  }
267  return true;
268  }
269  return false;
270 }
271 
272 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
273 {
274  if(orientation == Qt::Horizontal)
275  {
276  if(role == Qt::DisplayRole)
277  {
278  return columns[section];
279  }
280  }
281  return QVariant();
282 }
283 
284 Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
285 {
286  if(!index.isValid())
287  return 0;
288  AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
289 
290  Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
291  // Can edit address and label for sending addresses,
292  // and only label for receiving addresses.
293  if(rec->type == AddressTableEntry::Sending ||
294  (rec->type == AddressTableEntry::Receiving && index.column()==Label))
295  {
296  retval |= Qt::ItemIsEditable;
297  }
298  return retval;
299 }
300 
301 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
302 {
303  Q_UNUSED(parent);
304  AddressTableEntry *data = priv->index(row);
305  if(data)
306  {
307  return createIndex(row, column, priv->index(row));
308  }
309  else
310  {
311  return QModelIndex();
312  }
313 }
314 
315 void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
316 {
317  // Update address book model from Bitcoin core
318  priv->updateEntry(address, label, isMine, status);
319 }
320 
321 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
322 {
323  std::string strLabel = label.toStdString();
324  std::string strAddress = address.toStdString();
325 
326  editStatus = OK;
327 
328  if(type == Send)
329  {
330  if(!walletModel->validateAddress(address))
331  {
333  return QString();
334  }
335  // Check for duplicate addresses
336  {
338  if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
339  {
341  return QString();
342  }
343  }
344  }
345  else if(type == Receive)
346  {
347  // Generate a new address to associate with given label
349  if(!ctx.isValid())
350  {
351  // Unlock wallet failed or was cancelled
353  return QString();
354  }
355  CPubKey newKey;
356  if(!wallet->GetKeyFromPool(newKey, true))
357  {
359  return QString();
360  }
361  strAddress = CBitcoinAddress(newKey.GetID()).ToString();
362  }
363  else
364  {
365  return QString();
366  }
367 
368  // Add entry
369  {
371  wallet->SetAddressBookName(CBitcoinAddress(strAddress).Get(), strLabel);
372  }
373  return QString::fromStdString(strAddress);
374 }
375 
376 bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
377 {
378  Q_UNUSED(parent);
379  AddressTableEntry *rec = priv->index(row);
380  if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
381  {
382  // Can only remove one row at a time, and cannot remove rows not in model.
383  // Also refuse to remove receiving addresses.
384  return false;
385  }
386  {
388  wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
389  }
390  return true;
391 }
392 
393 /* Look up label for address in address book, if not found return empty string.
394  */
395 QString AddressTableModel::labelForAddress(const QString &address) const
396 {
397  {
399  CBitcoinAddress address_parsed(address.toStdString());
400  std::map<CTxDestination, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
401  if (mi != wallet->mapAddressBook.end())
402  {
403  return QString::fromStdString(mi->second);
404  }
405  }
406  return QString();
407 }
408 
409 int AddressTableModel::lookupAddress(const QString &address) const
410 {
411  QModelIndexList lst = match(index(0, Address, QModelIndex()),
412  Qt::EditRole, address, 1, Qt::MatchExactly);
413  if(lst.isEmpty())
414  {
415  return -1;
416  }
417  else
418  {
419  return lst.at(0).row();
420  }
421 }
422 
424 {
425  emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
426 }
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
std::string * value
Definition: version_set.cc:270
bool operator()(const AddressTableEntry &a, const QString &b) const
WalletModel * walletModel
#define PAIRTYPE(t1, t2)
Definition: util.h:78
CCriticalSection cs_wallet
Definition: wallet.h:83
int lookupAddress(const QString &address) const
UnlockContext requestUnlock()
AddressTableEntry * index(int idx)
Address already in address book.
Type
Type type
void updateEntry(const QString &address, const QString &label, bool isMine, int status)
CTxDestination Get() const
Definition: base58.h:345
static const QString Send
Specifies send address.
bool SetAddressBookName(const CTxDestination &address, const std::string &strName)
Definition: wallet.cpp:1463
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:108
An encapsulated public key.
Definition: key.h:40
int rowCount(const QModelIndex &parent) const
AddressTableModel * parent
bool DelAddressBookName(const CTxDestination &address)
Definition: wallet.cpp:1473
std::string ToString() const
Definition: base58.h:229
int OutputDebugStringF(const char *pszFormat,...)
Definition: util.cpp:237
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
AddressTablePriv * priv
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:36
void updateEntry(const QString &address, const QString &label, bool isMine, 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:1378
void emitDataChanged(int index)
Notify listeners that data changed.
bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true)
Definition: wallet.cpp:1646
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
Definition: wallet.h:69
std::map< CTxDestination, std::string > mapAddressBook
Definition: wallet.h:119
bool setData(const QModelIndex &index, const QVariant &value, int role)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: script.h:62
QString label
No changes were made during edit operation.
User specified label.
CKeyID GetID() const
Definition: key.h:129
friend class AddressTablePriv
Qt::ItemFlags flags(const QModelIndex &index) const
Type of address (Send or Receive)
QFont bitcoinAddressFont()
Definition: guiutil.cpp:61
int columnCount(const QModelIndex &parent) const
AddressTablePriv(CWallet *wallet, AddressTableModel *parent)