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