walletview.cpp raw

   1  // Copyright (c) 2011-present 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 Txid&>(&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).toLongLong();
 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  }
 287