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