You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

transactionview.cpp 23KB

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