1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include "helper/sorthelper.hxx"
12 #include "helper/debughelper.hxx"
13 #include "helper/qahelper.hxx"
16 #include <sortparam.hxx>
18 #include <patattr.hxx>
19 #include <formulacell.hxx>
20 #include <scopetools.hxx>
21 #include <globalnames.hxx>
22 #include <dbdocfun.hxx>
23 #include <docfunc.hxx>
24 #include <scitems.hxx>
25 #include <editutil.hxx>
26 #include <drwlayer.hxx>
27 #include <formulaopt.hxx>
29 #include <sal/config.h>
30 #include <editeng/wghtitem.hxx>
31 #include <editeng/postitem.hxx>
32 #include <editeng/editobj.hxx>
33 #include <svx/svdocirc.hxx>
34 #include <svx/svdpage.hxx>
38 m_pDoc
->InsertTab(0, "test1");
40 // We need a drawing layer in order to create caption objects.
41 m_pDoc
->InitDrawLayer(&getDocShell());
44 ScAddress
aPos(0,0,0);
46 const char* aData
[][2] = {
53 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData
), 0));
54 aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
55 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
58 // Insert note in cell B2.
59 ScAddress
rAddr(1, 1, 0);
60 ScPostIt
* pNote
= m_pDoc
->GetOrCreateNote(rAddr
);
61 pNote
->SetText(rAddr
, "Hello");
62 pNote
->SetAuthor("Jim Bob");
64 ScSortParam aSortData
;
69 aSortData
.maKeyState
[0].bDoSort
= true;
70 aSortData
.maKeyState
[0].nField
= 1;
71 aSortData
.maKeyState
[0].bAscending
= true;
73 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
75 double nVal
= m_pDoc
->GetValue(1,0,0);
76 ASSERT_DOUBLES_EQUAL(nVal
, 1.0);
78 // check that note is also moved after sorting
79 pNote
= m_pDoc
->GetNote(1, 0, 0);
80 CPPUNIT_ASSERT(pNote
);
82 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
85 const char* aData
[][1] = {
96 aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
97 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
100 aSortData
.nCol1
= aDataRange
.aStart
.Col();
101 aSortData
.nCol2
= aDataRange
.aEnd
.Col();
102 aSortData
.nRow1
= aDataRange
.aStart
.Row();
103 aSortData
.nRow2
= aDataRange
.aEnd
.Row();
104 aSortData
.bHasHeader
= true;
105 aSortData
.maKeyState
[0].nField
= 0;
106 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
108 // Title should stay at the top, numbers should be sorted numerically,
109 // numbers always come before strings, and empty cells always occur at the
111 CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc
->GetString(aPos
));
113 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc
->GetString(aPos
));
115 CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc
->GetString(aPos
));
117 CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc
->GetString(aPos
));
119 CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc
->GetString(aPos
));
121 CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc
->GetString(aPos
));
123 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE
, m_pDoc
->GetCellType(aPos
));
125 m_pDoc
->DeleteTab(0);
128 void Test::testSortHorizontal()
130 SortRefUpdateSetter aUpdateSet
;
132 ScFormulaOptions aOptions
;
133 aOptions
.SetFormulaSepArg(";");
134 aOptions
.SetFormulaSepArrayCol(";");
135 aOptions
.SetFormulaSepArrayRow("|");
136 getDocShell().SetFormulaOptions(aOptions
);
138 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true);
139 m_pDoc
->InsertTab(0, "Sort");
141 // Test case from fdo#78079.
144 const char* aData
[][4] = {
145 { "table", "has UNIQUE", "Publish to EC2", "flag" },
146 { "w2gi.mobilehit", "Yes", "No", "=CONCATENATE(B2;\"-\";C2)" },
147 { "w2gi.visitors", "No", "No", "=CONCATENATE(B3;\"-\";C3)" },
148 { "w2gi.pagedimension", "Yes", "Yes", "=CONCATENATE(B4;\"-\";C4)" },
151 // Insert raw data into A1:D4.
152 ScRange aDataRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
, SAL_N_ELEMENTS(aData
));
153 CPPUNIT_ASSERT_EQUAL(OUString("A1:D4"), aDataRange
.Format(ScRefFlags::VALID
));
155 // Check the formula values.
156 CPPUNIT_ASSERT_EQUAL(OUString("Yes-No"), m_pDoc
->GetString(ScAddress(3,1,0)));
157 CPPUNIT_ASSERT_EQUAL(OUString("No-No"), m_pDoc
->GetString(ScAddress(3,2,0)));
158 CPPUNIT_ASSERT_EQUAL(OUString("Yes-Yes"), m_pDoc
->GetString(ScAddress(3,3,0)));
160 // Define A1:D4 as sheet-local anonymous database range.
161 m_pDoc
->SetAnonymousDBData(
162 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 3, 3)));
164 // Sort A1:D4 horizontally, ascending by row 1.
165 ScDBDocFunc
aFunc(getDocShell());
167 ScSortParam aSortData
;
172 aSortData
.bHasHeader
= true;
173 aSortData
.bByRow
= false; // Sort by column (in horizontal direction).
174 aSortData
.bIncludePattern
= true;
175 aSortData
.maKeyState
[0].bDoSort
= true;
176 aSortData
.maKeyState
[0].nField
= 0;
177 aSortData
.maKeyState
[0].bAscending
= true;
178 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
179 CPPUNIT_ASSERT(bSorted
);
182 // Expected output table content. 0 = empty cell
183 std::vector
<std::vector
<const char*>> aOutputCheck
= {
184 { "table", "flag", "has UNIQUE", "Publish to EC2" },
185 { "w2gi.mobilehit", "Yes-No", "Yes", "No" },
186 { "w2gi.visitors", "No-No", "No", "No" },
187 { "w2gi.pagedimension", "Yes-Yes", "Yes", "Yes" },
190 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted by column with formula");
191 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
194 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "CONCATENATE(C2;\"-\";D2)", "Wrong formula!");
195 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "CONCATENATE(C3;\"-\";D3)", "Wrong formula!");
196 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,3,0), "CONCATENATE(C4;\"-\";D4)", "Wrong formula!");
198 m_pDoc
->DeleteTab(0);
201 void Test::testSortHorizontalWholeColumn()
203 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true);
204 m_pDoc
->InsertTab(0, "Sort");
207 const char* aData
[][5] = {
208 { "4", "2", "47", "a", "9" }
211 // Insert row data to C1:G1.
212 ScRange aSortRange
= insertRangeData(m_pDoc
, ScAddress(2,0,0), aData
, SAL_N_ELEMENTS(aData
));
213 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
214 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
215 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
216 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(5,0,0)));
217 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
219 // Extend the sort range to whole column.
220 aSortRange
.aEnd
.SetRow(MAXROW
);
222 SCCOL nCol1
= aSortRange
.aStart
.Col();
223 SCCOL nCol2
= aSortRange
.aEnd
.Col();
224 SCROW nRow1
= aSortRange
.aStart
.Row();
225 SCROW nRow2
= aSortRange
.aEnd
.Row();
227 // Define C:G as sheet-local anonymous database range.
228 m_pDoc
->SetAnonymousDBData(
229 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, nCol1
, nRow1
, nCol2
, nRow2
, false, false)));
231 // Sort C:G horizontally ascending by row 1.
232 ScDBDocFunc
aFunc(getDocShell());
234 ScSortParam aSortData
;
235 aSortData
.nCol1
= nCol1
;
236 aSortData
.nCol2
= nCol2
;
237 aSortData
.nRow1
= nRow1
;
238 aSortData
.nRow2
= nRow2
;
239 aSortData
.bHasHeader
= false;
240 aSortData
.bByRow
= false; // Sort by column (in horizontal direction).
241 aSortData
.bIncludePattern
= true;
242 aSortData
.maKeyState
[0].bDoSort
= true;
243 aSortData
.maKeyState
[0].nField
= 0;
244 aSortData
.maKeyState
[0].bAscending
= true;
245 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
246 CPPUNIT_ASSERT(bSorted
);
248 // Check the sort result.
249 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
250 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
251 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
252 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
253 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(6,0,0)));
257 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
258 CPPUNIT_ASSERT(pUndoMgr
);
261 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
262 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
263 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
264 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(5,0,0)));
265 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
269 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
270 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
271 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
272 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
273 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(6,0,0)));
275 m_pDoc
->DeleteTab(0);
278 void Test::testSortSingleRow()
280 // This test case is from fdo#80462.
282 m_pDoc
->InsertTab(0, "Test");
284 // Sort range consists of only one row.
285 m_pDoc
->SetString(ScAddress(0,0,0), "X");
286 m_pDoc
->SetString(ScAddress(1,0,0), "Y");
288 // Define A1:B1 as sheet-local anonymous database range.
289 m_pDoc
->SetAnonymousDBData(
290 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 0)));
292 // Sort A1:B1 horizontally, ascending by row 1.
293 ScDBDocFunc
aFunc(getDocShell());
295 ScSortParam aSortData
;
300 aSortData
.bHasHeader
= true;
301 aSortData
.bByRow
= true;
302 aSortData
.bIncludePattern
= true;
303 aSortData
.maKeyState
[0].bDoSort
= true;
304 aSortData
.maKeyState
[0].nField
= 0;
305 aSortData
.maKeyState
[0].bAscending
= true;
307 // Do the sorting. This should not crash.
308 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
309 CPPUNIT_ASSERT(bSorted
);
311 // Another test case - single row horizontal sort with header column.
312 clearSheet(m_pDoc
, 0);
315 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
316 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
317 m_pDoc
->SetValue(ScAddress(2,0,0), 10.0);
318 m_pDoc
->SetValue(ScAddress(3,0,0), 3.0);
319 m_pDoc
->SetValue(ScAddress(4,0,0), 9.0);
320 m_pDoc
->SetValue(ScAddress(5,0,0), 12.0);
321 m_pDoc
->SetValue(ScAddress(6,0,0), 2.0);
323 // Define A1:G1 as sheet-local anonymous database range.
324 m_pDoc
->SetAnonymousDBData(
325 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 6, 0, false, true)));
327 // Update the sort data.
330 aSortData
.bByRow
= false;
331 bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
332 CPPUNIT_ASSERT(bSorted
);
335 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
336 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
337 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
338 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
339 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
340 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
341 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
344 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
345 CPPUNIT_ASSERT(pUndoMgr
);
348 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
349 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
350 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
351 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
352 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
353 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
354 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
358 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
359 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
360 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
361 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
362 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
363 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
364 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
366 m_pDoc
->DeleteTab(0);
369 // regression test of fdo#53814, sorting doesn't work as expected
370 // if cells in the sort are referenced by formulas
371 void Test::testSortWithFormulaRefs()
373 SortRefUpdateSetter aUpdateSet
;
375 m_pDoc
->InsertTab(0, "List1");
376 m_pDoc
->InsertTab(1, "List2");
378 const char* aFormulaData
[6] = {
379 "=IF($List1.A2<>\"\";$List1.A2;\"\")",
380 "=IF($List1.A3<>\"\";$List1.A3;\"\")",
381 "=IF($List1.A4<>\"\";$List1.A4;\"\")",
382 "=IF($List1.A5<>\"\";$List1.A5;\"\")",
383 "=IF($List1.A6<>\"\";$List1.A6;\"\")",
384 "=IF($List1.A7<>\"\";$List1.A7;\"\")",
387 const char* const aTextData
[4] = {
394 const char* aResults
[6] = {
403 // Insert data to sort in A2:A5 on the 1st sheet.
404 for (SCROW i
= 1; i
<= 4; ++i
)
405 m_pDoc
->SetString( 0, i
, 0, OUString::createFromAscii(aTextData
[i
-1]) );
407 // Insert formulas in A1:A6 on the 2nd sheet.
408 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aFormulaData
); ++i
)
409 m_pDoc
->SetString( 0, i
, 1, OUString::createFromAscii(aFormulaData
[i
]) );
411 // Sort data in A2:A8 on the 1st sheet. No column header.
412 ScSortParam aSortData
;
417 aSortData
.maKeyState
[0].bDoSort
= true;
418 aSortData
.maKeyState
[0].nField
= 0;
420 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
422 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aResults
); ++i
)
424 OUString sResult
= m_pDoc
->GetString(0, i
+ 1, 0);
425 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults
[i
] ), sResult
);
427 m_pDoc
->DeleteTab(1);
428 m_pDoc
->DeleteTab(0);
431 void Test::testSortWithStrings()
433 m_pDoc
->InsertTab(0, "Test");
435 ScFieldEditEngine
& rEE
= m_pDoc
->GetEditEngine();
437 m_pDoc
->SetString(ScAddress(1,1,0), "Header");
438 m_pDoc
->SetString(ScAddress(1,2,0), "Val2");
439 m_pDoc
->SetEditText(ScAddress(1,3,0), rEE
.CreateTextObject());
441 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
442 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,2,0)));
443 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,3,0)));
450 aParam
.bHasHeader
= true;
451 aParam
.maKeyState
[0].bDoSort
= true;
452 aParam
.maKeyState
[0].bAscending
= true;
453 aParam
.maKeyState
[0].nField
= 1;
455 m_pDoc
->Sort(0, aParam
, false, true, nullptr, nullptr);
457 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
458 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,2,0)));
459 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,3,0)));
461 aParam
.maKeyState
[0].bAscending
= false;
463 m_pDoc
->Sort(0, aParam
, false, true, nullptr, nullptr);
465 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
466 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,2,0)));
467 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,3,0)));
469 m_pDoc
->DeleteTab(0);
472 void Test::testSortInFormulaGroup()
474 SortRefUpdateSetter aUpdateSet
;
476 static const struct {
481 { 0, 0, "3" }, { 1, 0, "=A1" },
482 { 0, 1, "1" }, { 1, 1, "=A2" },
483 { 0, 2, "20" }, { 1, 2, "=A3" },
484 { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups
485 { 0, 4, "2" }, { 1, 4, "=A5+1" },
486 { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguous group
487 { 0, 6, "100" }, { 1, 6, "=A7" },
488 { 0, 7, "102" }, { 1, 7, "=A8" },
489 { 0, 8, "104" }, { 1, 8, "=A9" },
490 { 0, 9, "103" }, { 1, 9, "=A10" },
493 m_pDoc
->InsertTab(0, "sorttest");
495 for ( SCROW i
= 0; i
< SCROW(SAL_N_ELEMENTS( aEntries
)); ++i
)
496 m_pDoc
->SetString( aEntries
[i
].nCol
, aEntries
[i
].nRow
, 0,
497 OUString::createFromAscii( aEntries
[i
].pData
) );
499 ScSortParam aSortData
;
504 aSortData
.maKeyState
[0].bDoSort
= true;
505 aSortData
.maKeyState
[0].nField
= 0;
506 aSortData
.maKeyState
[0].bAscending
= true;
508 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
510 static const struct {
515 { 0, 0, 1.0 }, { 1, 0, 1.0 },
516 { 0, 1, 2.0 }, { 1, 1, 3.0 },
517 { 0, 2, 3.0 }, { 1, 2, 3.0 },
518 { 0, 3, 10.0 }, { 1, 3, 11.0 },
519 { 0, 4, 20.0 }, { 1, 4, 20.0 },
520 { 0, 5, 100.0 }, { 1, 5, 100.0 },
521 { 0, 6, 101.0 }, { 1, 6, 101.0 },
522 { 0, 7, 102.0 }, { 1, 7, 102.0 },
523 { 0, 8, 103.0 }, { 1, 8, 103.0 },
524 { 0, 9, 104.0 }, { 1, 9, 104.0 },
527 for ( SCROW i
= 0; i
< SCROW(SAL_N_ELEMENTS( aEntries
)); ++i
)
529 double val
= m_pDoc
->GetValue( aEntries
[i
].nCol
, aEntries
[i
].nRow
, 0 );
530 // fprintf(stderr, "value at %d %d is %g = %g\n",
531 // (int)aResults[i].nRow, (int)aResults[i].nCol,
532 // val, aResults[i].fValue);
533 CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.",
534 rtl::math::approxEqual(val
, aResults
[i
].fValue
));
537 m_pDoc
->DeleteTab( 0 );
540 void Test::testSortWithCellFormats()
544 bool isBold( const ScPatternAttr
* pPat
) const
548 cerr
<< "Pattern is NULL!" << endl
;
552 const SfxPoolItem
* pItem
= nullptr;
553 if (!pPat
->GetItemSet().HasItem(ATTR_FONT_WEIGHT
, &pItem
))
555 cerr
<< "Pattern does not have a font weight item, but it should." << endl
;
559 CPPUNIT_ASSERT(pItem
);
561 if (static_cast<const SvxWeightItem
*>(pItem
)->GetEnumValue() != WEIGHT_BOLD
)
563 cerr
<< "Font weight should be bold." << endl
;
570 bool isItalic( const ScPatternAttr
* pPat
) const
574 cerr
<< "Pattern is NULL!" << endl
;
578 const SfxPoolItem
* pItem
= nullptr;
579 if (!pPat
->GetItemSet().HasItem(ATTR_FONT_POSTURE
, &pItem
))
581 cerr
<< "Pattern does not have a font posture item, but it should." << endl
;
585 CPPUNIT_ASSERT(pItem
);
587 if (static_cast<const SvxPostureItem
*>(pItem
)->GetEnumValue() != ITALIC_NORMAL
)
589 cerr
<< "Italic should be applied.." << endl
;
596 bool isNormal( const ScPatternAttr
* pPat
) const
600 cerr
<< "Pattern is NULL!" << endl
;
604 const SfxPoolItem
* pItem
= nullptr;
605 if (pPat
->GetItemSet().HasItem(ATTR_FONT_WEIGHT
, &pItem
))
607 // Check if the font weight is applied.
608 if (static_cast<const SvxWeightItem
*>(pItem
)->GetEnumValue() == WEIGHT_BOLD
)
610 cerr
<< "This cell is bold, but shouldn't." << endl
;
615 if (pPat
->GetItemSet().HasItem(ATTR_FONT_POSTURE
, &pItem
))
617 // Check if the italics is applied.
618 if (static_cast<const SvxPostureItem
*>(pItem
)->GetEnumValue() == ITALIC_NORMAL
)
620 cerr
<< "This cell is italic, but shouldn't." << endl
;
630 m_pDoc
->InsertTab(0, "Test");
632 // Insert some values into A1:A4.
633 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
634 m_pDoc
->SetString(ScAddress(0,1,0), "Normal");
635 m_pDoc
->SetString(ScAddress(0,2,0), "Bold");
636 m_pDoc
->SetString(ScAddress(0,3,0), "Italic");
638 // Set A3 bold and A4 italic.
639 const ScPatternAttr
* pPat
= m_pDoc
->GetPattern(ScAddress(0,2,0));
640 CPPUNIT_ASSERT(pPat
);
642 ScPatternAttr
aNewPat(*pPat
);
643 SfxItemSet
& rSet
= aNewPat
.GetItemSet();
644 rSet
.Put(SvxWeightItem(WEIGHT_BOLD
, ATTR_FONT_WEIGHT
));
645 m_pDoc
->ApplyPattern(0, 2, 0, aNewPat
);
647 // Make sure it's really in.
648 bool bGood
= aCheck
.isBold(m_pDoc
->GetPattern(ScAddress(0,2,0)));
649 CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood
);
652 pPat
= m_pDoc
->GetPattern(ScAddress(0,3,0));
653 CPPUNIT_ASSERT(pPat
);
655 ScPatternAttr
aNewPat(*pPat
);
656 SfxItemSet
& rSet
= aNewPat
.GetItemSet();
657 rSet
.Put(SvxPostureItem(ITALIC_NORMAL
, ATTR_FONT_POSTURE
));
658 m_pDoc
->ApplyPattern(0, 3, 0, aNewPat
);
660 bool bGood
= aCheck
.isItalic(m_pDoc
->GetPattern(ScAddress(0,3,0)));
661 CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood
);
664 // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run.
665 m_pDoc
->SetAnonymousDBData(
666 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 3)));
668 // Sort A1:A4 ascending with cell formats.
669 ScDBDocFunc
aFunc(getDocShell());
671 ScSortParam aSortData
;
676 aSortData
.bHasHeader
= true;
677 aSortData
.bIncludePattern
= true;
678 aSortData
.maKeyState
[0].bDoSort
= true;
679 aSortData
.maKeyState
[0].nField
= 0;
680 aSortData
.maKeyState
[0].bAscending
= true;
681 bool bSorted
= aFunc
.Sort(0, aSortData
, true, false, true);
682 CPPUNIT_ASSERT(bSorted
);
684 // Check the sort result.
685 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
686 CPPUNIT_ASSERT_EQUAL(OUString("Bold"), m_pDoc
->GetString(ScAddress(0,1,0)));
687 CPPUNIT_ASSERT_EQUAL(OUString("Italic"), m_pDoc
->GetString(ScAddress(0,2,0)));
688 CPPUNIT_ASSERT_EQUAL(OUString("Normal"), m_pDoc
->GetString(ScAddress(0,3,0)));
690 // A2 should be bold now.
691 bool bBold
= aCheck
.isBold(m_pDoc
->GetPattern(ScAddress(0,1,0)));
692 CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold
);
694 // and A3 should be italic.
695 bool bItalic
= aCheck
.isItalic(m_pDoc
->GetPattern(ScAddress(0,2,0)));
696 CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic
);
698 // A4 should have neither bold nor italic.
699 bool bNormal
= aCheck
.isNormal(m_pDoc
->GetPattern(ScAddress(0,3,0)));
700 CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal
);
702 m_pDoc
->DeleteTab(0);
705 void Test::testSortRefUpdate()
707 SortTypeSetter
aSortTypeSet(true);
709 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
710 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
712 m_pDoc
->InsertTab(0, "Sort");
714 // Set values to sort in column A.
715 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
717 double aValues
[] = { 4.0, 36.0, 14.0, 29.0, 98.0, 78.0, 0.0, 99.0, 1.0 };
718 size_t nCount
= SAL_N_ELEMENTS(aValues
);
719 for (size_t i
= 0; i
< nCount
; ++i
)
720 m_pDoc
->SetValue(ScAddress(0,i
+1,0), aValues
[i
]);
722 // Set formulas to reference these values in column C.
723 m_pDoc
->SetString(ScAddress(2,0,0), "Formula");
724 for (size_t i
= 0; i
< nCount
; ++i
)
725 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
727 // Check the values in column C.
728 for (size_t i
= 0; i
< nCount
; ++i
)
730 double fCheck
= aValues
[i
];
731 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
734 ScDBDocFunc
aFunc(getDocShell());
736 // Define A1:A10 as sheet-local anonymous database range, else sort wouldn't run.
737 m_pDoc
->SetAnonymousDBData(
738 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 9)));
740 // Sort A1:A10 (with a header row).
741 ScSortParam aSortData
;
746 aSortData
.bHasHeader
= true;
747 aSortData
.maKeyState
[0].bDoSort
= true;
748 aSortData
.maKeyState
[0].nField
= 0;
749 aSortData
.maKeyState
[0].bAscending
= true;
750 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
751 CPPUNIT_ASSERT(bSorted
);
753 double aSorted
[] = { 0.0, 1.0, 4.0, 14.0, 29.0, 36.0, 78.0, 98.0, 99.0 };
755 // Check the sort result.
756 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
757 for (size_t i
= 0; i
< nCount
; ++i
)
759 double fCheck
= aSorted
[i
];
760 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
763 // Sorting should not alter the values in column C.
764 m_pDoc
->CalcAll(); // just in case...
765 for (size_t i
= 0; i
< nCount
; ++i
)
767 double fCheck
= aValues
[i
];
768 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
771 // C2 should now point to A4.
772 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "R[2]C[-2]", "Wrong formula in C2!");
775 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
778 // Check the undo result.
779 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
780 for (size_t i
= 0; i
< nCount
; ++i
)
782 double fCheck
= aValues
[i
];
783 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
786 // Values in column C should still be unaltered.
787 m_pDoc
->CalcAll(); // just in case...
788 for (size_t i
= 0; i
< nCount
; ++i
)
790 double fCheck
= aValues
[i
];
791 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
794 // C2 should now point to A2.
795 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "RC[-2]", "Wrong formula in C2!");
800 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
801 for (size_t i
= 0; i
< nCount
; ++i
)
803 double fCheck
= aSorted
[i
];
804 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
807 // Sorting should not alter the values in column C.
808 m_pDoc
->CalcAll(); // just in case...
809 for (size_t i
= 0; i
< nCount
; ++i
)
811 double fCheck
= aValues
[i
];
812 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
815 // C2 should now point to A4.
816 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "R[2]C[-2]", "Wrong formula in C2!");
821 // Formulas in column C should all be "RC[-2]" again.
822 for (size_t i
= 0; i
< nCount
; ++i
)
823 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
825 // Turn off reference update on sort.
826 SortTypeSetter::changeTo(false);
828 bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
829 CPPUNIT_ASSERT(bSorted
);
831 // Check the sort result again.
832 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
833 for (size_t i
= 0; i
< nCount
; ++i
)
835 double fCheck
= aSorted
[i
];
836 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
839 // Formulas in column C should all remain "RC[-2]".
840 for (size_t i
= 0; i
< nCount
; ++i
)
841 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
843 // The values in column C should now be the same as sorted values in column A.
844 m_pDoc
->CalcAll(); // just in case...
845 for (size_t i
= 0; i
< nCount
; ++i
)
847 double fCheck
= aSorted
[i
];
848 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0))); // column C
851 m_pDoc
->DeleteTab(0);
854 void Test::testSortRefUpdate2()
856 SortRefUpdateSetter aUpdateSet
;
858 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
859 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
861 m_pDoc
->InsertTab(0, "Sort");
864 const char* aData
[][2] = {
870 { nullptr, nullptr } // terminator
873 for (SCROW i
= 0; aData
[i
][0]; ++i
)
875 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
876 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
879 // Check the values in B2:B5.
880 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
881 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
882 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
883 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
885 ScDBDocFunc
aFunc(getDocShell());
887 // Define A1:B5 as sheet-local anonymous database range, else sort wouldn't run.
888 m_pDoc
->SetAnonymousDBData(
889 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 4)));
891 // Sort A1:B5 by column A (with a row header).
892 ScSortParam aSortData
;
897 aSortData
.bHasHeader
= true;
898 aSortData
.maKeyState
[0].bDoSort
= true;
899 aSortData
.maKeyState
[0].nField
= 0;
900 aSortData
.maKeyState
[0].bAscending
= true;
901 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
902 CPPUNIT_ASSERT(bSorted
);
904 // Check the sort result in column A.
905 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
906 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
907 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
908 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
911 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
912 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
913 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
914 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
916 // Formulas in column B should still point to their respective left neighbor cell.
917 for (SCROW i
= 1; i
<= 4; ++i
)
919 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,i
,0), "RC[-1]", "Wrong formula!");
922 // Undo and check the result in column B.
923 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
926 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
927 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
928 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
929 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
934 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
935 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
936 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
937 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
939 m_pDoc
->DeleteTab(0);
942 void Test::testSortRefUpdate3()
944 SortRefUpdateSetter aUpdateSet
;
946 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
947 m_pDoc
->InsertTab(0, "Sort");
949 const char* pData
[] = {
956 nullptr // terminator
959 for (SCROW i
= 0; pData
[i
]; ++i
)
960 m_pDoc
->SetString(ScAddress(0,i
,0), OUString::createFromAscii(pData
[i
]));
962 // Check the initial values.
963 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
964 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
965 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
966 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
967 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
968 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
970 ScDBDocFunc
aFunc(getDocShell());
973 m_pDoc
->SetAnonymousDBData(
974 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 5)));
976 // Sort A1:A6 by column A (with a row header).
977 ScSortParam aSortData
;
982 aSortData
.bHasHeader
= true;
983 aSortData
.maKeyState
[0].bDoSort
= true;
984 aSortData
.maKeyState
[0].nField
= 0;
985 aSortData
.maKeyState
[0].bAscending
= true;
986 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
987 CPPUNIT_ASSERT(bSorted
);
989 // Check the sorted values.
991 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
992 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
993 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
994 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
995 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
996 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
998 // Make sure the formula cells have been adjusted correctly.
999 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,3,0), "A2+A3", "Wrong formula in A4.");
1000 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "A2+10", "Wrong formula in A5.");
1001 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "A3+10", "Wrong formula in A6.");
1003 // Undo and check the result.
1004 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1007 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1008 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1009 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1010 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1011 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1012 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1014 // Redo and check the result.
1017 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1018 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1019 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1020 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1021 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1022 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1024 m_pDoc
->DeleteTab(0);
1027 // Derived from fdo#79441 https://bugs.freedesktop.org/attachment.cgi?id=100144
1028 // testRefInterne.ods
1029 void Test::testSortRefUpdate4()
1031 // This test has to work in both update reference modes.
1033 SortRefNoUpdateSetter aUpdateSet
;
1034 testSortRefUpdate4_Impl();
1037 SortRefUpdateSetter aUpdateSet
;
1038 testSortRefUpdate4_Impl();
1042 void Test::testSortRefUpdate4_Impl()
1044 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1045 m_pDoc
->InsertTab(0, "Sort");
1046 m_pDoc
->InsertTab(1, "Lesson1");
1047 m_pDoc
->InsertTab(2, "Lesson2");
1049 ScRange aLesson1Range
;
1051 const char* aData
[][2] = {
1053 { "Student1", "1" },
1054 { "Student2", "2" },
1055 { "Student3", "3" },
1056 { "Student4", "4" },
1057 { "Student5", "5" },
1061 ScAddress
aPos(0,0,nTab
);
1062 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, SAL_N_ELEMENTS(aData
), nTab
));
1063 aLesson1Range
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1064 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aLesson1Range
.aStart
);
1067 ScRange aLesson2Range
;
1069 const char* aData
[][2] = {
1071 { "=Lesson1.A2", "3" },
1072 { "=Lesson1.A3", "4" },
1073 { "=Lesson1.A4", "9" },
1074 { "=Lesson1.A5", "6" },
1075 { "=Lesson1.A6", "3" },
1079 ScAddress
aPos(0,0,nTab
);
1080 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, SAL_N_ELEMENTS(aData
), nTab
));
1081 aLesson2Range
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1082 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aLesson2Range
.aStart
);
1087 const char* aData
[][4] = {
1088 { "Name", "Lesson1", "Lesson2", "Average" },
1089 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B2:C2)" },
1090 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B3:C3)" },
1091 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" },
1092 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B5:C5)" },
1093 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B6:C6)" },
1097 ScAddress
aPos(0,0,nTab
);
1098 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, SAL_N_ELEMENTS(aData
), nTab
));
1099 aSortRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1100 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aSortRange
.aStart
);
1103 ScDBDocFunc
aFunc(getDocShell());
1105 // Sort A1:D6 by column D (Average, with a row header).
1107 ScSortParam aSortData
;
1108 aSortData
.nCol1
= aSortRange
.aStart
.Col();
1109 aSortData
.nCol2
= aSortRange
.aEnd
.Col();
1110 aSortData
.nRow1
= aSortRange
.aStart
.Row();
1111 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1112 aSortData
.bHasHeader
= true;
1113 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1114 aSortData
.maKeyState
[0].nField
= 3; // Average
1115 aSortData
.maKeyState
[0].bAscending
= false; // descending
1117 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1118 aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nCol2
, aSortData
.nRow2
)));
1120 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1121 CPPUNIT_ASSERT(bSorted
);
1123 // Check the sorted values.
1125 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1126 CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc
->GetString(ScAddress(0,1,0)));
1127 CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc
->GetString(ScAddress(0,2,0)));
1128 CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc
->GetString(ScAddress(0,3,0)));
1129 CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc
->GetString(ScAddress(0,4,0)));
1130 CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc
->GetString(ScAddress(0,5,0)));
1131 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1132 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1133 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1134 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1135 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1137 // Make sure the formula cells have been adjusted correctly.
1138 const char* aCheck
[][4] = {
1139 // Name Lesson1 Lesson2 Average
1140 { "Lesson1.A4", "Lesson1.B4", "Lesson2.B4", "AVERAGE(B2:C2)" },
1141 { "Lesson1.A5", "Lesson1.B5", "Lesson2.B5", "AVERAGE(B3:C3)" },
1142 { "Lesson1.A6", "Lesson1.B6", "Lesson2.B6", "AVERAGE(B4:C4)" },
1143 { "Lesson1.A3", "Lesson1.B3", "Lesson2.B3", "AVERAGE(B5:C5)" },
1144 { "Lesson1.A2", "Lesson1.B2", "Lesson2.B2", "AVERAGE(B6:C6)" },
1146 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aCheck
)); ++nRow
)
1148 for (SCCOL nCol
=0; nCol
< 4; ++nCol
)
1150 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(nCol
,nRow
+1,0), aCheck
[nRow
][nCol
], OString("Wrong formula in " + OStringChar(char('A'+nCol
)) + OString::number(nRow
+2) + ".").getStr());
1154 // Undo and check the result.
1155 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1158 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1159 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1160 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1161 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1162 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1163 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1165 // Redo and check the result.
1168 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1169 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1170 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1171 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1172 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1173 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1176 // Sort A2:AMJ6 by column A (Name, without header).
1178 ScSortParam aSortData
;
1179 aSortData
.nCol1
= 0;
1180 aSortData
.nCol2
= MAXCOL
;
1181 aSortData
.nRow1
= aSortRange
.aStart
.Row()+1;
1182 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1183 aSortData
.bHasHeader
= false;
1184 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1185 aSortData
.maKeyState
[0].nField
= 0; // Name
1186 aSortData
.maKeyState
[0].bAscending
= false; // descending
1188 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1189 aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nCol2
, aSortData
.nRow2
)));
1191 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1192 CPPUNIT_ASSERT(bSorted
);
1194 // Check the sorted values.
1196 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1197 CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc
->GetString(ScAddress(0,1,0)));
1198 CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc
->GetString(ScAddress(0,2,0)));
1199 CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc
->GetString(ScAddress(0,3,0)));
1200 CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc
->GetString(ScAddress(0,4,0)));
1201 CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc
->GetString(ScAddress(0,5,0)));
1202 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1203 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1204 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1205 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1206 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1208 // Make sure the formula cells have been adjusted correctly.
1209 const char* aCheck
[][4] = {
1210 // Name Lesson1 Lesson2 Average
1211 { "Lesson1.A6", "Lesson1.B6", "Lesson2.B6", "AVERAGE(B2:C2)" },
1212 { "Lesson1.A5", "Lesson1.B5", "Lesson2.B5", "AVERAGE(B3:C3)" },
1213 { "Lesson1.A4", "Lesson1.B4", "Lesson2.B4", "AVERAGE(B4:C4)" },
1214 { "Lesson1.A3", "Lesson1.B3", "Lesson2.B3", "AVERAGE(B5:C5)" },
1215 { "Lesson1.A2", "Lesson1.B2", "Lesson2.B2", "AVERAGE(B6:C6)" },
1217 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aCheck
)); ++nRow
)
1219 for (SCCOL nCol
=0; nCol
< 4; ++nCol
)
1221 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(nCol
,nRow
+1,0), aCheck
[nRow
][nCol
], OString("Wrong formula in " + OStringChar(char('A'+nCol
)) + OString::number(nRow
+2) + ".").getStr());
1226 m_pDoc
->DeleteTab(2);
1227 m_pDoc
->DeleteTab(1);
1228 m_pDoc
->DeleteTab(0);
1231 // Make sure the refupdate works also with volatile cells, see fdo#83067
1232 /* FIXME: this test is not roll-over-midnight safe and will fail then! We may
1233 * want to have something different, but due to the nature of volatile
1234 * functions it's not that easy to come up with something reproducible staying
1235 * stable over sorts... ;-) Check for time and don't run test a few seconds
1236 * before midnight, ermm... */
1237 void Test::testSortRefUpdate5()
1239 SortRefUpdateSetter aUpdateSet
;
1241 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1242 m_pDoc
->InsertTab(0, "Sort");
1244 double aValCheck
[][3] = {
1245 // Result, Unsorted order, Sorted result.
1253 const char* aData
[][3] = {
1254 { "Date", "Volatile", "Order" },
1255 { "1999-05-05", "=TODAY()-$A2", "4" },
1256 { "1994-10-18", "=TODAY()-$A3", "1" },
1257 { "1996-06-30", "=TODAY()-$A4", "3" },
1258 { "1995-11-21", "=TODAY()-$A5", "2" },
1262 ScAddress
aPos(0,0,nTab
);
1263 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 2, SAL_N_ELEMENTS(aData
), nTab
));
1264 aSortRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1265 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aSortRange
.aStart
);
1267 // Actual results and expected sorted results.
1268 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1270 double fVal
= m_pDoc
->GetValue(ScAddress(1,nRow
+1,0));
1271 aValCheck
[nRow
][0] = fVal
;
1272 aValCheck
[static_cast<size_t>(aValCheck
[nRow
][1])-1][2] = fVal
;
1276 ScDBDocFunc
aFunc(getDocShell());
1279 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1280 aSortRange
.aStart
.Col(), aSortRange
.aStart
.Row(), aSortRange
.aEnd
.Col(), aSortRange
.aEnd
.Row())));
1282 // Sort by column A.
1283 ScSortParam aSortData
;
1284 aSortData
.nCol1
= aSortRange
.aStart
.Col();
1285 aSortData
.nCol2
= aSortRange
.aEnd
.Col();
1286 aSortData
.nRow1
= aSortRange
.aStart
.Row();
1287 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1288 aSortData
.bHasHeader
= true;
1289 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1290 aSortData
.maKeyState
[0].nField
= 0; // Date
1291 aSortData
.maKeyState
[0].bAscending
= true; // ascending
1292 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1293 CPPUNIT_ASSERT(bSorted
);
1295 // Check the sorted values.
1297 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1299 size_t i
= static_cast<size_t>(m_pDoc
->GetValue(ScAddress(2,nRow
+1,0))); // order 1..4
1300 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow
+1), i
);
1301 CPPUNIT_ASSERT_EQUAL( aValCheck
[i
-1][2], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1304 // Make sure the formula cells have been adjusted correctly.
1305 const char* aFormulaCheck
[] = {
1312 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aFormulaCheck
)); ++nRow
)
1314 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,nRow
+1,0), aFormulaCheck
[nRow
], OString("Wrong formula in B" + OString::number(nRow
+2) + ".").getStr());
1317 // Undo and check the result.
1318 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1321 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1323 CPPUNIT_ASSERT_EQUAL( aValCheck
[nRow
][0], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1324 CPPUNIT_ASSERT_EQUAL( aValCheck
[nRow
][1], m_pDoc
->GetValue(ScAddress(2,nRow
+1,0)));
1327 // Redo and check the result.
1330 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1332 size_t i
= static_cast<size_t>(m_pDoc
->GetValue(ScAddress(2,nRow
+1,0))); // order 1..4
1333 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow
+1), i
);
1334 CPPUNIT_ASSERT_EQUAL( aValCheck
[i
-1][2], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1337 m_pDoc
->DeleteTab(0);
1340 void Test::testSortRefUpdate6()
1342 SortRefNoUpdateSetter aUpdateSet
;
1344 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1345 m_pDoc
->InsertTab(0, "Sort");
1347 const char* aData
[][3] = {
1348 { "Order", "Value", "1" },
1349 { "9", "1", "=C1+B2" },
1350 { "1", "2", "=C2+B3" },
1351 { "8", "3", "=C3+B4" },
1354 ScAddress
aPos(0,0,0);
1355 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1356 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1359 // Expected output table content. 0 = empty cell
1360 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1361 { "Order", "Value", "1" },
1367 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1368 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1371 ScDBDocFunc
aFunc(getDocShell());
1374 m_pDoc
->SetAnonymousDBData(
1375 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 2, 3)));
1377 // Sort A1:A6 by column A (with a row header).
1378 ScSortParam aSortData
;
1379 aSortData
.nCol1
= 0;
1380 aSortData
.nCol2
= 2;
1381 aSortData
.nRow1
= 0;
1382 aSortData
.nRow2
= 3;
1383 aSortData
.bHasHeader
= true;
1384 aSortData
.maKeyState
[0].bDoSort
= true;
1385 aSortData
.maKeyState
[0].nField
= 0;
1386 aSortData
.maKeyState
[0].bAscending
= true;
1387 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1388 CPPUNIT_ASSERT(bSorted
);
1391 // Expected output table content. 0 = empty cell
1392 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1393 { "Order", "Value", "1" },
1399 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1400 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1403 // Make sure that the formulas in C2:C4 are not adjusted.
1404 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "C1+B2", "Wrong formula!");
1405 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,2,0), "C2+B3", "Wrong formula!");
1406 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,3,0), "C3+B4", "Wrong formula!");
1409 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1410 CPPUNIT_ASSERT(pUndoMgr
);
1415 // Expected output table content. 0 = empty cell
1416 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1417 { "Order", "Value", "1" },
1423 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After undo");
1424 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1430 // Expected output table content. 0 = empty cell
1431 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1432 { "Order", "Value", "1" },
1438 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After redo");
1439 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1442 // Change the value of C1 and make sure the formula broadcasting chain still works.
1443 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
1444 rFunc
.SetValueCell(ScAddress(2,0,0), 11.0, false);
1446 // Expected output table content. 0 = empty cell
1447 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1448 { "Order", "Value", "11" },
1454 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Change the header value");
1455 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1461 // Expected output table content. 0 = empty cell
1462 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1463 { "Order", "Value", "1" },
1469 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After undo of header value change");
1470 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1473 m_pDoc
->DeleteTab(0);
1476 // fdo#86762 check that broadcasters are sorted correctly and empty cell is
1478 void Test::testSortBroadcaster()
1480 SortRefNoUpdateSetter aUpdateSet
;
1482 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1483 m_pDoc
->InsertTab(0, "Sort");
1486 const char* aData
[][7] = {
1487 { "1", nullptr, nullptr, "=B1", "=$B$1", "=SUM(A1:B1)", "=SUM($A$1:$B$1)" },
1488 { "2", "8", nullptr, "=B2", "=$B$2", "=SUM(A2:B2)", "=SUM($A$2:$B$2)" },
1491 ScAddress
aPos(0,0,0);
1492 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1493 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1496 // Expected output table content. 0 = empty cell
1497 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1498 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1499 { "2", "8", nullptr, "8", "8", "10", "10" },
1502 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1503 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1507 m_pDoc
->SetAnonymousDBData(
1508 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 1)));
1510 ScDBDocFunc
aFunc(getDocShell());
1512 // Sort A1:B2 by column A descending.
1513 ScSortParam aSortData
;
1514 aSortData
.nCol1
= 0;
1515 aSortData
.nCol2
= 1;
1516 aSortData
.nRow1
= 0;
1517 aSortData
.nRow2
= 1;
1518 aSortData
.bHasHeader
= false;
1519 aSortData
.bByRow
= true;
1520 aSortData
.maKeyState
[0].bDoSort
= true;
1521 aSortData
.maKeyState
[0].nField
= 0;
1522 aSortData
.maKeyState
[0].bAscending
= false;
1523 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1524 CPPUNIT_ASSERT(bSorted
);
1527 // Expected output table content. 0 = empty cell
1528 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1529 { "2", "8", nullptr, "8", "8", "10", "10" },
1530 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1533 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1534 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1537 // Make sure that the formulas in D1:G2 are not adjusted.
1538 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,0,0), "B1", "Wrong formula!");
1539 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,0), "B2", "Wrong formula!");
1540 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,0,0), "$B$1", "Wrong formula!");
1541 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,1,0), "$B$2", "Wrong formula!");
1542 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(5,0,0), "SUM(A1:B1)", "Wrong formula!");
1543 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(5,1,0), "SUM(A2:B2)", "Wrong formula!");
1544 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(6,0,0), "SUM($A$1:$B$1)", "Wrong formula!");
1545 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(6,1,0), "SUM($A$2:$B$2)", "Wrong formula!");
1547 // Enter new value and check that it is broadcasted. First in empty cell.
1548 m_pDoc
->SetString(1,1,0, "16");
1549 double nVal
= m_pDoc
->GetValue(3,1,0);
1550 ASSERT_DOUBLES_EQUAL( 16.0, nVal
);
1551 nVal
= m_pDoc
->GetValue(4,1,0);
1552 ASSERT_DOUBLES_EQUAL( 16.0, nVal
);
1553 nVal
= m_pDoc
->GetValue(5,1,0);
1554 ASSERT_DOUBLES_EQUAL( 17.0, nVal
);
1555 nVal
= m_pDoc
->GetValue(6,1,0);
1556 ASSERT_DOUBLES_EQUAL( 17.0, nVal
);
1558 // Enter new value and check that it is broadcasted. Now overwriting data.
1559 m_pDoc
->SetString(1,0,0, "32");
1560 nVal
= m_pDoc
->GetValue(3,0,0);
1561 ASSERT_DOUBLES_EQUAL( 32.0, nVal
);
1562 nVal
= m_pDoc
->GetValue(4,0,0);
1563 ASSERT_DOUBLES_EQUAL( 32.0, nVal
);
1564 nVal
= m_pDoc
->GetValue(5,0,0);
1565 ASSERT_DOUBLES_EQUAL( 34.0, nVal
);
1566 nVal
= m_pDoc
->GetValue(6,0,0);
1567 ASSERT_DOUBLES_EQUAL( 34.0, nVal
);
1570 // The same for sort by column. Start data at A5.
1573 const char* aData
[][2] = {
1576 { nullptr, nullptr },
1578 { "=$A$6", "=$B$6" },
1579 { "=SUM(A5:A6)", "=SUM(B5:B6)" },
1580 { "=SUM($A$5:$A$6)", "=SUM($B$5:$B$6)" },
1583 ScAddress
aPos(0,4,0);
1584 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1585 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1588 // Expected output table content. 0 = empty cell
1589 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1592 { nullptr, nullptr },
1599 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1600 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1604 m_pDoc
->SetAnonymousDBData(
1605 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 4, 1, 5)));
1607 ScDBDocFunc
aFunc(getDocShell());
1609 // Sort A5:B6 by row 5 descending.
1610 ScSortParam aSortData
;
1611 aSortData
.nCol1
= 0;
1612 aSortData
.nCol2
= 1;
1613 aSortData
.nRow1
= 4;
1614 aSortData
.nRow2
= 5;
1615 aSortData
.bHasHeader
= false;
1616 aSortData
.bByRow
= false;
1617 aSortData
.maKeyState
[0].bDoSort
= true;
1618 aSortData
.maKeyState
[0].nField
= 0;
1619 aSortData
.maKeyState
[0].bAscending
= false;
1620 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1621 CPPUNIT_ASSERT(bSorted
);
1624 // Expected output table content. 0 = empty cell
1625 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1628 { nullptr, nullptr },
1635 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1636 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1639 // Make sure that the formulas in A8:B11 are not adjusted.
1640 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "A6", "Wrong formula!");
1641 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,7,0), "B6", "Wrong formula!");
1642 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,8,0), "$A$6", "Wrong formula!");
1643 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,8,0), "$B$6", "Wrong formula!");
1644 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,9,0), "SUM(A5:A6)", "Wrong formula!");
1645 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,9,0), "SUM(B5:B6)", "Wrong formula!");
1646 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,10,0), "SUM($A$5:$A$6)", "Wrong formula!");
1647 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,10,0), "SUM($B$5:$B$6)", "Wrong formula!");
1649 // Enter new value and check that it is broadcasted. First in empty cell.
1650 m_pDoc
->SetString(1,5,0, "16");
1651 double nVal
= m_pDoc
->GetValue(1,7,0);
1652 ASSERT_DOUBLES_EQUAL(nVal
, 16.0);
1653 nVal
= m_pDoc
->GetValue(1,8,0);
1654 ASSERT_DOUBLES_EQUAL(nVal
, 16.0);
1655 nVal
= m_pDoc
->GetValue(1,9,0);
1656 ASSERT_DOUBLES_EQUAL(nVal
, 17.0);
1657 nVal
= m_pDoc
->GetValue(1,10,0);
1658 ASSERT_DOUBLES_EQUAL(nVal
, 17.0);
1660 // Enter new value and check that it is broadcasted. Now overwriting data.
1661 m_pDoc
->SetString(0,5,0, "32");
1662 nVal
= m_pDoc
->GetValue(0,7,0);
1663 ASSERT_DOUBLES_EQUAL(nVal
, 32.0);
1664 nVal
= m_pDoc
->GetValue(0,8,0);
1665 ASSERT_DOUBLES_EQUAL(nVal
, 32.0);
1666 nVal
= m_pDoc
->GetValue(0,9,0);
1667 ASSERT_DOUBLES_EQUAL(nVal
, 34.0);
1668 nVal
= m_pDoc
->GetValue(0,10,0);
1669 ASSERT_DOUBLES_EQUAL(nVal
, 34.0);
1672 m_pDoc
->DeleteTab(0);
1675 // tdf#99417 check that formulas are tracked that *only* indirectly depend on
1676 // sorted data and no other broadcasting than BroadcastBroadcasters is
1677 // involved (for which this test can not be included in testSortBroadcaster()).
1678 void Test::testSortBroadcastBroadcaster()
1680 SortRefNoUpdateSetter aUpdateSet
;
1682 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1683 m_pDoc
->InsertTab(0, "Sort");
1686 const char* aData
[][3] = {
1687 { "1", "=A1", "=B1" },
1688 { "2", "=A2", "=B2" },
1691 ScAddress
aPos(0,0,0);
1692 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1693 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1696 // Expected output table content. 0 = empty cell
1697 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1702 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1703 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1707 m_pDoc
->SetAnonymousDBData(
1708 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 1)));
1710 ScDBDocFunc
aFunc(getDocShell());
1712 // Sort A1:A2 by column A descending.
1713 ScSortParam aSortData
;
1714 aSortData
.nCol1
= 0;
1715 aSortData
.nCol2
= 0;
1716 aSortData
.nRow1
= 0;
1717 aSortData
.nRow2
= 1;
1718 aSortData
.bHasHeader
= false;
1719 aSortData
.bByRow
= true;
1720 aSortData
.maKeyState
[0].bDoSort
= true;
1721 aSortData
.maKeyState
[0].nField
= 0;
1722 aSortData
.maKeyState
[0].bAscending
= false;
1723 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1724 CPPUNIT_ASSERT(bSorted
);
1727 // Expected output table content. 0 = empty cell
1728 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1733 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1734 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1737 // Make sure that the formulas in B1:C2 are not adjusted.
1738 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,0,0), "A1", "Wrong formula!");
1739 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "A2", "Wrong formula!");
1740 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,0,0), "B1", "Wrong formula!");
1741 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "B2", "Wrong formula!");
1744 m_pDoc
->DeleteTab(0);
1747 void Test::testSortOutOfPlaceResult()
1749 m_pDoc
->InsertTab(0, "Sort");
1750 m_pDoc
->InsertTab(1, "Result");
1752 const char* pData
[] = {
1759 nullptr // terminator
1762 // source data in A1:A6.
1763 for (SCROW i
= 0; pData
[i
]; ++i
)
1764 m_pDoc
->SetString(ScAddress(0,i
,0), OUString::createFromAscii(pData
[i
]));
1766 // Check the initial values.
1767 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1768 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1769 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1770 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1771 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1772 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1774 ScDBDocFunc
aFunc(getDocShell());
1776 // Sort A1:A6, and set the result to C2:C7
1777 m_pDoc
->SetAnonymousDBData(
1778 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 5)));
1780 ScSortParam aSortData
;
1781 aSortData
.nCol1
= 0;
1782 aSortData
.nCol2
= 0;
1783 aSortData
.nRow1
= 0;
1784 aSortData
.nRow2
= 5;
1785 aSortData
.bHasHeader
= true;
1786 aSortData
.bInplace
= false;
1787 aSortData
.nDestTab
= 1;
1788 aSortData
.nDestCol
= 2;
1789 aSortData
.nDestRow
= 1;
1790 aSortData
.maKeyState
[0].bDoSort
= true;
1791 aSortData
.maKeyState
[0].nField
= 0;
1792 aSortData
.maKeyState
[0].bAscending
= true;
1793 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1794 CPPUNIT_ASSERT(bSorted
);
1796 // Source data still intact.
1797 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1798 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1799 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1800 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1801 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1802 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1804 // Sort result in C2:C7 on sheet "Result".
1805 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(2,1,1)));
1806 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(2,2,1)));
1807 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(2,3,1)));
1808 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,4,1)));
1809 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(2,5,1)));
1810 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(2,6,1)));
1812 m_pDoc
->DeleteTab(1);
1813 m_pDoc
->DeleteTab(0);
1816 void Test::testSortPartialFormulaGroup()
1818 SortRefUpdateSetter aUpdateSet
;
1820 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1821 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
1823 m_pDoc
->InsertTab(0, "Sort");
1825 // Set up the sheet.
1826 const char* aData
[][2] = {
1828 { "43", "=RC[-1]" },
1829 { "50", "=RC[-1]" },
1831 { "47", "=RC[-1]" },
1832 { "28", "=RC[-1]" },
1833 { nullptr, nullptr } // terminator
1837 for (SCROW i
= 0; aData
[i
][0]; ++i
)
1839 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
1840 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
1843 // Check the initial condition.
1844 for (SCROW i
= 1; i
<= 5; ++i
)
1845 // A2:A6 should equal B2:B6.
1846 CPPUNIT_ASSERT_EQUAL(m_pDoc
->GetValue(ScAddress(0,i
,0)), m_pDoc
->GetValue(ScAddress(1,i
,0)));
1848 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,1,0));
1849 CPPUNIT_ASSERT(pFC
);
1850 CPPUNIT_ASSERT_MESSAGE("This formula cell should be the first in a group.", pFC
->IsSharedTop());
1851 CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect formula group length.", static_cast<SCROW
>(5), pFC
->GetSharedLength());
1853 ScDBDocFunc
aFunc(getDocShell());
1855 // Sort only B2:B4. This caused crash at one point (c.f. fdo#81617).
1857 m_pDoc
->SetAnonymousDBData(0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 1, 1, 1, 3)));
1859 ScSortParam aSortData
;
1860 aSortData
.nCol1
= 1;
1861 aSortData
.nCol2
= 1;
1862 aSortData
.nRow1
= 1;
1863 aSortData
.nRow2
= 3;
1864 aSortData
.bHasHeader
= false;
1865 aSortData
.bInplace
= true;
1866 aSortData
.maKeyState
[0].bDoSort
= true;
1867 aSortData
.maKeyState
[0].nField
= 0;
1868 aSortData
.maKeyState
[0].bAscending
= true;
1869 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1870 CPPUNIT_ASSERT(bSorted
);
1872 m_pDoc
->CalcAll(); // just in case...
1874 // Check the cell values after the partial sort.
1877 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1878 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1879 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1880 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1881 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1884 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
1885 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
1886 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
1887 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
1888 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(1,5,0)));
1890 m_pDoc
->DeleteTab(0);
1893 void Test::testSortImages()
1895 m_pDoc
->InsertTab(0, "testSortImages");
1897 // We need a drawing layer in order to create caption objects.
1898 m_pDoc
->InitDrawLayer(&getDocShell());
1899 ScDrawLayer
* pDrawLayer
= m_pDoc
->GetDrawLayer();
1900 CPPUNIT_ASSERT(pDrawLayer
);
1903 ScAddress
aPos(0,0,0);
1905 const char* aData
[][1] = {
1910 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData
), 0));
1911 aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
1912 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
1915 // Insert graphic in cell B2.
1916 const tools::Rectangle
aOrigRect(1000, 1000, 1200, 1200);
1917 SdrCircObj
* pObj
= new SdrCircObj(*pDrawLayer
, SdrCircKind::Full
, aOrigRect
);
1918 SdrPage
* pPage
= pDrawLayer
->GetPage(0);
1919 CPPUNIT_ASSERT(pPage
);
1920 pPage
->InsertObject(pObj
);
1922 ScDrawLayer::SetCellAnchoredFromPosition(*pObj
, *m_pDoc
, 0, false);
1924 ScAddress
aCellPos(1, 1, 0);
1925 pDrawLayer
->MoveObject(pObj
, aCellPos
);
1927 std::map
<SCROW
, std::vector
<SdrObject
*>> pRowObjects
1928 = pDrawLayer
->GetObjectsAnchoredToRange(aCellPos
.Tab(), aCellPos
.Col(), aCellPos
.Row(), aCellPos
.Row());
1929 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects
[aCellPos
.Row()].size());
1931 ScSortParam aSortData
;
1932 aSortData
.nCol1
= 0;
1933 aSortData
.nCol2
= 1;
1934 aSortData
.nRow1
= 0;
1935 aSortData
.nRow2
= 1;
1936 aSortData
.maKeyState
[0].bDoSort
= true;
1937 aSortData
.maKeyState
[0].nField
= 0;
1938 aSortData
.maKeyState
[0].bAscending
= true;
1940 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
1942 double nVal
= m_pDoc
->GetValue(0,0,0);
1943 ASSERT_DOUBLES_EQUAL(nVal
, 1.0);
1945 // check that note is also moved after sorting
1946 aCellPos
= ScAddress(1, 0, 0);
1948 = pDrawLayer
->GetObjectsAnchoredToRange(aCellPos
.Tab(), aCellPos
.Col(), aCellPos
.Row(), aCellPos
.Row());
1949 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects
[aCellPos
.Row()].size());
1951 m_pDoc
->DeleteTab(0);
1954 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */