weld TimeControl

Change-Id: Idb19639eb39bc83fb8bdc7c2181bf63e9c522d18
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/98670
Tested-by: Caolán McNamara <caolanm@redhat.com>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/include/svtools/editbrowsebox.hxx b/include/svtools/editbrowsebox.hxx
index 6732b9d..9ace81d 100644
--- a/include/svtools/editbrowsebox.hxx
+++ b/include/svtools/editbrowsebox.hxx
@@ -746,6 +746,12 @@ namespace svt
        LongCurrencyControl(BrowserDataWin* pParent, bool bSpinVariant);
    };

    class SVT_DLLPUBLIC TimeControl : public FormattedControlBase
    {
    public:
        TimeControl(BrowserDataWin* pParent, bool bSpinVariant);
    };

    //= FormattedFieldCellController
    class SVT_DLLPUBLIC FormattedFieldCellController final : public EditCellController
    {
diff --git a/include/vcl/field.hxx b/include/vcl/field.hxx
index 92cfbbf..1cb4974 100644
--- a/include/vcl/field.hxx
+++ b/include/vcl/field.hxx
@@ -263,77 +263,6 @@ public:
    bool             IsEnforceValidValue( ) const { return mbEnforceValidValue; }
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) TimeFormatter : public FormatterBase
{
private:
    tools::Time             maLastTime;
    tools::Time             maMin;
    tools::Time             maMax;
    TimeFieldFormat         meFormat;
    TimeFormat              mnTimeFormat;
    bool                    mbDuration;
    bool                    mbEnforceValidValue;

protected:
    tools::Time             maFieldTime;

                            TimeFormatter(Edit* pEdit);

    SAL_DLLPRIVATE void     ImplTimeReformat( const OUString& rStr, OUString& rOutStr );
    SAL_DLLPRIVATE void     ImplNewFieldValue( const tools::Time& rTime );
    SAL_DLLPRIVATE void     ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection = nullptr );
    SAL_DLLPRIVATE bool     ImplAllowMalformedInput() const;

public:
    static OUString         FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData);
    static bool             TextToTime(const OUString& rStr, tools::Time& rTime, TimeFieldFormat eFormat, bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters = true);
    static int              GetTimeArea(TimeFieldFormat eFormat, const OUString& rText, int nCursor,
                                        const LocaleDataWrapper& rLocaleDataWrapper);
    static tools::Time      SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
                                     bool bDuration, const OUString& rText, int nCursor,
                                     const LocaleDataWrapper& rLocaleDataWrapper);

    virtual                 ~TimeFormatter() override;

    virtual void            Reformat() override;
    virtual void            ReformatAll() override;

    void                    SetMin( const tools::Time& rNewMin );
    const tools::Time&             GetMin() const { return maMin; }
    void                    SetMax( const tools::Time& rNewMax );
    const tools::Time&             GetMax() const { return maMax; }

    void                    SetTimeFormat( TimeFormat eNewFormat );
    TimeFormat              GetTimeFormat() const { return mnTimeFormat;}

    void                    SetFormat( TimeFieldFormat eNewFormat );
    TimeFieldFormat         GetFormat() const { return meFormat; }

    void                    SetDuration( bool mbDuration );
    bool                    IsDuration() const { return mbDuration; }

    void                    SetTime( const tools::Time& rNewTime );
    void                    SetUserTime( const tools::Time& rNewTime );
    tools::Time             GetTime() const;
    void                    SetEmptyTime() { FormatterBase::SetEmptyFieldValue(); }
    bool                    IsEmptyTime() const { return FormatterBase::IsEmptyFieldValue(); }

    /** enables or disables the enforcement of valid values

        If this is set to true (which is the default), then GetTime will always return a valid
        time, no matter whether the current text can really be interpreted as time. (Note: this
        is the compatible behavior).

        If this is set to false, the GetTime will return GetInvalidTime, in case the current text
        cannot be interpreted as time.

        In addition, if this is set to false, the text in the field will <em>not</em> be corrected
        when the control loses the focus - instead, the invalid input will be preserved.
    */
    void                    EnforceValidValue( bool _bEnforce ) { mbEnforceValidValue = _bEnforce; }
    bool             IsEnforceValidValue( ) const { return mbEnforceValidValue; }
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) PatternField final : public SpinField, public PatternFormatter
{
public:
@@ -399,37 +328,6 @@ public:
    virtual void            dispose() override;
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) TimeField final : public SpinField, public TimeFormatter
{
private:
    tools::Time                    maFirst;
    tools::Time                    maLast;

    SAL_DLLPRIVATE void     ImplTimeSpinArea( bool bUp );

public:
    explicit                TimeField( vcl::Window* pParent, WinBits nWinStyle );

    virtual bool            PreNotify( NotifyEvent& rNEvt ) override;
    virtual bool            EventNotify( NotifyEvent& rNEvt ) override;
    virtual void            DataChanged( const DataChangedEvent& rDCEvt ) override;

    virtual void            Modify() override;

    virtual void            Up() override;
    virtual void            Down() override;
    virtual void            First() override;
    virtual void            Last() override;

    void                    SetFirst( const tools::Time& rNewFirst )   { maFirst = rNewFirst; }
    const tools::Time&      GetFirst() const                    { return maFirst; }
    void                    SetLast( const tools::Time& rNewLast )     { maLast = rNewLast; }
    const tools::Time&      GetLast() const                     { return maLast; }

    void                    SetExtFormat( ExtTimeFieldFormat eFormat );
    virtual void            dispose() override;
};

#endif // INCLUDED_VCL_FIELD_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/toolkit/field.hxx b/include/vcl/toolkit/field.hxx
index f996bd1..6b994b0 100644
--- a/include/vcl/toolkit/field.hxx
+++ b/include/vcl/toolkit/field.hxx
@@ -158,6 +158,108 @@ public:
    virtual void            dispose() override;
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) TimeFormatter : public FormatterBase
{
private:
    tools::Time             maLastTime;
    tools::Time             maMin;
    tools::Time             maMax;
    TimeFieldFormat         meFormat;
    TimeFormat              mnTimeFormat;
    bool                    mbDuration;
    bool                    mbEnforceValidValue;

protected:
    tools::Time             maFieldTime;

                            TimeFormatter(Edit* pEdit);

    SAL_DLLPRIVATE void     ImplTimeReformat( const OUString& rStr, OUString& rOutStr );
    SAL_DLLPRIVATE void     ImplNewFieldValue( const tools::Time& rTime );
    SAL_DLLPRIVATE void     ImplSetUserTime( const tools::Time& rNewTime, Selection const * pNewSelection = nullptr );
    SAL_DLLPRIVATE bool     ImplAllowMalformedInput() const;

public:
    static OUString         FormatTime(const tools::Time& rNewTime, TimeFieldFormat eFormat, TimeFormat eHourFormat, bool bDuration, const LocaleDataWrapper& rLocaleData);
    static bool             TextToTime(const OUString& rStr, tools::Time& rTime, TimeFieldFormat eFormat, bool bDuration, const LocaleDataWrapper& rLocaleDataWrapper, bool _bSkipInvalidCharacters = true);
    static int              GetTimeArea(TimeFieldFormat eFormat, const OUString& rText, int nCursor,
                                        const LocaleDataWrapper& rLocaleDataWrapper);
    static tools::Time      SpinTime(bool bUp, const tools::Time& rTime, TimeFieldFormat eFormat,
                                     bool bDuration, const OUString& rText, int nCursor,
                                     const LocaleDataWrapper& rLocaleDataWrapper);

    virtual                 ~TimeFormatter() override;

    virtual void            Reformat() override;
    virtual void            ReformatAll() override;

    void                    SetMin( const tools::Time& rNewMin );
    const tools::Time&             GetMin() const { return maMin; }
    void                    SetMax( const tools::Time& rNewMax );
    const tools::Time&             GetMax() const { return maMax; }

    void                    SetTimeFormat( TimeFormat eNewFormat );
    TimeFormat              GetTimeFormat() const { return mnTimeFormat;}

    void                    SetFormat( TimeFieldFormat eNewFormat );
    TimeFieldFormat         GetFormat() const { return meFormat; }

    void                    SetDuration( bool mbDuration );
    bool                    IsDuration() const { return mbDuration; }

    void                    SetTime( const tools::Time& rNewTime );
    void                    SetUserTime( const tools::Time& rNewTime );
    tools::Time             GetTime() const;
    void                    SetEmptyTime() { FormatterBase::SetEmptyFieldValue(); }
    bool                    IsEmptyTime() const { return FormatterBase::IsEmptyFieldValue(); }

    /** enables or disables the enforcement of valid values

        If this is set to true (which is the default), then GetTime will always return a valid
        time, no matter whether the current text can really be interpreted as time. (Note: this
        is the compatible behavior).

        If this is set to false, the GetTime will return GetInvalidTime, in case the current text
        cannot be interpreted as time.

        In addition, if this is set to false, the text in the field will <em>not</em> be corrected
        when the control loses the focus - instead, the invalid input will be preserved.
    */
    void                    EnforceValidValue( bool _bEnforce ) { mbEnforceValidValue = _bEnforce; }
    bool             IsEnforceValidValue( ) const { return mbEnforceValidValue; }
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) TimeField final : public SpinField, public TimeFormatter
{
private:
    tools::Time                    maFirst;
    tools::Time                    maLast;

    SAL_DLLPRIVATE void     ImplTimeSpinArea( bool bUp );

public:
    explicit                TimeField( vcl::Window* pParent, WinBits nWinStyle );

    virtual bool            PreNotify( NotifyEvent& rNEvt ) override;
    virtual bool            EventNotify( NotifyEvent& rNEvt ) override;
    virtual void            DataChanged( const DataChangedEvent& rDCEvt ) override;

    virtual void            Modify() override;

    virtual void            Up() override;
    virtual void            Down() override;
    virtual void            First() override;
    virtual void            Last() override;

    void                    SetFirst( const tools::Time& rNewFirst )   { maFirst = rNewFirst; }
    const tools::Time&      GetFirst() const                    { return maFirst; }
    void                    SetLast( const tools::Time& rNewLast )     { maLast = rNewLast; }
    const tools::Time&      GetLast() const                     { return maLast; }

    void                    SetExtFormat( ExtTimeFieldFormat eFormat );
    virtual void            dispose() override;
};

class UNLESS_MERGELIBS(VCL_DLLPUBLIC) NumericBox : public ComboBox, public NumericFormatter
{
    SAL_DLLPRIVATE void     ImplNumericReformat( const OUString& rStr, sal_Int64& rValue, OUString& rOutStr );
diff --git a/include/vcl/weldutils.hxx b/include/vcl/weldutils.hxx
index ebb458d..e4faeea 100644
--- a/include/vcl/weldutils.hxx
+++ b/include/vcl/weldutils.hxx
@@ -234,10 +234,45 @@ private:
    DECL_LINK(FormatOutputHdl, LinkParamNone*, bool);
    DECL_LINK(ParseInputHdl, sal_Int64*, TriState);

    void Init();

    OUString m_aCurrencySymbol;
    bool m_bThousandSep;
};

class VCL_DLLPUBLIC TimeFormatter final : public EntryFormatter
{
public:
    TimeFormatter(weld::Entry& rEntry);
    TimeFormatter(weld::FormattedSpinButton& rSpinButton);

    void SetExtFormat(ExtTimeFieldFormat eFormat);

    void SetMin(const tools::Time& rNewMin);
    void SetMax(const tools::Time& rNewMax);

    void SetTime(const tools::Time& rNewTime);
    tools::Time GetTime();

    virtual ~TimeFormatter() override;

private:
    DECL_LINK(FormatOutputHdl, LinkParamNone*, bool);
    DECL_LINK(ParseInputHdl, sal_Int64*, TriState);
    DECL_LINK(CursorChangedHdl, weld::Entry&, void);

    void Init();

    static tools::Time ConvertValue(int nValue);
    static int ConvertValue(const tools::Time& rTime);

    OUString FormatNumber(int nValue) const;

    TimeFieldFormat m_eFormat;
    TimeFormat m_eTimeFormat;
    bool m_bDuration;
};

// get the row the iterator is on
VCL_DLLPUBLIC size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter);

diff --git a/svtools/source/brwbox/ebbcontrols.cxx b/svtools/source/brwbox/ebbcontrols.cxx
index 400f0fb..ba90f7f 100644
--- a/svtools/source/brwbox/ebbcontrols.cxx
+++ b/svtools/source/brwbox/ebbcontrols.cxx
@@ -457,6 +457,16 @@ namespace svt
        InitFormattedControlBase();
    }

    TimeControl::TimeControl(BrowserDataWin* pParent, bool bSpinVariant)
        : FormattedControlBase(pParent, bSpinVariant)
    {
        if (bSpinVariant)
            m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xSpinButton));
        else
            m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xEntry));
        InitFormattedControlBase();
    }

    EditCellController::EditCellController(EditControlBase* pEdit)
        : CellController(pEdit)
        , m_pEditImplementation(new EntryImplementation(*pEdit))
diff --git a/svx/source/fmcomp/gridcell.cxx b/svx/source/fmcomp/gridcell.cxx
index bddf630..b29fdcb 100644
--- a/svx/source/fmcomp/gridcell.cxx
+++ b/svx/source/fmcomp/gridcell.cxx
@@ -2272,10 +2272,14 @@ DbTimeField::DbTimeField( DbGridColumn& _rColumn )
    doPropertyListening( FM_PROP_STRICTFORMAT );
}

VclPtr<Control> DbTimeField::createField(BrowserDataWin* _pParent, bool bSpinButton, const Reference< XPropertySet >& /*_rxModel*/ )
VclPtr<Control> DbTimeField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& /*rxModel*/ )
{
    WinBits _nFieldStyle = bSpinButton ? (WB_REPEAT | WB_SPIN) : 0;
    return VclPtr<TimeField>::Create( _pParent, _nFieldStyle );
    return VclPtr<TimeControl>::Create(pParent, bSpinButton);
}

CellControllerRef DbTimeField::CreateController() const
{
    return new ::svt::FormattedFieldCellController(static_cast<FormattedControlBase*>(m_pWindow.get()));
}

void DbTimeField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
@@ -2292,23 +2296,28 @@ void DbTimeField::implAdjustGenericFieldSetting( const Reference< XPropertySet >
    OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_TIMEMAX ) >>= aMax );
    bool    bStrict     = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );

    static_cast< TimeField* >( m_pWindow.get() )->SetExtFormat( static_cast<ExtTimeFieldFormat>(nFormat) );
    static_cast< TimeField* >( m_pWindow.get() )->SetMin( aMin );
    static_cast< TimeField* >( m_pWindow.get() )->SetMax( aMax );
    static_cast< TimeField* >( m_pWindow.get() )->SetStrictFormat( bStrict );
    static_cast< TimeField* >( m_pWindow.get() )->EnableEmptyFieldValue( true );
    FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
    weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());

    static_cast< TimeField* >( m_pPainter.get() )->SetExtFormat( static_cast<ExtTimeFieldFormat>(nFormat) );
    static_cast< TimeField* >( m_pPainter.get() )->SetMin( aMin );
    static_cast< TimeField* >( m_pPainter.get() )->SetMax( aMax );
    static_cast< TimeField* >( m_pPainter.get() )->SetStrictFormat( bStrict );
    static_cast< TimeField* >( m_pPainter.get() )->EnableEmptyFieldValue( true );
    rControlFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat));
    rControlFormatter.SetMin(aMin);
    rControlFormatter.SetMax(aMax);
    rControlFormatter.SetStrictFormat(bStrict);
    rControlFormatter.EnableEmptyField(true);

    FormattedControlBase* pPainter = static_cast<FormattedControlBase*>(m_pPainter.get());
    weld::TimeFormatter& rPainterFormatter = static_cast<weld::TimeFormatter&>(pPainter->get_formatter());

    rPainterFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat));
    rPainterFormatter.SetMin(aMin);
    rPainterFormatter.SetMax(aMax);
    rPainterFormatter.SetStrictFormat(bStrict);
    rPainterFormatter.EnableEmptyField(true);
}

namespace
{

    OUString lcl_setFormattedTime_nothrow( TimeField& _rField, const Reference< XColumn >& _rxField )
    OUString lcl_setFormattedTime_nothrow(TimeControl& _rField, const Reference<XColumn>& _rxField)
    {
        OUString sTime;
        if ( _rxField.is() )
@@ -2316,12 +2325,10 @@ namespace
            try
            {
                css::util::Time aValue = _rxField->getTime();
                if ( _rxField->wasNull() )
                    _rField.SetText( sTime );
                else
                if (!_rxField->wasNull())
                {
                    _rField.SetTime( ::tools::Time( aValue ) );
                    sTime = _rField.GetText();
                    static_cast<weld::TimeFormatter&>(_rField.get_formatter()).SetTime( ::tools::Time( aValue ) );
                    sTime = _rField.get_widget().get_text();
                }
            }
            catch( const Exception& )
@@ -2335,34 +2342,41 @@ namespace

OUString DbTimeField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, Color** /*ppColor*/)
{
    return lcl_setFormattedTime_nothrow( *static_cast< TimeField* >( m_pPainter.get() ), _rxField );
    return lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pPainter.get()), _rxField);
}

void DbTimeField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
{
    lcl_setFormattedTime_nothrow( *static_cast< TimeField* >( m_pWindow.get() ), _rxField );
    lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pWindow.get()), _rxField);
}

void DbTimeField::updateFromModel( Reference< XPropertySet > _rxModel )
{
    OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTimeField::updateFromModel: invalid call!" );

    FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
    weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());

    util::Time aTime;
    if ( _rxModel->getPropertyValue( FM_PROP_TIME ) >>= aTime )
        static_cast< TimeField* >( m_pWindow.get() )->SetTime( ::tools::Time( aTime ) );
        rControlFormatter.SetTime(::tools::Time(aTime));
    else
        static_cast< TimeField* >( m_pWindow.get() )->SetText( OUString() );
        pControl->get_widget().set_text(OUString());
}

bool DbTimeField::commitControl()
{
    OUString aText(m_pWindow->GetText());
    FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
    OUString aText(pControl->get_widget().get_text());
    Any aVal;
    if (!aText.isEmpty())
        aVal <<= static_cast<TimeField*>(m_pWindow.get())->GetTime().GetUNOTime();
    else
        aVal.clear();

    fprintf(stderr, "text is %s\n", aText.toUtf8().getStr());

    if (!aText.isEmpty())   // not empty
    {
        weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());
        aVal <<= rControlFormatter.GetTime().GetUNOTime();
    }
    m_rColumn.getModel()->setPropertyValue(FM_PROP_TIME, aVal);
    return true;
}
diff --git a/svx/source/inc/gridcell.hxx b/svx/source/inc/gridcell.hxx
index 121720b..6b4a3af 100644
--- a/svx/source/inc/gridcell.hxx
+++ b/svx/source/inc/gridcell.hxx
@@ -576,11 +576,11 @@ protected:
    virtual void    implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
};


class DbTimeField : public DbSpinField
{
public:
    DbTimeField(DbGridColumn& _rColumn);
    virtual ::svt::CellControllerRef CreateController() const override;
    virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, Color** ppColor = nullptr) override;
    virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;

diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx
index acf1ba3..21161e3 100644
--- a/vcl/source/app/weldutils.cxx
+++ b/vcl/source/app/weldutils.cxx
@@ -294,13 +294,20 @@ void DoubleNumericFormatter::ResetConformanceTester()

LongCurrencyFormatter::LongCurrencyFormatter(weld::Entry& rEntry)
    : EntryFormatter(rEntry)
    , m_bThousandSep(true)
{
    Init();
}

LongCurrencyFormatter::LongCurrencyFormatter(weld::FormattedSpinButton& rSpinButton)
    : EntryFormatter(rSpinButton)
    , m_bThousandSep(true)
{
    Init();
}

void LongCurrencyFormatter::Init()
{
    SetOutputHdl(LINK(this, LongCurrencyFormatter, FormatOutputHdl));
    SetInputHdl(LINK(this, LongCurrencyFormatter, ParseInputHdl));
}
@@ -318,6 +325,89 @@ void LongCurrencyFormatter::SetCurrencySymbol(const OUString& rStr)
}

LongCurrencyFormatter::~LongCurrencyFormatter() = default;

TimeFormatter::TimeFormatter(weld::Entry& rEntry)
    : EntryFormatter(rEntry)
    , m_eFormat(TimeFieldFormat::F_NONE)
    , m_eTimeFormat(TimeFormat::Hour24)
    , m_bDuration(false)
{
    Init();
}

TimeFormatter::TimeFormatter(weld::FormattedSpinButton& rSpinButton)
    : EntryFormatter(rSpinButton)
    , m_eFormat(TimeFieldFormat::F_NONE)
    , m_eTimeFormat(TimeFormat::Hour24)
    , m_bDuration(false)
{
    Init();
}

void TimeFormatter::Init()
{
    SetOutputHdl(LINK(this, TimeFormatter, FormatOutputHdl));
    SetInputHdl(LINK(this, TimeFormatter, ParseInputHdl));

    SetMin(tools::Time(0, 0));
    SetMax(tools::Time(23, 59, 59, 999999999));

    // so the spin size can depend on which zone the cursor is in
    get_widget().connect_cursor_position(LINK(this, TimeFormatter, CursorChangedHdl));
    // and set the initial spin size
    CursorChangedHdl(get_widget());
}

void TimeFormatter::SetExtFormat(ExtTimeFieldFormat eFormat)
{
    switch (eFormat)
    {
        case ExtTimeFieldFormat::Short24H:
        {
            m_eTimeFormat = TimeFormat::Hour24;
            m_bDuration = false;
            m_eFormat = TimeFieldFormat::F_NONE;
        }
        break;
        case ExtTimeFieldFormat::Long24H:
        {
            m_eTimeFormat = TimeFormat::Hour24;
            m_bDuration = false;
            m_eFormat = TimeFieldFormat::F_SEC;
        }
        break;
        case ExtTimeFieldFormat::Short12H:
        {
            m_eTimeFormat = TimeFormat::Hour12;
            m_bDuration = false;
            m_eFormat = TimeFieldFormat::F_NONE;
        }
        break;
        case ExtTimeFieldFormat::Long12H:
        {
            m_eTimeFormat = TimeFormat::Hour12;
            m_bDuration = false;
            m_eFormat = TimeFieldFormat::F_SEC;
        }
        break;
        case ExtTimeFieldFormat::ShortDuration:
        {
            m_bDuration = true;
            m_eFormat = TimeFieldFormat::F_NONE;
        }
        break;
        case ExtTimeFieldFormat::LongDuration:
        {
            m_bDuration = true;
            m_eFormat = TimeFieldFormat::F_SEC;
        }
        break;
    }

    ReFormat();
}

TimeFormatter::~TimeFormatter() = default;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/control/field2.cxx b/vcl/source/control/field2.cxx
index 6b69d19..2388daa 100644
--- a/vcl/source/control/field2.cxx
+++ b/vcl/source/control/field2.cxx
@@ -30,6 +30,7 @@
#include <vcl/toolkit/field.hxx>
#include <vcl/unohelp.hxx>
#include <vcl/settings.hxx>
#include <vcl/weldutils.hxx>

#include <svdata.hxx>

@@ -2705,4 +2706,84 @@ void TimeBox::ReformatAll()
    SetUpdateMode( true );
}

namespace weld
{
    tools::Time TimeFormatter::ConvertValue(int nValue)
    {
        tools::Time aTime(0);
        aTime.MakeTimeFromMS(nValue);
        return aTime;
    }

    int TimeFormatter::ConvertValue(const tools::Time& rTime)
    {
        return rTime.GetMSFromTime();
    }

    void TimeFormatter::SetTime(const tools::Time& rTime)
    {
        SetValue(ConvertValue(rTime));
    }

    tools::Time TimeFormatter::GetTime()
    {
        return ConvertValue(GetValue());
    }

    void TimeFormatter::SetMin(const tools::Time& rNewMin)
    {
        SetMinValue(ConvertValue(rNewMin));
    }

    void TimeFormatter::SetMax(const tools::Time& rNewMax)
    {
        SetMaxValue(ConvertValue(rNewMax));
    }

    OUString TimeFormatter::FormatNumber(int nValue) const
    {
        const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
        return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, m_eTimeFormat, m_bDuration, rLocaleData);
    }

    IMPL_LINK_NOARG(TimeFormatter, FormatOutputHdl, LinkParamNone*, bool)
    {
        OUString sText = FormatNumber(GetValue());
        ImplSetTextImpl(sText, nullptr);
        return true;
    }

    IMPL_LINK(TimeFormatter, ParseInputHdl, sal_Int64*, result, TriState)
    {
        const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();

        tools::Time aResult(0);
        bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, m_eFormat, m_bDuration, rLocaleDataWrapper);
        if (bRet)
            *result = ConvertValue(aResult);

        return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
    }

    IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
    {
        int nStartPos, nEndPos;
        rEntry.get_selection_bounds(nStartPos, nEndPos);

        const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
        const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, GetEntryText(), nEndPos, rLocaleData);

        int nIncrements = 1;

        if (nTimeArea == 1)
            nIncrements = 1000 * 60 * 60;
        else if (nTimeArea == 2)
            nIncrements = 1000 * 60;
        else if (nTimeArea == 3)
            nIncrements = 1000;

        SetSpinSize(nIncrements);
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 5738891..368c429 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -360,8 +360,8 @@ namespace weld
        m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);

        const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
        const int nTimeArea = TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos,
                                                         rLocaleData);
        const int nTimeArea = ::TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos,
                                                           rLocaleData);

        int nIncrements = 1;

@@ -390,12 +390,9 @@ namespace weld

    IMPL_LINK(TimeSpinButton, spin_button_input, int*, result, bool)
    {
        int nStartPos, nEndPos;
        m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);

        const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
        tools::Time aResult(0);
        bool bRet = TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData);
        bool bRet = ::TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData);
        if (bRet)
            *result = ConvertValue(aResult);
        return bRet;
@@ -426,7 +423,7 @@ namespace weld
    OUString TimeSpinButton::format_number(int nValue) const
    {
        const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
        return TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData);
        return ::TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData);
    }

    EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)