walletview.cpp
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 }