bd314ba0f064ab1663505a1ce106062f01f686e9
[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         strAddress = CBitcoinAddress(wallet->GetOrReuseKeyFromPool()).ToString();
271     }
272     else
273     {
274         return QString();
275     }
276     // Add entry and update list
277     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
278         wallet->SetAddressBookName(strAddress, strLabel);
279     updateList();
280     return QString::fromStdString(strAddress);
281 }
282
283 bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
284 {
285     Q_UNUSED(parent);
286     AddressTableEntry *rec = priv->index(row);
287     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
288     {
289         // Can only remove one row at a time, and cannot remove rows not in model.
290         // Also refuse to remove receiving addresses.
291         return false;
292     }
293     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
294     {
295         wallet->DelAddressBookName(rec->address.toStdString());
296     }
297     updateList();
298     return true;
299 }
300
301 void AddressTableModel::update()
302 {
303
304 }
305
306 /* Look up label for address in address book, if not found return empty string.
307  */
308 QString AddressTableModel::labelForAddress(const QString &address) const
309 {
310     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
311     {
312         CBitcoinAddress address_parsed(address.toStdString());
313         std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed);
314         if (mi != wallet->mapAddressBook.end())
315         {
316             return QString::fromStdString(mi->second);
317         }
318     }
319     return QString();
320 }
321
322 int AddressTableModel::lookupAddress(const QString &address) const
323 {
324     QModelIndexList lst = match(index(0, Address, QModelIndex()),
325                                 Qt::EditRole, address, 1, Qt::MatchExactly);
326     if(lst.isEmpty())
327     {
328         return -1;
329     }
330     else
331     {
332         return lst.at(0).row();
333     }
334 }
335