offapi/com/sun/star/sheet/SortNumberBehavior.idl            |   27 ++-
 sc/inc/sc.hrc                                               |    1 
 sc/inc/sortparam.hxx                                        |   14 +
 sc/qa/extras/macros-test.cxx                                |   37 ++++
 sc/qa/extras/testdocuments/tdf161948_NaturalSort_OldWay.ods |binary
 sc/qa/uitest/sort/naturalSort.py                            |   16 +-
 sc/qa/uitest/sort/tdf95192.py                               |    9 -
 sc/qa/uitest/sort/tdf99208.py                               |    5 
 sc/qa/uitest/sort/tdf99627.py                               |    8 -
 sc/qa/uitest/sort/tdf99773.py                               |    3 
 sc/qa/unit/subsequent_filters_test5.cxx                     |    8 -
 sc/qa/unit/ucalc_sort.cxx                                   |   96 +++++++++++-
 sc/sdi/scalc.sdi                                            |    2 
 sc/source/core/data/sortparam.cxx                           |   14 +
 sc/source/core/data/table3.cxx                              |   68 ++++++--
 sc/source/filter/xml/XMLExportDatabaseRanges.cxx            |    7 
 sc/source/filter/xml/xmlsorti.cxx                           |    5 
 sc/source/ui/dbgui/tpsort.cxx                               |   40 ++++-
 sc/source/ui/inc/tpsort.hxx                                 |    2 
 sc/source/ui/unoobj/datauno.cxx                             |   12 -
 sc/source/ui/view/cellsh2.cxx                               |   19 +-
 sc/source/ui/view/gridwin.cxx                               |    4 
 sc/uiconfig/scalc/ui/sortoptionspage.ui                     |   66 +++++++-
 23 files changed, 388 insertions(+), 75 deletions(-)

New commits:
commit 45e8e48301c100e35da4e5da3d643a92ab06ba17
Author:     Regina Henschel <[email protected]>
AuthorDate: Thu Sep 18 13:44:34 2025 +0200
Commit:     Regina Henschel <[email protected]>
CommitDate: Sun Oct 5 12:37:49 2025 +0200

    tdf#161948 support embedded-number-behavior='integer'
    
    The element <table:sort> has the values 'alpha-numeric', 'double' and
    'integer' in its attribute embedded-number-behavior in ODF. The member
    bNaturalSort in ScSortParam covers only 'alpha-numeric' and 'double'.
    
    The patch replaces the member bNaturalSort of ScSortParam with a new
    eSortNumberBehavior. That takes the three values from enum
    ScSortNumberBehavior. Only the parameter 'SfxBoolItem NaturalSort
    SID_SORT_NATURALSORT' in SID_SORT is kept so that existing macros that
    use it still work.
    
    Change-Id: I2a346223767a5a4aae79bac5fa08e608b0cea503
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191159
    Reviewed-by: Regina Henschel <[email protected]>
    Tested-by: Jenkins
    Reviewed-by: Heiko Tietze <[email protected]>

diff --git a/offapi/com/sun/star/sheet/SortNumberBehavior.idl 
b/offapi/com/sun/star/sheet/SortNumberBehavior.idl
index 61c5f0937964..e868e062e86a 100644
--- a/offapi/com/sun/star/sheet/SortNumberBehavior.idl
+++ b/offapi/com/sun/star/sheet/SortNumberBehavior.idl
@@ -11,10 +11,8 @@ module com {  module sun {  module star {  module sheet {
 
 /** Describes how numbers inside text are handled in text comparisons.
 
-    The constants correspond to the ODF attribute
-    table:embedded-number-behavior (19.628, part 3 ODF 1.4).
-    That has values 'alpha-numeric', 'double' and 'integer'.
-    Value 'integer' is not yet implemented.
+    <p>The constants correspond to the ODF attribute
+    table:embedded-number-behavior (19.628, part 3 ODF 1.4).</p>
 
     @since LibreOffice 26.2
  */
@@ -22,18 +20,27 @@ published constants SortNumberBehavior
 {
     /** Digits inside text are compared alphanumerically.
 
-        "K10" < "K2" < "K3", for example.
+        <p>"K10" &lt; "K2" &lt; "K3", for example.</p>
       */
     const long ALPHA_NUMERIC = 0;
 
-    /** Comparison of text uses natural sort.
+    /** Comparison of text uses natural sort with decimal numbers.
 
-        "K2" < "K3" < "K10", for example. The number parts inside the text
-        may be decimal numbers. Which character is considered a decimal
-        separator, depends on the language of the text.
-        Read ODF standard for details.
+        <p>Example: "K2" &lt; "K3" &lt; "K10".<br/>
+        The number parts inside the text may be decimal numbers.
+        Which character is considered a decimal separator, depends on
+        the locale. Read ODF standard for details.<br/>
+        Example with dot as decimal separator: "K2.40" &lt; "K2.5"</p>
       */
     const long DOUBLE = 1;
+
+    /** Comparison of text uses natural sort with integer numbers.
+
+        <p>Any decimal separator is treated as ordinary character.
+        A fractional part is handled as separate number.<br/>
+        Example: "K2.5" &lt; "K2.40", because 5 &lt; 40.</p>
+      */
+    const long INTEGER = 2;
 };
 
 }; }; }; };
diff --git a/sc/inc/sc.hrc b/sc/inc/sc.hrc
index c79f87ea08b1..2a1181655748 100644
--- a/sc/inc/sc.hrc
+++ b/sc/inc/sc.hrc
@@ -644,6 +644,7 @@ static_assert(SID_PREVIEW_END < SID_KEYFUNC_START, "calc 
slots ids trampling inf
 #define SID_SORT_NATURALSORT    TypedWhichId<SfxBoolItem>(SC_PARAM_START+5)
 #define SID_SORT_INCCOMMENTS    TypedWhichId<SfxBoolItem>(SC_PARAM_START+6)
 #define SID_SORT_INCIMAGES      TypedWhichId<SfxBoolItem>(SC_PARAM_START+7)
+#define SID_SORT_NUMBERBEHAVIOR TypedWhichId<SfxInt32Item>(SC_PARAM_START+8)
 
 // Sidebar -------------------------------------------------------------
 
diff --git a/sc/inc/sortparam.hxx b/sc/inc/sortparam.hxx
index 1dd105b6d9be..3f266984bd47 100644
--- a/sc/inc/sortparam.hxx
+++ b/sc/inc/sortparam.hxx
@@ -25,6 +25,7 @@
 
 #include "address.hxx"
 #include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/sheet/SortNumberBehavior.hpp>
 #include "scdllapi.h"
 #include "celltextattr.hxx"
 #include "cellvalue.hxx"
@@ -114,6 +115,17 @@ struct ScDataAreaExtras
     }
 };
 
+/** Specifies how numbers embedded in text are treated in text
+    comparisons. The values correspond to the ODF attribute
+    table:embedded-number-behavior (19.628, part 3 ODF 1.4).
+*/
+enum class ScSortNumberBehavior : sal_Int32
+{
+    ALPHA_NUMERIC = css::sheet::SortNumberBehavior::ALPHA_NUMERIC, // 0
+    DOUBLE = css::sheet::SortNumberBehavior::DOUBLE, // 1
+    INTEGER  = css::sheet::SortNumberBehavior::INTEGER, // 2
+};
+
 struct SC_DLLPUBLIC ScSortParam
 {
     SCCOL       nCol1;
@@ -126,7 +138,7 @@ struct SC_DLLPUBLIC ScSortParam
     bool        bHasHeader;
     bool        bByRow;
     bool        bCaseSens;
-    bool        bNaturalSort;
+    ScSortNumberBehavior eSortNumberBehavior;
     bool        bUserDef;
     bool        bInplace;
     SCTAB       nDestTab;
diff --git a/sc/qa/extras/macros-test.cxx b/sc/qa/extras/macros-test.cxx
index dd5a178a2ec7..50d98027d4dd 100644
--- a/sc/qa/extras/macros-test.cxx
+++ b/sc/qa/extras/macros-test.cxx
@@ -1004,7 +1004,7 @@ CPPUNIT_TEST_FIXTURE(ScMacrosTest, 
testTdf161948NaturalSortAPI)
     // The source has "K3", "K10", "K104", "K23", "K2" in Range A2:A6 and 
label in A1.
     // The result goes to range C1:C6.
 
-    // Enable natural sorting and sort. Examine cell C2
+    // Enable natural sorting "double" and sort. Examine cell C2
     executeMacro(u"vnd.sun.star.script:Standard.SortTest.EnableNaturalSort"
                  "?language=Basic&location=document"_ustr);
     OUString sCellContent = pDoc->GetString(2, 1, 0);
@@ -1034,6 +1034,41 @@ CPPUNIT_TEST_FIXTURE(ScMacrosTest, 
testTdf81003_DateCellToVbaUDF)
     CPPUNIT_ASSERT_EQUAL(45898.0, pDoc->GetValue(1, 0, 0));
 }
 
+CPPUNIT_TEST_FIXTURE(ScMacrosTest, testTdf161948NaturalSortDispatcher)
+{
+    // Since LibreOffice 26.2 the feature natural sort is available in the 
API. Prior to that,
+    // natural sort by macro was only possible by using the dispatcher.
+    // Here we test with a Basic macro, that the old way still works.
+    createScDoc("tdf161948_NaturalSort_OldWay.ods");
+    ScDocument* pDoc = getScDoc();
+
+    // The source has "ID", "K3", "K10", "K104", "K23", "K2" in Range A1:A6.
+
+    // The macro enables natural sorting via parameter NaturalSort=true of 
SID_SORT.
+    
executeMacro(u"vnd.sun.star.script:Standard.SortTest.Dispatcher_NaturalSort"
+                 "?language=Basic&location=document"_ustr);
+
+    // Verify sort result. Sorting via dispatcher is always 'inplace' thus 
results are in A1:A6.
+    const std::array<OUString, 6> aExpectedNaturalSort
+        = { u"ID"_ustr, u"K2"_ustr, u"K3"_ustr, u"K10"_ustr, u"K23"_ustr, 
u"K104"_ustr };
+    for (SCROW nRow = 0; nRow <= 5; nRow++) // ScAddress(col, row, tab)
+    {
+        CPPUNIT_ASSERT_EQUAL(aExpectedNaturalSort[nRow], 
pDoc->GetString(ScAddress(0, nRow, 0)));
+    }
+
+    // Same test for alpha-numeric sort, that is NaturalSort=false.
+    
executeMacro(u"vnd.sun.star.script:Standard.SortTest.Dispatcher_AlphaNumeric"
+                 "?language=Basic&location=document"_ustr);
+
+    // Verify sort result
+    const std::array<OUString, 6> aExpectedAlphaNumeric
+        = { u"ID"_ustr, u"K10"_ustr, u"K104"_ustr, u"K2"_ustr, u"K23"_ustr, 
u"K3"_ustr };
+    for (SCROW nRow = 0; nRow <= 5; nRow++)
+    {
+        CPPUNIT_ASSERT_EQUAL(aExpectedAlphaNumeric[nRow], 
pDoc->GetString(ScAddress(0, nRow, 0)));
+    }
+}
+
 ScMacrosTest::ScMacrosTest()
       : ScModelTestBase(u"/sc/qa/extras/testdocuments"_ustr)
 {
diff --git a/sc/qa/extras/testdocuments/tdf161948_NaturalSort_OldWay.ods 
b/sc/qa/extras/testdocuments/tdf161948_NaturalSort_OldWay.ods
new file mode 100644
index 000000000000..54698e57fa36
Binary files /dev/null and 
b/sc/qa/extras/testdocuments/tdf161948_NaturalSort_OldWay.ods differ
diff --git a/sc/qa/uitest/sort/naturalSort.py b/sc/qa/uitest/sort/naturalSort.py
index 91c2f35c3f71..e26d6371a11c 100644
--- a/sc/qa/uitest/sort/naturalSort.py
+++ b/sc/qa/uitest/sort/naturalSort.py
@@ -30,9 +30,14 @@ class CalcNaturalSorting(UITestCase):
             #Open sort dialog by DATA - SORT
             with self.ui_test.execute_dialog_through_command(".uno:DataSort") 
as xDialog:
                 xTabs = xDialog.getChild("tabcontrol")
-                select_pos(xTabs, "0")
+                select_pos(xTabs, "1")
                 xNatural = xDialog.getChild("naturalsort")
-                xNatural.executeAction("CLICK", tuple())
+                if (get_state_as_dict(xNatural)["Selected"]) == "false":
+                    xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
+
             #Verify
             self.assertEqual(get_cell_by_position(document, 0, 0, 
0).getString(), "MW100SSMOU456.996JIL4")
             self.assertEqual(get_cell_by_position(document, 0, 0, 
1).getString(), "MW101SSMOU456.996JIL4")
@@ -62,6 +67,9 @@ class CalcNaturalSorting(UITestCase):
                 xNatural = xDialog.getChild("naturalsort")
                 if (get_state_as_dict(xNatural)["Selected"]) == "false":
                     xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
 
             self.assertEqual(get_cell_by_position(document, 0, 3, 
0).getString(), "MW-1")
             self.assertEqual(get_cell_by_position(document, 0, 3, 
1).getString(), "MW-2")
@@ -88,6 +96,10 @@ class CalcNaturalSorting(UITestCase):
                 xNatural = xDialog.getChild("naturalsort")
                 if (get_state_as_dict(xNatural)["Selected"]) == "false":
                     xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
+
                 select_pos(xTabs, "0")
                 xleftright = xDialog.getChild("rbLeftRight")
                 xleftright.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/sort/tdf95192.py b/sc/qa/uitest/sort/tdf95192.py
index 1c33f56f7bf6..c5fe9f057592 100644
--- a/sc/qa/uitest/sort/tdf95192.py
+++ b/sc/qa/uitest/sort/tdf95192.py
@@ -7,7 +7,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #
 from uitest.framework import UITestCase
-from uitest.uihelper.common import get_url_for_data_file, select_pos
+from uitest.uihelper.common import get_url_for_data_file, select_pos, 
get_state_as_dict
 
 from libreoffice.calc.document import get_cell_by_position
 from libreoffice.uno.propertyvalue import mkPropertyValues
@@ -27,7 +27,12 @@ class tdf95192(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "1")
                 xNatural = xDialog.getChild("naturalsort")
-                xNatural.executeAction("CLICK", tuple())
+                if (get_state_as_dict(xNatural)["Selected"]) == "false":
+                    xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
+
             #Verify
             self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 
0).getString(), "Sal. Capra 1/17")
             self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 
1).getString(), "Sal. Capra 1/20")
diff --git a/sc/qa/uitest/sort/tdf99208.py b/sc/qa/uitest/sort/tdf99208.py
index e740a9c4a596..9e6870630180 100644
--- a/sc/qa/uitest/sort/tdf99208.py
+++ b/sc/qa/uitest/sort/tdf99208.py
@@ -29,9 +29,12 @@ class tdf99208(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "1")
                 xNatural = xDialog.getChild("naturalsort")
-                xFormats = xDialog.getChild("formats")
                 if (get_state_as_dict(xNatural)["Selected"]) == "false":
                     xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
+                xFormats = xDialog.getChild("formats")
                 if (get_state_as_dict(xFormats)["Selected"]) == "false":
                     xFormats.executeAction("CLICK", tuple())
                 select_pos(xTabs, "0")
diff --git a/sc/qa/uitest/sort/tdf99627.py b/sc/qa/uitest/sort/tdf99627.py
index 281f4340de46..25dc3b75268d 100644
--- a/sc/qa/uitest/sort/tdf99627.py
+++ b/sc/qa/uitest/sort/tdf99627.py
@@ -7,7 +7,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #
 from uitest.framework import UITestCase
-from uitest.uihelper.common import get_url_for_data_file, select_pos
+from uitest.uihelper.common import get_url_for_data_file, select_pos, 
get_state_as_dict
 
 from libreoffice.calc.document import get_cell_by_position
 from libreoffice.uno.propertyvalue import mkPropertyValues
@@ -27,8 +27,12 @@ class tdf99627(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "1")
                 xNatural = xDialog.getChild("naturalsort")
+                if (get_state_as_dict(xNatural)["Selected"]) == "false":
+                    xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
                 xdown = xDialog.getChild("down")
-                xNatural.executeAction("CLICK", tuple())
                 select_pos(xTabs, "0")
                 xdown.executeAction("CLICK", tuple())
             #Verify
diff --git a/sc/qa/uitest/sort/tdf99773.py b/sc/qa/uitest/sort/tdf99773.py
index 49775899fb02..3367fa132f66 100644
--- a/sc/qa/uitest/sort/tdf99773.py
+++ b/sc/qa/uitest/sort/tdf99773.py
@@ -36,6 +36,9 @@ class tdf99773(UITestCase):
                 xNatural = xDialog.getChild("naturalsort")
                 if (get_state_as_dict(xNatural)["Selected"]) == "false":
                     xNatural.executeAction("CLICK", tuple())
+                xNumberBehaviorDouble = xDialog.getChild("doublenaturalsortrb")
+                if (get_state_as_dict(xNumberBehaviorDouble)["Checked"]) == 
"false":
+                    xNumberBehaviorDouble.executeAction("CLICK", tuple())
             #Verify
             self.assertEqual(get_cell_by_position(document, 0, 0, 
0).getString(), "A 2")
             self.assertEqual(get_cell_by_position(document, 0, 0, 
1).getString(), "A 5")
diff --git a/sc/qa/unit/subsequent_filters_test5.cxx 
b/sc/qa/unit/subsequent_filters_test5.cxx
index 7bcb4e9bc32a..8f16c130fb7b 100644
--- a/sc/qa/unit/subsequent_filters_test5.cxx
+++ b/sc/qa/unit/subsequent_filters_test5.cxx
@@ -224,7 +224,7 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest5, 
testTdf161948_NaturalSortSaveLoad)
     ScDBData* pDBData = pDoc->GetDBAtArea(0, 0, 0, 0, 5); // tab, col1, row1, 
col2, row2
     ScSortParam aSortParam; // that is a struct
     pDBData->GetSortParam(aSortParam);
-    aSortParam.bNaturalSort = true;
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::DOUBLE;
 
     // The output range and the ScDBData are only updated, when you actual 
sort using the new
     // parameters.
@@ -248,10 +248,10 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest5, 
testTdf161948_NaturalSortSaveLoad)
     pDoc = getScDoc();
     pDBData = pDoc->GetDBAtArea(0, 0, 0, 0, 5);
     pDBData->GetSortParam(aSortParam);
-    CPPUNIT_ASSERT(aSortParam.bNaturalSort);
+    CPPUNIT_ASSERT_EQUAL(ScSortNumberBehavior::DOUBLE, 
aSortParam.eSortNumberBehavior);
 
     // disable natural sorted
-    aSortParam.bNaturalSort = false;
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
     pDocSh = getScDocShell();
     ScDBDocFunc aFunc2(*pDocSh);
     bSorted = aFunc2.Sort(0, aSortParam, true, true, true);
@@ -273,7 +273,7 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest5, 
testTdf161948_NaturalSortSaveLoad)
     pDoc = getScDoc();
     pDBData = pDoc->GetDBAtArea(0, 0, 0, 0, 5);
     pDBData->GetSortParam(aSortParam);
-    CPPUNIT_ASSERT(!aSortParam.bNaturalSort);
+    CPPUNIT_ASSERT_EQUAL(ScSortNumberBehavior::ALPHA_NUMERIC, 
aSortParam.eSortNumberBehavior);
 }
 
 CPPUNIT_TEST_FIXTURE(ScFiltersTest5, testTdf168589)
diff --git a/sc/qa/unit/ucalc_sort.cxx b/sc/qa/unit/ucalc_sort.cxx
index 86e44d9f9699..07cd73792d64 100644
--- a/sc/qa/unit/ucalc_sort.cxx
+++ b/sc/qa/unit/ucalc_sort.cxx
@@ -2254,7 +2254,7 @@ CPPUNIT_TEST_FIXTURE(TestSort, 
testLanguageDependentNaturalSort)
     aSortParam.nRow1 = 0;
     aSortParam.nRow2 = 6;
     aSortParam.bHasHeader = true;
-    aSortParam.bNaturalSort = true; // needs to be adapted when mode 'integer' 
is implemented
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::DOUBLE;
     aSortParam.bInplace = false;
     aSortParam.nDestTab = 0;
     aSortParam.nDestCol = 2;
@@ -2283,6 +2283,100 @@ CPPUNIT_TEST_FIXTURE(TestSort, 
testLanguageDependentNaturalSort)
     m_pDoc->DeleteTab(0);
 }
 
+CPPUNIT_TEST_FIXTURE(TestSort, testSortEmbeddedNumberTypes)
+{
+    // LibreOffice 26.2 introduces a new type INTEGER for sorting with numbers 
embedded in text. It
+    // is a natural sort in principle. But decimal separators are treated as 
normal characters. Thus
+    // even it would be a valid decimal number for the current local, integer 
part and fractional
+    // part are treated as separate integer values.
+    // The previous "natural sort" is type DOUBLE now. The previous non 
natural sort is type
+    // ALPHA_NUMERIC now.
+
+    // Force the system locale to "en-US" for to have a dot as well defined 
decimal separator
+    SvtSysLocaleOptions aOptions;
+    OUString sLocaleConfigString = aOptions.GetLanguageTag().getBcp47();
+    aOptions.SetLocaleConfigString(u"en-US"_ustr);
+    aOptions.Commit();
+    comphelper::ScopeGuard g([&aOptions, &sLocaleConfigString] {
+        aOptions.SetLocaleConfigString(sLocaleConfigString);
+        aOptions.Commit();
+    });
+
+    // Generate test data
+    m_pDoc->InsertTab(0, u"NaturalSortTest"_ustr);
+    m_pDoc->SetString(ScAddress(0,0,0),u"Item"_ustr); // ScAddress(col, row, 
tab)
+    m_pDoc->SetString(ScAddress(0,1,0),u"K2.5"_ustr);
+    m_pDoc->SetString(ScAddress(0,2,0),u"K2.501"_ustr);
+    m_pDoc->SetString(ScAddress(0,3,0),u"K10"_ustr);
+    m_pDoc->SetString(ScAddress(0,4,0),u"K1.104"_ustr);
+    m_pDoc->SetString(ScAddress(0,5,0),u"K1.2"_ustr);
+    m_pDoc->SetString(ScAddress(0,6,0),u"K2.40"_ustr);
+    m_pDoc->SetAnonymousDBData(
+        0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 
0, 0, 6)));
+
+    // Create sort parameters
+    ScSortParam aSortParam;
+    aSortParam.nCol1 = 0;
+    aSortParam.nCol2 = 0;
+    aSortParam.nRow1 = 0;
+    aSortParam.nRow2 = 6;
+    aSortParam.bHasHeader = true;
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::INTEGER;
+    aSortParam.bInplace = false;
+    aSortParam.nDestTab = 0;
+    aSortParam.nDestCol = 2;
+    aSortParam.nDestRow = 0;
+    // no aSortParam.aCollatorLocale. If no local is set, the global local is 
used, here en_US
+    aSortParam.maKeyState[0].bDoSort = true;
+    aSortParam.maKeyState[0].nField = 0;
+    aSortParam.maKeyState[0].bAscending = true;
+    aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
+
+    // Actually sort
+    ScDBDocFunc aFunc(*m_xDocShell);
+    bool bSorted = aFunc.Sort(0, aSortParam, true, true, true);
+    CPPUNIT_ASSERT(bSorted);
+
+    // Verify sort result: Item | K1.2 | K1.104 | K2.5 | K2.40 | K2.501 | K10
+    const std::array<OUString, 7> aExpectedInt
+        = { u"Item"_ustr, u"K1.2"_ustr, u"K1.104"_ustr, u"K2.5"_ustr,
+            u"K2.40"_ustr, u"K2.501"_ustr, u"K10"_ustr };
+    for (SCROW nRow = 0; nRow <= 6; nRow++)
+    {
+        CPPUNIT_ASSERT_EQUAL(aExpectedInt[nRow], 
m_pDoc->GetString(ScAddress(2, nRow, 0)));
+    }
+
+    // Make sure that type ScSortNumberBehavior::DOUBLE works as well
+    // It sorts according the values of the decimal numbers.
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::DOUBLE;
+    bSorted = aFunc.Sort(0, aSortParam, true, true, true);
+    CPPUNIT_ASSERT(bSorted);
+
+    const std::array<OUString, 7> aExpectedDbl
+        = { u"Item"_ustr, u"K1.104"_ustr, u"K1.2"_ustr, u"K2.40"_ustr,
+            u"K2.5"_ustr, u"K2.501"_ustr, u"K10"_ustr };
+    for (SCROW nRow = 0; nRow <= 6; nRow++)
+    {
+        CPPUNIT_ASSERT_EQUAL(aExpectedDbl[nRow], 
m_pDoc->GetString(ScAddress(2, nRow, 0)));
+    }
+
+    // And same for type ScSortNumberBehavior::ALPHA_NUMERIC
+    // It treats digits and decimal separator of the numbers as normal 
characters.
+    aSortParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
+    bSorted = aFunc.Sort(0, aSortParam, true, true, true);
+    CPPUNIT_ASSERT(bSorted);
+
+    const std::array<OUString, 7> aExpectedAlpha
+        = { u"Item"_ustr, u"K1.104"_ustr, u"K1.2"_ustr, u"K10"_ustr,
+            u"K2.40"_ustr, u"K2.5"_ustr, u"K2.501"_ustr };
+    for (SCROW nRow = 0; nRow <= 6; nRow++)
+    {
+        CPPUNIT_ASSERT_EQUAL(aExpectedAlpha[nRow], 
m_pDoc->GetString(ScAddress(2, nRow, 0)));
+    }
+
+    m_pDoc->DeleteTab(0);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/sdi/scalc.sdi b/sc/sdi/scalc.sdi
index 740ab2693f71..98f4c02599ed 100644
--- a/sc/sdi/scalc.sdi
+++ b/sc/sdi/scalc.sdi
@@ -1178,7 +1178,7 @@ SfxVoidItem DataSelect SID_DATA_SELECT
 
 
 SfxVoidItem DataSort SID_SORT
-(SfxBoolItem ByRows SID_SORT_BYROW,SfxBoolItem HasHeader 
SID_SORT_HASHEADER,SfxBoolItem CaseSensitive SID_SORT_CASESENS,SfxBoolItem 
NaturalSort SID_SORT_NATURALSORT,SfxBoolItem IncludeAttribs 
SID_SORT_ATTRIBS,SfxUInt16Item UserDefIndex SID_SORT_USERDEF,SfxInt32Item Col1 
FN_PARAM_1,SfxBoolItem Ascending1 FN_PARAM_2,SfxInt32Item Col2 
FN_PARAM_3,SfxBoolItem Ascending2 FN_PARAM_4,SfxInt32Item Col3 
FN_PARAM_5,SfxBoolItem Ascending3 FN_PARAM_6,SfxBoolItem IncludeComments 
SID_SORT_INCCOMMENTS, SfxBoolItem IncludeImages SID_SORT_INCIMAGES)
+(SfxBoolItem ByRows SID_SORT_BYROW,SfxBoolItem HasHeader 
SID_SORT_HASHEADER,SfxBoolItem CaseSensitive SID_SORT_CASESENS,SfxBoolItem 
NaturalSort SID_SORT_NATURALSORT,SfxBoolItem IncludeAttribs 
SID_SORT_ATTRIBS,SfxUInt16Item UserDefIndex SID_SORT_USERDEF,SfxInt32Item Col1 
FN_PARAM_1,SfxBoolItem Ascending1 FN_PARAM_2,SfxInt32Item Col2 
FN_PARAM_3,SfxBoolItem Ascending2 FN_PARAM_4,SfxInt32Item Col3 
FN_PARAM_5,SfxBoolItem Ascending3 FN_PARAM_6,SfxBoolItem IncludeComments 
SID_SORT_INCCOMMENTS, SfxBoolItem IncludeImages SID_SORT_INCIMAGES, 
SfxInt32Item NumberBehavior SID_SORT_NUMBERBEHAVIOR)
 [
     AutoUpdate = FALSE,
     FastCall = FALSE,
diff --git a/sc/source/core/data/sortparam.cxx 
b/sc/source/core/data/sortparam.cxx
index 868cf38a8df8..9e583bbeb5d2 100644
--- a/sc/source/core/data/sortparam.cxx
+++ b/sc/source/core/data/sortparam.cxx
@@ -38,7 +38,7 @@ ScSortParam::ScSortParam( const ScSortParam& r ) :
         aDataAreaExtras(r.aDataAreaExtras),
         nUserIndex(r.nUserIndex),
         bHasHeader(r.bHasHeader),bByRow(r.bByRow),bCaseSens(r.bCaseSens),
-        bNaturalSort(r.bNaturalSort),
+        eSortNumberBehavior(r.eSortNumberBehavior),
         bUserDef(r.bUserDef),
         bInplace(r.bInplace),
         nDestTab(r.nDestTab),nDestCol(r.nDestCol),nDestRow(r.nDestRow),
@@ -63,7 +63,8 @@ void ScSortParam::Clear()
     nCompatHeader = 2;
     nDestTab = 0;
     nUserIndex = 0;
-    bHasHeader=bCaseSens=bUserDef=bNaturalSort = false;
+    bHasHeader=bCaseSens=bUserDef = false;
+    eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
     bByRow = bInplace = true;
     aCollatorLocale = css::lang::Locale();
     aCollatorAlgorithm.clear();
@@ -89,7 +90,7 @@ ScSortParam& ScSortParam::operator=( const ScSortParam& r )
     bHasHeader      = r.bHasHeader;
     bByRow          = r.bByRow;
     bCaseSens       = r.bCaseSens;
-    bNaturalSort    = r.bNaturalSort;
+    eSortNumberBehavior = r.eSortNumberBehavior;
     bUserDef        = r.bUserDef;
     bInplace        = r.bInplace;
     nDestTab        = r.nDestTab;
@@ -133,7 +134,7 @@ bool ScSortParam::operator==( const ScSortParam& rOther ) 
const
         && (bHasHeader      == rOther.bHasHeader)
         && (bByRow          == rOther.bByRow)
         && (bCaseSens       == rOther.bCaseSens)
-        && (bNaturalSort    == rOther.bNaturalSort)
+        && (eSortNumberBehavior == rOther.eSortNumberBehavior)
         && (bUserDef        == rOther.bUserDef)
         && (nUserIndex      == rOther.nUserIndex)
         && (bInplace        == rOther.bInplace)
@@ -163,7 +164,8 @@ ScSortParam::ScSortParam( const ScSubTotalParam& rSub, 
const ScSortParam& rOld )
         nSourceTab(0),
         aDataAreaExtras(rOld.aDataAreaExtras),
         nUserIndex(rSub.nUserIndex),
-        
bHasHeader(true),bByRow(true),bCaseSens(rSub.bCaseSens),bNaturalSort(rOld.bNaturalSort),
+        bHasHeader(true),bByRow(true),bCaseSens(rSub.bCaseSens),
+        eSortNumberBehavior(rOld.eSortNumberBehavior),
         bUserDef(rSub.bUserDef),
         bInplace(true),
         nDestTab(0),nDestCol(0),nDestRow(0),
@@ -212,7 +214,7 @@ ScSortParam::ScSortParam( const ScQueryParam& rParam, SCCOL 
nCol ) :
         nSourceTab(rParam.nTab),
         nUserIndex(0),
         bHasHeader(rParam.bHasHeader),bByRow(true),bCaseSens(rParam.bCaseSens),
-        bNaturalSort(false),
+        eSortNumberBehavior(ScSortNumberBehavior::ALPHA_NUMERIC),
 //TODO: what about Locale and Algorithm?
         bUserDef(false),
         bInplace(true),
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index a8eb314f935b..a2e09a087c1c 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -100,11 +100,15 @@ using namespace ::com::sun::star::i18n;
     Number converted from the middle number string
     If no number was found, fNum is unchanged.
 
+    @param bInteger
+    If value is true, any decimal separator is treated as ordinary character. 
Use value true
+    in case of ScSortNumberBehavior::INTEGER.
+
     @return Returns TRUE if a numeral element is found in a given string, or
     FALSE if no numeral element is found.
 */
 static bool SplitString(const OUString &sWhole, const LanguageTag& 
rLanguageTag, OUString &sPrefix,
-                        OUString &sSuffix, double &fNum)
+                        OUString &sSuffix, double &fNum, const bool &bInteger 
= false)
 {
     // Get prefix element, search for any digit and stop.
     sal_Int32 nPos = 0;
@@ -134,6 +138,23 @@ static bool SplitString(const OUString &sWhole, const 
LanguageTag& rLanguageTag,
         return false;
     }
 
+    if (bInteger)
+    {
+        // ScSortNumberBehavior::INTEGER treats decimal separators as ordinary 
character. If we have
+        // a separator in the string number part, we need to recalculate value 
and start of sSuffix.
+        const OUString sNumber = sWhole.copy(nPos, aPRNum.EndPos - nPos);
+        if (sNumber.indexOf(sUser.toChar(), 0) >=0)
+        {
+            ParseResult aPRNumInt = aCharClass.parsePredefinedToken(
+                KParseType::ANY_NUMBER, sNumber.getToken(0, sUser.toChar()), 0,
+                KParseTokens::ANY_NUMBER, u""_ustr, KParseTokens::ANY_NUMBER, 
sUser);
+            sPrefix = sWhole.copy(0, nPos);
+            fNum = aPRNumInt.Value;
+            sSuffix = sWhole.copy(nPos + aPRNumInt.EndPos);
+            return true;
+        }
+    }
+
     sPrefix = sWhole.copy( 0, nPos );
     fNum = aPRNum.Value;
     sSuffix = sWhole.copy( aPRNum.EndPos );
@@ -165,20 +186,24 @@ static bool SplitString(const OUString &sWhole, const 
LanguageTag& rLanguageTag,
     @param pCW
     Pointer to collator wrapper for normal string comparison
 
+    @param bInteger
+    Use value true in case of ScSortNumberBehavior::INTEGER.
+
     @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
     sInput2 is greater.
 */
 static short Compare(const OUString &sInput1, const OUString &sInput2,
                      const LanguageTag& rLanguageTag, const bool bCaseSens,
-                     const ScUserListData* pData, const CollatorWrapper *pCW)
+                     const ScUserListData* pData, const CollatorWrapper *pCW,
+                     const bool& bInteger = false)
 {
     OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
 
     do
     {
         double nNum1, nNum2;
-        bool bNumFound1 = SplitString( sStr1, rLanguageTag, sPre1, sSuf1, 
nNum1 );
-        bool bNumFound2 = SplitString( sStr2, rLanguageTag, sPre2, sSuf2, 
nNum2 );
+        bool bNumFound1 = SplitString( sStr1, rLanguageTag, sPre1, sSuf1, 
nNum1, bInteger);
+        bool bNumFound2 = SplitString( sStr2, rLanguageTag, sPre2, sSuf2, 
nNum2, bInteger);
 
         short nPreRes; // Prefix comparison result
         if ( pData )
@@ -226,7 +251,7 @@ static short Compare(const OUString &sInput1, const 
OUString &sInput2,
     return 0;
 }
 
-}
+} // namespace naturalsort
 
 // Assume that we can handle 512MB, which with a ~100 bytes
 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
@@ -1502,34 +1527,45 @@ short ScTable::CompareCell(
                     aStr2 = GetString(nCell2Col, nCell2Row);
 
                 bool bUserDef     = aSortParam.bUserDef;        // custom sort 
order
-                bool bNaturalSort = aSortParam.bNaturalSort;    // natural sort
                 bool bCaseSens    = aSortParam.bCaseSens;       // case 
sensitivity
                 LanguageTag aSortLanguageTag(aSortParam.aCollatorLocale);
 
                 ScUserList& rList = ScGlobal::GetUserList();
                 if (bUserDef && rList.size() > aSortParam.nUserIndex)
                 {
+                    // ToDo: ScUserListData uses internally always ScGlobal. A 
comparison using the
+                    // sort locale would require an extended version of 
ScUserListData. So for now in
+                    // case of bUserDef the sort locale is not evaluated.
                     const ScUserListData& rData = rList[aSortParam.nUserIndex];
-
-                    if ( bNaturalSort )
-                        nRes = naturalsort::Compare(aStr1, aStr2, 
aSortLanguageTag, bCaseSens,
-                                                    &rData, pSortCollator);
-                    else
+                    if (aSortParam.eSortNumberBehavior == 
ScSortNumberBehavior::ALPHA_NUMERIC)
                     {
                         if ( bCaseSens )
                             nRes = sal::static_int_cast<short>( 
rData.Compare(aStr1, aStr2) );
                         else
                             nRes = sal::static_int_cast<short>( 
rData.ICompare(aStr1, aStr2) );
                     }
-
+                    else if (aSortParam.eSortNumberBehavior == 
ScSortNumberBehavior::DOUBLE)
+                        nRes = naturalsort::Compare(aStr1, aStr2, 
aSortLanguageTag, bCaseSens,
+                                                    &rData, pSortCollator);
+                    else // ScSortNumberBehavior::INTEGER
+                    {
+                        nRes = naturalsort::Compare(aStr1, aStr2, 
aSortLanguageTag, bCaseSens,
+                                                    &rData, pSortCollator, 
true /*bInteger*/);
+                    }
                 }
                 if (!bUserDef)
                 {
-                    if ( bNaturalSort )
+                    if (aSortParam.eSortNumberBehavior == 
ScSortNumberBehavior::ALPHA_NUMERIC)
+                        nRes = sal::static_int_cast<short>(
+                            pSortCollator->compareString(aStr1, aStr2));
+                    else if (aSortParam.eSortNumberBehavior == 
ScSortNumberBehavior::DOUBLE)
                         nRes = naturalsort::Compare(aStr1, aStr2, 
aSortLanguageTag, bCaseSens,
-                                                    nullptr, pSortCollator );
-                    else
-                        nRes = static_cast<short>( 
pSortCollator->compareString( aStr1, aStr2 ) );
+                                                    nullptr, pSortCollator);
+                    else // ScSortNumberBehavior::INTEGER
+                    {
+                        nRes = naturalsort::Compare(aStr1, aStr2, 
aSortLanguageTag, bCaseSens,
+                                                    nullptr, pSortCollator, 
true /*bInteger*/);
+                    }
                 }
             }
             else if ( bStr1 )               // String <-> Number or Error
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx 
b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
index f030fd81ac20..b93d54be867e 100644
--- a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
@@ -85,10 +85,13 @@ void writeSort(ScXMLExport& mrExport, const ScSortParam& 
aParam, const ScRange&
     if (aParam.bCaseSens)
         mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, 
XML_TRUE);
 
-    if (aParam.bNaturalSort)
+    if (aParam.eSortNumberBehavior != ScSortNumberBehavior::ALPHA_NUMERIC
+        && mrExport.getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
     {
-        if (mrExport.getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+        if (aParam.eSortNumberBehavior == ScSortNumberBehavior::DOUBLE)
             mrExport.AddAttribute(XML_NAMESPACE_TABLE, 
XML_EMBEDDED_NUMBER_BEHAVIOR, XML_DOUBLE);
+        else
+            mrExport.AddAttribute(XML_NAMESPACE_TABLE, 
XML_EMBEDDED_NUMBER_BEHAVIOR, XML_INTEGER);
     }
 
     mrExport.AddLanguageTagAttributes( XML_NAMESPACE_TABLE, 
XML_NAMESPACE_TABLE, aParam.aCollatorLocale, false);
diff --git a/sc/source/filter/xml/xmlsorti.cxx 
b/sc/source/filter/xml/xmlsorti.cxx
index 5122911e1839..230e46ab7e6c 100644
--- a/sc/source/filter/xml/xmlsorti.cxx
+++ b/sc/source/filter/xml/xmlsorti.cxx
@@ -158,9 +158,10 @@ void SAL_CALL ScXMLSortContext::endFastElement( sal_Int32 
/*nElement*/ )
     pSortDescriptor[6].Name = SC_UNONAME_SORTFLD;
     pSortDescriptor[6].Value <<= aSortFields;
     pSortDescriptor[7].Name = SC_UNONAME_NUMBERBEHAVIOR;
-    // value 'integer' is not yet implemented. Map it to 'double'.
-    if (msEmbeddedNumberBehavior.equals(u"double"_ustr) || 
msEmbeddedNumberBehavior.equals(u"integer"_ustr))
+    if (msEmbeddedNumberBehavior.equals(u"double"_ustr))
         pSortDescriptor[7].Value <<= sheet::SortNumberBehavior::DOUBLE;
+    else if (msEmbeddedNumberBehavior.equals(u"integer"_ustr))
+        pSortDescriptor[7].Value <<= sheet::SortNumberBehavior::INTEGER;
     else
         pSortDescriptor[7].Value <<= sheet::SortNumberBehavior::ALPHA_NUMERIC;
     if (!maLanguageTagODF.isEmpty())
diff --git a/sc/source/ui/dbgui/tpsort.cxx b/sc/source/ui/dbgui/tpsort.cxx
index f19c821ac76e..0ac10173d1ff 100644
--- a/sc/source/ui/dbgui/tpsort.cxx
+++ b/sc/source/ui/dbgui/tpsort.cxx
@@ -513,6 +513,8 @@ ScTabPageSortOptions::ScTabPageSortOptions(weld::Container* 
pPage, weld::DialogC
     , m_xLbAlgorithm(m_xBuilder->weld_combo_box(u"algorithmlb"_ustr))
     , m_xBtnIncComments(m_xBuilder->weld_check_button(u"includenotes"_ustr))
     , m_xBtnIncImages(m_xBuilder->weld_check_button(u"includeimages"_ustr))
+    , 
m_xRBDoubleNaturalSort(m_xBuilder->weld_radio_button(u"doublenaturalsortrb"_ustr))
+    , 
m_xRBIntegerNaturalSort(m_xBuilder->weld_radio_button(u"integernaturalsortrb"_ustr))
 {
     
m_xLbSortUser->set_size_request(m_xLbSortUser->get_approximate_digit_width() * 
50, -1);
     m_xLbSortUser->set_accessible_description(ScResId(STR_A11Y_DESC_SORTUSER));
@@ -533,6 +535,7 @@ void ScTabPageSortOptions::Init()
     m_xLbOutPos->connect_changed( LINK( this, ScTabPageSortOptions, 
SelOutPosHdl ) );
     m_xBtnCopyResult->connect_toggled( LINK( this, ScTabPageSortOptions, 
EnableHdl ) );
     m_xBtnSortUser->connect_toggled( LINK( this, ScTabPageSortOptions, 
EnableHdl ) );
+    m_xBtnNaturalSort->connect_toggled( LINK( this, ScTabPageSortOptions, 
EnableHdl ) );
     m_xLbLanguage->connect_changed( LINK( this, ScTabPageSortOptions, 
FillAlgorHdl ) );
 
     pViewData = rSortItem.GetViewData();
@@ -594,7 +597,6 @@ void ScTabPageSortOptions::Reset( const SfxItemSet* /* 
rArgSet */ )
 
     m_xBtnCase->set_active( aSortData.bCaseSens );
     m_xBtnFormats->set_active( aSortData.aDataAreaExtras.mbCellFormats );
-    m_xBtnNaturalSort->set_active( aSortData.bNaturalSort );
     m_xBtnIncComments->set_active( aSortData.aDataAreaExtras.mbCellNotes );
     m_xBtnIncImages->set_active( aSortData.aDataAreaExtras.mbCellDrawObjects );
 
@@ -632,6 +634,30 @@ void ScTabPageSortOptions::Reset( const SfxItemSet* /* 
rArgSet */ )
         m_xEdOutPos->set_sensitive(false);
         m_xEdOutPos->set_text( OUString() );
     }
+
+    m_xBtnNaturalSort->set_sensitive(true);
+    if (aSortData.eSortNumberBehavior == ScSortNumberBehavior::ALPHA_NUMERIC)
+    {
+        m_xBtnNaturalSort->set_active(false);
+        m_xRBDoubleNaturalSort->set_sensitive(false);
+        m_xRBIntegerNaturalSort->set_sensitive(false);
+    }
+    else
+    {
+        m_xBtnNaturalSort->set_active(true);
+        m_xRBDoubleNaturalSort->set_sensitive(true);
+        m_xRBIntegerNaturalSort->set_sensitive(true);
+        if (aSortData.eSortNumberBehavior == ScSortNumberBehavior::DOUBLE)
+        {
+            m_xRBDoubleNaturalSort->set_active(true);
+            m_xRBIntegerNaturalSort->set_active(false);
+        }
+        else
+        {
+            m_xRBDoubleNaturalSort->set_active(false);
+            m_xRBIntegerNaturalSort->set_active(true);
+        }
+    }
 }
 
 bool ScTabPageSortOptions::FillItemSet( SfxItemSet* rArgSet )
@@ -646,7 +672,6 @@ bool ScTabPageSortOptions::FillItemSet( SfxItemSet* rArgSet 
)
             aNewSortData = pSortItem->GetSortData();
     }
     aNewSortData.bCaseSens       = m_xBtnCase->get_active();
-    aNewSortData.bNaturalSort    = m_xBtnNaturalSort->get_active();
     aNewSortData.aDataAreaExtras.mbCellNotes = m_xBtnIncComments->get_active();
     aNewSortData.aDataAreaExtras.mbCellDrawObjects = 
m_xBtnIncImages->get_active();
     aNewSortData.aDataAreaExtras.mbCellFormats = m_xBtnFormats->get_active();
@@ -675,6 +700,12 @@ bool ScTabPageSortOptions::FillItemSet( SfxItemSet* 
rArgSet )
     }
     aNewSortData.aCollatorAlgorithm = sAlg;
 
+    if (m_xBtnNaturalSort->get_active())
+        aNewSortData.eSortNumberBehavior = m_xRBDoubleNaturalSort->get_active()
+            ? ScSortNumberBehavior::DOUBLE : ScSortNumberBehavior::INTEGER;
+    else
+        aNewSortData.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
+
     rArgSet->Put( ScSortItem( SCITEM_SORTDATA, pViewData, &aNewSortData ) );
 
     return true;
@@ -786,6 +817,11 @@ IMPL_LINK( ScTabPageSortOptions, EnableHdl, 
weld::Toggleable&, rButton, void )
         else
             m_xLbSortUser->set_sensitive(false);
     }
+    else if (&rButton == m_xBtnNaturalSort.get())
+    {
+        m_xRBDoubleNaturalSort->set_sensitive(rButton.get_active());
+        m_xRBIntegerNaturalSort->set_sensitive(rButton.get_active());
+    }
 }
 
 IMPL_LINK(ScTabPageSortOptions, SelOutPosHdl, weld::ComboBox&, rLb, void)
diff --git a/sc/source/ui/inc/tpsort.hxx b/sc/source/ui/inc/tpsort.hxx
index b8c630aa9315..c5569308f3a2 100644
--- a/sc/source/ui/inc/tpsort.hxx
+++ b/sc/source/ui/inc/tpsort.hxx
@@ -140,6 +140,8 @@ private:
     std::unique_ptr<weld::ComboBox> m_xLbAlgorithm;
     std::unique_ptr<weld::CheckButton> m_xBtnIncComments;
     std::unique_ptr<weld::CheckButton> m_xBtnIncImages;
+    std::unique_ptr<weld::RadioButton> m_xRBDoubleNaturalSort;
+    std::unique_ptr<weld::RadioButton> m_xRBIntegerNaturalSort;
 
 private:
     void Init                   ();
diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx
index 052133d19bba..e182e8622a7e 100644
--- a/sc/source/ui/unoobj/datauno.cxx
+++ b/sc/source/ui/unoobj/datauno.cxx
@@ -320,14 +320,14 @@ void ScSortDescriptor::FillProperties( 
uno::Sequence<beans::PropertyValue>& rSeq
     pArray[8].Value <<= static_cast<sal_Int32>( rParam.nUserIndex );
 
     pArray[9].Name = SC_UNONAME_NUMBERBEHAVIOR;
-    pArray[9].Value <<= rParam.bNaturalSort ? SortNumberBehavior::DOUBLE
-                            : SortNumberBehavior::ALPHA_NUMERIC;
+    pArray[9].Value <<= static_cast<sal_Int32>(rParam.eSortNumberBehavior);
 }
 
 void ScSortDescriptor::FillSortParam( ScSortParam& rParam, const 
uno::Sequence<beans::PropertyValue>& rSeq )
 {
     sal_Int32 nSortSize = static_cast<sal_Int32>(rParam.GetSortKeyCount());
-    rParam.bNaturalSort = false; // default if optional 
SC_UNONAME_NUMBERBEHAVIOR does not exist
+    // default if optional SC_UNONAME_NUMBERBEHAVIOR does not exist
+    rParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
 
     for (const beans::PropertyValue& rProp : rSeq)
     {
@@ -445,9 +445,11 @@ void ScSortDescriptor::FillSortParam( ScSortParam& rParam, 
const uno::Sequence<b
         }
         else if (aPropName == SC_UNONAME_NUMBERBEHAVIOR)
         {
-            sal_Int32 nVal = SortNumberBehavior::ALPHA_NUMERIC;
+            sal_Int32 nVal = css::sheet::SortNumberBehavior::ALPHA_NUMERIC;
             if (rProp.Value >>= nVal)
-                rParam.bNaturalSort = nVal == SortNumberBehavior::DOUBLE;
+            {
+                rParam.eSortNumberBehavior = 
static_cast<ScSortNumberBehavior>(nVal <= 0 || nVal > 2 ? 0 : nVal);
+            }
         }
     }
 }
diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx
index c11ebf00ad08..a2d407f5e365 100644
--- a/sc/source/ui/view/cellsh2.cxx
+++ b/sc/source/ui/view/cellsh2.cxx
@@ -410,7 +410,7 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq )
                         aSortParam.bHasHeader       = bHasHeader;
                         aSortParam.bByRow           = true;
                         aSortParam.bCaseSens        = false;
-                        aSortParam.bNaturalSort     = false;
+                        aSortParam.eSortNumberBehavior = 
ScSortNumberBehavior::ALPHA_NUMERIC;
                         aSortParam.aDataAreaExtras.mbCellNotes = false;
                         aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
                         aSortParam.aDataAreaExtras.mbCellFormats = true;
@@ -464,7 +464,12 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq )
                             if ( const SfxBoolItem* pItem = 
pArgs->GetItemIfSet( SID_SORT_CASESENS ) )
                                 aSortParam.bCaseSens = pItem->GetValue();
                             if ( const SfxBoolItem* pItem = 
pArgs->GetItemIfSet( SID_SORT_NATURALSORT ) )
-                                aSortParam.bNaturalSort = pItem->GetValue();
+                            {
+                                // For to keep old macros working. Its value 
will be overwritten by
+                                // SID_SORT_NUMBERBEHAVIOR if that one is set.
+                                sal_Int32 nVal = pItem->GetValue() ? 1 : 0;
+                                aSortParam.eSortNumberBehavior = 
static_cast<ScSortNumberBehavior>(nVal);
+                            }
                             if ( const SfxBoolItem* pItem = 
pArgs->GetItemIfSet( SID_SORT_INCCOMMENTS ) )
                                 aSortParam.aDataAreaExtras.mbCellNotes = 
pItem->GetValue();
                             if ( const SfxBoolItem* pItem = 
pArgs->GetItemIfSet( SID_SORT_INCIMAGES ) )
@@ -478,7 +483,13 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq )
                                 if ( nUserIndex )
                                     aSortParam.nUserIndex = nUserIndex - 1;    
 // Basic: 1-based
                             }
-
+                            if (const SfxInt32Item* pItem = 
pArgs->GetItemIfSet( SID_SORT_NUMBERBEHAVIOR))
+                            {
+                                sal_Int32 nVal = pItem->GetValue();
+                                if (nVal < 0 || nVal >2)
+                                    nVal = 0; // invalid value, use default
+                                aSortParam.eSortNumberBehavior = 
static_cast<ScSortNumberBehavior>(nVal);
+                            }
                             SCCOLROW nField0 = 0;
                             const SfxPoolItem* pItem = nullptr;
                             if ( pArgs->GetItemState( FN_PARAM_1, true, &pItem 
) == SfxItemState::SET )
@@ -554,8 +565,6 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq )
                                                 rOutParam.bHasHeader ) );
                                             aRequest.AppendItem( SfxBoolItem( 
SID_SORT_CASESENS,
                                                 rOutParam.bCaseSens ) );
-                                            aRequest.AppendItem( SfxBoolItem( 
SID_SORT_NATURALSORT,
-                                                        rOutParam.bNaturalSort 
) );
                                             aRequest.AppendItem( SfxBoolItem( 
SID_SORT_INCCOMMENTS,
                                                         
rOutParam.aDataAreaExtras.mbCellNotes ) );
                                             aRequest.AppendItem( SfxBoolItem( 
SID_SORT_INCIMAGES,
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 0f1b7a36ed33..3ee29d55bc59 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -700,7 +700,7 @@ public:
         aSortParam.bHasHeader = bHasHeader;
         aSortParam.bByRow = true;
         aSortParam.bCaseSens = false;
-        aSortParam.bNaturalSort = false;
+        aSortParam.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;
         aSortParam.aDataAreaExtras.mbCellNotes = false;
         aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
         aSortParam.aDataAreaExtras.mbCellFormats = true;
@@ -1201,7 +1201,7 @@ void 
ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
             aSortParam.bHasHeader = bHasHeader;
             aSortParam.bByRow = true;
             aSortParam.bCaseSens = false;
-            aSortParam.bNaturalSort = false;
+            aSortParam.eSortNumberBehavior = 
ScSortNumberBehavior::ALPHA_NUMERIC;
             aSortParam.aDataAreaExtras.mbCellNotes = false;
             aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
             aSortParam.aDataAreaExtras.mbCellFormats = true;
diff --git a/sc/uiconfig/scalc/ui/sortoptionspage.ui 
b/sc/uiconfig/scalc/ui/sortoptionspage.ui
index 9ba51748c246..b7467e3fcec4 100644
--- a/sc/uiconfig/scalc/ui/sortoptionspage.ui
+++ b/sc/uiconfig/scalc/ui/sortoptionspage.ui
@@ -63,17 +63,64 @@
               </packing>
             </child>
             <child>
-              <object class="GtkCheckButton" id="naturalsort">
-                <property name="label" translatable="yes" 
context="sortoptionspage|naturalsort">Enable natural sort</property>
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="can-focus">True</property>
-                <property name="receives-default">False</property>
-                <property name="use-underline">True</property>
-                <property name="draw-indicator">True</property>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="naturalsort-atkobject">
-                    <property name="AtkObject::accessible-description" 
translatable="yes" context="sortoptionspage|extended_tip|naturalsort">Natural 
sort is a sort algorithm that sorts string-prefixed numbers based on the value 
of the numerical element in each sorted number, instead of the traditional way 
of sorting them as ordinary strings.</property>
+                <property name="can-focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">3</property>
+                <child>
+                  <object class="GtkCheckButton" id="naturalsort">
+                    <property name="label" translatable="yes" 
context="sortoptionspage|naturalsort">Recognize numbers within 
strings</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">False</property>
+                    <property name="has-tooltip">True</property>
+                    <property name="tooltip-text" translatable="yes" 
context="sortoptionspage|naturalsort">For numbers embedded in text, sorting 
uses their values instead of their sequence of digit characters. Example: A1, 
A2, A10 instead of A1, A10, A2</property>
+                    <property name="use-underline">True</property>
+                    <property name="draw-indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="doublenaturalsortrb">
+                    <property name="label" translatable="yes" 
context="sortoptionspage|naturalsort|doublenaturalsortrb">Use decimal numbers 
as a whole</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">False</property>
+                    <property name="has-tooltip">True</property>
+                    <property name="tooltip-text" translatable="yes" 
context="sortoptionspage|naturalsort|doublenaturalsortrb">Decimal numbers are 
detected. Example: A1.14, A1.2, A2.5, A10 (with dot as separator)</property>
+                    <property name="margin-start">20</property>
+                    <property name="use-underline">True</property>
+                    <property name="draw-indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="integernaturalsortrb">
+                    <property name="label" translatable="yes" 
context="sortoptionspage|naturalsort|integernaturalsortrb">Split in integer and 
fractions</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">False</property>
+                    <property name="has-tooltip">True</property>
+                    <property name="tooltip-text" translatable="yes" 
context="sortoptionspage|naturalsort|integernaturalsortrb">Decimal separator is 
treated as ordinary character. Example: A1.2, A1.14, A2.5, A10</property>
+                    <property name="margin-start">20</property>
+                    <property name="use-underline">True</property>
+                    <property name="draw-indicator">True</property>
+                    <property name="group">doublenaturalsortrb</property>
                   </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
                 </child>
               </object>
               <packing>
@@ -266,7 +313,6 @@
                     <property name="has-entry">True</property>
                     <child internal-child="entry">
                       <object class="GtkEntry">
-                        <property name="truncate-multiline">True</property>
                         <property name="can-focus">False</property>
                       </object>
                     </child>

Reply via email to