framework/source/uielement/spinfieldtoolbarcontroller.cxx |   10 +-
 include/vcl/formatter.hxx                                 |   27 +++++
 include/vcl/weldutils.hxx                                 |    6 -
 sw/qa/extras/layout/layout2.cxx                           |   13 ++
 sw/source/core/text/redlnitr.cxx                          |   10 ++
 vcl/inc/salvtables.hxx                                    |    2 
 vcl/source/app/salvtables.cxx                             |   15 +--
 vcl/source/app/weldutils.cxx                              |    6 -
 vcl/source/control/field2.cxx                             |   19 ++--
 vcl/source/control/fmtfield.cxx                           |   65 ++++++++------
 vcl/source/control/longcurr.cxx                           |    9 +
 11 files changed, 121 insertions(+), 61 deletions(-)

New commits:
commit 572207aa731ab95feef562601d4e08e915e4718e
Author:     Michael Weghorn <[email protected]>
AuthorDate: Wed Feb 19 22:45:53 2025 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Thu Feb 20 18:24:49 2025 +0100

    tdf#130857 Formatter: Extract helper method to convert text to value
    
    Move the existing logic from Formatter::ImplGetValue
    that converts the text to a double value into a new
    method Formatter::ParseText and call it from
    Formatter::ImplGetValue.
    
    It returns a std::optional, which correspond to
    the `true`/`false` return values of Formatter::ImplGetValue
    (empty std::optional means `false`, i.e. no value could be
    retrieved from the text).
    
    The newly introduced method will be reused to
    implement text -> value conversion for
    QtInstanceFormattedSpinButton.
    
    Change-Id: I475fc97c78feb05703a9a134b004d1c12ccd855a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181943
    Reviewed-by: Michael Weghorn <[email protected]>
    Tested-by: Jenkins

diff --git a/include/vcl/formatter.hxx b/include/vcl/formatter.hxx
index c09d0e133cc4..49c2bc853e7a 100644
--- a/include/vcl/formatter.hxx
+++ b/include/vcl/formatter.hxx
@@ -268,6 +268,7 @@ public:
     void    SetFormatValueHdl(const Link<double, std::optional<OUString>>& 
rLink) { m_aFormatValueHdl = rLink; }
 
     OUString FormatValue(double fValue);
+    std::optional<double> ParseText(const OUString& rText);
 public:
 
     //The following methods are interesting, if m_bTreatAsNumber is set to 
sal_False
diff --git a/vcl/source/control/fmtfield.cxx b/vcl/source/control/fmtfield.cxx
index e3cad69651e0..6b83403e2931 100644
--- a/vcl/source/control/fmtfield.cxx
+++ b/vcl/source/control/fmtfield.cxx
@@ -784,32 +784,20 @@ void Formatter::ImplSetValue(double dVal, bool bForce)
     m_ValueState = valueDouble;
 }
 
-bool Formatter::ImplGetValue(double& dNewVal)
+std::optional<double> Formatter::ParseText(const OUString& rText)
 {
-    dNewVal = m_dCurrentValue;
-    if (m_ValueState == valueDouble)
-        return true;
-
-    // tdf#155241 default to m_dDefaultValue only if explicitly set
-    // otherwise default to m_dCurrentValue
-    if (m_bDefaultValueSet)
-        dNewVal = m_dDefaultValue;
-
-    OUString sText(GetEntryText());
-    if (sText.isEmpty())
-        return true;
-
+    double fValue = 0.0;
     bool bUseExternalFormatterValue = false;
     if (m_aParseTextHdl.IsSet())
     {
-        ParseResult aResult = m_aParseTextHdl.Call(sText);
+        ParseResult aResult = m_aParseTextHdl.Call(rText);
         bUseExternalFormatterValue = aResult.m_eState != TRISTATE_INDET;
         if (bUseExternalFormatterValue)
         {
             if (aResult.m_eState == TRISTATE_TRUE)
-                dNewVal = aResult.m_fValue;
+                fValue = aResult.m_fValue;
             else
-                dNewVal = m_dCurrentValue;
+                fValue = m_dCurrentValue;
         }
     }
 
@@ -822,6 +810,7 @@ bool Formatter::ImplGetValue(double& dNewVal)
             nFormatKey = 0;
 
         // special treatment for percentage formatting
+        OUString sText = rText;
         if (GetOrCreateFormatter().GetType(m_nFormatKey) == 
SvNumFormatType::PERCENT)
         {
             // the language of our format
@@ -839,14 +828,37 @@ 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 (!GetOrCreateFormatter().IsNumberFormat(sText, nFormatKey, dNewVal))
-            return false;
+        if (!GetOrCreateFormatter().IsNumberFormat(sText, nFormatKey, fValue))
+            return std::optional<double>();
     }
 
-    if (m_bHasMin && (dNewVal<m_dMinValue))
-        dNewVal = m_dMinValue;
-    if (m_bHasMax && (dNewVal>m_dMaxValue))
-        dNewVal = m_dMaxValue;
+    if (m_bHasMin && (fValue < m_dMinValue))
+        fValue = m_dMinValue;
+    if (m_bHasMax && (fValue > m_dMaxValue))
+        fValue = m_dMaxValue;
+    return std::optional<double>(fValue);
+}
+
+bool Formatter::ImplGetValue(double& dNewVal)
+{
+    dNewVal = m_dCurrentValue;
+    if (m_ValueState == valueDouble)
+        return true;
+
+    // tdf#155241 default to m_dDefaultValue only if explicitly set
+    // otherwise default to m_dCurrentValue
+    if (m_bDefaultValueSet)
+        dNewVal = m_dDefaultValue;
+
+    OUString sText(GetEntryText());
+    if (sText.isEmpty())
+        return true;
+
+    std::optional<double> aValue = ParseText(sText);
+    if (!aValue.has_value())
+        return false;
+
+    dNewVal = aValue.value();
     return true;
 }
 
commit eee97f8c66a60e2faf7485d6d59a6dc425a2eca5
Author:     Michael Weghorn <[email protected]>
AuthorDate: Wed Feb 19 17:50:39 2025 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Thu Feb 20 18:24:41 2025 +0100

    tdf#130857 Formatter: Pass string to convert to double
    
    Instead of letting the implementations set for
    Formatter::m_aInputHdl via Formatter::SetInputHdl
    always retrieve the current text and parse
    that one, pass the text to convert as a parameter
    instead.
    
    Introduce a new struct Formatter::ParseResult
    so that both, the state (which describes whether
    parsing was successful and/or a handler was set)
    and the value can still be returned at the same time.
    (Link/The handlers only supports a single param. So far,
    the state was returned by the function result and
    the value was returned via an out param, but the
    single param is now used for an input param to pass the
    text.)
    
    Make it the responsibility of Formatter::ImplGetValue
    to pass the current text instead of having the handlers
    read it themselves.
    
    Rename the members accordingly:
    
    * Formatter::m_aInputHdl -> Formatter:m_aParseTextHdl
    * Formatter::SetInputHdl -> Formatter::SetParseTextHdl
    
    This is similar to
    
        Change-Id: I6b3fbccc89b89446a1ea367a80aeed3a7b583298
        Author: Michael Weghorn <[email protected]>
        Date:   Tue Feb 18 20:29:20 2025 +0100
    
            tdf#130857 Let Formatter:m_aOutputHdl convert double to text
    
    (but for the conversion the other way around) and
    
        commit 15ad64a412d80fdf05ee1783bcc277753eaa34bc
        Author: Michael Weghorn <[email protected]>
        Date:   Sat Feb 15 11:22:54 2025 +0100
    
            tdf#130857 weld: Let SpinButton input hdl only parse value from 
string
    
    (which is a similar change, but in weld::SpinButton).
    
    No change in behavior is intended by this change
    by itself.
    
    The primary motivation is to allow reusing the
    logic to convert a string to a value in order
    to implement QtDoubleSpinBox::valueFromText
    for QtInstanceFormattedSpinButton.
    
    Change-Id: If6837409aadbe9cc37fdbb4c0650f5e1305229f7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181942
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/framework/source/uielement/spinfieldtoolbarcontroller.cxx 
b/framework/source/uielement/spinfieldtoolbarcontroller.cxx
index 13ffcdc0cb44..ef180bd5d51d 100644
--- a/framework/source/uielement/spinfieldtoolbarcontroller.cxx
+++ b/framework/source/uielement/spinfieldtoolbarcontroller.cxx
@@ -62,7 +62,7 @@ public:
 
     DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void);
     DECL_LINK(FormatOutputHdl, double, std::optional<OUString>);
-    DECL_LINK(ParseInputHdl, double*, TriState);
+    DECL_STATIC_LINK(SpinfieldControl, ParseInputHdl, const OUString&, 
Formatter::ParseResult);
     DECL_LINK(ModifyHdl, weld::Entry&, void);
     DECL_LINK(ActivateHdl, weld::Entry&, bool);
     DECL_LINK(FocusInHdl, weld::Widget&, void);
@@ -85,7 +85,7 @@ SpinfieldControl::SpinfieldControl(vcl::Window* pParent, 
SpinfieldToolbarControl
     m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl));
     Formatter& rFormatter = m_xWidget->GetFormatter();
     rFormatter.SetFormatValueHdl(LINK(this, SpinfieldControl, 
FormatOutputHdl));
-    rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl));
+    rFormatter.SetParseTextHdl(LINK(this, SpinfieldControl, ParseInputHdl));
     m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, 
ValueChangedHdl));
     m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl));
     m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl));
@@ -103,10 +103,10 @@ IMPL_LINK(SpinfieldControl, KeyInputHdl, const 
::KeyEvent&, rKEvt, bool)
     return ChildKeyInput(rKEvt);
 }
 
-IMPL_LINK(SpinfieldControl, ParseInputHdl, double*, result, TriState)
+IMPL_STATIC_LINK(SpinfieldControl, ParseInputHdl, const OUString&, rText, 
Formatter::ParseResult)
 {
-    *result = m_xWidget->get_text().toDouble();
-    return TRISTATE_TRUE;
+    const double fValue = rText.toDouble();
+    return Formatter::ParseResult(TRISTATE_TRUE, fValue);
 }
 
 SpinfieldControl::~SpinfieldControl()
diff --git a/include/vcl/formatter.hxx b/include/vcl/formatter.hxx
index 889c20a879f8..c09d0e133cc4 100644
--- a/include/vcl/formatter.hxx
+++ b/include/vcl/formatter.hxx
@@ -99,6 +99,28 @@ public:
         UNLESS_MERGELIBS(VCL_DLLPUBLIC) static SvNumberFormatter* 
GetFormatter();
     };
 
+    /**
+     * This struct gets returned by the handlers that can be set via 
Formatter::SetParseTextHdl
+     * and describes the result of trying to convert text to a double value.
+     */
+    struct ParseResult
+    {
+        ParseResult()
+            : ParseResult(TRISTATE_INDET, 0.0)
+        {
+        }
+
+        ParseResult(TriState eState, double fValue)
+            : m_eState(eState)
+            , m_fValue(fValue)
+        {
+        }
+
+        TriState m_eState;
+        // if m_eState is TRISTATE_TRUE, this contains the value
+        double m_fValue;
+    };
+
 protected:
     OUString      m_sLastValidText;
     // Has nothing to do with the current value. It is the last text, which 
was valid at input (checked by CheckText,
@@ -143,7 +165,7 @@ protected:
 
     bool                m_bUseInputStringForFormatting;
 
-    Link<double*, TriState> m_aInputHdl;
+    Link<const OUString&, ParseResult> m_aParseTextHdl;
     Link<double, std::optional<OUString>> m_aFormatValueHdl;
 
 public:
@@ -242,7 +264,7 @@ public:
     bool    TreatingAsNumber() const    { return m_bTreatAsNumber; }
     void    TreatAsNumber(bool bDoSo) { m_bTreatAsNumber = bDoSo; }
 
-    void    SetInputHdl(const Link<double*,TriState>& rLink) { m_aInputHdl = 
rLink; }
+    void    SetParseTextHdl(const Link<const OUString&, ParseResult>& rLink) { 
m_aParseTextHdl = rLink; }
     void    SetFormatValueHdl(const Link<double, std::optional<OUString>>& 
rLink) { m_aFormatValueHdl = rLink; }
 
     OUString FormatValue(double fValue);
diff --git a/include/vcl/weldutils.hxx b/include/vcl/weldutils.hxx
index 05a8ba2b22bd..8a3291c42345 100644
--- a/include/vcl/weldutils.hxx
+++ b/include/vcl/weldutils.hxx
@@ -285,7 +285,7 @@ public:
 
 private:
     DECL_DLLPRIVATE_LINK(FormatOutputHdl, double, std::optional<OUString>);
-    DECL_DLLPRIVATE_LINK(ParseInputHdl, double*, TriState);
+    DECL_DLLPRIVATE_LINK(ParseInputHdl, const OUString&, 
Formatter::ParseResult);
 
     SAL_DLLPRIVATE void Init();
 
@@ -313,7 +313,7 @@ public:
 
 private:
     DECL_DLLPRIVATE_LINK(FormatOutputHdl, double, std::optional<OUString>);
-    DECL_DLLPRIVATE_LINK(ParseInputHdl, double*, TriState);
+    DECL_DLLPRIVATE_LINK(ParseInputHdl, const OUString&, 
Formatter::ParseResult);
     DECL_DLLPRIVATE_LINK(CursorChangedHdl, weld::Entry&, void);
 
     SAL_DLLPRIVATE void Init();
@@ -346,7 +346,7 @@ public:
 
 private:
     DECL_DLLPRIVATE_LINK(FormatOutputHdl, double, std::optional<OUString>);
-    DECL_DLLPRIVATE_LINK(ParseInputHdl, double*, TriState);
+    DECL_DLLPRIVATE_LINK(ParseInputHdl, const OUString&, 
Formatter::ParseResult);
     DECL_DLLPRIVATE_LINK(CursorChangedHdl, weld::Entry&, void);
 
     SAL_DLLPRIVATE void Init();
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 5be5314157c9..f9793e2d6771 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -681,7 +681,7 @@ private:
     DECL_LINK(UpDownHdl, SpinField&, void);
     DECL_LINK(LoseFocusHdl, Control&, void);
     DECL_LINK(OutputHdl, double, std::optional<OUString>);
-    DECL_LINK(InputHdl, double*, TriState);
+    DECL_LINK(InputHdl, const OUString&, Formatter::ParseResult);
     DECL_LINK(ActivateHdl, Edit&, bool);
 
 public:
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 89c024ab220d..172f889ccfec 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5856,7 +5856,7 @@ 
SalInstanceSpinButton::SalInstanceSpinButton(FormattedField* pButton, SalInstanc
     m_xButton->SetDownHdl(LINK(this, SalInstanceSpinButton, UpDownHdl));
     m_xButton->SetLoseFocusHdl(LINK(this, SalInstanceSpinButton, 
LoseFocusHdl));
     m_rFormatter.SetFormatValueHdl(LINK(this, SalInstanceSpinButton, 
OutputHdl));
-    m_rFormatter.SetInputHdl(LINK(this, SalInstanceSpinButton, InputHdl));
+    m_rFormatter.SetParseTextHdl(LINK(this, SalInstanceSpinButton, InputHdl));
     if (Edit* pEdit = m_xButton->GetSubEdit())
         pEdit->SetActivateHdl(LINK(this, SalInstanceSpinButton, ActivateHdl));
     else
@@ -5913,7 +5913,7 @@ SalInstanceSpinButton::~SalInstanceSpinButton()
         pEdit->SetActivateHdl(Link<Edit&, bool>());
     else
         m_xButton->SetActivateHdl(Link<Edit&, bool>());
-    m_rFormatter.SetInputHdl(Link<double*, TriState>());
+    m_rFormatter.SetParseTextHdl(Link<const OUString&, 
Formatter::ParseResult>());
     m_rFormatter.SetFormatValueHdl(Link<double, std::optional<OUString>>());
     m_xButton->SetLoseFocusHdl(Link<Control&, void>());
     m_xButton->SetDownHdl(Link<SpinField&, void>());
@@ -5936,13 +5936,12 @@ IMPL_LINK(SalInstanceSpinButton, OutputHdl, double, 
fValue, std::optional<OUStri
     return format_floating_point_value(fValue);
 }
 
-IMPL_LINK(SalInstanceSpinButton, InputHdl, double*, pResult, TriState)
+IMPL_LINK(SalInstanceSpinButton, InputHdl, const OUString&, rText, 
Formatter::ParseResult)
 {
-    double fResult;
-    TriState eRet = parse_text(get_text(), &fResult);
-    if (eRet == TRISTATE_TRUE)
-        *pResult = fResult;
-    return eRet;
+    double fResult = 0;
+    TriState eRet = parse_text(rText, &fResult);
+
+    return Formatter::ParseResult(eRet, fResult);
 }
 
 SalInstanceFormattedSpinButton::SalInstanceFormattedSpinButton(FormattedField* 
pButton,
diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx
index 801a76f4eda6..51a933cab74a 100644
--- a/vcl/source/app/weldutils.cxx
+++ b/vcl/source/app/weldutils.cxx
@@ -318,7 +318,7 @@ 
LongCurrencyFormatter::LongCurrencyFormatter(weld::FormattedSpinButton& rSpinBut
 void LongCurrencyFormatter::Init()
 {
     SetFormatValueHdl(LINK(this, LongCurrencyFormatter, FormatOutputHdl));
-    SetInputHdl(LINK(this, LongCurrencyFormatter, ParseInputHdl));
+    SetParseTextHdl(LINK(this, LongCurrencyFormatter, ParseInputHdl));
 }
 
 void LongCurrencyFormatter::SetUseThousandSep(bool b)
@@ -358,7 +358,7 @@ void TimeFormatter::Init()
     DisableRemainderFactor(); //so with hh::mm::ss, incrementing mm will not 
reset ss
 
     SetFormatValueHdl(LINK(this, TimeFormatter, FormatOutputHdl));
-    SetInputHdl(LINK(this, TimeFormatter, ParseInputHdl));
+    SetParseTextHdl(LINK(this, TimeFormatter, ParseInputHdl));
 
     SetMin(tools::Time(0, 0));
     SetMax(tools::Time(23, 59, 59, 999999999));
@@ -442,7 +442,7 @@ DateFormatter::DateFormatter(weld::Entry& rEntry)
 void DateFormatter::Init()
 {
     SetFormatValueHdl(LINK(this, DateFormatter, FormatOutputHdl));
-    SetInputHdl(LINK(this, DateFormatter, ParseInputHdl));
+    SetParseTextHdl(LINK(this, DateFormatter, ParseInputHdl));
 
     SetMin(Date(1, 1, 1900));
     SetMax(Date(31, 12, 2200));
diff --git a/vcl/source/control/field2.cxx b/vcl/source/control/field2.cxx
index fc0cab000da8..c581b0381599 100644
--- a/vcl/source/control/field2.cxx
+++ b/vcl/source/control/field2.cxx
@@ -2252,17 +2252,19 @@ namespace weld
         return std::optional<OUString>(FormatNumber(fValue));
     }
 
-    IMPL_LINK(DateFormatter, ParseInputHdl, double*, result, TriState)
+    IMPL_LINK(DateFormatter, ParseInputHdl, const OUString&, rText, 
Formatter::ParseResult)
     {
         const LocaleDataWrapper& rLocaleDataWrapper = 
Application::GetSettings().GetLocaleDataWrapper();
 
         Date aResult(Date::EMPTY);
-        bool bRet = ::DateFormatter::TextToDate(GetEntryText(), aResult, 
ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
+        bool bRet = ::DateFormatter::TextToDate(rText, aResult, 
ResolveSystemFormat(m_eFormat, rLocaleDataWrapper),
                                                 rLocaleDataWrapper, 
GetCalendarWrapper());
+
+        double fValue = 0;
         if (bRet)
-            *result = aResult.GetDate();
+            fValue = aResult.GetDate();
 
-        return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
+        return Formatter::ParseResult(bRet ? TRISTATE_TRUE : TRISTATE_FALSE, 
fValue);
     }
 }
 
@@ -3148,16 +3150,17 @@ namespace weld
         return std::optional<OUString>(FormatNumber(fValue));
     }
 
-    IMPL_LINK(TimeFormatter, ParseInputHdl, double*, result, TriState)
+    IMPL_LINK(TimeFormatter, ParseInputHdl, const OUString&, rText, 
Formatter::ParseResult)
     {
         const LocaleDataWrapper& rLocaleDataWrapper = 
Application::GetSettings().GetLocaleDataWrapper();
 
+        double fValue = 0.0;
         tools::Time aResult(tools::Time::EMPTY);
-        bool bRet = ::TimeFormatter::TextToTime(GetEntryText(), aResult, 
m_eFormat, m_bDuration, rLocaleDataWrapper);
+        bool bRet = ::TimeFormatter::TextToTime(rText, aResult, m_eFormat, 
m_bDuration, rLocaleDataWrapper);
         if (bRet)
-            *result = ConvertValue(aResult);
+            fValue = ConvertValue(aResult);
 
-        return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
+        return Formatter::ParseResult(bRet ? TRISTATE_TRUE : TRISTATE_FALSE, 
fValue);
     }
 
     IMPL_LINK(TimeFormatter, CursorChangedHdl, weld::Entry&, rEntry, void)
diff --git a/vcl/source/control/fmtfield.cxx b/vcl/source/control/fmtfield.cxx
index 8dc97702e533..e3cad69651e0 100644
--- a/vcl/source/control/fmtfield.cxx
+++ b/vcl/source/control/fmtfield.cxx
@@ -800,15 +800,14 @@ bool Formatter::ImplGetValue(double& dNewVal)
         return true;
 
     bool bUseExternalFormatterValue = false;
-    if (m_aInputHdl.IsSet())
+    if (m_aParseTextHdl.IsSet())
     {
-        double fResult;
-        auto eState = m_aInputHdl.Call(&fResult);
-        bUseExternalFormatterValue = eState != TRISTATE_INDET;
+        ParseResult aResult = m_aParseTextHdl.Call(sText);
+        bUseExternalFormatterValue = aResult.m_eState != TRISTATE_INDET;
         if (bUseExternalFormatterValue)
         {
-            if (eState == TRISTATE_TRUE)
-                dNewVal = fResult;
+            if (aResult.m_eState == TRISTATE_TRUE)
+                dNewVal = aResult.m_fValue;
             else
                 dNewVal = m_dCurrentValue;
         }
diff --git a/vcl/source/control/longcurr.cxx b/vcl/source/control/longcurr.cxx
index a1698afb1f90..475f80939abb 100644
--- a/vcl/source/control/longcurr.cxx
+++ b/vcl/source/control/longcurr.cxx
@@ -225,17 +225,18 @@ namespace weld
         return std::optional<OUString>(aText);
     }
 
-    IMPL_LINK(LongCurrencyFormatter, ParseInputHdl, double*, result, TriState)
+    IMPL_LINK(LongCurrencyFormatter, ParseInputHdl, const OUString&, rText, 
Formatter::ParseResult)
     {
         const LocaleDataWrapper& rLocaleDataWrapper = 
Application::GetSettings().GetLocaleDataWrapper();
 
         BigInt value;
-        bool bRet = ImplCurrencyGetValue(GetEntryText(), value, 
GetDecimalDigits(), rLocaleDataWrapper);
+        bool bRet = ImplCurrencyGetValue(rText, value, GetDecimalDigits(), 
rLocaleDataWrapper);
 
+        double fValue = 0;
         if (bRet)
-            *result = double(value) / 
weld::SpinButton::Power10(GetDecimalDigits());
+            fValue = double(value) / 
weld::SpinButton::Power10(GetDecimalDigits());
 
-        return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
+        return Formatter::ParseResult(bRet ? TRISTATE_TRUE : TRISTATE_FALSE, 
fValue);
     }
 }
 
commit 21bc6e075d8b004bce7bf7d9305f5fdcff2df24d
Author:     Michael Stahl <[email protected]>
AuthorDate: Wed Feb 19 16:20:56 2025 +0100
Commit:     Michael Stahl <[email protected]>
CommitDate: Thu Feb 20 18:24:37 2025 +0100

    sw: fix bug with redline following fieldmark or hidden break
    
    It is now possible for HideIterator to move to another node without a
    redline being involved, but if this happens and there was no redline on
    the previous node, GetRedlinePos() had already returned
    SwRedlineTable::npos and now no redlines can be found in the new node.
    
    (regression from commit 657de5fba12b0e9afcdee361654d2a2d0dbd7311)
    
    Change-Id: I536b2660c97501be896203403cdd5e2f2015e816
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181946
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>

diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 9364add9ecaf..1e73f97af75e 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -959,6 +959,19 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testHiddenParaProps)
     CPPUNIT_ASSERT_EQUAL(
         SvxAdjust::Left,
         
pTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().Get(RES_PARATR_ADJUST).GetAdjust());
+
+    dispatchCommand(mxComponent, u".uno:ShowTrackedChanges"_ustr, {});
+
+    pTextFrame = dynamic_cast<SwTextFrame*>(pBody->GetLower());
+    for (int i = 0; i < 18; ++i)
+    {
+        pTextFrame = dynamic_cast<SwTextFrame*>(pTextFrame->GetNext());
+    }
+    // the problem was that this redline (following hidden) wasn't merged
+    CPPUNIT_ASSERT_EQUAL(u"10 visible, hidden-merge, visible, delete-merge, 
visible"_ustr,
+                         pTextFrame->GetText());
+    pTextFrame = dynamic_cast<SwTextFrame*>(pTextFrame->GetNext());
+    CPPUNIT_ASSERT_EQUAL(u"abcdefghi"_ustr, pTextFrame->GetText());
 }
 
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf151954)
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index ab9dfb59cea0..8b583d6860d8 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -78,6 +78,7 @@ private:
     /// current start/end pair
     SwPosition const* m_pStartPos;
     SwPosition const* m_pEndPos;
+    SwNode const* m_pCurrentRedlineNode;
 
 public:
     SwPosition const* GetStartPos() const { return m_pStartPos; }
@@ -95,6 +96,7 @@ public:
         , m_RedlineIndex(isHideRedlines ? m_rIDRA.GetRedlinePos(rTextNode, 
RedlineType::Any) : SwRedlineTable::npos)
         , m_pStartPos(nullptr)
         , m_pEndPos(&m_Start)
+        , m_pCurrentRedlineNode(&rTextNode)
     {
     }
 
@@ -108,6 +110,14 @@ public:
         assert(m_pEndPos);
         if (m_isHideRedlines)
         {
+            // GetRedlinePos() returns npos if there is no redline on the
+            // node but something else could have merged nodes so search again!
+            if (m_RedlineIndex == SwRedlineTable::npos
+                && &m_pEndPos->GetNode() != m_pCurrentRedlineNode)
+            {
+                m_RedlineIndex = m_rIDRA.GetRedlinePos(m_pEndPos->GetNode(), 
RedlineType::Any);
+                m_pCurrentRedlineNode = &m_pEndPos->GetNode();
+            }
             // position on current or next redline
             for (; m_RedlineIndex < m_rIDRA.GetRedlineTable().size(); 
++m_RedlineIndex)
             {

Reply via email to