Add context menu on transaction list: copy label, copy address, edit label, show...
[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
75 AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) :
76     QAbstractTableModel(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     if(role == Qt::EditRole)
154     {
155         switch(index.column())
156         {
157         case Label:
158             wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
159             rec->label = value.toString();
160             break;
161         case Address:
162             // Refuse to set invalid address
163             if(!validateAddress(value.toString()))
164                 return false;
165             // Double-check that we're not overwriting receiving address
166             if(rec->type == AddressTableEntry::Sending)
167             {
168                 CRITICAL_BLOCK(wallet->cs_mapAddressBook)
169                 {
170                     // Remove old entry
171                     wallet->DelAddressBookName(rec->address.toStdString());
172                     // Add new entry with new address
173                     wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
174                 }
175
176                 rec->address = value.toString();
177             }
178             break;
179         }
180         emit dataChanged(index, index);
181
182         return true;
183     }
184     return false;
185 }
186
187 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
188 {
189     if(orientation == Qt::Horizontal)
190     {
191         if(role == Qt::DisplayRole)
192         {
193             return columns[section];
194         }
195     }
196     return QVariant();
197 }
198
199 Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const
200 {
201     if(!index.isValid())
202         return 0;
203     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
204
205     Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
206     // Can edit address and label for sending addresses,
207     // and only label for receiving addresses.
208     if(rec->type == AddressTableEntry::Sending ||
209       (rec->type == AddressTableEntry::Receiving && index.column()==Label))
210     {
211         retval |= Qt::ItemIsEditable;
212     }
213     return retval;
214 }
215
216 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const
217 {
218     Q_UNUSED(parent);
219     AddressTableEntry *data = priv->index(row);
220     if(data)
221     {
222         return createIndex(row, column, priv->index(row));
223     }
224     else
225     {
226         return QModelIndex();
227     }
228 }
229
230 void AddressTableModel::updateList()
231 {
232     // Update internal model from Bitcoin core
233     beginResetModel();
234     priv->refreshAddressTable();
235     endResetModel();
236 }
237
238 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
239 {
240     std::string strLabel = label.toStdString();
241     std::string strAddress = address.toStdString();
242
243     if(type == Send)
244     {
245         // Check for duplicate
246         CRITICAL_BLOCK(wallet->cs_mapAddressBook)
247         {
248             if(wallet->mapAddressBook.count(strAddress))
249             {
250                 return QString();
251             }
252         }
253     }
254     else if(type == Receive)
255     {
256         // Generate a new address to associate with given label, optionally
257         // set as default receiving address.
258         strAddress = PubKeyToAddress(wallet->GetKeyFromKeyPool());
259     }
260     else
261     {
262         return QString();
263     }
264     // Add entry and update list
265     wallet->SetAddressBookName(strAddress, strLabel);
266     updateList();
267     return QString::fromStdString(strAddress);
268 }
269
270 bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
271 {
272     Q_UNUSED(parent);
273     AddressTableEntry *rec = priv->index(row);
274     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
275     {
276         // Can only remove one row at a time, and cannot remove rows not in model.
277         // Also refuse to remove receiving addresses.
278         return false;
279     }
280     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
281     {
282         wallet->DelAddressBookName(rec->address.toStdString());
283     }
284     updateList();
285     return true;
286 }
287
288 void AddressTableModel::update()
289 {
290
291 }
292
293 bool AddressTableModel::validateAddress(const QString &address)
294 {
295     uint160 hash160 = 0;
296
297     return AddressToHash160(address.toStdString(), hash160);
298 }
299
300 /* Look up label for address in address book, if not found return empty string.
301  */
302 QString AddressTableModel::labelForAddress(const QString &address) const
303 {
304     CRITICAL_BLOCK(wallet->cs_mapAddressBook)
305     {
306         std::map<std::string, std::string>::iterator mi = wallet->mapAddressBook.find(address.toStdString());
307         if (mi != wallet->mapAddressBook.end())
308         {
309             return QString::fromStdString(mi->second);
310         }
311     }
312     return QString();
313 }
314
315 int AddressTableModel::lookupAddress(const QString &address) const
316 {
317     QModelIndexList lst = match(index(0, Address, QModelIndex()),
318                                 Qt::EditRole, address, 1, Qt::MatchExactly);
319     if(lst.isEmpty())
320     {
321         return -1;
322     }
323     else
324     {
325         return lst.at(0).row();
326     }
327 }
328