/ src / qt / walletview.cpp
walletview.cpp
  1  // Copyright (c) 2011-2022 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 <qt/walletview.h>
  6  
  7  #include <qt/addressbookpage.h>
  8  #include <qt/askpassphrasedialog.h>
  9  #include <qt/clientmodel.h>
 10  #include <qt/guiutil.h>
 11  #include <qt/optionsmodel.h>
 12  #include <qt/overviewpage.h>
 13  #include <qt/platformstyle.h>
 14  #include <qt/receivecoinsdialog.h>
 15  #include <qt/sendcoinsdialog.h>
 16  #include <qt/signverifymessagedialog.h>
 17  #include <qt/transactiontablemodel.h>
 18  #include <qt/transactionview.h>
 19  #include <qt/walletmodel.h>
 20  
 21  #include <interfaces/node.h>
 22  #include <node/interface_ui.h>
 23  #include <util/strencodings.h>
 24  
 25  #include <QAction>
 26  #include <QFileDialog>
 27  #include <QHBoxLayout>
 28  #include <QProgressDialog>
 29  #include <QPushButton>
 30  #include <QVBoxLayout>
 31  
 32  WalletView::WalletView(WalletModel* wallet_model, const PlatformStyle* _platformStyle, QWidget* parent)
 33      : QStackedWidget(parent),
 34        walletModel(wallet_model),
 35        platformStyle(_platformStyle)
 36  {
 37      assert(walletModel);
 38  
 39      // Create tabs
 40      overviewPage = new OverviewPage(platformStyle);
 41      overviewPage->setWalletModel(walletModel);
 42  
 43      transactionsPage = new QWidget(this);
 44      QVBoxLayout *vbox = new QVBoxLayout();
 45      QHBoxLayout *hbox_buttons = new QHBoxLayout();
 46      transactionView = new TransactionView(platformStyle, this);
 47      transactionView->setModel(walletModel);
 48  
 49      vbox->addWidget(transactionView);
 50      QPushButton *exportButton = new QPushButton(tr("&Export"), this);
 51      exportButton->setToolTip(tr("Export the data in the current tab to a file"));
 52      if (platformStyle->getImagesOnButtons()) {
 53          exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
 54      }
 55      hbox_buttons->addStretch();
 56      hbox_buttons->addWidget(exportButton);
 57      vbox->addLayout(hbox_buttons);
 58      transactionsPage->setLayout(vbox);
 59  
 60      receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
 61      receiveCoinsPage->setModel(walletModel);
 62  
 63      sendCoinsPage = new SendCoinsDialog(platformStyle);
 64      sendCoinsPage->setModel(walletModel);
 65  
 66      usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
 67      usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
 68  
 69      usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
 70      usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
 71  
 72      addWidget(overviewPage);
 73      addWidget(transactionsPage);
 74      addWidget(receiveCoinsPage);
 75      addWidget(sendCoinsPage);
 76  
 77      connect(overviewPage, &OverviewPage::transactionClicked, this, &WalletView::transactionClicked);
 78      // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
 79      connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
 80  
 81      connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
 82  
 83      connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
 84      // Highlight transaction after send
 85      connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, qOverload<const uint256&>(&TransactionView::focusTransaction));
 86  
 87      // Clicking on "Export" allows to export the transaction list
 88      connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked);
 89  
 90      // Pass through messages from sendCoinsPage
 91      connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
 92      // Pass through messages from transactionView
 93      connect(transactionView, &TransactionView::message, this, &WalletView::message);
 94  
 95      connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy);
 96      connect(this, &WalletView::setPrivacy, this, &WalletView::disableTransactionView);
 97  
 98      // Receive and pass through messages from wallet model
 99      connect(walletModel, &WalletModel::message, this, &WalletView::message);
100  
101      // Handle changes in encryption status
102      connect(walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
103  
104      // Balloon pop-up for new transaction
105      connect(walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);
106  
107      // Ask for passphrase if needed
108      connect(walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);
109  
110      // Show progress dialog
111      connect(walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
112  }
113  
114  WalletView::~WalletView() = default;
115  
116  void WalletView::setClientModel(ClientModel *_clientModel)
117  {
118      this->clientModel = _clientModel;
119  
120      overviewPage->setClientModel(_clientModel);
121      sendCoinsPage->setClientModel(_clientModel);
122      walletModel->setClientModel(_clientModel);
123  }
124  
125  void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
126  {
127      // Prevent balloon-spam when initial block download is in progress
128      if (!clientModel || clientModel->node().isInitialBlockDownload()) {
129          return;
130      }
131  
132      TransactionTableModel *ttm = walletModel->getTransactionTableModel();
133      if (!ttm || ttm->processingQueuedTransactions())
134          return;
135  
136      QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
137      qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
138      QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
139      QModelIndex index = ttm->index(start, 0, parent);
140      QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
141      QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());
142  
143      Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
144  }
145  
146  void WalletView::gotoOverviewPage()
147  {
148      setCurrentWidget(overviewPage);
149  }
150  
151  void WalletView::gotoHistoryPage()
152  {
153      setCurrentWidget(transactionsPage);
154  }
155  
156  void WalletView::gotoReceiveCoinsPage()
157  {
158      setCurrentWidget(receiveCoinsPage);
159  }
160  
161  void WalletView::gotoSendCoinsPage(QString addr)
162  {
163      setCurrentWidget(sendCoinsPage);
164  
165      if (!addr.isEmpty())
166          sendCoinsPage->setAddress(addr);
167  }
168  
169  void WalletView::gotoSignMessageTab(QString addr)
170  {
171      // calls show() in showTab_SM()
172      SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
173      signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
174      signVerifyMessageDialog->setModel(walletModel);
175      signVerifyMessageDialog->showTab_SM(true);
176  
177      if (!addr.isEmpty())
178          signVerifyMessageDialog->setAddress_SM(addr);
179  }
180  
181  void WalletView::gotoVerifyMessageTab(QString addr)
182  {
183      // calls show() in showTab_VM()
184      SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
185      signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
186      signVerifyMessageDialog->setModel(walletModel);
187      signVerifyMessageDialog->showTab_VM(true);
188  
189      if (!addr.isEmpty())
190          signVerifyMessageDialog->setAddress_VM(addr);
191  }
192  
193  bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
194  {
195      return sendCoinsPage->handlePaymentRequest(recipient);
196  }
197  
198  void WalletView::showOutOfSyncWarning(bool fShow)
199  {
200      overviewPage->showOutOfSyncWarning(fShow);
201  }
202  
203  void WalletView::encryptWallet()
204  {
205      auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, this);
206      dlg->setModel(walletModel);
207      connect(dlg, &QDialog::finished, this, &WalletView::encryptionStatusChanged);
208      GUIUtil::ShowModalDialogAsynchronously(dlg);
209  }
210  
211  void WalletView::backupWallet()
212  {
213      QString filename = GUIUtil::getSaveFileName(this,
214          tr("Backup Wallet"), QString(),
215          //: Name of the wallet data file format.
216          tr("Wallet Data") + QLatin1String(" (*.dat)"), nullptr);
217  
218      if (filename.isEmpty())
219          return;
220  
221      if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
222          Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
223              CClientUIInterface::MSG_ERROR);
224          }
225      else {
226          Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
227              CClientUIInterface::MSG_INFORMATION);
228      }
229  }
230  
231  void WalletView::changePassphrase()
232  {
233      auto dlg = new AskPassphraseDialog(AskPassphraseDialog::ChangePass, this);
234      dlg->setModel(walletModel);
235      GUIUtil::ShowModalDialogAsynchronously(dlg);
236  }
237  
238  void WalletView::unlockWallet()
239  {
240      // Unlock wallet when requested by wallet model
241      if (walletModel->getEncryptionStatus() == WalletModel::Locked) {
242          AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
243          dlg.setModel(walletModel);
244          // A modal dialog must be synchronous here as expected
245          // in the WalletModel::requestUnlock() function.
246          dlg.exec();
247      }
248  }
249  
250  void WalletView::usedSendingAddresses()
251  {
252      GUIUtil::bringToFront(usedSendingAddressesPage);
253  }
254  
255  void WalletView::usedReceivingAddresses()
256  {
257      GUIUtil::bringToFront(usedReceivingAddressesPage);
258  }
259  
260  void WalletView::showProgress(const QString &title, int nProgress)
261  {
262      if (nProgress == 0) {
263          progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100);
264          GUIUtil::PolishProgressDialog(progressDialog);
265          progressDialog->setWindowModality(Qt::ApplicationModal);
266          progressDialog->setAutoClose(false);
267          progressDialog->setValue(0);
268      } else if (nProgress == 100) {
269          if (progressDialog) {
270              progressDialog->close();
271              progressDialog->deleteLater();
272              progressDialog = nullptr;
273          }
274      } else if (progressDialog) {
275          if (progressDialog->wasCanceled()) {
276              getWalletModel()->wallet().abortRescan();
277          } else {
278              progressDialog->setValue(nProgress);
279          }
280      }
281  }
282  
283  void WalletView::disableTransactionView(bool disable)
284  {
285      transactionView->setDisabled(disable);
286  }