Bitcoin Core  0.21.1
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules
overviewpage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2019 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/overviewpage.h>
6 #include <qt/forms/ui_overviewpage.h>
7 
8 #include <qt/bitcoinunits.h>
9 #include <qt/clientmodel.h>
10 #include <qt/guiconstants.h>
11 #include <qt/guiutil.h>
12 #include <qt/optionsmodel.h>
13 #include <qt/platformstyle.h>
17 #include <qt/walletmodel.h>
18 
19 #include <QAbstractItemDelegate>
20 #include <QApplication>
21 #include <QPainter>
22 #include <QStatusTipEvent>
23 
24 #include <algorithm>
25 #include <map>
26 
27 #define DECORATION_SIZE 54
28 #define NUM_ITEMS 5
29 
30 Q_DECLARE_METATYPE(interfaces::WalletBalances)
31 
32 class TxViewDelegate : public QAbstractItemDelegate
33 {
34  Q_OBJECT
35 public:
36  explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr):
37  QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC),
38  platformStyle(_platformStyle)
39  {
40  connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged);
41  }
42 
43  inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
44  const QModelIndex &index ) const override
45  {
46  painter->save();
47 
48  QIcon icon = qvariant_cast<QIcon>(index.data(TransactionTableModel::RawDecorationRole));
49  QRect mainRect = option.rect;
50  QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE));
51  int xspace = DECORATION_SIZE + 8;
52  int ypad = 6;
53  int halfheight = (mainRect.height() - 2*ypad)/2;
54  QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight);
55  QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight);
56  icon = platformStyle->SingleColorIcon(icon);
57  icon.paint(painter, decorationRect);
58 
59  QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime();
60  QString address = index.data(Qt::DisplayRole).toString();
61  qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong();
62  bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool();
63  QVariant value = index.data(Qt::ForegroundRole);
64  QColor foreground = option.palette.color(QPalette::Text);
65  if(value.canConvert<QBrush>())
66  {
67  QBrush brush = qvariant_cast<QBrush>(value);
68  foreground = brush.color();
69  }
70 
71  painter->setPen(foreground);
72  QRect boundingRect;
73  painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect);
74  int address_rect_min_width = boundingRect.width();
75 
76  if (index.data(TransactionTableModel::WatchonlyRole).toBool())
77  {
78  QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole));
79  QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight);
80  iconWatchonly.paint(painter, watchonlyRect);
81  address_rect_min_width += 5 + watchonlyRect.width();
82  }
83 
84  if(amount < 0)
85  {
86  foreground = COLOR_NEGATIVE;
87  }
88  else if(!confirmed)
89  {
90  foreground = COLOR_UNCONFIRMED;
91  }
92  else
93  {
94  foreground = option.palette.color(QPalette::Text);
95  }
96  painter->setPen(foreground);
97  QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::SeparatorStyle::ALWAYS);
98  if(!confirmed)
99  {
100  amountText = QString("[") + amountText + QString("]");
101  }
102 
103  QRect amount_bounding_rect;
104  painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText, &amount_bounding_rect);
105 
106  painter->setPen(option.palette.color(QPalette::Text));
107  QRect date_bounding_rect;
108  painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date), &date_bounding_rect);
109 
110  const int minimum_width = std::max(address_rect_min_width, amount_bounding_rect.width() + date_bounding_rect.width());
111  const auto search = m_minimum_width.find(index.row());
112  if (search == m_minimum_width.end() || search->second != minimum_width) {
113  m_minimum_width[index.row()] = minimum_width;
114  Q_EMIT width_changed(index);
115  }
116 
117  painter->restore();
118  }
119 
120  inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
121  {
122  const auto search = m_minimum_width.find(index.row());
123  const int minimum_text_width = search == m_minimum_width.end() ? 0 : search->second;
124  return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE};
125  }
126 
127  int unit;
128 
129 Q_SIGNALS:
131  void width_changed(const QModelIndex& index) const;
132 
133 private:
135  mutable std::map<int, int> m_minimum_width;
136 };
137 
138 #include <qt/overviewpage.moc>
139 
140 OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) :
141  QWidget(parent),
142  ui(new Ui::OverviewPage),
143  clientModel(nullptr),
144  walletModel(nullptr),
145  txdelegate(new TxViewDelegate(platformStyle, this))
146 {
147  ui->setupUi(this);
148 
149  m_balances.balance = -1;
150 
151  // use a SingleColorIcon for the "out of sync warning" icon
152  QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
153  icon.addPixmap(icon.pixmap(QSize(64,64), QIcon::Normal), QIcon::Disabled); // also set the disabled icon because we are using a disabled QPushButton to work around missing HiDPI support of QLabel (https://bugreports.qt.io/browse/QTBUG-42503)
154  ui->labelTransactionsStatus->setIcon(icon);
155  ui->labelWalletStatus->setIcon(icon);
156 
157  // Recent transactions
158  ui->listTransactions->setItemDelegate(txdelegate);
159  ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
160  ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
161  ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);
162 
163  connect(ui->listTransactions, &TransactionOverviewWidget::clicked, this, &OverviewPage::handleTransactionClicked);
164 
165  // start with displaying the "out of sync" warnings
166  showOutOfSyncWarning(true);
167  connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
168  connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
169 }
170 
171 void OverviewPage::handleTransactionClicked(const QModelIndex &index)
172 {
173  if(filter)
174  Q_EMIT transactionClicked(filter->mapToSource(index));
175 }
176 
178 {
179  Q_EMIT outOfSyncWarningClicked();
180 }
181 
182 void OverviewPage::setPrivacy(bool privacy)
183 {
184  m_privacy = privacy;
185  if (m_balances.balance != -1) {
187  }
188 
189  ui->listTransactions->setVisible(!m_privacy);
190 
191  const QString status_tip = m_privacy ? tr("Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings->Mask values.") : "";
192  setStatusTip(status_tip);
193  QStatusTipEvent event(status_tip);
194  QApplication::sendEvent(this, &event);
195 }
196 
198 {
199  delete ui;
200 }
201 
203 {
204  int unit = walletModel->getOptionsModel()->getDisplayUnit();
205  m_balances = balances;
206  if (walletModel->wallet().isLegacy()) {
212  } else {
221  }
222  } else {
227  }
228  // only show immature (newly mined) balance if it's non-zero, so as not to complicate things
229  // for the non-mining users
230  bool showImmature = balances.immature_balance != 0;
231  bool showWatchOnlyImmature = balances.immature_watch_only_balance != 0;
232 
233  // for symmetry reasons also show immature label when the watch-only one is shown
234  ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
235  ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature);
236  ui->labelWatchImmature->setVisible(!walletModel->wallet().privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance
237 }
238 
239 // show/hide watch-only labels
240 void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly)
241 {
242  ui->labelSpendable->setVisible(showWatchOnly); // show spendable label (only when watch-only is active)
243  ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only label
244  ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only balance separator line
245  ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only available balance
246  ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only pending balance
247  ui->labelWatchTotal->setVisible(showWatchOnly); // show watch-only total balance
248 
249  if (!showWatchOnly)
250  ui->labelWatchImmature->hide();
251 }
252 
254 {
255  this->clientModel = model;
256  if (model) {
257  // Show warning, for example if this is a prerelease version
260  }
261 }
262 
264 {
265  this->walletModel = model;
266  if(model && model->getOptionsModel())
267  {
268  // Set up transaction list
269  filter.reset(new TransactionFilterProxy());
270  filter->setSourceModel(model->getTransactionTableModel());
271  filter->setLimit(NUM_ITEMS);
272  filter->setDynamicSortFilter(true);
273  filter->setSortRole(Qt::EditRole);
274  filter->setShowInactive(false);
275  filter->sort(TransactionTableModel::Date, Qt::DescendingOrder);
276 
277  ui->listTransactions->setModel(filter.get());
278  ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
279 
280  // Keep up to date with wallet
281  interfaces::Wallet& wallet = model->wallet();
282  interfaces::WalletBalances balances = wallet.getBalances();
283  setBalance(balances);
284  connect(model, &WalletModel::balanceChanged, this, &OverviewPage::setBalance);
285 
287 
289  connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) {
291  });
292  }
293 
294  // update the display unit, to not use the default ("BTC")
296 }
297 
299 {
301  {
302  if (m_balances.balance != -1) {
304  }
305 
306  // Update txdelegate->unit with the current unit
308 
309  ui->listTransactions->update();
310  }
311 }
312 
313 void OverviewPage::updateAlerts(const QString &warnings)
314 {
315  this->ui->labelAlerts->setVisible(!warnings.isEmpty());
316  this->ui->labelAlerts->setText(warnings);
317 }
318 
320 {
321  ui->labelWalletStatus->setVisible(fShow);
322  ui->labelTransactionsStatus->setVisible(fShow);
323 }
virtual bool privateKeysDisabled()=0
void setWalletModel(WalletModel *walletModel)
Bitcoin unit definitions.
Definition: bitcoinunits.h:31
void updateAlerts(const QString &warnings)
std::map< int, int > m_minimum_width
#define DECORATION_SIZE
void handleTransactionClicked(const QModelIndex &index)
interfaces::WalletBalances m_balances
Definition: overviewpage.h:52
void setPrivacy(bool privacy)
std::unique_ptr< TransactionFilterProxy > filter
Definition: overviewpage.h:56
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:68
interfaces::Wallet & wallet() const
Definition: walletmodel.h:146
TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr)
TxViewDelegate * txdelegate
Definition: overviewpage.h:55
WalletModel * walletModel
Definition: overviewpage.h:51
virtual bool isLegacy()=0
Return whether is a legacy wallet.
QString getStatusBarWarnings() const
Return warnings to be displayed in status bar.
void outOfSyncWarningClicked()
void alertsChanged(const QString &warnings)
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
static QString formatWithPrivacy(int unit, const CAmount &amount, SeparatorStyle separators, bool privacy)
Format as string (with unit) of fixed length to preserve privacy, if it is set.
#define NUM_ITEMS
Collection of wallet balances.
Definition: wallet.h:349
void updateWatchOnlyLabels(bool showWatchOnly)
Interface for accessing a wallet.
Definition: wallet.h:52
void width_changed(const QModelIndex &index) const
An intermediate signal for emitting from the paint() const member function.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
CAmount immature_watch_only_balance
Definition: wallet.h:357
Date and time this transaction was created.
TransactionTableModel * getTransactionTableModel()
void displayUnitChanged(int unit)
#define COLOR_UNCONFIRMED
Definition: guiconstants.h:25
Model for Bitcoin network client.
Definition: clientmodel.h:46
ClientModel * clientModel
Definition: overviewpage.h:50
int getDisplayUnit() const
Definition: optionsmodel.h:84
void transactionClicked(const QModelIndex &index)
const PlatformStyle * platformStyle
void showOutOfSyncWarning(bool fShow)
CAmount unconfirmed_watch_only_balance
Definition: wallet.h:356
Filter the transaction list according to pre-specified rules.
void updateDisplayUnit()
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:51
virtual bool haveWatchOnly()=0
Return whether wallet has watch only keys.
void notifyWatchonlyChanged(bool fHaveWatchonly)
void setClientModel(ClientModel *clientModel)
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
void setBalance(const interfaces::WalletBalances &balances)
void handleOutOfSyncWarningClicks()
OverviewPage(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
virtual WalletBalances getBalances()=0
Get balances.
Overview ("home") page widget.
Definition: overviewpage.h:28
Ui::OverviewPage * ui
Definition: overviewpage.h:49
void balanceChanged(const interfaces::WalletBalances &balances)
#define COLOR_NEGATIVE
Definition: guiconstants.h:27
OptionsModel * getOptionsModel()