Resolves: tdf#162093 TableRef item specifier may occur standalone
[LibreOffice.git] / sc / qa / unit / ucalc.cxx
blobae0d6967def821413ab78bec9de54ccb2497882b
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, testSharedStringPool)
116 m_pDoc->InsertTab(0, u"foo"_ustr);
118 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
119 size_t extraCount = rPool.getCount(); // internal items such as SharedString::getEmptyString()
120 size_t extraCountIgnoreCase = rPool.getCountIgnoreCase();
122 // Strings that are identical.
123 m_pDoc->SetString(ScAddress(0,0,0), o3tl::nonStaticString(u"Andy")); // A1
124 m_pDoc->SetString(ScAddress(0,1,0), o3tl::nonStaticString(u"Andy")); // A2
125 m_pDoc->SetString(ScAddress(0,2,0), o3tl::nonStaticString(u"Bruce")); // A3
126 m_pDoc->SetString(ScAddress(0,3,0), o3tl::nonStaticString(u"andy")); // A4
127 m_pDoc->SetString(ScAddress(0,4,0), o3tl::nonStaticString(u"BRUCE")); // A5
130 // These two shared string objects must go out of scope before the purge test.
131 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
132 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(0,1,0));
133 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS1.isValid());
134 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS2.isValid());
135 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
137 aSS2 = m_pDoc->GetSharedString(ScAddress(0,2,0));
138 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
140 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
141 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
143 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
144 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
146 // A3 and A5 should differ but should be equal case-insensitively.
147 aSS1 = m_pDoc->GetSharedString(ScAddress(0,2,0));
148 aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
149 CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
150 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
152 // A2 and A4 should be equal when ignoring cases.
153 aSS1 = m_pDoc->GetSharedString(ScAddress(0,1,0));
154 aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
155 CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
158 // Check the string counts after purging. Purging shouldn't remove any strings in this case.
159 rPool.purge();
160 CPPUNIT_ASSERT_EQUAL(5+extraCount, rPool.getCount());
161 CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, rPool.getCountIgnoreCase());
163 // Clear A1
164 clearRange(m_pDoc, ScAddress(0,0,0));
165 // Clear A2
166 clearRange(m_pDoc, ScAddress(0,1,0));
167 // Clear A3
168 clearRange(m_pDoc, ScAddress(0,2,0));
169 // Clear A4
170 clearRange(m_pDoc, ScAddress(0,3,0));
171 // Clear A5 and the pool should be completely empty.
172 clearRange(m_pDoc, ScAddress(0,4,0));
173 rPool.purge();
174 CPPUNIT_ASSERT_EQUAL(extraCount, rPool.getCount());
175 CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, rPool.getCountIgnoreCase());
177 // Now, compare string and edit text cells.
178 m_pDoc->SetString(ScAddress(0,0,0), "Andy and Bruce"); // A1 // [-loplugin:ostr]
179 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
180 rEE.SetTextCurrentDefaults(u"Andy and Bruce"_ustr);
182 ESelection aSel;
183 aSel.nStartPara = aSel.nEndPara = 0;
186 // Set 'Andy' bold.
187 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
188 aSel.nStartPos = 0;
189 aSel.nEndPos = 4;
190 SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT);
191 aItemSet.Put(aWeight);
192 rEE.QuickSetAttribs(aItemSet, aSel);
196 // Set 'Bruce' italic.
197 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
198 SvxPostureItem aItalic(ITALIC_NORMAL, EE_CHAR_ITALIC);
199 aItemSet.Put(aItalic);
200 aSel.nStartPos = 9;
201 aSel.nEndPos = 14;
202 rEE.QuickSetAttribs(aItemSet, aSel);
205 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject()); // B1
207 // These two should be equal.
208 svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
209 svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(1,0,0));
210 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
211 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
212 CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
214 rEE.SetTextCurrentDefaults(u"ANDY and BRUCE"_ustr);
215 m_pDoc->SetEditText(ScAddress(2,0,0), rEE.CreateTextObject()); // C1
216 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
217 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
218 CPPUNIT_ASSERT_MESSAGE("These two should be different when cases are considered.", aSS1.getData() != aSS2.getData());
220 // But they should be considered equal when cases are ignored.
221 aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
222 aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
223 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
224 CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
225 CPPUNIT_ASSERT_EQUAL(aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
227 m_pDoc->DeleteTab(0);
230 CPPUNIT_TEST_FIXTURE(Test, testSharedStringPoolUndoDoc)
232 struct
234 bool check( const ScDocument& rSrcDoc, ScDocument& rCopyDoc )
236 // Copy A1:A4 to the undo document.
237 for (SCROW i = 0; i <= 4; ++i)
239 ScAddress aPos(0,i,0);
240 rCopyDoc.SetString(aPos, rSrcDoc.GetString(aPos));
243 // String values in A1:A4 should have identical hash.
244 for (SCROW i = 0; i <= 4; ++i)
246 ScAddress aPos(0,i,0);
247 svl::SharedString aSS1 = rSrcDoc.GetSharedString(aPos);
248 svl::SharedString aSS2 = rCopyDoc.GetSharedString(aPos);
249 if (aSS1.getDataIgnoreCase() != aSS2.getDataIgnoreCase())
251 cerr << "String hash values are not equal at row " << (i+1)
252 << " for string '" << aSS1.getString() << "'" << endl;
253 return false;
257 return true;
260 } aTest;
262 m_pDoc->InsertTab(0, u"Test"_ustr);
264 m_pDoc->SetString(ScAddress(0,0,0), u"Header"_ustr);
265 m_pDoc->SetString(ScAddress(0,1,0), u"A1"_ustr);
266 m_pDoc->SetString(ScAddress(0,2,0), u"A2"_ustr);
267 m_pDoc->SetString(ScAddress(0,3,0), u"A3"_ustr);
269 ScDocument aUndoDoc(SCDOCMODE_UNDO);
270 aUndoDoc.InitUndo(*m_pDoc, 0, 0);
272 bool bSuccess = aTest.check(*m_pDoc, aUndoDoc);
273 CPPUNIT_ASSERT_MESSAGE("Check failed with undo document.", bSuccess);
275 // Test the clip document as well.
276 ScDocument aClipDoc(SCDOCMODE_CLIP);
277 aClipDoc.ResetClip(m_pDoc, static_cast<SCTAB>(0));
279 bSuccess = aTest.check(*m_pDoc, aClipDoc);
280 CPPUNIT_ASSERT_MESSAGE("Check failed with clip document.", bSuccess);
282 m_pDoc->DeleteTab(0);
285 CPPUNIT_TEST_FIXTURE(Test, testRangeList)
287 m_pDoc->InsertTab(0, u"foo"_ustr);
289 ScRangeList aRL;
290 aRL.push_back(ScRange(1,1,0,3,10,0));
291 CPPUNIT_ASSERT_EQUAL_MESSAGE("List should have one range.", size_t(1), aRL.size());
292 const ScRange* p = &aRL[0];
293 CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
294 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(1,1,0), p->aStart);
295 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(3,10,0), p->aEnd);
297 // TODO: Add more tests here.
299 m_pDoc->DeleteTab(0);
302 CPPUNIT_TEST_FIXTURE(Test, testMarkData)
304 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
306 // Empty mark. Nothing is selected.
307 std::vector<sc::ColRowSpan> aSpans = aMarkData.GetMarkedRowSpans();
308 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
309 aSpans = aMarkData.GetMarkedColSpans();
310 CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
312 // Select B3:F7.
313 aMarkData.SetMarkArea(ScRange(1,2,0,5,6,0));
314 aSpans = aMarkData.GetMarkedRowSpans();
315 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
316 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
317 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
319 aSpans = aMarkData.GetMarkedColSpans();
320 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
321 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(1), aSpans[0].mnStart);
322 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
324 // Select A11:B13.
325 aMarkData.SetMultiMarkArea(ScRange(0,10,0,1,12,0));
326 aSpans = aMarkData.GetMarkedRowSpans();
327 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 2 selected row spans.", size_t(2), aSpans.size());
328 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
329 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
330 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(10), aSpans[1].mnStart);
331 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[1].mnEnd);
333 aSpans = aMarkData.GetMarkedColSpans();
334 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
335 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
336 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
338 // Select C8:C10.
339 aMarkData.SetMultiMarkArea(ScRange(2,7,0,2,9,0));
340 aSpans = aMarkData.GetMarkedRowSpans();
341 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
342 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
343 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[0].mnEnd);
345 aSpans = aMarkData.GetMarkedColSpans();
346 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
347 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
348 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
351 CPPUNIT_TEST_FIXTURE(Test, testInput)
354 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
355 m_pDoc->InsertTab (0, u"foo"_ustr));
356 OUString test;
358 m_pDoc->SetString(0, 0, 0, u"'10.5"_ustr);
359 test = m_pDoc->GetString(0, 0, 0);
360 bool bTest = test == "10.5";
361 CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
362 m_pDoc->SetString(0, 0, 0, u"'apple'"_ustr);
363 test = m_pDoc->GetString(0, 0, 0);
364 bTest = test == "apple'";
365 CPPUNIT_ASSERT_MESSAGE("Text content should have the first apostrophe stripped.", bTest);
367 // Customized string handling policy.
368 ScSetStringParam aParam;
369 aParam.setTextInput();
370 m_pDoc->SetString(0, 0, 0, u"000123"_ustr, &aParam);
371 test = m_pDoc->GetString(0, 0, 0);
372 CPPUNIT_ASSERT_EQUAL_MESSAGE("Text content should have been treated as string, not number.", u"000123"_ustr, test);
374 m_pDoc->DeleteTab(0);
377 CPPUNIT_TEST_FIXTURE(Test, testColumnIterator) // tdf#118620
379 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
380 m_pDoc->InsertTab (0, u"foo"_ustr));
382 m_pDoc->SetString(0, 0, 0, u"'10.5"_ustr);
383 m_pDoc->SetString(0, m_pDoc->MaxRow()-5, 0, u"42.0"_ustr);
384 std::optional<sc::ColumnIterator> it = m_pDoc->GetColumnIterator(0, 0, m_pDoc->MaxRow() - 10, m_pDoc->MaxRow());
385 while (it->hasCell())
387 it->getCell();
388 it->next();
391 m_pDoc->DeleteTab(0);
394 CPPUNIT_TEST_FIXTURE(Test, testTdf66613)
396 // Create different print ranges and col/row repetitions for two tabs
397 const SCTAB nFirstTab = 0;
398 CPPUNIT_ASSERT(m_pDoc->InsertTab(nFirstTab, u"FirstPrintRange"_ustr));
399 ScRange aFirstPrintRange(0, 0, nFirstTab, 2, 2, nFirstTab);
400 m_pDoc->AddPrintRange(nFirstTab, aFirstPrintRange);
401 ScRange aFirstRepeatColRange(0, 0, nFirstTab, 0, 0, nFirstTab);
402 m_pDoc->SetRepeatColRange(nFirstTab, aFirstRepeatColRange);
403 ScRange aFirstRepeatRowRange(1, 1, nFirstTab, 1, 1, nFirstTab);
404 m_pDoc->SetRepeatRowRange(nFirstTab, aFirstRepeatRowRange);
406 const SCTAB nSecondTab = 1;
407 CPPUNIT_ASSERT(m_pDoc->InsertTab(nSecondTab, u"SecondPrintRange"_ustr));
408 ScRange aSecondPrintRange(0, 0, nSecondTab, 3, 3, nSecondTab);
409 m_pDoc->AddPrintRange(nSecondTab, aSecondPrintRange);
410 ScRange aSecondRepeatColRange(1, 1, nSecondTab, 1, 1, nSecondTab);
411 m_pDoc->SetRepeatColRange(nSecondTab, aSecondRepeatColRange);
412 ScRange aSecondRepeatRowRange(2, 2, nSecondTab, 2, 2, nSecondTab);
413 m_pDoc->SetRepeatRowRange(nSecondTab, aSecondRepeatRowRange);
415 // Transfer generated tabs to a new document with different order
416 ScDocument aScDocument;
417 aScDocument.TransferTab(*m_pDoc, nSecondTab, nFirstTab);
418 aScDocument.TransferTab(*m_pDoc, nFirstTab, nSecondTab);
420 // Check the number of print ranges in both documents
421 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nFirstTab));
422 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nSecondTab));
423 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nFirstTab));
424 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nSecondTab));
426 // Check the print ranges and col/row repetitions in both documents
427 CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *m_pDoc->GetPrintRange(nFirstTab, 0));
428 CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *m_pDoc->GetRepeatColRange(nFirstTab));
429 CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *m_pDoc->GetRepeatRowRange(nFirstTab));
430 CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *m_pDoc->GetPrintRange(nSecondTab, 0));
431 CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *m_pDoc->GetRepeatColRange(nSecondTab));
432 CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *m_pDoc->GetRepeatRowRange(nSecondTab));
434 // Tabs have to be adjusted since the order of the tabs is inverted in the new document
435 std::vector<ScRange*> aScRanges
436 = { &aFirstPrintRange, &aFirstRepeatColRange, &aFirstRepeatRowRange,
437 &aSecondPrintRange, &aSecondRepeatColRange, &aSecondRepeatRowRange };
438 for (size_t i = 0; i < aScRanges.size(); i++)
440 const SCTAB nTab = i >= 3 ? nFirstTab : nSecondTab;
441 aScRanges[i]->aStart.SetTab(nTab);
442 aScRanges[i]->aEnd.SetTab(nTab);
445 // Without the fix in place, no print ranges and col/row repetitions would be present
446 CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *aScDocument.GetPrintRange(nSecondTab, 0));
447 CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *aScDocument.GetRepeatColRange(nSecondTab));
448 CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *aScDocument.GetRepeatRowRange(nSecondTab));
449 CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *aScDocument.GetPrintRange(nFirstTab, 0));
450 CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *aScDocument.GetRepeatColRange(nFirstTab));
451 CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *aScDocument.GetRepeatRowRange(nFirstTab));
453 m_pDoc->DeleteTab(nFirstTab);
454 m_pDoc->DeleteTab(nSecondTab);
457 CPPUNIT_TEST_FIXTURE(Test, testTdf113027)
459 // Insert some sheets including a whitespace in their name and switch the grammar to R1C1
460 CPPUNIT_ASSERT(m_pDoc->InsertTab(0, u"Sheet 1"_ustr));
461 CPPUNIT_ASSERT(m_pDoc->InsertTab(1, u"Sheet 2"_ustr));
462 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
464 // Add a formula containing a remote reference, i.e., to another sheet
465 const ScAddress aScAddress(0, 0, 0);
466 static constexpr OUString aFormula = u"='Sheet 2'!RC"_ustr;
467 m_pDoc->SetString(aScAddress, aFormula);
469 // Switch from relative to absolute cell reference
470 ScRefFinder aFinder(aFormula, aScAddress, *m_pDoc, m_pDoc->GetAddressConvention());
471 aFinder.ToggleRel(0, aFormula.getLength());
473 // Without the fix in place, this test would have failed with
474 // - Expected: ='Sheet 2'!R1C1
475 // - Actual : ='Sheet 2'!RC
476 // i.e. the cell reference was not changed from relative to absolute
477 CPPUNIT_ASSERT_EQUAL(u"='Sheet 2'!R1C1"_ustr, aFinder.GetText());
479 m_pDoc->DeleteTab(0);
480 m_pDoc->DeleteTab(1);
483 CPPUNIT_TEST_FIXTURE(Test, testTdf90698)
485 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
486 m_pDoc->SetString(ScAddress(0,0,0), u"=(1;2)"_ustr);
488 // Without the fix in place, this would have failed with
489 // - Expected: =(1;2)
490 // - Actual : =(1~2)
491 OUString aFormula = m_pDoc->GetFormula(0,0,0);
492 CPPUNIT_ASSERT_EQUAL(u"=(1;2)"_ustr, aFormula);
494 m_pDoc->DeleteTab(0);
497 CPPUNIT_TEST_FIXTURE(Test, testTdf114406)
499 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
500 m_pDoc->SetString(ScAddress(0,0,0), u"5"_ustr);
501 m_pDoc->SetString(ScAddress(1,0,0), u"=A1/100%"_ustr);
503 // Without the fix in place, this would have failed with
504 // - Expected: =A1/100%
505 // - Actual : =A1/1
506 OUString aFormula = m_pDoc->GetFormula(1,0,0);
507 CPPUNIT_ASSERT_EQUAL(u"=A1/100%"_ustr, aFormula);
509 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,0,0)));
511 m_pDoc->DeleteTab(0);
514 CPPUNIT_TEST_FIXTURE(Test, testTdf93951)
516 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
517 m_pDoc->SetString(ScAddress(0,0,0), u"=2*§*2"_ustr);
519 OUString aFormula = m_pDoc->GetFormula(0,0,0);
521 // Without the fix in place, this test would have failed with
522 // - Expected: =2*§*2
523 // - Actual : =2*
524 CPPUNIT_ASSERT_EQUAL(u"=2*§*2"_ustr, aFormula);
526 m_pDoc->DeleteTab(0);
529 CPPUNIT_TEST_FIXTURE(Test, testTdf134490)
531 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
533 m_pDoc->SetString(ScAddress(0,0,0), u"--1"_ustr);
534 m_pDoc->SetString(ScAddress(0,1,0), u"---1"_ustr);
535 m_pDoc->SetString(ScAddress(0,2,0), u"+-1"_ustr);
536 m_pDoc->SetString(ScAddress(0,3,0), u"+--1"_ustr);
538 // Without the fix in place, this test would have failed with
539 // - Expected: --1
540 // - Actual : -1
541 CPPUNIT_ASSERT_EQUAL(u"--1"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
542 CPPUNIT_ASSERT_EQUAL(u"---1"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
543 CPPUNIT_ASSERT_EQUAL(u"+-1"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
544 CPPUNIT_ASSERT_EQUAL(u"+--1"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
546 m_pDoc->DeleteTab(0);
549 CPPUNIT_TEST_FIXTURE(Test, testTdf135249)
551 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, u"Test"_ustr));
553 m_pDoc->SetString(ScAddress(0,0,0), u"1:60"_ustr);
554 m_pDoc->SetString(ScAddress(0,1,0), u"1:123"_ustr);
555 m_pDoc->SetString(ScAddress(0,2,0), u"1:1:123"_ustr);
556 m_pDoc->SetString(ScAddress(0,3,0), u"0:123"_ustr);
557 m_pDoc->SetString(ScAddress(0,4,0), u"0:0:123"_ustr);
558 m_pDoc->SetString(ScAddress(0,5,0), u"0:123:59"_ustr);
560 // These are not valid duration inputs
561 CPPUNIT_ASSERT_EQUAL(u"1:60"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
562 CPPUNIT_ASSERT_EQUAL(u"1:123"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
563 CPPUNIT_ASSERT_EQUAL(u"1:1:123"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
565 // These are valid duration inputs
566 // Without the fix in place, this test would have failed with
567 // - Expected: 02:03:00 AM
568 // - Actual : 0:123
569 CPPUNIT_ASSERT_EQUAL(u"02:03:00 AM"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
570 CPPUNIT_ASSERT_EQUAL(u"12:02:03 AM"_ustr, m_pDoc->GetString(ScAddress(0,4,0)));
571 CPPUNIT_ASSERT_EQUAL(u"02:03:59 AM"_ustr, m_pDoc->GetString(ScAddress(0,5,0)));
573 m_pDoc->DeleteTab(0);
576 CPPUNIT_TEST_FIXTURE(Test, testDocStatistics)
578 SCTAB nStartTabs = m_pDoc->GetTableCount();
579 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
580 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
581 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
582 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
583 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
584 static_cast<SCTAB>(nStartTabs+2), m_pDoc->GetTableCount());
586 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetCellCount());
587 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
588 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetCellCount());
589 m_pDoc->SetValue(ScAddress(2,2,0), 2.5);
590 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetCellCount());
591 m_pDoc->SetString(ScAddress(1,1,1), u"Test"_ustr);
592 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(3), m_pDoc->GetCellCount());
594 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(0), m_pDoc->GetFormulaGroupCount());
595 m_pDoc->SetString(ScAddress(3,0,1), u"=A1"_ustr);
596 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
597 m_pDoc->SetString(ScAddress(3,1,1), u"=A2"_ustr);
598 m_pDoc->SetString(ScAddress(3,2,1), u"=A3"_ustr);
599 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
600 m_pDoc->SetString(ScAddress(3,3,1), u"=A5"_ustr);
601 m_pDoc->SetString(ScAddress(3,4,1), u"=A6"_ustr);
602 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetFormulaGroupCount());
603 m_pDoc->SetString(ScAddress(3,1,1), u"=A3"_ustr);
604 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(4), m_pDoc->GetFormulaGroupCount());
606 m_pDoc->DeleteTab(1);
607 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to decrement sheet count.",
608 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
609 m_pDoc->DeleteTab(0); // This may fail in case there is only one sheet in the document.
612 CPPUNIT_TEST_FIXTURE(Test, testRowForHeight)
614 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
615 m_pDoc->SetRowHeightRange( 0, 9, 0, 100);
616 m_pDoc->SetRowHeightRange(10, 19, 0, 200);
617 m_pDoc->SetRowHeightRange(20, 29, 0, 300);
619 // Hide some rows.
620 m_pDoc->SetRowHidden(3, 5, 0, true);
621 m_pDoc->SetRowHidden(8, 12, 0, true);
623 struct Check
625 tools::Long nHeight;
626 SCROW nRow;
629 std::vector<Check> aChecks = {
630 { 1, 1 },
631 { 99, 1 },
632 { 120, 2 },
633 { 330, 7 },
634 { 420, 13 },
635 { 780, 15 },
636 { 1860, 20 },
637 { 4020, 28 },
640 for (const Check& rCheck : aChecks)
642 SCROW nRow = m_pDoc->GetRowForHeight(0, rCheck.nHeight);
643 CPPUNIT_ASSERT_EQUAL(rCheck.nRow, nRow);
647 CPPUNIT_TEST_FIXTURE(Test, testDataEntries)
650 * The 'data entries' data is a list of strings used for suggestions as
651 * the user types in new cell value.
653 m_pDoc->InsertTab(0, u"Test"_ustr);
655 m_pDoc->SetString(ScAddress(0,5,0), u"Andy"_ustr);
656 m_pDoc->SetString(ScAddress(0,6,0), u"Bruce"_ustr);
657 m_pDoc->SetString(ScAddress(0,7,0), u"Charlie"_ustr);
658 m_pDoc->SetString(ScAddress(0,10,0), u"Andy"_ustr);
660 std::vector<ScTypedStrData> aEntries;
661 m_pDoc->GetDataEntries(0, 0, 0, aEntries); // Try at the very top.
663 // Entries are supposed to be sorted in ascending order, and are all unique.
664 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
665 std::vector<ScTypedStrData>::const_iterator it = aEntries.begin();
666 CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
667 ++it;
668 CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
669 ++it;
670 CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
671 ++it;
672 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
674 aEntries.clear();
675 m_pDoc->GetDataEntries(0, m_pDoc->MaxRow(), 0, aEntries); // Try at the very bottom.
676 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
678 // Make sure we get the same set of suggestions.
679 it = aEntries.begin();
680 CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
681 ++it;
682 CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
683 ++it;
684 CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
685 ++it;
686 CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
688 m_pDoc->DeleteTab(0);
691 CPPUNIT_TEST_FIXTURE(Test, testSelectionFunction)
694 * Selection function is responsible for displaying quick calculation
695 * results in the status bar.
697 m_pDoc->InsertTab(0, u"Test"_ustr);
699 // Insert values into B2:B4.
700 m_pDoc->SetString(ScAddress(1,1,0), u"=1"_ustr); // formula
701 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
702 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
704 // Insert strings into B5:B8.
705 m_pDoc->SetString(ScAddress(1,4,0), u"A"_ustr);
706 m_pDoc->SetString(ScAddress(1,5,0), u"B"_ustr);
707 m_pDoc->SetString(ScAddress(1,6,0), u"=\"C\""_ustr); // formula
708 m_pDoc->SetString(ScAddress(1,7,0), u"D"_ustr);
710 // Insert values into D2:D4.
711 m_pDoc->SetValue(ScAddress(3,1,0), 4.0);
712 m_pDoc->SetValue(ScAddress(3,2,0), 5.0);
713 m_pDoc->SetValue(ScAddress(3,3,0), 6.0);
715 // Insert edit text into D5.
716 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
717 rEE.SetTextCurrentDefaults(u"Rich Text"_ustr);
718 m_pDoc->SetEditText(ScAddress(3,4,0), rEE.CreateTextObject());
720 // Insert Another string into D6.
721 m_pDoc->SetString(ScAddress(3,5,0), u"E"_ustr);
723 // Select B2:B8 & D2:D8 disjoint region.
724 ScRangeList aRanges;
725 aRanges.push_back(ScRange(1,1,0,1,7,0)); // B2:B8
726 aRanges.push_back(ScRange(3,1,0,3,7,0)); // D2:D8
727 ScMarkData aMark(m_pDoc->GetSheetLimits());
728 aMark.MarkFromRangeList(aRanges, true);
730 struct Check
732 ScSubTotalFunc meFunc;
733 double mfExpected;
737 static const Check aChecks[] =
739 { SUBTOTAL_FUNC_AVE, 3.5 },
740 { SUBTOTAL_FUNC_CNT2, 12.0 },
741 { SUBTOTAL_FUNC_CNT, 6.0 },
742 { SUBTOTAL_FUNC_MAX, 6.0 },
743 { SUBTOTAL_FUNC_MIN, 1.0 },
744 { SUBTOTAL_FUNC_SUM, 21.0 },
745 { SUBTOTAL_FUNC_SELECTION_COUNT, 14.0 }
748 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
750 double fRes = 0.0;
751 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
752 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
753 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
757 // Hide rows 4 and 6 and check the results again.
759 m_pDoc->SetRowHidden(3, 3, 0, true);
760 m_pDoc->SetRowHidden(5, 5, 0, true);
761 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(3, 0));
762 CPPUNIT_ASSERT_MESSAGE("This row should be hidden.", m_pDoc->RowHidden(5, 0));
765 static const Check aChecks[] =
767 { SUBTOTAL_FUNC_AVE, 3.0 },
768 { SUBTOTAL_FUNC_CNT2, 8.0 },
769 { SUBTOTAL_FUNC_CNT, 4.0 },
770 { SUBTOTAL_FUNC_MAX, 5.0 },
771 { SUBTOTAL_FUNC_MIN, 1.0 },
772 { SUBTOTAL_FUNC_SUM, 12.0 },
773 { SUBTOTAL_FUNC_SELECTION_COUNT, 10.0 }
776 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
778 double fRes = 0.0;
779 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, ScAddress(), aMark, fRes);
780 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
781 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
785 // Make sure that when no selection is present, use the current cursor position.
786 ScMarkData aEmpty(m_pDoc->GetSheetLimits());
789 // D3 (numeric cell containing 5.)
790 ScAddress aPos(3, 2, 0);
792 static const Check aChecks[] =
794 { SUBTOTAL_FUNC_AVE, 5.0 },
795 { SUBTOTAL_FUNC_CNT2, 1.0 },
796 { SUBTOTAL_FUNC_CNT, 1.0 },
797 { SUBTOTAL_FUNC_MAX, 5.0 },
798 { SUBTOTAL_FUNC_MIN, 5.0 },
799 { SUBTOTAL_FUNC_SUM, 5.0 },
800 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
803 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
805 double fRes = 0.0;
806 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
807 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
808 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
813 // B7 (string formula cell containing ="C".)
814 ScAddress aPos(1, 6, 0);
816 static const Check aChecks[] =
818 { SUBTOTAL_FUNC_CNT2, 1.0 },
819 { SUBTOTAL_FUNC_SELECTION_COUNT, 1.0 }
822 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
824 double fRes = 0.0;
825 bool bRes = m_pDoc->GetSelectionFunction(aChecks[i].meFunc, aPos, aEmpty, fRes);
826 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
827 CPPUNIT_ASSERT_EQUAL(aChecks[i].mfExpected, fRes);
831 // Calculate function across selected sheets.
832 clearSheet(m_pDoc, 0);
833 m_pDoc->InsertTab(1, u"Test2"_ustr);
834 m_pDoc->InsertTab(2, u"Test3"_ustr);
836 // Set values at B2 and C3 on each sheet.
837 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
838 m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
839 m_pDoc->SetValue(ScAddress(1,1,1), 4.0);
840 m_pDoc->SetValue(ScAddress(2,2,1), 8.0);
841 m_pDoc->SetValue(ScAddress(1,1,2), 16.0);
842 m_pDoc->SetValue(ScAddress(2,2,2), 32.0);
844 // Mark B2 and C3 on first sheet.
845 aRanges.RemoveAll();
846 aRanges.push_back(ScRange(1,1,0)); // B2
847 aRanges.push_back(ScRange(2,2,0)); // C3
848 aMark.MarkFromRangeList(aRanges, true);
849 // Additionally select third sheet.
850 aMark.SelectTable(2, true);
853 double fRes = 0.0;
854 bool bRes = m_pDoc->GetSelectionFunction( SUBTOTAL_FUNC_SUM, ScAddress(), aMark, fRes);
855 CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
856 CPPUNIT_ASSERT_EQUAL_MESSAGE("1+2+16+32=", 51.0, fRes);
859 m_pDoc->DeleteTab(2);
860 m_pDoc->DeleteTab(1);
861 m_pDoc->DeleteTab(0);
864 CPPUNIT_TEST_FIXTURE(Test, testMarkedCellIteration)
866 m_pDoc->InsertTab(0, u"Test"_ustr);
868 // Insert cells to A1, A5, B2 and C3.
869 m_pDoc->SetString(ScAddress(0,0,0), u"California"_ustr);
870 m_pDoc->SetValue(ScAddress(0,4,0), 1.2);
871 m_pDoc->SetEditText(ScAddress(1,1,0), u"Boston"_ustr);
872 m_pDoc->SetFormula(ScAddress(2,2,0), u"=SUM(1,2,3)"_ustr, m_pDoc->GetGrammar());
874 // Select A1:C5.
875 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
876 aMarkData.SetMarkArea(ScRange(0,0,0,2,4,0));
877 aMarkData.MarkToMulti(); // TODO : we shouldn't have to do this.
879 struct Check
881 SCCOL mnCol;
882 SCROW mnRow;
885 const std::vector<Check> aChecks = {
886 { 0, 0 }, // A1
887 { 0, 4 }, // A5
888 { 1, 1 }, // B2
889 { 2, 2 }, // C3
892 SCROW nRow = -1; // Start from the imaginary row before A1.
893 SCCOL nCol = 0;
895 for (const Check& rCheck : aChecks)
897 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
898 if (!bFound)
900 std::ostringstream os;
901 os << ScAddress(rCheck.mnCol, rCheck.mnRow, 0).GetColRowString() << " was expected, but not found.";
902 CPPUNIT_FAIL(os.str());
905 CPPUNIT_ASSERT_EQUAL(rCheck.mnRow, nRow);
906 CPPUNIT_ASSERT_EQUAL(rCheck.mnCol, nCol);
909 // No more marked cells on this sheet.
910 bool bFound = m_pDoc->GetNextMarkedCell(nCol, nRow, 0, aMarkData);
911 CPPUNIT_ASSERT(!bFound);
913 m_pDoc->DeleteTab(0);
916 CPPUNIT_TEST_FIXTURE(Test, testCopyToDocument)
918 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"src"_ustr));
920 // We need a drawing layer in order to create caption objects.
921 m_pDoc->InitDrawLayer(m_xDocShell.get());
923 m_pDoc->SetString(0, 0, 0, u"Header"_ustr);
924 m_pDoc->SetString(0, 1, 0, u"1"_ustr);
925 m_pDoc->SetString(0, 2, 0, u"2"_ustr);
926 m_pDoc->SetString(0, 3, 0, u"3"_ustr);
927 m_pDoc->SetString(0, 4, 0, u"=4/2"_ustr);
928 m_pDoc->CalcAll();
930 //note on A1
931 ScAddress aAdrA1 (0, 0, 0); // numerical cell content
932 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
933 pNote->SetText(aAdrA1, u"Hello world in A1"_ustr);
935 // Copy statically to another document.
937 ScDocShellRef xDocSh2;
938 getNewDocShell(xDocSh2);
939 ScDocument* pDestDoc = &xDocSh2->GetDocument();
940 pDestDoc->InsertTab(0, u"src"_ustr);
941 pDestDoc->InitDrawLayer(xDocSh2.get()); // for note caption objects
943 m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, *pDestDoc); // Copy A2:A4
944 m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, *pDestDoc); // Copy A1
945 m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, *pDestDoc); // Copy A5:A8
947 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), pDestDoc->GetString(0,0,0));
948 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), pDestDoc->GetString(0,1,0));
949 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), pDestDoc->GetString(0,2,0));
950 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), pDestDoc->GetString(0,3,0));
951 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), pDestDoc->GetString(0,4,0));
953 // verify note
954 CPPUNIT_ASSERT_MESSAGE("There should be a note in A1 destDocument", pDestDoc->HasNote(ScAddress(0, 0, 0)));
955 CPPUNIT_ASSERT_EQUAL_MESSAGE("The notes content should be the same on both documents",
956 m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText(), pDestDoc->GetNote(ScAddress(0, 0, 0))->GetText());
958 pDestDoc->DeleteTab(0);
959 xDocSh2->DoClose();
960 xDocSh2.clear();
962 m_pDoc->DeleteTab(0);
965 bool Test::checkHorizontalIterator(ScDocument& rDoc, const std::vector<std::vector<const char*>>& rData, const HoriIterCheck* pChecks, size_t nCheckCount)
967 ScAddress aPos(0,0,0);
968 insertRangeData(&rDoc, aPos, rData);
969 ScHorizontalCellIterator aIter(rDoc, 0, 0, 0, 1, rData.size() - 1);
971 SCCOL nCol;
972 SCROW nRow;
973 size_t i = 0;
974 for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
976 if (i >= nCheckCount)
978 cerr << "hit invalid check " << i << " of " << nCheckCount << endl;
979 CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
980 return false;
983 if (pChecks[i].nCol != nCol)
985 cerr << "Column mismatch " << pChecks[i].nCol << " vs. " << nCol << endl;
986 return false;
989 if (pChecks[i].nRow != nRow)
991 cerr << "Row mismatch " << pChecks[i].nRow << " vs. " << nRow << endl;
992 return false;
995 if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString(&rDoc))
997 cerr << "String mismatch " << pChecks[i].pVal << " vs. " <<
998 pCell->getString(&rDoc) << endl;
999 return false;
1003 return true;
1006 CPPUNIT_TEST_FIXTURE(Test, testHorizontalIterator)
1008 m_pDoc->InsertTab(0, u"test"_ustr);
1011 // Raw data - mixed types
1012 std::vector<std::vector<const char*>> aData = {
1013 { "A", "B" },
1014 { "C", "1" },
1015 { "D", "2" },
1016 { "E", "3" }
1019 static const HoriIterCheck aChecks[] = {
1020 { 0, 0, "A" },
1021 { 1, 0, "B" },
1022 { 0, 1, "C" },
1023 { 1, 1, "1" },
1024 { 0, 2, "D" },
1025 { 1, 2, "2" },
1026 { 0, 3, "E" },
1027 { 1, 3, "3" },
1030 bool bRes = checkHorizontalIterator(
1031 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1033 if (!bRes)
1034 CPPUNIT_FAIL("Failed on test mixed.");
1038 // Raw data - 'hole' data
1039 std::vector<std::vector<const char*>> aData = {
1040 { "A", "B" },
1041 { "C", nullptr },
1042 { "D", "E" },
1045 static const HoriIterCheck aChecks[] = {
1046 { 0, 0, "A" },
1047 { 1, 0, "B" },
1048 { 0, 1, "C" },
1049 { 0, 2, "D" },
1050 { 1, 2, "E" },
1053 bool bRes = checkHorizontalIterator(
1054 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1056 if (!bRes)
1057 CPPUNIT_FAIL("Failed on test hole.");
1061 // Very holy data
1062 std::vector<std::vector<const char*>> aData = {
1063 { nullptr, "A" },
1064 { nullptr, nullptr },
1065 { nullptr, "1" },
1066 { "B", nullptr },
1067 { "C", "2" },
1068 { "D", "3" },
1069 { "E", nullptr },
1070 { nullptr, "G" },
1071 { nullptr, nullptr },
1074 static const HoriIterCheck aChecks[] = {
1075 { 1, 0, "A" },
1076 { 1, 2, "1" },
1077 { 0, 3, "B" },
1078 { 0, 4, "C" },
1079 { 1, 4, "2" },
1080 { 0, 5, "D" },
1081 { 1, 5, "3" },
1082 { 0, 6, "E" },
1083 { 1, 7, "G" },
1086 bool bRes = checkHorizontalIterator(
1087 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1089 if (!bRes)
1090 CPPUNIT_FAIL("Failed on test holy.");
1094 // Degenerate case
1095 std::vector<std::vector<const char*>> aData = {
1096 { nullptr, nullptr },
1097 { nullptr, nullptr },
1098 { nullptr, nullptr },
1101 bool bRes = checkHorizontalIterator(
1102 *m_pDoc, aData, nullptr, 0);
1104 if (!bRes)
1105 CPPUNIT_FAIL("Failed on test degenerate.");
1109 // Data at end
1110 std::vector<std::vector<const char*>> aData = {
1111 { nullptr, nullptr },
1112 { nullptr, nullptr },
1113 { nullptr, "A" },
1116 static const HoriIterCheck aChecks[] = {
1117 { 1, 2, "A" },
1120 bool bRes = checkHorizontalIterator(
1121 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1123 if (!bRes)
1124 CPPUNIT_FAIL("Failed on test at end.");
1128 // Data in middle
1129 std::vector<std::vector<const char*>> aData = {
1130 { nullptr, nullptr },
1131 { nullptr, nullptr },
1132 { nullptr, "A" },
1133 { nullptr, "1" },
1134 { nullptr, nullptr },
1137 static const HoriIterCheck aChecks[] = {
1138 { 1, 2, "A" },
1139 { 1, 3, "1" },
1142 bool bRes = checkHorizontalIterator(
1143 *m_pDoc, aData, aChecks, SAL_N_ELEMENTS(aChecks));
1145 if (!bRes)
1146 CPPUNIT_FAIL("Failed on test in middle.");
1149 m_pDoc->DeleteTab(0);
1152 CPPUNIT_TEST_FIXTURE(Test, testValueIterator)
1154 m_pDoc->InsertTab(0, u"Test"_ustr);
1156 // Turn on "precision as shown" option.
1157 ScDocOptions aOpt = m_pDoc->GetDocOptions();
1158 aOpt.SetCalcAsShown(true);
1159 m_pDoc->SetDocOptions(aOpt);
1161 ScInterpreterContext aContext(*m_pDoc, m_pDoc->GetFormatTable());
1163 // Purely horizontal data layout with numeric data.
1164 for (SCCOL i = 1; i <= 3; ++i)
1165 m_pDoc->SetValue(ScAddress(i,2,0), i);
1168 const double aChecks[] = { 1.0, 2.0, 3.0 };
1169 size_t const nCheckLen = SAL_N_ELEMENTS(aChecks);
1170 ScValueIterator aIter(aContext, ScRange(1,2,0,3,2,0));
1171 bool bHas = false;
1172 size_t nCheckPos = 0;
1173 double fVal;
1174 FormulaError nErr;
1175 for (bHas = aIter.GetFirst(fVal, nErr); bHas; bHas = aIter.GetNext(fVal, nErr), ++nCheckPos)
1177 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1178 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos], fVal);
1179 CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(nErr));
1183 m_pDoc->DeleteTab(0);
1186 CPPUNIT_TEST_FIXTURE(Test, testHorizontalAttrIterator)
1188 m_pDoc->InsertTab(0, u"Test"_ustr);
1190 // Set the background color of B2:C3,D2,E3,C4:D4,B5:D5 to blue
1191 ScPatternAttr aCellBackColor(m_pDoc->getCellAttributeHelper());
1192 aCellBackColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
1193 m_pDoc->ApplyPatternAreaTab(1, 1, 2, 2, 0, aCellBackColor);
1194 m_pDoc->ApplyPatternAreaTab(3, 1, 3, 1, 0, aCellBackColor);
1195 m_pDoc->ApplyPatternAreaTab(4, 2, 4, 2, 0, aCellBackColor);
1196 m_pDoc->ApplyPatternAreaTab(2, 3, 3, 3, 0, aCellBackColor);
1197 m_pDoc->ApplyPatternAreaTab(1, 4, 4, 4, 0, aCellBackColor);
1199 // some numeric data
1200 for (SCCOL i = 1; i <= 4; ++i)
1201 for (SCROW j = 1; j <= 4; ++j)
1202 m_pDoc->SetValue(ScAddress(i,j,0), i*10+j);
1205 const int aChecks[][3] = { {1, 3, 1}, {1, 2, 2}, {4, 4, 2}, {2, 3, 3}, {1, 4, 4} };
1206 const size_t nCheckLen = SAL_N_ELEMENTS(aChecks);
1208 ScHorizontalAttrIterator aIter(*m_pDoc, 0, 0, 0, 5, 5);
1209 SCCOL nCol1, nCol2;
1210 SCROW nRow;
1211 size_t nCheckPos = 0;
1212 for (const ScPatternAttr* pAttr = aIter.GetNext(nCol1, nCol2, nRow); pAttr; pAttr = aIter.GetNext(nCol1, nCol2, nRow))
1214 if (pAttr->isDefault())
1215 continue;
1216 CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", nCheckPos < nCheckLen);
1217 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][0], static_cast<int>(nCol1));
1218 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][1], static_cast<int>(nCol2));
1219 CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][2], static_cast<int>(nRow));
1220 ++nCheckPos;
1224 m_pDoc->DeleteTab(0);
1227 CPPUNIT_TEST_FIXTURE(Test, testIteratorsUnallocatedColumnsAttributes)
1229 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1231 // Set values in first two columns, to ensure allocation of those columns.
1232 m_pDoc->SetValue(ScAddress(0,1,0), 1);
1233 m_pDoc->SetValue(ScAddress(1,1,0), 2);
1234 constexpr SCCOL allocatedColsCount = 2;
1235 assert( allocatedColsCount >= INITIALCOLCOUNT );
1236 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1238 // Make entire second row and third row bold.
1239 ScPatternAttr boldAttr(m_pDoc->getCellAttributeHelper());
1240 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1241 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 2, 0, boldAttr);
1243 // That shouldn't need allocating more columns, just changing the default attribute.
1244 CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
1245 vcl::Font aFont;
1246 const ScPatternAttr* pattern = m_pDoc->GetPattern(m_pDoc->MaxCol(), 1, 0);
1247 pattern->fillFontOnly(aFont);
1248 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight());
1250 // Test iterators.
1251 ScDocAttrIterator docit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1252 SCCOL col1, col2;
1253 SCROW row1, row2;
1254 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1255 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1256 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1257 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1258 CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
1259 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col1 );
1260 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1261 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1262 CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == nullptr );
1264 ScAttrRectIterator rectit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1265 CPPUNIT_ASSERT_EQUAL( pattern, rectit.GetNext( col1, col2, row1, row2 ));
1266 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1267 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1268 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1269 CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
1270 CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1272 ScHorizontalAttrIterator horit( *m_pDoc, 0, allocatedColsCount - 1, 1, allocatedColsCount, 2 );
1273 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1274 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1275 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1276 CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
1277 CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
1278 CPPUNIT_ASSERT_EQUAL( SCCOL(allocatedColsCount - 1), col1 );
1279 CPPUNIT_ASSERT_EQUAL( allocatedColsCount, col2 );
1280 CPPUNIT_ASSERT_EQUAL( SCROW(2), row1 );
1281 CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == nullptr );
1283 m_pDoc->DeleteTab(0);
1286 CPPUNIT_TEST_FIXTURE(Test, testIteratorsDefPattern)
1288 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1290 // The default pattern is the default style, which can be edited by the user.
1291 // As such iterators should not ignore it by default, because it might contain
1292 // some attributes set.
1294 // Set cells as bold, default allocated, bold, default unallocated.
1295 SCCOL firstCol = 100;
1296 SCCOL lastCol = 103;
1297 ScPatternAttr boldAttr(m_pDoc->getCellAttributeHelper());
1298 boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
1299 m_pDoc->ApplyPattern(100, 0, 0, boldAttr);
1300 m_pDoc->ApplyPattern(102, 0, 0, boldAttr);
1302 CPPUNIT_ASSERT_EQUAL(SCCOL(102 + 1), m_pDoc->GetAllocatedColumnsCount(0));
1303 const ScPatternAttr* pattern = m_pDoc->GetPattern(100, 0, 0);
1304 const ScPatternAttr* defPattern(&m_pDoc->getCellAttributeHelper().getDefaultCellAttribute()); //GetDefPattern();
1305 CPPUNIT_ASSERT(!ScPatternAttr::areSame(pattern, defPattern));
1306 CPPUNIT_ASSERT_EQUAL(pattern, m_pDoc->GetPattern(102, 0, 0));
1307 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(101, 0, 0));
1308 CPPUNIT_ASSERT_EQUAL(defPattern, m_pDoc->GetPattern(103, 0, 0));
1310 // Test iterators.
1311 ScDocAttrIterator docit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1312 SCCOL col1, col2;
1313 SCROW row1, row2;
1314 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1315 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1316 CPPUNIT_ASSERT_EQUAL(pattern, docit.GetNext( col1, row1, row2 ));
1317 CPPUNIT_ASSERT_EQUAL(defPattern, docit.GetNext( col1, row1, row2 ));
1318 CPPUNIT_ASSERT(docit.GetNext( col1, row1, row2 ) == nullptr );
1320 ScAttrRectIterator rectit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1321 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1322 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1323 CPPUNIT_ASSERT_EQUAL(pattern, rectit.GetNext( col1, col2, row1, row2 ));
1324 CPPUNIT_ASSERT_EQUAL(defPattern, rectit.GetNext( col1, col2, row1, row2 ));
1325 CPPUNIT_ASSERT(rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
1327 ScHorizontalAttrIterator horit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
1328 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1329 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1330 CPPUNIT_ASSERT_EQUAL(pattern, horit.GetNext( col1, col2, row1 ));
1331 CPPUNIT_ASSERT_EQUAL(defPattern, horit.GetNext( col1, col2, row1 ));
1332 CPPUNIT_ASSERT(horit.GetNext( col1, col2, row1 ) == nullptr );
1334 m_pDoc->DeleteTab(0);
1337 CPPUNIT_TEST_FIXTURE(Test, testLastChangedColFlagsWidth)
1339 m_pDoc->InsertTab(0, u"Tab1"_ustr);
1341 constexpr SCCOL firstChangedCol = 100;
1342 assert( firstChangedCol > m_pDoc->GetAllocatedColumnsCount(0));
1343 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1344 for( SCCOL col = firstChangedCol; col <= m_pDoc->MaxCol(); ++col )
1345 m_pDoc->SetColWidth( col, 0, 10 );
1347 // That shouldn't need allocating more columns, just changing column flags.
1348 CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0));
1349 // But the flags are changed.
1350 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), m_pDoc->GetLastChangedColFlagsWidth(0));
1352 m_pDoc->DeleteTab(0);
1355 namespace {
1357 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
1359 const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
1360 if (pBC)
1362 cerr << "Broadcaster shouldn't be here." << endl;
1363 return false;
1366 pBC = rDoc.GetBroadcaster(rTo);
1367 if (!pBC)
1369 cerr << "Broadcaster should be here." << endl;
1370 return false;
1372 return true;
1375 formula::FormulaToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
1377 ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
1378 if (!pFC)
1380 cerr << "Formula cell expected, but not found." << endl;
1381 return nullptr;
1384 ScTokenArray* pTokens = pFC->GetCode();
1385 if (!pTokens)
1387 cerr << "Token array is not present." << endl;
1388 return nullptr;
1391 formula::FormulaToken* pToken = pTokens->FirstToken();
1392 if (!pToken || pToken->GetType() != formula::svSingleRef)
1394 cerr << "Not a single reference token." << endl;
1395 return nullptr;
1398 return pToken;
1401 bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCCOL nRelCol, SCROW nRelRow)
1403 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1404 if (!pToken)
1405 return false;
1407 ScSingleRefData& rRef = *pToken->GetSingleRef();
1408 if (!rRef.IsColRel() || rRef.Col() != nRelCol)
1410 cerr << "Unexpected relative column address." << endl;
1411 return false;
1414 if (!rRef.IsRowRel() || rRef.Row() != nRelRow)
1416 cerr << "Unexpected relative row address." << endl;
1417 return false;
1420 return true;
1423 bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
1425 formula::FormulaToken* pToken = getSingleRefToken(rDoc, rPos);
1426 if (!pToken)
1427 return false;
1429 ScSingleRefData& rRef = *pToken->GetSingleRef();
1430 if (!rRef.IsDeleted())
1432 cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
1433 return false;
1436 return true;
1441 CPPUNIT_TEST_FIXTURE(Test, testCellBroadcaster)
1444 * More direct test for cell broadcaster management, used to track formula
1445 * dependencies.
1447 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"foo"_ustr));
1449 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1450 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1.
1451 double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
1452 CPPUNIT_ASSERT_EQUAL(0.0, val);
1454 const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1455 CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
1457 // Change the value of A1 and make sure that B1 follows.
1458 m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
1459 val = m_pDoc->GetValue(ScAddress(1,0,0));
1460 CPPUNIT_ASSERT_EQUAL(1.23, val);
1462 // Move column A down 5 cells. Make sure B1 now references A6, not A1.
1463 m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
1464 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1465 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
1467 // Make sure the broadcaster has also moved.
1468 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1469 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
1471 // Set new value to A6 and make sure B1 gets updated.
1472 m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
1473 val = m_pDoc->GetValue(ScAddress(1,0,0));
1474 CPPUNIT_ASSERT_EQUAL(45.6, val);
1476 // Move column A up 3 cells, and make sure B1 now references A3, not A6.
1477 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
1478 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1479 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
1481 // The broadcaster should also have been relocated from A6 to A3.
1482 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1483 broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
1485 // Insert cells over A1:A10 and shift cells to right.
1486 m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
1487 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1488 checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
1489 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1490 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
1492 // Delete formula in C2, which should remove the broadcaster in B3.
1493 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1494 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
1495 clearRange(m_pDoc, ScAddress(2,0,0));
1496 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
1497 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
1498 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
1500 // Clear everything and start over.
1501 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1503 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1.
1504 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1505 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
1507 // While column A is still empty, move column A down 2 cells. This should
1508 // move the broadcaster from A1 to A3.
1509 m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
1510 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1511 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
1513 // Move it back while column A is still empty.
1514 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
1515 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1516 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
1518 // Clear everything again
1519 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1521 // B1:B3 depends on A1:A3
1522 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr);
1523 m_pDoc->SetString(ScAddress(1,1,0), u"=A2"_ustr);
1524 m_pDoc->SetString(ScAddress(1,2,0), u"=A3"_ustr);
1525 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1526 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1527 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1528 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
1529 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
1530 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
1531 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1532 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
1533 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
1535 // Insert Rows at row 2, down 5 rows.
1536 m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
1537 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1538 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
1539 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
1541 // Broadcasters in A2 and A3 should shift down by 5 rows.
1542 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1543 broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
1544 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1545 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
1547 // B2 and B3 should reference shifted cells.
1548 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1549 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
1550 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
1551 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
1553 // Delete cells with broadcasters.
1554 m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
1555 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
1556 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
1558 // References in B2 and B3 should be invalid.
1559 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
1560 checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
1561 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
1562 checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
1564 // Clear everything again
1565 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1568 // Switch to R1C1 to make it easier to input relative references in multiple cells.
1569 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1571 // Have B1:B20 reference A1:A20.
1572 val = 0.0;
1573 for (SCROW i = 0; i < 20; ++i)
1575 m_pDoc->SetValue(ScAddress(0,i,0), val++);
1576 m_pDoc->SetString(ScAddress(1,i,0), u"=RC[-1]"_ustr);
1580 // Ensure that the formula cells show correct values, and the referenced
1581 // cells have broadcasters.
1582 val = 0.0;
1583 for (SCROW i = 0; i < 20; ++i, ++val)
1585 CPPUNIT_ASSERT_EQUAL(val, m_pDoc->GetValue(ScAddress(1,i,0)));
1586 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1587 CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
1590 // Delete formula cells in B2:B19.
1591 clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
1592 // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
1593 CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
1594 CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
1595 for (SCROW i = 1; i <= 18; ++i)
1597 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
1598 CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
1601 // Clear everything again
1602 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
1604 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
1605 m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr);
1606 m_pDoc->SetString(ScAddress(2,0,0), u"=B1"_ustr);
1607 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,0,0));
1608 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,0,0));
1609 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2,0,0));
1611 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1612 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1613 pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
1614 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1616 // Change the value of A1 and make sure everyone follows suit.
1617 m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
1618 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
1619 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
1620 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
1622 // Insert a column at column B.
1623 m_pDoc->InsertCol(ScRange(1,0,0,1,m_pDoc->MaxRow(),0));
1624 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1625 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1626 pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
1627 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
1629 // Change the value of A1 again.
1630 m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
1631 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
1632 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
1633 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
1635 m_pDoc->DeleteTab(0);
1638 CPPUNIT_TEST_FIXTURE(Test, testFuncParam)
1641 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1642 m_pDoc->InsertTab (0, u"foo"_ustr));
1644 // First, the normal case, with no missing parameters.
1645 m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;2;3)"_ustr);
1646 m_pDoc->CalcFormulaTree(false, false);
1647 double val = m_pDoc->GetValue(0, 0, 0);
1648 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 2.0, val);
1650 // Now function with missing parameters. Missing values should be treated
1651 // as zeros.
1652 m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;;;)"_ustr);
1653 m_pDoc->CalcFormulaTree(false, false);
1654 val = m_pDoc->GetValue(0, 0, 0);
1655 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 0.25, val);
1657 // Conversion of string to numeric argument.
1658 m_pDoc->SetString(0, 0, 0, u"=\"\"+3"_ustr); // empty string
1659 m_pDoc->SetString(0, 1, 0, u"=\" \"+3"_ustr); // only blank
1660 m_pDoc->SetString(0, 2, 0, u"=\" 4 \"+3"_ustr); // number in blanks
1661 m_pDoc->SetString(0, 3, 0, u"=\" x \"+3"_ustr); // non-numeric
1662 m_pDoc->SetString(0, 4, 0, u"=\"4.4\"+3"_ustr); // locale dependent
1664 OUString aVal;
1665 ScCalcConfig aConfig;
1667 // With "Convert also locale dependent" and "Empty string as zero"=True option.
1668 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1669 aConfig.mbEmptyStringAsZero = true;
1670 m_pDoc->SetCalcConfig(aConfig);
1671 m_pDoc->CalcAll();
1672 val = m_pDoc->GetValue(0, 0, 0);
1673 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1674 val = m_pDoc->GetValue(0, 1, 0);
1675 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1676 val = m_pDoc->GetValue(0, 2, 0);
1677 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1678 aVal = m_pDoc->GetString( 0, 3, 0);
1679 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1680 val = m_pDoc->GetValue(0, 4, 0);
1681 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1683 // With "Convert also locale dependent" and "Empty string as zero"=False option.
1684 aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
1685 aConfig.mbEmptyStringAsZero = false;
1686 m_pDoc->SetCalcConfig(aConfig);
1687 m_pDoc->CalcAll();
1688 aVal = m_pDoc->GetString( 0, 0, 0);
1689 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1690 aVal = m_pDoc->GetString( 0, 1, 0);
1691 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1692 val = m_pDoc->GetValue(0, 2, 0);
1693 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1694 aVal = m_pDoc->GetString( 0, 3, 0);
1695 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1696 val = m_pDoc->GetValue(0, 4, 0);
1697 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
1699 // With "Convert only unambiguous" and "Empty string as zero"=True option.
1700 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1701 aConfig.mbEmptyStringAsZero = true;
1702 m_pDoc->SetCalcConfig(aConfig);
1703 m_pDoc->CalcAll();
1704 val = m_pDoc->GetValue(0, 0, 0);
1705 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1706 val = m_pDoc->GetValue(0, 1, 0);
1707 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1708 val = m_pDoc->GetValue(0, 2, 0);
1709 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1710 aVal = m_pDoc->GetString( 0, 3, 0);
1711 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1712 aVal = m_pDoc->GetString( 0, 4, 0);
1713 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1715 // With "Convert only unambiguous" and "Empty string as zero"=False option.
1716 aConfig.meStringConversion = ScCalcConfig::StringConversion::UNAMBIGUOUS;
1717 aConfig.mbEmptyStringAsZero = false;
1718 m_pDoc->SetCalcConfig(aConfig);
1719 m_pDoc->CalcAll();
1720 aVal = m_pDoc->GetString( 0, 0, 0);
1721 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1722 aVal = m_pDoc->GetString( 0, 1, 0);
1723 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1724 m_pDoc->GetValue(0, 2, 0);
1725 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
1726 aVal = m_pDoc->GetString( 0, 3, 0);
1727 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1728 aVal = m_pDoc->GetString( 0, 4, 0);
1729 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1731 // With "Treat as zero" ("Empty string as zero" is ignored).
1732 aConfig.meStringConversion = ScCalcConfig::StringConversion::ZERO;
1733 aConfig.mbEmptyStringAsZero = true;
1734 m_pDoc->SetCalcConfig(aConfig);
1735 m_pDoc->CalcAll();
1736 val = m_pDoc->GetValue(0, 0, 0);
1737 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1738 val = m_pDoc->GetValue(0, 1, 0);
1739 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1740 val = m_pDoc->GetValue(0, 2, 0);
1741 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1742 val = m_pDoc->GetValue(0, 3, 0);
1743 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1744 val = m_pDoc->GetValue(0, 4, 0);
1745 ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
1747 // With "Generate #VALUE! error" ("Empty string as zero" is ignored).
1748 aConfig.meStringConversion = ScCalcConfig::StringConversion::ILLEGAL;
1749 aConfig.mbEmptyStringAsZero = false;
1750 m_pDoc->SetCalcConfig(aConfig);
1751 m_pDoc->CalcAll();
1752 aVal = m_pDoc->GetString( 0, 0, 0);
1753 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1754 aVal = m_pDoc->GetString( 0, 1, 0);
1755 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1756 aVal = m_pDoc->GetString( 0, 2, 0);
1757 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1758 aVal = m_pDoc->GetString( 0, 3, 0);
1759 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1760 aVal = m_pDoc->GetString( 0, 4, 0);
1761 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
1763 m_pDoc->DeleteTab(0);
1766 CPPUNIT_TEST_FIXTURE(Test, testNamedRange)
1768 static const RangeNameDef aNames[] = {
1769 { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
1770 { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
1771 { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
1772 { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
1775 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"Sheet1"_ustr));
1777 m_pDoc->SetValue (0, 0, 0, 101);
1779 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
1780 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + SAL_N_ELEMENTS(aNames));
1781 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1782 m_pDoc->SetRangeName(std::move(pNames));
1784 ScRangeName* pNewRanges = m_pDoc->GetRangeName();
1785 CPPUNIT_ASSERT(pNewRanges);
1787 // Make sure the index lookup does the right thing.
1788 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1790 const ScRangeData* p = pNewRanges->findByIndex(aNames[i].mnIndex);
1791 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
1792 OUString aName = p->GetName();
1793 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].mpName));
1796 // Test usage in formula expression.
1797 m_pDoc->SetString (1, 0, 0, u"=A1/Divisor"_ustr);
1798 m_pDoc->CalcAll();
1800 double result = m_pDoc->GetValue (1, 0, 0);
1801 ASSERT_DOUBLES_EQUAL_MESSAGE ("calculation failed", 1.0, result);
1803 // Test copy-ability of range names.
1804 std::unique_ptr<ScRangeName> pCopiedRanges(new ScRangeName(*pNewRanges));
1805 m_pDoc->SetRangeName(std::move(pCopiedRanges));
1806 // Make sure the index lookup still works.
1807 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1809 const ScRangeData* p = m_pDoc->GetRangeName()->findByIndex(aNames[i].mnIndex);
1810 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
1811 OUString aName = p->GetName();
1812 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].mpName));
1815 // Test using another-sheet-local name, scope Sheet1.
1816 ScRangeData* pLocal1 = new ScRangeData( *m_pDoc, u"local1"_ustr, ScAddress(0,0,0));
1817 ScRangeData* pLocal2 = new ScRangeData( *m_pDoc, u"local2"_ustr, u"$Sheet1.$A$1"_ustr);
1818 ScRangeData* pLocal3 = new ScRangeData( *m_pDoc, u"local3"_ustr, u"Sheet1.$A$1"_ustr);
1819 ScRangeData* pLocal4 = new ScRangeData( *m_pDoc, u"local4"_ustr, u"$A$1"_ustr); // implicit relative sheet reference
1820 std::unique_ptr<ScRangeName> pLocalRangeName1(new ScRangeName);
1821 pLocalRangeName1->insert(pLocal1);
1822 pLocalRangeName1->insert(pLocal2);
1823 pLocalRangeName1->insert(pLocal3);
1824 pLocalRangeName1->insert(pLocal4);
1825 m_pDoc->SetRangeName(0, std::move(pLocalRangeName1));
1827 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (1, u"Sheet2"_ustr));
1829 // Use other-sheet-local name of Sheet1 on Sheet2.
1830 ScAddress aPos(1,0,1);
1831 OUString aFormula(u"=Sheet1.local1+Sheet1.local2+Sheet1.local3+Sheet1.local4"_ustr);
1832 m_pDoc->SetString(aPos, aFormula);
1833 OUString aString = m_pDoc->GetFormula(1,0,1);
1834 CPPUNIT_ASSERT_EQUAL_MESSAGE("formula string should be equal", aFormula, aString);
1835 double fValue = m_pDoc->GetValue(aPos);
1836 ASSERT_DOUBLES_EQUAL_MESSAGE("value should be 4 times Sheet1.A1", 404.0, fValue);
1838 m_pDoc->DeleteTab(1);
1839 m_pDoc->SetRangeName(0,nullptr); // Delete the names.
1840 m_pDoc->SetRangeName(nullptr); // Delete the names.
1841 m_pDoc->DeleteTab(0);
1844 CPPUNIT_TEST_FIXTURE(Test, testInsertNameList)
1846 m_pDoc->InsertTab(0, u"Test"_ustr);
1848 static const RangeNameDef aNames[] = {
1849 { "MyRange1", "$Test.$A$1:$A$100", 1 },
1850 { "MyRange2", "$Test.$B$1:$B$100", 2 },
1851 { "MyRange3", "$Test.$C$1:$C$100", 3 }
1854 std::unique_ptr<ScRangeName> pNames(new ScRangeName);
1855 bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), aNames, aNames + SAL_N_ELEMENTS(aNames));
1856 CPPUNIT_ASSERT_MESSAGE("Failed to insert range names.", bSuccess);
1857 m_pDoc->SetRangeName(std::move(pNames));
1859 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
1860 ScAddress aPos(1,1,0);
1861 rDocFunc.InsertNameList(aPos, true);
1863 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i, aPos.IncRow())
1865 OUString aName = m_pDoc->GetString(aPos);
1866 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aNames[i].mpName), aName);
1867 ScAddress aExprPos = aPos;
1868 aExprPos.IncCol();
1869 OUString aExpr = m_pDoc->GetString(aExprPos);
1870 OUString aExpected = "=" + OUString::createFromAscii(aNames[i].mpExpr);
1871 CPPUNIT_ASSERT_EQUAL(aExpected, aExpr);
1874 m_pDoc->DeleteTab(0);
1877 CPPUNIT_TEST_FIXTURE(Test, testCSV)
1879 const int English = 0, European = 1;
1880 struct {
1881 const char *pStr; int eSep; bool bResult; double nValue;
1882 } aTests[] = {
1883 { "foo", English, false, 0.0 },
1884 { "1.0", English, true, 1.0 },
1885 { "1,0", English, false, 0.0 },
1886 { "1.0", European, false, 0.0 },
1887 { "1.000", European, true, 1000.0 },
1888 { "1,000", European, true, 1.0 },
1889 { "1.000", English, true, 1.0 },
1890 { "1,000", English, true, 1000.0 },
1891 { " 1.0", English, true, 1.0 },
1892 { " 1.0 ", English, true, 1.0 },
1893 { "1.0 ", European, false, 0.0 },
1894 { "1.000", European, true, 1000.0 },
1895 { "1137.999", English, true, 1137.999 },
1896 { "1.000.00", European, false, 0.0 },
1897 { "+,123", English, false, 0.0 },
1898 { "-,123", English, false, 0.0 }
1900 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
1901 OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
1902 double nValue = 0.0;
1903 bool bResult = ScStringUtil::parseSimpleNumber
1904 (aStr, aTests[i].eSep == English ? '.' : ',',
1905 aTests[i].eSep == English ? ',' : '.',
1907 nValue);
1908 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric detection failure", aTests[i].bResult, bResult);
1909 CPPUNIT_ASSERT_EQUAL_MESSAGE ("CSV numeric value failure", aTests[i].nValue, nValue);
1913 template<typename Evaluator>
1914 static void checkMatrixElements(const ScMatrix& rMat)
1916 SCSIZE nC, nR;
1917 rMat.GetDimensions(nC, nR);
1918 Evaluator aEval;
1919 for (SCSIZE i = 0; i < nC; ++i)
1921 for (SCSIZE j = 0; j < nR; ++j)
1923 aEval(i, j, rMat.Get(i, j));
1928 namespace {
1930 struct AllZeroMatrix
1932 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1934 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
1935 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
1939 struct PartiallyFilledZeroMatrix
1941 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1943 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
1944 if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
1946 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be 3.0", 3.0, rVal.fVal);
1948 else
1950 ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
1955 struct AllEmptyMatrix
1957 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1959 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
1960 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
1964 struct PartiallyFilledEmptyMatrix
1966 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1968 if (nCol == 1 && nRow == 1)
1970 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of boolean type", int(ScMatValType::Boolean), static_cast<int>(rVal.nType));
1971 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", 1.0, rVal.fVal);
1973 else if (nCol == 4 && nRow == 5)
1975 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
1976 ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", -12.5, rVal.fVal);
1978 else if (nCol == 8 && nRow == 2)
1980 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::String), static_cast<int>(rVal.nType));
1981 CPPUNIT_ASSERT_EQUAL_MESSAGE("element value is not what is expected", u"Test"_ustr, rVal.aStr.getString());
1983 else if (nCol == 8 && nRow == 11)
1985 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty path type", int(ScMatValType::EmptyPath), static_cast<int>(rVal.nType));
1986 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
1988 else
1990 CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
1991 ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
1998 CPPUNIT_TEST_FIXTURE(Test, testMatrix)
2000 svl::SharedStringPool& rPool = m_pDoc->GetSharedStringPool();
2001 ScMatrixRef pMat, pMat2;
2003 // First, test the zero matrix type.
2004 pMat = new ScMatrix(0, 0, 0.0);
2005 SCSIZE nC, nR;
2006 pMat->GetDimensions(nC, nR);
2007 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nC);
2008 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nR);
2009 pMat->Resize(4, 10, 0.0);
2010 pMat->GetDimensions(nC, nR);
2011 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(4), nC);
2012 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nR);
2013 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2014 !pMat->And());
2015 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2016 !pMat->Or());
2018 // Resizing into a larger matrix should fill the void space with zeros.
2019 checkMatrixElements<AllZeroMatrix>(*pMat);
2021 pMat->FillDouble(3.0, 1, 2, 2, 8);
2022 checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
2023 CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
2024 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2025 !pMat->And());
2026 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2027 pMat->Or());
2028 pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
2029 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2030 pMat->And());
2031 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2032 pMat->Or());
2034 // Test the AND and OR evaluations.
2035 pMat = new ScMatrix(2, 2, 0.0);
2037 // Only some of the elements are non-zero.
2038 pMat->PutBoolean(true, 0, 0);
2039 pMat->PutDouble(1.0, 1, 1);
2040 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2041 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2043 // All of the elements are non-zero.
2044 pMat->PutBoolean(true, 0, 1);
2045 pMat->PutDouble(2.3, 1, 0);
2046 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2047 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2049 // Now test the empty matrix type.
2050 pMat = new ScMatrix(10, 20);
2051 pMat->GetDimensions(nC, nR);
2052 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nC);
2053 CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(20), nR);
2054 checkMatrixElements<AllEmptyMatrix>(*pMat);
2056 pMat->PutBoolean(true, 1, 1);
2057 pMat->PutDouble(-12.5, 4, 5);
2058 pMat->PutString(rPool.intern(u"Test"_ustr), 8, 2);
2059 pMat->PutEmptyPath(8, 11);
2060 checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2062 // Test resizing.
2063 pMat = new ScMatrix(0, 0);
2064 pMat->Resize(2, 2, 1.5);
2065 pMat->PutEmpty(1, 1);
2067 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2068 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2069 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2070 CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2072 // Max and min values.
2073 pMat = new ScMatrix(2, 2, 0.0);
2074 pMat->PutDouble(-10, 0, 0);
2075 pMat->PutDouble(-12, 0, 1);
2076 pMat->PutDouble(-8, 1, 0);
2077 pMat->PutDouble(-25, 1, 1);
2078 CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
2079 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
2080 pMat->PutString(rPool.intern(u"Test"_ustr), 0, 0);
2081 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
2082 CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
2083 pMat->PutBoolean(true, 0, 0);
2084 CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
2085 pMat = new ScMatrix(2, 2, 10.0);
2086 pMat->PutBoolean(false, 0, 0);
2087 pMat->PutDouble(12.5, 1, 1);
2088 CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
2089 CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
2091 // Convert matrix into a linear double array. String elements become NaN
2092 // and empty elements become 0.
2093 pMat = new ScMatrix(3, 3);
2094 pMat->PutDouble(2.5, 0, 0);
2095 pMat->PutDouble(1.2, 0, 1);
2096 pMat->PutString(rPool.intern(u"A"_ustr), 1, 1);
2097 pMat->PutDouble(2.3, 2, 1);
2098 pMat->PutDouble(-20, 2, 2);
2100 static const double fNaN = std::numeric_limits<double>::quiet_NaN();
2102 std::vector<double> aDoubles;
2103 pMat->GetDoubleArray(aDoubles);
2106 const double pChecks[] = { 2.5, 1.2, 0, 0, fNaN, 0, 0, 2.3, -20 };
2107 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2108 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2110 if (std::isnan(pChecks[i]))
2111 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2112 else
2113 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2117 pMat2 = new ScMatrix(3, 3, 10.0);
2118 pMat2->PutString(rPool.intern(u"B"_ustr), 1, 0);
2119 pMat2->MergeDoubleArrayMultiply(aDoubles);
2122 const double pChecks[] = { 25, 12, 0, fNaN, fNaN, 0, 0, 23, -200 };
2123 CPPUNIT_ASSERT_EQUAL(SAL_N_ELEMENTS(pChecks), aDoubles.size());
2124 for (size_t i = 0, n = aDoubles.size(); i < n; ++i)
2126 if (std::isnan(pChecks[i]))
2127 CPPUNIT_ASSERT_MESSAGE("NaN is expected, but it's not.", std::isnan(aDoubles[i]));
2128 else
2129 CPPUNIT_ASSERT_EQUAL(pChecks[i], aDoubles[i]);
2134 CPPUNIT_TEST_FIXTURE(Test, testMatrixComparisonWithErrors)
2136 m_pDoc->InsertTab(0, u"foo"_ustr);
2138 // Insert the source values in A1:A2.
2139 m_pDoc->SetString(0, 0, 0, u"=1/0"_ustr);
2140 m_pDoc->SetValue( 0, 1, 0, 1.0);
2142 // Create a matrix formula in B3:B4 referencing A1:A2 and doing a greater
2143 // than comparison on it's values. Error value must be propagated.
2144 ScMarkData aMark(m_pDoc->GetSheetLimits());
2145 aMark.SelectOneTable(0);
2146 m_pDoc->InsertMatrixFormula(1, 2, 1, 3, aMark, u"=A1:A2>0"_ustr);
2148 CPPUNIT_ASSERT_EQUAL(u"#DIV/0!"_ustr, m_pDoc->GetString(0,0,0));
2149 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue( 0,1,0));
2150 CPPUNIT_ASSERT_EQUAL(u"#DIV/0!"_ustr, m_pDoc->GetString(1,2,0));
2151 CPPUNIT_ASSERT_EQUAL(u"TRUE"_ustr, m_pDoc->GetString(1,3,0));
2153 m_pDoc->DeleteTab(0);
2156 CPPUNIT_TEST_FIXTURE(Test, testMatrixConditionalBooleanResult)
2158 m_pDoc->InsertTab(0, u"foo"_ustr);
2160 // Create matrix formulas in A1:B1,A2:B2,A3:B3,A4:B4 producing mixed
2161 // boolean and numeric results in an unformatted area.
2162 ScMarkData aMark(m_pDoc->GetSheetLimits());
2163 aMark.SelectOneTable(0);
2164 m_pDoc->InsertMatrixFormula( 0,0, 1,0, aMark, u"=IF({1;0};TRUE();42)"_ustr); // {TRUE,42}
2165 m_pDoc->InsertMatrixFormula( 0,1, 1,1, aMark, u"=IF({0;1};TRUE();42)"_ustr); // {42,1} aim for {42,TRUE}
2166 m_pDoc->InsertMatrixFormula( 0,2, 1,2, aMark, u"=IF({1;0};42;FALSE())"_ustr); // {42,0} aim for {42,FALSE}
2167 m_pDoc->InsertMatrixFormula( 0,3, 1,3, aMark, u"=IF({0;1};42;FALSE())"_ustr); // {FALSE,42}
2169 CPPUNIT_ASSERT_EQUAL( u"TRUE"_ustr, m_pDoc->GetString(0,0,0));
2170 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(1,0,0));
2171 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(0,1,0));
2172 //CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(1,1,0)); // not yet
2173 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(0,2,0));
2174 //CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(1,2,0)); // not yet
2175 CPPUNIT_ASSERT_EQUAL( u"FALSE"_ustr, m_pDoc->GetString(0,3,0));
2176 CPPUNIT_ASSERT_EQUAL( u"42"_ustr, m_pDoc->GetString(1,3,0));
2178 m_pDoc->DeleteTab(0);
2181 CPPUNIT_TEST_FIXTURE(Test, testEnterMixedMatrix)
2183 m_pDoc->InsertTab(0, u"foo"_ustr);
2185 // Insert the source values in A1:B2.
2186 m_pDoc->SetString(0, 0, 0, u"A"_ustr);
2187 m_pDoc->SetString(1, 0, 0, u"B"_ustr);
2188 double val = 1.0;
2189 m_pDoc->SetValue(0, 1, 0, val);
2190 val = 2.0;
2191 m_pDoc->SetValue(1, 1, 0, val);
2193 // Create a matrix range in A4:B5 referencing A1:B2.
2194 ScMarkData aMark(m_pDoc->GetSheetLimits());
2195 aMark.SelectOneTable(0);
2196 m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, u"=A1:B2"_ustr);
2198 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2199 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2200 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2201 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2203 m_pDoc->DeleteTab(0);
2206 CPPUNIT_TEST_FIXTURE(Test, testMatrixEditable)
2208 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2210 m_pDoc->InsertTab(0, u"Test"_ustr);
2212 // Values in A1:B1.
2213 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2214 m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
2216 // A2 is a normal formula.
2217 m_pDoc->SetString(ScAddress(0,1,0), u"=5"_ustr);
2219 // A3:A4 is a matrix.
2220 ScRange aMatRange(0,2,0,0,3,0);
2221 ScMarkData aMark(m_pDoc->GetSheetLimits());
2222 aMark.SetMarkArea(aMatRange);
2223 m_pDoc->InsertMatrixFormula(0, 2, 0, 3, aMark, u"=TRANSPOSE(A1:B1)"_ustr);
2225 // Check their values.
2226 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2227 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2228 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2230 // Make sure A3:A4 is a matrix.
2231 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2232 CPPUNIT_ASSERT(pFC);
2233 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should be matrix origin.",
2234 ScMatrixMode::Formula, pFC->GetMatrixFlag());
2236 pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
2237 CPPUNIT_ASSERT(pFC);
2238 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should be matrix reference.",
2239 ScMatrixMode::Reference, pFC->GetMatrixFlag());
2241 // Check to make sure A3:A4 combined is editable.
2242 ScEditableTester aTester;
2243 aTester.TestSelection(*m_pDoc, aMark);
2244 CPPUNIT_ASSERT(aTester.IsEditable());
2246 m_pDoc->DeleteTab(0);
2249 CPPUNIT_TEST_FIXTURE(Test, testCellCopy)
2251 m_pDoc->InsertTab(0, u"TestTab"_ustr);
2252 ScAddress aSrc(0,0,0);
2253 ScAddress aDest(0,1,0);
2254 OUString aStr(u"please copy me"_ustr);
2255 m_pDoc->SetString(aSrc, u"please copy me"_ustr);
2256 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aSrc));
2257 // copy to self - why not ?
2258 m_pDoc->CopyCellToDocument(aSrc,aDest,*m_pDoc);
2259 CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aDest));
2262 CPPUNIT_TEST_FIXTURE(Test, testSheetCopy)
2264 m_pDoc->InsertTab(0, u"TestTab"_ustr);
2265 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
2266 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2268 // We need a drawing layer in order to create caption objects.
2269 m_pDoc->InitDrawLayer(m_xDocShell.get());
2271 // Insert text in A1.
2272 m_pDoc->SetString(ScAddress(0,0,0), u"copy me"_ustr);
2274 // Insert edit cells in B1:B3.
2275 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
2276 rEE.SetTextCurrentDefaults(u"Edit 1"_ustr);
2277 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject());
2278 rEE.SetTextCurrentDefaults(u"Edit 2"_ustr);
2279 m_pDoc->SetEditText(ScAddress(1,1,0), rEE.CreateTextObject());
2280 rEE.SetTextCurrentDefaults(u"Edit 3"_ustr);
2281 m_pDoc->SetEditText(ScAddress(1,2,0), rEE.CreateTextObject());
2283 SCROW nRow1, nRow2;
2284 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2285 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2286 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2287 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2289 // insert a note
2290 ScAddress aAdrA1 (0,2,0); // empty cell content.
2291 ScPostIt *pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
2292 pNoteA1->SetText(aAdrA1, u"Hello world in A3"_ustr);
2294 // Copy and test the result.
2295 m_pDoc->CopyTab(0, 1);
2296 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2297 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2299 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2300 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2301 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2302 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2303 CPPUNIT_ASSERT_MESSAGE("There should be note on A3 in new sheet", m_pDoc->HasNote(ScAddress(0,2,1)));
2304 CPPUNIT_ASSERT_EQUAL(u"copy me"_ustr, m_pDoc->GetString(ScAddress(0,0,1)));
2306 // Check the copied edit cells.
2307 const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(1,0,1));
2308 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B1.", pEditObj);
2309 CPPUNIT_ASSERT_EQUAL(u"Edit 1"_ustr, pEditObj->GetText(0));
2310 pEditObj = m_pDoc->GetEditText(ScAddress(1,1,1));
2311 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B2.", pEditObj);
2312 CPPUNIT_ASSERT_EQUAL(u"Edit 2"_ustr, pEditObj->GetText(0));
2313 pEditObj = m_pDoc->GetEditText(ScAddress(1,2,1));
2314 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B3.", pEditObj);
2315 CPPUNIT_ASSERT_EQUAL(u"Edit 3"_ustr, pEditObj->GetText(0));
2317 m_pDoc->DeleteTab(1);
2319 m_pDoc->SetRowHidden(5, 10, 0, true);
2320 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2321 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2322 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2323 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2324 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2325 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2326 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2327 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2328 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2329 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2330 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2331 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2333 // Copy the sheet once again.
2334 m_pDoc->CopyTab(0, 1);
2335 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.",
2336 static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2337 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2338 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2339 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2340 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2341 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2342 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2343 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2344 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2345 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2346 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2347 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2348 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2349 m_pDoc->DeleteTab(1);
2350 m_pDoc->DeleteTab(0);
2353 CPPUNIT_TEST_FIXTURE(Test, testSheetMove)
2355 m_pDoc->InsertTab(0, u"TestTab1"_ustr);
2356 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
2357 SCROW nRow1, nRow2;
2358 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2359 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2360 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2361 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2363 //test if inserting before another sheet works
2364 m_pDoc->InsertTab(0, u"TestTab2"_ustr);
2365 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2366 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2367 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
2368 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
2369 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
2371 // Move and test the result.
2372 m_pDoc->MoveTab(0, 1);
2373 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2374 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2375 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
2376 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
2377 CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
2378 OUString aName;
2379 m_pDoc->GetName(0, aName);
2380 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab1"_ustr, aName);
2382 m_pDoc->SetRowHidden(5, 10, 0, true);
2383 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
2384 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2385 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2386 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2387 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
2388 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2389 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2390 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2391 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
2392 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2393 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2394 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2396 // Move the sheet once again.
2397 m_pDoc->MoveTab(1, 0);
2398 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
2399 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
2400 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
2401 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
2402 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
2403 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
2404 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
2405 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
2406 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
2407 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
2408 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
2409 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
2410 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
2411 m_pDoc->GetName(0, aName);
2412 CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab2"_ustr, aName);
2413 m_pDoc->DeleteTab(1);
2414 m_pDoc->DeleteTab(0);
2417 CPPUNIT_TEST_FIXTURE(Test, testDataArea)
2419 m_pDoc->InsertTab(0, u"Data"_ustr);
2421 // Totally empty sheet should be rightfully considered empty in all accounts.
2422 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 100, 100, 0));
2423 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2425 // Now, set borders in some cells...
2426 ::editeng::SvxBorderLine aLine(nullptr, 50, SvxBorderLineStyle::SOLID);
2427 SvxBoxItem aBorderItem(ATTR_BORDER);
2428 aBorderItem.SetLine(&aLine, SvxBoxItemLine::LEFT);
2429 aBorderItem.SetLine(&aLine, SvxBoxItemLine::RIGHT);
2430 for (SCROW i = 0; i < 100; ++i)
2431 // Set borders from row 1 to 100.
2432 m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
2434 // Now the sheet is considered non-empty for printing purposes, but still
2435 // be empty in all the other cases.
2436 CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
2437 !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
2438 CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
2439 m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2441 // Adding a real cell content should turn the block non-empty.
2442 m_pDoc->SetString(0, 0, 0, u"Some text"_ustr);
2443 CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
2444 !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
2446 // TODO: Add more tests for normal data area calculation.
2448 m_pDoc->DeleteTab(0);
2451 CPPUNIT_TEST_FIXTURE(Test, testStreamValid)
2454 * Make sure the sheet streams are invalidated properly.
2456 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
2457 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
2458 m_pDoc->InsertTab(2, u"Sheet3"_ustr);
2459 m_pDoc->InsertTab(3, u"Sheet4"_ustr);
2460 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", static_cast<SCTAB>(4), m_pDoc->GetTableCount());
2462 OUString a1(u"A1"_ustr);
2463 OUString a2(u"A2"_ustr);
2464 OUString test;
2466 // Put values into Sheet1.
2467 m_pDoc->SetString(0, 0, 0, a1);
2468 m_pDoc->SetString(0, 1, 0, a2);
2469 test = m_pDoc->GetString(0, 0, 0);
2470 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A1", test, a1);
2471 test = m_pDoc->GetString(0, 1, 0);
2472 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A2", test, a2);
2474 // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
2475 m_pDoc->SetString(0, 0, 1, u"=Sheet1.A1"_ustr);
2476 m_pDoc->SetString(0, 1, 1, u"=Sheet1.A2"_ustr);
2477 m_pDoc->SetString(0, 0, 2, u"=Sheet1.A1"_ustr);
2478 m_pDoc->SetString(0, 0, 3, u"=Sheet1.A2"_ustr);
2480 test = m_pDoc->GetString(0, 0, 1);
2481 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A1", test, a1);
2482 test = m_pDoc->GetString(0, 1, 1);
2483 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A2", test, a2);
2484 test = m_pDoc->GetString(0, 0, 2);
2485 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a1);
2486 test = m_pDoc->GetString(0, 0, 3);
2487 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a2);
2489 // Set all sheet streams valid after all the initial cell values are in
2490 // place. In reality we need to have real XML streams stored in order to
2491 // claim they are valid, but we are just testing the flag values here.
2492 m_pDoc->SetStreamValid(0, true);
2493 m_pDoc->SetStreamValid(1, true);
2494 m_pDoc->SetStreamValid(2, true);
2495 m_pDoc->SetStreamValid(3, true);
2496 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
2497 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
2498 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
2499 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
2501 // Now, insert a new row at row 2 position on Sheet1. This will move cell
2502 // A2 downward but cell A1 remains unmoved.
2503 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 2);
2504 test = m_pDoc->GetString(0, 0, 0);
2505 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell A1 should not have moved.", test, a1);
2506 test = m_pDoc->GetString(0, 3, 0);
2507 CPPUNIT_ASSERT_EQUAL_MESSAGE("the old cell A2 should now be at A4.", test, a2);
2508 ScRefCellValue aCell;
2509 aCell.assign(*m_pDoc, ScAddress(0,1,0));
2510 CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
2511 aCell.assign(*m_pDoc, ScAddress(0,2,0));
2512 CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
2514 // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
2515 // invalidated, whereas Sheet3's stream should still be valid.
2516 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
2517 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
2518 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
2519 CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
2521 m_pDoc->DeleteTab(3);
2522 m_pDoc->DeleteTab(2);
2523 m_pDoc->DeleteTab(1);
2524 m_pDoc->DeleteTab(0);
2527 CPPUNIT_TEST_FIXTURE(Test, testFunctionLists)
2530 * Test built-in cell functions to make sure their categories and order
2531 * are correct.
2533 const char* aDataBase[] = {
2534 "DAVERAGE",
2535 "DCOUNT",
2536 "DCOUNTA",
2537 "DGET",
2538 "DMAX",
2539 "DMIN",
2540 "DPRODUCT",
2541 "DSTDEV",
2542 "DSTDEVP",
2543 "DSUM",
2544 "DVAR",
2545 "DVARP",
2546 nullptr
2549 const char* aDateTime[] = {
2550 "DATE",
2551 "DATEDIF",
2552 "DATEVALUE",
2553 "DAY",
2554 "DAYS",
2555 "DAYS360",
2556 "DAYSINMONTH",
2557 "DAYSINYEAR",
2558 "EASTERSUNDAY",
2559 "HOUR",
2560 "ISLEAPYEAR",
2561 "ISOWEEKNUM",
2562 "MINUTE",
2563 "MONTH",
2564 "MONTHS",
2565 "NETWORKDAYS",
2566 "NETWORKDAYS.INTL",
2567 "NOW",
2568 "SECOND",
2569 "TIME",
2570 "TIMEVALUE",
2571 "TODAY",
2572 "WEEKDAY",
2573 "WEEKNUM",
2574 "WEEKNUM_OOO",
2575 "WEEKS",
2576 "WEEKSINYEAR",
2577 "WORKDAY.INTL",
2578 "YEAR",
2579 "YEARS",
2580 nullptr
2583 const char* aFinancial[] = {
2584 "CUMIPMT",
2585 "CUMPRINC",
2586 "DB",
2587 "DDB",
2588 "EFFECT",
2589 "FV",
2590 "IPMT",
2591 "IRR",
2592 "ISPMT",
2593 "MIRR",
2594 "NOMINAL",
2595 "NPER",
2596 "NPV",
2597 "OPT_BARRIER",
2598 "OPT_PROB_HIT",
2599 "OPT_PROB_INMONEY",
2600 "OPT_TOUCH",
2601 "PDURATION",
2602 "PMT",
2603 "PPMT",
2604 "PV",
2605 "RATE",
2606 "RRI",
2607 "SLN",
2608 "SYD",
2609 "VDB",
2610 nullptr
2613 const char* aInformation[] = {
2614 "CELL",
2615 "CURRENT",
2616 "FORMULA",
2617 "INFO",
2618 "ISBLANK",
2619 "ISERR",
2620 "ISERROR",
2621 "ISEVEN",
2622 "ISFORMULA",
2623 "ISLOGICAL",
2624 "ISNA",
2625 "ISNONTEXT",
2626 "ISNUMBER",
2627 "ISODD",
2628 "ISREF",
2629 "ISTEXT",
2630 "N",
2631 "NA",
2632 "TYPE",
2633 nullptr
2636 const char* aLogical[] = {
2637 "AND",
2638 "FALSE",
2639 "IF",
2640 "IFERROR",
2641 "IFNA",
2642 "IFS",
2643 "NOT",
2644 "OR",
2645 "SWITCH",
2646 "TRUE",
2647 "XOR",
2648 nullptr
2651 const char* aMathematical[] = {
2652 "ABS",
2653 "ACOS",
2654 "ACOSH",
2655 "ACOT",
2656 "ACOTH",
2657 "AGGREGATE",
2658 "ASIN",
2659 "ASINH",
2660 "ATAN",
2661 "ATAN2",
2662 "ATANH",
2663 "BITAND",
2664 "BITLSHIFT",
2665 "BITOR",
2666 "BITRSHIFT",
2667 "BITXOR",
2668 "CEILING",
2669 "CEILING.MATH",
2670 "CEILING.PRECISE",
2671 "CEILING.XCL",
2672 "COLOR",
2673 "COMBIN",
2674 "COMBINA",
2675 "CONVERT_OOO",
2676 "COS",
2677 "COSH",
2678 "COT",
2679 "COTH",
2680 "CSC",
2681 "CSCH",
2682 "DEGREES",
2683 "EUROCONVERT",
2684 "EVEN",
2685 "EXP",
2686 "FACT",
2687 "FLOOR",
2688 "FLOOR.MATH",
2689 "FLOOR.PRECISE",
2690 "FLOOR.XCL",
2691 "GCD",
2692 "INT",
2693 "ISO.CEILING",
2694 "LCM",
2695 "LN",
2696 "LOG",
2697 "LOG10",
2698 "MOD",
2699 "ODD",
2700 "PI",
2701 "POWER",
2702 "PRODUCT",
2703 "RADIANS",
2704 "RAND",
2705 "RAND.NV",
2706 "RANDARRAY",
2707 "RANDBETWEEN.NV",
2708 "RAWSUBTRACT",
2709 "ROUND",
2710 "ROUNDDOWN",
2711 "ROUNDSIG",
2712 "ROUNDUP",
2713 "SEC",
2714 "SECH",
2715 "SIGN",
2716 "SIN",
2717 "SINH",
2718 "SQRT",
2719 "SUBTOTAL",
2720 "SUM",
2721 "SUMIF",
2722 "SUMIFS",
2723 "SUMSQ",
2724 "TAN",
2725 "TANH",
2726 "TRUNC",
2727 nullptr
2730 const char* aArray[] = {
2731 "FOURIER",
2732 "FREQUENCY",
2733 "GROWTH",
2734 "LINEST",
2735 "LOGEST",
2736 "MDETERM",
2737 "MINVERSE",
2738 "MMULT",
2739 "MUNIT",
2740 "SEQUENCE",
2741 "SUMPRODUCT",
2742 "SUMX2MY2",
2743 "SUMX2PY2",
2744 "SUMXMY2",
2745 "TRANSPOSE",
2746 "TREND",
2747 nullptr
2750 const char* aStatistical[] = {
2751 "AVEDEV",
2752 "AVERAGE",
2753 "AVERAGEA",
2754 "AVERAGEIF",
2755 "AVERAGEIFS",
2756 "B",
2757 "BETA.DIST",
2758 "BETA.INV",
2759 "BETADIST",
2760 "BETAINV",
2761 "BINOM.DIST",
2762 "BINOM.INV",
2763 "BINOMDIST",
2764 "CHIDIST",
2765 "CHIINV",
2766 "CHISQ.DIST",
2767 "CHISQ.DIST.RT",
2768 "CHISQ.INV",
2769 "CHISQ.INV.RT",
2770 "CHISQ.TEST",
2771 "CHISQDIST",
2772 "CHISQINV",
2773 "CHITEST",
2774 "CONFIDENCE",
2775 "CONFIDENCE.NORM",
2776 "CONFIDENCE.T",
2777 "CORREL",
2778 "COUNT",
2779 "COUNTA",
2780 "COUNTBLANK",
2781 "COUNTIF",
2782 "COUNTIFS",
2783 "COVAR",
2784 "COVARIANCE.P",
2785 "COVARIANCE.S",
2786 "CRITBINOM",
2787 "DEVSQ",
2788 "ERF.PRECISE",
2789 "ERFC.PRECISE",
2790 "EXPON.DIST",
2791 "EXPONDIST",
2792 "F.DIST",
2793 "F.DIST.RT",
2794 "F.INV",
2795 "F.INV.RT",
2796 "F.TEST",
2797 "FDIST",
2798 "FINV",
2799 "FISHER",
2800 "FISHERINV",
2801 "FORECAST",
2802 "FORECAST.ETS.ADD",
2803 "FORECAST.ETS.MULT",
2804 "FORECAST.ETS.PI.ADD",
2805 "FORECAST.ETS.PI.MULT",
2806 "FORECAST.ETS.SEASONALITY",
2807 "FORECAST.ETS.STAT.ADD",
2808 "FORECAST.ETS.STAT.MULT",
2809 "FORECAST.LINEAR",
2810 "FTEST",
2811 "GAMMA",
2812 "GAMMA.DIST",
2813 "GAMMA.INV",
2814 "GAMMADIST",
2815 "GAMMAINV",
2816 "GAMMALN",
2817 "GAMMALN.PRECISE",
2818 "GAUSS",
2819 "GEOMEAN",
2820 "HARMEAN",
2821 "HYPGEOM.DIST",
2822 "HYPGEOMDIST",
2823 "INTERCEPT",
2824 "KURT",
2825 "LARGE",
2826 "LOGINV",
2827 "LOGNORM.DIST",
2828 "LOGNORM.INV",
2829 "LOGNORMDIST",
2830 "MAX",
2831 "MAXA",
2832 "MAXIFS",
2833 "MEDIAN",
2834 "MIN",
2835 "MINA",
2836 "MINIFS",
2837 "MODE",
2838 "MODE.MULT",
2839 "MODE.SNGL",
2840 "NEGBINOM.DIST",
2841 "NEGBINOMDIST",
2842 "NORM.DIST",
2843 "NORM.INV",
2844 "NORM.S.DIST",
2845 "NORM.S.INV",
2846 "NORMDIST",
2847 "NORMINV",
2848 "NORMSDIST",
2849 "NORMSINV",
2850 "PEARSON",
2851 "PERCENTILE",
2852 "PERCENTILE.EXC",
2853 "PERCENTILE.INC",
2854 "PERCENTRANK",
2855 "PERCENTRANK.EXC",
2856 "PERCENTRANK.INC",
2857 "PERMUT",
2858 "PERMUTATIONA",
2859 "PHI",
2860 "POISSON",
2861 "POISSON.DIST",
2862 "PROB",
2863 "QUARTILE",
2864 "QUARTILE.EXC",
2865 "QUARTILE.INC",
2866 "RANK",
2867 "RANK.AVG",
2868 "RANK.EQ",
2869 "RSQ",
2870 "SKEW",
2871 "SKEWP",
2872 "SLOPE",
2873 "SMALL",
2874 "STANDARDIZE",
2875 "STDEV",
2876 "STDEV.P",
2877 "STDEV.S",
2878 "STDEVA",
2879 "STDEVP",
2880 "STDEVPA",
2881 "STEYX",
2882 "T.DIST",
2883 "T.DIST.2T",
2884 "T.DIST.RT",
2885 "T.INV",
2886 "T.INV.2T",
2887 "T.TEST",
2888 "TDIST",
2889 "TINV",
2890 "TRIMMEAN",
2891 "TTEST",
2892 "VAR",
2893 "VAR.P",
2894 "VAR.S",
2895 "VARA",
2896 "VARP",
2897 "VARPA",
2898 "WEIBULL",
2899 "WEIBULL.DIST",
2900 "Z.TEST",
2901 "ZTEST",
2902 nullptr
2905 const char* aSpreadsheet[] = {
2906 "ADDRESS",
2907 "AREAS",
2908 "CHOOSE",
2909 "COLUMN",
2910 "COLUMNS",
2911 "DDE",
2912 "ERROR.TYPE",
2913 "ERRORTYPE",
2914 "FILTER",
2915 "GETPIVOTDATA",
2916 "HLOOKUP",
2917 "HYPERLINK",
2918 "INDEX",
2919 "INDIRECT",
2920 "LET",
2921 "LOOKUP",
2922 "MATCH",
2923 "OFFSET",
2924 "ROW",
2925 "ROWS",
2926 "SHEET",
2927 "SHEETS",
2928 "SORT",
2929 "SORTBY",
2930 "STYLE",
2931 "UNIQUE",
2932 "VLOOKUP",
2933 "XLOOKUP",
2934 "XMATCH",
2935 nullptr
2938 const char* aText[] = {
2939 "ARABIC",
2940 "ASC",
2941 "BAHTTEXT",
2942 "BASE",
2943 "CHAR",
2944 "CLEAN",
2945 "CODE",
2946 "CONCAT",
2947 "CONCATENATE",
2948 "DECIMAL",
2949 "DOLLAR",
2950 "ENCODEURL",
2951 "EXACT",
2952 "FILTERXML",
2953 "FIND",
2954 "FINDB",
2955 "FIXED",
2956 "JIS",
2957 "LEFT",
2958 "LEFTB",
2959 "LEN",
2960 "LENB",
2961 "LOWER",
2962 "MID",
2963 "MIDB",
2964 "NUMBERVALUE",
2965 "PROPER",
2966 "REGEX",
2967 "REPLACE",
2968 "REPLACEB",
2969 "REPT",
2970 "RIGHT",
2971 "RIGHTB",
2972 "ROMAN",
2973 "ROT13",
2974 "SEARCH",
2975 "SEARCHB",
2976 "SUBSTITUTE",
2977 "T",
2978 "TEXT",
2979 "TEXTJOIN",
2980 "TRIM",
2981 "UNICHAR",
2982 "UNICODE",
2983 "UPPER",
2984 "VALUE",
2985 "WEBSERVICE",
2986 nullptr
2989 struct {
2990 const char* Category; const char** Functions;
2991 } aTests[] = {
2992 { "Database", aDataBase },
2993 { "Date&Time", aDateTime },
2994 { "Financial", aFinancial },
2995 { "Information", aInformation },
2996 { "Logical", aLogical },
2997 { "Mathematical", aMathematical },
2998 { "Array", aArray },
2999 { "Statistical", aStatistical },
3000 { "Spreadsheet", aSpreadsheet },
3001 { "Text", aText },
3002 { "Add-in", nullptr },
3003 { nullptr, nullptr }
3006 ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
3007 sal_uInt32 n = pFuncMgr->getCount();
3008 for (sal_uInt32 i = 0; i < n; ++i)
3010 const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
3011 CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
3012 sal_uInt32 nFuncCount = pCat->getCount();
3013 for (sal_uInt32 j = 0; j < nFuncCount; ++j)
3015 const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
3016 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
3021 CPPUNIT_TEST_FIXTURE(Test, testGraphicsInGroup)
3023 m_pDoc->InsertTab(0, u"TestTab"_ustr);
3024 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
3025 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
3026 SCROW nRow1, nRow2;
3027 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3028 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
3029 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
3030 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
3032 m_pDoc->InitDrawLayer();
3033 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
3034 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
3035 SdrPage* pPage = pDrawLayer->GetPage(0);
3036 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
3039 //Add a square
3040 tools::Rectangle aOrigRect(2,2,100,100);
3041 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aOrigRect);
3042 pPage->InsertObject(pObj.get());
3043 const tools::Rectangle &rNewRect = pObj->GetLogicRect();
3044 CPPUNIT_ASSERT_EQUAL_MESSAGE("must have equal position and size",
3045 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3047 ScDrawLayer::SetPageAnchored(*pObj);
3049 //Use a range of rows guaranteed to include all of the square
3050 m_pDoc->ShowRows(0, 100, 0, false);
3051 m_pDoc->SetDrawPageSize(0);
3052 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3053 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3054 m_pDoc->ShowRows(0, 100, 0, true);
3055 m_pDoc->SetDrawPageSize(0);
3056 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3057 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3059 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
3060 CPPUNIT_ASSERT_EQUAL_MESSAGE("That shouldn't change size or positioning",
3061 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3063 m_pDoc->ShowRows(0, 100, 0, false);
3064 m_pDoc->SetDrawPageSize(0);
3066 CPPUNIT_ASSERT_EQUAL_MESSAGE("Hiding should not change the logic rectangle",
3067 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3068 CPPUNIT_ASSERT_MESSAGE("Hiding should make invisible", !pObj->IsVisible());
3070 m_pDoc->ShowRows(0, 100, 0, true);
3071 m_pDoc->SetDrawPageSize(0);
3072 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when cell anchored",
3073 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3074 CPPUNIT_ASSERT_MESSAGE("Show should make visible", pObj->IsVisible());
3078 // Add a circle.
3079 tools::Rectangle aOrigRect(10,10,210,210); // 200 x 200
3080 rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
3081 pPage->InsertObject(pObj.get());
3082 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3083 CPPUNIT_ASSERT_EQUAL_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
3084 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3086 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3087 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell anchored. Not good.",
3088 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3090 // Insert 2 rows at the top. This should push the circle object down.
3091 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3092 m_pDoc->SetDrawPageSize(0);
3094 // Make sure the size of the circle is still identical.
3095 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of the circle has changed, but shouldn't!",
3096 aOrigRect.GetSize(), rNewRect.GetSize());
3098 // Delete 2 rows at the top. This should bring the circle object to its original position.
3099 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3100 m_pDoc->SetDrawPageSize(0);
3101 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to move back to its original position.",
3102 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3106 // Add a line.
3107 basegfx::B2DPolygon aTempPoly;
3108 Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
3109 tools::Rectangle aOrigRect(10,200,110,300); // 100 x 100
3110 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
3111 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
3112 rtl::Reference<SdrPathObj> pObj = new SdrPathObj(*pDrawLayer, SdrObjKind::Line, basegfx::B2DPolyPolygon(aTempPoly));
3113 pObj->NbcSetLogicRect(aOrigRect);
3114 pPage->InsertObject(pObj.get());
3115 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3116 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size differ.",
3117 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3119 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3120 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell-anchored. Not good.",
3121 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3123 // Insert 2 rows at the top and delete them immediately.
3124 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3125 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3126 m_pDoc->SetDrawPageSize(0);
3127 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of a line object changed after row insertion and removal.",
3128 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3130 sal_Int32 n = pObj->GetPointCount();
3131 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 2 points in a line object.", static_cast<sal_Int32>(2), n);
3132 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3133 aStartPos, pObj->GetPoint(0));
3134 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3135 aEndPos, pObj->GetPoint(1));
3138 m_pDoc->DeleteTab(0);
3141 CPPUNIT_TEST_FIXTURE(Test, testGraphicsOnSheetMove)
3143 m_pDoc->InsertTab(0, u"Tab1"_ustr);
3144 m_pDoc->InsertTab(1, u"Tab2"_ustr);
3145 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 2 sheets to begin with", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
3147 m_pDoc->InitDrawLayer();
3148 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
3149 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
3150 SdrPage* pPage = pDrawLayer->GetPage(0);
3151 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
3153 // Insert an object.
3154 tools::Rectangle aObjRect(2,2,100,100);
3155 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
3156 pPage->InsertObject(pObj.get());
3157 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3159 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", static_cast<size_t>(1), pPage->GetObjCount());
3161 const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
3162 CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
3163 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3164 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3166 pPage = pDrawLayer->GetPage(1);
3167 CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
3168 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", static_cast<size_t>(0), pPage->GetObjCount());
3170 // Insert a new sheet at left-end, and make sure the object has moved to
3171 // the 2nd page.
3172 m_pDoc->InsertTab(0, u"NewTab"_ustr);
3173 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", static_cast<SCTAB>(3), m_pDoc->GetTableCount());
3174 pPage = pDrawLayer->GetPage(0);
3175 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3176 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3177 pPage = pDrawLayer->GetPage(1);
3178 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3179 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3180 pPage = pDrawLayer->GetPage(2);
3181 CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage);
3182 CPPUNIT_ASSERT_EQUAL_MESSAGE("3rd sheet should have no object.", size_t(0), pPage->GetObjCount());
3184 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3185 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3187 // Now, delete the sheet that just got inserted. The object should be back
3188 // on the 1st sheet.
3189 m_pDoc->DeleteTab(0);
3190 pPage = pDrawLayer->GetPage(0);
3191 CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage);
3192 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have one object.", size_t(1), pPage->GetObjCount());
3193 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size and position of the object shouldn't change.",
3194 aObjRect, pObj->GetLogicRect());
3196 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3197 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3199 // Move the 1st sheet to the last position.
3200 m_pDoc->MoveTab(0, 1);
3201 pPage = pDrawLayer->GetPage(0);
3202 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3203 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3204 pPage = pDrawLayer->GetPage(1);
3205 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3206 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3207 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3208 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3210 // Copy the 2nd sheet, which has one drawing object to the last position.
3211 m_pDoc->CopyTab(1, 2);
3212 pPage = pDrawLayer->GetPage(2);
3213 CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage);
3214 CPPUNIT_ASSERT_EQUAL_MESSAGE("Copied sheet should have one object.", size_t(1), pPage->GetObjCount());
3215 pObj = pPage->GetObj(0);
3216 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
3217 pData = ScDrawLayer::GetObjData(pObj.get());
3218 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
3219 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maStart.Tab());
3220 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maEnd.Tab());
3222 m_pDoc->DeleteTab(2);
3223 m_pDoc->DeleteTab(1);
3224 m_pDoc->DeleteTab(0);
3227 CPPUNIT_TEST_FIXTURE(Test, testToggleRefFlag)
3230 * Test toggling relative/absolute flag of cell and cell range references.
3231 * This corresponds with hitting Shift-F4 while the cursor is on a formula
3232 * cell.
3234 // In this test, there is no need to insert formula string into a cell in
3235 // the document, as ScRefFinder does not depend on the content of the
3236 // document except for the sheet names.
3238 m_pDoc->InsertTab(0, u"Test"_ustr);
3241 // Calc A1: basic 2D reference
3243 OUString aFormula(u"=B100"_ustr);
3244 ScAddress aPos(1, 5, 0);
3245 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3247 // Original
3248 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3250 // column relative / row relative -> column absolute / row absolute
3251 aFinder.ToggleRel(0, aFormula.getLength());
3252 aFormula = aFinder.GetText();
3253 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=$B$100"_ustr, aFormula );
3255 // column absolute / row absolute -> column relative / row absolute
3256 aFinder.ToggleRel(0, aFormula.getLength());
3257 aFormula = aFinder.GetText();
3258 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=B$100"_ustr, aFormula );
3260 // column relative / row absolute -> column absolute / row relative
3261 aFinder.ToggleRel(0, aFormula.getLength());
3262 aFormula = aFinder.GetText();
3263 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=$B100"_ustr, aFormula );
3265 // column absolute / row relative -> column relative / row relative
3266 aFinder.ToggleRel(0, aFormula.getLength());
3267 aFormula = aFinder.GetText();
3268 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", u"=B100"_ustr, aFormula );
3272 // Excel R1C1: basic 2D reference
3274 OUString aFormula(u"=R2C1"_ustr);
3275 ScAddress aPos(3, 5, 0);
3276 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3278 // Original
3279 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3281 // column absolute / row absolute -> column relative / row absolute
3282 aFinder.ToggleRel(0, aFormula.getLength());
3283 aFormula = aFinder.GetText();
3284 CPPUNIT_ASSERT_EQUAL(u"=R2C[-3]"_ustr, aFormula);
3286 // column relative / row absolute - > column absolute / row relative
3287 aFinder.ToggleRel(0, aFormula.getLength());
3288 aFormula = aFinder.GetText();
3289 CPPUNIT_ASSERT_EQUAL(u"=R[-4]C1"_ustr, aFormula);
3291 // column absolute / row relative -> column relative / row relative
3292 aFinder.ToggleRel(0, aFormula.getLength());
3293 aFormula = aFinder.GetText();
3294 CPPUNIT_ASSERT_EQUAL(u"=R[-4]C[-3]"_ustr, aFormula);
3296 // column relative / row relative -> column absolute / row absolute
3297 aFinder.ToggleRel(0, aFormula.getLength());
3298 aFormula = aFinder.GetText();
3299 CPPUNIT_ASSERT_EQUAL(u"=R2C1"_ustr, aFormula);
3303 // Excel R1C1: Selection at the end of the formula string and does not
3304 // overlap the formula string at all (inspired by fdo#39135).
3305 OUString aFormula(u"=R1C1"_ustr);
3306 ScAddress aPos(1, 1, 0);
3307 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3309 // Original
3310 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3312 // Make the column relative.
3313 sal_Int32 n = aFormula.getLength();
3314 aFinder.ToggleRel(n, n);
3315 aFormula = aFinder.GetText();
3316 CPPUNIT_ASSERT_EQUAL(u"=R1C[-1]"_ustr, aFormula);
3318 // Make the row relative.
3319 n = aFormula.getLength();
3320 aFinder.ToggleRel(n, n);
3321 aFormula = aFinder.GetText();
3322 CPPUNIT_ASSERT_EQUAL(u"=R[-1]C1"_ustr, aFormula);
3324 // Make both relative.
3325 n = aFormula.getLength();
3326 aFinder.ToggleRel(n, n);
3327 aFormula = aFinder.GetText();
3328 CPPUNIT_ASSERT_EQUAL(u"=R[-1]C[-1]"_ustr, aFormula);
3330 // Back to the original.
3331 n = aFormula.getLength();
3332 aFinder.ToggleRel(n, n);
3333 aFormula = aFinder.GetText();
3334 CPPUNIT_ASSERT_EQUAL(u"=R1C1"_ustr, aFormula);
3338 // Calc A1:
3339 OUString aFormula(u"=A1+4"_ustr);
3340 ScAddress aPos(1, 1, 0);
3341 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3343 // Original
3344 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3346 // Set the cursor over the 'A1' part and toggle.
3347 aFinder.ToggleRel(2, 2);
3348 aFormula = aFinder.GetText();
3349 CPPUNIT_ASSERT_EQUAL(u"=$A$1+4"_ustr, aFormula);
3351 aFinder.ToggleRel(2, 2);
3352 aFormula = aFinder.GetText();
3353 CPPUNIT_ASSERT_EQUAL(u"=A$1+4"_ustr, aFormula);
3355 aFinder.ToggleRel(2, 2);
3356 aFormula = aFinder.GetText();
3357 CPPUNIT_ASSERT_EQUAL(u"=$A1+4"_ustr, aFormula);
3359 aFinder.ToggleRel(2, 2);
3360 aFormula = aFinder.GetText();
3361 CPPUNIT_ASSERT_EQUAL(u"=A1+4"_ustr, aFormula);
3364 // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
3365 // partial selection within formula string.
3367 m_pDoc->DeleteTab(0);
3370 CPPUNIT_TEST_FIXTURE(Test, testAutofilter)
3372 m_pDoc->InsertTab( 0, u"Test"_ustr );
3374 // cell contents (0 = empty cell)
3375 const char* aData[][3] = {
3376 { "C1", "C2", "C3" },
3377 { "0", "1", "A" },
3378 { "1", "2", nullptr },
3379 { "1", "2", "B" },
3380 { "0", "2", "B" }
3383 SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
3384 SCROW nRows = SAL_N_ELEMENTS(aData);
3386 // Populate cells.
3387 for (SCROW i = 0; i < nRows; ++i)
3388 for (SCCOL j = 0; j < nCols; ++j)
3389 if (aData[i][j])
3390 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
3392 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols-1, nRows-1);
3393 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3395 pDBData->SetAutoFilter(true);
3396 ScRange aRange;
3397 pDBData->GetArea(aRange);
3398 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3399 aRange.aEnd.Col(), aRange.aStart.Row(),
3400 aRange.aStart.Tab(), ScMF::Auto);
3402 //create the query param
3403 ScQueryParam aParam;
3404 pDBData->GetQueryParam(aParam);
3405 ScQueryEntry& rEntry = aParam.GetEntry(0);
3406 rEntry.bDoQuery = true;
3407 rEntry.nField = 0;
3408 rEntry.eOp = SC_EQUAL;
3409 rEntry.GetQueryItem().mfVal = 0;
3410 // add queryParam to database range.
3411 pDBData->SetQueryParam(aParam);
3413 // perform the query.
3414 m_pDoc->Query(0, aParam, true);
3416 //control output
3417 SCROW nRow1, nRow2;
3418 bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3419 CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden);
3420 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(2), nRow1);
3421 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(3), nRow2);
3423 // Remove filtering.
3424 rEntry.Clear();
3425 m_pDoc->Query(0, aParam, true);
3426 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3427 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden);
3428 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3429 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3431 // Filter for non-empty cells by column C.
3432 rEntry.bDoQuery = true;
3433 rEntry.nField = 2;
3434 rEntry.SetQueryByNonEmpty();
3435 m_pDoc->Query(0, aParam, true);
3437 // only row 3 should be hidden. The rest should be visible.
3438 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3439 CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden);
3440 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(0), nRow1);
3441 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(1), nRow2);
3442 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3443 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden);
3444 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow1);
3445 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow2);
3446 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3447 CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden);
3448 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", SCROW(3), nRow1);
3449 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", m_pDoc->MaxRow(), nRow2);
3451 // Now, filter for empty cells by column C.
3452 rEntry.SetQueryByEmpty();
3453 m_pDoc->Query(0, aParam, true);
3455 // Now, only row 1 and 3, and 6 and down should be visible.
3456 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3457 CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden);
3458 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow1);
3459 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow2);
3460 bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
3461 CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden);
3462 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow1);
3463 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow2);
3464 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3465 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden);
3466 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow1);
3467 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow2);
3468 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3469 CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden);
3470 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(3), nRow1);
3471 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(4), nRow2);
3472 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3473 CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden);
3474 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", SCROW(5), nRow1);
3475 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", m_pDoc->MaxRow(), nRow2);
3477 m_pDoc->DeleteTab(0);
3480 CPPUNIT_TEST_FIXTURE(Test, testAutoFilterTimeValue)
3482 m_pDoc->InsertTab(0, u"Test"_ustr);
3484 m_pDoc->SetString(ScAddress(0,0,0), u"Hours"_ustr);
3485 m_pDoc->SetValue(ScAddress(0,1,0), 72.3604166666671);
3486 m_pDoc->SetValue(ScAddress(0,2,0), 265);
3488 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 0, 2);
3489 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3491 // Apply the "hour:minute:second" format to A2:A3.
3492 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3493 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_TIME_HH_MMSS, LANGUAGE_ENGLISH_US);
3494 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3495 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3496 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3498 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 2, 0, aNewAttrs); // apply it to A2:A3.
3500 printRange(m_pDoc, ScRange(0,0,0,0,2,0), "Data"); // A1:A3
3502 // Make sure the hour:minute:second format is really applied.
3503 CPPUNIT_ASSERT_EQUAL(u"1736:39:00"_ustr, m_pDoc->GetString(ScAddress(0,1,0))); // A2
3504 CPPUNIT_ASSERT_EQUAL(u"6360:00:00"_ustr, m_pDoc->GetString(ScAddress(0,2,0))); // A3
3506 // Filter by the A2 value. Only A1 and A2 should be visible.
3507 ScQueryParam aParam;
3508 pDBData->GetQueryParam(aParam);
3509 ScQueryEntry& rEntry = aParam.GetEntry(0);
3510 rEntry.bDoQuery = true;
3511 rEntry.nField = 0;
3512 rEntry.eOp = SC_EQUAL;
3513 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern(u"1736:39:00"_ustr);
3514 rEntry.GetQueryItem().meType = ScQueryEntry::ByString;
3516 pDBData->SetQueryParam(aParam);
3518 // perform the query.
3519 m_pDoc->Query(0, aParam, true);
3521 // A1:A2 should be visible while A3 should be filtered out.
3522 CPPUNIT_ASSERT_MESSAGE("A1 should be visible.", !m_pDoc->RowFiltered(0,0));
3523 CPPUNIT_ASSERT_MESSAGE("A2 should be visible.", !m_pDoc->RowFiltered(1,0));
3524 CPPUNIT_ASSERT_MESSAGE("A3 should be filtered out.", m_pDoc->RowFiltered(2,0));
3526 m_pDoc->DeleteTab(0);
3529 CPPUNIT_TEST_FIXTURE(Test, testAutofilterOptimizations)
3531 m_pDoc->InsertTab( 0, u"Test"_ustr );
3533 constexpr SCCOL nCols = 4;
3534 constexpr SCROW nRows = 200;
3535 m_pDoc->SetString(0, 0, 0, u"Column1"_ustr);
3536 m_pDoc->SetString(1, 0, 0, u"Column2"_ustr);
3537 m_pDoc->SetString(2, 0, 0, u"Column3"_ustr);
3538 m_pDoc->SetString(3, 0, 0, u"Column4"_ustr);
3540 // Fill 1st column with 0-199, 2nd with 1-200, 3rd with "1000"-"1199", 4th with "1001-1200"
3541 // (the pairs are off by one to each other to check filtering out a value filters out
3542 // only the relevant column).
3543 for(SCROW i = 0; i < nRows; ++i)
3545 m_pDoc->SetValue(0, i + 1, 0, i);
3546 m_pDoc->SetValue(1, i + 1, 0, i+1);
3547 m_pDoc->SetString(2, i + 1, 0, "val" + OUString::number(i+1000));
3548 m_pDoc->SetString(3, i + 1, 0, "val" + OUString::number(i+1000+1));
3551 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols, nRows);
3552 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3554 pDBData->SetAutoFilter(true);
3555 ScRange aRange;
3556 pDBData->GetArea(aRange);
3557 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3558 aRange.aEnd.Col(), aRange.aStart.Row(),
3559 aRange.aStart.Tab(), ScMF::Auto);
3561 //create the query param
3562 ScQueryParam aParam;
3563 pDBData->GetQueryParam(aParam);
3564 ScQueryEntry& rEntry0 = aParam.GetEntry(0);
3565 rEntry0.bDoQuery = true;
3566 rEntry0.nField = 0;
3567 rEntry0.eOp = SC_EQUAL;
3568 rEntry0.GetQueryItems().resize(nRows);
3569 ScQueryEntry& rEntry1 = aParam.GetEntry(1);
3570 rEntry1.bDoQuery = true;
3571 rEntry1.nField = 1;
3572 rEntry1.eOp = SC_EQUAL;
3573 rEntry1.GetQueryItems().resize(nRows);
3574 ScQueryEntry& rEntry2 = aParam.GetEntry(2);
3575 rEntry2.bDoQuery = true;
3576 rEntry2.nField = 2;
3577 rEntry2.eOp = SC_EQUAL;
3578 rEntry2.GetQueryItems().resize(nRows);
3579 ScQueryEntry& rEntry3 = aParam.GetEntry(3);
3580 rEntry3.bDoQuery = true;
3581 rEntry3.nField = 3;
3582 rEntry3.eOp = SC_EQUAL;
3583 rEntry3.GetQueryItems().resize(nRows);
3584 // Set up autofilter to select all values except one in each column.
3585 // This should only filter out 2nd, 3rd, 6th and 7th rows.
3586 for( int i = 0; i < nRows; ++i )
3588 if(i!= 1)
3589 rEntry0.GetQueryItems()[i].mfVal = i;
3590 if(i!= 2)
3591 rEntry1.GetQueryItems()[i].mfVal = i + 1;
3592 if(i!= 5)
3594 rEntry2.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000));
3595 rEntry2.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3597 if(i!= 6)
3599 rEntry3.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000+1));
3600 rEntry3.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3603 // add queryParam to database range.
3604 pDBData->SetQueryParam(aParam);
3606 // perform the query.
3607 m_pDoc->Query(0, aParam, true);
3609 // check that only rows with filtered out values are hidden, and not rows that share
3610 // a value in a different column
3611 SCROW nRow1, nRow2;
3612 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0, &nRow1, &nRow2));
3613 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden", m_pDoc->RowHidden(2, 0, &nRow1, &nRow2));
3614 CPPUNIT_ASSERT_MESSAGE("row 4 should be hidden", m_pDoc->RowHidden(3, 0, &nRow1, &nRow2));
3615 CPPUNIT_ASSERT_MESSAGE("row 5 should be visible", !m_pDoc->RowHidden(4, 0, &nRow1, &nRow2));
3616 CPPUNIT_ASSERT_MESSAGE("row 6 should be visible", !m_pDoc->RowHidden(5, 0, &nRow1, &nRow2));
3617 CPPUNIT_ASSERT_MESSAGE("row 7 should be hidden", m_pDoc->RowHidden(6, 0, &nRow1, &nRow2));
3618 CPPUNIT_ASSERT_MESSAGE("row 8 should be hidden", m_pDoc->RowHidden(7, 0, &nRow1, &nRow2));
3619 CPPUNIT_ASSERT_MESSAGE("row 9 should be visible", !m_pDoc->RowHidden(8, 0, &nRow1, &nRow2));
3621 // Remove filtering.
3622 rEntry0.Clear();
3623 rEntry1.Clear();
3624 rEntry2.Clear();
3625 m_pDoc->Query(0, aParam, true);
3626 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !m_pDoc->RowHidden(0, 0, &nRow1, &nRow2));
3627 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3628 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3630 m_pDoc->DeleteTab(0);
3633 CPPUNIT_TEST_FIXTURE(Test, testTdf76441)
3635 m_pDoc->InsertTab(0, u"Test"_ustr);
3637 // The result will be different depending on whether the format is set before
3638 // or after inserting the string
3640 OUString aCode = u"MM:SS"_ustr;
3641 sal_Int32 nCheckPos;
3642 SvNumFormatType nType;
3643 sal_uInt32 nFormat;
3644 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3645 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3647 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3648 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3649 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3651 // First insert the string, then the format
3652 m_pDoc->SetString(ScAddress(0,0,0), u"01:20"_ustr);
3654 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3656 CPPUNIT_ASSERT_EQUAL(u"20:00"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3660 // First set the format, then insert the string
3661 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3663 m_pDoc->SetString(ScAddress(0,1,0), u"01:20"_ustr);
3665 // Without the fix in place, this test would have failed with
3666 // - Expected: 01:20
3667 // - Actual : 20:00
3668 CPPUNIT_ASSERT_EQUAL(u"01:20"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3671 m_pDoc->DeleteTab(0);
3674 CPPUNIT_TEST_FIXTURE(Test, testTdf76836)
3676 m_pDoc->InsertTab(0, u"Test"_ustr);
3678 OUString aCode = u"\"192.168.0.\"@"_ustr;
3679 sal_Int32 nCheckPos;
3680 SvNumFormatType nType;
3681 sal_uInt32 nFormat;
3682 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3683 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3685 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3686 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3687 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3689 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3690 m_pDoc->SetValue(0,0,0, 10.0);
3692 // Without the fix in place, this test would have failed with
3693 // - Expected: 10
3694 // - Actual : 192.168.0.10
3695 CPPUNIT_ASSERT_EQUAL(u"10"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3697 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3698 m_pDoc->SetString(ScAddress(0,1,0), u"10"_ustr);
3699 CPPUNIT_ASSERT_EQUAL(u"192.168.0.10"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3701 m_pDoc->DeleteTab(0);
3704 CPPUNIT_TEST_FIXTURE(Test, testTdf151752)
3706 m_pDoc->InsertTab(0, u"Test"_ustr);
3708 m_pDoc->SetString(ScAddress(0,0,0), u"66000:00"_ustr);
3710 // Without the fix in place, this test would have failed with
3711 // - Expected: 66000:00:00
3712 // - Actual : 464:00:00
3713 CPPUNIT_ASSERT_EQUAL(u"66000:00:00"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3715 m_pDoc->DeleteTab(0);
3718 CPPUNIT_TEST_FIXTURE(Test, testTdf142186)
3720 m_pDoc->InsertTab(0, u"Test"_ustr);
3722 // The result will be different depending on whether the format is set before
3723 // or after inserting the string
3725 OUString aCode = u"0\".\"0"_ustr;
3726 sal_Int32 nCheckPos;
3727 SvNumFormatType nType;
3728 sal_uInt32 nFormat;
3729 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3730 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3732 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3733 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3734 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3736 // First insert the string, then the format
3737 m_pDoc->SetString(ScAddress(0,0,0), u"123.45"_ustr);
3739 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3741 CPPUNIT_ASSERT_EQUAL(u"12.3"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3745 // First set the format, then insert the string
3746 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3748 m_pDoc->SetString(ScAddress(0,1,0), u"123.45"_ustr);
3750 // Without the fix in place, this test would have failed with
3751 // - Expected: 12.3
3752 // - Actual : 1234.5
3753 CPPUNIT_ASSERT_EQUAL(u"12.3"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3756 m_pDoc->DeleteTab(0);
3759 CPPUNIT_TEST_FIXTURE(Test, testTdf137063)
3761 m_pDoc->InsertTab(0, u"Test"_ustr);
3763 m_pDoc->SetValue(0,0,0, 0.000000006);
3764 m_pDoc->SetValue(0,1,0, 0.0000000006);
3766 // Without the fix in place, this test would have failed with
3767 // - Expected: 0.000000006
3768 // - Actual : 6E-09
3769 CPPUNIT_ASSERT_EQUAL(u"0.000000006"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3770 CPPUNIT_ASSERT_EQUAL(u"6E-10"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
3772 m_pDoc->DeleteTab(0);
3775 CPPUNIT_TEST_FIXTURE(Test, testTdf126342)
3777 m_pDoc->InsertTab(0, u"Test"_ustr);
3779 OUString aCode = u"YYYY-MM-DD"_ustr;
3780 sal_Int32 nCheckPos;
3781 SvNumFormatType nType;
3782 sal_uInt32 nFormat;
3783 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3784 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3786 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3787 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3788 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3789 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3791 m_pDoc->SetString(ScAddress(0,0,0), u"11/7/19"_ustr);
3793 CPPUNIT_ASSERT_EQUAL(u"2019-11-07"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3795 // Overwrite the existing date with the exact same input
3796 m_pDoc->SetString(ScAddress(0,0,0), u"11/7/19"_ustr);
3798 // Without the fix in place, this test would have failed with
3799 // - Expected: 2019-11-07
3800 // - Actual : 2011-07-19
3801 CPPUNIT_ASSERT_EQUAL(u"2019-11-07"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
3803 m_pDoc->DeleteTab(0);
3806 CPPUNIT_TEST_FIXTURE(Test, testAdvancedFilter)
3808 m_pDoc->InsertTab(0, u"Test"_ustr);
3810 // cell contents (nullptr = empty cell)
3811 std::vector<std::vector<const char*>> aData = {
3812 { "Value", "Tag" }, // A1:B11
3813 { "1", "R" },
3814 { "2", "R" },
3815 { "3", "R" },
3816 { "4", "C" },
3817 { "5", "C" },
3818 { "6", "C" },
3819 { "7", "R" },
3820 { "8", "R" },
3821 { "9", "R" },
3822 { "10", "C" },
3823 { nullptr },
3824 { "Value", "Tag" }, // A13:B14
3825 { "> 5", "R" },
3828 // Populate cells.
3829 for (size_t nRow = 0; nRow < aData.size(); ++nRow)
3831 const std::vector<const char*>& rRowData = aData[nRow];
3832 for (size_t nCol = 0; nCol < rRowData.size(); ++nCol)
3834 const char* pCell = rRowData[nCol];
3835 if (pCell)
3836 m_pDoc->SetString(nCol, nRow, 0, OUString::createFromAscii(pCell));
3840 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 1, 10);
3841 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3843 ScRange aDataRange(0,0,0,1,10,0);
3844 ScRange aFilterRuleRange(0,12,0,1,13,0);
3846 printRange(m_pDoc, aDataRange, "Data");
3847 printRange(m_pDoc, aFilterRuleRange, "Filter Rule");
3849 ScQueryParam aQueryParam;
3850 aQueryParam.bHasHeader = true;
3851 aQueryParam.nCol1 = aDataRange.aStart.Col();
3852 aQueryParam.nRow1 = aDataRange.aStart.Row();
3853 aQueryParam.nCol2 = aDataRange.aEnd.Col();
3854 aQueryParam.nRow2 = aDataRange.aEnd.Row();
3855 aQueryParam.nTab = aDataRange.aStart.Tab();
3857 bool bGood = m_pDoc->CreateQueryParam(aFilterRuleRange, aQueryParam);
3858 CPPUNIT_ASSERT_MESSAGE("failed to create query param.", bGood);
3860 // First entry is for the 'Value' field, and is greater than 5.
3861 ScQueryEntry aEntry = aQueryParam.GetEntry(0);
3862 CPPUNIT_ASSERT(aEntry.bDoQuery);
3863 CPPUNIT_ASSERT_EQUAL(SCCOLROW(0), aEntry.nField);
3864 CPPUNIT_ASSERT_EQUAL(SC_GREATER, aEntry.eOp);
3866 ScQueryEntry::QueryItemsType aItems = aEntry.GetQueryItems();
3867 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3868 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByValue, aItems[0].meType);
3869 CPPUNIT_ASSERT_EQUAL(5.0, aItems[0].mfVal);
3871 // Second entry is for the 'Tag' field, and is == 'R'.
3872 aEntry = aQueryParam.GetEntry(1);
3873 CPPUNIT_ASSERT(aEntry.bDoQuery);
3874 CPPUNIT_ASSERT_EQUAL(SCCOLROW(1), aEntry.nField);
3875 CPPUNIT_ASSERT_EQUAL(SC_EQUAL, aEntry.eOp);
3877 aItems = aEntry.GetQueryItems();
3878 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3879 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByString, aItems[0].meType);
3880 CPPUNIT_ASSERT_EQUAL(u"R"_ustr, aItems[0].maString.getString());
3882 // perform the query.
3883 m_pDoc->Query(0, aQueryParam, true);
3885 // Only rows 1,8-10 should be visible.
3886 bool bFiltered = m_pDoc->RowFiltered(0, 0);
3887 CPPUNIT_ASSERT_MESSAGE("row 1 (header row) should be visible", !bFiltered);
3889 SCROW nRow1 = -1, nRow2 = -1;
3890 bFiltered = m_pDoc->RowFiltered(1, 0, &nRow1, &nRow2);
3891 CPPUNIT_ASSERT_MESSAGE("rows 2-7 should be filtered out.", bFiltered);
3892 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(1), nRow1);
3893 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(6), nRow2);
3895 bFiltered = m_pDoc->RowFiltered(7, 0, &nRow1, &nRow2);
3896 CPPUNIT_ASSERT_MESSAGE("rows 8-10 should be visible.", !bFiltered);
3897 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(7), nRow1);
3898 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(9), nRow2);
3900 m_pDoc->DeleteTab(0);
3903 CPPUNIT_TEST_FIXTURE(Test, testDateFilterContains)
3905 m_pDoc->InsertTab(0, u"Test"_ustr);
3907 constexpr SCCOL nCols = 1;
3908 constexpr SCROW nRows = 5;
3909 m_pDoc->SetString(0, 0, 0, u"Date"_ustr);
3910 m_pDoc->SetString(0, 1, 0, u"1/2/2021"_ustr);
3911 m_pDoc->SetString(0, 2, 0, u"2/1/1999"_ustr);
3912 m_pDoc->SetString(0, 3, 0, u"2/1/1997"_ustr);
3913 m_pDoc->SetString(0, 4, 0, u"3/3/2001"_ustr);
3914 m_pDoc->SetString(0, 5, 0, u"3/3/1996"_ustr);
3916 // Set the fields as dates.
3917 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3918 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_DATE_DIN_YYMMDD, LANGUAGE_ENGLISH_US);
3919 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
3920 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3921 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3922 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 5, 0, aNewAttrs); // apply it to A1:A6
3924 ScDBData* pDBData = new ScDBData(u"NONAME"_ustr, 0, 0, 0, nCols, nRows);
3925 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3927 pDBData->SetAutoFilter(true);
3928 ScRange aRange;
3929 pDBData->GetArea(aRange);
3930 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3931 aRange.aEnd.Col(), aRange.aStart.Row(),
3932 aRange.aStart.Tab(), ScMF::Auto);
3934 //create the query param
3935 ScQueryParam aParam;
3936 pDBData->GetQueryParam(aParam);
3937 ScQueryEntry& rEntry = aParam.GetEntry(0);
3938 rEntry.bDoQuery = true;
3939 rEntry.nField = 0;
3940 rEntry.eOp = SC_CONTAINS;
3941 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern(u"2"_ustr);
3942 pDBData->SetQueryParam(aParam);
3944 // perform the query.
3945 m_pDoc->Query(0, aParam, true);
3947 // Dates in rows 2-4 contain '2', row 5 shows 2001 only as 01, and row 6 doesn't contain it at all.
3948 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0));
3949 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible", !m_pDoc->RowHidden(2, 0));
3950 CPPUNIT_ASSERT_MESSAGE("row 4 should be visible", !m_pDoc->RowHidden(3, 0));
3951 CPPUNIT_ASSERT_MESSAGE("row 5 should be hidden", m_pDoc->RowHidden(4, 0));
3952 CPPUNIT_ASSERT_MESSAGE("row 6 should be hidden", m_pDoc->RowHidden(5, 0));
3954 m_pDoc->DeleteTab(0);
3957 CPPUNIT_TEST_FIXTURE(Test, testTdf98642)
3959 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
3960 m_pDoc->SetString(0, 0, 0, u"test"_ustr);
3962 ScRangeData* pName1 = new ScRangeData( *m_pDoc, u"name1"_ustr, u"$Sheet1.$A$1"_ustr);
3963 ScRangeData* pName2 = new ScRangeData( *m_pDoc, u"name2"_ustr, u"$Sheet1.$A$1"_ustr);
3965 std::unique_ptr<ScRangeName> pGlobalRangeName(new ScRangeName());
3966 pGlobalRangeName->insert(pName1);
3967 pGlobalRangeName->insert(pName2);
3968 m_pDoc->SetRangeName(std::move(pGlobalRangeName));
3970 m_pDoc->SetString(1, 0, 0, u"=name1"_ustr);
3971 m_pDoc->SetString(1, 1, 0, u"=name2"_ustr);
3973 CPPUNIT_ASSERT_EQUAL(u"test"_ustr, m_pDoc->GetString(1, 0, 0));
3974 CPPUNIT_ASSERT_EQUAL(u"test"_ustr, m_pDoc->GetString(1, 1, 0));
3976 OUString aFormula = m_pDoc->GetFormula(1,0,0);
3977 CPPUNIT_ASSERT_EQUAL(u"=name1"_ustr, aFormula);
3978 aFormula = m_pDoc->GetFormula(1,1,0);
3980 // Without the fix in place, this test would have failed with
3981 // - Expected: =name2
3982 // - Actual : =name1
3983 CPPUNIT_ASSERT_EQUAL(u"=name2"_ustr, aFormula);
3985 m_pDoc->DeleteTab(0);
3988 CPPUNIT_TEST_FIXTURE(Test, testMergedCells)
3990 //test merge and unmerge
3991 //TODO: an undo/redo test for this would be a good idea
3992 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
3993 m_pDoc->DoMerge(1, 1, 3, 3, 0, false);
3994 SCCOL nEndCol = 1;
3995 SCROW nEndRow = 1;
3996 m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0);
3997 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCCOL(3), nEndCol);
3998 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCROW(3), nEndRow);
3999 ScRange aRange(0,2,0,m_pDoc->MaxCol(),2,0);
4000 ScMarkData aMark(m_pDoc->GetSheetLimits());
4001 aMark.SetMarkArea(aRange);
4002 m_xDocShell->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS_BEFORE, true, true);
4003 m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0);
4004 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCCOL(3), nEndCol);
4005 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCROW(4), nEndRow);
4006 m_pDoc->DeleteTab(0);
4009 CPPUNIT_TEST_FIXTURE(Test, testRenameTable)
4011 //test set rename table
4012 //TODO: set name1 and name2 and do an undo to check if name 1 is set now
4013 //TODO: also check if new name for table is same as another table
4015 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4016 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4018 //test case 1 , rename table2 to sheet 1, it should return error
4019 OUString nameToSet = u"Sheet1"_ustr;
4020 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4021 CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
4023 //test case 2 , simple rename to check name
4024 nameToSet = "test1";
4025 m_xDocShell->GetDocFunc().RenameTable(0,nameToSet,false,true);
4026 OUString nameJustSet;
4027 m_pDoc->GetName(0,nameJustSet);
4028 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4030 //test case 3 , rename again
4031 OUString anOldName;
4032 m_pDoc->GetName(0,anOldName);
4034 nameToSet = "test2";
4035 rDocFunc.RenameTable(0,nameToSet,false,true);
4036 m_pDoc->GetName(0,nameJustSet);
4037 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4039 //test case 4 , check if undo works
4040 SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShell.get(),0,anOldName,nameToSet);
4041 pUndo->Undo();
4042 m_pDoc->GetName(0,nameJustSet);
4043 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct name is not set after undo", nameJustSet, anOldName);
4045 pUndo->Redo();
4046 m_pDoc->GetName(0,nameJustSet);
4047 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after redo", nameJustSet, nameToSet);
4048 delete pUndo;
4050 m_pDoc->DeleteTab(0);
4051 m_pDoc->DeleteTab(1);
4054 CPPUNIT_TEST_FIXTURE(Test, testSetBackgroundColor)
4056 //test set background color
4057 //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
4059 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4060 Color aColor;
4062 //test yellow
4063 aColor=COL_YELLOW;
4064 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4065 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set",
4066 aColor, m_pDoc->GetTabBgColor(0));
4068 Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
4069 aColor = COL_BLUE;
4070 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4071 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set the second time",
4072 aColor, m_pDoc->GetTabBgColor(0));
4074 //now check for undo
4075 SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShell.get(), 0, aOldTabBgColor, aColor);
4076 pUndo->Undo();
4077 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aOldTabBgColor, m_pDoc->GetTabBgColor(0));
4078 pUndo->Redo();
4079 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aColor, m_pDoc->GetTabBgColor(0));
4080 delete pUndo;
4081 m_pDoc->DeleteTab(0);
4084 CPPUNIT_TEST_FIXTURE(Test, testUpdateReference)
4086 //test that formulas are correctly updated during sheet delete
4087 //TODO: add tests for relative references, updating of named ranges, ...
4088 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4089 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4090 m_pDoc->InsertTab(2, u"Sheet3"_ustr);
4091 m_pDoc->InsertTab(3, u"Sheet4"_ustr);
4093 m_pDoc->SetValue(0,0,2, 1);
4094 m_pDoc->SetValue(1,0,2, 2);
4095 m_pDoc->SetValue(1,1,3, 4);
4096 m_pDoc->SetString(2,0,2, u"=A1+B1"_ustr);
4097 m_pDoc->SetString(2,1,2, u"=Sheet4.B2+A1"_ustr);
4099 double aValue;
4100 aValue = m_pDoc->GetValue(2,0,2);
4101 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 3, aValue);
4102 aValue = m_pDoc->GetValue(2,1,2);
4103 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 5, aValue);
4105 //test deleting both sheets: one is not directly before the sheet, the other one is
4106 m_pDoc->DeleteTab(0);
4107 aValue = m_pDoc->GetValue(2,0,1);
4108 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 3, aValue);
4109 aValue = m_pDoc->GetValue(2,1,1);
4110 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 5, aValue);
4112 m_pDoc->DeleteTab(0);
4113 aValue = m_pDoc->GetValue(2,0,0);
4114 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 3, aValue);
4115 aValue = m_pDoc->GetValue(2,1,0);
4116 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 5, aValue);
4118 //test adding two sheets
4119 m_pDoc->InsertTab(0, u"Sheet2"_ustr);
4120 aValue = m_pDoc->GetValue(2,0,1);
4121 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 3, aValue);
4122 aValue = m_pDoc->GetValue(2,1,1);
4123 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 5, aValue);
4125 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4126 aValue = m_pDoc->GetValue(2,0,2);
4127 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 3, aValue);
4128 aValue = m_pDoc->GetValue(2,1,2);
4129 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 5, aValue);
4131 //test new DeleteTabs/InsertTabs methods
4132 m_pDoc->DeleteTabs(0, 2);
4133 aValue = m_pDoc->GetValue(2, 0, 0);
4134 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 3, aValue);
4135 aValue = m_pDoc->GetValue(2, 1, 0);
4136 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 5, aValue);
4138 std::vector<OUString> aSheets;
4139 aSheets.emplace_back("Sheet1");
4140 aSheets.emplace_back("Sheet2");
4141 m_pDoc->InsertTabs(0, aSheets, true);
4142 aValue = m_pDoc->GetValue(2, 0, 2);
4144 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 3, aValue);
4145 aValue = m_pDoc->GetValue(2, 1, 2);
4146 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 5, aValue);
4148 m_pDoc->DeleteTab(3);
4149 m_pDoc->DeleteTab(2);
4150 m_pDoc->DeleteTab(1);
4151 m_pDoc->DeleteTab(0);
4153 // Test positional update and invalidation of lookup cache for insertion
4154 // and deletion within entire column reference.
4155 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4156 m_pDoc->InsertTab(1, u"Sheet2"_ustr);
4157 m_pDoc->SetString(0,1,0, u"s1"_ustr);
4158 m_pDoc->SetString(0,0,1, u"=MATCH(\"s1\";Sheet1.A:A;0)"_ustr);
4159 aValue = m_pDoc->GetValue(0,0,1);
4160 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4161 m_pDoc->InsertRow(0,0,m_pDoc->MaxCol(),0,0,1); // insert 1 row before row 1 in Sheet1
4162 aValue = m_pDoc->GetValue(0,0,1);
4163 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 3, aValue);
4164 m_pDoc->DeleteRow(0,0,m_pDoc->MaxCol(),0,0,1); // delete row 1 in Sheet1
4165 aValue = m_pDoc->GetValue(0,0,1);
4166 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4167 m_pDoc->DeleteTab(1);
4168 m_pDoc->DeleteTab(0);
4171 CPPUNIT_TEST_FIXTURE(Test, testSearchCells)
4173 m_pDoc->InsertTab(0, u"Test"_ustr);
4175 m_pDoc->SetString(ScAddress(0,0,0), u"A"_ustr);
4176 m_pDoc->SetString(ScAddress(0,1,0), u"B"_ustr);
4177 m_pDoc->SetString(ScAddress(0,2,0), u"A"_ustr);
4178 // Leave A4 blank.
4179 m_pDoc->SetString(ScAddress(0,4,0), u"A"_ustr);
4180 m_pDoc->SetString(ScAddress(0,5,0), u"B"_ustr);
4181 m_pDoc->SetString(ScAddress(0,6,0), u"C"_ustr);
4183 SvxSearchItem aItem(SID_SEARCH_ITEM);
4184 aItem.SetSearchString(u"A"_ustr);
4185 aItem.SetCommand(SvxSearchCmd::FIND_ALL);
4186 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4187 aMarkData.SelectOneTable(0);
4188 SCCOL nCol = 0;
4189 SCROW nRow = 0;
4190 SCTAB nTab = 0;
4191 ScRangeList aMatchedRanges;
4192 OUString aUndoStr;
4193 bool bMatchedRangesWereClamped = false;
4194 bool bSuccess = m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped);
4196 CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
4197 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 3 matching cells.", size_t(3), aMatchedRanges.size());
4198 ScAddress aHit(0,0,0);
4199 CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4200 aHit.SetRow(2);
4201 CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4202 aHit.SetRow(4);
4203 CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4205 m_pDoc->DeleteTab(0);
4208 CPPUNIT_TEST_FIXTURE(Test, testFormulaPosition)
4210 m_pDoc->InsertTab(0, u"Test"_ustr);
4212 ScAddress aPos(0,0,0); // A1
4213 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4214 aPos.IncRow(); // A2
4215 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4216 aPos.SetRow(3); // A4;
4217 m_pDoc->SetString(aPos, u"=ROW()"_ustr);
4220 SCROW aRows[] = { 0, 1, 3 };
4221 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4222 CPPUNIT_ASSERT(bRes);
4225 m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
4227 SCROW aRows[] = { 0, 6, 8 };
4228 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4229 CPPUNIT_ASSERT(bRes);
4232 m_pDoc->DeleteTab(0);
4235 namespace {
4237 bool hasRange(const ScDocument* pDoc, const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange, const ScAddress& rPos)
4239 for (const ScTokenRef& p : rRefTokens)
4241 if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4242 continue;
4244 switch (p->GetType())
4246 case formula::svSingleRef:
4248 ScSingleRefData aData = *p->GetSingleRef();
4249 if (rRange.aStart != rRange.aEnd)
4250 break;
4252 ScAddress aThis = aData.toAbs(*pDoc, rPos);
4253 if (aThis == rRange.aStart)
4254 return true;
4256 break;
4257 case formula::svDoubleRef:
4259 ScComplexRefData aData = *p->GetDoubleRef();
4260 ScRange aThis = aData.toAbs(*pDoc, rPos);
4261 if (aThis == rRange)
4262 return true;
4264 break;
4265 default:
4269 return false;
4274 CPPUNIT_TEST_FIXTURE(Test, testJumpToPrecedentsDependents)
4277 * Test to make sure correct precedent / dependent cells are obtained when
4278 * preparing to jump to them.
4280 // Precedent is another cell that the cell references, while dependent is
4281 // another cell that references it.
4282 m_pDoc->InsertTab(0, u"Test"_ustr);
4284 m_pDoc->SetString(2, 0, 0, u"=A1+A2+B3"_ustr); // C1
4285 m_pDoc->SetString(2, 1, 0, u"=A1"_ustr); // C2
4286 m_pDoc->CalcAll();
4288 std::vector<ScTokenRef> aRefTokens;
4289 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4292 // C1's precedent should be A1:A2,B3.
4293 ScAddress aC1(2, 0, 0);
4294 ScRangeList aRange(aC1);
4295 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4296 CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4297 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
4298 CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4299 hasRange(m_pDoc, aRefTokens, ScRange(1, 2, 0), aC1));
4303 // C2's precedent should be A1 only.
4304 ScAddress aC2(2, 1, 0);
4305 ScRangeList aRange(aC2);
4306 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4307 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4308 static_cast<size_t>(1), aRefTokens.size());
4309 CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4310 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0), aC2));
4314 // A1's dependent should be C1:C2.
4315 ScAddress aA1(0, 0, 0);
4316 ScRangeList aRange(aA1);
4317 rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4318 CPPUNIT_ASSERT_EQUAL_MESSAGE("C1:C2 should be the only dependent of A1.",
4319 std::vector<ScTokenRef>::size_type(1), aRefTokens.size());
4320 CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4321 hasRange(m_pDoc, aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
4324 m_pDoc->DeleteTab(0);
4327 CPPUNIT_TEST_FIXTURE(Test, testTdf149665)
4329 m_pDoc->InsertTab(0, u"Test"_ustr);
4331 m_pDoc->SetString(0, 0, 0, u"''1"_ustr);
4333 // Without the fix in place, this test would have failed with
4334 // - Expected: '1
4335 // - Actual : ''1
4336 CPPUNIT_ASSERT_EQUAL( u"'1"_ustr, m_pDoc->GetString( 0, 0, 0 ) );
4338 m_pDoc->DeleteTab(0);
4341 CPPUNIT_TEST_FIXTURE(Test, testTdf64001)
4343 m_pDoc->InsertTab(0, u"test"_ustr);
4345 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4346 aMarkData.SelectTable(0, true);
4348 m_pDoc->SetString( 0, 0, 0, u"TRUE"_ustr );
4349 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 9, FILL_TO_BOTTOM, FILL_AUTO );
4351 for (SCCOL i = 0; i < 10; ++i)
4353 CPPUNIT_ASSERT_EQUAL( u"TRUE"_ustr, m_pDoc->GetString( 0, i, 0 ) );
4356 m_pDoc->SetString( 0, 10, 0, u"FALSE"_ustr );
4358 m_pDoc->SetString( 1, 0, 0, u"=COUNTIF(A1:A11;TRUE)"_ustr );
4360 // Without the fix in place, this test would have failed with
4361 // - Expected: 10
4362 // - Actual : 1
4363 CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue( 1, 0, 0 ) );
4365 m_pDoc->DeleteTab(0);
4368 CPPUNIT_TEST_FIXTURE(Test, testAutoFill)
4370 m_pDoc->InsertTab(0, u"test"_ustr);
4372 m_pDoc->SetValue(0,0,0,1);
4374 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4375 aMarkData.SelectTable(0, true);
4377 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5);
4378 for (SCROW i = 0; i< 6; ++i)
4379 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
4381 // check that hidden rows are not affected by autofill
4382 // set values for hidden rows
4383 m_pDoc->SetValue(0,1,0,10);
4384 m_pDoc->SetValue(0,2,0,10);
4386 m_pDoc->SetRowHidden(1, 2, 0, true);
4387 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 8);
4389 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
4390 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
4391 for (SCROW i = 3; i< 8; ++i)
4392 ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
4394 m_pDoc->Fill( 0, 0, 0, 8, nullptr, aMarkData, 5, FILL_TO_RIGHT );
4395 for (SCCOL i = 0; i < 5; ++i)
4397 for(SCROW j = 0; j < 8; ++j)
4399 if (j > 2)
4401 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
4403 else if (j == 0)
4405 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
4407 else // j == 1 || j == 2
4409 if(i == 0)
4410 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
4411 else
4412 ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
4417 // test auto fill user data lists
4418 m_pDoc->SetString( 0, 100, 0, u"January"_ustr );
4419 m_pDoc->Fill( 0, 100, 0, 100, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4420 OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
4421 CPPUNIT_ASSERT_EQUAL( u"February"_ustr, aTestValue );
4422 aTestValue = m_pDoc->GetString( 0, 102, 0 );
4423 CPPUNIT_ASSERT_EQUAL( u"March"_ustr, aTestValue );
4425 // test that two same user data list entries will not result in incremental fill
4426 m_pDoc->SetString( 0, 101, 0, u"January"_ustr );
4427 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4428 for ( SCROW i = 102; i <= 103; ++i )
4430 aTestValue = m_pDoc->GetString( 0, i, 0 );
4431 CPPUNIT_ASSERT_EQUAL( u"January"_ustr, aTestValue );
4434 // Clear column A for a new test.
4435 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4436 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4438 // Fill A1:A6 with 1,2,3,4,5,6.
4439 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4440 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4441 ScRange aRange(0,0,0,0,5,0);
4442 aMarkData.SetMarkArea(aRange);
4443 rFunc.FillSeries(aRange, &aMarkData, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, MAXDOUBLE, 1.0, MAXDOUBLE, true);
4444 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4445 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4446 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4447 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4448 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4449 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4451 // Undo should clear the area except for the top cell.
4452 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4453 CPPUNIT_ASSERT(pUndoMgr);
4454 pUndoMgr->Undo();
4456 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4457 for (SCROW i = 1; i <= 5; ++i)
4458 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0,i,0)));
4460 // Redo should put the serial values back in.
4461 pUndoMgr->Redo();
4462 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4463 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4464 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4465 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4466 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4467 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4469 // test that filling formulas vertically up does the right thing
4470 for(SCROW nRow = 0; nRow < 10; ++nRow)
4471 m_pDoc->SetValue(100, 100 + nRow, 0, 1);
4473 m_pDoc->SetString(100, 110, 0, u"=A111"_ustr);
4475 m_pDoc->Fill(100, 110, 100, 110, nullptr, aMarkData, 10, FILL_TO_TOP, FILL_AUTO);
4476 for(SCROW nRow = 110; nRow >= 100; --nRow)
4478 OUString aExpected = "=A" + OUString::number(nRow +1);
4479 OUString aFormula = m_pDoc->GetFormula(100, nRow, 0);
4480 CPPUNIT_ASSERT_EQUAL(aExpected, aFormula);
4483 // Clear column A for a new test.
4484 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4485 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4487 m_pDoc->SetString( 0, 100, 0, u"2012-10-31"_ustr );
4488 m_pDoc->SetString( 0, 101, 0, u"2012-10-31"_ustr );
4489 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4491 // tdf#89754, Without the fix in place, this test would have failed with
4492 // - Expected: 2012-10-31
4493 // - Actual : 2012-11-01
4494 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 102, 0 ) );
4495 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 103, 0 ) );
4496 CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 104, 0 ) );
4498 // Clear column A for a new test.
4499 clearRange(m_pDoc, ScRange(0, 0, 0, 0, m_pDoc->MaxRow(), 0));
4500 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4502 m_pDoc->SetString(0, 100, 0, u"2019-10-31"_ustr);
4503 m_pDoc->SetString(0, 101, 0, u"2019-11-30"_ustr);
4504 m_pDoc->SetString(0, 102, 0, u"2019-12-31"_ustr);
4505 m_pDoc->Fill(0, 100, 0, 102, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO);
4507 // tdf#58745, Without the fix in place, this test would have failed with
4508 // - Expected: 2020-01-31
4509 // - Actual : 2019-01-11
4510 CPPUNIT_ASSERT_EQUAL(u"2020-01-31"_ustr, m_pDoc->GetString(0, 103, 0));
4511 CPPUNIT_ASSERT_EQUAL(u"2020-02-29"_ustr, m_pDoc->GetString(0, 104, 0));
4512 CPPUNIT_ASSERT_EQUAL(u"2020-03-31"_ustr, m_pDoc->GetString(0, 105, 0));
4514 // Clear column A for a new test.
4515 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4516 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4518 m_pDoc->SetString( 0, 50, 0, u"1.0"_ustr );
4519 m_pDoc->SetString( 0, 51, 0, u"1.1"_ustr );
4520 m_pDoc->SetString( 0, 52, 0, u"1.2"_ustr );
4521 m_pDoc->SetString( 0, 53, 0, u"1.3"_ustr );
4522 m_pDoc->Fill( 0, 50, 0, 53, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4524 CPPUNIT_ASSERT_EQUAL( u"1.4"_ustr, m_pDoc->GetString( 0, 54, 0 ) );
4525 CPPUNIT_ASSERT_EQUAL( u"1.5"_ustr, m_pDoc->GetString( 0, 55, 0 ) );
4526 CPPUNIT_ASSERT_EQUAL( u"1.6"_ustr, m_pDoc->GetString( 0, 56, 0 ) );
4528 m_pDoc->SetString( 0, 60, 0, u"4.0"_ustr );
4529 m_pDoc->SetString( 0, 61, 0, u"4.1"_ustr );
4530 m_pDoc->SetString( 0, 62, 0, u"4.2"_ustr );
4531 m_pDoc->SetString( 0, 63, 0, u"4.3"_ustr );
4532 m_pDoc->Fill( 0, 60, 0, 63, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4534 // tdf#37424: Without the fix in place, this test would have failed with
4535 // - Expected: 4.4
4536 // - Actual : 5
4537 CPPUNIT_ASSERT_EQUAL( u"4.4"_ustr, m_pDoc->GetString( 0, 64, 0 ) );
4538 CPPUNIT_ASSERT_EQUAL( u"4.5"_ustr, m_pDoc->GetString( 0, 65, 0 ) );
4539 CPPUNIT_ASSERT_EQUAL( u"4.6"_ustr, m_pDoc->GetString( 0, 66, 0 ) );
4541 // Clear column A for a new test.
4542 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4543 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4545 m_pDoc->SetString( 0, 70, 0, u"001-001-001"_ustr );
4546 m_pDoc->Fill( 0, 70, 0, 70, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4548 // tdf#105268: Without the fix in place, this test would have failed with
4549 // - Expected: 001-001-002
4550 // - Actual : 001-001000
4551 CPPUNIT_ASSERT_EQUAL( u"001-001-002"_ustr, m_pDoc->GetString( 0, 71, 0 ) );
4552 CPPUNIT_ASSERT_EQUAL( u"001-001-003"_ustr, m_pDoc->GetString( 0, 72, 0 ) );
4553 CPPUNIT_ASSERT_EQUAL( u"001-001-004"_ustr, m_pDoc->GetString( 0, 73, 0 ) );
4555 // Clear column A for a new test.
4556 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4557 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4559 m_pDoc->SetString( 0, 80, 0, u"1%"_ustr );
4560 m_pDoc->Fill( 0, 80, 0, 80, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4562 // tdf#89998: Without the fix in place, this test would have failed with
4563 // - Expected: 2.00%
4564 // - Actual : 101.00%
4565 CPPUNIT_ASSERT_EQUAL( u"2.00%"_ustr, m_pDoc->GetString( 0, 81, 0 ) );
4566 CPPUNIT_ASSERT_EQUAL( u"3.00%"_ustr, m_pDoc->GetString( 0, 82, 0 ) );
4567 CPPUNIT_ASSERT_EQUAL( u"4.00%"_ustr, m_pDoc->GetString( 0, 83, 0 ) );
4569 // Clear column A for a new test.
4570 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4571 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4573 m_pDoc->SetString( 0, 0, 0, u"1"_ustr );
4574 m_pDoc->SetString( 0, 1, 0, u"1.1"_ustr );
4575 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 60, FILL_TO_BOTTOM, FILL_AUTO );
4577 // tdf#129606: Without the fix in place, this test would have failed with
4578 // - Expected: 6
4579 // - Actual : 6.00000000000001
4580 CPPUNIT_ASSERT_EQUAL( u"6"_ustr, m_pDoc->GetString( 0, 50, 0 ) );
4582 // Clear column A for a new test.
4583 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4584 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4586 m_pDoc->SetString( 0, 0, 0, u"2022-10-01 00:00:00.000"_ustr );
4587 m_pDoc->SetString( 0, 1, 0, u"2022-10-01 01:00:00.000"_ustr );
4588 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4590 // tdf#151460: Without the fix in place, this test would have failed with
4591 // - Expected: 2022-10-01 20:00:00.000
4592 // - Actual : 2022-10-01 19:59:59.999
4593 CPPUNIT_ASSERT_EQUAL( u"2022-10-01 20:00:00.000"_ustr, m_pDoc->GetString( 0, 20, 0 ) );
4595 // Clear column A for a new test.
4596 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4597 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4599 m_pDoc->SetString( 0, 0, 0, u"1st"_ustr );
4601 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5, FILL_TO_BOTTOM, FILL_AUTO );
4603 CPPUNIT_ASSERT_EQUAL( u"1st"_ustr, m_pDoc->GetString( 0, 0, 0 ) );
4604 CPPUNIT_ASSERT_EQUAL( u"2nd"_ustr, m_pDoc->GetString( 0, 1, 0 ) );
4605 CPPUNIT_ASSERT_EQUAL( u"3rd"_ustr, m_pDoc->GetString( 0, 2, 0 ) );
4607 // Clear column A for a new test.
4608 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4609 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4611 m_pDoc->SetString( 0, 200, 0, u"15:00"_ustr );
4612 m_pDoc->SetString( 0, 201, 0, u"15:20"_ustr );
4613 m_pDoc->Fill( 0, 200, 0, 201, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4615 CPPUNIT_ASSERT_EQUAL( u"03:00:00 PM"_ustr, m_pDoc->GetString( 0, 200, 0 ) );
4617 // tdf#153517: Without the fix in place, this test would have failed with
4618 // - Expected: 03:20:00 PM
4619 // - Actual : 03:19:59 PM
4620 CPPUNIT_ASSERT_EQUAL( u"03:20:00 PM"_ustr, m_pDoc->GetString( 0, 201, 0 ) );
4621 CPPUNIT_ASSERT_EQUAL( u"03:40:00 PM"_ustr, m_pDoc->GetString( 0, 202, 0 ) );
4622 CPPUNIT_ASSERT_EQUAL( u"04:00:00 PM"_ustr, m_pDoc->GetString( 0, 203, 0 ) );
4624 m_pDoc->DeleteTab(0);
4627 CPPUNIT_TEST_FIXTURE(Test, testAutoFillSimple)
4629 m_pDoc->InsertTab(0, u"test"_ustr);
4631 m_pDoc->SetValue(0, 0, 0, 1);
4632 m_pDoc->SetString(0, 1, 0, u"=10"_ustr);
4634 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4635 aMarkData.SelectTable(0, true);
4637 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 6, FILL_TO_BOTTOM, FILL_AUTO);
4639 for(SCROW nRow = 0; nRow < 8; ++nRow)
4641 if (nRow % 2 == 0)
4643 double nVal = m_pDoc->GetValue(0, nRow, 0);
4644 CPPUNIT_ASSERT_EQUAL((nRow+2)/2.0, nVal);
4646 else
4648 OString aMsg = "wrong value in row: " + OString::number(nRow);
4649 double nVal = m_pDoc->GetValue(0, nRow, 0);
4650 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, nVal);
4654 m_pDoc->DeleteTab(0);
4657 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosVertical)
4659 std::vector<std::vector<const char*>> aData = {
4660 { nullptr, "1", "1" },
4661 { "1", nullptr, "1" },
4662 { "1", "1", "1" },
4663 { nullptr, "1", "1" },
4664 { "1", "1", "1" },
4665 { "1", nullptr, "1" },
4666 { "1", "1", "1" },
4669 m_pDoc->InsertTab(0, u"Test1"_ustr);
4670 clearRange( m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
4671 ScAddress aPos(0,0,0);
4672 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4673 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4675 m_pDoc->SetRowHidden(4,4,0,true);
4676 bool bHidden = m_pDoc->RowHidden(4,0);
4677 CPPUNIT_ASSERT(bHidden);
4679 SCCOL nCol = 0;
4680 SCROW nRow = 0;
4681 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4683 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4684 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4686 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4688 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
4689 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4691 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4693 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
4694 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4696 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4698 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4699 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4701 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4703 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), nRow);
4704 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4706 nCol = 1;
4707 nRow = 2;
4709 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4711 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
4712 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4714 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4716 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4717 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4719 nCol = 2;
4720 nRow = 6;
4721 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
4722 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4723 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4725 m_pDoc->DeleteTab(0);
4728 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosColRight)
4730 std::vector<std::vector<const char*>> aData = {
4731 { "", "1", "1", "", "1", "1", "1" },
4732 { "", "", "1", "1", "1", "", "1" },
4735 m_pDoc->InsertTab(0, u"test1"_ustr);
4736 clearRange( m_pDoc, ScRange(0, 0, 0, 7, aData.size(), 0));
4737 ScAddress aPos(0,0,0);
4738 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4739 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4741 m_pDoc->SetColHidden(4,4,0,true);
4742 bool bHidden = m_pDoc->ColHidden(4,0);
4743 CPPUNIT_ASSERT(bHidden);
4745 SCCOL nCol = 0;
4746 SCROW nRow = 0;
4747 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4749 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4750 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4752 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4754 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4755 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4757 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4759 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4760 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
4762 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4764 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4765 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4767 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4769 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4770 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), nCol);
4772 nCol = 2;
4773 nRow = 1;
4775 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4777 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4778 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
4780 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4782 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4783 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4785 m_pDoc->DeleteTab(0);
4788 CPPUNIT_TEST_FIXTURE(Test, testShiftCells)
4790 m_pDoc->InsertTab(0, u"foo"_ustr);
4792 // We need a drawing layer in order to create caption objects.
4793 m_pDoc->InitDrawLayer(m_xDocShell.get());
4795 OUString aTestVal(u"Some Text"_ustr);
4797 // Text into cell E5.
4798 m_pDoc->SetString(4, 3, 0, aTestVal);
4800 // put a Note in cell E5
4801 ScAddress rAddr(4, 3, 0);
4802 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4803 pNote->SetText(rAddr, u"Hello"_ustr);
4805 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4807 // Insert cell at D5. This should shift the string cell to right.
4808 m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
4809 OUString aStr = m_pDoc->GetString(5, 3, 0);
4810 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4811 CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(3, 4, 3, 4, 0));
4813 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
4814 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
4816 // Delete cell D5, to shift the text cell back into D5.
4817 m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
4818 aStr = m_pDoc->GetString(4, 3, 0);
4819 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4820 CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(4, 4, 4, 4, 0));
4822 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
4823 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4825 m_pDoc->DeleteTab(0);
4828 CPPUNIT_TEST_FIXTURE(Test, testNoteDefaultStyle)
4830 m_pDoc->InsertTab(0, u"PostIts"_ustr);
4832 // We need a drawing layer in order to create caption objects.
4833 m_pDoc->InitDrawLayer(m_xDocShell.get());
4835 auto pNote = m_pDoc->GetOrCreateNote({0, 0, 0});
4836 auto pCaption = pNote->GetCaption();
4838 CPPUNIT_ASSERT(pCaption);
4839 CPPUNIT_ASSERT_EQUAL(ScResId(STR_STYLENAME_NOTE), pCaption->GetStyleSheet()->GetName());
4841 m_pDoc->DeleteTab(0);
4844 CPPUNIT_TEST_FIXTURE(Test, testNoteBasic)
4846 m_pDoc->InsertTab(0, u"PostIts"_ustr);
4848 // We need a drawing layer in order to create caption objects.
4849 m_pDoc->InitDrawLayer(m_xDocShell.get());
4851 CPPUNIT_ASSERT(!m_pDoc->HasNotes());
4853 // Check for note's presence in all tables before inserting any notes.
4854 for (SCTAB i = 0; i <= MAXTAB; ++i)
4856 bool bHasNotes = m_pDoc->HasTabNotes(i);
4857 CPPUNIT_ASSERT(!bHasNotes);
4860 ScAddress aAddr(2, 2, 0); // cell C3
4861 ScPostIt *pNote = m_pDoc->GetOrCreateNote(aAddr);
4863 pNote->SetText(aAddr, u"Hello world"_ustr);
4864 pNote->SetAuthor(u"Jim Bob"_ustr);
4866 ScPostIt *pGetNote = m_pDoc->GetNote(aAddr);
4867 CPPUNIT_ASSERT_EQUAL_MESSAGE("note should be itself", pNote, pGetNote);
4869 // Insert one row at row 1.
4870 bool bInsertRow = m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
4871 CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
4873 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4874 aAddr.IncRow(); // cell C4
4875 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4877 // Insert column at column A.
4878 bool bInsertCol = m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
4879 CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
4881 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4882 aAddr.IncCol(); // cell D4
4883 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4885 // Insert a new sheet to shift the current sheet to the right.
4886 m_pDoc->InsertTab(0, u"Table2"_ustr);
4887 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4888 aAddr.IncTab(); // Move to the next sheet.
4889 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4891 m_pDoc->DeleteTab(0);
4892 aAddr.IncTab(-1);
4893 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4895 // Insert cell at C4. This should NOT shift the note position.
4896 bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
4897 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
4898 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4900 // Delete cell at C4. Again, this should NOT shift the note position.
4901 m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
4902 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4904 // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
4905 m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
4906 aAddr.IncRow(-1); // cell D3
4907 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D4 should have shifted up to D3.", pNote, m_pDoc->GetNote(aAddr));
4909 // Delete column C. This should shift the note one cell left.
4910 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 2, 1);
4911 aAddr.IncCol(-1); // cell C3
4912 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D3 should have shifted left to C3.", pNote, m_pDoc->GetNote(aAddr));
4914 // Insert a text where the note is.
4915 m_pDoc->SetString(aAddr, u"Note is here."_ustr);
4917 // Delete row 1. This should shift the note from C3 to C2.
4918 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 1);
4919 aAddr.IncRow(-1); // C2
4920 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at C3 should have shifted up to C2.", pNote, m_pDoc->GetNote(aAddr));
4922 m_pDoc->DeleteTab(0);
4925 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteRow)
4927 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
4929 // We need a drawing layer in order to create caption objects.
4930 m_pDoc->InitDrawLayer(m_xDocShell.get());
4932 ScAddress aPos(1, 1, 0);
4933 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
4934 pNote->SetText(aPos, u"Hello"_ustr);
4935 pNote->SetAuthor(u"Jim Bob"_ustr);
4937 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
4939 // test with IsBlockEmpty
4940 CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsEmptyData(0, 0, 100, 100, 0));
4941 CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
4943 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
4945 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
4947 // Set values and notes into B3:B4.
4948 aPos = ScAddress(1,2,0); // B3
4949 m_pDoc->SetString(aPos, u"First"_ustr);
4950 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, u"First Note"_ustr, false, false);
4952 aPos = ScAddress(1,3,0); // B4
4953 m_pDoc->SetString(aPos, u"Second"_ustr);
4954 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, u"Second Note"_ustr, false, false);
4956 // Delete row 2.
4957 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4958 ScMarkData aMark(m_pDoc->GetSheetLimits());
4959 aMark.SelectOneTable(0);
4960 rDocFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
4962 // Check to make sure the notes have shifted upward.
4963 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4964 CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
4965 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
4966 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4967 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4968 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
4969 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4970 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4972 // Undo.
4974 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4975 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
4976 m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
4978 pUndoMgr->Undo();
4979 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4980 CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
4981 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4982 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4983 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
4984 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4985 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
4986 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
4988 // Delete row 3.
4989 rDocFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4991 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4992 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4993 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
4994 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4995 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4997 // Undo and check the result.
4998 pUndoMgr->Undo();
4999 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
5000 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
5001 CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
5002 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
5003 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
5004 CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
5006 m_pDoc->DeleteTab(0);
5009 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteCol)
5011 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
5013 // We need a drawing layer in order to create caption objects.
5014 m_pDoc->InitDrawLayer(m_xDocShell.get());
5016 ScAddress rAddr(1, 1, 0);
5017 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5018 pNote->SetText(rAddr, u"Hello"_ustr);
5019 pNote->SetAuthor(u"Jim Bob"_ustr);
5021 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5023 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
5025 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5027 m_pDoc->DeleteTab(0);
5030 CPPUNIT_TEST_FIXTURE(Test, testNoteLifeCycle)
5032 m_pDoc->InsertTab(0, u"Test"_ustr);
5034 // We need a drawing layer in order to create caption objects.
5035 m_pDoc->InitDrawLayer(m_xDocShell.get());
5037 ScAddress aPos(1,1,0);
5038 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5039 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
5041 pNote->SetText(aPos, u"New note"_ustr);
5042 std::unique_ptr<ScPostIt> pNote2 = m_pDoc->ReleaseNote(aPos);
5043 CPPUNIT_ASSERT_EQUAL_MESSAGE("This note instance is expected to be identical to the original.", pNote, pNote2.get());
5044 CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
5046 // Modify the internal state of the note instance to make sure it's really
5047 // been released.
5048 pNote->SetText(aPos, u"New content"_ustr);
5050 // Re-insert the note back to the same place.
5051 m_pDoc->SetNote(aPos, std::move(pNote2));
5052 SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
5053 CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
5054 CPPUNIT_ASSERT_EQUAL_MESSAGE("This caption should belong to the drawing layer of the document.",
5055 m_pDoc->GetDrawLayer(), static_cast<ScDrawLayer*>(&pCaption->getSdrModelFromSdrObject()));
5057 // Copy B2 with note to a clipboard.
5059 ScClipParam aClipParam(aPos, false);
5060 ScDocument aClipDoc(SCDOCMODE_CLIP);
5061 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
5062 aMarkData.SelectOneTable(0);
5063 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMarkData, false, true);
5065 ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
5066 CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
5067 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note on the clipboard should share the same caption object from the original.",
5068 pCaption, pClipNote->GetCaption());
5071 // Move B2 to B3 with note, which creates an ScUndoDragDrop, and Undo.
5073 ScAddress aOrigPos(aPos);
5074 ScAddress aMovePos(1,2,0);
5075 ScPostIt* pOrigNote = m_pDoc->GetNote(aOrigPos);
5076 const SdrCaptionObj* pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5077 bool const bCut = true; // like Drag&Drop
5078 bool bRecord = true; // record Undo
5079 bool const bPaint = false; // don't care about
5080 bool bApi = true; // API to prevent dialogs
5081 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5082 bool bMoveDone = rDocFunc.MoveBlock(ScRange(aOrigPos, aOrigPos), aMovePos, bCut, bRecord, bPaint, bApi);
5083 CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
5085 // Verify the note move.
5086 ScPostIt* pGoneNote = m_pDoc->GetNote(aOrigPos);
5087 CPPUNIT_ASSERT_MESSAGE("Failed to move the note from source.", !pGoneNote);
5088 ScPostIt* pMoveNote = m_pDoc->GetNote(aMovePos);
5089 CPPUNIT_ASSERT_MESSAGE("Failed to move the note to destination.", pMoveNote);
5091 // The caption object should not be identical, it was newly created upon
5092 // Drop from clipboard.
5093 // pOrigCaption is a dangling pointer.
5094 const SdrCaptionObj* pMoveCaption = pMoveNote->GetOrCreateCaption(aMovePos);
5095 CPPUNIT_ASSERT_MESSAGE("Captions identical after move.", pOrigCaption != pMoveCaption);
5097 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5098 CPPUNIT_ASSERT(pUndoMgr);
5099 pUndoMgr->Undo(); // this should not crash ... tdf#92995
5101 // Verify the note move Undo.
5102 pMoveNote = m_pDoc->GetNote(aMovePos);
5103 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move from destination.", !pMoveNote);
5104 pOrigNote = m_pDoc->GetNote(aOrigPos);
5105 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move to source.", pOrigNote);
5107 // The caption object still should not be identical.
5108 // pMoveCaption is a dangling pointer.
5109 pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5110 CPPUNIT_ASSERT_MESSAGE("Captions identical after move undo.", pOrigCaption != pMoveCaption);
5113 // Create a note at B4, merge B4 and B5 with ScUndoMerge, and Undo.
5115 ScAddress aPosB4(1,3,0);
5116 ScPostIt* pNoteB4 = m_pDoc->GetOrCreateNote(aPosB4);
5117 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B4.", pNoteB4);
5118 const SdrCaptionObj* pCaptionB4 = pNoteB4->GetOrCreateCaption(aPosB4);
5119 ScCellMergeOption aCellMergeOption(1,3,2,3);
5120 rDocFunc.MergeCells( aCellMergeOption, true /*bContents*/, bRecord, bApi, false /*bEmptyMergedCells*/ );
5122 SfxUndoManager* pMergeUndoManager = m_pDoc->GetUndoManager();
5123 CPPUNIT_ASSERT(pMergeUndoManager);
5124 pMergeUndoManager->Undo(); // this should not crash ... tdf#105667
5126 // Undo contained the original caption object pointer which was still alive
5127 // at B4 after the merge and not cloned nor recreated during Undo.
5128 ScPostIt* pUndoNoteB4 = m_pDoc->GetNote(aPosB4);
5129 CPPUNIT_ASSERT_MESSAGE("No cell comment at B4 after Undo.", pUndoNoteB4);
5130 const SdrCaptionObj* pUndoCaptionB4 = pUndoNoteB4->GetCaption();
5131 CPPUNIT_ASSERT_EQUAL_MESSAGE("Captions not identical after Merge Undo.", pCaptionB4, pUndoCaptionB4);
5134 // In a second document copy a note from B5 to clipboard, close the
5135 // document and then paste the note into this document.
5137 ScDocShellRef xDocSh2;
5138 getNewDocShell(xDocSh2);
5139 ScDocument* pDoc2 = &xDocSh2->GetDocument();
5140 pDoc2->InsertTab(0, u"OtherSheet1"_ustr);
5141 pDoc2->InitDrawLayer(xDocSh2.get());
5143 ScAddress aPosB5(1,4,0);
5144 ScPostIt* pOtherNoteB5 = pDoc2->GetOrCreateNote(aPosB5);
5145 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B5.", pOtherNoteB5);
5146 const SdrCaptionObj* pOtherCaptionB5 = pOtherNoteB5->GetOrCreateCaption(aPosB5);
5147 CPPUNIT_ASSERT_MESSAGE("No caption at B5.", pOtherCaptionB5);
5149 ScDocument aClipDoc2(SCDOCMODE_CLIP);
5150 copyToClip( pDoc2, aPosB5, &aClipDoc2);
5152 // There's no ScTransferObject involved in the "fake" clipboard copy
5153 // and ScDocument dtor asking IsClipboardSource() gets no, so emulate
5154 // the part that normally is responsible for forgetting the caption
5155 // objects.
5156 aClipDoc2.ClosingClipboardSource();
5158 pDoc2->DeleteTab(0);
5159 xDocSh2->DoClose();
5160 xDocSh2.clear();
5162 pasteFromClip( m_pDoc, aPosB5, &aClipDoc2); // should not crash... tdf#104967
5163 ScPostIt* pNoteB5 = m_pDoc->GetNote(aPosB5);
5164 CPPUNIT_ASSERT_MESSAGE("Failed to paste cell comment at B5.", pNoteB5);
5165 const SdrCaptionObj* pCaptionB5 = pNoteB5->GetOrCreateCaption(aPosB5);
5166 CPPUNIT_ASSERT_MESSAGE("No caption at pasted B5.", pCaptionB5);
5167 // Do not test if pCaptionB5 != pOtherCaptionB5 because since pDoc2
5168 // has been closed and the caption been deleted objects *may* be
5169 // allocated at the very same memory location.
5172 m_pDoc->DeleteTab(0);
5175 CPPUNIT_TEST_FIXTURE(Test, testNoteCopyPaste)
5177 m_pDoc->InsertTab(0, u"Test"_ustr);
5179 // We need a drawing layer in order to create caption objects.
5180 m_pDoc->InitDrawLayer(m_xDocShell.get());
5182 // Insert in B2 a text and cell comment.
5183 ScAddress aPos(1,1,0);
5184 m_pDoc->SetString(aPos, u"Text"_ustr);
5185 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5186 CPPUNIT_ASSERT(pNote);
5187 pNote->SetText(aPos, u"Note1"_ustr);
5189 // Insert in B4 a number and cell comment.
5190 aPos.SetRow(3);
5191 m_pDoc->SetValue(aPos, 1.1);
5192 pNote = m_pDoc->GetOrCreateNote(aPos);
5193 CPPUNIT_ASSERT(pNote);
5194 pNote->SetText(aPos, u"Note2"_ustr);
5196 // Copy B2:B4 to clipboard.
5197 ScMarkData aMark(m_pDoc->GetSheetLimits());
5198 aMark.SelectOneTable(0);
5199 ScRange aCopyRange(1,1,0,1,3,0);
5200 ScDocument aClipDoc(SCDOCMODE_CLIP);
5201 aClipDoc.ResetClip(m_pDoc, &aMark);
5202 ScClipParam aClipParam(aCopyRange, false);
5203 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
5205 // Make sure the notes are in the clipboard.
5206 pNote = aClipDoc.GetNote(ScAddress(1,1,0));
5207 CPPUNIT_ASSERT(pNote);
5208 CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
5210 pNote = aClipDoc.GetNote(ScAddress(1,3,0));
5211 CPPUNIT_ASSERT(pNote);
5212 CPPUNIT_ASSERT_EQUAL(u"Note2"_ustr, pNote->GetText());
5214 // Paste to B6:B8 but only cell notes.
5215 ScRange aDestRange(1,5,0,1,7,0);
5216 m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::NOTE, nullptr, &aClipDoc);
5218 // Make sure the notes are there.
5219 pNote = m_pDoc->GetNote(ScAddress(1,5,0));
5220 CPPUNIT_ASSERT(pNote);
5221 CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
5223 pNote = m_pDoc->GetNote(ScAddress(1,7,0));
5224 CPPUNIT_ASSERT(pNote);
5225 CPPUNIT_ASSERT_EQUAL(u"Note2"_ustr, pNote->GetText());
5227 // Test that GetNotesInRange includes the end of its range
5228 // and so can find the note
5229 std::vector<sc::NoteEntry> aNotes;
5230 m_pDoc->GetNotesInRange(ScRange(1,7,0), aNotes);
5231 CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
5233 m_pDoc->DeleteTab(0);
5236 // tdf#112454
5237 CPPUNIT_TEST_FIXTURE(Test, testNoteContainsNotesInRange)
5239 m_pDoc->InsertTab(0, u"PostIts"_ustr);
5241 // We need a drawing layer in order to create caption objects.
5242 m_pDoc->InitDrawLayer(m_xDocShell.get());
5244 ScAddress aAddr(2, 2, 0); // cell C3
5246 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in a document that doesn't have any.",
5247 !m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5249 m_pDoc->GetOrCreateNote(aAddr);
5251 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in range that doesn't have any.",
5252 !m_pDoc->ContainsNotesInRange(ScRange(ScAddress(0, 0, 0), ScAddress(0, 1, 0))));
5253 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies on border of range.",
5254 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5255 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies in inner area of range.",
5256 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), ScAddress(3, 3, 0)))));
5259 CPPUNIT_TEST_FIXTURE(Test, testAreasWithNotes)
5261 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
5263 // We need a drawing layer in order to create caption objects.
5264 m_pDoc->InitDrawLayer(m_xDocShell.get());
5266 ScAddress rAddr(1, 5, 0);
5267 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5268 pNote->SetText(rAddr, u"Hello"_ustr);
5269 pNote->SetAuthor(u"Jim Bob"_ustr);
5270 ScAddress rAddrMin(2, 2, 0);
5271 ScPostIt* pNoteMin = m_pDoc->GetOrCreateNote(rAddrMin);
5272 pNoteMin->SetText(rAddrMin, u"Hello"_ustr);
5274 SCCOL col;
5275 SCROW row;
5276 bool dataFound;
5278 // only cell notes (empty content)
5280 dataFound = m_pDoc->GetDataStart(0,col,row);
5282 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5283 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col for notes", static_cast<SCCOL>(1), col);
5284 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row for notes", static_cast<SCROW>(2), row);
5286 dataFound = m_pDoc->GetCellArea(0,col,row);
5288 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5289 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col for notes", static_cast<SCCOL>(2), col);
5290 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row for notes", static_cast<SCROW>(5), row);
5292 bool bNotes = true;
5293 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5295 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5296 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col for notes", static_cast<SCCOL>(2), col);
5297 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row for notes", static_cast<SCROW>(5), row);
5299 bNotes = false;
5300 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5301 CPPUNIT_ASSERT_MESSAGE("No PrintArea should be found", !dataFound);
5303 bNotes = true;
5304 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5305 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5306 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(5), row);
5308 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5309 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5310 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(2), row);
5312 dataFound = m_pDoc->GetPrintAreaVer(0,20,21,row, bNotes); // cols 20 & 21
5313 CPPUNIT_ASSERT_MESSAGE("PrintAreaVer found", !dataFound);
5314 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(0), row);
5316 bNotes = false;
5317 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // col 0 & 1
5318 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer should be found", !dataFound);
5320 // now add cells with value, check that notes are taken into account in good cases
5322 m_pDoc->SetString(0, 3, 0, u"Some Text"_ustr);
5323 m_pDoc->SetString(3, 3, 0, u"Some Text"_ustr);
5325 dataFound = m_pDoc->GetDataStart(0,col,row);
5327 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5328 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col", static_cast<SCCOL>(0), col);
5329 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row", static_cast<SCROW>(2), row);
5331 dataFound = m_pDoc->GetCellArea(0,col,row);
5333 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5334 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col", static_cast<SCCOL>(3), col);
5335 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row", static_cast<SCROW>(5), row);
5337 bNotes = true;
5338 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5340 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5341 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5342 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(5), row);
5344 bNotes = false;
5345 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5346 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5347 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5348 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(3), row);
5350 bNotes = true;
5351 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5352 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5353 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(5), row);
5355 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5356 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5357 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5359 bNotes = false;
5360 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5361 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5362 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5364 m_pDoc->DeleteTab(0);
5367 CPPUNIT_TEST_FIXTURE(Test, testAnchoredRotatedShape)
5369 m_pDoc->InsertTab(0, u"TestTab"_ustr);
5370 SCROW nRow1, nRow2;
5371 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5372 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
5373 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
5374 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
5376 m_pDoc->InitDrawLayer();
5377 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5378 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
5379 SdrPage* pPage = pDrawLayer->GetPage(0);
5380 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
5381 m_pDoc->SetRowHeightRange(0, m_pDoc->MaxRow(), 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5382 constexpr tools::Long TOLERANCE = 30; //30 hmm
5383 for ( SCCOL nCol = 0; nCol < m_pDoc->MaxCol(); ++nCol )
5384 m_pDoc->SetColWidth(nCol, 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5386 //Add a rect
5387 tools::Rectangle aRect( 4000, 5000, 10000, 7000 );
5389 tools::Rectangle aRotRect( 6000, 3000, 8000, 9000 );
5390 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aRect);
5391 pPage->InsertObject(pObj.get());
5392 Point aRef1(pObj->GetSnapRect().Center());
5393 Degree100 nAngle = 9000_deg100; //90 deg.
5394 double nSin = 1.0; // sin(90 deg)
5395 double nCos = 0.0; // cos(90 deg)
5396 pObj->Rotate(aRef1,nAngle,nSin,nCos);
5398 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
5400 tools::Rectangle aSnap = pObj->GetSnapRect();
5401 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5402 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5403 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Left(), aSnap.Left(), TOLERANCE );
5404 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Top(), aSnap.Top(), TOLERANCE );
5406 ScDrawObjData aAnchor;
5407 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj.get() );
5408 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5410 aAnchor.maStart = pData->maStart;
5411 aAnchor.maEnd = pData->maEnd;
5413 m_pDoc->SetDrawPageSize(0);
5415 // increase row 5 by 2000 hmm
5416 m_pDoc->SetRowHeight(5, 0, o3tl::toTwips(3000, o3tl::Length::mm100));
5417 // increase col 6 by 1000 hmm
5418 m_pDoc->SetColWidth(6, 0, o3tl::toTwips(2000, o3tl::Length::mm100));
5420 aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
5421 aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
5423 m_pDoc->SetDrawPageSize(0);
5425 aSnap = pObj->GetSnapRect();
5427 // ensure that width and height have been adjusted accordingly
5428 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5429 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5431 // ensure that anchor start and end addresses haven't changed
5432 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
5433 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
5434 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
5435 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
5437 m_pDoc->DeleteTab(0);
5440 CPPUNIT_TEST_FIXTURE(Test, testCellTextWidth)
5442 m_pDoc->InsertTab(0, u"Test"_ustr);
5444 ScAddress aTopCell(0, 0, 0);
5446 // Sheet is empty.
5447 std::unique_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5448 CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
5450 // Sheet only has one cell.
5451 m_pDoc->SetString(0, 0, 0, u"Only one cell"_ustr);
5452 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5453 CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
5454 CPPUNIT_ASSERT_EQUAL(SCROW(0), pIter->getPos());
5456 // Setting a text width here should commit it to the column.
5457 sal_uInt16 nTestVal = 432;
5458 pIter->setValue(nTestVal);
5459 CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
5461 // Set values to row 2 through 6.
5462 for (SCROW i = 2; i <= 6; ++i)
5463 m_pDoc->SetString(0, i, 0, u"foo"_ustr);
5465 // Set values to row 10 through 18.
5466 for (SCROW i = 10; i <= 18; ++i)
5467 m_pDoc->SetString(0, i, 0, u"foo"_ustr);
5470 // Full range.
5471 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5472 SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
5473 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5475 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5476 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5478 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5482 // Specify start and end rows (6 - 16)
5483 ScAddress aStart = aTopCell;
5484 aStart.SetRow(6);
5485 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
5486 SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
5487 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5489 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5490 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5492 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5495 // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
5496 clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
5499 // Full range again.
5500 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5501 SCROW aRows[] = { 0, 2, 18 };
5502 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5504 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5505 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5507 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5510 // Delete row 2 which shifts all cells below row 2 upward. After this, we
5511 // should only have cells at rows 0 and 17.
5512 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), MAXTAB, 2, 1);
5514 // Full range again.
5515 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5516 SCROW aRows[] = { 0, 17 };
5517 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5519 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5520 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5522 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5525 m_pDoc->DeleteTab(0);
5528 static bool checkEditTextIterator(sc::EditTextIterator& rIter, const char** pChecks)
5530 const EditTextObject* pText = rIter.first();
5531 const char* p = *pChecks;
5533 for (int i = 0; i < 100; ++i) // cap it to 100 loops.
5535 if (!pText)
5536 // No more edit cells. The check string array should end too.
5537 return p == nullptr;
5539 if (!p)
5540 // More edit cell, but no more check string. Bad.
5541 return false;
5543 if (pText->GetParagraphCount() != 1)
5544 // For this test, we don't handle multi-paragraph text.
5545 return false;
5547 if (pText->GetText(0) != OUString::createFromAscii(p))
5548 // Text differs from what's expected.
5549 return false;
5551 pText = rIter.next();
5552 ++pChecks;
5553 p = *pChecks;
5556 return false;
5559 CPPUNIT_TEST_FIXTURE(Test, testEditTextIterator)
5561 m_pDoc->InsertTab(0, u"Test"_ustr);
5564 // First, try with an empty sheet.
5565 sc::EditTextIterator aIter(*m_pDoc,0);
5566 const char* pChecks[] = { nullptr };
5567 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5570 ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
5573 // Only set one edit cell.
5574 rEditEngine.SetTextCurrentDefaults(u"A2"_ustr);
5575 m_pDoc->SetEditText(ScAddress(0,1,0), rEditEngine.CreateTextObject());
5576 sc::EditTextIterator aIter(*m_pDoc,0);
5577 const char* pChecks[] = { "A2", nullptr };
5578 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5582 // Add a series of edit cells.
5583 rEditEngine.SetTextCurrentDefaults(u"A5"_ustr);
5584 m_pDoc->SetEditText(ScAddress(0,4,0), rEditEngine.CreateTextObject());
5585 rEditEngine.SetTextCurrentDefaults(u"A6"_ustr);
5586 m_pDoc->SetEditText(ScAddress(0,5,0), rEditEngine.CreateTextObject());
5587 rEditEngine.SetTextCurrentDefaults(u"A7"_ustr);
5588 m_pDoc->SetEditText(ScAddress(0,6,0), rEditEngine.CreateTextObject());
5589 sc::EditTextIterator aIter(*m_pDoc,0);
5590 const char* pChecks[] = { "A2", "A5", "A6", "A7", nullptr };
5591 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5595 // Add more edit cells to column C. Skip column B.
5596 rEditEngine.SetTextCurrentDefaults(u"C1"_ustr);
5597 m_pDoc->SetEditText(ScAddress(2,0,0), rEditEngine.CreateTextObject());
5598 rEditEngine.SetTextCurrentDefaults(u"C3"_ustr);
5599 m_pDoc->SetEditText(ScAddress(2,2,0), rEditEngine.CreateTextObject());
5600 rEditEngine.SetTextCurrentDefaults(u"C4"_ustr);
5601 m_pDoc->SetEditText(ScAddress(2,3,0), rEditEngine.CreateTextObject());
5602 sc::EditTextIterator aIter(*m_pDoc,0);
5603 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5604 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5608 // Add some numeric, string and formula cells. This shouldn't affect the outcome.
5609 m_pDoc->SetString(ScAddress(0,99,0), u"=ROW()"_ustr);
5610 m_pDoc->SetValue(ScAddress(1,3,0), 1.2);
5611 m_pDoc->SetString(ScAddress(2,4,0), u"Simple string"_ustr);
5612 sc::EditTextIterator aIter(*m_pDoc,0);
5613 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5614 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5617 m_pDoc->DeleteTab(0);
5620 CPPUNIT_TEST_FIXTURE(Test, testImportStream)
5622 sc::AutoCalcSwitch aAC(*m_pDoc, true); // turn on auto calc.
5623 sc::UndoSwitch aUndo(*m_pDoc, true); // enable undo.
5625 m_pDoc->InsertTab(0, u"Test"_ustr);
5627 m_pDoc->SetString(ScAddress(0,1,0), u"=SUM(A1:C1)"_ustr); // A2
5629 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5631 // CSV import options.
5632 ScAsciiOptions aOpt;
5633 aOpt.SetFieldSeps(u","_ustr);
5635 // Import values to A1:C1.
5636 ScImportExport aObj(*m_pDoc, ScAddress(0,0,0));
5637 aObj.SetImportBroadcast(true);
5638 aObj.SetExtOptions(aOpt);
5639 aObj.ImportString(u"1,2,3"_ustr, SotClipboardFormatId::STRING);
5641 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5642 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5643 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5645 // Formula value should have been updated.
5646 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5648 // Undo, and check the result.
5649 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5650 CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
5651 pUndoMgr->Undo();
5653 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5654 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5655 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5657 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5659 // Redo, and check the result.
5660 pUndoMgr->Redo();
5662 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5663 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5664 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5666 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5668 pUndoMgr->Clear();
5670 m_pDoc->DeleteTab(0);
5673 CPPUNIT_TEST_FIXTURE(Test, testDeleteContents)
5675 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
5676 sc::UndoSwitch aUndoSwitch(*m_pDoc, true); // enable undo.
5678 m_pDoc->InsertTab(0, u"Test"_ustr);
5680 m_pDoc->SetValue(ScAddress(3,1,0), 1.0);
5681 m_pDoc->SetValue(ScAddress(3,2,0), 1.0);
5682 m_pDoc->SetValue(ScAddress(3,3,0), 1.0);
5683 m_pDoc->SetValue(ScAddress(3,4,0), 1.0);
5684 m_pDoc->SetValue(ScAddress(3,5,0), 1.0);
5685 m_pDoc->SetValue(ScAddress(3,6,0), 1.0);
5686 m_pDoc->SetValue(ScAddress(3,7,0), 1.0);
5687 m_pDoc->SetValue(ScAddress(3,8,0), 1.0);
5688 m_pDoc->SetString(ScAddress(3,15,0), u"=SUM(D2:D15)"_ustr);
5690 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5692 // Delete D2:D6.
5693 ScRange aRange(3,1,0,3,5,0);
5694 ScMarkData aMark(m_pDoc->GetSheetLimits());
5695 aMark.SelectOneTable(0);
5696 aMark.SetMarkArea(aRange);
5698 ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
5699 pUndoDoc->InitUndo(*m_pDoc, 0, 0);
5700 m_pDoc->CopyToDocument(aRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc, &aMark);
5701 ScUndoDeleteContents aUndo(m_xDocShell.get(), aMark, aRange, std::move(pUndoDoc), false, InsertDeleteFlags::CONTENTS, true);
5703 clearRange(m_pDoc, aRange);
5704 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5706 aUndo.Undo();
5707 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5709 aUndo.Redo();
5710 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5712 m_pDoc->DeleteTab(0);
5715 CPPUNIT_TEST_FIXTURE(Test, testTransliterateText)
5717 m_pDoc->InsertTab(0, u"Test"_ustr);
5719 // Set texts to A1:A3.
5720 m_pDoc->SetString(ScAddress(0,0,0), u"Mike"_ustr);
5721 m_pDoc->SetString(ScAddress(0,1,0), u"Noah"_ustr);
5722 m_pDoc->SetString(ScAddress(0,2,0), u"Oscar"_ustr);
5724 // Change them to uppercase.
5725 ScMarkData aMark(m_pDoc->GetSheetLimits());
5726 aMark.SetMarkArea(ScRange(0,0,0,0,2,0));
5727 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5728 rFunc.TransliterateText(
5729 aMark, TransliterationFlags::LOWERCASE_UPPERCASE, true);
5731 CPPUNIT_ASSERT_EQUAL(u"MIKE"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5732 CPPUNIT_ASSERT_EQUAL(u"NOAH"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5733 CPPUNIT_ASSERT_EQUAL(u"OSCAR"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5735 // Test the undo and redo.
5736 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5737 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5739 pUndoMgr->Undo();
5740 CPPUNIT_ASSERT_EQUAL(u"Mike"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5741 CPPUNIT_ASSERT_EQUAL(u"Noah"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5742 CPPUNIT_ASSERT_EQUAL(u"Oscar"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5744 pUndoMgr->Redo();
5745 CPPUNIT_ASSERT_EQUAL(u"MIKE"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
5746 CPPUNIT_ASSERT_EQUAL(u"NOAH"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
5747 CPPUNIT_ASSERT_EQUAL(u"OSCAR"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
5749 m_pDoc->DeleteTab(0);
5752 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue)
5754 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5755 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5757 m_pDoc->InsertTab(0, u"Test"_ustr);
5759 std::vector<std::vector<const char*>> aData = {
5760 { "=1", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5761 { "=2", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5762 { "=3", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5763 { "=4", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5764 { "=5", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5765 { "=6", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5768 ScAddress aPos(1,2,0); // B3
5769 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5770 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5773 // Expected output table content. 0 = empty cell
5774 std::vector<std::vector<const char*>> aOutputCheck = {
5775 { "1", "2", "TRUE" },
5776 { "2", "4", "TRUE" },
5777 { "3", "6", "TRUE" },
5778 { "4", "8", "TRUE" },
5779 { "5", "10", "TRUE" },
5780 { "6", "12", "TRUE" },
5783 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5784 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5787 // Convert B5:C6 to static values, and check the result.
5788 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5789 ScRange aConvRange(1,4,0,2,5,0); // B5:C6
5790 rFunc.ConvertFormulaToValue(aConvRange, false);
5793 // Expected output table content. 0 = empty cell
5794 std::vector<std::vector<const char*>> aOutputCheck = {
5795 { "1", "2", "TRUE" },
5796 { "2", "4", "TRUE" },
5797 { "3", "6", "FALSE" },
5798 { "4", "8", "FALSE" },
5799 { "5", "10", "TRUE" },
5800 { "6", "12", "TRUE" },
5803 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5804 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5807 // Make sure that B3:B4 and B7:B8 are formula cells.
5808 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5809 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5810 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5811 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5813 // Make sure that B5:C6 are numeric cells.
5814 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5815 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5816 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5817 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5819 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5820 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5821 CPPUNIT_ASSERT(pFC);
5822 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5823 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5824 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5825 CPPUNIT_ASSERT(pFC);
5826 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5827 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5829 // Undo and check.
5830 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5831 CPPUNIT_ASSERT(pUndoMgr);
5832 pUndoMgr->Undo();
5835 // Expected output table content. 0 = empty cell
5836 std::vector<std::vector<const char*>> aOutputCheck = {
5837 { "1", "2", "TRUE" },
5838 { "2", "4", "TRUE" },
5839 { "3", "6", "TRUE" },
5840 { "4", "8", "TRUE" },
5841 { "5", "10", "TRUE" },
5842 { "6", "12", "TRUE" },
5845 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
5846 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5849 // B3:B8 should all be (ungrouped) formula cells.
5850 for (SCROW i = 2; i <= 7; ++i)
5852 pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
5853 CPPUNIT_ASSERT(pFC);
5854 CPPUNIT_ASSERT(!pFC->IsShared());
5857 // C3:C8 should be shared formula cells.
5858 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5859 CPPUNIT_ASSERT(pFC);
5860 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5861 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
5863 // Redo and check.
5864 pUndoMgr->Redo();
5866 // Expected output table content. 0 = empty cell
5867 std::vector<std::vector<const char*>> aOutputCheck = {
5868 { "1", "2", "TRUE" },
5869 { "2", "4", "TRUE" },
5870 { "3", "6", "FALSE" },
5871 { "4", "8", "FALSE" },
5872 { "5", "10", "TRUE" },
5873 { "6", "12", "TRUE" },
5876 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5877 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5880 // Make sure that B3:B4 and B7:B8 are formula cells.
5881 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5882 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5883 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5884 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5886 // Make sure that B5:C6 are numeric cells.
5887 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5888 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5889 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5890 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5892 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5893 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5894 CPPUNIT_ASSERT(pFC);
5895 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5896 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5897 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5898 CPPUNIT_ASSERT(pFC);
5899 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5900 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5902 // Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
5903 pUndoMgr->Undo();
5904 m_pDoc->SetValue(ScAddress(1,4,0), 10);
5905 m_pDoc->SetValue(ScAddress(1,5,0), 11);
5906 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
5907 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
5909 m_pDoc->DeleteTab(0);
5912 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue2)
5914 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5915 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5917 m_pDoc->InsertTab(0, u"Test"_ustr);
5919 std::vector<std::vector<const char*>> aData = {
5920 { "=1", "=ISFORMULA(RC[-1])" },
5921 { "=2", "=ISFORMULA(RC[-1])" },
5922 { "3", "=ISFORMULA(RC[-1])" },
5923 { "=4", "=ISFORMULA(RC[-1])" },
5924 { "=5", "=ISFORMULA(RC[-1])" },
5927 // Insert data into B2:C6.
5928 ScAddress aPos(1,1,0); // B2
5929 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5930 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5933 // Expected output table content. 0 = empty cell
5934 std::vector<std::vector<const char*>> aOutputCheck = {
5935 { "1", "TRUE" },
5936 { "2", "TRUE" },
5937 { "3", "FALSE" },
5938 { "4", "TRUE" },
5939 { "5", "TRUE" },
5942 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5943 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5946 // Convert B3:B5 to a value.
5947 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5948 ScRange aConvRange(1,2,0,1,4,0); // B3:B5
5949 rFunc.ConvertFormulaToValue(aConvRange, false);
5952 // Expected output table content. 0 = empty cell
5953 std::vector<std::vector<const char*>> aOutputCheck = {
5954 { "1", "TRUE" },
5955 { "2", "FALSE" },
5956 { "3", "FALSE" },
5957 { "4", "FALSE" },
5958 { "5", "TRUE" },
5961 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5962 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5965 // Undo and check.
5966 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5967 CPPUNIT_ASSERT(pUndoMgr);
5968 pUndoMgr->Undo();
5971 // Expected output table content. 0 = empty cell
5972 std::vector<std::vector<const char*>> aOutputCheck = {
5973 { "1", "TRUE" },
5974 { "2", "TRUE" },
5975 { "3", "FALSE" },
5976 { "4", "TRUE" },
5977 { "5", "TRUE" },
5980 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5981 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5984 m_pDoc->DeleteTab(0);
5987 CPPUNIT_TEST_FIXTURE(Test, testColumnFindEditCells)
5989 m_pDoc->InsertTab(0, u"Test"_ustr);
5991 // Test the basics with real edit cells, using Column A.
5993 SCROW nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
5994 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
5995 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,0,0));
5996 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
5997 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,10,0));
5998 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
6000 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
6001 rEE.SetTextCurrentDefaults(u"Test"_ustr);
6002 m_pDoc->SetEditText(ScAddress(0,0,0), rEE.CreateTextObject());
6003 const EditTextObject* pObj = m_pDoc->GetEditText(ScAddress(0,0,0));
6004 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell here.", pObj);
6006 ScRange aRange(0,0,0,0,0,0);
6007 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6008 CPPUNIT_ASSERT_EQUAL_MESSAGE("There is an edit cell here.", SCROW(0), nResRow);
6010 aRange.aStart.SetRow(1);
6011 aRange.aEnd.SetRow(1);
6012 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6013 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6015 aRange.aStart.SetRow(2);
6016 aRange.aEnd.SetRow(4);
6017 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6018 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6020 aRange.aStart.SetRow(0);
6021 aRange.aEnd.SetRow(m_pDoc->MaxRow());
6022 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6023 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an edit cell in specified range.", SCROW(0), nResRow);
6025 m_pDoc->SetString(ScAddress(0,0,0), u"Test"_ustr);
6026 m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
6027 ScRefCellValue aCell;
6028 aCell.assign(*m_pDoc, ScAddress(0,0,0));
6029 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string cell.", CELLTYPE_STRING, aCell.getType());
6030 aCell.assign(*m_pDoc, ScAddress(0,1,0));
6031 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6032 aCell.assign(*m_pDoc, ScAddress(0,2,0));
6033 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a numeric cell.", CELLTYPE_VALUE, aCell.getType());
6034 aCell.assign(*m_pDoc, ScAddress(0,3,0));
6035 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6037 aRange.aStart.SetRow(1);
6038 aRange.aEnd.SetRow(1);
6039 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6040 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6042 // Test with non-edit cell but with ambiguous script type.
6044 m_pDoc->SetString(ScAddress(1,11,0), u"Some text"_ustr);
6045 m_pDoc->SetString(ScAddress(1,12,0), u"Some text"_ustr);
6046 m_pDoc->SetString(ScAddress(1,13,0), u"Other text"_ustr);
6048 m_pDoc->SetScriptType(ScAddress(1,11,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6049 m_pDoc->SetScriptType(ScAddress(1,12,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6050 m_pDoc->SetScriptType(ScAddress(1,13,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6052 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,11,0));
6053 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), nResRow);
6054 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,12,0));
6055 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), nResRow);
6057 for (SCROW i = 0; i <= 5; ++i)
6058 m_pDoc->SetString(ScAddress(2,i,0), u"Text"_ustr);
6060 m_pDoc->SetScriptType(ScAddress(2,5,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6062 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(2,1,0));
6063 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(-1), nResRow);
6065 m_pDoc->DeleteTab(0);
6069 CPPUNIT_TEST_FIXTURE(Test, testSetFormula)
6071 m_pDoc->InsertTab(0, u"Test"_ustr);
6073 static struct aInputs
6075 SCROW nRow;
6076 SCCOL nCol;
6077 const char* aFormula1; // Represents the formula that is input to SetFormula function.
6078 const char* aFormula2; // Represents the formula that is actually stored in the cell.
6079 formula::FormulaGrammar::Grammar const eGram;
6081 } const aTest[] = {
6082 { 5 , 4 , "=SUM($D$2:$F$3)" ,"=SUM($D$2:$F$3)" , formula::FormulaGrammar::Grammar::GRAM_ENGLISH },
6083 { 5 , 5 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE },
6084 { 6 , 6 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE_XL_A1},
6085 { 7 , 8 , "=[.A1]-[.$C2]+[.G$3]-[.$F$4]","=A1-$C2+G$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_ODFF }
6088 for(size_t i = 0; i < SAL_N_ELEMENTS(aTest); ++i)
6090 m_pDoc->SetFormula(ScAddress(aTest[i].nCol, aTest[i].nRow, 0), OUString::createFromAscii(aTest[i].aFormula1), aTest[i].eGram);
6091 OUString aBuffer = m_pDoc->GetFormula(aTest[i].nCol, aTest[i].nRow, 0);
6093 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set formula", OUString::createFromAscii(aTest[i].aFormula2), aBuffer);
6096 m_pDoc->DeleteTab(0);
6099 CPPUNIT_TEST_FIXTURE(Test, testMultipleDataCellsInRange)
6101 m_pDoc->InsertTab(0, u"Test"_ustr);
6103 ScRange aRange(1,2,0); // B3
6104 sc::MultiDataCellState aState = m_pDoc->HasMultipleDataCells(aRange);
6105 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::Empty, aState.meState);
6107 // Set a numeric value to B3.
6108 m_pDoc->SetValue(ScAddress(1,2,0), 1.0);
6109 aState = m_pDoc->HasMultipleDataCells(aRange);
6110 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6111 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6112 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6114 // Set another numeric value to B4.
6115 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
6116 aRange.aEnd.SetRow(3); // B3:B4
6117 aState = m_pDoc->HasMultipleDataCells(aRange);
6118 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6119 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6120 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6122 // Set the query range to B4:B5. Now it should only report one cell, with
6123 // B4 being the first non-empty cell.
6124 aRange.aStart.SetRow(3);
6125 aRange.aEnd.SetRow(4);
6126 aState = m_pDoc->HasMultipleDataCells(aRange);
6127 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6128 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6129 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6131 // Set the query range to A1:C3. The first non-empty cell should be B3.
6132 aRange = ScRange(0,0,0,2,2,0);
6133 aState = m_pDoc->HasMultipleDataCells(aRange);
6134 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6135 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6136 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6138 // Set string cells to D4 and F5, and query D3:F5. D4 should be the first
6139 // non-empty cell.
6140 m_pDoc->SetString(ScAddress(3,3,0), u"foo"_ustr);
6141 m_pDoc->SetString(ScAddress(5,4,0), u"bar"_ustr);
6142 aRange = ScRange(3,2,0,5,4,0);
6143 aState = m_pDoc->HasMultipleDataCells(aRange);
6144 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6145 CPPUNIT_ASSERT_EQUAL(SCCOL(3), aState.mnCol1);
6146 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6148 // TODO : add more test cases as needed.
6150 m_pDoc->DeleteTab(0);
6153 CPPUNIT_TEST_FIXTURE(Test, testFormulaWizardSubformula)
6155 m_pDoc->InsertTab(0, u"Test"_ustr);
6157 m_pDoc->SetString(ScAddress(1,0,0), u"=1"_ustr); // B1
6158 m_pDoc->SetString(ScAddress(1,1,0), u"=1/0"_ustr); // B2
6159 m_pDoc->SetString(ScAddress(1,2,0), u"=gibberish"_ustr); // B3
6161 ScSimpleFormulaCalculator aFCell1( *m_pDoc, ScAddress(0,0,0), u"=B1:B3"_ustr, true );
6162 FormulaError nErrCode = aFCell1.GetErrCode();
6163 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell1.IsMatrix() );
6164 CPPUNIT_ASSERT_EQUAL( u"{1|#DIV/0!|#NAME?}"_ustr, aFCell1.GetString().getString() );
6166 m_pDoc->SetString(ScAddress(1,0,0), u"=NA()"_ustr); // B1
6167 m_pDoc->SetString(ScAddress(1,1,0), u"2"_ustr); // B2
6168 m_pDoc->SetString(ScAddress(1,2,0), u"=1+2"_ustr); // B3
6169 ScSimpleFormulaCalculator aFCell2( *m_pDoc, ScAddress(0,0,0), u"=B1:B3"_ustr, true );
6170 nErrCode = aFCell2.GetErrCode();
6171 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell2.IsMatrix() );
6172 CPPUNIT_ASSERT_EQUAL( u"{#N/A|2|3}"_ustr, aFCell2.GetString().getString() );
6174 m_pDoc->DeleteTab(0);
6177 CPPUNIT_TEST_FIXTURE(Test, testDiagonalBorders)
6179 m_pDoc->InsertTab(0, u"Diagonal"_ustr);
6181 ScAddress aPos;
6182 const editeng::SvxBorderLine* pLine;
6183 const ScPatternAttr* pPat;
6185 // diagonal down border
6186 ::editeng::SvxBorderLine dDownBorderLine(nullptr, 1);
6187 SvxLineItem dDownLineItem(ATTR_BORDER_TLBR);
6188 dDownLineItem.SetLine(&dDownBorderLine);
6190 // set diagonal down border to cell(A1)
6191 m_pDoc->ApplyAttr(0, 0, 0, dDownLineItem);
6193 aPos = { 0, 0, 0 };
6194 pPat = m_pDoc->GetPattern(aPos);
6195 CPPUNIT_ASSERT(pPat);
6197 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6198 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6200 // diagonal up border
6201 ::editeng::SvxBorderLine dUpBorderLine(nullptr, 1);
6202 SvxLineItem dUpLineItem(ATTR_BORDER_BLTR);
6203 dUpLineItem.SetLine(&dUpBorderLine);
6205 // set diagonal up border to cell(A2)
6206 m_pDoc->ApplyAttr(0, 1, 0, dUpLineItem);
6208 aPos = { 0, 1, 0 };
6209 pPat = m_pDoc->GetPattern(aPos);
6210 CPPUNIT_ASSERT(pPat);
6212 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6213 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6215 // diagonal down and up border in the same cell (A5)
6216 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6217 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6219 // test if both borders are applied successfully in the same cell (A5)
6220 aPos = { 0, 4, 0 };
6221 pPat = m_pDoc->GetPattern(aPos);
6222 CPPUNIT_ASSERT(pPat);
6224 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6225 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6226 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6227 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6229 // test if both borders are removed successfully
6230 dDownLineItem.SetLine(nullptr);
6231 dUpLineItem.SetLine(nullptr);
6233 // SetLine(nullptr) should remove the lines from (A5)
6234 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6235 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6237 pPat = m_pDoc->GetPattern(aPos);
6238 CPPUNIT_ASSERT(pPat);
6240 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6241 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was not expected, but is found!", !pLine);
6242 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6243 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was not expected, but is found!", !pLine);
6245 m_pDoc->DeleteTab(0);
6248 CPPUNIT_TEST_FIXTURE(Test, testWholeDocBorders)
6250 m_pDoc->InsertTab(0, u"Borders"_ustr);
6252 // Set outside border to be on all sides, and inside borders to be only vertical.
6253 // This should result in edge borders of the spreadsheets being set, but internal
6254 // borders between cells should be only vertical, not horizontal.
6255 ::editeng::SvxBorderLine line(nullptr, 50, SvxBorderLineStyle::SOLID);
6256 SvxBoxItem borderItem(ATTR_BORDER);
6257 borderItem.SetLine(&line, SvxBoxItemLine::LEFT);
6258 borderItem.SetLine(&line, SvxBoxItemLine::RIGHT);
6259 borderItem.SetLine(&line, SvxBoxItemLine::TOP);
6260 borderItem.SetLine(&line, SvxBoxItemLine::BOTTOM);
6261 SvxBoxInfoItem boxInfoItem(ATTR_BORDER);
6262 boxInfoItem.SetLine(&line, SvxBoxInfoItemLine::VERT);
6264 ScMarkData mark( m_pDoc->GetSheetLimits(), ScRange( 0, 0, 0, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 ));
6265 m_pDoc->ApplySelectionFrame( mark, borderItem, &boxInfoItem );
6267 const ScPatternAttr* attr;
6268 attr = m_pDoc->GetPattern( 0, 0, 0 );
6269 CPPUNIT_ASSERT(attr);
6270 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6271 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6272 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6273 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6275 attr = m_pDoc->GetPattern( 1, 0, 0 );
6276 CPPUNIT_ASSERT(attr);
6277 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6278 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6279 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6280 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6282 attr = m_pDoc->GetPattern( 0, 1, 0 );
6283 CPPUNIT_ASSERT(attr);
6284 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6285 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6286 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6287 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6289 attr = m_pDoc->GetPattern( 1, 1, 0 );
6290 CPPUNIT_ASSERT(attr);
6291 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6292 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6293 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6294 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6296 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), 0, 0 );
6297 CPPUNIT_ASSERT(attr);
6298 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6299 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6300 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6301 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6303 attr = m_pDoc->GetPattern( 0, m_pDoc->MaxRow(), 0 );
6304 CPPUNIT_ASSERT(attr);
6305 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6306 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6307 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6308 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6310 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 );
6311 CPPUNIT_ASSERT(attr);
6312 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6313 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6314 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6315 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6317 m_pDoc->DeleteTab(0);
6320 CPPUNIT_TEST_FIXTURE(Test, testSetStringAndNote)
6322 m_pDoc->InsertTab(0, u"Test"_ustr);
6324 // We need a drawing layer in order to create caption objects.
6325 m_pDoc->InitDrawLayer(m_xDocShell.get());
6327 //note on A1
6328 ScAddress aAdrA1 (0, 0, 0);
6329 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
6330 pNote->SetText(aAdrA1, u"Hello world in A1"_ustr);
6332 m_pDoc->SetString(0, 0, 0, u""_ustr);
6334 pNote = m_pDoc->GetNote(aAdrA1);
6335 CPPUNIT_ASSERT(pNote);
6337 m_pDoc->DeleteTab(0);
6340 CPPUNIT_TEST_FIXTURE(Test, testUndoDataAnchor)
6342 m_pDoc->InsertTab(0, u"Tab1"_ustr);
6343 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 1 sheets to begin with",
6344 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
6346 m_pDoc->InitDrawLayer();
6347 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
6348 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
6349 SdrPage* pPage = pDrawLayer->GetPage(0);
6350 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
6352 // Insert an object.
6353 tools::Rectangle aObjRect(2,1000,100,1100);
6354 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
6355 pPage->InsertObject(pObj.get());
6356 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
6358 // Get anchor data
6359 ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
6360 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6362 ScAddress aOldStart = pData->maStart;
6363 ScAddress aOldEnd = pData->maEnd;
6365 // Get non rotated anchor data
6366 ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6367 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6369 ScAddress aNOldStart = pNData->maStart;
6370 ScAddress aNOldEnd = pNData->maEnd;
6371 CPPUNIT_ASSERT_EQUAL(aOldStart, aNOldStart);
6372 CPPUNIT_ASSERT_EQUAL(aOldEnd, aNOldEnd);
6374 //pDrawLayer->BeginCalcUndo(false);
6375 // Insert a new row at row 3.
6376 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6377 ScMarkData aMark(m_pDoc->GetSheetLimits());
6378 aMark.SelectOneTable(0);
6379 rFunc.InsertCells(ScRange( 0, aOldStart.Row() - 1, 0, m_pDoc->MaxCol(), aOldStart.Row(), 0 ), &aMark, INS_INSROWS_BEFORE, true, true);
6381 pData = ScDrawLayer::GetObjData(pObj.get());
6382 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6384 ScAddress aNewStart = pData->maStart;
6385 ScAddress aNewEnd = pData->maEnd;
6387 // Get non rotated anchor data
6388 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6389 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6391 ScAddress aNNewStart = pNData->maStart;
6392 ScAddress aNNewEnd = pNData->maEnd;
6393 CPPUNIT_ASSERT_EQUAL(aNewStart, aNNewStart);
6394 CPPUNIT_ASSERT_EQUAL(aNewEnd, aNNewEnd);
6395 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewStart != aOldStart );
6396 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewEnd != aOldEnd );
6397 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewStart != aNOldStart );
6398 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewEnd != aNOldEnd );
6400 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6401 CPPUNIT_ASSERT(pUndoMgr);
6402 pUndoMgr->Undo();
6404 // Check state
6405 ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObj);
6406 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Failed to check state SCA_CELL.", SCA_CELL, oldType);
6408 // Get anchor data
6409 pData = ScDrawLayer::GetObjData(pObj.get());
6410 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6412 // Get non rotated anchor data
6413 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6414 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6416 // Check if data has moved to new rows
6417 CPPUNIT_ASSERT_EQUAL(pData->maStart, aOldStart);
6418 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aOldEnd);
6420 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNOldStart);
6421 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNOldEnd);
6423 pUndoMgr->Redo();
6425 // Get anchor data
6426 pData = ScDrawLayer::GetObjData(pObj.get());
6427 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6429 // Get non rotated anchor data
6430 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6431 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6433 // Check if data has moved to new rows
6434 CPPUNIT_ASSERT_EQUAL(pData->maStart, aNewStart);
6435 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aNewEnd);
6437 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNNewStart);
6438 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNNewEnd);
6440 m_pDoc->DeleteTab(0);
6444 CPPUNIT_TEST_FIXTURE(Test, testEmptyCalcDocDefaults)
6446 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCellCount() );
6447 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetFormulaGroupCount() );
6448 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCodeCount() );
6449 CPPUNIT_ASSERT_EQUAL( int(CharCompressType::NONE), static_cast<int>(m_pDoc->GetAsianCompression()) );
6451 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasPrintRange() );
6452 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInVBAMode() );
6453 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasNotes() );
6454 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6456 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedFonts() );
6457 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedUsedFontsOnly() );
6458 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptLatin() );
6459 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptAsian() );
6460 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptComplex() );
6461 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedded() );
6463 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDocEditable() );
6464 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocProtected() );
6465 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocVisible() );
6466 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUserInteractionEnabled() );
6468 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasAnyCalcNotification() );
6469 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAutoCalcShellDisabled() );
6470 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsForcedFormulaPending() );
6471 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalculatingFormulaTree() );
6473 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipOrUndo() );
6474 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboard() );
6475 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsUndo() );
6476 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUndoEnabled() );
6477 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6478 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboardSource() );
6479 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInsertingFromOtherDoc() );
6480 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->PastingDrawFromOtherDoc() );
6482 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAdjustHeightLocked() );
6483 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsExecuteLinkEnabled() );
6484 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChangeReadOnlyEnabled() );
6486 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IdleCalcTextWidth() );
6487 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsIdleEnabled() );
6488 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDetectiveDirty() );
6489 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasLinkFormulaNeedingCheck() );
6490 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChartListenerCollectionNeedsUpdate() );
6492 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasRangeOverflow() );
6493 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsImportingXML() );
6494 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalcingAfterLoad() );
6495 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetNoListening() );
6497 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianCompression() );
6498 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetAsianKerning() );
6499 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianKerning() );
6501 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreter() );
6502 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreterTableOp() );
6503 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInDtorClear() );
6504 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsExpandRefs() );
6505 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInLinkUpdate() );
6507 SCTAB tab = m_pDoc->GetVisibleTab();
6509 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsVisible(tab) );
6510 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDefaultTabBgColor(tab) );
6511 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasTable(tab) );
6513 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsActiveScenario(tab) );
6514 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasCalcNotification(tab) );
6515 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasManualBreaks(tab) );
6518 void Test::checkPrecisionAsShown( OUString& rCode, double fValue, double fExpectedRoundVal )
6520 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
6521 sal_uInt32 nFormat = pFormatter->GetEntryKey( rCode );
6522 if ( nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
6524 sal_Int32 nCheckPos = 0;
6525 SvNumFormatType nType;
6526 pFormatter->PutEntry( rCode, nCheckPos, nType, nFormat );
6527 CPPUNIT_ASSERT_EQUAL( sal_Int32(0), nCheckPos );
6529 double fRoundValue = m_pDoc->RoundValueAsShown( fValue, nFormat );
6530 OString aMessage = "Format \"" +
6531 OUStringToOString( rCode, RTL_TEXTENCODING_ASCII_US ) +
6532 "\" is not correctly rounded";
6533 CPPUNIT_ASSERT_EQUAL_MESSAGE( aMessage.getStr(), fExpectedRoundVal, fRoundValue );
6536 CPPUNIT_TEST_FIXTURE(Test, testPrecisionAsShown)
6538 m_pDoc->InsertTab(0, u"Test"_ustr);
6540 // Turn on "precision as shown" option.
6541 setCalcAsShown( m_pDoc, true);
6543 OUString aCode;
6544 double fValue, fExpectedRoundVal;
6545 { // decimal rounding
6546 aCode = "0.00";
6547 fValue = 1.0/3.0;
6548 fExpectedRoundVal = 0.33;
6549 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6550 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6551 fValue = 10.001;
6552 fExpectedRoundVal = 10.0;
6553 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6554 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6556 { // thousand rounding tdf#106253
6557 aCode = "0,,";
6558 fValue = 4.0e9 / 7.0;
6559 fExpectedRoundVal = 571e6;
6560 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6561 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6562 aCode = "\"k\"[$$-409]* #,;[RED]-\"k\"[$$-409]* #,";
6563 fValue = 4.0e8 / 7.0;
6564 fExpectedRoundVal = 57.143e6;
6565 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6566 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6568 { // percent rounding
6569 aCode = "0.00%";
6570 fValue = 4.0 / 7.0;
6571 fExpectedRoundVal = 0.5714;
6572 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6573 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6574 fValue = 40.0 / 7.0;
6575 fExpectedRoundVal = 5.7143;
6576 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6577 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6579 { // scientific rounding
6580 aCode = "0.00E0";
6581 fValue = 400000.0 / 7.0;
6582 fExpectedRoundVal = 57100.0;
6583 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6584 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6585 fValue = 4.0 / 70000.0;
6586 fExpectedRoundVal = 5.71e-5;
6587 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6588 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6589 // engineering rounding tdf#106252
6590 aCode = "##0.000E0";
6591 fValue = 400000.0 / 7.0;
6592 fExpectedRoundVal = 57.143e3;
6593 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6594 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6595 fValue = 4000000.0 / 7.0;
6596 fExpectedRoundVal = 571.429e3;
6597 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6598 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6599 fValue = 40000000.0 / 7.0;
6600 fExpectedRoundVal = 5.714e6;
6601 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6602 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6603 fValue = 4.0 / 70000.0;
6604 fExpectedRoundVal = 57.143e-6;
6605 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6606 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6607 fValue = 4.0 / 7000.0;
6608 fExpectedRoundVal = 571.429e-6;
6609 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6610 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6611 fValue = 4.0 / 700.0;
6612 fExpectedRoundVal = 5.714e-3;
6613 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6614 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6615 aCode = "##?0.0#E0";
6616 fValue = 400000.0 / 7.0;
6617 fExpectedRoundVal = 5.71e4;
6618 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6619 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6620 fValue = 4000000.0 / 7.0;
6621 fExpectedRoundVal = 57.14e4;
6622 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6623 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6624 fValue = 40000000.0 / 7.0;
6625 fExpectedRoundVal = 571.43e4;
6626 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6627 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6628 fValue = 400000000.0 / 7.0;
6629 fExpectedRoundVal = 5714.29e4;
6630 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6631 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6632 fValue = 4.0 / 70000.0;
6633 fExpectedRoundVal = 5714.29e-8;
6634 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6635 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6636 fValue = 4.0 / 7000.0;
6637 fExpectedRoundVal = 5.71e-4;
6638 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6639 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6640 fValue = 4.0 / 700.0;
6641 fExpectedRoundVal = 57.14e-4;
6642 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6643 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6644 fValue = 4.0 / 70.0;
6645 fExpectedRoundVal = 571.43e-4;
6646 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6647 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6649 { // fraction rounding tdf#105657
6650 aCode = "# ?/?";
6651 fValue = 0.35;
6652 fExpectedRoundVal = 1.0/3.0;
6653 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6654 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6656 { // exact fraction
6657 aCode = "# ?/??";
6658 fValue = 0.35;
6659 fExpectedRoundVal = 0.35;
6660 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6661 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6663 { // several sub-formats tdf#106052
6664 aCode = "0.00;-0.000";
6665 fValue = 1.0/3.0;
6666 fExpectedRoundVal = 0.33;
6667 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6668 fValue = -1.0/3.0;
6669 fExpectedRoundVal = -0.333;
6670 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6673 setCalcAsShown( m_pDoc, false);
6674 m_pDoc->DeleteTab(0);
6677 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByRow)
6679 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6680 m_pDoc->InsertTab(0, u"Protected"_ustr);
6683 // Remove protected flags from rows 2-5.
6684 ScPatternAttr aAttr(m_pDoc->getCellAttributeHelper());
6685 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6686 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 4, 0, aAttr);
6688 // Protect the sheet without any options.
6689 ScTableProtection aProtect;
6690 aProtect.setProtected(true);
6691 m_pDoc->SetTabProtection(0, &aProtect);
6693 // Try to delete row 3. It should fail.
6694 ScRange aRow3(0,2,0,m_pDoc->MaxCol(),2,0);
6695 ScMarkData aMark(m_pDoc->GetSheetLimits());
6696 aMark.SelectOneTable(0);
6697 bool bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6698 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should fail.", !bDeleted);
6700 // Protect the sheet but allow row deletion.
6701 aProtect.setOption(ScTableProtection::DELETE_ROWS, true);
6702 m_pDoc->SetTabProtection(0, &aProtect);
6704 // Now we should be able to delete row 3.
6705 bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6706 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should succeed.", bDeleted);
6708 // But, row deletion should still fail on a protected row.
6709 ScRange aRow10(0,9,0,m_pDoc->MaxCol(),9,0);
6710 bDeleted = rDocFunc.DeleteCells(aRow10, &aMark, DelCellCmd::Rows, true);
6711 CPPUNIT_ASSERT_MESSAGE("deletion of row 10 should not be allowed.", !bDeleted);
6713 // Try inserting a new row. It should fail.
6714 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6715 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6717 // Allow row insertions.
6718 aProtect.setOption(ScTableProtection::INSERT_ROWS, true);
6719 m_pDoc->SetTabProtection(0, &aProtect);
6721 bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6722 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should succeed.", bInserted);
6724 // Row insertion is allowed even when the rows above and below have protected flags set.
6725 bInserted = rDocFunc.InsertCells(aRow10, &aMark, INS_INSROWS_AFTER, true, true);
6726 CPPUNIT_ASSERT_MESSAGE("row insertion at row 10 should succeed.", bInserted);
6729 m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
6732 // Insert matrix into B2:C3.
6733 ScMarkData aMark(m_pDoc->GetSheetLimits());
6734 aMark.SelectOneTable(1);
6735 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, u"={1;2|3;4}"_ustr);
6737 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6738 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6739 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6740 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6742 // Try to insert a row at row 3. It should fail because of matrix's presence.
6744 ScRange aRow3(0,2,1,m_pDoc->MaxCol(),2,1);
6745 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_BEFORE, true, true);
6746 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6749 m_pDoc->DeleteTab(1);
6750 m_pDoc->DeleteTab(0);
6753 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByColumn)
6755 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6756 m_pDoc->InsertTab(0, u"Protected"_ustr);
6759 // Remove protected flags from columns B to E.
6760 ScPatternAttr aAttr(m_pDoc->getCellAttributeHelper());
6761 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6762 m_pDoc->ApplyPatternAreaTab(1, 0, 4, m_pDoc->MaxRow(), 0, aAttr);
6764 // Protect the sheet without any options.
6765 ScTableProtection aProtect;
6766 aProtect.setProtected(true);
6767 m_pDoc->SetTabProtection(0, &aProtect);
6769 // Try to delete column C. It should fail.
6770 ScRange aCol3(2,0,0,2,m_pDoc->MaxRow(),0);
6771 ScMarkData aMark(m_pDoc->GetSheetLimits());
6772 aMark.SelectOneTable(0);
6773 bool bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6774 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should fail.", !bDeleted);
6776 // Protect the sheet but allow column deletion.
6777 aProtect.setOption(ScTableProtection::DELETE_COLUMNS, true);
6778 m_pDoc->SetTabProtection(0, &aProtect);
6780 // Now we should be able to delete column C.
6781 bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6782 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should succeed.", bDeleted);
6784 // But, column deletion should still fail on a protected column.
6785 ScRange aCol10(9,0,0,9,m_pDoc->MaxRow(),0);
6786 bDeleted = rDocFunc.DeleteCells(aCol10, &aMark, DelCellCmd::Cols, true);
6787 CPPUNIT_ASSERT_MESSAGE("deletion of column 10 should not be allowed.", !bDeleted);
6789 // Try inserting a new column. It should fail.
6790 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6791 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should fail.", !bInserted);
6793 // Allow column insertions.
6794 aProtect.setOption(ScTableProtection::INSERT_COLUMNS, true);
6795 m_pDoc->SetTabProtection(0, &aProtect);
6797 bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6798 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should succeed.", bInserted);
6800 // Column insertion is allowed even when the columns above and below have protected flags set.
6801 bInserted = rDocFunc.InsertCells(aCol10, &aMark, INS_INSCOLS_AFTER, true, true);
6802 CPPUNIT_ASSERT_MESSAGE("column insertion at column 10 should succeed.", bInserted);
6805 m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
6808 // Insert matrix into B2:C3.
6809 ScMarkData aMark(m_pDoc->GetSheetLimits());
6810 aMark.SelectOneTable(1);
6811 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, u"={1;2|3;4}"_ustr);
6813 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6814 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6815 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6816 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6818 // Try to insert a column at column C. It should fail because of matrix's presence.
6820 ScRange aCol3(2,0,1,2,m_pDoc->MaxRow(),1);
6821 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_BEFORE, true, true);
6822 CPPUNIT_ASSERT_MESSAGE("column insertion at column C should fail.", !bInserted);
6825 m_pDoc->DeleteTab(1);
6826 m_pDoc->DeleteTab(0);
6829 CPPUNIT_TEST_FIXTURE(Test, testInsertColumnsWithFormulaCells)
6831 m_pDoc->InsertTab(0, u"Tab1"_ustr);
6833 std::set<SCCOL> aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6834 CPPUNIT_ASSERT_MESSAGE("empty sheet should contain no formula cells.", aCols.empty());
6836 auto equals = [](const std::set<SCCOL>& left, const std::set<SCCOL>& right)
6838 return left == right;
6841 // insert formula cells in columns 2, 4 and 6.
6842 m_pDoc->SetFormula(ScAddress(2, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
6843 m_pDoc->SetFormula(ScAddress(4, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
6844 m_pDoc->SetFormula(ScAddress(6, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
6846 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6848 std::set<SCCOL> aExpected = { 2, 4, 6 };
6849 CPPUNIT_ASSERT_MESSAGE("Columns 2, 4 and 6 should contain formula cells.", equals(aExpected, aCols));
6851 // Insert 2 columns at column A to shift everything to right by 2.
6852 m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 0, 2);
6854 aExpected = { 4, 6, 8 };
6855 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6856 CPPUNIT_ASSERT_MESSAGE("Columns 4, 6 and 8 should contain formula cells.", equals(aExpected, aCols));
6860 m_pDoc->CheckIntegrity(0);
6862 catch (const std::exception& e)
6864 std::ostringstream os;
6865 os << "document integrity check failed: " << e.what();
6866 CPPUNIT_FAIL(os.str());
6869 m_pDoc->DeleteTab(0);
6872 CPPUNIT_TEST_FIXTURE(Test, testDocumentModelAccessor_getDocumentCurrencies)
6874 m_pDoc->InsertTab(0, u"Sheet1"_ustr);
6876 // Check document currencies - expect 0
6877 auto pAccessor = m_xDocShell->GetDocumentModelAccessor();
6878 CPPUNIT_ASSERT(pAccessor);
6879 CPPUNIT_ASSERT_EQUAL(size_t(0), pAccessor->getDocumentCurrencies().size());
6881 // Set a currency to a cell
6883 m_pDoc->SetValue(ScAddress(0, 0, 0), 2.0);
6885 OUString aCode = u"#.##0,00[$€-424]"_ustr;
6887 sal_Int32 nCheckPos;
6888 SvNumFormatType eType;
6889 sal_uInt32 nFormat;
6891 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
6892 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
6894 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
6895 SfxItemSet& rSet = aNewAttrs.GetItemSet();
6896 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
6897 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs); // A1.
6899 CPPUNIT_ASSERT_EQUAL(u"2,00€"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
6902 // Check document currencies again
6903 auto aCurrencyIDs = pAccessor->getDocumentCurrencies();
6904 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
6906 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
6907 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
6908 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
6910 // Set the same currency to 2 more cells
6912 m_pDoc->SetValue(ScAddress(1, 1, 0), 5.0);
6913 m_pDoc->SetValue(ScAddress(2, 2, 0), 7.0);
6915 OUString aCode = u"#.##0,00[$€-424]"_ustr;
6917 sal_Int32 nCheckPos;
6918 SvNumFormatType eType;
6919 sal_uInt32 nFormat;
6921 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
6922 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
6924 ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
6925 SfxItemSet& rSet = aNewAttrs.GetItemSet();
6926 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
6927 m_pDoc->ApplyPattern(1, 1, 0, aNewAttrs); // B2.
6928 m_pDoc->ApplyPattern(2, 2, 0, aNewAttrs); // C3.
6930 CPPUNIT_ASSERT_EQUAL(u"5,00€"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0)));
6931 CPPUNIT_ASSERT_EQUAL(u"7,00€"_ustr, m_pDoc->GetString(ScAddress(2, 2, 0)));
6934 // Check document currencies again - should be 1 entry only
6935 aCurrencyIDs = pAccessor->getDocumentCurrencies();
6936 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
6938 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
6939 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
6940 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
6944 CPPUNIT_PLUGIN_IMPLEMENT();
6946 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */