[Libreoffice-commits] core.git: Branch 'private/kohei/calc-sort-fix' - 1091 commits - accessibility/inc accessibility/source android/abs-lib android/Bootstrap android/experimental autogen.sh avmedia/Library_avmediaogl.mk avmedia/Module_avmedia.mk avmedia/source basctl/inc basctl/source basebmp/source basegfx/source basic/qa basic/source binaryurp/source bin/fixincludeguards.sh bin/get-bugzilla-attachments-by-mimetype bin/ios-mapfile-statistics bin/lo-all-static-libs bin/test-hid-vs-ui.py bridges/Library_cpp_uno.mk bridges/test canvas/source chart2/AllLangResTarget_chartcontroller.mk chart2/CppunitTest_chart2_export.mk chart2/CppunitTest_chart2_import.mk chart2/CppunitTest_chart2_xshape.mk chart2/inc chart2/Library_chartcore.mk chart2/Library_chartopengl.mk chart2/qa chart2/source cli_ure/source codemaker/source comphelper/source compilerplugins/clang compilerplugins/Makefile.mk config_host/config_features.h.in config_host.mk.in configmgr/CppunitTest_configmgr_unit.mk configure.ac connectivity/qa connectivity/source cppcanvas/qa cppuhelper/qa cppuhelper/source cppuhelper/test cppu/source cui/inc cui/Library_cui.mk cui/source dbaccess/CppunitTest_dbaccess_dialog_save.mk dbaccess/CppunitTest_dbaccess_macros_test.mk dbaccess/inc dbaccess/qa dbaccess/source desktop/inc desktop/source desktop/unx download.lst drawinglayer/inc drawinglayer/source dtrans/source dtrans/test editeng/source embeddedobj/source embedserv/source eventattacher/source extensions/Library_ldapbe2.mk extensions/Module_extensions.mk extensions/source extensions/test extensions/uiconfig extensions/UIConfig_sabpilot.mk external/boost external/freetype external/glew external/icu external/libetonyek external/libgltf external/Module_external.mk external/openldap external/python3 external/twain extras/source filter/Configuration_filter.mk filter/CppunitTest_filter_pcx_test.mk filter/CppunitTest_filter_ppm_test.mk filter/CppunitTest_filter_psd_test.mk filter/CppunitTest_filter_ras_test.mk filter/CustomTarget_svg.mk f ilter/Module_filter.mk filter/qa filter/source forms/Library_frm.mk forms/source formula/source fpicker/source framework/inc framework/Library_fwi.mk framework/qa framework/source helpcompiler/inc helpcompiler/source helpcontent2 hwpfilter/source i18nlangtag/source i18npool/CustomTarget_breakiterator.mk i18npool/CustomTarget_collator.mk i18npool/Executable_gencoll_rule.mk i18npool/Executable_gendict.mk i18npool/Executable_genindex_data.mk i18npool/inc i18npool/Library_i18npool.mk i18npool/Module_i18npool.mk i18npool/README i18npool/source icon-themes/galaxy idlc/inc idlc/source idl/inc idl/source include/avmedia include/basegfx include/basic include/comphelper include/connectivity include/cppu include/cppuhelper include/drawinglayer include/editeng include/filter include/formula include/framework include/linguistic include/o3tl include/oox include/osl include/registry include/rtl include/sal include/salhelper include/sfx2 include/svl include/svtools include/svx include/toolkit inclu de/tools include/ucbhelper include/uno include/unotest include/unotools include/vbahelper include/vcl include/xmloff ios/CustomTarget_TiledLibreOffice_app.mk ios/experimental ios/MobileLibreOffice io/source io/test javaunohelper/source jvmfwk/plugins jvmfwk/source l10ntools/inc l10ntools/source lingucomponent/source linguistic/source lotuswordpro/source Makefile.fetch Makefile.in mysqlc/source odk/examples odk/source offapi/com offapi/UnoApi_offapi.mk officecfg/registry oovbaapi/ooo oox/CustomTarget_generated.mk oox/inc oox/Library_oox.mk oox/Module_oox.mk oox/Package_customshapes.mk oox/Package_generated.mk oox/source package/inc package/source postprocess/Rdb_services.mk pyuno/Module_pyuno.mk pyuno/PythonTest_pytests.mk pyuno/PythonTest_pyuno_pytests_ssl.mk pyuno/qa qadevOOo/runner qadevOOo/tests readlicense_oo/docs reportbuilder/java reportdesign/inc reportdesign/source RepositoryExternal.mk Repository.mk RepositoryModule_host.mk rsc/inc rsc/source sal/cppunittester sal/inc sal/o sl sal/qa sal/textenc sal/workben sax/source sax/test scaddins/source sc/CppunitTest_sc_annotationobj.mk sc/CppunitTest_sc_annotationshapeobj.mk sc/CppunitTest_sc_annotationsobj.mk sc/CppunitTest_sc_cellrangeobj.mk sc/CppunitTest_sc_chart_regression_test.mk sc/CppunitTest_sc_databaserangeobj.mk sc/CppunitTest_sc_datapilotfieldobj.mk sc/CppunitTest_sc_datapilottableobj.mk sc/CppunitTest_sc_editfieldobj_cell.mk sc/CppunitTest_sc_editfieldobj_header.mk sc/CppunitTest_sc_filters_test.mk sc/CppunitTest_sc_macros_test.mk sc/CppunitTest_sc_modelobj.mk sc/CppunitTest_sc_namedrangeobj.mk sc/CppunitTest_sc_namedrangesobj.mk sc/CppunitTest_sc_opencl_test.mk sc/CppunitTest_sc_outlineobj.mk sc/CppunitTest_sc_rangelst_test.mk sc/CppunitTest_sc_subsequent_export_test.mk sc/CppunitTest_sc_subsequent_filters_test.mk sc/CppunitTest_sc_tableautoformatfield.mk sc/CppunitTest_sc_tablesheetobj.mk sc/CppunitTest_sc_tablesheetsobj.mk sc/CppunitTest_sc_ucalc.mk sc/inc sc/Library_sc.mk scp2/InstallModule_ooo .mk scp2/macros scp2/source sc/qa scripting/examples scripting/java scripting/source scripting/workben sc/source sc/uiconfig sc/workben sd/CppunitTest_sd_filters_test.mk sd/CppunitTest_sd_html_export_tests.mk sd/CppunitTest_sd_import_tests.mk sd/CppunitTest_sd_uimpress.mk sdext/CustomTarget_pdfimport.mk sdext/source sd/inc sd/Library_sd.mk sd/Module_sd.mk sd/qa sd/sdi sd/source sd/uiconfig setup_native/source sfx2/inc sfx2/Library_sfx.mk sfx2/qa sfx2/source slideshow/CppunitTest_slideshow.mk slideshow/inc slideshow/Library_slideshow.mk slideshow/source slideshow/test smoketest/com solenv/doc solenv/gbuild sot/source starmath/inc starmath/Library_sm.mk starmath/source starmath/uiconfig store/source svgio/CppunitTest_svgio.mk svgio/inc svgio/Module_svgio.mk svgio/qa svgio/source svl/qa svl/source svtools/inc svtools/source svx/doc svx/inc svx/Library_svxcore.mk svx/Library_svx.mk svx/source svx/uiconfig svx/workben sw/CppunitTest_sw_layout_test.mk sw/CppunitTest_sw_macros_test.mk sw/C ppunitTest_sw_uwriter.mk sw/inc sw/Library_sw.mk sw/qa sw/sdi sw/source sw/uiconfig sysui/desktop test/source toolkit/inc toolkit/Library_tk.mk toolkit/source toolkit/test tools/source translations ucbhelper/source ucb/source ucb/workben udkapi/com UnoControls/inc UnoControls/source unotest/source unotools/source unoxml/source unusedcode.easy uui/source vbahelper/source vcl/generic vcl/headless vcl/inc vcl/ios vcl/Library_vcl.mk vcl/Library_vclopengl.mk vcl/Module_vcl.mk vcl/osx vcl/qa vcl/quartz vcl/source vcl/unx vcl/win winaccessibility/source wizards/com writerfilter/inc writerfilter/qa writerfilter/source writerperfect/Library_wpftdraw.mk writerperfect/Library_wpftwriter.mk xmlhelp/source xmloff/dtd xmloff/inc xmloff/qa xmloff/source xmlscript/source xmlsecurity/inc xmlsecurity/source xmlsecurity/test_docs
Kohei Yoshida
kohei.yoshida at collabora.com
Wed Apr 23 12:46:53 PDT 2014
Rebased ref, commits from common ancestor:
commit 4d01cb17cc27c39ccbfa3db1658abe8aabfda9a6
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Wed Apr 23 15:40:05 2014 -0400
Remove compiler warnings.
Change-Id: I050dee872e0f85efe1f96729cbcc96966c73c25b
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index b1223fa..6b5585a 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4629,7 +4629,7 @@ void Test::testSortWithFormulaRefs()
m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
// Insert forumulas in A1:A6 on the 2nd sheet.
- for (SCROW i = 0; i < SAL_N_ELEMENTS(aFormulaData); ++i)
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aFormulaData); ++i)
m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
// Sort data in A2:A8 on the 1st sheet. No column header.
@@ -4643,7 +4643,7 @@ void Test::testSortWithFormulaRefs()
m_pDoc->Sort(0, aSortData, false, NULL);
- for (SCROW i = 0; i < SAL_N_ELEMENTS(aResults); ++i)
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aResults); ++i)
{
OUString sResult = m_pDoc->GetString(0, i + 1, 0);
CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult );
commit 8de0d9783a7849da91b47a42130c317e7f582404
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Wed Apr 23 15:29:56 2014 -0400
Set mdds 0.10.3 as the new package requirement.
Change-Id: Ide0e10fa528d53a7e732d00b54c940111beebe19
diff --git a/configure.ac b/configure.ac
index 07c7d13..ad3fa59 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8614,7 +8614,7 @@ AC_SUBST(SYSTEM_BOOST)
dnl ===================================================================
dnl Check for system mdds
dnl ===================================================================
-libo_CHECK_SYSTEM_MODULE([mdds], [MDDS], [mdds >= 0.10.2], ["-I${WORKDIR}/UnpackedTarball/mdds/include"])
+libo_CHECK_SYSTEM_MODULE([mdds], [MDDS], [mdds >= 0.10.3], ["-I${WORKDIR}/UnpackedTarball/mdds/include"])
dnl ===================================================================
dnl Check for system glm
diff --git a/download.lst b/download.lst
index 2a16e7e..45cc0be 100644
--- a/download.lst
+++ b/download.lst
@@ -95,7 +95,7 @@ export LIBXML_TARBALL := 9c0cfef285d5c4a5c80d00904ddab380-libxml2-2.9.1.tar.gz
export LIBXSLT_TARBALL := 9667bf6f9310b957254fdcf6596600b7-libxslt-1.1.28.tar.gz
export LPSOLVE_TARBALL := 26b3e95ddf3d9c077c480ea45874b3b8-lp_solve_5.5.tar.gz
export MARIADB_TARBALL := 05f84c95b610c21c5fd510d10debcabf-mariadb-native-client-1.0.0.tar.bz2
-export MDDS_TARBALL := 47203e7cade74e5c385aa812f21e7932-mdds_0.10.2.tar.bz2
+export MDDS_TARBALL := aa5ca9d1ed1082890835afab26400a39-mdds_0.10.3.tar.bz2
export MYSQLCPPCONN_TARBALL := 0981bda6548a8c8233ffce2b6e4b2a23-mysql-connector-c++-1.1.0.tar.gz
export MYTHES_TARBALL := 46e92b68e31e858512b680b3b61dc4c1-mythes-1.2.3.tar.gz
export NEON_TARBALL := ff369e69ef0f0143beb5626164e87ae2-neon-0.29.5.tar.gz
commit 0d7571230f35b17d72a4cf06ddf197f1db3b8cd4
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Wed Apr 23 12:31:14 2014 -0400
Add unit test for sorting with cell formats.
Change-Id: Ie1ec0308f72efe3bc46564d81f0cf37c0d606114
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index beb1bf1..b1223fa 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -59,6 +59,7 @@
#include <docoptio.hxx>
#include <patattr.hxx>
#include <docpool.hxx>
+#include <globalnames.hxx>
#include "formula/IFunctionDescription.hxx"
@@ -4847,6 +4848,167 @@ void Test::testSortInFormulaGroup()
m_pDoc->DeleteTab( 0 );
}
+void Test::testSortWithCellFormats()
+{
+ struct
+ {
+ bool isBold( const ScPatternAttr* pPat ) const
+ {
+ if (!pPat)
+ {
+ cerr << "Pattern is NULL!" << endl;
+ return false;
+ }
+
+ const SfxPoolItem* pItem = NULL;
+ if (!pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem))
+ {
+ cerr << "Pattern does not have a font weight item, but it should." << endl;
+ return false;
+ }
+
+ if (static_cast<const SvxWeightItem*>(pItem)->GetEnumValue() != WEIGHT_BOLD)
+ {
+ cerr << "Font weight should be bold." << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isItalic( const ScPatternAttr* pPat ) const
+ {
+ if (!pPat)
+ {
+ cerr << "Pattern is NULL!" << endl;
+ return false;
+ }
+
+ const SfxPoolItem* pItem = NULL;
+ if (!pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem))
+ {
+ cerr << "Pattern does not have a font posture item, but it should." << endl;
+ return false;
+ }
+
+ if (static_cast<const SvxPostureItem*>(pItem)->GetEnumValue() != ITALIC_NORMAL)
+ {
+ cerr << "Italic should be applied.." << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isNormal( const ScPatternAttr* pPat ) const
+ {
+ if (!pPat)
+ {
+ cerr << "Pattern is NULL!" << endl;
+ return false;
+ }
+
+ const SfxPoolItem* pItem = NULL;
+ if (pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT))
+ {
+ // Check if the font weight is applied.
+ if (static_cast<const SvxWeightItem*>(pItem)->GetEnumValue() == WEIGHT_BOLD)
+ {
+ cerr << "This cell is bold, but shouldn't." << endl;
+ return false;
+ }
+ }
+
+ if (pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE))
+ {
+ // Check if the italics is applied.
+ if (static_cast<const SvxPostureItem*>(pItem)->GetEnumValue() == ITALIC_NORMAL)
+ {
+ cerr << "This cell is bold, but shouldn't." << endl;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ } aCheck;
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Insert some values into A1:A4.
+ m_pDoc->SetString(ScAddress(0,0,0), "Header");
+ m_pDoc->SetString(ScAddress(0,1,0), "Normal");
+ m_pDoc->SetString(ScAddress(0,2,0), "Bold");
+ m_pDoc->SetString(ScAddress(0,3,0), "Italic");
+
+ // Set A3 bold and A4 italic.
+ const ScPatternAttr* pPat = m_pDoc->GetPattern(ScAddress(0,2,0));
+ CPPUNIT_ASSERT(pPat);
+ {
+ ScPatternAttr aNewPat(*pPat);
+ SfxItemSet& rSet = aNewPat.GetItemSet();
+ rSet.Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
+ m_pDoc->ApplyPattern(0, 2, 0, aNewPat);
+
+ // Make sure it's really in.
+ bool bGood = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,2,0)));
+ CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood);
+ }
+
+ pPat = m_pDoc->GetPattern(ScAddress(0,3,0));
+ CPPUNIT_ASSERT(pPat);
+ {
+ ScPatternAttr aNewPat(*pPat);
+ SfxItemSet& rSet = aNewPat.GetItemSet();
+ rSet.Put(SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE));
+ m_pDoc->ApplyPattern(0, 3, 0, aNewPat);
+
+ bool bGood = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,3,0)));
+ CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood);
+ }
+
+ // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run.
+ m_pDoc->SetAnonymousDBData(
+ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 3));
+
+ // Sort A1:A4 ascending with cell formats.
+ ScDBDocFunc aFunc(getDocShell());
+
+ ScSortParam aSortData;
+ aSortData.nCol1 = 0;
+ aSortData.nCol2 = 0;
+ aSortData.nRow1 = 0;
+ aSortData.nRow2 = 3;
+ aSortData.bHasHeader = true;
+ aSortData.bIncludePattern = true;
+ aSortData.maKeyState[0].bDoSort = true;
+ aSortData.maKeyState[0].nField = 0;
+ aSortData.maKeyState[0].bAscending = true;
+ bool bSorted = aFunc.Sort(0, aSortData, true, false, true);
+ CPPUNIT_ASSERT(bSorted);
+
+ // Check the sort result.
+ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("Bold"), m_pDoc->GetString(ScAddress(0,1,0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("Italic"), m_pDoc->GetString(ScAddress(0,2,0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("Normal"), m_pDoc->GetString(ScAddress(0,3,0)));
+
+ // A2 should be bold now.
+ bool bBold = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,1,0)));
+ CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold);
+
+ // and A3 should be italic.
+ bool bItalic = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,2,0)));
+ CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic);
+
+ // A4 should have neither bold nor italic.
+ bool bNormal = aCheck.isNormal(m_pDoc->GetPattern(ScAddress(0,3,0)));
+ CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal);
+
+ m_pDoc->DeleteTab(0);
+}
+
void Test::testShiftCells()
{
m_pDoc->InsertTab(0, "foo");
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index 56eaf73..2939ac5 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -322,6 +322,7 @@ public:
void testSortWithFormulaRefs();
void testSortWithStrings();
void testSortInFormulaGroup();
+ void testSortWithCellFormats();
void testShiftCells();
void testNoteBasic();
@@ -473,6 +474,7 @@ public:
CPPUNIT_TEST(testSortWithFormulaRefs);
CPPUNIT_TEST(testSortWithStrings);
CPPUNIT_TEST(testSortInFormulaGroup);
+ CPPUNIT_TEST(testSortWithCellFormats);
CPPUNIT_TEST(testShiftCells);
CPPUNIT_TEST(testNoteBasic);
CPPUNIT_TEST(testNoteDeleteRow);
commit 7ef76e309ad3b27e87e2f8af638d7a0249d86301
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Wed Apr 23 10:06:53 2014 -0400
fdo#76607: Writer unit test for this.
Change-Id: Ia69283a9998c233784c0da0f7a65f58a6c102596
diff --git a/sc/qa/unit/data/ods/shared-formula/sort-crash.ods b/sc/qa/unit/data/ods/shared-formula/sort-crash.ods
new file mode 100644
index 0000000..21f3d2e
Binary files /dev/null and b/sc/qa/unit/data/ods/shared-formula/sort-crash.ods differ
diff --git a/sc/qa/unit/filters-test.cxx b/sc/qa/unit/filters-test.cxx
index 6f84075..796a01d 100644
--- a/sc/qa/unit/filters-test.cxx
+++ b/sc/qa/unit/filters-test.cxx
@@ -32,6 +32,9 @@
#include "userdat.hxx"
#include "formulacell.hxx"
#include "tabprotection.hxx"
+#include <dbdocfun.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
#include <svx/svdpage.hxx>
@@ -72,6 +75,7 @@ public:
void testLegacyCellAnchoredRotatedShape();
void testEnhancedProtectionXLS();
void testEnhancedProtectionXLSX();
+ void testSortWithSharedFormulasODS();
CPPUNIT_TEST_SUITE(ScFiltersTest);
CPPUNIT_TEST(testCVEs);
@@ -88,6 +92,7 @@ public:
CPPUNIT_TEST(testLegacyCellAnchoredRotatedShape);
CPPUNIT_TEST(testEnhancedProtectionXLS);
CPPUNIT_TEST(testEnhancedProtectionXLSX);
+ CPPUNIT_TEST(testSortWithSharedFormulasODS);
CPPUNIT_TEST_SUITE_END();
@@ -541,6 +546,55 @@ void ScFiltersTest::testEnhancedProtectionXLSX()
xDocSh->DoClose();
}
+void ScFiltersTest::testSortWithSharedFormulasODS()
+{
+ ScDocShellRef xDocSh = loadDoc("shared-formula/sort-crash.", ODS, true);
+ CPPUNIT_ASSERT(xDocSh.Is());
+ ScDocument* pDoc = xDocSh->GetDocument();
+
+ // E2:E10 should be shared.
+ const ScFormulaCell* pFC = pDoc->GetFormulaCell(ScAddress(4,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedLength());
+
+ // E12:E17 should be shared.
+ pFC = pDoc->GetFormulaCell(ScAddress(4,11,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
+
+ // Set A1:E17 as an anonymous database range to sheet, or else Calc would
+ // refuse to sort the range.
+ ScDBData* pDBData = new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 4, 16, true, true);
+ pDoc->SetAnonymousDBData(0, pDBData);
+
+ // Sort ascending by Column E.
+
+ ScSortParam aSortData;
+ aSortData.nCol1 = 0;
+ aSortData.nCol2 = 4;
+ aSortData.nRow1 = 0;
+ aSortData.nRow2 = 16;
+ aSortData.bHasHeader = true;
+ aSortData.maKeyState[0].bDoSort = true;
+ aSortData.maKeyState[0].nField = 4;
+ aSortData.maKeyState[0].bAscending = true;
+
+ // Do the sorting. This should not crash.
+ ScDBDocFunc aFunc(*xDocSh);
+ bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
+ CPPUNIT_ASSERT(bSorted);
+
+ // After the sort, E2:E16 should be shared.
+ pFC = pDoc->GetFormulaCell(ScAddress(4,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(15), pFC->GetSharedLength());
+
+ xDocSh->DoClose();
+}
+
ScFiltersTest::ScFiltersTest()
: ScBootstrapFixture( "/sc/qa/unit/data" )
{
diff --git a/sc/source/ui/inc/dbdocfun.hxx b/sc/source/ui/inc/dbdocfun.hxx
index 0831490..13fd9bc 100644
--- a/sc/source/ui/inc/dbdocfun.hxx
+++ b/sc/source/ui/inc/dbdocfun.hxx
@@ -70,8 +70,8 @@ public:
static void ShowInBeamer( const ScImportParam& rParam, SfxViewFrame* pFrame );
- bool Sort( SCTAB nTab, const ScSortParam& rSortParam,
- bool bRecord, bool bPaint, bool bApi );
+ SC_DLLPUBLIC bool Sort(
+ SCTAB nTab, const ScSortParam& rSortParam, bool bRecord, bool bPaint, bool bApi );
SC_DLLPUBLIC bool Query( SCTAB nTab, const ScQueryParam& rQueryParam,
const ScRange* pAdvSource, bool bRecord, bool bApi );
commit 2a8e5720e71766893019ff4efdc368831a21696c
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 23:07:34 2014 -0400
SwapRow() is no more!
Good riddance. It was optimized for the old cell storage.
Change-Id: I374194fe211d5b13dd60dbb68b9511a366fb32bd
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 51ddaf4..4bbdb63 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -186,7 +186,6 @@ public:
void Delete( SCROW nRow );
void FreeAll();
- void SwapRow( SCROW nRow1, SCROW nRow2 );
void SwapCell( SCROW nRow, ScColumn& rCol);
bool HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 7610eb7..b13e7d1 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1007,7 +1007,6 @@ private:
bool IsSorted(SCCOLROW nStart, SCCOLROW nEnd) const;
void DecoladeRow( ScSortInfoArray*, SCROW nRow1, SCROW nRow2 );
void SwapCol(SCCOL nCol1, SCCOL nCol2);
- void SwapRow(SCROW nRow1, SCROW nRow2);
short CompareCell(
sal_uInt16 nSort,
ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index de9f65e..89d998b 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -841,338 +841,6 @@ const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition&
namespace {
-ScFormulaCell* cloneFormulaCell(ScDocument* pDoc, const ScAddress& rNewPos, ScFormulaCell& rOldCell)
-{
- ScFormulaCell* pNew = new ScFormulaCell(rOldCell, *pDoc, rNewPos, SC_CLONECELL_ADJUST3DREL);
- rOldCell.EndListeningTo(pDoc);
- pNew->StartListeningTo(pDoc);
- pNew->SetDirty();
- return pNew;
-}
-
-}
-
-void ScColumn::SwapRow(SCROW nRow1, SCROW nRow2)
-{
- if (nRow1 == nRow2)
- // Nothing to swap.
- return;
-
- // Ensure that nRow1 < nRow2.
- if (nRow2 < nRow1)
- std::swap(nRow1, nRow2);
-
- // Broadcasters (if exist) should NOT be swapped.
-
- sc::CellStoreType::position_type aPos1 = maCells.position(nRow1);
- if (aPos1.first == maCells.end())
- return;
-
- sc::CellStoreType::position_type aPos2 = maCells.position(aPos1.first, nRow2);
- if (aPos2.first == maCells.end())
- return;
-
- std::vector<SCROW> aRows;
- aRows.reserve(2);
- aRows.push_back(nRow1);
- aRows.push_back(nRow2);
-
- sc::CellStoreType::iterator it1 = aPos1.first, it2 = aPos2.first;
-
- if (it1->type == it2->type)
- {
- // Both positions are of the same type. Do a simple value swap.
- switch (it1->type)
- {
- case sc::element_type_empty:
- // Both are empty. Nothing to swap.
- return;
- case sc::element_type_numeric:
- std::swap(
- sc::numeric_block::at(*it1->data, aPos1.second),
- sc::numeric_block::at(*it2->data, aPos2.second));
- break;
- case sc::element_type_string:
- std::swap(
- sc::string_block::at(*it1->data, aPos1.second),
- sc::string_block::at(*it2->data, aPos2.second));
- break;
- case sc::element_type_edittext:
- std::swap(
- sc::edittext_block::at(*it1->data, aPos1.second),
- sc::edittext_block::at(*it2->data, aPos2.second));
- break;
- case sc::element_type_formula:
- {
- // Swapping of formula cells involve adjustment of references wrt their positions.
- sc::formula_block::iterator itf1 = sc::formula_block::begin(*it1->data);
- sc::formula_block::iterator itf2 = sc::formula_block::begin(*it2->data);
- std::advance(itf1, aPos1.second);
- std::advance(itf2, aPos2.second);
-
- // Is it an identical formula in the same group - if so,
- // take a shortcut to swap the result data:
- if(!(*itf1)->SwapWithinGroup(*itf2))
- {
- // otherwise we need to really move the formula &
- // re-write dependencies etc.
- boost::scoped_ptr<ScFormulaCell> pOld1(*itf1);
- boost::scoped_ptr<ScFormulaCell> pOld2(*itf2);
-
- DetachFormulaCell(aPos1, **itf1);
- DetachFormulaCell(aPos2, **itf2);
- ScFormulaCell* pNew1 = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *pOld2);
- ScFormulaCell* pNew2 = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *pOld1);
- *itf1 = pNew1;
- *itf2 = pNew2;
-
- ActivateNewFormulaCell(aPos1, *pNew1);
- ActivateNewFormulaCell(aPos2, *pNew2);
- }
- }
- break;
- default:
- ;
- }
-
- SwapCellTextAttrs(nRow1, nRow2);
- SwapCellNotes(nRow1, nRow2);
- CellStorageModified();
- BroadcastCells(aRows, SC_HINT_DATACHANGED);
- return;
- }
-
- // The two cells are of different types.
-
- ScRefCellValue aCell1 = GetCellValue(aPos1.first, aPos1.second);
- ScRefCellValue aCell2 = GetCellValue(aPos2.first, aPos2.second);
-
- // Make sure to put cells in row 1 first then row 2!
-
- if (aCell1.meType == CELLTYPE_NONE)
- {
- // cell 1 is empty and cell 2 is not.
- switch (aCell2.meType)
- {
- case CELLTYPE_VALUE:
- it1 = maCells.set(it1, nRow1, aCell2.mfValue); // it2 becomes invalid.
- maCells.set_empty(it1, nRow2, nRow2);
- break;
- case CELLTYPE_STRING:
- it1 = maCells.set(it1, nRow1, *aCell2.mpString);
- maCells.set_empty(it1, nRow2, nRow2);
- break;
- case CELLTYPE_EDIT:
- {
- it1 = maCells.set(
- it1, nRow1, const_cast<EditTextObject*>(aCell2.mpEditText));
- EditTextObject* p;
- maCells.release(it1, nRow2, p);
- }
- break;
- case CELLTYPE_FORMULA:
- {
- // cell 1 is empty and cell 2 is a formula cell.
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula);
- DetachFormulaCell(aPos2, *aCell2.mpFormula);
- it1 = maCells.set(it1, nRow1, pNew);
- maCells.set_empty(it1, nRow2, nRow2); // original formula cell gets deleted.
- ActivateNewFormulaCell(it1, nRow1, *pNew);
- }
- break;
- default:
- ;
- }
-
- SwapCellTextAttrs(nRow1, nRow2);
- SwapCellNotes(nRow1, nRow2);
- CellStorageModified();
- BroadcastCells(aRows, SC_HINT_DATACHANGED);
- return;
- }
-
- if (aCell2.meType == CELLTYPE_NONE)
- {
- // cell 1 is not empty and cell 2 is empty.
- switch (aCell1.meType)
- {
- case CELLTYPE_VALUE:
- // Value is copied in Cell1.
- it1 = maCells.set_empty(it1, nRow1, nRow1);
- maCells.set(it1, nRow2, aCell1.mfValue);
- break;
- case CELLTYPE_STRING:
- {
- svl::SharedString aStr = *aCell1.mpString; // make a copy.
- it1 = maCells.set_empty(it1, nRow1, nRow1); // original string is gone.
- maCells.set(it1, nRow2, aStr);
- }
- break;
- case CELLTYPE_EDIT:
- {
- EditTextObject* p;
- it1 = maCells.release(it1, nRow1, p);
- maCells.set(it1, nRow2, p);
- }
- break;
- case CELLTYPE_FORMULA:
- {
- // cell 1 is a formula cell and cell 2 is empty.
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *aCell1.mpFormula);
- DetachFormulaCell(aPos1, *aCell1.mpFormula);
- it1 = maCells.set_empty(it1, nRow1, nRow1); // original formula cell is gone.
- it1 = maCells.set(it1, nRow2, pNew);
- ActivateNewFormulaCell(it1, nRow2, *pNew);
- }
- break;
- default:
- ;
- }
-
- SwapCellTextAttrs(nRow1, nRow2);
- SwapCellNotes(nRow1, nRow2);
- CellStorageModified();
- BroadcastCells(aRows, SC_HINT_DATACHANGED);
- return;
- }
-
- // Neither cells are empty, and they are of different types.
- switch (aCell1.meType)
- {
- case CELLTYPE_VALUE:
- {
- switch (aCell2.meType)
- {
- case CELLTYPE_STRING:
- it1 = maCells.set(it1, nRow1, *aCell2.mpString);
- break;
- case CELLTYPE_EDIT:
- {
- it1 = maCells.set(
- it1, nRow1, const_cast<EditTextObject*>(aCell2.mpEditText));
- EditTextObject* p;
- it1 = maCells.release(it1, nRow2, p);
- }
- break;
- case CELLTYPE_FORMULA:
- {
- DetachFormulaCell(aPos2, *aCell2.mpFormula);
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula);
- it1 = maCells.set(it1, nRow1, pNew);
- ActivateNewFormulaCell(it1, nRow1, *pNew);
- // The old formula cell will get overwritten below.
- }
- break;
- default:
- ;
- }
-
- maCells.set(it1, nRow2, aCell1.mfValue);
-
- }
- break;
- case CELLTYPE_STRING:
- {
- svl::SharedString aStr = *aCell1.mpString; // make a copy.
- switch (aCell2.meType)
- {
- case CELLTYPE_VALUE:
- it1 = maCells.set(it1, nRow1, aCell2.mfValue);
- break;
- case CELLTYPE_EDIT:
- {
- it1 = maCells.set(
- it1, nRow1, const_cast<EditTextObject*>(aCell2.mpEditText));
- EditTextObject* p;
- it1 = maCells.release(it1, nRow2, p); // prevent it being overwritten.
- }
- break;
- case CELLTYPE_FORMULA:
- {
- // cell 1 - string, cell 2 - formula
- DetachFormulaCell(aPos2, *aCell2.mpFormula);
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula);
- it1 = maCells.set(it1, nRow1, pNew);
- ActivateNewFormulaCell(it1, nRow1, *pNew);
- // Old formula cell will get overwritten below.
- }
- break;
- default:
- ;
- }
-
- maCells.set(it1, nRow2, aStr);
- }
- break;
- case CELLTYPE_EDIT:
- {
- EditTextObject* p;
- it1 = maCells.release(it1, nRow1, p);
-
- switch (aCell2.meType)
- {
- case CELLTYPE_VALUE:
- it1 = maCells.set(it1, nRow1, aCell2.mfValue);
- break;
- case CELLTYPE_STRING:
- it1 = maCells.set(it1, nRow1, *aCell2.mpString);
- break;
- case CELLTYPE_FORMULA:
- {
- DetachFormulaCell(aPos2, *aCell2.mpFormula);
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula);
- it1 = maCells.set(it1, nRow1, pNew);
- ActivateNewFormulaCell(it1, nRow1, *pNew);
- // Old formula cell will get overwritten below.
- }
- break;
- default:
- ;
- }
-
- maCells.set(it1, nRow2, const_cast<EditTextObject*>(aCell1.mpEditText));
- }
- break;
- case CELLTYPE_FORMULA:
- {
- // cell 1 is a formula cell and cell 2 is not.
- DetachFormulaCell(aPos1, *aCell1.mpFormula);
- ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *aCell1.mpFormula);
- switch (aCell2.meType)
- {
- case CELLTYPE_VALUE:
- it1 = maCells.set(it1, nRow1, aCell2.mfValue);
- break;
- case CELLTYPE_STRING:
- it1 = maCells.set(it1, nRow1, *aCell2.mpString);
- break;
- case CELLTYPE_EDIT:
- {
- it1 = maCells.set(it1, nRow1, aCell2.mpEditText);
- EditTextObject* p;
- it1 = maCells.release(it1, nRow2, p);
- }
- break;
- default:
- ;
- }
-
- it1 = maCells.set(it1, nRow2, pNew);
- ActivateNewFormulaCell(it1, nRow2, *pNew);
- }
- break;
- default:
- ;
- }
-
- SwapCellTextAttrs(nRow1, nRow2);
- SwapCellNotes(nRow1, nRow2);
- CellStorageModified();
- BroadcastCells(aRows, SC_HINT_DATACHANGED);
-}
-
-namespace {
-
/**
* Adjust references in formula cell with respect to column-wise relocation.
*/
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index f39bbbe..acd8f5e 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -935,40 +935,6 @@ void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
}
}
-void ScTable::SwapRow(SCROW nRow1, SCROW nRow2)
-{
- SCCOL nColStart = aSortParam.nCol1;
- SCCOL nColEnd = aSortParam.nCol2;
- for (SCCOL nCol = nColStart; nCol <= nColEnd; nCol++)
- {
- aCol[nCol].SwapRow(nRow1, nRow2);
- if (aSortParam.bIncludePattern)
- {
- const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1);
- const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2);
- if (pPat1 != pPat2)
- {
- pDocument->GetPool()->Put(*pPat1);
- SetPattern(nCol, nRow1, *pPat2, true);
- SetPattern(nCol, nRow2, *pPat1, true);
- pDocument->GetPool()->Remove(*pPat1);
- }
- }
- }
- if (bGlobalKeepQuery)
- {
- bool bRow1Hidden = RowHidden(nRow1);
- bool bRow2Hidden = RowHidden(nRow2);
- SetRowHidden(nRow1, nRow1, bRow2Hidden);
- SetRowHidden(nRow2, nRow2, bRow1Hidden);
-
- bool bRow1Filtered = RowFiltered(nRow1);
- bool bRow2Filtered = RowFiltered(nRow2);
- SetRowFiltered(nRow1, nRow1, bRow2Filtered);
- SetRowFiltered(nRow2, nRow2, bRow1Filtered);
- }
-}
-
short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
{
short nRes;
commit 46c34a9cc8189bf12e4373b5a34f02b3362ab27b
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 22:54:25 2014 -0400
Use scoped_ptr for this.
Change-Id: I54d0bbe460f95ada7632d7c9cbedb8a677995fd4
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 88f3ea7..f39bbbe 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -2063,9 +2063,9 @@ void ScTable::TopTenQuery( ScQueryParam& rParam )
bSortCollatorInitialized = true;
InitSortCollator( aLocalSortParam );
}
- ScSortInfoArray* pArray = CreateSortInfoArray(nRow1, rParam.nRow2, bGlobalKeepQuery);
- DecoladeRow( pArray, nRow1, rParam.nRow2 );
- QuickSort( pArray, nRow1, rParam.nRow2 );
+ boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(nRow1, rParam.nRow2, bGlobalKeepQuery));
+ DecoladeRow( pArray.get(), nRow1, rParam.nRow2 );
+ QuickSort( pArray.get(), nRow1, rParam.nRow2 );
ScSortInfo** ppInfo = pArray->GetFirstArray();
SCSIZE nValidCount = nCount;
// keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
@@ -2142,7 +2142,6 @@ void ScTable::TopTenQuery( ScQueryParam& rParam )
rItem.meType = ScQueryEntry::ByValue;
rItem.mfVal = 0;
}
- delete pArray;
}
break;
default:
commit 30d60d0e7a49aa768b4ebedf61663a64e9299d7f
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 22:47:48 2014 -0400
New method SortReorderByRow() for the new row-based reordering.
Easier to follow this way.
Change-Id: Idf7e93106f1e1927967f4917062ee619ed71d694
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index f6e2583..7610eb7 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1016,7 +1016,8 @@ private:
short Compare( ScSortInfoArray*, SCCOLROW nIndex1, SCCOLROW nIndex2) const;
ScSortInfoArray* CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2, bool bKeepQuery );
void QuickSort( ScSortInfoArray*, SCsCOLROW nLo, SCsCOLROW nHi);
- void SortReorder( ScSortInfoArray*, ScProgress* );
+ void SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress );
+ void SortReorderByRow( ScSortInfoArray* pArray, ScProgress* pProgress );
bool CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam);
bool CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam);
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index bedbb62..88f3ea7 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -523,224 +523,228 @@ void ScTable::DestroySortCollator()
void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
{
+ if (aSortParam.bByRow)
+ {
+ SortReorderByRow(pArray, pProgress);
+ return;
+ }
+
size_t nCount = pArray->GetCount();
SCCOLROW nStart = pArray->GetStart();
ScSortInfo** ppInfo = pArray->GetFirstArray();
- if (aSortParam.bByRow)
- {
- SCROW nRow1 = pArray->GetStart();
- SCROW nRow2 = pArray->GetLast();
- ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
- assert(pRows); // In sort-by-row mode we must have data rows already populated.
+ std::vector<ScSortInfo*> aTable(nCount);
+ SCSIZE nPos;
+ for ( nPos = 0; nPos < nCount; nPos++ )
+ aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
+
+ SCCOLROW nDest = nStart;
+ for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
+ {
+ SCCOLROW nOrg = ppInfo[nPos]->nOrg;
+ if ( nDest != nOrg )
+ {
+ SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
+ // neue Position des weggeswapten eintragen
+ ScSortInfo* p = ppInfo[nPos];
+ p->nOrg = nDest;
+ ::std::swap(p, aTable[nDest-nStart]);
+ p->nOrg = nOrg;
+ ::std::swap(p, aTable[nOrg-nStart]);
+ OSL_ENSURE( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
+ }
+ if(pProgress)
+ pProgress->SetStateOnPercent( nPos );
+ }
+}
+
+void ScTable::SortReorderByRow( ScSortInfoArray* pArray, ScProgress* pProgress )
+{
+ SCROW nRow1 = pArray->GetStart();
+ SCROW nRow2 = pArray->GetLast();
+ ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
+ assert(pRows); // In sort-by-row mode we must have data rows already populated.
- // Detach all formula cells within the sorted range first.
- sc::EndListeningContext aCxt(*pDocument);
- DetachFormulaCells(aCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
+ // Detach all formula cells within the sorted range first.
+ sc::EndListeningContext aCxt(*pDocument);
+ DetachFormulaCells(aCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
- // Cells in the data rows only reference values in the document. Make
- // a copy before updating the document.
+ // Cells in the data rows only reference values in the document. Make
+ // a copy before updating the document.
- size_t nColCount = aSortParam.nCol2 - aSortParam.nCol1 + 1;
- boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
- SortedRowFlags aRowFlags;
- aSortedCols.reserve(nColCount);
- for (size_t i = 0; i < nColCount; ++i)
- {
- // In the sorted column container, element positions and row
- // positions must match, else formula cells may mis-behave during
- // grouping.
- aSortedCols.push_back(new SortedColumn(nRow1));
- }
+ size_t nColCount = aSortParam.nCol2 - aSortParam.nCol1 + 1;
+ boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
+ SortedRowFlags aRowFlags;
+ aSortedCols.reserve(nColCount);
+ for (size_t i = 0; i < nColCount; ++i)
+ {
+ // In the sorted column container, element positions and row
+ // positions must match, else formula cells may mis-behave during
+ // grouping.
+ aSortedCols.push_back(new SortedColumn(nRow1));
+ }
- for (size_t i = 0; i < pRows->size(); ++i)
+ for (size_t i = 0; i < pRows->size(); ++i)
+ {
+ ScSortInfoArray::Row* pRow = (*pRows)[i];
+ for (size_t j = 0; j < pRow->maCells.size(); ++j)
{
- ScSortInfoArray::Row* pRow = (*pRows)[i];
- for (size_t j = 0; j < pRow->maCells.size(); ++j)
- {
- ScAddress aCellPos(aSortParam.nCol1 + j, nRow1 + i, nTab);
+ ScAddress aCellPos(aSortParam.nCol1 + j, nRow1 + i, nTab);
- ScSortInfoArray::Cell& rCell = pRow->maCells[j];
+ ScSortInfoArray::Cell& rCell = pRow->maCells[j];
- sc::CellStoreType& rCellStore = aSortedCols.at(j).maCells;
- switch (rCell.maCell.meType)
+ sc::CellStoreType& rCellStore = aSortedCols.at(j).maCells;
+ switch (rCell.maCell.meType)
+ {
+ case CELLTYPE_STRING:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(*rCell.maCell.mpString);
+ break;
+ case CELLTYPE_VALUE:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(rCell.maCell.mfValue);
+ break;
+ case CELLTYPE_EDIT:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(rCell.maCell.mpEditText->Clone());
+ break;
+ case CELLTYPE_FORMULA:
{
- case CELLTYPE_STRING:
- assert(rCell.mpAttr);
- rCellStore.push_back(*rCell.maCell.mpString);
- break;
- case CELLTYPE_VALUE:
- assert(rCell.mpAttr);
- rCellStore.push_back(rCell.maCell.mfValue);
- break;
- case CELLTYPE_EDIT:
- assert(rCell.mpAttr);
- rCellStore.push_back(rCell.maCell.mpEditText->Clone());
- break;
- case CELLTYPE_FORMULA:
- {
- assert(rCell.mpAttr);
- size_t n = rCellStore.size();
- sc::CellStoreType::iterator itBlk = rCellStore.push_back(rCell.maCell.mpFormula->Clone(aCellPos));
-
- // Join the formula cells as we fill the container.
- size_t nOffset = n - itBlk->position;
- sc::CellStoreType::position_type aPos(itBlk, nOffset);
- sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
- }
- break;
- default:
- assert(!rCell.mpAttr);
- rCellStore.push_back_empty();
+ assert(rCell.mpAttr);
+ size_t n = rCellStore.size();
+ sc::CellStoreType::iterator itBlk = rCellStore.push_back(rCell.maCell.mpFormula->Clone(aCellPos));
+
+ // Join the formula cells as we fill the container.
+ size_t nOffset = n - itBlk->position;
+ sc::CellStoreType::position_type aPos(itBlk, nOffset);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
}
+ break;
+ default:
+ assert(!rCell.mpAttr);
+ rCellStore.push_back_empty();
+ }
- sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j).maCellTextAttrs;
- if (rCell.mpAttr)
- rAttrStore.push_back(*rCell.mpAttr);
- else
- rAttrStore.push_back_empty();
-
- // At this point each broadcaster instance is managed by 2
- // containers. We will release those in the original storage
- // below before transferring them to the document.
- sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j).maBroadcasters;
- if (rCell.mpBroadcaster)
- // A const pointer would be implicitly converted to a bool type.
- rBCStore.push_back(const_cast<SvtBroadcaster*>(rCell.mpBroadcaster));
- else
- rBCStore.push_back_empty();
-
- // The same with cell note instances ...
- sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j).maCellNotes;
- if (rCell.mpNote)
- rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
- else
- rNoteStore.push_back_empty();
+ sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j).maCellTextAttrs;
+ if (rCell.mpAttr)
+ rAttrStore.push_back(*rCell.mpAttr);
+ else
+ rAttrStore.push_back_empty();
+
+ // At this point each broadcaster instance is managed by 2
+ // containers. We will release those in the original storage
+ // below before transferring them to the document.
+ sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j).maBroadcasters;
+ if (rCell.mpBroadcaster)
+ // A const pointer would be implicitly converted to a bool type.
+ rBCStore.push_back(const_cast<SvtBroadcaster*>(rCell.mpBroadcaster));
+ else
+ rBCStore.push_back_empty();
- if (rCell.mpPattern)
- aSortedCols.at(j).setPattern(aCellPos.Row(), rCell.mpPattern);
- }
+ // The same with cell note instances ...
+ sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j).maCellNotes;
+ if (rCell.mpNote)
+ rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
+ else
+ rNoteStore.push_back_empty();
- if (pArray->IsKeepQuery())
- {
- // Hidden and filtered flags are first converted to segments.
- SCROW nRow = nRow1 + i;
- aRowFlags.setRowHidden(nRow, pRow->mbHidden);
- aRowFlags.setRowFiltered(nRow, pRow->mbFiltered);
- }
+ if (rCell.mpPattern)
+ aSortedCols.at(j).setPattern(aCellPos.Row(), rCell.mpPattern);
+ }
- if (pProgress)
- pProgress->SetStateOnPercent(i);
+ if (pArray->IsKeepQuery())
+ {
+ // Hidden and filtered flags are first converted to segments.
+ SCROW nRow = nRow1 + i;
+ aRowFlags.setRowHidden(nRow, pRow->mbHidden);
+ aRowFlags.setRowFiltered(nRow, pRow->mbFiltered);
}
- for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
+ if (pProgress)
+ pProgress->SetStateOnPercent(i);
+ }
+
+ for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
+ {
+ SCCOL nThisCol = i + aSortParam.nCol1;
+
{
- SCCOL nThisCol = i + aSortParam.nCol1;
+ sc::CellStoreType& rDest = aCol[nThisCol].maCells;
+ sc::CellStoreType& rSrc = aSortedCols[i].maCells;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
- {
- sc::CellStoreType& rDest = aCol[nThisCol].maCells;
- sc::CellStoreType& rSrc = aSortedCols[i].maCells;
- rSrc.transfer(nRow1, nRow2, rDest, nRow1);
- }
+ {
+ sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
+ sc::CellTextAttrStoreType& rSrc = aSortedCols[i].maCellTextAttrs;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
- {
- sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
- sc::CellTextAttrStoreType& rSrc = aSortedCols[i].maCellTextAttrs;
- rSrc.transfer(nRow1, nRow2, rDest, nRow1);
- }
+ {
+ sc::BroadcasterStoreType& rSrc = aSortedCols[i].maBroadcasters;
+ sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
- {
- sc::BroadcasterStoreType& rSrc = aSortedCols[i].maBroadcasters;
- sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
+ // Release current broadcasters first, to prevent them from getting deleted.
+ rDest.release_range(nRow1, nRow2);
- // Release current broadcasters first, to prevent them from getting deleted.
- rDest.release_range(nRow1, nRow2);
+ // Transfer sorted broadcaster segment to the document.
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
- // Transfer sorted broadcaster segment to the document.
- rSrc.transfer(nRow1, nRow2, rDest, nRow1);
- }
+ {
+ sc::CellNoteStoreType& rSrc = aSortedCols[i].maCellNotes;
+ sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
- {
- sc::CellNoteStoreType& rSrc = aSortedCols[i].maCellNotes;
- sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
+ // Do the same as broadcaster storage transfer (to prevent double deletion).
+ rDest.release_range(nRow1, nRow2);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
+ }
- // Do the same as broadcaster storage transfer (to prevent double deletion).
- rDest.release_range(nRow1, nRow2);
- rSrc.transfer(nRow1, nRow2, rDest, nRow1);
- aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
- }
+ {
+ // Get all row spans where the pattern is not NULL.
+ std::vector<PatternSpan> aSpans =
+ sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
+ aSortedCols[i].maPatterns);
+ std::vector<PatternSpan>::iterator it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
{
- // Get all row spans where the pattern is not NULL.
- std::vector<PatternSpan> aSpans =
- sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
- aSortedCols[i].maPatterns);
-
- std::vector<PatternSpan>::iterator it = aSpans.begin(), itEnd = aSpans.end();
- for (; it != itEnd; ++it)
- {
- assert(it->mpPattern); // should never be NULL.
- aCol[nThisCol].SetPatternArea(it->mnRow1, it->mnRow2, *it->mpPattern, true);
- }
+ assert(it->mpPattern); // should never be NULL.
+ aCol[nThisCol].SetPatternArea(it->mnRow1, it->mnRow2, *it->mpPattern, true);
}
-
- aCol[nThisCol].CellStorageModified();
}
- if (pArray->IsKeepQuery())
- {
- aRowFlags.maRowsHidden.build_tree();
- aRowFlags.maRowsFiltered.build_tree();
+ aCol[nThisCol].CellStorageModified();
+ }
- // Remove all flags in the range first.
- SetRowHidden(nRow1, nRow2, false);
- SetRowFiltered(nRow1, nRow2, false);
+ if (pArray->IsKeepQuery())
+ {
+ aRowFlags.maRowsHidden.build_tree();
+ aRowFlags.maRowsFiltered.build_tree();
- std::vector<sc::RowSpan> aSpans =
- sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
+ // Remove all flags in the range first.
+ SetRowHidden(nRow1, nRow2, false);
+ SetRowFiltered(nRow1, nRow2, false);
- std::vector<sc::RowSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
- for (; it != itEnd; ++it)
- SetRowHidden(it->mnRow1, it->mnRow2, true);
+ std::vector<sc::RowSpan> aSpans =
+ sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
- aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
+ std::vector<sc::RowSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
+ SetRowHidden(it->mnRow1, it->mnRow2, true);
- it = aSpans.begin(), itEnd = aSpans.end();
- for (; it != itEnd; ++it)
- SetRowFiltered(it->mnRow1, it->mnRow2, true);
- }
+ aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
- // Attach all formula cells within sorted range, to have them start listening again.
- sc::StartListeningContext aStartListenCxt(*pDocument);
- AttachFormulaCells(
- aStartListenCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
+ it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
+ SetRowFiltered(it->mnRow1, it->mnRow2, true);
}
- else
- {
- std::vector<ScSortInfo*> aTable(nCount);
- SCSIZE nPos;
- for ( nPos = 0; nPos < nCount; nPos++ )
- aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
- SCCOLROW nDest = nStart;
- for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
- {
- SCCOLROW nOrg = ppInfo[nPos]->nOrg;
- if ( nDest != nOrg )
- {
- SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
- // neue Position des weggeswapten eintragen
- ScSortInfo* p = ppInfo[nPos];
- p->nOrg = nDest;
- ::std::swap(p, aTable[nDest-nStart]);
- p->nOrg = nOrg;
- ::std::swap(p, aTable[nOrg-nStart]);
- OSL_ENSURE( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
- }
- if(pProgress)
- pProgress->SetStateOnPercent( nPos );
- }
- }
+ // Attach all formula cells within sorted range, to have them start listening again.
+ sc::StartListeningContext aStartListenCxt(*pDocument);
+ AttachFormulaCells(
+ aStartListenCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
}
short ScTable::CompareCell(
commit ae08391b61e03b1be397cafbb93f0f80c008b38f
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 21:38:05 2014 -0400
Apply sorted patterns as ranges instead of row-by-row.
This scrapes off additional 2.2 seconds.
Change-Id: I08ae67f45946721d722be7183270d7bcf01f96b0
diff --git a/sc/inc/fstalgorithm.hxx b/sc/inc/fstalgorithm.hxx
index 8a50ae0..08d68d4 100644
--- a/sc/inc/fstalgorithm.hxx
+++ b/sc/inc/fstalgorithm.hxx
@@ -44,6 +44,35 @@ void buildSpan(
}
}
+template<typename _Key, typename _Val, typename _Span>
+void buildSpanWithValue(
+ std::vector<_Span>& rSpans,
+ typename mdds::flat_segment_tree<_Key,_Val>::const_iterator it,
+ typename mdds::flat_segment_tree<_Key,_Val>::const_iterator itEnd, const _Key* pStart )
+{
+ _Key nLastPos = it->first;
+ _Val nLastVal = it->second;
+ for (++it; it != itEnd; ++it)
+ {
+ _Key nThisPos = it->first;
+ _Val nThisVal = it->second;
+
+ if (nLastVal)
+ {
+ _Key nIndex1 = nLastPos;
+ _Key nIndex2 = nThisPos-1;
+
+ if (!pStart || *pStart < nIndex1)
+ rSpans.push_back(_Span(nIndex1, nIndex2, nLastVal));
+ else if (*pStart <= nIndex2)
+ rSpans.push_back(_Span(*pStart, nIndex2, nLastVal));
+ }
+
+ nLastPos = nThisPos;
+ nLastVal = nThisVal;
+ }
+}
+
/**
* Convert a flat_segment_tree structure whose value type is boolean, into
* an array of ranges that corresponds with the segments that have a 'true'
@@ -61,6 +90,25 @@ std::vector<_Span> toSpanArray( const mdds::flat_segment_tree<_Key,bool>& rTree
return aSpans;
}
+/**
+ * Convert a flat_segment_tree structure into an array of ranges with
+ * values. Only those ranges whose value is evaluated to be true will be
+ * included. The value type must be something that supports bool operator.
+ * The span type must support a constructor that takes a start key, an end
+ * key and a value in this order.
+ */
+template<typename _Key, typename _Val, typename _Span>
+std::vector<_Span> toSpanArrayWithValue( const mdds::flat_segment_tree<_Key,_Val>& rTree )
+{
+ typedef mdds::flat_segment_tree<_Key,_Val> FstType;
+
+ std::vector<_Span> aSpans;
+
+ typename FstType::const_iterator it = rTree.begin(), itEnd = rTree.end();
+ buildSpanWithValue<_Key,_Val,_Span>(aSpans, it, itEnd, NULL);
+ return aSpans;
+}
+
template<typename _Key, typename _Span>
std::vector<_Span> toSpanArray( const mdds::flat_segment_tree<_Key,bool>& rTree, _Key nStartPos )
{
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 77ecb69..bedbb62 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -69,6 +69,7 @@
#include <boost/unordered_set.hpp>
#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
+#include <mdds/flat_segment_tree.hpp>
using namespace ::com::sun::star;
@@ -422,17 +423,28 @@ namespace {
struct SortedColumn : boost::noncopyable
{
+ typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
sc::CellStoreType maCells;
sc::CellTextAttrStoreType maCellTextAttrs;
sc::BroadcasterStoreType maBroadcasters;
sc::CellNoteStoreType maCellNotes;
+ PatRangeType maPatterns;
+ PatRangeType::const_iterator miPatternPos;
+
SortedColumn( size_t nTopEmptyRows ) :
maCells(nTopEmptyRows),
maCellTextAttrs(nTopEmptyRows),
maBroadcasters(nTopEmptyRows),
- maCellNotes(nTopEmptyRows) {}
+ maCellNotes(nTopEmptyRows),
+ maPatterns(0, MAXROWCOUNT, NULL),
+ miPatternPos(maPatterns.begin()) {}
+
+ void setPattern( SCROW nRow, const ScPatternAttr* pPat )
+ {
+ miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
+ }
};
struct SortedRowFlags
@@ -461,6 +473,16 @@ struct SortedRowFlags
}
};
+struct PatternSpan
+{
+ SCROW mnRow1;
+ SCROW mnRow2;
+ const ScPatternAttr* mpPattern;
+
+ PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
+ mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
+};
+
}
bool ScTable::IsSortCollatorGlobal() const
@@ -595,9 +617,8 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
else
rNoteStore.push_back_empty();
- // Set formats to the document directly.
if (rCell.mpPattern)
- aCol[aCellPos.Col()].SetPattern(aCellPos.Row(), *rCell.mpPattern, true);
+ aSortedCols.at(j).setPattern(aCellPos.Row(), rCell.mpPattern);
}
if (pArray->IsKeepQuery())
@@ -649,6 +670,20 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
}
+ {
+ // Get all row spans where the pattern is not NULL.
+ std::vector<PatternSpan> aSpans =
+ sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
+ aSortedCols[i].maPatterns);
+
+ std::vector<PatternSpan>::iterator it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
+ {
+ assert(it->mpPattern); // should never be NULL.
+ aCol[nThisCol].SetPatternArea(it->mnRow1, it->mnRow2, *it->mpPattern, true);
+ }
+ }
+
aCol[nThisCol].CellStorageModified();
}
commit 961415f4418c99ae435c57135bb7156afb84a7eb
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 20:59:13 2014 -0400
Skip reordering of cell formats when whole range is over one pattern entry.
Change-Id: I2ee377a865f361673912b796af1eb8517c1456f7
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index a672303..77ecb69 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -370,6 +370,10 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2, b
for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; ++nCol)
{
ScColumn& rCol = aCol[nCol];
+
+ // Skip reordering of cell formats if the whole span is on the same pattern entry.
+ bool bUniformPattern = rCol.GetPatternCount(nInd1, nInd2) < 2u;
+
sc::ColumnBlockConstPosition aBlockPos;
rCol.InitBlockPosition(aBlockPos);
for (SCROW nRow = nInd1; nRow <= nInd2; ++nRow)
@@ -382,7 +386,7 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2, b
rCell.mpBroadcaster = rCol.GetBroadcaster(aBlockPos, nRow);
rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
- if (aSortParam.bIncludePattern)
+ if (!bUniformPattern && aSortParam.bIncludePattern)
rCell.mpPattern = rCol.GetPattern(nRow);
}
}
commit ac8fc9edb79e1be15a13e3a90399ec7f84a8c588
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 16:29:14 2014 -0400
Skip adjustment of row height when all rows have the same height.
Change-Id: I490ecade6b909bcf36b848c05e198d58adc90e0a
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index ac81e17..f16b292 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1572,6 +1572,8 @@ public:
SC_DLLPUBLIC bool IsManualRowHeight(SCROW nRow, SCTAB nTab) const;
+ bool HasUniformRowHeight( SCTAB nTab, SCROW nRow1, SCROW nRow2 ) const;
+
/**
* Write all column row flags to table's flag data, because not all column
* row attributes are stored in the flag data members. This is necessary
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index ac44f30..f6e2583 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -815,6 +815,8 @@ public:
bool IsManualRowHeight(SCROW nRow) const;
+ bool HasUniformRowHeight( SCROW nRow1, SCROW nRow2 ) const;
+
void SyncColRowFlags();
void StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 );
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index abf5297..9513dfb 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -279,4 +279,13 @@ void ScDocument::UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW
pTab->UpdateScriptTypes(rPos.Col(), rPos.Row(), rPos.Col()+nColSize-1, rPos.Row()+nRowSize-1);
}
+bool ScDocument::HasUniformRowHeight( SCTAB nTab, SCROW nRow1, SCROW nRow2 ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->HasUniformRowHeight(nRow1, nRow2);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index eeb4643..385bea1 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -12,6 +12,7 @@
#include <document.hxx>
#include <clipparam.hxx>
#include <bcaslot.hxx>
+#include <segmenttree.hxx>
bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
{
@@ -110,4 +111,17 @@ void ScTable::UpdateScriptTypes( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nR
aCol[nCol].UpdateScriptTypes(nRow1, nRow2);
}
+bool ScTable::HasUniformRowHeight( SCROW nRow1, SCROW nRow2 ) const
+{
+ if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
+ return false;
+
+ ScFlatUInt16RowSegments::RangeData aData;
+ if (!mpRowHeights->getRangeData(nRow1, aData))
+ // Search failed.
+ return false;
+
+ return nRow2 <= aData.mnRow2;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx
index de45b1a..deaf244 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -490,13 +490,19 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
WaitObject aWait( rDocShell.GetActiveDialogParent() );
+ SCROW nStartRow = aLocalParam.nRow1 + (aLocalParam.bHasHeader ? 1 : 0);
+
// Calculate the script types for all cells in the sort range beforehand.
// This will speed up the row height adjustment that takes place after the
// sort.
pDoc->UpdateScriptTypes(
- ScAddress(rSortParam.nCol1,rSortParam.nRow1,nTab),
- rSortParam.nCol2-rSortParam.nCol1+1,
- rSortParam.nRow2-rSortParam.nRow1+1);
+ ScAddress(aLocalParam.nCol1,nStartRow,nTab),
+ aLocalParam.nCol2-aLocalParam.nCol1+1,
+ aLocalParam.nRow2-nStartRow+1);
+
+ // No point adjusting row heights after the sort when all rows have the same height.
+ bool bUniformRowHeight =
+ pDoc->HasUniformRowHeight(nTab, nStartRow, aLocalParam.nRow2);
bool bRepeatQuery = false; // bestehenden Filter wiederholen?
ScQueryParam aQueryParam;
@@ -632,8 +638,9 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
}
}
- ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
- aLocalParam.nCol2, aLocalParam.nRow2, nTab );
+ ScRange aDirtyRange(
+ aLocalParam.nCol1, nStartRow, nTab,
+ aLocalParam.nCol2, aLocalParam.nRow2, nTab);
pDoc->SetDirty( aDirtyRange );
if (bPaint)
@@ -659,7 +666,8 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
}
- rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
+ if (!bUniformRowHeight)
+ rDocShell.AdjustRowHeight(nStartRow, aLocalParam.nRow2, nTab);
// #i59745# set collected drawing undo actions at sorting undo action
if( pUndoAction && pDrawLayer )
commit ab3893f83536829797218f91adb779c12044069a
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 15:28:49 2014 -0400
Update script types of all cells in sort range ahead of time.
To ensure that there is no SC_SCRIPTTYPE_UNKNOWN in the sort range,
the presence of which would slow down during AdjustRowHeight().
This only adds a tiny overhead (0.3 second) and cuts the duration of
AdjustRowHeight() from 15 seconds to 5 seconds.
Change-Id: I145e901225ef1136f53c6f682ffed3902099859c
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 25f1be6..51ddaf4 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -499,6 +499,7 @@ public:
sc::CellStoreType::iterator itr);
void SetScriptType( SCROW nRow, sal_uInt8 nType );
+ void UpdateScriptTypes( SCROW nRow1, SCROW nRow2 );
size_t GetFormulaHash( SCROW nRow ) const;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index b57ce70..ac81e17 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -2060,6 +2060,7 @@ public:
sal_uInt8 GetScriptType( const ScAddress& rPos ) const;
void SetScriptType( const ScAddress& rPos, sal_uInt8 nType );
+ void UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW nRowSize );
size_t GetFormulaHash( const ScAddress& rPos ) const;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index b29bb8d..ac44f30 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -864,6 +864,7 @@ public:
sal_uInt8 GetScriptType( SCCOL nCol, SCROW nRow ) const;
void SetScriptType( SCCOL nCol, SCROW nRow, sal_uInt8 nType );
+ void UpdateScriptTypes( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
sal_uInt8 GetRangeScriptType( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 );
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 2698f0b..cad94e5 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -25,6 +25,9 @@
#include <conditio.hxx>
#include <formulagroup.hxx>
#include <tokenarray.hxx>
+#include <globalnames.hxx>
+#include <scitems.hxx>
+#include <cellform.hxx>
#include <svl/sharedstringpool.hxx>
@@ -721,4 +724,105 @@ void ScColumn::PostprocessRangeNameUpdate( sc::CompileFormulaContext& rCompileCx
std::for_each(aGroups.begin(), aGroups.end(), aFunc);
}
+namespace {
+
+class ScriptTypeUpdater
+{
+ ScColumn& mrCol;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miPosAttr;
+ ScConditionalFormatList* mpCFList;
+ SvNumberFormatter* mpFormatter;
+ ScAddress maPos;
+ bool mbUpdated;
+
+private:
+ void updateScriptType( size_t nRow, ScRefCellValue& rCell )
+ {
+ sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
+ miPosAttr = aAttrPos.first;
+
+ if (aAttrPos.first->type != sc::element_type_celltextattr)
+ return;
+
+ sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
+ if (rAttr.mnScriptType != SC_SCRIPTTYPE_UNKNOWN)
+ // Script type already deteremined. Skip it.
+ return;
+
+ const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
+ if (!pPat)
+ // In theory this should never return NULL. But let's be safe.
+ return;
+
+ const SfxItemSet* pCondSet = NULL;
+ if (mpCFList)
+ {
+ maPos.SetRow(nRow);
+ const ScCondFormatItem& rItem =
+ static_cast<const ScCondFormatItem&>(pPat->GetItem(ATTR_CONDITIONAL));
+ const std::vector<sal_uInt32>& rData = rItem.GetCondFormatData();
+ pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
+ }
+
+ OUString aStr;
+ Color* pColor;
+ sal_uLong nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
+ ScCellFormat::GetString(rCell, nFormat, aStr, &pColor, *mpFormatter, &mrCol.GetDoc());
+
+ rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
+ mbUpdated = true;
+ }
+
+public:
+ ScriptTypeUpdater( ScColumn& rCol ) :
+ mrCol(rCol),
+ mrTextAttrs(rCol.GetCellAttrStore()),
+ miPosAttr(mrTextAttrs.begin()),
+ mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
+ mpFormatter(rCol.GetDoc().GetFormatTable()),
+ maPos(rCol.GetCol(), 0, rCol.GetTab()),
+ mbUpdated(false)
+ {}
+
+ void operator() ( size_t nRow, double fVal )
+ {
+ ScRefCellValue aCell(fVal);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const svl::SharedString& rStr )
+ {
+ ScRefCellValue aCell(&rStr);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const EditTextObject* pText )
+ {
+ ScRefCellValue aCell(pText);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const ScFormulaCell* pCell )
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
+ updateScriptType(nRow, aCell);
+ }
+
+ bool isUpdated() const { return mbUpdated; }
+};
+
+}
+
+void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
+{
+ if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
+ return;
+
+ ScriptTypeUpdater aFunc(*this);
+ sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+ if (aFunc.isUpdated())
+ CellStorageModified();
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index a04e8f9..abf5297 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -270,4 +270,13 @@ void ScDocument::SharePooledResources( ScDocument* pSrcDoc )
mpCellStringPool = pSrcDoc->mpCellStringPool;
}
+void ScDocument::UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW nRowSize )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->UpdateScriptTypes(rPos.Col(), rPos.Row(), rPos.Col()+nColSize-1, rPos.Row()+nRowSize-1);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index 928c109..eeb4643 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -101,4 +101,13 @@ void ScTable::PostprocessRangeNameUpdate( sc::CompileFormulaContext& rCompileCxt
aCol[i].PostprocessRangeNameUpdate(rCompileCxt);
}
+void ScTable::UpdateScriptTypes( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
+ return;
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].UpdateScriptTypes(nRow1, nRow2);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx
index 605ba47..de45b1a 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -490,6 +490,14 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
WaitObject aWait( rDocShell.GetActiveDialogParent() );
+ // Calculate the script types for all cells in the sort range beforehand.
+ // This will speed up the row height adjustment that takes place after the
+ // sort.
+ pDoc->UpdateScriptTypes(
+ ScAddress(rSortParam.nCol1,rSortParam.nRow1,nTab),
+ rSortParam.nCol2-rSortParam.nCol1+1,
+ rSortParam.nRow2-rSortParam.nRow1+1);
+
bool bRepeatQuery = false; // bestehenden Filter wiederholen?
ScQueryParam aQueryParam;
pDBData->GetQueryParam( aQueryParam );
@@ -651,7 +659,6 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
}
- // AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, bPaint );
rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
// #i59745# set collected drawing undo actions at sorting undo action
commit add87f111b94751b962a701824f6197ddea123e4
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Tue Apr 22 00:36:40 2014 -0400
Handle reordering of row hidden and filtered flags during sort.
Only when the "keep query" flag is set.
Change-Id: Ia799f62ec53a08cfd92e93c546965002c030b324
diff --git a/sc/inc/fstalgorithm.hxx b/sc/inc/fstalgorithm.hxx
index 20972ef..8a50ae0 100644
--- a/sc/inc/fstalgorithm.hxx
+++ b/sc/inc/fstalgorithm.hxx
@@ -15,6 +15,35 @@
namespace sc {
+template<typename _Key, typename _Span>
+void buildSpan(
+ std::vector<_Span>& rSpans,
+ typename mdds::flat_segment_tree<_Key,bool>::const_iterator it,
+ typename mdds::flat_segment_tree<_Key,bool>::const_iterator itEnd, const _Key* pStart )
+{
+ _Key nLastPos = it->first;
+ bool bLastVal = it->second;
+ for (++it; it != itEnd; ++it)
+ {
+ _Key nThisPos = it->first;
+ bool bThisVal = it->second;
+
+ if (bLastVal)
+ {
+ _Key nIndex1 = nLastPos;
+ _Key nIndex2 = nThisPos-1;
+
+ if (!pStart || *pStart < nIndex1)
+ rSpans.push_back(_Span(nIndex1, nIndex2));
+ else if (*pStart <= nIndex2)
+ rSpans.push_back(_Span(*pStart, nIndex2));
+ }
+
+ nLastPos = nThisPos;
+ bLastVal = bThisVal;
+ }
+}
+
/**
* Convert a flat_segment_tree structure whose value type is boolean, into
* an array of ranges that corresponds with the segments that have a 'true'
@@ -28,20 +57,29 @@ std::vector<_Span> toSpanArray( const mdds::flat_segment_tree<_Key,bool>& rTree
std::vector<_Span> aSpans;
typename FstType::const_iterator it = rTree.begin(), itEnd = rTree.end();
- _Key nLastPos = it->first;
- bool bLastVal = it->second;
- for (++it; it != itEnd; ++it)
- {
- _Key nThisPos = it->first;
- bool bThisVal = it->second;
+ buildSpan<_Key,_Span>(aSpans, it, itEnd, NULL);
+ return aSpans;
+}
- if (bLastVal)
- aSpans.push_back(_Span(nLastPos, nThisPos-1));
+template<typename _Key, typename _Span>
+std::vector<_Span> toSpanArray( const mdds::flat_segment_tree<_Key,bool>& rTree, _Key nStartPos )
+{
+ typedef mdds::flat_segment_tree<_Key,bool> FstType;
- nLastPos = nThisPos;
- bLastVal = bThisVal;
- }
+ std::vector<_Span> aSpans;
+ if (!rTree.is_tree_valid())
+ return aSpans;
+
+ bool bThisVal = false;
+ std::pair<typename FstType::const_iterator, bool> r =
+ rTree.search_tree(nStartPos, bThisVal);
+
+ if (!r.second)
+ // Tree search failed.
+ return aSpans;
+ typename FstType::const_iterator it = r.first, itEnd = rTree.end();
+ buildSpan<_Key,_Span>(aSpans, it, itEnd, &nStartPos);
return aSpans;
}
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 413152e..b29bb8d 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1011,7 +1011,7 @@ private:
ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const;
short Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const;
short Compare( ScSortInfoArray*, SCCOLROW nIndex1, SCCOLROW nIndex2) const;
- ScSortInfoArray* CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 );
+ ScSortInfoArray* CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2, bool bKeepQuery );
void QuickSort( ScSortInfoArray*, SCsCOLROW nLo, SCsCOLROW nHi);
void SortReorder( ScSortInfoArray*, ScProgress* );
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index b1a40d0..a672303 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -57,6 +57,7 @@
#include "mtvcellfunc.hxx"
#include "columnspanset.hxx"
#include <stlalgorithm.hxx>
+#include <fstalgorithm.hxx>
#include <listenercontext.hxx>
#include <sharedformula.hxx>
@@ -234,8 +235,17 @@ public:
Cell() : mpAttr(NULL), mpBroadcaster(NULL), mpNote(NULL), mpPattern(NULL) {}
};
- typedef std::vector<Cell> RowType;
- typedef std::vector<RowType*> RowsType;
+ struct Row
+ {
+ std::vector<Cell> maCells;
+
+ bool mbHidden:1;
+ bool mbFiltered:1;
+
+ Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
+ };
+
+ typedef std::vector<Row*> RowsType;
private:
boost::scoped_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
@@ -246,21 +256,24 @@ private:
SCCOLROW mnLastIndex; /// index of last non-empty cell position.
sal_uInt16 nUsedSorts;
+ bool mbKeepQuery;
+
public:
ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
- pppInfo( new ScSortInfo**[nSorts]),
- nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
- mnLastIndex(nInd2),
- nUsedSorts( nSorts )
+ pppInfo( new ScSortInfo**[nSorts]),
+ nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
+ mnLastIndex(nInd2),
+ nUsedSorts(nSorts),
+ mbKeepQuery(false)
+ {
+ for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
{
- for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
- {
- ScSortInfo** ppInfo = new ScSortInfo* [nCount];
- for ( SCSIZE j = 0; j < nCount; j++ )
- ppInfo[j] = new ScSortInfo;
- pppInfo[nSort] = ppInfo;
- }
+ ScSortInfo** ppInfo = new ScSortInfo* [nCount];
+ for ( SCSIZE j = 0; j < nCount; j++ )
+ ppInfo[j] = new ScSortInfo;
+ pppInfo[nSort] = ppInfo;
}
+ }
~ScSortInfoArray()
{
@@ -274,9 +287,13 @@ public:
delete[] pppInfo;
if (mpRows)
- std::for_each(mpRows->begin(), mpRows->end(), ScDeleteObjectByPtr<RowType>());
+ std::for_each(mpRows->begin(), mpRows->end(), ScDeleteObjectByPtr<Row>());
}
+ void SetKeepQuery( bool b ) { mbKeepQuery = b; }
+
+ bool IsKeepQuery() const { return mbKeepQuery; }
+
ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd )
{ return (pppInfo[nSort])[ nInd - nStart ]; }
@@ -311,7 +328,7 @@ public:
mpRows.reset(new RowsType);
mpRows->reserve(nRowSize);
for (size_t i = 0; i < nRowSize; ++i)
- mpRows->push_back(new RowType(nColSize, Cell()));
+ mpRows->push_back(new Row(nColSize));
return *mpRows;
}
@@ -322,12 +339,14 @@ public:
}
};
-ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
+ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2, bool bKeepQuery )
{
sal_uInt16 nUsedSorts = 1;
while ( nUsedSorts < aSortParam.GetSortKeyCount() && aSortParam.maKeyState[nUsedSorts].bDoSort )
nUsedSorts++;
ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
+ pArray->SetKeepQuery(bKeepQuery);
+
if ( aSortParam.bByRow )
{
for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
@@ -355,8 +374,8 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
rCol.InitBlockPosition(aBlockPos);
for (SCROW nRow = nInd1; nRow <= nInd2; ++nRow)
{
- ScSortInfoArray::RowType& rRow = *rRows[nRow-nInd1];
- ScSortInfoArray::Cell& rCell = rRow[nCol-aSortParam.nCol1];
+ ScSortInfoArray::Row& rRow = *rRows[nRow-nInd1];
+ ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-aSortParam.nCol1];
rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
@@ -367,6 +386,16 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
rCell.mpPattern = rCol.GetPattern(nRow);
}
}
+
+ if (bKeepQuery)
+ {
+ for (SCROW nRow = nInd1; nRow <= nInd2; ++nRow)
+ {
+ ScSortInfoArray::Row& rRow = *rRows[nRow-nInd1];
+ rRow.mbHidden = RowHidden(nRow);
+ rRow.mbFiltered = RowFiltered(nRow);
+ }
+ }
}
else
{
@@ -389,6 +418,7 @@ namespace {
struct SortedColumn : boost::noncopyable
{
+
sc::CellStoreType maCells;
sc::CellTextAttrStoreType maCellTextAttrs;
sc::BroadcasterStoreType maBroadcasters;
@@ -401,6 +431,32 @@ struct SortedColumn : boost::noncopyable
maCellNotes(nTopEmptyRows) {}
};
+struct SortedRowFlags
+{
+ typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
+
+ FlagsType maRowsHidden;
+ FlagsType maRowsFiltered;
+ FlagsType::const_iterator miPosHidden;
+ FlagsType::const_iterator miPosFiltered;
+
+ SortedRowFlags() :
+ maRowsHidden(0, MAXROWCOUNT, false),
+ maRowsFiltered(0, MAXROWCOUNT, false),
+ miPosHidden(maRowsHidden.begin()),
+ miPosFiltered(maRowsFiltered.begin()) {}
+
+ void setRowHidden( SCROW nRow, bool b )
+ {
+ miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
+ }
+
+ void setRowFiltered( SCROW nRow, bool b )
+ {
+ miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
+ }
+};
+
}
bool ScTable::IsSortCollatorGlobal() const
@@ -461,6 +517,7 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
size_t nColCount = aSortParam.nCol2 - aSortParam.nCol1 + 1;
boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
+ SortedRowFlags aRowFlags;
aSortedCols.reserve(nColCount);
for (size_t i = 0; i < nColCount; ++i)
{
@@ -472,12 +529,12 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
for (size_t i = 0; i < pRows->size(); ++i)
{
- ScSortInfoArray::RowType* pRow = (*pRows)[i];
- for (size_t j = 0; j < pRow->size(); ++j)
+ ScSortInfoArray::Row* pRow = (*pRows)[i];
+ for (size_t j = 0; j < pRow->maCells.size(); ++j)
{
ScAddress aCellPos(aSortParam.nCol1 + j, nRow1 + i, nTab);
- ScSortInfoArray::Cell& rCell = (*pRow)[j];
+ ScSortInfoArray::Cell& rCell = pRow->maCells[j];
sc::CellStoreType& rCellStore = aSortedCols.at(j).maCells;
switch (rCell.maCell.meType)
@@ -539,6 +596,14 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
aCol[aCellPos.Col()].SetPattern(aCellPos.Row(), *rCell.mpPattern, true);
}
+ if (pArray->IsKeepQuery())
+ {
+ // Hidden and filtered flags are first converted to segments.
+ SCROW nRow = nRow1 + i;
+ aRowFlags.setRowHidden(nRow, pRow->mbHidden);
+ aRowFlags.setRowFiltered(nRow, pRow->mbFiltered);
+ }
+
if (pProgress)
pProgress->SetStateOnPercent(i);
}
@@ -583,6 +648,29 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
aCol[nThisCol].CellStorageModified();
}
+ if (pArray->IsKeepQuery())
+ {
+ aRowFlags.maRowsHidden.build_tree();
+ aRowFlags.maRowsFiltered.build_tree();
+
+ // Remove all flags in the range first.
+ SetRowHidden(nRow1, nRow2, false);
+ SetRowFiltered(nRow1, nRow2, false);
+
+ std::vector<sc::RowSpan> aSpans =
+ sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
+
+ std::vector<sc::RowSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
+ SetRowHidden(it->mnRow1, it->mnRow2, true);
+
+ aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
+
+ it = aSpans.begin(), itEnd = aSpans.end();
+ for (; it != itEnd; ++it)
+ SetRowFiltered(it->mnRow1, it->mnRow2, true);
+ }
+
// Attach all formula cells within sorted range, to have them start listening again.
sc::StartListeningContext aStartListenCxt(*pDocument);
AttachFormulaCells(
@@ -906,7 +994,7 @@ void ScTable::Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* p
if(pProgress)
pProgress->SetState( 0, nLastRow-nRow1 );
- boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(nRow1, nLastRow));
+ boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(nRow1, nLastRow, bKeepQuery));
if ( nLastRow - nRow1 > 255 )
DecoladeRow(pArray.get(), nRow1, nLastRow);
@@ -931,7 +1019,7 @@ void ScTable::Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* p
if(pProgress)
pProgress->SetState( 0, nLastCol-nCol1 );
- boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(nCol1, nLastCol));
+ boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(nCol1, nLastCol, bKeepQuery));
QuickSort(pArray.get(), nCol1, nLastCol);
SortReorder(pArray.get(), pProgress);
@@ -1932,7 +2020,7 @@ void ScTable::TopTenQuery( ScQueryParam& rParam )
bSortCollatorInitialized = true;
InitSortCollator( aLocalSortParam );
}
- ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 );
+ ScSortInfoArray* pArray = CreateSortInfoArray(nRow1, rParam.nRow2, bGlobalKeepQuery);
DecoladeRow( pArray, nRow1, rParam.nRow2 );
QuickSort( pArray, nRow1, rParam.nRow2 );
ScSortInfo** ppInfo = pArray->GetFirstArray();
commit bf8b2c162f82e84ad6995c9d370b617fef908e06
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Mon Apr 21 21:50:44 2014 -0400
Handle cell formats correctly during sort.
Unfortunately sorting becomes noticeably slower when cell format is
enabled. Perhaps a future project.
Change-Id: Ieb02b207b62df343d1f667a8075874b7688ee9a7
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 88e693e..b1a40d0 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -229,8 +229,9 @@ public:
const sc::CellTextAttr* mpAttr;
const SvtBroadcaster* mpBroadcaster;
const ScPostIt* mpNote;
+ const ScPatternAttr* mpPattern;
- Cell() : mpAttr(NULL), mpBroadcaster(NULL), mpNote(NULL) {}
+ Cell() : mpAttr(NULL), mpBroadcaster(NULL), mpNote(NULL), mpPattern(NULL) {}
};
typedef std::vector<Cell> RowType;
@@ -361,6 +362,9 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
rCell.mpBroadcaster = rCol.GetBroadcaster(aBlockPos, nRow);
rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
+
+ if (aSortParam.bIncludePattern)
+ rCell.mpPattern = rCol.GetPattern(nRow);
}
}
}
@@ -471,6 +475,8 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
ScSortInfoArray::RowType* pRow = (*pRows)[i];
for (size_t j = 0; j < pRow->size(); ++j)
{
+ ScAddress aCellPos(aSortParam.nCol1 + j, nRow1 + i, nTab);
+
ScSortInfoArray::Cell& rCell = (*pRow)[j];
sc::CellStoreType& rCellStore = aSortedCols.at(j).maCells;
@@ -492,9 +498,9 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
{
assert(rCell.mpAttr);
size_t n = rCellStore.size();
- ScAddress aCellPos(aSortParam.nCol1 + j, nRow1 + i, nTab);
sc::CellStoreType::iterator itBlk = rCellStore.push_back(rCell.maCell.mpFormula->Clone(aCellPos));
+ // Join the formula cells as we fill the container.
size_t nOffset = n - itBlk->position;
sc::CellStoreType::position_type aPos(itBlk, nOffset);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
@@ -527,6 +533,10 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
else
rNoteStore.push_back_empty();
+
+ // Set formats to the document directly.
+ if (rCell.mpPattern)
+ aCol[aCellPos.Col()].SetPattern(aCellPos.Row(), *rCell.mpPattern, true);
}
if (pProgress)
commit 55c1a82e837da2a6b80cfe454017a461e882e9fe
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Mon Apr 21 19:10:47 2014 -0400
Make sure to ignore trailing empty rows in all places.
Otherwise it'd throw an exception.
All unit tests pass now.
Change-Id: I1dc49afcdf3e6604de4c0bc2440106c5ce0da090
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 9caae2b..88e693e 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -242,22 +242,24 @@ private:
ScSortInfo*** pppInfo;
SCSIZE nCount;
SCCOLROW nStart;
+ SCCOLROW mnLastIndex; /// index of last non-empty cell position.
sal_uInt16 nUsedSorts;
public:
- ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
- pppInfo( new ScSortInfo**[nSorts]),
- nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
- nUsedSorts( nSorts )
- {
- for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
- {
- ScSortInfo** ppInfo = new ScSortInfo* [nCount];
- for ( SCSIZE j = 0; j < nCount; j++ )
- ppInfo[j] = new ScSortInfo;
- pppInfo[nSort] = ppInfo;
- }
- }
+ ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
+ pppInfo( new ScSortInfo**[nSorts]),
+ nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
+ mnLastIndex(nInd2),
+ nUsedSorts( nSorts )
+ {
+ for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
+ {
+ ScSortInfo** ppInfo = new ScSortInfo* [nCount];
+ for ( SCSIZE j = 0; j < nCount; j++ )
+ ppInfo[j] = new ScSortInfo;
+ pppInfo[nSort] = ppInfo;
+ }
+ }
~ScSortInfoArray()
{
@@ -300,6 +302,7 @@ public:
sal_uInt16 GetUsedSorts() const { return nUsedSorts; }
ScSortInfo** GetFirstArray() const { return pppInfo[0]; }
SCCOLROW GetStart() const { return nStart; }
+ SCCOLROW GetLast() const { return mnLastIndex; }
SCSIZE GetCount() const { return nCount; }
RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
@@ -340,7 +343,7 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
}
}
- // Filll row-wise data table.
+ // Fill row-wise data table.
ScSortInfoArray::RowsType& rRows = pArray->InitDataRows(
nInd2 - nInd1 + 1, aSortParam.nCol2 - aSortParam.nCol1 + 1);
@@ -440,13 +443,14 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
if (aSortParam.bByRow)
{
- SCROW nRow1 = aSortParam.nRow1 + (aSortParam.bHasHeader ? 1 : 0);
+ SCROW nRow1 = pArray->GetStart();
+ SCROW nRow2 = pArray->GetLast();
ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
assert(pRows); // In sort-by-row mode we must have data rows already populated.
// Detach all formula cells within the sorted range first.
sc::EndListeningContext aCxt(*pDocument);
- DetachFormulaCells(aCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, aSortParam.nRow2);
+ DetachFormulaCells(aCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
// Cells in the data rows only reference values in the document. Make
// a copy before updating the document.
@@ -536,13 +540,13 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
{
sc::CellStoreType& rDest = aCol[nThisCol].maCells;
sc::CellStoreType& rSrc = aSortedCols[i].maCells;
- rSrc.transfer(nRow1, aSortParam.nRow2, rDest, nRow1);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
}
{
sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
sc::CellTextAttrStoreType& rSrc = aSortedCols[i].maCellTextAttrs;
- rSrc.transfer(nRow1, aSortParam.nRow2, rDest, nRow1);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
}
{
@@ -550,10 +554,10 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
// Release current broadcasters first, to prevent them from getting deleted.
- rDest.release_range(nRow1, aSortParam.nRow2);
+ rDest.release_range(nRow1, nRow2);
// Transfer sorted broadcaster segment to the document.
- rSrc.transfer(nRow1, aSortParam.nRow2, rDest, nRow1);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
}
{
@@ -561,9 +565,9 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
// Do the same as broadcaster storage transfer (to prevent double deletion).
- rDest.release_range(nRow1, aSortParam.nRow2);
- rSrc.transfer(nRow1, aSortParam.nRow2, rDest, nRow1);
- aCol[nThisCol].UpdateNoteCaptions(nRow1, aSortParam.nRow2);
+ rDest.release_range(nRow1, nRow2);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
}
aCol[nThisCol].CellStorageModified();
@@ -572,7 +576,7 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
// Attach all formula cells within sorted range, to have them start listening again.
sc::StartListeningContext aStartListenCxt(*pDocument);
AttachFormulaCells(
- aStartListenCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, aSortParam.nRow2);
+ aStartListenCxt, aSortParam.nCol1, nRow1, aSortParam.nCol2, nRow2);
}
else
{
commit 55617da42b99cca58e2f73934a93e3df8cb648e7
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Mon Apr 21 17:14:47 2014 -0400
Annotate the test code a bit to make it easier to follow.
Also, during unit test, the function separator is set to ';', not ','.
Change-Id: I3ba948c54bee3c05715928f6f2f2945621fbacad
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 824f84a..beb1bf1 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4599,12 +4599,12 @@ void Test::testSortWithFormulaRefs()
m_pDoc->InsertTab(1, "List2");
const char* aFormulaData[6] = {
- "=IF($List1.A2<>\"\",$List1.A2,\"\")",
- "=IF($List1.A3<>\"\",$List1.A3,\"\")",
- "=IF($List1.A4<>\"\",$List1.A4,\"\")",
- "=IF($List1.A5<>\"\",$List1.A5,\"\")",
- "=IF($List1.A6<>\"\",$List1.A6,\"\")",
- "=IF($List1.A7<>\"\",$List1.A7,\"\")",
+ "=IF($List1.A2<>\"\";$List1.A2;\"\")",
+ "=IF($List1.A3<>\"\";$List1.A3;\"\")",
+ "=IF($List1.A4<>\"\";$List1.A4;\"\")",
+ "=IF($List1.A5<>\"\";$List1.A5;\"\")",
+ "=IF($List1.A6<>\"\";$List1.A6;\"\")",
+ "=IF($List1.A7<>\"\";$List1.A7;\"\")",
};
const char* aTextData[4] = {
@@ -4622,16 +4622,16 @@ void Test::testSortWithFormulaRefs()
"",
"",
};
- // insert data to sort
- SCROW nStart = 1, nEnd = 4;
- for ( SCROW i = nStart; i <= nEnd; ++i )
+
+ // Insert data to sort in A2:A5 on the 1st sheet.
+ for (SCROW i = 1; i <= 4; ++i)
m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
- // insert forumulas
- nStart = 0;
- nEnd = SAL_N_ELEMENTS(aFormulaData);
- for ( SCROW i = nStart; i < nEnd; ++i )
+
+ // Insert forumulas in A1:A6 on the 2nd sheet.
+ for (SCROW i = 0; i < SAL_N_ELEMENTS(aFormulaData); ++i)
m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
+ // Sort data in A2:A8 on the 1st sheet. No column header.
ScSortParam aSortData;
aSortData.nCol1 = 0;
aSortData.nCol2 = 0;
@@ -4642,8 +4642,7 @@ void Test::testSortWithFormulaRefs()
m_pDoc->Sort(0, aSortData, false, NULL);
- nEnd = SAL_N_ELEMENTS( aResults );
- for ( SCROW i = nStart; i < nEnd; ++i )
+ for (SCROW i = 0; i < SAL_N_ELEMENTS(aResults); ++i)
{
OUString sResult = m_pDoc->GetString(0, i + 1, 0);
CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult );
commit ddd6ce8036dc16a24d1074cbad211578f10949e8
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Mon Apr 21 16:38:42 2014 -0400
We have m_pDoc available here in the test...
Change-Id: Ib541cb9f99b3f1094e33d7b333fdbeeeccadce26
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 7563012..824f84a 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4595,9 +4595,8 @@ void Test::testFindAreaPosColRight()
// if cells in the sort are referenced by formulas
void Test::testSortWithFormulaRefs()
{
- ScDocument* pDoc = getDocShell().GetDocument();
- pDoc->InsertTab(0, "List1");
- pDoc->InsertTab(1, "List2");
+ m_pDoc->InsertTab(0, "List1");
+ m_pDoc->InsertTab(1, "List2");
const char* aFormulaData[6] = {
"=IF($List1.A2<>\"\",$List1.A2,\"\")",
@@ -4615,7 +4614,7 @@ void Test::testSortWithFormulaRefs()
"larry",
};
- const char* aResults[ 6 ] = {
+ const char* aResults[6] = {
"bob",
"brian",
"larry",
@@ -4626,12 +4625,12 @@ void Test::testSortWithFormulaRefs()
// insert data to sort
SCROW nStart = 1, nEnd = 4;
for ( SCROW i = nStart; i <= nEnd; ++i )
- pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
+ m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
// insert forumulas
nStart = 0;
nEnd = SAL_N_ELEMENTS(aFormulaData);
for ( SCROW i = nStart; i < nEnd; ++i )
- pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
+ m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
ScSortParam aSortData;
aSortData.nCol1 = 0;
@@ -4641,16 +4640,16 @@ void Test::testSortWithFormulaRefs()
aSortData.maKeyState[0].bDoSort = true;
aSortData.maKeyState[0].nField = 0;
- pDoc->Sort(0, aSortData, false, NULL);
+ m_pDoc->Sort(0, aSortData, false, NULL);
nEnd = SAL_N_ELEMENTS( aResults );
for ( SCROW i = nStart; i < nEnd; ++i )
{
- OUString sResult = pDoc->GetString( 0, i + 1, 0);
- CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[ i ] ), sResult );
+ OUString sResult = m_pDoc->GetString(0, i + 1, 0);
+ CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult );
}
- pDoc->DeleteTab(0);
- pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(0);
}
void Test::testSortWithStrings()
commit d2cb4d75ff69890aa790eba64325164788c900c2
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Mon Apr 21 16:34:00 2014 -0400
Move cell notes too when sorting.
Change-Id: Ibb330aa6f515d756c5578296fd9c83aa235a59fd
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index d26214b..25f1be6 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -526,6 +526,7 @@ public:
// cell notes
ScPostIt* GetCellNote( SCROW nRow );
const ScPostIt* GetCellNote( SCROW nRow ) const;
+ const ScPostIt* GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const;
void DeleteCellNotes( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 );
bool HasCellNotes() const;
void SetCellNote( SCROW nRow, ScPostIt* pNote);
@@ -548,7 +549,8 @@ public:
void DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol,
sc::ColumnBlockPosition& maDestBlockPos, bool bCloneCaption = true, SCROW nRowOffsetDest=0 ) const;
- void UpdateNoteCaptions();
+
+ void UpdateNoteCaptions( SCROW nRow1, SCROW nRow2 );
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list