sc/qa/unit/ucalc_sort.cxx | 61 +++++++++++++++++++++++++++++++++++++++++ sc/source/core/data/table3.cxx | 40 ++++++++++++++++++-------- 2 files changed, 88 insertions(+), 13 deletions(-)
New commits: commit b0732e1ba9944a07aed737dcc52ef9e7614c38df Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Fri Sep 5 15:16:07 2025 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Sep 11 08:55:52 2025 +0200 tdf#168175 Use sort parameter language in natural sort Now the natural sort uses the language given in the sort parameter. Only if it does not contain a language, then the global locale is used. The language determines, which character is used as decimal separator. This corresponds to ODF where both the locale info and whether to use natural sort belong to the database range attributes. Change-Id: I52629bdff7f6a93974770d2922e80ac95df21a5d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190622 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/sc/qa/unit/ucalc_sort.cxx b/sc/qa/unit/ucalc_sort.cxx index 0c24b4b8063b..86e44d9f9699 100644 --- a/sc/qa/unit/ucalc_sort.cxx +++ b/sc/qa/unit/ucalc_sort.cxx @@ -30,6 +30,7 @@ #include <svx/svdocirc.hxx> #include <svx/svdpage.hxx> #include <rtl/math.hxx> +#include <unotools/syslocaleoptions.hxx> class TestSort : public ScUcalcTestBase { @@ -2222,6 +2223,66 @@ CPPUNIT_TEST_FIXTURE(TestSort, testQueryBinarySearch) m_pDoc->DeleteTab(0); } +CPPUNIT_TEST_FIXTURE(TestSort, testLanguageDependentNaturalSort) +{ + // Set the system locale to "en-US" for to have different decimal separator than "de-EN". + 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.bNaturalSort = true; // needs to be adapted when mode 'integer' is implemented + aSortParam.bInplace = false; + aSortParam.nDestTab = 0; + aSortParam.nDestCol = 2; + aSortParam.nDestRow = 0; + aSortParam.aCollatorLocale = css::lang::Locale(u"de"_ustr, u"DE"_ustr, u""_ustr); + 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. Without fix the comma was treated as ordinary character and thus the order + // had been Item | K1,2 | K1,104 | K2,5 | K2,40 | K2,501 | K10 + const std::array<OUString, 7> aExpected + = { 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(aExpected[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/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index 055617dfec14..a8eb314f935b 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -23,11 +23,13 @@ #include <svl/zforlist.hxx> #include <svl/zformat.hxx> #include <unotools/collatorwrapper.hxx> +#include <unotools/charclass.hxx> #include <stdlib.h> #include <com/sun/star/i18n/KParseTokens.hpp> #include <com/sun/star/i18n/KParseType.hpp> #include <sal/log.hxx> #include <osl/diagnose.h> +#include <i18nlangtag/languagetag.hxx> #include <refdata.hxx> #include <table.hxx> @@ -83,6 +85,9 @@ using namespace ::com::sun::star::i18n; @param sWhole Original string to be split into pieces + @param rLanguageTag + Contains the language related infos from the sort parameters + @param sPrefix Prefix string that consists of the part before the first number token. If no number was found, sPrefix is unchanged. @@ -98,14 +103,15 @@ using namespace ::com::sun::star::i18n; @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, - OUString &sPrefix, OUString &sSuffix, double &fNum ) +static bool SplitString(const OUString &sWhole, const LanguageTag& rLanguageTag, OUString &sPrefix, + OUString &sSuffix, double &fNum) { // Get prefix element, search for any digit and stop. sal_Int32 nPos = 0; + const CharClass aCharClass(rLanguageTag); while (nPos < sWhole.getLength()) { - const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos); + const sal_uInt16 nType = aCharClass.getCharacterType( sWhole, nPos); if (nType & KCharacterType::DIGIT) break; sWhole.iterateCodePoints( &nPos ); @@ -116,10 +122,10 @@ static bool SplitString( const OUString &sWhole, return false; // Get numeral element - const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep(); - ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken( - KParseType::ANY_NUMBER, sWhole, nPos, - KParseTokens::ANY_NUMBER, u""_ustr, KParseTokens::ANY_NUMBER, sUser ); + const OUString& sUser = LocaleDataWrapper::get(rLanguageTag)->getNumDecimalSep(); + ParseResult aPRNum = aCharClass.parsePredefinedToken(KParseType::ANY_NUMBER, sWhole, nPos, + KParseTokens::ANY_NUMBER, u""_ustr, + KParseTokens::ANY_NUMBER, sUser); if ( aPRNum.EndPos == nPos ) { @@ -146,6 +152,10 @@ static bool SplitString( const OUString &sWhole, @param sInput2 Input string 2 + @param rLanguageTag + Contains the language related infos from the sort parameters. They are needed + in method SplitString. + @param bCaseSens Boolean value for case sensitivity @@ -158,16 +168,17 @@ static bool SplitString( const OUString &sWhole, @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 bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW ) +static short Compare(const OUString &sInput1, const OUString &sInput2, + const LanguageTag& rLanguageTag, const bool bCaseSens, + const ScUserListData* pData, const CollatorWrapper *pCW) { OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2; do { double nNum1, nNum2; - bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 ); - bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 ); + bool bNumFound1 = SplitString( sStr1, rLanguageTag, sPre1, sSuf1, nNum1 ); + bool bNumFound2 = SplitString( sStr2, rLanguageTag, sPre2, sSuf2, nNum2 ); short nPreRes; // Prefix comparison result if ( pData ) @@ -1493,6 +1504,7 @@ short ScTable::CompareCell( 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) @@ -1500,7 +1512,8 @@ short ScTable::CompareCell( const ScUserListData& rData = rList[aSortParam.nUserIndex]; if ( bNaturalSort ) - nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator ); + nRes = naturalsort::Compare(aStr1, aStr2, aSortLanguageTag, bCaseSens, + &rData, pSortCollator); else { if ( bCaseSens ) @@ -1513,7 +1526,8 @@ short ScTable::CompareCell( if (!bUserDef) { if ( bNaturalSort ) - nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator ); + nRes = naturalsort::Compare(aStr1, aStr2, aSortLanguageTag, bCaseSens, + nullptr, pSortCollator ); else nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) ); }