Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / qa / unit / ucalc.cxx
blobc78cc476b691ddca6c2739b3ce46883db619a268
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 <docpool.hxx>
42 #include <globalnames.hxx>
43 #include <columnspanset.hxx>
45 #include <editable.hxx>
46 #include <tabprotection.hxx>
47 #include <undomanager.hxx>
49 #include <formula/IFunctionDescription.hxx>
51 #include <editeng/borderline.hxx>
52 #include <editeng/brushitem.hxx>
53 #include <editeng/eeitem.hxx>
54 #include <editeng/wghtitem.hxx>
55 #include <editeng/postitem.hxx>
56 #include <editeng/lineitem.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("A", "B");
111 CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
114 CPPUNIT_TEST_FIXTURE(Test, testSharedStringPool)
116 m_pDoc->InsertTab(0, "foo");
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), "Andy"); // A1
124 m_pDoc->SetString(ScAddress(0,1,0), "Andy"); // A2
125 m_pDoc->SetString(ScAddress(0,2,0), "Bruce"); // A3
126 m_pDoc->SetString(ScAddress(0,3,0), "andy"); // A4
127 m_pDoc->SetString(ScAddress(0,4,0), "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
179 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
180 rEE.SetTextCurrentDefaults("Andy and Bruce");
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("ANDY and BRUCE");
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, "Test");
264 m_pDoc->SetString(ScAddress(0,0,0), "Header");
265 m_pDoc->SetString(ScAddress(0,1,0), "A1");
266 m_pDoc->SetString(ScAddress(0,2,0), "A2");
267 m_pDoc->SetString(ScAddress(0,3,0), "A3");
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, "foo");
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, "foo"));
356 OUString test;
358 m_pDoc->SetString(0, 0, 0, "'10.5");
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, "'apple'");
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, "000123", &aParam);
371 test = m_pDoc->GetString(0, 0, 0);
372 CPPUNIT_ASSERT_EQUAL_MESSAGE("Text content should have been treated as string, not number.", OUString("000123"), 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, "foo"));
382 m_pDoc->SetString(0, 0, 0, "'10.5");
383 m_pDoc->SetString(0, m_pDoc->MaxRow()-5, 0, "42.0");
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, "FirstPrintRange"));
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, "SecondPrintRange"));
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, "Sheet 1"));
461 CPPUNIT_ASSERT(m_pDoc->InsertTab(1, "Sheet 2"));
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(OUString("='Sheet 2'!R1C1"), 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, "Test"));
486 m_pDoc->SetString(ScAddress(0,0,0), "=(1;2)");
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(OUString("=(1;2)"), aFormula);
494 m_pDoc->DeleteTab(0);
497 CPPUNIT_TEST_FIXTURE(Test, testTdf114406)
499 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
500 m_pDoc->SetString(ScAddress(0,0,0), "5");
501 m_pDoc->SetString(ScAddress(1,0,0), "=A1/100%");
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(OUString("=A1/100%"), 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, "Test"));
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, "Test"));
533 m_pDoc->SetString(ScAddress(0,0,0), "--1");
534 m_pDoc->SetString(ScAddress(0,1,0), "---1");
535 m_pDoc->SetString(ScAddress(0,2,0), "+-1");
536 m_pDoc->SetString(ScAddress(0,3,0), "+--1");
538 // Without the fix in place, this test would have failed with
539 // - Expected: --1
540 // - Actual : -1
541 CPPUNIT_ASSERT_EQUAL(OUString("--1"), m_pDoc->GetString(ScAddress(0,0,0)));
542 CPPUNIT_ASSERT_EQUAL(OUString("---1"), m_pDoc->GetString(ScAddress(0,1,0)));
543 CPPUNIT_ASSERT_EQUAL(OUString("+-1"), m_pDoc->GetString(ScAddress(0,2,0)));
544 CPPUNIT_ASSERT_EQUAL(OUString("+--1"), 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, "Test"));
553 m_pDoc->SetString(ScAddress(0,0,0), "1:60");
554 m_pDoc->SetString(ScAddress(0,1,0), "1:123");
555 m_pDoc->SetString(ScAddress(0,2,0), "1:1:123");
556 m_pDoc->SetString(ScAddress(0,3,0), "0:123");
557 m_pDoc->SetString(ScAddress(0,4,0), "0:0:123");
558 m_pDoc->SetString(ScAddress(0,5,0), "0:123:59");
560 // These are not valid duration inputs
561 CPPUNIT_ASSERT_EQUAL(OUString("1:60"), m_pDoc->GetString(ScAddress(0,0,0)));
562 CPPUNIT_ASSERT_EQUAL(OUString("1:123"), m_pDoc->GetString(ScAddress(0,1,0)));
563 CPPUNIT_ASSERT_EQUAL(OUString("1:1:123"), 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(OUString("02:03:00 AM"), m_pDoc->GetString(ScAddress(0,3,0)));
570 CPPUNIT_ASSERT_EQUAL(OUString("12:02:03 AM"), m_pDoc->GetString(ScAddress(0,4,0)));
571 CPPUNIT_ASSERT_EQUAL(OUString("02:03:59 AM"), 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, "Sheet1");
580 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to increment sheet count.",
581 static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
582 m_pDoc->InsertTab(1, "Sheet2");
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), "Test");
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), "=A1");
596 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
597 m_pDoc->SetString(ScAddress(3,1,1), "=A2");
598 m_pDoc->SetString(ScAddress(3,2,1), "=A3");
599 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(1), m_pDoc->GetFormulaGroupCount());
600 m_pDoc->SetString(ScAddress(3,3,1), "=A5");
601 m_pDoc->SetString(ScAddress(3,4,1), "=A6");
602 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt64>(2), m_pDoc->GetFormulaGroupCount());
603 m_pDoc->SetString(ScAddress(3,1,1), "=A3");
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, "Sheet1");
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, "Test");
655 m_pDoc->SetString(ScAddress(0,5,0), "Andy");
656 m_pDoc->SetString(ScAddress(0,6,0), "Bruce");
657 m_pDoc->SetString(ScAddress(0,7,0), "Charlie");
658 m_pDoc->SetString(ScAddress(0,10,0), "Andy");
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(OUString("Andy"), it->GetString());
667 ++it;
668 CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
669 ++it;
670 CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), 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(OUString("Andy"), it->GetString());
681 ++it;
682 CPPUNIT_ASSERT_EQUAL(OUString("Bruce"), it->GetString());
683 ++it;
684 CPPUNIT_ASSERT_EQUAL(OUString("Charlie"), 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, "Test");
699 // Insert values into B2:B4.
700 m_pDoc->SetString(ScAddress(1,1,0), "=1"); // 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), "A");
706 m_pDoc->SetString(ScAddress(1,5,0), "B");
707 m_pDoc->SetString(ScAddress(1,6,0), "=\"C\""); // formula
708 m_pDoc->SetString(ScAddress(1,7,0), "D");
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("Rich Text");
718 m_pDoc->SetEditText(ScAddress(3,4,0), rEE.CreateTextObject());
720 // Insert Another string into D6.
721 m_pDoc->SetString(ScAddress(3,5,0), "E");
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, "Test2");
834 m_pDoc->InsertTab(2, "Test3");
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, "Test");
868 // Insert cells to A1, A5, B2 and C3.
869 m_pDoc->SetString(ScAddress(0,0,0), "California");
870 m_pDoc->SetValue(ScAddress(0,4,0), 1.2);
871 m_pDoc->SetEditText(ScAddress(1,1,0), "Boston");
872 m_pDoc->SetFormula(ScAddress(2,2,0), "=SUM(1,2,3)", 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, "src"));
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, "Header");
924 m_pDoc->SetString(0, 1, 0, "1");
925 m_pDoc->SetString(0, 2, 0, "2");
926 m_pDoc->SetString(0, 3, 0, "3");
927 m_pDoc->SetString(0, 4, 0, "=4/2");
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, "Hello world in A1");
935 // Copy statically to another document.
937 ScDocShellRef xDocSh2;
938 getNewDocShell(xDocSh2);
939 ScDocument* pDestDoc = &xDocSh2->GetDocument();
940 pDestDoc->InsertTab(0, "src");
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, "test");
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, "Test");
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, "Test");
1190 // Set the background color of B2:C3,D2,E3,C4:D4,B5:D5 to blue
1191 ScPatternAttr aCellBackColor(m_pDoc->GetPool());
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 (SfxPoolItem::areSame( pAttr, m_pDoc->GetDefPattern()))
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, "Tab1");
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->GetPool());
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, "Tab1");
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->GetPool());
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->GetDefPattern();
1305 CPPUNIT_ASSERT(!SfxPoolItem::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, "Tab1");
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, "foo"));
1449 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1450 m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // 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), "=A1"); // 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), "=A1");
1523 m_pDoc->SetString(ScAddress(1,1,0), "=A2");
1524 m_pDoc->SetString(ScAddress(1,2,0), "=A3");
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), "=RC[-1]");
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), "=A1");
1606 m_pDoc->SetString(ScAddress(2,0,0), "=B1");
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, "foo"));
1644 // First, the normal case, with no missing parameters.
1645 m_pDoc->SetString(0, 0, 0, "=AVERAGE(1;2;3)");
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, "=AVERAGE(1;;;)");
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, "=\"\"+3"); // empty string
1659 m_pDoc->SetString(0, 1, 0, "=\" \"+3"); // only blank
1660 m_pDoc->SetString(0, 2, 0, "=\" 4 \"+3"); // number in blanks
1661 m_pDoc->SetString(0, 3, 0, "=\" x \"+3"); // non-numeric
1662 m_pDoc->SetString(0, 4, 0, "=\"4.4\"+3"); // 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", OUString("#VALUE!"), 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", OUString("#VALUE!"), aVal);
1690 aVal = m_pDoc->GetString( 0, 1, 0);
1691 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), 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", OUString("#VALUE!"), 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", OUString("#VALUE!"), aVal);
1712 aVal = m_pDoc->GetString( 0, 4, 0);
1713 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), 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", OUString("#VALUE!"), aVal);
1722 aVal = m_pDoc->GetString( 0, 1, 0);
1723 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), 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", OUString("#VALUE!"), aVal);
1728 aVal = m_pDoc->GetString( 0, 4, 0);
1729 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), 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", OUString("#VALUE!"), aVal);
1754 aVal = m_pDoc->GetString( 0, 1, 0);
1755 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1756 aVal = m_pDoc->GetString( 0, 2, 0);
1757 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1758 aVal = m_pDoc->GetString( 0, 3, 0);
1759 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), aVal);
1760 aVal = m_pDoc->GetString( 0, 4, 0);
1761 CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", OUString("#VALUE!"), 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, "Sheet1"));
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, "=A1/Divisor");
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, "local1", ScAddress(0,0,0));
1817 ScRangeData* pLocal2 = new ScRangeData( *m_pDoc, "local2", "$Sheet1.$A$1");
1818 ScRangeData* pLocal3 = new ScRangeData( *m_pDoc, "local3", "Sheet1.$A$1");
1819 ScRangeData* pLocal4 = new ScRangeData( *m_pDoc, "local4", "$A$1"); // 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, "Sheet2"));
1829 // Use other-sheet-local name of Sheet1 on Sheet2.
1830 ScAddress aPos(1,0,1);
1831 OUString aFormula("=Sheet1.local1+Sheet1.local2+Sheet1.local3+Sheet1.local4");
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, "Test");
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", OUString("Test"), 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("Test"), 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("Test"), 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("A"), 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("B"), 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, "foo");
2138 // Insert the source values in A1:A2.
2139 m_pDoc->SetString(0, 0, 0, "=1/0");
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, "=A1:A2>0");
2148 CPPUNIT_ASSERT_EQUAL(OUString("#DIV/0!"), m_pDoc->GetString(0,0,0));
2149 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue( 0,1,0));
2150 CPPUNIT_ASSERT_EQUAL(OUString("#DIV/0!"), m_pDoc->GetString(1,2,0));
2151 CPPUNIT_ASSERT_EQUAL(OUString("TRUE"), m_pDoc->GetString(1,3,0));
2153 m_pDoc->DeleteTab(0);
2156 CPPUNIT_TEST_FIXTURE(Test, testMatrixConditionalBooleanResult)
2158 m_pDoc->InsertTab(0, "foo");
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, "=IF({1;0};TRUE();42)"); // {TRUE,42}
2165 m_pDoc->InsertMatrixFormula( 0,1, 1,1, aMark, "=IF({0;1};TRUE();42)"); // {42,1} aim for {42,TRUE}
2166 m_pDoc->InsertMatrixFormula( 0,2, 1,2, aMark, "=IF({1;0};42;FALSE())"); // {42,0} aim for {42,FALSE}
2167 m_pDoc->InsertMatrixFormula( 0,3, 1,3, aMark, "=IF({0;1};42;FALSE())"); // {FALSE,42}
2169 CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(0,0,0));
2170 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(1,0,0));
2171 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(0,1,0));
2172 //CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString(1,1,0)); // not yet
2173 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(0,2,0));
2174 //CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(1,2,0)); // not yet
2175 CPPUNIT_ASSERT_EQUAL( OUString("FALSE"), m_pDoc->GetString(0,3,0));
2176 CPPUNIT_ASSERT_EQUAL( OUString("42"), m_pDoc->GetString(1,3,0));
2178 m_pDoc->DeleteTab(0);
2181 CPPUNIT_TEST_FIXTURE(Test, testEnterMixedMatrix)
2183 m_pDoc->InsertTab(0, "foo");
2185 // Insert the source values in A1:B2.
2186 m_pDoc->SetString(0, 0, 0, "A");
2187 m_pDoc->SetString(1, 0, 0, "B");
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, "=A1:B2");
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, "Test");
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), "=5");
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, "=TRANSPOSE(A1:B1)");
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, "TestTab");
2252 ScAddress aSrc(0,0,0);
2253 ScAddress aDest(0,1,0);
2254 OUString aStr("please copy me");
2255 m_pDoc->SetString(aSrc, "please copy me");
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, "TestTab");
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), "copy me");
2274 // Insert edit cells in B1:B3.
2275 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
2276 rEE.SetTextCurrentDefaults("Edit 1");
2277 m_pDoc->SetEditText(ScAddress(1,0,0), rEE.CreateTextObject());
2278 rEE.SetTextCurrentDefaults("Edit 2");
2279 m_pDoc->SetEditText(ScAddress(1,1,0), rEE.CreateTextObject());
2280 rEE.SetTextCurrentDefaults("Edit 3");
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, "Hello world in A3");
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(OUString("copy me"), 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(OUString("Edit 1"), 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(OUString("Edit 2"), 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(OUString("Edit 3"), 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, "TestTab1");
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, "TestTab2");
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", OUString("TestTab1"), 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", OUString("TestTab2"), aName);
2413 m_pDoc->DeleteTab(1);
2414 m_pDoc->DeleteTab(0);
2417 CPPUNIT_TEST_FIXTURE(Test, testDataArea)
2419 m_pDoc->InsertTab(0, "Data");
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, "Some text");
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, "Sheet1");
2457 m_pDoc->InsertTab(1, "Sheet2");
2458 m_pDoc->InsertTab(2, "Sheet3");
2459 m_pDoc->InsertTab(3, "Sheet4");
2460 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", static_cast<SCTAB>(4), m_pDoc->GetTableCount());
2462 OUString a1("A1");
2463 OUString a2("A2");
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, "=Sheet1.A1");
2476 m_pDoc->SetString(0, 1, 1, "=Sheet1.A2");
2477 m_pDoc->SetString(0, 0, 2, "=Sheet1.A1");
2478 m_pDoc->SetString(0, 0, 3, "=Sheet1.A2");
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 "RANDBETWEEN.NV",
2707 "RAWSUBTRACT",
2708 "ROUND",
2709 "ROUNDDOWN",
2710 "ROUNDSIG",
2711 "ROUNDUP",
2712 "SEC",
2713 "SECH",
2714 "SIGN",
2715 "SIN",
2716 "SINH",
2717 "SQRT",
2718 "SUBTOTAL",
2719 "SUM",
2720 "SUMIF",
2721 "SUMIFS",
2722 "SUMSQ",
2723 "TAN",
2724 "TANH",
2725 "TRUNC",
2726 nullptr
2729 const char* aArray[] = {
2730 "FOURIER",
2731 "FREQUENCY",
2732 "GROWTH",
2733 "LINEST",
2734 "LOGEST",
2735 "MDETERM",
2736 "MINVERSE",
2737 "MMULT",
2738 "MUNIT",
2739 "SUMPRODUCT",
2740 "SUMX2MY2",
2741 "SUMX2PY2",
2742 "SUMXMY2",
2743 "TRANSPOSE",
2744 "TREND",
2745 nullptr
2748 const char* aStatistical[] = {
2749 "AVEDEV",
2750 "AVERAGE",
2751 "AVERAGEA",
2752 "AVERAGEIF",
2753 "AVERAGEIFS",
2754 "B",
2755 "BETA.DIST",
2756 "BETA.INV",
2757 "BETADIST",
2758 "BETAINV",
2759 "BINOM.DIST",
2760 "BINOM.INV",
2761 "BINOMDIST",
2762 "CHIDIST",
2763 "CHIINV",
2764 "CHISQ.DIST",
2765 "CHISQ.DIST.RT",
2766 "CHISQ.INV",
2767 "CHISQ.INV.RT",
2768 "CHISQ.TEST",
2769 "CHISQDIST",
2770 "CHISQINV",
2771 "CHITEST",
2772 "CONFIDENCE",
2773 "CONFIDENCE.NORM",
2774 "CONFIDENCE.T",
2775 "CORREL",
2776 "COUNT",
2777 "COUNTA",
2778 "COUNTBLANK",
2779 "COUNTIF",
2780 "COUNTIFS",
2781 "COVAR",
2782 "COVARIANCE.P",
2783 "COVARIANCE.S",
2784 "CRITBINOM",
2785 "DEVSQ",
2786 "ERF.PRECISE",
2787 "ERFC.PRECISE",
2788 "EXPON.DIST",
2789 "EXPONDIST",
2790 "F.DIST",
2791 "F.DIST.RT",
2792 "F.INV",
2793 "F.INV.RT",
2794 "F.TEST",
2795 "FDIST",
2796 "FINV",
2797 "FISHER",
2798 "FISHERINV",
2799 "FORECAST",
2800 "FORECAST.ETS.ADD",
2801 "FORECAST.ETS.MULT",
2802 "FORECAST.ETS.PI.ADD",
2803 "FORECAST.ETS.PI.MULT",
2804 "FORECAST.ETS.SEASONALITY",
2805 "FORECAST.ETS.STAT.ADD",
2806 "FORECAST.ETS.STAT.MULT",
2807 "FORECAST.LINEAR",
2808 "FTEST",
2809 "GAMMA",
2810 "GAMMA.DIST",
2811 "GAMMA.INV",
2812 "GAMMADIST",
2813 "GAMMAINV",
2814 "GAMMALN",
2815 "GAMMALN.PRECISE",
2816 "GAUSS",
2817 "GEOMEAN",
2818 "HARMEAN",
2819 "HYPGEOM.DIST",
2820 "HYPGEOMDIST",
2821 "INTERCEPT",
2822 "KURT",
2823 "LARGE",
2824 "LOGINV",
2825 "LOGNORM.DIST",
2826 "LOGNORM.INV",
2827 "LOGNORMDIST",
2828 "MAX",
2829 "MAXA",
2830 "MAXIFS",
2831 "MEDIAN",
2832 "MIN",
2833 "MINA",
2834 "MINIFS",
2835 "MODE",
2836 "MODE.MULT",
2837 "MODE.SNGL",
2838 "NEGBINOM.DIST",
2839 "NEGBINOMDIST",
2840 "NORM.DIST",
2841 "NORM.INV",
2842 "NORM.S.DIST",
2843 "NORM.S.INV",
2844 "NORMDIST",
2845 "NORMINV",
2846 "NORMSDIST",
2847 "NORMSINV",
2848 "PEARSON",
2849 "PERCENTILE",
2850 "PERCENTILE.EXC",
2851 "PERCENTILE.INC",
2852 "PERCENTRANK",
2853 "PERCENTRANK.EXC",
2854 "PERCENTRANK.INC",
2855 "PERMUT",
2856 "PERMUTATIONA",
2857 "PHI",
2858 "POISSON",
2859 "POISSON.DIST",
2860 "PROB",
2861 "QUARTILE",
2862 "QUARTILE.EXC",
2863 "QUARTILE.INC",
2864 "RANK",
2865 "RANK.AVG",
2866 "RANK.EQ",
2867 "RSQ",
2868 "SKEW",
2869 "SKEWP",
2870 "SLOPE",
2871 "SMALL",
2872 "STANDARDIZE",
2873 "STDEV",
2874 "STDEV.P",
2875 "STDEV.S",
2876 "STDEVA",
2877 "STDEVP",
2878 "STDEVPA",
2879 "STEYX",
2880 "T.DIST",
2881 "T.DIST.2T",
2882 "T.DIST.RT",
2883 "T.INV",
2884 "T.INV.2T",
2885 "T.TEST",
2886 "TDIST",
2887 "TINV",
2888 "TRIMMEAN",
2889 "TTEST",
2890 "VAR",
2891 "VAR.P",
2892 "VAR.S",
2893 "VARA",
2894 "VARP",
2895 "VARPA",
2896 "WEIBULL",
2897 "WEIBULL.DIST",
2898 "Z.TEST",
2899 "ZTEST",
2900 nullptr
2903 const char* aSpreadsheet[] = {
2904 "ADDRESS",
2905 "AREAS",
2906 "CHOOSE",
2907 "COLUMN",
2908 "COLUMNS",
2909 "DDE",
2910 "ERROR.TYPE",
2911 "ERRORTYPE",
2912 "FILTER",
2913 "GETPIVOTDATA",
2914 "HLOOKUP",
2915 "HYPERLINK",
2916 "INDEX",
2917 "INDIRECT",
2918 "LOOKUP",
2919 "MATCH",
2920 "OFFSET",
2921 "ROW",
2922 "ROWS",
2923 "SHEET",
2924 "SHEETS",
2925 "SORT",
2926 "SORTBY",
2927 "STYLE",
2928 "VLOOKUP",
2929 "XLOOKUP",
2930 "XMATCH",
2931 nullptr
2934 const char* aText[] = {
2935 "ARABIC",
2936 "ASC",
2937 "BAHTTEXT",
2938 "BASE",
2939 "CHAR",
2940 "CLEAN",
2941 "CODE",
2942 "CONCAT",
2943 "CONCATENATE",
2944 "DECIMAL",
2945 "DOLLAR",
2946 "ENCODEURL",
2947 "EXACT",
2948 "FILTERXML",
2949 "FIND",
2950 "FINDB",
2951 "FIXED",
2952 "JIS",
2953 "LEFT",
2954 "LEFTB",
2955 "LEN",
2956 "LENB",
2957 "LOWER",
2958 "MID",
2959 "MIDB",
2960 "NUMBERVALUE",
2961 "PROPER",
2962 "REGEX",
2963 "REPLACE",
2964 "REPLACEB",
2965 "REPT",
2966 "RIGHT",
2967 "RIGHTB",
2968 "ROMAN",
2969 "ROT13",
2970 "SEARCH",
2971 "SEARCHB",
2972 "SUBSTITUTE",
2973 "T",
2974 "TEXT",
2975 "TEXTJOIN",
2976 "TRIM",
2977 "UNICHAR",
2978 "UNICODE",
2979 "UPPER",
2980 "VALUE",
2981 "WEBSERVICE",
2982 nullptr
2985 struct {
2986 const char* Category; const char** Functions;
2987 } aTests[] = {
2988 { "Database", aDataBase },
2989 { "Date&Time", aDateTime },
2990 { "Financial", aFinancial },
2991 { "Information", aInformation },
2992 { "Logical", aLogical },
2993 { "Mathematical", aMathematical },
2994 { "Array", aArray },
2995 { "Statistical", aStatistical },
2996 { "Spreadsheet", aSpreadsheet },
2997 { "Text", aText },
2998 { "Add-in", nullptr },
2999 { nullptr, nullptr }
3002 ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
3003 sal_uInt32 n = pFuncMgr->getCount();
3004 for (sal_uInt32 i = 0; i < n; ++i)
3006 const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
3007 CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
3008 sal_uInt32 nFuncCount = pCat->getCount();
3009 for (sal_uInt32 j = 0; j < nFuncCount; ++j)
3011 const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
3012 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
3017 CPPUNIT_TEST_FIXTURE(Test, testGraphicsInGroup)
3019 m_pDoc->InsertTab(0, "TestTab");
3020 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.",
3021 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
3022 SCROW nRow1, nRow2;
3023 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3024 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
3025 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
3026 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
3028 m_pDoc->InitDrawLayer();
3029 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
3030 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
3031 SdrPage* pPage = pDrawLayer->GetPage(0);
3032 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
3035 //Add a square
3036 tools::Rectangle aOrigRect(2,2,100,100);
3037 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aOrigRect);
3038 pPage->InsertObject(pObj.get());
3039 const tools::Rectangle &rNewRect = pObj->GetLogicRect();
3040 CPPUNIT_ASSERT_EQUAL_MESSAGE("must have equal position and size",
3041 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3043 ScDrawLayer::SetPageAnchored(*pObj);
3045 //Use a range of rows guaranteed to include all of the square
3046 m_pDoc->ShowRows(0, 100, 0, false);
3047 m_pDoc->SetDrawPageSize(0);
3048 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3049 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3050 m_pDoc->ShowRows(0, 100, 0, true);
3051 m_pDoc->SetDrawPageSize(0);
3052 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored",
3053 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3055 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
3056 CPPUNIT_ASSERT_EQUAL_MESSAGE("That shouldn't change size or positioning",
3057 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3059 m_pDoc->ShowRows(0, 100, 0, false);
3060 m_pDoc->SetDrawPageSize(0);
3062 CPPUNIT_ASSERT_EQUAL_MESSAGE("Hiding should not change the logic rectangle",
3063 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3064 CPPUNIT_ASSERT_MESSAGE("Hiding should make invisible", !pObj->IsVisible());
3066 m_pDoc->ShowRows(0, 100, 0, true);
3067 m_pDoc->SetDrawPageSize(0);
3068 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when cell anchored",
3069 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3070 CPPUNIT_ASSERT_MESSAGE("Show should make visible", pObj->IsVisible());
3074 // Add a circle.
3075 tools::Rectangle aOrigRect(10,10,210,210); // 200 x 200
3076 rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
3077 pPage->InsertObject(pObj.get());
3078 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3079 CPPUNIT_ASSERT_EQUAL_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
3080 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3082 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3083 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell anchored. Not good.",
3084 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3086 // Insert 2 rows at the top. This should push the circle object down.
3087 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3088 m_pDoc->SetDrawPageSize(0);
3090 // Make sure the size of the circle is still identical.
3091 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of the circle has changed, but shouldn't!",
3092 aOrigRect.GetSize(), rNewRect.GetSize());
3094 // Delete 2 rows at the top. This should bring the circle object to its original position.
3095 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3096 m_pDoc->SetDrawPageSize(0);
3097 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to move back to its original position.",
3098 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3102 // Add a line.
3103 basegfx::B2DPolygon aTempPoly;
3104 Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
3105 tools::Rectangle aOrigRect(10,200,110,300); // 100 x 100
3106 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
3107 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
3108 rtl::Reference<SdrPathObj> pObj = new SdrPathObj(*pDrawLayer, SdrObjKind::Line, basegfx::B2DPolyPolygon(aTempPoly));
3109 pObj->NbcSetLogicRect(aOrigRect);
3110 pPage->InsertObject(pObj.get());
3111 const tools::Rectangle& rNewRect = pObj->GetLogicRect();
3112 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size differ.",
3113 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3115 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3116 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell-anchored. Not good.",
3117 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3119 // Insert 2 rows at the top and delete them immediately.
3120 m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3121 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
3122 m_pDoc->SetDrawPageSize(0);
3123 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of a line object changed after row insertion and removal.",
3124 const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
3126 sal_Int32 n = pObj->GetPointCount();
3127 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 2 points in a line object.", static_cast<sal_Int32>(2), n);
3128 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3129 aStartPos, pObj->GetPoint(0));
3130 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
3131 aEndPos, pObj->GetPoint(1));
3134 m_pDoc->DeleteTab(0);
3137 CPPUNIT_TEST_FIXTURE(Test, testGraphicsOnSheetMove)
3139 m_pDoc->InsertTab(0, "Tab1");
3140 m_pDoc->InsertTab(1, "Tab2");
3141 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 2 sheets to begin with", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
3143 m_pDoc->InitDrawLayer();
3144 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
3145 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
3146 SdrPage* pPage = pDrawLayer->GetPage(0);
3147 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
3149 // Insert an object.
3150 tools::Rectangle aObjRect(2,2,100,100);
3151 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
3152 pPage->InsertObject(pObj.get());
3153 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
3155 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", static_cast<size_t>(1), pPage->GetObjCount());
3157 const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
3158 CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
3159 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3160 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3162 pPage = pDrawLayer->GetPage(1);
3163 CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
3164 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", static_cast<size_t>(0), pPage->GetObjCount());
3166 // Insert a new sheet at left-end, and make sure the object has moved to
3167 // the 2nd page.
3168 m_pDoc->InsertTab(0, "NewTab");
3169 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", static_cast<SCTAB>(3), m_pDoc->GetTableCount());
3170 pPage = pDrawLayer->GetPage(0);
3171 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3172 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3173 pPage = pDrawLayer->GetPage(1);
3174 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3175 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3176 pPage = pDrawLayer->GetPage(2);
3177 CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage);
3178 CPPUNIT_ASSERT_EQUAL_MESSAGE("3rd sheet should have no object.", size_t(0), pPage->GetObjCount());
3180 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3181 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3183 // Now, delete the sheet that just got inserted. The object should be back
3184 // on the 1st sheet.
3185 m_pDoc->DeleteTab(0);
3186 pPage = pDrawLayer->GetPage(0);
3187 CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage);
3188 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have one object.", size_t(1), pPage->GetObjCount());
3189 CPPUNIT_ASSERT_EQUAL_MESSAGE("Size and position of the object shouldn't change.",
3190 aObjRect, pObj->GetLogicRect());
3192 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
3193 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
3195 // Move the 1st sheet to the last position.
3196 m_pDoc->MoveTab(0, 1);
3197 pPage = pDrawLayer->GetPage(0);
3198 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
3199 CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
3200 pPage = pDrawLayer->GetPage(1);
3201 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
3202 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
3203 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
3204 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
3206 // Copy the 2nd sheet, which has one drawing object to the last position.
3207 m_pDoc->CopyTab(1, 2);
3208 pPage = pDrawLayer->GetPage(2);
3209 CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage);
3210 CPPUNIT_ASSERT_EQUAL_MESSAGE("Copied sheet should have one object.", size_t(1), pPage->GetObjCount());
3211 pObj = pPage->GetObj(0);
3212 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
3213 pData = ScDrawLayer::GetObjData(pObj.get());
3214 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
3215 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maStart.Tab());
3216 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maEnd.Tab());
3218 m_pDoc->DeleteTab(2);
3219 m_pDoc->DeleteTab(1);
3220 m_pDoc->DeleteTab(0);
3223 CPPUNIT_TEST_FIXTURE(Test, testToggleRefFlag)
3226 * Test toggling relative/absolute flag of cell and cell range references.
3227 * This corresponds with hitting Shift-F4 while the cursor is on a formula
3228 * cell.
3230 // In this test, there is no need to insert formula string into a cell in
3231 // the document, as ScRefFinder does not depend on the content of the
3232 // document except for the sheet names.
3234 m_pDoc->InsertTab(0, "Test");
3237 // Calc A1: basic 2D reference
3239 OUString aFormula("=B100");
3240 ScAddress aPos(1, 5, 0);
3241 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3243 // Original
3244 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3246 // column relative / row relative -> column absolute / row absolute
3247 aFinder.ToggleRel(0, aFormula.getLength());
3248 aFormula = aFinder.GetText();
3249 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=$B$100"), aFormula );
3251 // column absolute / row absolute -> column relative / row absolute
3252 aFinder.ToggleRel(0, aFormula.getLength());
3253 aFormula = aFinder.GetText();
3254 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=B$100"), aFormula );
3256 // column relative / row absolute -> column absolute / row relative
3257 aFinder.ToggleRel(0, aFormula.getLength());
3258 aFormula = aFinder.GetText();
3259 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=$B100"), aFormula );
3261 // column absolute / row relative -> column relative / row relative
3262 aFinder.ToggleRel(0, aFormula.getLength());
3263 aFormula = aFinder.GetText();
3264 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong conversion.", OUString("=B100"), aFormula );
3268 // Excel R1C1: basic 2D reference
3270 OUString aFormula("=R2C1");
3271 ScAddress aPos(3, 5, 0);
3272 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3274 // Original
3275 CPPUNIT_ASSERT_EQUAL_MESSAGE("Does not equal the original text.", aFormula, aFinder.GetText());
3277 // column absolute / row absolute -> column relative / row absolute
3278 aFinder.ToggleRel(0, aFormula.getLength());
3279 aFormula = aFinder.GetText();
3280 CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
3282 // column relative / row absolute - > column absolute / row relative
3283 aFinder.ToggleRel(0, aFormula.getLength());
3284 aFormula = aFinder.GetText();
3285 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
3287 // column absolute / row relative -> column relative / row relative
3288 aFinder.ToggleRel(0, aFormula.getLength());
3289 aFormula = aFinder.GetText();
3290 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
3292 // column relative / row relative -> column absolute / row absolute
3293 aFinder.ToggleRel(0, aFormula.getLength());
3294 aFormula = aFinder.GetText();
3295 CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
3299 // Excel R1C1: Selection at the end of the formula string and does not
3300 // overlap the formula string at all (inspired by fdo#39135).
3301 OUString aFormula("=R1C1");
3302 ScAddress aPos(1, 1, 0);
3303 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
3305 // Original
3306 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3308 // Make the column relative.
3309 sal_Int32 n = aFormula.getLength();
3310 aFinder.ToggleRel(n, n);
3311 aFormula = aFinder.GetText();
3312 CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
3314 // Make the row relative.
3315 n = aFormula.getLength();
3316 aFinder.ToggleRel(n, n);
3317 aFormula = aFinder.GetText();
3318 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
3320 // Make both relative.
3321 n = aFormula.getLength();
3322 aFinder.ToggleRel(n, n);
3323 aFormula = aFinder.GetText();
3324 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
3326 // Back to the original.
3327 n = aFormula.getLength();
3328 aFinder.ToggleRel(n, n);
3329 aFormula = aFinder.GetText();
3330 CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
3334 // Calc A1:
3335 OUString aFormula("=A1+4");
3336 ScAddress aPos(1, 1, 0);
3337 ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_OOO);
3339 // Original
3340 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
3342 // Set the cursor over the 'A1' part and toggle.
3343 aFinder.ToggleRel(2, 2);
3344 aFormula = aFinder.GetText();
3345 CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
3347 aFinder.ToggleRel(2, 2);
3348 aFormula = aFinder.GetText();
3349 CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
3351 aFinder.ToggleRel(2, 2);
3352 aFormula = aFinder.GetText();
3353 CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
3355 aFinder.ToggleRel(2, 2);
3356 aFormula = aFinder.GetText();
3357 CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
3360 // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
3361 // partial selection within formula string.
3363 m_pDoc->DeleteTab(0);
3366 CPPUNIT_TEST_FIXTURE(Test, testAutofilter)
3368 m_pDoc->InsertTab( 0, "Test" );
3370 // cell contents (0 = empty cell)
3371 const char* aData[][3] = {
3372 { "C1", "C2", "C3" },
3373 { "0", "1", "A" },
3374 { "1", "2", nullptr },
3375 { "1", "2", "B" },
3376 { "0", "2", "B" }
3379 SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
3380 SCROW nRows = SAL_N_ELEMENTS(aData);
3382 // Populate cells.
3383 for (SCROW i = 0; i < nRows; ++i)
3384 for (SCCOL j = 0; j < nCols; ++j)
3385 if (aData[i][j])
3386 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
3388 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols-1, nRows-1);
3389 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3391 pDBData->SetAutoFilter(true);
3392 ScRange aRange;
3393 pDBData->GetArea(aRange);
3394 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3395 aRange.aEnd.Col(), aRange.aStart.Row(),
3396 aRange.aStart.Tab(), ScMF::Auto);
3398 //create the query param
3399 ScQueryParam aParam;
3400 pDBData->GetQueryParam(aParam);
3401 ScQueryEntry& rEntry = aParam.GetEntry(0);
3402 rEntry.bDoQuery = true;
3403 rEntry.nField = 0;
3404 rEntry.eOp = SC_EQUAL;
3405 rEntry.GetQueryItem().mfVal = 0;
3406 // add queryParam to database range.
3407 pDBData->SetQueryParam(aParam);
3409 // perform the query.
3410 m_pDoc->Query(0, aParam, true);
3412 //control output
3413 SCROW nRow1, nRow2;
3414 bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3415 CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden);
3416 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(2), nRow1);
3417 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(3), nRow2);
3419 // Remove filtering.
3420 rEntry.Clear();
3421 m_pDoc->Query(0, aParam, true);
3422 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3423 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden);
3424 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3425 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3427 // Filter for non-empty cells by column C.
3428 rEntry.bDoQuery = true;
3429 rEntry.nField = 2;
3430 rEntry.SetQueryByNonEmpty();
3431 m_pDoc->Query(0, aParam, true);
3433 // only row 3 should be hidden. The rest should be visible.
3434 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3435 CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden);
3436 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(0), nRow1);
3437 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(1), nRow2);
3438 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3439 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden);
3440 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow1);
3441 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow2);
3442 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3443 CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden);
3444 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", SCROW(3), nRow1);
3445 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", m_pDoc->MaxRow(), nRow2);
3447 // Now, filter for empty cells by column C.
3448 rEntry.SetQueryByEmpty();
3449 m_pDoc->Query(0, aParam, true);
3451 // Now, only row 1 and 3, and 6 and down should be visible.
3452 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3453 CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden);
3454 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow1);
3455 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow2);
3456 bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
3457 CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden);
3458 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow1);
3459 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow2);
3460 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
3461 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden);
3462 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow1);
3463 CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow2);
3464 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
3465 CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden);
3466 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(3), nRow1);
3467 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(4), nRow2);
3468 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3469 CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden);
3470 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", SCROW(5), nRow1);
3471 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", m_pDoc->MaxRow(), nRow2);
3473 m_pDoc->DeleteTab(0);
3476 CPPUNIT_TEST_FIXTURE(Test, testAutoFilterTimeValue)
3478 m_pDoc->InsertTab(0, "Test");
3480 m_pDoc->SetString(ScAddress(0,0,0), "Hours");
3481 m_pDoc->SetValue(ScAddress(0,1,0), 72.3604166666671);
3482 m_pDoc->SetValue(ScAddress(0,2,0), 265);
3484 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 0, 2);
3485 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3487 // Apply the "hour:minute:second" format to A2:A3.
3488 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3489 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_TIME_HH_MMSS, LANGUAGE_ENGLISH_US);
3490 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3491 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3492 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3494 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 2, 0, aNewAttrs); // apply it to A2:A3.
3496 printRange(m_pDoc, ScRange(0,0,0,0,2,0), "Data"); // A1:A3
3498 // Make sure the hour:minute:second format is really applied.
3499 CPPUNIT_ASSERT_EQUAL(OUString("1736:39:00"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3500 CPPUNIT_ASSERT_EQUAL(OUString("6360:00:00"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3502 // Filter by the A2 value. Only A1 and A2 should be visible.
3503 ScQueryParam aParam;
3504 pDBData->GetQueryParam(aParam);
3505 ScQueryEntry& rEntry = aParam.GetEntry(0);
3506 rEntry.bDoQuery = true;
3507 rEntry.nField = 0;
3508 rEntry.eOp = SC_EQUAL;
3509 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern("1736:39:00");
3510 rEntry.GetQueryItem().meType = ScQueryEntry::ByString;
3512 pDBData->SetQueryParam(aParam);
3514 // perform the query.
3515 m_pDoc->Query(0, aParam, true);
3517 // A1:A2 should be visible while A3 should be filtered out.
3518 CPPUNIT_ASSERT_MESSAGE("A1 should be visible.", !m_pDoc->RowFiltered(0,0));
3519 CPPUNIT_ASSERT_MESSAGE("A2 should be visible.", !m_pDoc->RowFiltered(1,0));
3520 CPPUNIT_ASSERT_MESSAGE("A3 should be filtered out.", m_pDoc->RowFiltered(2,0));
3522 m_pDoc->DeleteTab(0);
3525 CPPUNIT_TEST_FIXTURE(Test, testAutofilterOptimizations)
3527 m_pDoc->InsertTab( 0, "Test" );
3529 constexpr SCCOL nCols = 4;
3530 constexpr SCROW nRows = 200;
3531 m_pDoc->SetString(0, 0, 0, "Column1");
3532 m_pDoc->SetString(1, 0, 0, "Column2");
3533 m_pDoc->SetString(2, 0, 0, "Column3");
3534 m_pDoc->SetString(3, 0, 0, "Column4");
3536 // Fill 1st column with 0-199, 2nd with 1-200, 3rd with "1000"-"1199", 4th with "1001-1200"
3537 // (the pairs are off by one to each other to check filtering out a value filters out
3538 // only the relevant column).
3539 for(SCROW i = 0; i < nRows; ++i)
3541 m_pDoc->SetValue(0, i + 1, 0, i);
3542 m_pDoc->SetValue(1, i + 1, 0, i+1);
3543 m_pDoc->SetString(2, i + 1, 0, "val" + OUString::number(i+1000));
3544 m_pDoc->SetString(3, i + 1, 0, "val" + OUString::number(i+1000+1));
3547 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols, nRows);
3548 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3550 pDBData->SetAutoFilter(true);
3551 ScRange aRange;
3552 pDBData->GetArea(aRange);
3553 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3554 aRange.aEnd.Col(), aRange.aStart.Row(),
3555 aRange.aStart.Tab(), ScMF::Auto);
3557 //create the query param
3558 ScQueryParam aParam;
3559 pDBData->GetQueryParam(aParam);
3560 ScQueryEntry& rEntry0 = aParam.GetEntry(0);
3561 rEntry0.bDoQuery = true;
3562 rEntry0.nField = 0;
3563 rEntry0.eOp = SC_EQUAL;
3564 rEntry0.GetQueryItems().resize(nRows);
3565 ScQueryEntry& rEntry1 = aParam.GetEntry(1);
3566 rEntry1.bDoQuery = true;
3567 rEntry1.nField = 1;
3568 rEntry1.eOp = SC_EQUAL;
3569 rEntry1.GetQueryItems().resize(nRows);
3570 ScQueryEntry& rEntry2 = aParam.GetEntry(2);
3571 rEntry2.bDoQuery = true;
3572 rEntry2.nField = 2;
3573 rEntry2.eOp = SC_EQUAL;
3574 rEntry2.GetQueryItems().resize(nRows);
3575 ScQueryEntry& rEntry3 = aParam.GetEntry(3);
3576 rEntry3.bDoQuery = true;
3577 rEntry3.nField = 3;
3578 rEntry3.eOp = SC_EQUAL;
3579 rEntry3.GetQueryItems().resize(nRows);
3580 // Set up autofilter to select all values except one in each column.
3581 // This should only filter out 2nd, 3rd, 6th and 7th rows.
3582 for( int i = 0; i < nRows; ++i )
3584 if(i!= 1)
3585 rEntry0.GetQueryItems()[i].mfVal = i;
3586 if(i!= 2)
3587 rEntry1.GetQueryItems()[i].mfVal = i + 1;
3588 if(i!= 5)
3590 rEntry2.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000));
3591 rEntry2.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3593 if(i!= 6)
3595 rEntry3.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000+1));
3596 rEntry3.GetQueryItems()[i].meType = ScQueryEntry::ByString;
3599 // add queryParam to database range.
3600 pDBData->SetQueryParam(aParam);
3602 // perform the query.
3603 m_pDoc->Query(0, aParam, true);
3605 // check that only rows with filtered out values are hidden, and not rows that share
3606 // a value in a different column
3607 SCROW nRow1, nRow2;
3608 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0, &nRow1, &nRow2));
3609 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden", m_pDoc->RowHidden(2, 0, &nRow1, &nRow2));
3610 CPPUNIT_ASSERT_MESSAGE("row 4 should be hidden", m_pDoc->RowHidden(3, 0, &nRow1, &nRow2));
3611 CPPUNIT_ASSERT_MESSAGE("row 5 should be visible", !m_pDoc->RowHidden(4, 0, &nRow1, &nRow2));
3612 CPPUNIT_ASSERT_MESSAGE("row 6 should be visible", !m_pDoc->RowHidden(5, 0, &nRow1, &nRow2));
3613 CPPUNIT_ASSERT_MESSAGE("row 7 should be hidden", m_pDoc->RowHidden(6, 0, &nRow1, &nRow2));
3614 CPPUNIT_ASSERT_MESSAGE("row 8 should be hidden", m_pDoc->RowHidden(7, 0, &nRow1, &nRow2));
3615 CPPUNIT_ASSERT_MESSAGE("row 9 should be visible", !m_pDoc->RowHidden(8, 0, &nRow1, &nRow2));
3617 // Remove filtering.
3618 rEntry0.Clear();
3619 rEntry1.Clear();
3620 rEntry2.Clear();
3621 m_pDoc->Query(0, aParam, true);
3622 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !m_pDoc->RowHidden(0, 0, &nRow1, &nRow2));
3623 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
3624 CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
3626 m_pDoc->DeleteTab(0);
3629 CPPUNIT_TEST_FIXTURE(Test, testTdf76441)
3631 m_pDoc->InsertTab(0, "Test");
3633 // The result will be different depending on whether the format is set before
3634 // or after inserting the string
3636 OUString aCode = "MM:SS";
3637 sal_Int32 nCheckPos;
3638 SvNumFormatType nType;
3639 sal_uInt32 nFormat;
3640 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3641 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3643 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3644 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3645 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3647 // First insert the string, then the format
3648 m_pDoc->SetString(ScAddress(0,0,0), "01:20");
3650 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3652 CPPUNIT_ASSERT_EQUAL(OUString("20:00"), m_pDoc->GetString(ScAddress(0,0,0)));
3656 // First set the format, then insert the string
3657 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3659 m_pDoc->SetString(ScAddress(0,1,0), "01:20");
3661 // Without the fix in place, this test would have failed with
3662 // - Expected: 01:20
3663 // - Actual : 20:00
3664 CPPUNIT_ASSERT_EQUAL(OUString("01:20"), m_pDoc->GetString(ScAddress(0,1,0)));
3667 m_pDoc->DeleteTab(0);
3670 CPPUNIT_TEST_FIXTURE(Test, testTdf76836)
3672 m_pDoc->InsertTab(0, "Test");
3674 OUString aCode = "\"192.168.0.\"@";
3675 sal_Int32 nCheckPos;
3676 SvNumFormatType nType;
3677 sal_uInt32 nFormat;
3678 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3679 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3681 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3682 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3683 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3685 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3686 m_pDoc->SetValue(0,0,0, 10.0);
3688 // Without the fix in place, this test would have failed with
3689 // - Expected: 10
3690 // - Actual : 192.168.0.10
3691 CPPUNIT_ASSERT_EQUAL(OUString("10"), m_pDoc->GetString(ScAddress(0,0,0)));
3693 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3694 m_pDoc->SetString(ScAddress(0,1,0), "10");
3695 CPPUNIT_ASSERT_EQUAL(OUString("192.168.0.10"), m_pDoc->GetString(ScAddress(0,1,0)));
3697 m_pDoc->DeleteTab(0);
3700 CPPUNIT_TEST_FIXTURE(Test, testTdf142186)
3702 m_pDoc->InsertTab(0, "Test");
3704 // The result will be different depending on whether the format is set before
3705 // or after inserting the string
3707 OUString aCode = "0\".\"0";
3708 sal_Int32 nCheckPos;
3709 SvNumFormatType nType;
3710 sal_uInt32 nFormat;
3711 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3712 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3714 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3715 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3716 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3718 // First insert the string, then the format
3719 m_pDoc->SetString(ScAddress(0,0,0), "123.45");
3721 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3723 CPPUNIT_ASSERT_EQUAL(OUString("12.3"), m_pDoc->GetString(ScAddress(0,0,0)));
3727 // First set the format, then insert the string
3728 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
3730 m_pDoc->SetString(ScAddress(0,1,0), "123.45");
3732 // Without the fix in place, this test would have failed with
3733 // - Expected: 12.3
3734 // - Actual : 1234.5
3735 CPPUNIT_ASSERT_EQUAL(OUString("12.3"), m_pDoc->GetString(ScAddress(0,1,0)));
3738 m_pDoc->DeleteTab(0);
3741 CPPUNIT_TEST_FIXTURE(Test, testTdf137063)
3743 m_pDoc->InsertTab(0, "Test");
3745 m_pDoc->SetValue(0,0,0, 0.000000006);
3746 m_pDoc->SetValue(0,1,0, 0.0000000006);
3748 // Without the fix in place, this test would have failed with
3749 // - Expected: 0.000000006
3750 // - Actual : 6E-09
3751 CPPUNIT_ASSERT_EQUAL(OUString("0.000000006"), m_pDoc->GetString(ScAddress(0,0,0)));
3752 CPPUNIT_ASSERT_EQUAL(OUString("6E-10"), m_pDoc->GetString(ScAddress(0,1,0)));
3754 m_pDoc->DeleteTab(0);
3757 CPPUNIT_TEST_FIXTURE(Test, testTdf126342)
3759 m_pDoc->InsertTab(0, "Test");
3761 OUString aCode = "YYYY-MM-DD";
3762 sal_Int32 nCheckPos;
3763 SvNumFormatType nType;
3764 sal_uInt32 nFormat;
3765 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3766 pFormatter->PutEntry( aCode, nCheckPos, nType, nFormat );
3768 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3769 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3770 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3771 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs);
3773 m_pDoc->SetString(ScAddress(0,0,0), "11/7/19");
3775 CPPUNIT_ASSERT_EQUAL(OUString("2019-11-07"), m_pDoc->GetString(ScAddress(0,0,0)));
3777 // Overwrite the existing date with the exact same input
3778 m_pDoc->SetString(ScAddress(0,0,0), "11/7/19");
3780 // Without the fix in place, this test would have failed with
3781 // - Expected: 2019-11-07
3782 // - Actual : 2011-07-19
3783 CPPUNIT_ASSERT_EQUAL(OUString("2019-11-07"), m_pDoc->GetString(ScAddress(0,0,0)));
3785 m_pDoc->DeleteTab(0);
3788 CPPUNIT_TEST_FIXTURE(Test, testAdvancedFilter)
3790 m_pDoc->InsertTab(0, "Test");
3792 // cell contents (nullptr = empty cell)
3793 std::vector<std::vector<const char*>> aData = {
3794 { "Value", "Tag" }, // A1:B11
3795 { "1", "R" },
3796 { "2", "R" },
3797 { "3", "R" },
3798 { "4", "C" },
3799 { "5", "C" },
3800 { "6", "C" },
3801 { "7", "R" },
3802 { "8", "R" },
3803 { "9", "R" },
3804 { "10", "C" },
3805 { nullptr },
3806 { "Value", "Tag" }, // A13:B14
3807 { "> 5", "R" },
3810 // Populate cells.
3811 for (size_t nRow = 0; nRow < aData.size(); ++nRow)
3813 const std::vector<const char*>& rRowData = aData[nRow];
3814 for (size_t nCol = 0; nCol < rRowData.size(); ++nCol)
3816 const char* pCell = rRowData[nCol];
3817 if (pCell)
3818 m_pDoc->SetString(nCol, nRow, 0, OUString::createFromAscii(pCell));
3822 ScDBData* pDBData = new ScDBData(STR_DB_GLOBAL_NONAME, 0, 0, 0, 1, 10);
3823 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3825 ScRange aDataRange(0,0,0,1,10,0);
3826 ScRange aFilterRuleRange(0,12,0,1,13,0);
3828 printRange(m_pDoc, aDataRange, "Data");
3829 printRange(m_pDoc, aFilterRuleRange, "Filter Rule");
3831 ScQueryParam aQueryParam;
3832 aQueryParam.bHasHeader = true;
3833 aQueryParam.nCol1 = aDataRange.aStart.Col();
3834 aQueryParam.nRow1 = aDataRange.aStart.Row();
3835 aQueryParam.nCol2 = aDataRange.aEnd.Col();
3836 aQueryParam.nRow2 = aDataRange.aEnd.Row();
3837 aQueryParam.nTab = aDataRange.aStart.Tab();
3839 bool bGood = m_pDoc->CreateQueryParam(aFilterRuleRange, aQueryParam);
3840 CPPUNIT_ASSERT_MESSAGE("failed to create query param.", bGood);
3842 // First entry is for the 'Value' field, and is greater than 5.
3843 ScQueryEntry aEntry = aQueryParam.GetEntry(0);
3844 CPPUNIT_ASSERT(aEntry.bDoQuery);
3845 CPPUNIT_ASSERT_EQUAL(SCCOLROW(0), aEntry.nField);
3846 CPPUNIT_ASSERT_EQUAL(SC_GREATER, aEntry.eOp);
3848 ScQueryEntry::QueryItemsType aItems = aEntry.GetQueryItems();
3849 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3850 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByValue, aItems[0].meType);
3851 CPPUNIT_ASSERT_EQUAL(5.0, aItems[0].mfVal);
3853 // Second entry is for the 'Tag' field, and is == 'R'.
3854 aEntry = aQueryParam.GetEntry(1);
3855 CPPUNIT_ASSERT(aEntry.bDoQuery);
3856 CPPUNIT_ASSERT_EQUAL(SCCOLROW(1), aEntry.nField);
3857 CPPUNIT_ASSERT_EQUAL(SC_EQUAL, aEntry.eOp);
3859 aItems = aEntry.GetQueryItems();
3860 CPPUNIT_ASSERT_EQUAL(size_t(1), aItems.size());
3861 CPPUNIT_ASSERT_EQUAL(ScQueryEntry::ByString, aItems[0].meType);
3862 CPPUNIT_ASSERT_EQUAL(OUString("R"), aItems[0].maString.getString());
3864 // perform the query.
3865 m_pDoc->Query(0, aQueryParam, true);
3867 // Only rows 1,8-10 should be visible.
3868 bool bFiltered = m_pDoc->RowFiltered(0, 0);
3869 CPPUNIT_ASSERT_MESSAGE("row 1 (header row) should be visible", !bFiltered);
3871 SCROW nRow1 = -1, nRow2 = -1;
3872 bFiltered = m_pDoc->RowFiltered(1, 0, &nRow1, &nRow2);
3873 CPPUNIT_ASSERT_MESSAGE("rows 2-7 should be filtered out.", bFiltered);
3874 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(1), nRow1);
3875 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(6), nRow2);
3877 bFiltered = m_pDoc->RowFiltered(7, 0, &nRow1, &nRow2);
3878 CPPUNIT_ASSERT_MESSAGE("rows 8-10 should be visible.", !bFiltered);
3879 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(7), nRow1);
3880 CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(9), nRow2);
3882 m_pDoc->DeleteTab(0);
3885 CPPUNIT_TEST_FIXTURE(Test, testDateFilterContains)
3887 m_pDoc->InsertTab(0, "Test");
3889 constexpr SCCOL nCols = 1;
3890 constexpr SCROW nRows = 5;
3891 m_pDoc->SetString(0, 0, 0, "Date");
3892 m_pDoc->SetString(0, 1, 0, "1/2/2021");
3893 m_pDoc->SetString(0, 2, 0, "2/1/1999");
3894 m_pDoc->SetString(0, 3, 0, "2/1/1997");
3895 m_pDoc->SetString(0, 4, 0, "3/3/2001");
3896 m_pDoc->SetString(0, 5, 0, "3/3/1996");
3898 // Set the fields as dates.
3899 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
3900 sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_DATE_DIN_YYMMDD, LANGUAGE_ENGLISH_US);
3901 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
3902 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3903 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
3904 m_pDoc->ApplyPatternAreaTab(0, 1, 0, 5, 0, aNewAttrs); // apply it to A1:A6
3906 ScDBData* pDBData = new ScDBData("NONAME", 0, 0, 0, nCols, nRows);
3907 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));
3909 pDBData->SetAutoFilter(true);
3910 ScRange aRange;
3911 pDBData->GetArea(aRange);
3912 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
3913 aRange.aEnd.Col(), aRange.aStart.Row(),
3914 aRange.aStart.Tab(), ScMF::Auto);
3916 //create the query param
3917 ScQueryParam aParam;
3918 pDBData->GetQueryParam(aParam);
3919 ScQueryEntry& rEntry = aParam.GetEntry(0);
3920 rEntry.bDoQuery = true;
3921 rEntry.nField = 0;
3922 rEntry.eOp = SC_CONTAINS;
3923 rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern("2");
3924 pDBData->SetQueryParam(aParam);
3926 // perform the query.
3927 m_pDoc->Query(0, aParam, true);
3929 // Dates in rows 2-4 contain '2', row 5 shows 2001 only as 01, and row 6 doesn't contain it at all.
3930 CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0));
3931 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible", !m_pDoc->RowHidden(2, 0));
3932 CPPUNIT_ASSERT_MESSAGE("row 4 should be visible", !m_pDoc->RowHidden(3, 0));
3933 CPPUNIT_ASSERT_MESSAGE("row 5 should be hidden", m_pDoc->RowHidden(4, 0));
3934 CPPUNIT_ASSERT_MESSAGE("row 6 should be hidden", m_pDoc->RowHidden(5, 0));
3936 m_pDoc->DeleteTab(0);
3939 CPPUNIT_TEST_FIXTURE(Test, testTdf98642)
3941 m_pDoc->InsertTab(0, "Sheet1");
3942 m_pDoc->SetString(0, 0, 0, "test");
3944 ScRangeData* pName1 = new ScRangeData( *m_pDoc, "name1", "$Sheet1.$A$1");
3945 ScRangeData* pName2 = new ScRangeData( *m_pDoc, "name2", "$Sheet1.$A$1");
3947 std::unique_ptr<ScRangeName> pGlobalRangeName(new ScRangeName());
3948 pGlobalRangeName->insert(pName1);
3949 pGlobalRangeName->insert(pName2);
3950 m_pDoc->SetRangeName(std::move(pGlobalRangeName));
3952 m_pDoc->SetString(1, 0, 0, "=name1");
3953 m_pDoc->SetString(1, 1, 0, "=name2");
3955 CPPUNIT_ASSERT_EQUAL(OUString("test"), m_pDoc->GetString(1, 0, 0));
3956 CPPUNIT_ASSERT_EQUAL(OUString("test"), m_pDoc->GetString(1, 1, 0));
3958 OUString aFormula = m_pDoc->GetFormula(1,0,0);
3959 CPPUNIT_ASSERT_EQUAL(OUString("=name1"), aFormula);
3960 aFormula = m_pDoc->GetFormula(1,1,0);
3962 // Without the fix in place, this test would have failed with
3963 // - Expected: =name2
3964 // - Actual : =name1
3965 CPPUNIT_ASSERT_EQUAL(OUString("=name2"), aFormula);
3967 m_pDoc->DeleteTab(0);
3970 CPPUNIT_TEST_FIXTURE(Test, testMergedCells)
3972 //test merge and unmerge
3973 //TODO: an undo/redo test for this would be a good idea
3974 m_pDoc->InsertTab(0, "Sheet1");
3975 m_pDoc->DoMerge(1, 1, 3, 3, 0, false);
3976 SCCOL nEndCol = 1;
3977 SCROW nEndRow = 1;
3978 m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0);
3979 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCCOL(3), nEndCol);
3980 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCROW(3), nEndRow);
3981 ScRange aRange(0,2,0,m_pDoc->MaxCol(),2,0);
3982 ScMarkData aMark(m_pDoc->GetSheetLimits());
3983 aMark.SetMarkArea(aRange);
3984 m_xDocShell->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS_BEFORE, true, true);
3985 m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0);
3986 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCCOL(3), nEndCol);
3987 CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCROW(4), nEndRow);
3988 m_pDoc->DeleteTab(0);
3991 CPPUNIT_TEST_FIXTURE(Test, testRenameTable)
3993 //test set rename table
3994 //TODO: set name1 and name2 and do an undo to check if name 1 is set now
3995 //TODO: also check if new name for table is same as another table
3997 m_pDoc->InsertTab(0, "Sheet1");
3998 m_pDoc->InsertTab(1, "Sheet2");
4000 //test case 1 , rename table2 to sheet 1, it should return error
4001 OUString nameToSet = "Sheet1";
4002 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4003 CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
4005 //test case 2 , simple rename to check name
4006 nameToSet = "test1";
4007 m_xDocShell->GetDocFunc().RenameTable(0,nameToSet,false,true);
4008 OUString nameJustSet;
4009 m_pDoc->GetName(0,nameJustSet);
4010 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4012 //test case 3 , rename again
4013 OUString anOldName;
4014 m_pDoc->GetName(0,anOldName);
4016 nameToSet = "test2";
4017 rDocFunc.RenameTable(0,nameToSet,false,true);
4018 m_pDoc->GetName(0,nameJustSet);
4019 CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
4021 //test case 4 , check if undo works
4022 SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShell.get(),0,anOldName,nameToSet);
4023 pUndo->Undo();
4024 m_pDoc->GetName(0,nameJustSet);
4025 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct name is not set after undo", nameJustSet, anOldName);
4027 pUndo->Redo();
4028 m_pDoc->GetName(0,nameJustSet);
4029 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after redo", nameJustSet, nameToSet);
4030 delete pUndo;
4032 m_pDoc->DeleteTab(0);
4033 m_pDoc->DeleteTab(1);
4036 CPPUNIT_TEST_FIXTURE(Test, testSetBackgroundColor)
4038 //test set background color
4039 //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
4041 m_pDoc->InsertTab(0, "Sheet1");
4042 Color aColor;
4044 //test yellow
4045 aColor=COL_YELLOW;
4046 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4047 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set",
4048 aColor, m_pDoc->GetTabBgColor(0));
4050 Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
4051 aColor = COL_BLUE;
4052 m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4053 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set the second time",
4054 aColor, m_pDoc->GetTabBgColor(0));
4056 //now check for undo
4057 SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShell.get(), 0, aOldTabBgColor, aColor);
4058 pUndo->Undo();
4059 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aOldTabBgColor, m_pDoc->GetTabBgColor(0));
4060 pUndo->Redo();
4061 CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aColor, m_pDoc->GetTabBgColor(0));
4062 delete pUndo;
4063 m_pDoc->DeleteTab(0);
4066 CPPUNIT_TEST_FIXTURE(Test, testUpdateReference)
4068 //test that formulas are correctly updated during sheet delete
4069 //TODO: add tests for relative references, updating of named ranges, ...
4070 m_pDoc->InsertTab(0, "Sheet1");
4071 m_pDoc->InsertTab(1, "Sheet2");
4072 m_pDoc->InsertTab(2, "Sheet3");
4073 m_pDoc->InsertTab(3, "Sheet4");
4075 m_pDoc->SetValue(0,0,2, 1);
4076 m_pDoc->SetValue(1,0,2, 2);
4077 m_pDoc->SetValue(1,1,3, 4);
4078 m_pDoc->SetString(2,0,2, "=A1+B1");
4079 m_pDoc->SetString(2,1,2, "=Sheet4.B2+A1");
4081 double aValue;
4082 aValue = m_pDoc->GetValue(2,0,2);
4083 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 3, aValue);
4084 aValue = m_pDoc->GetValue(2,1,2);
4085 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 5, aValue);
4087 //test deleting both sheets: one is not directly before the sheet, the other one is
4088 m_pDoc->DeleteTab(0);
4089 aValue = m_pDoc->GetValue(2,0,1);
4090 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 3, aValue);
4091 aValue = m_pDoc->GetValue(2,1,1);
4092 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 5, aValue);
4094 m_pDoc->DeleteTab(0);
4095 aValue = m_pDoc->GetValue(2,0,0);
4096 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 3, aValue);
4097 aValue = m_pDoc->GetValue(2,1,0);
4098 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 5, aValue);
4100 //test adding two sheets
4101 m_pDoc->InsertTab(0, "Sheet2");
4102 aValue = m_pDoc->GetValue(2,0,1);
4103 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 3, aValue);
4104 aValue = m_pDoc->GetValue(2,1,1);
4105 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 5, aValue);
4107 m_pDoc->InsertTab(0, "Sheet1");
4108 aValue = m_pDoc->GetValue(2,0,2);
4109 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 3, aValue);
4110 aValue = m_pDoc->GetValue(2,1,2);
4111 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 5, aValue);
4113 //test new DeleteTabs/InsertTabs methods
4114 m_pDoc->DeleteTabs(0, 2);
4115 aValue = m_pDoc->GetValue(2, 0, 0);
4116 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 3, aValue);
4117 aValue = m_pDoc->GetValue(2, 1, 0);
4118 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 5, aValue);
4120 std::vector<OUString> aSheets;
4121 aSheets.emplace_back("Sheet1");
4122 aSheets.emplace_back("Sheet2");
4123 m_pDoc->InsertTabs(0, aSheets, true);
4124 aValue = m_pDoc->GetValue(2, 0, 2);
4126 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 3, aValue);
4127 aValue = m_pDoc->GetValue(2, 1, 2);
4128 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 5, aValue);
4130 m_pDoc->DeleteTab(3);
4131 m_pDoc->DeleteTab(2);
4132 m_pDoc->DeleteTab(1);
4133 m_pDoc->DeleteTab(0);
4135 // Test positional update and invalidation of lookup cache for insertion
4136 // and deletion within entire column reference.
4137 m_pDoc->InsertTab(0, "Sheet1");
4138 m_pDoc->InsertTab(1, "Sheet2");
4139 m_pDoc->SetString(0,1,0, "s1");
4140 m_pDoc->SetString(0,0,1, "=MATCH(\"s1\";Sheet1.A:A;0)");
4141 aValue = m_pDoc->GetValue(0,0,1);
4142 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4143 m_pDoc->InsertRow(0,0,m_pDoc->MaxCol(),0,0,1); // insert 1 row before row 1 in Sheet1
4144 aValue = m_pDoc->GetValue(0,0,1);
4145 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 3, aValue);
4146 m_pDoc->DeleteRow(0,0,m_pDoc->MaxCol(),0,0,1); // delete row 1 in Sheet1
4147 aValue = m_pDoc->GetValue(0,0,1);
4148 ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected MATCH result", 2, aValue);
4149 m_pDoc->DeleteTab(1);
4150 m_pDoc->DeleteTab(0);
4153 CPPUNIT_TEST_FIXTURE(Test, testSearchCells)
4155 m_pDoc->InsertTab(0, "Test");
4157 m_pDoc->SetString(ScAddress(0,0,0), "A");
4158 m_pDoc->SetString(ScAddress(0,1,0), "B");
4159 m_pDoc->SetString(ScAddress(0,2,0), "A");
4160 // Leave A4 blank.
4161 m_pDoc->SetString(ScAddress(0,4,0), "A");
4162 m_pDoc->SetString(ScAddress(0,5,0), "B");
4163 m_pDoc->SetString(ScAddress(0,6,0), "C");
4165 SvxSearchItem aItem(SID_SEARCH_ITEM);
4166 aItem.SetSearchString("A");
4167 aItem.SetCommand(SvxSearchCmd::FIND_ALL);
4168 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4169 aMarkData.SelectOneTable(0);
4170 SCCOL nCol = 0;
4171 SCROW nRow = 0;
4172 SCTAB nTab = 0;
4173 ScRangeList aMatchedRanges;
4174 OUString aUndoStr;
4175 bool bMatchedRangesWereClamped = false;
4176 bool bSuccess = m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped);
4178 CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
4179 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 3 matching cells.", size_t(3), aMatchedRanges.size());
4180 ScAddress aHit(0,0,0);
4181 CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4182 aHit.SetRow(2);
4183 CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4184 aHit.SetRow(4);
4185 CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.Contains(aHit));
4187 m_pDoc->DeleteTab(0);
4190 CPPUNIT_TEST_FIXTURE(Test, testFormulaPosition)
4192 m_pDoc->InsertTab(0, "Test");
4194 ScAddress aPos(0,0,0); // A1
4195 m_pDoc->SetString(aPos, "=ROW()");
4196 aPos.IncRow(); // A2
4197 m_pDoc->SetString(aPos, "=ROW()");
4198 aPos.SetRow(3); // A4;
4199 m_pDoc->SetString(aPos, "=ROW()");
4202 SCROW aRows[] = { 0, 1, 3 };
4203 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4204 CPPUNIT_ASSERT(bRes);
4207 m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
4209 SCROW aRows[] = { 0, 6, 8 };
4210 bool bRes = checkFormulaPositions(*m_pDoc, aPos.Tab(), aPos.Col(), aRows, SAL_N_ELEMENTS(aRows));
4211 CPPUNIT_ASSERT(bRes);
4214 m_pDoc->DeleteTab(0);
4217 namespace {
4219 bool hasRange(const ScDocument* pDoc, const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange, const ScAddress& rPos)
4221 for (const ScTokenRef& p : rRefTokens)
4223 if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4224 continue;
4226 switch (p->GetType())
4228 case formula::svSingleRef:
4230 ScSingleRefData aData = *p->GetSingleRef();
4231 if (rRange.aStart != rRange.aEnd)
4232 break;
4234 ScAddress aThis = aData.toAbs(*pDoc, rPos);
4235 if (aThis == rRange.aStart)
4236 return true;
4238 break;
4239 case formula::svDoubleRef:
4241 ScComplexRefData aData = *p->GetDoubleRef();
4242 ScRange aThis = aData.toAbs(*pDoc, rPos);
4243 if (aThis == rRange)
4244 return true;
4246 break;
4247 default:
4251 return false;
4256 CPPUNIT_TEST_FIXTURE(Test, testJumpToPrecedentsDependents)
4259 * Test to make sure correct precedent / dependent cells are obtained when
4260 * preparing to jump to them.
4262 // Precedent is another cell that the cell references, while dependent is
4263 // another cell that references it.
4264 m_pDoc->InsertTab(0, "Test");
4266 m_pDoc->SetString(2, 0, 0, "=A1+A2+B3"); // C1
4267 m_pDoc->SetString(2, 1, 0, "=A1"); // C2
4268 m_pDoc->CalcAll();
4270 std::vector<ScTokenRef> aRefTokens;
4271 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4274 // C1's precedent should be A1:A2,B3.
4275 ScAddress aC1(2, 0, 0);
4276 ScRangeList aRange(aC1);
4277 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4278 CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4279 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
4280 CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4281 hasRange(m_pDoc, aRefTokens, ScRange(1, 2, 0), aC1));
4285 // C2's precedent should be A1 only.
4286 ScAddress aC2(2, 1, 0);
4287 ScRangeList aRange(aC2);
4288 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4289 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4290 static_cast<size_t>(1), aRefTokens.size());
4291 CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4292 hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0), aC2));
4296 // A1's dependent should be C1:C2.
4297 ScAddress aA1(0, 0, 0);
4298 ScRangeList aRange(aA1);
4299 rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4300 CPPUNIT_ASSERT_EQUAL_MESSAGE("C1:C2 should be the only dependent of A1.",
4301 std::vector<ScTokenRef>::size_type(1), aRefTokens.size());
4302 CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4303 hasRange(m_pDoc, aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
4306 m_pDoc->DeleteTab(0);
4309 CPPUNIT_TEST_FIXTURE(Test, testTdf149665)
4311 m_pDoc->InsertTab(0, "Test");
4313 m_pDoc->SetString(0, 0, 0, "''1");
4315 // Without the fix in place, this test would have failed with
4316 // - Expected: '1
4317 // - Actual : ''1
4318 CPPUNIT_ASSERT_EQUAL( OUString("'1"), m_pDoc->GetString( 0, 0, 0 ) );
4320 m_pDoc->DeleteTab(0);
4323 CPPUNIT_TEST_FIXTURE(Test, testTdf64001)
4325 m_pDoc->InsertTab(0, "test");
4327 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4328 aMarkData.SelectTable(0, true);
4330 m_pDoc->SetString( 0, 0, 0, "TRUE" );
4331 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 9, FILL_TO_BOTTOM, FILL_AUTO );
4333 for (SCCOL i = 0; i < 10; ++i)
4335 CPPUNIT_ASSERT_EQUAL( OUString("TRUE"), m_pDoc->GetString( 0, i, 0 ) );
4338 m_pDoc->SetString( 0, 10, 0, "FALSE" );
4340 m_pDoc->SetString( 1, 0, 0, "=COUNTIF(A1:A11;TRUE)" );
4342 // Without the fix in place, this test would have failed with
4343 // - Expected: 10
4344 // - Actual : 1
4345 CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue( 1, 0, 0 ) );
4347 m_pDoc->DeleteTab(0);
4350 CPPUNIT_TEST_FIXTURE(Test, testAutoFill)
4352 m_pDoc->InsertTab(0, "test");
4354 m_pDoc->SetValue(0,0,0,1);
4356 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4357 aMarkData.SelectTable(0, true);
4359 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5);
4360 for (SCROW i = 0; i< 6; ++i)
4361 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
4363 // check that hidden rows are not affected by autofill
4364 // set values for hidden rows
4365 m_pDoc->SetValue(0,1,0,10);
4366 m_pDoc->SetValue(0,2,0,10);
4368 m_pDoc->SetRowHidden(1, 2, 0, true);
4369 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 8);
4371 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
4372 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
4373 for (SCROW i = 3; i< 8; ++i)
4374 ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
4376 m_pDoc->Fill( 0, 0, 0, 8, nullptr, aMarkData, 5, FILL_TO_RIGHT );
4377 for (SCCOL i = 0; i < 5; ++i)
4379 for(SCROW j = 0; j < 8; ++j)
4381 if (j > 2)
4383 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
4385 else if (j == 0)
4387 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
4389 else // j == 1 || j == 2
4391 if(i == 0)
4392 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
4393 else
4394 ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
4399 // test auto fill user data lists
4400 m_pDoc->SetString( 0, 100, 0, "January" );
4401 m_pDoc->Fill( 0, 100, 0, 100, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4402 OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
4403 CPPUNIT_ASSERT_EQUAL( OUString("February"), aTestValue );
4404 aTestValue = m_pDoc->GetString( 0, 102, 0 );
4405 CPPUNIT_ASSERT_EQUAL( OUString("March"), aTestValue );
4407 // test that two same user data list entries will not result in incremental fill
4408 m_pDoc->SetString( 0, 101, 0, "January" );
4409 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
4410 for ( SCROW i = 102; i <= 103; ++i )
4412 aTestValue = m_pDoc->GetString( 0, i, 0 );
4413 CPPUNIT_ASSERT_EQUAL( OUString("January"), aTestValue );
4416 // Clear column A for a new test.
4417 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4418 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4420 // Fill A1:A6 with 1,2,3,4,5,6.
4421 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4422 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4423 ScRange aRange(0,0,0,0,5,0);
4424 aMarkData.SetMarkArea(aRange);
4425 rFunc.FillSeries(aRange, &aMarkData, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, MAXDOUBLE, 1.0, MAXDOUBLE, true);
4426 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4427 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4428 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4429 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4430 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4431 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4433 // Undo should clear the area except for the top cell.
4434 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4435 CPPUNIT_ASSERT(pUndoMgr);
4436 pUndoMgr->Undo();
4438 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4439 for (SCROW i = 1; i <= 5; ++i)
4440 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0,i,0)));
4442 // Redo should put the serial values back in.
4443 pUndoMgr->Redo();
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 // test that filling formulas vertically up does the right thing
4452 for(SCROW nRow = 0; nRow < 10; ++nRow)
4453 m_pDoc->SetValue(100, 100 + nRow, 0, 1);
4455 m_pDoc->SetString(100, 110, 0, "=A111");
4457 m_pDoc->Fill(100, 110, 100, 110, nullptr, aMarkData, 10, FILL_TO_TOP, FILL_AUTO);
4458 for(SCROW nRow = 110; nRow >= 100; --nRow)
4460 OUString aExpected = "=A" + OUString::number(nRow +1);
4461 OUString aFormula = m_pDoc->GetFormula(100, nRow, 0);
4462 CPPUNIT_ASSERT_EQUAL(aExpected, aFormula);
4465 // Clear column A for a new test.
4466 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4467 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4469 m_pDoc->SetString( 0, 100, 0, "2012-10-31" );
4470 m_pDoc->SetString( 0, 101, 0, "2012-10-31" );
4471 m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4473 // tdf#89754, Without the fix in place, this test would have failed with
4474 // - Expected: 2012-10-31
4475 // - Actual : 2012-11-01
4476 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 102, 0 ) );
4477 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 103, 0 ) );
4478 CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 104, 0 ) );
4480 // Clear column A for a new test.
4481 clearRange(m_pDoc, ScRange(0, 0, 0, 0, m_pDoc->MaxRow(), 0));
4482 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4484 m_pDoc->SetString(0, 100, 0, "2019-10-31");
4485 m_pDoc->SetString(0, 101, 0, "2019-11-30");
4486 m_pDoc->SetString(0, 102, 0, "2019-12-31");
4487 m_pDoc->Fill(0, 100, 0, 102, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO);
4489 // tdf#58745, Without the fix in place, this test would have failed with
4490 // - Expected: 2020-01-31
4491 // - Actual : 2019-01-11
4492 CPPUNIT_ASSERT_EQUAL(OUString("2020-01-31"), m_pDoc->GetString(0, 103, 0));
4493 CPPUNIT_ASSERT_EQUAL(OUString("2020-02-29"), m_pDoc->GetString(0, 104, 0));
4494 CPPUNIT_ASSERT_EQUAL(OUString("2020-03-31"), m_pDoc->GetString(0, 105, 0));
4496 // Clear column A for a new test.
4497 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4498 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4500 m_pDoc->SetString( 0, 50, 0, "1.0" );
4501 m_pDoc->SetString( 0, 51, 0, "1.1" );
4502 m_pDoc->SetString( 0, 52, 0, "1.2" );
4503 m_pDoc->SetString( 0, 53, 0, "1.3" );
4504 m_pDoc->Fill( 0, 50, 0, 53, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4506 CPPUNIT_ASSERT_EQUAL( OUString("1.4"), m_pDoc->GetString( 0, 54, 0 ) );
4507 CPPUNIT_ASSERT_EQUAL( OUString("1.5"), m_pDoc->GetString( 0, 55, 0 ) );
4508 CPPUNIT_ASSERT_EQUAL( OUString("1.6"), m_pDoc->GetString( 0, 56, 0 ) );
4510 m_pDoc->SetString( 0, 60, 0, "4.0" );
4511 m_pDoc->SetString( 0, 61, 0, "4.1" );
4512 m_pDoc->SetString( 0, 62, 0, "4.2" );
4513 m_pDoc->SetString( 0, 63, 0, "4.3" );
4514 m_pDoc->Fill( 0, 60, 0, 63, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4516 // tdf#37424: Without the fix in place, this test would have failed with
4517 // - Expected: 4.4
4518 // - Actual : 5
4519 CPPUNIT_ASSERT_EQUAL( OUString("4.4"), m_pDoc->GetString( 0, 64, 0 ) );
4520 CPPUNIT_ASSERT_EQUAL( OUString("4.5"), m_pDoc->GetString( 0, 65, 0 ) );
4521 CPPUNIT_ASSERT_EQUAL( OUString("4.6"), m_pDoc->GetString( 0, 66, 0 ) );
4523 // Clear column A for a new test.
4524 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4525 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4527 m_pDoc->SetString( 0, 70, 0, "001-001-001" );
4528 m_pDoc->Fill( 0, 70, 0, 70, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4530 // tdf#105268: Without the fix in place, this test would have failed with
4531 // - Expected: 001-001-002
4532 // - Actual : 001-001000
4533 CPPUNIT_ASSERT_EQUAL( OUString("001-001-002"), m_pDoc->GetString( 0, 71, 0 ) );
4534 CPPUNIT_ASSERT_EQUAL( OUString("001-001-003"), m_pDoc->GetString( 0, 72, 0 ) );
4535 CPPUNIT_ASSERT_EQUAL( OUString("001-001-004"), m_pDoc->GetString( 0, 73, 0 ) );
4537 // Clear column A for a new test.
4538 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4539 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4541 m_pDoc->SetString( 0, 80, 0, "1%" );
4542 m_pDoc->Fill( 0, 80, 0, 80, nullptr, aMarkData, 3, FILL_TO_BOTTOM, FILL_AUTO );
4544 // tdf#89998: Without the fix in place, this test would have failed with
4545 // - Expected: 2.00%
4546 // - Actual : 101.00%
4547 CPPUNIT_ASSERT_EQUAL( OUString("2.00%"), m_pDoc->GetString( 0, 81, 0 ) );
4548 CPPUNIT_ASSERT_EQUAL( OUString("3.00%"), m_pDoc->GetString( 0, 82, 0 ) );
4549 CPPUNIT_ASSERT_EQUAL( OUString("4.00%"), m_pDoc->GetString( 0, 83, 0 ) );
4551 // Clear column A for a new test.
4552 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4553 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4555 m_pDoc->SetString( 0, 0, 0, "1" );
4556 m_pDoc->SetString( 0, 1, 0, "1.1" );
4557 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 60, FILL_TO_BOTTOM, FILL_AUTO );
4559 // tdf#129606: Without the fix in place, this test would have failed with
4560 // - Expected: 6
4561 // - Actual : 6.00000000000001
4562 CPPUNIT_ASSERT_EQUAL( OUString("6"), m_pDoc->GetString( 0, 50, 0 ) );
4564 // Clear column A for a new test.
4565 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4566 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4568 m_pDoc->SetString( 0, 0, 0, "2022-10-01 00:00:00.000" );
4569 m_pDoc->SetString( 0, 1, 0, "2022-10-01 01:00:00.000" );
4570 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4572 // tdf#151460: Without the fix in place, this test would have failed with
4573 // - Expected: 2022-10-01 20:00:00.000
4574 // - Actual : 2022-10-01 19:59:59.999
4575 CPPUNIT_ASSERT_EQUAL( OUString("2022-10-01 20:00:00.000"), m_pDoc->GetString( 0, 20, 0 ) );
4577 // Clear column A for a new test.
4578 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4579 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4581 m_pDoc->SetString( 0, 0, 0, "1st" );
4583 m_pDoc->Fill( 0, 0, 0, 0, nullptr, aMarkData, 5, FILL_TO_BOTTOM, FILL_AUTO );
4585 CPPUNIT_ASSERT_EQUAL( OUString("1st"), m_pDoc->GetString( 0, 0, 0 ) );
4586 CPPUNIT_ASSERT_EQUAL( OUString("2nd"), m_pDoc->GetString( 0, 1, 0 ) );
4587 CPPUNIT_ASSERT_EQUAL( OUString("3rd"), m_pDoc->GetString( 0, 2, 0 ) );
4589 // Clear column A for a new test.
4590 clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
4591 m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
4593 m_pDoc->SetString( 0, 200, 0, "15:00" );
4594 m_pDoc->SetString( 0, 201, 0, "15:20" );
4595 m_pDoc->Fill( 0, 200, 0, 201, nullptr, aMarkData, 25, FILL_TO_BOTTOM, FILL_AUTO );
4597 CPPUNIT_ASSERT_EQUAL( OUString("03:00:00 PM"), m_pDoc->GetString( 0, 200, 0 ) );
4599 // tdf#153517: Without the fix in place, this test would have failed with
4600 // - Expected: 03:20:00 PM
4601 // - Actual : 03:19:59 PM
4602 CPPUNIT_ASSERT_EQUAL( OUString("03:20:00 PM"), m_pDoc->GetString( 0, 201, 0 ) );
4603 CPPUNIT_ASSERT_EQUAL( OUString("03:40:00 PM"), m_pDoc->GetString( 0, 202, 0 ) );
4604 CPPUNIT_ASSERT_EQUAL( OUString("04:00:00 PM"), m_pDoc->GetString( 0, 203, 0 ) );
4606 m_pDoc->DeleteTab(0);
4609 CPPUNIT_TEST_FIXTURE(Test, testAutoFillSimple)
4611 m_pDoc->InsertTab(0, "test");
4613 m_pDoc->SetValue(0, 0, 0, 1);
4614 m_pDoc->SetString(0, 1, 0, "=10");
4616 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
4617 aMarkData.SelectTable(0, true);
4619 m_pDoc->Fill( 0, 0, 0, 1, nullptr, aMarkData, 6, FILL_TO_BOTTOM, FILL_AUTO);
4621 for(SCROW nRow = 0; nRow < 8; ++nRow)
4623 if (nRow % 2 == 0)
4625 double nVal = m_pDoc->GetValue(0, nRow, 0);
4626 CPPUNIT_ASSERT_EQUAL((nRow+2)/2.0, nVal);
4628 else
4630 OString aMsg = "wrong value in row: " + OString::number(nRow);
4631 double nVal = m_pDoc->GetValue(0, nRow, 0);
4632 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, nVal);
4636 m_pDoc->DeleteTab(0);
4639 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosVertical)
4641 std::vector<std::vector<const char*>> aData = {
4642 { nullptr, "1", "1" },
4643 { "1", nullptr, "1" },
4644 { "1", "1", "1" },
4645 { nullptr, "1", "1" },
4646 { "1", "1", "1" },
4647 { "1", nullptr, "1" },
4648 { "1", "1", "1" },
4651 m_pDoc->InsertTab(0, "Test1");
4652 clearRange( m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
4653 ScAddress aPos(0,0,0);
4654 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4655 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4657 m_pDoc->SetRowHidden(4,4,0,true);
4658 bool bHidden = m_pDoc->RowHidden(4,0);
4659 CPPUNIT_ASSERT(bHidden);
4661 SCCOL nCol = 0;
4662 SCROW nRow = 0;
4663 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4665 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4666 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4668 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4670 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
4671 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4673 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4675 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
4676 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4678 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4680 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
4681 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4683 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4685 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), nRow);
4686 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
4688 nCol = 1;
4689 nRow = 2;
4691 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
4693 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
4694 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), 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>(1), nCol);
4701 nCol = 2;
4702 nRow = 6;
4703 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
4704 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4705 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4707 m_pDoc->DeleteTab(0);
4710 CPPUNIT_TEST_FIXTURE(Test, testFindAreaPosColRight)
4712 std::vector<std::vector<const char*>> aData = {
4713 { "", "1", "1", "", "1", "1", "1" },
4714 { "", "", "1", "1", "1", "", "1" },
4717 m_pDoc->InsertTab(0, "test1");
4718 clearRange( m_pDoc, ScRange(0, 0, 0, 7, aData.size(), 0));
4719 ScAddress aPos(0,0,0);
4720 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
4721 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
4723 m_pDoc->SetColHidden(4,4,0,true);
4724 bool bHidden = m_pDoc->ColHidden(4,0);
4725 CPPUNIT_ASSERT(bHidden);
4727 SCCOL nCol = 0;
4728 SCROW nRow = 0;
4729 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4731 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4732 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
4734 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4736 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4737 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
4739 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4741 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4742 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
4744 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4746 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4747 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4749 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4751 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
4752 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), nCol);
4754 nCol = 2;
4755 nRow = 1;
4757 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4759 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4760 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
4762 m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
4764 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
4765 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
4767 m_pDoc->DeleteTab(0);
4770 CPPUNIT_TEST_FIXTURE(Test, testShiftCells)
4772 m_pDoc->InsertTab(0, "foo");
4774 // We need a drawing layer in order to create caption objects.
4775 m_pDoc->InitDrawLayer(m_xDocShell.get());
4777 OUString aTestVal("Some Text");
4779 // Text into cell E5.
4780 m_pDoc->SetString(4, 3, 0, aTestVal);
4782 // put a Note in cell E5
4783 ScAddress rAddr(4, 3, 0);
4784 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
4785 pNote->SetText(rAddr, "Hello");
4787 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4789 // Insert cell at D5. This should shift the string cell to right.
4790 m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
4791 OUString aStr = m_pDoc->GetString(5, 3, 0);
4792 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4793 CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(3, 4, 3, 4, 0));
4795 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
4796 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
4798 // Delete cell D5, to shift the text cell back into D5.
4799 m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
4800 aStr = m_pDoc->GetString(4, 3, 0);
4801 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
4802 CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(4, 4, 4, 4, 0));
4804 CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
4805 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
4807 m_pDoc->DeleteTab(0);
4810 CPPUNIT_TEST_FIXTURE(Test, testNoteDefaultStyle)
4812 m_pDoc->InsertTab(0, "PostIts");
4814 // We need a drawing layer in order to create caption objects.
4815 m_pDoc->InitDrawLayer(m_xDocShell.get());
4817 auto pNote = m_pDoc->GetOrCreateNote({0, 0, 0});
4818 auto pCaption = pNote->GetCaption();
4820 CPPUNIT_ASSERT(pCaption);
4821 CPPUNIT_ASSERT_EQUAL(ScResId(STR_STYLENAME_NOTE), pCaption->GetStyleSheet()->GetName());
4823 m_pDoc->DeleteTab(0);
4826 CPPUNIT_TEST_FIXTURE(Test, testNoteBasic)
4828 m_pDoc->InsertTab(0, "PostIts");
4830 // We need a drawing layer in order to create caption objects.
4831 m_pDoc->InitDrawLayer(m_xDocShell.get());
4833 CPPUNIT_ASSERT(!m_pDoc->HasNotes());
4835 // Check for note's presence in all tables before inserting any notes.
4836 for (SCTAB i = 0; i <= MAXTAB; ++i)
4838 bool bHasNotes = m_pDoc->HasTabNotes(i);
4839 CPPUNIT_ASSERT(!bHasNotes);
4842 ScAddress aAddr(2, 2, 0); // cell C3
4843 ScPostIt *pNote = m_pDoc->GetOrCreateNote(aAddr);
4845 pNote->SetText(aAddr, "Hello world");
4846 pNote->SetAuthor("Jim Bob");
4848 ScPostIt *pGetNote = m_pDoc->GetNote(aAddr);
4849 CPPUNIT_ASSERT_EQUAL_MESSAGE("note should be itself", pNote, pGetNote);
4851 // Insert one row at row 1.
4852 bool bInsertRow = m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
4853 CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
4855 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4856 aAddr.IncRow(); // cell C4
4857 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4859 // Insert column at column A.
4860 bool bInsertCol = m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
4861 CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
4863 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4864 aAddr.IncCol(); // cell D4
4865 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4867 // Insert a new sheet to shift the current sheet to the right.
4868 m_pDoc->InsertTab(0, "Table2");
4869 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
4870 aAddr.IncTab(); // Move to the next sheet.
4871 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4873 m_pDoc->DeleteTab(0);
4874 aAddr.IncTab(-1);
4875 CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
4877 // Insert cell at C4. This should NOT shift the note position.
4878 bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
4879 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
4880 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4882 // Delete cell at C4. Again, this should NOT shift the note position.
4883 m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
4884 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
4886 // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
4887 m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
4888 aAddr.IncRow(-1); // cell D3
4889 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D4 should have shifted up to D3.", pNote, m_pDoc->GetNote(aAddr));
4891 // Delete column C. This should shift the note one cell left.
4892 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 2, 1);
4893 aAddr.IncCol(-1); // cell C3
4894 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D3 should have shifted left to C3.", pNote, m_pDoc->GetNote(aAddr));
4896 // Insert a text where the note is.
4897 m_pDoc->SetString(aAddr, "Note is here.");
4899 // Delete row 1. This should shift the note from C3 to C2.
4900 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 1);
4901 aAddr.IncRow(-1); // C2
4902 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at C3 should have shifted up to C2.", pNote, m_pDoc->GetNote(aAddr));
4904 m_pDoc->DeleteTab(0);
4907 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteRow)
4909 m_pDoc->InsertTab(0, "Sheet1");
4911 // We need a drawing layer in order to create caption objects.
4912 m_pDoc->InitDrawLayer(m_xDocShell.get());
4914 ScAddress aPos(1, 1, 0);
4915 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
4916 pNote->SetText(aPos, "Hello");
4917 pNote->SetAuthor("Jim Bob");
4919 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
4921 // test with IsBlockEmpty
4922 CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsEmptyData(0, 0, 100, 100, 0));
4923 CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
4925 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 1, 1);
4927 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
4929 // Set values and notes into B3:B4.
4930 aPos = ScAddress(1,2,0); // B3
4931 m_pDoc->SetString(aPos, "First");
4932 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "First Note", false, false);
4934 aPos = ScAddress(1,3,0); // B4
4935 m_pDoc->SetString(aPos, "Second");
4936 ScNoteUtil::CreateNoteFromString(*m_pDoc, aPos, "Second Note", false, false);
4938 // Delete row 2.
4939 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
4940 ScMarkData aMark(m_pDoc->GetSheetLimits());
4941 aMark.SelectOneTable(0);
4942 rDocFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
4944 // Check to make sure the notes have shifted upward.
4945 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4946 CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
4947 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
4948 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4949 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4950 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4951 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4952 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4954 // Undo.
4956 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4957 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
4958 m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
4960 pUndoMgr->Undo();
4961 pNote = m_pDoc->GetNote(ScAddress(1,1,0));
4962 CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
4963 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4964 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4965 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), pNote->GetText());
4966 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4967 CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
4968 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4970 // Delete row 3.
4971 rDocFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4973 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4974 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4975 CPPUNIT_ASSERT_EQUAL(OUString("Second Note"), pNote->GetText());
4976 pNote = m_pDoc->GetNote(ScAddress(1,3,0));
4977 CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
4979 // Undo and check the result.
4980 pUndoMgr->Undo();
4981 pNote = m_pDoc->GetNote(ScAddress(1,2,0));
4982 CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
4983 CPPUNIT_ASSERT_EQUAL(OUString("First Note"), 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(OUString("Second Note"), pNote->GetText());
4988 m_pDoc->DeleteTab(0);
4991 CPPUNIT_TEST_FIXTURE(Test, testNoteDeleteCol)
4993 m_pDoc->InsertTab(0, "Sheet1");
4995 // We need a drawing layer in order to create caption objects.
4996 m_pDoc->InitDrawLayer(m_xDocShell.get());
4998 ScAddress rAddr(1, 1, 0);
4999 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5000 pNote->SetText(rAddr, "Hello");
5001 pNote->SetAuthor("Jim Bob");
5003 CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
5005 m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 1, 1);
5007 CPPUNIT_ASSERT_MESSAGE("there should be no more note", !m_pDoc->HasNote(1, 1, 0));
5009 m_pDoc->DeleteTab(0);
5012 CPPUNIT_TEST_FIXTURE(Test, testNoteLifeCycle)
5014 m_pDoc->InsertTab(0, "Test");
5016 // We need a drawing layer in order to create caption objects.
5017 m_pDoc->InitDrawLayer(m_xDocShell.get());
5019 ScAddress aPos(1,1,0);
5020 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5021 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
5023 pNote->SetText(aPos, "New note");
5024 std::unique_ptr<ScPostIt> pNote2 = m_pDoc->ReleaseNote(aPos);
5025 CPPUNIT_ASSERT_EQUAL_MESSAGE("This note instance is expected to be identical to the original.", pNote, pNote2.get());
5026 CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
5028 // Modify the internal state of the note instance to make sure it's really
5029 // been released.
5030 pNote->SetText(aPos, "New content");
5032 // Re-insert the note back to the same place.
5033 m_pDoc->SetNote(aPos, std::move(pNote2));
5034 SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
5035 CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
5036 CPPUNIT_ASSERT_EQUAL_MESSAGE("This caption should belong to the drawing layer of the document.",
5037 m_pDoc->GetDrawLayer(), static_cast<ScDrawLayer*>(&pCaption->getSdrModelFromSdrObject()));
5039 // Copy B2 with note to a clipboard.
5041 ScClipParam aClipParam(aPos, false);
5042 ScDocument aClipDoc(SCDOCMODE_CLIP);
5043 ScMarkData aMarkData(m_pDoc->GetSheetLimits());
5044 aMarkData.SelectOneTable(0);
5045 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMarkData, false, true);
5047 ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
5048 CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
5049 CPPUNIT_ASSERT_EQUAL_MESSAGE("Note on the clipboard should share the same caption object from the original.",
5050 pCaption, pClipNote->GetCaption());
5053 // Move B2 to B3 with note, which creates an ScUndoDragDrop, and Undo.
5055 ScAddress aOrigPos(aPos);
5056 ScAddress aMovePos(1,2,0);
5057 ScPostIt* pOrigNote = m_pDoc->GetNote(aOrigPos);
5058 const SdrCaptionObj* pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5059 bool const bCut = true; // like Drag&Drop
5060 bool bRecord = true; // record Undo
5061 bool const bPaint = false; // don't care about
5062 bool bApi = true; // API to prevent dialogs
5063 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
5064 bool bMoveDone = rDocFunc.MoveBlock(ScRange(aOrigPos, aOrigPos), aMovePos, bCut, bRecord, bPaint, bApi);
5065 CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
5067 // Verify the note move.
5068 ScPostIt* pGoneNote = m_pDoc->GetNote(aOrigPos);
5069 CPPUNIT_ASSERT_MESSAGE("Failed to move the note from source.", !pGoneNote);
5070 ScPostIt* pMoveNote = m_pDoc->GetNote(aMovePos);
5071 CPPUNIT_ASSERT_MESSAGE("Failed to move the note to destination.", pMoveNote);
5073 // The caption object should not be identical, it was newly created upon
5074 // Drop from clipboard.
5075 // pOrigCaption is a dangling pointer.
5076 const SdrCaptionObj* pMoveCaption = pMoveNote->GetOrCreateCaption(aMovePos);
5077 CPPUNIT_ASSERT_MESSAGE("Captions identical after move.", pOrigCaption != pMoveCaption);
5079 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5080 CPPUNIT_ASSERT(pUndoMgr);
5081 pUndoMgr->Undo(); // this should not crash ... tdf#92995
5083 // Verify the note move Undo.
5084 pMoveNote = m_pDoc->GetNote(aMovePos);
5085 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move from destination.", !pMoveNote);
5086 pOrigNote = m_pDoc->GetNote(aOrigPos);
5087 CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move to source.", pOrigNote);
5089 // The caption object still should not be identical.
5090 // pMoveCaption is a dangling pointer.
5091 pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
5092 CPPUNIT_ASSERT_MESSAGE("Captions identical after move undo.", pOrigCaption != pMoveCaption);
5095 // Create a note at B4, merge B4 and B5 with ScUndoMerge, and Undo.
5097 ScAddress aPosB4(1,3,0);
5098 ScPostIt* pNoteB4 = m_pDoc->GetOrCreateNote(aPosB4);
5099 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B4.", pNoteB4);
5100 const SdrCaptionObj* pCaptionB4 = pNoteB4->GetOrCreateCaption(aPosB4);
5101 ScCellMergeOption aCellMergeOption(1,3,2,3);
5102 rDocFunc.MergeCells( aCellMergeOption, true /*bContents*/, bRecord, bApi, false /*bEmptyMergedCells*/ );
5104 SfxUndoManager* pMergeUndoManager = m_pDoc->GetUndoManager();
5105 CPPUNIT_ASSERT(pMergeUndoManager);
5106 pMergeUndoManager->Undo(); // this should not crash ... tdf#105667
5108 // Undo contained the original caption object pointer which was still alive
5109 // at B4 after the merge and not cloned nor recreated during Undo.
5110 ScPostIt* pUndoNoteB4 = m_pDoc->GetNote(aPosB4);
5111 CPPUNIT_ASSERT_MESSAGE("No cell comment at B4 after Undo.", pUndoNoteB4);
5112 const SdrCaptionObj* pUndoCaptionB4 = pUndoNoteB4->GetCaption();
5113 CPPUNIT_ASSERT_EQUAL_MESSAGE("Captions not identical after Merge Undo.", pCaptionB4, pUndoCaptionB4);
5116 // In a second document copy a note from B5 to clipboard, close the
5117 // document and then paste the note into this document.
5119 ScDocShellRef xDocSh2;
5120 getNewDocShell(xDocSh2);
5121 ScDocument* pDoc2 = &xDocSh2->GetDocument();
5122 pDoc2->InsertTab(0, "OtherSheet1");
5123 pDoc2->InitDrawLayer(xDocSh2.get());
5125 ScAddress aPosB5(1,4,0);
5126 ScPostIt* pOtherNoteB5 = pDoc2->GetOrCreateNote(aPosB5);
5127 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B5.", pOtherNoteB5);
5128 const SdrCaptionObj* pOtherCaptionB5 = pOtherNoteB5->GetOrCreateCaption(aPosB5);
5129 CPPUNIT_ASSERT_MESSAGE("No caption at B5.", pOtherCaptionB5);
5131 ScDocument aClipDoc2(SCDOCMODE_CLIP);
5132 copyToClip( pDoc2, aPosB5, &aClipDoc2);
5134 // There's no ScTransferObject involved in the "fake" clipboard copy
5135 // and ScDocument dtor asking IsClipboardSource() gets no, so emulate
5136 // the part that normally is responsible for forgetting the caption
5137 // objects.
5138 aClipDoc2.ClosingClipboardSource();
5140 pDoc2->DeleteTab(0);
5141 xDocSh2->DoClose();
5142 xDocSh2.clear();
5144 pasteFromClip( m_pDoc, aPosB5, &aClipDoc2); // should not crash... tdf#104967
5145 ScPostIt* pNoteB5 = m_pDoc->GetNote(aPosB5);
5146 CPPUNIT_ASSERT_MESSAGE("Failed to paste cell comment at B5.", pNoteB5);
5147 const SdrCaptionObj* pCaptionB5 = pNoteB5->GetOrCreateCaption(aPosB5);
5148 CPPUNIT_ASSERT_MESSAGE("No caption at pasted B5.", pCaptionB5);
5149 // Do not test if pCaptionB5 != pOtherCaptionB5 because since pDoc2
5150 // has been closed and the caption been deleted objects *may* be
5151 // allocated at the very same memory location.
5154 m_pDoc->DeleteTab(0);
5157 CPPUNIT_TEST_FIXTURE(Test, testNoteCopyPaste)
5159 m_pDoc->InsertTab(0, "Test");
5161 // We need a drawing layer in order to create caption objects.
5162 m_pDoc->InitDrawLayer(m_xDocShell.get());
5164 // Insert in B2 a text and cell comment.
5165 ScAddress aPos(1,1,0);
5166 m_pDoc->SetString(aPos, "Text");
5167 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
5168 CPPUNIT_ASSERT(pNote);
5169 pNote->SetText(aPos, "Note1");
5171 // Insert in B4 a number and cell comment.
5172 aPos.SetRow(3);
5173 m_pDoc->SetValue(aPos, 1.1);
5174 pNote = m_pDoc->GetOrCreateNote(aPos);
5175 CPPUNIT_ASSERT(pNote);
5176 pNote->SetText(aPos, "Note2");
5178 // Copy B2:B4 to clipboard.
5179 ScMarkData aMark(m_pDoc->GetSheetLimits());
5180 aMark.SelectOneTable(0);
5181 ScRange aCopyRange(1,1,0,1,3,0);
5182 ScDocument aClipDoc(SCDOCMODE_CLIP);
5183 aClipDoc.ResetClip(m_pDoc, &aMark);
5184 ScClipParam aClipParam(aCopyRange, false);
5185 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
5187 // Make sure the notes are in the clipboard.
5188 pNote = aClipDoc.GetNote(ScAddress(1,1,0));
5189 CPPUNIT_ASSERT(pNote);
5190 CPPUNIT_ASSERT_EQUAL(OUString("Note1"), pNote->GetText());
5192 pNote = aClipDoc.GetNote(ScAddress(1,3,0));
5193 CPPUNIT_ASSERT(pNote);
5194 CPPUNIT_ASSERT_EQUAL(OUString("Note2"), pNote->GetText());
5196 // Paste to B6:B8 but only cell notes.
5197 ScRange aDestRange(1,5,0,1,7,0);
5198 m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::NOTE, nullptr, &aClipDoc);
5200 // Make sure the notes are there.
5201 pNote = m_pDoc->GetNote(ScAddress(1,5,0));
5202 CPPUNIT_ASSERT(pNote);
5203 CPPUNIT_ASSERT_EQUAL(OUString("Note1"), pNote->GetText());
5205 pNote = m_pDoc->GetNote(ScAddress(1,7,0));
5206 CPPUNIT_ASSERT(pNote);
5207 CPPUNIT_ASSERT_EQUAL(OUString("Note2"), pNote->GetText());
5209 // Test that GetNotesInRange includes the end of its range
5210 // and so can find the note
5211 std::vector<sc::NoteEntry> aNotes;
5212 m_pDoc->GetNotesInRange(ScRange(1,7,0), aNotes);
5213 CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
5215 m_pDoc->DeleteTab(0);
5218 // tdf#112454
5219 CPPUNIT_TEST_FIXTURE(Test, testNoteContainsNotesInRange)
5221 m_pDoc->InsertTab(0, "PostIts");
5223 // We need a drawing layer in order to create caption objects.
5224 m_pDoc->InitDrawLayer(m_xDocShell.get());
5226 ScAddress aAddr(2, 2, 0); // cell C3
5228 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in a document that doesn't have any.",
5229 !m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5231 m_pDoc->GetOrCreateNote(aAddr);
5233 CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in range that doesn't have any.",
5234 !m_pDoc->ContainsNotesInRange(ScRange(ScAddress(0, 0, 0), ScAddress(0, 1, 0))));
5235 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies on border of range.",
5236 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
5237 CPPUNIT_ASSERT_MESSAGE("Note not detected that lies in inner area of range.",
5238 m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), ScAddress(3, 3, 0)))));
5241 CPPUNIT_TEST_FIXTURE(Test, testAreasWithNotes)
5243 m_pDoc->InsertTab(0, "Sheet1");
5245 // We need a drawing layer in order to create caption objects.
5246 m_pDoc->InitDrawLayer(m_xDocShell.get());
5248 ScAddress rAddr(1, 5, 0);
5249 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
5250 pNote->SetText(rAddr, "Hello");
5251 pNote->SetAuthor("Jim Bob");
5252 ScAddress rAddrMin(2, 2, 0);
5253 ScPostIt* pNoteMin = m_pDoc->GetOrCreateNote(rAddrMin);
5254 pNoteMin->SetText(rAddrMin, "Hello");
5256 SCCOL col;
5257 SCROW row;
5258 bool dataFound;
5260 // only cell notes (empty content)
5262 dataFound = m_pDoc->GetDataStart(0,col,row);
5264 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5265 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col for notes", static_cast<SCCOL>(1), col);
5266 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row for notes", static_cast<SCROW>(2), row);
5268 dataFound = m_pDoc->GetCellArea(0,col,row);
5270 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5271 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col for notes", static_cast<SCCOL>(2), col);
5272 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row for notes", static_cast<SCROW>(5), row);
5274 bool bNotes = true;
5275 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5277 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5278 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col for notes", static_cast<SCCOL>(2), col);
5279 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row for notes", static_cast<SCROW>(5), row);
5281 bNotes = false;
5282 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5283 CPPUNIT_ASSERT_MESSAGE("No PrintArea should be found", !dataFound);
5285 bNotes = true;
5286 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5287 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5288 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(5), row);
5290 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5291 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5292 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(2), row);
5294 dataFound = m_pDoc->GetPrintAreaVer(0,20,21,row, bNotes); // cols 20 & 21
5295 CPPUNIT_ASSERT_MESSAGE("PrintAreaVer found", !dataFound);
5296 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row for notes", static_cast<SCROW>(0), row);
5298 bNotes = false;
5299 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // col 0 & 1
5300 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer should be found", !dataFound);
5302 // now add cells with value, check that notes are taken into account in good cases
5304 m_pDoc->SetString(0, 3, 0, "Some Text");
5305 m_pDoc->SetString(3, 3, 0, "Some Text");
5307 dataFound = m_pDoc->GetDataStart(0,col,row);
5309 CPPUNIT_ASSERT_MESSAGE("No DataStart found", dataFound);
5310 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong col", static_cast<SCCOL>(0), col);
5311 CPPUNIT_ASSERT_EQUAL_MESSAGE("DataStart wrong row", static_cast<SCROW>(2), row);
5313 dataFound = m_pDoc->GetCellArea(0,col,row);
5315 CPPUNIT_ASSERT_MESSAGE("No CellArea found", dataFound);
5316 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong col", static_cast<SCCOL>(3), col);
5317 CPPUNIT_ASSERT_EQUAL_MESSAGE("CellArea wrong row", static_cast<SCROW>(5), row);
5319 bNotes = true;
5320 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5322 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5323 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5324 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(5), row);
5326 bNotes = false;
5327 dataFound = m_pDoc->GetPrintArea(0,col,row, bNotes);
5328 CPPUNIT_ASSERT_MESSAGE("No PrintArea found", dataFound);
5329 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong col", static_cast<SCCOL>(3), col);
5330 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintArea wrong row", static_cast<SCROW>(3), row);
5332 bNotes = true;
5333 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5334 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5335 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(5), row);
5337 dataFound = m_pDoc->GetPrintAreaVer(0,2,3,row, bNotes); // cols 2 & 3
5338 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5339 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5341 bNotes = false;
5342 dataFound = m_pDoc->GetPrintAreaVer(0,0,1,row, bNotes); // cols 0 & 1
5343 CPPUNIT_ASSERT_MESSAGE("No PrintAreaVer found", dataFound);
5344 CPPUNIT_ASSERT_EQUAL_MESSAGE("PrintAreaVer wrong row", static_cast<SCROW>(3), row);
5346 m_pDoc->DeleteTab(0);
5349 CPPUNIT_TEST_FIXTURE(Test, testAnchoredRotatedShape)
5351 m_pDoc->InsertTab(0, "TestTab");
5352 SCROW nRow1, nRow2;
5353 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5354 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
5355 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
5356 CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
5358 m_pDoc->InitDrawLayer();
5359 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5360 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
5361 SdrPage* pPage = pDrawLayer->GetPage(0);
5362 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
5363 m_pDoc->SetRowHeightRange(0, m_pDoc->MaxRow(), 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5364 constexpr tools::Long TOLERANCE = 30; //30 hmm
5365 for ( SCCOL nCol = 0; nCol < m_pDoc->MaxCol(); ++nCol )
5366 m_pDoc->SetColWidth(nCol, 0, o3tl::toTwips(1000, o3tl::Length::mm100));
5368 //Add a rect
5369 tools::Rectangle aRect( 4000, 5000, 10000, 7000 );
5371 tools::Rectangle aRotRect( 6000, 3000, 8000, 9000 );
5372 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aRect);
5373 pPage->InsertObject(pObj.get());
5374 Point aRef1(pObj->GetSnapRect().Center());
5375 Degree100 nAngle = 9000_deg100; //90 deg.
5376 double nSin = 1.0; // sin(90 deg)
5377 double nCos = 0.0; // cos(90 deg)
5378 pObj->Rotate(aRef1,nAngle,nSin,nCos);
5380 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, true);
5382 tools::Rectangle aSnap = pObj->GetSnapRect();
5383 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5384 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5385 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Left(), aSnap.Left(), TOLERANCE );
5386 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.Top(), aSnap.Top(), TOLERANCE );
5388 ScDrawObjData aAnchor;
5389 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj.get() );
5390 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5392 aAnchor.maStart = pData->maStart;
5393 aAnchor.maEnd = pData->maEnd;
5395 m_pDoc->SetDrawPageSize(0);
5397 // increase row 5 by 2000 hmm
5398 m_pDoc->SetRowHeight(5, 0, o3tl::toTwips(3000, o3tl::Length::mm100));
5399 // increase col 6 by 1000 hmm
5400 m_pDoc->SetColWidth(6, 0, o3tl::toTwips(2000, o3tl::Length::mm100));
5402 aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
5403 aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
5405 m_pDoc->SetDrawPageSize(0);
5407 aSnap = pObj->GetSnapRect();
5409 // ensure that width and height have been adjusted accordingly
5410 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
5411 CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
5413 // ensure that anchor start and end addresses haven't changed
5414 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
5415 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
5416 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
5417 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
5419 m_pDoc->DeleteTab(0);
5422 CPPUNIT_TEST_FIXTURE(Test, testCellTextWidth)
5424 m_pDoc->InsertTab(0, "Test");
5426 ScAddress aTopCell(0, 0, 0);
5428 // Sheet is empty.
5429 std::unique_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5430 CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
5432 // Sheet only has one cell.
5433 m_pDoc->SetString(0, 0, 0, "Only one cell");
5434 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5435 CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
5436 CPPUNIT_ASSERT_EQUAL(SCROW(0), pIter->getPos());
5438 // Setting a text width here should commit it to the column.
5439 sal_uInt16 nTestVal = 432;
5440 pIter->setValue(nTestVal);
5441 CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
5443 // Set values to row 2 through 6.
5444 for (SCROW i = 2; i <= 6; ++i)
5445 m_pDoc->SetString(0, i, 0, "foo");
5447 // Set values to row 10 through 18.
5448 for (SCROW i = 10; i <= 18; ++i)
5449 m_pDoc->SetString(0, i, 0, "foo");
5452 // Full range.
5453 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5454 SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
5455 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5457 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5458 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5460 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5464 // Specify start and end rows (6 - 16)
5465 ScAddress aStart = aTopCell;
5466 aStart.SetRow(6);
5467 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
5468 SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
5469 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5471 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5472 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5474 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5477 // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
5478 clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
5481 // Full range again.
5482 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5483 SCROW aRows[] = { 0, 2, 18 };
5484 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5486 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5487 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5489 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5492 // Delete row 2 which shifts all cells below row 2 upward. After this, we
5493 // should only have cells at rows 0 and 17.
5494 m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), MAXTAB, 2, 1);
5496 // Full range again.
5497 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
5498 SCROW aRows[] = { 0, 17 };
5499 for (size_t i = 0; i < SAL_N_ELEMENTS(aRows); ++i, pIter->next())
5501 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
5502 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
5504 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
5507 m_pDoc->DeleteTab(0);
5510 static bool checkEditTextIterator(sc::EditTextIterator& rIter, const char** pChecks)
5512 const EditTextObject* pText = rIter.first();
5513 const char* p = *pChecks;
5515 for (int i = 0; i < 100; ++i) // cap it to 100 loops.
5517 if (!pText)
5518 // No more edit cells. The check string array should end too.
5519 return p == nullptr;
5521 if (!p)
5522 // More edit cell, but no more check string. Bad.
5523 return false;
5525 if (pText->GetParagraphCount() != 1)
5526 // For this test, we don't handle multi-paragraph text.
5527 return false;
5529 if (pText->GetText(0) != OUString::createFromAscii(p))
5530 // Text differs from what's expected.
5531 return false;
5533 pText = rIter.next();
5534 ++pChecks;
5535 p = *pChecks;
5538 return false;
5541 CPPUNIT_TEST_FIXTURE(Test, testEditTextIterator)
5543 m_pDoc->InsertTab(0, "Test");
5546 // First, try with an empty sheet.
5547 sc::EditTextIterator aIter(*m_pDoc,0);
5548 const char* pChecks[] = { nullptr };
5549 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5552 ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
5555 // Only set one edit cell.
5556 rEditEngine.SetTextCurrentDefaults("A2");
5557 m_pDoc->SetEditText(ScAddress(0,1,0), rEditEngine.CreateTextObject());
5558 sc::EditTextIterator aIter(*m_pDoc,0);
5559 const char* pChecks[] = { "A2", nullptr };
5560 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5564 // Add a series of edit cells.
5565 rEditEngine.SetTextCurrentDefaults("A5");
5566 m_pDoc->SetEditText(ScAddress(0,4,0), rEditEngine.CreateTextObject());
5567 rEditEngine.SetTextCurrentDefaults("A6");
5568 m_pDoc->SetEditText(ScAddress(0,5,0), rEditEngine.CreateTextObject());
5569 rEditEngine.SetTextCurrentDefaults("A7");
5570 m_pDoc->SetEditText(ScAddress(0,6,0), rEditEngine.CreateTextObject());
5571 sc::EditTextIterator aIter(*m_pDoc,0);
5572 const char* pChecks[] = { "A2", "A5", "A6", "A7", nullptr };
5573 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5577 // Add more edit cells to column C. Skip column B.
5578 rEditEngine.SetTextCurrentDefaults("C1");
5579 m_pDoc->SetEditText(ScAddress(2,0,0), rEditEngine.CreateTextObject());
5580 rEditEngine.SetTextCurrentDefaults("C3");
5581 m_pDoc->SetEditText(ScAddress(2,2,0), rEditEngine.CreateTextObject());
5582 rEditEngine.SetTextCurrentDefaults("C4");
5583 m_pDoc->SetEditText(ScAddress(2,3,0), rEditEngine.CreateTextObject());
5584 sc::EditTextIterator aIter(*m_pDoc,0);
5585 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5586 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5590 // Add some numeric, string and formula cells. This shouldn't affect the outcome.
5591 m_pDoc->SetString(ScAddress(0,99,0), "=ROW()");
5592 m_pDoc->SetValue(ScAddress(1,3,0), 1.2);
5593 m_pDoc->SetString(ScAddress(2,4,0), "Simple string");
5594 sc::EditTextIterator aIter(*m_pDoc,0);
5595 const char* pChecks[] = { "A2", "A5", "A6", "A7", "C1", "C3", "C4", nullptr };
5596 CPPUNIT_ASSERT_MESSAGE("Wrong iterator behavior.", checkEditTextIterator(aIter, pChecks));
5599 m_pDoc->DeleteTab(0);
5602 CPPUNIT_TEST_FIXTURE(Test, testImportStream)
5604 sc::AutoCalcSwitch aAC(*m_pDoc, true); // turn on auto calc.
5605 sc::UndoSwitch aUndo(*m_pDoc, true); // enable undo.
5607 m_pDoc->InsertTab(0, "Test");
5609 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(A1:C1)"); // A2
5611 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5613 // CSV import options.
5614 ScAsciiOptions aOpt;
5615 aOpt.SetFieldSeps(",");
5617 // Import values to A1:C1.
5618 ScImportExport aObj(*m_pDoc, ScAddress(0,0,0));
5619 aObj.SetImportBroadcast(true);
5620 aObj.SetExtOptions(aOpt);
5621 aObj.ImportString("1,2,3", SotClipboardFormatId::STRING);
5623 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5624 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5625 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5627 // Formula value should have been updated.
5628 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5630 // Undo, and check the result.
5631 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5632 CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
5633 pUndoMgr->Undo();
5635 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5636 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5637 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5639 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5641 // Redo, and check the result.
5642 pUndoMgr->Redo();
5644 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5645 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5646 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5648 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0))); // formula
5650 pUndoMgr->Clear();
5652 m_pDoc->DeleteTab(0);
5655 CPPUNIT_TEST_FIXTURE(Test, testDeleteContents)
5657 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
5658 sc::UndoSwitch aUndoSwitch(*m_pDoc, true); // enable undo.
5660 m_pDoc->InsertTab(0, "Test");
5662 m_pDoc->SetValue(ScAddress(3,1,0), 1.0);
5663 m_pDoc->SetValue(ScAddress(3,2,0), 1.0);
5664 m_pDoc->SetValue(ScAddress(3,3,0), 1.0);
5665 m_pDoc->SetValue(ScAddress(3,4,0), 1.0);
5666 m_pDoc->SetValue(ScAddress(3,5,0), 1.0);
5667 m_pDoc->SetValue(ScAddress(3,6,0), 1.0);
5668 m_pDoc->SetValue(ScAddress(3,7,0), 1.0);
5669 m_pDoc->SetValue(ScAddress(3,8,0), 1.0);
5670 m_pDoc->SetString(ScAddress(3,15,0), "=SUM(D2:D15)");
5672 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5674 // Delete D2:D6.
5675 ScRange aRange(3,1,0,3,5,0);
5676 ScMarkData aMark(m_pDoc->GetSheetLimits());
5677 aMark.SelectOneTable(0);
5678 aMark.SetMarkArea(aRange);
5680 ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
5681 pUndoDoc->InitUndo(*m_pDoc, 0, 0);
5682 m_pDoc->CopyToDocument(aRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc, &aMark);
5683 ScUndoDeleteContents aUndo(m_xDocShell.get(), aMark, aRange, std::move(pUndoDoc), false, InsertDeleteFlags::CONTENTS, true);
5685 clearRange(m_pDoc, aRange);
5686 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5688 aUndo.Undo();
5689 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5691 aUndo.Redo();
5692 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,15,0))); // formula
5694 m_pDoc->DeleteTab(0);
5697 CPPUNIT_TEST_FIXTURE(Test, testTransliterateText)
5699 m_pDoc->InsertTab(0, "Test");
5701 // Set texts to A1:A3.
5702 m_pDoc->SetString(ScAddress(0,0,0), "Mike");
5703 m_pDoc->SetString(ScAddress(0,1,0), "Noah");
5704 m_pDoc->SetString(ScAddress(0,2,0), "Oscar");
5706 // Change them to uppercase.
5707 ScMarkData aMark(m_pDoc->GetSheetLimits());
5708 aMark.SetMarkArea(ScRange(0,0,0,0,2,0));
5709 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5710 rFunc.TransliterateText(
5711 aMark, TransliterationFlags::LOWERCASE_UPPERCASE, true);
5713 CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5714 CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5715 CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5717 // Test the undo and redo.
5718 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5719 CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
5721 pUndoMgr->Undo();
5722 CPPUNIT_ASSERT_EQUAL(OUString("Mike"), m_pDoc->GetString(ScAddress(0,0,0)));
5723 CPPUNIT_ASSERT_EQUAL(OUString("Noah"), m_pDoc->GetString(ScAddress(0,1,0)));
5724 CPPUNIT_ASSERT_EQUAL(OUString("Oscar"), m_pDoc->GetString(ScAddress(0,2,0)));
5726 pUndoMgr->Redo();
5727 CPPUNIT_ASSERT_EQUAL(OUString("MIKE"), m_pDoc->GetString(ScAddress(0,0,0)));
5728 CPPUNIT_ASSERT_EQUAL(OUString("NOAH"), m_pDoc->GetString(ScAddress(0,1,0)));
5729 CPPUNIT_ASSERT_EQUAL(OUString("OSCAR"), m_pDoc->GetString(ScAddress(0,2,0)));
5731 m_pDoc->DeleteTab(0);
5734 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue)
5736 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5737 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5739 m_pDoc->InsertTab(0, "Test");
5741 std::vector<std::vector<const char*>> aData = {
5742 { "=1", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5743 { "=2", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5744 { "=3", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5745 { "=4", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5746 { "=5", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5747 { "=6", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
5750 ScAddress aPos(1,2,0); // B3
5751 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5752 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5755 // Expected output table content. 0 = empty cell
5756 std::vector<std::vector<const char*>> aOutputCheck = {
5757 { "1", "2", "TRUE" },
5758 { "2", "4", "TRUE" },
5759 { "3", "6", "TRUE" },
5760 { "4", "8", "TRUE" },
5761 { "5", "10", "TRUE" },
5762 { "6", "12", "TRUE" },
5765 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5766 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5769 // Convert B5:C6 to static values, and check the result.
5770 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5771 ScRange aConvRange(1,4,0,2,5,0); // B5:C6
5772 rFunc.ConvertFormulaToValue(aConvRange, false);
5775 // Expected output table content. 0 = empty cell
5776 std::vector<std::vector<const char*>> aOutputCheck = {
5777 { "1", "2", "TRUE" },
5778 { "2", "4", "TRUE" },
5779 { "3", "6", "FALSE" },
5780 { "4", "8", "FALSE" },
5781 { "5", "10", "TRUE" },
5782 { "6", "12", "TRUE" },
5785 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5786 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5789 // Make sure that B3:B4 and B7:B8 are formula cells.
5790 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5791 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5792 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5793 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5795 // Make sure that B5:C6 are numeric cells.
5796 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5797 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5798 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5799 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5801 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5802 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5803 CPPUNIT_ASSERT(pFC);
5804 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5805 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5806 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5807 CPPUNIT_ASSERT(pFC);
5808 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5809 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5811 // Undo and check.
5812 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5813 CPPUNIT_ASSERT(pUndoMgr);
5814 pUndoMgr->Undo();
5817 // Expected output table content. 0 = empty cell
5818 std::vector<std::vector<const char*>> aOutputCheck = {
5819 { "1", "2", "TRUE" },
5820 { "2", "4", "TRUE" },
5821 { "3", "6", "TRUE" },
5822 { "4", "8", "TRUE" },
5823 { "5", "10", "TRUE" },
5824 { "6", "12", "TRUE" },
5827 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
5828 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5831 // B3:B8 should all be (ungrouped) formula cells.
5832 for (SCROW i = 2; i <= 7; ++i)
5834 pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
5835 CPPUNIT_ASSERT(pFC);
5836 CPPUNIT_ASSERT(!pFC->IsShared());
5839 // C3:C8 should be shared formula cells.
5840 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5841 CPPUNIT_ASSERT(pFC);
5842 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5843 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
5845 // Redo and check.
5846 pUndoMgr->Redo();
5848 // Expected output table content. 0 = empty cell
5849 std::vector<std::vector<const char*>> aOutputCheck = {
5850 { "1", "2", "TRUE" },
5851 { "2", "4", "TRUE" },
5852 { "3", "6", "FALSE" },
5853 { "4", "8", "FALSE" },
5854 { "5", "10", "TRUE" },
5855 { "6", "12", "TRUE" },
5858 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Converted");
5859 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5862 // Make sure that B3:B4 and B7:B8 are formula cells.
5863 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
5864 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
5865 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
5866 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
5868 // Make sure that B5:C6 are numeric cells.
5869 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
5870 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
5871 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
5872 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
5874 // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
5875 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
5876 CPPUNIT_ASSERT(pFC);
5877 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
5878 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5879 pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
5880 CPPUNIT_ASSERT(pFC);
5881 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
5882 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
5884 // Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
5885 pUndoMgr->Undo();
5886 m_pDoc->SetValue(ScAddress(1,4,0), 10);
5887 m_pDoc->SetValue(ScAddress(1,5,0), 11);
5888 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
5889 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
5891 m_pDoc->DeleteTab(0);
5894 CPPUNIT_TEST_FIXTURE(Test, testFormulaToValue2)
5896 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5897 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5899 m_pDoc->InsertTab(0, "Test");
5901 std::vector<std::vector<const char*>> aData = {
5902 { "=1", "=ISFORMULA(RC[-1])" },
5903 { "=2", "=ISFORMULA(RC[-1])" },
5904 { "3", "=ISFORMULA(RC[-1])" },
5905 { "=4", "=ISFORMULA(RC[-1])" },
5906 { "=5", "=ISFORMULA(RC[-1])" },
5909 // Insert data into B2:C6.
5910 ScAddress aPos(1,1,0); // B2
5911 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
5912 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5915 // Expected output table content. 0 = empty cell
5916 std::vector<std::vector<const char*>> aOutputCheck = {
5917 { "1", "TRUE" },
5918 { "2", "TRUE" },
5919 { "3", "FALSE" },
5920 { "4", "TRUE" },
5921 { "5", "TRUE" },
5924 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5925 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5928 // Convert B3:B5 to a value.
5929 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
5930 ScRange aConvRange(1,2,0,1,4,0); // B3:B5
5931 rFunc.ConvertFormulaToValue(aConvRange, false);
5934 // Expected output table content. 0 = empty cell
5935 std::vector<std::vector<const char*>> aOutputCheck = {
5936 { "1", "TRUE" },
5937 { "2", "FALSE" },
5938 { "3", "FALSE" },
5939 { "4", "FALSE" },
5940 { "5", "TRUE" },
5943 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5944 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5947 // Undo and check.
5948 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
5949 CPPUNIT_ASSERT(pUndoMgr);
5950 pUndoMgr->Undo();
5953 // Expected output table content. 0 = empty cell
5954 std::vector<std::vector<const char*>> aOutputCheck = {
5955 { "1", "TRUE" },
5956 { "2", "TRUE" },
5957 { "3", "FALSE" },
5958 { "4", "TRUE" },
5959 { "5", "TRUE" },
5962 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
5963 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
5966 m_pDoc->DeleteTab(0);
5969 CPPUNIT_TEST_FIXTURE(Test, testColumnFindEditCells)
5971 m_pDoc->InsertTab(0, "Test");
5973 // Test the basics with real edit cells, using Column A.
5975 SCROW nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
5976 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
5977 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,0,0));
5978 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
5979 nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,10,0));
5980 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
5982 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
5983 rEE.SetTextCurrentDefaults("Test");
5984 m_pDoc->SetEditText(ScAddress(0,0,0), rEE.CreateTextObject());
5985 const EditTextObject* pObj = m_pDoc->GetEditText(ScAddress(0,0,0));
5986 CPPUNIT_ASSERT_MESSAGE("There should be an edit cell here.", pObj);
5988 ScRange aRange(0,0,0,0,0,0);
5989 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
5990 CPPUNIT_ASSERT_EQUAL_MESSAGE("There is an edit cell here.", SCROW(0), nResRow);
5992 aRange.aStart.SetRow(1);
5993 aRange.aEnd.SetRow(1);
5994 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
5995 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
5997 aRange.aStart.SetRow(2);
5998 aRange.aEnd.SetRow(4);
5999 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6000 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6002 aRange.aStart.SetRow(0);
6003 aRange.aEnd.SetRow(m_pDoc->MaxRow());
6004 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6005 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an edit cell in specified range.", SCROW(0), nResRow);
6007 m_pDoc->SetString(ScAddress(0,0,0), "Test");
6008 m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
6009 ScRefCellValue aCell;
6010 aCell.assign(*m_pDoc, ScAddress(0,0,0));
6011 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string cell.", CELLTYPE_STRING, aCell.getType());
6012 aCell.assign(*m_pDoc, ScAddress(0,1,0));
6013 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6014 aCell.assign(*m_pDoc, ScAddress(0,2,0));
6015 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a numeric cell.", CELLTYPE_VALUE, aCell.getType());
6016 aCell.assign(*m_pDoc, ScAddress(0,3,0));
6017 CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
6019 aRange.aStart.SetRow(1);
6020 aRange.aEnd.SetRow(1);
6021 nResRow = m_pDoc->GetFirstEditTextRow(aRange);
6022 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
6024 // Test with non-edit cell but with ambiguous script type.
6026 m_pDoc->SetString(ScAddress(1,11,0), "Some text");
6027 m_pDoc->SetString(ScAddress(1,12,0), "Some text");
6028 m_pDoc->SetString(ScAddress(1,13,0), "Other text");
6030 m_pDoc->SetScriptType(ScAddress(1,11,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6031 m_pDoc->SetScriptType(ScAddress(1,12,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6032 m_pDoc->SetScriptType(ScAddress(1,13,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6034 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,11,0));
6035 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), nResRow);
6036 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(1,12,0));
6037 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), nResRow);
6039 for (SCROW i = 0; i <= 5; ++i)
6040 m_pDoc->SetString(ScAddress(2,i,0), "Text");
6042 m_pDoc->SetScriptType(ScAddress(2,5,0), (SvtScriptType::LATIN | SvtScriptType::ASIAN));
6044 nResRow = m_pDoc->GetFirstEditTextRow(ScAddress(2,1,0));
6045 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(-1), nResRow);
6047 m_pDoc->DeleteTab(0);
6051 CPPUNIT_TEST_FIXTURE(Test, testSetFormula)
6053 m_pDoc->InsertTab(0, "Test");
6055 static struct aInputs
6057 SCROW nRow;
6058 SCCOL nCol;
6059 const char* aFormula1; // Represents the formula that is input to SetFormula function.
6060 const char* aFormula2; // Represents the formula that is actually stored in the cell.
6061 formula::FormulaGrammar::Grammar const eGram;
6063 } const aTest[] = {
6064 { 5 , 4 , "=SUM($D$2:$F$3)" ,"=SUM($D$2:$F$3)" , formula::FormulaGrammar::Grammar::GRAM_ENGLISH },
6065 { 5 , 5 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE },
6066 { 6 , 6 , "=A1-$C2+B$3-$F$4" ,"=A1-$C2+B$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_NATIVE_XL_A1},
6067 { 7 , 8 , "=[.A1]-[.$C2]+[.G$3]-[.$F$4]","=A1-$C2+G$3-$F$4", formula::FormulaGrammar::Grammar::GRAM_ODFF }
6070 for(size_t i = 0; i < SAL_N_ELEMENTS(aTest); ++i)
6072 m_pDoc->SetFormula(ScAddress(aTest[i].nCol, aTest[i].nRow, 0), OUString::createFromAscii(aTest[i].aFormula1), aTest[i].eGram);
6073 OUString aBuffer = m_pDoc->GetFormula(aTest[i].nCol, aTest[i].nRow, 0);
6075 CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to set formula", OUString::createFromAscii(aTest[i].aFormula2), aBuffer);
6078 m_pDoc->DeleteTab(0);
6081 CPPUNIT_TEST_FIXTURE(Test, testMultipleDataCellsInRange)
6083 m_pDoc->InsertTab(0, "Test");
6085 ScRange aRange(1,2,0); // B3
6086 sc::MultiDataCellState aState = m_pDoc->HasMultipleDataCells(aRange);
6087 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::Empty, aState.meState);
6089 // Set a numeric value to B3.
6090 m_pDoc->SetValue(ScAddress(1,2,0), 1.0);
6091 aState = m_pDoc->HasMultipleDataCells(aRange);
6092 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6093 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6094 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6096 // Set another numeric value to B4.
6097 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
6098 aRange.aEnd.SetRow(3); // B3:B4
6099 aState = m_pDoc->HasMultipleDataCells(aRange);
6100 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6101 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6102 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6104 // Set the query range to B4:B5. Now it should only report one cell, with
6105 // B4 being the first non-empty cell.
6106 aRange.aStart.SetRow(3);
6107 aRange.aEnd.SetRow(4);
6108 aState = m_pDoc->HasMultipleDataCells(aRange);
6109 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6110 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6111 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6113 // Set the query range to A1:C3. The first non-empty cell should be B3.
6114 aRange = ScRange(0,0,0,2,2,0);
6115 aState = m_pDoc->HasMultipleDataCells(aRange);
6116 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
6117 CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
6118 CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
6120 // Set string cells to D4 and F5, and query D3:F5. D4 should be the first
6121 // non-empty cell.
6122 m_pDoc->SetString(ScAddress(3,3,0), "foo");
6123 m_pDoc->SetString(ScAddress(5,4,0), "bar");
6124 aRange = ScRange(3,2,0,5,4,0);
6125 aState = m_pDoc->HasMultipleDataCells(aRange);
6126 CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
6127 CPPUNIT_ASSERT_EQUAL(SCCOL(3), aState.mnCol1);
6128 CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
6130 // TODO : add more test cases as needed.
6132 m_pDoc->DeleteTab(0);
6135 CPPUNIT_TEST_FIXTURE(Test, testFormulaWizardSubformula)
6137 m_pDoc->InsertTab(0, "Test");
6139 m_pDoc->SetString(ScAddress(1,0,0), "=1"); // B1
6140 m_pDoc->SetString(ScAddress(1,1,0), "=1/0"); // B2
6141 m_pDoc->SetString(ScAddress(1,2,0), "=gibberish"); // B3
6143 ScSimpleFormulaCalculator aFCell1( *m_pDoc, ScAddress(0,0,0), "=B1:B3", true );
6144 FormulaError nErrCode = aFCell1.GetErrCode();
6145 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell1.IsMatrix() );
6146 CPPUNIT_ASSERT_EQUAL( OUString("{1|#DIV/0!|#NAME?}"), aFCell1.GetString().getString() );
6148 m_pDoc->SetString(ScAddress(1,0,0), "=NA()"); // B1
6149 m_pDoc->SetString(ScAddress(1,1,0), "2"); // B2
6150 m_pDoc->SetString(ScAddress(1,2,0), "=1+2"); // B3
6151 ScSimpleFormulaCalculator aFCell2( *m_pDoc, ScAddress(0,0,0), "=B1:B3", true );
6152 nErrCode = aFCell2.GetErrCode();
6153 CPPUNIT_ASSERT( nErrCode == FormulaError::NONE || aFCell2.IsMatrix() );
6154 CPPUNIT_ASSERT_EQUAL( OUString("{#N/A|2|3}"), aFCell2.GetString().getString() );
6156 m_pDoc->DeleteTab(0);
6159 CPPUNIT_TEST_FIXTURE(Test, testDiagonalBorders)
6161 m_pDoc->InsertTab(0, "Diagonal");
6163 ScAddress aPos;
6164 const editeng::SvxBorderLine* pLine;
6165 const ScPatternAttr* pPat;
6167 // diagonal down border
6168 ::editeng::SvxBorderLine dDownBorderLine(nullptr, 1);
6169 SvxLineItem dDownLineItem(ATTR_BORDER_TLBR);
6170 dDownLineItem.SetLine(&dDownBorderLine);
6172 // set diagonal down border to cell(A1)
6173 m_pDoc->ApplyAttr(0, 0, 0, dDownLineItem);
6175 aPos = { 0, 0, 0 };
6176 pPat = m_pDoc->GetPattern(aPos);
6177 CPPUNIT_ASSERT(pPat);
6179 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6180 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6182 // diagonal up border
6183 ::editeng::SvxBorderLine dUpBorderLine(nullptr, 1);
6184 SvxLineItem dUpLineItem(ATTR_BORDER_BLTR);
6185 dUpLineItem.SetLine(&dUpBorderLine);
6187 // set diagonal up border to cell(A2)
6188 m_pDoc->ApplyAttr(0, 1, 0, dUpLineItem);
6190 aPos = { 0, 1, 0 };
6191 pPat = m_pDoc->GetPattern(aPos);
6192 CPPUNIT_ASSERT(pPat);
6194 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6195 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6197 // diagonal down and up border in the same cell (A5)
6198 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6199 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6201 // test if both borders are applied successfully in the same cell (A5)
6202 aPos = { 0, 4, 0 };
6203 pPat = m_pDoc->GetPattern(aPos);
6204 CPPUNIT_ASSERT(pPat);
6206 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6207 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
6208 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6209 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
6211 // test if both borders are removed successfully
6212 dDownLineItem.SetLine(nullptr);
6213 dUpLineItem.SetLine(nullptr);
6215 // SetLine(nullptr) should remove the lines from (A5)
6216 m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
6217 m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
6219 pPat = m_pDoc->GetPattern(aPos);
6220 CPPUNIT_ASSERT(pPat);
6222 pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
6223 CPPUNIT_ASSERT_MESSAGE("Diagonal down border was not expected, but is found!", !pLine);
6224 pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
6225 CPPUNIT_ASSERT_MESSAGE("Diagonal up border was not expected, but is found!", !pLine);
6227 m_pDoc->DeleteTab(0);
6230 CPPUNIT_TEST_FIXTURE(Test, testWholeDocBorders)
6232 m_pDoc->InsertTab(0, "Borders");
6234 // Set outside border to be on all sides, and inside borders to be only vertical.
6235 // This should result in edge borders of the spreadsheets being set, but internal
6236 // borders between cells should be only vertical, not horizontal.
6237 ::editeng::SvxBorderLine line(nullptr, 50, SvxBorderLineStyle::SOLID);
6238 SvxBoxItem borderItem(ATTR_BORDER);
6239 borderItem.SetLine(&line, SvxBoxItemLine::LEFT);
6240 borderItem.SetLine(&line, SvxBoxItemLine::RIGHT);
6241 borderItem.SetLine(&line, SvxBoxItemLine::TOP);
6242 borderItem.SetLine(&line, SvxBoxItemLine::BOTTOM);
6243 SvxBoxInfoItem boxInfoItem(ATTR_BORDER);
6244 boxInfoItem.SetLine(&line, SvxBoxInfoItemLine::VERT);
6246 ScMarkData mark( m_pDoc->GetSheetLimits(), ScRange( 0, 0, 0, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 ));
6247 m_pDoc->ApplySelectionFrame( mark, borderItem, &boxInfoItem );
6249 const ScPatternAttr* attr;
6250 attr = m_pDoc->GetPattern( 0, 0, 0 );
6251 CPPUNIT_ASSERT(attr);
6252 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6253 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6254 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6255 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6257 attr = m_pDoc->GetPattern( 1, 0, 0 );
6258 CPPUNIT_ASSERT(attr);
6259 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6260 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6261 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6262 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6264 attr = m_pDoc->GetPattern( 0, 1, 0 );
6265 CPPUNIT_ASSERT(attr);
6266 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6267 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6268 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6269 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6271 attr = m_pDoc->GetPattern( 1, 1, 0 );
6272 CPPUNIT_ASSERT(attr);
6273 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6274 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6275 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6276 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6278 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), 0, 0 );
6279 CPPUNIT_ASSERT(attr);
6280 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetTop());
6281 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6282 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6283 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetBottom());
6285 attr = m_pDoc->GetPattern( 0, m_pDoc->MaxRow(), 0 );
6286 CPPUNIT_ASSERT(attr);
6287 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6288 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6289 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6290 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6292 attr = m_pDoc->GetPattern( m_pDoc->MaxCol(), m_pDoc->MaxRow(), 0 );
6293 CPPUNIT_ASSERT(attr);
6294 CPPUNIT_ASSERT(!attr->GetItem(ATTR_BORDER).GetTop());
6295 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetLeft());
6296 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetRight());
6297 CPPUNIT_ASSERT(attr->GetItem(ATTR_BORDER).GetBottom());
6299 m_pDoc->DeleteTab(0);
6302 CPPUNIT_TEST_FIXTURE(Test, testSetStringAndNote)
6304 m_pDoc->InsertTab(0, "Test");
6306 // We need a drawing layer in order to create caption objects.
6307 m_pDoc->InitDrawLayer(m_xDocShell.get());
6309 //note on A1
6310 ScAddress aAdrA1 (0, 0, 0);
6311 ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdrA1);
6312 pNote->SetText(aAdrA1, "Hello world in A1");
6314 m_pDoc->SetString(0, 0, 0, "");
6316 pNote = m_pDoc->GetNote(aAdrA1);
6317 CPPUNIT_ASSERT(pNote);
6319 m_pDoc->DeleteTab(0);
6322 CPPUNIT_TEST_FIXTURE(Test, testUndoDataAnchor)
6324 m_pDoc->InsertTab(0, "Tab1");
6325 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 1 sheets to begin with",
6326 static_cast<SCTAB>(1), m_pDoc->GetTableCount());
6328 m_pDoc->InitDrawLayer();
6329 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
6330 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
6331 SdrPage* pPage = pDrawLayer->GetPage(0);
6332 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
6334 // Insert an object.
6335 tools::Rectangle aObjRect(2,1000,100,1100);
6336 rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
6337 pPage->InsertObject(pObj.get());
6338 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
6340 // Get anchor data
6341 ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
6342 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6344 ScAddress aOldStart = pData->maStart;
6345 ScAddress aOldEnd = pData->maEnd;
6347 // Get non rotated anchor data
6348 ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6349 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6351 ScAddress aNOldStart = pNData->maStart;
6352 ScAddress aNOldEnd = pNData->maEnd;
6353 CPPUNIT_ASSERT_EQUAL(aOldStart, aNOldStart);
6354 CPPUNIT_ASSERT_EQUAL(aOldEnd, aNOldEnd);
6356 //pDrawLayer->BeginCalcUndo(false);
6357 // Insert a new row at row 3.
6358 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6359 ScMarkData aMark(m_pDoc->GetSheetLimits());
6360 aMark.SelectOneTable(0);
6361 rFunc.InsertCells(ScRange( 0, aOldStart.Row() - 1, 0, m_pDoc->MaxCol(), aOldStart.Row(), 0 ), &aMark, INS_INSROWS_BEFORE, true, true);
6363 pData = ScDrawLayer::GetObjData(pObj.get());
6364 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6366 ScAddress aNewStart = pData->maStart;
6367 ScAddress aNewEnd = pData->maEnd;
6369 // Get non rotated anchor data
6370 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6371 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6373 ScAddress aNNewStart = pNData->maStart;
6374 ScAddress aNNewEnd = pNData->maEnd;
6375 CPPUNIT_ASSERT_EQUAL(aNewStart, aNNewStart);
6376 CPPUNIT_ASSERT_EQUAL(aNewEnd, aNNewEnd);
6377 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewStart != aOldStart );
6378 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNewEnd != aOldEnd );
6379 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewStart != aNOldStart );
6380 CPPUNIT_ASSERT_MESSAGE("Failed to compare Address.", aNNewEnd != aNOldEnd );
6382 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6383 CPPUNIT_ASSERT(pUndoMgr);
6384 pUndoMgr->Undo();
6386 // Check state
6387 ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObj);
6388 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Failed to check state SCA_CELL.", SCA_CELL, oldType);
6390 // Get anchor data
6391 pData = ScDrawLayer::GetObjData(pObj.get());
6392 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6394 // Get non rotated anchor data
6395 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6396 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6398 // Check if data has moved to new rows
6399 CPPUNIT_ASSERT_EQUAL(pData->maStart, aOldStart);
6400 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aOldEnd);
6402 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNOldStart);
6403 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNOldEnd);
6405 pUndoMgr->Redo();
6407 // Get anchor data
6408 pData = ScDrawLayer::GetObjData(pObj.get());
6409 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
6411 // Get non rotated anchor data
6412 pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
6413 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
6415 // Check if data has moved to new rows
6416 CPPUNIT_ASSERT_EQUAL(pData->maStart, aNewStart);
6417 CPPUNIT_ASSERT_EQUAL(pData->maEnd, aNewEnd);
6419 CPPUNIT_ASSERT_EQUAL(pNData->maStart, aNNewStart);
6420 CPPUNIT_ASSERT_EQUAL(pNData->maEnd, aNNewEnd);
6422 m_pDoc->DeleteTab(0);
6426 CPPUNIT_TEST_FIXTURE(Test, testEmptyCalcDocDefaults)
6428 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCellCount() );
6429 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetFormulaGroupCount() );
6430 CPPUNIT_ASSERT_EQUAL( sal_uInt64(0), m_pDoc->GetCodeCount() );
6431 CPPUNIT_ASSERT_EQUAL( int(CharCompressType::NONE), static_cast<int>(m_pDoc->GetAsianCompression()) );
6433 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasPrintRange() );
6434 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInVBAMode() );
6435 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasNotes() );
6436 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6438 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedFonts() );
6439 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedUsedFontsOnly() );
6440 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptLatin() );
6441 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptAsian() );
6442 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsEmbedFontScriptComplex() );
6443 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsEmbedded() );
6445 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDocEditable() );
6446 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocProtected() );
6447 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDocVisible() );
6448 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUserInteractionEnabled() );
6450 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasAnyCalcNotification() );
6451 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAutoCalcShellDisabled() );
6452 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsForcedFormulaPending() );
6453 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalculatingFormulaTree() );
6455 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipOrUndo() );
6456 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboard() );
6457 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsUndo() );
6458 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsUndoEnabled() );
6459 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCutMode() );
6460 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsClipboardSource() );
6461 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInsertingFromOtherDoc() );
6462 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->PastingDrawFromOtherDoc() );
6464 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsAdjustHeightLocked() );
6465 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsExecuteLinkEnabled() );
6466 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChangeReadOnlyEnabled() );
6468 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IdleCalcTextWidth() );
6469 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsIdleEnabled() );
6470 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsDetectiveDirty() );
6471 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasLinkFormulaNeedingCheck() );
6472 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsChartListenerCollectionNeedsUpdate() );
6474 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasRangeOverflow() );
6475 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsImportingXML() );
6476 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsCalcingAfterLoad() );
6477 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetNoListening() );
6479 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianCompression() );
6480 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->GetAsianKerning() );
6481 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsValidAsianKerning() );
6483 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreter() );
6484 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInInterpreterTableOp() );
6485 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInDtorClear() );
6486 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsExpandRefs() );
6487 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsInLinkUpdate() );
6489 SCTAB tab = m_pDoc->GetVisibleTab();
6491 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsVisible(tab) );
6492 CPPUNIT_ASSERT_EQUAL( true, m_pDoc->IsDefaultTabBgColor(tab) );
6493 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasTable(tab) );
6495 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->IsActiveScenario(tab) );
6496 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasCalcNotification(tab) );
6497 CPPUNIT_ASSERT_EQUAL( false, m_pDoc->HasManualBreaks(tab) );
6500 void Test::checkPrecisionAsShown( OUString& rCode, double fValue, double fExpectedRoundVal )
6502 SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
6503 sal_uInt32 nFormat = pFormatter->GetEntryKey( rCode );
6504 if ( nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
6506 sal_Int32 nCheckPos = 0;
6507 SvNumFormatType nType;
6508 pFormatter->PutEntry( rCode, nCheckPos, nType, nFormat );
6509 CPPUNIT_ASSERT_EQUAL( sal_Int32(0), nCheckPos );
6511 double fRoundValue = m_pDoc->RoundValueAsShown( fValue, nFormat );
6512 OString aMessage = "Format \"" +
6513 OUStringToOString( rCode, RTL_TEXTENCODING_ASCII_US ) +
6514 "\" is not correctly rounded";
6515 CPPUNIT_ASSERT_EQUAL_MESSAGE( aMessage.getStr(), fExpectedRoundVal, fRoundValue );
6518 CPPUNIT_TEST_FIXTURE(Test, testPrecisionAsShown)
6520 m_pDoc->InsertTab(0, "Test");
6522 // Turn on "precision as shown" option.
6523 setCalcAsShown( m_pDoc, true);
6525 OUString aCode;
6526 double fValue, fExpectedRoundVal;
6527 { // decimal rounding
6528 aCode = "0.00";
6529 fValue = 1.0/3.0;
6530 fExpectedRoundVal = 0.33;
6531 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6532 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6533 fValue = 10.001;
6534 fExpectedRoundVal = 10.0;
6535 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6536 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6538 { // thousand rounding tdf#106253
6539 aCode = "0,,";
6540 fValue = 4.0e9 / 7.0;
6541 fExpectedRoundVal = 571e6;
6542 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6543 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6544 aCode = "\"k\"[$$-409]* #,;[RED]-\"k\"[$$-409]* #,";
6545 fValue = 4.0e8 / 7.0;
6546 fExpectedRoundVal = 57.143e6;
6547 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6548 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6550 { // percent rounding
6551 aCode = "0.00%";
6552 fValue = 4.0 / 7.0;
6553 fExpectedRoundVal = 0.5714;
6554 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6555 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6556 fValue = 40.0 / 7.0;
6557 fExpectedRoundVal = 5.7143;
6558 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6559 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6561 { // scientific rounding
6562 aCode = "0.00E0";
6563 fValue = 400000.0 / 7.0;
6564 fExpectedRoundVal = 57100.0;
6565 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6566 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6567 fValue = 4.0 / 70000.0;
6568 fExpectedRoundVal = 5.71e-5;
6569 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6570 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6571 // engineering rounding tdf#106252
6572 aCode = "##0.000E0";
6573 fValue = 400000.0 / 7.0;
6574 fExpectedRoundVal = 57.143e3;
6575 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6576 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6577 fValue = 4000000.0 / 7.0;
6578 fExpectedRoundVal = 571.429e3;
6579 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6580 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6581 fValue = 40000000.0 / 7.0;
6582 fExpectedRoundVal = 5.714e6;
6583 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6584 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6585 fValue = 4.0 / 70000.0;
6586 fExpectedRoundVal = 57.143e-6;
6587 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6588 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6589 fValue = 4.0 / 7000.0;
6590 fExpectedRoundVal = 571.429e-6;
6591 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6592 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6593 fValue = 4.0 / 700.0;
6594 fExpectedRoundVal = 5.714e-3;
6595 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6596 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6597 aCode = "##?0.0#E0";
6598 fValue = 400000.0 / 7.0;
6599 fExpectedRoundVal = 5.71e4;
6600 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6601 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6602 fValue = 4000000.0 / 7.0;
6603 fExpectedRoundVal = 57.14e4;
6604 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6605 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6606 fValue = 40000000.0 / 7.0;
6607 fExpectedRoundVal = 571.43e4;
6608 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6609 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6610 fValue = 400000000.0 / 7.0;
6611 fExpectedRoundVal = 5714.29e4;
6612 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6613 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6614 fValue = 4.0 / 70000.0;
6615 fExpectedRoundVal = 5714.29e-8;
6616 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6617 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6618 fValue = 4.0 / 7000.0;
6619 fExpectedRoundVal = 5.71e-4;
6620 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6621 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6622 fValue = 4.0 / 700.0;
6623 fExpectedRoundVal = 57.14e-4;
6624 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6625 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6626 fValue = 4.0 / 70.0;
6627 fExpectedRoundVal = 571.43e-4;
6628 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6629 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6631 { // fraction rounding tdf#105657
6632 aCode = "# ?/?";
6633 fValue = 0.35;
6634 fExpectedRoundVal = 1.0/3.0;
6635 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6636 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6638 { // exact fraction
6639 aCode = "# ?/??";
6640 fValue = 0.35;
6641 fExpectedRoundVal = 0.35;
6642 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6643 checkPrecisionAsShown( aCode, -fValue, -fExpectedRoundVal );
6645 { // several sub-formats tdf#106052
6646 aCode = "0.00;-0.000";
6647 fValue = 1.0/3.0;
6648 fExpectedRoundVal = 0.33;
6649 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6650 fValue = -1.0/3.0;
6651 fExpectedRoundVal = -0.333;
6652 checkPrecisionAsShown( aCode, fValue, fExpectedRoundVal );
6655 setCalcAsShown( m_pDoc, false);
6656 m_pDoc->DeleteTab(0);
6659 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByRow)
6661 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6662 m_pDoc->InsertTab(0, "Protected");
6665 // Remove protected flags from rows 2-5.
6666 ScPatternAttr aAttr(m_pDoc->GetPool());
6667 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6668 m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 4, 0, aAttr);
6670 // Protect the sheet without any options.
6671 ScTableProtection aProtect;
6672 aProtect.setProtected(true);
6673 m_pDoc->SetTabProtection(0, &aProtect);
6675 // Try to delete row 3. It should fail.
6676 ScRange aRow3(0,2,0,m_pDoc->MaxCol(),2,0);
6677 ScMarkData aMark(m_pDoc->GetSheetLimits());
6678 aMark.SelectOneTable(0);
6679 bool bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6680 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should fail.", !bDeleted);
6682 // Protect the sheet but allow row deletion.
6683 aProtect.setOption(ScTableProtection::DELETE_ROWS, true);
6684 m_pDoc->SetTabProtection(0, &aProtect);
6686 // Now we should be able to delete row 3.
6687 bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
6688 CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should succeed.", bDeleted);
6690 // But, row deletion should still fail on a protected row.
6691 ScRange aRow10(0,9,0,m_pDoc->MaxCol(),9,0);
6692 bDeleted = rDocFunc.DeleteCells(aRow10, &aMark, DelCellCmd::Rows, true);
6693 CPPUNIT_ASSERT_MESSAGE("deletion of row 10 should not be allowed.", !bDeleted);
6695 // Try inserting a new row. It should fail.
6696 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6697 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6699 // Allow row insertions.
6700 aProtect.setOption(ScTableProtection::INSERT_ROWS, true);
6701 m_pDoc->SetTabProtection(0, &aProtect);
6703 bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
6704 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should succeed.", bInserted);
6706 // Row insertion is allowed even when the rows above and below have protected flags set.
6707 bInserted = rDocFunc.InsertCells(aRow10, &aMark, INS_INSROWS_AFTER, true, true);
6708 CPPUNIT_ASSERT_MESSAGE("row insertion at row 10 should succeed.", bInserted);
6711 m_pDoc->InsertTab(1, "Matrix"); // This sheet is unprotected.
6714 // Insert matrix into B2:C3.
6715 ScMarkData aMark(m_pDoc->GetSheetLimits());
6716 aMark.SelectOneTable(1);
6717 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, "={1;2|3;4}");
6719 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6720 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6721 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6722 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6724 // Try to insert a row at row 3. It should fail because of matrix's presence.
6726 ScRange aRow3(0,2,1,m_pDoc->MaxCol(),2,1);
6727 bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_BEFORE, true, true);
6728 CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
6731 m_pDoc->DeleteTab(1);
6732 m_pDoc->DeleteTab(0);
6735 CPPUNIT_TEST_FIXTURE(Test, testProtectedSheetEditByColumn)
6737 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
6738 m_pDoc->InsertTab(0, "Protected");
6741 // Remove protected flags from columns B to E.
6742 ScPatternAttr aAttr(m_pDoc->GetPool());
6743 aAttr.GetItemSet().Put(ScProtectionAttr(false));
6744 m_pDoc->ApplyPatternAreaTab(1, 0, 4, m_pDoc->MaxRow(), 0, aAttr);
6746 // Protect the sheet without any options.
6747 ScTableProtection aProtect;
6748 aProtect.setProtected(true);
6749 m_pDoc->SetTabProtection(0, &aProtect);
6751 // Try to delete column C. It should fail.
6752 ScRange aCol3(2,0,0,2,m_pDoc->MaxRow(),0);
6753 ScMarkData aMark(m_pDoc->GetSheetLimits());
6754 aMark.SelectOneTable(0);
6755 bool bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6756 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should fail.", !bDeleted);
6758 // Protect the sheet but allow column deletion.
6759 aProtect.setOption(ScTableProtection::DELETE_COLUMNS, true);
6760 m_pDoc->SetTabProtection(0, &aProtect);
6762 // Now we should be able to delete column C.
6763 bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
6764 CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should succeed.", bDeleted);
6766 // But, column deletion should still fail on a protected column.
6767 ScRange aCol10(9,0,0,9,m_pDoc->MaxRow(),0);
6768 bDeleted = rDocFunc.DeleteCells(aCol10, &aMark, DelCellCmd::Cols, true);
6769 CPPUNIT_ASSERT_MESSAGE("deletion of column 10 should not be allowed.", !bDeleted);
6771 // Try inserting a new column. It should fail.
6772 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6773 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should fail.", !bInserted);
6775 // Allow column insertions.
6776 aProtect.setOption(ScTableProtection::INSERT_COLUMNS, true);
6777 m_pDoc->SetTabProtection(0, &aProtect);
6779 bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
6780 CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should succeed.", bInserted);
6782 // Column insertion is allowed even when the columns above and below have protected flags set.
6783 bInserted = rDocFunc.InsertCells(aCol10, &aMark, INS_INSCOLS_AFTER, true, true);
6784 CPPUNIT_ASSERT_MESSAGE("column insertion at column 10 should succeed.", bInserted);
6787 m_pDoc->InsertTab(1, "Matrix"); // This sheet is unprotected.
6790 // Insert matrix into B2:C3.
6791 ScMarkData aMark(m_pDoc->GetSheetLimits());
6792 aMark.SelectOneTable(1);
6793 m_pDoc->InsertMatrixFormula(1, 1, 2, 2, aMark, "={1;2|3;4}");
6795 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,1)));
6796 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,1)));
6797 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,1)));
6798 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,2,1)));
6800 // Try to insert a column at column C. It should fail because of matrix's presence.
6802 ScRange aCol3(2,0,1,2,m_pDoc->MaxRow(),1);
6803 bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_BEFORE, true, true);
6804 CPPUNIT_ASSERT_MESSAGE("column insertion at column C should fail.", !bInserted);
6807 m_pDoc->DeleteTab(1);
6808 m_pDoc->DeleteTab(0);
6811 CPPUNIT_TEST_FIXTURE(Test, testInsertColumnsWithFormulaCells)
6813 m_pDoc->InsertTab(0, "Tab1");
6815 std::set<SCCOL> aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6816 CPPUNIT_ASSERT_MESSAGE("empty sheet should contain no formula cells.", aCols.empty());
6818 auto equals = [](const std::set<SCCOL>& left, const std::set<SCCOL>& right)
6820 return left == right;
6823 // insert formula cells in columns 2, 4 and 6.
6824 m_pDoc->SetFormula(ScAddress(2, 2, 0), "=1", m_pDoc->GetGrammar());
6825 m_pDoc->SetFormula(ScAddress(4, 2, 0), "=1", m_pDoc->GetGrammar());
6826 m_pDoc->SetFormula(ScAddress(6, 2, 0), "=1", m_pDoc->GetGrammar());
6828 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6830 std::set<SCCOL> aExpected = { 2, 4, 6 };
6831 CPPUNIT_ASSERT_MESSAGE("Columns 2, 4 and 6 should contain formula cells.", equals(aExpected, aCols));
6833 // Insert 2 columns at column A to shift everything to right by 2.
6834 m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 0, 2);
6836 aExpected = { 4, 6, 8 };
6837 aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
6838 CPPUNIT_ASSERT_MESSAGE("Columns 4, 6 and 8 should contain formula cells.", equals(aExpected, aCols));
6842 m_pDoc->CheckIntegrity(0);
6844 catch (const std::exception& e)
6846 std::ostringstream os;
6847 os << "document integrity check failed: " << e.what();
6848 CPPUNIT_FAIL(os.str());
6851 m_pDoc->DeleteTab(0);
6854 CPPUNIT_TEST_FIXTURE(Test, testDocumentModelAccessor_getDocumentCurrencies)
6856 m_pDoc->InsertTab(0, "Sheet1");
6858 // Check document currencies - expect 0
6859 auto pAccessor = m_xDocShell->GetDocumentModelAccessor();
6860 CPPUNIT_ASSERT(pAccessor);
6861 CPPUNIT_ASSERT_EQUAL(size_t(0), pAccessor->getDocumentCurrencies().size());
6863 // Set a currency to a cell
6865 m_pDoc->SetValue(ScAddress(0, 0, 0), 2.0);
6867 OUString aCode = u"#.##0,00[$€-424]"_ustr;
6869 sal_Int32 nCheckPos;
6870 SvNumFormatType eType;
6871 sal_uInt32 nFormat;
6873 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
6874 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
6876 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
6877 SfxItemSet& rSet = aNewAttrs.GetItemSet();
6878 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
6879 m_pDoc->ApplyPattern(0, 0, 0, aNewAttrs); // A1.
6881 CPPUNIT_ASSERT_EQUAL(u"2,00€"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
6884 // Check document currencies again
6885 auto aCurrencyIDs = pAccessor->getDocumentCurrencies();
6886 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
6888 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
6889 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
6890 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
6892 // Set the same currency to 2 more cells
6894 m_pDoc->SetValue(ScAddress(1, 1, 0), 5.0);
6895 m_pDoc->SetValue(ScAddress(2, 2, 0), 7.0);
6897 OUString aCode = u"#.##0,00[$€-424]"_ustr;
6899 sal_Int32 nCheckPos;
6900 SvNumFormatType eType;
6901 sal_uInt32 nFormat;
6903 m_pDoc->GetFormatTable()->PutEntry(aCode, nCheckPos, eType, nFormat, LANGUAGE_SLOVENIAN);
6904 CPPUNIT_ASSERT_EQUAL(SvNumFormatType::CURRENCY, eType);
6906 ScPatternAttr aNewAttrs(m_pDoc->GetPool());
6907 SfxItemSet& rSet = aNewAttrs.GetItemSet();
6908 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
6909 m_pDoc->ApplyPattern(1, 1, 0, aNewAttrs); // B2.
6910 m_pDoc->ApplyPattern(2, 2, 0, aNewAttrs); // C3.
6912 CPPUNIT_ASSERT_EQUAL(u"5,00€"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0)));
6913 CPPUNIT_ASSERT_EQUAL(u"7,00€"_ustr, m_pDoc->GetString(ScAddress(2, 2, 0)));
6916 // Check document currencies again - should be 1 entry only
6917 aCurrencyIDs = pAccessor->getDocumentCurrencies();
6918 CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
6920 CPPUNIT_ASSERT_EQUAL(LANGUAGE_SLOVENIAN, aCurrencyIDs[0].eLanguage);
6921 CPPUNIT_ASSERT_EQUAL(u"-424"_ustr, aCurrencyIDs[0].aExtension);
6922 CPPUNIT_ASSERT_EQUAL(u"€"_ustr, aCurrencyIDs[0].aSymbol);
6926 CPPUNIT_PLUGIN_IMPLEMENT();
6928 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */