Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / sc / qa / unit / ucalc.cxx
blobb94ccf7526c22ad71f56186fbe2a442d0f60017a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "helper/debughelper.hxx"
11 #include "helper/qahelper.hxx"
13 #include <svl/asiancfg.hxx>
15 #include <simpleformulacalc.hxx>
16 #include <stringutil.hxx>
17 #include <scmatrix.hxx>
18 #include <scitems.hxx>
19 #include <reffind.hxx>
20 #include <clipparam.hxx>
21 #include <undoblk.hxx>
22 #include <undotab.hxx>
23 #include <attrib.hxx>
24 #include <dbdata.hxx>
25 #include <reftokenhelper.hxx>
26 #include <userdat.hxx>
27 #include <refdata.hxx>
29 #include <docfunc.hxx>
30 #include <funcdesc.hxx>
32 #include <columniterator.hxx>
33 #include <scopetools.hxx>
34 #include <dociter.hxx>
35 #include <edittextiterator.hxx>
36 #include <editutil.hxx>
37 #include <asciiopt.hxx>
38 #include <impex.hxx>
39 #include <docpool.hxx>
40 #include <globalnames.hxx>
41 #include <columnspanset.hxx>
43 #include <editable.hxx>
44 #include <table.hxx>
45 #include <tabprotection.hxx>
46 #include <undomanager.hxx>
48 #include <formula/IFunctionDescription.hxx>
50 #include <editeng/borderline.hxx>
51 #include <editeng/brushitem.hxx>
52 #include <editeng/eeitem.hxx>
53 #include <editeng/wghtitem.hxx>
54 #include <editeng/postitem.hxx>
55 #include <editeng/lineitem.hxx>
57 #include <svx/svdpage.hxx>
58 #include <svx/svdocirc.hxx>
59 #include <svx/svdopath.hxx>
60 #include <svx/svdocapt.hxx>
61 #include <svl/numformat.hxx>
62 #include <svl/srchitem.hxx>
63 #include <svl/sharedstringpool.hxx>
64 #include <unotools/collatorwrapper.hxx>
66 #include <sfx2/sfxsids.hrc>
68 class ScUndoPaste;
69 class ScUndoCut;
71 namespace {
73 struct HoriIterCheck
75 SCCOL nCol;
76 SCROW nRow;
77 const char* pVal;
82 class Test : public ScUcalcTestBase
84 public:
85 void checkPrecisionAsShown(OUString& rCode, double fValue, double fExpectedRoundVal);
87 /** Get a separate new ScDocShell with ScDocument that suits unit test needs. */
88 void getNewDocShell(ScDocShellRef& rDocShellRef);
90 void testCollator();
91 void testSharedStringPool();
92 void testSharedStringPoolUndoDoc();
93 void testRangeList();
94 void testMarkData();
95 void testInput();
96 void testColumnIterator();
97 void testTdf90698();
98 void testTdf114406();
99 void testTdf93951();
100 void testTdf134490();
101 void testTdf135249();
102 void testDocStatistics();
103 void testRowForHeight();
106 * The 'data entries' data is a list of strings used for suggestions as
107 * the user types in new cell value.
109 void testDataEntries();
112 * Selection function is responsible for displaying quick calculation
113 * results in the status bar.
115 void testSelectionFunction();
117 void testMarkedCellIteration();
119 void testCopyToDocument();
121 void testHorizontalIterator();
122 void testValueIterator();
123 void testHorizontalAttrIterator();
124 void testIteratorsUnallocatedColumnsAttributes();
125 void testIteratorsDefPattern();
126 void testLastChangedColFlagsWidth();
129 * More direct test for cell broadcaster management, used to track formula
130 * dependencies.
132 void testCellBroadcaster();
134 void testFuncParam();
135 void testNamedRange();
136 void testInsertNameList();
137 void testCSV();
138 void testMatrix();
139 void testMatrixComparisonWithErrors();
140 void testMatrixConditionalBooleanResult();
141 void testEnterMixedMatrix();
142 void testMatrixEditable();
144 void testCellCopy();
145 void testSheetCopy();
146 void testSheetMove();
147 void testDataArea();
148 void testAutofilter();
149 void testAutoFilterTimeValue();
150 void testAutofilterOptimizations();
151 void testTdf76836();
152 void testTdf76441();
153 void testTdf142186();
154 void testTdf137063();
155 void testTdf126342();
156 void testAdvancedFilter();
157 void testDateFilterContains();
158 void testTdf98642();
159 void testMergedCells();
160 void testUpdateReference();
161 void testSearchCells();
162 void testFormulaPosition();
163 void testFormulaWizardSubformula();
164 void testDiagonalBorders();
165 void testWholeDocBorders();
168 * Make sure the sheet streams are invalidated properly.
170 void testStreamValid();
173 * Test built-in cell functions to make sure their categories and order
174 * are correct.
176 void testFunctionLists();
178 void testGraphicsInGroup();
179 void testGraphicsOnSheetMove();
182 * Test toggling relative/absolute flag of cell and cell range references.
183 * This corresponds with hitting Shift-F4 while the cursor is on a formula
184 * cell.
186 void testToggleRefFlag();
189 * Test to make sure correct precedent / dependent cells are obtained when
190 * preparing to jump to them.
192 void testJumpToPrecedentsDependents();
194 void testSetBackgroundColor();
195 void testRenameTable();
197 void testTdf149665();
198 void testTdf64001();
199 void testAutoFill();
200 void testAutoFillSimple();
202 void testFindAreaPosVertical();
203 void testFindAreaPosColRight();
204 void testShiftCells();
206 void testNoteBasic();
207 void testNoteDeleteRow();
208 void testNoteDeleteCol();
209 void testNoteLifeCycle();
210 void testNoteCopyPaste();
211 void testNoteContainsNotesInRange();
212 void testAreasWithNotes();
213 void testAnchoredRotatedShape();
214 void testCellTextWidth();
215 void testEditTextIterator();
217 void testImportStream();
218 void testDeleteContents();
219 void testTransliterateText();
221 void testFormulaToValue();
222 void testFormulaToValue2();
224 void testColumnFindEditCells();
225 void testSetStringAndNote();
227 void testUndoDataAnchor();
228 void testSetFormula();
229 void testMultipleDataCellsInRange();
231 void testEmptyCalcDocDefaults();
233 void testPrecisionAsShown();
234 void testProtectedSheetEditByRow();
235 void testProtectedSheetEditByColumn();
237 void testInsertColumnsWithFormulaCells();
239 CPPUNIT_TEST_SUITE(Test);
240 CPPUNIT_TEST(testCollator);
241 CPPUNIT_TEST(testSharedStringPool);
242 CPPUNIT_TEST(testSharedStringPoolUndoDoc);
243 CPPUNIT_TEST(testRangeList);
244 CPPUNIT_TEST(testMarkData);
245 CPPUNIT_TEST(testInput);
246 CPPUNIT_TEST(testColumnIterator);
247 CPPUNIT_TEST(testTdf90698);
248 CPPUNIT_TEST(testTdf114406);
249 CPPUNIT_TEST(testTdf93951);
250 CPPUNIT_TEST(testTdf134490);
251 CPPUNIT_TEST(testTdf135249);
252 CPPUNIT_TEST(testDocStatistics);
253 CPPUNIT_TEST(testRowForHeight);
254 CPPUNIT_TEST(testDataEntries);
255 CPPUNIT_TEST(testSelectionFunction);
256 CPPUNIT_TEST(testMarkedCellIteration);
257 CPPUNIT_TEST(testCopyToDocument);
258 CPPUNIT_TEST(testHorizontalIterator);
259 CPPUNIT_TEST(testValueIterator);
260 CPPUNIT_TEST(testHorizontalAttrIterator);
261 CPPUNIT_TEST(testIteratorsUnallocatedColumnsAttributes);
262 CPPUNIT_TEST(testIteratorsDefPattern);
263 CPPUNIT_TEST(testLastChangedColFlagsWidth);
264 CPPUNIT_TEST(testCellBroadcaster);
265 CPPUNIT_TEST(testFuncParam);
266 CPPUNIT_TEST(testNamedRange);
267 CPPUNIT_TEST(testInsertNameList);
268 CPPUNIT_TEST(testCSV);
269 CPPUNIT_TEST(testMatrix);
270 CPPUNIT_TEST(testMatrixComparisonWithErrors);
271 CPPUNIT_TEST(testMatrixConditionalBooleanResult);
272 CPPUNIT_TEST(testEnterMixedMatrix);
273 CPPUNIT_TEST(testMatrixEditable);
274 CPPUNIT_TEST(testCellCopy);
275 CPPUNIT_TEST(testSheetCopy);
276 CPPUNIT_TEST(testSheetMove);
277 CPPUNIT_TEST(testDataArea);
278 CPPUNIT_TEST(testGraphicsInGroup);
279 CPPUNIT_TEST(testGraphicsOnSheetMove);
280 CPPUNIT_TEST(testStreamValid);
281 CPPUNIT_TEST(testFunctionLists);
282 CPPUNIT_TEST(testToggleRefFlag);
283 CPPUNIT_TEST(testAutofilter);
284 CPPUNIT_TEST(testAutoFilterTimeValue);
285 CPPUNIT_TEST(testAutofilterOptimizations);
286 CPPUNIT_TEST(testTdf76836);
287 CPPUNIT_TEST(testTdf76441);
288 CPPUNIT_TEST(testTdf142186);
289 CPPUNIT_TEST(testTdf137063);
290 CPPUNIT_TEST(testTdf126342);
291 CPPUNIT_TEST(testAdvancedFilter);
292 CPPUNIT_TEST(testDateFilterContains);
293 CPPUNIT_TEST(testTdf98642);
294 CPPUNIT_TEST(testMergedCells);
295 CPPUNIT_TEST(testUpdateReference);
296 CPPUNIT_TEST(testSearchCells);
297 CPPUNIT_TEST(testFormulaPosition);
298 CPPUNIT_TEST(testFormulaWizardSubformula);
299 CPPUNIT_TEST(testDiagonalBorders);
300 CPPUNIT_TEST(testWholeDocBorders);
301 CPPUNIT_TEST(testJumpToPrecedentsDependents);
302 CPPUNIT_TEST(testSetBackgroundColor);
303 CPPUNIT_TEST(testRenameTable);
304 CPPUNIT_TEST(testTdf149665);
305 CPPUNIT_TEST(testTdf64001);
306 CPPUNIT_TEST(testAutoFill);
307 CPPUNIT_TEST(testAutoFillSimple);
308 CPPUNIT_TEST(testFindAreaPosVertical);
309 CPPUNIT_TEST(testFindAreaPosColRight);
310 CPPUNIT_TEST(testShiftCells);
311 CPPUNIT_TEST(testNoteBasic);
312 CPPUNIT_TEST(testNoteDeleteRow);
313 CPPUNIT_TEST(testNoteDeleteCol);
314 CPPUNIT_TEST(testNoteLifeCycle);
315 CPPUNIT_TEST(testNoteCopyPaste);
316 CPPUNIT_TEST(testNoteContainsNotesInRange);
317 CPPUNIT_TEST(testAreasWithNotes);
318 CPPUNIT_TEST(testAnchoredRotatedShape);
319 CPPUNIT_TEST(testCellTextWidth);
320 CPPUNIT_TEST(testEditTextIterator);
321 CPPUNIT_TEST(testImportStream);
322 CPPUNIT_TEST(testDeleteContents);
323 CPPUNIT_TEST(testTransliterateText);
324 CPPUNIT_TEST(testFormulaToValue);
325 CPPUNIT_TEST(testFormulaToValue2);
326 CPPUNIT_TEST(testColumnFindEditCells);
327 CPPUNIT_TEST(testSetStringAndNote);
328 CPPUNIT_TEST(testUndoDataAnchor);
329 CPPUNIT_TEST(testSetFormula);
330 CPPUNIT_TEST(testMultipleDataCellsInRange);
331 CPPUNIT_TEST(testEmptyCalcDocDefaults);
332 CPPUNIT_TEST(testPrecisionAsShown);
333 CPPUNIT_TEST(testProtectedSheetEditByRow);
334 CPPUNIT_TEST(testProtectedSheetEditByColumn);
335 CPPUNIT_TEST(testInsertColumnsWithFormulaCells);
336 CPPUNIT_TEST_SUITE_END();
338 private:
339 bool checkHorizontalIterator(ScDocument& rDoc, const std::vector<std::vector<const char*>>& rData,
340 const HoriIterCheck* pChecks, size_t nCheckCount);
343 void Test::getNewDocShell( ScDocShellRef& rDocShellRef )
345 rDocShellRef = new ScDocShell(
346 SfxModelFlags::EMBEDDED_OBJECT |
347 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
348 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
350 rDocShellRef->DoInitUnitTest();
353 void Test::testCollator()
355 sal_Int32 nRes = ScGlobal::GetCollator().compareString("A", "B");
356 CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
359 void Test::testSharedStringPool()
361 m_pDoc->InsertTab(0, "foo");
363 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
364 size_t extraCount = rPool.getCount(); // internal items such as SharedString::getEmptyString()
365 size_t extraCountIgnoreCase = rPool.getCountIgnoreCase();
367 // Strings that are identical.
368 m_pDoc->SetString(ScAddress(0,0,0), "Andy"); // A1
369 m_pDoc->SetString(ScAddress(0,1,0), "Andy"); // A2
370 m_pDoc->SetString(ScAddress(0,2,0), "Bruce"); // A3
371 m_pDoc->SetString(ScAddress(0,3,0), "andy"); // A4
372 m_pDoc->SetString(ScAddress(0,4,0), "BRUCE"); // A5
375 // These two shared string objects must go out of scope before the purge test.
376 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
377 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(0,1,0));
378 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS1.isValid());
379 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS2.isValid());
380 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
382 aSS2 = m_pDoc->GetSharedString(ScAddress(0,2,0));
383 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
385 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
386 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
388 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
389 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
391 // A3 and A5 should differ but should be equal case-insensitively.
392 aSS1 = m_pDoc->GetSharedString(ScAddress(0,2,0));
393 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
394 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
395 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
397 // A2 and A4 should be equal when ignoring cases.
398 aSS1 = m_pDoc->GetSharedString(ScAddress(0,1,0));
399 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
400 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
403 // Check the string counts after purging. Purging shouldn't remove any strings in this case.
404 rPool.purge();
405 CPPUNIT_ASSERT_EQUAL(5+extraCount, rPool.getCount());
406 CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, rPool.getCountIgnoreCase());
408 // Clear A1
409 clearRange(m_pDoc, ScAddress(0,0,0));
410 // Clear A2
411 clearRange(m_pDoc, ScAddress(0,1,0));
412 // Clear A3
413 clearRange(m_pDoc, ScAddress(0,2,0));
414 // Clear A4
415 clearRange(m_pDoc, ScAddress(0,3,0));
416 // Clear A5 and the pool should be completely empty.
417 clearRange(m_pDoc, ScAddress(0,4,0));
418 rPool.purge();
419 CPPUNIT_ASSERT_EQUAL(extraCount, rPool.getCount());
420 CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, rPool.getCountIgnoreCase());
422 // Now, compare string and edit text cells.
423 m_pDoc->SetString(ScAddress(0,0,0), "Andy and Bruce"); // A1
424 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
425 rEE.SetTextCurrentDefaults("Andy and Bruce");
427 ESelection aSel;
428 aSel.nStartPara = aSel.nEndPara = 0;
431 // Set 'Andy' bold.
432 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
433 aSel.nStartPos = 0;
434 aSel.nEndPos = 4;
435 SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT);
436 aItemSet.Put(aWeight);
437 rEE.QuickSetAttribs(aItemSet, aSel);
441 // Set 'Bruce' italic.
442 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
443 SvxPostureItem aItalic(ITALIC_NORMAL, EE_CHAR_ITALIC);
444 aItemSet.Put(aItalic);
445 aSel.nStartPos = 9;
446 aSel.nEndPos = 14;
447 rEE.QuickSetAttribs(aItemSet, aSel);
450 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject()); // B1
452 // These two should be equal.
453 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
454 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(1,0,0));
455 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
456 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
457 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
459 rEE.SetTextCurrentDefaults("ANDY and BRUCE");
460 m_pDoc->SetEditText(ScAddress(2,0,0), rEE.CreateTextObject()); // C1
461 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
462 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
463 CPPUNIT_ASSERT_MESSAGE("These two should be different when cases are considered.", aSS1.getData() != aSS2.getData());
465 // But they should be considered equal when cases are ignored.
466 aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
467 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
468 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
469 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
470 CPPUNIT_ASSERT_EQUAL(aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
472 m_pDoc->DeleteTab(0);
475 void Test::testSharedStringPoolUndoDoc()
477 struct
479 bool check( const ScDocument& rSrcDoc, ScDocument& rCopyDoc )
481 // Copy A1:A4 to the undo document.
482 for (SCROW i = 0; i <= 4; ++i)
484 ScAddress aPos(0,i,0);
485 rCopyDoc.SetString(aPos, rSrcDoc.GetString(aPos));
488 // String values in A1:A4 should have identical hash.
489 for (SCROW i = 0; i <= 4; ++i)
491 ScAddress aPos(0,i,0);
492 svl::SharedString aSS1 = rSrcDoc.GetSharedString(aPos);
493 svl::SharedString aSS2 = rCopyDoc.GetSharedString(aPos);
494 if (aSS1.getDataIgnoreCase() != aSS2.getDataIgnoreCase())
496 cerr << "String hash values are not equal at row " << (i+1)
497 << " for string '" << aSS1.getString() << "'" << endl;
498 return false;
502 return true;
505 } aTest;
507 m_pDoc->InsertTab(0, "Test");
509 m_pDoc->SetString(ScAddress(0,0,0), "Header");
510 m_pDoc->SetString(ScAddress(0,1,0), "A1");
511 m_pDoc->SetString(ScAddress(0,2,0), "A2");
512 m_pDoc->SetString(ScAddress(0,3,0), "A3");
514 ScDocument aUndoDoc(SCDOCMODE_UNDO);
515 aUndoDoc.InitUndo(*m_pDoc, 0, 0);
517 bool bSuccess = aTest.check(*m_pDoc, aUndoDoc);
518 CPPUNIT_ASSERT_MESSAGE("Check failed with undo document.", bSuccess);
520 // Test the clip document as well.
521 ScDocument aClipDoc(SCDOCMODE_CLIP);
522 aClipDoc.ResetClip(m_pDoc, static_cast<SCTAB>(0));
524 bSuccess = aTest.check(*m_pDoc, aClipDoc);
525 CPPUNIT_ASSERT_MESSAGE("Check failed with clip document.", bSuccess);
527 m_pDoc->DeleteTab(0);
530 void Test::testRangeList()
532 m_pDoc->InsertTab(0, "foo");
534 ScRangeList aRL;
535 aRL.push_back(ScRange(1,1,0,3,10,0));
536 CPPUNIT_ASSERT_EQUAL_MESSAGE("List should have one range.", size_t(1), aRL.size());
537 const ScRange* p = &aRL[0];
538 CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
539 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(1,1,0), p->aStart);
540 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(3,10,0), p->aEnd);
542 // TODO: Add more tests here.
544 m_pDoc->DeleteTab(0);
547 void Test::testMarkData()
549 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
551 // Empty mark. Nothing is selected.
552 std::vector<sc::ColRowSpan> aSpans = aMarkData.GetMarkedRowSpans();
553 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
554 aSpans = aMarkData.GetMarkedColSpans();
555 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
557 // Select B3:F7.
558 aMarkData.SetMarkArea(ScRange(1,2,0,5,6,0));
559 aSpans = aMarkData.GetMarkedRowSpans();
560 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
561 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
562 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
564 aSpans = aMarkData.GetMarkedColSpans();
565 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
566 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(1), aSpans[0].mnStart);
567 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
569 // Select A11:B13.
570 aMarkData.SetMultiMarkArea(ScRange(0,10,0,1,12,0));
571 aSpans = aMarkData.GetMarkedRowSpans();
572 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 2 selected row spans.", size_t(2), aSpans.size());
573 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
574 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
575 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(10), aSpans[1].mnStart);
576 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[1].mnEnd);
578 aSpans = aMarkData.GetMarkedColSpans();
579 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
580 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
581 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
583 // Select C8:C10.
584 aMarkData.SetMultiMarkArea(ScRange(2,7,0,2,9,0));
585 aSpans = aMarkData.GetMarkedRowSpans();
586 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
587 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
588 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[0].mnEnd);
590 aSpans = aMarkData.GetMarkedColSpans();
591 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
592 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
593 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
596 void Test::testInput()
599 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
600 m_pDoc->InsertTab (0, "foo"));
601 OUString test;
603 m_pDoc->SetString(0, 0, 0, "'10.5");
604 test = m_pDoc->GetString(0, 0, 0);
605 bool bTest = test == "10.5";
606 CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
607 m_pDoc->SetString(0, 0, 0, "'apple'");
608 test = m_pDoc->GetString(0, 0, 0);
609 bTest = test == "apple'";
610 CPPUNIT_ASSERT_MESSAGE("Text content should have the first apostrophe stripped.", bTest);
612 // Customized string handling policy.
613 ScSetStringParam aParam;
614 aParam.setTextInput();
615 m_pDoc->SetString(0, 0, 0, "000123", &aParam);
616 test = m_pDoc->GetString(0, 0, 0);
617 CPPUNIT_ASSERT_EQUAL_MESSAGE("Text content should have been treated as string, not number.", OUString("000123"), test);
619 m_pDoc->DeleteTab(0);
622 void Test::testColumnIterator() // tdf#118620
624 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
625 m_pDoc->InsertTab (0, "foo"));
627 m_pDoc->SetString(0, 0, 0, "'10.5");
628 m_pDoc->SetString(0, m_pDoc->MaxRow()-5, 0, "42.0");
629 std::optional<sc::ColumnIterator> it = m_pDoc->GetColumnIterator(0, 0, m_pDoc->MaxRow() - 10, m_pDoc->MaxRow());
630 while (it->hasCell())
632 it->getCell();
633 it->next();
636 m_pDoc->DeleteTab(0);
639 void Test::testTdf90698()
641 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
642 m_pDoc->SetString(ScAddress(0,0,0), "=(1;2)");
644 // Without the fix in place, this would have failed with
645 // - Expected: =(1;2)
646 // - Actual : =(1~2)
647 OUString aFormula = m_pDoc->GetFormula(0,0,0);
648 CPPUNIT_ASSERT_EQUAL(OUString("=(1;2)"), aFormula);
650 m_pDoc->DeleteTab(0);
653 void Test::testTdf114406()
655 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
656 m_pDoc->SetString(ScAddress(0,0,0), "5");
657 m_pDoc->SetString(ScAddress(1,0,0), "=A1/100%");
659 // Without the fix in place, this would have failed with
660 // - Expected: =A1/100%
661 // - Actual : =A1/1
662 OUString aFormula = m_pDoc->GetFormula(1,0,0);
663 CPPUNIT_ASSERT_EQUAL(OUString("=A1/100%"), aFormula);
665 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,0,0)));
667 m_pDoc->DeleteTab(0);
670 void Test::testTdf93951()
672 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
673 m_pDoc->SetString(ScAddress(0,0,0), u"=2*§*2");
675 OUString aFormula = m_pDoc->GetFormula(0,0,0);
677 // Without the fix in place, this test would have failed with
678 // - Expected: =2*§*2
679 // - Actual : =2*
680 CPPUNIT_ASSERT_EQUAL(OUString(u"=2*§*2"), aFormula);
682 m_pDoc->DeleteTab(0);
685 void Test::testTdf134490()
687 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
689 m_pDoc->SetString(ScAddress(0,0,0), "--1");
690 m_pDoc->SetString(ScAddress(0,1,0), "---1");
691 m_pDoc->SetString(ScAddress(0,2,0), "+-1");
692 m_pDoc->SetString(ScAddress(0,3,0), "+--1");
694 // Without the fix in place, this test would have failed with
695 // - Expected: --1
696 // - Actual : -1
697 CPPUNIT_ASSERT_EQUAL(OUString("--1"), m_pDoc->GetString(ScAddress(0,0,0)));
698 CPPUNIT_ASSERT_EQUAL(OUString("---1"), m_pDoc->GetString(ScAddress(0,1,0)));
699 CPPUNIT_ASSERT_EQUAL(OUString("+-1"), m_pDoc->GetString(ScAddress(0,2,0)));
700 CPPUNIT_ASSERT_EQUAL(OUString("+--1"), m_pDoc->GetString(ScAddress(0,3,0)));
702 m_pDoc->DeleteTab(0);
705 void Test::testTdf135249()
707 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
709 m_pDoc->SetString(ScAddress(0,0,0), "1:60");
710 m_pDoc->SetString(ScAddress(0,1,0), "1:123");
711 m_pDoc->SetString(ScAddress(0,2,0), "1:1:123");
712 m_pDoc->SetString(ScAddress(0,3,0), "0:123");
713 m_pDoc->SetString(ScAddress(0,4,0), "0:0:123");
714 m_pDoc->SetString(ScAddress(0,5,0), "0:123:59");
716 // These are not valid duration inputs
717 CPPUNIT_ASSERT_EQUAL(OUString("1:60"), m_pDoc->GetString(ScAddress(0,0,0)));
718 CPPUNIT_ASSERT_EQUAL(OUString("1:123"), m_pDoc->GetString(ScAddress(0,1,0)));
719 CPPUNIT_ASSERT_EQUAL(OUString("1:1:123"), m_pDoc->GetString(ScAddress(0,2,0)));
721 // These are valid duration inputs
722 // Without the fix in place, this test would have failed with
723 // - Expected: 02:03:00 AM
724 // - Actual : 0:123
725 CPPUNIT_ASSERT_EQUAL(OUString("02:03:00 AM"), m_pDoc->GetString(ScAddress(0,3,0)));
726 CPPUNIT_ASSERT_EQUAL(OUString("12:02:03 AM"), m_pDoc->GetString(ScAddress(0,4,0)));
727 CPPUNIT_ASSERT_EQUAL(OUString("02:03:59 AM"), m_pDoc->GetString(ScAddress(0,5,0)));
729 m_pDoc->DeleteTab(0);
732 void Test::testDocStatistics()
734 SCTAB nStartTabs = m_pDoc->GetTableCount();
735 m_pDoc->InsertTab(0, "Sheet1");
736 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
737 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
738 m_pDoc->InsertTab(1, "Sheet2");
739 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
740 static_cast<SCTAB>(nStartTabs+2), m_pDoc->GetTableCount());
742 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetCellCount());
743 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
744 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetCellCount());
745 m_pDoc->SetValue(ScAddress(2,2,0), 2.5);
746 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetCellCount());
747 m_pDoc->SetString(ScAddress(1,1,1), "Test");
748 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(3), m_pDoc->GetCellCount());
750 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetFormulaGroupCount());
751 m_pDoc->SetString(ScAddress(3,0,1), "=A1");
752 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
753 m_pDoc->SetString(ScAddress(3,1,1), "=A2");
754 m_pDoc->SetString(ScAddress(3,2,1), "=A3");
755 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
756 m_pDoc->SetString(ScAddress(3,3,1), "=A5");
757 m_pDoc->SetString(ScAddress(3,4,1), "=A6");
758 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetFormulaGroupCount());
759 m_pDoc->SetString(ScAddress(3,1,1), "=A3");
760 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(4), m_pDoc->GetFormulaGroupCount());
762 m_pDoc->DeleteTab(1);
763 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to decrement sheet count.",
764 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
765 m_pDoc->DeleteTab(0); // This may fail in case there is only one sheet in the document.
768 void Test::testRowForHeight()
770 m_pDoc->InsertTab(0, "Sheet1");
771 m_pDoc->SetRowHeightRange( 0, 9, 0, 100);
772 m_pDoc->SetRowHeightRange(10, 19, 0, 200);
773 m_pDoc->SetRowHeightRange(20, 29, 0, 300);
775 // Hide some rows.
776 m_pDoc->SetRowHidden(3, 5, 0, true);
777 m_pDoc->SetRowHidden(8, 12, 0, true);
779 struct Check
781 tools::Long nHeight;
782 SCROW nRow;
785 std::vector<Check> aChecks = {
786 { 1, 1 },
787 { 99, 1 },
788 { 120, 2 },
789 { 330, 7 },
790 { 420, 13 },
791 { 780, 15 },
792 { 1860, 20 },
793 { 4020, 28 },
796 for (const Check& rCheck : aChecks)
798 SCROW nRow = m_pDoc->GetRowForHeight(0, rCheck.nHeight);
799 CPPUNIT_ASSERT_EQUAL(rCheck.nRow, nRow);
803 void Test::testDataEntries()
805 m_pDoc->InsertTab(0, "Test");
807 m_pDoc->SetString(ScAddress(0,5,0), "Andy");
808 m_pDoc->SetString(ScAddress(0,6,0), "Bruce");
809 m_pDoc->SetString(ScAddress(0,7,0), "Charlie");
810 m_pDoc->SetString(ScAddress(0,10,0), "Andy");
812 std::vector<ScTypedStrData> aEntries;
813 m_pDoc->GetDataEntries(0, 0, 0, aEntries); // Try at the very top.
815 // Entries are supposed to be sorted in ascending order, and are all unique.
816 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
817 std::vector<ScTypedStrData>::const_iterator it = aEntries.begin();
818 CPPUNIT_ASSERT_EQUAL(OUString("Andy"), it->GetString());
819 ++it;
820 CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
821 ++it;
822 CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), it->GetString());
823 ++it;
824 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
826 aEntries.clear();
827 m_pDoc->GetDataEntries(0, m_pDoc->MaxRow(), 0, aEntries); // Try at the very bottom.
828 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
830 // Make sure we get the same set of suggestions.
831 it = aEntries.begin();
832 CPPUNIT_ASSERT_EQUAL(OUString("Andy"), it->GetString());
833 ++it;
834 CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
835 ++it;
836 CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), it->GetString());
837 ++it;
838 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
840 m_pDoc->DeleteTab(0);
843 void Test::testSelectionFunction()
845 m_pDoc->InsertTab(0, "Test");
847 // Insert values into B2:B4.
848 m_pDoc->SetString(ScAddress(1,1,0), "=1"); // formula
849 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
850 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
852 // Insert strings into B5:B8.
853 m_pDoc->SetString(ScAddress(1,4,0), "A");
854 m_pDoc->SetString(ScAddress(1,5,0), "B");
855 m_pDoc->SetString(ScAddress(1,6,0), "=\"C\""); // formula
856 m_pDoc->SetString(ScAddress(1,7,0), "D");
858 // Insert values into D2:D4.
859 m_pDoc->SetValue(ScAddress(3,1,0), 4.0);
860 m_pDoc->SetValue(ScAddress(3,2,0), 5.0);
861 m_pDoc->SetValue(ScAddress(3,3,0), 6.0);
863 // Insert edit text into D5.
864 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
865 rEE.SetTextCurrentDefaults("Rich Text");
866 m_pDoc->SetEditText(ScAddress(3,4,0), rEE.CreateTextObject());
868 // Insert Another string into D6.
869 m_pDoc->SetString(ScAddress(3,5,0), "E");
871 // Select B2:B8 & D2:D8 disjoint region.
872 ScRangeList aRanges;
873 aRanges.push_back(ScRange(1,1,0,1,7,0)); // B2:B8
874 aRanges.push_back(ScRange(3,1,0,3,7,0)); // D2:D8
875 ScMarkData aMark(m_pDoc->GetSheetLimits());
876 aMark.MarkFromRangeList(aRanges, true);
878 struct Check
880 ScSubTotalFunc meFunc;
881 double mfExpected;
885 static const Check aChecks[] =
887 { SUBTOTAL_FUNC_AVE, 3.5 },
888 { SUBTOTAL_FUNC_CNT2, 12.0 },
889 { SUBTOTAL_FUNC_CNT, 6.0 },
890 { SUBTOTAL_FUNC_MAX, 6.0 },
891 { SUBTOTAL_FUNC_MIN, 1.0 },
892 { SUBTOTAL_FUNC_SUM, 21.0 },
893 { SUBTOTAL_FUNC_SELECTION_COUNT, 14.0 }
896 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
898 double fRes = 0.0;
899 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
900 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
901 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
905 // Hide rows 4 and 6 and check the results again.
907 m_pDoc->SetRowHidden(3, 3, 0, true);
908 m_pDoc->SetRowHidden(5, 5, 0, true);
909 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(3, 0));
910 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(5, 0));
913 static const Check aChecks[] =
915 { SUBTOTAL_FUNC_AVE, 3.0 },
916 { SUBTOTAL_FUNC_CNT2, 8.0 },
917 { SUBTOTAL_FUNC_CNT, 4.0 },
918 { SUBTOTAL_FUNC_MAX, 5.0 },
919 { SUBTOTAL_FUNC_MIN, 1.0 },
920 { SUBTOTAL_FUNC_SUM, 12.0 },
921 { SUBTOTAL_FUNC_SELECTION_COUNT, 10.0 }
924 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
926 double fRes = 0.0;
927 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
928 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
929 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
933 // Make sure that when no selection is present, use the current cursor position.
934 ScMarkData aEmpty(m_pDoc->GetSheetLimits());
937 // D3 (numeric cell containing 5.)
938 ScAddress aPos(3, 2, 0);
940 static const Check aChecks[] =
942 { SUBTOTAL_FUNC_AVE, 5.0 },
943 { SUBTOTAL_FUNC_CNT2, 1.0 },
944 { SUBTOTAL_FUNC_CNT, 1.0 },
945 { SUBTOTAL_FUNC_MAX, 5.0 },
946 { SUBTOTAL_FUNC_MIN, 5.0 },
947 { SUBTOTAL_FUNC_SUM, 5.0 },
948 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
951 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
953 double fRes = 0.0;
954 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
955 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
956 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
961 // B7 (string formula cell containing ="C".)
962 ScAddress aPos(1, 6, 0);
964 static const Check aChecks[] =
966 { SUBTOTAL_FUNC_CNT2, 1.0 },
967 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
970 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
972 double fRes = 0.0;
973 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
974 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
975 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
979 // Calculate function across selected sheets.
980 clearSheet(m_pDoc, 0);
981 m_pDoc->InsertTab(1, "Test2");
982 m_pDoc->InsertTab(2, "Test3");
984 // Set values at B2 and C3 on each sheet.
985 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
986 m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
987 m_pDoc->SetValue(ScAddress(1,1,1), 4.0);
988 m_pDoc->SetValue(ScAddress(2,2,1), 8.0);
989 m_pDoc->SetValue(ScAddress(1,1,2), 16.0);
990 m_pDoc->SetValue(ScAddress(2,2,2), 32.0);
992 // Mark B2 and C3 on first sheet.
993 aRanges.RemoveAll();
994 aRanges.push_back(ScRange(1,1,0)); // B2
995 aRanges.push_back(ScRange(2,2,0)); // C3
996 aMark.MarkFromRangeList(aRanges, true);
997 // Additionally select third sheet.
998 aMark.SelectTable(2, true);
1001 double fRes = 0.0;
1002 bool bRes = m_pDoc->GetSelectionFunction( SUBTOTAL_FUNC_SUM, ScAddress(), aMark, fRes);
1003 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
1004 CPPUNIT_ASSERT_EQUAL_MESSAGE("1+2+16+32=", 51.0, fRes);
1007 m_pDoc->DeleteTab(2);
1008 m_pDoc->DeleteTab(1);
1009 m_pDoc->DeleteTab(0);
1012 void Test::testMarkedCellIteration()
1014 m_pDoc->InsertTab(0, "Test");
1016 // Insert cells to A1, A5, B2 and C3.
1017 m_pDoc->SetString(ScAddress(0,0,0), "California");
1018 m_pDoc->SetValue(ScAddress(0,4,0), 1.2);
1019 m_pDoc->SetEditText(ScAddress(1,1,0), "Boston");
1020 m_pDoc->SetFormula(ScAddress(2,2,0), "=SUM(1,2,3)", m_pDoc->GetGrammar());
1022 // Select A1:C5.
1023 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
1024 aMarkData.SetMarkArea(ScRange(0,0,0,2,4,0));
1025 aMarkData.MarkToMulti(); // TODO : we shouldn't have to do this.
1027 struct Check
1029 SCCOL mnCol;
1030 SCROW mnRow;
1033 const std::vector<Check> aChecks = {
1034 { 0, 0 }, // A1
1035 { 0, 4 }, // A5
1036 { 1, 1 }, // B2
1037 { 2, 2 }, // C3
1040 SCROW nRow = -1; // Start from the imaginary row before A1.
1041 SCCOL nCol = 0;
1043 for (const Check& rCheck : aChecks)
1045 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
1046 if (!bFound)
1048 std::ostringstream os;
1049 os << ScAddress(rCheck.mnCol, rCheck.mnRow, 0).GetColRowString() << " was expected, but not found.";
1050 CPPUNIT_FAIL(os.str());
1053 CPPUNIT_ASSERT_EQUAL(rCheck.mnRow, nRow);
1054 CPPUNIT_ASSERT_EQUAL(rCheck.mnCol, nCol);
1057 // No more marked cells on this sheet.
1058 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
1059 CPPUNIT_ASSERT(!bFound);
1061 m_pDoc->DeleteTab(0);
1064 void Test::testCopyToDocument()
1066 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
1068 // We need a drawing layer in order to create caption objects.
1069 m_pDoc->InitDrawLayer(m_xDocShell.get());
1071 m_pDoc->SetString(0, 0, 0, "Header");
1072 m_pDoc->SetString(0, 1, 0, "1");
1073 m_pDoc->SetString(0, 2, 0, "2");
1074 m_pDoc->SetString(0, 3, 0, "3");
1075 m_pDoc->SetString(0, 4, 0, "=4/2");
1076 m_pDoc->CalcAll();
1078 //note on A1
1079 ScAddress aAdrA1 (0, 0, 0); // numerical cell content
1080 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
1081 pNote->SetText(aAdrA1, "Hello world in A1");
1083 // Copy statically to another document.
1085 ScDocShellRef xDocSh2;
1086 getNewDocShell(xDocSh2);
1087 ScDocument* pDestDoc = &xDocSh2->GetDocument();
1088 pDestDoc->InsertTab(0, "src");
1089 pDestDoc->InitDrawLayer(xDocSh2.get()); // for note caption objects
1091 m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, *pDestDoc); // Copy A2:A4
1092 m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, *pDestDoc); // Copy A1
1093 m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, *pDestDoc); // Copy A5:A8
1095 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), pDestDoc->GetString(0,0,0));
1096 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), pDestDoc->GetString(0,1,0));
1097 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), pDestDoc->GetString(0,2,0));
1098 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), pDestDoc->GetString(0,3,0));
1099 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), pDestDoc->GetString(0,4,0));
1101 // verify note
1102 CPPUNIT_ASSERT_MESSAGE("There should be a note in A1 destDocument", pDestDoc->HasNote(ScAddress(0, 0, 0)));
1103 CPPUNIT_ASSERT_EQUAL_MESSAGE("The notes content should be the same on both documents",
1104 m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText(), pDestDoc->GetNote(ScAddress(0, 0, 0))->GetText());
1106 pDestDoc->DeleteTab(0);
1107 xDocSh2->DoClose();
1108 xDocSh2.clear();
1110 m_pDoc->DeleteTab(0);
1113 bool Test::checkHorizontalIterator(ScDocument& rDoc, const std::vector<std::vector<const char*>>& rData, const HoriIterCheck* pChecks, size_t nCheckCount)
1115 ScAddress aPos(0,0,0);
1116 insertRangeData(&rDoc, aPos, rData);
1117 ScHorizontalCellIterator aIter(rDoc, 0, 0, 0, 1, rData.size() - 1);
1119 SCCOL nCol;
1120 SCROW nRow;
1121 size_t i = 0;
1122 for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
1124 if (i >= nCheckCount)
1126 cerr << "hit invalid check " << i << " of " << nCheckCount << endl;
1127 CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
1128 return false;
1131 if (pChecks[i].nCol != nCol)
1133 cerr << "Column mismatch " << pChecks[i].nCol << " vs. " << nCol << endl;
1134 return false;
1137 if (pChecks[i].nRow != nRow)
1139 cerr << "Row mismatch " << pChecks[i].nRow << " vs. " << nRow << endl;
1140 return false;
1143 if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString(&rDoc))
1145 cerr << "String mismatch " << pChecks[i].pVal << " vs. " <<
1146 pCell->getString(&rDoc) << endl;
1147 return false;
1151 return true;
1154 void Test::testHorizontalIterator()
1156 m_pDoc->InsertTab(0, "test");
1159 // Raw data - mixed types
1160 std::vector<std::vector<const char*>> aData = {
1161 { "A", "B" },
1162 { "C", "1" },
1163 { "D", "2" },
1164 { "E", "3" }
1167 static const HoriIterCheck aChecks[] = {
1168 { 0, 0, "A" },
1169 { 1, 0, "B" },
1170 { 0, 1, "C" },
1171 { 1, 1, "1" },
1172 { 0, 2, "D" },
1173 { 1, 2, "2" },
1174 { 0, 3, "E" },
1175 { 1, 3, "3" },
1178 bool bRes = checkHorizontalIterator(
1179 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1181 if (!bRes)
1182 CPPUNIT_FAIL("Failed on test mixed.");
1186 // Raw data - 'hole' data
1187 std::vector<std::vector<const char*>> aData = {
1188 { "A", "B" },
1189 { "C", nullptr },
1190 { "D", "E" },
1193 static const HoriIterCheck aChecks[] = {
1194 { 0, 0, "A" },
1195 { 1, 0, "B" },
1196 { 0, 1, "C" },
1197 { 0, 2, "D" },
1198 { 1, 2, "E" },
1201 bool bRes = checkHorizontalIterator(
1202 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1204 if (!bRes)
1205 CPPUNIT_FAIL("Failed on test hole.");
1209 // Very holy data
1210 std::vector<std::vector<const char*>> aData = {
1211 { nullptr, "A" },
1212 { nullptr, nullptr },
1213 { nullptr, "1" },
1214 { "B", nullptr },
1215 { "C", "2" },
1216 { "D", "3" },
1217 { "E", nullptr },
1218 { nullptr, "G" },
1219 { nullptr, nullptr },
1222 static const HoriIterCheck aChecks[] = {
1223 { 1, 0, "A" },
1224 { 1, 2, "1" },
1225 { 0, 3, "B" },
1226 { 0, 4, "C" },
1227 { 1, 4, "2" },
1228 { 0, 5, "D" },
1229 { 1, 5, "3" },
1230 { 0, 6, "E" },
1231 { 1, 7, "G" },
1234 bool bRes = checkHorizontalIterator(
1235 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1237 if (!bRes)
1238 CPPUNIT_FAIL("Failed on test holy.");
1242 // Degenerate case
1243 std::vector<std::vector<const char*>> aData = {
1244 { nullptr, nullptr },
1245 { nullptr, nullptr },
1246 { nullptr, nullptr },
1249 bool bRes = checkHorizontalIterator(
1250 *m_pDoc, aData, nullptr, 0);
1252 if (!bRes)
1253 CPPUNIT_FAIL("Failed on test degenerate.");
1257 // Data at end
1258 std::vector<std::vector<const char*>> aData = {
1259 { nullptr, nullptr },
1260 { nullptr, nullptr },
1261 { nullptr, "A" },
1264 static const HoriIterCheck aChecks[] = {
1265 { 1, 2, "A" },
1268 bool bRes = checkHorizontalIterator(
1269 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1271 if (!bRes)
1272 CPPUNIT_FAIL("Failed on test at end.");
1276 // Data in middle
1277 std::vector<std::vector<const char*>> aData = {
1278 { nullptr, nullptr },
1279 { nullptr, nullptr },
1280 { nullptr, "A" },
1281 { nullptr, "1" },
1282 { nullptr, nullptr },
1285 static const HoriIterCheck aChecks[] = {
1286 { 1, 2, "A" },
1287 { 1, 3, "1" },
1290 bool bRes = checkHorizontalIterator(
1291 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1293 if (!bRes)
1294 CPPUNIT_FAIL("Failed on test in middle.");
1297 m_pDoc->DeleteTab(0);
1300 void Test::testValueIterator()
1302 m_pDoc->InsertTab(0, "Test");
1304 // Turn on "precision as shown" option.
1305 ScDocOptions aOpt = m_pDoc->GetDocOptions();
1306 aOpt.SetCalcAsShown(true);
1307 m_pDoc->SetDocOptions(aOpt);
1309 ScInterpreterContext aContext(*m_pDoc, m_pDoc->GetFormatTable());
1311 // Purely horizontal data layout with numeric data.
1312 for (SCCOL i = 1; i <= 3; ++i)
1313 m_pDoc->SetValue(ScAddress(i,2,0), i);
1316 const double aChecks[] = { 1.0, 2.0, 3.0 };
1317 size_t const nCheckLen = SAL_N_ELEMENTS(aChecks);
1318 ScValueIterator aIter(aContext, ScRange(1,2,0,3,2,0));
1319 bool bHas = false;
1320 size_t nCheckPos = 0;
1321 double fVal;
1322 FormulaError nErr;
1323 for (bHas = aIter.GetFirst(fVal, nErr); bHas; bHas = aIter.GetNext(fVal, nErr), ++nCheckPos)
1325 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1326 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos], fVal);
1327 CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(nErr));
1331 m_pDoc->DeleteTab(0);
1334 void Test::testHorizontalAttrIterator()
1336 m_pDoc->InsertTab(0, "Test");
1338 // Set the background color of B2:C3,D2,E3,C4:D4,B5:D5 to blue
1339 ScPatternAttr aCellBackColor(m_pDoc->GetPool());
1340 aCellBackColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
1341 m_pDoc->ApplyPatternAreaTab(1, 1, 2, 2, 0, aCellBackColor);
1342 m_pDoc->ApplyPatternAreaTab(3, 1, 3, 1, 0, aCellBackColor);
1343 m_pDoc->ApplyPatternAreaTab(4, 2, 4, 2, 0, aCellBackColor);
1344 m_pDoc->ApplyPatternAreaTab(2, 3, 3, 3, 0, aCellBackColor);
1345 m_pDoc->ApplyPatternAreaTab(1, 4, 4, 4, 0, aCellBackColor);
1347 // some numeric data
1348 for (SCCOL i = 1; i <= 4; ++i)
1349 for (SCROW j = 1; j <= 4; ++j)
1350 m_pDoc->SetValue(ScAddress(i,j,0), i*10+j);
1353 const int aChecks[][3] = { {1, 3, 1}, {1, 2, 2}, {4, 4, 2}, {2, 3, 3}, {1, 4, 4} };
1354 const size_t nCheckLen = SAL_N_ELEMENTS(aChecks);
1356 ScHorizontalAttrIterator aIter(*m_pDoc, 0, 0, 0, 5, 5);
1357 SCCOL nCol1, nCol2;
1358 SCROW nRow;
1359 size_t nCheckPos = 0;
1360 for (const ScPatternAttr* pAttr = aIter.GetNext(nCol1, nCol2, nRow); pAttr; pAttr = aIter.GetNext(nCol1, nCol2, nRow))
1362 if( pAttr == m_pDoc->GetDefPattern())
1363 continue;
1364 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1365 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][0], static_cast<int>(nCol1));
1366 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][1], static_cast<int>(nCol2));
1367 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][2], static_cast<int>(nRow));
1368 ++nCheckPos;
1372 m_pDoc->DeleteTab(0);
1375 void Test::testIteratorsUnallocatedColumnsAttributes()
1377 m_pDoc->InsertTab(0, "Tab1");
1379 // Set values in first two columns, to ensure allocation of those columns.
1380 m_pDoc->SetValue(ScAddress(0,1,0), 1);
1381 m_pDoc->SetValue(ScAddress(1,1,0), 2);
1382 constexpr SCCOL allocatedColsCount = 2;
1383 assert( allocatedColsCount >= INITIALCOLCOUNT );
1384 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1386 // Make entire second row and third row bold.
1387 ScPatternAttr boldAttr(m_pDoc->GetPool());
1388 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1389 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 2, 0, boldAttr);
1391 // That shouldn't need allocating more columns, just changing the default attribute.
1392 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1393 vcl::Font aFont;
1394 const ScPatternAttr* pattern = m_pDoc->GetPattern(m_pDoc->MaxCol(), 1, 0);
1395 pattern->GetFont(aFont, SC_AUTOCOL_RAW);
1396 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight());
1398 // Test iterators.
1399 ScDocAttrIterator docit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1400 SCCOL col1, col2;
1401 SCROW row1, row2;
1402 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1403 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1404 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1405 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1406 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1407 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col1 );
1408 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1409 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1410 CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == nullptr );
1412 ScAttrRectIterator rectit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1413 CPPUNIT_ASSERT_EQUAL( pattern, rectit.GetNext( col1, col2, row1, row2 ));
1414 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1415 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1416 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1417 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1418 CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1420 ScHorizontalAttrIterator horit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1421 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1422 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1423 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1424 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1425 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1426 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1427 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1428 CPPUNIT_ASSERT_EQUAL( SCROW(2), row1 );
1429 CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == nullptr );
1431 m_pDoc->DeleteTab(0);
1434 void Test::testIteratorsDefPattern()
1436 m_pDoc->InsertTab(0, "Tab1");
1438 // The default pattern is the default style, which can be edited by the user.
1439 // As such iterators should not ignore it by default, because it might contain
1440 // some attributes set.
1442 // Set cells as bold, default allocated, bold, default unallocated.
1443 SCCOL firstCol = 100;
1444 SCCOL lastCol = 103;
1445 ScPatternAttr boldAttr(m_pDoc->GetPool());
1446 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1447 m_pDoc->ApplyPattern(100, 0, 0, boldAttr);
1448 m_pDoc->ApplyPattern(102, 0, 0, boldAttr);
1450 CPPUNIT_ASSERT_EQUAL(SCCOL(102 + 1), m_pDoc->GetAllocatedColumnsCount(0));
1451 const ScPatternAttr* pattern = m_pDoc->GetPattern(100, 0, 0);
1452 const ScPatternAttr* defPattern = m_pDoc->GetDefPattern();
1453 CPPUNIT_ASSERT(pattern != defPattern);
1454 CPPUNIT_ASSERT_EQUAL(pattern, m_pDoc->GetPattern(102, 0, 0));
1455 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(101, 0, 0));
1456 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(103, 0, 0));
1458 // Test iterators.
1459 ScDocAttrIterator docit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1460 SCCOL col1, col2;
1461 SCROW row1, row2;
1462 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1463 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1464 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1465 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1466 CPPUNIT_ASSERT(docit.GetNext( col1, row1, row2 ) == nullptr );
1468 ScAttrRectIterator rectit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1469 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1470 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1471 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1472 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1473 CPPUNIT_ASSERT(rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1475 ScHorizontalAttrIterator horit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1476 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1477 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1478 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1479 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1480 CPPUNIT_ASSERT(horit.GetNext( col1, col2, row1 ) == nullptr );
1482 m_pDoc->DeleteTab(0);
1485 void Test::testLastChangedColFlagsWidth()
1487 m_pDoc->InsertTab(0, "Tab1");
1489 constexpr SCCOL firstChangedCol = 100;
1490 assert( firstChangedCol > m_pDoc->GetAllocatedColumnsCount(0));
1491 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1492 for( SCCOL col = firstChangedCol; col <= m_pDoc->MaxCol(); ++col )
1493 m_pDoc->SetColWidth( col, 0, 10 );
1495 // That shouldn't need allocating more columns, just changing column flags.
1496 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1497 // But the flags are changed.
1498 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), m_pDoc->GetLastChangedColFlagsWidth(0));
1500 m_pDoc->DeleteTab(0);
1503 namespace {
1505 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
1507 const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
1508 if (pBC)
1510 cerr << "Broadcaster shouldn't be here." << endl;
1511 return false;
1514 pBC = rDoc.GetBroadcaster(rTo);
1515 if (!pBC)
1517 cerr << "Broadcaster should be here." << endl;
1518 return false;
1520 return true;
1523 formula::FormulaToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
1525 ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
1526 if (!pFC)
1528 cerr << "Formula cell expected, but not found." << endl;
1529 return nullptr;
1532 ScTokenArray* pTokens = pFC->GetCode();
1533 if (!pTokens)
1535 cerr << "Token array is not present." << endl;
1536 return nullptr;
1539 formula::FormulaToken* pToken = pTokens->FirstToken();
1540 if (!pToken || pToken->GetType() != formula::svSingleRef)
1542 cerr << "Not a single reference token." << endl;
1543 return nullptr;
1546 return pToken;
1549 bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCCOL nRelCol, SCROW nRelRow)
1551 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1552 if (!pToken)
1553 return false;
1555 ScSingleRefData& rRef = *pToken->GetSingleRef();
1556 if (!rRef.IsColRel() || rRef.Col() != nRelCol)
1558 cerr << "Unexpected relative column address." << endl;
1559 return false;
1562 if (!rRef.IsRowRel() || rRef.Row() != nRelRow)
1564 cerr << "Unexpected relative row address." << endl;
1565 return false;
1568 return true;
1571 bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
1573 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1574 if (!pToken)
1575 return false;
1577 ScSingleRefData& rRef = *pToken->GetSingleRef();
1578 if (!rRef.IsDeleted())
1580 cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
1581 return false;
1584 return true;
1589 void Test::testCellBroadcaster()
1591 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1593 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1594 m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
1595 double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
1596 CPPUNIT_ASSERT_EQUAL(0.0, val);
1598 const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1599 CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
1601 // Change the value of A1 and make sure that B1 follows.
1602 m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
1603 val = m_pDoc->GetValue(ScAddress(1,0,0));
1604 CPPUNIT_ASSERT_EQUAL(1.23, val);
1606 // Move column A down 5 cells. Make sure B1 now references A6, not A1.
1607 m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
1608 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1609 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
1611 // Make sure the broadcaster has also moved.
1612 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1613 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
1615 // Set new value to A6 and make sure B1 gets updated.
1616 m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
1617 val = m_pDoc->GetValue(ScAddress(1,0,0));
1618 CPPUNIT_ASSERT_EQUAL(45.6, val);
1620 // Move column A up 3 cells, and make sure B1 now references A3, not A6.
1621 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
1622 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1623 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
1625 // The broadcaster should also have been relocated from A6 to A3.
1626 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1627 broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
1629 // Insert cells over A1:A10 and shift cells to right.
1630 m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
1631 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1632 checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
1633 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1634 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
1636 // Delete formula in C2, which should remove the broadcaster in B3.
1637 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1638 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
1639 clearRange(m_pDoc, ScAddress(2,0,0));
1640 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
1641 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1642 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
1644 // Clear everything and start over.
1645 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1647 m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
1648 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1649 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
1651 // While column A is still empty, move column A down 2 cells. This should
1652 // move the broadcaster from A1 to A3.
1653 m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
1654 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1655 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
1657 // Move it back while column A is still empty.
1658 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
1659 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1660 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
1662 // Clear everything again
1663 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1665 // B1:B3 depends on A1:A3
1666 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
1667 m_pDoc->SetString(ScAddress(1,1,0), "=A2");
1668 m_pDoc->SetString(ScAddress(1,2,0), "=A3");
1669 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1670 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1671 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1672 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
1673 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
1674 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
1675 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1676 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
1677 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
1679 // Insert Rows at row 2, down 5 rows.
1680 m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
1681 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1682 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1683 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1685 // Broadcasters in A2 and A3 should shift down by 5 rows.
1686 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1687 broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
1688 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1689 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
1691 // B2 and B3 should reference shifted cells.
1692 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1693 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
1694 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1695 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
1697 // Delete cells with broadcasters.
1698 m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
1699 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
1700 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
1702 // References in B2 and B3 should be invalid.
1703 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
1704 checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
1705 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
1706 checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
1708 // Clear everything again
1709 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1712 // Switch to R1C1 to make it easier to input relative references in multiple cells.
1713 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1715 // Have B1:B20 reference A1:A20.
1716 val = 0.0;
1717 for (SCROW i = 0; i < 20; ++i)
1719 m_pDoc->SetValue(ScAddress(0,i,0), val++);
1720 m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
1724 // Ensure that the formula cells show correct values, and the referenced
1725 // cells have broadcasters.
1726 val = 0.0;
1727 for (SCROW i = 0; i < 20; ++i, ++val)
1729 CPPUNIT_ASSERT_EQUAL(val, m_pDoc->GetValue(ScAddress(1,i,0)));
1730 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1731 CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
1734 // Delete formula cells in B2:B19.
1735 clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
1736 // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
1737 CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1738 CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
1739 for (SCROW i = 1; i <= 18; ++i)
1741 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1742 CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
1745 // Clear everything again
1746 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1748 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
1749 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
1750 m_pDoc->SetString(ScAddress(2,0,0), "=B1");
1751 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,0,0));
1752 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,0,0));
1753 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2,0,0));
1755 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1756 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1757 pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
1758 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1760 // Change the value of A1 and make sure everyone follows suit.
1761 m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
1762 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
1763 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
1764 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
1766 // Insert a column at column B.
1767 m_pDoc->InsertCol(ScRange(1,0,0,1,m_pDoc->MaxRow(),0));
1768 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1769 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1770 pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
1771 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1773 // Change the value of A1 again.
1774 m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
1775 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
1776 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
1777 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
1779 m_pDoc->DeleteTab(0);
1782 void Test::testFuncParam()
1785 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1786 m_pDoc->InsertTab (0, "foo"));
1788 // First, the normal case, with no missing parameters.
1789 m_pDoc->SetString(0, 0, 0, "=AVERAGE(1;2;3)");
1790 m_pDoc->CalcFormulaTree(false, false);
1791 double val = m_pDoc->GetValue(0, 0, 0);
1792 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 2.0, val);
1794 // Now function with missing parameters. Missing values should be treated
1795 // as zeros.
1796 m_pDoc->SetString(0, 0, 0, "=AVERAGE(1;;;)");
1797 m_pDoc->CalcFormulaTree(false, false);
1798 val = m_pDoc->GetValue(0, 0, 0);
1799 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 0.25, val);
1801 // Conversion of string to numeric argument.
1802 m_pDoc->SetString(0, 0, 0, "=\"\"+3"); // empty string
1803 m_pDoc->SetString(0, 1, 0, "=\" \"+3"); // only blank
1804 m_pDoc->SetString(0, 2, 0, "=\" 4 \"+3"); // number in blanks
1805 m_pDoc->SetString(0, 3, 0, "=\" x \"+3"); // non-numeric
1806 m_pDoc->SetString(0, 4, 0, "=\"4.4\"+3"); // locale dependent
1808 OUString aVal;
1809 ScCalcConfig aConfig;
1811 // With "Convert also locale dependent" and "Empty string as zero"=True option.
1812 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1813 aConfig.mbEmptyStringAsZero = true;
1814 m_pDoc->SetCalcConfig(aConfig);
1815 m_pDoc->CalcAll();
1816 val = m_pDoc->GetValue(0, 0, 0);
1817 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1818 val = m_pDoc->GetValue(0, 1, 0);
1819 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1820 val = m_pDoc->GetValue(0, 2, 0);
1821 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1822 aVal = m_pDoc->GetString( 0, 3, 0);
1823 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1824 val = m_pDoc->GetValue(0, 4, 0);
1825 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1827 // With "Convert also locale dependent" and "Empty string as zero"=False option.
1828 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1829 aConfig.mbEmptyStringAsZero = false;
1830 m_pDoc->SetCalcConfig(aConfig);
1831 m_pDoc->CalcAll();
1832 aVal = m_pDoc->GetString( 0, 0, 0);
1833 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1834 aVal = m_pDoc->GetString( 0, 1, 0);
1835 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1836 val = m_pDoc->GetValue(0, 2, 0);
1837 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1838 aVal = m_pDoc->GetString( 0, 3, 0);
1839 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1840 val = m_pDoc->GetValue(0, 4, 0);
1841 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1843 // With "Convert only unambiguous" and "Empty string as zero"=True option.
1844 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1845 aConfig.mbEmptyStringAsZero = true;
1846 m_pDoc->SetCalcConfig(aConfig);
1847 m_pDoc->CalcAll();
1848 val = m_pDoc->GetValue(0, 0, 0);
1849 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1850 val = m_pDoc->GetValue(0, 1, 0);
1851 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1852 val = m_pDoc->GetValue(0, 2, 0);
1853 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1854 aVal = m_pDoc->GetString( 0, 3, 0);
1855 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1856 aVal = m_pDoc->GetString( 0, 4, 0);
1857 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1859 // With "Convert only unambiguous" and "Empty string as zero"=False option.
1860 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1861 aConfig.mbEmptyStringAsZero = false;
1862 m_pDoc->SetCalcConfig(aConfig);
1863 m_pDoc->CalcAll();
1864 aVal = m_pDoc->GetString( 0, 0, 0);
1865 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1866 aVal = m_pDoc->GetString( 0, 1, 0);
1867 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1868 m_pDoc->GetValue(0, 2, 0);
1869 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1870 aVal = m_pDoc->GetString( 0, 3, 0);
1871 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1872 aVal = m_pDoc->GetString( 0, 4, 0);
1873 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1875 // With "Treat as zero" ("Empty string as zero" is ignored).
1876 aConfig.meStringConversion = ScCalcConfig::StringConversion::ZERO;
1877 aConfig.mbEmptyStringAsZero = true;
1878 m_pDoc->SetCalcConfig(aConfig);
1879 m_pDoc->CalcAll();
1880 val = m_pDoc->GetValue(0, 0, 0);
1881 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1882 val = m_pDoc->GetValue(0, 1, 0);
1883 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1884 val = m_pDoc->GetValue(0, 2, 0);
1885 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1886 val = m_pDoc->GetValue(0, 3, 0);
1887 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1888 val = m_pDoc->GetValue(0, 4, 0);
1889 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1891 // With "Generate #VALUE! error" ("Empty string as zero" is ignored).
1892 aConfig.meStringConversion = ScCalcConfig::StringConversion::ILLEGAL;
1893 aConfig.mbEmptyStringAsZero = false;
1894 m_pDoc->SetCalcConfig(aConfig);
1895 m_pDoc->CalcAll();
1896 aVal = m_pDoc->GetString( 0, 0, 0);
1897 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1898 aVal = m_pDoc->GetString( 0, 1, 0);
1899 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1900 aVal = m_pDoc->GetString( 0, 2, 0);
1901 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1902 aVal = m_pDoc->GetString( 0, 3, 0);
1903 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1904 aVal = m_pDoc->GetString( 0, 4, 0);
1905 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1907 m_pDoc->DeleteTab(0);
1910 void Test::testNamedRange()
1912 static const RangeNameDef aNames[] = {
1913 { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
1914 { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
1915 { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
1916 { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
1919 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "Sheet1"));
1921 m_pDoc->SetValue (0, 0, 0, 101);
1923 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
1924 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + SAL_N_ELEMENTS(aNames));
1925 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1926 m_pDoc->SetRangeName(std::move(pNames));
1928 ScRangeName* pNewRanges = m_pDoc->GetRangeName();
1929 CPPUNIT_ASSERT(pNewRanges);
1931 // Make sure the index lookup does the right thing.
1932 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1934 const ScRangeData* p = pNewRanges->findByIndex(aNames[i].mnIndex);
1935 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
1936 OUString aName = p->GetName();
1937 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].mpName));
1940 // Test usage in formula expression.
1941 m_pDoc->SetString (1, 0, 0, "=A1/Divisor");
1942 m_pDoc->CalcAll();
1944 double result = m_pDoc->GetValue (1, 0, 0);
1945 ASSERT_DOUBLES_EQUAL_MESSAGE ("calculation failed", 1.0, result);
1947 // Test copy-ability of range names.
1948 std::unique_ptr<ScRangeName> pCopiedRanges(new ScRangeName(*pNewRanges));
1949 m_pDoc->SetRangeName(std::move(pCopiedRanges));
1950 // Make sure the index lookup still works.
1951 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1953 const ScRangeData* p = m_pDoc->GetRangeName()->findByIndex(aNames[i].mnIndex);
1954 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
1955 OUString aName = p->GetName();
1956 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].mpName));
1959 // Test using another-sheet-local name, scope Sheet1.
1960 ScRangeData* pLocal1 = new ScRangeData( *m_pDoc, "local1", ScAddress(0,0,0));
1961 ScRangeData* pLocal2 = new ScRangeData( *m_pDoc, "local2", "$Sheet1.$A$1");
1962 ScRangeData* pLocal3 = new ScRangeData( *m_pDoc, "local3", "Sheet1.$A$1");
1963 ScRangeData* pLocal4 = new ScRangeData( *m_pDoc, "local4", "$A$1"); // implicit relative sheet reference
1964 std::unique_ptr<ScRangeName> pLocalRangeName1(new ScRangeName);
1965 pLocalRangeName1->insert(pLocal1);
1966 pLocalRangeName1->insert(pLocal2);
1967 pLocalRangeName1->insert(pLocal3);
1968 pLocalRangeName1->insert(pLocal4);
1969 m_pDoc->SetRangeName(0, std::move(pLocalRangeName1));
1971 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (1, "Sheet2"));
1973 // Use other-sheet-local name of Sheet1 on Sheet2.
1974 ScAddress aPos(1,0,1);
1975 OUString aFormula("=Sheet1.local1+Sheet1.local2+Sheet1.local3+Sheet1.local4");
1976 m_pDoc->SetString(aPos, aFormula);
1977 OUString aString = m_pDoc->GetFormula(1,0,1);
1978 CPPUNIT_ASSERT_EQUAL_MESSAGE("formula string should be equal", aFormula, aString);
1979 double fValue = m_pDoc->GetValue(aPos);
1980 ASSERT_DOUBLES_EQUAL_MESSAGE("value should be 4 times Sheet1.A1", 404.0, fValue);
1982 m_pDoc->DeleteTab(1);
1983 m_pDoc->SetRangeName(0,nullptr); // Delete the names.
1984 m_pDoc->SetRangeName(nullptr); // Delete the names.
1985 m_pDoc->DeleteTab(0);
1988 void Test::testInsertNameList()
1990 m_pDoc->InsertTab(0, "Test");
1992 static const RangeNameDef aNames[] = {
1993 { "MyRange1", "$Test.$A$1:$A$100", 1 },
1994 { "MyRange2", "$Test.$B$1:$B$100", 2 },
1995 { "MyRange3", "$Test.$C$1:$C$100", 3 }
1998 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
1999 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + SAL_N_ELEMENTS(aNames));
2000 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
2001 m_pDoc->SetRangeName(std::move(pNames));
2003 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
2004 ScAddress aPos(1,1,0);
2005 rDocFunc.InsertNameList(aPos, true);
2007 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i, aPos.IncRow())
2009 OUString aName = m_pDoc->GetString(aPos);
2010 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aNames[i].mpName), aName);
2011 ScAddress aExprPos = aPos;
2012 aExprPos.IncCol();
2013 OUString aExpr = m_pDoc->GetString(aExprPos);
2014 OUString aExpected = "=" + OUString::createFromAscii(aNames[i].mpExpr);
2015 CPPUNIT_ASSERT_EQUAL(aExpected, aExpr);
2018 m_pDoc->DeleteTab(0);
2021 void Test::testCSV()
2023 const int English = 0, European = 1;
2024 struct {
2025 const char *pStr; int eSep; bool bResult; double nValue;
2026 } aTests[] = {
2027 { "foo", English, false, 0.0 },
2028 { "1.0", English, true, 1.0 },
2029 { "1,0", English, false, 0.0 },
2030 { "1.0", European, false, 0.0 },
2031 { "1.000", European, true, 1000.0 },
2032 { "1,000", European, true, 1.0 },
2033 { "1.000", English, true, 1.0 },
2034 { "1,000", English, true, 1000.0 },
2035 { " 1.0", English, true, 1.0 },
2036 { " 1.0 ", English, true, 1.0 },
2037 { "1.0 ", European, false, 0.0 },
2038 { "1.000", European, true, 1000.0 },
2039 { "1137.999", English, true, 1137.999 },
2040 { "1.000.00", European, false, 0.0 },
2041 { "+,123", English, false, 0.0 },
2042 { "-,123", English, false, 0.0 }
2044 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
2045 OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
2046 double nValue = 0.0;
2047 bool bResult = ScStringUtil::parseSimpleNumber
2048 (aStr, aTests[i].eSep == English ? '.' : ',',
2049 aTests[i].eSep == English ? ',' : '.',
2051 nValue);
2052 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric detection failure", aTests[i].bResult, bResult);
2053 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric value failure", aTests[i].nValue, nValue);
2057 template<typename Evaluator>
2058 static void checkMatrixElements(const ScMatrix& rMat)
2060 SCSIZE nC, nR;
2061 rMat.GetDimensions(nC, nR);
2062 Evaluator aEval;
2063 for (SCSIZE i = 0; i < nC; ++i)
2065 for (SCSIZE j = 0; j < nR; ++j)
2067 aEval(i, j, rMat.Get(i, j));
2072 namespace {
2074 struct AllZeroMatrix
2076 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2078 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2079 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
2083 struct PartiallyFilledZeroMatrix
2085 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2087 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2088 if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
2090 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be 3.0", 3.0, rVal.fVal);
2092 else
2094 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
2099 struct AllEmptyMatrix
2101 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2103 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
2104 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2108 struct PartiallyFilledEmptyMatrix
2110 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2112 if (nCol == 1 && nRow == 1)
2114 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of boolean type", int(ScMatValType::Boolean), static_cast<int>(rVal.nType));
2115 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", 1.0, rVal.fVal);
2117 else if (nCol == 4 && nRow == 5)
2119 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2120 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", -12.5, rVal.fVal);
2122 else if (nCol == 8 && nRow == 2)
2124 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::String), static_cast<int>(rVal.nType));
2125 CPPUNIT_ASSERT_EQUAL_MESSAGE("element value is not what is expected", OUString("Test"), rVal.aStr.getString());
2127 else if (nCol == 8 && nRow == 11)
2129 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty path type", int(ScMatValType::EmptyPath), static_cast<int>(rVal.nType));
2130 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2132 else
2134 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
2135 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2142 void Test::testMatrix()
2144 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
2145 ScMatrixRef pMat, pMat2;
2147 // First, test the zero matrix type.
2148 pMat = new ScMatrix(0, 0, 0.0);
2149 SCSIZE nC, nR;
2150 pMat->GetDimensions(nC, nR);
2151 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nC);
2152 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nR);
2153 pMat->Resize(4, 10, 0.0);
2154 pMat->GetDimensions(nC, nR);
2155 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(4), nC);
2156 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nR);
2157 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2158 !pMat->And());
2159 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2160 !pMat->Or());
2162 // Resizing into a larger matrix should fill the void space with zeros.
2163 checkMatrixElements<AllZeroMatrix>(*pMat);
2165 pMat->FillDouble(3.0, 1, 2, 2, 8);
2166 checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
2167 CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
2168 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2169 !pMat->And());
2170 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2171 pMat->Or());
2172 pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
2173 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2174 pMat->And());
2175 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2176 pMat->Or());
2178 // Test the AND and OR evaluations.
2179 pMat = new ScMatrix(2, 2, 0.0);
2181 // Only some of the elements are non-zero.
2182 pMat->PutBoolean(true, 0, 0);
2183 pMat->PutDouble(1.0, 1, 1);
2184 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2185 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2187 // All of the elements are non-zero.
2188 pMat->PutBoolean(true, 0, 1);
2189 pMat->PutDouble(2.3, 1, 0);
2190 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2191 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2193 // Now test the empty matrix type.
2194 pMat = new ScMatrix(10, 20);
2195 pMat->GetDimensions(nC, nR);
2196 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nC);
2197 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(20), nR);
2198 checkMatrixElements<AllEmptyMatrix>(*pMat);
2200 pMat->PutBoolean(true, 1, 1);
2201 pMat->PutDouble(-12.5, 4, 5);
2202 pMat->PutString(rPool.intern("Test"), 8, 2);
2203 pMat->PutEmptyPath(8, 11);
2204 checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2206 // Test resizing.
2207 pMat = new ScMatrix(0, 0);
2208 pMat->Resize(2, 2, 1.5);
2209 pMat->PutEmpty(1, 1);
2211 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2212 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2213 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2214 CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2216 // Max and min values.
2217 pMat = new ScMatrix(2, 2, 0.0);
2218 pMat->PutDouble(-10, 0, 0);
2219 pMat->PutDouble(-12, 0, 1);
2220 pMat->PutDouble(-8, 1, 0);
2221 pMat->PutDouble(-25, 1, 1);
2222 CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
2223 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
2224 pMat->PutString(rPool.intern("Test"), 0, 0);
2225 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
2226 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
2227 pMat->PutBoolean(true, 0, 0);
2228 CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
2229 pMat = new ScMatrix(2, 2, 10.0);
2230 pMat->PutBoolean(false, 0, 0);
2231 pMat->PutDouble(12.5, 1, 1);
2232 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
2233 CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
2235 // Convert matrix into a linear double array. String elements become NaN
2236 // and empty elements become 0.
2237 pMat = new ScMatrix(3, 3);
2238 pMat->PutDouble(2.5, 0, 0);
2239 pMat->PutDouble(1.2, 0, 1);
2240 pMat->PutString(rPool.intern("A"), 1, 1);
2241 pMat->PutDouble(2.3, 2, 1);
2242 pMat->PutDouble(-20, 2, 2);
2244 static const double fNaN = std::numeric_limits<double>::quiet_NaN();
2246 std::vector<double> aDoubles;
2247 pMat->GetDoubleArray(aDoubles);
2250 const double pChecks[] = { 2.5, 1.2, 0, 0, fNaN, 0, 0, 2.3, -20 };
2251 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2252 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2254 if (std::isnan(pChecks[i]))
2255 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2256 else
2257 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2261 pMat2 = new ScMatrix(3, 3, 10.0);
2262 pMat2->PutString(rPool.intern("B"), 1, 0);
2263 pMat2->MergeDoubleArrayMultiply(aDoubles);
2266 const double pChecks[] = { 25, 12, 0, fNaN, fNaN, 0, 0, 23, -200 };
2267 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2268 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2270 if (std::isnan(pChecks[i]))
2271 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2272 else
2273 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2278 void Test::testMatrixComparisonWithErrors()
2280 m_pDoc->InsertTab(0, "foo");
2282 // Insert the source values in A1:A2.
2283 m_pDoc->SetString(0, 0, 0, "=1/0");
2284 m_pDoc->SetValue( 0, 1, 0, 1.0);
2286 // Create a matrix formula in B3:B4 referencing A1:A2 and doing a greater
2287 // than comparison on it's values. Error value must be propagated.
2288 ScMarkData aMark(m_pDoc->GetSheetLimits());
2289 aMark.SelectOneTable(0);
2290 m_pDoc->InsertMatrixFormula(1, 2, 1, 3, aMark, "=A1:A2>0");
2292 CPPUNIT_ASSERT_EQUAL(OUString("#DIV/0!"), m_pDoc->GetString(0,0,0));
2293 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue( 0,1,0));
2294 CPPUNIT_ASSERT_EQUAL(OUString("#DIV/0!"), m_pDoc->GetString(1,2,0));
2295 CPPUNIT_ASSERT_EQUAL(OUString("TRUE"), m_pDoc->GetString(1,3,0));
2297 m_pDoc->DeleteTab(0);
2300 void Test::testMatrixConditionalBooleanResult()
2302 m_pDoc->InsertTab(0, "foo");
2304 // Create matrix formulas in A1:B1,A2:B2,A3:B3,A4:B4 producing mixed
2305 // boolean and numeric results in an unformatted area.
2306 ScMarkData aMark(m_pDoc->GetSheetLimits());
2307 aMark.SelectOneTable(0);
2308 m_pDoc->InsertMatrixFormula( 0,0, 1,0, aMark, "=IF({1;0};TRUE();42)"); // {TRUE,42}
2309 m_pDoc->InsertMatrixFormula( 0,1, 1,1, aMark, "=IF({0;1};TRUE();42)"); // {42,1} aim for {42,TRUE}
2310 m_pDoc->InsertMatrixFormula( 0,2, 1,2, aMark, "=IF({1;0};42;FALSE())"); // {42,0} aim for {42,FALSE}
2311 m_pDoc->InsertMatrixFormula( 0,3, 1,3, aMark, "=IF({0;1};42;FALSE())"); // {FALSE,42}
2313 CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(0,0,0));
2314 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(1,0,0));
2315 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(0,1,0));
2316 //CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(1,1,0)); // not yet
2317 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(0,2,0));
2318 //CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(1,2,0)); // not yet
2319 CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(0,3,0));
2320 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(1,3,0));
2322 m_pDoc->DeleteTab(0);
2325 void Test::testEnterMixedMatrix()
2327 m_pDoc->InsertTab(0, "foo");
2329 // Insert the source values in A1:B2.
2330 m_pDoc->SetString(0, 0, 0, "A");
2331 m_pDoc->SetString(1, 0, 0, "B");
2332 double val = 1.0;
2333 m_pDoc->SetValue(0, 1, 0, val);
2334 val = 2.0;
2335 m_pDoc->SetValue(1, 1, 0, val);
2337 // Create a matrix range in A4:B5 referencing A1:B2.
2338 ScMarkData aMark(m_pDoc->GetSheetLimits());
2339 aMark.SelectOneTable(0);
2340 m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2");
2342 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2343 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2344 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2345 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2347 m_pDoc->DeleteTab(0);
2350 void Test::testMatrixEditable()
2352 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2354 m_pDoc->InsertTab(0, "Test");
2356 // Values in A1:B1.
2357 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2358 m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
2360 // A2 is a normal formula.
2361 m_pDoc->SetString(ScAddress(0,1,0), "=5");
2363 // A3:A4 is a matrix.
2364 ScRange aMatRange(0,2,0,0,3,0);
2365 ScMarkData aMark(m_pDoc->GetSheetLimits());
2366 aMark.SetMarkArea(aMatRange);
2367 m_pDoc->InsertMatrixFormula(0, 2, 0, 3, aMark, "=TRANSPOSE(A1:B1)");
2369 // Check their values.
2370 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2371 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2372 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2374 // Make sure A3:A4 is a matrix.
2375 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2376 CPPUNIT_ASSERT(pFC);
2377 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should be matrix origin.",
2378 ScMatrixMode::Formula, pFC->GetMatrixFlag());
2380 pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
2381 CPPUNIT_ASSERT(pFC);
2382 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should be matrix reference.",
2383 ScMatrixMode::Reference, pFC->GetMatrixFlag());
2385 // Check to make sure A3:A4 combined is editable.
2386 ScEditableTester aTester;
2387 aTester.TestSelection(*m_pDoc, aMark);
2388 CPPUNIT_ASSERT(aTester.IsEditable());
2390 m_pDoc->DeleteTab(0);
2393 void Test::testCellCopy()
2395 m_pDoc->InsertTab(0, "TestTab");
2396 ScAddress aSrc(0,0,0);
2397 ScAddress aDest(0,1,0);
2398 OUString aStr("please copy me");
2399 m_pDoc->SetString(aSrc, "please copy me");
2400 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aSrc));
2401 // copy to self - why not ?
2402 m_pDoc->CopyCellToDocument(aSrc,aDest,*m_pDoc);
2403 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aDest));
2406 void Test::testSheetCopy()
2408 m_pDoc->InsertTab(0, "TestTab");
2409 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
2410 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2412 // We need a drawing layer in order to create caption objects.
2413 m_pDoc->InitDrawLayer(m_xDocShell.get());
2415 // Insert text in A1.
2416 m_pDoc->SetString(ScAddress(0,0,0), "copy me");
2418 // Insert edit cells in B1:B3.
2419 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
2420 rEE.SetTextCurrentDefaults("Edit 1");
2421 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject());
2422 rEE.SetTextCurrentDefaults("Edit 2");
2423 m_pDoc->SetEditText(ScAddress(1,1,0), rEE.CreateTextObject());
2424 rEE.SetTextCurrentDefaults("Edit 3");
2425 m_pDoc->SetEditText(ScAddress(1,2,0), rEE.CreateTextObject());
2427 SCROW nRow1, nRow2;
2428 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2429 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2430 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2431 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2433 // insert a note
2434 ScAddress aAdrA1 (0,2,0); // empty cell content.
2435 ScPostIt *pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
2436 pNoteA1->SetText(aAdrA1, "Hello world in A3");
2438 // Copy and test the result.
2439 m_pDoc->CopyTab(0, 1);
2440 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2441 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2443 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2444 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2445 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2446 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2447 CPPUNIT_ASSERT_MESSAGE("There should be note on A3 in new sheet", m_pDoc->HasNote(ScAddress(0,2,1)));
2448 CPPUNIT_ASSERT_EQUAL(OUString("copy me"), m_pDoc->GetString(ScAddress(0,0,1)));
2450 // Check the copied edit cells.
2451 const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(1,0,1));
2452 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B1.", pEditObj);
2453 CPPUNIT_ASSERT_EQUAL(OUString("Edit 1"), pEditObj->GetText(0));
2454 pEditObj = m_pDoc->GetEditText(ScAddress(1,1,1));
2455 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B2.", pEditObj);
2456 CPPUNIT_ASSERT_EQUAL(OUString("Edit 2"), pEditObj->GetText(0));
2457 pEditObj = m_pDoc->GetEditText(ScAddress(1,2,1));
2458 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B3.", pEditObj);
2459 CPPUNIT_ASSERT_EQUAL(OUString("Edit 3"), pEditObj->GetText(0));
2461 m_pDoc->DeleteTab(1);
2463 m_pDoc->SetRowHidden(5, 10, 0, true);
2464 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2465 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2466 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2467 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2468 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2469 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2470 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2471 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2472 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2473 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2474 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2475 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2477 // Copy the sheet once again.
2478 m_pDoc->CopyTab(0, 1);
2479 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2480 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2481 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2482 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2483 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2484 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2485 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2486 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2487 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2488 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2489 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2490 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2491 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2492 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2493 m_pDoc->DeleteTab(1);
2494 m_pDoc->DeleteTab(0);
2497 void Test::testSheetMove()
2499 m_pDoc->InsertTab(0, "TestTab1");
2500 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2501 SCROW nRow1, nRow2;
2502 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2503 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2504 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2505 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2507 //test if inserting before another sheet works
2508 m_pDoc->InsertTab(0, "TestTab2");
2509 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2510 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2511 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2512 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2513 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2515 // Move and test the result.
2516 m_pDoc->MoveTab(0, 1);
2517 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2518 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2519 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2520 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2521 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2522 OUString aName;
2523 m_pDoc->GetName(0, aName);
2524 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", OUString("TestTab1"), aName);
2526 m_pDoc->SetRowHidden(5, 10, 0, true);
2527 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2528 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2529 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2530 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2531 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2532 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2533 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2534 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2535 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2536 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2537 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2538 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2540 // Move the sheet once again.
2541 m_pDoc->MoveTab(1, 0);
2542 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2543 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2544 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2545 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2546 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2547 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2548 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2549 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2550 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2551 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2552 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2553 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2554 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2555 m_pDoc->GetName(0, aName);
2556 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", OUString("TestTab2"), aName);
2557 m_pDoc->DeleteTab(1);
2558 m_pDoc->DeleteTab(0);
2561 void Test::testDataArea()
2563 m_pDoc->InsertTab(0, "Data");
2565 // Totally empty sheet should be rightfully considered empty in all accounts.
2566 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 100, 100, 0));
2567 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2569 // Now, set borders in some cells...
2570 ::editeng::SvxBorderLine aLine(nullptr, 50, SvxBorderLineStyle::SOLID);
2571 SvxBoxItem aBorderItem(ATTR_BORDER);
2572 aBorderItem.SetLine(&aLine, SvxBoxItemLine::LEFT);
2573 aBorderItem.SetLine(&aLine, SvxBoxItemLine::RIGHT);
2574 for (SCROW i = 0; i < 100; ++i)
2575 // Set borders from row 1 to 100.
2576 m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
2578 // Now the sheet is considered non-empty for printing purposes, but still
2579 // be empty in all the other cases.
2580 CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
2581 !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
2582 CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
2583 m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2585 // Adding a real cell content should turn the block non-empty.
2586 m_pDoc->SetString(0, 0, 0, "Some text");
2587 CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
2588 !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2590 // TODO: Add more tests for normal data area calculation.
2592 m_pDoc->DeleteTab(0);
2595 void Test::testStreamValid()
2597 m_pDoc->InsertTab(0, "Sheet1");
2598 m_pDoc->InsertTab(1, "Sheet2");
2599 m_pDoc->InsertTab(2, "Sheet3");
2600 m_pDoc->InsertTab(3, "Sheet4");
2601 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", static_cast<SCTAB>(4), m_pDoc->GetTableCount());
2603 OUString a1("A1");
2604 OUString a2("A2");
2605 OUString test;
2607 // Put values into Sheet1.
2608 m_pDoc->SetString(0, 0, 0, a1);
2609 m_pDoc->SetString(0, 1, 0, a2);
2610 test = m_pDoc->GetString(0, 0, 0);
2611 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A1", test, a1);
2612 test = m_pDoc->GetString(0, 1, 0);
2613 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A2", test, a2);
2615 // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
2616 m_pDoc->SetString(0, 0, 1, "=Sheet1.A1");
2617 m_pDoc->SetString(0, 1, 1, "=Sheet1.A2");
2618 m_pDoc->SetString(0, 0, 2, "=Sheet1.A1");
2619 m_pDoc->SetString(0, 0, 3, "=Sheet1.A2");
2621 test = m_pDoc->GetString(0, 0, 1);
2622 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A1", test, a1);
2623 test = m_pDoc->GetString(0, 1, 1);
2624 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A2", test, a2);
2625 test = m_pDoc->GetString(0, 0, 2);
2626 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a1);
2627 test = m_pDoc->GetString(0, 0, 3);
2628 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a2);
2630 // Set all sheet streams valid after all the initial cell values are in
2631 // place. In reality we need to have real XML streams stored in order to
2632 // claim they are valid, but we are just testing the flag values here.
2633 m_pDoc->SetStreamValid(0, true);
2634 m_pDoc->SetStreamValid(1, true);
2635 m_pDoc->SetStreamValid(2, true);
2636 m_pDoc->SetStreamValid(3, true);
2637 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
2638 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
2639 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
2640 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
2642 // Now, insert a new row at row 2 position on Sheet1. This will move cell
2643 // A2 downward but cell A1 remains unmoved.
2644 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 2);
2645 test = m_pDoc->GetString(0, 0, 0);
2646 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell A1 should not have moved.", test, a1);
2647 test = m_pDoc->GetString(0, 3, 0);
2648 CPPUNIT_ASSERT_EQUAL_MESSAGE("the old cell A2 should now be at A4.", test, a2);
2649 ScRefCellValue aCell;
2650 aCell.assign(*m_pDoc, ScAddress(0,1,0));
2651 CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
2652 aCell.assign(*m_pDoc, ScAddress(0,2,0));
2653 CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
2655 // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
2656 // invalidated, whereas Sheet3's stream should still be valid.
2657 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
2658 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
2659 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
2660 CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
2662 m_pDoc->DeleteTab(3);
2663 m_pDoc->DeleteTab(2);
2664 m_pDoc->DeleteTab(1);
2665 m_pDoc->DeleteTab(0);
2668 void Test::testFunctionLists()
2670 const char* aDataBase[] = {
2671 "DAVERAGE",
2672 "DCOUNT",
2673 "DCOUNTA",
2674 "DGET",
2675 "DMAX",
2676 "DMIN",
2677 "DPRODUCT",
2678 "DSTDEV",
2679 "DSTDEVP",
2680 "DSUM",
2681 "DVAR",
2682 "DVARP",
2683 nullptr
2686 const char* aDateTime[] = {
2687 "DATE",
2688 "DATEDIF",
2689 "DATEVALUE",
2690 "DAY",
2691 "DAYS",
2692 "DAYS360",
2693 "DAYSINMONTH",
2694 "DAYSINYEAR",
2695 "EASTERSUNDAY",
2696 "HOUR",
2697 "ISLEAPYEAR",
2698 "ISOWEEKNUM",
2699 "MINUTE",
2700 "MONTH",
2701 "MONTHS",
2702 "NETWORKDAYS",
2703 "NETWORKDAYS.INTL",
2704 "NOW",
2705 "SECOND",
2706 "TIME",
2707 "TIMEVALUE",
2708 "TODAY",
2709 "WEEKDAY",
2710 "WEEKNUM",
2711 "WEEKNUM_OOO",
2712 "WEEKS",
2713 "WEEKSINYEAR",
2714 "WORKDAY.INTL",
2715 "YEAR",
2716 "YEARS",
2717 nullptr
2720 const char* aFinancial[] = {
2721 "CUMIPMT",
2722 "CUMPRINC",
2723 "DB",
2724 "DDB",
2725 "EFFECT",
2726 "FV",
2727 "IPMT",
2728 "IRR",
2729 "ISPMT",
2730 "MIRR",
2731 "NOMINAL",
2732 "NPER",
2733 "NPV",
2734 "OPT_BARRIER",
2735 "OPT_PROB_HIT",
2736 "OPT_PROB_INMONEY",
2737 "OPT_TOUCH",
2738 "PDURATION",
2739 "PMT",
2740 "PPMT",
2741 "PV",
2742 "RATE",
2743 "RRI",
2744 "SLN",
2745 "SYD",
2746 "VDB",
2747 nullptr
2750 const char* aInformation[] = {
2751 "CELL",
2752 "CURRENT",
2753 "FORMULA",
2754 "INFO",
2755 "ISBLANK",
2756 "ISERR",
2757 "ISERROR",
2758 "ISEVEN",
2759 "ISFORMULA",
2760 "ISLOGICAL",
2761 "ISNA",
2762 "ISNONTEXT",
2763 "ISNUMBER",
2764 "ISODD",
2765 "ISREF",
2766 "ISTEXT",
2767 "N",
2768 "NA",
2769 "TYPE",
2770 nullptr
2773 const char* aLogical[] = {
2774 "AND",
2775 "FALSE",
2776 "IF",
2777 "IFERROR",
2778 "IFNA",
2779 "IFS",
2780 "NOT",
2781 "OR",
2782 "SWITCH",
2783 "TRUE",
2784 "XOR",
2785 nullptr
2788 const char* aMathematical[] = {
2789 "ABS",
2790 "ACOS",
2791 "ACOSH",
2792 "ACOT",
2793 "ACOTH",
2794 "AGGREGATE",
2795 "ASIN",
2796 "ASINH",
2797 "ATAN",
2798 "ATAN2",
2799 "ATANH",
2800 "BITAND",
2801 "BITLSHIFT",
2802 "BITOR",
2803 "BITRSHIFT",
2804 "BITXOR",
2805 "CEILING",
2806 "CEILING.MATH",
2807 "CEILING.PRECISE",
2808 "CEILING.XCL",
2809 "COLOR",
2810 "COMBIN",
2811 "COMBINA",
2812 "CONVERT_OOO",
2813 "COS",
2814 "COSH",
2815 "COT",
2816 "COTH",
2817 "CSC",
2818 "CSCH",
2819 "DEGREES",
2820 "EUROCONVERT",
2821 "EVEN",
2822 "EXP",
2823 "FACT",
2824 "FLOOR",
2825 "FLOOR.MATH",
2826 "FLOOR.PRECISE",
2827 "FLOOR.XCL",
2828 "GCD",
2829 "INT",
2830 "ISO.CEILING",
2831 "LCM",
2832 "LN",
2833 "LOG",
2834 "LOG10",
2835 "MOD",
2836 "ODD",
2837 "PI",
2838 "POWER",
2839 "PRODUCT",
2840 "RADIANS",
2841 "RAND",
2842 "RAND.NV",
2843 "RANDBETWEEN.NV",
2844 "RAWSUBTRACT",
2845 "ROUND",
2846 "ROUNDDOWN",
2847 "ROUNDSIG",
2848 "ROUNDUP",
2849 "SEC",
2850 "SECH",
2851 "SIGN",
2852 "SIN",
2853 "SINH",
2854 "SQRT",
2855 "SUBTOTAL",
2856 "SUM",
2857 "SUMIF",
2858 "SUMIFS",
2859 "SUMSQ",
2860 "TAN",
2861 "TANH",
2862 "TRUNC",
2863 nullptr
2866 const char* aArray[] = {
2867 "FOURIER",
2868 "FREQUENCY",
2869 "GROWTH",
2870 "LINEST",
2871 "LOGEST",
2872 "MDETERM",
2873 "MINVERSE",
2874 "MMULT",
2875 "MUNIT",
2876 "SUMPRODUCT",
2877 "SUMX2MY2",
2878 "SUMX2PY2",
2879 "SUMXMY2",
2880 "TRANSPOSE",
2881 "TREND",
2882 nullptr
2885 const char* aStatistical[] = {
2886 "AVEDEV",
2887 "AVERAGE",
2888 "AVERAGEA",
2889 "AVERAGEIF",
2890 "AVERAGEIFS",
2891 "B",
2892 "BETA.DIST",
2893 "BETA.INV",
2894 "BETADIST",
2895 "BETAINV",
2896 "BINOM.DIST",
2897 "BINOM.INV",
2898 "BINOMDIST",
2899 "CHIDIST",
2900 "CHIINV",
2901 "CHISQ.DIST",
2902 "CHISQ.DIST.RT",
2903 "CHISQ.INV",
2904 "CHISQ.INV.RT",
2905 "CHISQ.TEST",
2906 "CHISQDIST",
2907 "CHISQINV",
2908 "CHITEST",
2909 "CONFIDENCE",
2910 "CONFIDENCE.NORM",
2911 "CONFIDENCE.T",
2912 "CORREL",
2913 "COUNT",
2914 "COUNTA",
2915 "COUNTBLANK",
2916 "COUNTIF",
2917 "COUNTIFS",
2918 "COVAR",
2919 "COVARIANCE.P",
2920 "COVARIANCE.S",
2921 "CRITBINOM",
2922 "DEVSQ",
2923 "ERF.PRECISE",
2924 "ERFC.PRECISE",
2925 "EXPON.DIST",
2926 "EXPONDIST",
2927 "F.DIST",
2928 "F.DIST.RT",
2929 "F.INV",
2930 "F.INV.RT",
2931 "F.TEST",
2932 "FDIST",
2933 "FINV",
2934 "FISHER",
2935 "FISHERINV",
2936 "FORECAST",
2937 "FORECAST.ETS.ADD",
2938 "FORECAST.ETS.MULT",
2939 "FORECAST.ETS.PI.ADD",
2940 "FORECAST.ETS.PI.MULT",
2941 "FORECAST.ETS.SEASONALITY",
2942 "FORECAST.ETS.STAT.ADD",
2943 "FORECAST.ETS.STAT.MULT",
2944 "FORECAST.LINEAR",
2945 "FTEST",
2946 "GAMMA",
2947 "GAMMA.DIST",
2948 "GAMMA.INV",
2949 "GAMMADIST",
2950 "GAMMAINV",
2951 "GAMMALN",
2952 "GAMMALN.PRECISE",
2953 "GAUSS",
2954 "GEOMEAN",
2955 "HARMEAN",
2956 "HYPGEOM.DIST",
2957 "HYPGEOMDIST",
2958 "INTERCEPT",
2959 "KURT",
2960 "LARGE",
2961 "LOGINV",
2962 "LOGNORM.DIST",
2963 "LOGNORM.INV",
2964 "LOGNORMDIST",
2965 "MAX",
2966 "MAXA",
2967 "MAXIFS",
2968 "MEDIAN",
2969 "MIN",
2970 "MINA",
2971 "MINIFS",
2972 "MODE",
2973 "MODE.MULT",
2974 "MODE.SNGL",
2975 "NEGBINOM.DIST",
2976 "NEGBINOMDIST",
2977 "NORM.DIST",
2978 "NORM.INV",
2979 "NORM.S.DIST",
2980 "NORM.S.INV",
2981 "NORMDIST",
2982 "NORMINV",
2983 "NORMSDIST",
2984 "NORMSINV",
2985 "PEARSON",
2986 "PERCENTILE",
2987 "PERCENTILE.EXC",
2988 "PERCENTILE.INC",
2989 "PERCENTRANK",
2990 "PERCENTRANK.EXC",
2991 "PERCENTRANK.INC",
2992 "PERMUT",
2993 "PERMUTATIONA",
2994 "PHI",
2995 "POISSON",
2996 "POISSON.DIST",
2997 "PROB",
2998 "QUARTILE",
2999 "QUARTILE.EXC",
3000 "QUARTILE.INC",
3001 "RANK",
3002 "RANK.AVG",
3003 "RANK.EQ",
3004 "RSQ",
3005 "SKEW",
3006 "SKEWP",
3007 "SLOPE",
3008 "SMALL",
3009 "STANDARDIZE",
3010 "STDEV",
3011 "STDEV.P",
3012 "STDEV.S",
3013 "STDEVA",
3014 "STDEVP",
3015 "STDEVPA",
3016 "STEYX",
3017 "T.DIST",
3018 "T.DIST.2T",
3019 "T.DIST.RT",
3020 "T.INV",
3021 "T.INV.2T",
3022 "T.TEST",
3023 "TDIST",
3024 "TINV",
3025 "TRIMMEAN",
3026 "TTEST",
3027 "VAR",
3028 "VAR.P",
3029 "VAR.S",
3030 "VARA",
3031 "VARP",
3032 "VARPA",
3033 "WEIBULL",
3034 "WEIBULL.DIST",
3035 "Z.TEST",
3036 "ZTEST",
3037 nullptr
3040 const char* aSpreadsheet[] = {
3041 "ADDRESS",
3042 "AREAS",
3043 "CHOOSE",
3044 "COLUMN",
3045 "COLUMNS",
3046 "DDE",
3047 "ERROR.TYPE",
3048 "ERRORTYPE",
3049 "GETPIVOTDATA",
3050 "HLOOKUP",
3051 "HYPERLINK",
3052 "INDEX",
3053 "INDIRECT",
3054 "LOOKUP",
3055 "MATCH",
3056 "OFFSET",
3057 "ROW",
3058 "ROWS",
3059 "SHEET",
3060 "SHEETS",
3061 "STYLE",
3062 "VLOOKUP",
3063 nullptr
3066 const char* aText[] = {
3067 "ARABIC",
3068 "ASC",
3069 "BAHTTEXT",
3070 "BASE",
3071 "CHAR",
3072 "CLEAN",
3073 "CODE",
3074 "CONCAT",
3075 "CONCATENATE",
3076 "DECIMAL",
3077 "DOLLAR",
3078 "ENCODEURL",
3079 "EXACT",
3080 "FILTERXML",
3081 "FIND",
3082 "FINDB",
3083 "FIXED",
3084 "JIS",
3085 "LEFT",
3086 "LEFTB",
3087 "LEN",
3088 "LENB",
3089 "LOWER",
3090 "MID",
3091 "MIDB",
3092 "NUMBERVALUE",
3093 "PROPER",
3094 "REGEX",
3095 "REPLACE",
3096 "REPLACEB",
3097 "REPT",
3098 "RIGHT",
3099 "RIGHTB",
3100 "ROMAN",
3101 "ROT13",
3102 "SEARCH",
3103 "SEARCHB",
3104 "SUBSTITUTE",
3105 "T",
3106 "TEXT",
3107 "TEXTJOIN",
3108 "TRIM",
3109 "UNICHAR",
3110 "UNICODE",
3111 "UPPER",
3112 "VALUE",
3113 "WEBSERVICE",
3114 nullptr
3117 struct {
3118 const char* Category; const char** Functions;
3119 } aTests[] = {
3120 { "Database", aDataBase },
3121 { "Date&Time", aDateTime },
3122 { "Financial", aFinancial },
3123 { "Information", aInformation },
3124 { "Logical", aLogical },
3125 { "Mathematical", aMathematical },
3126 { "Array", aArray },
3127 { "Statistical", aStatistical },
3128 { "Spreadsheet", aSpreadsheet },
3129 { "Text", aText },
3130 { "Add-in", nullptr },
3131 { nullptr, nullptr }
3134 ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
3135 sal_uInt32 n = pFuncMgr->getCount();
3136 for (sal_uInt32 i = 0; i < n; ++i)
3138 const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
3139 CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
3140 sal_uInt32 nFuncCount = pCat->getCount();
3141 for (sal_uInt32 j = 0; j < nFuncCount; ++j)
3143 const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
3144 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
3149 void Test::testGraphicsInGroup()
3151 m_pDoc->InsertTab(0, "TestTab");
3152 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
3153 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
3154 SCROW nRow1, nRow2;
3155 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3156 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
3157 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
3158 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
3160 m_pDoc->InitDrawLayer();
3161 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
3162 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
3163 SdrPage* pPage = pDrawLayer->GetPage(0);
3164 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
3167 //Add a square
3168 tools::Rectangle aOrigRect(2,2,100,100);
3169 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aOrigRect);
3170 pPage->InsertObject(pObj.get());
3171 const tools::Rectangle &rNewRect = pObj->GetLogicRect();
3172 CPPUNIT_ASSERT_EQUAL_MESSAGE("must have equal position and size",
3173 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3175 ScDrawLayer::SetPageAnchored(*pObj);
3177 //Use a range of rows guaranteed to include all of the square
3178 m_pDoc->ShowRows(0, 100, 0, false);
3179 m_pDoc->SetDrawPageSize(0);
3180 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3181 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3182 m_pDoc->ShowRows(0, 100, 0, true);
3183 m_pDoc->SetDrawPageSize(0);
3184 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3185 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3187 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
3188 CPPUNIT_ASSERT_EQUAL_MESSAGE("That shouldn't change size or positioning",
3189 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3191 m_pDoc->ShowRows(0, 100, 0, false);
3192 m_pDoc->SetDrawPageSize(0);
3194 CPPUNIT_ASSERT_EQUAL_MESSAGE("Hiding should not change the logic rectangle",
3195 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3196 CPPUNIT_ASSERT_MESSAGE("Hiding should make invisible", !pObj->IsVisible());
3198 m_pDoc->ShowRows(0, 100, 0, true);
3199 m_pDoc->SetDrawPageSize(0);
3200 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when cell anchored",
3201 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3202 CPPUNIT_ASSERT_MESSAGE("Show should make visible", pObj->IsVisible());
3206 // Add a circle.
3207 tools::Rectangle aOrigRect(10,10,210,210); // 200 x 200
3208 rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
3209 pPage->InsertObject(pObj.get());
3210 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3211 CPPUNIT_ASSERT_EQUAL_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
3212 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3214 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3215 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell anchored. Not good.",
3216 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3218 // Insert 2 rows at the top. This should push the circle object down.
3219 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3220 m_pDoc->SetDrawPageSize(0);
3222 // Make sure the size of the circle is still identical.
3223 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of the circle has changed, but shouldn't!",
3224 aOrigRect.GetSize(), rNewRect.GetSize());
3226 // Delete 2 rows at the top. This should bring the circle object to its original position.
3227 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3228 m_pDoc->SetDrawPageSize(0);
3229 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to move back to its original position.",
3230 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3234 // Add a line.
3235 basegfx::B2DPolygon aTempPoly;
3236 Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
3237 tools::Rectangle aOrigRect(10,200,110,300); // 100 x 100
3238 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
3239 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
3240 rtl::Reference<SdrPathObj> pObj = new SdrPathObj(*pDrawLayer, SdrObjKind::Line, basegfx::B2DPolyPolygon(aTempPoly));
3241 pObj->NbcSetLogicRect(aOrigRect);
3242 pPage->InsertObject(pObj.get());
3243 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3244 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size differ.",
3245 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3247 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3248 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell-anchored. Not good.",
3249 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3251 // Insert 2 rows at the top and delete them immediately.
3252 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3253 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3254 m_pDoc->SetDrawPageSize(0);
3255 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of a line object changed after row insertion and removal.",
3256 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3258 sal_Int32 n = pObj->GetPointCount();
3259 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 2 points in a line object.", static_cast<sal_Int32>(2), n);
3260 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3261 aStartPos, pObj->GetPoint(0));
3262 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3263 aEndPos, pObj->GetPoint(1));
3266 m_pDoc->DeleteTab(0);
3269 void Test::testGraphicsOnSheetMove()
3271 m_pDoc->InsertTab(0, "Tab1");
3272 m_pDoc->InsertTab(1, "Tab2");
3273 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 2 sheets to begin with", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
3275 m_pDoc->InitDrawLayer();
3276 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
3277 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
3278 SdrPage* pPage = pDrawLayer->GetPage(0);
3279 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
3281 // Insert an object.
3282 tools::Rectangle aObjRect(2,2,100,100);
3283 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
3284 pPage->InsertObject(pObj.get());
3285 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3287 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", static_cast<size_t>(1), pPage->GetObjCount());
3289 const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
3290 CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
3291 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3292 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3294 pPage = pDrawLayer->GetPage(1);
3295 CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
3296 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", static_cast<size_t>(0), pPage->GetObjCount());
3298 // Insert a new sheet at left-end, and make sure the object has moved to
3299 // the 2nd page.
3300 m_pDoc->InsertTab(0, "NewTab");
3301 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", static_cast<SCTAB>(3), m_pDoc->GetTableCount());
3302 pPage = pDrawLayer->GetPage(0);
3303 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3304 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3305 pPage = pDrawLayer->GetPage(1);
3306 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3307 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3308 pPage = pDrawLayer->GetPage(2);
3309 CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage);
3310 CPPUNIT_ASSERT_EQUAL_MESSAGE("3rd sheet should have no object.", size_t(0), pPage->GetObjCount());
3312 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3313 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3315 // Now, delete the sheet that just got inserted. The object should be back
3316 // on the 1st sheet.
3317 m_pDoc->DeleteTab(0);
3318 pPage = pDrawLayer->GetPage(0);
3319 CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage);
3320 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have one object.", size_t(1), pPage->GetObjCount());
3321 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size and position of the object shouldn't change.",
3322 aObjRect, pObj->GetLogicRect());
3324 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3325 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3327 // Move the 1st sheet to the last position.
3328 m_pDoc->MoveTab(0, 1);
3329 pPage = pDrawLayer->GetPage(0);
3330 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3331 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3332 pPage = pDrawLayer->GetPage(1);
3333 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3334 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3335 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3336 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3338 // Copy the 2nd sheet, which has one drawing object to the last position.
3339 m_pDoc->CopyTab(1, 2);
3340 pPage = pDrawLayer->GetPage(2);
3341 CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage);
3342 CPPUNIT_ASSERT_EQUAL_MESSAGE("Copied sheet should have one object.", size_t(1), pPage->GetObjCount());
3343 pObj = pPage->GetObj(0);
3344 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
3345 pData = ScDrawLayer::GetObjData(pObj.get());
3346 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
3347 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maStart.Tab());
3348 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maEnd.Tab());
3350 m_pDoc->DeleteTab(2);
3351 m_pDoc->DeleteTab(1);
3352 m_pDoc->DeleteTab(0);
3355 void Test::testToggleRefFlag()
3357 // In this test, there is no need to insert formula string into a cell in
3358 // the document, as ScRefFinder does not depend on the content of the
3359 // document except for the sheet names.
3361 m_pDoc->InsertTab(0, "Test");
3364 // Calc A1: basic 2D reference
3366 OUString aFormula("=B100");
3367 ScAddress aPos(1, 5, 0);
3368 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3370 // Original
3371 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3373 // column relative / row relative -> column absolute / row absolute
3374 aFinder.ToggleRel(0, aFormula.getLength());
3375 aFormula = aFinder.GetText();
3376 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=$B$100"), aFormula );
3378 // column absolute / row absolute -> column relative / row absolute
3379 aFinder.ToggleRel(0, aFormula.getLength());
3380 aFormula = aFinder.GetText();
3381 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=B$100"), aFormula );
3383 // column relative / row absolute -> column absolute / row relative
3384 aFinder.ToggleRel(0, aFormula.getLength());
3385 aFormula = aFinder.GetText();
3386 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=$B100"), aFormula );
3388 // column absolute / row relative -> column relative / row relative
3389 aFinder.ToggleRel(0, aFormula.getLength());
3390 aFormula = aFinder.GetText();
3391 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=B100"), aFormula );
3395 // Excel R1C1: basic 2D reference
3397 OUString aFormula("=R2C1");
3398 ScAddress aPos(3, 5, 0);
3399 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3401 // Original
3402 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3404 // column absolute / row absolute -> column relative / row absolute
3405 aFinder.ToggleRel(0, aFormula.getLength());
3406 aFormula = aFinder.GetText();
3407 CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
3409 // column relative / row absolute - > column absolute / row relative
3410 aFinder.ToggleRel(0, aFormula.getLength());
3411 aFormula = aFinder.GetText();
3412 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
3414 // column absolute / row relative -> column relative / row relative
3415 aFinder.ToggleRel(0, aFormula.getLength());
3416 aFormula = aFinder.GetText();
3417 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
3419 // column relative / row relative -> column absolute / row absolute
3420 aFinder.ToggleRel(0, aFormula.getLength());
3421 aFormula = aFinder.GetText();
3422 CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
3426 // Excel R1C1: Selection at the end of the formula string and does not
3427 // overlap the formula string at all (inspired by fdo#39135).
3428 OUString aFormula("=R1C1");
3429 ScAddress aPos(1, 1, 0);
3430 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3432 // Original
3433 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3435 // Make the column relative.
3436 sal_Int32 n = aFormula.getLength();
3437 aFinder.ToggleRel(n, n);
3438 aFormula = aFinder.GetText();
3439 CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
3441 // Make the row relative.
3442 n = aFormula.getLength();
3443 aFinder.ToggleRel(n, n);
3444 aFormula = aFinder.GetText();
3445 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
3447 // Make both relative.
3448 n = aFormula.getLength();
3449 aFinder.ToggleRel(n, n);
3450 aFormula = aFinder.GetText();
3451 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
3453 // Back to the original.
3454 n = aFormula.getLength();
3455 aFinder.ToggleRel(n, n);
3456 aFormula = aFinder.GetText();
3457 CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
3461 // Calc A1:
3462 OUString aFormula("=A1+4");
3463 ScAddress aPos(1, 1, 0);
3464 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3466 // Original
3467 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3469 // Set the cursor over the 'A1' part and toggle.
3470 aFinder.ToggleRel(2, 2);
3471 aFormula = aFinder.GetText();
3472 CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
3474 aFinder.ToggleRel(2, 2);
3475 aFormula = aFinder.GetText();
3476 CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
3478 aFinder.ToggleRel(2, 2);
3479 aFormula = aFinder.GetText();
3480 CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
3482 aFinder.ToggleRel(2, 2);
3483 aFormula = aFinder.GetText();
3484 CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
3487 // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
3488 // partial selection within formula string.
3490 m_pDoc->DeleteTab(0);
3493 void Test::testAutofilter()
3495 m_pDoc->InsertTab( 0, "Test" );
3497 // cell contents (0 = empty cell)
3498 const char* aData[][3] = {
3499 { "C1", "C2", "C3" },
3500 { "0", "1", "A" },
3501 { "1", "2", nullptr },
3502 { "1", "2", "B" },
3503 { "0", "2", "B" }
3506 SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
3507 SCROW nRows = SAL_N_ELEMENTS(aData);
3509 // Populate cells.
3510 for (SCROW i = 0; i < nRows; ++i)
3511 for (SCCOL j = 0; j < nCols; ++j)
3512 if (aData[i][j])
3513 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
3515 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols-1, nRows-1);
3516 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3518 pDBData->SetAutoFilter(true);
3519 ScRange aRange;
3520 pDBData->GetArea(aRange);
3521 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3522 aRange.aEnd.Col(), aRange.aStart.Row(),
3523 aRange.aStart.Tab(), ScMF::Auto);
3525 //create the query param
3526 ScQueryParam aParam;
3527 pDBData->GetQueryParam(aParam);
3528 ScQueryEntry& rEntry = aParam.GetEntry(0);
3529 rEntry.bDoQuery = true;
3530 rEntry.nField = 0;
3531 rEntry.eOp = SC_EQUAL;
3532 rEntry.GetQueryItem().mfVal = 0;
3533 // add queryParam to database range.
3534 pDBData->SetQueryParam(aParam);
3536 // perform the query.
3537 m_pDoc->Query(0, aParam, true);
3539 //control output
3540 SCROW nRow1, nRow2;
3541 bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3542 CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden);
3543 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(2), nRow1);
3544 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(3), nRow2);
3546 // Remove filtering.
3547 rEntry.Clear();
3548 m_pDoc->Query(0, aParam, true);
3549 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3550 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden);
3551 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3552 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3554 // Filter for non-empty cells by column C.
3555 rEntry.bDoQuery = true;
3556 rEntry.nField = 2;
3557 rEntry.SetQueryByNonEmpty();
3558 m_pDoc->Query(0, aParam, true);
3560 // only row 3 should be hidden. The rest should be visible.
3561 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3562 CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden);
3563 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(0), nRow1);
3564 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(1), nRow2);
3565 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3566 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden);
3567 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow1);
3568 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow2);
3569 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3570 CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden);
3571 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", SCROW(3), nRow1);
3572 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", m_pDoc->MaxRow(), nRow2);
3574 // Now, filter for empty cells by column C.
3575 rEntry.SetQueryByEmpty();
3576 m_pDoc->Query(0, aParam, true);
3578 // Now, only row 1 and 3, and 6 and down should be visible.
3579 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3580 CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden);
3581 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow1);
3582 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow2);
3583 bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
3584 CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden);
3585 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow1);
3586 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow2);
3587 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3588 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden);
3589 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow1);
3590 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow2);
3591 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3592 CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden);
3593 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(3), nRow1);
3594 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(4), nRow2);
3595 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3596 CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden);
3597 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", SCROW(5), nRow1);
3598 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", m_pDoc->MaxRow(), nRow2);
3600 m_pDoc->DeleteTab(0);
3603 void Test::testAutoFilterTimeValue()
3605 m_pDoc->InsertTab(0, "Test");
3607 m_pDoc->SetString(ScAddress(0,0,0), "Hours");
3608 m_pDoc->SetValue(ScAddress(0,1,0), 72.3604166666671);
3609 m_pDoc->SetValue(ScAddress(0,2,0), 265);
3611 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 0, 2);
3612 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3614 // Apply the "hour:minute:second" format to A2:A3.
3615 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3616 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_TIME_HH_MMSS, LANGUAGE_ENGLISH_US);
3617 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3618 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3619 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3621 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 2, 0, aNewAttrs); // apply it to A2:A3.
3623 printRange(m_pDoc, ScRange(0,0,0,0,2,0), "Data"); // A1:A3
3625 // Make sure the hour:minute:second format is really applied.
3626 CPPUNIT_ASSERT_EQUAL(OUString("1736:39:00"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3627 CPPUNIT_ASSERT_EQUAL(OUString("6360:00:00"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3629 // Filter by the A2 value. Only A1 and A2 should be visible.
3630 ScQueryParam aParam;
3631 pDBData->GetQueryParam(aParam);
3632 ScQueryEntry& rEntry = aParam.GetEntry(0);
3633 rEntry.bDoQuery = true;
3634 rEntry.nField = 0;
3635 rEntry.eOp = SC_EQUAL;
3636 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern("1736:39:00");
3637 rEntry.GetQueryItem().meType = ScQueryEntry::ByString;
3639 pDBData->SetQueryParam(aParam);
3641 // perform the query.
3642 m_pDoc->Query(0, aParam, true);
3644 // A1:A2 should be visible while A3 should be filtered out.
3645 CPPUNIT_ASSERT_MESSAGE("A1 should be visible.", !m_pDoc->RowFiltered(0,0));
3646 CPPUNIT_ASSERT_MESSAGE("A2 should be visible.", !m_pDoc->RowFiltered(1,0));
3647 CPPUNIT_ASSERT_MESSAGE("A3 should be filtered out.", m_pDoc->RowFiltered(2,0));
3649 m_pDoc->DeleteTab(0);
3652 void Test::testAutofilterOptimizations()
3654 m_pDoc->InsertTab( 0, "Test" );
3656 constexpr SCCOL nCols = 4;
3657 constexpr SCROW nRows = 200;
3658 m_pDoc->SetString(0, 0, 0, "Column1");
3659 m_pDoc->SetString(1, 0, 0, "Column2");
3660 m_pDoc->SetString(2, 0, 0, "Column3");
3661 m_pDoc->SetString(3, 0, 0, "Column4");
3663 // Fill 1st column with 0-199, 2nd with 1-200, 3rd with "1000"-"1199", 4th with "1001-1200"
3664 // (the pairs are off by one to each other to check filtering out a value filters out
3665 // only the relevant column).
3666 for(SCROW i = 0; i < nRows; ++i)
3668 m_pDoc->SetValue(0, i + 1, 0, i);
3669 m_pDoc->SetValue(1, i + 1, 0, i+1);
3670 m_pDoc->SetString(2, i + 1, 0, "val" + OUString::number(i+1000));
3671 m_pDoc->SetString(3, i + 1, 0, "val" + OUString::number(i+1000+1));
3674 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols, nRows);
3675 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3677 pDBData->SetAutoFilter(true);
3678 ScRange aRange;
3679 pDBData->GetArea(aRange);
3680 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3681 aRange.aEnd.Col(), aRange.aStart.Row(),
3682 aRange.aStart.Tab(), ScMF::Auto);
3684 //create the query param
3685 ScQueryParam aParam;
3686 pDBData->GetQueryParam(aParam);
3687 ScQueryEntry& rEntry0 = aParam.GetEntry(0);
3688 rEntry0.bDoQuery = true;
3689 rEntry0.nField = 0;
3690 rEntry0.eOp = SC_EQUAL;
3691 rEntry0.GetQueryItems().resize(nRows);
3692 ScQueryEntry& rEntry1 = aParam.GetEntry(1);
3693 rEntry1.bDoQuery = true;
3694 rEntry1.nField = 1;
3695 rEntry1.eOp = SC_EQUAL;
3696 rEntry1.GetQueryItems().resize(nRows);
3697 ScQueryEntry& rEntry2 = aParam.GetEntry(2);
3698 rEntry2.bDoQuery = true;
3699 rEntry2.nField = 2;
3700 rEntry2.eOp = SC_EQUAL;
3701 rEntry2.GetQueryItems().resize(nRows);
3702 ScQueryEntry& rEntry3 = aParam.GetEntry(3);
3703 rEntry3.bDoQuery = true;
3704 rEntry3.nField = 3;
3705 rEntry3.eOp = SC_EQUAL;
3706 rEntry3.GetQueryItems().resize(nRows);
3707 // Set up autofilter to select all values except one in each column.
3708 // This should only filter out 2nd, 3rd, 6th and 7th rows.
3709 for( int i = 0; i < nRows; ++i )
3711 if(i!= 1)
3712 rEntry0.GetQueryItems()[i].mfVal = i;
3713 if(i!= 2)
3714 rEntry1.GetQueryItems()[i].mfVal = i + 1;
3715 if(i!= 5)
3717 rEntry2.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000));
3718 rEntry2.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3720 if(i!= 6)
3722 rEntry3.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000+1));
3723 rEntry3.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3726 // add queryParam to database range.
3727 pDBData->SetQueryParam(aParam);
3729 // perform the query.
3730 m_pDoc->Query(0, aParam, true);
3732 // check that only rows with filtered out values are hidden, and not rows that share
3733 // a value in a different column
3734 SCROW nRow1, nRow2;
3735 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0, &nRow1, &nRow2));
3736 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden", m_pDoc->RowHidden(2, 0, &nRow1, &nRow2));
3737 CPPUNIT_ASSERT_MESSAGE("row 4 should be hidden", m_pDoc->RowHidden(3, 0, &nRow1, &nRow2));
3738 CPPUNIT_ASSERT_MESSAGE("row 5 should be visible", !m_pDoc->RowHidden(4, 0, &nRow1, &nRow2));
3739 CPPUNIT_ASSERT_MESSAGE("row 6 should be visible", !m_pDoc->RowHidden(5, 0, &nRow1, &nRow2));
3740 CPPUNIT_ASSERT_MESSAGE("row 7 should be hidden", m_pDoc->RowHidden(6, 0, &nRow1, &nRow2));
3741 CPPUNIT_ASSERT_MESSAGE("row 8 should be hidden", m_pDoc->RowHidden(7, 0, &nRow1, &nRow2));
3742 CPPUNIT_ASSERT_MESSAGE("row 9 should be visible", !m_pDoc->RowHidden(8, 0, &nRow1, &nRow2));
3744 // Remove filtering.
3745 rEntry0.Clear();
3746 rEntry1.Clear();
3747 rEntry2.Clear();
3748 m_pDoc->Query(0, aParam, true);
3749 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !m_pDoc->RowHidden(0, 0, &nRow1, &nRow2));
3750 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3751 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3753 m_pDoc->DeleteTab(0);
3756 void Test::testTdf76441()
3758 m_pDoc->InsertTab(0, "Test");
3760 // The result will be different depending on whether the format is set before
3761 // or after inserting the string
3763 OUString aCode = "MM:SS";
3764 sal_Int32 nCheckPos;
3765 SvNumFormatType nType;
3766 sal_uInt32 nFormat;
3767 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3768 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3770 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3771 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3772 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3774 // First insert the string, then the format
3775 m_pDoc->SetString(ScAddress(0,0,0), "01:20");
3777 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3779 CPPUNIT_ASSERT_EQUAL(OUString("20:00"), m_pDoc->GetString(ScAddress(0,0,0)));
3783 // First set the format, then insert the string
3784 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3786 m_pDoc->SetString(ScAddress(0,1,0), "01:20");
3788 // Without the fix in place, this test would have failed with
3789 // - Expected: 01:20
3790 // - Actual : 20:00
3791 CPPUNIT_ASSERT_EQUAL(OUString("01:20"), m_pDoc->GetString(ScAddress(0,1,0)));
3794 m_pDoc->DeleteTab(0);
3797 void Test::testTdf76836()
3799 m_pDoc->InsertTab(0, "Test");
3801 OUString aCode = "\"192.168.0.\"@";
3802 sal_Int32 nCheckPos;
3803 SvNumFormatType nType;
3804 sal_uInt32 nFormat;
3805 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3806 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3808 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3809 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3810 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3812 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3813 m_pDoc->SetValue(0,0,0, 10.0);
3815 // Without the fix in place, this test would have failed with
3816 // - Expected: 10
3817 // - Actual : 192.168.0.10
3818 CPPUNIT_ASSERT_EQUAL(OUString("10"), m_pDoc->GetString(ScAddress(0,0,0)));
3820 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3821 m_pDoc->SetString(ScAddress(0,1,0), "10");
3822 CPPUNIT_ASSERT_EQUAL(OUString("192.168.0.10"), m_pDoc->GetString(ScAddress(0,1,0)));
3824 m_pDoc->DeleteTab(0);
3827 void Test::testTdf142186()
3829 m_pDoc->InsertTab(0, "Test");
3831 // The result will be different depending on whether the format is set before
3832 // or after inserting the string
3834 OUString aCode = "0\".\"0";
3835 sal_Int32 nCheckPos;
3836 SvNumFormatType nType;
3837 sal_uInt32 nFormat;
3838 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3839 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3841 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3842 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3843 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3845 // First insert the string, then the format
3846 m_pDoc->SetString(ScAddress(0,0,0), "123.45");
3848 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3850 CPPUNIT_ASSERT_EQUAL(OUString("12.3"), m_pDoc->GetString(ScAddress(0,0,0)));
3854 // First set the format, then insert the string
3855 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3857 m_pDoc->SetString(ScAddress(0,1,0), "123.45");
3859 // Without the fix in place, this test would have failed with
3860 // - Expected: 12.3
3861 // - Actual : 1234.5
3862 CPPUNIT_ASSERT_EQUAL(OUString("12.3"), m_pDoc->GetString(ScAddress(0,1,0)));
3865 m_pDoc->DeleteTab(0);
3868 void Test::testTdf137063()
3870 m_pDoc->InsertTab(0, "Test");
3872 m_pDoc->SetValue(0,0,0, 0.000000006);
3873 m_pDoc->SetValue(0,1,0, 0.0000000006);
3875 // Without the fix in place, this test would have failed with
3876 // - Expected: 0.000000006
3877 // - Actual : 6E-09
3878 CPPUNIT_ASSERT_EQUAL(OUString("0.000000006"), m_pDoc->GetString(ScAddress(0,0,0)));
3879 CPPUNIT_ASSERT_EQUAL(OUString("6E-10"), m_pDoc->GetString(ScAddress(0,1,0)));
3881 m_pDoc->DeleteTab(0);
3884 void Test::testTdf126342()
3886 m_pDoc->InsertTab(0, "Test");
3888 OUString aCode = "YYYY-MM-DD";
3889 sal_Int32 nCheckPos;
3890 SvNumFormatType nType;
3891 sal_uInt32 nFormat;
3892 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3893 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3895 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3896 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3897 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3898 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3900 m_pDoc->SetString(ScAddress(0,0,0), "11/7/19");
3902 CPPUNIT_ASSERT_EQUAL(OUString("2019-11-07"), m_pDoc->GetString(ScAddress(0,0,0)));
3904 // Overwrite the existing date with the exact same input
3905 m_pDoc->SetString(ScAddress(0,0,0), "11/7/19");
3907 // Without the fix in place, this test would have failed with
3908 // - Expected: 2019-11-07
3909 // - Actual : 2011-07-19
3910 CPPUNIT_ASSERT_EQUAL(OUString("2019-11-07"), m_pDoc->GetString(ScAddress(0,0,0)));
3912 m_pDoc->DeleteTab(0);
3915 void Test::testAdvancedFilter()
3917 m_pDoc->InsertTab(0, "Test");
3919 // cell contents (nullptr = empty cell)
3920 std::vector<std::vector<const char*>> aData = {
3921 { "Value", "Tag" }, // A1:B11
3922 { "1", "R" },
3923 { "2", "R" },
3924 { "3", "R" },
3925 { "4", "C" },
3926 { "5", "C" },
3927 { "6", "C" },
3928 { "7", "R" },
3929 { "8", "R" },
3930 { "9", "R" },
3931 { "10", "C" },
3932 { nullptr },
3933 { "Value", "Tag" }, // A13:B14
3934 { "> 5", "R" },
3937 // Populate cells.
3938 for (size_t nRow = 0; nRow < aData.size(); ++nRow)
3940 const std::vector<const char*>& rRowData = aData[nRow];
3941 for (size_t nCol = 0; nCol < rRowData.size(); ++nCol)
3943 const char* pCell = rRowData[nCol];
3944 if (pCell)
3945 m_pDoc->SetString(nCol, nRow, 0, OUString::createFromAscii(pCell));
3949 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 1, 10);
3950 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3952 ScRange aDataRange(0,0,0,1,10,0);
3953 ScRange aFilterRuleRange(0,12,0,1,13,0);
3955 printRange(m_pDoc, aDataRange, "Data");
3956 printRange(m_pDoc, aFilterRuleRange, "Filter Rule");
3958 ScQueryParam aQueryParam;
3959 aQueryParam.bHasHeader = true;
3960 aQueryParam.nCol1 = aDataRange.aStart.Col();
3961 aQueryParam.nRow1 = aDataRange.aStart.Row();
3962 aQueryParam.nCol2 = aDataRange.aEnd.Col();
3963 aQueryParam.nRow2 = aDataRange.aEnd.Row();
3964 aQueryParam.nTab = aDataRange.aStart.Tab();
3966 bool bGood = m_pDoc->CreateQueryParam(aFilterRuleRange, aQueryParam);
3967 CPPUNIT_ASSERT_MESSAGE("failed to create query param.", bGood);
3969 // First entry is for the 'Value' field, and is greater than 5.
3970 ScQueryEntry aEntry = aQueryParam.GetEntry(0);
3971 CPPUNIT_ASSERT(aEntry.bDoQuery);
3972 CPPUNIT_ASSERT_EQUAL(SCCOLROW(0), aEntry.nField);
3973 CPPUNIT_ASSERT_EQUAL(SC_GREATER, aEntry.eOp);
3975 ScQueryEntry::QueryItemsType aItems = aEntry.GetQueryItems();
3976 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3977 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByValue, aItems[0].meType);
3978 CPPUNIT_ASSERT_EQUAL(5.0, aItems[0].mfVal);
3980 // Second entry is for the 'Tag' field, and is == 'R'.
3981 aEntry = aQueryParam.GetEntry(1);
3982 CPPUNIT_ASSERT(aEntry.bDoQuery);
3983 CPPUNIT_ASSERT_EQUAL(SCCOLROW(1), aEntry.nField);
3984 CPPUNIT_ASSERT_EQUAL(SC_EQUAL, aEntry.eOp);
3986 aItems = aEntry.GetQueryItems();
3987 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3988 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByString, aItems[0].meType);
3989 CPPUNIT_ASSERT_EQUAL(OUString("R"), aItems[0].maString.getString());
3991 // perform the query.
3992 m_pDoc->Query(0, aQueryParam, true);
3994 // Only rows 1,8-10 should be visible.
3995 bool bFiltered = m_pDoc->RowFiltered(0, 0);
3996 CPPUNIT_ASSERT_MESSAGE("row 1 (header row) should be visible", !bFiltered);
3998 SCROW nRow1 = -1, nRow2 = -1;
3999 bFiltered = m_pDoc->RowFiltered(1, 0, &nRow1, &nRow2);
4000 CPPUNIT_ASSERT_MESSAGE("rows 2-7 should be filtered out.", bFiltered);
4001 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(1), nRow1);
4002 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(6), nRow2);
4004 bFiltered = m_pDoc->RowFiltered(7, 0, &nRow1, &nRow2);
4005 CPPUNIT_ASSERT_MESSAGE("rows 8-10 should be visible.", !bFiltered);
4006 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(7), nRow1);
4007 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(9), nRow2);
4009 m_pDoc->DeleteTab(0);
4012 void Test::testDateFilterContains()
4014 m_pDoc->InsertTab(0, "Test");
4016 constexpr SCCOL nCols = 1;
4017 constexpr SCROW nRows = 5;
4018 m_pDoc->SetString(0, 0, 0, "Date");
4019 m_pDoc->SetString(0, 1, 0, "1/2/2021");
4020 m_pDoc->SetString(0, 2, 0, "2/1/1999");
4021 m_pDoc->SetString(0, 3, 0, "2/1/1997");
4022 m_pDoc->SetString(0, 4, 0, "3/3/2001");
4023 m_pDoc->SetString(0, 5, 0, "3/3/1996");
4025 // Set the fields as dates.
4026 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
4027 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_DATE_DIN_YYMMDD, LANGUAGE_ENGLISH_US);
4028 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
4029 SfxItemSet& rSet = aNewAttrs.GetItemSet();
4030 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
4031 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 5, 0, aNewAttrs); // apply it to A1:A6
4033 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols, nRows);
4034 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
4036 pDBData->SetAutoFilter(true);
4037 ScRange aRange;
4038 pDBData->GetArea(aRange);
4039 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
4040 aRange.aEnd.Col(), aRange.aStart.Row(),
4041 aRange.aStart.Tab(), ScMF::Auto);
4043 //create the query param
4044 ScQueryParam aParam;
4045 pDBData->GetQueryParam(aParam);
4046 ScQueryEntry& rEntry = aParam.GetEntry(0);
4047 rEntry.bDoQuery = true;
4048 rEntry.nField = 0;
4049 rEntry.eOp = SC_CONTAINS;
4050 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern("2");
4051 pDBData->SetQueryParam(aParam);
4053 // perform the query.
4054 m_pDoc->Query(0, aParam, true);
4056 // Dates in rows 2-4 contain '2', row 5 shows 2001 only as 01, and row 6 doesn't contain it at all.
4057 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0));
4058 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible", !m_pDoc->RowHidden(2, 0));
4059 CPPUNIT_ASSERT_MESSAGE("row 4 should be visible", !m_pDoc->RowHidden(3, 0));
4060 CPPUNIT_ASSERT_MESSAGE("row 5 should be hidden", m_pDoc->RowHidden(4, 0));
4061 CPPUNIT_ASSERT_MESSAGE("row 6 should be hidden", m_pDoc->RowHidden(5, 0));
4063 m_pDoc->DeleteTab(0);
4066 void Test::testTdf98642()
4068 m_pDoc->InsertTab(0, "Sheet1");
4069 m_pDoc->SetString(0, 0, 0, "test");
4071 ScRangeData* pName1 = new ScRangeData( *m_pDoc, "name1", "$Sheet1.$A$1");
4072 ScRangeData* pName2 = new ScRangeData( *m_pDoc, "name2", "$Sheet1.$A$1");
4074 std::unique_ptr<ScRangeName> pGlobalRangeName(new ScRangeName());
4075 pGlobalRangeName->insert(pName1);
4076 pGlobalRangeName->insert(pName2);
4077 m_pDoc->SetRangeName(std::move(pGlobalRangeName));
4079 m_pDoc->SetString(1, 0, 0, "=name1");
4080 m_pDoc->SetString(1, 1, 0, "=name2");
4082 CPPUNIT_ASSERT_EQUAL(OUString("test"), m_pDoc->GetString(1, 0, 0));
4083 CPPUNIT_ASSERT_EQUAL(OUString("test"), m_pDoc->GetString(1, 1, 0));
4085 OUString aFormula = m_pDoc->GetFormula(1,0,0);
4086 CPPUNIT_ASSERT_EQUAL(OUString("=name1"), aFormula);
4087 aFormula = m_pDoc->GetFormula(1,1,0);
4089 // Without the fix in place, this test would have failed with
4090 // - Expected: =name2
4091 // - Actual : =name1
4092 CPPUNIT_ASSERT_EQUAL(OUString("=name2"), aFormula);
4094 m_pDoc->DeleteTab(0);
4097 void Test::testMergedCells()
4099 //test merge and unmerge
4100 //TODO: an undo/redo test for this would be a good idea
4101 m_pDoc->InsertTab(0, "Sheet1");
4102 m_pDoc->DoMerge(1, 1, 3, 3, 0, false);
4103 SCCOL nEndCol = 1;
4104 SCROW nEndRow = 1;
4105 m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0);
4106 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCCOL(3), nEndCol);
4107 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCROW(3), nEndRow);
4108 ScRange aRange(0,2,0,m_pDoc->MaxCol(),2,0);
4109 ScMarkData aMark(m_pDoc->GetSheetLimits());
4110 aMark.SetMarkArea(aRange);
4111 m_xDocShell->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS_BEFORE, true, true);
4112 m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0);
4113 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCCOL(3), nEndCol);
4114 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCROW(4), nEndRow);
4115 m_pDoc->DeleteTab(0);
4118 void Test::testRenameTable()
4120 //test set rename table
4121 //TODO: set name1 and name2 and do an undo to check if name 1 is set now
4122 //TODO: also check if new name for table is same as another table
4124 m_pDoc->InsertTab(0, "Sheet1");
4125 m_pDoc->InsertTab(1, "Sheet2");
4127 //test case 1 , rename table2 to sheet 1, it should return error
4128 OUString nameToSet = "Sheet1";
4129 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4130 CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
4132 //test case 2 , simple rename to check name
4133 nameToSet = "test1";
4134 m_xDocShell->GetDocFunc().RenameTable(0,nameToSet,false,true);
4135 OUString nameJustSet;
4136 m_pDoc->GetName(0,nameJustSet);
4137 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4139 //test case 3 , rename again
4140 OUString anOldName;
4141 m_pDoc->GetName(0,anOldName);
4143 nameToSet = "test2";
4144 rDocFunc.RenameTable(0,nameToSet,false,true);
4145 m_pDoc->GetName(0,nameJustSet);
4146 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4148 //test case 4 , check if undo works
4149 SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShell.get(),0,anOldName,nameToSet);
4150 pUndo->Undo();
4151 m_pDoc->GetName(0,nameJustSet);
4152 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct name is not set after undo", nameJustSet, anOldName);
4154 pUndo->Redo();
4155 m_pDoc->GetName(0,nameJustSet);
4156 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after redo", nameJustSet, nameToSet);
4157 delete pUndo;
4159 m_pDoc->DeleteTab(0);
4160 m_pDoc->DeleteTab(1);
4163 void Test::testSetBackgroundColor()
4165 //test set background color
4166 //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
4168 m_pDoc->InsertTab(0, "Sheet1");
4169 Color aColor;
4171 //test yellow
4172 aColor=COL_YELLOW;
4173 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4174 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set",
4175 aColor, m_pDoc->GetTabBgColor(0));
4177 Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
4178 aColor = COL_BLUE;
4179 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4180 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set the second time",
4181 aColor, m_pDoc->GetTabBgColor(0));
4183 //now check for undo
4184 SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShell.get(), 0, aOldTabBgColor, aColor);
4185 pUndo->Undo();
4186 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aOldTabBgColor, m_pDoc->GetTabBgColor(0));
4187 pUndo->Redo();
4188 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aColor, m_pDoc->GetTabBgColor(0));
4189 delete pUndo;
4190 m_pDoc->DeleteTab(0);
4193 void Test::testUpdateReference()
4195 //test that formulas are correctly updated during sheet delete
4196 //TODO: add tests for relative references, updating of named ranges, ...
4197 m_pDoc->InsertTab(0, "Sheet1");
4198 m_pDoc->InsertTab(1, "Sheet2");
4199 m_pDoc->InsertTab(2, "Sheet3");
4200 m_pDoc->InsertTab(3, "Sheet4");
4202 m_pDoc->SetValue(0,0,2, 1);
4203 m_pDoc->SetValue(1,0,2, 2);
4204 m_pDoc->SetValue(1,1,3, 4);
4205 m_pDoc->SetString(2,0,2, "=A1+B1");
4206 m_pDoc->SetString(2,1,2, "=Sheet4.B2+A1");
4208 double aValue;
4209 aValue = m_pDoc->GetValue(2,0,2);
4210 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
4211 aValue = m_pDoc->GetValue(2,1,2);
4212 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
4214 //test deleting both sheets: one is not directly before the sheet, the other one is
4215 m_pDoc->DeleteTab(0);
4216 aValue = m_pDoc->GetValue(2,0,1);
4217 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
4218 aValue = m_pDoc->GetValue(2,1,1);
4219 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
4221 m_pDoc->DeleteTab(0);
4222 aValue = m_pDoc->GetValue(2,0,0);
4223 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
4224 aValue = m_pDoc->GetValue(2,1,0);
4225 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
4227 //test adding two sheets
4228 m_pDoc->InsertTab(0, "Sheet2");
4229 aValue = m_pDoc->GetValue(2,0,1);
4230 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
4231 aValue = m_pDoc->GetValue(2,1,1);
4232 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
4234 m_pDoc->InsertTab(0, "Sheet1");
4235 aValue = m_pDoc->GetValue(2,0,2);
4236 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
4237 aValue = m_pDoc->GetValue(2,1,2);
4238 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
4240 //test new DeleteTabs/InsertTabs methods
4241 m_pDoc->DeleteTabs(0, 2);
4242 aValue = m_pDoc->GetValue(2, 0, 0);
4243 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
4244 aValue = m_pDoc->GetValue(2, 1, 0);
4245 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
4247 std::vector<OUString> aSheets;
4248 aSheets.emplace_back("Sheet1");
4249 aSheets.emplace_back("Sheet2");
4250 m_pDoc->InsertTabs(0, aSheets, true);
4251 aValue = m_pDoc->GetValue(2, 0, 2);
4253 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
4254 aValue = m_pDoc->GetValue(2, 1, 2);
4255 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
4257 m_pDoc->DeleteTab(3);
4258 m_pDoc->DeleteTab(2);
4259 m_pDoc->DeleteTab(1);
4260 m_pDoc->DeleteTab(0);
4262 // Test positional update and invalidation of lookup cache for insertion
4263 // and deletion within entire column reference.
4264 m_pDoc->InsertTab(0, "Sheet1");
4265 m_pDoc->InsertTab(1, "Sheet2");
4266 m_pDoc->SetString(0,1,0, "s1");
4267 m_pDoc->SetString(0,0,1, "=MATCH(\"s1\";Sheet1.A:A;0)");
4268 aValue = m_pDoc->GetValue(0,0,1);
4269 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4270 m_pDoc->InsertRow(0,0,m_pDoc->MaxCol(),0,0,1); // insert 1 row before row 1 in Sheet1
4271 aValue = m_pDoc->GetValue(0,0,1);
4272 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 3, aValue);
4273 m_pDoc->DeleteRow(0,0,m_pDoc->MaxCol(),0,0,1); // delete row 1 in Sheet1
4274 aValue = m_pDoc->GetValue(0,0,1);
4275 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4276 m_pDoc->DeleteTab(1);
4277 m_pDoc->DeleteTab(0);
4280 void Test::testSearchCells()
4282 m_pDoc->InsertTab(0, "Test");
4284 m_pDoc->SetString(ScAddress(0,0,0), "A");
4285 m_pDoc->SetString(ScAddress(0,1,0), "B");
4286 m_pDoc->SetString(ScAddress(0,2,0), "A");
4287 // Leave A4 blank.
4288 m_pDoc->SetString(ScAddress(0,4,0), "A");
4289 m_pDoc->SetString(ScAddress(0,5,0), "B");
4290 m_pDoc->SetString(ScAddress(0,6,0), "C");
4292 SvxSearchItem aItem(SID_SEARCH_ITEM);
4293 aItem.SetSearchString("A");
4294 aItem.SetCommand(SvxSearchCmd::FIND_ALL);
4295 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4296 aMarkData.SelectOneTable(0);
4297 SCCOL nCol = 0;
4298 SCROW nRow = 0;
4299 SCTAB nTab = 0;
4300 ScRangeList aMatchedRanges;
4301 OUString aUndoStr;
4302 bool bMatchedRangesWereClamped = false;
4303 bool bSuccess = m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped);
4305 CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
4306 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 3 matching cells.", size_t(3), aMatchedRanges.size());
4307 ScAddress aHit(0,0,0);
4308 CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4309 aHit.SetRow(2);
4310 CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4311 aHit.SetRow(4);
4312 CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4314 m_pDoc->DeleteTab(0);
4317 void Test::testFormulaPosition()
4319 m_pDoc->InsertTab(0, "Test");
4321 ScAddress aPos(0,0,0); // A1
4322 m_pDoc->SetString(aPos, "=ROW()");
4323 aPos.IncRow(); // A2
4324 m_pDoc->SetString(aPos, "=ROW()");
4325 aPos.SetRow(3); // A4;
4326 m_pDoc->SetString(aPos, "=ROW()");
4329 SCROW aRows[] = { 0, 1, 3 };
4330 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4331 CPPUNIT_ASSERT(bRes);
4334 m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
4336 SCROW aRows[] = { 0, 6, 8 };
4337 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4338 CPPUNIT_ASSERT(bRes);
4341 m_pDoc->DeleteTab(0);
4344 namespace {
4346 bool hasRange(const ScDocument* pDoc, const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange, const ScAddress& rPos)
4348 for (const ScTokenRef& p : rRefTokens)
4350 if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4351 continue;
4353 switch (p->GetType())
4355 case formula::svSingleRef:
4357 ScSingleRefData aData = *p->GetSingleRef();
4358 if (rRange.aStart != rRange.aEnd)
4359 break;
4361 ScAddress aThis = aData.toAbs(*pDoc, rPos);
4362 if (aThis == rRange.aStart)
4363 return true;
4365 break;
4366 case formula::svDoubleRef:
4368 ScComplexRefData aData = *p->GetDoubleRef();
4369 ScRange aThis = aData.toAbs(*pDoc, rPos);
4370 if (aThis == rRange)
4371 return true;
4373 break;
4374 default:
4378 return false;
4383 void Test::testJumpToPrecedentsDependents()
4385 // Precedent is another cell that the cell references, while dependent is
4386 // another cell that references it.
4387 m_pDoc->InsertTab(0, "Test");
4389 m_pDoc->SetString(2, 0, 0, "=A1+A2+B3"); // C1
4390 m_pDoc->SetString(2, 1, 0, "=A1"); // C2
4391 m_pDoc->CalcAll();
4393 std::vector<ScTokenRef> aRefTokens;
4394 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4397 // C1's precedent should be A1:A2,B3.
4398 ScAddress aC1(2, 0, 0);
4399 ScRangeList aRange(aC1);
4400 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4401 CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4402 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
4403 CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4404 hasRange(m_pDoc, aRefTokens, ScRange(1, 2, 0), aC1));
4408 // C2's precedent should be A1 only.
4409 ScAddress aC2(2, 1, 0);
4410 ScRangeList aRange(aC2);
4411 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4412 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4413 static_cast<size_t>(1), aRefTokens.size());
4414 CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4415 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0), aC2));
4419 // A1's dependent should be C1:C2.
4420 ScAddress aA1(0, 0, 0);
4421 ScRangeList aRange(aA1);
4422 rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4423 CPPUNIT_ASSERT_EQUAL_MESSAGE("C1:C2 should be the only dependent of A1.",
4424 std::vector<ScTokenRef>::size_type(1), aRefTokens.size());
4425 CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4426 hasRange(m_pDoc, aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
4429 m_pDoc->DeleteTab(0);
4432 void Test::testTdf149665()
4434 m_pDoc->InsertTab(0, "Test");
4436 m_pDoc->SetString(0, 0, 0, "''1");
4438 // Without the fix in place, this test would have failed with
4439 // - Expected: '1
4440 // - Actual : ''1
4441 CPPUNIT_ASSERT_EQUAL( OUString("'1"), m_pDoc->GetString( 0, 0, 0 ) );
4443 m_pDoc->DeleteTab(0);
4446 void Test::testTdf64001()
4448 m_pDoc->InsertTab(0, "test");
4450 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4451 aMarkData.SelectTable(0, true);
4453 m_pDoc->SetString( 0, 0, 0, "TRUE" );
4454 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 9, FILL_TO_BOTTOM, FILL_AUTO );
4456 for (SCCOL i = 0; i < 10; ++i)
4458 CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString( 0, i, 0 ) );
4461 m_pDoc->SetString( 0, 10, 0, "FALSE" );
4463 m_pDoc->SetString( 1, 0, 0, "=COUNTIF(A1:A11;TRUE)" );
4465 // Without the fix in place, this test would have failed with
4466 // - Expected: 10
4467 // - Actual : 1
4468 CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue( 1, 0, 0 ) );
4470 m_pDoc->DeleteTab(0);
4473 void Test::testAutoFill()
4475 m_pDoc->InsertTab(0, "test");
4477 m_pDoc->SetValue(0,0,0,1);
4479 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4480 aMarkData.SelectTable(0, true);
4482 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5);
4483 for (SCROW i = 0; i< 6; ++i)
4484 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
4486 // check that hidden rows are not affected by autofill
4487 // set values for hidden rows
4488 m_pDoc->SetValue(0,1,0,10);
4489 m_pDoc->SetValue(0,2,0,10);
4491 m_pDoc->SetRowHidden(1, 2, 0, true);
4492 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 8);
4494 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
4495 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
4496 for (SCROW i = 3; i< 8; ++i)
4497 ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
4499 m_pDoc->Fill( 0, 0, 0, 8, nullptr, aMarkData, 5, FILL_TO_RIGHT );
4500 for (SCCOL i = 0; i < 5; ++i)
4502 for(SCROW j = 0; j < 8; ++j)
4504 if (j > 2)
4506 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
4508 else if (j == 0)
4510 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
4512 else // j == 1 || j == 2
4514 if(i == 0)
4515 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
4516 else
4517 ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
4522 // test auto fill user data lists
4523 m_pDoc->SetString( 0, 100, 0, "January" );
4524 m_pDoc->Fill( 0, 100, 0, 100, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4525 OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
4526 CPPUNIT_ASSERT_EQUAL( OUString("February"), aTestValue );
4527 aTestValue = m_pDoc->GetString( 0, 102, 0 );
4528 CPPUNIT_ASSERT_EQUAL( OUString("March"), aTestValue );
4530 // test that two same user data list entries will not result in incremental fill
4531 m_pDoc->SetString( 0, 101, 0, "January" );
4532 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4533 for ( SCROW i = 102; i <= 103; ++i )
4535 aTestValue = m_pDoc->GetString( 0, i, 0 );
4536 CPPUNIT_ASSERT_EQUAL( OUString("January"), aTestValue );
4539 // Clear column A for a new test.
4540 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4541 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4543 // Fill A1:A6 with 1,2,3,4,5,6.
4544 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4545 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4546 ScRange aRange(0,0,0,0,5,0);
4547 aMarkData.SetMarkArea(aRange);
4548 rFunc.FillSeries(aRange, &aMarkData, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, MAXDOUBLE, 1.0, MAXDOUBLE, true);
4549 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4550 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4551 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4552 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4553 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4554 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4556 // Undo should clear the area except for the top cell.
4557 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4558 CPPUNIT_ASSERT(pUndoMgr);
4559 pUndoMgr->Undo();
4561 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4562 for (SCROW i = 1; i <= 5; ++i)
4563 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0,i,0)));
4565 // Redo should put the serial values back in.
4566 pUndoMgr->Redo();
4567 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4568 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4569 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4570 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4571 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4572 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4574 // test that filling formulas vertically up does the right thing
4575 for(SCROW nRow = 0; nRow < 10; ++nRow)
4576 m_pDoc->SetValue(100, 100 + nRow, 0, 1);
4578 m_pDoc->SetString(100, 110, 0, "=A111");
4580 m_pDoc->Fill(100, 110, 100, 110, nullptr, aMarkData, 10, FILL_TO_TOP, FILL_AUTO);
4581 for(SCROW nRow = 110; nRow >= 100; --nRow)
4583 OUString aExpected = "=A" + OUString::number(nRow +1);
4584 OUString aFormula = m_pDoc->GetFormula(100, nRow, 0);
4585 CPPUNIT_ASSERT_EQUAL(aExpected, aFormula);
4588 // Clear column A for a new test.
4589 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4590 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4592 m_pDoc->SetString( 0, 100, 0, "2012-10-31" );
4593 m_pDoc->SetString( 0, 101, 0, "2012-10-31" );
4594 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4596 // tdf#89754, Without the fix in place, this test would have failed with
4597 // - Expected: 2012-10-31
4598 // - Actual : 2012-11-01
4599 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 102, 0 ) );
4600 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 103, 0 ) );
4601 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 104, 0 ) );
4603 // Clear column A for a new test.
4604 clearRange(m_pDoc, ScRange(0, 0, 0, 0, m_pDoc->MaxRow(), 0));
4605 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4607 m_pDoc->SetString(0, 100, 0, "2019-10-31");
4608 m_pDoc->SetString(0, 101, 0, "2019-11-30");
4609 m_pDoc->SetString(0, 102, 0, "2019-12-31");
4610 m_pDoc->Fill(0, 100, 0, 102, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO);
4612 // tdf#58745, Without the fix in place, this test would have failed with
4613 // - Expected: 2020-01-31
4614 // - Actual : 2019-01-11
4615 CPPUNIT_ASSERT_EQUAL(OUString("2020-01-31"), m_pDoc->GetString(0, 103, 0));
4616 CPPUNIT_ASSERT_EQUAL(OUString("2020-02-29"), m_pDoc->GetString(0, 104, 0));
4617 CPPUNIT_ASSERT_EQUAL(OUString("2020-03-31"), m_pDoc->GetString(0, 105, 0));
4619 // Clear column A for a new test.
4620 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4621 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4623 m_pDoc->SetString( 0, 50, 0, "1.0" );
4624 m_pDoc->SetString( 0, 51, 0, "1.1" );
4625 m_pDoc->SetString( 0, 52, 0, "1.2" );
4626 m_pDoc->SetString( 0, 53, 0, "1.3" );
4627 m_pDoc->Fill( 0, 50, 0, 53, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4629 CPPUNIT_ASSERT_EQUAL( OUString("1.4"), m_pDoc->GetString( 0, 54, 0 ) );
4630 CPPUNIT_ASSERT_EQUAL( OUString("1.5"), m_pDoc->GetString( 0, 55, 0 ) );
4631 CPPUNIT_ASSERT_EQUAL( OUString("1.6"), m_pDoc->GetString( 0, 56, 0 ) );
4633 m_pDoc->SetString( 0, 60, 0, "4.0" );
4634 m_pDoc->SetString( 0, 61, 0, "4.1" );
4635 m_pDoc->SetString( 0, 62, 0, "4.2" );
4636 m_pDoc->SetString( 0, 63, 0, "4.3" );
4637 m_pDoc->Fill( 0, 60, 0, 63, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4639 // tdf#37424: Without the fix in place, this test would have failed with
4640 // - Expected: 4.4
4641 // - Actual : 5
4642 CPPUNIT_ASSERT_EQUAL( OUString("4.4"), m_pDoc->GetString( 0, 64, 0 ) );
4643 CPPUNIT_ASSERT_EQUAL( OUString("4.5"), m_pDoc->GetString( 0, 65, 0 ) );
4644 CPPUNIT_ASSERT_EQUAL( OUString("4.6"), m_pDoc->GetString( 0, 66, 0 ) );
4646 // Clear column A for a new test.
4647 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4648 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4650 m_pDoc->SetString( 0, 70, 0, "001-001-001" );
4651 m_pDoc->Fill( 0, 70, 0, 70, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4653 // tdf#105268: Without the fix in place, this test would have failed with
4654 // - Expected: 001-001-002
4655 // - Actual : 001-001000
4656 CPPUNIT_ASSERT_EQUAL( OUString("001-001-002"), m_pDoc->GetString( 0, 71, 0 ) );
4657 CPPUNIT_ASSERT_EQUAL( OUString("001-001-003"), m_pDoc->GetString( 0, 72, 0 ) );
4658 CPPUNIT_ASSERT_EQUAL( OUString("001-001-004"), m_pDoc->GetString( 0, 73, 0 ) );
4660 // Clear column A for a new test.
4661 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4662 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4664 m_pDoc->SetString( 0, 80, 0, "1%" );
4665 m_pDoc->Fill( 0, 80, 0, 80, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4667 // tdf#89998: Without the fix in place, this test would have failed with
4668 // - Expected: 2.00%
4669 // - Actual : 101.00%
4670 CPPUNIT_ASSERT_EQUAL( OUString("2.00%"), m_pDoc->GetString( 0, 81, 0 ) );
4671 CPPUNIT_ASSERT_EQUAL( OUString("3.00%"), m_pDoc->GetString( 0, 82, 0 ) );
4672 CPPUNIT_ASSERT_EQUAL( OUString("4.00%"), m_pDoc->GetString( 0, 83, 0 ) );
4674 // Clear column A for a new test.
4675 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4676 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4678 m_pDoc->SetString( 0, 0, 0, "1" );
4679 m_pDoc->SetString( 0, 1, 0, "1.1" );
4680 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 60, FILL_TO_BOTTOM, FILL_AUTO );
4682 // tdf#129606: Without the fix in place, this test would have failed with
4683 // - Expected: 6
4684 // - Actual : 6.00000000000001
4685 CPPUNIT_ASSERT_EQUAL( OUString("6"), m_pDoc->GetString( 0, 50, 0 ) );
4687 // Clear column A for a new test.
4688 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4689 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4691 m_pDoc->SetString( 0, 0, 0, "2022-10-01 00:00:00.000" );
4692 m_pDoc->SetString( 0, 1, 0, "2022-10-01 01:00:00.000" );
4693 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4695 // tdf#151460: Without the fix in place, this test would have failed with
4696 // - Expected: 2022-10-01 20:00:00.000
4697 // - Actual : 2022-10-01 19:59:59.999
4698 CPPUNIT_ASSERT_EQUAL( OUString("2022-10-01 20:00:00.000"), m_pDoc->GetString( 0, 20, 0 ) );
4700 // Clear column A for a new test.
4701 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4702 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4704 m_pDoc->SetString( 0, 0, 0, "1st" );
4706 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5, FILL_TO_BOTTOM, FILL_AUTO );
4708 CPPUNIT_ASSERT_EQUAL( OUString("1st"), m_pDoc->GetString( 0, 0, 0 ) );
4709 CPPUNIT_ASSERT_EQUAL( OUString("2nd"), m_pDoc->GetString( 0, 1, 0 ) );
4710 CPPUNIT_ASSERT_EQUAL( OUString("3rd"), m_pDoc->GetString( 0, 2, 0 ) );
4712 m_pDoc->DeleteTab(0);
4715 void Test::testAutoFillSimple()
4717 m_pDoc->InsertTab(0, "test");
4719 m_pDoc->SetValue(0, 0, 0, 1);
4720 m_pDoc->SetString(0, 1, 0, "=10");
4722 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4723 aMarkData.SelectTable(0, true);
4725 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 6, FILL_TO_BOTTOM, FILL_AUTO);
4727 for(SCROW nRow = 0; nRow < 8; ++nRow)
4729 if (nRow % 2 == 0)
4731 double nVal = m_pDoc->GetValue(0, nRow, 0);
4732 CPPUNIT_ASSERT_EQUAL((nRow+2)/2.0, nVal);
4734 else
4736 OString aMsg = "wrong value in row: " + OString::number(nRow);
4737 double nVal = m_pDoc->GetValue(0, nRow, 0);
4738 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, nVal);
4742 m_pDoc->DeleteTab(0);
4745 void Test::testFindAreaPosVertical()
4747 std::vector<std::vector<const char*>> aData = {
4748 { nullptr, "1", "1" },
4749 { "1", nullptr, "1" },
4750 { "1", "1", "1" },
4751 { nullptr, "1", "1" },
4752 { "1", "1", "1" },
4753 { "1", nullptr, "1" },
4754 { "1", "1", "1" },
4757 m_pDoc->InsertTab(0, "Test1");
4758 clearRange( m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
4759 ScAddress aPos(0,0,0);
4760 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4761 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4763 m_pDoc->SetRowHidden(4,4,0,true);
4764 bool bHidden = m_pDoc->RowHidden(4,0);
4765 CPPUNIT_ASSERT(bHidden);
4767 SCCOL nCol = 0;
4768 SCROW nRow = 0;
4769 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4771 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4772 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4774 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4776 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
4777 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4779 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4781 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
4782 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4784 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4786 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4787 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4789 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4791 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), nRow);
4792 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4794 nCol = 1;
4795 nRow = 2;
4797 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4799 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
4800 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4802 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4804 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4805 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4807 nCol = 2;
4808 nRow = 6;
4809 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
4810 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4811 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4813 m_pDoc->DeleteTab(0);
4816 void Test::testFindAreaPosColRight()
4818 std::vector<std::vector<const char*>> aData = {
4819 { "", "1", "1", "", "1", "1", "1" },
4820 { "", "", "1", "1", "1", "", "1" },
4823 m_pDoc->InsertTab(0, "test1");
4824 clearRange( m_pDoc, ScRange(0, 0, 0, 7, aData.size(), 0));
4825 ScAddress aPos(0,0,0);
4826 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4827 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4829 m_pDoc->SetColHidden(4,4,0,true);
4830 bool bHidden = m_pDoc->ColHidden(4,0);
4831 CPPUNIT_ASSERT(bHidden);
4833 SCCOL nCol = 0;
4834 SCROW nRow = 0;
4835 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4837 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4838 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4840 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4842 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4843 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4845 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4847 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4848 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
4850 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4852 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4853 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4855 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4857 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4858 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), nCol);
4860 nCol = 2;
4861 nRow = 1;
4863 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4865 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4866 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
4868 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4870 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4871 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4873 m_pDoc->DeleteTab(0);
4876 void Test::testShiftCells()
4878 m_pDoc->InsertTab(0, "foo");
4880 // We need a drawing layer in order to create caption objects.
4881 m_pDoc->InitDrawLayer(m_xDocShell.get());
4883 OUString aTestVal("Some Text");
4885 // Text into cell E5.
4886 m_pDoc->SetString(4, 3, 0, aTestVal);
4888 // put a Note in cell E5
4889 ScAddress rAddr(4, 3, 0);
4890 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4891 pNote->SetText(rAddr, "Hello");
4893 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4895 // Insert cell at D5. This should shift the string cell to right.
4896 m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
4897 OUString aStr = m_pDoc->GetString(5, 3, 0);
4898 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4899 CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(3, 4, 3, 4, 0));
4901 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
4902 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
4904 // Delete cell D5, to shift the text cell back into D5.
4905 m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
4906 aStr = m_pDoc->GetString(4, 3, 0);
4907 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4908 CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(4, 4, 4, 4, 0));
4910 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
4911 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4913 m_pDoc->DeleteTab(0);
4916 void Test::testNoteBasic()
4918 m_pDoc->InsertTab(0, "PostIts");
4920 // We need a drawing layer in order to create caption objects.
4921 m_pDoc->InitDrawLayer(m_xDocShell.get());
4923 CPPUNIT_ASSERT(!m_pDoc->HasNotes());
4925 // Check for note's presence in all tables before inserting any notes.
4926 for (SCTAB i = 0; i <= MAXTAB; ++i)
4928 bool bHasNotes = m_pDoc->HasTabNotes(i);
4929 CPPUNIT_ASSERT(!bHasNotes);
4932 ScAddress aAddr(2, 2, 0); // cell C3
4933 ScPostIt *pNote = m_pDoc->GetOrCreateNote(aAddr);
4935 pNote->SetText(aAddr, "Hello world");
4936 pNote->SetAuthor("Jim Bob");
4938 ScPostIt *pGetNote = m_pDoc->GetNote(aAddr);
4939 CPPUNIT_ASSERT_EQUAL_MESSAGE("note should be itself", pNote, pGetNote);
4941 // Insert one row at row 1.
4942 bool bInsertRow = m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
4943 CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
4945 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4946 aAddr.IncRow(); // cell C4
4947 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4949 // Insert column at column A.
4950 bool bInsertCol = m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
4951 CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
4953 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4954 aAddr.IncCol(); // cell D4
4955 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4957 // Insert a new sheet to shift the current sheet to the right.
4958 m_pDoc->InsertTab(0, "Table2");
4959 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4960 aAddr.IncTab(); // Move to the next sheet.
4961 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4963 m_pDoc->DeleteTab(0);
4964 aAddr.IncTab(-1);
4965 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4967 // Insert cell at C4. This should NOT shift the note position.
4968 bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
4969 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
4970 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4972 // Delete cell at C4. Again, this should NOT shift the note position.
4973 m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
4974 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4976 // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
4977 m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
4978 aAddr.IncRow(-1); // cell D3
4979 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D4 should have shifted up to D3.", pNote, m_pDoc->GetNote(aAddr));
4981 // Delete column C. This should shift the note one cell left.
4982 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 2, 1);
4983 aAddr.IncCol(-1); // cell C3
4984 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D3 should have shifted left to C3.", pNote, m_pDoc->GetNote(aAddr));
4986 // Insert a text where the note is.
4987 m_pDoc->SetString(aAddr, "Note is here.");
4989 // Delete row 1. This should shift the note from C3 to C2.
4990 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 1);
4991 aAddr.IncRow(-1); // C2
4992 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at C3 should have shifted up to C2.", pNote, m_pDoc->GetNote(aAddr));
4994 m_pDoc->DeleteTab(0);
4997 void Test::testNoteDeleteRow()
4999 m_pDoc->InsertTab(0, "Sheet1");
5001 // We need a drawing layer in order to create caption objects.
5002 m_pDoc->InitDrawLayer(m_xDocShell.get());
5004 ScAddress aPos(1, 1, 0);
5005 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5006 pNote->SetText(aPos, "Hello");
5007 pNote->SetAuthor("Jim Bob");
5009 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5011 // test with IsBlockEmpty
5012 CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsEmptyData(0, 0, 100, 100, 0));
5013 CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
5015 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
5017 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5019 // Set values and notes into B3:B4.
5020 aPos = ScAddress(1,2,0); // B3
5021 m_pDoc->SetString(aPos, "First");
5022 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "First Note", false, false);
5024 aPos = ScAddress(1,3,0); // B4
5025 m_pDoc->SetString(aPos, "Second");
5026 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "Second Note", false, false);
5028 // Delete row 2.
5029 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5030 ScMarkData aMark(m_pDoc->GetSheetLimits());
5031 aMark.SelectOneTable(0);
5032 rDocFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
5034 // Check to make sure the notes have shifted upward.
5035 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
5036 CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
5037 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
5038 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5039 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5040 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
5041 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5042 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
5044 // Undo.
5046 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5047 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5048 m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
5050 pUndoMgr->Undo();
5051 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
5052 CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
5053 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5054 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5055 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
5056 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5057 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5058 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
5060 // Delete row 3.
5061 rDocFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
5063 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5064 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5065 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
5066 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5067 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
5069 // Undo and check the result.
5070 pUndoMgr->Undo();
5071 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5072 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5073 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
5074 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5075 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5076 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
5078 m_pDoc->DeleteTab(0);
5081 void Test::testNoteDeleteCol()
5083 m_pDoc->InsertTab(0, "Sheet1");
5085 // We need a drawing layer in order to create caption objects.
5086 m_pDoc->InitDrawLayer(m_xDocShell.get());
5088 ScAddress rAddr(1, 1, 0);
5089 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5090 pNote->SetText(rAddr, "Hello");
5091 pNote->SetAuthor("Jim Bob");
5093 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5095 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
5097 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5099 m_pDoc->DeleteTab(0);
5102 void Test::testNoteLifeCycle()
5104 m_pDoc->InsertTab(0, "Test");
5106 // We need a drawing layer in order to create caption objects.
5107 m_pDoc->InitDrawLayer(m_xDocShell.get());
5109 ScAddress aPos(1,1,0);
5110 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5111 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
5113 pNote->SetText(aPos, "New note");
5114 std::unique_ptr<ScPostIt> pNote2 = m_pDoc->ReleaseNote(aPos);
5115 CPPUNIT_ASSERT_EQUAL_MESSAGE("This note instance is expected to be identical to the original.", pNote, pNote2.get());
5116 CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
5118 // Modify the internal state of the note instance to make sure it's really
5119 // been released.
5120 pNote->SetText(aPos, "New content");
5122 // Re-insert the note back to the same place.
5123 m_pDoc->SetNote(aPos, std::move(pNote2));
5124 SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
5125 CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
5126 CPPUNIT_ASSERT_EQUAL_MESSAGE("This caption should belong to the drawing layer of the document.",
5127 m_pDoc->GetDrawLayer(), static_cast<ScDrawLayer*>(&pCaption->getSdrModelFromSdrObject()));
5129 // Copy B2 with note to a clipboard.
5131 ScClipParam aClipParam(aPos, false);
5132 ScDocument aClipDoc(SCDOCMODE_CLIP);
5133 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
5134 aMarkData.SelectOneTable(0);
5135 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMarkData, false, true);
5137 ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
5138 CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
5139 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note on the clipboard should share the same caption object from the original.",
5140 pCaption, pClipNote->GetCaption());
5143 // Move B2 to B3 with note, which creates an ScUndoDragDrop, and Undo.
5145 ScAddress aOrigPos(aPos);
5146 ScAddress aMovePos(1,2,0);
5147 ScPostIt* pOrigNote = m_pDoc->GetNote(aOrigPos);
5148 const SdrCaptionObj* pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5149 bool const bCut = true; // like Drag&Drop
5150 bool bRecord = true; // record Undo
5151 bool const bPaint = false; // don't care about
5152 bool bApi = true; // API to prevent dialogs
5153 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5154 bool bMoveDone = rDocFunc.MoveBlock(ScRange(aOrigPos, aOrigPos), aMovePos, bCut, bRecord, bPaint, bApi);
5155 CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
5157 // Verify the note move.
5158 ScPostIt* pGoneNote = m_pDoc->GetNote(aOrigPos);
5159 CPPUNIT_ASSERT_MESSAGE("Failed to move the note from source.", !pGoneNote);
5160 ScPostIt* pMoveNote = m_pDoc->GetNote(aMovePos);
5161 CPPUNIT_ASSERT_MESSAGE("Failed to move the note to destination.", pMoveNote);
5163 // The caption object should not be identical, it was newly created upon
5164 // Drop from clipboard.
5165 // pOrigCaption is a dangling pointer.
5166 const SdrCaptionObj* pMoveCaption = pMoveNote->GetOrCreateCaption(aMovePos);
5167 CPPUNIT_ASSERT_MESSAGE("Captions identical after move.", pOrigCaption != pMoveCaption);
5169 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5170 CPPUNIT_ASSERT(pUndoMgr);
5171 pUndoMgr->Undo(); // this should not crash ... tdf#92995
5173 // Verify the note move Undo.
5174 pMoveNote = m_pDoc->GetNote(aMovePos);
5175 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move from destination.", !pMoveNote);
5176 pOrigNote = m_pDoc->GetNote(aOrigPos);
5177 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move to source.", pOrigNote);
5179 // The caption object still should not be identical.
5180 // pMoveCaption is a dangling pointer.
5181 pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5182 CPPUNIT_ASSERT_MESSAGE("Captions identical after move undo.", pOrigCaption != pMoveCaption);
5185 // Create a note at B4, merge B4 and B5 with ScUndoMerge, and Undo.
5187 ScAddress aPosB4(1,3,0);
5188 ScPostIt* pNoteB4 = m_pDoc->GetOrCreateNote(aPosB4);
5189 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B4.", pNoteB4);
5190 const SdrCaptionObj* pCaptionB4 = pNoteB4->GetOrCreateCaption(aPosB4);
5191 ScCellMergeOption aCellMergeOption(1,3,2,3);
5192 rDocFunc.MergeCells( aCellMergeOption, true /*bContents*/, bRecord, bApi, false /*bEmptyMergedCells*/ );
5194 SfxUndoManager* pMergeUndoManager = m_pDoc->GetUndoManager();
5195 CPPUNIT_ASSERT(pMergeUndoManager);
5196 pMergeUndoManager->Undo(); // this should not crash ... tdf#105667
5198 // Undo contained the original caption object pointer which was still alive
5199 // at B4 after the merge and not cloned nor recreated during Undo.
5200 ScPostIt* pUndoNoteB4 = m_pDoc->GetNote(aPosB4);
5201 CPPUNIT_ASSERT_MESSAGE("No cell comment at B4 after Undo.", pUndoNoteB4);
5202 const SdrCaptionObj* pUndoCaptionB4 = pUndoNoteB4->GetCaption();
5203 CPPUNIT_ASSERT_EQUAL_MESSAGE("Captions not identical after Merge Undo.", pCaptionB4, pUndoCaptionB4);
5206 // In a second document copy a note from B5 to clipboard, close the
5207 // document and then paste the note into this document.
5209 ScDocShellRef xDocSh2;
5210 getNewDocShell(xDocSh2);
5211 ScDocument* pDoc2 = &xDocSh2->GetDocument();
5212 pDoc2->InsertTab(0, "OtherSheet1");
5213 pDoc2->InitDrawLayer(xDocSh2.get());
5215 ScAddress aPosB5(1,4,0);
5216 ScPostIt* pOtherNoteB5 = pDoc2->GetOrCreateNote(aPosB5);
5217 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B5.", pOtherNoteB5);
5218 const SdrCaptionObj* pOtherCaptionB5 = pOtherNoteB5->GetOrCreateCaption(aPosB5);
5219 CPPUNIT_ASSERT_MESSAGE("No caption at B5.", pOtherCaptionB5);
5221 ScDocument aClipDoc2(SCDOCMODE_CLIP);
5222 copyToClip( pDoc2, aPosB5, &aClipDoc2);
5224 // There's no ScTransferObject involved in the "fake" clipboard copy
5225 // and ScDocument dtor asking IsClipboardSource() gets no, so emulate
5226 // the part that normally is responsible for forgetting the caption
5227 // objects.
5228 aClipDoc2.ClosingClipboardSource();
5230 pDoc2->DeleteTab(0);
5231 xDocSh2->DoClose();
5232 xDocSh2.clear();
5234 pasteFromClip( m_pDoc, aPosB5, &aClipDoc2); // should not crash... tdf#104967
5235 ScPostIt* pNoteB5 = m_pDoc->GetNote(aPosB5);
5236 CPPUNIT_ASSERT_MESSAGE("Failed to paste cell comment at B5.", pNoteB5);
5237 const SdrCaptionObj* pCaptionB5 = pNoteB5->GetOrCreateCaption(aPosB5);
5238 CPPUNIT_ASSERT_MESSAGE("No caption at pasted B5.", pCaptionB5);
5239 // Do not test if pCaptionB5 != pOtherCaptionB5 because since pDoc2
5240 // has been closed and the caption been deleted objects *may* be
5241 // allocated at the very same memory location.
5244 m_pDoc->DeleteTab(0);
5247 void Test::testNoteCopyPaste()
5249 m_pDoc->InsertTab(0, "Test");
5251 // We need a drawing layer in order to create caption objects.
5252 m_pDoc->InitDrawLayer(m_xDocShell.get());
5254 // Insert in B2 a text and cell comment.
5255 ScAddress aPos(1,1,0);
5256 m_pDoc->SetString(aPos, "Text");
5257 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5258 CPPUNIT_ASSERT(pNote);
5259 pNote->SetText(aPos, "Note1");
5261 // Insert in B4 a number and cell comment.
5262 aPos.SetRow(3);
5263 m_pDoc->SetValue(aPos, 1.1);
5264 pNote = m_pDoc->GetOrCreateNote(aPos);
5265 CPPUNIT_ASSERT(pNote);
5266 pNote->SetText(aPos, "Note2");
5268 // Copy B2:B4 to clipboard.
5269 ScMarkData aMark(m_pDoc->GetSheetLimits());
5270 aMark.SelectOneTable(0);
5271 ScRange aCopyRange(1,1,0,1,3,0);
5272 ScDocument aClipDoc(SCDOCMODE_CLIP);
5273 aClipDoc.ResetClip(m_pDoc, &aMark);
5274 ScClipParam aClipParam(aCopyRange, false);
5275 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
5277 // Make sure the notes are in the clipboard.
5278 pNote = aClipDoc.GetNote(ScAddress(1,1,0));
5279 CPPUNIT_ASSERT(pNote);
5280 CPPUNIT_ASSERT_EQUAL(OUString("Note1"), pNote->GetText());
5282 pNote = aClipDoc.GetNote(ScAddress(1,3,0));
5283 CPPUNIT_ASSERT(pNote);
5284 CPPUNIT_ASSERT_EQUAL(OUString("Note2"), pNote->GetText());
5286 // Paste to B6:B8 but only cell notes.
5287 ScRange aDestRange(1,5,0,1,7,0);
5288 m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::NOTE, nullptr, &aClipDoc);
5290 // Make sure the notes are there.
5291 pNote = m_pDoc->GetNote(ScAddress(1,5,0));
5292 CPPUNIT_ASSERT(pNote);
5293 CPPUNIT_ASSERT_EQUAL(OUString("Note1"), pNote->GetText());
5295 pNote = m_pDoc->GetNote(ScAddress(1,7,0));
5296 CPPUNIT_ASSERT(pNote);
5297 CPPUNIT_ASSERT_EQUAL(OUString("Note2"), pNote->GetText());
5299 // Test that GetNotesInRange includes the end of its range
5300 // and so can find the note
5301 std::vector<sc::NoteEntry> aNotes;
5302 m_pDoc->GetNotesInRange(ScRange(1,7,0), aNotes);
5303 CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
5305 m_pDoc->DeleteTab(0);
5308 // tdf#112454
5309 void Test::testNoteContainsNotesInRange() {
5310 m_pDoc->InsertTab(0, "PostIts");
5312 // We need a drawing layer in order to create caption objects.
5313 m_pDoc->InitDrawLayer(m_xDocShell.get());
5315 ScAddress aAddr(2, 2, 0); // cell C3
5317 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in a document that doesn't have any.",
5318 !m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5320 m_pDoc->GetOrCreateNote(aAddr);
5322 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in range that doesn't have any.",
5323 !m_pDoc->ContainsNotesInRange(ScRange(ScAddress(0, 0, 0), ScAddress(0, 1, 0))));
5324 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies on border of range.",
5325 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5326 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies in inner area of range.",
5327 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), ScAddress(3, 3, 0)))));
5330 void Test::testAreasWithNotes()
5332 m_pDoc->InsertTab(0, "Sheet1");
5334 // We need a drawing layer in order to create caption objects.
5335 m_pDoc->InitDrawLayer(m_xDocShell.get());
5337 ScAddress rAddr(1, 5, 0);
5338 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5339 pNote->SetText(rAddr, "Hello");
5340 pNote->SetAuthor("Jim Bob");
5341 ScAddress rAddrMin(2, 2, 0);
5342 ScPostIt* pNoteMin = m_pDoc->GetOrCreateNote(rAddrMin);
5343 pNoteMin->SetText(rAddrMin, "Hello");
5345 SCCOL col;
5346 SCROW row;
5347 bool dataFound;
5349 // only cell notes (empty content)
5351 dataFound = m_pDoc->GetDataStart(0,col,row);
5353 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5354 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col for notes", static_cast<SCCOL>(1), col);
5355 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row for notes", static_cast<SCROW>(2), row);
5357 dataFound = m_pDoc->GetCellArea(0,col,row);
5359 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5360 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col for notes", static_cast<SCCOL>(2), col);
5361 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row for notes", static_cast<SCROW>(5), row);
5363 bool bNotes = true;
5364 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5366 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5367 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col for notes", static_cast<SCCOL>(2), col);
5368 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row for notes", static_cast<SCROW>(5), row);
5370 bNotes = false;
5371 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5372 CPPUNIT_ASSERT_MESSAGE("No PrintArea should be found", !dataFound);
5374 bNotes = true;
5375 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5376 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5377 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(5), row);
5379 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5380 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5381 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(2), row);
5383 bNotes = false;
5384 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // col 0 & 1
5385 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer should be found", !dataFound);
5387 // now add cells with value, check that notes are taken into account in good cases
5389 m_pDoc->SetString(0, 3, 0, "Some Text");
5390 m_pDoc->SetString(3, 3, 0, "Some Text");
5391 m_pDoc->FetchTable(0)->InvalidateCellArea();
5393 dataFound = m_pDoc->GetDataStart(0,col,row);
5395 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5396 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col", static_cast<SCCOL>(0), col);
5397 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row", static_cast<SCROW>(2), row);
5399 dataFound = m_pDoc->GetCellArea(0,col,row);
5401 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5402 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col", static_cast<SCCOL>(3), col);
5403 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row", static_cast<SCROW>(5), row);
5405 bNotes = true;
5406 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5408 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5409 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5410 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(5), row);
5412 bNotes = false;
5413 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5414 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5415 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5416 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(3), row);
5418 bNotes = true;
5419 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5420 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5421 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(5), row);
5423 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5424 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5425 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5427 bNotes = false;
5428 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5429 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5430 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5432 m_pDoc->DeleteTab(0);
5435 void Test::testAnchoredRotatedShape()
5437 m_pDoc->InsertTab(0, "TestTab");
5438 SCROW nRow1, nRow2;
5439 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5440 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
5441 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
5442 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
5444 m_pDoc->InitDrawLayer();
5445 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5446 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
5447 SdrPage* pPage = pDrawLayer->GetPage(0);
5448 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
5449 m_pDoc->SetRowHeightRange(0, m_pDoc->MaxRow(), 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5450 constexpr tools::Long TOLERANCE = 30; //30 hmm
5451 for ( SCCOL nCol = 0; nCol < m_pDoc->MaxCol(); ++nCol )
5452 m_pDoc->SetColWidth(nCol, 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5454 //Add a rect
5455 tools::Rectangle aRect( 4000, 5000, 10000, 7000 );
5457 tools::Rectangle aRotRect( 6000, 3000, 8000, 9000 );
5458 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aRect);
5459 pPage->InsertObject(pObj.get());
5460 Point aRef1(pObj->GetSnapRect().Center());
5461 Degree100 nAngle = 9000_deg100; //90 deg.
5462 double nSin = 1.0; // sin(90 deg)
5463 double nCos = 0.0; // cos(90 deg)
5464 pObj->Rotate(aRef1,nAngle,nSin,nCos);
5466 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
5468 tools::Rectangle aSnap = pObj->GetSnapRect();
5469 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5470 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5471 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Left(), aSnap.Left(), TOLERANCE );
5472 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Top(), aSnap.Top(), TOLERANCE );
5474 ScDrawObjData aAnchor;
5475 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj.get() );
5476 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5478 aAnchor.maStart = pData->maStart;
5479 aAnchor.maEnd = pData->maEnd;
5481 m_pDoc->SetDrawPageSize(0);
5483 // increase row 5 by 2000 hmm
5484 m_pDoc->SetRowHeight(5, 0, o3tl::toTwips(3000, o3tl::Length::mm100));
5485 // increase col 6 by 1000 hmm
5486 m_pDoc->SetColWidth(6, 0, o3tl::toTwips(2000, o3tl::Length::mm100));
5488 aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
5489 aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
5491 m_pDoc->SetDrawPageSize(0);
5493 aSnap = pObj->GetSnapRect();
5495 // ensure that width and height have been adjusted accordingly
5496 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5497 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5499 // ensure that anchor start and end addresses haven't changed
5500 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
5501 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
5502 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
5503 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
5505 m_pDoc->DeleteTab(0);
5508 void Test::testCellTextWidth()
5510 m_pDoc->InsertTab(0, "Test");
5512 ScAddress aTopCell(0, 0, 0);
5514 // Sheet is empty.
5515 std::unique_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5516 CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
5518 // Sheet only has one cell.
5519 m_pDoc->SetString(0, 0, 0, "Only one cell");
5520 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5521 CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
5522 CPPUNIT_ASSERT_EQUAL(SCROW(0), pIter->getPos());
5524 // Setting a text width here should commit it to the column.
5525 sal_uInt16 nTestVal = 432;
5526 pIter->setValue(nTestVal);
5527 CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
5529 // Set values to row 2 through 6.
5530 for (SCROW i = 2; i <= 6; ++i)
5531 m_pDoc->SetString(0, i, 0, "foo");
5533 // Set values to row 10 through 18.
5534 for (SCROW i = 10; i <= 18; ++i)
5535 m_pDoc->SetString(0, i, 0, "foo");
5538 // Full range.
5539 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5540 SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
5541 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5543 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5544 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5546 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5550 // Specify start and end rows (6 - 16)
5551 ScAddress aStart = aTopCell;
5552 aStart.SetRow(6);
5553 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
5554 SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
5555 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5557 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5558 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5560 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5563 // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
5564 clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
5567 // Full range again.
5568 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5569 SCROW aRows[] = { 0, 2, 18 };
5570 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5572 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5573 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5575 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5578 // Delete row 2 which shifts all cells below row 2 upward. After this, we
5579 // should only have cells at rows 0 and 17.
5580 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), MAXTAB, 2, 1);
5582 // Full range again.
5583 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5584 SCROW aRows[] = { 0, 17 };
5585 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5587 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5588 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5590 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5593 m_pDoc->DeleteTab(0);
5596 static bool checkEditTextIterator(sc::EditTextIterator& rIter, const char** pChecks)
5598 const EditTextObject* pText = rIter.first();
5599 const char* p = *pChecks;
5601 for (int i = 0; i < 100; ++i) // cap it to 100 loops.
5603 if (!pText)
5604 // No more edit cells. The check string array should end too.
5605 return p == nullptr;
5607 if (!p)
5608 // More edit cell, but no more check string. Bad.
5609 return false;
5611 if (pText->GetParagraphCount() != 1)
5612 // For this test, we don't handle multi-paragraph text.
5613 return false;
5615 if (pText->GetText(0) != OUString::createFromAscii(p))
5616 // Text differs from what's expected.
5617 return false;
5619 pText = rIter.next();
5620 ++pChecks;
5621 p = *pChecks;
5624 return false;
5627 void Test::testEditTextIterator()
5629 m_pDoc->InsertTab(0, "Test");
5632 // First, try with an empty sheet.
5633 sc::EditTextIterator aIter(*m_pDoc,0);
5634 const char* pChecks[] = { nullptr };
5635 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5638 ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
5641 // Only set one edit cell.
5642 rEditEngine.SetTextCurrentDefaults("A2");
5643 m_pDoc->SetEditText(ScAddress(0,1,0), rEditEngine.CreateTextObject());
5644 sc::EditTextIterator aIter(*m_pDoc,0);
5645 const char* pChecks[] = { "A2", nullptr };
5646 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5650 // Add a series of edit cells.
5651 rEditEngine.SetTextCurrentDefaults("A5");
5652 m_pDoc->SetEditText(ScAddress(0,4,0), rEditEngine.CreateTextObject());
5653 rEditEngine.SetTextCurrentDefaults("A6");
5654 m_pDoc->SetEditText(ScAddress(0,5,0), rEditEngine.CreateTextObject());
5655 rEditEngine.SetTextCurrentDefaults("A7");
5656 m_pDoc->SetEditText(ScAddress(0,6,0), rEditEngine.CreateTextObject());
5657 sc::EditTextIterator aIter(*m_pDoc,0);
5658 const char* pChecks[] = { "A2", "A5", "A6", "A7", nullptr };
5659 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5663 // Add more edit cells to column C. Skip column B.
5664 rEditEngine.SetTextCurrentDefaults("C1");
5665 m_pDoc->SetEditText(ScAddress(2,0,0), rEditEngine.CreateTextObject());
5666 rEditEngine.SetTextCurrentDefaults("C3");
5667 m_pDoc->SetEditText(ScAddress(2,2,0), rEditEngine.CreateTextObject());
5668 rEditEngine.SetTextCurrentDefaults("C4");
5669 m_pDoc->SetEditText(ScAddress(2,3,0), rEditEngine.CreateTextObject());
5670 sc::EditTextIterator aIter(*m_pDoc,0);
5671 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5672 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5676 // Add some numeric, string and formula cells. This shouldn't affect the outcome.
5677 m_pDoc->SetString(ScAddress(0,99,0), "=ROW()");
5678 m_pDoc->SetValue(ScAddress(1,3,0), 1.2);
5679 m_pDoc->SetString(ScAddress(2,4,0), "Simple string");
5680 sc::EditTextIterator aIter(*m_pDoc,0);
5681 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5682 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5685 m_pDoc->DeleteTab(0);
5688 void Test::testImportStream()
5690 sc::AutoCalcSwitch aAC(*m_pDoc, true); // turn on auto calc.
5691 sc::UndoSwitch aUndo(*m_pDoc, true); // enable undo.
5693 m_pDoc->InsertTab(0, "Test");
5695 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(A1:C1)"); // A2
5697 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5699 // CSV import options.
5700 ScAsciiOptions aOpt;
5701 aOpt.SetFieldSeps(",");
5703 // Import values to A1:C1.
5704 ScImportExport aObj(*m_pDoc, ScAddress(0,0,0));
5705 aObj.SetImportBroadcast(true);
5706 aObj.SetExtOptions(aOpt);
5707 aObj.ImportString("1,2,3", SotClipboardFormatId::STRING);
5709 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5710 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5711 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5713 // Formula value should have been updated.
5714 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5716 // Undo, and check the result.
5717 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5718 CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
5719 pUndoMgr->Undo();
5721 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5722 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5723 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5725 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5727 // Redo, and check the result.
5728 pUndoMgr->Redo();
5730 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5731 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5732 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5734 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5736 pUndoMgr->Clear();
5738 m_pDoc->DeleteTab(0);
5741 void Test::testDeleteContents()
5743 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
5744 sc::UndoSwitch aUndoSwitch(*m_pDoc, true); // enable undo.
5746 m_pDoc->InsertTab(0, "Test");
5748 m_pDoc->SetValue(ScAddress(3,1,0), 1.0);
5749 m_pDoc->SetValue(ScAddress(3,2,0), 1.0);
5750 m_pDoc->SetValue(ScAddress(3,3,0), 1.0);
5751 m_pDoc->SetValue(ScAddress(3,4,0), 1.0);
5752 m_pDoc->SetValue(ScAddress(3,5,0), 1.0);
5753 m_pDoc->SetValue(ScAddress(3,6,0), 1.0);
5754 m_pDoc->SetValue(ScAddress(3,7,0), 1.0);
5755 m_pDoc->SetValue(ScAddress(3,8,0), 1.0);
5756 m_pDoc->SetString(ScAddress(3,15,0), "=SUM(D2:D15)");
5758 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5760 // Delete D2:D6.
5761 ScRange aRange(3,1,0,3,5,0);
5762 ScMarkData aMark(m_pDoc->GetSheetLimits());
5763 aMark.SelectOneTable(0);
5764 aMark.SetMarkArea(aRange);
5766 ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
5767 pUndoDoc->InitUndo(*m_pDoc, 0, 0);
5768 m_pDoc->CopyToDocument(aRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc, &aMark);
5769 ScUndoDeleteContents aUndo(m_xDocShell.get(), aMark, aRange, std::move(pUndoDoc), false, InsertDeleteFlags::CONTENTS, true);
5771 clearRange(m_pDoc, aRange);
5772 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5774 aUndo.Undo();
5775 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5777 aUndo.Redo();
5778 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5780 m_pDoc->DeleteTab(0);
5783 void Test::testTransliterateText()
5785 m_pDoc->InsertTab(0, "Test");
5787 // Set texts to A1:A3.
5788 m_pDoc->SetString(ScAddress(0,0,0), "Mike");
5789 m_pDoc->SetString(ScAddress(0,1,0), "Noah");
5790 m_pDoc->SetString(ScAddress(0,2,0), "Oscar");
5792 // Change them to uppercase.
5793 ScMarkData aMark(m_pDoc->GetSheetLimits());
5794 aMark.SetMarkArea(ScRange(0,0,0,0,2,0));
5795 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5796 rFunc.TransliterateText(
5797 aMark, TransliterationFlags::LOWERCASE_UPPERCASE, true);
5799 CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5800 CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5801 CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5803 // Test the undo and redo.
5804 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5805 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5807 pUndoMgr->Undo();
5808 CPPUNIT_ASSERT_EQUAL(OUString("Mike"), m_pDoc->GetString(ScAddress(0,0,0)));
5809 CPPUNIT_ASSERT_EQUAL(OUString("Noah"), m_pDoc->GetString(ScAddress(0,1,0)));
5810 CPPUNIT_ASSERT_EQUAL(OUString("Oscar"), m_pDoc->GetString(ScAddress(0,2,0)));
5812 pUndoMgr->Redo();
5813 CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5814 CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5815 CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5817 m_pDoc->DeleteTab(0);
5820 void Test::testFormulaToValue()
5822 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5823 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5825 m_pDoc->InsertTab(0, "Test");
5827 std::vector<std::vector<const char*>> aData = {
5828 { "=1", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5829 { "=2", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5830 { "=3", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5831 { "=4", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5832 { "=5", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5833 { "=6", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5836 ScAddress aPos(1,2,0); // B3
5837 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5838 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5841 // Expected output table content. 0 = empty cell
5842 std::vector<std::vector<const char*>> aOutputCheck = {
5843 { "1", "2", "TRUE" },
5844 { "2", "4", "TRUE" },
5845 { "3", "6", "TRUE" },
5846 { "4", "8", "TRUE" },
5847 { "5", "10", "TRUE" },
5848 { "6", "12", "TRUE" },
5851 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5852 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5855 // Convert B5:C6 to static values, and check the result.
5856 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5857 ScRange aConvRange(1,4,0,2,5,0); // B5:C6
5858 rFunc.ConvertFormulaToValue(aConvRange, false);
5861 // Expected output table content. 0 = empty cell
5862 std::vector<std::vector<const char*>> aOutputCheck = {
5863 { "1", "2", "TRUE" },
5864 { "2", "4", "TRUE" },
5865 { "3", "6", "FALSE" },
5866 { "4", "8", "FALSE" },
5867 { "5", "10", "TRUE" },
5868 { "6", "12", "TRUE" },
5871 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5872 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5875 // Make sure that B3:B4 and B7:B8 are formula cells.
5876 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5877 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5878 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5879 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5881 // Make sure that B5:C6 are numeric cells.
5882 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5883 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5884 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5885 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5887 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5888 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5889 CPPUNIT_ASSERT(pFC);
5890 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5891 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5892 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5893 CPPUNIT_ASSERT(pFC);
5894 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5895 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5897 // Undo and check.
5898 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5899 CPPUNIT_ASSERT(pUndoMgr);
5900 pUndoMgr->Undo();
5903 // Expected output table content. 0 = empty cell
5904 std::vector<std::vector<const char*>> aOutputCheck = {
5905 { "1", "2", "TRUE" },
5906 { "2", "4", "TRUE" },
5907 { "3", "6", "TRUE" },
5908 { "4", "8", "TRUE" },
5909 { "5", "10", "TRUE" },
5910 { "6", "12", "TRUE" },
5913 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
5914 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5917 // B3:B8 should all be (ungrouped) formula cells.
5918 for (SCROW i = 2; i <= 7; ++i)
5920 pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
5921 CPPUNIT_ASSERT(pFC);
5922 CPPUNIT_ASSERT(!pFC->IsShared());
5925 // C3:C8 should be shared formula cells.
5926 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5927 CPPUNIT_ASSERT(pFC);
5928 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5929 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
5931 // Redo and check.
5932 pUndoMgr->Redo();
5934 // Expected output table content. 0 = empty cell
5935 std::vector<std::vector<const char*>> aOutputCheck = {
5936 { "1", "2", "TRUE" },
5937 { "2", "4", "TRUE" },
5938 { "3", "6", "FALSE" },
5939 { "4", "8", "FALSE" },
5940 { "5", "10", "TRUE" },
5941 { "6", "12", "TRUE" },
5944 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5945 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5948 // Make sure that B3:B4 and B7:B8 are formula cells.
5949 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5950 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5951 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5952 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5954 // Make sure that B5:C6 are numeric cells.
5955 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5956 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5957 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5958 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5960 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5961 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5962 CPPUNIT_ASSERT(pFC);
5963 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5964 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5965 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5966 CPPUNIT_ASSERT(pFC);
5967 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5968 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5970 // Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
5971 pUndoMgr->Undo();
5972 m_pDoc->SetValue(ScAddress(1,4,0), 10);
5973 m_pDoc->SetValue(ScAddress(1,5,0), 11);
5974 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
5975 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
5977 m_pDoc->DeleteTab(0);
5980 void Test::testFormulaToValue2()
5982 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5983 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5985 m_pDoc->InsertTab(0, "Test");
5987 std::vector<std::vector<const char*>> aData = {
5988 { "=1", "=ISFORMULA(RC[-1])" },
5989 { "=2", "=ISFORMULA(RC[-1])" },
5990 { "3", "=ISFORMULA(RC[-1])" },
5991 { "=4", "=ISFORMULA(RC[-1])" },
5992 { "=5", "=ISFORMULA(RC[-1])" },
5995 // Insert data into B2:C6.
5996 ScAddress aPos(1,1,0); // B2
5997 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5998 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
6001 // Expected output table content. 0 = empty cell
6002 std::vector<std::vector<const char*>> aOutputCheck = {
6003 { "1", "TRUE" },
6004 { "2", "TRUE" },
6005 { "3", "FALSE" },
6006 { "4", "TRUE" },
6007 { "5", "TRUE" },
6010 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6011 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6014 // Convert B3:B5 to a value.
6015 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6016 ScRange aConvRange(1,2,0,1,4,0); // B3:B5
6017 rFunc.ConvertFormulaToValue(aConvRange, false);
6020 // Expected output table content. 0 = empty cell
6021 std::vector<std::vector<const char*>> aOutputCheck = {
6022 { "1", "TRUE" },
6023 { "2", "FALSE" },
6024 { "3", "FALSE" },
6025 { "4", "FALSE" },
6026 { "5", "TRUE" },
6029 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6030 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6033 // Undo and check.
6034 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6035 CPPUNIT_ASSERT(pUndoMgr);
6036 pUndoMgr->Undo();
6039 // Expected output table content. 0 = empty cell
6040 std::vector<std::vector<const char*>> aOutputCheck = {
6041 { "1", "TRUE" },
6042 { "2", "TRUE" },
6043 { "3", "FALSE" },
6044 { "4", "TRUE" },
6045 { "5", "TRUE" },
6048 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6049 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6052 m_pDoc->DeleteTab(0);
6055 void Test::testColumnFindEditCells()
6057 m_pDoc->InsertTab(0, "Test");
6059 // Test the basics with real edit cells, using Column A.
6061 SCROW nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
6062 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6063 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,0,0));
6064 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6065 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,10,0));
6066 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6068 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
6069 rEE.SetTextCurrentDefaults("Test");
6070 m_pDoc->SetEditText(ScAddress(0,0,0), rEE.CreateTextObject());
6071 const EditTextObject* pObj = m_pDoc->GetEditText(ScAddress(0,0,0));
6072 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell here.", pObj);
6074 ScRange aRange(0,0,0,0,0,0);
6075 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6076 CPPUNIT_ASSERT_EQUAL_MESSAGE("There is an edit cell here.", SCROW(0), nResRow);
6078 aRange.aStart.SetRow(1);
6079 aRange.aEnd.SetRow(1);
6080 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6081 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6083 aRange.aStart.SetRow(2);
6084 aRange.aEnd.SetRow(4);
6085 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6086 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6088 aRange.aStart.SetRow(0);
6089 aRange.aEnd.SetRow(m_pDoc->MaxRow());
6090 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6091 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an edit cell in specified range.", SCROW(0), nResRow);
6093 m_pDoc->SetString(ScAddress(0,0,0), "Test");
6094 m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
6095 ScRefCellValue aCell;
6096 aCell.assign(*m_pDoc, ScAddress(0,0,0));
6097 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string cell.", CELLTYPE_STRING, aCell.getType());
6098 aCell.assign(*m_pDoc, ScAddress(0,1,0));
6099 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6100 aCell.assign(*m_pDoc, ScAddress(0,2,0));
6101 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a numeric cell.", CELLTYPE_VALUE, aCell.getType());
6102 aCell.assign(*m_pDoc, ScAddress(0,3,0));
6103 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6105 aRange.aStart.SetRow(1);
6106 aRange.aEnd.SetRow(1);
6107 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6108 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6110 // Test with non-edit cell but with ambiguous script type.
6112 m_pDoc->SetString(ScAddress(1,11,0), "Some text");
6113 m_pDoc->SetString(ScAddress(1,12,0), "Some text");
6114 m_pDoc->SetString(ScAddress(1,13,0), "Other text");
6116 m_pDoc->SetScriptType(ScAddress(1,11,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6117 m_pDoc->SetScriptType(ScAddress(1,12,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6118 m_pDoc->SetScriptType(ScAddress(1,13,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6120 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,11,0));
6121 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), nResRow);
6122 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,12,0));
6123 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), nResRow);
6125 for (SCROW i = 0; i <= 5; ++i)
6126 m_pDoc->SetString(ScAddress(2,i,0), "Text");
6128 m_pDoc->SetScriptType(ScAddress(2,5,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6130 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(2,1,0));
6131 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(-1), nResRow);
6133 m_pDoc->DeleteTab(0);
6137 void Test::testSetFormula()
6139 m_pDoc->InsertTab(0, "Test");
6141 static struct aInputs
6143 SCROW nRow;
6144 SCCOL nCol;
6145 const char* aFormula1; // Represents the formula that is input to SetFormula function.
6146 const char* aFormula2; // Represents the formula that is actually stored in the cell.
6147 formula::FormulaGrammar::Grammar const eGram;
6149 } const aTest[] = {
6150 { 5 , 4 , "=SUM($D$2:$F$3)" ,"=SUM($D$2:$F$3)" , formula::FormulaGrammar::Grammar::GRAM_ENGLISH },
6151 { 5 , 5 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE },
6152 { 6 , 6 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE_XL_A1},
6153 { 7 , 8 , "=[.A1]-[.$C2]+[.G$3]-[.$F$4]","=A1-$C2+G$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_ODFF }
6156 for(size_t i = 0; i < SAL_N_ELEMENTS(aTest); ++i)
6158 m_pDoc->SetFormula(ScAddress(aTest[i].nCol, aTest[i].nRow, 0), OUString::createFromAscii(aTest[i].aFormula1), aTest[i].eGram);
6159 OUString aBuffer = m_pDoc->GetFormula(aTest[i].nCol, aTest[i].nRow, 0);
6161 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set formula", OUString::createFromAscii(aTest[i].aFormula2), aBuffer);
6164 m_pDoc->DeleteTab(0);
6167 void Test::testMultipleDataCellsInRange()
6169 m_pDoc->InsertTab(0, "Test");
6171 ScRange aRange(1,2,0); // B3
6172 sc::MultiDataCellState aState = m_pDoc->HasMultipleDataCells(aRange);
6173 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::Empty, aState.meState);
6175 // Set a numeric value to B3.
6176 m_pDoc->SetValue(ScAddress(1,2,0), 1.0);
6177 aState = m_pDoc->HasMultipleDataCells(aRange);
6178 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6179 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6180 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6182 // Set another numeric value to B4.
6183 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
6184 aRange.aEnd.SetRow(3); // B3:B4
6185 aState = m_pDoc->HasMultipleDataCells(aRange);
6186 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6187 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6188 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6190 // Set the query range to B4:B5. Now it should only report one cell, with
6191 // B4 being the first non-empty cell.
6192 aRange.aStart.SetRow(3);
6193 aRange.aEnd.SetRow(4);
6194 aState = m_pDoc->HasMultipleDataCells(aRange);
6195 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6196 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6197 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6199 // Set the query range to A1:C3. The first non-empty cell should be B3.
6200 aRange = ScRange(0,0,0,2,2,0);
6201 aState = m_pDoc->HasMultipleDataCells(aRange);
6202 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6203 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6204 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6206 // Set string cells to D4 and F5, and query D3:F5. D4 should be the first
6207 // non-empty cell.
6208 m_pDoc->SetString(ScAddress(3,3,0), "foo");
6209 m_pDoc->SetString(ScAddress(5,4,0), "bar");
6210 aRange = ScRange(3,2,0,5,4,0);
6211 aState = m_pDoc->HasMultipleDataCells(aRange);
6212 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6213 CPPUNIT_ASSERT_EQUAL(SCCOL(3), aState.mnCol1);
6214 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6216 // TODO : add more test cases as needed.
6218 m_pDoc->DeleteTab(0);
6221 void Test::testFormulaWizardSubformula()
6223 m_pDoc->InsertTab(0, "Test");
6225 m_pDoc->SetString(ScAddress(1,0,0), "=1"); // B1
6226 m_pDoc->SetString(ScAddress(1,1,0), "=1/0"); // B2
6227 m_pDoc->SetString(ScAddress(1,2,0), "=gibberish"); // B3
6229 ScSimpleFormulaCalculator aFCell1( *m_pDoc, ScAddress(0,0,0), "=B1:B3", true );
6230 FormulaError nErrCode = aFCell1.GetErrCode();
6231 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell1.IsMatrix() );
6232 CPPUNIT_ASSERT_EQUAL( OUString("{1|#DIV/0!|#NAME?}"), aFCell1.GetString().getString() );
6234 m_pDoc->SetString(ScAddress(1,0,0), "=NA()"); // B1
6235 m_pDoc->SetString(ScAddress(1,1,0), "2"); // B2
6236 m_pDoc->SetString(ScAddress(1,2,0), "=1+2"); // B3
6237 ScSimpleFormulaCalculator aFCell2( *m_pDoc, ScAddress(0,0,0), "=B1:B3", true );
6238 nErrCode = aFCell2.GetErrCode();
6239 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell2.IsMatrix() );
6240 CPPUNIT_ASSERT_EQUAL( OUString("{#N/A|2|3}"), aFCell2.GetString().getString() );
6242 m_pDoc->DeleteTab(0);
6245 void Test::testDiagonalBorders()
6247 m_pDoc->InsertTab(0, "Diagonal");
6249 ScAddress aPos;
6250 const editeng::SvxBorderLine* pLine;
6251 const ScPatternAttr* pPat;
6253 // diagonal down border
6254 ::editeng::SvxBorderLine dDownBorderLine(nullptr, 1);
6255 SvxLineItem dDownLineItem(ATTR_BORDER_TLBR);
6256 dDownLineItem.SetLine(&dDownBorderLine);
6258 // set diagonal down border to cell(A1)
6259 m_pDoc->ApplyAttr(0, 0, 0, dDownLineItem);
6261 aPos = { 0, 0, 0 };
6262 pPat = m_pDoc->GetPattern(aPos);
6263 CPPUNIT_ASSERT(pPat);
6265 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6266 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6268 // diagonal up border
6269 ::editeng::SvxBorderLine dUpBorderLine(nullptr, 1);
6270 SvxLineItem dUpLineItem(ATTR_BORDER_BLTR);
6271 dUpLineItem.SetLine(&dUpBorderLine);
6273 // set diagonal up border to cell(A2)
6274 m_pDoc->ApplyAttr(0, 1, 0, dUpLineItem);
6276 aPos = { 0, 1, 0 };
6277 pPat = m_pDoc->GetPattern(aPos);
6278 CPPUNIT_ASSERT(pPat);
6280 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6281 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6283 // diagonal down and up border in the same cell (A5)
6284 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6285 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6287 // test if both borders are applied successfully in the same cell (A5)
6288 aPos = { 0, 4, 0 };
6289 pPat = m_pDoc->GetPattern(aPos);
6290 CPPUNIT_ASSERT(pPat);
6292 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6293 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6294 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6295 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6297 // test if both borders are removed successfully
6298 dDownLineItem.SetLine(nullptr);
6299 dUpLineItem.SetLine(nullptr);
6301 // SetLine(nullptr) should remove the lines from (A5)
6302 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6303 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6305 pPat = m_pDoc->GetPattern(aPos);
6306 CPPUNIT_ASSERT(pPat);
6308 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6309 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was not expected, but is found!", !pLine);
6310 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6311 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was not expected, but is found!", !pLine);
6313 m_pDoc->DeleteTab(0);
6316 void Test::testWholeDocBorders()
6318 m_pDoc->InsertTab(0, "Borders");
6320 // Set outside border to be on all sides, and inside borders to be only vertical.
6321 // This should result in edge borders of the spreadsheets being set, but internal
6322 // borders between cells should be only vertical, not horizontal.
6323 ::editeng::SvxBorderLine line(nullptr, 50, SvxBorderLineStyle::SOLID);
6324 SvxBoxItem borderItem(ATTR_BORDER);
6325 borderItem.SetLine(&line, SvxBoxItemLine::LEFT);
6326 borderItem.SetLine(&line, SvxBoxItemLine::RIGHT);
6327 borderItem.SetLine(&line, SvxBoxItemLine::TOP);
6328 borderItem.SetLine(&line, SvxBoxItemLine::BOTTOM);
6329 SvxBoxInfoItem boxInfoItem(ATTR_BORDER);
6330 boxInfoItem.SetLine(&line, SvxBoxInfoItemLine::VERT);
6332 ScMarkData mark( m_pDoc->GetSheetLimits(), ScRange( 0, 0, 0, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 ));
6333 m_pDoc->ApplySelectionFrame( mark, borderItem, &boxInfoItem );
6335 const ScPatternAttr* attr;
6336 attr = m_pDoc->GetPattern( 0, 0, 0 );
6337 CPPUNIT_ASSERT(attr);
6338 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6339 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6340 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6341 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6343 attr = m_pDoc->GetPattern( 1, 0, 0 );
6344 CPPUNIT_ASSERT(attr);
6345 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6346 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6347 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6348 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6350 attr = m_pDoc->GetPattern( 0, 1, 0 );
6351 CPPUNIT_ASSERT(attr);
6352 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6353 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6354 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6355 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6357 attr = m_pDoc->GetPattern( 1, 1, 0 );
6358 CPPUNIT_ASSERT(attr);
6359 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6360 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6361 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6362 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6364 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), 0, 0 );
6365 CPPUNIT_ASSERT(attr);
6366 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6367 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6368 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6369 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6371 attr = m_pDoc->GetPattern( 0, m_pDoc->MaxRow(), 0 );
6372 CPPUNIT_ASSERT(attr);
6373 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6374 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6375 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6376 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6378 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 );
6379 CPPUNIT_ASSERT(attr);
6380 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6381 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6382 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6383 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6385 m_pDoc->DeleteTab(0);
6388 void Test::testSetStringAndNote()
6390 m_pDoc->InsertTab(0, "Test");
6392 // We need a drawing layer in order to create caption objects.
6393 m_pDoc->InitDrawLayer(m_xDocShell.get());
6395 //note on A1
6396 ScAddress aAdrA1 (0, 0, 0);
6397 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
6398 pNote->SetText(aAdrA1, "Hello world in A1");
6400 m_pDoc->SetString(0, 0, 0, "");
6402 pNote = m_pDoc->GetNote(aAdrA1);
6403 CPPUNIT_ASSERT(pNote);
6405 m_pDoc->DeleteTab(0);
6408 void Test::testUndoDataAnchor()
6410 m_pDoc->InsertTab(0, "Tab1");
6411 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 1 sheets to begin with",
6412 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
6414 m_pDoc->InitDrawLayer();
6415 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
6416 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
6417 SdrPage* pPage = pDrawLayer->GetPage(0);
6418 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
6420 // Insert an object.
6421 tools::Rectangle aObjRect(2,1000,100,1100);
6422 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
6423 pPage->InsertObject(pObj.get());
6424 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
6426 // Get anchor data
6427 ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
6428 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6430 ScAddress aOldStart = pData->maStart;
6431 ScAddress aOldEnd = pData->maEnd;
6433 // Get non rotated anchor data
6434 ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6435 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6437 ScAddress aNOldStart = pNData->maStart;
6438 ScAddress aNOldEnd = pNData->maEnd;
6439 CPPUNIT_ASSERT_EQUAL(aOldStart, aNOldStart);
6440 CPPUNIT_ASSERT_EQUAL(aOldEnd, aNOldEnd);
6442 //pDrawLayer->BeginCalcUndo(false);
6443 // Insert a new row at row 3.
6444 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6445 ScMarkData aMark(m_pDoc->GetSheetLimits());
6446 aMark.SelectOneTable(0);
6447 rFunc.InsertCells(ScRange( 0, aOldStart.Row() - 1, 0, m_pDoc->MaxCol(), aOldStart.Row(), 0 ), &aMark, INS_INSROWS_BEFORE, true, true);
6449 pData = ScDrawLayer::GetObjData(pObj.get());
6450 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6452 ScAddress aNewStart = pData->maStart;
6453 ScAddress aNewEnd = pData->maEnd;
6455 // Get non rotated anchor data
6456 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6457 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6459 ScAddress aNNewStart = pNData->maStart;
6460 ScAddress aNNewEnd = pNData->maEnd;
6461 CPPUNIT_ASSERT_EQUAL(aNewStart, aNNewStart);
6462 CPPUNIT_ASSERT_EQUAL(aNewEnd, aNNewEnd);
6463 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewStart != aOldStart );
6464 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewEnd != aOldEnd );
6465 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewStart != aNOldStart );
6466 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewEnd != aNOldEnd );
6468 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6469 CPPUNIT_ASSERT(pUndoMgr);
6470 pUndoMgr->Undo();
6472 // Check state
6473 ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObj);
6474 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Failed to check state SCA_CELL.", SCA_CELL, oldType);
6476 // Get anchor data
6477 pData = ScDrawLayer::GetObjData(pObj.get());
6478 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6480 // Get non rotated anchor data
6481 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6482 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6484 // Check if data has moved to new rows
6485 CPPUNIT_ASSERT_EQUAL(pData->maStart, aOldStart);
6486 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aOldEnd);
6488 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNOldStart);
6489 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNOldEnd);
6491 pUndoMgr->Redo();
6493 // Get anchor data
6494 pData = ScDrawLayer::GetObjData(pObj.get());
6495 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6497 // Get non rotated anchor data
6498 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6499 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6501 // Check if data has moved to new rows
6502 CPPUNIT_ASSERT_EQUAL(pData->maStart, aNewStart);
6503 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aNewEnd);
6505 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNNewStart);
6506 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNNewEnd);
6508 m_pDoc->DeleteTab(0);
6512 void Test::testEmptyCalcDocDefaults()
6514 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCellCount() );
6515 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetFormulaGroupCount() );
6516 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCodeCount() );
6517 CPPUNIT_ASSERT_EQUAL( int(CharCompressType::NONE), static_cast<int>(m_pDoc->GetAsianCompression()) );
6519 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasPrintRange() );
6520 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInVBAMode() );
6521 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasNotes() );
6522 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6524 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedFonts() );
6525 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedUsedFontsOnly() );
6526 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptLatin() );
6527 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptAsian() );
6528 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptComplex() );
6529 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedded() );
6531 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDocEditable() );
6532 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocProtected() );
6533 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocVisible() );
6534 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUserInteractionEnabled() );
6536 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasAnyCalcNotification() );
6537 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAutoCalcShellDisabled() );
6538 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsForcedFormulaPending() );
6539 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalculatingFormulaTree() );
6541 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipOrUndo() );
6542 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboard() );
6543 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsUndo() );
6544 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUndoEnabled() );
6545 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6546 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboardSource() );
6547 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInsertingFromOtherDoc() );
6548 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->PastingDrawFromOtherDoc() );
6550 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAdjustHeightLocked() );
6551 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsExecuteLinkEnabled() );
6552 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChangeReadOnlyEnabled() );
6554 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IdleCalcTextWidth() );
6555 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsIdleEnabled() );
6556 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDetectiveDirty() );
6557 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasLinkFormulaNeedingCheck() );
6558 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChartListenerCollectionNeedsUpdate() );
6560 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasRangeOverflow() );
6561 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsImportingXML() );
6562 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalcingAfterLoad() );
6563 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetNoListening() );
6565 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianCompression() );
6566 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetAsianKerning() );
6567 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianKerning() );
6569 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreter() );
6570 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreterTableOp() );
6571 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInDtorClear() );
6572 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsExpandRefs() );
6573 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInLinkUpdate() );
6575 SCTAB tab = m_pDoc->GetVisibleTab();
6577 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsVisible(tab) );
6578 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDefaultTabBgColor(tab) );
6579 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasTable(tab) );
6581 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsActiveScenario(tab) );
6582 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasCalcNotification(tab) );
6583 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasManualBreaks(tab) );
6586 void Test::checkPrecisionAsShown( OUString& rCode, double fValue, double fExpectedRoundVal )
6588 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
6589 sal_uInt32 nFormat = pFormatter->GetEntryKey( rCode );
6590 if ( nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
6592 sal_Int32 nCheckPos = 0;
6593 SvNumFormatType nType;
6594 pFormatter->PutEntry( rCode, nCheckPos, nType, nFormat );
6595 CPPUNIT_ASSERT_EQUAL( sal_Int32(0), nCheckPos );
6597 double fRoundValue = m_pDoc->RoundValueAsShown( fValue, nFormat );
6598 OString aMessage = "Format \"" +
6599 OUStringToOString( rCode, RTL_TEXTENCODING_ASCII_US ) +
6600 "\" is not correctly rounded";
6601 CPPUNIT_ASSERT_EQUAL_MESSAGE( aMessage.getStr(), fExpectedRoundVal, fRoundValue );
6604 void Test::testPrecisionAsShown()
6606 m_pDoc->InsertTab(0, "Test");
6608 // Turn on "precision as shown" option.
6609 setCalcAsShown( m_pDoc, true);
6611 OUString aCode;
6612 double fValue, fExpectedRoundVal;
6613 { // decimal rounding
6614 aCode = "0.00";
6615 fValue = 1.0/3.0;
6616 fExpectedRoundVal = 0.33;
6617 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6618 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6619 fValue = 10.001;
6620 fExpectedRoundVal = 10.0;
6621 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6622 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6624 { // thousand rounding tdf#106253
6625 aCode = "0,,";
6626 fValue = 4.0e9 / 7.0;
6627 fExpectedRoundVal = 571e6;
6628 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6629 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6630 aCode = "\"k\"[$$-409]* #,;[RED]-\"k\"[$$-409]* #,";
6631 fValue = 4.0e8 / 7.0;
6632 fExpectedRoundVal = 57.143e6;
6633 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6634 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6636 { // percent rounding
6637 aCode = "0.00%";
6638 fValue = 4.0 / 7.0;
6639 fExpectedRoundVal = 0.5714;
6640 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6641 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6642 fValue = 40.0 / 7.0;
6643 fExpectedRoundVal = 5.7143;
6644 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6645 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6647 { // scientific rounding
6648 aCode = "0.00E0";
6649 fValue = 400000.0 / 7.0;
6650 fExpectedRoundVal = 57100.0;
6651 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6652 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6653 fValue = 4.0 / 70000.0;
6654 fExpectedRoundVal = 5.71e-5;
6655 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6656 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6657 // engineering rounding tdf#106252
6658 aCode = "##0.000E0";
6659 fValue = 400000.0 / 7.0;
6660 fExpectedRoundVal = 57.143e3;
6661 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6662 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6663 fValue = 4000000.0 / 7.0;
6664 fExpectedRoundVal = 571.429e3;
6665 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6666 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6667 fValue = 40000000.0 / 7.0;
6668 fExpectedRoundVal = 5.714e6;
6669 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6670 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6671 fValue = 4.0 / 70000.0;
6672 fExpectedRoundVal = 57.143e-6;
6673 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6674 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6675 fValue = 4.0 / 7000.0;
6676 fExpectedRoundVal = 571.429e-6;
6677 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6678 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6679 fValue = 4.0 / 700.0;
6680 fExpectedRoundVal = 5.714e-3;
6681 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6682 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6683 aCode = "##?0.0#E0";
6684 fValue = 400000.0 / 7.0;
6685 fExpectedRoundVal = 5.71e4;
6686 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6687 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6688 fValue = 4000000.0 / 7.0;
6689 fExpectedRoundVal = 57.14e4;
6690 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6691 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6692 fValue = 40000000.0 / 7.0;
6693 fExpectedRoundVal = 571.43e4;
6694 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6695 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6696 fValue = 400000000.0 / 7.0;
6697 fExpectedRoundVal = 5714.29e4;
6698 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6699 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6700 fValue = 4.0 / 70000.0;
6701 fExpectedRoundVal = 5714.29e-8;
6702 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6703 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6704 fValue = 4.0 / 7000.0;
6705 fExpectedRoundVal = 5.71e-4;
6706 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6707 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6708 fValue = 4.0 / 700.0;
6709 fExpectedRoundVal = 57.14e-4;
6710 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6711 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6712 fValue = 4.0 / 70.0;
6713 fExpectedRoundVal = 571.43e-4;
6714 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6715 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6717 { // fraction rounding tdf#105657
6718 aCode = "# ?/?";
6719 fValue = 0.35;
6720 fExpectedRoundVal = 1.0/3.0;
6721 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6722 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6724 { // exact fraction
6725 aCode = "# ?/??";
6726 fValue = 0.35;
6727 fExpectedRoundVal = 0.35;
6728 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6729 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6731 { // several sub-formats tdf#106052
6732 aCode = "0.00;-0.000";
6733 fValue = 1.0/3.0;
6734 fExpectedRoundVal = 0.33;
6735 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6736 fValue = -1.0/3.0;
6737 fExpectedRoundVal = -0.333;
6738 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6741 setCalcAsShown( m_pDoc, false);
6742 m_pDoc->DeleteTab(0);
6745 void Test::testProtectedSheetEditByRow()
6747 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6748 m_pDoc->InsertTab(0, "Protected");
6751 // Remove protected flags from rows 2-5.
6752 ScPatternAttr aAttr(m_pDoc->GetPool());
6753 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6754 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 4, 0, aAttr);
6756 // Protect the sheet without any options.
6757 ScTableProtection aProtect;
6758 aProtect.setProtected(true);
6759 m_pDoc->SetTabProtection(0, &aProtect);
6761 // Try to delete row 3. It should fail.
6762 ScRange aRow3(0,2,0,m_pDoc->MaxCol(),2,0);
6763 ScMarkData aMark(m_pDoc->GetSheetLimits());
6764 aMark.SelectOneTable(0);
6765 bool bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6766 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should fail.", !bDeleted);
6768 // Protect the sheet but allow row deletion.
6769 aProtect.setOption(ScTableProtection::DELETE_ROWS, true);
6770 m_pDoc->SetTabProtection(0, &aProtect);
6772 // Now we should be able to delete row 3.
6773 bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6774 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should succeed.", bDeleted);
6776 // But, row deletion should still fail on a protected row.
6777 ScRange aRow10(0,9,0,m_pDoc->MaxCol(),9,0);
6778 bDeleted = rDocFunc.DeleteCells(aRow10, &aMark, DelCellCmd::Rows, true);
6779 CPPUNIT_ASSERT_MESSAGE("deletion of row 10 should not be allowed.", !bDeleted);
6781 // Try inserting a new row. It should fail.
6782 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6783 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6785 // Allow row insertions.
6786 aProtect.setOption(ScTableProtection::INSERT_ROWS, true);
6787 m_pDoc->SetTabProtection(0, &aProtect);
6789 bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6790 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should succeed.", bInserted);
6792 // Row insertion is allowed even when the rows above and below have protected flags set.
6793 bInserted = rDocFunc.InsertCells(aRow10, &aMark, INS_INSROWS_AFTER, true, true);
6794 CPPUNIT_ASSERT_MESSAGE("row insertion at row 10 should succeed.", bInserted);
6797 m_pDoc->InsertTab(1, "Matrix"); // This sheet is unprotected.
6800 // Insert matrix into B2:C3.
6801 ScMarkData aMark(m_pDoc->GetSheetLimits());
6802 aMark.SelectOneTable(1);
6803 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, "={1;2|3;4}");
6805 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6806 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6807 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6808 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6810 // Try to insert a row at row 3. It should fail because of matrix's presence.
6812 ScRange aRow3(0,2,1,m_pDoc->MaxCol(),2,1);
6813 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_BEFORE, true, true);
6814 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6817 m_pDoc->DeleteTab(1);
6818 m_pDoc->DeleteTab(0);
6821 void Test::testProtectedSheetEditByColumn()
6823 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6824 m_pDoc->InsertTab(0, "Protected");
6827 // Remove protected flags from columns B to E.
6828 ScPatternAttr aAttr(m_pDoc->GetPool());
6829 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6830 m_pDoc->ApplyPatternAreaTab(1, 0, 4, m_pDoc->MaxRow(), 0, aAttr);
6832 // Protect the sheet without any options.
6833 ScTableProtection aProtect;
6834 aProtect.setProtected(true);
6835 m_pDoc->SetTabProtection(0, &aProtect);
6837 // Try to delete column C. It should fail.
6838 ScRange aCol3(2,0,0,2,m_pDoc->MaxRow(),0);
6839 ScMarkData aMark(m_pDoc->GetSheetLimits());
6840 aMark.SelectOneTable(0);
6841 bool bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6842 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should fail.", !bDeleted);
6844 // Protect the sheet but allow column deletion.
6845 aProtect.setOption(ScTableProtection::DELETE_COLUMNS, true);
6846 m_pDoc->SetTabProtection(0, &aProtect);
6848 // Now we should be able to delete column C.
6849 bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6850 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should succeed.", bDeleted);
6852 // But, column deletion should still fail on a protected column.
6853 ScRange aCol10(9,0,0,9,m_pDoc->MaxRow(),0);
6854 bDeleted = rDocFunc.DeleteCells(aCol10, &aMark, DelCellCmd::Cols, true);
6855 CPPUNIT_ASSERT_MESSAGE("deletion of column 10 should not be allowed.", !bDeleted);
6857 // Try inserting a new column. It should fail.
6858 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6859 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should fail.", !bInserted);
6861 // Allow column insertions.
6862 aProtect.setOption(ScTableProtection::INSERT_COLUMNS, true);
6863 m_pDoc->SetTabProtection(0, &aProtect);
6865 bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6866 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should succeed.", bInserted);
6868 // Column insertion is allowed even when the columns above and below have protected flags set.
6869 bInserted = rDocFunc.InsertCells(aCol10, &aMark, INS_INSCOLS_AFTER, true, true);
6870 CPPUNIT_ASSERT_MESSAGE("column insertion at column 10 should succeed.", bInserted);
6873 m_pDoc->InsertTab(1, "Matrix"); // This sheet is unprotected.
6876 // Insert matrix into B2:C3.
6877 ScMarkData aMark(m_pDoc->GetSheetLimits());
6878 aMark.SelectOneTable(1);
6879 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, "={1;2|3;4}");
6881 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6882 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6883 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6884 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6886 // Try to insert a column at column C. It should fail because of matrix's presence.
6888 ScRange aCol3(2,0,1,2,m_pDoc->MaxRow(),1);
6889 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_BEFORE, true, true);
6890 CPPUNIT_ASSERT_MESSAGE("column insertion at column C should fail.", !bInserted);
6893 m_pDoc->DeleteTab(1);
6894 m_pDoc->DeleteTab(0);
6897 void Test::testInsertColumnsWithFormulaCells()
6899 m_pDoc->InsertTab(0, "Tab1");
6901 std::set<SCCOL> aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6902 CPPUNIT_ASSERT_MESSAGE("empty sheet should contain no formula cells.", aCols.empty());
6904 auto equals = [](const std::set<SCCOL>& left, const std::set<SCCOL>& right)
6906 return left == right;
6909 // insert formula cells in columns 2, 4 and 6.
6910 m_pDoc->SetFormula(ScAddress(2, 2, 0), "=1", m_pDoc->GetGrammar());
6911 m_pDoc->SetFormula(ScAddress(4, 2, 0), "=1", m_pDoc->GetGrammar());
6912 m_pDoc->SetFormula(ScAddress(6, 2, 0), "=1", m_pDoc->GetGrammar());
6914 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6916 std::set<SCCOL> aExpected = { 2, 4, 6 };
6917 CPPUNIT_ASSERT_MESSAGE("Columns 2, 4 and 6 should contain formula cells.", equals(aExpected, aCols));
6919 // Insert 2 columns at column A to shift everything to right by 2.
6920 m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 0, 2);
6922 aExpected = { 4, 6, 8 };
6923 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6924 CPPUNIT_ASSERT_MESSAGE("Columns 4, 6 and 8 should contain formula cells.", equals(aExpected, aCols));
6928 m_pDoc->CheckIntegrity(0);
6930 catch (const std::exception& e)
6932 std::ostringstream os;
6933 os << "document integrity check failed: " << e.what();
6934 CPPUNIT_FAIL(os.str());
6937 m_pDoc->DeleteTab(0);
6940 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
6942 CPPUNIT_PLUGIN_IMPLEMENT();
6944 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */