walletview.cpp raw

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