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