tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / qa / unit / ucalc.cxx
blob7ef7eb9a3f084cabc370d523182e68e557e4ddb8
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>
31 #include <globstr.hrc>
32 #include <scresid.hxx>
34 #include <columniterator.hxx>
35 #include <scopetools.hxx>
36 #include <dociter.hxx>
37 #include <edittextiterator.hxx>
38 #include <editutil.hxx>
39 #include <asciiopt.hxx>
40 #include <impex.hxx>
41 #include <globalnames.hxx>
42 #include <columnspanset.hxx>
44 #include <editable.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 <o3tl/nonstaticstring.hxx>
58 #include <svx/svdpage.hxx>
59 #include <svx/svdocirc.hxx>
60 #include <svx/svdopath.hxx>
61 #include <svx/svdocapt.hxx>
62 #include <svl/numformat.hxx>
63 #include <svl/srchitem.hxx>
64 #include <svl/sharedstringpool.hxx>
65 #include <unotools/collatorwrapper.hxx>
66 #include <sfx2/IDocumentModelAccessor.hxx>
68 #include <sfx2/sfxsids.hrc>
70 class ScUndoPaste;
71 class ScUndoCut;
72 using ::std::cerr;
73 using ::std::endl;
75 namespace {
77 struct HoriIterCheck
79 SCCOL nCol;
80 SCROW nRow;
81 const char* pVal;
86 class Test : public ScUcalcTestBase
88 protected:
89 void checkPrecisionAsShown(OUString& rCode, double fValue, double fExpectedRoundVal);
91 /** Get a separate new ScDocShell with ScDocument that suits unit test needs. */
92 void getNewDocShell(ScDocShellRef& rDocShellRef);
94 bool checkHorizontalIterator(ScDocument& rDoc, const std::vector<std::vector<const char*>>& rData,
95 const HoriIterCheck* pChecks, size_t nCheckCount);
98 void Test::getNewDocShell( ScDocShellRef& rDocShellRef )
100 rDocShellRef = new ScDocShell(
101 SfxModelFlags::EMBEDDED_OBJECT |
102 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
103 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
105 rDocShellRef->DoInitUnitTest();
108 CPPUNIT_TEST_FIXTURE(Test, testCollator)
110 sal_Int32 nRes = ScGlobal::GetCollator().compareString(u"A"_ustr, u"B"_ustr);
111 CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
114 CPPUNIT_TEST_FIXTURE(Test, testUndoBackgroundColorInsertRow)
116 m_pDoc->InsertTab(0, u"Table1"_ustr);
118 ScMarkData aMark(m_pDoc->GetSheetLimits());
120 // Set Values to B1, C2, D5
121 m_pDoc->SetValue(ScAddress(1, 0, 0), 1.0); // B1
122 m_pDoc->SetValue(ScAddress(2, 1, 0), 2.0); // C2
123 m_pDoc->SetValue(ScAddress(3, 4, 0), 3.0); // D5
125 // Add patterns
126 ScPatternAttr aCellBlueColor(m_pDoc->getCellAttributeHelper());
127 aCellBlueColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
128 m_pDoc->ApplyPatternAreaTab(0, 3, m_pDoc->MaxCol(), 3, 0, aCellBlueColor);
130 // Insert a new row at row 3
131 ScRange aRowOne(0, 2, 0, m_pDoc->MaxCol(), 2, 0);
132 aMark.SetMarkArea(aRowOne);
133 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
134 rFunc.InsertCells(aRowOne, &aMark, INS_INSROWS_BEFORE, true, true);
136 // Check patterns
137 const SfxPoolItem* pItem = nullptr;
138 m_pDoc->GetPattern(ScAddress(1000, 4, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
139 CPPUNIT_ASSERT(pItem);
140 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
142 // Undo the new row
143 m_pDoc->GetUndoManager()->Undo();
145 // Check patterns
146 // Failed if row 3 is not blue all the way through
147 pItem = nullptr;
148 m_pDoc->GetPattern(ScAddress(1000, 3, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
149 CPPUNIT_ASSERT(pItem);
150 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
152 m_pDoc->DeleteTab(0);
155 CPPUNIT_TEST_FIXTURE(Test, testUndoBackgroundColorInsertColumn)
157 m_pDoc->InsertTab(0, u"Table1"_ustr);
159 ScMarkData aMark(m_pDoc->GetSheetLimits());
161 // Set Values to B1, C2, D5
162 m_pDoc->SetValue(ScAddress(1, 0, 0), 1.0); // B1
163 m_pDoc->SetValue(ScAddress(2, 1, 0), 2.0); // C2
164 m_pDoc->SetValue(ScAddress(3, 4, 0), 3.0); // D5
166 // Add patterns
167 ScPatternAttr aCellBlueColor(m_pDoc->getCellAttributeHelper());
168 aCellBlueColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
169 m_pDoc->ApplyPatternAreaTab(3, 0, 3, m_pDoc->MaxRow(), 0, aCellBlueColor);
171 // Insert a new column at column 3
172 ScRange aColumnOne(2, 0, 0, 2, m_pDoc->MaxRow(), 0);
173 aMark.SetMarkArea(aColumnOne);
174 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
175 rFunc.InsertCells(aColumnOne, &aMark, INS_INSCOLS_BEFORE, true, true);
177 // Check patterns
178 const SfxPoolItem* pItem = nullptr;
179 m_pDoc->GetPattern(ScAddress(4, 1000, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
180 CPPUNIT_ASSERT(pItem);
181 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
183 // Undo the new column
184 m_pDoc->GetUndoManager()->Undo();
186 // Check patterns
187 // Failed if column 3 is not blue all the way through
188 pItem = nullptr;
189 m_pDoc->GetPattern(ScAddress(3, 1000, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
190 CPPUNIT_ASSERT(pItem);
191 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
193 m_pDoc->DeleteTab(0);
196 CPPUNIT_TEST_FIXTURE(Test, testMergedHyperlink)
198 m_pDoc->InsertTab(0, u"Table1"_ustr);
199 m_pDoc->InitDrawLayer(m_xDocShell.get());
201 ScFieldEditEngine& pEE = m_pDoc->GetEditEngine();
202 pEE.SetTextCurrentDefaults(u"https://libreoffice.org/"_ustr);
203 m_pDoc->SetEditText(ScAddress(1, 0, 0), pEE.CreateTextObject()); // B1
205 m_pDoc->DoMergeContents(0, 0, 1, 0, 0); // A1:B1
207 CPPUNIT_ASSERT_EQUAL(CELLTYPE_EDIT, m_pDoc->GetCellType(ScAddress(0, 0, 0))); // A1
208 const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(0, 0, 0)); // A1
209 CPPUNIT_ASSERT(pEditObj);
210 CPPUNIT_ASSERT_EQUAL(u"https://libreoffice.org/"_ustr, pEditObj->GetText(0));
213 CPPUNIT_TEST_FIXTURE(Test, testSharedStringPool)
215 m_pDoc->InsertTab(0, u"foo"_ustr);
217 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
218 size_t extraCount = rPool.getCount(); // internal items such as SharedString::getEmptyString()
219 size_t extraCountIgnoreCase = rPool.getCountIgnoreCase();
221 // Strings that are identical.
222 m_pDoc->SetString(ScAddress(0,0,0), o3tl::nonStaticString(u"Andy")); // A1
223 m_pDoc->SetString(ScAddress(0,1,0), o3tl::nonStaticString(u"Andy")); // A2
224 m_pDoc->SetString(ScAddress(0,2,0), o3tl::nonStaticString(u"Bruce")); // A3
225 m_pDoc->SetString(ScAddress(0,3,0), o3tl::nonStaticString(u"andy")); // A4
226 m_pDoc->SetString(ScAddress(0,4,0), o3tl::nonStaticString(u"BRUCE")); // A5
229 // These two shared string objects must go out of scope before the purge test.
230 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
231 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(0,1,0));
232 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS1.isValid());
233 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS2.isValid());
234 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
236 aSS2 = m_pDoc->GetSharedString(ScAddress(0,2,0));
237 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
239 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
240 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
242 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
243 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
245 // A3 and A5 should differ but should be equal case-insensitively.
246 aSS1 = m_pDoc->GetSharedString(ScAddress(0,2,0));
247 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
248 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
249 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
251 // A2 and A4 should be equal when ignoring cases.
252 aSS1 = m_pDoc->GetSharedString(ScAddress(0,1,0));
253 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
254 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
257 // Check the string counts after purging. Purging shouldn't remove any strings in this case.
258 rPool.purge();
259 CPPUNIT_ASSERT_EQUAL(5+extraCount, rPool.getCount());
260 CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, rPool.getCountIgnoreCase());
262 // Clear A1
263 clearRange(m_pDoc, ScRange(ScAddress(0,0,0)));
264 // Clear A2
265 clearRange(m_pDoc, ScRange(ScAddress(0,1,0)));
266 // Clear A3
267 clearRange(m_pDoc, ScRange(ScAddress(0,2,0)));
268 // Clear A4
269 clearRange(m_pDoc, ScRange(ScAddress(0,3,0)));
270 // Clear A5 and the pool should be completely empty.
271 clearRange(m_pDoc, ScRange(ScAddress(0,4,0)));
272 rPool.purge();
273 CPPUNIT_ASSERT_EQUAL(extraCount, rPool.getCount());
274 CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, rPool.getCountIgnoreCase());
276 // Now, compare string and edit text cells.
277 m_pDoc->SetString(ScAddress(0,0,0), "Andy and Bruce"); // A1 // [-loplugin:ostr]
278 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
279 rEE.SetTextCurrentDefaults(u"Andy and Bruce"_ustr);
282 // Set 'Andy' bold.
283 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
284 SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT);
285 aItemSet.Put(aWeight);
286 rEE.QuickSetAttribs(aItemSet, ESelection(0, 0, 0, 4));
290 // Set 'Bruce' italic.
291 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
292 SvxPostureItem aItalic(ITALIC_NORMAL, EE_CHAR_ITALIC);
293 aItemSet.Put(aItalic);
294 rEE.QuickSetAttribs(aItemSet, ESelection(0, 9, 0, 14));
297 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject()); // B1
299 // These two should be equal.
300 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
301 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(1,0,0));
302 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
303 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
304 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
306 rEE.SetTextCurrentDefaults(u"ANDY and BRUCE"_ustr);
307 m_pDoc->SetEditText(ScAddress(2,0,0), rEE.CreateTextObject()); // C1
308 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
309 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
310 CPPUNIT_ASSERT_MESSAGE("These two should be different when cases are considered.", aSS1.getData() != aSS2.getData());
312 // But they should be considered equal when cases are ignored.
313 aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
314 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
315 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
316 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
317 CPPUNIT_ASSERT_EQUAL(aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
319 m_pDoc->DeleteTab(0);
322 CPPUNIT_TEST_FIXTURE(Test, testBackgroundColorDeleteColumn)
324 m_pDoc->InsertTab(0, u"Table1"_ustr);
326 ScMarkData aMark(m_pDoc->GetSheetLimits());
328 // Set Values to B1, C2, D5
329 m_pDoc->SetValue(ScAddress(1, 0, 0), 1.0); // B1
330 m_pDoc->SetValue(ScAddress(2, 1, 0), 2.0); // C2
331 m_pDoc->SetValue(ScAddress(3, 4, 0), 3.0); // D5
333 // Add patterns
334 ScPatternAttr aCellBlueColor(m_pDoc->getCellAttributeHelper());
335 aCellBlueColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
336 m_pDoc->ApplyPatternAreaTab(3, 0, 3, m_pDoc->MaxRow(), 0, aCellBlueColor);
338 // Delete column 10
339 m_pDoc->DeleteCol(ScRange(9,0,0,9,m_pDoc->MaxRow(),0));
341 // Check patterns
342 const SfxPoolItem* pItem = nullptr;
343 m_pDoc->GetPattern(ScAddress(3, 1000, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
344 CPPUNIT_ASSERT(pItem);
345 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
347 // Delete column 2
348 m_pDoc->DeleteCol(ScRange(1,0,0,1,m_pDoc->MaxRow(),0));
350 // Check patterns
351 pItem = nullptr;
352 m_pDoc->GetPattern(ScAddress(2, 1000, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
353 CPPUNIT_ASSERT(pItem);
354 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
356 m_pDoc->DeleteTab(0);
359 CPPUNIT_TEST_FIXTURE(Test, testBackgroundColorDeleteRow)
361 m_pDoc->InsertTab(0, u"Table1"_ustr);
363 ScMarkData aMark(m_pDoc->GetSheetLimits());
365 // Set Values to B1, C2, D5
366 m_pDoc->SetValue(ScAddress(1, 0, 0), 1.0); // B1
367 m_pDoc->SetValue(ScAddress(2, 1, 0), 2.0); // C2
368 m_pDoc->SetValue(ScAddress(3, 4, 0), 3.0); // D5
370 // Add patterns
371 ScPatternAttr aCellBlueColor(m_pDoc->getCellAttributeHelper());
372 aCellBlueColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
373 m_pDoc->ApplyPatternAreaTab(0, 3, m_pDoc->MaxCol(), 3, 0, aCellBlueColor);
375 // Delete row 10
376 m_pDoc->DeleteRow(ScRange(0,9,0,m_pDoc->MaxCol(),9,0));
378 // Check patterns
379 const SfxPoolItem* pItem = nullptr;
380 m_pDoc->GetPattern(ScAddress(1000, 3, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
381 CPPUNIT_ASSERT(pItem);
382 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
384 // Delete row 2
385 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
387 // Check patterns
388 pItem = nullptr;
389 m_pDoc->GetPattern(ScAddress(1000, 2, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
390 CPPUNIT_ASSERT(pItem);
391 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
393 m_pDoc->DeleteTab(0);
396 CPPUNIT_TEST_FIXTURE(Test, testSharedStringPoolUndoDoc)
398 struct
400 bool check( const ScDocument& rSrcDoc, ScDocument& rCopyDoc )
402 // Copy A1:A4 to the undo document.
403 for (SCROW i = 0; i <= 4; ++i)
405 ScAddress aPos(0,i,0);
406 rCopyDoc.SetString(aPos, rSrcDoc.GetString(aPos));
409 // String values in A1:A4 should have identical hash.
410 for (SCROW i = 0; i <= 4; ++i)
412 ScAddress aPos(0,i,0);
413 svl::SharedString aSS1 = rSrcDoc.GetSharedString(aPos);
414 svl::SharedString aSS2 = rCopyDoc.GetSharedString(aPos);
415 if (aSS1.getDataIgnoreCase() != aSS2.getDataIgnoreCase())
417 cerr << "String hash values are not equal at row " << (i+1)
418 << " for string '" << aSS1.getString() << "'" << endl;
419 return false;
423 return true;
426 } aTest;
428 m_pDoc->InsertTab(0, u"Test"_ustr);
430 m_pDoc->SetString(ScAddress(0,0,0), u"Header"_ustr);
431 m_pDoc->SetString(ScAddress(0,1,0), u"A1"_ustr);
432 m_pDoc->SetString(ScAddress(0,2,0), u"A2"_ustr);
433 m_pDoc->SetString(ScAddress(0,3,0), u"A3"_ustr);
435 ScDocument aUndoDoc(SCDOCMODE_UNDO);
436 aUndoDoc.InitUndo(*m_pDoc, 0, 0);
438 bool bSuccess = aTest.check(*m_pDoc, aUndoDoc);
439 CPPUNIT_ASSERT_MESSAGE("Check failed with undo document.", bSuccess);
441 // Test the clip document as well.
442 ScDocument aClipDoc(SCDOCMODE_CLIP);
443 aClipDoc.ResetClip(m_pDoc, static_cast<SCTAB>(0));
445 bSuccess = aTest.check(*m_pDoc, aClipDoc);
446 CPPUNIT_ASSERT_MESSAGE("Check failed with clip document.", bSuccess);
448 m_pDoc->DeleteTab(0);
451 CPPUNIT_TEST_FIXTURE(Test, testRangeList)
453 m_pDoc->InsertTab(0, u"foo"_ustr);
455 ScRangeList aRL;
456 aRL.push_back(ScRange(1,1,0,3,10,0));
457 CPPUNIT_ASSERT_EQUAL_MESSAGE("List should have one range.", size_t(1), aRL.size());
458 const ScRange* p = &aRL[0];
459 CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
460 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(1,1,0), p->aStart);
461 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(3,10,0), p->aEnd);
463 // TODO: Add more tests here.
465 m_pDoc->DeleteTab(0);
468 CPPUNIT_TEST_FIXTURE(Test, testMarkData)
470 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
472 // Empty mark. Nothing is selected.
473 std::vector<sc::ColRowSpan> aSpans = aMarkData.GetMarkedRowSpans();
474 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
475 aSpans = aMarkData.GetMarkedColSpans();
476 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
478 // Select B3:F7.
479 aMarkData.SetMarkArea(ScRange(1,2,0,5,6,0));
480 aSpans = aMarkData.GetMarkedRowSpans();
481 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
482 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
483 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
485 aSpans = aMarkData.GetMarkedColSpans();
486 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
487 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(1), aSpans[0].mnStart);
488 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
490 // Select A11:B13.
491 aMarkData.SetMultiMarkArea(ScRange(0,10,0,1,12,0));
492 aSpans = aMarkData.GetMarkedRowSpans();
493 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 2 selected row spans.", size_t(2), aSpans.size());
494 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
495 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
496 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(10), aSpans[1].mnStart);
497 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[1].mnEnd);
499 aSpans = aMarkData.GetMarkedColSpans();
500 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
501 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
502 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
504 // Select C8:C10.
505 aMarkData.SetMultiMarkArea(ScRange(2,7,0,2,9,0));
506 aSpans = aMarkData.GetMarkedRowSpans();
507 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
508 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
509 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[0].mnEnd);
511 aSpans = aMarkData.GetMarkedColSpans();
512 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
513 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
514 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
517 CPPUNIT_TEST_FIXTURE(Test, testInput)
520 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
521 m_pDoc->InsertTab (0, u"foo"_ustr));
522 OUString test;
524 m_pDoc->SetString(0, 0, 0, u"'10.5"_ustr);
525 test = m_pDoc->GetString(0, 0, 0);
526 bool bTest = test == "10.5";
527 CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
528 m_pDoc->SetString(0, 0, 0, u"'apple'"_ustr);
529 test = m_pDoc->GetString(0, 0, 0);
530 bTest = test == "apple'";
531 CPPUNIT_ASSERT_MESSAGE("Text content should have the first apostrophe stripped.", bTest);
533 // Customized string handling policy.
534 ScSetStringParam aParam;
535 aParam.setTextInput();
536 m_pDoc->SetString(0, 0, 0, u"000123"_ustr, &aParam);
537 test = m_pDoc->GetString(0, 0, 0);
538 CPPUNIT_ASSERT_EQUAL_MESSAGE("Text content should have been treated as string, not number.", u"000123"_ustr, test);
540 m_pDoc->DeleteTab(0);
543 CPPUNIT_TEST_FIXTURE(Test, testColumnIterator) // tdf#118620
545 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
546 m_pDoc->InsertTab (0, u"foo"_ustr));
548 m_pDoc->SetString(0, 0, 0, u"'10.5"_ustr);
549 m_pDoc->SetString(0, m_pDoc->MaxRow()-5, 0, u"42.0"_ustr);
550 std::optional<sc::ColumnIterator> it = m_pDoc->GetColumnIterator(0, 0, m_pDoc->MaxRow() - 10, m_pDoc->MaxRow());
551 while (it->hasCell())
553 it->getCell();
554 it->next();
557 m_pDoc->DeleteTab(0);
560 CPPUNIT_TEST_FIXTURE(Test, testTdf66613)
562 // Create different print ranges and col/row repetitions for two tabs
563 const SCTAB nFirstTab = 0;
564 CPPUNIT_ASSERT(m_pDoc->InsertTab(nFirstTab, u"FirstPrintRange"_ustr));
565 ScRange aFirstPrintRange(0, 0, nFirstTab, 2, 2, nFirstTab);
566 m_pDoc->AddPrintRange(nFirstTab, aFirstPrintRange);
567 ScRange aFirstRepeatColRange(0, 0, nFirstTab, 0, 0, nFirstTab);
568 m_pDoc->SetRepeatColRange(nFirstTab, aFirstRepeatColRange);
569 ScRange aFirstRepeatRowRange(1, 1, nFirstTab, 1, 1, nFirstTab);
570 m_pDoc->SetRepeatRowRange(nFirstTab, aFirstRepeatRowRange);
572 const SCTAB nSecondTab = 1;
573 CPPUNIT_ASSERT(m_pDoc->InsertTab(nSecondTab, u"SecondPrintRange"_ustr));
574 ScRange aSecondPrintRange(0, 0, nSecondTab, 3, 3, nSecondTab);
575 m_pDoc->AddPrintRange(nSecondTab, aSecondPrintRange);
576 ScRange aSecondRepeatColRange(1, 1, nSecondTab, 1, 1, nSecondTab);
577 m_pDoc->SetRepeatColRange(nSecondTab, aSecondRepeatColRange);
578 ScRange aSecondRepeatRowRange(2, 2, nSecondTab, 2, 2, nSecondTab);
579 m_pDoc->SetRepeatRowRange(nSecondTab, aSecondRepeatRowRange);
581 // Transfer generated tabs to a new document with different order
582 ScDocument aScDocument;
583 aScDocument.TransferTab(*m_pDoc, nSecondTab, nFirstTab);
584 aScDocument.TransferTab(*m_pDoc, nFirstTab, nSecondTab);
586 // Check the number of print ranges in both documents
587 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nFirstTab));
588 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nSecondTab));
589 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nFirstTab));
590 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nSecondTab));
592 // Check the print ranges and col/row repetitions in both documents
593 CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *m_pDoc->GetPrintRange(nFirstTab, 0));
594 CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *m_pDoc->GetRepeatColRange(nFirstTab));
595 CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *m_pDoc->GetRepeatRowRange(nFirstTab));
596 CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *m_pDoc->GetPrintRange(nSecondTab, 0));
597 CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *m_pDoc->GetRepeatColRange(nSecondTab));
598 CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *m_pDoc->GetRepeatRowRange(nSecondTab));
600 // Tabs have to be adjusted since the order of the tabs is inverted in the new document
601 std::vector<ScRange*> aScRanges
602 = { &aFirstPrintRange, &aFirstRepeatColRange, &aFirstRepeatRowRange,
603 &aSecondPrintRange, &aSecondRepeatColRange, &aSecondRepeatRowRange };
604 for (size_t i = 0; i < aScRanges.size(); i++)
606 const SCTAB nTab = i >= 3 ? nFirstTab : nSecondTab;
607 aScRanges[i]->aStart.SetTab(nTab);
608 aScRanges[i]->aEnd.SetTab(nTab);
611 // Without the fix in place, no print ranges and col/row repetitions would be present
612 CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *aScDocument.GetPrintRange(nSecondTab, 0));
613 CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *aScDocument.GetRepeatColRange(nSecondTab));
614 CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *aScDocument.GetRepeatRowRange(nSecondTab));
615 CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *aScDocument.GetPrintRange(nFirstTab, 0));
616 CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *aScDocument.GetRepeatColRange(nFirstTab));
617 CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *aScDocument.GetRepeatRowRange(nFirstTab));
619 m_pDoc->DeleteTab(nFirstTab);
620 m_pDoc->DeleteTab(nSecondTab);
623 CPPUNIT_TEST_FIXTURE(Test, testTdf113027)
625 // Insert some sheets including a whitespace in their name and switch the grammar to R1C1
626 CPPUNIT_ASSERT(m_pDoc->InsertTab(0, u"Sheet 1"_ustr));
627 CPPUNIT_ASSERT(m_pDoc->InsertTab(1, u"Sheet 2"_ustr));
628 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
630 // Add a formula containing a remote reference, i.e., to another sheet
631 const ScAddress aScAddress(0, 0, 0);
632 static constexpr OUString aFormula = u"='Sheet 2'!RC"_ustr;
633 m_pDoc->SetString(aScAddress, aFormula);
635 // Switch from relative to absolute cell reference
636 ScRefFinder aFinder(aFormula, aScAddress, *m_pDoc, m_pDoc->GetAddressConvention());
637 aFinder.ToggleRel(0, aFormula.getLength());
639 // Without the fix in place, this test would have failed with
640 // - Expected: ='Sheet 2'!R1C1
641 // - Actual : ='Sheet 2'!RC
642 // i.e. the cell reference was not changed from relative to absolute
643 CPPUNIT_ASSERT_EQUAL(u"='Sheet 2'!R1C1"_ustr, aFinder.GetText());
645 m_pDoc->DeleteTab(0);
646 m_pDoc->DeleteTab(1);
649 CPPUNIT_TEST_FIXTURE(Test, testTdf90698)
651 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
652 m_pDoc->SetString(ScAddress(0,0,0), u"=(1;2)"_ustr);
654 // Without the fix in place, this would have failed with
655 // - Expected: =(1;2)
656 // - Actual : =(1~2)
657 OUString aFormula = m_pDoc->GetFormula(0,0,0);
658 CPPUNIT_ASSERT_EQUAL(u"=(1;2)"_ustr, aFormula);
660 m_pDoc->DeleteTab(0);
663 CPPUNIT_TEST_FIXTURE(Test, testTdf114406)
665 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
666 m_pDoc->SetString(ScAddress(0,0,0), u"5"_ustr);
667 m_pDoc->SetString(ScAddress(1,0,0), u"=A1/100%"_ustr);
669 // Without the fix in place, this would have failed with
670 // - Expected: =A1/100%
671 // - Actual : =A1/1
672 OUString aFormula = m_pDoc->GetFormula(1,0,0);
673 CPPUNIT_ASSERT_EQUAL(u"=A1/100%"_ustr, aFormula);
675 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,0,0)));
677 m_pDoc->DeleteTab(0);
680 CPPUNIT_TEST_FIXTURE(Test, testTdf93951)
682 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
683 m_pDoc->SetString(ScAddress(0,0,0), u"=2*§*2"_ustr);
685 OUString aFormula = m_pDoc->GetFormula(0,0,0);
687 // Without the fix in place, this test would have failed with
688 // - Expected: =2*§*2
689 // - Actual : =2*
690 CPPUNIT_ASSERT_EQUAL(u"=2*§*2"_ustr, aFormula);
692 m_pDoc->DeleteTab(0);
695 CPPUNIT_TEST_FIXTURE(Test, testTdf134490)
697 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
699 m_pDoc->SetString(ScAddress(0,0,0), u"--1"_ustr);
700 m_pDoc->SetString(ScAddress(0,1,0), u"---1"_ustr);
701 m_pDoc->SetString(ScAddress(0,2,0), u"+-1"_ustr);
702 m_pDoc->SetString(ScAddress(0,3,0), u"+--1"_ustr);
704 // Without the fix in place, this test would have failed with
705 // - Expected: --1
706 // - Actual : -1
707 CPPUNIT_ASSERT_EQUAL(u"--1"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
708 CPPUNIT_ASSERT_EQUAL(u"---1"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
709 CPPUNIT_ASSERT_EQUAL(u"+-1"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
710 CPPUNIT_ASSERT_EQUAL(u"+--1"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
712 m_pDoc->DeleteTab(0);
715 CPPUNIT_TEST_FIXTURE(Test, testTdf135249)
717 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
719 m_pDoc->SetString(ScAddress(0,0,0), u"1:60"_ustr);
720 m_pDoc->SetString(ScAddress(0,1,0), u"1:123"_ustr);
721 m_pDoc->SetString(ScAddress(0,2,0), u"1:1:123"_ustr);
722 m_pDoc->SetString(ScAddress(0,3,0), u"0:123"_ustr);
723 m_pDoc->SetString(ScAddress(0,4,0), u"0:0:123"_ustr);
724 m_pDoc->SetString(ScAddress(0,5,0), u"0:123:59"_ustr);
726 // These are not valid duration inputs
727 CPPUNIT_ASSERT_EQUAL(u"1:60"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
728 CPPUNIT_ASSERT_EQUAL(u"1:123"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
729 CPPUNIT_ASSERT_EQUAL(u"1:1:123"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
731 // These are valid duration inputs
732 // Without the fix in place, this test would have failed with
733 // - Expected: 02:03:00 AM
734 // - Actual : 0:123
735 CPPUNIT_ASSERT_EQUAL(u"02:03:00 AM"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
736 CPPUNIT_ASSERT_EQUAL(u"12:02:03 AM"_ustr, m_pDoc->GetString(ScAddress(0,4,0)));
737 CPPUNIT_ASSERT_EQUAL(u"02:03:59 AM"_ustr, m_pDoc->GetString(ScAddress(0,5,0)));
739 m_pDoc->DeleteTab(0);
742 CPPUNIT_TEST_FIXTURE(Test, testDocStatistics)
744 SCTAB nStartTabs = m_pDoc->GetTableCount();
745 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
746 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
747 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
748 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
749 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
750 static_cast<SCTAB>(nStartTabs+2), m_pDoc->GetTableCount());
752 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetCellCount());
753 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
754 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetCellCount());
755 m_pDoc->SetValue(ScAddress(2,2,0), 2.5);
756 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetCellCount());
757 m_pDoc->SetString(ScAddress(1,1,1), u"Test"_ustr);
758 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(3), m_pDoc->GetCellCount());
760 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetFormulaGroupCount());
761 m_pDoc->SetString(ScAddress(3,0,1), u"=A1"_ustr);
762 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
763 m_pDoc->SetString(ScAddress(3,1,1), u"=A2"_ustr);
764 m_pDoc->SetString(ScAddress(3,2,1), u"=A3"_ustr);
765 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
766 m_pDoc->SetString(ScAddress(3,3,1), u"=A5"_ustr);
767 m_pDoc->SetString(ScAddress(3,4,1), u"=A6"_ustr);
768 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetFormulaGroupCount());
769 m_pDoc->SetString(ScAddress(3,1,1), u"=A3"_ustr);
770 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(4), m_pDoc->GetFormulaGroupCount());
772 m_pDoc->DeleteTab(1);
773 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to decrement sheet count.",
774 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
775 m_pDoc->DeleteTab(0); // This may fail in case there is only one sheet in the document.
778 CPPUNIT_TEST_FIXTURE(Test, testRowForHeight)
780 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
781 m_pDoc->SetRowHeightRange( 0, 9, 0, 100);
782 m_pDoc->SetRowHeightRange(10, 19, 0, 200);
783 m_pDoc->SetRowHeightRange(20, 29, 0, 300);
785 // Hide some rows.
786 m_pDoc->SetRowHidden(3, 5, 0, true);
787 m_pDoc->SetRowHidden(8, 12, 0, true);
789 struct Check
791 tools::Long nHeight;
792 SCROW nRow;
795 std::vector<Check> aChecks = {
796 { 1, 1 },
797 { 99, 1 },
798 { 120, 2 },
799 { 330, 7 },
800 { 420, 13 },
801 { 780, 15 },
802 { 1860, 20 },
803 { 4020, 28 },
806 for (const Check& rCheck : aChecks)
808 SCROW nRow = m_pDoc->GetRowForHeight(0, rCheck.nHeight);
809 CPPUNIT_ASSERT_EQUAL(rCheck.nRow, nRow);
813 CPPUNIT_TEST_FIXTURE(Test, testDataEntries)
816 * The 'data entries' data is a list of strings used for suggestions as
817 * the user types in new cell value.
819 m_pDoc->InsertTab(0, u"Test"_ustr);
821 m_pDoc->SetString(ScAddress(0,5,0), u"Andy"_ustr);
822 m_pDoc->SetString(ScAddress(0,6,0), u"Bruce"_ustr);
823 m_pDoc->SetString(ScAddress(0,7,0), u"Charlie"_ustr);
824 m_pDoc->SetString(ScAddress(0,10,0), u"Andy"_ustr);
826 std::vector<ScTypedStrData> aEntries;
827 m_pDoc->GetDataEntries(0, 0, 0, aEntries); // Try at the very top.
829 // Entries are supposed to be sorted in ascending order, and are all unique.
830 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
831 std::vector<ScTypedStrData>::const_iterator it = aEntries.begin();
832 CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
833 ++it;
834 CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
835 ++it;
836 CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
837 ++it;
838 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
840 aEntries.clear();
841 m_pDoc->GetDataEntries(0, m_pDoc->MaxRow(), 0, aEntries); // Try at the very bottom.
842 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
844 // Make sure we get the same set of suggestions.
845 it = aEntries.begin();
846 CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
847 ++it;
848 CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
849 ++it;
850 CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
851 ++it;
852 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
854 m_pDoc->DeleteTab(0);
857 CPPUNIT_TEST_FIXTURE(Test, testSelectionFunction)
860 * Selection function is responsible for displaying quick calculation
861 * results in the status bar.
863 m_pDoc->InsertTab(0, u"Test"_ustr);
865 // Insert values into B2:B4.
866 m_pDoc->SetString(ScAddress(1,1,0), u"=1"_ustr); // formula
867 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
868 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
870 // Insert strings into B5:B8.
871 m_pDoc->SetString(ScAddress(1,4,0), u"A"_ustr);
872 m_pDoc->SetString(ScAddress(1,5,0), u"B"_ustr);
873 m_pDoc->SetString(ScAddress(1,6,0), u"=\"C\""_ustr); // formula
874 m_pDoc->SetString(ScAddress(1,7,0), u"D"_ustr);
876 // Insert values into D2:D4.
877 m_pDoc->SetValue(ScAddress(3,1,0), 4.0);
878 m_pDoc->SetValue(ScAddress(3,2,0), 5.0);
879 m_pDoc->SetValue(ScAddress(3,3,0), 6.0);
881 // Insert edit text into D5.
882 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
883 rEE.SetTextCurrentDefaults(u"Rich Text"_ustr);
884 m_pDoc->SetEditText(ScAddress(3,4,0), rEE.CreateTextObject());
886 // Insert Another string into D6.
887 m_pDoc->SetString(ScAddress(3,5,0), u"E"_ustr);
889 // Select B2:B8 & D2:D8 disjoint region.
890 ScRangeList aRanges;
891 aRanges.push_back(ScRange(1,1,0,1,7,0)); // B2:B8
892 aRanges.push_back(ScRange(3,1,0,3,7,0)); // D2:D8
893 ScMarkData aMark(m_pDoc->GetSheetLimits());
894 aMark.MarkFromRangeList(aRanges, true);
896 struct Check
898 ScSubTotalFunc meFunc;
899 double mfExpected;
903 static const Check aChecks[] =
905 { SUBTOTAL_FUNC_AVE, 3.5 },
906 { SUBTOTAL_FUNC_CNT2, 12.0 },
907 { SUBTOTAL_FUNC_CNT, 6.0 },
908 { SUBTOTAL_FUNC_MAX, 6.0 },
909 { SUBTOTAL_FUNC_MIN, 1.0 },
910 { SUBTOTAL_FUNC_SUM, 21.0 },
911 { SUBTOTAL_FUNC_SELECTION_COUNT, 14.0 }
914 for (const auto& rCheck : aChecks)
916 double fRes = 0.0;
917 bool bRes = m_pDoc->GetSelectionFunction(rCheck.meFunc, ScAddress(), aMark, fRes);
918 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
919 CPPUNIT_ASSERT_EQUAL(rCheck.mfExpected, fRes);
923 // Hide rows 4 and 6 and check the results again.
925 m_pDoc->SetRowHidden(3, 3, 0, true);
926 m_pDoc->SetRowHidden(5, 5, 0, true);
927 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(3, 0));
928 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(5, 0));
931 static const Check aChecks[] =
933 { SUBTOTAL_FUNC_AVE, 3.0 },
934 { SUBTOTAL_FUNC_CNT2, 8.0 },
935 { SUBTOTAL_FUNC_CNT, 4.0 },
936 { SUBTOTAL_FUNC_MAX, 5.0 },
937 { SUBTOTAL_FUNC_MIN, 1.0 },
938 { SUBTOTAL_FUNC_SUM, 12.0 },
939 { SUBTOTAL_FUNC_SELECTION_COUNT, 10.0 }
942 for (const auto& rCheck : aChecks)
944 double fRes = 0.0;
945 bool bRes = m_pDoc->GetSelectionFunction(rCheck.meFunc, ScAddress(), aMark, fRes);
946 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
947 CPPUNIT_ASSERT_EQUAL(rCheck.mfExpected, fRes);
951 // Make sure that when no selection is present, use the current cursor position.
952 ScMarkData aEmpty(m_pDoc->GetSheetLimits());
955 // D3 (numeric cell containing 5.)
956 ScAddress aPos(3, 2, 0);
958 static const Check aChecks[] =
960 { SUBTOTAL_FUNC_AVE, 5.0 },
961 { SUBTOTAL_FUNC_CNT2, 1.0 },
962 { SUBTOTAL_FUNC_CNT, 1.0 },
963 { SUBTOTAL_FUNC_MAX, 5.0 },
964 { SUBTOTAL_FUNC_MIN, 5.0 },
965 { SUBTOTAL_FUNC_SUM, 5.0 },
966 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
969 for (const auto& rCheck : aChecks)
971 double fRes = 0.0;
972 bool bRes = m_pDoc->GetSelectionFunction(rCheck.meFunc, aPos, aEmpty, fRes);
973 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
974 CPPUNIT_ASSERT_EQUAL(rCheck.mfExpected, fRes);
979 // B7 (string formula cell containing ="C".)
980 ScAddress aPos(1, 6, 0);
982 static const Check aChecks[] =
984 { SUBTOTAL_FUNC_CNT2, 1.0 },
985 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
988 for (const auto& rCheck : aChecks)
990 double fRes = 0.0;
991 bool bRes = m_pDoc->GetSelectionFunction(rCheck.meFunc, aPos, aEmpty, fRes);
992 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
993 CPPUNIT_ASSERT_EQUAL(rCheck.mfExpected, fRes);
997 // Calculate function across selected sheets.
998 clearSheet(m_pDoc, 0);
999 m_pDoc->InsertTab(1, u"Test2"_ustr);
1000 m_pDoc->InsertTab(2, u"Test3"_ustr);
1002 // Set values at B2 and C3 on each sheet.
1003 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
1004 m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
1005 m_pDoc->SetValue(ScAddress(1,1,1), 4.0);
1006 m_pDoc->SetValue(ScAddress(2,2,1), 8.0);
1007 m_pDoc->SetValue(ScAddress(1,1,2), 16.0);
1008 m_pDoc->SetValue(ScAddress(2,2,2), 32.0);
1010 // Mark B2 and C3 on first sheet.
1011 aRanges.RemoveAll();
1012 aRanges.push_back(ScRange(1,1,0)); // B2
1013 aRanges.push_back(ScRange(2,2,0)); // C3
1014 aMark.MarkFromRangeList(aRanges, true);
1015 // Additionally select third sheet.
1016 aMark.SelectTable(2, true);
1019 double fRes = 0.0;
1020 bool bRes = m_pDoc->GetSelectionFunction( SUBTOTAL_FUNC_SUM, ScAddress(), aMark, fRes);
1021 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
1022 CPPUNIT_ASSERT_EQUAL_MESSAGE("1+2+16+32=", 51.0, fRes);
1025 m_pDoc->DeleteTab(2);
1026 m_pDoc->DeleteTab(1);
1027 m_pDoc->DeleteTab(0);
1030 CPPUNIT_TEST_FIXTURE(Test, testMarkedCellIteration)
1032 m_pDoc->InsertTab(0, u"Test"_ustr);
1034 // Insert cells to A1, A5, B2 and C3.
1035 m_pDoc->SetString(ScAddress(0,0,0), u"California"_ustr);
1036 m_pDoc->SetValue(ScAddress(0,4,0), 1.2);
1037 m_pDoc->SetEditText(ScAddress(1,1,0), u"Boston"_ustr);
1038 m_pDoc->SetFormula(ScAddress(2,2,0), u"=SUM(1,2,3)"_ustr, m_pDoc->GetGrammar());
1040 // Select A1:C5.
1041 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
1042 aMarkData.SetMarkArea(ScRange(0,0,0,2,4,0));
1043 aMarkData.MarkToMulti(); // TODO : we shouldn't have to do this.
1045 struct Check
1047 SCCOL mnCol;
1048 SCROW mnRow;
1051 const std::vector<Check> aChecks = {
1052 { 0, 0 }, // A1
1053 { 0, 4 }, // A5
1054 { 1, 1 }, // B2
1055 { 2, 2 }, // C3
1058 SCROW nRow = -1; // Start from the imaginary row before A1.
1059 SCCOL nCol = 0;
1061 for (const Check& rCheck : aChecks)
1063 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
1064 if (!bFound)
1066 std::ostringstream os;
1067 os << ScAddress(rCheck.mnCol, rCheck.mnRow, 0).GetColRowString() << " was expected, but not found.";
1068 CPPUNIT_FAIL(os.str());
1071 CPPUNIT_ASSERT_EQUAL(rCheck.mnRow, nRow);
1072 CPPUNIT_ASSERT_EQUAL(rCheck.mnCol, nCol);
1075 // No more marked cells on this sheet.
1076 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
1077 CPPUNIT_ASSERT(!bFound);
1079 m_pDoc->DeleteTab(0);
1082 CPPUNIT_TEST_FIXTURE(Test, testCopyToDocument)
1084 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"src"_ustr));
1086 // We need a drawing layer in order to create caption objects.
1087 m_pDoc->InitDrawLayer(m_xDocShell.get());
1089 m_pDoc->SetString(0, 0, 0, u"Header"_ustr);
1090 m_pDoc->SetString(0, 1, 0, u"1"_ustr);
1091 m_pDoc->SetString(0, 2, 0, u"2"_ustr);
1092 m_pDoc->SetString(0, 3, 0, u"3"_ustr);
1093 m_pDoc->SetString(0, 4, 0, u"=4/2"_ustr);
1094 m_pDoc->CalcAll();
1096 //note on A1
1097 ScAddress aAdrA1 (0, 0, 0); // numerical cell content
1098 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
1099 pNote->SetText(aAdrA1, u"Hello world in A1"_ustr);
1101 // Copy statically to another document.
1103 ScDocShellRef xDocSh2;
1104 getNewDocShell(xDocSh2);
1105 ScDocument* pDestDoc = &xDocSh2->GetDocument();
1106 pDestDoc->InsertTab(0, u"src"_ustr);
1107 pDestDoc->InitDrawLayer(xDocSh2.get()); // for note caption objects
1109 m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, *pDestDoc); // Copy A2:A4
1110 m_pDoc->CopyStaticToDocument(ScRange(ScAddress(0,0,0)), 0, *pDestDoc); // Copy A1
1111 m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, *pDestDoc); // Copy A5:A8
1113 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), pDestDoc->GetString(0,0,0));
1114 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), pDestDoc->GetString(0,1,0));
1115 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), pDestDoc->GetString(0,2,0));
1116 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), pDestDoc->GetString(0,3,0));
1117 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), pDestDoc->GetString(0,4,0));
1119 // verify note
1120 CPPUNIT_ASSERT_MESSAGE("There should be a note in A1 destDocument", pDestDoc->HasNote(ScAddress(0, 0, 0)));
1121 CPPUNIT_ASSERT_EQUAL_MESSAGE("The notes content should be the same on both documents",
1122 m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText(), pDestDoc->GetNote(ScAddress(0, 0, 0))->GetText());
1124 pDestDoc->DeleteTab(0);
1125 xDocSh2->DoClose();
1126 xDocSh2.clear();
1128 m_pDoc->DeleteTab(0);
1131 bool Test::checkHorizontalIterator(ScDocument& rDoc, const std::vector<std::vector<const char*>>& rData, const HoriIterCheck* pChecks, size_t nCheckCount)
1133 ScAddress aPos(0,0,0);
1134 insertRangeData(&rDoc, aPos, rData);
1135 ScHorizontalCellIterator aIter(rDoc, 0, 0, 0, 1, rData.size() - 1);
1137 SCCOL nCol;
1138 SCROW nRow;
1139 size_t i = 0;
1140 for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
1142 if (i >= nCheckCount)
1144 cerr << "hit invalid check " << i << " of " << nCheckCount << endl;
1145 CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
1146 return false;
1149 if (pChecks[i].nCol != nCol)
1151 cerr << "Column mismatch " << pChecks[i].nCol << " vs. " << nCol << endl;
1152 return false;
1155 if (pChecks[i].nRow != nRow)
1157 cerr << "Row mismatch " << pChecks[i].nRow << " vs. " << nRow << endl;
1158 return false;
1161 if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString(&rDoc))
1163 cerr << "String mismatch " << pChecks[i].pVal << " vs. " <<
1164 pCell->getString(&rDoc) << endl;
1165 return false;
1169 return true;
1172 CPPUNIT_TEST_FIXTURE(Test, testHorizontalIterator)
1174 m_pDoc->InsertTab(0, u"test"_ustr);
1177 // Raw data - mixed types
1178 std::vector<std::vector<const char*>> aData = {
1179 { "A", "B" },
1180 { "C", "1" },
1181 { "D", "2" },
1182 { "E", "3" }
1185 static const HoriIterCheck aChecks[] = {
1186 { 0, 0, "A" },
1187 { 1, 0, "B" },
1188 { 0, 1, "C" },
1189 { 1, 1, "1" },
1190 { 0, 2, "D" },
1191 { 1, 2, "2" },
1192 { 0, 3, "E" },
1193 { 1, 3, "3" },
1196 bool bRes = checkHorizontalIterator(
1197 *m_pDoc, aData, aChecks, std::size(aChecks));
1199 if (!bRes)
1200 CPPUNIT_FAIL("Failed on test mixed.");
1204 // Raw data - 'hole' data
1205 std::vector<std::vector<const char*>> aData = {
1206 { "A", "B" },
1207 { "C", nullptr },
1208 { "D", "E" },
1211 static const HoriIterCheck aChecks[] = {
1212 { 0, 0, "A" },
1213 { 1, 0, "B" },
1214 { 0, 1, "C" },
1215 { 0, 2, "D" },
1216 { 1, 2, "E" },
1219 bool bRes = checkHorizontalIterator(
1220 *m_pDoc, aData, aChecks, std::size(aChecks));
1222 if (!bRes)
1223 CPPUNIT_FAIL("Failed on test hole.");
1227 // Very holy data
1228 std::vector<std::vector<const char*>> aData = {
1229 { nullptr, "A" },
1230 { nullptr, nullptr },
1231 { nullptr, "1" },
1232 { "B", nullptr },
1233 { "C", "2" },
1234 { "D", "3" },
1235 { "E", nullptr },
1236 { nullptr, "G" },
1237 { nullptr, nullptr },
1240 static const HoriIterCheck aChecks[] = {
1241 { 1, 0, "A" },
1242 { 1, 2, "1" },
1243 { 0, 3, "B" },
1244 { 0, 4, "C" },
1245 { 1, 4, "2" },
1246 { 0, 5, "D" },
1247 { 1, 5, "3" },
1248 { 0, 6, "E" },
1249 { 1, 7, "G" },
1252 bool bRes = checkHorizontalIterator(
1253 *m_pDoc, aData, aChecks, std::size(aChecks));
1255 if (!bRes)
1256 CPPUNIT_FAIL("Failed on test holy.");
1260 // Degenerate case
1261 std::vector<std::vector<const char*>> aData = {
1262 { nullptr, nullptr },
1263 { nullptr, nullptr },
1264 { nullptr, nullptr },
1267 bool bRes = checkHorizontalIterator(
1268 *m_pDoc, aData, nullptr, 0);
1270 if (!bRes)
1271 CPPUNIT_FAIL("Failed on test degenerate.");
1275 // Data at end
1276 std::vector<std::vector<const char*>> aData = {
1277 { nullptr, nullptr },
1278 { nullptr, nullptr },
1279 { nullptr, "A" },
1282 static const HoriIterCheck aChecks[] = {
1283 { 1, 2, "A" },
1286 bool bRes = checkHorizontalIterator(
1287 *m_pDoc, aData, aChecks, std::size(aChecks));
1289 if (!bRes)
1290 CPPUNIT_FAIL("Failed on test at end.");
1294 // Data in middle
1295 std::vector<std::vector<const char*>> aData = {
1296 { nullptr, nullptr },
1297 { nullptr, nullptr },
1298 { nullptr, "A" },
1299 { nullptr, "1" },
1300 { nullptr, nullptr },
1303 static const HoriIterCheck aChecks[] = {
1304 { 1, 2, "A" },
1305 { 1, 3, "1" },
1308 bool bRes = checkHorizontalIterator(
1309 *m_pDoc, aData, aChecks, std::size(aChecks));
1311 if (!bRes)
1312 CPPUNIT_FAIL("Failed on test in middle.");
1315 m_pDoc->DeleteTab(0);
1318 CPPUNIT_TEST_FIXTURE(Test, testValueIterator)
1320 m_pDoc->InsertTab(0, u"Test"_ustr);
1322 // Turn on "precision as shown" option.
1323 ScDocOptions aOpt = m_pDoc->GetDocOptions();
1324 aOpt.SetCalcAsShown(true);
1325 m_pDoc->SetDocOptions(aOpt);
1327 ScInterpreterContext aContext(*m_pDoc, m_pDoc->GetFormatTable());
1329 // Purely horizontal data layout with numeric data.
1330 for (SCCOL i = 1; i <= 3; ++i)
1331 m_pDoc->SetValue(ScAddress(i,2,0), i);
1334 const double aChecks[] = { 1.0, 2.0, 3.0 };
1335 size_t const nCheckLen = std::size(aChecks);
1336 ScValueIterator aIter(aContext, ScRange(1,2,0,3,2,0));
1337 bool bHas = false;
1338 size_t nCheckPos = 0;
1339 double fVal;
1340 FormulaError nErr;
1341 for (bHas = aIter.GetFirst(fVal, nErr); bHas; bHas = aIter.GetNext(fVal, nErr), ++nCheckPos)
1343 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1344 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos], fVal);
1345 CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(nErr));
1349 m_pDoc->DeleteTab(0);
1352 CPPUNIT_TEST_FIXTURE(Test, testHorizontalAttrIterator)
1354 m_pDoc->InsertTab(0, u"Test"_ustr);
1356 // Set the background color of B2:C3,D2,E3,C4:D4,B5:D5 to blue
1357 ScPatternAttr aCellBackColor(m_pDoc->getCellAttributeHelper());
1358 aCellBackColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
1359 m_pDoc->ApplyPatternAreaTab(1, 1, 2, 2, 0, aCellBackColor);
1360 m_pDoc->ApplyPatternAreaTab(3, 1, 3, 1, 0, aCellBackColor);
1361 m_pDoc->ApplyPatternAreaTab(4, 2, 4, 2, 0, aCellBackColor);
1362 m_pDoc->ApplyPatternAreaTab(2, 3, 3, 3, 0, aCellBackColor);
1363 m_pDoc->ApplyPatternAreaTab(1, 4, 4, 4, 0, aCellBackColor);
1365 // some numeric data
1366 for (SCCOL i = 1; i <= 4; ++i)
1367 for (SCROW j = 1; j <= 4; ++j)
1368 m_pDoc->SetValue(ScAddress(i,j,0), i*10+j);
1371 const int aChecks[][3] = { {1, 3, 1}, {1, 2, 2}, {4, 4, 2}, {2, 3, 3}, {1, 4, 4} };
1372 const size_t nCheckLen = std::size(aChecks);
1374 ScHorizontalAttrIterator aIter(*m_pDoc, 0, 0, 0, 5, 5);
1375 SCCOL nCol1, nCol2;
1376 SCROW nRow;
1377 size_t nCheckPos = 0;
1378 for (const ScPatternAttr* pAttr = aIter.GetNext(nCol1, nCol2, nRow); pAttr; pAttr = aIter.GetNext(nCol1, nCol2, nRow))
1380 if (pAttr->isDefault())
1381 continue;
1382 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1383 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][0], static_cast<int>(nCol1));
1384 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][1], static_cast<int>(nCol2));
1385 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][2], static_cast<int>(nRow));
1386 ++nCheckPos;
1390 m_pDoc->DeleteTab(0);
1393 CPPUNIT_TEST_FIXTURE(Test, testIteratorsUnallocatedColumnsAttributes)
1395 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1397 // Set values in first two columns, to ensure allocation of those columns.
1398 m_pDoc->SetValue(ScAddress(0,1,0), 1);
1399 m_pDoc->SetValue(ScAddress(1,1,0), 2);
1400 constexpr SCCOL allocatedColsCount = 2;
1401 assert( allocatedColsCount >= INITIALCOLCOUNT );
1402 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1404 // Make entire second row and third row bold.
1405 ScPatternAttr boldAttr(m_pDoc->getCellAttributeHelper());
1406 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1407 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 2, 0, boldAttr);
1409 // That shouldn't need allocating more columns, just changing the default attribute.
1410 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1411 vcl::Font aFont;
1412 const ScPatternAttr* pattern = m_pDoc->GetPattern(m_pDoc->MaxCol(), 1, 0);
1413 pattern->fillFontOnly(aFont);
1414 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight());
1416 // Test iterators.
1417 ScDocAttrIterator docit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1418 SCCOL col1, col2;
1419 SCROW row1, row2;
1420 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1421 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1422 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1423 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1424 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1425 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col1 );
1426 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1427 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1428 CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == nullptr );
1430 ScAttrRectIterator rectit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1431 CPPUNIT_ASSERT_EQUAL( pattern, rectit.GetNext( col1, col2, row1, row2 ));
1432 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1433 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1434 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1435 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1436 CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1438 ScHorizontalAttrIterator horit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1439 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1440 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1441 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1442 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1443 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1444 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1445 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1446 CPPUNIT_ASSERT_EQUAL( SCROW(2), row1 );
1447 CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == nullptr );
1449 m_pDoc->DeleteTab(0);
1452 CPPUNIT_TEST_FIXTURE(Test, testIteratorsDefPattern)
1454 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1456 // The default pattern is the default style, which can be edited by the user.
1457 // As such iterators should not ignore it by default, because it might contain
1458 // some attributes set.
1460 // Set cells as bold, default allocated, bold, default unallocated.
1461 SCCOL firstCol = 100;
1462 SCCOL lastCol = 103;
1463 ScPatternAttr boldAttr(m_pDoc->getCellAttributeHelper());
1464 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1465 m_pDoc->ApplyPattern(100, 0, 0, boldAttr);
1466 m_pDoc->ApplyPattern(102, 0, 0, boldAttr);
1468 CPPUNIT_ASSERT_EQUAL(SCCOL(102 + 1), m_pDoc->GetAllocatedColumnsCount(0));
1469 const ScPatternAttr* pattern = m_pDoc->GetPattern(100, 0, 0);
1470 const ScPatternAttr* defPattern(&m_pDoc->getCellAttributeHelper().getDefaultCellAttribute()); //GetDefPattern();
1471 CPPUNIT_ASSERT(!ScPatternAttr::areSame(pattern, defPattern));
1472 CPPUNIT_ASSERT_EQUAL(pattern, m_pDoc->GetPattern(102, 0, 0));
1473 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(101, 0, 0));
1474 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(103, 0, 0));
1476 // Test iterators.
1477 ScDocAttrIterator docit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1478 SCCOL col1, col2;
1479 SCROW row1, row2;
1480 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1481 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1482 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1483 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1484 CPPUNIT_ASSERT(docit.GetNext( col1, row1, row2 ) == nullptr );
1486 ScAttrRectIterator rectit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1487 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1488 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1489 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1490 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1491 CPPUNIT_ASSERT(rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1493 ScHorizontalAttrIterator horit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1494 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1495 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1496 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1497 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1498 CPPUNIT_ASSERT(horit.GetNext( col1, col2, row1 ) == nullptr );
1500 m_pDoc->DeleteTab(0);
1503 CPPUNIT_TEST_FIXTURE(Test, testLastChangedColFlagsWidth)
1505 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1507 constexpr SCCOL firstChangedCol = 100;
1508 assert( firstChangedCol > m_pDoc->GetAllocatedColumnsCount(0));
1509 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1510 for( SCCOL col = firstChangedCol; col <= m_pDoc->MaxCol(); ++col )
1511 m_pDoc->SetColWidth( col, 0, 10 );
1513 // That shouldn't need allocating more columns, just changing column flags.
1514 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1515 // But the flags are changed.
1516 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), m_pDoc->GetLastChangedColFlagsWidth(0));
1518 m_pDoc->DeleteTab(0);
1521 namespace {
1523 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
1525 const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
1526 if (pBC)
1528 cerr << "Broadcaster shouldn't be here." << endl;
1529 return false;
1532 pBC = rDoc.GetBroadcaster(rTo);
1533 if (!pBC)
1535 cerr << "Broadcaster should be here." << endl;
1536 return false;
1538 return true;
1541 formula::FormulaToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
1543 ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
1544 if (!pFC)
1546 cerr << "Formula cell expected, but not found." << endl;
1547 return nullptr;
1550 ScTokenArray* pTokens = pFC->GetCode();
1551 if (!pTokens)
1553 cerr << "Token array is not present." << endl;
1554 return nullptr;
1557 formula::FormulaToken* pToken = pTokens->FirstToken();
1558 if (!pToken || pToken->GetType() != formula::svSingleRef)
1560 cerr << "Not a single reference token." << endl;
1561 return nullptr;
1564 return pToken;
1567 bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCCOL nRelCol, SCROW nRelRow)
1569 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1570 if (!pToken)
1571 return false;
1573 ScSingleRefData& rRef = *pToken->GetSingleRef();
1574 if (!rRef.IsColRel() || rRef.Col() != nRelCol)
1576 cerr << "Unexpected relative column address." << endl;
1577 return false;
1580 if (!rRef.IsRowRel() || rRef.Row() != nRelRow)
1582 cerr << "Unexpected relative row address." << endl;
1583 return false;
1586 return true;
1589 bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
1591 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1592 if (!pToken)
1593 return false;
1595 ScSingleRefData& rRef = *pToken->GetSingleRef();
1596 if (!rRef.IsDeleted())
1598 cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
1599 return false;
1602 return true;
1607 CPPUNIT_TEST_FIXTURE(Test, testCellBroadcaster)
1610 * More direct test for cell broadcaster management, used to track formula
1611 * dependencies.
1613 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"foo"_ustr));
1615 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1616 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1.
1617 double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
1618 CPPUNIT_ASSERT_EQUAL(0.0, val);
1620 const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1621 CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
1623 // Change the value of A1 and make sure that B1 follows.
1624 m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
1625 val = m_pDoc->GetValue(ScAddress(1,0,0));
1626 CPPUNIT_ASSERT_EQUAL(1.23, val);
1628 // Move column A down 5 cells. Make sure B1 now references A6, not A1.
1629 m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
1630 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1631 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
1633 // Make sure the broadcaster has also moved.
1634 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1635 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
1637 // Set new value to A6 and make sure B1 gets updated.
1638 m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
1639 val = m_pDoc->GetValue(ScAddress(1,0,0));
1640 CPPUNIT_ASSERT_EQUAL(45.6, val);
1642 // Move column A up 3 cells, and make sure B1 now references A3, not A6.
1643 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
1644 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1645 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
1647 // The broadcaster should also have been relocated from A6 to A3.
1648 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1649 broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
1651 // Insert cells over A1:A10 and shift cells to right.
1652 m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
1653 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1654 checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
1655 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1656 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
1658 // Delete formula in C2, which should remove the broadcaster in B3.
1659 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1660 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
1661 clearRange(m_pDoc, ScRange(ScAddress(2,0,0)));
1662 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
1663 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1664 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
1666 // Clear everything and start over.
1667 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1669 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1.
1670 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1671 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
1673 // While column A is still empty, move column A down 2 cells. This should
1674 // move the broadcaster from A1 to A3.
1675 m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
1676 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1677 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
1679 // Move it back while column A is still empty.
1680 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
1681 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1682 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
1684 // Clear everything again
1685 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1687 // B1:B3 depends on A1:A3
1688 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr);
1689 m_pDoc->SetString(ScAddress(1,1,0), u"=A2"_ustr);
1690 m_pDoc->SetString(ScAddress(1,2,0), u"=A3"_ustr);
1691 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1692 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1693 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1694 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
1695 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
1696 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
1697 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1698 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
1699 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
1701 // Insert Rows at row 2, down 5 rows.
1702 m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
1703 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1704 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1705 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1707 // Broadcasters in A2 and A3 should shift down by 5 rows.
1708 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1709 broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
1710 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1711 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
1713 // B2 and B3 should reference shifted cells.
1714 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1715 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
1716 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1717 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
1719 // Delete cells with broadcasters.
1720 m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
1721 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
1722 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
1724 // References in B2 and B3 should be invalid.
1725 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
1726 checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
1727 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
1728 checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
1730 // Clear everything again
1731 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1734 // Switch to R1C1 to make it easier to input relative references in multiple cells.
1735 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1737 // Have B1:B20 reference A1:A20.
1738 val = 0.0;
1739 for (SCROW i = 0; i < 20; ++i)
1741 m_pDoc->SetValue(ScAddress(0,i,0), val++);
1742 m_pDoc->SetString(ScAddress(1,i,0), u"=RC[-1]"_ustr);
1746 // Ensure that the formula cells show correct values, and the referenced
1747 // cells have broadcasters.
1748 val = 0.0;
1749 for (SCROW i = 0; i < 20; ++i, ++val)
1751 CPPUNIT_ASSERT_EQUAL(val, m_pDoc->GetValue(ScAddress(1,i,0)));
1752 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1753 CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
1756 // Delete formula cells in B2:B19.
1757 clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
1758 // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
1759 CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1760 CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
1761 for (SCROW i = 1; i <= 18; ++i)
1763 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1764 CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
1767 // Clear everything again
1768 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1770 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
1771 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr);
1772 m_pDoc->SetString(ScAddress(2,0,0), u"=B1"_ustr);
1773 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,0,0));
1774 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,0,0));
1775 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2,0,0));
1777 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1778 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1779 pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
1780 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1782 // Change the value of A1 and make sure everyone follows suit.
1783 m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
1784 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
1785 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
1786 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
1788 // Insert a column at column B.
1789 m_pDoc->InsertCol(ScRange(1,0,0,1,m_pDoc->MaxRow(),0));
1790 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1791 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1792 pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
1793 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1795 // Change the value of A1 again.
1796 m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
1797 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
1798 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
1799 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
1801 m_pDoc->DeleteTab(0);
1804 CPPUNIT_TEST_FIXTURE(Test, testFuncParam)
1807 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1808 m_pDoc->InsertTab (0, u"foo"_ustr));
1810 // First, the normal case, with no missing parameters.
1811 m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;2;3)"_ustr);
1812 m_pDoc->CalcFormulaTree(false, false);
1813 double val = m_pDoc->GetValue(0, 0, 0);
1814 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 2.0, val);
1816 // Now function with missing parameters. Missing values should be treated
1817 // as zeros.
1818 m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;;;)"_ustr);
1819 m_pDoc->CalcFormulaTree(false, false);
1820 val = m_pDoc->GetValue(0, 0, 0);
1821 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 0.25, val);
1823 // Conversion of string to numeric argument.
1824 m_pDoc->SetString(0, 0, 0, u"=\"\"+3"_ustr); // empty string
1825 m_pDoc->SetString(0, 1, 0, u"=\" \"+3"_ustr); // only blank
1826 m_pDoc->SetString(0, 2, 0, u"=\" 4 \"+3"_ustr); // number in blanks
1827 m_pDoc->SetString(0, 3, 0, u"=\" x \"+3"_ustr); // non-numeric
1828 m_pDoc->SetString(0, 4, 0, u"=\"4.4\"+3"_ustr); // locale dependent
1830 OUString aVal;
1831 ScCalcConfig aConfig;
1833 // With "Convert also locale dependent" and "Empty string as zero"=True option.
1834 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1835 aConfig.mbEmptyStringAsZero = true;
1836 m_pDoc->SetCalcConfig(aConfig);
1837 m_pDoc->CalcAll();
1838 val = m_pDoc->GetValue(0, 0, 0);
1839 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1840 val = m_pDoc->GetValue(0, 1, 0);
1841 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1842 val = m_pDoc->GetValue(0, 2, 0);
1843 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1844 aVal = m_pDoc->GetString( 0, 3, 0);
1845 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1846 val = m_pDoc->GetValue(0, 4, 0);
1847 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1849 // With "Convert also locale dependent" and "Empty string as zero"=False option.
1850 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1851 aConfig.mbEmptyStringAsZero = false;
1852 m_pDoc->SetCalcConfig(aConfig);
1853 m_pDoc->CalcAll();
1854 aVal = m_pDoc->GetString( 0, 0, 0);
1855 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1856 aVal = m_pDoc->GetString( 0, 1, 0);
1857 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1858 val = m_pDoc->GetValue(0, 2, 0);
1859 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1860 aVal = m_pDoc->GetString( 0, 3, 0);
1861 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1862 val = m_pDoc->GetValue(0, 4, 0);
1863 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1865 // With "Convert only unambiguous" and "Empty string as zero"=True option.
1866 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1867 aConfig.mbEmptyStringAsZero = true;
1868 m_pDoc->SetCalcConfig(aConfig);
1869 m_pDoc->CalcAll();
1870 val = m_pDoc->GetValue(0, 0, 0);
1871 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1872 val = m_pDoc->GetValue(0, 1, 0);
1873 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1874 val = m_pDoc->GetValue(0, 2, 0);
1875 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1876 aVal = m_pDoc->GetString( 0, 3, 0);
1877 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1878 aVal = m_pDoc->GetString( 0, 4, 0);
1879 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1881 // With "Convert only unambiguous" and "Empty string as zero"=False option.
1882 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1883 aConfig.mbEmptyStringAsZero = false;
1884 m_pDoc->SetCalcConfig(aConfig);
1885 m_pDoc->CalcAll();
1886 aVal = m_pDoc->GetString( 0, 0, 0);
1887 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1888 aVal = m_pDoc->GetString( 0, 1, 0);
1889 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1890 m_pDoc->GetValue(0, 2, 0);
1891 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1892 aVal = m_pDoc->GetString( 0, 3, 0);
1893 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1894 aVal = m_pDoc->GetString( 0, 4, 0);
1895 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1897 // With "Treat as zero" ("Empty string as zero" is ignored).
1898 aConfig.meStringConversion = ScCalcConfig::StringConversion::ZERO;
1899 aConfig.mbEmptyStringAsZero = true;
1900 m_pDoc->SetCalcConfig(aConfig);
1901 m_pDoc->CalcAll();
1902 val = m_pDoc->GetValue(0, 0, 0);
1903 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1904 val = m_pDoc->GetValue(0, 1, 0);
1905 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1906 val = m_pDoc->GetValue(0, 2, 0);
1907 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1908 val = m_pDoc->GetValue(0, 3, 0);
1909 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1910 val = m_pDoc->GetValue(0, 4, 0);
1911 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1913 // With "Generate #VALUE! error" ("Empty string as zero" is ignored).
1914 aConfig.meStringConversion = ScCalcConfig::StringConversion::ILLEGAL;
1915 aConfig.mbEmptyStringAsZero = false;
1916 m_pDoc->SetCalcConfig(aConfig);
1917 m_pDoc->CalcAll();
1918 aVal = m_pDoc->GetString( 0, 0, 0);
1919 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1920 aVal = m_pDoc->GetString( 0, 1, 0);
1921 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1922 aVal = m_pDoc->GetString( 0, 2, 0);
1923 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1924 aVal = m_pDoc->GetString( 0, 3, 0);
1925 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1926 aVal = m_pDoc->GetString( 0, 4, 0);
1927 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1929 m_pDoc->DeleteTab(0);
1932 CPPUNIT_TEST_FIXTURE(Test, testNamedRange)
1934 static const RangeNameDef aNames[] = {
1935 { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
1936 { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
1937 { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
1938 { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
1941 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"Sheet1"_ustr));
1943 m_pDoc->SetValue (0, 0, 0, 101);
1945 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
1946 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + std::size(aNames));
1947 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1948 m_pDoc->SetRangeName(std::move(pNames));
1950 ScRangeName* pNewRanges = m_pDoc->GetRangeName();
1951 CPPUNIT_ASSERT(pNewRanges);
1953 // Make sure the index lookup does the right thing.
1954 for (const auto& rName : aNames)
1956 const ScRangeData* p = pNewRanges->findByIndex(rName.mnIndex);
1957 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
1958 OUString aName = p->GetName();
1959 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(rName.mpName));
1962 // Test usage in formula expression.
1963 m_pDoc->SetString (1, 0, 0, u"=A1/Divisor"_ustr);
1964 m_pDoc->CalcAll();
1966 double result = m_pDoc->GetValue (1, 0, 0);
1967 ASSERT_DOUBLES_EQUAL_MESSAGE ("calculation failed", 1.0, result);
1969 // Test copy-ability of range names.
1970 std::unique_ptr<ScRangeName> pCopiedRanges(new ScRangeName(*pNewRanges));
1971 m_pDoc->SetRangeName(std::move(pCopiedRanges));
1972 // Make sure the index lookup still works.
1973 for (const auto& rName : aNames)
1975 const ScRangeData* p = m_pDoc->GetRangeName()->findByIndex(rName.mnIndex);
1976 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
1977 OUString aName = p->GetName();
1978 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(rName.mpName));
1981 // Test using another-sheet-local name, scope Sheet1.
1982 ScRangeData* pLocal1 = new ScRangeData( *m_pDoc, u"local1"_ustr, ScAddress(0,0,0));
1983 ScRangeData* pLocal2 = new ScRangeData( *m_pDoc, u"local2"_ustr, u"$Sheet1.$A$1"_ustr);
1984 ScRangeData* pLocal3 = new ScRangeData( *m_pDoc, u"local3"_ustr, u"Sheet1.$A$1"_ustr);
1985 ScRangeData* pLocal4 = new ScRangeData( *m_pDoc, u"local4"_ustr, u"$A$1"_ustr); // implicit relative sheet reference
1986 std::unique_ptr<ScRangeName> pLocalRangeName1(new ScRangeName);
1987 pLocalRangeName1->insert(pLocal1);
1988 pLocalRangeName1->insert(pLocal2);
1989 pLocalRangeName1->insert(pLocal3);
1990 pLocalRangeName1->insert(pLocal4);
1991 m_pDoc->SetRangeName(0, std::move(pLocalRangeName1));
1993 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (1, u"Sheet2"_ustr));
1995 // Use other-sheet-local name of Sheet1 on Sheet2.
1996 ScAddress aPos(1,0,1);
1997 OUString aFormula(u"=Sheet1.local1+Sheet1.local2+Sheet1.local3+Sheet1.local4"_ustr);
1998 m_pDoc->SetString(aPos, aFormula);
1999 OUString aString = m_pDoc->GetFormula(1,0,1);
2000 CPPUNIT_ASSERT_EQUAL_MESSAGE("formula string should be equal", aFormula, aString);
2001 double fValue = m_pDoc->GetValue(aPos);
2002 ASSERT_DOUBLES_EQUAL_MESSAGE("value should be 4 times Sheet1.A1", 404.0, fValue);
2004 m_pDoc->DeleteTab(1);
2005 m_pDoc->SetRangeName(0,nullptr); // Delete the names.
2006 m_pDoc->SetRangeName(nullptr); // Delete the names.
2007 m_pDoc->DeleteTab(0);
2010 CPPUNIT_TEST_FIXTURE(Test, testInsertNameList)
2012 m_pDoc->InsertTab(0, u"Test"_ustr);
2014 static const RangeNameDef aNames[] = {
2015 { "MyRange1", "$Test.$A$1:$A$100", 1 },
2016 { "MyRange2", "$Test.$B$1:$B$100", 2 },
2017 { "MyRange3", "$Test.$C$1:$C$100", 3 }
2020 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
2021 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + std::size(aNames));
2022 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
2023 m_pDoc->SetRangeName(std::move(pNames));
2025 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
2026 ScAddress aPos(1,1,0);
2027 rDocFunc.InsertNameList(aPos, true);
2029 for (auto const& rName : aNames)
2031 OUString aName = m_pDoc->GetString(aPos);
2032 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(rName.mpName), aName);
2033 ScAddress aExprPos = aPos;
2034 aExprPos.IncCol();
2035 OUString aExpr = m_pDoc->GetString(aExprPos);
2036 OUString aExpected = "=" + OUString::createFromAscii(rName.mpExpr);
2037 CPPUNIT_ASSERT_EQUAL(aExpected, aExpr);
2038 aPos.IncRow();
2041 m_pDoc->DeleteTab(0);
2044 CPPUNIT_TEST_FIXTURE(Test, testCSV)
2046 const int English = 0, European = 1;
2047 struct {
2048 const char *pStr; int eSep; bool bResult; double nValue;
2049 } aTests[] = {
2050 { "foo", English, false, 0.0 },
2051 { "1.0", English, true, 1.0 },
2052 { "1,0", English, false, 0.0 },
2053 { "1.0", European, false, 0.0 },
2054 { "1.000", European, true, 1000.0 },
2055 { "1,000", European, true, 1.0 },
2056 { "1.000", English, true, 1.0 },
2057 { "1,000", English, true, 1000.0 },
2058 { " 1.0", English, true, 1.0 },
2059 { " 1.0 ", English, true, 1.0 },
2060 { "1.0 ", European, false, 0.0 },
2061 { "1.000", European, true, 1000.0 },
2062 { "1137.999", English, true, 1137.999 },
2063 { "1.000.00", European, false, 0.0 },
2064 { "+,123", English, false, 0.0 },
2065 { "-,123", English, false, 0.0 }
2067 for (const auto& rTest : aTests) {
2068 OUString aStr(rTest.pStr, strlen (rTest.pStr), RTL_TEXTENCODING_UTF8);
2069 double nValue = 0.0;
2070 bool bResult = ScStringUtil::parseSimpleNumber
2071 (aStr, rTest.eSep == English ? '.' : ',',
2072 rTest.eSep == English ? ',' : '.',
2074 nValue);
2075 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric detection failure", rTest.bResult, bResult);
2076 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric value failure", rTest.nValue, nValue);
2080 template<typename Evaluator>
2081 static void checkMatrixElements(const ScMatrix& rMat)
2083 SCSIZE nC, nR;
2084 rMat.GetDimensions(nC, nR);
2085 Evaluator aEval;
2086 for (SCSIZE i = 0; i < nC; ++i)
2088 for (SCSIZE j = 0; j < nR; ++j)
2090 aEval(i, j, rMat.Get(i, j));
2095 namespace {
2097 struct AllZeroMatrix
2099 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2101 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2102 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
2106 struct PartiallyFilledZeroMatrix
2108 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2110 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2111 if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
2113 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be 3.0", 3.0, rVal.fVal);
2115 else
2117 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
2122 struct AllEmptyMatrix
2124 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2126 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
2127 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2131 struct PartiallyFilledEmptyMatrix
2133 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2135 if (nCol == 1 && nRow == 1)
2137 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of boolean type", int(ScMatValType::Boolean), static_cast<int>(rVal.nType));
2138 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", 1.0, rVal.fVal);
2140 else if (nCol == 4 && nRow == 5)
2142 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
2143 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", -12.5, rVal.fVal);
2145 else if (nCol == 8 && nRow == 2)
2147 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::String), static_cast<int>(rVal.nType));
2148 CPPUNIT_ASSERT_EQUAL_MESSAGE("element value is not what is expected", u"Test"_ustr, rVal.aStr.getString());
2150 else if (nCol == 8 && nRow == 11)
2152 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty path type", int(ScMatValType::EmptyPath), static_cast<int>(rVal.nType));
2153 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2155 else
2157 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
2158 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
2165 CPPUNIT_TEST_FIXTURE(Test, testMatrix)
2167 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
2168 ScMatrixRef pMat, pMat2;
2170 // First, test the zero matrix type.
2171 pMat = new ScMatrix(0, 0, 0.0);
2172 SCSIZE nC, nR;
2173 pMat->GetDimensions(nC, nR);
2174 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nC);
2175 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nR);
2176 pMat->Resize(4, 10, 0.0);
2177 pMat->GetDimensions(nC, nR);
2178 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(4), nC);
2179 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nR);
2180 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2181 !pMat->And());
2182 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2183 !pMat->Or());
2185 // Resizing into a larger matrix should fill the void space with zeros.
2186 checkMatrixElements<AllZeroMatrix>(*pMat);
2188 pMat->FillDouble(3.0, 1, 2, 2, 8);
2189 checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
2190 CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
2191 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2192 !pMat->And());
2193 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2194 pMat->Or());
2195 pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
2196 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2197 pMat->And());
2198 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2199 pMat->Or());
2201 // Test the AND and OR evaluations.
2202 pMat = new ScMatrix(2, 2, 0.0);
2204 // Only some of the elements are non-zero.
2205 pMat->PutBoolean(true, 0, 0);
2206 pMat->PutDouble(1.0, 1, 1);
2207 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2208 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2210 // All of the elements are non-zero.
2211 pMat->PutBoolean(true, 0, 1);
2212 pMat->PutDouble(2.3, 1, 0);
2213 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2214 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2216 // Now test the empty matrix type.
2217 pMat = new ScMatrix(10, 20);
2218 pMat->GetDimensions(nC, nR);
2219 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nC);
2220 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(20), nR);
2221 checkMatrixElements<AllEmptyMatrix>(*pMat);
2223 pMat->PutBoolean(true, 1, 1);
2224 pMat->PutDouble(-12.5, 4, 5);
2225 pMat->PutString(rPool.intern(u"Test"_ustr), 8, 2);
2226 pMat->PutEmptyPath(8, 11);
2227 checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2229 // Test resizing.
2230 pMat = new ScMatrix(0, 0);
2231 pMat->Resize(2, 2, 1.5);
2232 pMat->PutEmpty(1, 1);
2234 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2235 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2236 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2237 CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2239 // Max and min values.
2240 pMat = new ScMatrix(2, 2, 0.0);
2241 pMat->PutDouble(-10, 0, 0);
2242 pMat->PutDouble(-12, 0, 1);
2243 pMat->PutDouble(-8, 1, 0);
2244 pMat->PutDouble(-25, 1, 1);
2245 CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
2246 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
2247 pMat->PutString(rPool.intern(u"Test"_ustr), 0, 0);
2248 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
2249 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
2250 pMat->PutBoolean(true, 0, 0);
2251 CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
2252 pMat = new ScMatrix(2, 2, 10.0);
2253 pMat->PutBoolean(false, 0, 0);
2254 pMat->PutDouble(12.5, 1, 1);
2255 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
2256 CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
2258 // Convert matrix into a linear double array. String elements become NaN
2259 // and empty elements become 0.
2260 pMat = new ScMatrix(3, 3);
2261 pMat->PutDouble(2.5, 0, 0);
2262 pMat->PutDouble(1.2, 0, 1);
2263 pMat->PutString(rPool.intern(u"A"_ustr), 1, 1);
2264 pMat->PutDouble(2.3, 2, 1);
2265 pMat->PutDouble(-20, 2, 2);
2267 static const double fNaN = std::numeric_limits<double>::quiet_NaN();
2269 std::vector<double> aDoubles;
2270 pMat->GetDoubleArray(aDoubles);
2273 const double pChecks[] = { 2.5, 1.2, 0, 0, fNaN, 0, 0, 2.3, -20 };
2274 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2275 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2277 if (std::isnan(pChecks[i]))
2278 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2279 else
2280 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2284 pMat2 = new ScMatrix(3, 3, 10.0);
2285 pMat2->PutString(rPool.intern(u"B"_ustr), 1, 0);
2286 pMat2->MergeDoubleArrayMultiply(aDoubles);
2289 const double pChecks[] = { 25, 12, 0, fNaN, fNaN, 0, 0, 23, -200 };
2290 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2291 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2293 if (std::isnan(pChecks[i]))
2294 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2295 else
2296 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2301 CPPUNIT_TEST_FIXTURE(Test, testMatrixComparisonWithErrors)
2303 m_pDoc->InsertTab(0, u"foo"_ustr);
2305 // Insert the source values in A1:A2.
2306 m_pDoc->SetString(0, 0, 0, u"=1/0"_ustr);
2307 m_pDoc->SetValue( 0, 1, 0, 1.0);
2309 // Create a matrix formula in B3:B4 referencing A1:A2 and doing a greater
2310 // than comparison on it's values. Error value must be propagated.
2311 ScMarkData aMark(m_pDoc->GetSheetLimits());
2312 aMark.SelectOneTable(0);
2313 m_pDoc->InsertMatrixFormula(1, 2, 1, 3, aMark, u"=A1:A2>0"_ustr);
2315 CPPUNIT_ASSERT_EQUAL(u"#DIV/0!"_ustr, m_pDoc->GetString(0,0,0));
2316 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue( 0,1,0));
2317 CPPUNIT_ASSERT_EQUAL(u"#DIV/0!"_ustr, m_pDoc->GetString(1,2,0));
2318 CPPUNIT_ASSERT_EQUAL(u"TRUE"_ustr, m_pDoc->GetString(1,3,0));
2320 m_pDoc->DeleteTab(0);
2323 CPPUNIT_TEST_FIXTURE(Test, testMatrixConditionalBooleanResult)
2325 m_pDoc->InsertTab(0, u"foo"_ustr);
2327 // Create matrix formulas in A1:B1,A2:B2,A3:B3,A4:B4 producing mixed
2328 // boolean and numeric results in an unformatted area.
2329 ScMarkData aMark(m_pDoc->GetSheetLimits());
2330 aMark.SelectOneTable(0);
2331 m_pDoc->InsertMatrixFormula( 0,0, 1,0, aMark, u"=IF({1;0};TRUE();42)"_ustr); // {TRUE,42}
2332 m_pDoc->InsertMatrixFormula( 0,1, 1,1, aMark, u"=IF({0;1};TRUE();42)"_ustr); // {42,1} aim for {42,TRUE}
2333 m_pDoc->InsertMatrixFormula( 0,2, 1,2, aMark, u"=IF({1;0};42;FALSE())"_ustr); // {42,0} aim for {42,FALSE}
2334 m_pDoc->InsertMatrixFormula( 0,3, 1,3, aMark, u"=IF({0;1};42;FALSE())"_ustr); // {FALSE,42}
2336 CPPUNIT_ASSERT_EQUAL( u"TRUE"_ustr, m_pDoc->GetString(0,0,0));
2337 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(1,0,0));
2338 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(0,1,0));
2339 //CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(1,1,0)); // not yet
2340 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(0,2,0));
2341 //CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(1,2,0)); // not yet
2342 CPPUNIT_ASSERT_EQUAL( u"FALSE"_ustr, m_pDoc->GetString(0,3,0));
2343 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(1,3,0));
2345 m_pDoc->DeleteTab(0);
2348 CPPUNIT_TEST_FIXTURE(Test, testEnterMixedMatrix)
2350 m_pDoc->InsertTab(0, u"foo"_ustr);
2352 // Insert the source values in A1:B2.
2353 m_pDoc->SetString(0, 0, 0, u"A"_ustr);
2354 m_pDoc->SetString(1, 0, 0, u"B"_ustr);
2355 double val = 1.0;
2356 m_pDoc->SetValue(0, 1, 0, val);
2357 val = 2.0;
2358 m_pDoc->SetValue(1, 1, 0, val);
2360 // Create a matrix range in A4:B5 referencing A1:B2.
2361 ScMarkData aMark(m_pDoc->GetSheetLimits());
2362 aMark.SelectOneTable(0);
2363 m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, u"=A1:B2"_ustr);
2365 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2366 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2367 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2368 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2370 m_pDoc->DeleteTab(0);
2373 CPPUNIT_TEST_FIXTURE(Test, testMatrixEditable)
2375 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2377 m_pDoc->InsertTab(0, u"Test"_ustr);
2379 // Values in A1:B1.
2380 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2381 m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
2383 // A2 is a normal formula.
2384 m_pDoc->SetString(ScAddress(0,1,0), u"=5"_ustr);
2386 // A3:A4 is a matrix.
2387 ScRange aMatRange(0,2,0,0,3,0);
2388 ScMarkData aMark(m_pDoc->GetSheetLimits());
2389 aMark.SetMarkArea(aMatRange);
2390 m_pDoc->InsertMatrixFormula(0, 2, 0, 3, aMark, u"=TRANSPOSE(A1:B1)"_ustr);
2392 // Check their values.
2393 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2394 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2395 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2397 // Make sure A3:A4 is a matrix.
2398 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2399 CPPUNIT_ASSERT(pFC);
2400 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should be matrix origin.",
2401 ScMatrixMode::Formula, pFC->GetMatrixFlag());
2403 pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
2404 CPPUNIT_ASSERT(pFC);
2405 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should be matrix reference.",
2406 ScMatrixMode::Reference, pFC->GetMatrixFlag());
2408 // Check to make sure A3:A4 combined is editable.
2409 ScEditableTester aTester;
2410 aTester.TestSelection(*m_pDoc, aMark);
2411 CPPUNIT_ASSERT(aTester.IsEditable());
2413 m_pDoc->DeleteTab(0);
2416 CPPUNIT_TEST_FIXTURE(Test, testCellCopy)
2418 m_pDoc->InsertTab(0, u"TestTab"_ustr);
2419 ScAddress aSrc(0,0,0);
2420 ScAddress aDest(0,1,0);
2421 OUString aStr(u"please copy me"_ustr);
2422 m_pDoc->SetString(aSrc, u"please copy me"_ustr);
2423 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aSrc));
2424 // copy to self - why not ?
2425 m_pDoc->CopyCellToDocument(aSrc,aDest,*m_pDoc);
2426 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aDest));
2429 CPPUNIT_TEST_FIXTURE(Test, testSheetCopy)
2431 m_pDoc->InsertTab(0, u"TestTab"_ustr);
2432 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
2433 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2435 // We need a drawing layer in order to create caption objects.
2436 m_pDoc->InitDrawLayer(m_xDocShell.get());
2438 // Insert text in A1.
2439 m_pDoc->SetString(ScAddress(0,0,0), u"copy me"_ustr);
2441 // Insert edit cells in B1:B3.
2442 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
2443 rEE.SetTextCurrentDefaults(u"Edit 1"_ustr);
2444 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject());
2445 rEE.SetTextCurrentDefaults(u"Edit 2"_ustr);
2446 m_pDoc->SetEditText(ScAddress(1,1,0), rEE.CreateTextObject());
2447 rEE.SetTextCurrentDefaults(u"Edit 3"_ustr);
2448 m_pDoc->SetEditText(ScAddress(1,2,0), rEE.CreateTextObject());
2450 SCROW nRow1, nRow2;
2451 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2452 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2453 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2454 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2456 // insert a note
2457 ScAddress aAdrA1 (0,2,0); // empty cell content.
2458 ScPostIt *pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
2459 pNoteA1->SetText(aAdrA1, u"Hello world in A3"_ustr);
2461 // Copy and test the result.
2462 m_pDoc->CopyTab(0, 1);
2463 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2464 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2466 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2467 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2468 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2469 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2470 CPPUNIT_ASSERT_MESSAGE("There should be note on A3 in new sheet", m_pDoc->HasNote(ScAddress(0,2,1)));
2471 CPPUNIT_ASSERT_EQUAL(u"copy me"_ustr, m_pDoc->GetString(ScAddress(0,0,1)));
2473 // Check the copied edit cells.
2474 const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(1,0,1));
2475 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B1.", pEditObj);
2476 CPPUNIT_ASSERT_EQUAL(u"Edit 1"_ustr, pEditObj->GetText(0));
2477 pEditObj = m_pDoc->GetEditText(ScAddress(1,1,1));
2478 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B2.", pEditObj);
2479 CPPUNIT_ASSERT_EQUAL(u"Edit 2"_ustr, pEditObj->GetText(0));
2480 pEditObj = m_pDoc->GetEditText(ScAddress(1,2,1));
2481 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B3.", pEditObj);
2482 CPPUNIT_ASSERT_EQUAL(u"Edit 3"_ustr, pEditObj->GetText(0));
2484 m_pDoc->DeleteTab(1);
2486 m_pDoc->SetRowHidden(5, 10, 0, true);
2487 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2488 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2489 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2490 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2491 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2492 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2493 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2494 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2495 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2496 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2497 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2498 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2500 // Copy the sheet once again.
2501 m_pDoc->CopyTab(0, 1);
2502 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2503 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2504 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2505 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2506 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2507 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2508 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2509 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2510 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2511 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2512 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2513 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2514 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2515 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2516 m_pDoc->DeleteTab(1);
2517 m_pDoc->DeleteTab(0);
2520 CPPUNIT_TEST_FIXTURE(Test, testSheetMove)
2522 m_pDoc->InsertTab(0, u"TestTab1"_ustr);
2523 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2524 SCROW nRow1, nRow2;
2525 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2526 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2527 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2528 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2530 //test if inserting before another sheet works
2531 m_pDoc->InsertTab(0, u"TestTab2"_ustr);
2532 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2533 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2534 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2535 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2536 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2538 // Move and test the result.
2539 m_pDoc->MoveTab(0, 1);
2540 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2541 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2542 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2543 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2544 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2545 OUString aName;
2546 m_pDoc->GetName(0, aName);
2547 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab1"_ustr, aName);
2549 m_pDoc->SetRowHidden(5, 10, 0, true);
2550 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2551 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2552 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2553 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2554 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2555 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2556 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2557 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2558 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2559 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2560 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2561 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2563 // Move the sheet once again.
2564 m_pDoc->MoveTab(1, 0);
2565 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2566 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2567 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2568 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2569 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2570 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2571 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2572 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2573 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2574 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2575 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2576 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2577 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2578 m_pDoc->GetName(0, aName);
2579 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab2"_ustr, aName);
2580 m_pDoc->DeleteTab(1);
2581 m_pDoc->DeleteTab(0);
2584 CPPUNIT_TEST_FIXTURE(Test, testDataArea)
2586 m_pDoc->InsertTab(0, u"Data"_ustr);
2588 // Totally empty sheet should be rightfully considered empty in all accounts.
2589 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 100, 100, 0));
2590 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2592 // Now, set borders in some cells...
2593 ::editeng::SvxBorderLine aLine(nullptr, 50, SvxBorderLineStyle::SOLID);
2594 SvxBoxItem aBorderItem(ATTR_BORDER);
2595 aBorderItem.SetLine(&aLine, SvxBoxItemLine::LEFT);
2596 aBorderItem.SetLine(&aLine, SvxBoxItemLine::RIGHT);
2597 for (SCROW i = 0; i < 100; ++i)
2598 // Set borders from row 1 to 100.
2599 m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
2601 // Now the sheet is considered non-empty for printing purposes, but still
2602 // be empty in all the other cases.
2603 CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
2604 !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
2605 CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
2606 m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2608 // Adding a real cell content should turn the block non-empty.
2609 m_pDoc->SetString(0, 0, 0, u"Some text"_ustr);
2610 CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
2611 !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2613 // TODO: Add more tests for normal data area calculation.
2615 m_pDoc->DeleteTab(0);
2618 CPPUNIT_TEST_FIXTURE(Test, testStreamValid)
2621 * Make sure the sheet streams are invalidated properly.
2623 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
2624 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
2625 m_pDoc->InsertTab(2, u"Sheet3"_ustr);
2626 m_pDoc->InsertTab(3, u"Sheet4"_ustr);
2627 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", static_cast<SCTAB>(4), m_pDoc->GetTableCount());
2629 OUString a1(u"A1"_ustr);
2630 OUString a2(u"A2"_ustr);
2631 OUString test;
2633 // Put values into Sheet1.
2634 m_pDoc->SetString(0, 0, 0, a1);
2635 m_pDoc->SetString(0, 1, 0, a2);
2636 test = m_pDoc->GetString(0, 0, 0);
2637 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A1", test, a1);
2638 test = m_pDoc->GetString(0, 1, 0);
2639 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A2", test, a2);
2641 // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
2642 m_pDoc->SetString(0, 0, 1, u"=Sheet1.A1"_ustr);
2643 m_pDoc->SetString(0, 1, 1, u"=Sheet1.A2"_ustr);
2644 m_pDoc->SetString(0, 0, 2, u"=Sheet1.A1"_ustr);
2645 m_pDoc->SetString(0, 0, 3, u"=Sheet1.A2"_ustr);
2647 test = m_pDoc->GetString(0, 0, 1);
2648 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A1", test, a1);
2649 test = m_pDoc->GetString(0, 1, 1);
2650 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A2", test, a2);
2651 test = m_pDoc->GetString(0, 0, 2);
2652 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a1);
2653 test = m_pDoc->GetString(0, 0, 3);
2654 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a2);
2656 // Set all sheet streams valid after all the initial cell values are in
2657 // place. In reality we need to have real XML streams stored in order to
2658 // claim they are valid, but we are just testing the flag values here.
2659 m_pDoc->SetStreamValid(0, true);
2660 m_pDoc->SetStreamValid(1, true);
2661 m_pDoc->SetStreamValid(2, true);
2662 m_pDoc->SetStreamValid(3, true);
2663 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
2664 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
2665 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
2666 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
2668 // Now, insert a new row at row 2 position on Sheet1. This will move cell
2669 // A2 downward but cell A1 remains unmoved.
2670 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 2);
2671 test = m_pDoc->GetString(0, 0, 0);
2672 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell A1 should not have moved.", test, a1);
2673 test = m_pDoc->GetString(0, 3, 0);
2674 CPPUNIT_ASSERT_EQUAL_MESSAGE("the old cell A2 should now be at A4.", test, a2);
2675 ScRefCellValue aCell;
2676 aCell.assign(*m_pDoc, ScAddress(0,1,0));
2677 CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
2678 aCell.assign(*m_pDoc, ScAddress(0,2,0));
2679 CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
2681 // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
2682 // invalidated, whereas Sheet3's stream should still be valid.
2683 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
2684 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
2685 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
2686 CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
2688 m_pDoc->DeleteTab(3);
2689 m_pDoc->DeleteTab(2);
2690 m_pDoc->DeleteTab(1);
2691 m_pDoc->DeleteTab(0);
2694 CPPUNIT_TEST_FIXTURE(Test, testFunctionLists)
2697 * Test built-in cell functions to make sure their categories and order
2698 * are correct.
2700 const char* aDataBase[] = {
2701 "DAVERAGE",
2702 "DCOUNT",
2703 "DCOUNTA",
2704 "DGET",
2705 "DMAX",
2706 "DMIN",
2707 "DPRODUCT",
2708 "DSTDEV",
2709 "DSTDEVP",
2710 "DSUM",
2711 "DVAR",
2712 "DVARP",
2713 nullptr
2716 const char* aDateTime[] = {
2717 "DATE",
2718 "DATEDIF",
2719 "DATEVALUE",
2720 "DAY",
2721 "DAYS",
2722 "DAYS360",
2723 "DAYSINMONTH",
2724 "DAYSINYEAR",
2725 "EASTERSUNDAY",
2726 "HOUR",
2727 "ISLEAPYEAR",
2728 "ISOWEEKNUM",
2729 "MINUTE",
2730 "MONTH",
2731 "MONTHS",
2732 "NETWORKDAYS",
2733 "NETWORKDAYS.INTL",
2734 "NOW",
2735 "SECOND",
2736 "TIME",
2737 "TIMEVALUE",
2738 "TODAY",
2739 "WEEKDAY",
2740 "WEEKNUM",
2741 "WEEKNUM_OOO",
2742 "WEEKS",
2743 "WEEKSINYEAR",
2744 "WORKDAY.INTL",
2745 "YEAR",
2746 "YEARS",
2747 nullptr
2750 const char* aFinancial[] = {
2751 "CUMIPMT",
2752 "CUMPRINC",
2753 "DB",
2754 "DDB",
2755 "EFFECT",
2756 "FV",
2757 "IPMT",
2758 "IRR",
2759 "ISPMT",
2760 "MIRR",
2761 "NOMINAL",
2762 "NPER",
2763 "NPV",
2764 "OPT_BARRIER",
2765 "OPT_PROB_HIT",
2766 "OPT_PROB_INMONEY",
2767 "OPT_TOUCH",
2768 "PDURATION",
2769 "PMT",
2770 "PPMT",
2771 "PV",
2772 "RATE",
2773 "RRI",
2774 "SLN",
2775 "SYD",
2776 "VDB",
2777 nullptr
2780 const char* aInformation[] = {
2781 "CELL",
2782 "CURRENT",
2783 "FORMULA",
2784 "INFO",
2785 "ISBLANK",
2786 "ISERR",
2787 "ISERROR",
2788 "ISEVEN",
2789 "ISFORMULA",
2790 "ISLOGICAL",
2791 "ISNA",
2792 "ISNONTEXT",
2793 "ISNUMBER",
2794 "ISODD",
2795 "ISREF",
2796 "ISTEXT",
2797 "N",
2798 "NA",
2799 "TYPE",
2800 nullptr
2803 const char* aLogical[] = {
2804 "AND",
2805 "FALSE",
2806 "IF",
2807 "IFERROR",
2808 "IFNA",
2809 "IFS",
2810 "NOT",
2811 "OR",
2812 "SWITCH",
2813 "TRUE",
2814 "XOR",
2815 nullptr
2818 const char* aMathematical[] = {
2819 "ABS",
2820 "ACOS",
2821 "ACOSH",
2822 "ACOT",
2823 "ACOTH",
2824 "AGGREGATE",
2825 "ASIN",
2826 "ASINH",
2827 "ATAN",
2828 "ATAN2",
2829 "ATANH",
2830 "BITAND",
2831 "BITLSHIFT",
2832 "BITOR",
2833 "BITRSHIFT",
2834 "BITXOR",
2835 "CEILING",
2836 "CEILING.MATH",
2837 "CEILING.PRECISE",
2838 "CEILING.XCL",
2839 "COLOR",
2840 "COMBIN",
2841 "COMBINA",
2842 "CONVERT_OOO",
2843 "COS",
2844 "COSH",
2845 "COT",
2846 "COTH",
2847 "CSC",
2848 "CSCH",
2849 "DEGREES",
2850 "EUROCONVERT",
2851 "EVEN",
2852 "EXP",
2853 "FACT",
2854 "FLOOR",
2855 "FLOOR.MATH",
2856 "FLOOR.PRECISE",
2857 "FLOOR.XCL",
2858 "GCD",
2859 "INT",
2860 "ISO.CEILING",
2861 "LCM",
2862 "LN",
2863 "LOG",
2864 "LOG10",
2865 "MOD",
2866 "ODD",
2867 "PI",
2868 "POWER",
2869 "PRODUCT",
2870 "RADIANS",
2871 "RAND",
2872 "RAND.NV",
2873 "RANDARRAY",
2874 "RANDBETWEEN.NV",
2875 "RAWSUBTRACT",
2876 "ROUND",
2877 "ROUNDDOWN",
2878 "ROUNDSIG",
2879 "ROUNDUP",
2880 "SEC",
2881 "SECH",
2882 "SIGN",
2883 "SIN",
2884 "SINH",
2885 "SQRT",
2886 "SUBTOTAL",
2887 "SUM",
2888 "SUMIF",
2889 "SUMIFS",
2890 "SUMSQ",
2891 "TAN",
2892 "TANH",
2893 "TRUNC",
2894 nullptr
2897 const char* aArray[] = {
2898 "FOURIER",
2899 "FREQUENCY",
2900 "GROWTH",
2901 "LINEST",
2902 "LOGEST",
2903 "MDETERM",
2904 "MINVERSE",
2905 "MMULT",
2906 "MUNIT",
2907 "SEQUENCE",
2908 "SUMPRODUCT",
2909 "SUMX2MY2",
2910 "SUMX2PY2",
2911 "SUMXMY2",
2912 "TRANSPOSE",
2913 "TREND",
2914 nullptr
2917 const char* aStatistical[] = {
2918 "AVEDEV",
2919 "AVERAGE",
2920 "AVERAGEA",
2921 "AVERAGEIF",
2922 "AVERAGEIFS",
2923 "B",
2924 "BETA.DIST",
2925 "BETA.INV",
2926 "BETADIST",
2927 "BETAINV",
2928 "BINOM.DIST",
2929 "BINOM.INV",
2930 "BINOMDIST",
2931 "CHIDIST",
2932 "CHIINV",
2933 "CHISQ.DIST",
2934 "CHISQ.DIST.RT",
2935 "CHISQ.INV",
2936 "CHISQ.INV.RT",
2937 "CHISQ.TEST",
2938 "CHISQDIST",
2939 "CHISQINV",
2940 "CHITEST",
2941 "CONFIDENCE",
2942 "CONFIDENCE.NORM",
2943 "CONFIDENCE.T",
2944 "CORREL",
2945 "COUNT",
2946 "COUNTA",
2947 "COUNTBLANK",
2948 "COUNTIF",
2949 "COUNTIFS",
2950 "COVAR",
2951 "COVARIANCE.P",
2952 "COVARIANCE.S",
2953 "CRITBINOM",
2954 "DEVSQ",
2955 "ERF.PRECISE",
2956 "ERFC.PRECISE",
2957 "EXPON.DIST",
2958 "EXPONDIST",
2959 "F.DIST",
2960 "F.DIST.RT",
2961 "F.INV",
2962 "F.INV.RT",
2963 "F.TEST",
2964 "FDIST",
2965 "FINV",
2966 "FISHER",
2967 "FISHERINV",
2968 "FORECAST",
2969 "FORECAST.ETS.ADD",
2970 "FORECAST.ETS.MULT",
2971 "FORECAST.ETS.PI.ADD",
2972 "FORECAST.ETS.PI.MULT",
2973 "FORECAST.ETS.SEASONALITY",
2974 "FORECAST.ETS.STAT.ADD",
2975 "FORECAST.ETS.STAT.MULT",
2976 "FORECAST.LINEAR",
2977 "FTEST",
2978 "GAMMA",
2979 "GAMMA.DIST",
2980 "GAMMA.INV",
2981 "GAMMADIST",
2982 "GAMMAINV",
2983 "GAMMALN",
2984 "GAMMALN.PRECISE",
2985 "GAUSS",
2986 "GEOMEAN",
2987 "HARMEAN",
2988 "HYPGEOM.DIST",
2989 "HYPGEOMDIST",
2990 "INTERCEPT",
2991 "KURT",
2992 "LARGE",
2993 "LOGINV",
2994 "LOGNORM.DIST",
2995 "LOGNORM.INV",
2996 "LOGNORMDIST",
2997 "MAX",
2998 "MAXA",
2999 "MAXIFS",
3000 "MEDIAN",
3001 "MIN",
3002 "MINA",
3003 "MINIFS",
3004 "MODE",
3005 "MODE.MULT",
3006 "MODE.SNGL",
3007 "NEGBINOM.DIST",
3008 "NEGBINOMDIST",
3009 "NORM.DIST",
3010 "NORM.INV",
3011 "NORM.S.DIST",
3012 "NORM.S.INV",
3013 "NORMDIST",
3014 "NORMINV",
3015 "NORMSDIST",
3016 "NORMSINV",
3017 "PEARSON",
3018 "PERCENTILE",
3019 "PERCENTILE.EXC",
3020 "PERCENTILE.INC",
3021 "PERCENTRANK",
3022 "PERCENTRANK.EXC",
3023 "PERCENTRANK.INC",
3024 "PERMUT",
3025 "PERMUTATIONA",
3026 "PHI",
3027 "POISSON",
3028 "POISSON.DIST",
3029 "PROB",
3030 "QUARTILE",
3031 "QUARTILE.EXC",
3032 "QUARTILE.INC",
3033 "RANK",
3034 "RANK.AVG",
3035 "RANK.EQ",
3036 "RSQ",
3037 "SKEW",
3038 "SKEWP",
3039 "SLOPE",
3040 "SMALL",
3041 "STANDARDIZE",
3042 "STDEV",
3043 "STDEV.P",
3044 "STDEV.S",
3045 "STDEVA",
3046 "STDEVP",
3047 "STDEVPA",
3048 "STEYX",
3049 "T.DIST",
3050 "T.DIST.2T",
3051 "T.DIST.RT",
3052 "T.INV",
3053 "T.INV.2T",
3054 "T.TEST",
3055 "TDIST",
3056 "TINV",
3057 "TRIMMEAN",
3058 "TTEST",
3059 "VAR",
3060 "VAR.P",
3061 "VAR.S",
3062 "VARA",
3063 "VARP",
3064 "VARPA",
3065 "WEIBULL",
3066 "WEIBULL.DIST",
3067 "Z.TEST",
3068 "ZTEST",
3069 nullptr
3072 const char* aSpreadsheet[] = {
3073 "ADDRESS",
3074 "AREAS",
3075 "CHOOSE",
3076 "COLUMN",
3077 "COLUMNS",
3078 "DDE",
3079 "ERROR.TYPE",
3080 "ERRORTYPE",
3081 "FILTER",
3082 "GETPIVOTDATA",
3083 "HLOOKUP",
3084 "HYPERLINK",
3085 "INDEX",
3086 "INDIRECT",
3087 "LET",
3088 "LOOKUP",
3089 "MATCH",
3090 "OFFSET",
3091 "ROW",
3092 "ROWS",
3093 "SHEET",
3094 "SHEETS",
3095 "SORT",
3096 "SORTBY",
3097 "STYLE",
3098 "UNIQUE",
3099 "VLOOKUP",
3100 "XLOOKUP",
3101 "XMATCH",
3102 nullptr
3105 const char* aText[] = {
3106 "ARABIC",
3107 "ASC",
3108 "BAHTTEXT",
3109 "BASE",
3110 "CHAR",
3111 "CLEAN",
3112 "CODE",
3113 "CONCAT",
3114 "CONCATENATE",
3115 "DECIMAL",
3116 "DOLLAR",
3117 "ENCODEURL",
3118 "EXACT",
3119 "FILTERXML",
3120 "FIND",
3121 "FINDB",
3122 "FIXED",
3123 "JIS",
3124 "LEFT",
3125 "LEFTB",
3126 "LEN",
3127 "LENB",
3128 "LOWER",
3129 "MID",
3130 "MIDB",
3131 "NUMBERVALUE",
3132 "PROPER",
3133 "REGEX",
3134 "REPLACE",
3135 "REPLACEB",
3136 "REPT",
3137 "RIGHT",
3138 "RIGHTB",
3139 "ROMAN",
3140 "ROT13",
3141 "SEARCH",
3142 "SEARCHB",
3143 "SUBSTITUTE",
3144 "T",
3145 "TEXT",
3146 "TEXTJOIN",
3147 "TRIM",
3148 "UNICHAR",
3149 "UNICODE",
3150 "UPPER",
3151 "VALUE",
3152 "WEBSERVICE",
3153 nullptr
3156 struct {
3157 const char* Category; const char** Functions;
3158 } aTests[] = {
3159 { "Database", aDataBase },
3160 { "Date&Time", aDateTime },
3161 { "Financial", aFinancial },
3162 { "Information", aInformation },
3163 { "Logical", aLogical },
3164 { "Mathematical", aMathematical },
3165 { "Array", aArray },
3166 { "Statistical", aStatistical },
3167 { "Spreadsheet", aSpreadsheet },
3168 { "Text", aText },
3169 { "Add-in", nullptr },
3170 { nullptr, nullptr }
3173 ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
3174 sal_uInt32 n = pFuncMgr->getCount();
3175 for (sal_uInt32 i = 0; i < n; ++i)
3177 const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
3178 CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
3179 sal_uInt32 nFuncCount = pCat->getCount();
3180 for (sal_uInt32 j = 0; j < nFuncCount; ++j)
3182 const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
3183 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
3188 CPPUNIT_TEST_FIXTURE(Test, testGraphicsInGroup)
3190 m_pDoc->InsertTab(0, u"TestTab"_ustr);
3191 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
3192 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
3193 SCROW nRow1, nRow2;
3194 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3195 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
3196 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
3197 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
3199 m_pDoc->InitDrawLayer();
3200 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
3201 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
3202 SdrPage* pPage = pDrawLayer->GetPage(0);
3203 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
3206 //Add a square
3207 tools::Rectangle aOrigRect(2,2,100,100);
3208 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aOrigRect);
3209 pPage->InsertObject(pObj.get());
3210 const tools::Rectangle &rNewRect = pObj->GetLogicRect();
3211 CPPUNIT_ASSERT_EQUAL_MESSAGE("must have equal position and size",
3212 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3214 ScDrawLayer::SetPageAnchored(*pObj);
3216 //Use a range of rows guaranteed to include all of the square
3217 m_pDoc->ShowRows(0, 100, 0, false);
3218 m_pDoc->SetDrawPageSize(0);
3219 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3220 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3221 m_pDoc->ShowRows(0, 100, 0, true);
3222 m_pDoc->SetDrawPageSize(0);
3223 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3224 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3226 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
3227 CPPUNIT_ASSERT_EQUAL_MESSAGE("That shouldn't change size or positioning",
3228 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3230 m_pDoc->ShowRows(0, 100, 0, false);
3231 m_pDoc->SetDrawPageSize(0);
3233 CPPUNIT_ASSERT_EQUAL_MESSAGE("Hiding should not change the logic rectangle",
3234 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3235 CPPUNIT_ASSERT_MESSAGE("Hiding should make invisible", !pObj->IsVisible());
3237 m_pDoc->ShowRows(0, 100, 0, true);
3238 m_pDoc->SetDrawPageSize(0);
3239 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when cell anchored",
3240 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3241 CPPUNIT_ASSERT_MESSAGE("Show should make visible", pObj->IsVisible());
3245 // Add a circle.
3246 tools::Rectangle aOrigRect(10,10,210,210); // 200 x 200
3247 rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
3248 pPage->InsertObject(pObj.get());
3249 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3250 CPPUNIT_ASSERT_EQUAL_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
3251 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3253 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3254 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell anchored. Not good.",
3255 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3257 // Insert 2 rows at the top. This should push the circle object down.
3258 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3259 m_pDoc->SetDrawPageSize(0);
3261 // Make sure the size of the circle is still identical.
3262 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of the circle has changed, but shouldn't!",
3263 aOrigRect.GetSize(), rNewRect.GetSize());
3265 // Delete 2 rows at the top. This should bring the circle object to its original position.
3266 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3267 m_pDoc->SetDrawPageSize(0);
3268 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to move back to its original position.",
3269 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3273 // Add a line.
3274 basegfx::B2DPolygon aTempPoly;
3275 Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
3276 tools::Rectangle aOrigRect(10,200,110,300); // 100 x 100
3277 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
3278 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
3279 rtl::Reference<SdrPathObj> pObj = new SdrPathObj(*pDrawLayer, SdrObjKind::Line, basegfx::B2DPolyPolygon(aTempPoly));
3280 pObj->NbcSetLogicRect(aOrigRect);
3281 pPage->InsertObject(pObj.get());
3282 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3283 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size differ.",
3284 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3286 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3287 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell-anchored. Not good.",
3288 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3290 // Insert 2 rows at the top and delete them immediately.
3291 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3292 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3293 m_pDoc->SetDrawPageSize(0);
3294 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of a line object changed after row insertion and removal.",
3295 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3297 sal_Int32 n = pObj->GetPointCount();
3298 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 2 points in a line object.", static_cast<sal_Int32>(2), n);
3299 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3300 aStartPos, pObj->GetPoint(0));
3301 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3302 aEndPos, pObj->GetPoint(1));
3305 m_pDoc->DeleteTab(0);
3308 CPPUNIT_TEST_FIXTURE(Test, testGraphicsOnSheetMove)
3310 m_pDoc->InsertTab(0, u"Tab1"_ustr);
3311 m_pDoc->InsertTab(1, u"Tab2"_ustr);
3312 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 2 sheets to begin with", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
3314 m_pDoc->InitDrawLayer();
3315 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
3316 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
3317 SdrPage* pPage = pDrawLayer->GetPage(0);
3318 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
3320 // Insert an object.
3321 tools::Rectangle aObjRect(2,2,100,100);
3322 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
3323 pPage->InsertObject(pObj.get());
3324 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3326 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", static_cast<size_t>(1), pPage->GetObjCount());
3328 const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
3329 CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
3330 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3331 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3333 pPage = pDrawLayer->GetPage(1);
3334 CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
3335 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", static_cast<size_t>(0), pPage->GetObjCount());
3337 // Insert a new sheet at left-end, and make sure the object has moved to
3338 // the 2nd page.
3339 m_pDoc->InsertTab(0, u"NewTab"_ustr);
3340 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", static_cast<SCTAB>(3), m_pDoc->GetTableCount());
3341 pPage = pDrawLayer->GetPage(0);
3342 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3343 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3344 pPage = pDrawLayer->GetPage(1);
3345 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3346 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3347 pPage = pDrawLayer->GetPage(2);
3348 CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage);
3349 CPPUNIT_ASSERT_EQUAL_MESSAGE("3rd sheet should have no object.", size_t(0), pPage->GetObjCount());
3351 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3352 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3354 // Now, delete the sheet that just got inserted. The object should be back
3355 // on the 1st sheet.
3356 m_pDoc->DeleteTab(0);
3357 pPage = pDrawLayer->GetPage(0);
3358 CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage);
3359 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have one object.", size_t(1), pPage->GetObjCount());
3360 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size and position of the object shouldn't change.",
3361 aObjRect, pObj->GetLogicRect());
3363 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3364 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3366 // Move the 1st sheet to the last position.
3367 m_pDoc->MoveTab(0, 1);
3368 pPage = pDrawLayer->GetPage(0);
3369 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3370 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3371 pPage = pDrawLayer->GetPage(1);
3372 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3373 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3374 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3375 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3377 // Copy the 2nd sheet, which has one drawing object to the last position.
3378 m_pDoc->CopyTab(1, 2);
3379 pPage = pDrawLayer->GetPage(2);
3380 CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage);
3381 CPPUNIT_ASSERT_EQUAL_MESSAGE("Copied sheet should have one object.", size_t(1), pPage->GetObjCount());
3382 pObj = pPage->GetObj(0);
3383 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
3384 pData = ScDrawLayer::GetObjData(pObj.get());
3385 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
3386 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maStart.Tab());
3387 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maEnd.Tab());
3389 m_pDoc->DeleteTab(2);
3390 m_pDoc->DeleteTab(1);
3391 m_pDoc->DeleteTab(0);
3394 CPPUNIT_TEST_FIXTURE(Test, testToggleRefFlag)
3397 * Test toggling relative/absolute flag of cell and cell range references.
3398 * This corresponds with hitting Shift-F4 while the cursor is on a formula
3399 * cell.
3401 // In this test, there is no need to insert formula string into a cell in
3402 // the document, as ScRefFinder does not depend on the content of the
3403 // document except for the sheet names.
3405 m_pDoc->InsertTab(0, u"Test"_ustr);
3408 // Calc A1: basic 2D reference
3410 OUString aFormula(u"=B100"_ustr);
3411 ScAddress aPos(1, 5, 0);
3412 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3414 // Original
3415 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3417 // column relative / row relative -> column absolute / row absolute
3418 aFinder.ToggleRel(0, aFormula.getLength());
3419 aFormula = aFinder.GetText();
3420 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=$B$100"_ustr, aFormula );
3422 // column absolute / row absolute -> column relative / row absolute
3423 aFinder.ToggleRel(0, aFormula.getLength());
3424 aFormula = aFinder.GetText();
3425 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=B$100"_ustr, aFormula );
3427 // column relative / row absolute -> column absolute / row relative
3428 aFinder.ToggleRel(0, aFormula.getLength());
3429 aFormula = aFinder.GetText();
3430 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=$B100"_ustr, aFormula );
3432 // column absolute / row relative -> column relative / row relative
3433 aFinder.ToggleRel(0, aFormula.getLength());
3434 aFormula = aFinder.GetText();
3435 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=B100"_ustr, aFormula );
3439 // Excel R1C1: basic 2D reference
3441 OUString aFormula(u"=R2C1"_ustr);
3442 ScAddress aPos(3, 5, 0);
3443 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3445 // Original
3446 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3448 // column absolute / row absolute -> column relative / row absolute
3449 aFinder.ToggleRel(0, aFormula.getLength());
3450 aFormula = aFinder.GetText();
3451 CPPUNIT_ASSERT_EQUAL(u"=R2C[-3]"_ustr, aFormula);
3453 // column relative / row absolute - > column absolute / row relative
3454 aFinder.ToggleRel(0, aFormula.getLength());
3455 aFormula = aFinder.GetText();
3456 CPPUNIT_ASSERT_EQUAL(u"=R[-4]C1"_ustr, aFormula);
3458 // column absolute / row relative -> column relative / row relative
3459 aFinder.ToggleRel(0, aFormula.getLength());
3460 aFormula = aFinder.GetText();
3461 CPPUNIT_ASSERT_EQUAL(u"=R[-4]C[-3]"_ustr, aFormula);
3463 // column relative / row relative -> column absolute / row absolute
3464 aFinder.ToggleRel(0, aFormula.getLength());
3465 aFormula = aFinder.GetText();
3466 CPPUNIT_ASSERT_EQUAL(u"=R2C1"_ustr, aFormula);
3470 // Excel R1C1: Selection at the end of the formula string and does not
3471 // overlap the formula string at all (inspired by fdo#39135).
3472 OUString aFormula(u"=R1C1"_ustr);
3473 ScAddress aPos(1, 1, 0);
3474 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3476 // Original
3477 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3479 // Make the column relative.
3480 sal_Int32 n = aFormula.getLength();
3481 aFinder.ToggleRel(n, n);
3482 aFormula = aFinder.GetText();
3483 CPPUNIT_ASSERT_EQUAL(u"=R1C[-1]"_ustr, aFormula);
3485 // Make the row relative.
3486 n = aFormula.getLength();
3487 aFinder.ToggleRel(n, n);
3488 aFormula = aFinder.GetText();
3489 CPPUNIT_ASSERT_EQUAL(u"=R[-1]C1"_ustr, aFormula);
3491 // Make both relative.
3492 n = aFormula.getLength();
3493 aFinder.ToggleRel(n, n);
3494 aFormula = aFinder.GetText();
3495 CPPUNIT_ASSERT_EQUAL(u"=R[-1]C[-1]"_ustr, aFormula);
3497 // Back to the original.
3498 n = aFormula.getLength();
3499 aFinder.ToggleRel(n, n);
3500 aFormula = aFinder.GetText();
3501 CPPUNIT_ASSERT_EQUAL(u"=R1C1"_ustr, aFormula);
3505 // Calc A1:
3506 OUString aFormula(u"=A1+4"_ustr);
3507 ScAddress aPos(1, 1, 0);
3508 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3510 // Original
3511 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3513 // Set the cursor over the 'A1' part and toggle.
3514 aFinder.ToggleRel(2, 2);
3515 aFormula = aFinder.GetText();
3516 CPPUNIT_ASSERT_EQUAL(u"=$A$1+4"_ustr, aFormula);
3518 aFinder.ToggleRel(2, 2);
3519 aFormula = aFinder.GetText();
3520 CPPUNIT_ASSERT_EQUAL(u"=A$1+4"_ustr, aFormula);
3522 aFinder.ToggleRel(2, 2);
3523 aFormula = aFinder.GetText();
3524 CPPUNIT_ASSERT_EQUAL(u"=$A1+4"_ustr, aFormula);
3526 aFinder.ToggleRel(2, 2);
3527 aFormula = aFinder.GetText();
3528 CPPUNIT_ASSERT_EQUAL(u"=A1+4"_ustr, aFormula);
3531 // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
3532 // partial selection within formula string.
3534 m_pDoc->DeleteTab(0);
3537 CPPUNIT_TEST_FIXTURE(Test, testAutofilter)
3539 m_pDoc->InsertTab( 0, u"Test"_ustr );
3541 // cell contents (0 = empty cell)
3542 const char* aData[][3] = {
3543 { "C1", "C2", "C3" },
3544 { "0", "1", "A" },
3545 { "1", "2", nullptr },
3546 { "1", "2", "B" },
3547 { "0", "2", "B" }
3550 SCCOL nCols = std::size(aData[0]);
3551 SCROW nRows = std::size(aData);
3553 // Populate cells.
3554 for (SCROW i = 0; i < nRows; ++i)
3555 for (SCCOL j = 0; j < nCols; ++j)
3556 if (aData[i][j])
3557 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
3559 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols-1, nRows-1);
3560 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3562 pDBData->SetAutoFilter(true);
3563 ScRange aRange;
3564 pDBData->GetArea(aRange);
3565 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3566 aRange.aEnd.Col(), aRange.aStart.Row(),
3567 aRange.aStart.Tab(), ScMF::Auto);
3569 //create the query param
3570 ScQueryParam aParam;
3571 pDBData->GetQueryParam(aParam);
3572 ScQueryEntry& rEntry = aParam.GetEntry(0);
3573 rEntry.bDoQuery = true;
3574 rEntry.nField = 0;
3575 rEntry.eOp = SC_EQUAL;
3576 rEntry.GetQueryItem().mfVal = 0;
3577 // add queryParam to database range.
3578 pDBData->SetQueryParam(aParam);
3580 // perform the query.
3581 m_pDoc->Query(0, aParam, true);
3583 //control output
3584 SCROW nRow1, nRow2;
3585 bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3586 CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden);
3587 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(2), nRow1);
3588 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(3), nRow2);
3590 // Remove filtering.
3591 rEntry.Clear();
3592 m_pDoc->Query(0, aParam, true);
3593 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3594 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden);
3595 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3596 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3598 // Filter for non-empty cells by column C.
3599 rEntry.bDoQuery = true;
3600 rEntry.nField = 2;
3601 rEntry.SetQueryByNonEmpty();
3602 m_pDoc->Query(0, aParam, true);
3604 // only row 3 should be hidden. The rest should be visible.
3605 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3606 CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden);
3607 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(0), nRow1);
3608 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(1), nRow2);
3609 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3610 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden);
3611 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow1);
3612 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow2);
3613 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3614 CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden);
3615 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", SCROW(3), nRow1);
3616 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", m_pDoc->MaxRow(), nRow2);
3618 // Now, filter for empty cells by column C.
3619 rEntry.SetQueryByEmpty();
3620 m_pDoc->Query(0, aParam, true);
3622 // Now, only row 1 and 3, and 6 and down should be visible.
3623 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3624 CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden);
3625 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow1);
3626 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow2);
3627 bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
3628 CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden);
3629 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow1);
3630 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow2);
3631 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3632 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden);
3633 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow1);
3634 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow2);
3635 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3636 CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden);
3637 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(3), nRow1);
3638 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(4), nRow2);
3639 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3640 CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden);
3641 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", SCROW(5), nRow1);
3642 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", m_pDoc->MaxRow(), nRow2);
3644 m_pDoc->DeleteTab(0);
3647 CPPUNIT_TEST_FIXTURE(Test, testAutoFilterTimeValue)
3649 m_pDoc->InsertTab(0, u"Test"_ustr);
3651 m_pDoc->SetString(ScAddress(0,0,0), u"Hours"_ustr);
3652 m_pDoc->SetValue(ScAddress(0,1,0), 72.3604166666671);
3653 m_pDoc->SetValue(ScAddress(0,2,0), 265);
3655 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 0, 2);
3656 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3658 // Apply the "hour:minute:second" format to A2:A3.
3659 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3660 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_TIME_HH_MMSS, LANGUAGE_ENGLISH_US);
3661 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3662 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3663 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3665 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 2, 0, aNewAttrs); // apply it to A2:A3.
3667 printRange(m_pDoc, ScRange(0,0,0,0,2,0), "Data"); // A1:A3
3669 // Make sure the hour:minute:second format is really applied.
3670 CPPUNIT_ASSERT_EQUAL(u"1736:39:00"_ustr, m_pDoc->GetString(ScAddress(0,1,0))); // A2
3671 CPPUNIT_ASSERT_EQUAL(u"6360:00:00"_ustr, m_pDoc->GetString(ScAddress(0,2,0))); // A3
3673 // Filter by the A2 value. Only A1 and A2 should be visible.
3674 ScQueryParam aParam;
3675 pDBData->GetQueryParam(aParam);
3676 ScQueryEntry& rEntry = aParam.GetEntry(0);
3677 rEntry.bDoQuery = true;
3678 rEntry.nField = 0;
3679 rEntry.eOp = SC_EQUAL;
3680 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern(u"1736:39:00"_ustr);
3681 rEntry.GetQueryItem().meType = ScQueryEntry::ByString;
3683 pDBData->SetQueryParam(aParam);
3685 // perform the query.
3686 m_pDoc->Query(0, aParam, true);
3688 // A1:A2 should be visible while A3 should be filtered out.
3689 CPPUNIT_ASSERT_MESSAGE("A1 should be visible.", !m_pDoc->RowFiltered(0,0));
3690 CPPUNIT_ASSERT_MESSAGE("A2 should be visible.", !m_pDoc->RowFiltered(1,0));
3691 CPPUNIT_ASSERT_MESSAGE("A3 should be filtered out.", m_pDoc->RowFiltered(2,0));
3693 m_pDoc->DeleteTab(0);
3696 CPPUNIT_TEST_FIXTURE(Test, testAutofilterOptimizations)
3698 m_pDoc->InsertTab( 0, u"Test"_ustr );
3700 constexpr SCCOL nCols = 4;
3701 constexpr SCROW nRows = 200;
3702 m_pDoc->SetString(0, 0, 0, u"Column1"_ustr);
3703 m_pDoc->SetString(1, 0, 0, u"Column2"_ustr);
3704 m_pDoc->SetString(2, 0, 0, u"Column3"_ustr);
3705 m_pDoc->SetString(3, 0, 0, u"Column4"_ustr);
3707 // Fill 1st column with 0-199, 2nd with 1-200, 3rd with "1000"-"1199", 4th with "1001-1200"
3708 // (the pairs are off by one to each other to check filtering out a value filters out
3709 // only the relevant column).
3710 for(SCROW i = 0; i < nRows; ++i)
3712 m_pDoc->SetValue(0, i + 1, 0, i);
3713 m_pDoc->SetValue(1, i + 1, 0, i+1);
3714 m_pDoc->SetString(2, i + 1, 0, "val" + OUString::number(i+1000));
3715 m_pDoc->SetString(3, i + 1, 0, "val" + OUString::number(i+1000+1));
3718 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols, nRows);
3719 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3721 pDBData->SetAutoFilter(true);
3722 ScRange aRange;
3723 pDBData->GetArea(aRange);
3724 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3725 aRange.aEnd.Col(), aRange.aStart.Row(),
3726 aRange.aStart.Tab(), ScMF::Auto);
3728 //create the query param
3729 ScQueryParam aParam;
3730 pDBData->GetQueryParam(aParam);
3731 ScQueryEntry& rEntry0 = aParam.GetEntry(0);
3732 rEntry0.bDoQuery = true;
3733 rEntry0.nField = 0;
3734 rEntry0.eOp = SC_EQUAL;
3735 rEntry0.GetQueryItems().resize(nRows);
3736 ScQueryEntry& rEntry1 = aParam.GetEntry(1);
3737 rEntry1.bDoQuery = true;
3738 rEntry1.nField = 1;
3739 rEntry1.eOp = SC_EQUAL;
3740 rEntry1.GetQueryItems().resize(nRows);
3741 ScQueryEntry& rEntry2 = aParam.GetEntry(2);
3742 rEntry2.bDoQuery = true;
3743 rEntry2.nField = 2;
3744 rEntry2.eOp = SC_EQUAL;
3745 rEntry2.GetQueryItems().resize(nRows);
3746 ScQueryEntry& rEntry3 = aParam.GetEntry(3);
3747 rEntry3.bDoQuery = true;
3748 rEntry3.nField = 3;
3749 rEntry3.eOp = SC_EQUAL;
3750 rEntry3.GetQueryItems().resize(nRows);
3751 // Set up autofilter to select all values except one in each column.
3752 // This should only filter out 2nd, 3rd, 6th and 7th rows.
3753 for( int i = 0; i < nRows; ++i )
3755 if(i!= 1)
3756 rEntry0.GetQueryItems()[i].mfVal = i;
3757 if(i!= 2)
3758 rEntry1.GetQueryItems()[i].mfVal = i + 1;
3759 if(i!= 5)
3761 rEntry2.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000));
3762 rEntry2.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3764 if(i!= 6)
3766 rEntry3.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000+1));
3767 rEntry3.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3770 // add queryParam to database range.
3771 pDBData->SetQueryParam(aParam);
3773 // perform the query.
3774 m_pDoc->Query(0, aParam, true);
3776 // check that only rows with filtered out values are hidden, and not rows that share
3777 // a value in a different column
3778 SCROW nRow1, nRow2;
3779 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0, &nRow1, &nRow2));
3780 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden", m_pDoc->RowHidden(2, 0, &nRow1, &nRow2));
3781 CPPUNIT_ASSERT_MESSAGE("row 4 should be hidden", m_pDoc->RowHidden(3, 0, &nRow1, &nRow2));
3782 CPPUNIT_ASSERT_MESSAGE("row 5 should be visible", !m_pDoc->RowHidden(4, 0, &nRow1, &nRow2));
3783 CPPUNIT_ASSERT_MESSAGE("row 6 should be visible", !m_pDoc->RowHidden(5, 0, &nRow1, &nRow2));
3784 CPPUNIT_ASSERT_MESSAGE("row 7 should be hidden", m_pDoc->RowHidden(6, 0, &nRow1, &nRow2));
3785 CPPUNIT_ASSERT_MESSAGE("row 8 should be hidden", m_pDoc->RowHidden(7, 0, &nRow1, &nRow2));
3786 CPPUNIT_ASSERT_MESSAGE("row 9 should be visible", !m_pDoc->RowHidden(8, 0, &nRow1, &nRow2));
3788 // Remove filtering.
3789 rEntry0.Clear();
3790 rEntry1.Clear();
3791 rEntry2.Clear();
3792 m_pDoc->Query(0, aParam, true);
3793 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !m_pDoc->RowHidden(0, 0, &nRow1, &nRow2));
3794 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3795 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3797 m_pDoc->DeleteTab(0);
3800 CPPUNIT_TEST_FIXTURE(Test, testTdf76441)
3802 m_pDoc->InsertTab(0, u"Test"_ustr);
3804 // The result will be different depending on whether the format is set before
3805 // or after inserting the string
3807 OUString aCode = u"MM:SS"_ustr;
3808 sal_Int32 nCheckPos;
3809 SvNumFormatType nType;
3810 sal_uInt32 nFormat;
3811 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3812 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3814 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3815 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3816 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3818 // First insert the string, then the format
3819 m_pDoc->SetString(ScAddress(0,0,0), u"01:20"_ustr);
3821 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3823 CPPUNIT_ASSERT_EQUAL(u"20:00"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3827 // First set the format, then insert the string
3828 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3830 m_pDoc->SetString(ScAddress(0,1,0), u"01:20"_ustr);
3832 // Without the fix in place, this test would have failed with
3833 // - Expected: 01:20
3834 // - Actual : 20:00
3835 CPPUNIT_ASSERT_EQUAL(u"01:20"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3838 m_pDoc->DeleteTab(0);
3841 CPPUNIT_TEST_FIXTURE(Test, testTdf76836)
3843 m_pDoc->InsertTab(0, u"Test"_ustr);
3845 OUString aCode = u"\"192.168.0.\"@"_ustr;
3846 sal_Int32 nCheckPos;
3847 SvNumFormatType nType;
3848 sal_uInt32 nFormat;
3849 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3850 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3852 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3853 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3854 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3856 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3857 m_pDoc->SetValue(0,0,0, 10.0);
3859 // Without the fix in place, this test would have failed with
3860 // - Expected: 10
3861 // - Actual : 192.168.0.10
3862 CPPUNIT_ASSERT_EQUAL(u"10"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3864 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3865 m_pDoc->SetString(ScAddress(0,1,0), u"10"_ustr);
3866 CPPUNIT_ASSERT_EQUAL(u"192.168.0.10"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3868 m_pDoc->DeleteTab(0);
3871 CPPUNIT_TEST_FIXTURE(Test, testTdf151752)
3873 m_pDoc->InsertTab(0, u"Test"_ustr);
3875 m_pDoc->SetString(ScAddress(0,0,0), u"66000:00"_ustr);
3877 // Without the fix in place, this test would have failed with
3878 // - Expected: 66000:00:00
3879 // - Actual : 464:00:00
3880 CPPUNIT_ASSERT_EQUAL(u"66000:00:00"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3882 m_pDoc->DeleteTab(0);
3885 CPPUNIT_TEST_FIXTURE(Test, testTdf142186)
3887 m_pDoc->InsertTab(0, u"Test"_ustr);
3889 // The result will be different depending on whether the format is set before
3890 // or after inserting the string
3892 OUString aCode = u"0\".\"0"_ustr;
3893 sal_Int32 nCheckPos;
3894 SvNumFormatType nType;
3895 sal_uInt32 nFormat;
3896 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3897 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3899 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3900 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3901 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3903 // First insert the string, then the format
3904 m_pDoc->SetString(ScAddress(0,0,0), u"123.45"_ustr);
3906 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3908 CPPUNIT_ASSERT_EQUAL(u"12.3"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3912 // First set the format, then insert the string
3913 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3915 m_pDoc->SetString(ScAddress(0,1,0), u"123.45"_ustr);
3917 // Without the fix in place, this test would have failed with
3918 // - Expected: 12.3
3919 // - Actual : 1234.5
3920 CPPUNIT_ASSERT_EQUAL(u"12.3"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3923 m_pDoc->DeleteTab(0);
3926 CPPUNIT_TEST_FIXTURE(Test, testTdf137063)
3928 m_pDoc->InsertTab(0, u"Test"_ustr);
3930 m_pDoc->SetValue(0,0,0, 0.000000006);
3931 m_pDoc->SetValue(0,1,0, 0.0000000006);
3933 // Without the fix in place, this test would have failed with
3934 // - Expected: 0.000000006
3935 // - Actual : 6E-09
3936 CPPUNIT_ASSERT_EQUAL(u"0.000000006"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3937 CPPUNIT_ASSERT_EQUAL(u"6E-10"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3939 m_pDoc->DeleteTab(0);
3942 CPPUNIT_TEST_FIXTURE(Test, testTdf126342)
3944 m_pDoc->InsertTab(0, u"Test"_ustr);
3946 OUString aCode = u"YYYY-MM-DD"_ustr;
3947 sal_Int32 nCheckPos;
3948 SvNumFormatType nType;
3949 sal_uInt32 nFormat;
3950 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3951 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3953 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3954 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3955 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3956 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3958 m_pDoc->SetString(ScAddress(0,0,0), u"11/7/19"_ustr);
3960 CPPUNIT_ASSERT_EQUAL(u"2019-11-07"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3962 // Overwrite the existing date with the exact same input
3963 m_pDoc->SetString(ScAddress(0,0,0), u"11/7/19"_ustr);
3965 // Without the fix in place, this test would have failed with
3966 // - Expected: 2019-11-07
3967 // - Actual : 2011-07-19
3968 CPPUNIT_ASSERT_EQUAL(u"2019-11-07"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3970 m_pDoc->DeleteTab(0);
3973 CPPUNIT_TEST_FIXTURE(Test, testAdvancedFilter)
3975 m_pDoc->InsertTab(0, u"Test"_ustr);
3977 // cell contents (nullptr = empty cell)
3978 std::vector<std::vector<const char*>> aData = {
3979 { "Value", "Tag" }, // A1:B11
3980 { "1", "R" },
3981 { "2", "R" },
3982 { "3", "R" },
3983 { "4", "C" },
3984 { "5", "C" },
3985 { "6", "C" },
3986 { "7", "R" },
3987 { "8", "R" },
3988 { "9", "R" },
3989 { "10", "C" },
3990 { nullptr },
3991 { "Value", "Tag" }, // A13:B14
3992 { "> 5", "R" },
3995 // Populate cells.
3996 for (size_t nRow = 0; nRow < aData.size(); ++nRow)
3998 const std::vector<const char*>& rRowData = aData[nRow];
3999 for (size_t nCol = 0; nCol < rRowData.size(); ++nCol)
4001 const char* pCell = rRowData[nCol];
4002 if (pCell)
4003 m_pDoc->SetString(nCol, nRow, 0, OUString::createFromAscii(pCell));
4007 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 1, 10);
4008 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
4010 ScRange aDataRange(0,0,0,1,10,0);
4011 ScRange aFilterRuleRange(0,12,0,1,13,0);
4013 printRange(m_pDoc, aDataRange, "Data");
4014 printRange(m_pDoc, aFilterRuleRange, "Filter Rule");
4016 ScQueryParam aQueryParam;
4017 aQueryParam.bHasHeader = true;
4018 aQueryParam.nCol1 = aDataRange.aStart.Col();
4019 aQueryParam.nRow1 = aDataRange.aStart.Row();
4020 aQueryParam.nCol2 = aDataRange.aEnd.Col();
4021 aQueryParam.nRow2 = aDataRange.aEnd.Row();
4022 aQueryParam.nTab = aDataRange.aStart.Tab();
4024 bool bGood = m_pDoc->CreateQueryParam(aFilterRuleRange, aQueryParam);
4025 CPPUNIT_ASSERT_MESSAGE("failed to create query param.", bGood);
4027 // First entry is for the 'Value' field, and is greater than 5.
4028 ScQueryEntry aEntry = aQueryParam.GetEntry(0);
4029 CPPUNIT_ASSERT(aEntry.bDoQuery);
4030 CPPUNIT_ASSERT_EQUAL(SCCOLROW(0), aEntry.nField);
4031 CPPUNIT_ASSERT_EQUAL(SC_GREATER, aEntry.eOp);
4033 ScQueryEntry::QueryItemsType aItems = aEntry.GetQueryItems();
4034 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
4035 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByValue, aItems[0].meType);
4036 CPPUNIT_ASSERT_EQUAL(5.0, aItems[0].mfVal);
4038 // Second entry is for the 'Tag' field, and is == 'R'.
4039 aEntry = aQueryParam.GetEntry(1);
4040 CPPUNIT_ASSERT(aEntry.bDoQuery);
4041 CPPUNIT_ASSERT_EQUAL(SCCOLROW(1), aEntry.nField);
4042 CPPUNIT_ASSERT_EQUAL(SC_EQUAL, aEntry.eOp);
4044 aItems = aEntry.GetQueryItems();
4045 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
4046 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByString, aItems[0].meType);
4047 CPPUNIT_ASSERT_EQUAL(u"R"_ustr, aItems[0].maString.getString());
4049 // perform the query.
4050 m_pDoc->Query(0, aQueryParam, true);
4052 // Only rows 1,8-10 should be visible.
4053 bool bFiltered = m_pDoc->RowFiltered(0, 0);
4054 CPPUNIT_ASSERT_MESSAGE("row 1 (header row) should be visible", !bFiltered);
4056 SCROW nRow1 = -1, nRow2 = -1;
4057 bFiltered = m_pDoc->RowFiltered(1, 0, &nRow1, &nRow2);
4058 CPPUNIT_ASSERT_MESSAGE("rows 2-7 should be filtered out.", bFiltered);
4059 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(1), nRow1);
4060 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(6), nRow2);
4062 bFiltered = m_pDoc->RowFiltered(7, 0, &nRow1, &nRow2);
4063 CPPUNIT_ASSERT_MESSAGE("rows 8-10 should be visible.", !bFiltered);
4064 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(7), nRow1);
4065 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(9), nRow2);
4067 m_pDoc->DeleteTab(0);
4070 CPPUNIT_TEST_FIXTURE(Test, testDateFilterContains)
4072 m_pDoc->InsertTab(0, u"Test"_ustr);
4074 constexpr SCCOL nCols = 1;
4075 constexpr SCROW nRows = 5;
4076 m_pDoc->SetString(0, 0, 0, u"Date"_ustr);
4077 m_pDoc->SetString(0, 1, 0, u"1/2/2021"_ustr);
4078 m_pDoc->SetString(0, 2, 0, u"2/1/1999"_ustr);
4079 m_pDoc->SetString(0, 3, 0, u"2/1/1997"_ustr);
4080 m_pDoc->SetString(0, 4, 0, u"3/3/2001"_ustr);
4081 m_pDoc->SetString(0, 5, 0, u"3/3/1996"_ustr);
4083 // Set the fields as dates.
4084 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
4085 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_DATE_DIN_YYMMDD, LANGUAGE_ENGLISH_US);
4086 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
4087 SfxItemSet& rSet = aNewAttrs.GetItemSet();
4088 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
4089 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 5, 0, aNewAttrs); // apply it to A1:A6
4091 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols, nRows);
4092 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
4094 pDBData->SetAutoFilter(true);
4095 ScRange aRange;
4096 pDBData->GetArea(aRange);
4097 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
4098 aRange.aEnd.Col(), aRange.aStart.Row(),
4099 aRange.aStart.Tab(), ScMF::Auto);
4101 //create the query param
4102 ScQueryParam aParam;
4103 pDBData->GetQueryParam(aParam);
4104 ScQueryEntry& rEntry = aParam.GetEntry(0);
4105 rEntry.bDoQuery = true;
4106 rEntry.nField = 0;
4107 rEntry.eOp = SC_CONTAINS;
4108 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern(u"2"_ustr);
4109 pDBData->SetQueryParam(aParam);
4111 // perform the query.
4112 m_pDoc->Query(0, aParam, true);
4114 // Dates in rows 2-4 contain '2', row 5 shows 2001 only as 01, and row 6 doesn't contain it at all.
4115 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0));
4116 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible", !m_pDoc->RowHidden(2, 0));
4117 CPPUNIT_ASSERT_MESSAGE("row 4 should be visible", !m_pDoc->RowHidden(3, 0));
4118 CPPUNIT_ASSERT_MESSAGE("row 5 should be hidden", m_pDoc->RowHidden(4, 0));
4119 CPPUNIT_ASSERT_MESSAGE("row 6 should be hidden", m_pDoc->RowHidden(5, 0));
4121 m_pDoc->DeleteTab(0);
4124 CPPUNIT_TEST_FIXTURE(Test, testTdf98642)
4126 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4127 m_pDoc->SetString(0, 0, 0, u"test"_ustr);
4129 ScRangeData* pName1 = new ScRangeData( *m_pDoc, u"name1"_ustr, u"$Sheet1.$A$1"_ustr);
4130 ScRangeData* pName2 = new ScRangeData( *m_pDoc, u"name2"_ustr, u"$Sheet1.$A$1"_ustr);
4132 std::unique_ptr<ScRangeName> pGlobalRangeName(new ScRangeName());
4133 pGlobalRangeName->insert(pName1);
4134 pGlobalRangeName->insert(pName2);
4135 m_pDoc->SetRangeName(std::move(pGlobalRangeName));
4137 m_pDoc->SetString(1, 0, 0, u"=name1"_ustr);
4138 m_pDoc->SetString(1, 1, 0, u"=name2"_ustr);
4140 CPPUNIT_ASSERT_EQUAL(u"test"_ustr, m_pDoc->GetString(1, 0, 0));
4141 CPPUNIT_ASSERT_EQUAL(u"test"_ustr, m_pDoc->GetString(1, 1, 0));
4143 OUString aFormula = m_pDoc->GetFormula(1,0,0);
4144 CPPUNIT_ASSERT_EQUAL(u"=name1"_ustr, aFormula);
4145 aFormula = m_pDoc->GetFormula(1,1,0);
4147 // Without the fix in place, this test would have failed with
4148 // - Expected: =name2
4149 // - Actual : =name1
4150 CPPUNIT_ASSERT_EQUAL(u"=name2"_ustr, aFormula);
4152 m_pDoc->DeleteTab(0);
4155 CPPUNIT_TEST_FIXTURE(Test, testMergedCells)
4157 //test merge and unmerge
4158 //TODO: an undo/redo test for this would be a good idea
4159 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4160 m_pDoc->DoMerge(1, 1, 3, 3, 0, false);
4161 SCCOL nEndCol = 1;
4162 SCROW nEndRow = 1;
4163 m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0);
4164 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCCOL(3), nEndCol);
4165 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCROW(3), nEndRow);
4166 ScRange aRange(0,2,0,m_pDoc->MaxCol(),2,0);
4167 ScMarkData aMark(m_pDoc->GetSheetLimits());
4168 aMark.SetMarkArea(aRange);
4169 m_xDocShell->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS_BEFORE, true, true);
4170 m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0);
4171 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCCOL(3), nEndCol);
4172 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCROW(4), nEndRow);
4173 m_pDoc->DeleteTab(0);
4176 CPPUNIT_TEST_FIXTURE(Test, testRenameTable)
4178 //test set rename table
4179 //TODO: set name1 and name2 and do an undo to check if name 1 is set now
4180 //TODO: also check if new name for table is same as another table
4182 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4183 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4185 //test case 1 , rename table2 to sheet 1, it should return error
4186 OUString nameToSet = u"Sheet1"_ustr;
4187 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4188 CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
4190 //test case 2 , simple rename to check name
4191 nameToSet = "test1";
4192 m_xDocShell->GetDocFunc().RenameTable(0,nameToSet,false,true);
4193 OUString nameJustSet;
4194 m_pDoc->GetName(0,nameJustSet);
4195 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4197 //test case 3 , rename again
4198 OUString anOldName;
4199 m_pDoc->GetName(0,anOldName);
4201 nameToSet = "test2";
4202 rDocFunc.RenameTable(0,nameToSet,false,true);
4203 m_pDoc->GetName(0,nameJustSet);
4204 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4206 //test case 4 , check if undo works
4207 SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShell.get(),0,anOldName,nameToSet);
4208 pUndo->Undo();
4209 m_pDoc->GetName(0,nameJustSet);
4210 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct name is not set after undo", nameJustSet, anOldName);
4212 pUndo->Redo();
4213 m_pDoc->GetName(0,nameJustSet);
4214 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after redo", nameJustSet, nameToSet);
4215 delete pUndo;
4217 m_pDoc->DeleteTab(0);
4218 m_pDoc->DeleteTab(1);
4221 CPPUNIT_TEST_FIXTURE(Test, testSetBackgroundColor)
4223 //test set background color
4224 //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
4226 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4227 Color aColor;
4229 //test yellow
4230 aColor=COL_YELLOW;
4231 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4232 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set",
4233 aColor, m_pDoc->GetTabBgColor(0));
4235 Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
4236 aColor = COL_BLUE;
4237 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4238 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set the second time",
4239 aColor, m_pDoc->GetTabBgColor(0));
4241 //now check for undo
4242 SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShell.get(), 0, aOldTabBgColor, aColor);
4243 pUndo->Undo();
4244 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aOldTabBgColor, m_pDoc->GetTabBgColor(0));
4245 pUndo->Redo();
4246 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aColor, m_pDoc->GetTabBgColor(0));
4247 delete pUndo;
4248 m_pDoc->DeleteTab(0);
4251 CPPUNIT_TEST_FIXTURE(Test, testUpdateReference)
4253 //test that formulas are correctly updated during sheet delete
4254 //TODO: add tests for relative references, updating of named ranges, ...
4255 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4256 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4257 m_pDoc->InsertTab(2, u"Sheet3"_ustr);
4258 m_pDoc->InsertTab(3, u"Sheet4"_ustr);
4260 m_pDoc->SetValue(0,0,2, 1);
4261 m_pDoc->SetValue(1,0,2, 2);
4262 m_pDoc->SetValue(1,1,3, 4);
4263 m_pDoc->SetString(2,0,2, u"=A1+B1"_ustr);
4264 m_pDoc->SetString(2,1,2, u"=Sheet4.B2+A1"_ustr);
4266 double aValue;
4267 aValue = m_pDoc->GetValue(2,0,2);
4268 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 3, aValue);
4269 aValue = m_pDoc->GetValue(2,1,2);
4270 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 5, aValue);
4272 //test deleting both sheets: one is not directly before the sheet, the other one is
4273 m_pDoc->DeleteTab(0);
4274 aValue = m_pDoc->GetValue(2,0,1);
4275 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 3, aValue);
4276 aValue = m_pDoc->GetValue(2,1,1);
4277 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 5, aValue);
4279 m_pDoc->DeleteTab(0);
4280 aValue = m_pDoc->GetValue(2,0,0);
4281 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 3, aValue);
4282 aValue = m_pDoc->GetValue(2,1,0);
4283 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 5, aValue);
4285 //test adding two sheets
4286 m_pDoc->InsertTab(0, u"Sheet2"_ustr);
4287 aValue = m_pDoc->GetValue(2,0,1);
4288 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 3, aValue);
4289 aValue = m_pDoc->GetValue(2,1,1);
4290 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 5, aValue);
4292 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4293 aValue = m_pDoc->GetValue(2,0,2);
4294 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 3, aValue);
4295 aValue = m_pDoc->GetValue(2,1,2);
4296 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 5, aValue);
4298 //test new DeleteTabs/InsertTabs methods
4299 m_pDoc->DeleteTabs(0, 2);
4300 aValue = m_pDoc->GetValue(2, 0, 0);
4301 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 3, aValue);
4302 aValue = m_pDoc->GetValue(2, 1, 0);
4303 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 5, aValue);
4305 std::vector<OUString> aSheets;
4306 aSheets.emplace_back("Sheet1");
4307 aSheets.emplace_back("Sheet2");
4308 m_pDoc->InsertTabs(0, aSheets, true);
4309 aValue = m_pDoc->GetValue(2, 0, 2);
4311 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 3, aValue);
4312 aValue = m_pDoc->GetValue(2, 1, 2);
4313 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 5, aValue);
4315 m_pDoc->DeleteTab(3);
4316 m_pDoc->DeleteTab(2);
4317 m_pDoc->DeleteTab(1);
4318 m_pDoc->DeleteTab(0);
4320 // Test positional update and invalidation of lookup cache for insertion
4321 // and deletion within entire column reference.
4322 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4323 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4324 m_pDoc->SetString(0,1,0, u"s1"_ustr);
4325 m_pDoc->SetString(0,0,1, u"=MATCH(\"s1\";Sheet1.A:A;0)"_ustr);
4326 aValue = m_pDoc->GetValue(0,0,1);
4327 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4328 m_pDoc->InsertRow(0,0,m_pDoc->MaxCol(),0,0,1); // insert 1 row before row 1 in Sheet1
4329 aValue = m_pDoc->GetValue(0,0,1);
4330 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 3, aValue);
4331 m_pDoc->DeleteRow(0,0,m_pDoc->MaxCol(),0,0,1); // delete row 1 in Sheet1
4332 aValue = m_pDoc->GetValue(0,0,1);
4333 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4334 m_pDoc->DeleteTab(1);
4335 m_pDoc->DeleteTab(0);
4338 CPPUNIT_TEST_FIXTURE(Test, testSearchCells)
4340 m_pDoc->InsertTab(0, u"Test"_ustr);
4342 m_pDoc->SetString(ScAddress(0,0,0), u"A"_ustr);
4343 m_pDoc->SetString(ScAddress(0,1,0), u"B"_ustr);
4344 m_pDoc->SetString(ScAddress(0,2,0), u"A"_ustr);
4345 // Leave A4 blank.
4346 m_pDoc->SetString(ScAddress(0,4,0), u"A"_ustr);
4347 m_pDoc->SetString(ScAddress(0,5,0), u"B"_ustr);
4348 m_pDoc->SetString(ScAddress(0,6,0), u"C"_ustr);
4350 SvxSearchItem aItem(SID_SEARCH_ITEM);
4351 aItem.SetSearchString(u"A"_ustr);
4352 aItem.SetCommand(SvxSearchCmd::FIND_ALL);
4353 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4354 aMarkData.SelectOneTable(0);
4355 SCCOL nCol = 0;
4356 SCROW nRow = 0;
4357 SCTAB nTab = 0;
4358 ScRangeList aMatchedRanges;
4359 OUString aUndoStr;
4360 bool bMatchedRangesWereClamped = false;
4361 bool bSuccess = m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped);
4363 CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
4364 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 3 matching cells.", size_t(3), aMatchedRanges.size());
4365 ScAddress aHit(0,0,0);
4366 CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
4367 aHit.SetRow(2);
4368 CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
4369 aHit.SetRow(4);
4370 CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
4372 m_pDoc->DeleteTab(0);
4375 CPPUNIT_TEST_FIXTURE(Test, testFormulaPosition)
4377 m_pDoc->InsertTab(0, u"Test"_ustr);
4379 ScAddress aPos(0,0,0); // A1
4380 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4381 aPos.IncRow(); // A2
4382 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4383 aPos.SetRow(3); // A4;
4384 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4387 SCROW aRows[] = { 0, 1, 3 };
4388 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, std::size(aRows));
4389 CPPUNIT_ASSERT(bRes);
4392 m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
4394 SCROW aRows[] = { 0, 6, 8 };
4395 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, std::size(aRows));
4396 CPPUNIT_ASSERT(bRes);
4399 m_pDoc->DeleteTab(0);
4402 namespace {
4404 bool hasRange(const ScDocument* pDoc, const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange, const ScAddress& rPos)
4406 for (const ScTokenRef& p : rRefTokens)
4408 if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4409 continue;
4411 switch (p->GetType())
4413 case formula::svSingleRef:
4415 ScSingleRefData aData = *p->GetSingleRef();
4416 if (rRange.aStart != rRange.aEnd)
4417 break;
4419 ScAddress aThis = aData.toAbs(*pDoc, rPos);
4420 if (aThis == rRange.aStart)
4421 return true;
4423 break;
4424 case formula::svDoubleRef:
4426 ScComplexRefData aData = *p->GetDoubleRef();
4427 ScRange aThis = aData.toAbs(*pDoc, rPos);
4428 if (aThis == rRange)
4429 return true;
4431 break;
4432 default:
4436 return false;
4441 CPPUNIT_TEST_FIXTURE(Test, testJumpToPrecedentsDependents)
4444 * Test to make sure correct precedent / dependent cells are obtained when
4445 * preparing to jump to them.
4447 // Precedent is another cell that the cell references, while dependent is
4448 // another cell that references it.
4449 m_pDoc->InsertTab(0, u"Test"_ustr);
4451 m_pDoc->SetString(2, 0, 0, u"=A1+A2+B3"_ustr); // C1
4452 m_pDoc->SetString(2, 1, 0, u"=A1"_ustr); // C2
4453 m_pDoc->CalcAll();
4455 std::vector<ScTokenRef> aRefTokens;
4456 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4459 // C1's precedent should be A1:A2,B3.
4460 ScAddress aC1(2, 0, 0);
4461 ScRangeList aRange((ScRange(aC1)));
4462 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4463 CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4464 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
4465 CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4466 hasRange(m_pDoc, aRefTokens, ScRange(1, 2, 0), aC1));
4470 // C2's precedent should be A1 only.
4471 ScAddress aC2(2, 1, 0);
4472 ScRangeList aRange((ScRange(aC2)));
4473 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4474 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4475 static_cast<size_t>(1), aRefTokens.size());
4476 CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4477 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0), aC2));
4481 // A1's dependent should be C1:C2.
4482 ScAddress aA1(0, 0, 0);
4483 ScRangeList aRange((ScRange(aA1)));
4484 rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4485 CPPUNIT_ASSERT_EQUAL_MESSAGE("C1:C2 should be the only dependent of A1.",
4486 std::vector<ScTokenRef>::size_type(1), aRefTokens.size());
4487 CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4488 hasRange(m_pDoc, aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
4491 m_pDoc->DeleteTab(0);
4494 CPPUNIT_TEST_FIXTURE(Test, testTdf149665)
4496 m_pDoc->InsertTab(0, u"Test"_ustr);
4498 m_pDoc->SetString(0, 0, 0, u"''1"_ustr);
4500 // Without the fix in place, this test would have failed with
4501 // - Expected: '1
4502 // - Actual : ''1
4503 CPPUNIT_ASSERT_EQUAL( u"'1"_ustr, m_pDoc->GetString( 0, 0, 0 ) );
4505 m_pDoc->DeleteTab(0);
4508 CPPUNIT_TEST_FIXTURE(Test, testTdf64001)
4510 m_pDoc->InsertTab(0, u"test"_ustr);
4512 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4513 aMarkData.SelectTable(0, true);
4515 m_pDoc->SetString( 0, 0, 0, u"TRUE"_ustr );
4516 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 9, FILL_TO_BOTTOM, FILL_AUTO );
4518 for (SCCOL i = 0; i < 10; ++i)
4520 CPPUNIT_ASSERT_EQUAL( u"TRUE"_ustr, m_pDoc->GetString( 0, i, 0 ) );
4523 m_pDoc->SetString( 0, 10, 0, u"FALSE"_ustr );
4525 m_pDoc->SetString( 1, 0, 0, u"=COUNTIF(A1:A11;TRUE)"_ustr );
4527 // Without the fix in place, this test would have failed with
4528 // - Expected: 10
4529 // - Actual : 1
4530 CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue( 1, 0, 0 ) );
4532 m_pDoc->DeleteTab(0);
4535 CPPUNIT_TEST_FIXTURE(Test, testAutoFill)
4537 m_pDoc->InsertTab(0, u"test"_ustr);
4539 m_pDoc->SetValue(0,0,0,1);
4541 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4542 aMarkData.SelectTable(0, true);
4544 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5);
4545 for (SCROW i = 0; i< 6; ++i)
4546 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
4548 // check that hidden rows are not affected by autofill
4549 // set values for hidden rows
4550 m_pDoc->SetValue(0,1,0,10);
4551 m_pDoc->SetValue(0,2,0,10);
4553 m_pDoc->SetRowHidden(1, 2, 0, true);
4554 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 8);
4556 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
4557 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
4558 for (SCROW i = 3; i< 8; ++i)
4559 ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
4561 m_pDoc->Fill( 0, 0, 0, 8, nullptr, aMarkData, 5, FILL_TO_RIGHT );
4562 for (SCCOL i = 0; i < 5; ++i)
4564 for(SCROW j = 0; j < 8; ++j)
4566 if (j > 2)
4568 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
4570 else if (j == 0)
4572 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
4574 else // j == 1 || j == 2
4576 if(i == 0)
4577 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
4578 else
4579 ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
4584 // test auto fill user data lists
4585 m_pDoc->SetString( 0, 100, 0, u"January"_ustr );
4586 m_pDoc->Fill( 0, 100, 0, 100, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4587 OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
4588 CPPUNIT_ASSERT_EQUAL( u"February"_ustr, aTestValue );
4589 aTestValue = m_pDoc->GetString( 0, 102, 0 );
4590 CPPUNIT_ASSERT_EQUAL( u"March"_ustr, aTestValue );
4592 // test that two same user data list entries will not result in incremental fill
4593 m_pDoc->SetString( 0, 101, 0, u"January"_ustr );
4594 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4595 for ( SCROW i = 102; i <= 103; ++i )
4597 aTestValue = m_pDoc->GetString( 0, i, 0 );
4598 CPPUNIT_ASSERT_EQUAL( u"January"_ustr, aTestValue );
4601 // Clear column A for a new test.
4602 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4603 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4605 // Fill A1:A6 with 1,2,3,4,5,6.
4606 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4607 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4608 ScRange aRange(0,0,0,0,5,0);
4609 aMarkData.SetMarkArea(aRange);
4610 rFunc.FillSeries(aRange, &aMarkData, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, MAXDOUBLE, 1.0, MAXDOUBLE, true);
4611 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4612 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4613 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4614 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4615 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4616 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4618 // Undo should clear the area except for the top cell.
4619 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4620 CPPUNIT_ASSERT(pUndoMgr);
4621 pUndoMgr->Undo();
4623 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4624 for (SCROW i = 1; i <= 5; ++i)
4625 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0,i,0)));
4627 // Redo should put the serial values back in.
4628 pUndoMgr->Redo();
4629 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4630 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4631 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4632 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4633 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4634 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4636 // test that filling formulas vertically up does the right thing
4637 for(SCROW nRow = 0; nRow < 10; ++nRow)
4638 m_pDoc->SetValue(100, 100 + nRow, 0, 1);
4640 m_pDoc->SetString(100, 110, 0, u"=A111"_ustr);
4642 m_pDoc->Fill(100, 110, 100, 110, nullptr, aMarkData, 10, FILL_TO_TOP, FILL_AUTO);
4643 for(SCROW nRow = 110; nRow >= 100; --nRow)
4645 OUString aExpected = "=A" + OUString::number(nRow +1);
4646 OUString aFormula = m_pDoc->GetFormula(100, nRow, 0);
4647 CPPUNIT_ASSERT_EQUAL(aExpected, aFormula);
4650 // Clear column A for a new test.
4651 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4652 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4654 m_pDoc->SetString( 0, 100, 0, u"2012-10-31"_ustr );
4655 m_pDoc->SetString( 0, 101, 0, u"2012-10-31"_ustr );
4656 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4658 // tdf#89754, Without the fix in place, this test would have failed with
4659 // - Expected: 2012-10-31
4660 // - Actual : 2012-11-01
4661 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 102, 0 ) );
4662 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 103, 0 ) );
4663 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 104, 0 ) );
4665 // Clear column A for a new test.
4666 clearRange(m_pDoc, ScRange(0, 0, 0, 0, m_pDoc->MaxRow(), 0));
4667 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4669 m_pDoc->SetString(0, 100, 0, u"2019-10-31"_ustr);
4670 m_pDoc->SetString(0, 101, 0, u"2019-11-30"_ustr);
4671 m_pDoc->SetString(0, 102, 0, u"2019-12-31"_ustr);
4672 m_pDoc->Fill(0, 100, 0, 102, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO);
4674 // tdf#58745, Without the fix in place, this test would have failed with
4675 // - Expected: 2020-01-31
4676 // - Actual : 2019-01-11
4677 CPPUNIT_ASSERT_EQUAL(u"2020-01-31"_ustr, m_pDoc->GetString(0, 103, 0));
4678 CPPUNIT_ASSERT_EQUAL(u"2020-02-29"_ustr, m_pDoc->GetString(0, 104, 0));
4679 CPPUNIT_ASSERT_EQUAL(u"2020-03-31"_ustr, m_pDoc->GetString(0, 105, 0));
4681 // Clear column A for a new test.
4682 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4683 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4685 m_pDoc->SetString( 0, 50, 0, u"1.0"_ustr );
4686 m_pDoc->SetString( 0, 51, 0, u"1.1"_ustr );
4687 m_pDoc->SetString( 0, 52, 0, u"1.2"_ustr );
4688 m_pDoc->SetString( 0, 53, 0, u"1.3"_ustr );
4689 m_pDoc->Fill( 0, 50, 0, 53, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4691 CPPUNIT_ASSERT_EQUAL( u"1.4"_ustr, m_pDoc->GetString( 0, 54, 0 ) );
4692 CPPUNIT_ASSERT_EQUAL( u"1.5"_ustr, m_pDoc->GetString( 0, 55, 0 ) );
4693 CPPUNIT_ASSERT_EQUAL( u"1.6"_ustr, m_pDoc->GetString( 0, 56, 0 ) );
4695 m_pDoc->SetString( 0, 60, 0, u"4.0"_ustr );
4696 m_pDoc->SetString( 0, 61, 0, u"4.1"_ustr );
4697 m_pDoc->SetString( 0, 62, 0, u"4.2"_ustr );
4698 m_pDoc->SetString( 0, 63, 0, u"4.3"_ustr );
4699 m_pDoc->Fill( 0, 60, 0, 63, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4701 // tdf#37424: Without the fix in place, this test would have failed with
4702 // - Expected: 4.4
4703 // - Actual : 5
4704 CPPUNIT_ASSERT_EQUAL( u"4.4"_ustr, m_pDoc->GetString( 0, 64, 0 ) );
4705 CPPUNIT_ASSERT_EQUAL( u"4.5"_ustr, m_pDoc->GetString( 0, 65, 0 ) );
4706 CPPUNIT_ASSERT_EQUAL( u"4.6"_ustr, m_pDoc->GetString( 0, 66, 0 ) );
4708 // Clear column A for a new test.
4709 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4710 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4712 m_pDoc->SetString( 0, 70, 0, u"001-001-001"_ustr );
4713 m_pDoc->Fill( 0, 70, 0, 70, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4715 // tdf#105268: Without the fix in place, this test would have failed with
4716 // - Expected: 001-001-002
4717 // - Actual : 001-001000
4718 CPPUNIT_ASSERT_EQUAL( u"001-001-002"_ustr, m_pDoc->GetString( 0, 71, 0 ) );
4719 CPPUNIT_ASSERT_EQUAL( u"001-001-003"_ustr, m_pDoc->GetString( 0, 72, 0 ) );
4720 CPPUNIT_ASSERT_EQUAL( u"001-001-004"_ustr, m_pDoc->GetString( 0, 73, 0 ) );
4722 // Clear column A for a new test.
4723 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4724 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4726 m_pDoc->SetString( 0, 80, 0, u"1%"_ustr );
4727 m_pDoc->Fill( 0, 80, 0, 80, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4729 // tdf#89998: Without the fix in place, this test would have failed with
4730 // - Expected: 2.00%
4731 // - Actual : 101.00%
4732 CPPUNIT_ASSERT_EQUAL( u"2.00%"_ustr, m_pDoc->GetString( 0, 81, 0 ) );
4733 CPPUNIT_ASSERT_EQUAL( u"3.00%"_ustr, m_pDoc->GetString( 0, 82, 0 ) );
4734 CPPUNIT_ASSERT_EQUAL( u"4.00%"_ustr, m_pDoc->GetString( 0, 83, 0 ) );
4736 // Clear column A for a new test.
4737 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4738 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4740 m_pDoc->SetString( 0, 0, 0, u"1"_ustr );
4741 m_pDoc->SetString( 0, 1, 0, u"1.1"_ustr );
4742 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 60, FILL_TO_BOTTOM, FILL_AUTO );
4744 // tdf#129606: Without the fix in place, this test would have failed with
4745 // - Expected: 6
4746 // - Actual : 6.00000000000001
4747 CPPUNIT_ASSERT_EQUAL( u"6"_ustr, m_pDoc->GetString( 0, 50, 0 ) );
4749 // Clear column A for a new test.
4750 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4751 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4753 m_pDoc->SetString( 0, 0, 0, u"2022-10-01 00:00:00.000"_ustr );
4754 m_pDoc->SetString( 0, 1, 0, u"2022-10-01 01:00:00.000"_ustr );
4755 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4757 // tdf#151460: Without the fix in place, this test would have failed with
4758 // - Expected: 2022-10-01 20:00:00.000
4759 // - Actual : 2022-10-01 19:59:59.999
4760 CPPUNIT_ASSERT_EQUAL( u"2022-10-01 20:00:00.000"_ustr, m_pDoc->GetString( 0, 20, 0 ) );
4762 // Clear column A for a new test.
4763 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4764 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4766 m_pDoc->SetString( 0, 0, 0, u"1st"_ustr );
4768 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5, FILL_TO_BOTTOM, FILL_AUTO );
4770 CPPUNIT_ASSERT_EQUAL( u"1st"_ustr, m_pDoc->GetString( 0, 0, 0 ) );
4771 CPPUNIT_ASSERT_EQUAL( u"2nd"_ustr, m_pDoc->GetString( 0, 1, 0 ) );
4772 CPPUNIT_ASSERT_EQUAL( u"3rd"_ustr, m_pDoc->GetString( 0, 2, 0 ) );
4774 // Clear column A for a new test.
4775 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4776 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4778 m_pDoc->SetString( 0, 200, 0, u"15:00"_ustr );
4779 m_pDoc->SetString( 0, 201, 0, u"15:20"_ustr );
4780 m_pDoc->Fill( 0, 200, 0, 201, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4782 CPPUNIT_ASSERT_EQUAL( u"03:00:00 PM"_ustr, m_pDoc->GetString( 0, 200, 0 ) );
4784 // tdf#153517: Without the fix in place, this test would have failed with
4785 // - Expected: 03:20:00 PM
4786 // - Actual : 03:19:59 PM
4787 CPPUNIT_ASSERT_EQUAL( u"03:20:00 PM"_ustr, m_pDoc->GetString( 0, 201, 0 ) );
4788 CPPUNIT_ASSERT_EQUAL( u"03:40:00 PM"_ustr, m_pDoc->GetString( 0, 202, 0 ) );
4789 CPPUNIT_ASSERT_EQUAL( u"04:00:00 PM"_ustr, m_pDoc->GetString( 0, 203, 0 ) );
4791 m_pDoc->DeleteTab(0);
4794 CPPUNIT_TEST_FIXTURE(Test, testAutoFillSimple)
4796 m_pDoc->InsertTab(0, u"test"_ustr);
4798 m_pDoc->SetValue(0, 0, 0, 1);
4799 m_pDoc->SetString(0, 1, 0, u"=10"_ustr);
4801 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4802 aMarkData.SelectTable(0, true);
4804 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 6, FILL_TO_BOTTOM, FILL_AUTO);
4806 for(SCROW nRow = 0; nRow < 8; ++nRow)
4808 if (nRow % 2 == 0)
4810 double nVal = m_pDoc->GetValue(0, nRow, 0);
4811 CPPUNIT_ASSERT_EQUAL((nRow+2)/2.0, nVal);
4813 else
4815 OString aMsg = "wrong value in row: " + OString::number(nRow);
4816 double nVal = m_pDoc->GetValue(0, nRow, 0);
4817 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, nVal);
4821 m_pDoc->DeleteTab(0);
4824 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosVertical)
4826 std::vector<std::vector<const char*>> aData = {
4827 { nullptr, "1", "1" },
4828 { "1", nullptr, "1" },
4829 { "1", "1", "1" },
4830 { nullptr, "1", "1" },
4831 { "1", "1", "1" },
4832 { "1", nullptr, "1" },
4833 { "1", "1", "1" },
4836 m_pDoc->InsertTab(0, u"Test1"_ustr);
4837 clearRange( m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
4838 ScAddress aPos(0,0,0);
4839 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4840 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4842 m_pDoc->SetRowHidden(4,4,0,true);
4843 bool bHidden = m_pDoc->RowHidden(4,0);
4844 CPPUNIT_ASSERT(bHidden);
4846 SCCOL nCol = 0;
4847 SCROW nRow = 0;
4848 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4850 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4851 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4853 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4855 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
4856 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4858 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4860 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
4861 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4863 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4865 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4866 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4868 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4870 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), nRow);
4871 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4873 nCol = 1;
4874 nRow = 2;
4876 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4878 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
4879 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4881 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4883 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4884 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4886 nCol = 2;
4887 nRow = 6;
4888 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
4889 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4890 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4892 m_pDoc->DeleteTab(0);
4895 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosColRight)
4897 std::vector<std::vector<const char*>> aData = {
4898 { "", "1", "1", "", "1", "1", "1" },
4899 { "", "", "1", "1", "1", "", "1" },
4902 m_pDoc->InsertTab(0, u"test1"_ustr);
4903 clearRange( m_pDoc, ScRange(0, 0, 0, 7, aData.size(), 0));
4904 ScAddress aPos(0,0,0);
4905 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4906 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4908 m_pDoc->SetColHidden(4,4,0,true);
4909 bool bHidden = m_pDoc->ColHidden(4,0);
4910 CPPUNIT_ASSERT(bHidden);
4912 SCCOL nCol = 0;
4913 SCROW nRow = 0;
4914 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4916 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4917 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4919 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4921 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4922 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4924 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4926 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4927 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
4929 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4931 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4932 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4934 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4936 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4937 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), nCol);
4939 nCol = 2;
4940 nRow = 1;
4942 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4944 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4945 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
4947 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4949 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4950 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4952 m_pDoc->DeleteTab(0);
4955 CPPUNIT_TEST_FIXTURE(Test, testShiftCells)
4957 m_pDoc->InsertTab(0, u"foo"_ustr);
4959 // We need a drawing layer in order to create caption objects.
4960 m_pDoc->InitDrawLayer(m_xDocShell.get());
4962 OUString aTestVal(u"Some Text"_ustr);
4964 // Text into cell E5.
4965 m_pDoc->SetString(4, 3, 0, aTestVal);
4967 // put a Note in cell E5
4968 ScAddress rAddr(4, 3, 0);
4969 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4970 pNote->SetText(rAddr, u"Hello"_ustr);
4972 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4974 // Insert cell at D5. This should shift the string cell to right.
4975 m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
4976 OUString aStr = m_pDoc->GetString(5, 3, 0);
4977 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4978 CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(3, 4, 3, 4, 0));
4980 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
4981 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
4983 // Delete cell D5, to shift the text cell back into D5.
4984 m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
4985 aStr = m_pDoc->GetString(4, 3, 0);
4986 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4987 CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(4, 4, 4, 4, 0));
4989 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
4990 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4992 m_pDoc->DeleteTab(0);
4995 CPPUNIT_TEST_FIXTURE(Test, testNoteDefaultStyle)
4997 m_pDoc->InsertTab(0, u"PostIts"_ustr);
4999 // We need a drawing layer in order to create caption objects.
5000 m_pDoc->InitDrawLayer(m_xDocShell.get());
5002 auto pNote = m_pDoc->GetOrCreateNote({0, 0, 0});
5003 auto pCaption = pNote->GetCaption();
5005 CPPUNIT_ASSERT(pCaption);
5006 CPPUNIT_ASSERT_EQUAL(ScResId(STR_STYLENAME_NOTE), pCaption->GetStyleSheet()->GetName());
5008 m_pDoc->DeleteTab(0);
5011 CPPUNIT_TEST_FIXTURE(Test, testNoteBasic)
5013 m_pDoc->InsertTab(0, u"PostIts"_ustr);
5015 // We need a drawing layer in order to create caption objects.
5016 m_pDoc->InitDrawLayer(m_xDocShell.get());
5018 CPPUNIT_ASSERT(!m_pDoc->HasNotes());
5020 // Check for note's presence in all tables before inserting any notes.
5021 for (SCTAB i = 0; i <= MAXTAB; ++i)
5023 bool bHasNotes = m_pDoc->HasTabNotes(i);
5024 CPPUNIT_ASSERT(!bHasNotes);
5027 ScAddress aAddr(2, 2, 0); // cell C3
5028 ScPostIt *pNote = m_pDoc->GetOrCreateNote(aAddr);
5030 pNote->SetText(aAddr, u"Hello world"_ustr);
5031 pNote->SetAuthor(u"Jim Bob"_ustr);
5033 ScPostIt *pGetNote = m_pDoc->GetNote(aAddr);
5034 CPPUNIT_ASSERT_EQUAL_MESSAGE("note should be itself", pNote, pGetNote);
5036 // Insert one row at row 1.
5037 bool bInsertRow = m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
5038 CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
5040 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
5041 aAddr.IncRow(); // cell C4
5042 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
5044 // Insert column at column A.
5045 bool bInsertCol = m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
5046 CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
5048 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
5049 aAddr.IncCol(); // cell D4
5050 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
5052 // Insert a new sheet to shift the current sheet to the right.
5053 m_pDoc->InsertTab(0, u"Table2"_ustr);
5054 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
5055 aAddr.IncTab(); // Move to the next sheet.
5056 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
5058 m_pDoc->DeleteTab(0);
5059 aAddr.IncTab(-1);
5060 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
5062 // Insert cell at C4. This should NOT shift the note position.
5063 bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
5064 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
5065 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
5067 // Delete cell at C4. Again, this should NOT shift the note position.
5068 m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
5069 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
5071 // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
5072 m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
5073 aAddr.IncRow(-1); // cell D3
5074 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D4 should have shifted up to D3.", pNote, m_pDoc->GetNote(aAddr));
5076 // Delete column C. This should shift the note one cell left.
5077 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 2, 1);
5078 aAddr.IncCol(-1); // cell C3
5079 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D3 should have shifted left to C3.", pNote, m_pDoc->GetNote(aAddr));
5081 // Insert a text where the note is.
5082 m_pDoc->SetString(aAddr, u"Note is here."_ustr);
5084 // Delete row 1. This should shift the note from C3 to C2.
5085 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 1);
5086 aAddr.IncRow(-1); // C2
5087 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at C3 should have shifted up to C2.", pNote, m_pDoc->GetNote(aAddr));
5089 m_pDoc->DeleteTab(0);
5092 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteRow)
5094 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
5096 // We need a drawing layer in order to create caption objects.
5097 m_pDoc->InitDrawLayer(m_xDocShell.get());
5099 ScAddress aPos(1, 1, 0);
5100 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5101 pNote->SetText(aPos, u"Hello"_ustr);
5102 pNote->SetAuthor(u"Jim Bob"_ustr);
5104 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5106 // test with IsBlockEmpty
5107 CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsEmptyData(0, 0, 100, 100, 0));
5108 CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
5110 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
5112 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5114 // Set values and notes into B3:B4.
5115 aPos = ScAddress(1,2,0); // B3
5116 m_pDoc->SetString(aPos, u"First"_ustr);
5117 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, u"First Note"_ustr, false, false);
5119 aPos = ScAddress(1,3,0); // B4
5120 m_pDoc->SetString(aPos, u"Second"_ustr);
5121 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, u"Second Note"_ustr, false, false);
5123 // Delete row 2.
5124 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5125 ScMarkData aMark(m_pDoc->GetSheetLimits());
5126 aMark.SelectOneTable(0);
5127 rDocFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
5129 // Check to make sure the notes have shifted upward.
5130 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
5131 CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
5132 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
5133 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5134 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5135 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
5136 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5137 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
5139 // Undo.
5141 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5142 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5143 m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
5145 pUndoMgr->Undo();
5146 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
5147 CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
5148 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5149 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5150 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
5151 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5152 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5153 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
5155 // Delete row 3.
5156 rDocFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
5158 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5159 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5160 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
5161 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5162 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
5164 // Undo and check the result.
5165 pUndoMgr->Undo();
5166 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5167 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5168 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
5169 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5170 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5171 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
5173 m_pDoc->DeleteTab(0);
5176 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteCol)
5178 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
5180 // We need a drawing layer in order to create caption objects.
5181 m_pDoc->InitDrawLayer(m_xDocShell.get());
5183 ScAddress rAddr(1, 1, 0);
5184 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5185 pNote->SetText(rAddr, u"Hello"_ustr);
5186 pNote->SetAuthor(u"Jim Bob"_ustr);
5188 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5190 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
5192 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5194 m_pDoc->DeleteTab(0);
5197 CPPUNIT_TEST_FIXTURE(Test, testNoteLifeCycle)
5199 m_pDoc->InsertTab(0, u"Test"_ustr);
5201 // We need a drawing layer in order to create caption objects.
5202 m_pDoc->InitDrawLayer(m_xDocShell.get());
5204 ScAddress aPos(1,1,0);
5205 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5206 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
5208 pNote->SetText(aPos, u"New note"_ustr);
5209 std::unique_ptr<ScPostIt> pNote2 = m_pDoc->ReleaseNote(aPos);
5210 CPPUNIT_ASSERT_EQUAL_MESSAGE("This note instance is expected to be identical to the original.", pNote, pNote2.get());
5211 CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
5213 // Modify the internal state of the note instance to make sure it's really
5214 // been released.
5215 pNote->SetText(aPos, u"New content"_ustr);
5217 // Re-insert the note back to the same place.
5218 m_pDoc->SetNote(aPos, std::move(pNote2));
5219 SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
5220 CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
5221 CPPUNIT_ASSERT_EQUAL_MESSAGE("This caption should belong to the drawing layer of the document.",
5222 m_pDoc->GetDrawLayer(), static_cast<ScDrawLayer*>(&pCaption->getSdrModelFromSdrObject()));
5224 // Copy B2 with note to a clipboard.
5226 ScClipParam aClipParam(ScRange(aPos), false);
5227 ScDocument aClipDoc(SCDOCMODE_CLIP);
5228 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
5229 aMarkData.SelectOneTable(0);
5230 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMarkData, false, true);
5232 ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
5233 CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
5234 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note on the clipboard should share the same caption object from the original.",
5235 pCaption, pClipNote->GetCaption());
5238 // Move B2 to B3 with note, which creates an ScUndoDragDrop, and Undo.
5240 ScAddress aOrigPos(aPos);
5241 ScAddress aMovePos(1,2,0);
5242 ScPostIt* pOrigNote = m_pDoc->GetNote(aOrigPos);
5243 const SdrCaptionObj* pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5244 bool const bCut = true; // like Drag&Drop
5245 bool bRecord = true; // record Undo
5246 bool const bPaint = false; // don't care about
5247 bool bApi = true; // API to prevent dialogs
5248 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5249 bool bMoveDone = rDocFunc.MoveBlock(ScRange(aOrigPos, aOrigPos), aMovePos, bCut, bRecord, bPaint, bApi);
5250 CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
5252 // Verify the note move.
5253 ScPostIt* pGoneNote = m_pDoc->GetNote(aOrigPos);
5254 CPPUNIT_ASSERT_MESSAGE("Failed to move the note from source.", !pGoneNote);
5255 ScPostIt* pMoveNote = m_pDoc->GetNote(aMovePos);
5256 CPPUNIT_ASSERT_MESSAGE("Failed to move the note to destination.", pMoveNote);
5258 // The caption object should not be identical, it was newly created upon
5259 // Drop from clipboard.
5260 // pOrigCaption is a dangling pointer.
5261 const SdrCaptionObj* pMoveCaption = pMoveNote->GetOrCreateCaption(aMovePos);
5262 CPPUNIT_ASSERT_MESSAGE("Captions identical after move.", pOrigCaption != pMoveCaption);
5264 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5265 CPPUNIT_ASSERT(pUndoMgr);
5266 pUndoMgr->Undo(); // this should not crash ... tdf#92995
5268 // Verify the note move Undo.
5269 pMoveNote = m_pDoc->GetNote(aMovePos);
5270 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move from destination.", !pMoveNote);
5271 pOrigNote = m_pDoc->GetNote(aOrigPos);
5272 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move to source.", pOrigNote);
5274 // The caption object still should not be identical.
5275 // pMoveCaption is a dangling pointer.
5276 pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5277 CPPUNIT_ASSERT_MESSAGE("Captions identical after move undo.", pOrigCaption != pMoveCaption);
5280 // Create a note at B4, merge B4 and B5 with ScUndoMerge, and Undo.
5282 ScAddress aPosB4(1,3,0);
5283 ScPostIt* pNoteB4 = m_pDoc->GetOrCreateNote(aPosB4);
5284 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B4.", pNoteB4);
5285 const SdrCaptionObj* pCaptionB4 = pNoteB4->GetOrCreateCaption(aPosB4);
5286 ScCellMergeOption aCellMergeOption(1,3,2,3);
5287 rDocFunc.MergeCells( aCellMergeOption, true /*bContents*/, bRecord, bApi, false /*bEmptyMergedCells*/ );
5289 SfxUndoManager* pMergeUndoManager = m_pDoc->GetUndoManager();
5290 CPPUNIT_ASSERT(pMergeUndoManager);
5291 pMergeUndoManager->Undo(); // this should not crash ... tdf#105667
5293 // Undo contained the original caption object pointer which was still alive
5294 // at B4 after the merge and not cloned nor recreated during Undo.
5295 ScPostIt* pUndoNoteB4 = m_pDoc->GetNote(aPosB4);
5296 CPPUNIT_ASSERT_MESSAGE("No cell comment at B4 after Undo.", pUndoNoteB4);
5297 const SdrCaptionObj* pUndoCaptionB4 = pUndoNoteB4->GetCaption();
5298 CPPUNIT_ASSERT_EQUAL_MESSAGE("Captions not identical after Merge Undo.", pCaptionB4, pUndoCaptionB4);
5301 // In a second document copy a note from B5 to clipboard, close the
5302 // document and then paste the note into this document.
5304 ScDocShellRef xDocSh2;
5305 getNewDocShell(xDocSh2);
5306 ScDocument* pDoc2 = &xDocSh2->GetDocument();
5307 pDoc2->InsertTab(0, u"OtherSheet1"_ustr);
5308 pDoc2->InitDrawLayer(xDocSh2.get());
5310 ScAddress aPosB5(1,4,0);
5311 ScPostIt* pOtherNoteB5 = pDoc2->GetOrCreateNote(aPosB5);
5312 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B5.", pOtherNoteB5);
5313 const SdrCaptionObj* pOtherCaptionB5 = pOtherNoteB5->GetOrCreateCaption(aPosB5);
5314 CPPUNIT_ASSERT_MESSAGE("No caption at B5.", pOtherCaptionB5);
5316 ScDocument aClipDoc2(SCDOCMODE_CLIP);
5317 copyToClip( pDoc2, ScRange(aPosB5), &aClipDoc2);
5319 // There's no ScTransferObject involved in the "fake" clipboard copy
5320 // and ScDocument dtor asking IsClipboardSource() gets no, so emulate
5321 // the part that normally is responsible for forgetting the caption
5322 // objects.
5323 aClipDoc2.ClosingClipboardSource();
5325 pDoc2->DeleteTab(0);
5326 xDocSh2->DoClose();
5327 xDocSh2.clear();
5329 pasteFromClip( m_pDoc, ScRange(aPosB5), &aClipDoc2); // should not crash... tdf#104967
5330 ScPostIt* pNoteB5 = m_pDoc->GetNote(aPosB5);
5331 CPPUNIT_ASSERT_MESSAGE("Failed to paste cell comment at B5.", pNoteB5);
5332 const SdrCaptionObj* pCaptionB5 = pNoteB5->GetOrCreateCaption(aPosB5);
5333 CPPUNIT_ASSERT_MESSAGE("No caption at pasted B5.", pCaptionB5);
5334 // Do not test if pCaptionB5 != pOtherCaptionB5 because since pDoc2
5335 // has been closed and the caption been deleted objects *may* be
5336 // allocated at the very same memory location.
5339 m_pDoc->DeleteTab(0);
5342 CPPUNIT_TEST_FIXTURE(Test, testNoteCopyPaste)
5344 m_pDoc->InsertTab(0, u"Test"_ustr);
5346 // We need a drawing layer in order to create caption objects.
5347 m_pDoc->InitDrawLayer(m_xDocShell.get());
5349 // Insert in B2 a text and cell comment.
5350 ScAddress aPos(1,1,0);
5351 m_pDoc->SetString(aPos, u"Text"_ustr);
5352 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5353 CPPUNIT_ASSERT(pNote);
5354 pNote->SetText(aPos, u"Note1"_ustr);
5356 // Insert in B4 a number and cell comment.
5357 aPos.SetRow(3);
5358 m_pDoc->SetValue(aPos, 1.1);
5359 pNote = m_pDoc->GetOrCreateNote(aPos);
5360 CPPUNIT_ASSERT(pNote);
5361 pNote->SetText(aPos, u"Note2"_ustr);
5363 // Copy B2:B4 to clipboard.
5364 ScMarkData aMark(m_pDoc->GetSheetLimits());
5365 aMark.SelectOneTable(0);
5366 ScRange aCopyRange(1,1,0,1,3,0);
5367 ScDocument aClipDoc(SCDOCMODE_CLIP);
5368 aClipDoc.ResetClip(m_pDoc, &aMark);
5369 ScClipParam aClipParam(aCopyRange, false);
5370 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
5372 // Make sure the notes are in the clipboard.
5373 pNote = aClipDoc.GetNote(ScAddress(1,1,0));
5374 CPPUNIT_ASSERT(pNote);
5375 CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
5377 pNote = aClipDoc.GetNote(ScAddress(1,3,0));
5378 CPPUNIT_ASSERT(pNote);
5379 CPPUNIT_ASSERT_EQUAL(u"Note2"_ustr, pNote->GetText());
5381 // Paste to B6:B8 but only cell notes.
5382 ScRange aDestRange(1,5,0,1,7,0);
5383 m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::NOTE, nullptr, &aClipDoc);
5385 // Make sure the notes are there.
5386 pNote = m_pDoc->GetNote(ScAddress(1,5,0));
5387 CPPUNIT_ASSERT(pNote);
5388 CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
5390 pNote = m_pDoc->GetNote(ScAddress(1,7,0));
5391 CPPUNIT_ASSERT(pNote);
5392 CPPUNIT_ASSERT_EQUAL(u"Note2"_ustr, pNote->GetText());
5394 // Test that GetNotesInRange includes the end of its range
5395 // and so can find the note
5396 std::vector<sc::NoteEntry> aNotes;
5397 m_pDoc->GetNotesInRange(ScRange(1,7,0), aNotes);
5398 CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
5400 m_pDoc->DeleteTab(0);
5403 // tdf#112454
5404 CPPUNIT_TEST_FIXTURE(Test, testNoteContainsNotesInRange)
5406 m_pDoc->InsertTab(0, u"PostIts"_ustr);
5408 // We need a drawing layer in order to create caption objects.
5409 m_pDoc->InitDrawLayer(m_xDocShell.get());
5411 ScAddress aAddr(2, 2, 0); // cell C3
5413 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in a document that doesn't have any.",
5414 !m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5416 m_pDoc->GetOrCreateNote(aAddr);
5418 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in range that doesn't have any.",
5419 !m_pDoc->ContainsNotesInRange(ScRange(ScAddress(0, 0, 0), ScAddress(0, 1, 0))));
5420 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies on border of range.",
5421 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5422 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies in inner area of range.",
5423 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), ScAddress(3, 3, 0)))));
5426 CPPUNIT_TEST_FIXTURE(Test, testAreasWithNotes)
5428 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
5430 // We need a drawing layer in order to create caption objects.
5431 m_pDoc->InitDrawLayer(m_xDocShell.get());
5433 ScAddress rAddr(1, 5, 0);
5434 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5435 pNote->SetText(rAddr, u"Hello"_ustr);
5436 pNote->SetAuthor(u"Jim Bob"_ustr);
5437 ScAddress rAddrMin(2, 2, 0);
5438 ScPostIt* pNoteMin = m_pDoc->GetOrCreateNote(rAddrMin);
5439 pNoteMin->SetText(rAddrMin, u"Hello"_ustr);
5441 SCCOL col;
5442 SCROW row;
5443 bool dataFound;
5445 // only cell notes (empty content)
5447 dataFound = m_pDoc->GetDataStart(0,col,row);
5449 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5450 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col for notes", static_cast<SCCOL>(1), col);
5451 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row for notes", static_cast<SCROW>(2), row);
5453 dataFound = m_pDoc->GetCellArea(0,col,row);
5455 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5456 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col for notes", static_cast<SCCOL>(2), col);
5457 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row for notes", static_cast<SCROW>(5), row);
5459 bool bNotes = true;
5460 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5462 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5463 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col for notes", static_cast<SCCOL>(2), col);
5464 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row for notes", static_cast<SCROW>(5), row);
5466 bNotes = false;
5467 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5468 CPPUNIT_ASSERT_MESSAGE("No PrintArea should be found", !dataFound);
5470 bNotes = true;
5471 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5472 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5473 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(5), row);
5475 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5476 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5477 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(2), row);
5479 dataFound = m_pDoc->GetPrintAreaVer(0,20,21,row, bNotes); // cols 20 & 21
5480 CPPUNIT_ASSERT_MESSAGE("PrintAreaVer found", !dataFound);
5481 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(0), row);
5483 bNotes = false;
5484 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // col 0 & 1
5485 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer should be found", !dataFound);
5487 // now add cells with value, check that notes are taken into account in good cases
5489 m_pDoc->SetString(0, 3, 0, u"Some Text"_ustr);
5490 m_pDoc->SetString(3, 3, 0, u"Some Text"_ustr);
5492 dataFound = m_pDoc->GetDataStart(0,col,row);
5494 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5495 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col", static_cast<SCCOL>(0), col);
5496 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row", static_cast<SCROW>(2), row);
5498 dataFound = m_pDoc->GetCellArea(0,col,row);
5500 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5501 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col", static_cast<SCCOL>(3), col);
5502 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row", static_cast<SCROW>(5), row);
5504 bNotes = true;
5505 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5507 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5508 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5509 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(5), row);
5511 bNotes = false;
5512 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5513 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5514 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5515 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(3), row);
5517 bNotes = true;
5518 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5519 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5520 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(5), row);
5522 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5523 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5524 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5526 bNotes = false;
5527 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5528 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5529 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5531 m_pDoc->DeleteTab(0);
5534 CPPUNIT_TEST_FIXTURE(Test, testAnchoredRotatedShape)
5536 m_pDoc->InsertTab(0, u"TestTab"_ustr);
5537 SCROW nRow1, nRow2;
5538 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5539 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
5540 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
5541 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
5543 m_pDoc->InitDrawLayer();
5544 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5545 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
5546 SdrPage* pPage = pDrawLayer->GetPage(0);
5547 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
5548 m_pDoc->SetRowHeightRange(0, m_pDoc->MaxRow(), 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5549 constexpr tools::Long TOLERANCE = 30; //30 hmm
5550 for ( SCCOL nCol = 0; nCol < m_pDoc->MaxCol(); ++nCol )
5551 m_pDoc->SetColWidth(nCol, 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5553 //Add a rect
5554 tools::Rectangle aRect( 4000, 5000, 10000, 7000 );
5556 tools::Rectangle aRotRect( 6000, 3000, 8000, 9000 );
5557 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aRect);
5558 pPage->InsertObject(pObj.get());
5559 Point aRef1(pObj->GetSnapRect().Center());
5560 Degree100 nAngle = 9000_deg100; //90 deg.
5561 double nSin = 1.0; // sin(90 deg)
5562 double nCos = 0.0; // cos(90 deg)
5563 pObj->Rotate(aRef1,nAngle,nSin,nCos);
5565 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
5567 tools::Rectangle aSnap = pObj->GetSnapRect();
5568 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5569 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5570 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Left(), aSnap.Left(), TOLERANCE );
5571 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Top(), aSnap.Top(), TOLERANCE );
5573 ScDrawObjData aAnchor;
5574 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj.get() );
5575 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5577 aAnchor.maStart = pData->maStart;
5578 aAnchor.maEnd = pData->maEnd;
5580 m_pDoc->SetDrawPageSize(0);
5582 // increase row 5 by 2000 hmm
5583 m_pDoc->SetRowHeight(5, 0, o3tl::toTwips(3000, o3tl::Length::mm100));
5584 // increase col 6 by 1000 hmm
5585 m_pDoc->SetColWidth(6, 0, o3tl::toTwips(2000, o3tl::Length::mm100));
5587 aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
5588 aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
5590 m_pDoc->SetDrawPageSize(0);
5592 aSnap = pObj->GetSnapRect();
5594 // ensure that width and height have been adjusted accordingly
5595 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5596 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5598 // ensure that anchor start and end addresses haven't changed
5599 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
5600 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
5601 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
5602 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
5604 m_pDoc->DeleteTab(0);
5607 CPPUNIT_TEST_FIXTURE(Test, testCellTextWidth)
5609 m_pDoc->InsertTab(0, u"Test"_ustr);
5611 ScAddress aTopCell(0, 0, 0);
5613 // Sheet is empty.
5614 std::unique_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5615 CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
5617 // Sheet only has one cell.
5618 m_pDoc->SetString(0, 0, 0, u"Only one cell"_ustr);
5619 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5620 CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
5621 CPPUNIT_ASSERT_EQUAL(SCROW(0), pIter->getPos());
5623 // Setting a text width here should commit it to the column.
5624 sal_uInt16 nTestVal = 432;
5625 pIter->setValue(nTestVal);
5626 CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
5628 // Set values to row 2 through 6.
5629 for (SCROW i = 2; i <= 6; ++i)
5630 m_pDoc->SetString(0, i, 0, u"foo"_ustr);
5632 // Set values to row 10 through 18.
5633 for (SCROW i = 10; i <= 18; ++i)
5634 m_pDoc->SetString(0, i, 0, u"foo"_ustr);
5637 // Full range.
5638 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5639 SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
5640 for (const auto& rRow : aRows)
5642 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5643 CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
5644 pIter->next();
5646 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5650 // Specify start and end rows (6 - 16)
5651 ScAddress aStart = aTopCell;
5652 aStart.SetRow(6);
5653 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
5654 SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
5655 for (const auto& rRow : aRows)
5657 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5658 CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
5659 pIter->next();
5661 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5664 // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
5665 clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
5668 // Full range again.
5669 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5670 SCROW aRows[] = { 0, 2, 18 };
5671 for (const auto& rRow : aRows)
5673 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5674 CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
5675 pIter->next();
5677 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5680 // Delete row 2 which shifts all cells below row 2 upward. After this, we
5681 // should only have cells at rows 0 and 17.
5682 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), MAXTAB, 2, 1);
5684 // Full range again.
5685 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5686 SCROW aRows[] = { 0, 17 };
5687 for (const auto& rRow : aRows)
5689 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5690 CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
5691 pIter->next();
5693 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5696 m_pDoc->DeleteTab(0);
5699 static bool checkEditTextIterator(sc::EditTextIterator& rIter, const char** pChecks)
5701 const EditTextObject* pText = rIter.first();
5702 const char* p = *pChecks;
5704 for (int i = 0; i < 100; ++i) // cap it to 100 loops.
5706 if (!pText)
5707 // No more edit cells. The check string array should end too.
5708 return p == nullptr;
5710 if (!p)
5711 // More edit cell, but no more check string. Bad.
5712 return false;
5714 if (pText->GetParagraphCount() != 1)
5715 // For this test, we don't handle multi-paragraph text.
5716 return false;
5718 if (pText->GetText(0) != OUString::createFromAscii(p))
5719 // Text differs from what's expected.
5720 return false;
5722 pText = rIter.next();
5723 ++pChecks;
5724 p = *pChecks;
5727 return false;
5730 CPPUNIT_TEST_FIXTURE(Test, testEditTextIterator)
5732 m_pDoc->InsertTab(0, u"Test"_ustr);
5735 // First, try with an empty sheet.
5736 sc::EditTextIterator aIter(*m_pDoc,0);
5737 const char* pChecks[] = { nullptr };
5738 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5741 ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
5744 // Only set one edit cell.
5745 rEditEngine.SetTextCurrentDefaults(u"A2"_ustr);
5746 m_pDoc->SetEditText(ScAddress(0,1,0), rEditEngine.CreateTextObject());
5747 sc::EditTextIterator aIter(*m_pDoc,0);
5748 const char* pChecks[] = { "A2", nullptr };
5749 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5753 // Add a series of edit cells.
5754 rEditEngine.SetTextCurrentDefaults(u"A5"_ustr);
5755 m_pDoc->SetEditText(ScAddress(0,4,0), rEditEngine.CreateTextObject());
5756 rEditEngine.SetTextCurrentDefaults(u"A6"_ustr);
5757 m_pDoc->SetEditText(ScAddress(0,5,0), rEditEngine.CreateTextObject());
5758 rEditEngine.SetTextCurrentDefaults(u"A7"_ustr);
5759 m_pDoc->SetEditText(ScAddress(0,6,0), rEditEngine.CreateTextObject());
5760 sc::EditTextIterator aIter(*m_pDoc,0);
5761 const char* pChecks[] = { "A2", "A5", "A6", "A7", nullptr };
5762 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5766 // Add more edit cells to column C. Skip column B.
5767 rEditEngine.SetTextCurrentDefaults(u"C1"_ustr);
5768 m_pDoc->SetEditText(ScAddress(2,0,0), rEditEngine.CreateTextObject());
5769 rEditEngine.SetTextCurrentDefaults(u"C3"_ustr);
5770 m_pDoc->SetEditText(ScAddress(2,2,0), rEditEngine.CreateTextObject());
5771 rEditEngine.SetTextCurrentDefaults(u"C4"_ustr);
5772 m_pDoc->SetEditText(ScAddress(2,3,0), rEditEngine.CreateTextObject());
5773 sc::EditTextIterator aIter(*m_pDoc,0);
5774 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5775 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5779 // Add some numeric, string and formula cells. This shouldn't affect the outcome.
5780 m_pDoc->SetString(ScAddress(0,99,0), u"=ROW()"_ustr);
5781 m_pDoc->SetValue(ScAddress(1,3,0), 1.2);
5782 m_pDoc->SetString(ScAddress(2,4,0), u"Simple string"_ustr);
5783 sc::EditTextIterator aIter(*m_pDoc,0);
5784 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5785 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5788 m_pDoc->DeleteTab(0);
5791 CPPUNIT_TEST_FIXTURE(Test, testImportStream)
5793 sc::AutoCalcSwitch aAC(*m_pDoc, true); // turn on auto calc.
5794 sc::UndoSwitch aUndo(*m_pDoc, true); // enable undo.
5796 m_pDoc->InsertTab(0, u"Test"_ustr);
5798 m_pDoc->SetString(ScAddress(0,1,0), u"=SUM(A1:C1)"_ustr); // A2
5800 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5802 // CSV import options.
5803 ScAsciiOptions aOpt;
5804 aOpt.SetFieldSeps(u","_ustr);
5806 // Import values to A1:C1.
5807 ScImportExport aObj(*m_pDoc, ScAddress(0,0,0));
5808 aObj.SetImportBroadcast(true);
5809 aObj.SetExtOptions(aOpt);
5810 aObj.ImportString(u"1,2,3"_ustr, SotClipboardFormatId::STRING);
5812 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5813 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5814 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5816 // Formula value should have been updated.
5817 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5819 // Undo, and check the result.
5820 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5821 CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
5822 pUndoMgr->Undo();
5824 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5825 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5826 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5828 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5830 // Redo, and check the result.
5831 pUndoMgr->Redo();
5833 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5834 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5835 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5837 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5839 pUndoMgr->Clear();
5841 m_pDoc->DeleteTab(0);
5844 CPPUNIT_TEST_FIXTURE(Test, testDeleteContents)
5846 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
5847 sc::UndoSwitch aUndoSwitch(*m_pDoc, true); // enable undo.
5849 m_pDoc->InsertTab(0, u"Test"_ustr);
5851 m_pDoc->SetValue(ScAddress(3,1,0), 1.0);
5852 m_pDoc->SetValue(ScAddress(3,2,0), 1.0);
5853 m_pDoc->SetValue(ScAddress(3,3,0), 1.0);
5854 m_pDoc->SetValue(ScAddress(3,4,0), 1.0);
5855 m_pDoc->SetValue(ScAddress(3,5,0), 1.0);
5856 m_pDoc->SetValue(ScAddress(3,6,0), 1.0);
5857 m_pDoc->SetValue(ScAddress(3,7,0), 1.0);
5858 m_pDoc->SetValue(ScAddress(3,8,0), 1.0);
5859 m_pDoc->SetString(ScAddress(3,15,0), u"=SUM(D2:D15)"_ustr);
5861 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5863 // Delete D2:D6.
5864 ScRange aRange(3,1,0,3,5,0);
5865 ScMarkData aMark(m_pDoc->GetSheetLimits());
5866 aMark.SelectOneTable(0);
5867 aMark.SetMarkArea(aRange);
5869 ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
5870 pUndoDoc->InitUndo(*m_pDoc, 0, 0);
5871 m_pDoc->CopyToDocument(aRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc, &aMark);
5872 ScUndoDeleteContents aUndo(m_xDocShell.get(), aMark, aRange, std::move(pUndoDoc), false, InsertDeleteFlags::CONTENTS, true);
5874 clearRange(m_pDoc, aRange);
5875 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5877 aUndo.Undo();
5878 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5880 aUndo.Redo();
5881 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5883 m_pDoc->DeleteTab(0);
5886 CPPUNIT_TEST_FIXTURE(Test, testTransliterateText)
5888 m_pDoc->InsertTab(0, u"Test"_ustr);
5890 // Set texts to A1:A3.
5891 m_pDoc->SetString(ScAddress(0,0,0), u"Mike"_ustr);
5892 m_pDoc->SetString(ScAddress(0,1,0), u"Noah"_ustr);
5893 m_pDoc->SetString(ScAddress(0,2,0), u"Oscar"_ustr);
5895 // Change them to uppercase.
5896 ScMarkData aMark(m_pDoc->GetSheetLimits());
5897 aMark.SetMarkArea(ScRange(0,0,0,0,2,0));
5898 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5899 rFunc.TransliterateText(
5900 aMark, TransliterationFlags::LOWERCASE_UPPERCASE, true);
5902 CPPUNIT_ASSERT_EQUAL(u"MIKE"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5903 CPPUNIT_ASSERT_EQUAL(u"NOAH"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5904 CPPUNIT_ASSERT_EQUAL(u"OSCAR"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5906 // Test the undo and redo.
5907 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5908 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5910 pUndoMgr->Undo();
5911 CPPUNIT_ASSERT_EQUAL(u"Mike"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5912 CPPUNIT_ASSERT_EQUAL(u"Noah"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5913 CPPUNIT_ASSERT_EQUAL(u"Oscar"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5915 pUndoMgr->Redo();
5916 CPPUNIT_ASSERT_EQUAL(u"MIKE"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5917 CPPUNIT_ASSERT_EQUAL(u"NOAH"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5918 CPPUNIT_ASSERT_EQUAL(u"OSCAR"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5920 m_pDoc->DeleteTab(0);
5923 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue)
5925 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5926 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5928 m_pDoc->InsertTab(0, u"Test"_ustr);
5930 std::vector<std::vector<const char*>> aData = {
5931 { "=1", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5932 { "=2", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5933 { "=3", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5934 { "=4", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5935 { "=5", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5936 { "=6", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5939 ScAddress aPos(1,2,0); // B3
5940 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5941 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5944 // Expected output table content. 0 = empty cell
5945 std::vector<std::vector<const char*>> aOutputCheck = {
5946 { "1", "2", "TRUE" },
5947 { "2", "4", "TRUE" },
5948 { "3", "6", "TRUE" },
5949 { "4", "8", "TRUE" },
5950 { "5", "10", "TRUE" },
5951 { "6", "12", "TRUE" },
5954 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5955 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5958 // Convert B5:C6 to static values, and check the result.
5959 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5960 ScRange aConvRange(1,4,0,2,5,0); // B5:C6
5961 rFunc.ConvertFormulaToValue(aConvRange, false);
5964 // Expected output table content. 0 = empty cell
5965 std::vector<std::vector<const char*>> aOutputCheck = {
5966 { "1", "2", "TRUE" },
5967 { "2", "4", "TRUE" },
5968 { "3", "6", "FALSE" },
5969 { "4", "8", "FALSE" },
5970 { "5", "10", "TRUE" },
5971 { "6", "12", "TRUE" },
5974 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5975 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5978 // Make sure that B3:B4 and B7:B8 are formula cells.
5979 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5980 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5981 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5982 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5984 // Make sure that B5:C6 are numeric cells.
5985 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5986 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5987 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5988 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5990 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5991 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5992 CPPUNIT_ASSERT(pFC);
5993 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5994 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5995 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5996 CPPUNIT_ASSERT(pFC);
5997 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5998 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6000 // Undo and check.
6001 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6002 CPPUNIT_ASSERT(pUndoMgr);
6003 pUndoMgr->Undo();
6006 // Expected output table content. 0 = empty cell
6007 std::vector<std::vector<const char*>> aOutputCheck = {
6008 { "1", "2", "TRUE" },
6009 { "2", "4", "TRUE" },
6010 { "3", "6", "TRUE" },
6011 { "4", "8", "TRUE" },
6012 { "5", "10", "TRUE" },
6013 { "6", "12", "TRUE" },
6016 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
6017 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6020 // B3:B8 should all be (ungrouped) formula cells.
6021 for (SCROW i = 2; i <= 7; ++i)
6023 pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
6024 CPPUNIT_ASSERT(pFC);
6025 CPPUNIT_ASSERT(!pFC->IsShared());
6028 // C3:C8 should be shared formula cells.
6029 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
6030 CPPUNIT_ASSERT(pFC);
6031 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6032 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
6034 // Redo and check.
6035 pUndoMgr->Redo();
6037 // Expected output table content. 0 = empty cell
6038 std::vector<std::vector<const char*>> aOutputCheck = {
6039 { "1", "2", "TRUE" },
6040 { "2", "4", "TRUE" },
6041 { "3", "6", "FALSE" },
6042 { "4", "8", "FALSE" },
6043 { "5", "10", "TRUE" },
6044 { "6", "12", "TRUE" },
6047 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
6048 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6051 // Make sure that B3:B4 and B7:B8 are formula cells.
6052 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
6053 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
6054 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
6055 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
6057 // Make sure that B5:C6 are numeric cells.
6058 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
6059 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
6060 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
6061 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
6063 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
6064 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
6065 CPPUNIT_ASSERT(pFC);
6066 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6067 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6068 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
6069 CPPUNIT_ASSERT(pFC);
6070 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
6071 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6073 // Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
6074 pUndoMgr->Undo();
6075 m_pDoc->SetValue(ScAddress(1,4,0), 10);
6076 m_pDoc->SetValue(ScAddress(1,5,0), 11);
6077 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
6078 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
6080 m_pDoc->DeleteTab(0);
6083 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue2)
6085 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
6086 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
6088 m_pDoc->InsertTab(0, u"Test"_ustr);
6090 std::vector<std::vector<const char*>> aData = {
6091 { "=1", "=ISFORMULA(RC[-1])" },
6092 { "=2", "=ISFORMULA(RC[-1])" },
6093 { "3", "=ISFORMULA(RC[-1])" },
6094 { "=4", "=ISFORMULA(RC[-1])" },
6095 { "=5", "=ISFORMULA(RC[-1])" },
6098 // Insert data into B2:C6.
6099 ScAddress aPos(1,1,0); // B2
6100 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
6101 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
6104 // Expected output table content. 0 = empty cell
6105 std::vector<std::vector<const char*>> aOutputCheck = {
6106 { "1", "TRUE" },
6107 { "2", "TRUE" },
6108 { "3", "FALSE" },
6109 { "4", "TRUE" },
6110 { "5", "TRUE" },
6113 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6114 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6117 // Convert B3:B5 to a value.
6118 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6119 ScRange aConvRange(1,2,0,1,4,0); // B3:B5
6120 rFunc.ConvertFormulaToValue(aConvRange, false);
6123 // Expected output table content. 0 = empty cell
6124 std::vector<std::vector<const char*>> aOutputCheck = {
6125 { "1", "TRUE" },
6126 { "2", "FALSE" },
6127 { "3", "FALSE" },
6128 { "4", "FALSE" },
6129 { "5", "TRUE" },
6132 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6133 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6136 // Undo and check.
6137 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6138 CPPUNIT_ASSERT(pUndoMgr);
6139 pUndoMgr->Undo();
6142 // Expected output table content. 0 = empty cell
6143 std::vector<std::vector<const char*>> aOutputCheck = {
6144 { "1", "TRUE" },
6145 { "2", "TRUE" },
6146 { "3", "FALSE" },
6147 { "4", "TRUE" },
6148 { "5", "TRUE" },
6151 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
6152 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6155 m_pDoc->DeleteTab(0);
6158 CPPUNIT_TEST_FIXTURE(Test, testColumnFindEditCells)
6160 m_pDoc->InsertTab(0, u"Test"_ustr);
6162 // Test the basics with real edit cells, using Column A.
6164 SCROW nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
6165 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6166 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,0,0));
6167 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6168 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,10,0));
6169 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6171 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
6172 rEE.SetTextCurrentDefaults(u"Test"_ustr);
6173 m_pDoc->SetEditText(ScAddress(0,0,0), rEE.CreateTextObject());
6174 const EditTextObject* pObj = m_pDoc->GetEditText(ScAddress(0,0,0));
6175 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell here.", pObj);
6177 ScRange aRange(0,0,0,0,0,0);
6178 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6179 CPPUNIT_ASSERT_EQUAL_MESSAGE("There is an edit cell here.", SCROW(0), nResRow);
6181 aRange.aStart.SetRow(1);
6182 aRange.aEnd.SetRow(1);
6183 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6184 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6186 aRange.aStart.SetRow(2);
6187 aRange.aEnd.SetRow(4);
6188 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6189 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6191 aRange.aStart.SetRow(0);
6192 aRange.aEnd.SetRow(m_pDoc->MaxRow());
6193 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6194 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an edit cell in specified range.", SCROW(0), nResRow);
6196 m_pDoc->SetString(ScAddress(0,0,0), u"Test"_ustr);
6197 m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
6198 ScRefCellValue aCell;
6199 aCell.assign(*m_pDoc, ScAddress(0,0,0));
6200 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string cell.", CELLTYPE_STRING, aCell.getType());
6201 aCell.assign(*m_pDoc, ScAddress(0,1,0));
6202 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6203 aCell.assign(*m_pDoc, ScAddress(0,2,0));
6204 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a numeric cell.", CELLTYPE_VALUE, aCell.getType());
6205 aCell.assign(*m_pDoc, ScAddress(0,3,0));
6206 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6208 aRange.aStart.SetRow(1);
6209 aRange.aEnd.SetRow(1);
6210 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6211 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6213 // Test with non-edit cell but with ambiguous script type.
6215 m_pDoc->SetString(ScAddress(1,11,0), u"Some text"_ustr);
6216 m_pDoc->SetString(ScAddress(1,12,0), u"Some text"_ustr);
6217 m_pDoc->SetString(ScAddress(1,13,0), u"Other text"_ustr);
6219 m_pDoc->SetScriptType(ScAddress(1,11,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6220 m_pDoc->SetScriptType(ScAddress(1,12,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6221 m_pDoc->SetScriptType(ScAddress(1,13,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6223 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(ScAddress(1,11,0)));
6224 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), nResRow);
6225 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(ScAddress(1,12,0)));
6226 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), nResRow);
6228 for (SCROW i = 0; i <= 5; ++i)
6229 m_pDoc->SetString(ScAddress(2,i,0), u"Text"_ustr);
6231 m_pDoc->SetScriptType(ScAddress(2,5,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6233 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(ScAddress(2,1,0)));
6234 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(-1), nResRow);
6236 m_pDoc->DeleteTab(0);
6240 CPPUNIT_TEST_FIXTURE(Test, testSetFormula)
6242 m_pDoc->InsertTab(0, u"Test"_ustr);
6244 static struct aInputs
6246 SCROW nRow;
6247 SCCOL nCol;
6248 const char* aFormula1; // Represents the formula that is input to SetFormula function.
6249 const char* aFormula2; // Represents the formula that is actually stored in the cell.
6250 formula::FormulaGrammar::Grammar const eGram;
6252 } const aTest[] = {
6253 { 5 , 4 , "=SUM($D$2:$F$3)" ,"=SUM($D$2:$F$3)" , formula::FormulaGrammar::Grammar::GRAM_ENGLISH },
6254 { 5 , 5 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE },
6255 { 6 , 6 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE_XL_A1},
6256 { 7 , 8 , "=[.A1]-[.$C2]+[.G$3]-[.$F$4]","=A1-$C2+G$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_ODFF }
6259 for(const auto& rTest : aTest)
6261 m_pDoc->SetFormula(ScAddress(rTest.nCol, rTest.nRow, 0), OUString::createFromAscii(rTest.aFormula1), rTest.eGram);
6262 OUString aBuffer = m_pDoc->GetFormula(rTest.nCol, rTest.nRow, 0);
6264 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set formula", OUString::createFromAscii(rTest.aFormula2), aBuffer);
6267 m_pDoc->DeleteTab(0);
6270 CPPUNIT_TEST_FIXTURE(Test, testMultipleDataCellsInRange)
6272 m_pDoc->InsertTab(0, u"Test"_ustr);
6274 ScRange aRange(1,2,0); // B3
6275 sc::MultiDataCellState aState = m_pDoc->HasMultipleDataCells(aRange);
6276 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::Empty, aState.meState);
6278 // Set a numeric value to B3.
6279 m_pDoc->SetValue(ScAddress(1,2,0), 1.0);
6280 aState = m_pDoc->HasMultipleDataCells(aRange);
6281 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6282 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6283 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6285 // Set another numeric value to B4.
6286 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
6287 aRange.aEnd.SetRow(3); // B3:B4
6288 aState = m_pDoc->HasMultipleDataCells(aRange);
6289 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6290 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6291 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6293 // Set the query range to B4:B5. Now it should only report one cell, with
6294 // B4 being the first non-empty cell.
6295 aRange.aStart.SetRow(3);
6296 aRange.aEnd.SetRow(4);
6297 aState = m_pDoc->HasMultipleDataCells(aRange);
6298 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6299 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6300 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6302 // Set the query range to A1:C3. The first non-empty cell should be B3.
6303 aRange = ScRange(0,0,0,2,2,0);
6304 aState = m_pDoc->HasMultipleDataCells(aRange);
6305 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6306 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6307 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6309 // Set string cells to D4 and F5, and query D3:F5. D4 should be the first
6310 // non-empty cell.
6311 m_pDoc->SetString(ScAddress(3,3,0), u"foo"_ustr);
6312 m_pDoc->SetString(ScAddress(5,4,0), u"bar"_ustr);
6313 aRange = ScRange(3,2,0,5,4,0);
6314 aState = m_pDoc->HasMultipleDataCells(aRange);
6315 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6316 CPPUNIT_ASSERT_EQUAL(SCCOL(3), aState.mnCol1);
6317 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6319 // TODO : add more test cases as needed.
6321 m_pDoc->DeleteTab(0);
6324 CPPUNIT_TEST_FIXTURE(Test, testFormulaWizardSubformula)
6326 m_pDoc->InsertTab(0, u"Test"_ustr);
6328 m_pDoc->SetString(ScAddress(1,0,0), u"=1"_ustr); // B1
6329 m_pDoc->SetString(ScAddress(1,1,0), u"=1/0"_ustr); // B2
6330 m_pDoc->SetString(ScAddress(1,2,0), u"=gibberish"_ustr); // B3
6332 ScSimpleFormulaCalculator aFCell1( *m_pDoc, ScAddress(0,0,0), u"=B1:B3"_ustr, true );
6333 FormulaError nErrCode = aFCell1.GetErrCode();
6334 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell1.IsMatrix() );
6335 CPPUNIT_ASSERT_EQUAL( u"{1|#DIV/0!|#NAME?}"_ustr, aFCell1.GetString().getString() );
6337 m_pDoc->SetString(ScAddress(1,0,0), u"=NA()"_ustr); // B1
6338 m_pDoc->SetString(ScAddress(1,1,0), u"2"_ustr); // B2
6339 m_pDoc->SetString(ScAddress(1,2,0), u"=1+2"_ustr); // B3
6340 ScSimpleFormulaCalculator aFCell2( *m_pDoc, ScAddress(0,0,0), u"=B1:B3"_ustr, true );
6341 nErrCode = aFCell2.GetErrCode();
6342 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell2.IsMatrix() );
6343 CPPUNIT_ASSERT_EQUAL( u"{#N/A|2|3}"_ustr, aFCell2.GetString().getString() );
6345 m_pDoc->DeleteTab(0);
6348 CPPUNIT_TEST_FIXTURE(Test, testDiagonalBorders)
6350 m_pDoc->InsertTab(0, u"Diagonal"_ustr);
6352 ScAddress aPos;
6353 const editeng::SvxBorderLine* pLine;
6354 const ScPatternAttr* pPat;
6356 // diagonal down border
6357 ::editeng::SvxBorderLine dDownBorderLine(nullptr, 1);
6358 SvxLineItem dDownLineItem(ATTR_BORDER_TLBR);
6359 dDownLineItem.SetLine(&dDownBorderLine);
6361 // set diagonal down border to cell(A1)
6362 m_pDoc->ApplyAttr(0, 0, 0, dDownLineItem);
6364 aPos = { 0, 0, 0 };
6365 pPat = m_pDoc->GetPattern(aPos);
6366 CPPUNIT_ASSERT(pPat);
6368 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6369 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6371 // diagonal up border
6372 ::editeng::SvxBorderLine dUpBorderLine(nullptr, 1);
6373 SvxLineItem dUpLineItem(ATTR_BORDER_BLTR);
6374 dUpLineItem.SetLine(&dUpBorderLine);
6376 // set diagonal up border to cell(A2)
6377 m_pDoc->ApplyAttr(0, 1, 0, dUpLineItem);
6379 aPos = { 0, 1, 0 };
6380 pPat = m_pDoc->GetPattern(aPos);
6381 CPPUNIT_ASSERT(pPat);
6383 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6384 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6386 // diagonal down and up border in the same cell (A5)
6387 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6388 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6390 // test if both borders are applied successfully in the same cell (A5)
6391 aPos = { 0, 4, 0 };
6392 pPat = m_pDoc->GetPattern(aPos);
6393 CPPUNIT_ASSERT(pPat);
6395 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6396 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6397 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6398 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6400 // test if both borders are removed successfully
6401 dDownLineItem.SetLine(nullptr);
6402 dUpLineItem.SetLine(nullptr);
6404 // SetLine(nullptr) should remove the lines from (A5)
6405 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6406 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6408 pPat = m_pDoc->GetPattern(aPos);
6409 CPPUNIT_ASSERT(pPat);
6411 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6412 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was not expected, but is found!", !pLine);
6413 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6414 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was not expected, but is found!", !pLine);
6416 m_pDoc->DeleteTab(0);
6419 CPPUNIT_TEST_FIXTURE(Test, testWholeDocBorders)
6421 m_pDoc->InsertTab(0, u"Borders"_ustr);
6423 // Set outside border to be on all sides, and inside borders to be only vertical.
6424 // This should result in edge borders of the spreadsheets being set, but internal
6425 // borders between cells should be only vertical, not horizontal.
6426 ::editeng::SvxBorderLine line(nullptr, 50, SvxBorderLineStyle::SOLID);
6427 SvxBoxItem borderItem(ATTR_BORDER);
6428 borderItem.SetLine(&line, SvxBoxItemLine::LEFT);
6429 borderItem.SetLine(&line, SvxBoxItemLine::RIGHT);
6430 borderItem.SetLine(&line, SvxBoxItemLine::TOP);
6431 borderItem.SetLine(&line, SvxBoxItemLine::BOTTOM);
6432 SvxBoxInfoItem boxInfoItem(ATTR_BORDER);
6433 boxInfoItem.SetLine(&line, SvxBoxInfoItemLine::VERT);
6435 ScMarkData mark( m_pDoc->GetSheetLimits(), ScRange( 0, 0, 0, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 ));
6436 m_pDoc->ApplySelectionFrame( mark, borderItem, &boxInfoItem );
6438 const ScPatternAttr* attr;
6439 attr = m_pDoc->GetPattern( 0, 0, 0 );
6440 CPPUNIT_ASSERT(attr);
6441 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6442 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6443 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6444 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6446 attr = m_pDoc->GetPattern( 1, 0, 0 );
6447 CPPUNIT_ASSERT(attr);
6448 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6449 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6450 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6451 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6453 attr = m_pDoc->GetPattern( 0, 1, 0 );
6454 CPPUNIT_ASSERT(attr);
6455 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6456 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6457 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6458 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6460 attr = m_pDoc->GetPattern( 1, 1, 0 );
6461 CPPUNIT_ASSERT(attr);
6462 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6463 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6464 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6465 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6467 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), 0, 0 );
6468 CPPUNIT_ASSERT(attr);
6469 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6470 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6471 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6472 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6474 attr = m_pDoc->GetPattern( 0, m_pDoc->MaxRow(), 0 );
6475 CPPUNIT_ASSERT(attr);
6476 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6477 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6478 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6479 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6481 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 );
6482 CPPUNIT_ASSERT(attr);
6483 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6484 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6485 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6486 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6488 m_pDoc->DeleteTab(0);
6491 CPPUNIT_TEST_FIXTURE(Test, testSetStringAndNote)
6493 m_pDoc->InsertTab(0, u"Test"_ustr);
6495 // We need a drawing layer in order to create caption objects.
6496 m_pDoc->InitDrawLayer(m_xDocShell.get());
6498 //note on A1
6499 ScAddress aAdrA1 (0, 0, 0);
6500 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
6501 pNote->SetText(aAdrA1, u"Hello world in A1"_ustr);
6503 m_pDoc->SetString(0, 0, 0, u""_ustr);
6505 pNote = m_pDoc->GetNote(aAdrA1);
6506 CPPUNIT_ASSERT(pNote);
6508 m_pDoc->DeleteTab(0);
6511 CPPUNIT_TEST_FIXTURE(Test, testUndoDataAnchor)
6513 m_pDoc->InsertTab(0, u"Tab1"_ustr);
6514 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 1 sheets to begin with",
6515 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
6517 m_pDoc->InitDrawLayer();
6518 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
6519 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
6520 SdrPage* pPage = pDrawLayer->GetPage(0);
6521 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
6523 // Insert an object.
6524 tools::Rectangle aObjRect(2,1000,100,1100);
6525 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
6526 pPage->InsertObject(pObj.get());
6527 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
6529 // Get anchor data
6530 ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
6531 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6533 ScAddress aOldStart = pData->maStart;
6534 ScAddress aOldEnd = pData->maEnd;
6536 // Get non rotated anchor data
6537 ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6538 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6540 ScAddress aNOldStart = pNData->maStart;
6541 ScAddress aNOldEnd = pNData->maEnd;
6542 CPPUNIT_ASSERT_EQUAL(aOldStart, aNOldStart);
6543 CPPUNIT_ASSERT_EQUAL(aOldEnd, aNOldEnd);
6545 //pDrawLayer->BeginCalcUndo(false);
6546 // Insert a new row at row 3.
6547 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6548 ScMarkData aMark(m_pDoc->GetSheetLimits());
6549 aMark.SelectOneTable(0);
6550 rFunc.InsertCells(ScRange( 0, aOldStart.Row() - 1, 0, m_pDoc->MaxCol(), aOldStart.Row(), 0 ), &aMark, INS_INSROWS_BEFORE, true, true);
6552 pData = ScDrawLayer::GetObjData(pObj.get());
6553 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6555 ScAddress aNewStart = pData->maStart;
6556 ScAddress aNewEnd = pData->maEnd;
6558 // Get non rotated anchor data
6559 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6560 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6562 ScAddress aNNewStart = pNData->maStart;
6563 ScAddress aNNewEnd = pNData->maEnd;
6564 CPPUNIT_ASSERT_EQUAL(aNewStart, aNNewStart);
6565 CPPUNIT_ASSERT_EQUAL(aNewEnd, aNNewEnd);
6566 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewStart != aOldStart );
6567 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewEnd != aOldEnd );
6568 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewStart != aNOldStart );
6569 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewEnd != aNOldEnd );
6571 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6572 CPPUNIT_ASSERT(pUndoMgr);
6573 pUndoMgr->Undo();
6575 // Check state
6576 ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObj);
6577 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Failed to check state SCA_CELL.", SCA_CELL, oldType);
6579 // Get anchor data
6580 pData = ScDrawLayer::GetObjData(pObj.get());
6581 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6583 // Get non rotated anchor data
6584 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6585 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6587 // Check if data has moved to new rows
6588 CPPUNIT_ASSERT_EQUAL(pData->maStart, aOldStart);
6589 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aOldEnd);
6591 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNOldStart);
6592 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNOldEnd);
6594 pUndoMgr->Redo();
6596 // Get anchor data
6597 pData = ScDrawLayer::GetObjData(pObj.get());
6598 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6600 // Get non rotated anchor data
6601 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6602 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6604 // Check if data has moved to new rows
6605 CPPUNIT_ASSERT_EQUAL(pData->maStart, aNewStart);
6606 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aNewEnd);
6608 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNNewStart);
6609 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNNewEnd);
6611 m_pDoc->DeleteTab(0);
6615 CPPUNIT_TEST_FIXTURE(Test, testEmptyCalcDocDefaults)
6617 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCellCount() );
6618 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetFormulaGroupCount() );
6619 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCodeCount() );
6620 CPPUNIT_ASSERT_EQUAL( int(CharCompressType::NONE), static_cast<int>(m_pDoc->GetAsianCompression()) );
6622 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasPrintRange() );
6623 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInVBAMode() );
6624 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasNotes() );
6625 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6627 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedFonts() );
6628 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedUsedFontsOnly() );
6629 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptLatin() );
6630 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptAsian() );
6631 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptComplex() );
6632 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedded() );
6634 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDocEditable() );
6635 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocProtected() );
6636 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocVisible() );
6637 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUserInteractionEnabled() );
6639 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasAnyCalcNotification() );
6640 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAutoCalcShellDisabled() );
6641 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsForcedFormulaPending() );
6642 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalculatingFormulaTree() );
6644 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipOrUndo() );
6645 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboard() );
6646 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsUndo() );
6647 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUndoEnabled() );
6648 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6649 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboardSource() );
6650 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInsertingFromOtherDoc() );
6651 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->PastingDrawFromOtherDoc() );
6653 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAdjustHeightLocked() );
6654 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsExecuteLinkEnabled() );
6655 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChangeReadOnlyEnabled() );
6657 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IdleCalcTextWidth() );
6658 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsIdleEnabled() );
6659 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDetectiveDirty() );
6660 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasLinkFormulaNeedingCheck() );
6661 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChartListenerCollectionNeedsUpdate() );
6663 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasRangeOverflow() );
6664 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsImportingXML() );
6665 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalcingAfterLoad() );
6666 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetNoListening() );
6668 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianCompression() );
6669 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetAsianKerning() );
6670 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianKerning() );
6672 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreter() );
6673 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreterTableOp() );
6674 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInDtorClear() );
6675 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsExpandRefs() );
6676 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInLinkUpdate() );
6678 SCTAB tab = m_pDoc->GetVisibleTab();
6680 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsVisible(tab) );
6681 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDefaultTabBgColor(tab) );
6682 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasTable(tab) );
6684 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsActiveScenario(tab) );
6685 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasCalcNotification(tab) );
6686 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasManualBreaks(tab) );
6689 void Test::checkPrecisionAsShown( OUString& rCode, double fValue, double fExpectedRoundVal )
6691 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
6692 sal_uInt32 nFormat = pFormatter->GetEntryKey( rCode );
6693 if ( nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
6695 sal_Int32 nCheckPos = 0;
6696 SvNumFormatType nType;
6697 pFormatter->PutEntry( rCode, nCheckPos, nType, nFormat );
6698 CPPUNIT_ASSERT_EQUAL( sal_Int32(0), nCheckPos );
6700 double fRoundValue = m_pDoc->RoundValueAsShown( fValue, nFormat );
6701 OString aMessage = "Format \"" +
6702 OUStringToOString( rCode, RTL_TEXTENCODING_ASCII_US ) +
6703 "\" is not correctly rounded";
6704 CPPUNIT_ASSERT_EQUAL_MESSAGE( aMessage.getStr(), fExpectedRoundVal, fRoundValue );
6707 CPPUNIT_TEST_FIXTURE(Test, testPrecisionAsShown)
6709 m_pDoc->InsertTab(0, u"Test"_ustr);
6711 // Turn on "precision as shown" option.
6712 setCalcAsShown( m_pDoc, true);
6714 OUString aCode;
6715 double fValue, fExpectedRoundVal;
6716 { // decimal rounding
6717 aCode = "0.00";
6718 fValue = 1.0/3.0;
6719 fExpectedRoundVal = 0.33;
6720 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6721 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6722 fValue = 10.001;
6723 fExpectedRoundVal = 10.0;
6724 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6725 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6727 { // thousand rounding tdf#106253
6728 aCode = "0,,";
6729 fValue = 4.0e9 / 7.0;
6730 fExpectedRoundVal = 571e6;
6731 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6732 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6733 aCode = "\"k\"[$$-409]* #,;[RED]-\"k\"[$$-409]* #,";
6734 fValue = 4.0e8 / 7.0;
6735 fExpectedRoundVal = 57.143e6;
6736 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6737 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6739 { // percent rounding
6740 aCode = "0.00%";
6741 fValue = 4.0 / 7.0;
6742 fExpectedRoundVal = 0.5714;
6743 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6744 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6745 fValue = 40.0 / 7.0;
6746 fExpectedRoundVal = 5.7143;
6747 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6748 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6750 { // scientific rounding
6751 aCode = "0.00E0";
6752 fValue = 400000.0 / 7.0;
6753 fExpectedRoundVal = 57100.0;
6754 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6755 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6756 fValue = 4.0 / 70000.0;
6757 fExpectedRoundVal = 5.71e-5;
6758 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6759 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6760 // engineering rounding tdf#106252
6761 aCode = "##0.000E0";
6762 fValue = 400000.0 / 7.0;
6763 fExpectedRoundVal = 57.143e3;
6764 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6765 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6766 fValue = 4000000.0 / 7.0;
6767 fExpectedRoundVal = 571.429e3;
6768 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6769 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6770 fValue = 40000000.0 / 7.0;
6771 fExpectedRoundVal = 5.714e6;
6772 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6773 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6774 fValue = 4.0 / 70000.0;
6775 fExpectedRoundVal = 57.143e-6;
6776 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6777 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6778 fValue = 4.0 / 7000.0;
6779 fExpectedRoundVal = 571.429e-6;
6780 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6781 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6782 fValue = 4.0 / 700.0;
6783 fExpectedRoundVal = 5.714e-3;
6784 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6785 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6786 aCode = "##?0.0#E0";
6787 fValue = 400000.0 / 7.0;
6788 fExpectedRoundVal = 5.71e4;
6789 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6790 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6791 fValue = 4000000.0 / 7.0;
6792 fExpectedRoundVal = 57.14e4;
6793 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6794 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6795 fValue = 40000000.0 / 7.0;
6796 fExpectedRoundVal = 571.43e4;
6797 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6798 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6799 fValue = 400000000.0 / 7.0;
6800 fExpectedRoundVal = 5714.29e4;
6801 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6802 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6803 fValue = 4.0 / 70000.0;
6804 fExpectedRoundVal = 5714.29e-8;
6805 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6806 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6807 fValue = 4.0 / 7000.0;
6808 fExpectedRoundVal = 5.71e-4;
6809 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6810 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6811 fValue = 4.0 / 700.0;
6812 fExpectedRoundVal = 57.14e-4;
6813 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6814 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6815 fValue = 4.0 / 70.0;
6816 fExpectedRoundVal = 571.43e-4;
6817 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6818 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6820 { // fraction rounding tdf#105657
6821 aCode = "# ?/?";
6822 fValue = 0.35;
6823 fExpectedRoundVal = 1.0/3.0;
6824 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6825 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6827 { // exact fraction
6828 aCode = "# ?/??";
6829 fValue = 0.35;
6830 fExpectedRoundVal = 0.35;
6831 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6832 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6834 { // several sub-formats tdf#106052
6835 aCode = "0.00;-0.000";
6836 fValue = 1.0/3.0;
6837 fExpectedRoundVal = 0.33;
6838 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6839 fValue = -1.0/3.0;
6840 fExpectedRoundVal = -0.333;
6841 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6844 setCalcAsShown( m_pDoc, false);
6845 m_pDoc->DeleteTab(0);
6848 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByRow)
6850 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6851 m_pDoc->InsertTab(0, u"Protected"_ustr);
6854 // Remove protected flags from rows 2-5.
6855 ScPatternAttr aAttr(m_pDoc->getCellAttributeHelper());
6856 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6857 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 4, 0, aAttr);
6859 // Protect the sheet without any options.
6860 ScTableProtection aProtect;
6861 aProtect.setProtected(true);
6862 m_pDoc->SetTabProtection(0, &aProtect);
6864 // Try to delete row 3. It should fail.
6865 ScRange aRow3(0,2,0,m_pDoc->MaxCol(),2,0);
6866 ScMarkData aMark(m_pDoc->GetSheetLimits());
6867 aMark.SelectOneTable(0);
6868 bool bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6869 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should fail.", !bDeleted);
6871 // Protect the sheet but allow row deletion.
6872 aProtect.setOption(ScTableProtection::DELETE_ROWS, true);
6873 m_pDoc->SetTabProtection(0, &aProtect);
6875 // Now we should be able to delete row 3.
6876 bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6877 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should succeed.", bDeleted);
6879 // But, row deletion should still fail on a protected row.
6880 ScRange aRow10(0,9,0,m_pDoc->MaxCol(),9,0);
6881 bDeleted = rDocFunc.DeleteCells(aRow10, &aMark, DelCellCmd::Rows, true);
6882 CPPUNIT_ASSERT_MESSAGE("deletion of row 10 should not be allowed.", !bDeleted);
6884 // Try inserting a new row. It should fail.
6885 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6886 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6888 // Allow row insertions.
6889 aProtect.setOption(ScTableProtection::INSERT_ROWS, true);
6890 m_pDoc->SetTabProtection(0, &aProtect);
6892 bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6893 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should succeed.", bInserted);
6895 // Row insertion is allowed even when the rows above and below have protected flags set.
6896 bInserted = rDocFunc.InsertCells(aRow10, &aMark, INS_INSROWS_AFTER, true, true);
6897 CPPUNIT_ASSERT_MESSAGE("row insertion at row 10 should succeed.", bInserted);
6900 m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
6903 // Insert matrix into B2:C3.
6904 ScMarkData aMark(m_pDoc->GetSheetLimits());
6905 aMark.SelectOneTable(1);
6906 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, u"={1;2|3;4}"_ustr);
6908 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6909 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6910 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6911 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6913 // Try to insert a row at row 3. It should fail because of matrix's presence.
6915 ScRange aRow3(0,2,1,m_pDoc->MaxCol(),2,1);
6916 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_BEFORE, true, true);
6917 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6920 m_pDoc->DeleteTab(1);
6921 m_pDoc->DeleteTab(0);
6924 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByColumn)
6926 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6927 m_pDoc->InsertTab(0, u"Protected"_ustr);
6930 // Remove protected flags from columns B to E.
6931 ScPatternAttr aAttr(m_pDoc->getCellAttributeHelper());
6932 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6933 m_pDoc->ApplyPatternAreaTab(1, 0, 4, m_pDoc->MaxRow(), 0, aAttr);
6935 // Protect the sheet without any options.
6936 ScTableProtection aProtect;
6937 aProtect.setProtected(true);
6938 m_pDoc->SetTabProtection(0, &aProtect);
6940 // Try to delete column C. It should fail.
6941 ScRange aCol3(2,0,0,2,m_pDoc->MaxRow(),0);
6942 ScMarkData aMark(m_pDoc->GetSheetLimits());
6943 aMark.SelectOneTable(0);
6944 bool bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6945 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should fail.", !bDeleted);
6947 // Protect the sheet but allow column deletion.
6948 aProtect.setOption(ScTableProtection::DELETE_COLUMNS, true);
6949 m_pDoc->SetTabProtection(0, &aProtect);
6951 // Now we should be able to delete column C.
6952 bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6953 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should succeed.", bDeleted);
6955 // But, column deletion should still fail on a protected column.
6956 ScRange aCol10(9,0,0,9,m_pDoc->MaxRow(),0);
6957 bDeleted = rDocFunc.DeleteCells(aCol10, &aMark, DelCellCmd::Cols, true);
6958 CPPUNIT_ASSERT_MESSAGE("deletion of column 10 should not be allowed.", !bDeleted);
6960 // Try inserting a new column. It should fail.
6961 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6962 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should fail.", !bInserted);
6964 // Allow column insertions.
6965 aProtect.setOption(ScTableProtection::INSERT_COLUMNS, true);
6966 m_pDoc->SetTabProtection(0, &aProtect);
6968 bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6969 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should succeed.", bInserted);
6971 // Column insertion is allowed even when the columns above and below have protected flags set.
6972 bInserted = rDocFunc.InsertCells(aCol10, &aMark, INS_INSCOLS_AFTER, true, true);
6973 CPPUNIT_ASSERT_MESSAGE("column insertion at column 10 should succeed.", bInserted);
6976 m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
6979 // Insert matrix into B2:C3.
6980 ScMarkData aMark(m_pDoc->GetSheetLimits());
6981 aMark.SelectOneTable(1);
6982 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, u"={1;2|3;4}"_ustr);
6984 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6985 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6986 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6987 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6989 // Try to insert a column at column C. It should fail because of matrix's presence.
6991 ScRange aCol3(2,0,1,2,m_pDoc->MaxRow(),1);
6992 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_BEFORE, true, true);
6993 CPPUNIT_ASSERT_MESSAGE("column insertion at column C should fail.", !bInserted);
6996 m_pDoc->DeleteTab(1);
6997 m_pDoc->DeleteTab(0);
7000 CPPUNIT_TEST_FIXTURE(Test, testInsertColumnsWithFormulaCells)
7002 m_pDoc->InsertTab(0, u"Tab1"_ustr);
7004 std::set<SCCOL> aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
7005 CPPUNIT_ASSERT_MESSAGE("empty sheet should contain no formula cells.", aCols.empty());
7007 auto equals = [](const std::set<SCCOL>& left, const std::set<SCCOL>& right)
7009 return left == right;
7012 // insert formula cells in columns 2, 4 and 6.
7013 m_pDoc->SetFormula(ScAddress(2, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
7014 m_pDoc->SetFormula(ScAddress(4, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
7015 m_pDoc->SetFormula(ScAddress(6, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
7017 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
7019 std::set<SCCOL> aExpected = { 2, 4, 6 };
7020 CPPUNIT_ASSERT_MESSAGE("Columns 2, 4 and 6 should contain formula cells.", equals(aExpected, aCols));
7022 // Insert 2 columns at column A to shift everything to right by 2.
7023 m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 0, 2);
7025 aExpected = { 4, 6, 8 };
7026 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
7027 CPPUNIT_ASSERT_MESSAGE("Columns 4, 6 and 8 should contain formula cells.", equals(aExpected, aCols));
7031 m_pDoc->CheckIntegrity(0);
7033 catch (const std::exception& e)
7035 std::ostringstream os;
7036 os << "document integrity check failed: " << e.what();
7037 CPPUNIT_FAIL(os.str());
7040 m_pDoc->DeleteTab(0);
7043 CPPUNIT_TEST_FIXTURE(Test, testDocumentModelAccessor_getDocumentCurrencies)
7045 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
7047 // Check document currencies - expect 0
7048 auto pAccessor = m_xDocShell->GetDocumentModelAccessor();
7049 CPPUNIT_ASSERT(pAccessor);
7050 CPPUNIT_ASSERT_EQUAL(size_t(0), pAccessor->getDocumentCurrencies().size());
7052 // Set a currency to a cell
7054 m_pDoc->SetValue(ScAddress(0, 0, 0), 2.0);
7056 OUString aCode = u"#.##0,00[$€-424]"_ustr;
7058 sal_Int32 nCheckPos;
7059 SvNumFormatType eType;
7060 sal_uInt32 nFormat;
7062 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
7063 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
7065 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
7066 SfxItemSet& rSet = aNewAttrs.GetItemSet();
7067 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
7068 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs); // A1.
7070 CPPUNIT_ASSERT_EQUAL(u"2,00€"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
7073 // Check document currencies again
7074 auto aCurrencyIDs = pAccessor->getDocumentCurrencies();
7075 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
7077 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
7078 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
7079 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
7081 // Set the same currency to 2 more cells
7083 m_pDoc->SetValue(ScAddress(1, 1, 0), 5.0);
7084 m_pDoc->SetValue(ScAddress(2, 2, 0), 7.0);
7086 OUString aCode = u"#.##0,00[$€-424]"_ustr;
7088 sal_Int32 nCheckPos;
7089 SvNumFormatType eType;
7090 sal_uInt32 nFormat;
7092 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
7093 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
7095 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
7096 SfxItemSet& rSet = aNewAttrs.GetItemSet();
7097 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
7098 m_pDoc->ApplyPattern(1, 1, 0, aNewAttrs); // B2.
7099 m_pDoc->ApplyPattern(2, 2, 0, aNewAttrs); // C3.
7101 CPPUNIT_ASSERT_EQUAL(u"5,00€"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0)));
7102 CPPUNIT_ASSERT_EQUAL(u"7,00€"_ustr, m_pDoc->GetString(ScAddress(2, 2, 0)));
7105 // Check document currencies again - should be 1 entry only
7106 aCurrencyIDs = pAccessor->getDocumentCurrencies();
7107 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
7109 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
7110 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
7111 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
7115 CPPUNIT_PLUGIN_IMPLEMENT();
7117 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */