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