File indexing completed on 2024-05-19 05:08:30

0001 /*
0002     SPDX-FileCopyrightText: 2010-2020 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "amountedit.h"
0008 
0009 // ----------------------------------------------------------------------------
0010 // QT Includes
0011 
0012 #include <QDebug>
0013 #include <QFrame>
0014 #include <QKeyEvent>
0015 #include <QLocale>
0016 #include <QStyle>
0017 #include <QToolButton>
0018 
0019 // ----------------------------------------------------------------------------
0020 // KDE Includes
0021 
0022 #include <KConfigGroup>
0023 #include <KLocalizedString>
0024 #include <KSharedConfig>
0025 
0026 // ----------------------------------------------------------------------------
0027 // Project Includes
0028 
0029 #include "amountvalidator.h"
0030 #include "kmymoneycalculator.h"
0031 #include "mymoneysecurity.h"
0032 #include "icons.h"
0033 #include "popuppositioner.h"
0034 
0035 using namespace Icons;
0036 
0037 class AmountEditHelper
0038 {
0039 public:
0040     AmountEditHelper() : q(nullptr) {}
0041     ~AmountEditHelper() {
0042         delete q;
0043     }
0044     AmountEdit *q;
0045 };
0046 
0047 Q_GLOBAL_STATIC(AmountEditHelper, s_globalAmountEdit)
0048 
0049 AmountEdit* AmountEdit::global()
0050 {
0051     if (!s_globalAmountEdit()->q) {
0052         s_globalAmountEdit()->q = new AmountEdit(0, 2);
0053     }
0054 
0055     return s_globalAmountEdit()->q;
0056 }
0057 
0058 class AmountEditPrivate
0059 {
0060     Q_DISABLE_COPY(AmountEditPrivate)
0061     Q_DECLARE_PUBLIC(AmountEdit)
0062 
0063 public:
0064     enum Item {
0065         NoItem = 0x0,
0066         ShowCalculator = 0x1,
0067         ShowCurrencySymbol = 0x02,
0068         ShowAll = 0x3,
0069     };
0070     Q_DECLARE_FLAGS(Items, Item)
0071 
0072     explicit AmountEditPrivate(AmountEdit* qq)
0073         : q_ptr(qq)
0074         , m_calculatorFrame(nullptr)
0075         , m_calculator(nullptr)
0076         , m_calculatorButton(nullptr)
0077         , m_prec(2)
0078         , m_allowEmpty(false)
0079         , m_actionIcons(NoItem)
0080         , m_initialExchangeRate(MyMoneyMoney::ONE)
0081         , m_state(AmountEdit::DisplayValue)
0082         , sharesSet(false)
0083         , valueSet(false)
0084         , m_isCashAmount(false)
0085     {
0086         m_calculatorFrame = new QFrame;
0087         m_calculatorFrame->setWindowFlags(Qt::Popup);
0088 
0089         m_calculatorFrame->setFrameStyle(QFrame::Panel | QFrame::Raised);
0090         m_calculatorFrame->setLineWidth(3);
0091 
0092         m_calculator = new KMyMoneyCalculator(m_calculatorFrame);
0093         m_calculatorFrame->hide();
0094     }
0095 
0096     void init()
0097     {
0098         Q_Q(AmountEdit);
0099         // Yes, just a simple double validator !
0100         auto validator = new AmountValidator(q);
0101         q->setValidator(validator);
0102         q->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
0103 
0104         const auto btnSize = q->sizeHint().height() - 5;
0105 
0106         m_calculatorButton = new QToolButton(q);
0107         m_calculatorButton->setIcon(Icons::get(Icon::Calculator));
0108         m_calculatorButton->setCursor(Qt::ArrowCursor);
0109         m_calculatorButton->setStyleSheet("QToolButton { border: none; padding: 2px}");
0110         m_calculatorButton->setFixedSize(btnSize, btnSize);
0111         m_calculatorButton->setFocusPolicy(Qt::ClickFocus);
0112 
0113         q->connect(m_calculatorButton, &QAbstractButton::clicked, q, &AmountEdit::slotCalculatorOpen);
0114 
0115         m_currencyButton = new QToolButton(q);
0116         m_currencyButton->setCursor(Qt::ArrowCursor);
0117         m_currencyButton->setAutoRaise(true);
0118         m_currencyButton->hide();
0119         m_currencyButton->setFocusPolicy(Qt::ClickFocus);
0120 
0121         // setup items
0122         KSharedConfig::Ptr kconfig = KSharedConfig::openConfig();
0123         KConfigGroup grp = kconfig->group("General Options");
0124         q->setCalculatorButtonVisible(!grp.readEntry("DontShowCalculatorButton", false));
0125         q->setCurrencySymbol(QString(), QString());
0126 
0127         updateLineEditSize(m_currencyButton, true);
0128 
0129         q->connect(q, &QLineEdit::textChanged, q, &AmountEdit::theTextChanged);
0130         q->connect(m_calculator, &KMyMoneyCalculator::signalResultAvailable, q, &AmountEdit::slotCalculatorResult);
0131         q->connect(m_calculator, &KMyMoneyCalculator::signalQuit, q, &AmountEdit::slotCalculatorClose);
0132     }
0133 
0134     /**
0135       * Internal helper function for value() and ensureFractionalPart().
0136       */
0137     void ensureFractionalPart(QString& s) const
0138     {
0139         s = MyMoneyMoney(s).formatMoney(QString(), precision(m_state), false);
0140     }
0141 
0142     /**
0143       * This method opens the calculator and replays the key
0144       * event pointed to by @p ev. If @p ev is 0, then no key
0145       * event is replayed.
0146       *
0147       * @param ev pointer to QKeyEvent that started the calculator.
0148       */
0149     void calculatorOpen(QKeyEvent* k)
0150     {
0151         Q_Q(AmountEdit);
0152         m_calculator->setInitialValues(q->text(), k);
0153 
0154         // do not open the calculator in read-only mode
0155         if (q->isReadOnly())
0156             return;
0157 
0158         // show calculator and update size
0159         m_calculatorFrame->show();
0160         m_calculatorFrame->setGeometry(m_calculator->geometry());
0161 
0162         PopupPositioner pos(q, m_calculatorFrame, PopupPositioner::BottemLeft);
0163         m_calculator->setFocus();
0164     }
0165 
0166     void updateLineEditSize(QWidget* widget, int w, int h)
0167     {
0168         Q_Q(AmountEdit);
0169         widget->resize(w, h);
0170 
0171         int height = q->sizeHint().height();
0172         int frameWidth = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0173         const int btnHeight = height - 2*frameWidth;
0174         const int btnX = (height - btnHeight) / 2;
0175         const int currencyX = (height - h) / 2;
0176 
0177         int gaps = 0;
0178         gaps += m_actionIcons.testFlag(ShowCalculator) ? 1 : 0;
0179         gaps += m_actionIcons.testFlag(ShowCurrencySymbol) ? 1 : 0;
0180 
0181         m_calculatorButton->move(q->width() - m_calculatorButton->width() - frameWidth, btnX);
0182         widget->move(q->width() - m_calculatorButton->width() - widget->width() - gaps * frameWidth, currencyX);
0183 
0184         const int padding = m_calculatorButton->width() + widget->width() + ((gaps - 1) * frameWidth);
0185         q->setStyleSheet(QString("QLineEdit { padding-right: %1px }").arg(padding));
0186         q->setMinimumHeight(height);
0187     }
0188 
0189     void cut()
0190     {
0191         Q_Q(AmountEdit);
0192         // only cut if parts of the text are selected
0193         if (q->hasSelectedText() && (q->text() != q->selectedText())) {
0194             cut();
0195         }
0196     }
0197 
0198     bool hasMultipleCurrencies() const
0199     {
0200         return m_sharesCommodity.id().compare(m_valueCommodity.id());
0201     }
0202 
0203     void updateLineEditSize(QToolButton* button, bool ofs = false)
0204     {
0205         Q_Q(AmountEdit);
0206         const int currencyWidth = q->fontMetrics().boundingRect(button->text()).width() + 10;
0207         const auto frameWidth = ofs ? q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0;
0208 
0209         const int currencyHeight = button->height() - frameWidth;
0210 
0211         AmountEditPrivate::updateLineEditSize(button, currencyWidth, currencyHeight);
0212     }
0213 
0214     void setCurrencySymbol(const QString& symbol, const QString& name)
0215     {
0216         m_currencyButton->setText(symbol);
0217         m_currencyButton->setToolTip(name);
0218         m_currencyButton->setHidden(symbol.isEmpty());
0219         m_actionIcons.setFlag(AmountEditPrivate::ShowCurrencySymbol, !symbol.isEmpty());
0220         updateLineEditSize(m_currencyButton);
0221     }
0222 
0223     void updateWidgets()
0224     {
0225         Q_Q(AmountEdit);
0226         if (hasMultipleCurrencies()) {
0227             m_currencyButton->setEnabled(true);
0228             QString currentCurrency;
0229             QString otherCurrency;
0230             // prevent to change the values due to emitted textChanged() signals
0231             QSignalBlocker block(q);
0232             if (m_state == AmountEdit::DisplayShares) {
0233                 currentCurrency = m_sharesCommodity.name();
0234                 otherCurrency = m_valueCommodity.name();
0235                 setCurrencySymbol(m_sharesCommodity.tradingSymbol(), currentCurrency);
0236                 q->QLineEdit::setText(m_sharesText);
0237             } else {
0238                 currentCurrency = m_valueCommodity.name();
0239                 otherCurrency = m_sharesCommodity.name();
0240                 setCurrencySymbol(m_valueCommodity.tradingSymbol(), m_valueCommodity.name());
0241                 q->QLineEdit::setText(m_valueText);
0242             }
0243 
0244             m_currencyButton->setToolTip(i18nc("@info:tooltip Swap currencies for entry/display",
0245                                                "Value are presented in %1. Press this button to switch the currency to %2.",
0246                                                currentCurrency,
0247                                                otherCurrency));
0248 
0249         } else {
0250             if (!m_valueCommodity.id().isEmpty()) {
0251                 m_currencyButton->setEnabled(false);
0252                 m_currencyButton->setToolTip(i18nc("@info:tooltip Swap currencies for entry/display", "Values are presented in %1.", m_valueCommodity.name()));
0253                 setCurrencySymbol(m_valueCommodity.tradingSymbol(), m_valueCommodity.name());
0254 
0255             } else {
0256                 // hide the currency symbol
0257                 setCurrencySymbol(QString(), QString());
0258             }
0259         }
0260     }
0261 
0262     void swapCommodities()
0263     {
0264         Q_Q(AmountEdit);
0265         q->setDisplayState((m_state == AmountEdit::DisplayShares) ? AmountEdit::DisplayValue : AmountEdit::DisplayShares);
0266     }
0267 
0268     MyMoneyMoney adjustToPrecision(AmountEdit::DisplayState state, const MyMoneyMoney& amount) const
0269     {
0270         auto money(amount);
0271         const MyMoneySecurity& sec((state == AmountEdit::DisplayValue) ? m_valueCommodity : m_sharesCommodity);
0272         if (!sec.id().isEmpty()) {
0273             const auto fraction = m_isCashAmount ? sec.smallestCashFraction() : sec.smallestAccountFraction();
0274             money = money.convert(fraction);
0275         } else if (m_prec != -1)
0276             money = money.convert(MyMoneyMoney::precToDenom(m_prec));
0277         return money;
0278     }
0279 
0280     int precision(AmountEdit::DisplayState state) const
0281     {
0282         const MyMoneySecurity& sec((state == AmountEdit::DisplayValue) ? m_valueCommodity : m_sharesCommodity);
0283         auto prec(m_prec);
0284 
0285         if (!sec.id().isEmpty()) {
0286             prec = MyMoneyMoney::denomToPrec(m_isCashAmount ? sec.smallestCashFraction() : sec.smallestAccountFraction());
0287         }
0288         return prec;
0289     }
0290 
0291     void setValueText(const QString& txt)
0292     {
0293         Q_Q(AmountEdit);
0294         m_valueText = txt;
0295         if (q->isEnabled() && !txt.isEmpty()) {
0296             ensureFractionalPart(m_valueText);
0297         }
0298         // only update text if it really differs
0299         if (m_state == AmountEdit::DisplayValue && m_valueText.compare(q->QLineEdit::text())) {
0300             // prevent to change the values due to emitted textChanged() signals
0301             QSignalBlocker block(q);
0302             q->QLineEdit::setText(m_valueText);
0303         }
0304     }
0305 
0306     void setSharesText(const QString& txt)
0307     {
0308         Q_Q(AmountEdit);
0309         m_sharesText = txt;
0310         if (q->isEnabled() && !txt.isEmpty()) {
0311             ensureFractionalPart(m_sharesText);
0312         }
0313         // only update text if it really differs
0314         if (m_state == AmountEdit::DisplayShares && m_sharesText.compare(q->QLineEdit::text())) {
0315             // prevent to change the values due to emitted textChanged() signals
0316             QSignalBlocker block(q);
0317             q->QLineEdit::setText(m_sharesText);
0318         }
0319     }
0320 
0321     AmountEdit* q_ptr;
0322     QFrame* m_calculatorFrame;
0323     KMyMoneyCalculator* m_calculator;
0324     QToolButton* m_calculatorButton;
0325     QToolButton* m_currencyButton;
0326     int m_prec;
0327     bool m_allowEmpty;
0328     QString m_previousText; // keep track of what has been typed
0329 
0330     QString m_valueText; // keep track of what was the original value
0331     QString m_sharesText; // keep track of what was the original value
0332 
0333     QFlags<Item> m_actionIcons;
0334 
0335     MyMoneyMoney m_value; // original amount when starting editing
0336     MyMoneyMoney m_shares; // original amount when starting editing
0337     MyMoneyMoney m_initialExchangeRate;
0338 
0339     MyMoneySecurity m_sharesCommodity;
0340     MyMoneySecurity m_valueCommodity;
0341 
0342     /**
0343      * Which part is displayed
0344      */
0345     AmountEdit::DisplayState m_state;
0346 
0347     bool sharesSet;
0348     bool valueSet;
0349     bool m_isCashAmount;
0350 };
0351 
0352 Q_DECLARE_OPERATORS_FOR_FLAGS(AmountEditPrivate::Items)
0353 
0354 void AmountEdit::setReadOnly(bool ro)
0355 {
0356     Q_D(AmountEdit);
0357     d->m_calculatorButton->setEnabled(!ro);
0358     QLineEdit::setReadOnly(ro);
0359 }
0360 
0361 AmountEdit::AmountEdit(QWidget *parent, const int prec) :
0362     QLineEdit(parent),
0363     d_ptr(new AmountEditPrivate(this))
0364 {
0365     Q_D(AmountEdit);
0366     d->m_prec = prec;
0367     if (prec < -1 || prec > 20) {
0368         d->m_prec = AmountEdit::global()->standardPrecision();
0369     }
0370 
0371     d->init();
0372 
0373     connect(d->m_currencyButton, &QToolButton::clicked, this, [&]() {
0374         Q_D(AmountEdit);
0375         d->swapCommodities();
0376     });
0377 }
0378 
0379 AmountEdit::AmountEdit(const MyMoneySecurity& sec, QWidget *parent) :
0380     QLineEdit(parent),
0381     d_ptr(new AmountEditPrivate(this))
0382 {
0383     Q_D(AmountEdit);
0384     d->m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction());
0385     d->init();
0386 }
0387 
0388 AmountEdit::AmountEdit(QWidget* parent, const int prec, AmountEditPrivate* dd)
0389     : QLineEdit(parent)
0390     , d_ptr(dd)
0391 {
0392     Q_D(AmountEdit);
0393     d->m_prec = prec;
0394     if (prec < -1 || prec > 20) {
0395         d->m_prec = AmountEdit::global()->standardPrecision();
0396     }
0397     d->init();
0398 }
0399 
0400 AmountEdit::~AmountEdit()
0401 {
0402     Q_D(AmountEdit);
0403     delete d;
0404 }
0405 
0406 void AmountEdit::setStandardPrecision(int prec)
0407 {
0408     if (prec >= 0 && prec < 20) {
0409         global()->d_ptr->m_prec = prec;
0410     }
0411 }
0412 
0413 int AmountEdit::standardPrecision()
0414 {
0415     return global()->d_ptr->m_prec;
0416 }
0417 
0418 
0419 void AmountEdit::resizeEvent(QResizeEvent* event)
0420 {
0421     Q_UNUSED(event);
0422     Q_D(AmountEdit);
0423     d->updateLineEditSize(d->m_currencyButton);
0424 }
0425 
0426 void AmountEdit::focusInEvent(QFocusEvent* event)
0427 {
0428     QLineEdit::focusInEvent(event);
0429     if ((event->reason() == Qt::MouseFocusReason) || (event->reason() == Qt::ActiveWindowFocusReason)) {
0430         if (!hasSelectedText()) {
0431             // we need to wait until all processing is done before
0432             // we can successfully call selectAll. Hence the
0433             // delayed execution when we return back to the event loop
0434             metaObject()->invokeMethod(this, &QLineEdit::selectAll, Qt::QueuedConnection);
0435         }
0436     }
0437 }
0438 
0439 void AmountEdit::focusOutEvent(QFocusEvent* event)
0440 {
0441     Q_D(AmountEdit);
0442     QLineEdit::focusOutEvent(event);
0443 
0444     // make sure we have a zero value in case the current text
0445     // is empty but this is not allowed
0446     if (text().isEmpty() && !d->m_allowEmpty) {
0447         QLineEdit::setText(QLatin1String("0"));
0448     }
0449 
0450     // make sure we have a fractional part
0451     if (!text().isEmpty())
0452         ensureFractionalPart();
0453 
0454     // in case the widget contains a different value we emit
0455     // the amountChanged signal
0456     if ((d->m_value != value()) || (d->m_shares != shares())) {
0457         d->m_value = value();
0458         d->m_shares = shares();
0459         Q_EMIT amountChanged();
0460     }
0461 }
0462 
0463 void AmountEdit::keyPressEvent(QKeyEvent* event)
0464 {
0465     Q_D(AmountEdit);
0466     switch(event->key()) {
0467     case Qt::Key_Plus:
0468     case Qt::Key_Minus:
0469         d->cut();
0470         if (text().length() == 0) {
0471             break;
0472         }
0473         // in case of '-' we do not enter the calculator when
0474         // the current position is the beginning and there is
0475         // no '-' sign at the first position.
0476         if (event->key() == Qt::Key_Minus) {
0477             if (cursorPosition() == 0 && text().at(0) != '-') {
0478                 break;
0479             }
0480         }
0481         // intentional fall through
0482 
0483     case Qt::Key_Slash:
0484     case Qt::Key_Asterisk:
0485     case Qt::Key_Percent:
0486         d->cut();
0487         d->calculatorOpen(event);
0488         return;
0489 
0490     case Qt::Key_Return:
0491     case Qt::Key_Escape:
0492     case Qt::Key_Enter:
0493         break;
0494 
0495     case Qt::Key_Space:
0496         if (event->modifiers() == Qt::ControlModifier) {
0497             event->accept();
0498             d->m_currencyButton->animateClick();
0499             return;
0500         }
0501         break;
0502 
0503     default:
0504         // make sure to use the locale's decimalPoint when the
0505         // keypad comma/dot is pressed
0506         QString keyText = event->text();
0507         int key = event->key();
0508         if (event->modifiers() & Qt::KeypadModifier) {
0509             if ((key == Qt::Key_Period) || (key == Qt::Key_Comma)) {
0510 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0511                 key = QLocale().decimalPoint().unicode();
0512 #else
0513                 key = QLocale().decimalPoint().unicode()->toLatin1(); // TODO Test this
0514 #endif
0515                 keyText = QLocale().decimalPoint();
0516             }
0517         }
0518         // create a (possibly adjusted) copy of the event
0519         QKeyEvent newEvent(event->type(),
0520                            key,
0521                            event->modifiers(),
0522                            event->nativeScanCode(),
0523                            event->nativeVirtualKey(),
0524                            event->nativeModifiers(),
0525                            keyText,
0526                            event->isAutoRepeat(),
0527                            event->count());
0528 
0529         // in case all text is selected and the user presses the decimal point
0530         // we fill the widget with the leading "0". The outcome of this will be
0531         // that the widget then contains "0.".
0532 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0533         if ((newEvent.key() == QLocale().decimalPoint().unicode()) && (selectedText() == text())) {
0534 #else
0535         if ((newEvent.key() == QLocale().decimalPoint().at(0).unicode()) && (selectedText() == text())) {
0536 #endif
0537             QLineEdit::setText(QLatin1String("0"));
0538         }
0539         *event = newEvent;
0540     }
0541 
0542     // in case we have not processed anything, we
0543     // need to call the base class implementation
0544     QLineEdit::keyPressEvent(event);
0545 }
0546 
0547 
0548 void AmountEdit::setPrecision(const int prec)
0549 {
0550     Q_D(AmountEdit);
0551     if (prec >= -1 && prec <= 20) {
0552         if (prec != d->m_prec) {
0553             d->m_prec = prec;
0554             // update current display
0555             setValue(value());
0556         }
0557     }
0558 }
0559 
0560 int AmountEdit::precision() const
0561 {
0562     Q_D(const AmountEdit);
0563     return d->m_prec;
0564 }
0565 
0566 int AmountEdit::precision(MultiCurrencyEdit::DisplayState state) const
0567 {
0568     Q_D(const AmountEdit);
0569     return d->precision(state);
0570 }
0571 
0572 MultiCurrencyEdit::DisplayState AmountEdit::displayState() const
0573 {
0574     Q_D(const AmountEdit);
0575     return d->m_state;
0576 }
0577 
0578 bool AmountEdit::isValid() const
0579 {
0580     return !(text().isEmpty());
0581 }
0582 
0583 MyMoneyMoney AmountEdit::value() const
0584 {
0585     Q_D(const AmountEdit);
0586     MyMoneyMoney money(d->m_valueText);
0587     return d->adjustToPrecision(AmountEdit::DisplayValue, money);
0588 }
0589 
0590 void AmountEdit::setValue(const MyMoneyMoney& amount)
0591 {
0592     Q_D(AmountEdit);
0593     if (d->sharesSet && !d->valueSet) {
0594         qWarning() << objectName() << "Call AmountEdit::setValue() before AmountEdit::setShares(). Fix source code!";
0595     }
0596     if (d->valueSet && (amount == d->m_value))
0597         return;
0598 
0599     d->valueSet = true;
0600     d->m_value = amount;
0601     const auto txt(amount.formatMoney(QString(), d->precision(AmountEdit::DisplayValue), false));
0602     d->setValueText(txt);
0603 
0604     if (!hasMultipleCurrencies()) {
0605         setShares(amount);
0606     }
0607 }
0608 
0609 MyMoneyMoney AmountEdit::shares() const
0610 {
0611     Q_D(const AmountEdit);
0612     MyMoneyMoney money(d->m_sharesText);
0613     return d->adjustToPrecision(AmountEdit::DisplayShares, money);
0614 }
0615 
0616 void AmountEdit::setShares(const MyMoneyMoney& amount)
0617 {
0618     Q_D(AmountEdit);
0619     d->sharesSet = !d->valueSet;
0620     d->m_shares = amount;
0621 
0622     const auto txt(amount.formatMoney(QString(), d->precision(AmountEdit::DisplayShares), false));
0623     d->setSharesText(txt);
0624 
0625     if (!d->m_shares.isZero()) {
0626         d->m_initialExchangeRate = d->m_value / d->m_shares;
0627     }
0628 }
0629 
0630 void AmountEdit::clear()
0631 {
0632     Q_D(AmountEdit);
0633     d->m_sharesText.clear();
0634     d->m_valueText.clear();
0635     QLineEdit::clear();
0636     d->m_previousText.clear();
0637 }
0638 
0639 void AmountEdit::theTextChanged(const QString & theText)
0640 {
0641     Q_D(AmountEdit);
0642 
0643     const auto initiallyValid = isValid();
0644     QLocale locale;
0645     QString dec = locale.groupSeparator();
0646     QString l_text = theText;
0647     QString nsign, psign;
0648     nsign = locale.negativeSign();
0649     psign = locale.positiveSign();
0650 
0651     auto i = 0;
0652     if (isEnabled()) {
0653         QValidator::State state =  validator()->validate(l_text, i);
0654         if (state == QValidator::Intermediate) {
0655             if (l_text.length() == 1) {
0656                 if (l_text != dec && l_text != nsign && l_text != psign)
0657                     state = QValidator::Invalid;
0658             }
0659         }
0660         if (state == QValidator::Invalid)
0661             QLineEdit::setText(d->m_previousText);
0662         else {
0663             d->m_previousText = l_text;
0664 
0665             if (!l_text.isEmpty()) {
0666                 // adjust value or shares depending on state
0667                 // by using the initialExchangeRate
0668                 if (d->m_state == AmountEdit::DisplayValue) {
0669                     d->m_valueText = l_text;
0670                     MyMoneyMoney amount(l_text);
0671                     d->adjustToPrecision(AmountEdit::DisplayValue, amount);
0672                     amount /= d->m_initialExchangeRate;
0673                     d->m_sharesText = amount.formatMoney(QString(), d->precision(AmountEdit::DisplayShares), false);
0674                 } else {
0675                     d->m_sharesText = l_text;
0676                     MyMoneyMoney amount(l_text);
0677                     d->adjustToPrecision(AmountEdit::DisplayShares, amount);
0678                     amount *= d->m_initialExchangeRate;
0679                     d->m_valueText = amount.formatMoney(QString(), d->precision(AmountEdit::DisplayValue), false);
0680                 }
0681             }
0682             Q_EMIT validatedTextChanged(text());
0683         }
0684     }
0685 
0686     if (initiallyValid != isValid()) {
0687         Q_EMIT validityChanged(isValid());
0688     }
0689 }
0690 
0691 void AmountEdit::slotCalculatorOpen()
0692 {
0693     Q_D(AmountEdit);
0694     d->calculatorOpen(0);
0695 }
0696 
0697 void AmountEdit::slotCalculatorClose()
0698 {
0699     Q_D(AmountEdit);
0700     if (d->m_calculator != 0) {
0701         d->m_calculatorFrame->hide();
0702     }
0703 }
0704 
0705 void AmountEdit::setText(const QString& txt)
0706 {
0707     Q_D(AmountEdit);
0708     if (!d->hasMultipleCurrencies()) {
0709         QLineEdit::setText(txt);
0710     } else {
0711         switch (d->m_state) {
0712         case MultiCurrencyEdit::DisplayShares:
0713             d->m_sharesText = txt;
0714             break;
0715         case MultiCurrencyEdit::DisplayValue:
0716             d->m_valueText = txt;
0717             break;
0718         }
0719         d->updateWidgets();
0720     }
0721 }
0722 
0723 void AmountEdit::slotCalculatorResult()
0724 {
0725     Q_D(AmountEdit);
0726     slotCalculatorClose();
0727     if (d->m_calculator != 0) {
0728         MyMoneyMoney amount(d->m_calculator->result());
0729         amount = d->adjustToPrecision(d->m_state, amount);
0730         setText(amount.formatMoney(QString(), d->precision(d->m_state), false));
0731     }
0732 }
0733 
0734 void AmountEdit::setCalculatorButtonVisible(const bool show)
0735 {
0736     Q_D(AmountEdit);
0737     if (d->m_calculatorButton->isVisible() != show) {
0738         d->m_calculatorButton->setVisible(show);
0739         d->m_actionIcons.setFlag(AmountEditPrivate::ShowCalculator, show);
0740         d->updateLineEditSize(d->m_currencyButton);
0741         Q_EMIT calculatorButtonVisibilityChanged(show);
0742     }
0743 }
0744 
0745 void AmountEdit::setAllowEmpty(bool allowed)
0746 {
0747     Q_D(AmountEdit);
0748     if (d->m_allowEmpty != allowed) {
0749         d->m_allowEmpty = allowed;
0750         Q_EMIT allowEmptyChanged(allowed);
0751     }
0752 }
0753 
0754 bool AmountEdit::isEmptyAllowed() const
0755 {
0756     Q_D(const AmountEdit);
0757     return d->m_allowEmpty;
0758 }
0759 
0760 bool AmountEdit::isCalculatorButtonVisible() const
0761 {
0762     Q_D(const AmountEdit);
0763     return d->m_calculatorButton->isVisible();
0764 }
0765 
0766 void AmountEdit::ensureFractionalPart()
0767 {
0768     Q_D(AmountEdit);
0769     QString s(text());
0770     d->ensureFractionalPart(s);
0771     // by setting the text only when it's different then the one that it is already there
0772     // we preserve the edit widget's state (like the selection for example) during a
0773     // call to ensureFractionalPart() that does not change anything
0774     if (s != text())
0775         QLineEdit::setText(s);
0776 }
0777 
0778 void AmountEdit::setCurrencySymbol(const QString& symbol, const QString& name)
0779 {
0780     Q_D(AmountEdit);
0781     d->setCurrencySymbol(symbol, name);
0782 }
0783 
0784 void AmountEdit::setDisplayState(AmountEdit::DisplayState state)
0785 {
0786     Q_D(AmountEdit);
0787     if (state != d->m_state) {
0788         d->m_state = state;
0789         d->updateWidgets();
0790         Q_EMIT displayStateChanged(state);
0791     }
0792 }
0793 
0794 void AmountEdit::setShowShares(bool show)
0795 {
0796     if (!show) {
0797         setShowValue();
0798         return;
0799     }
0800     setDisplayState(AmountEdit::DisplayShares);
0801 }
0802 
0803 void AmountEdit::setShowValue(bool show)
0804 {
0805     if (!show) {
0806         setShowShares();
0807         return;
0808     }
0809     setDisplayState(AmountEdit::DisplayValue);
0810 }
0811 
0812 void AmountEdit::setCommodity(const MyMoneySecurity& commodity)
0813 {
0814     Q_D(AmountEdit);
0815     d->m_sharesCommodity = commodity;
0816     d->m_valueCommodity = commodity;
0817     d->updateWidgets();
0818 }
0819 
0820 void AmountEdit::setSharesCommodity(const MyMoneySecurity& commodity)
0821 {
0822     Q_D(AmountEdit);
0823     if (d->m_sharesCommodity.id() != commodity.id()) {
0824         d->m_sharesCommodity = commodity;
0825         d->updateWidgets();
0826     }
0827 }
0828 
0829 MyMoneySecurity AmountEdit::sharesCommodity() const
0830 {
0831     Q_D(const AmountEdit);
0832     return d->m_sharesCommodity;
0833 }
0834 
0835 void AmountEdit::setValueCommodity(const MyMoneySecurity& commodity)
0836 {
0837     Q_D(AmountEdit);
0838     if (d->m_valueCommodity.id() != commodity.id()) {
0839         d->m_valueCommodity = commodity;
0840         d->updateWidgets();
0841     }
0842 }
0843 
0844 MyMoneySecurity AmountEdit::valueCommodity() const
0845 {
0846     Q_D(const AmountEdit);
0847     return d->m_valueCommodity;
0848 }
0849 
0850 void AmountEdit::setInitialExchangeRate(const MyMoneyMoney& price)
0851 {
0852     Q_D(AmountEdit);
0853     d->m_initialExchangeRate = price;
0854 }
0855 
0856 MyMoneyMoney AmountEdit::initialExchangeRate() const
0857 {
0858     Q_D(const AmountEdit);
0859     return d->m_initialExchangeRate;
0860 }
0861 
0862 QWidget* AmountEdit::widget()
0863 {
0864     return this;
0865 }
0866 
0867 bool AmountEdit::hasMultipleCurrencies() const
0868 {
0869     Q_D(const AmountEdit);
0870     return d->hasMultipleCurrencies();
0871 }