]> Git Repo - VerusCoin.git/blob - src/qt/addresstablemodel.cpp
Merge pull request #2539 from gavinandresen/paymentrequest
[VerusCoin.git] / src / qt / addresstablemodel.cpp
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
14 struct AddressTableEntry
15 {
16     enum Type {
17         Sending,
18         Receiving
19     };
20
21     Type type;
22     QString label;
23     QString address;
24
25     AddressTableEntry() {}
26     AddressTableEntry(Type type, const QString &label, const QString &address):
27         type(type), label(label), address(address) {}
28 };
29
30 struct AddressTableEntryLessThan
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
47 class AddressTablePriv
48 {
49 public:
50     CWallet *wallet;
51     QList<AddressTableEntry> cachedAddressTable;
52     AddressTableModel *parent;
53
54     AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
55         wallet(wallet), parent(parent) {}
56
57     void refreshAddressTable()
58     {
59         cachedAddressTable.clear();
60         {
61             LOCK(wallet->cs_wallet);
62             BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
63             {
64                 const CBitcoinAddress& address = item.first;
65
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);
73                 }
74                 else continue; // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
75
76                 const std::string& strName = item.second.name;
77                 cachedAddressTable.append(AddressTableEntry(addressType,
78                                   QString::fromStdString(strName),
79                                   QString::fromStdString(address.ToString())));
80             }
81         }
82         // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
83         qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
84     }
85
86     void updateEntry(const QString &address, const QString &label, bool isMine, int status)
87     {
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;
97
98         switch(status)
99         {
100         case CT_NEW:
101             if(inModel)
102             {
103                 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
104                 break;
105             }
106             parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
107             cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
108             parent->endInsertRows();
109             break;
110         case CT_UPDATED:
111             if(!inModel)
112             {
113                 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
114                 break;
115             }
116             lower->type = newEntryType;
117             lower->label = label;
118             parent->emitDataChanged(lowerIndex);
119             break;
120         case CT_DELETED:
121             if(!inModel)
122             {
123                 OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
124                 break;
125             }
126             parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
127             cachedAddressTable.erase(lower, upper);
128             parent->endRemoveRows();
129             break;
130         }
131     }
132
133     int size()
134     {
135         return cachedAddressTable.size();
136     }
137
138     AddressTableEntry *index(int idx)
139     {
140         if(idx >= 0 && idx < cachedAddressTable.size())
141         {
142             return &cachedAddressTable[idx];
143         }
144         else
145         {
146             return 0;
147         }
148     }
149 };
150
151 AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
152     QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
153 {
154     columns << tr("Label") << tr("Address");
155     priv = new AddressTablePriv(wallet, this);
156     priv->refreshAddressTable();
157 }
158
159 AddressTableModel::~AddressTableModel()
160 {
161     delete priv;
162 }
163
164 int AddressTableModel::rowCount(const QModelIndex &parent) const
165 {
166     Q_UNUSED(parent);
167     return priv->size();
168 }
169
170 int AddressTableModel::columnCount(const QModelIndex &parent) const
171 {
172     Q_UNUSED(parent);
173     return columns.length();
174 }
175
176 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
177 {
178     if(!index.isValid())
179         return QVariant();
180
181     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
182
183     if(role == Qt::DisplayRole || role == Qt::EditRole)
184     {
185         switch(index.column())
186         {
187         case Label:
188             if(rec->label.isEmpty() && role == Qt::DisplayRole)
189             {
190                 return tr("(no label)");
191             }
192             else
193             {
194                 return rec->label;
195             }
196         case Address:
197             return rec->address;
198         }
199     }
200     else if (role == Qt::FontRole)
201     {
202         QFont font;
203         if(index.column() == Address)
204         {
205             font = GUIUtil::bitcoinAddressFont();
206         }
207         return font;
208     }
209     else if (role == TypeRole)
210     {
211         switch(rec->type)
212         {
213         case AddressTableEntry::Sending:
214             return Send;
215         case AddressTableEntry::Receiving:
216             return Receive;
217         default: break;
218         }
219     }
220     return QVariant();
221 }
222
223 bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
224 {
225     if(!index.isValid())
226         return false;
227     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
228     std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
229     editStatus = OK;
230
231     if(role == Qt::EditRole)
232     {
233         switch(index.column())
234         {
235         case Label:
236             // Do nothing, if old label == new label
237             if(rec->label == value.toString())
238             {
239                 editStatus = NO_CHANGES;
240                 return false;
241             }
242             wallet->SetAddressBook(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString(), strPurpose);
243             break;
244         case Address:
245             // Do nothing, if old address == new address
246             if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString()))
247             {
248                 editStatus = NO_CHANGES;
249                 return false;
250             }
251             // Refuse to set invalid address, set error status and return false
252             else if(!walletModel->validateAddress(value.toString()))
253             {
254                 editStatus = INVALID_ADDRESS;
255                 return false;
256             }
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()))
260             {
261                 editStatus = DUPLICATE_ADDRESS;
262                 return false;
263             }
264             // Double-check that we're not overwriting a receiving address
265             else if(rec->type == AddressTableEntry::Sending)
266             {
267                 {
268                     LOCK(wallet->cs_wallet);
269                     // Remove old entry
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);
273                 }
274             }
275             break;
276         }
277         return true;
278     }
279     return false;
280 }
281
282 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
283 {
284     if(orientation == Qt::Horizontal)
285     {
286         if(role == Qt::DisplayRole)
287         {
288             return columns[section];
289         }
290     }
291     return QVariant();
292 }
293
294 Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
295 {
296     if(!index.isValid())
297         return 0;
298     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
299
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))
305     {
306         retval |= Qt::ItemIsEditable;
307     }
308     return retval;
309 }
310
311 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
312 {
313     Q_UNUSED(parent);
314     AddressTableEntry *data = priv->index(row);
315     if(data)
316     {
317         return createIndex(row, column, priv->index(row));
318     }
319     else
320     {
321         return QModelIndex();
322     }
323 }
324
325 void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
326 {
327     // Update address book model from Bitcoin core
328     priv->updateEntry(address, label, isMine, status);
329 }
330
331 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
332 {
333     std::string strLabel = label.toStdString();
334     std::string strAddress = address.toStdString();
335
336     editStatus = OK;
337
338     if(type == Send)
339     {
340         if(!walletModel->validateAddress(address))
341         {
342             editStatus = INVALID_ADDRESS;
343             return QString();
344         }
345         // Check for duplicate addresses
346         {
347             LOCK(wallet->cs_wallet);
348             if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
349             {
350                 editStatus = DUPLICATE_ADDRESS;
351                 return QString();
352             }
353         }
354     }
355     else if(type == Receive)
356     {
357         // Generate a new address to associate with given label
358         WalletModel::UnlockContext ctx(walletModel->requestUnlock());
359         if(!ctx.isValid())
360         {
361             // Unlock wallet failed or was cancelled
362             editStatus = WALLET_UNLOCK_FAILURE;
363             return QString();
364         }
365         CPubKey newKey;
366         if(!wallet->GetKeyFromPool(newKey, true))
367         {
368             editStatus = KEY_GENERATION_FAILURE;
369             return QString();
370         }
371         strAddress = CBitcoinAddress(newKey.GetID()).ToString();
372     }
373     else
374     {
375         return QString();
376     }
377
378     // Add entry
379     {
380         LOCK(wallet->cs_wallet);
381         wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
382                                (type == Send ? "send" : "receive"));
383     }
384     return QString::fromStdString(strAddress);
385 }
386
387 bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
388 {
389     Q_UNUSED(parent);
390     AddressTableEntry *rec = priv->index(row);
391     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
392     {
393         // Can only remove one row at a time, and cannot remove rows not in model.
394         // Also refuse to remove receiving addresses.
395         return false;
396     }
397     {
398         LOCK(wallet->cs_wallet);
399         wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
400     }
401     return true;
402 }
403
404 /* Look up label for address in address book, if not found return empty string.
405  */
406 QString AddressTableModel::labelForAddress(const QString &address) const
407 {
408     {
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())
413         {
414             return QString::fromStdString(mi->second.name);
415         }
416     }
417     return QString();
418 }
419
420 int AddressTableModel::lookupAddress(const QString &address) const
421 {
422     QModelIndexList lst = match(index(0, Address, QModelIndex()),
423                                 Qt::EditRole, address, 1, Qt::MatchExactly);
424     if(lst.isEmpty())
425     {
426         return -1;
427     }
428     else
429     {
430         return lst.at(0).row();
431     }
432 }
433
434 void AddressTableModel::emitDataChanged(int idx)
435 {
436     emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
437 }
This page took 0.050004 seconds and 4 git commands to generate.