[Libreoffice-commits] core.git: include/vcl svx/source toolkit/source vcl/inc vcl/source

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Fri Jul 3 16:09:37 UTC 2020


 include/vcl/fmtfield.hxx           |   25 --
 include/vcl/formatter.hxx          |   65 +++++
 svx/source/fmcomp/gridcell.cxx     |   41 +--
 toolkit/source/awt/vclxwindows.cxx |  194 ++++++++---------
 vcl/inc/salvtables.hxx             |    2 
 vcl/source/app/salvtables.cxx      |   81 +++----
 vcl/source/app/weldutils.cxx       |    2 
 vcl/source/control/fmtfield.cxx    |  411 +++++++++++++++++++------------------
 vcl/source/uitest/uiobject.cxx     |    2 
 vcl/source/window/builder.cxx      |   11 
 10 files changed, 452 insertions(+), 382 deletions(-)

New commits:
commit 97ccd327c66660c9f7c9e625e3c5469b2ce42622
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Thu Jul 2 16:46:46 2020 +0100
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Fri Jul 3 18:08:47 2020 +0200

    change FormattedField so it doesn't inherit from Formatter but provides one
    
    Change-Id: I728380fb4e2ed914c4b96c0915075af097846c55
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97825
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/include/vcl/fmtfield.hxx b/include/vcl/fmtfield.hxx
index 08fc7d9174f8..26715e687218 100644
--- a/include/vcl/fmtfield.hxx
+++ b/include/vcl/fmtfield.hxx
@@ -24,7 +24,6 @@
 #include <vcl/spinfld.hxx>
 
 class VCL_DLLPUBLIC FormattedField : public SpinField
-                                   , public Formatter
 {
 public:
     FormattedField(vcl::Window* pParent, WinBits nStyle);
@@ -48,15 +47,11 @@ public:
 
     virtual FactoryFunction GetUITestFactory() const override;
 
-    // Formatter overrides
-    virtual Selection GetEntrySelection() const override;
-    virtual OUString GetEntryText() const override;
-    virtual void SetEntryText(const OUString& rText, const Selection& rSel) override;
-    virtual void SetEntryTextColor(const Color* pColor) override;
-    virtual SelectionOptions GetEntrySelectionOptions() const override;
-    virtual void FieldModified() override;
+    Formatter* GetFormatter();
 
 protected:
+    std::unique_ptr<Formatter> m_xFormatter;
+
     virtual bool EventNotify(NotifyEvent& rNEvt) override;
     virtual void Modify() override;
 
@@ -70,16 +65,14 @@ public:
 
     virtual ~DoubleNumericField() override;
 
-private:
-    virtual bool CheckText(const OUString& sText) const override;
-
-    virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override;
+    validation::NumberValidator& GetNumberValidator() { return *m_pNumberValidator; }
     void ResetConformanceTester();
 
+private:
+
     std::unique_ptr<validation::NumberValidator> m_pNumberValidator;
 };
 
-
 class UNLESS_MERGELIBS(VCL_DLLPUBLIC) DoubleCurrencyField final : public FormattedField
 {
 public:
@@ -91,14 +84,10 @@ public:
     bool        getPrependCurrSym() const { return m_bPrependCurrSym; }
     void        setPrependCurrSym(bool _bPrepend);
 
-private:
-    virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override;
-
     void UpdateCurrencyFormat();
-
+private:
     OUString   m_sCurrencySymbol;
     bool       m_bPrependCurrSym;
-    bool       m_bChangingFormat;
 };
 
 #endif // INCLUDED_VCL_FMTFIELD_HXX
diff --git a/include/vcl/formatter.hxx b/include/vcl/formatter.hxx
index 8f02969635e9..f54d59ca4600 100644
--- a/include/vcl/formatter.hxx
+++ b/include/vcl/formatter.hxx
@@ -21,11 +21,53 @@
 
 #include <config_options.h>
 #include <vcl/settings.hxx>
+#include <map>
 #include <memory>
 
 class SvNumberFormatter;
 
-namespace validation { class NumberValidator; }
+namespace validation
+{
+    // the states of our automat.
+    enum State
+    {
+        START,              // at the very start of the string
+        NUM_START,          // the very start of the number
+
+        DIGIT_PRE_COMMA,    // some pre-comma digits are read, perhaps including some thousand separators
+
+        DIGIT_POST_COMMA,   // reading digits after the comma
+        EXPONENT_START,     // at the very start of the exponent value
+                            //    (means: not including the "e" which denotes the exponent)
+        EXPONENT_DIGIT,     // currently reading the digits of the exponent
+
+        END                 // reached the end of the string
+    };
+
+    // a row in the transition table (means the set of states to be reached from a given state)
+    typedef ::std::map< sal_Unicode, State >        StateTransitions;
+
+    // a single transition
+    typedef StateTransitions::value_type            Transition;
+
+    // the complete transition table
+    typedef ::std::map< State, StateTransitions >   TransitionTable;
+
+    // the validator class
+    class NumberValidator
+    {
+    private:
+        TransitionTable     m_aTransitions;
+
+    public:
+        NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep );
+
+        bool isValidNumericFragment( const OUString& _rText );
+
+    private:
+        bool implValidateNormalized( const OUString& _rText );
+    };
+}
 
 enum class FORMAT_CHANGE_TYPE
 {
@@ -139,10 +181,14 @@ public:
     // If the current String is invalid, GetValue() returns this value
     double  GetDefaultValue() const             { return m_dDefaultValue; }
 
+    void SetLastSelection(const Selection& rSelection) { m_aLastSelection = rSelection; }
+
     // Settings for the format
     sal_uLong   GetFormatKey() const                { return m_nFormatKey; }
     void    SetFormatKey(sal_uLong nFormatKey);
 
+    SvNumberFormatter*  GetOrCreateFormatter() const { return m_pFormatter ? m_pFormatter : const_cast<Formatter*>(this)->CreateFormatter(); }
+
     SvNumberFormatter*  GetFormatter() const    { return m_pFormatter; }
     void    SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat = true);
     // If bResetFormat is sal_False, the old format is tried to be kept. (expensive, if it is no default format, available in all formatters)
@@ -153,6 +199,9 @@ public:
         // the is no check if the current format is numeric, so be cautious when calling these functions
 
     void    DisableRemainderFactor();
+    bool    GetDisableRemainderFactor() const { return m_bDisableRemainderFactor; }
+
+    void    SetWrapOnLimits(bool bWrapOnLimits) { m_bWrapOnLimits = bWrapOnLimits; }
 
     sal_uInt16  GetDecimalDigits() const;
     void    SetDecimalDigits(sal_uInt16 _nPrecision);
@@ -234,15 +283,18 @@ public:
     void    UseInputStringForFormatting();
     bool    IsUsingInputStringForFormatting() const { return m_bUseInputStringForFormatting;}
 
-protected:
-    void impl_Modify(bool makeValueDirty = true);
+    void    Modify(bool makeValueDirty = true);
 
-    // Override CheckText for input-time checks
-    virtual bool CheckText(const OUString&) const { return true; }
+    void    EntryLostFocus();
 
     // any aspect of the current format has changed
     virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat);
 
+protected:
+
+    // Override CheckText for input-time checks
+    virtual bool CheckText(const OUString&) const { return true; }
+
     void ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel);
     void ImplSetValue(double dValue, bool bForce);
     bool ImplGetValue(double& dNewVal);
@@ -250,10 +302,7 @@ protected:
     void ImplSetFormatKey(sal_uLong nFormatKey);
         // SetFormatKey without FormatChanged notification
 
-    void EntryLostFocus();
-
     SvNumberFormatter*  CreateFormatter() { SetFormatter(StandardFormatter()); return m_pFormatter; }
-    SvNumberFormatter*  ImplGetFormatter() const { return m_pFormatter ? m_pFormatter : const_cast<Formatter*>(this)->CreateFormatter(); }
 
     void ReFormat();
 };
diff --git a/svx/source/fmcomp/gridcell.cxx b/svx/source/fmcomp/gridcell.cxx
index dfcec5ec6d2b..430fd9cbc8e1 100644
--- a/svx/source/fmcomp/gridcell.cxx
+++ b/svx/source/fmcomp/gridcell.cxx
@@ -1905,15 +1905,16 @@ void DbNumericField::implAdjustGenericFieldSetting( const Reference< XPropertySe
     sal_Int16   nScale      = getINT16( _rxModel->getPropertyValue( FM_PROP_DECIMAL_ACCURACY ) );
     bool    bThousand   = getBOOL( _rxModel->getPropertyValue( FM_PROP_SHOWTHOUSANDSEP ) );
 
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetMinValue(nMin);
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetMaxValue(nMax);
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetSpinSize(nStep);
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetStrictFormat(bStrict);
-
-    static_cast< DoubleNumericField* >( m_pPainter.get() )->SetMinValue(nMin);
-    static_cast< DoubleNumericField* >( m_pPainter.get() )->SetMaxValue(nMax);
-    static_cast< DoubleNumericField* >( m_pPainter.get() )->SetStrictFormat(bStrict);
+    Formatter* pEditFormatter = static_cast<DoubleNumericField*>(m_pWindow.get())->GetFormatter();
+    pEditFormatter->SetMinValue(nMin);
+    pEditFormatter->SetMaxValue(nMax);
+    pEditFormatter->SetSpinSize(nStep);
+    pEditFormatter->SetStrictFormat(bStrict);
 
+    Formatter* pPaintFormatter = static_cast<DoubleNumericField*>(m_pPainter.get())->GetFormatter();
+    pPaintFormatter->SetMinValue(nMin);
+    pPaintFormatter->SetMaxValue(nMax);
+    pPaintFormatter->SetStrictFormat(bStrict);
 
     // give a formatter to the field and the painter;
     // test first if I can get from the service behind a connection
@@ -1931,18 +1932,18 @@ void DbNumericField::implAdjustGenericFieldSetting( const Reference< XPropertySe
     }
     if ( nullptr == pFormatterUsed )
     {   // the cursor didn't lead to success -> standard
-        pFormatterUsed = static_cast< DoubleNumericField* >( m_pWindow.get() )->StandardFormatter();
+        pFormatterUsed = pEditFormatter->StandardFormatter();
         DBG_ASSERT( pFormatterUsed != nullptr, "DbNumericField::implAdjustGenericFieldSetting: no standard formatter given by the numeric field !" );
     }
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetFormatter( pFormatterUsed );
-    static_cast< DoubleNumericField* >( m_pPainter.get() )->SetFormatter( pFormatterUsed );
+    pEditFormatter->SetFormatter( pFormatterUsed );
+    pPaintFormatter->SetFormatter( pFormatterUsed );
 
     // and then generate a format which has the desired length after the decimal point, etc.
     LanguageType aAppLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType();
     OUString sFormatString = pFormatterUsed->GenerateFormat(0, aAppLanguage, bThousand, false, nScale);
 
-    static_cast< DoubleNumericField* >( m_pWindow.get() )->SetFormat( sFormatString, aAppLanguage );
-    static_cast< DoubleNumericField* >( m_pPainter.get() )->SetFormat( sFormatString, aAppLanguage );
+    pEditFormatter->SetFormat( sFormatString, aAppLanguage );
+    pPaintFormatter->SetFormat( sFormatString, aAppLanguage );
 }
 
 
@@ -1965,7 +1966,7 @@ namespace
                 double fValue = _rControl.GetValue( _rxField, _rxFormatter );
                 if ( !_rxField->wasNull() )
                 {
-                    _rField.SetValue( fValue );
+                    _rField.GetFormatter()->SetValue( fValue );
                     sValue = _rField.GetText();
                 }
             }
@@ -1978,31 +1979,30 @@ namespace
     }
 }
 
-
 OUString DbNumericField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, Color** /*ppColor*/)
 {
     return lcl_setFormattedNumeric_nothrow(dynamic_cast<DoubleNumericField&>(*m_pPainter), *this, _rxField, _rxFormatter);
 }
 
-
 void DbNumericField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter)
 {
     lcl_setFormattedNumeric_nothrow(dynamic_cast<DoubleNumericField&>(*m_pWindow), *this, _rxField, _rxFormatter);
 }
 
-
 void DbNumericField::updateFromModel( Reference< XPropertySet > _rxModel )
 {
     OSL_ENSURE( _rxModel.is() && m_pWindow, "DbNumericField::updateFromModel: invalid call!" );
 
     double dValue = 0;
     if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue )
-        static_cast< DoubleNumericField* >( m_pWindow.get() )->SetValue( dValue );
+    {
+        Formatter* pFormatter = static_cast<DoubleNumericField*>(m_pWindow.get())->GetFormatter();
+        pFormatter->SetValue(dValue);
+    }
     else
         m_pWindow->SetText( OUString() );
 }
 
-
 bool DbNumericField::commitControl()
 {
     OUString aText( m_pWindow->GetText());
@@ -2010,7 +2010,8 @@ bool DbNumericField::commitControl()
 
     if (!aText.isEmpty())   // not empty
     {
-        double fValue = static_cast<DoubleNumericField*>(m_pWindow.get())->GetValue();
+        Formatter* pFormatter = static_cast<DoubleNumericField*>(m_pWindow.get())->GetFormatter();
+        double fValue = pFormatter->GetValue();
         aVal <<= fValue;
     }
     m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal);
diff --git a/toolkit/source/awt/vclxwindows.cxx b/toolkit/source/awt/vclxwindows.cxx
index 719ee7e56056..151843191341 100644
--- a/toolkit/source/awt/vclxwindows.cxx
+++ b/toolkit/source/awt/vclxwindows.cxx
@@ -7186,10 +7186,9 @@ void SVTXFormattedField::SetWindow( const VclPtr< vcl::Window > &_pWindow )
 {
     VCLXSpinField::SetWindow(_pWindow);
     if (GetAs< FormattedField >())
-        GetAs< FormattedField >()->SetAutoColor(true);
+        GetAs< FormattedField >()->GetFormatter()->SetAutoColor(true);
 }
 
-
 void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::uno::Any& Value)
 {
     SolarMutexGuard aGuard;
@@ -7197,6 +7196,7 @@ void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::u
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
     {
+        Formatter* pFormatter = pField->GetFormatter();
         sal_uInt16 nPropType = GetPropertyId( PropertyName );
         switch (nPropType)
         {
@@ -7204,7 +7204,7 @@ void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::u
             {
                 bool bEnable( true );
                 if ( Value >>= bEnable )
-                    pField->EnableNotANumber( !bEnable );
+                    pFormatter->EnableNotANumber( !bEnable );
             }
             break;
 
@@ -7276,12 +7276,12 @@ void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::u
             {
                 double d = 0.0;
                 if ( Value >>= d )
-                     pField->SetSpinSize( d );
+                     pFormatter->SetSpinSize( d );
                 else
                 {
                     sal_Int32 n = 0;
                     if ( Value >>= n )
-                         pField->SetSpinSize( n );
+                         pFormatter->SetSpinSize( n );
                 }
             }
             break;
@@ -7289,14 +7289,14 @@ void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::u
             {
                 sal_Int32 n = 0;
                 if ( Value >>= n )
-                     pField->SetDecimalDigits( static_cast<sal_uInt16>(n) );
+                     pFormatter->SetDecimalDigits( static_cast<sal_uInt16>(n) );
             }
             break;
             case BASEPROPERTY_NUMSHOWTHOUSANDSEP:
             {
                     bool b;
                     if ( Value >>= b )
-                     pField->SetThousandsSep( b );
+                     pFormatter->SetThousandsSep( b );
             }
             break;
 
@@ -7307,14 +7307,13 @@ void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::u
         if (BASEPROPERTY_TEXTCOLOR == nPropType)
         {   // after setting a new text color, think again about the AutoColor flag of the control
             // 17.05.2001 - 86859 - frank.schoenheit at germany.sun.com
-            pField->SetAutoColor(!Value.hasValue());
+            pFormatter->SetAutoColor(!Value.hasValue());
         }
     }
     else
         VCLXSpinField::setProperty( PropertyName, Value );
 }
 
-
 css::uno::Any SVTXFormattedField::getProperty( const OUString& PropertyName )
 {
     SolarMutexGuard aGuard;
@@ -7324,6 +7323,7 @@ css::uno::Any SVTXFormattedField::getProperty( const OUString& PropertyName )
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
     {
+        Formatter* pFormatter = pField->GetFormatter();
         sal_uInt16 nPropType = GetPropertyId( PropertyName );
         switch (nPropType)
         {
@@ -7351,11 +7351,11 @@ css::uno::Any SVTXFormattedField::getProperty( const OUString& PropertyName )
                 break;
 
             case BASEPROPERTY_VALUESTEP_DOUBLE:
-                aReturn <<= pField->GetSpinSize();
+                aReturn <<= pFormatter->GetSpinSize();
                 break;
 
             case BASEPROPERTY_DECIMALACCURACY:
-                aReturn <<= pField->GetDecimalDigits();
+                aReturn <<= pFormatter->GetDecimalDigits();
                 break;
 
             case BASEPROPERTY_FORMATSSUPPLIER:
@@ -7390,10 +7390,11 @@ css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rVa
     if (!pField)
         return aReturn;
 
+    Formatter* pFieldFormatter = pField->GetFormatter();
     switch (rValue.getValueType().getTypeClass())
     {
         case css::uno::TypeClass_DOUBLE:
-            if (pField->TreatingAsNumber())
+            if (pFieldFormatter->TreatingAsNumber())
             {
                 double d = 0.0;
                 rValue >>= d;
@@ -7401,9 +7402,9 @@ css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rVa
             }
             else
             {
-                SvNumberFormatter* pFormatter = pField->GetFormatter();
+                SvNumberFormatter* pFormatter = pFieldFormatter->GetFormatter();
                 if (!pFormatter)
-                    pFormatter = pField->StandardFormatter();
+                    pFormatter = pFieldFormatter->StandardFormatter();
                     // should never fail
 
                 Color* pDum;
@@ -7418,11 +7419,11 @@ css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rVa
         {
             OUString aStr;
             rValue >>= aStr;
-            if (pField->TreatingAsNumber())
+            if (pFieldFormatter->TreatingAsNumber())
             {
-                SvNumberFormatter* pFormatter = pField->GetFormatter();
+                SvNumberFormatter* pFormatter = pFieldFormatter->GetFormatter();
                 if (!pFormatter)
-                    pFormatter = pField->StandardFormatter();
+                    pFormatter = pFieldFormatter->StandardFormatter();
 
                 double dVal;
                 sal_uInt32 nTestFormat(0);
@@ -7441,13 +7442,13 @@ css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rVa
     return aReturn;
 }
 
-
 void SVTXFormattedField::SetMinValue(const css::uno::Any& rValue)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if (!pField)
         return;
 
+    Formatter* pFormatter = pField->GetFormatter();
     switch (rValue.getValueType().getTypeClass())
 
     {
@@ -7455,9 +7456,9 @@ void SVTXFormattedField::SetMinValue(const css::uno::Any& rValue)
         {
             double d = 0.0;
             rValue >>= d;
-            pField->SetMinValue(d);
-        }
+            pFormatter->SetMinValue(d);
             break;
+        }
         default:
             DBG_ASSERT(rValue.getValueType().getTypeClass() == css::uno::TypeClass_VOID, "SVTXFormattedField::SetMinValue : invalid argument (an exception will be thrown) !");
             if ( rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID )
@@ -7465,20 +7466,22 @@ void SVTXFormattedField::SetMinValue(const css::uno::Any& rValue)
             {
                 throw css::lang::IllegalArgumentException();
             }
-            pField->ClearMinValue();
+            pFormatter->ClearMinValue();
             break;
     }
 }
 
-
 css::uno::Any SVTXFormattedField::GetMinValue() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    if (!pField || !pField->HasMinValue())
+    if (!pField)
+        return css::uno::Any();
+    Formatter* pFormatter = pField->GetFormatter();
+    if (!pFormatter->HasMinValue())
         return css::uno::Any();
 
     css::uno::Any aReturn;
-    aReturn <<= pField->GetMinValue();
+    aReturn <<= pFormatter->GetMinValue();
     return aReturn;
 }
 
@@ -7489,40 +7492,41 @@ void SVTXFormattedField::SetMaxValue(const css::uno::Any& rValue)
     if (!pField)
         return;
 
+    Formatter* pFormatter = pField->GetFormatter();
     switch (rValue.getValueType().getTypeClass())
-
     {
         case css::uno::TypeClass_DOUBLE:
         {
             double d = 0.0;
             rValue >>= d;
-            pField->SetMaxValue(d);
-        }
+            pFormatter->SetMaxValue(d);
             break;
+        }
         default:
             if (rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID)
 
             {
                 throw css::lang::IllegalArgumentException();
             }
-            pField->ClearMaxValue();
+            pFormatter->ClearMaxValue();
             break;
     }
 }
 
-
 css::uno::Any SVTXFormattedField::GetMaxValue() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    if (!pField || !pField->HasMaxValue())
+    if (!pField)
+        return css::uno::Any();
+    Formatter* pFormatter = pField->GetFormatter();
+    if (!pFormatter->HasMaxValue())
         return css::uno::Any();
 
     css::uno::Any aReturn;
-    aReturn <<= pField->GetMaxValue();
+    aReturn <<= pFormatter->GetMaxValue();
     return aReturn;
 }
 
-
 void SVTXFormattedField::SetDefaultValue(const css::uno::Any& rValue)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
@@ -7531,86 +7535,85 @@ void SVTXFormattedField::SetDefaultValue(const css::uno::Any& rValue)
 
     css::uno::Any aConverted = convertEffectiveValue(rValue);
 
+    Formatter* pFormatter = pField->GetFormatter();
     switch (aConverted.getValueType().getTypeClass())
-
     {
         case css::uno::TypeClass_DOUBLE:
         {
             double d = 0.0;
             aConverted >>= d;
-            pField->SetDefaultValue(d);
+            pFormatter->SetDefaultValue(d);
         }
         break;
         case css::uno::TypeClass_STRING:
         {
             OUString aStr;
             aConverted >>= aStr;
-            pField->SetDefaultText( aStr );
+            pFormatter->SetDefaultText( aStr );
         }
         break;
         default:
-            pField->EnableEmptyField(true);
+            pFormatter->EnableEmptyField(true);
                 // only void accepted
             break;
     }
 }
 
-
 css::uno::Any SVTXFormattedField::GetDefaultValue() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    if (!pField || pField->IsEmptyFieldEnabled())
+    if (!pField)
+        return css::uno::Any();
+    Formatter* pFormatter = pField->GetFormatter();
+    if (pFormatter->IsEmptyFieldEnabled())
         return css::uno::Any();
 
     css::uno::Any aReturn;
-    if (pField->TreatingAsNumber())
-        aReturn <<= pField->GetDefaultValue();
+    if (pFormatter->TreatingAsNumber())
+        aReturn <<= pFormatter->GetDefaultValue();
     else
-        aReturn <<= pField->GetDefaultText();
+        aReturn <<= pFormatter->GetDefaultText();
     return aReturn;
 }
 
-
 bool SVTXFormattedField::GetTreatAsNumber() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if (pField)
-        return pField->TreatingAsNumber();
+        return pField->GetFormatter()->TreatingAsNumber();
 
     return true;
 }
 
-
 void SVTXFormattedField::SetTreatAsNumber(bool bSet)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if (pField)
-        pField->TreatAsNumber(bSet);
+        pField->GetFormatter()->TreatAsNumber(bSet);
 }
 
-
 css::uno::Any SVTXFormattedField::GetValue() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if (!pField)
         return css::uno::Any();
 
+    Formatter* pFormatter = pField->GetFormatter();
     css::uno::Any aReturn;
-    if (!pField->TreatingAsNumber())
+    if (!pFormatter->TreatingAsNumber())
     {
-        OUString sText = pField->GetTextValue();
+        OUString sText = pFormatter->GetTextValue();
         aReturn <<= sText;
     }
     else
     {
         if (!pField->GetText().isEmpty())    // empty is returned as void by default
-            aReturn <<= pField->GetValue();
+            aReturn <<= pFormatter->GetValue();
     }
 
     return aReturn;
 }
 
-
 void SVTXFormattedField::SetValue(const css::uno::Any& rValue)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
@@ -7623,11 +7626,12 @@ void SVTXFormattedField::SetValue(const css::uno::Any& rValue)
     }
     else
     {
+        Formatter* pFormatter = pField->GetFormatter();
         if (rValue.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE )
         {
             double d = 0.0;
             rValue >>= d;
-            pField->SetValue(d);
+            pFormatter->SetValue(d);
         }
         else
         {
@@ -7635,10 +7639,10 @@ void SVTXFormattedField::SetValue(const css::uno::Any& rValue)
 
             OUString sText;
             rValue >>= sText;
-            if (!pField->TreatingAsNumber())
-                pField->SetTextFormatted(sText);
+            if (!pFormatter->TreatingAsNumber())
+                pFormatter->SetTextFormatted(sText);
             else
-                pField->SetTextValue(sText);
+                pFormatter->SetTextValue(sText);
         }
     }
 //  NotifyTextListeners();
@@ -7648,13 +7652,14 @@ void SVTXFormattedField::SetValue(const css::uno::Any& rValue)
 void SVTXFormattedField::setFormatsSupplier(const css::uno::Reference< css::util::XNumberFormatsSupplier > & xSupplier)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
+    Formatter* pFormatter = pField ? pField->GetFormatter() : nullptr;
 
     SvNumberFormatsSupplierObj* pNew = nullptr;
     if (!xSupplier.is())
     {
         if (pField)
         {
-            pNew = new SvNumberFormatsSupplierObj(pField->StandardFormatter());
+            pNew = new SvNumberFormatsSupplierObj(pFormatter->StandardFormatter());
             bIsStandardSupplier = true;
         }
     }
@@ -7673,32 +7678,31 @@ void SVTXFormattedField::setFormatsSupplier(const css::uno::Reference< css::util
 
     // save the actual value
     css::uno::Any aCurrent = GetValue();
-    pField->SetFormatter(m_xCurrentSupplier->GetNumberFormatter(), false);
+    pFormatter->SetFormatter(m_xCurrentSupplier->GetNumberFormatter(), false);
     if (nKeyToSetDelayed != -1)
     {
-        pField->SetFormatKey(nKeyToSetDelayed);
+        pFormatter->SetFormatKey(nKeyToSetDelayed);
         nKeyToSetDelayed = -1;
     }
     SetValue(aCurrent);
     NotifyTextListeners();
 }
 
-
 sal_Int32 SVTXFormattedField::getFormatKey() const
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetFormatKey() : 0;
+    return pField ? pField->GetFormatter()->GetFormatKey() : 0;
 }
 
-
 void SVTXFormattedField::setFormatKey(sal_Int32 nKey)
 {
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if (!pField)
         return;
 
-    if (pField->GetFormatter())
-        pField->SetFormatKey(nKey);
+    Formatter* pFormatter = pField->GetFormatter();
+    if (pFormatter->GetFormatter())
+        pFormatter->SetFormatKey(nKey);
     else
     {
         // probably I am in a block, in which first the key and next the formatter will be set,
@@ -7709,7 +7713,6 @@ void SVTXFormattedField::setFormatKey(sal_Int32 nKey)
     NotifyTextListeners();
 }
 
-
 void SVTXFormattedField::NotifyTextListeners()
 {
     if ( GetTextListeners().getLength() )
@@ -7783,7 +7786,7 @@ void SVTXCurrencyField::setValue( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetValue( Value );
+        pField->GetFormatter()->SetValue( Value );
 }
 
 double SVTXCurrencyField::getValue()
@@ -7791,7 +7794,7 @@ double SVTXCurrencyField::getValue()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetValue() : 0;
+    return pField ? pField->GetFormatter()->GetValue() : 0;
 }
 
 void SVTXCurrencyField::setMin( double Value )
@@ -7800,7 +7803,7 @@ void SVTXCurrencyField::setMin( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetMinValue( Value );
+        pField->GetFormatter()->SetMinValue( Value );
 }
 
 double SVTXCurrencyField::getMin()
@@ -7808,7 +7811,7 @@ double SVTXCurrencyField::getMin()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetMinValue() : 0;
+    return pField ? pField->GetFormatter()->GetMinValue() : 0;
 }
 
 void SVTXCurrencyField::setMax( double Value )
@@ -7817,7 +7820,7 @@ void SVTXCurrencyField::setMax( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetMaxValue( Value );
+        pField->GetFormatter()->SetMaxValue( Value );
 }
 
 double SVTXCurrencyField::getMax()
@@ -7825,7 +7828,7 @@ double SVTXCurrencyField::getMax()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetMaxValue() : 0;
+    return pField ? pField->GetFormatter()->GetMaxValue() : 0;
 }
 
 void SVTXCurrencyField::setFirst( double Value )
@@ -7834,7 +7837,7 @@ void SVTXCurrencyField::setFirst( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinFirst( Value );
+        pField->GetFormatter()->SetSpinFirst( Value );
 }
 
 double SVTXCurrencyField::getFirst()
@@ -7842,7 +7845,7 @@ double SVTXCurrencyField::getFirst()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinFirst() : 0;
+    return pField ? pField->GetFormatter()->GetSpinFirst() : 0;
 }
 
 void SVTXCurrencyField::setLast( double Value )
@@ -7851,7 +7854,7 @@ void SVTXCurrencyField::setLast( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinLast( Value );
+        pField->GetFormatter()->SetSpinLast( Value );
 }
 
 double SVTXCurrencyField::getLast()
@@ -7859,7 +7862,7 @@ double SVTXCurrencyField::getLast()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinLast() : 0;
+    return pField ? pField->GetFormatter()->GetSpinLast() : 0;
 }
 
 void SVTXCurrencyField::setSpinSize( double Value )
@@ -7868,7 +7871,7 @@ void SVTXCurrencyField::setSpinSize( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinSize( Value );
+        pField->GetFormatter()->SetSpinSize( Value );
 }
 
 double SVTXCurrencyField::getSpinSize()
@@ -7876,7 +7879,7 @@ double SVTXCurrencyField::getSpinSize()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinSize() : 0;
+    return pField ? pField->GetFormatter()->GetSpinSize() : 0;
 }
 
 void SVTXCurrencyField::setDecimalDigits( sal_Int16 Value )
@@ -7885,7 +7888,7 @@ void SVTXCurrencyField::setDecimalDigits( sal_Int16 Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetDecimalDigits( Value );
+        pField->GetFormatter()->SetDecimalDigits( Value );
 }
 
 sal_Int16 SVTXCurrencyField::getDecimalDigits()
@@ -7893,7 +7896,7 @@ sal_Int16 SVTXCurrencyField::getDecimalDigits()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetDecimalDigits() : 0;
+    return pField ? pField->GetFormatter()->GetDecimalDigits() : 0;
 }
 
 void SVTXCurrencyField::setStrictFormat( sal_Bool bStrict )
@@ -7902,7 +7905,7 @@ void SVTXCurrencyField::setStrictFormat( sal_Bool bStrict )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetStrictFormat( bStrict );
+        pField->GetFormatter()->SetStrictFormat( bStrict );
 }
 
 sal_Bool SVTXCurrencyField::isStrictFormat()
@@ -7910,7 +7913,7 @@ sal_Bool SVTXCurrencyField::isStrictFormat()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField && pField->IsStrictFormat();
+    return pField && pField->GetFormatter()->IsStrictFormat();
 }
 
 void SVTXCurrencyField::setProperty( const OUString& PropertyName, const css::uno::Any& Value)
@@ -8013,14 +8016,13 @@ css::uno::Sequence< css::uno::Type > SVTXNumericField::getTypes()
     return aTypeList.getTypes();
 }
 
-
 void SVTXNumericField::setValue( double Value )
 {
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetValue( Value );
+        pField->GetFormatter()->SetValue( Value );
 }
 
 double SVTXNumericField::getValue()
@@ -8028,7 +8030,7 @@ double SVTXNumericField::getValue()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetValue() : 0;
+    return pField ? pField->GetFormatter()->GetValue() : 0;
 }
 
 void SVTXNumericField::setMin( double Value )
@@ -8037,7 +8039,7 @@ void SVTXNumericField::setMin( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetMinValue( Value );
+        pField->GetFormatter()->SetMinValue( Value );
 }
 
 double SVTXNumericField::getMin()
@@ -8045,7 +8047,7 @@ double SVTXNumericField::getMin()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetMinValue() : 0;
+    return pField ? pField->GetFormatter()->GetMinValue() : 0;
 }
 
 void SVTXNumericField::setMax( double Value )
@@ -8054,7 +8056,7 @@ void SVTXNumericField::setMax( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetMaxValue( Value );
+        pField->GetFormatter()->SetMaxValue( Value );
 }
 
 double SVTXNumericField::getMax()
@@ -8062,7 +8064,7 @@ double SVTXNumericField::getMax()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetMaxValue() : 0;
+    return pField ? pField->GetFormatter()->GetMaxValue() : 0;
 }
 
 void SVTXNumericField::setFirst( double Value )
@@ -8071,7 +8073,7 @@ void SVTXNumericField::setFirst( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinFirst( Value );
+        pField->GetFormatter()->SetSpinFirst( Value );
 }
 
 double SVTXNumericField::getFirst()
@@ -8079,7 +8081,7 @@ double SVTXNumericField::getFirst()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinFirst() : 0;
+    return pField ? pField->GetFormatter()->GetSpinFirst() : 0;
 }
 
 void SVTXNumericField::setLast( double Value )
@@ -8088,7 +8090,7 @@ void SVTXNumericField::setLast( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinLast( Value );
+        pField->GetFormatter()->SetSpinLast( Value );
 }
 
 double SVTXNumericField::getLast()
@@ -8096,7 +8098,7 @@ double SVTXNumericField::getLast()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinLast() : 0;
+    return pField ? pField->GetFormatter()->GetSpinLast() : 0;
 }
 
 void SVTXNumericField::setSpinSize( double Value )
@@ -8105,7 +8107,7 @@ void SVTXNumericField::setSpinSize( double Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetSpinSize( Value );
+        pField->GetFormatter()->SetSpinSize( Value );
 }
 
 double SVTXNumericField::getSpinSize()
@@ -8113,7 +8115,7 @@ double SVTXNumericField::getSpinSize()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetSpinSize() : 0;
+    return pField ? pField->GetFormatter()->GetSpinSize() : 0;
 }
 
 void SVTXNumericField::setDecimalDigits( sal_Int16 Value )
@@ -8122,7 +8124,7 @@ void SVTXNumericField::setDecimalDigits( sal_Int16 Value )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetDecimalDigits( Value );
+        pField->GetFormatter()->SetDecimalDigits( Value );
 }
 
 sal_Int16 SVTXNumericField::getDecimalDigits()
@@ -8130,7 +8132,7 @@ sal_Int16 SVTXNumericField::getDecimalDigits()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField ? pField->GetDecimalDigits() : 0;
+    return pField ? pField->GetFormatter()->GetDecimalDigits() : 0;
 }
 
 void SVTXNumericField::setStrictFormat( sal_Bool bStrict )
@@ -8139,7 +8141,7 @@ void SVTXNumericField::setStrictFormat( sal_Bool bStrict )
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
     if ( pField )
-        pField->SetStrictFormat( bStrict );
+        pField->GetFormatter()->SetStrictFormat( bStrict );
 }
 
 sal_Bool SVTXNumericField::isStrictFormat()
@@ -8147,7 +8149,7 @@ sal_Bool SVTXNumericField::isStrictFormat()
     SolarMutexGuard aGuard;
 
     VclPtr<FormattedField> pField = GetAs< FormattedField >();
-    return pField && pField->IsStrictFormat();
+    return pField && pField->GetFormatter()->IsStrictFormat();
 }
 
 void SVTXNumericField::GetPropertyIds( std::vector< sal_uInt16 > &rIds )
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 271f41a37e54..d621e3fc470c 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -15,6 +15,7 @@
 #include <vcl/virdev.hxx>
 #include <vcl/ctrl.hxx>
 #include <vcl/edit.hxx>
+#include <vcl/formatter.hxx>
 #include <vcl/spinfld.hxx>
 #include <vcl/toolkit/fixed.hxx>
 #include <vcl/toolkit/lstbox.hxx>
@@ -617,6 +618,7 @@ class SalInstanceSpinButton : public SalInstanceEntry, public virtual weld::Spin
 {
 private:
     VclPtr<FormattedField> m_xButton;
+    Formatter* m_pFormatter;
 
     DECL_LINK(UpDownHdl, SpinField&, void);
     DECL_LINK(LoseFocusHdl, Control&, void);
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 6e1bac5e91b3..e670a8fe4d6b 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5207,13 +5207,14 @@ SalInstanceSpinButton::SalInstanceSpinButton(FormattedField* pButton, SalInstanc
                         bool bTakeOwnership)
     : SalInstanceEntry(pButton, pBuilder, bTakeOwnership)
     , m_xButton(pButton)
+    , m_pFormatter(m_xButton->GetFormatter())
 {
-    m_xButton->SetThousandsSep(false); //off by default, MetricSpinButton enables it
+    m_pFormatter->SetThousandsSep(false); //off by default, MetricSpinButton enables it
     m_xButton->SetUpHdl(LINK(this, SalInstanceSpinButton, UpDownHdl));
     m_xButton->SetDownHdl(LINK(this, SalInstanceSpinButton, UpDownHdl));
     m_xButton->SetLoseFocusHdl(LINK(this, SalInstanceSpinButton, LoseFocusHdl));
-    m_xButton->SetOutputHdl(LINK(this, SalInstanceSpinButton, OutputHdl));
-    m_xButton->SetInputHdl(LINK(this, SalInstanceSpinButton, InputHdl));
+    m_pFormatter->SetOutputHdl(LINK(this, SalInstanceSpinButton, OutputHdl));
+    m_pFormatter->SetInputHdl(LINK(this, SalInstanceSpinButton, InputHdl));
     if (Edit* pEdit = m_xButton->GetSubEdit())
         pEdit->SetActivateHdl(LINK(this, SalInstanceSpinButton, ActivateHdl));
     else
@@ -5222,40 +5223,40 @@ SalInstanceSpinButton::SalInstanceSpinButton(FormattedField* pButton, SalInstanc
 
 int SalInstanceSpinButton::get_value() const
 {
-    return fromField(m_xButton->GetValue());
-    }
+    return fromField(m_pFormatter->GetValue());
+}
 
 void SalInstanceSpinButton::set_value(int value)
 {
-    m_xButton->SetValue(toField(value));
-    }
+    m_pFormatter->SetValue(toField(value));
+}
 
 void SalInstanceSpinButton::set_range(int min, int max)
 {
-    m_xButton->SetMinValue(toField(min));
-    m_xButton->SetMaxValue(toField(max));
+    m_pFormatter->SetMinValue(toField(min));
+    m_pFormatter->SetMaxValue(toField(max));
 }
 
 void SalInstanceSpinButton::get_range(int& min, int& max) const
 {
-    min = fromField(m_xButton->GetMinValue());
-    max = fromField(m_xButton->GetMaxValue());
+    min = fromField(m_pFormatter->GetMinValue());
+    max = fromField(m_pFormatter->GetMaxValue());
 }
 
 void SalInstanceSpinButton::set_increments(int step, int /*page*/)
 {
-    m_xButton->SetSpinSize(toField(step));
+    m_pFormatter->SetSpinSize(toField(step));
 }
 
 void SalInstanceSpinButton::get_increments(int& step, int& page) const
 {
-    step = fromField(m_xButton->GetSpinSize());
-    page = fromField(m_xButton->GetSpinSize());
+    step = fromField(m_pFormatter->GetSpinSize());
+    page = fromField(m_pFormatter->GetSpinSize());
 }
 
 void SalInstanceSpinButton::set_digits(unsigned int digits)
 {
-    m_xButton->SetDecimalDigits(digits);
+    m_pFormatter->SetDecimalDigits(digits);
 }
 
 // SpinButton may be comprised of multiple subwidgets, consider the lot as
@@ -5268,18 +5269,18 @@ bool SalInstanceSpinButton::has_focus() const
 //so with hh::mm::ss, incrementing mm will not reset ss
 void SalInstanceSpinButton::DisableRemainderFactor()
 {
-    m_xButton->DisableRemainderFactor();
+    m_pFormatter->DisableRemainderFactor();
 }
 
 //off by default for direct SpinButtons, MetricSpinButton enables it
 void SalInstanceSpinButton::SetUseThousandSep()
 {
-    m_xButton->SetThousandsSep(true);
+    m_pFormatter->SetThousandsSep(true);
 }
 
 unsigned int SalInstanceSpinButton::get_digits() const
 {
-    return m_xButton->GetDecimalDigits();
+    return m_pFormatter->GetDecimalDigits();
 }
 
 SalInstanceSpinButton::~SalInstanceSpinButton()
@@ -5288,8 +5289,8 @@ SalInstanceSpinButton::~SalInstanceSpinButton()
         pEdit->SetActivateHdl(Link<Edit&, bool>());
     else
         m_xButton->SetActivateHdl(Link<Edit&, bool>());
-    m_xButton->SetInputHdl(Link<sal_Int64*, TriState>());
-    m_xButton->SetOutputHdl(Link<LinkParamNone*, bool>());
+    m_pFormatter->SetInputHdl(Link<sal_Int64*, TriState>());
+    m_pFormatter->SetOutputHdl(Link<LinkParamNone*, bool>());
     m_xButton->SetLoseFocusHdl(Link<Control&, void>());
     m_xButton->SetDownHdl(Link<SpinField&, void>());
     m_xButton->SetUpHdl(Link<SpinField&, void>());
@@ -5324,6 +5325,7 @@ class SalInstanceFormattedSpinButton : public SalInstanceEntry,
 {
 private:
     VclPtr<FormattedField> m_xButton;
+    Formatter* m_pFormatter;
 
     DECL_LINK(OutputHdl, LinkParamNone*, bool);
     DECL_LINK(InputHdl, sal_Int64*, TriState);
@@ -5333,61 +5335,62 @@ public:
                                    bool bTakeOwnership)
         : SalInstanceEntry(pButton, pBuilder, bTakeOwnership)
         , m_xButton(pButton)
+        , m_pFormatter(m_xButton->GetFormatter())
     {
-        m_xButton->SetOutputHdl(LINK(this, SalInstanceFormattedSpinButton, OutputHdl));
-        m_xButton->SetInputHdl(LINK(this, SalInstanceFormattedSpinButton, InputHdl));
+        m_pFormatter->SetOutputHdl(LINK(this, SalInstanceFormattedSpinButton, OutputHdl));
+        m_pFormatter->SetInputHdl(LINK(this, SalInstanceFormattedSpinButton, InputHdl));
 
         // #i6278# allow more decimal places than the output format.  As
         // the numbers shown in the edit fields are used for input, it makes more
         // sense to display the values in the input format rather than the output
         // format.
-        m_xButton->UseInputStringForFormatting();
+        m_pFormatter->UseInputStringForFormatting();
     }
 
     virtual ~SalInstanceFormattedSpinButton() override
     {
-        m_xButton->SetInputHdl(Link<sal_Int64*, TriState>());
-        m_xButton->SetOutputHdl(Link<LinkParamNone*, bool>());
+        m_pFormatter->SetInputHdl(Link<sal_Int64*, TriState>());
+        m_pFormatter->SetOutputHdl(Link<LinkParamNone*, bool>());
     }
 
-    virtual double get_value() const override { return m_xButton->GetValue(); }
+    virtual double get_value() const override { return m_pFormatter->GetValue(); }
 
-    virtual void set_value(double value) override { m_xButton->SetValue(value); }
+    virtual void set_value(double value) override { m_pFormatter->SetValue(value); }
 
     virtual void set_range(double min, double max) override
     {
-        m_xButton->SetMinValue(min);
-        m_xButton->SetMaxValue(max);
+        m_pFormatter->SetMinValue(min);
+        m_pFormatter->SetMaxValue(max);
     }
 
     virtual void get_range(double& min, double& max) const override
     {
-        min = m_xButton->GetMinValue();
-        max = m_xButton->GetMaxValue();
+        min = m_pFormatter->GetMinValue();
+        max = m_pFormatter->GetMaxValue();
     }
 
     virtual void set_increments(double step, double /*page*/) override
     {
-        m_xButton->SetSpinSize(step);
+        m_pFormatter->SetSpinSize(step);
     }
 
     virtual void set_formatter(SvNumberFormatter* pFormatter) override
     {
-        m_xButton->SetFormatter(pFormatter);
+        m_pFormatter->SetFormatter(pFormatter);
     }
 
-    virtual SvNumberFormatter* get_formatter() override { return m_xButton->GetFormatter(); }
+    virtual SvNumberFormatter* get_formatter() override { return m_pFormatter->GetFormatter(); }
 
-    virtual sal_Int32 get_format_key() const override { return m_xButton->GetFormatKey(); }
+    virtual sal_Int32 get_format_key() const override { return m_pFormatter->GetFormatKey(); }
 
     virtual void set_format_key(sal_Int32 nFormatKey) override
     {
-        m_xButton->SetFormatKey(nFormatKey);
+        m_pFormatter->SetFormatKey(nFormatKey);
     }
 
-    virtual void treat_as_number(bool bSet) override { m_xButton->TreatAsNumber(bSet); }
+    virtual void treat_as_number(bool bSet) override { m_pFormatter->TreatAsNumber(bSet); }
 
-    virtual void set_digits(unsigned int digits) override { m_xButton->SetDecimalDigits(digits); }
+    virtual void set_digits(unsigned int digits) override { m_pFormatter->SetDecimalDigits(digits); }
 };
 
 IMPL_LINK_NOARG(SalInstanceFormattedSpinButton, OutputHdl, LinkParamNone*, bool)
@@ -5408,7 +5411,7 @@ IMPL_LINK(SalInstanceFormattedSpinButton, InputHdl, sal_Int64*, pResult, TriStat
     double value;
     TriState eRet = m_aInputHdl.Call(&value) ? TRISTATE_TRUE : TRISTATE_FALSE;
     if (eRet == TRISTATE_TRUE)
-        *pResult = std::round(value * weld::SpinButton::Power10(m_xButton->GetDecimalDigits()));
+        *pResult = std::round(value * weld::SpinButton::Power10(m_pFormatter->GetDecimalDigits()));
     return eRet;
 }
 
diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx
index 9cb6dfd53296..2b175e403304 100644
--- a/vcl/source/app/weldutils.cxx
+++ b/vcl/source/app/weldutils.cxx
@@ -159,7 +159,7 @@ SelectionOptions FormattedEntry::GetEntrySelectionOptions() const { return m_eOp
 
 void FormattedEntry::FieldModified() { m_aModifyHdl.Call(*m_xEntry); }
 
-IMPL_LINK_NOARG(FormattedEntry, ModifyHdl, weld::Entry&, void) { impl_Modify(); }
+IMPL_LINK_NOARG(FormattedEntry, ModifyHdl, weld::Entry&, void) { Modify(); }
 
 IMPL_LINK_NOARG(FormattedEntry, FocusOutHdl, weld::Widget&, void) { EntryLostFocus(); }
 }
diff --git a/vcl/source/control/fmtfield.cxx b/vcl/source/control/fmtfield.cxx
index f3a703cc3306..d67b0b9ba792 100644
--- a/vcl/source/control/fmtfield.cxx
+++ b/vcl/source/control/fmtfield.cxx
@@ -48,50 +48,6 @@ using namespace ::com::sun::star::util;
 
 namespace validation
 {
-    namespace {
-
-    // the states of our automat.
-    enum State
-    {
-        START,              // at the very start of the string
-        NUM_START,          // the very start of the number
-
-        DIGIT_PRE_COMMA,    // some pre-comma digits are read, perhaps including some thousand separators
-
-        DIGIT_POST_COMMA,   // reading digits after the comma
-        EXPONENT_START,     // at the very start of the exponent value
-                            //    (means: not including the "e" which denotes the exponent)
-        EXPONENT_DIGIT,     // currently reading the digits of the exponent
-
-        END                 // reached the end of the string
-    };
-
-    }
-
-    // a row in the transition table (means the set of states to be reached from a given state)
-    typedef ::std::map< sal_Unicode, State >        StateTransitions;
-
-    // a single transition
-    typedef StateTransitions::value_type            Transition;
-
-    // the complete transition table
-    typedef ::std::map< State, StateTransitions >   TransitionTable;
-
-    // the validator class
-    class NumberValidator
-    {
-    private:
-        TransitionTable     m_aTransitions;
-
-    public:
-        NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep );
-
-        bool isValidNumericFragment( const OUString& _rText );
-
-    private:
-        bool implValidateNormalized( const OUString& _rText );
-    };
-
     static void lcl_insertStopTransition( StateTransitions& _rRow )
     {
         _rRow.insert( Transition( '_', END ) );
@@ -266,10 +222,10 @@ namespace validation
     }
 }
 
-SvNumberFormatter* FormattedField::StaticFormatter::s_cFormatter = nullptr;
-sal_uLong FormattedField::StaticFormatter::s_nReferences = 0;
+SvNumberFormatter* Formatter::StaticFormatter::s_cFormatter = nullptr;
+sal_uLong Formatter::StaticFormatter::s_nReferences = 0;
 
-SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
+SvNumberFormatter* Formatter::StaticFormatter::GetFormatter()
 {
     if (!s_cFormatter)
     {
@@ -282,12 +238,12 @@ SvNumberFormatter* FormattedField::StaticFormatter::GetFormatter()
     return s_cFormatter;
 }
 
-FormattedField::StaticFormatter::StaticFormatter()
+Formatter::StaticFormatter::StaticFormatter()
 {
     ++s_nReferences;
 }
 
-FormattedField::StaticFormatter::~StaticFormatter()
+Formatter::StaticFormatter::~StaticFormatter()
 {
     if (--s_nReferences == 0)
     {
@@ -334,7 +290,7 @@ void Formatter::SetFieldText(const OUString& rStr, const Selection& rNewSelectio
 
 void Formatter::SetTextFormatted(const OUString& rStr)
 {
-    SAL_INFO_IF(ImplGetFormatter()->IsTextFormat(m_nFormatKey), "svtools",
+    SAL_INFO_IF(GetOrCreateFormatter()->IsTextFormat(m_nFormatKey), "svtools",
         "FormattedField::SetTextFormatted : valid only with text formats !");
 
     m_sCurrentTextValue = rStr;
@@ -344,13 +300,13 @@ void Formatter::SetTextFormatted(const OUString& rStr)
     // IsNumberFormat changes the format key parameter
     sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
     if( IsUsingInputStringForFormatting() &&
-        ImplGetFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
+        GetOrCreateFormatter()->IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
     {
-        ImplGetFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
+        GetOrCreateFormatter()->GetInputLineString(dNumber, m_nFormatKey, sFormatted);
     }
     else
     {
-        ImplGetFormatter()->GetOutputString(m_sCurrentTextValue,
+        GetOrCreateFormatter()->GetOutputString(m_sCurrentTextValue,
                                             m_nFormatKey,
                                             sFormatted,
                                             &m_pLastOutputColor);
@@ -422,7 +378,7 @@ void Formatter::SetAutoColor(bool _bAutomatic)
     }
 }
 
-void Formatter::impl_Modify(bool makeValueDirty)
+void Formatter::Modify(bool makeValueDirty)
 {
     if (!IsStrictFormat())
     {
@@ -497,7 +453,7 @@ void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
     bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
     if (bNeedFormatter)
     {
-        ImplGetFormatter(); // this creates a standard formatter
+        GetOrCreateFormatter(); // this creates a standard formatter
 
         // It might happen that the standard formatter makes no sense here, but it takes a default
         // format. Thus, it is possible to set one of the other standard keys (which are spanning
@@ -560,7 +516,7 @@ void Formatter::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
 
 OUString Formatter::GetFormat(LanguageType& eLang) const
 {
-    const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
+    const SvNumberformat* pFormatEntry = GetOrCreateFormatter()->GetEntry(m_nFormatKey);
     DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
     OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
     eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
@@ -570,13 +526,13 @@ OUString Formatter::GetFormat(LanguageType& eLang) const
 
 bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
 {
-    sal_uInt32 nNewKey = ImplGetFormatter()->TestNewString(rFormatString, eLang);
+    sal_uInt32 nNewKey = GetOrCreateFormatter()->TestNewString(rFormatString, eLang);
     if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
     {
         sal_Int32 nCheckPos;
         SvNumFormatType nType;
         OUString rFormat(rFormatString);
-        if (!ImplGetFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
+        if (!GetOrCreateFormatter()->PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
             return false;
         DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
     }
@@ -588,25 +544,25 @@ bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
 
 bool Formatter::GetThousandsSep() const
 {
-    DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
+    DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
         "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
 
     bool bThousand, IsRed;
     sal_uInt16 nPrecision, nLeadingCnt;
-    ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
+    GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
 
     return bThousand;
 }
 
 void Formatter::SetThousandsSep(bool _bUseSeparator)
 {
-    DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
+    DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
         "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
 
     // get the current settings
     bool bThousand, IsRed;
     sal_uInt16 nPrecision, nLeadingCnt;
-    ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
+    GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
     if (bThousand == _bUseSeparator)
         return;
 
@@ -615,12 +571,12 @@ void Formatter::SetThousandsSep(bool _bUseSeparator)
     GetFormat(eLang);
 
     // generate a new format ...
-    OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
+    OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
     // ... and introduce it to the formatter
     sal_Int32 nCheckPos = 0;
     sal_uInt32 nNewKey;
     SvNumFormatType nType;
-    ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
+    GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
 
     // set the new key
     ImplSetFormatKey(nNewKey);
@@ -629,25 +585,25 @@ void Formatter::SetThousandsSep(bool _bUseSeparator)
 
 sal_uInt16 Formatter::GetDecimalDigits() const
 {
-    DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
+    DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
         "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
 
     bool bThousand, IsRed;
     sal_uInt16 nPrecision, nLeadingCnt;
-    ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
+    GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
 
     return nPrecision;
 }
 
 void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
 {
-    DBG_ASSERT(!ImplGetFormatter()->IsTextFormat(m_nFormatKey),
+    DBG_ASSERT(!GetOrCreateFormatter()->IsTextFormat(m_nFormatKey),
         "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
 
     // get the current settings
     bool bThousand, IsRed;
     sal_uInt16 nPrecision, nLeadingCnt;
-    ImplGetFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
+    GetOrCreateFormatter()->GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
     if (nPrecision == _nPrecision)
         return;
 
@@ -656,12 +612,12 @@ void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
     GetFormat(eLang);
 
     // generate a new format ...
-    OUString sFmtDescription = ImplGetFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
+    OUString sFmtDescription = GetOrCreateFormatter()->GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
     // ... and introduce it to the formatter
     sal_Int32 nCheckPos = 0;
     sal_uInt32 nNewKey;
     SvNumFormatType nType;
-    ImplGetFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
+    GetOrCreateFormatter()->PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
 
     // set the new key
     ImplSetFormatKey(nNewKey);
@@ -688,7 +644,7 @@ void Formatter::EntryLostFocus()
             if (TreatingAsNumber())
             {
                 ImplSetValue(m_dCurrentValue, true);
-                impl_Modify();
+                Modify();
                 m_ValueState = valueDouble;
             }
             else
@@ -723,7 +679,7 @@ void Formatter::Commit()
         // don't reparse it from the text
         // (can lead to data loss when the format is lossy,
         //  as is e.g. our default date format: 2-digit year!)
-        impl_Modify(false);
+        Modify(false);
     }
 }
 
@@ -795,7 +751,7 @@ void Formatter::ImplSetValue(double dVal, bool bForce)
     if (!bForce && (dVal == GetValue()))
         return;
 
-    DBG_ASSERT(ImplGetFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
+    DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplSetValue : can't set a value without a formatter !");
 
     m_ValueState = valueDouble;
     m_dCurrentValue = dVal;
@@ -803,23 +759,23 @@ void Formatter::ImplSetValue(double dVal, bool bForce)
     if (!m_aOutputHdl.IsSet() || !m_aOutputHdl.Call(nullptr))
     {
         OUString sNewText;
-        if (ImplGetFormatter()->IsTextFormat(m_nFormatKey))
+        if (GetOrCreateFormatter()->IsTextFormat(m_nFormatKey))
         {
             // first convert the number as string in standard format
             OUString sTemp;
-            ImplGetFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
+            GetOrCreateFormatter()->GetOutputString(dVal, 0, sTemp, &m_pLastOutputColor);
             // then encode the string in the corresponding text format
-            ImplGetFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
+            GetOrCreateFormatter()->GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
         }
         else
         {
             if( IsUsingInputStringForFormatting())
             {
-                ImplGetFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
+                GetOrCreateFormatter()->GetInputLineString(dVal, m_nFormatKey, sNewText);
             }
             else
             {
-                ImplGetFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
+                GetOrCreateFormatter()->GetOutputString(dVal, m_nFormatKey, sNewText, &m_pLastOutputColor);
             }
         }
         ImplSetTextImpl(sNewText, nullptr);
@@ -860,16 +816,16 @@ bool Formatter::ImplGetValue(double& dNewVal)
 
     if (!bUseExternalFormatterValue)
     {
-        DBG_ASSERT(ImplGetFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
+        DBG_ASSERT(GetOrCreateFormatter() != nullptr, "FormattedField::ImplGetValue : can't give you a current value without a formatter !");
 
         sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
 
-        if (ImplGetFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
+        if (GetOrCreateFormatter()->IsTextFormat(nFormatKey) && m_bTreatAsNumber)
             // for detection of values like "1,1" in fields that are formatted as text
             nFormatKey = 0;
 
         // special treatment for percentage formatting
-        if (ImplGetFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
+        if (GetOrCreateFormatter()->GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
         {
             // the language of our format
             LanguageType eLanguage = m_pFormatter->GetEntry(m_nFormatKey)->GetLanguage();
@@ -886,7 +842,7 @@ bool Formatter::ImplGetValue(double& dNewVal)
             // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
             // which equals 300 percent.
         }
-        if (!ImplGetFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
+        if (!GetOrCreateFormatter()->IsNumberFormat(sText, nFormatKey, dNewVal))
             return false;
     }
 
@@ -922,38 +878,150 @@ void Formatter::DisableRemainderFactor()
     m_bDisableRemainderFactor = true;
 }
 
-
 void Formatter::UseInputStringForFormatting()
 {
     m_bUseInputStringForFormatting = true;
 }
 
-DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
-    : FormattedField(pParent, nStyle)
+namespace
 {
-    ResetConformanceTester();
-}
+    class FieldFormatter : public Formatter
+    {
+    private:
+        FormattedField& m_rSpinButton;
+    public:
+        FieldFormatter(FormattedField& rSpinButton)
+            : m_rSpinButton(rSpinButton)
+        {
+        }
 
-DoubleNumericField::~DoubleNumericField() = default;
+        // Formatter overrides
+        virtual Selection GetEntrySelection() const override
+        {
+            return m_rSpinButton.GetSelection();
+        }
 
-void DoubleNumericField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
-{
-    ResetConformanceTester();
-    FormattedField::FormatChanged(nWhat);
+        virtual OUString GetEntryText() const override
+        {
+            return m_rSpinButton.GetText();
+        }
+
+        void SetEntryText(const OUString& rText, const Selection& rSel) override
+        {
+            m_rSpinButton.SpinField::SetText(rText, rSel);
+        }
+
+        virtual void SetEntryTextColor(const Color* pColor) override
+        {
+            if (pColor)
+                m_rSpinButton.SetControlForeground(*pColor);
+            else
+                m_rSpinButton.SetControlForeground();
+        }
+
+        virtual SelectionOptions GetEntrySelectionOptions() const override
+        {
+            return m_rSpinButton.GetSettings().GetStyleSettings().GetSelectionOptions();
+        }
+
+        virtual void FieldModified() override
+        {
+            m_rSpinButton.SpinField::Modify();
+        }
+    };
+
+    class DoubleNumericFormatter : public FieldFormatter
+    {
+    private:
+        DoubleNumericField& m_rNumericSpinButton;
+    public:
+        DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
+            : FieldFormatter(rNumericSpinButton)
+            , m_rNumericSpinButton(rNumericSpinButton)
+        {
+        }
+
+        virtual bool CheckText(const OUString& sText) const override
+        {
+            // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
+            // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
+            // Thus, the roundabout way via a regular expression
+            return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
+        }
+
+        virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
+        {
+            m_rNumericSpinButton.ResetConformanceTester();
+            FieldFormatter::FormatChanged(nWhat);
+        }
+    };
+
+    class DoubleCurrencyFormatter : public FieldFormatter
+    {
+    private:
+        DoubleCurrencyField& m_rCurrencySpinButton;
+        bool m_bChangingFormat;
+    public:
+        DoubleCurrencyFormatter(DoubleCurrencyField& rNumericSpinButton)
+            : FieldFormatter(rNumericSpinButton)
+            , m_rCurrencySpinButton(rNumericSpinButton)
+            , m_bChangingFormat(false)
+        {
+        }
+
+        virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
+        {
+            if (m_bChangingFormat)
+            {
+                FieldFormatter::FormatChanged(nWhat);
+                return;
+            }
+
+            switch (nWhat)
+            {
+                case FORMAT_CHANGE_TYPE::FORMATTER:
+                case FORMAT_CHANGE_TYPE::PRECISION:
+                case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
+                    // the aspects which changed don't take our currency settings into account (in fact, they most probably
+                    // destroyed them)
+                    m_rCurrencySpinButton.UpdateCurrencyFormat();
+                    break;
+                case FORMAT_CHANGE_TYPE::KEYONLY:
+                    OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
+                    // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
+                    // Nobody but ourself should modify the format key directly!
+                    break;
+                default: break;
+            }
+
+            FieldFormatter::FormatChanged(nWhat);
+        }
+
+        void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
+        {
+            // set this new basic format
+            m_bChangingFormat = true;
+            SetFormat(rString, eLanguage);
+            m_bChangingFormat = false;
+        }
+
+    };
 }
 
-bool DoubleNumericField::CheckText(const OUString& sText) const
+DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
+    : FormattedField(pParent, nStyle)
 {
-    // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
-    // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
-    // Thus, the roundabout way via a regular expression
-    return m_pNumberValidator->isValidNumericFragment( sText );
+    m_xFormatter.reset(new DoubleNumericFormatter(*this));
+    ResetConformanceTester();
 }
 
+DoubleNumericField::~DoubleNumericField() = default;
+
 void DoubleNumericField::ResetConformanceTester()
 {
     // the thousands and the decimal separator are language dependent
-    const SvNumberformat* pFormatEntry = ImplGetFormatter()->GetEntry(m_nFormatKey);
+    Formatter* pFormatter = GetFormatter();
+    const SvNumberformat* pFormatEntry = pFormatter->GetOrCreateFormatter()->GetEntry(pFormatter->GetFormatKey());
 
     sal_Unicode cSeparatorThousand = ',';
     sal_Unicode cSeparatorDecimal = '.';
@@ -973,10 +1041,11 @@ void DoubleNumericField::ResetConformanceTester()
     m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
 }
 
+
 DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
     :FormattedField(pParent, nStyle)
-    ,m_bChangingFormat(false)
 {
+    m_xFormatter.reset(new DoubleCurrencyFormatter(*this));
     m_bPrependCurrSym = false;
 
     // initialize with a system currency format
@@ -984,34 +1053,6 @@ DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
     UpdateCurrencyFormat();
 }
 
-void DoubleCurrencyField::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
-{
-    if (m_bChangingFormat)
-    {
-        FormattedField::FormatChanged(nWhat);
-        return;
-    }
-
-    switch (nWhat)
-    {
-        case FORMAT_CHANGE_TYPE::FORMATTER:
-        case FORMAT_CHANGE_TYPE::PRECISION:
-        case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
-            // the aspects which changed don't take our currency settings into account (in fact, they most probably
-            // destroyed them)
-            UpdateCurrencyFormat();
-            break;
-        case FORMAT_CHANGE_TYPE::KEYONLY:
-            OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
-            // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
-            // Nobody but ourself should modify the format key directly!
-            break;
-        default: break;
-    }
-
-    FormattedField::FormatChanged(nWhat);
-}
-
 void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
 {
     if (m_sCurrencySymbol == rSymbol)
@@ -1019,7 +1060,7 @@ void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
 
     m_sCurrencySymbol = rSymbol;
     UpdateCurrencyFormat();
-    FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
+    m_xFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
 }
 
 void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
@@ -1029,16 +1070,16 @@ void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
 
     m_bPrependCurrSym = _bPrepend;
     UpdateCurrencyFormat();
-    FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
+    m_xFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
 }
 
 void DoubleCurrencyField::UpdateCurrencyFormat()
 {
     // the old settings
     LanguageType eLanguage;
-    GetFormat(eLanguage);
-    bool bThSep = GetThousandsSep();
-    sal_uInt16 nDigits = GetDecimalDigits();
+    m_xFormatter->GetFormat(eLanguage);
+    bool bThSep = m_xFormatter->GetThousandsSep();
+    sal_uInt16 nDigits = m_xFormatter->GetDecimalDigits();
 
     // build a new format string with the base class' and my own settings
 
@@ -1102,9 +1143,7 @@ void DoubleCurrencyField::UpdateCurrencyFormat()
     }
 
     // set this new basic format
-    m_bChangingFormat = true;
-    SetFormat(sNewFormat.makeStringAndClear(), eLanguage);
-    m_bChangingFormat = false;
+    static_cast<DoubleCurrencyFormatter*>(m_xFormatter.get())->GuardSetFormat(sNewFormat.makeStringAndClear(), eLanguage);
 }
 
 FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
@@ -1114,21 +1153,21 @@ FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
 
 void FormattedField::SetText(const OUString& rStr)
 {
-    SetFieldText(rStr, Selection(0, 0));
+    GetFormatter()->SetFieldText(rStr, Selection(0, 0));
 }
 
 void FormattedField::SetText(const OUString& rStr, const Selection& rNewSelection)
 {
-    SetFieldText(rStr, rNewSelection);
+    GetFormatter()->SetFieldText(rStr, rNewSelection);
     SetSelection(rNewSelection);
 }
 
 bool FormattedField::set_property(const OString &rKey, const OUString &rValue)
 {
     if (rKey == "digits")
-        SetDecimalDigits(rValue.toInt32());
+        GetFormatter()->SetDecimalDigits(rValue.toInt32());
     else if (rKey == "wrap")
-        m_bWrapOnLimits = toBool(rValue);
+        GetFormatter()->SetWrapOnLimits(toBool(rValue));
     else
         return SpinField::set_property(rKey, rValue);
     return true;
@@ -1136,18 +1175,19 @@ bool FormattedField::set_property(const OString &rKey, const OUString &rValue)
 
 void FormattedField::Up()
 {
-    auto nScale = weld::SpinButton::Power10(GetDecimalDigits());
+    Formatter* pFormatter = GetFormatter();
+    auto nScale = weld::SpinButton::Power10(pFormatter->GetDecimalDigits());
 
-    sal_Int64 nValue = std::round(GetValue() * nScale);
-    sal_Int64 nSpinSize = std::round(m_dSpinSize * nScale);
-    sal_Int64 nRemainder = m_bDisableRemainderFactor ? 0 : nValue % nSpinSize;
+    sal_Int64 nValue = std::round(pFormatter->GetValue() * nScale);
+    sal_Int64 nSpinSize = std::round(pFormatter->GetSpinSize() * nScale);
+    sal_Int64 nRemainder = pFormatter->GetDisableRemainderFactor() ? 0 : nValue % nSpinSize;
     if (nValue >= 0)
         nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue + nSpinSize - nRemainder;
     else
         nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue - nRemainder;
 
     // setValue handles under- and overflows (min/max) automatically
-    SetValue(static_cast<double>(nValue) / nScale);
+    pFormatter->SetValue(static_cast<double>(nValue) / nScale);
     SetModifyFlag();
     Modify();
 
@@ -1156,18 +1196,19 @@ void FormattedField::Up()
 
 void FormattedField::Down()
 {
-    auto nScale = weld::SpinButton::Power10(GetDecimalDigits());
+    Formatter* pFormatter = GetFormatter();
+    auto nScale = weld::SpinButton::Power10(pFormatter->GetDecimalDigits());
 
-    sal_Int64 nValue = std::round(GetValue() * nScale);
-    sal_Int64 nSpinSize = std::round(m_dSpinSize * nScale);
-    sal_Int64 nRemainder = m_bDisableRemainderFactor ? 0 : nValue % nSpinSize;
+    sal_Int64 nValue = std::round(pFormatter->GetValue() * nScale);
+    sal_Int64 nSpinSize = std::round(pFormatter->GetSpinSize() * nScale);
+    sal_Int64 nRemainder = pFormatter->GetDisableRemainderFactor() ? 0 : nValue % nSpinSize;
     if (nValue >= 0)
         nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
     else
         nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
 
     // setValue handles under- and overflows (min/max) automatically
-    SetValue(static_cast<double>(nValue) / nScale);
+    pFormatter->SetValue(static_cast<double>(nValue) / nScale);
     SetModifyFlag();
     Modify();
 
@@ -1176,9 +1217,10 @@ void FormattedField::Down()
 
 void FormattedField::First()
 {
-    if (m_bHasMin)
+    Formatter* pFormatter = GetFormatter();
+    if (pFormatter->HasMinValue())
     {
-        SetValue(m_dMinValue);
+        pFormatter->SetValue(pFormatter->GetMinValue());
         SetModifyFlag();
         Modify();
     }
@@ -1188,9 +1230,10 @@ void FormattedField::First()
 
 void FormattedField::Last()
 {
-    if (m_bHasMax)
+    Formatter* pFormatter = GetFormatter();
+    if (pFormatter->HasMaxValue())
     {
-        SetValue(m_dMaxValue);
+        pFormatter->SetValue(pFormatter->GetMaxValue());
         SetModifyFlag();
         Modify();
     }
@@ -1200,19 +1243,18 @@ void FormattedField::Last()
 
 void FormattedField::Modify()
 {
-    impl_Modify();
+    GetFormatter()->Modify();
 }
 
 bool FormattedField::PreNotify(NotifyEvent& rNEvt)
 {
     if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
-        m_aLastSelection = GetSelection();
+        GetFormatter()->SetLastSelection(GetSelection());
     return SpinField::PreNotify(rNEvt);
 }
 
 bool FormattedField::EventNotify(NotifyEvent& rNEvt)
 {
-
     if ((rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !IsReadOnly())
     {
         const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
@@ -1223,12 +1265,15 @@ bool FormattedField::EventNotify(NotifyEvent& rNEvt)
             case KEY_DOWN:
             case KEY_PAGEUP:
             case KEY_PAGEDOWN:
-                if (!nMod && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
+            {
+                Formatter* pFormatter = GetFormatter();
+                if (!nMod && pFormatter->GetOrCreateFormatter()->IsTextFormat(pFormatter->GetFormatKey()))
                 {
                     // the base class would translate this into calls to Up/Down/First/Last,
                     // but we don't want this if we are text-formatted
                     return true;
                 }
+            }
         }
     }
 
@@ -1238,7 +1283,9 @@ bool FormattedField::EventNotify(NotifyEvent& rNEvt)
         if (pCommand->GetCommand() == CommandEventId::Wheel)
         {
             const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
-            if ((pData->GetMode() == CommandWheelMode::SCROLL) && ImplGetFormatter()->IsTextFormat(m_nFormatKey))
+            Formatter* pFormatter = GetFormatter();
+            if ((pData->GetMode() == CommandWheelMode::SCROLL) &&
+                pFormatter->GetOrCreateFormatter()->IsTextFormat(pFormatter->GetFormatKey()))
             {
                 // same as above : prevent the base class from doing Up/Down-calls
                 // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
@@ -1249,42 +1296,16 @@ bool FormattedField::EventNotify(NotifyEvent& rNEvt)
     }
 
     if (rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS)
-        EntryLostFocus();
+        GetFormatter()->EntryLostFocus();
 
     return SpinField::EventNotify( rNEvt );
 }
 
-Selection FormattedField::GetEntrySelection() const
-{
-    return GetSelection();
-}
-
-OUString FormattedField::GetEntryText() const
-{
-    return GetText();
-}
-
-void FormattedField::SetEntryText(const OUString& rText, const Selection& rSel)
-{
-    SpinField::SetText(rText, rSel);
-}
-
-void FormattedField::SetEntryTextColor(const Color* pColor)
-{
-    if (pColor)
-        SetControlForeground(*pColor);
-    else
-        SetControlForeground();
-}
-
-SelectionOptions FormattedField::GetEntrySelectionOptions() const
-{
-    return GetSettings().GetStyleSettings().GetSelectionOptions();
-}
-
-void FormattedField::FieldModified()
+Formatter* FormattedField::GetFormatter()
 {
-    SpinField::Modify();
+    if (!m_xFormatter)
+        m_xFormatter.reset(new FieldFormatter(*this));
+    return m_xFormatter.get();
 }
 
 // currently used by online
@@ -1292,12 +1313,13 @@ void FormattedField::SetValueFromString(const OUString& rStr)
 {
     sal_Int32 nEnd;
     rtl_math_ConversionStatus eStatus;
-    double fValue = ::rtl::math::stringToDouble(rStr, '.', GetDecimalDigits(), &eStatus, &nEnd );
+    Formatter* pFormatter = GetFormatter();
+    double fValue = ::rtl::math::stringToDouble(rStr, '.', pFormatter->GetDecimalDigits(), &eStatus, &nEnd );
 
     if (eStatus == rtl_math_ConversionStatus_Ok &&
         nEnd == rStr.getLength())
     {
-        SetValue(fValue);
+        pFormatter->SetValue(fValue);
         SetModifyFlag();
         Modify();
 
@@ -1313,9 +1335,10 @@ void FormattedField::SetValueFromString(const OUString& rStr)
 void FormattedField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
 {
     SpinField::DumpAsPropertyTree(rJsonWriter);
-    rJsonWriter.put("min", GetMinValue());
-    rJsonWriter.put("max", GetMaxValue());
-    rJsonWriter.put("value", GetValue());
+    Formatter* pFormatter = GetFormatter();
+    rJsonWriter.put("min", pFormatter->GetMinValue());
+    rJsonWriter.put("max", pFormatter->GetMaxValue());
+    rJsonWriter.put("value", pFormatter->GetValue());
 }
 
 FactoryFunction FormattedField::GetUITestFactory() const
diff --git a/vcl/source/uitest/uiobject.cxx b/vcl/source/uitest/uiobject.cxx
index 947dda576c73..c8170a17caee 100644
--- a/vcl/source/uitest/uiobject.cxx
+++ b/vcl/source/uitest/uiobject.cxx
@@ -1374,7 +1374,7 @@ void FormattedFieldUIObject::execute(const OUString& rAction,
 StringMap FormattedFieldUIObject::get_state()
 {
     StringMap aMap = EditUIObject::get_state();
-    aMap["Value"] = OUString::number(mxFormattedField->GetValue());
+    aMap["Value"] = OUString::number(mxFormattedField->GetFormatter()->GetValue());
 
     return aMap;
 }
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 422babaae27a..85ee2ca4f4f1 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -1951,7 +1951,7 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &
         {
             connectFormattedFormatterAdjustment(id, sAdjustment);
             VclPtrInstance<FormattedField> xField(pParent, nBits);
-            xField->SetMinValue(0);
+            xField->GetFormatter()->SetMinValue(0);
             xWindow = xField;
         }
     }
@@ -4411,10 +4411,11 @@ void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdj
             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
     }
 
-    rTarget.SetMinValue(nMinValue);
-    rTarget.SetMaxValue(nMaxValue);
-    rTarget.SetValue(nValue);
-    rTarget.SetSpinSize(nSpinSize);
+    Formatter* pFormatter = rTarget.GetFormatter();
+    pFormatter->SetMinValue(nMinValue);
+    pFormatter->SetMaxValue(nMaxValue);
+    pFormatter->SetValue(nValue);
+    pFormatter->SetSpinSize(nSpinSize);
 }
 
 void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)


More information about the Libreoffice-commits mailing list