Merge branch '0.4.x' into 0.5.x
[novacoin.git] / src / qt / addresstablemodel.cpp
1 #include "addresstablemodel.h"
2 #include "guiutil.h"
3 #include "walletmodel.h"
4
5 #include "headers.h"
6
7 #include <QFont>
8 #include <QColor>
9
10 const QString AddressTableModel::Send = "S";
11 const QString AddressTableModel::Receive = "R";
12
13 struct AddressTableEntry
14 {
15     enum Type {
16         Sending,
17         Receiving
18     };
19
20     Type type;
21     QString label;
22     QString address;
23
24     AddressTableEntry() {}
25     AddressTableEntry(Type type, const QString &label, const QString &address):
26         type(type), label(label), address(address) {}
27 };
28
29 // Private implementation
30 class AddressTablePriv
31 {
32 public:
33     CWallet *wallet;
34     QList<AddressTableEntry> cachedAddressTable;
35
36     AddressTablePriv(CWallet *wallet):
37             wallet(wallet) {}
38
39     void refreshAddressTable()
40     {
41         cachedAddressTable.clear();
42
43         CRITICAL_BLOCK(wallet->cs_wallet)
44         {
45             BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook)
46             {
47                 const CBitcoinAddress& address = item.first;
48                 const std::string& strName = item.second;
49                 bool fMine = wallet->HaveKey(address);
50                 cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
51                                   QString::fromStdString(strName),
52                                   QString::fromStdString(address.ToString())));
53             }
54         }
55     }
56
57     int size()
58     {
59         return cachedAddressTable.size();
60     }
61
62     AddressTableEntry *index(int idx)
63     {
64         if(idx >= 0 && idx < cachedAddressTable.size())
65         {
66             return &cachedAddressTable[idx];
67         }
68         else
69         {
70             return 0;
71         }
72     }
73 };
74
75 AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
76     QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
77 {
78     columns << tr("Label") << tr("Address");
79     priv = new AddressTablePriv(wallet);
80     priv->refreshAddressTable();
81 }
82
83 AddressTableModel::~AddressTableModel()
84 {
85     delete priv;
86 }
87
88 int AddressTableModel::rowCount(const QModelIndex &parent) const
89 {
90     Q_UNUSED(parent);
91     return priv->size();
92 }
93
94 int AddressTableModel::columnCount(const QModelIndex &parent) const
95 {
96     Q_UNUSED(parent);
97     return columns.length();
98 }
99
100 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
101 {
102     if(!index.isValid())
103         return QVariant();
104
105     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
106
107     if(role == Qt::DisplayRole || role == Qt::EditRole)
108     {
109         switch(index.column())
110         {
111         case Label:
112             if(rec->label.isEmpty() && role == Qt::DisplayRole)
113             {
114                 return tr("(no label)");
115             }
116             else
117             {
118                 return rec->label;
119             }
120         case Address:
121             return rec->address;
122         }
123     }
124     else if (role == Qt::FontRole)
125     {
126         QFont font;
127         if(index.column() == Address)
128         {
129             font = GUIUtil::bitcoinAddressFont();
130         }
131         return font;
132     }
133     else if (role == TypeRole)
134     {
135         switch(rec->type)
136         {
137         case AddressTableEntry::Sending:
138             return Send;
139         case AddressTableEntry::Receiving:
140             return Receive;
141         default: break;
142         }
143     }
144     return QVariant();
145 }
146
147 bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
148 {
149     if(!index.isValid())
150         return false;
151     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
152
153     editStatus = OK;
154
155     if(role == Qt::EditRole)
156     {
157         switch(index.column())
158         {
159         case Label:
160             wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
161             rec->label = value.toString();
162             break;
163         case Address:
164             // Refuse to set invalid address, set error status and return false
165             if(!walletModel->validateAddress(value.toString()))
166             {
167                 editStatus = INVALID_ADDRESS;
168                 return false;
169             }
170             // Double-check that we're not overwriting a receiving address
171             if(rec->type == AddressTableEntry::Sending)
172             {
173                 CRITICAL_BLOCK(wallet->cs_wallet)
174                 {
175                     // Remove old entry
176                     wallet->DelAddressBookName(rec->address.toStdString());
177                     // Add new entry with new address
178                     wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
179                 }
180
181                 rec->address = value.toString();
182             }
183             break;
184         }
185         emit dataChanged(index, index);
186
187         return true;
188     }
189     return false;
190 }
191
192 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
193 {
194     if(orientation == Qt::Horizontal)
195     {
196         if(role == Qt::DisplayRole)
197         {
198             return columns[section];
199         }
200     }
201     return QVariant();
202 }
203
204 Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const
205 {
206     if(!index.isValid())
207         return 0;
208     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
209
210     Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
211     // Can edit address and label for sending addresses,
212     // and only label for receiving addresses.
213     if(rec->type == AddressTableEntry::Sending ||
214       (rec->type == AddressTableEntry::Receiving && index.column()==Label))
215     {
216         retval |= Qt::ItemIsEditable;
217     }
218     return retval;
219 }
220
221 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const
222 {
223     Q_UNUSED(parent);
224     AddressTableEntry *data = priv->index(row);
225     if(data)
226     {
227         return createIndex(row, column, priv->index(row));
228     }
229     else
230     {
231         return QModelIndex();
232     }
233 }
234
235 void AddressTableModel::updateList()
236 {
237     // Update address book model from Bitcoin core
238     beginResetModel();
239     priv->refreshAddressTable();
240     endResetModel();
241 }
242
243 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
244 {
245     std::string strLabel = label.toStdString();
246     std::string strAddress = address.toStdString();
247
248     editStatus = OK;
249
250     if(type == Send)
251     {
252         if(!walletModel->validateAddress(address))
253         {
254             editStatus = INVALID_ADDRESS;
255             return QString();
256         }
257         // Check for duplicate addresses
258         CRITICAL_BLOCK(wallet->cs_wallet)
259         {
260             if(wallet->mapAddressBook.count(strAddress))
261             {
262                 editStatus = DUPLICATE_ADDRESS;
263                 return QString();
264             }
265         }
266     }
267     else if(type == Receive)
268     {
269         // Generate a new address to associate with given label
270         WalletModel::UnlockContext ctx(walletModel->requestUnlock());
271         if(!ctx.isValid())
272         {
273             // Unlock wallet failed or was cancelled
274             editStatus = WALLET_UNLOCK_FAILURE;
275             return QString();
276         }
277         std::vector<unsigned char> newKey;
278         if(!wallet->GetKeyFromPool(newKey, true))
279         {
280             editStatus = KEY_GENERATION_FAILURE;
281             return QString();
282         }
283         strAddress = CBitcoinAddress(newKey).ToString();
284     }
285     else
286     {
287         return QString();
288     }
289     // Add entry and update list
290     CRITICAL_BLOCK(wallet->cs_wallet)
291         wallet->SetAddressBookName(strAddress, strLabel);
292     updateList();
293     return QString::fromStdString(strAddress);
294 }
295
296 bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
297 {
298     Q_UNUSED(parent);
299     AddressTableEntry *rec = priv->index(row);
300     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
301     {
302         // Can only remove one row at a time, and cannot remove rows not in model.
303         // Also refuse to remove receiving addresses.
304         return false;
305     }
306     CRITICAL_BLOCK(wallet->cs_wallet)
307     {
308         wallet->DelAddressBookName(rec->address.toStdString());
309     }
310     updateList();
311     return true;
312 }
313
314 void AddressTableModel::update()
315 {
316
317 }
318
319 /* Look up label for address in address book, if not found return empty string.
320  */
321 QString AddressTableModel::labelForAddress(const QString &address) const
322 {
323     CRITICAL_BLOCK(wallet->cs_wallet)
324     {
325         CBitcoinAddress address_parsed(address.toStdString());
326         std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed);
327         if (mi != wallet->mapAddressBook.end())
328         {
329             return QString::fromStdString(mi->second);
330         }
331     }
332     return QString();
333 }
334
335 int AddressTableModel::lookupAddress(const QString &address) const
336 {
337     QModelIndexList lst = match(index(0, Address, QModelIndex()),
338                                 Qt::EditRole, address, 1, Qt::MatchExactly);
339     if(lst.isEmpty())
340     {
341         return -1;
342     }
343     else
344     {
345         return lst.at(0).row();
346     }
347 }
348