fix balance display, display number of transactions
[novacoin.git] / gui / src / transactiontablemodel.cpp
1 #include "transactiontablemodel.h"
2 #include "guiutil.h"
3 #include "transactionrecord.h"
4 #include "main.h"
5
6 #include <QLocale>
7 #include <QDebug>
8 #include <QList>
9 #include <QColor>
10
11 const QString TransactionTableModel::Sent = "s";
12 const QString TransactionTableModel::Received = "r";
13 const QString TransactionTableModel::Other = "o";
14
15 /* Internal implementation */
16 class TransactionTableImpl
17 {
18 public:
19     QList<TransactionRecord> cachedWallet;
20
21     /* Update our model of the wallet */
22     void updateWallet()
23     {
24         QList<int> insertedIndices;
25         QList<int> removedIndices;
26
27         cachedWallet.clear();
28
29         /* Query wallet from core, and compare with our own
30            representation.
31          */
32         CRITICAL_BLOCK(cs_mapWallet)
33         {
34             for(std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
35             {
36                 /* TODO: Make note of new and removed transactions */
37                 /* insertedIndices */
38                 /* removedIndices */
39                 cachedWallet.append(TransactionRecord::decomposeTransaction(it->second));
40             }
41         }
42         /* beginInsertRows(QModelIndex(), first, last) */
43         /* endInsertRows */
44         /* beginRemoveRows(QModelIndex(), first, last) */
45         /* beginEndRows */
46     }
47
48     int size()
49     {
50         return cachedWallet.size();
51     }
52
53     TransactionRecord *index(int idx)
54     {
55         if(idx >= 0 && idx < cachedWallet.size())
56         {
57             return &cachedWallet[idx];
58         } else {
59             return 0;
60         }
61     }
62 };
63
64 /* Credit and Debit columns are right-aligned as they contain numbers */
65 static int column_alignments[] = {
66         Qt::AlignLeft|Qt::AlignVCenter,
67         Qt::AlignLeft|Qt::AlignVCenter,
68         Qt::AlignLeft|Qt::AlignVCenter,
69         Qt::AlignRight|Qt::AlignVCenter,
70         Qt::AlignRight|Qt::AlignVCenter,
71         Qt::AlignLeft|Qt::AlignVCenter
72     };
73
74 TransactionTableModel::TransactionTableModel(QObject *parent):
75         QAbstractTableModel(parent),
76         impl(new TransactionTableImpl())
77 {
78     columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit");
79
80     impl->updateWallet();
81 }
82
83 TransactionTableModel::~TransactionTableModel()
84 {
85     delete impl;
86 }
87
88 void TransactionTableModel::updateWallet()
89 {
90     beginResetModel();
91     impl->updateWallet();
92     endResetModel();
93 }
94
95 int TransactionTableModel::rowCount(const QModelIndex &parent) const
96 {
97     Q_UNUSED(parent);
98     return impl->size();
99 }
100
101 int TransactionTableModel::columnCount(const QModelIndex &parent) const
102 {
103     Q_UNUSED(parent);
104     return columns.length();
105 }
106
107 QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const
108 {
109     QString status;
110
111     switch(wtx->status.status)
112     {
113     case TransactionStatus::OpenUntilBlock:
114         status = tr("Open for %n block(s)","",wtx->status.open_for);
115         break;
116     case TransactionStatus::OpenUntilDate:
117         status = tr("Open until ") + DateTimeStr(wtx->status.open_for);
118         break;
119     case TransactionStatus::Offline:
120         status = tr("%1/offline").arg(wtx->status.depth);
121         break;
122     case TransactionStatus::Unconfirmed:
123         status = tr("%1/unconfirmed").arg(wtx->status.depth);
124         break;
125     case TransactionStatus::HaveConfirmations:
126         status = tr("%1 confirmations").arg(wtx->status.depth);
127         break;
128     }
129
130     return QVariant(status);
131 }
132
133 QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
134 {
135     if(wtx->time)
136     {
137         return QVariant(DateTimeStr(wtx->time));
138     } else {
139         return QVariant();
140     }
141 }
142
143 /* Look up address in address book, if found return
144      address[0:12]... (label)
145    otherwise just return address
146  */
147 std::string lookupAddress(const std::string &address)
148 {
149     std::string description;
150     CRITICAL_BLOCK(cs_mapAddressBook)
151     {
152         std::map<std::string, std::string>::iterator mi = mapAddressBook.find(address);
153         if (mi != mapAddressBook.end() && !(*mi).second.empty())
154         {
155             std::string label = (*mi).second;
156             description += address.substr(0,12) + "... ";
157             description += "(" + label + ")";
158         }
159         else
160             description += address;
161     }
162     return description;
163 }
164
165 QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const
166 {
167     QString description;
168
169     switch(wtx->type)
170     {
171     case TransactionRecord::RecvFromAddress:
172         description = tr("From: ") + QString::fromStdString(lookupAddress(wtx->address));
173         break;
174     case TransactionRecord::RecvFromIP:
175         description = tr("From IP: ") + QString::fromStdString(wtx->address);
176         break;
177     case TransactionRecord::SendToAddress:
178         description = tr("To: ") + QString::fromStdString(lookupAddress(wtx->address));
179         break;
180     case TransactionRecord::SendToIP:
181         description = tr("To IP: ") + QString::fromStdString(wtx->address);
182         break;
183     case TransactionRecord::SendToSelf:
184         description = tr("Payment to yourself");
185         break;
186     case TransactionRecord::Generated:
187         /* TODO: more extensive description */
188         description = tr("Generated");
189         break;
190     }
191     return QVariant(description);
192 }
193
194 QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const
195 {
196     if(wtx->debit)
197     {
198         QString str = QString::fromStdString(FormatMoney(wtx->debit));
199         if(!wtx->status.confirmed)
200         {
201             str = QString("[") + str + QString("]");
202         }
203         return QVariant(str);
204     } else {
205         return QVariant();
206     }
207 }
208
209 QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const
210 {
211     if(wtx->credit)
212     {
213         QString str = QString::fromStdString(FormatMoney(wtx->credit));
214         if(!wtx->status.confirmed)
215         {
216             str = QString("[") + str + QString("]");
217         }
218         return QVariant(str);
219     } else {
220         return QVariant();
221     }
222 }
223
224 QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
225 {
226     if(!index.isValid())
227         return QVariant();
228     TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
229
230     if(role == Qt::DisplayRole)
231     {
232         switch(index.column())
233         {
234         case Status:
235             return formatTxStatus(rec);
236         case Date:
237             return formatTxDate(rec);
238         case Description:
239             return formatTxDescription(rec);
240         case Debit:
241             return formatTxDebit(rec);
242         case Credit:
243             return formatTxCredit(rec);
244         }
245     } else if (role == Qt::TextAlignmentRole)
246     {
247         return column_alignments[index.column()];
248     } else if (role == Qt::ForegroundRole)
249     {
250         if(rec->status.confirmed)
251         {
252             return QColor(0, 0, 0);
253         } else {
254             return QColor(128, 128, 128);
255         }
256     } else if (role == TypeRole)
257     {
258         switch(rec->type)
259         {
260         case TransactionRecord::RecvFromAddress:
261         case TransactionRecord::RecvFromIP:
262         case TransactionRecord::Generated:
263             return TransactionTableModel::Received;
264         case TransactionRecord::SendToAddress:
265         case TransactionRecord::SendToIP:
266         case TransactionRecord::SendToSelf:
267             return TransactionTableModel::Sent;
268         default:
269             return TransactionTableModel::Other;
270         }
271     }
272     return QVariant();
273 }
274
275 QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const
276 {
277     if(role == Qt::DisplayRole)
278     {
279         if(orientation == Qt::Horizontal)
280         {
281             return columns[section];
282         }
283     } else if (role == Qt::TextAlignmentRole)
284     {
285         return column_alignments[section];
286     }
287     return QVariant();
288 }
289
290 Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const
291 {
292     return QAbstractTableModel::flags(index);
293 }
294
295
296 QModelIndex TransactionTableModel::index ( int row, int column, const QModelIndex & parent ) const
297 {
298     Q_UNUSED(parent);
299     TransactionRecord *data = impl->index(row);
300     if(data)
301     {
302         return createIndex(row, column, impl->index(row));
303     } else {
304         return QModelIndex();
305     }
306 }
307