b2777b7b20fa3d82c50af545de658b7a3e7675cc
[novacoin.git] / src / qt / transactionview.cpp
1 #include "transactionview.h"
2
3 #include "transactionfilterproxy.h"
4 #include "transactionrecord.h"
5 #include "walletmodel.h"
6 #include "addresstablemodel.h"
7 #include "transactiontablemodel.h"
8 #include "bitcoinunits.h"
9 #include "csvmodelwriter.h"
10 #include "transactiondescdialog.h"
11 #include "editaddressdialog.h"
12 #include "optionsmodel.h"
13
14 #include <QScrollBar>
15 #include <QComboBox>
16 #include <QDoubleValidator>
17 #include <QHBoxLayout>
18 #include <QVBoxLayout>
19 #include <QLineEdit>
20 #include <QTableView>
21 #include <QHeaderView>
22 #include <QPushButton>
23 #include <QFileDialog>
24 #include <QMessageBox>
25 #include <QPoint>
26 #include <QMenu>
27 #include <QApplication>
28 #include <QClipboard>
29 #include <QLabel>
30 #include <QDateTimeEdit>
31
32 TransactionView::TransactionView(QWidget *parent) :
33     QWidget(parent), model(0), transactionProxyModel(0),
34     transactionView(0)
35 {
36     // Build filter row
37     setContentsMargins(0,0,0,0);
38
39     QHBoxLayout *hlayout = new QHBoxLayout();
40     hlayout->setContentsMargins(0,0,0,0);
41     hlayout->setSpacing(0);
42
43     hlayout->addSpacing(23);
44
45     dateWidget = new QComboBox(this);
46     dateWidget->setMaximumWidth(120);
47     dateWidget->setMinimumWidth(120);
48     dateWidget->addItem(tr("All"), All);
49     dateWidget->addItem(tr("Today"), Today);
50     dateWidget->addItem(tr("This week"), ThisWeek);
51     dateWidget->addItem(tr("This month"), ThisMonth);
52     dateWidget->addItem(tr("Last month"), LastMonth);
53     dateWidget->addItem(tr("This year"), ThisYear);
54     dateWidget->addItem(tr("Range..."), Range);
55     hlayout->addWidget(dateWidget);
56
57     typeWidget = new QComboBox(this);
58     typeWidget->setMaximumWidth(120);
59     typeWidget->setMinimumWidth(120);
60
61     typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
62     typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
63                                         TransactionFilterProxy::TYPE(TransactionRecord::RecvFromIP));
64     typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
65                                   TransactionFilterProxy::TYPE(TransactionRecord::SendToIP));
66     typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
67     typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
68     typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
69
70     hlayout->addWidget(typeWidget);
71
72     addressWidget = new QLineEdit(this);
73 #if QT_VERSION >= 0x040700
74     addressWidget->setPlaceholderText(tr("Enter address or label to search"));
75 #endif
76     hlayout->addWidget(addressWidget);
77
78     amountWidget = new QLineEdit(this);
79 #if QT_VERSION >= 0x040700
80     amountWidget->setPlaceholderText(tr("Min amount"));
81 #endif
82     amountWidget->setMaximumWidth(100);
83     amountWidget->setMinimumWidth(100);
84     amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
85     hlayout->addWidget(amountWidget);
86
87     QVBoxLayout *vlayout = new QVBoxLayout(this);
88     vlayout->setContentsMargins(0,0,0,0);
89     vlayout->setSpacing(0);
90     //vlayout->addLayout(hlayout2);
91
92     QTableView *view = new QTableView(this);
93     vlayout->addLayout(hlayout);
94     vlayout->addWidget(createDateRangeWidget());
95     vlayout->addWidget(view);
96     vlayout->setSpacing(0);
97     int width = view->verticalScrollBar()->sizeHint().width();
98     // Cover scroll bar width with spacing
99     hlayout->addSpacing(width);
100     // Always show scroll bar
101     view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
102     view->setTabKeyNavigation(false);
103     view->setContextMenuPolicy(Qt::CustomContextMenu);
104
105     transactionView = view;
106
107     // Actions
108     QAction *copyAddressAction = new QAction(tr("Copy address"), this);
109     QAction *copyLabelAction = new QAction(tr("Copy label"), this);
110     QAction *editLabelAction = new QAction(tr("Edit label"), this);
111     QAction *showDetailsAction = new QAction(tr("Show details..."), this);
112
113     contextMenu = new QMenu();
114     contextMenu->addAction(copyAddressAction);
115     contextMenu->addAction(copyLabelAction);
116     contextMenu->addAction(editLabelAction);
117     contextMenu->addAction(showDetailsAction);
118
119     // Connect actions
120     connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int)));
121     connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int)));
122     connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString)));
123     connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString)));
124
125     connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
126
127     connect(view,
128             SIGNAL(customContextMenuRequested(QPoint)),
129             this,
130             SLOT(contextualMenu(QPoint)));
131
132     connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
133     connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
134     connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel()));
135     connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
136 }
137
138 void TransactionView::setModel(WalletModel *model)
139 {
140     this->model = model;
141
142     transactionProxyModel = new TransactionFilterProxy(this);
143     transactionProxyModel->setSourceModel(model->getTransactionTableModel());
144     transactionProxyModel->setDynamicSortFilter(true);
145
146     transactionProxyModel->setSortRole(Qt::EditRole);
147
148     transactionView->setModel(transactionProxyModel);
149     transactionView->setAlternatingRowColors(true);
150     transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
151     transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
152     transactionView->setSortingEnabled(true);
153     transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder);
154     transactionView->verticalHeader()->hide();
155
156     transactionView->horizontalHeader()->resizeSection(
157             TransactionTableModel::Status, 23);
158     transactionView->horizontalHeader()->resizeSection(
159             TransactionTableModel::Date, 120);
160     transactionView->horizontalHeader()->resizeSection(
161             TransactionTableModel::Type, 120);
162     transactionView->horizontalHeader()->setResizeMode(
163             TransactionTableModel::ToAddress, QHeaderView::Stretch);
164     transactionView->horizontalHeader()->resizeSection(
165             TransactionTableModel::Amount, 100);
166
167 }
168
169 void TransactionView::chooseDate(int idx)
170 {
171     QDate current = QDate::currentDate();
172     dateRangeWidget->setVisible(false);
173     switch(dateWidget->itemData(idx).toInt())
174     {
175     case All:
176         transactionProxyModel->setDateRange(
177                 TransactionFilterProxy::MIN_DATE,
178                 TransactionFilterProxy::MAX_DATE);
179         break;
180     case Today:
181         transactionProxyModel->setDateRange(
182                 QDateTime(current),
183                 TransactionFilterProxy::MAX_DATE);
184         break;
185     case ThisWeek: {
186         // Find last monday
187         QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1));
188         transactionProxyModel->setDateRange(
189                 QDateTime(startOfWeek),
190                 TransactionFilterProxy::MAX_DATE);
191
192         } break;
193     case ThisMonth:
194         transactionProxyModel->setDateRange(
195                 QDateTime(QDate(current.year(), current.month(), 1)),
196                 TransactionFilterProxy::MAX_DATE);
197         break;
198     case LastMonth:
199         transactionProxyModel->setDateRange(
200                 QDateTime(QDate(current.year(), current.month()-1, 1)),
201                 QDateTime(QDate(current.year(), current.month(), 1)));
202         break;
203     case ThisYear:
204         transactionProxyModel->setDateRange(
205                 QDateTime(QDate(current.year(), 1, 1)),
206                 TransactionFilterProxy::MAX_DATE);
207         break;
208     case Range:
209         dateRangeWidget->setVisible(true);
210         dateRangeChanged();
211         break;
212     }
213 }
214
215 void TransactionView::chooseType(int idx)
216 {
217     transactionProxyModel->setTypeFilter(
218         typeWidget->itemData(idx).toInt());
219 }
220
221 void TransactionView::changedPrefix(const QString &prefix)
222 {
223     transactionProxyModel->setAddressPrefix(prefix);
224 }
225
226 void TransactionView::changedAmount(const QString &amount)
227 {
228     qint64 amount_parsed = 0;
229     if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed))
230     {
231         transactionProxyModel->setMinAmount(amount_parsed);
232     }
233     else
234     {
235         transactionProxyModel->setMinAmount(0);
236     }
237 }
238
239 void TransactionView::exportClicked()
240 {
241     // CSV is currently the only supported format
242     QString filename = QFileDialog::getSaveFileName(
243             this,
244             tr("Export Transaction Data"),
245             QDir::currentPath(),
246             tr("Comma separated file (*.csv)"));
247
248     if (filename.isNull()) return;
249
250     CSVModelWriter writer(filename);
251
252     // name, column, role
253     writer.setModel(transactionProxyModel);
254     writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
255     writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
256     writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
257     writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
258     writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
259     writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole);
260     writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
261
262     if(!writer.write())
263     {
264         QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
265                               QMessageBox::Abort, QMessageBox::Abort);
266     }
267 }
268
269 void TransactionView::contextualMenu(const QPoint &point)
270 {
271     QModelIndex index = transactionView->indexAt(point);
272     if(index.isValid())
273     {
274         contextMenu->exec(QCursor::pos());
275     }
276 }
277
278 void TransactionView::copyAddress()
279 {
280     QModelIndexList selection = transactionView->selectionModel()->selectedRows();
281     if(!selection.isEmpty())
282     {
283         QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::AddressRole).toString());
284     }
285 }
286
287 void TransactionView::copyLabel()
288 {
289     QModelIndexList selection = transactionView->selectionModel()->selectedRows();
290     if(!selection.isEmpty())
291     {
292         QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::LabelRole).toString());
293     }
294 }
295
296 void TransactionView::editLabel()
297 {
298     QModelIndexList selection = transactionView->selectionModel()->selectedRows();
299     if(!selection.isEmpty())
300     {
301         AddressTableModel *addressBook = model->getAddressTableModel();
302         QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString();
303         if(address.isEmpty())
304         {
305             // If this transaction has no associated address, exit
306             return;
307         }
308         int idx = addressBook->lookupAddress(address);
309         if(idx != -1)
310         {
311             // Edit sending / receiving address
312             QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex());
313             // Determine type of address, launch appropriate editor dialog type
314             QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
315
316             EditAddressDialog dlg(type==AddressTableModel::Receive
317                                          ? EditAddressDialog::EditReceivingAddress
318                                          : EditAddressDialog::EditSendingAddress,
319                                   this);
320             dlg.setModel(addressBook);
321             dlg.loadRow(idx);
322             dlg.exec();
323         }
324         else
325         {
326             // Add sending address
327             EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
328                                   this);
329             dlg.exec();
330         }
331     }
332 }
333
334 void TransactionView::showDetails()
335 {
336     QModelIndexList selection = transactionView->selectionModel()->selectedRows();
337     if(!selection.isEmpty())
338     {
339         TransactionDescDialog dlg(selection.at(0));
340         dlg.exec();
341     }
342 }
343
344 QWidget *TransactionView::createDateRangeWidget()
345 {
346     dateRangeWidget = new QFrame();
347     dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
348     dateRangeWidget->setContentsMargins(1,1,1,1);
349     QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget);
350     layout->setContentsMargins(0,0,0,0);
351     layout->addSpacing(23);
352     layout->addWidget(new QLabel(tr("Range:")));
353
354     dateFrom = new QDateTimeEdit(this);
355     dateFrom->setDisplayFormat("dd/MM/yy");
356     dateFrom->setCalendarPopup(true);
357     dateFrom->setMinimumWidth(100);
358     dateFrom->setDate(QDate::currentDate().addDays(-7));
359     layout->addWidget(dateFrom);
360     layout->addWidget(new QLabel(tr("to")));
361
362     dateTo = new QDateTimeEdit(this);
363     dateTo->setDisplayFormat("dd/MM/yy");
364     dateTo->setCalendarPopup(true);
365     dateTo->setMinimumWidth(100);
366     dateTo->setDate(QDate::currentDate());
367     layout->addWidget(dateTo);
368     layout->addStretch();
369
370     // Hide by default
371     dateRangeWidget->setVisible(false);
372
373     // Notify on change
374     connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
375     connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
376
377     return dateRangeWidget;
378 }
379
380 void TransactionView::dateRangeChanged()
381 {
382     transactionProxyModel->setDateRange(
383             QDateTime(dateFrom->date()),
384             QDateTime(dateTo->date()).addDays(1));
385 }