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