LCOV - code coverage report
Current view: top level - src/qt - bitcoinamountfield.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 207 0.0 %
Date: 2020-09-26 01:30:44 Functions: 0 36 0.0 %

          Line data    Source code
       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/bitcoinamountfield.h>
       6             : 
       7             : #include <qt/bitcoinunits.h>
       8             : #include <qt/guiconstants.h>
       9             : #include <qt/guiutil.h>
      10             : #include <qt/qvaluecombobox.h>
      11             : 
      12             : #include <QApplication>
      13             : #include <QAbstractSpinBox>
      14             : #include <QHBoxLayout>
      15             : #include <QKeyEvent>
      16             : #include <QLineEdit>
      17             : 
      18             : /** QSpinBox that uses fixed-point numbers internally and uses our own
      19             :  * formatting/parsing functions.
      20             :  */
      21           0 : class AmountSpinBox: public QAbstractSpinBox
      22             : {
      23             :     Q_OBJECT
      24             : 
      25             : public:
      26           0 :     explicit AmountSpinBox(QWidget *parent):
      27           0 :         QAbstractSpinBox(parent)
      28           0 :     {
      29           0 :         setAlignment(Qt::AlignRight);
      30             : 
      31           0 :         connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged);
      32           0 :     }
      33             : 
      34           0 :     QValidator::State validate(QString &text, int &pos) const override
      35             :     {
      36           0 :         if(text.isEmpty())
      37           0 :             return QValidator::Intermediate;
      38           0 :         bool valid = false;
      39           0 :         parse(text, &valid);
      40             :         /* Make sure we return Intermediate so that fixup() is called on defocus */
      41           0 :         return valid ? QValidator::Intermediate : QValidator::Invalid;
      42           0 :     }
      43             : 
      44           0 :     void fixup(QString &input) const override
      45             :     {
      46           0 :         bool valid;
      47           0 :         CAmount val;
      48             : 
      49           0 :         if (input.isEmpty() && !m_allow_empty) {
      50           0 :             valid = true;
      51           0 :             val = m_min_amount;
      52           0 :         } else {
      53           0 :             valid = false;
      54           0 :             val = parse(input, &valid);
      55             :         }
      56             : 
      57           0 :         if (valid) {
      58           0 :             val = qBound(m_min_amount, val, m_max_amount);
      59           0 :             input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::SeparatorStyle::ALWAYS);
      60           0 :             lineEdit()->setText(input);
      61           0 :         }
      62           0 :     }
      63             : 
      64           0 :     CAmount value(bool *valid_out=nullptr) const
      65             :     {
      66           0 :         return parse(text(), valid_out);
      67           0 :     }
      68             : 
      69           0 :     void setValue(const CAmount& value)
      70             :     {
      71           0 :         lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::SeparatorStyle::ALWAYS));
      72           0 :         Q_EMIT valueChanged();
      73           0 :     }
      74             : 
      75           0 :     void SetAllowEmpty(bool allow)
      76             :     {
      77           0 :         m_allow_empty = allow;
      78           0 :     }
      79             : 
      80           0 :     void SetMinValue(const CAmount& value)
      81             :     {
      82           0 :         m_min_amount = value;
      83           0 :     }
      84             : 
      85           0 :     void SetMaxValue(const CAmount& value)
      86             :     {
      87           0 :         m_max_amount = value;
      88           0 :     }
      89             : 
      90           0 :     void stepBy(int steps) override
      91             :     {
      92           0 :         bool valid = false;
      93           0 :         CAmount val = value(&valid);
      94           0 :         val = val + steps * singleStep;
      95           0 :         val = qBound(m_min_amount, val, m_max_amount);
      96           0 :         setValue(val);
      97           0 :     }
      98             : 
      99           0 :     void setDisplayUnit(int unit)
     100             :     {
     101           0 :         bool valid = false;
     102           0 :         CAmount val = value(&valid);
     103             : 
     104           0 :         currentUnit = unit;
     105           0 :         lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::SeparatorStyle::ALWAYS));
     106           0 :         if(valid)
     107           0 :             setValue(val);
     108             :         else
     109           0 :             clear();
     110           0 :     }
     111             : 
     112           0 :     void setSingleStep(const CAmount& step)
     113             :     {
     114           0 :         singleStep = step;
     115           0 :     }
     116             : 
     117           0 :     QSize minimumSizeHint() const override
     118             :     {
     119           0 :         if(cachedMinimumSizeHint.isEmpty())
     120             :         {
     121           0 :             ensurePolished();
     122             : 
     123           0 :             const QFontMetrics fm(fontMetrics());
     124           0 :             int h = lineEdit()->minimumSizeHint().height();
     125           0 :             int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));
     126           0 :             w += 2; // cursor blinking space
     127             : 
     128           0 :             QStyleOptionSpinBox opt;
     129           0 :             initStyleOption(&opt);
     130           0 :             QSize hint(w, h);
     131           0 :             QSize extra(35, 6);
     132           0 :             opt.rect.setSize(hint + extra);
     133           0 :             extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
     134           0 :                                                     QStyle::SC_SpinBoxEditField, this).size();
     135             :             // get closer to final result by repeating the calculation
     136           0 :             opt.rect.setSize(hint + extra);
     137           0 :             extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
     138           0 :                                                     QStyle::SC_SpinBoxEditField, this).size();
     139           0 :             hint += extra;
     140           0 :             hint.setHeight(h);
     141             : 
     142           0 :             opt.rect = rect();
     143             : 
     144           0 :             cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
     145           0 :                                     .expandedTo(QApplication::globalStrut());
     146           0 :         }
     147           0 :         return cachedMinimumSizeHint;
     148           0 :     }
     149             : 
     150             : private:
     151           0 :     int currentUnit{BitcoinUnits::BTC};
     152           0 :     CAmount singleStep{CAmount(100000)}; // satoshis
     153             :     mutable QSize cachedMinimumSizeHint;
     154           0 :     bool m_allow_empty{true};
     155           0 :     CAmount m_min_amount{CAmount(0)};
     156           0 :     CAmount m_max_amount{BitcoinUnits::maxMoney()};
     157             : 
     158             :     /**
     159             :      * Parse a string into a number of base monetary units and
     160             :      * return validity.
     161             :      * @note Must return 0 if !valid.
     162             :      */
     163           0 :     CAmount parse(const QString &text, bool *valid_out=nullptr) const
     164             :     {
     165           0 :         CAmount val = 0;
     166           0 :         bool valid = BitcoinUnits::parse(currentUnit, text, &val);
     167           0 :         if(valid)
     168             :         {
     169           0 :             if(val < 0 || val > BitcoinUnits::maxMoney())
     170           0 :                 valid = false;
     171             :         }
     172           0 :         if(valid_out)
     173           0 :             *valid_out = valid;
     174           0 :         return valid ? val : 0;
     175           0 :     }
     176             : 
     177             : protected:
     178           0 :     bool event(QEvent *event) override
     179             :     {
     180           0 :         if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
     181             :         {
     182           0 :             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
     183           0 :             if (keyEvent->key() == Qt::Key_Comma)
     184             :             {
     185             :                 // Translate a comma into a period
     186           0 :                 QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
     187           0 :                 return QAbstractSpinBox::event(&periodKeyEvent);
     188           0 :             }
     189           0 :         }
     190           0 :         return QAbstractSpinBox::event(event);
     191           0 :     }
     192             : 
     193           0 :     StepEnabled stepEnabled() const override
     194             :     {
     195           0 :         if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
     196           0 :             return StepNone;
     197           0 :         if (text().isEmpty()) // Allow step-up with empty field
     198           0 :             return StepUpEnabled;
     199             : 
     200           0 :         StepEnabled rv = StepNone;
     201           0 :         bool valid = false;
     202           0 :         CAmount val = value(&valid);
     203           0 :         if (valid) {
     204           0 :             if (val > m_min_amount)
     205           0 :                 rv |= StepDownEnabled;
     206           0 :             if (val < m_max_amount)
     207           0 :                 rv |= StepUpEnabled;
     208             :         }
     209           0 :         return rv;
     210           0 :     }
     211             : 
     212             : Q_SIGNALS:
     213             :     void valueChanged();
     214             : };
     215             : 
     216             : #include <qt/bitcoinamountfield.moc>
     217             : 
     218           0 : BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
     219           0 :     QWidget(parent),
     220           0 :     amount(nullptr)
     221           0 : {
     222           0 :     amount = new AmountSpinBox(this);
     223           0 :     amount->setLocale(QLocale::c());
     224           0 :     amount->installEventFilter(this);
     225           0 :     amount->setMaximumWidth(240);
     226             : 
     227           0 :     QHBoxLayout *layout = new QHBoxLayout(this);
     228           0 :     layout->addWidget(amount);
     229           0 :     unit = new QValueComboBox(this);
     230           0 :     unit->setModel(new BitcoinUnits(this));
     231           0 :     layout->addWidget(unit);
     232           0 :     layout->addStretch(1);
     233           0 :     layout->setContentsMargins(0,0,0,0);
     234             : 
     235           0 :     setLayout(layout);
     236             : 
     237           0 :     setFocusPolicy(Qt::TabFocus);
     238           0 :     setFocusProxy(amount);
     239             : 
     240             :     // If one if the widgets changes, the combined content changes as well
     241           0 :     connect(amount, &AmountSpinBox::valueChanged, this, &BitcoinAmountField::valueChanged);
     242           0 :     connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);
     243             : 
     244             :     // Set default based on configuration
     245           0 :     unitChanged(unit->currentIndex());
     246           0 : }
     247             : 
     248           0 : void BitcoinAmountField::clear()
     249             : {
     250           0 :     amount->clear();
     251           0 :     unit->setCurrentIndex(0);
     252           0 : }
     253             : 
     254           0 : void BitcoinAmountField::setEnabled(bool fEnabled)
     255             : {
     256           0 :     amount->setEnabled(fEnabled);
     257           0 :     unit->setEnabled(fEnabled);
     258           0 : }
     259             : 
     260           0 : bool BitcoinAmountField::validate()
     261             : {
     262           0 :     bool valid = false;
     263           0 :     value(&valid);
     264           0 :     setValid(valid);
     265           0 :     return valid;
     266           0 : }
     267             : 
     268           0 : void BitcoinAmountField::setValid(bool valid)
     269             : {
     270           0 :     if (valid)
     271           0 :         amount->setStyleSheet("");
     272             :     else
     273           0 :         amount->setStyleSheet(STYLE_INVALID);
     274           0 : }
     275             : 
     276           0 : bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
     277             : {
     278           0 :     if (event->type() == QEvent::FocusIn)
     279             :     {
     280             :         // Clear invalid flag on focus
     281           0 :         setValid(true);
     282           0 :     }
     283           0 :     return QWidget::eventFilter(object, event);
     284             : }
     285             : 
     286           0 : QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
     287             : {
     288           0 :     QWidget::setTabOrder(prev, amount);
     289           0 :     QWidget::setTabOrder(amount, unit);
     290           0 :     return unit;
     291             : }
     292             : 
     293           0 : CAmount BitcoinAmountField::value(bool *valid_out) const
     294             : {
     295           0 :     return amount->value(valid_out);
     296             : }
     297             : 
     298           0 : void BitcoinAmountField::setValue(const CAmount& value)
     299             : {
     300           0 :     amount->setValue(value);
     301           0 : }
     302             : 
     303           0 : void BitcoinAmountField::SetAllowEmpty(bool allow)
     304             : {
     305           0 :     amount->SetAllowEmpty(allow);
     306           0 : }
     307             : 
     308           0 : void BitcoinAmountField::SetMinValue(const CAmount& value)
     309             : {
     310           0 :     amount->SetMinValue(value);
     311           0 : }
     312             : 
     313           0 : void BitcoinAmountField::SetMaxValue(const CAmount& value)
     314             : {
     315           0 :     amount->SetMaxValue(value);
     316           0 : }
     317             : 
     318           0 : void BitcoinAmountField::setReadOnly(bool fReadOnly)
     319             : {
     320           0 :     amount->setReadOnly(fReadOnly);
     321           0 : }
     322             : 
     323           0 : void BitcoinAmountField::unitChanged(int idx)
     324             : {
     325             :     // Use description tooltip for current unit for the combobox
     326           0 :     unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
     327             : 
     328             :     // Determine new unit ID
     329           0 :     int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
     330             : 
     331           0 :     amount->setDisplayUnit(newUnit);
     332           0 : }
     333             : 
     334           0 : void BitcoinAmountField::setDisplayUnit(int newUnit)
     335             : {
     336           0 :     unit->setValue(newUnit);
     337           0 : }
     338             : 
     339           0 : void BitcoinAmountField::setSingleStep(const CAmount& step)
     340             : {
     341           0 :     amount->setSingleStep(step);
     342           0 : }

Generated by: LCOV version 1.15