Somewhat confident now, tested on GNOME+KDE, with all types of transactions. Next...
[novacoin.git] / gui / src / addresstablemodel.cpp
1 #include "addresstablemodel.h"
2 #include "guiutil.h"
3 #include "main.h"
4
5 #include <QFont>
6
7 const QString AddressTableModel::Send = "S";
8 const QString AddressTableModel::Receive = "R";
9
10 struct AddressTableEntry
11 {
12     enum Type {
13         Sending,
14         Receiving
15     };
16
17     Type type;
18     QString label;
19     QString address;
20
21     AddressTableEntry() {}
22     AddressTableEntry(Type type, const QString &label, const QString &address):
23         type(type), label(label), address(address) {}
24 };
25
26 /* Private implementation */
27 struct AddressTablePriv
28 {
29     QList<AddressTableEntry> cachedAddressTable;
30
31     void refreshAddressTable()
32     {
33         cachedAddressTable.clear();
34
35         CRITICAL_BLOCK(cs_mapKeys)
36         CRITICAL_BLOCK(cs_mapAddressBook)
37         {
38             BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, mapAddressBook)
39             {
40                 std::string strAddress = item.first;
41                 std::string strName = item.second;
42                 uint160 hash160;
43                 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
44                 cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
45                                   QString::fromStdString(strName),
46                                   QString::fromStdString(strAddress)));
47             }
48         }
49     }
50
51     int size()
52     {
53         return cachedAddressTable.size();
54     }
55
56     AddressTableEntry *index(int idx)
57     {
58         if(idx >= 0 && idx < cachedAddressTable.size())
59         {
60             return &cachedAddressTable[idx];
61         }
62         else
63         {
64             return 0;
65         }
66     }
67 };
68
69 AddressTableModel::AddressTableModel(QObject *parent) :
70     QAbstractTableModel(parent),priv(0)
71 {
72     columns << tr("Label") << tr("Address");
73     priv = new AddressTablePriv();
74     priv->refreshAddressTable();
75 }
76
77 AddressTableModel::~AddressTableModel()
78 {
79     delete priv;
80 }
81
82 int AddressTableModel::rowCount(const QModelIndex &parent) const
83 {
84     Q_UNUSED(parent);
85     return priv->size();
86 }
87
88 int AddressTableModel::columnCount(const QModelIndex &parent) const
89 {
90     Q_UNUSED(parent);
91     return columns.length();
92 }
93
94 QVariant AddressTableModel::data(const QModelIndex &index, int role) const
95 {
96     if(!index.isValid())
97         return QVariant();
98
99     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
100
101     if(role == Qt::DisplayRole || role == Qt::EditRole)
102     {
103         switch(index.column())
104         {
105         case Label:
106             return rec->label;
107         case Address:
108             return rec->address;
109         }
110     }
111     else if (role == Qt::FontRole)
112     {
113         if(index.column() == Address)
114         {
115             return GUIUtil::bitcoinAddressFont();
116         }
117     }
118     else if (role == TypeRole)
119     {
120         switch(rec->type)
121         {
122         case AddressTableEntry::Sending:
123             return Send;
124         case AddressTableEntry::Receiving:
125             return Receive;
126         default: break;
127         }
128     }
129     return QVariant();
130 }
131
132 bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
133 {
134     if(!index.isValid())
135         return false;
136     AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
137
138     if(role == Qt::EditRole)
139     {
140         switch(index.column())
141         {
142         case Label:
143             SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
144             rec->label = value.toString();
145             break;
146         case Address:
147             /* Double-check that we're not overwriting receiving address */
148             if(rec->type == AddressTableEntry::Sending)
149             {
150                 /* Remove old entry */
151                 CWalletDB().EraseName(rec->address.toStdString());
152                 /* Add new entry with new address */
153                 SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
154
155                 rec->address = value.toString();
156             }
157             break;
158         }
159         emit dataChanged(index, index);
160
161         return true;
162     }
163     return false;
164 }
165
166 QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
167 {
168     if(orientation == Qt::Horizontal)
169     {
170         if(role == Qt::DisplayRole)
171         {
172             return columns[section];
173         }
174     }
175     return QVariant();
176 }
177
178 QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const
179 {
180     Q_UNUSED(parent);
181     AddressTableEntry *data = priv->index(row);
182     if(data)
183     {
184         return createIndex(row, column, priv->index(row));
185     }
186     else
187     {
188         return QModelIndex();
189     }
190 }
191
192 void AddressTableModel::updateList()
193 {
194     /* Update internal model from Bitcoin core */
195     beginResetModel();
196     priv->refreshAddressTable();
197     endResetModel();
198 }
199
200 QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
201 {
202     std::string strLabel = label.toStdString();
203     std::string strAddress = address.toStdString();
204
205     if(type == Send)
206     {
207         /* Check for duplicate */
208         CRITICAL_BLOCK(cs_mapAddressBook)
209         {
210             if(mapAddressBook.count(strAddress))
211             {
212                 return QString();
213             }
214         }
215     }
216     else if(type == Receive)
217     {
218         /* Generate a new address to associate with given label */
219         strAddress = PubKeyToAddress(GetKeyFromKeyPool());
220     }
221     else
222     {
223         return QString();
224     }
225     /* Add entry and update list */
226     SetAddressBookName(strAddress, strLabel);
227     updateList();
228     return QString::fromStdString(strAddress);
229 }
230
231 bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
232 {
233     Q_UNUSED(parent);
234     AddressTableEntry *rec = priv->index(row);
235     if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
236     {
237         /* Can only remove one row at a time, and cannot remove rows not in model.
238            Also refuse to remove receiving addresses.
239          */
240         return false;
241     }
242     CWalletDB().EraseName(rec->address.toStdString());
243     updateList();
244     return true;
245 }