1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include "transactionview.h"
6
7 #include "addresstablemodel.h"
8 #include "bitcoinunits.h"
9 #include "csvmodelwriter.h"
10 #include "editaddressdialog.h"
11 #include "guiutil.h"
12 #include "optionsmodel.h"
13 #include "platformstyle.h"
14 #include "transactiondescdialog.h"
15 #include "transactionfilterproxy.h"
16 #include "transactionrecord.h"
17 #include "transactiontablemodel.h"
18 #include "walletmodel.h"
19
20 #include "ui_interface.h"
21
22 #include <QComboBox>
23 #include <QDateTimeEdit>
24 #include <QDesktopServices>
25 #include <QDoubleValidator>
26 #include <QHBoxLayout>
27 #include <QHeaderView>
28 #include <QLabel>
29 #include <QLineEdit>
30 #include <QMenu>
31 #include <QPoint>
32 #include <QScrollBar>
33 #include <QSignalMapper>
34 #include <QTableView>
35 #include <QUrl>
36 #include <QVBoxLayout>
37
TransactionView(const PlatformStyle * platformStyle,QWidget * parent)38 TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
39 QWidget(parent), model(0), transactionProxyModel(0),
40 transactionView(0), abandonAction(0), columnResizingFixer(0)
41 {
42 // Build filter row
43 setContentsMargins(0,0,0,0);
44
45 QHBoxLayout *hlayout = new QHBoxLayout();
46 hlayout->setContentsMargins(0,0,0,0);
47
48 if (platformStyle->getUseExtraSpacing()) {
49 hlayout->setSpacing(5);
50 hlayout->addSpacing(26);
51 } else {
52 hlayout->setSpacing(0);
53 hlayout->addSpacing(23);
54 }
55
56 watchOnlyWidget = new QComboBox(this);
57 watchOnlyWidget->setFixedWidth(24);
58 watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All);
59 watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes);
60 watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No);
61 hlayout->addWidget(watchOnlyWidget);
62
63 dateWidget = new QComboBox(this);
64 if (platformStyle->getUseExtraSpacing()) {
65 dateWidget->setFixedWidth(121);
66 } else {
67 dateWidget->setFixedWidth(120);
68 }
69 dateWidget->addItem(tr("All"), All);
70 dateWidget->addItem(tr("Today"), Today);
71 dateWidget->addItem(tr("This week"), ThisWeek);
72 dateWidget->addItem(tr("This month"), ThisMonth);
73 dateWidget->addItem(tr("Last month"), LastMonth);
74 dateWidget->addItem(tr("This year"), ThisYear);
75 dateWidget->addItem(tr("Range..."), Range);
76 hlayout->addWidget(dateWidget);
77
78 typeWidget = new QComboBox(this);
79 if (platformStyle->getUseExtraSpacing()) {
80 typeWidget->setFixedWidth(121);
81 } else {
82 typeWidget->setFixedWidth(120);
83 }
84
85 typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
86 typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
87 TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
88 typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
89 TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
90 typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
91 typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
92 typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
93
94 hlayout->addWidget(typeWidget);
95
96 addressWidget = new QLineEdit(this);
97 #if QT_VERSION >= 0x040700
98 addressWidget->setPlaceholderText(tr("Enter address or label to search"));
99 #endif
100 hlayout->addWidget(addressWidget);
101
102 amountWidget = new QLineEdit(this);
103 #if QT_VERSION >= 0x040700
104 amountWidget->setPlaceholderText(tr("Min amount"));
105 #endif
106 if (platformStyle->getUseExtraSpacing()) {
107 amountWidget->setFixedWidth(97);
108 } else {
109 amountWidget->setFixedWidth(100);
110 }
111 amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
112 hlayout->addWidget(amountWidget);
113
114 QVBoxLayout *vlayout = new QVBoxLayout(this);
115 vlayout->setContentsMargins(0,0,0,0);
116 vlayout->setSpacing(0);
117
118 QTableView *view = new QTableView(this);
119 vlayout->addLayout(hlayout);
120 vlayout->addWidget(createDateRangeWidget());
121 vlayout->addWidget(view);
122 vlayout->setSpacing(0);
123 int width = view->verticalScrollBar()->sizeHint().width();
124 // Cover scroll bar width with spacing
125 if (platformStyle->getUseExtraSpacing()) {
126 hlayout->addSpacing(width+2);
127 } else {
128 hlayout->addSpacing(width);
129 }
130 // Always show scroll bar
131 view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
132 view->setTabKeyNavigation(false);
133 view->setContextMenuPolicy(Qt::CustomContextMenu);
134
135 view->installEventFilter(this);
136
137 transactionView = view;
138
139 // Actions
140 abandonAction = new QAction(tr("Abandon transaction"), this);
141 QAction *copyAddressAction = new QAction(tr("Copy address"), this);
142 QAction *copyLabelAction = new QAction(tr("Copy label"), this);
143 QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
144 QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
145 QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
146 QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this);
147 QAction *editLabelAction = new QAction(tr("Edit label"), this);
148 QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
149
150 contextMenu = new QMenu(this);
151 contextMenu->addAction(copyAddressAction);
152 contextMenu->addAction(copyLabelAction);
153 contextMenu->addAction(copyAmountAction);
154 contextMenu->addAction(copyTxIDAction);
155 contextMenu->addAction(copyTxHexAction);
156 contextMenu->addAction(copyTxPlainText);
157 contextMenu->addAction(showDetailsAction);
158 contextMenu->addSeparator();
159 contextMenu->addAction(abandonAction);
160 contextMenu->addAction(editLabelAction);
161
162 mapperThirdPartyTxUrls = new QSignalMapper(this);
163
164 // Connect actions
165 connect(mapperThirdPartyTxUrls, SIGNAL(mapped(QString)), this, SLOT(openThirdPartyTxUrl(QString)));
166
167 connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int)));
168 connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int)));
169 connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int)));
170 connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString)));
171 connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString)));
172
173 connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
174 connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
175
176 connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
177 connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
178 connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
179 connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
180 connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID()));
181 connect(copyTxHexAction, SIGNAL(triggered()), this, SLOT(copyTxHex()));
182 connect(copyTxPlainText, SIGNAL(triggered()), this, SLOT(copyTxPlainText()));
183 connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel()));
184 connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
185 }
186
setModel(WalletModel * model)187 void TransactionView::setModel(WalletModel *model)
188 {
189 this->model = model;
190 if(model)
191 {
192 transactionProxyModel = new TransactionFilterProxy(this);
193 transactionProxyModel->setSourceModel(model->getTransactionTableModel());
194 transactionProxyModel->setDynamicSortFilter(true);
195 transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
196 transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
197
198 transactionProxyModel->setSortRole(Qt::EditRole);
199
200 transactionView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
201 transactionView->setModel(transactionProxyModel);
202 transactionView->setAlternatingRowColors(true);
203 transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
204 transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
205 transactionView->setSortingEnabled(true);
206 transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder);
207 transactionView->verticalHeader()->hide();
208
209 transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH);
210 transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH);
211 transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH);
212 transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH);
213 transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
214
215 columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this);
216
217 if (model->getOptionsModel())
218 {
219 // Add third party transaction URLs to context menu
220 QStringList listUrls = model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts);
221 for (int i = 0; i < listUrls.size(); ++i)
222 {
223 QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host();
224 if (!host.isEmpty())
225 {
226 QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label
227 if (i == 0)
228 contextMenu->addSeparator();
229 contextMenu->addAction(thirdPartyTxUrlAction);
230 connect(thirdPartyTxUrlAction, SIGNAL(triggered()), mapperThirdPartyTxUrls, SLOT(map()));
231 mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction, listUrls[i].trimmed());
232 }
233 }
234 }
235
236 // show/hide column Watch-only
237 updateWatchOnlyColumn(model->haveWatchOnly());
238
239 // Watch-only signal
240 connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool)));
241 }
242 }
243
chooseDate(int idx)244 void TransactionView::chooseDate(int idx)
245 {
246 if(!transactionProxyModel)
247 return;
248 QDate current = QDate::currentDate();
249 dateRangeWidget->setVisible(false);
250 switch(dateWidget->itemData(idx).toInt())
251 {
252 case All:
253 transactionProxyModel->setDateRange(
254 TransactionFilterProxy::MIN_DATE,
255 TransactionFilterProxy::MAX_DATE);
256 break;
257 case Today:
258 transactionProxyModel->setDateRange(
259 QDateTime(current),
260 TransactionFilterProxy::MAX_DATE);
261 break;
262 case ThisWeek: {
263 // Find last Monday
264 QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1));
265 transactionProxyModel->setDateRange(
266 QDateTime(startOfWeek),
267 TransactionFilterProxy::MAX_DATE);
268
269 } break;
270 case ThisMonth:
271 transactionProxyModel->setDateRange(
272 QDateTime(QDate(current.year(), current.month(), 1)),
273 TransactionFilterProxy::MAX_DATE);
274 break;
275 case LastMonth:
276 transactionProxyModel->setDateRange(
277 QDateTime(QDate(current.year(), current.month(), 1).addMonths(-1)),
278 QDateTime(QDate(current.year(), current.month(), 1)));
279 break;
280 case ThisYear:
281 transactionProxyModel->setDateRange(
282 QDateTime(QDate(current.year(), 1, 1)),
283 TransactionFilterProxy::MAX_DATE);
284 break;
285 case Range:
286 dateRangeWidget->setVisible(true);
287 dateRangeChanged();
288 break;
289 }
290 }
291
chooseType(int idx)292 void TransactionView::chooseType(int idx)
293 {
294 if(!transactionProxyModel)
295 return;
296 transactionProxyModel->setTypeFilter(
297 typeWidget->itemData(idx).toInt());
298 }
299
chooseWatchonly(int idx)300 void TransactionView::chooseWatchonly(int idx)
301 {
302 if(!transactionProxyModel)
303 return;
304 transactionProxyModel->setWatchOnlyFilter(
305 (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt());
306 }
307
changedPrefix(const QString & prefix)308 void TransactionView::changedPrefix(const QString &prefix)
309 {
310 if(!transactionProxyModel)
311 return;
312 transactionProxyModel->setAddressPrefix(prefix);
313 }
314
changedAmount(const QString & amount)315 void TransactionView::changedAmount(const QString &amount)
316 {
317 if(!transactionProxyModel)
318 return;
319 CAmount amount_parsed = 0;
320 if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed))
321 {
322 transactionProxyModel->setMinAmount(amount_parsed);
323 }
324 else
325 {
326 transactionProxyModel->setMinAmount(0);
327 }
328 }
329
exportClicked()330 void TransactionView::exportClicked()
331 {
332 // CSV is currently the only supported format
333 QString filename = GUIUtil::getSaveFileName(this,
334 tr("Export Transaction History"), QString(),
335 tr("Comma separated file (*.csv)"), NULL);
336
337 if (filename.isNull())
338 return;
339
340 CSVModelWriter writer(filename);
341
342 // name, column, role
343 writer.setModel(transactionProxyModel);
344 writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
345 if (model && model->haveWatchOnly())
346 writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
347 writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
348 writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
349 writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
350 writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
351 writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole);
352 writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
353
354 if(!writer.write()) {
355 Q_EMIT message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename),
356 CClientUIInterface::MSG_ERROR);
357 }
358 else {
359 Q_EMIT message(tr("Exporting Successful"), tr("The transaction history was successfully saved to %1.").arg(filename),
360 CClientUIInterface::MSG_INFORMATION);
361 }
362 }
363
contextualMenu(const QPoint & point)364 void TransactionView::contextualMenu(const QPoint &point)
365 {
366 QModelIndex index = transactionView->indexAt(point);
367 QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
368 if (selection.empty())
369 return;
370
371 // check if transaction can be abandoned, disable context menu action in case it doesn't
372 uint256 hash;
373 hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
374 abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));
375
376 if(index.isValid())
377 {
378 contextMenu->exec(QCursor::pos());
379 }
380 }
381
abandonTx()382 void TransactionView::abandonTx()
383 {
384 if(!transactionView || !transactionView->selectionModel())
385 return;
386 QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
387
388 // get the hash from the TxHashRole (QVariant / QString)
389 uint256 hash;
390 QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
391 hash.SetHex(hashQStr.toStdString());
392
393 // Abandon the wallet transaction over the walletModel
394 model->abandonTransaction(hash);
395
396 // Update the table
397 model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
398 }
399
copyAddress()400 void TransactionView::copyAddress()
401 {
402 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);
403 }
404
copyLabel()405 void TransactionView::copyLabel()
406 {
407 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::LabelRole);
408 }
409
copyAmount()410 void TransactionView::copyAmount()
411 {
412 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::FormattedAmountRole);
413 }
414
copyTxID()415 void TransactionView::copyTxID()
416 {
417 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole);
418 }
419
copyTxHex()420 void TransactionView::copyTxHex()
421 {
422 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxHexRole);
423 }
424
copyTxPlainText()425 void TransactionView::copyTxPlainText()
426 {
427 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxPlainTextRole);
428 }
429
editLabel()430 void TransactionView::editLabel()
431 {
432 if(!transactionView->selectionModel() ||!model)
433 return;
434 QModelIndexList selection = transactionView->selectionModel()->selectedRows();
435 if(!selection.isEmpty())
436 {
437 AddressTableModel *addressBook = model->getAddressTableModel();
438 if(!addressBook)
439 return;
440 QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString();
441 if(address.isEmpty())
442 {
443 // If this transaction has no associated address, exit
444 return;
445 }
446 // Is address in address book? Address book can miss address when a transaction is
447 // sent from outside the UI.
448 int idx = addressBook->lookupAddress(address);
449 if(idx != -1)
450 {
451 // Edit sending / receiving address
452 QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex());
453 // Determine type of address, launch appropriate editor dialog type
454 QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
455
456 EditAddressDialog dlg(
457 type == AddressTableModel::Receive
458 ? EditAddressDialog::EditReceivingAddress
459 : EditAddressDialog::EditSendingAddress, this);
460 dlg.setModel(addressBook);
461 dlg.loadRow(idx);
462 dlg.exec();
463 }
464 else
465 {
466 // Add sending address
467 EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
468 this);
469 dlg.setModel(addressBook);
470 dlg.setAddress(address);
471 dlg.exec();
472 }
473 }
474 }
475
showDetails()476 void TransactionView::showDetails()
477 {
478 if(!transactionView->selectionModel())
479 return;
480 QModelIndexList selection = transactionView->selectionModel()->selectedRows();
481 if(!selection.isEmpty())
482 {
483 TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0));
484 dlg->setAttribute(Qt::WA_DeleteOnClose);
485 dlg->show();
486 }
487 }
488
openThirdPartyTxUrl(QString url)489 void TransactionView::openThirdPartyTxUrl(QString url)
490 {
491 if(!transactionView || !transactionView->selectionModel())
492 return;
493 QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
494 if(!selection.isEmpty())
495 QDesktopServices::openUrl(QUrl::fromUserInput(url.replace("%s", selection.at(0).data(TransactionTableModel::TxHashRole).toString())));
496 }
497
createDateRangeWidget()498 QWidget *TransactionView::createDateRangeWidget()
499 {
500 dateRangeWidget = new QFrame();
501 dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
502 dateRangeWidget->setContentsMargins(1,1,1,1);
503 QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget);
504 layout->setContentsMargins(0,0,0,0);
505 layout->addSpacing(23);
506 layout->addWidget(new QLabel(tr("Range:")));
507
508 dateFrom = new QDateTimeEdit(this);
509 dateFrom->setDisplayFormat("dd/MM/yy");
510 dateFrom->setCalendarPopup(true);
511 dateFrom->setMinimumWidth(100);
512 dateFrom->setDate(QDate::currentDate().addDays(-7));
513 layout->addWidget(dateFrom);
514 layout->addWidget(new QLabel(tr("to")));
515
516 dateTo = new QDateTimeEdit(this);
517 dateTo->setDisplayFormat("dd/MM/yy");
518 dateTo->setCalendarPopup(true);
519 dateTo->setMinimumWidth(100);
520 dateTo->setDate(QDate::currentDate());
521 layout->addWidget(dateTo);
522 layout->addStretch();
523
524 // Hide by default
525 dateRangeWidget->setVisible(false);
526
527 // Notify on change
528 connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
529 connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
530
531 return dateRangeWidget;
532 }
533
dateRangeChanged()534 void TransactionView::dateRangeChanged()
535 {
536 if(!transactionProxyModel)
537 return;
538 transactionProxyModel->setDateRange(
539 QDateTime(dateFrom->date()),
540 QDateTime(dateTo->date()).addDays(1));
541 }
542
focusTransaction(const QModelIndex & idx)543 void TransactionView::focusTransaction(const QModelIndex &idx)
544 {
545 if(!transactionProxyModel)
546 return;
547 QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx);
548 transactionView->scrollTo(targetIdx);
549 transactionView->setCurrentIndex(targetIdx);
550 transactionView->setFocus();
551 }
552
553 // We override the virtual resizeEvent of the QWidget to adjust tables column
554 // sizes as the tables width is proportional to the dialogs width.
resizeEvent(QResizeEvent * event)555 void TransactionView::resizeEvent(QResizeEvent* event)
556 {
557 QWidget::resizeEvent(event);
558 columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress);
559 }
560
561 // Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text
eventFilter(QObject * obj,QEvent * event)562 bool TransactionView::eventFilter(QObject *obj, QEvent *event)
563 {
564 if (event->type() == QEvent::KeyPress)
565 {
566 QKeyEvent *ke = static_cast<QKeyEvent *>(event);
567 if (ke->key() == Qt::Key_C && ke->modifiers().testFlag(Qt::ControlModifier))
568 {
569 GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxPlainTextRole);
570 return true;
571 }
572 }
573 return QWidget::eventFilter(obj, event);
574 }
575
576 // show/hide column Watch-only
updateWatchOnlyColumn(bool fHaveWatchOnly)577 void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly)
578 {
579 watchOnlyWidget->setVisible(fHaveWatchOnly);
580 transactionView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly);
581 }
582