Wallet encryption part 2: ask passphrase when needed, add menu options
[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 struct AddressTablePriv
31 {
32     CWallet *wallet;
33     QList<AddressTableEntry> cachedAddressTable;
34
35     AddressTablePriv(CWallet *wallet):
36             wallet(wallet) {}
37
38     void refreshAddressTable()
39     {
40         cachedAddressTable.clear();
41
42         CRITICAL_BLOCK(wallet->cs_KeyStore)
43         CRITICAL_BLOCK(wallet->cs_mapAddressBook)
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_mapAddressBook)
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_mapAddressBook)
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
278         strAddress = CBitcoinAddress(wallet->GetOrReuseKeyFromPool()).ToString();
279     }
280     else
281     {
282         return QString();
283     }
284     // Add entry and update list
285     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
286         wallet->SetAddressBookName(strAddress, strLabel);
287     updateList();
288     return QString::fromStdString(strAddress);
289 }
290
291 bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
292 {
293     Q_UNUSED(parent);
294     AddressTableEntry *rec = priv->index(row);
295     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
296     {
297         // Can only remove one row at a time, and cannot remove rows not in model.
298         // Also refuse to remove receiving addresses.
299         return false;
300     }
301     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
302     {
303         wallet->DelAddressBookName(rec->address.toStdString());
304     }
305     updateList();
306     return true;
307 }
308
309 void AddressTableModel::update()
310 {
311
312 }
313
314 /* Look up label for address in address book, if not found return empty string.
315  */
316 QString AddressTableModel::labelForAddress(const QString &address) const
317 {
318     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
319     {
320         CBitcoinAddress address_parsed(address.toStdString());
321         std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed);
322         if (mi != wallet->mapAddressBook.end())
323         {
324             return QString::fromStdString(mi->second);
325         }
326     }
327     return QString();
328 }
329
330 int AddressTableModel::lookupAddress(const QString &address) const
331 {
332     QModelIndexList lst = match(index(0, Address, QModelIndex()),
333                                 Qt::EditRole, address, 1, Qt::MatchExactly);
334     if(lst.isEmpty())
335     {
336         return -1;
337     }
338     else
339     {
340         return lst.at(0).row();
341     }
342 }
343