Address book: show unlabeled addresses as (no label)
[novacoin.git] / src / qt / addresstablemodel.cpp
1 #include "addresstablemodel.h"
2 #include "guiutil.h"
3
4 #include "headers.h"
5
6 #include <QFont>
7 #include <QColor>
8
9 const QString AddressTableModel::Send = "S";
10 const QString AddressTableModel::Receive = "R";
11
12 struct AddressTableEntry
13 {
14     enum Type {
15         Sending,
16         Receiving
17     };
18
19     Type type;
20     QString label;
21     QString address;
22
23     AddressTableEntry() {}
24     AddressTableEntry(Type type, const QString &label, const QString &address):
25         type(type), label(label), address(address) {}
26 };
27
28 // Private implementation
29 struct AddressTablePriv
30 {
31     CWallet *wallet;
32     QList<AddressTableEntry> cachedAddressTable;
33
34     AddressTablePriv(CWallet *wallet):
35             wallet(wallet) {}
36
37     void refreshAddressTable()
38     {
39         cachedAddressTable.clear();
40
41         CRITICAL_BLOCK(wallet->cs_mapKeys)
42         CRITICAL_BLOCK(wallet->cs_mapAddressBook)
43         {
44             BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, wallet->mapAddressBook)
45             {
46                 std::string strAddress = item.first;
47                 std::string strName = item.second;
48                 uint160 hash160;
49                 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
50                 cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
51                                   QString::fromStdString(strName),
52                                   QString::fromStdString(strAddress)));
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     bool isDefaultAddress(const AddressTableEntry *rec)
75     {
76         return rec->address == QString::fromStdString(wallet->GetDefaultAddress());
77     }
78 };
79
80 AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) :
81     QAbstractTableModel(parent),wallet(wallet),priv(0)
82 {
83     columns << tr("Label") << tr("Address");
84     priv = new AddressTablePriv(wallet);
85     priv->refreshAddressTable();
86 }
87
88 AddressTableModel::~AddressTableModel()
89 {
90     delete priv;
91 }
92
93 int AddressTableModel::rowCount(const QModelIndex &parent) const
94 {
95     Q_UNUSED(parent);
96     return priv->size();
97 }
98
99 int AddressTableModel::columnCount(const QModelIndex &parent) const
100 {
101     Q_UNUSED(parent);
102     return columns.length();
103 }
104
105 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
106 {
107     if(!index.isValid())
108         return QVariant();
109
110     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
111
112     if(role == Qt::DisplayRole || role == Qt::EditRole)
113     {
114         switch(index.column())
115         {
116         case Label:
117             if(rec->label.isEmpty())
118             {
119                 return tr("(no label)");
120             }
121             else
122             {
123                 return rec->label;
124             }
125         case Address:
126             return rec->address;
127         case IsDefaultAddress:
128             return priv->isDefaultAddress(rec);
129         }
130     }
131     else if (role == Qt::FontRole)
132     {
133         QFont font;
134         if(index.column() == Address)
135         {
136             font = GUIUtil::bitcoinAddressFont();
137         }
138         if(priv->isDefaultAddress(rec))
139         {
140             font.setBold(true);
141         }
142         return font;
143     }
144     else if (role == Qt::BackgroundRole)
145     {
146         // Show default address in alternative color
147         if(priv->isDefaultAddress(rec))
148         {
149             return QColor(255,255,128);
150         }
151     }
152     else if (role == Qt::ToolTipRole)
153     {
154         if(priv->isDefaultAddress(rec))
155         {
156             return tr("Default receiving address");
157         }
158     }
159     else if (role == TypeRole)
160     {
161         switch(rec->type)
162         {
163         case AddressTableEntry::Sending:
164             return Send;
165         case AddressTableEntry::Receiving:
166             return Receive;
167         default: break;
168         }
169     }
170     return QVariant();
171 }
172
173 bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
174 {
175     if(!index.isValid())
176         return false;
177     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
178
179     if(role == Qt::EditRole)
180     {
181         switch(index.column())
182         {
183         case Label:
184             wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
185             rec->label = value.toString();
186             break;
187         case Address:
188             // Double-check that we're not overwriting receiving address
189             if(rec->type == AddressTableEntry::Sending)
190             {
191                 // Remove old entry
192                 wallet->EraseAddressBookName(rec->address.toStdString());
193                 // Add new entry with new address
194                 wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
195
196                 rec->address = value.toString();
197             }
198             break;
199         case IsDefaultAddress:
200             if(value.toBool())
201             {
202                 setDefaultAddress(rec->address);
203             }
204             break;
205         }
206         emit dataChanged(index, index);
207
208         return true;
209     }
210     return false;
211 }
212
213 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
214 {
215     if(orientation == Qt::Horizontal)
216     {
217         if(role == Qt::DisplayRole)
218         {
219             return columns[section];
220         }
221     }
222     return QVariant();
223 }
224
225 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const
226 {
227     Q_UNUSED(parent);
228     AddressTableEntry *data = priv->index(row);
229     if(data)
230     {
231         return createIndex(row, column, priv->index(row));
232     }
233     else
234     {
235         return QModelIndex();
236     }
237 }
238
239 void AddressTableModel::updateList()
240 {
241     // Update internal model from Bitcoin core
242     beginResetModel();
243     priv->refreshAddressTable();
244     endResetModel();
245 }
246
247 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault)
248 {
249     std::string strLabel = label.toStdString();
250     std::string strAddress = address.toStdString();
251
252     if(type == Send)
253     {
254         // Check for duplicate
255         CRITICAL_BLOCK(wallet->cs_mapAddressBook)
256         {
257             if(wallet->mapAddressBook.count(strAddress))
258             {
259                 return QString();
260             }
261         }
262     }
263     else if(type == Receive)
264     {
265         // Generate a new address to associate with given label, optionally
266         // set as default receiving address.
267         strAddress = PubKeyToAddress(wallet->GetKeyFromKeyPool());
268         if(setAsDefault)
269         {
270             setDefaultAddress(QString::fromStdString(strAddress));
271         }
272     }
273     else
274     {
275         return QString();
276     }
277     // Add entry and update list
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     wallet->EraseAddressBookName(rec->address.toStdString());
294     updateList();
295     return true;
296 }
297
298 QString AddressTableModel::getDefaultAddress() const
299 {
300     return QString::fromStdString(wallet->GetDefaultAddress());
301 }
302
303 void AddressTableModel::setDefaultAddress(const QString &defaultAddress)
304 {
305     wallet->SetDefaultAddress(defaultAddress.toStdString());
306 }
307
308 void AddressTableModel::update()
309 {
310     emit defaultAddressChanged(getDefaultAddress());
311 }