/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Undo the new row
m_pDoc->GetUndoManager()->Undo();
// Check patterns // Failed if row 3 is not blue all the way through
pItem = nullptr;
m_pDoc->GetPattern(ScAddress(1000, 3, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
CPPUNIT_ASSERT(pItem);
CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
// Undo the new column
m_pDoc->GetUndoManager()->Undo();
// Check patterns // Failed if column 3 is not blue all the way through
pItem = nullptr;
m_pDoc->GetPattern(ScAddress(3, 1000, 0))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
CPPUNIT_ASSERT(pItem);
CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
{ // These two shared string objects must go out of scope before the purge test.
svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(0,1,0));
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS1.isValid());
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid shared string.", aSS2.isValid());
CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
aSS2 = m_pDoc->GetSharedString(ScAddress(0,2,0));
CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
// A3 and A5 should differ but should be equal case-insensitively.
aSS1 = m_pDoc->GetSharedString(ScAddress(0,2,0));
aSS2 = m_pDoc->GetSharedString(ScAddress(0,4,0));
CPPUNIT_ASSERT_MESSAGE("They must differ", aSS1.getData() != aSS2.getData());
CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
// A2 and A4 should be equal when ignoring cases.
aSS1 = m_pDoc->GetSharedString(ScAddress(0,1,0));
aSS2 = m_pDoc->GetSharedString(ScAddress(0,3,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("They must be equal when cases are ignored.", aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
}
// Check the string counts after purging. Purging shouldn't remove any strings in this case.
rPool.purge();
CPPUNIT_ASSERT_EQUAL(5+extraCount, rPool.getCount());
CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, rPool.getCountIgnoreCase());
// Clear A1
clearRange(m_pDoc, ScRange(ScAddress(0,0,0))); // Clear A2
clearRange(m_pDoc, ScRange(ScAddress(0,1,0))); // Clear A3
clearRange(m_pDoc, ScRange(ScAddress(0,2,0))); // Clear A4
clearRange(m_pDoc, ScRange(ScAddress(0,3,0))); // Clear A5 and the pool should be completely empty.
clearRange(m_pDoc, ScRange(ScAddress(0,4,0)));
rPool.purge();
CPPUNIT_ASSERT_EQUAL(extraCount, rPool.getCount());
CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, rPool.getCountIgnoreCase());
// Now, compare string and edit text cells.
m_pDoc->SetString(ScAddress(0,0,0), "Andy and Bruce"); // A1 // [-loplugin:ostr]
ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
rEE.SetTextCurrentDefaults(u"Andy and Bruce"_ustr);
// These two should be equal.
svl::SharedString aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
svl::SharedString aSS2 = m_pDoc->GetSharedString(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
CPPUNIT_ASSERT_EQUAL(aSS1.getData(), aSS2.getData());
rEE.SetTextCurrentDefaults(u"ANDY and BRUCE"_ustr);
m_pDoc->SetEditText(ScAddress(2,0,0), rEE.CreateTextObject()); // C1
aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
CPPUNIT_ASSERT_MESSAGE("These two should be different when cases are considered.", aSS1.getData() != aSS2.getData());
// But they should be considered equal when cases are ignored.
aSS1 = m_pDoc->GetSharedString(ScAddress(0,0,0));
aSS2 = m_pDoc->GetSharedString(ScAddress(2,0,0));
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS1.isValid());
CPPUNIT_ASSERT_MESSAGE("Failed to get a valid string ID.", aSS2.isValid());
CPPUNIT_ASSERT_EQUAL(aSS1.getDataIgnoreCase(), aSS2.getDataIgnoreCase());
ScRangeList aRL;
aRL.push_back(ScRange(1,1,0,3,10,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("List should have one range.", size_t(1), aRL.size()); const ScRange* p = &aRL[0];
CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(1,1,0), p->aStart);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong range.", ScAddress(3,10,0), p->aEnd);
// Empty mark. Nothing is selected.
std::vector<sc::ColRowSpan> aSpans = aMarkData.GetMarkedRowSpans();
CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
aSpans = aMarkData.GetMarkedColSpans();
CPPUNIT_ASSERT_MESSAGE("Span should be empty.", aSpans.empty());
// Select B3:F7.
aMarkData.SetMarkArea(ScRange(1,2,0,5,6,0));
aSpans = aMarkData.GetMarkedRowSpans();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(6), aSpans[0].mnEnd);
aSpans = aMarkData.GetMarkedColSpans();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(1), aSpans[0].mnStart);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
aSpans = aMarkData.GetMarkedColSpans();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
// Select C8:C10.
aMarkData.SetMultiMarkArea(ScRange(2,7,0,2,9,0));
aSpans = aMarkData.GetMarkedRowSpans();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected row span.", size_t(1), aSpans.size());
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(2), aSpans[0].mnStart);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(12), aSpans[0].mnEnd);
aSpans = aMarkData.GetMarkedColSpans();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one selected column span.", size_t(1), aSpans.size());
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(0), aSpans[0].mnStart);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOLROW>(5), aSpans[0].mnEnd);
}
CPPUNIT_TEST_FIXTURE(Test, testInput)
{
CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
m_pDoc->InsertTab (0, u"foo"_ustr));
OUString test;
m_pDoc->SetString(0, 0, 0, u"'10.5"_ustr);
test = m_pDoc->GetString(0, 0, 0); bool bTest = test == "10.5";
CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
m_pDoc->SetString(0, 0, 0, u"'apple'"_ustr);
test = m_pDoc->GetString(0, 0, 0);
bTest = test == "apple'";
CPPUNIT_ASSERT_MESSAGE("Text content should have the first apostrophe stripped.", bTest);
// Customized string handling policy.
ScSetStringParam aParam;
aParam.setTextInput();
m_pDoc->SetString(0, 0, 0, u"000123"_ustr, &aParam);
test = m_pDoc->GetString(0, 0, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Text content should have been treated as string, not number.", u"000123"_ustr, test);
// Transfer generated tabs to a new document with different order
ScDocument aScDocument;
aScDocument.TransferTab(*m_pDoc, nSecondTab, nFirstTab);
aScDocument.TransferTab(*m_pDoc, nFirstTab, nSecondTab);
// Check the number of print ranges in both documents
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nFirstTab));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), m_pDoc->GetPrintRangeCount(nSecondTab));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nFirstTab));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(1), aScDocument.GetPrintRangeCount(nSecondTab));
// Check the print ranges and col/row repetitions in both documents
CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *m_pDoc->GetPrintRange(nFirstTab, 0));
CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *m_pDoc->GetRepeatColRange(nFirstTab));
CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *m_pDoc->GetRepeatRowRange(nFirstTab));
CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *m_pDoc->GetPrintRange(nSecondTab, 0));
CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *m_pDoc->GetRepeatColRange(nSecondTab));
CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *m_pDoc->GetRepeatRowRange(nSecondTab));
// Tabs have to be adjusted since the order of the tabs is inverted in the new document
std::vector<ScRange*> aScRanges
= { &aFirstPrintRange, &aFirstRepeatColRange, &aFirstRepeatRowRange,
&aSecondPrintRange, &aSecondRepeatColRange, &aSecondRepeatRowRange }; for (size_t i = 0; i < aScRanges.size(); i++)
{ const SCTAB nTab = i >= 3 ? nFirstTab : nSecondTab;
aScRanges[i]->aStart.SetTab(nTab);
aScRanges[i]->aEnd.SetTab(nTab);
}
// Without the fix in place, no print ranges and col/row repetitions would be present
CPPUNIT_ASSERT_EQUAL(aFirstPrintRange, *aScDocument.GetPrintRange(nSecondTab, 0));
CPPUNIT_ASSERT_EQUAL(aFirstRepeatColRange, *aScDocument.GetRepeatColRange(nSecondTab));
CPPUNIT_ASSERT_EQUAL(aFirstRepeatRowRange, *aScDocument.GetRepeatRowRange(nSecondTab));
CPPUNIT_ASSERT_EQUAL(aSecondPrintRange, *aScDocument.GetPrintRange(nFirstTab, 0));
CPPUNIT_ASSERT_EQUAL(aSecondRepeatColRange, *aScDocument.GetRepeatColRange(nFirstTab));
CPPUNIT_ASSERT_EQUAL(aSecondRepeatRowRange, *aScDocument.GetRepeatRowRange(nFirstTab));
CPPUNIT_TEST_FIXTURE(Test, testTdf113027)
{ // Insert some sheets including a whitespace in their name and switch the grammar to R1C1
CPPUNIT_ASSERT(m_pDoc->InsertTab(0, u"Sheet 1"_ustr));
CPPUNIT_ASSERT(m_pDoc->InsertTab(1, u"Sheet 2"_ustr));
FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
// Add a formula containing a remote reference, i.e., to another sheet const ScAddress aScAddress(0, 0, 0); static constexpr OUString aFormula = u"='Sheet 2'!RC"_ustr;
m_pDoc->SetString(aScAddress, aFormula);
// Switch from relative to absolute cell reference
ScRefFinder aFinder(aFormula, aScAddress, *m_pDoc, m_pDoc->GetAddressConvention());
aFinder.ToggleRel(0, aFormula.getLength());
// Without the fix in place, this test would have failed with // - Expected: ='Sheet 2'!R1C1 // - Actual : ='Sheet 2'!RC // i.e. the cell reference was not changed from relative to absolute
CPPUNIT_ASSERT_EQUAL(u"='Sheet 2'!R1C1"_ustr, aFinder.GetText());
// Without the fix in place, this would have failed with // - Expected: =(1;2) // - Actual : =(1~2)
OUString aFormula = m_pDoc->GetFormula(0,0,0);
CPPUNIT_ASSERT_EQUAL(u"=(1;2)"_ustr, aFormula);
// Without the fix in place, this would have failed with // - Expected: =A1/100% // - Actual : =A1/1
OUString aFormula = m_pDoc->GetFormula(1,0,0);
CPPUNIT_ASSERT_EQUAL(u"=A1/100%"_ustr, aFormula);
// Without the fix in place, this test would have failed with // - Expected: --1 // - Actual : -1
CPPUNIT_ASSERT_EQUAL(u"--1"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
CPPUNIT_ASSERT_EQUAL(u"---1"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
CPPUNIT_ASSERT_EQUAL(u"+-1"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
CPPUNIT_ASSERT_EQUAL(u"+--1"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
// These are not valid duration inputs
CPPUNIT_ASSERT_EQUAL(u"1:60"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
CPPUNIT_ASSERT_EQUAL(u"1:123"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
CPPUNIT_ASSERT_EQUAL(u"1:1:123"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
// These are valid duration inputs // Without the fix in place, this test would have failed with // - Expected: 02:03:00 AM // - Actual : 0:123
CPPUNIT_ASSERT_EQUAL(u"02:03:00 AM"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
CPPUNIT_ASSERT_EQUAL(u"12:02:03 AM"_ustr, m_pDoc->GetString(ScAddress(0,4,0)));
CPPUNIT_ASSERT_EQUAL(u"02:03:59 AM"_ustr, m_pDoc->GetString(ScAddress(0,5,0)));
m_pDoc->DeleteTab(1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to decrement sheet count.", static_cast<SCTAB>(nStartTabs+1), m_pDoc->GetTableCount());
m_pDoc->DeleteTab(0); // This may fail in case there is only one sheet in the document.
}
CPPUNIT_TEST_FIXTURE(Test, testDataEntries)
{ /** * The 'data entries' data is a list of strings used for suggestions as * the user types in new cell value.
*/
m_pDoc->InsertTab(0, u"Test"_ustr);
std::vector<ScTypedStrData> aEntries;
m_pDoc->GetDataEntries(0, 0, 0, aEntries); // Try at the very top.
// Entries are supposed to be sorted in ascending order, and are all unique.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
std::vector<ScTypedStrData>::const_iterator it = aEntries.begin();
CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
aEntries.clear();
m_pDoc->GetDataEntries(0, m_pDoc->MaxRow(), 0, aEntries); // Try at the very bottom.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aEntries.size());
// Make sure we get the same set of suggestions.
it = aEntries.begin();
CPPUNIT_ASSERT_EQUAL(u"Andy"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_EQUAL(u"Bruce"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_EQUAL(u"Charlie"_ustr, it->GetString());
++it;
CPPUNIT_ASSERT_MESSAGE("The entries should have ended here.", bool(it == aEntries.end()));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testSelectionFunction)
{ /** * Selection function is responsible for displaying quick calculation * results in the status bar.
*/
m_pDoc->InsertTab(0, u"Test"_ustr);
// Insert values into B2:B4.
m_pDoc->SetString(ScAddress(1,1,0), u"=1"_ustr); // formula
m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
// Insert strings into B5:B8.
m_pDoc->SetString(ScAddress(1,4,0), u"A"_ustr);
m_pDoc->SetString(ScAddress(1,5,0), u"B"_ustr);
m_pDoc->SetString(ScAddress(1,6,0), u"=\"C\""_ustr); // formula
m_pDoc->SetString(ScAddress(1,7,0), u"D"_ustr);
for (constauto& rCheck : aChecks)
{ double fRes = 0.0; bool bRes = m_pDoc->GetSelectionFunction(rCheck.meFunc, aPos, aEmpty, fRes);
CPPUNIT_ASSERT_MESSAGE("Failed to fetch selection function result.", bRes);
CPPUNIT_ASSERT_EQUAL(rCheck.mfExpected, fRes);
}
}
// Calculate function across selected sheets.
clearSheet(m_pDoc, 0);
m_pDoc->InsertTab(1, u"Test2"_ustr);
m_pDoc->InsertTab(2, u"Test3"_ustr);
// Set values at B2 and C3 on each sheet.
m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
m_pDoc->SetValue(ScAddress(1,1,1), 4.0);
m_pDoc->SetValue(ScAddress(2,2,1), 8.0);
m_pDoc->SetValue(ScAddress(1,1,2), 16.0);
m_pDoc->SetValue(ScAddress(2,2,2), 32.0);
// Mark B2 and C3 on first sheet.
aRanges.RemoveAll();
aRanges.push_back(ScRange(1,1,0)); // B2
aRanges.push_back(ScRange(2,2,0)); // C3
aMark.MarkFromRangeList(aRanges, true); // Additionally select third sheet.
aMark.SelectTable(2, true);
// Insert cells to A1, A5, B2 and C3.
m_pDoc->SetString(ScAddress(0,0,0), u"California"_ustr);
m_pDoc->SetValue(ScAddress(0,4,0), 1.2);
m_pDoc->SetEditText(ScAddress(1,1,0), u"Boston"_ustr);
m_pDoc->SetFormula(ScAddress(2,2,0), u"=SUM(1,2,3)"_ustr, m_pDoc->GetGrammar());
// Select A1:C5.
ScMarkData aMarkData(m_pDoc->GetSheetLimits());
aMarkData.SetMarkArea(ScRange(0,0,0,2,4,0));
aMarkData.MarkToMulti(); // TODO : we shouldn't have to do this.
// verify note
CPPUNIT_ASSERT_MESSAGE("There should be a note in A1 destDocument", pDestDoc->HasNote(ScAddress(0, 0, 0)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("The notes content should be the same on both documents",
m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText(), pDestDoc->GetNote(ScAddress(0, 0, 0))->GetText());
SCCOL nCol;
SCROW nRow;
size_t i = 0; for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
{ if (i >= nCheckCount)
{
cerr << "hit invalid check " << i << " of " << nCheckCount << endl;
CPPUNIT_FAIL("Iterator claims there is more data than there should be."); returnfalse;
}
// Set values in first two columns, to ensure allocation of those columns.
m_pDoc->SetValue(ScAddress(0,1,0), 1);
m_pDoc->SetValue(ScAddress(1,1,0), 2);
constexpr SCCOL allocatedColsCount = 2;
assert( allocatedColsCount >= INITIALCOLCOUNT );
CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
// Make entire second row and third row bold.
ScPatternAttr boldAttr(m_pDoc->getCellAttributeHelper());
boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 2, 0, boldAttr);
// That shouldn't need allocating more columns, just changing the default attribute.
CPPUNIT_ASSERT_EQUAL(allocatedColsCount, m_pDoc->GetAllocatedColumnsCount(0));
vcl::Font aFont; const ScPatternAttr* pattern = m_pDoc->GetPattern(m_pDoc->MaxCol(), 1, 0);
pattern->fillFontOnly(aFont);
CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeightMaybeAskConfig());
// The default pattern is the default style, which can be edited by the user. // As such iterators should not ignore it by default, because it might contain // some attributes set.
// That shouldn't need allocating more columns, just changing column flags.
CPPUNIT_ASSERT_EQUAL(INITIALCOLCOUNT, m_pDoc->GetAllocatedColumnsCount(0)); // But the flags are changed.
CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), m_pDoc->GetLastChangedColFlagsWidth(0));
ScSingleRefData& rRef = *pToken->GetSingleRef(); if (!rRef.IsDeleted())
{
cerr << "Deleted reference is expected, but it's still a valid reference." << endl; returnfalse;
}
returntrue;
}
}
CPPUNIT_TEST_FIXTURE(Test, testCellBroadcaster)
{ /** * More direct test for cell broadcaster management, used to track formula * dependencies.
*/
CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, u"foo"_ustr));
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1. double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
CPPUNIT_ASSERT_EQUAL(0.0, val);
const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
// Change the value of A1 and make sure that B1 follows.
m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
val = m_pDoc->GetValue(ScAddress(1,0,0));
CPPUNIT_ASSERT_EQUAL(1.23, val);
// Move column A down 5 cells. Make sure B1 now references A6, not A1.
m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
// Make sure the broadcaster has also moved.
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
// Set new value to A6 and make sure B1 gets updated.
m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
val = m_pDoc->GetValue(ScAddress(1,0,0));
CPPUNIT_ASSERT_EQUAL(45.6, val);
// Move column A up 3 cells, and make sure B1 now references A3, not A6.
m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
// The broadcaster should also have been relocated from A6 to A3.
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
// Insert cells over A1:A10 and shift cells to right.
m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
// Delete formula in C2, which should remove the broadcaster in B3.
pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
clearRange(m_pDoc, ScRange(ScAddress(2,0,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
// Clear everything and start over.
clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr); // B1 depends on A1.
pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
// While column A is still empty, move column A down 2 cells. This should // move the broadcaster from A1 to A3.
m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
// Move it back while column A is still empty.
m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
// Clear everything again
clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
// B1:B3 depends on A1:A3
m_pDoc->SetString(ScAddress(1,0,0), u"=A1"_ustr);
m_pDoc->SetString(ScAddress(1,1,0), u"=A2"_ustr);
m_pDoc->SetString(ScAddress(1,2,0), u"=A3"_ustr);
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
// Insert Rows at row 2, down 5 rows.
m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
// Broadcasters in A2 and A3 should shift down by 5 rows.
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
// B2 and B3 should reference shifted cells.
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
// Delete cells with broadcasters.
m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
// References in B2 and B3 should be invalid.
CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
// Clear everything again
clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
{ // Switch to R1C1 to make it easier to input relative references in multiple cells.
FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
// Have B1:B20 reference A1:A20.
val = 0.0; for (SCROW i = 0; i < 20; ++i)
{
m_pDoc->SetValue(ScAddress(0,i,0), val++);
m_pDoc->SetString(ScAddress(1,i,0), u"=RC[-1]"_ustr);
}
}
// Ensure that the formula cells show correct values, and the referenced // cells have broadcasters.
val = 0.0; for (SCROW i = 0; i < 20; ++i, ++val)
{
CPPUNIT_ASSERT_EQUAL(val, m_pDoc->GetValue(ScAddress(1,i,0)));
pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
}
// Delete formula cells in B2:B19.
clearRange(m_pDoc, ScRange(1,1,0,1,18,0)); // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0))); for (SCROW i = 1; i <= 18; ++i)
{
pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
}
// Clear everything again
clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
// Change the value of A1 and make sure everyone follows suit.
m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
// Insert a column at column B.
m_pDoc->InsertCol(ScRange(1,0,0,1,m_pDoc->MaxRow(),0));
pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
// Change the value of A1 again.
m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testFuncParam)
{
CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
m_pDoc->InsertTab (0, u"foo"_ustr));
// First, the normal case, with no missing parameters.
m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;2;3)"_ustr);
m_pDoc->CalcFormulaTree(false, false); double val = m_pDoc->GetValue(0, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 2.0, val);
// Now function with missing parameters. Missing values should be treated // as zeros.
m_pDoc->SetString(0, 0, 0, u"=AVERAGE(1;;;)"_ustr);
m_pDoc->CalcFormulaTree(false, false);
val = m_pDoc->GetValue(0, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 0.25, val);
// Conversion of string to numeric argument.
m_pDoc->SetString(0, 0, 0, u"=\"\"+3"_ustr); // empty string
m_pDoc->SetString(0, 1, 0, u"=\" \"+3"_ustr); // only blank
m_pDoc->SetString(0, 2, 0, u"=\" 4 \"+3"_ustr); // number in blanks
m_pDoc->SetString(0, 3, 0, u"=\" x \"+3"_ustr); // non-numeric
m_pDoc->SetString(0, 4, 0, u"=\"4.4\"+3"_ustr); // locale dependent
OUString aVal;
ScCalcConfig aConfig;
// With "Convert also locale dependent" and "Empty string as zero"=True option.
aConfig.meStringConversion = ScCalcConfig::StringConversion::LOCALE;
aConfig.mbEmptyStringAsZero = true;
m_pDoc->SetCalcConfig(aConfig);
m_pDoc->CalcAll();
val = m_pDoc->GetValue(0, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
val = m_pDoc->GetValue(0, 1, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 3.0, val);
val = m_pDoc->GetValue(0, 2, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.0, val);
aVal = m_pDoc->GetString( 0, 3, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("incorrect result", u"#VALUE!"_ustr, aVal);
val = m_pDoc->GetValue(0, 4, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("incorrect result", 7.4, val);
// Make sure the index lookup does the right thing. for (constauto& rName : aNames)
{ const ScRangeData* p = pNewRanges->findByIndex(rName.mnIndex);
CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
OUString aName = p->GetName();
CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(rName.mpName));
}
// Test usage in formula expression.
m_pDoc->SetString (1, 0, 0, u"=A1/Divisor"_ustr);
m_pDoc->CalcAll();
// Test copy-ability of range names.
std::unique_ptr<ScRangeName> pCopiedRanges(new ScRangeName(*pNewRanges));
m_pDoc->SetRangeName(std::move(pCopiedRanges)); // Make sure the index lookup still works. for (constauto& rName : aNames)
{ const ScRangeData* p = m_pDoc->GetRangeName()->findByIndex(rName.mnIndex);
CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
OUString aName = p->GetName();
CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(rName.mpName));
}
// Test using another-sheet-local name, scope Sheet1.
ScRangeData* pLocal1 = new ScRangeData( *m_pDoc, u"local1"_ustr, ScAddress(0,0,0));
ScRangeData* pLocal2 = new ScRangeData( *m_pDoc, u"local2"_ustr, u"$Sheet1.$A$1"_ustr);
ScRangeData* pLocal3 = new ScRangeData( *m_pDoc, u"local3"_ustr, u"Sheet1.$A$1"_ustr);
ScRangeData* pLocal4 = new ScRangeData( *m_pDoc, u"local4"_ustr, u"$A$1"_ustr); // implicit relative sheet reference
std::unique_ptr<ScRangeName> pLocalRangeName1(new ScRangeName);
pLocalRangeName1->insert(pLocal1);
pLocalRangeName1->insert(pLocal2);
pLocalRangeName1->insert(pLocal3);
pLocalRangeName1->insert(pLocal4);
m_pDoc->SetRangeName(0, std::move(pLocalRangeName1));
CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (1, u"Sheet2"_ustr));
// Use other-sheet-local name of Sheet1 on Sheet2.
ScAddress aPos(1,0,1);
OUString aFormula(u"=Sheet1.local1+Sheet1.local2+Sheet1.local3+Sheet1.local4"_ustr);
m_pDoc->SetString(aPos, aFormula);
OUString aString = m_pDoc->GetFormula(1,0,1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("formula string should be equal", aFormula, aString); double fValue = m_pDoc->GetValue(aPos);
ASSERT_DOUBLES_EQUAL_MESSAGE("value should be 4 times Sheet1.A1", 404.0, fValue);
m_pDoc->DeleteTab(1);
m_pDoc->SetRangeName(0,nullptr); // Delete the names.
m_pDoc->SetRangeName(nullptr); // Delete the names.
m_pDoc->DeleteTab(0);
}
struct AllZeroMatrix
{ voidoperator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
}
};
struct PartiallyFilledZeroMatrix
{ voidoperator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of numeric type", int(ScMatValType::Value), static_cast<int>(rVal.nType)); if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
{
ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be 3.0", 3.0, rVal.fVal);
} else
{
ASSERT_DOUBLES_EQUAL_MESSAGE("element value must be zero", 0.0, rVal.fVal);
}
}
};
struct AllEmptyMatrix
{ voidoperator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
}
};
struct PartiallyFilledEmptyMatrix
{ voidoperator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
{ if (nCol == 1 && nRow == 1)
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of boolean type", int(ScMatValType::Boolean), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", 1.0, rVal.fVal);
} elseif (nCol == 4 && nRow == 5)
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::Value), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("element value is not what is expected", -12.5, rVal.fVal);
} elseif (nCol == 8 && nRow == 2)
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of value type", int(ScMatValType::String), static_cast<int>(rVal.nType));
CPPUNIT_ASSERT_EQUAL_MESSAGE("element value is not what is expected", u"Test"_ustr, rVal.aStr.getString());
} elseif (nCol == 8 && nRow == 11)
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty path type", int(ScMatValType::EmptyPath), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
} else
{
CPPUNIT_ASSERT_EQUAL_MESSAGE("element is not of empty type", int(ScMatValType::Empty), static_cast<int>(rVal.nType));
ASSERT_DOUBLES_EQUAL_MESSAGE("value of \"empty\" element is expected to be zero", 0.0, rVal.fVal);
}
}
};
// First, test the zero matrix type.
pMat = new ScMatrix(0, 0, 0.0);
SCSIZE nC, nR;
pMat->GetDimensions(nC, nR);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix is not empty", SCSIZE(0), nR);
pMat->Resize(4, 10, 0.0);
pMat->GetDimensions(nC, nR);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(4), nC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nR);
CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
!pMat->And());
CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
!pMat->Or());
// Resizing into a larger matrix should fill the void space with zeros.
checkMatrixElements<AllZeroMatrix>(*pMat);
pMat->FillDouble(3.0, 1, 2, 2, 8);
checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
!pMat->And());
CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
pMat->Or());
pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
pMat->And());
CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
pMat->Or());
// Test the AND and OR evaluations.
pMat = new ScMatrix(2, 2, 0.0);
// Only some of the elements are non-zero.
pMat->PutBoolean(true, 0, 0);
pMat->PutDouble(1.0, 1, 1);
CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
// All of the elements are non-zero.
pMat->PutBoolean(true, 0, 1);
pMat->PutDouble(2.3, 1, 0);
CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
// Now test the empty matrix type.
pMat = new ScMatrix(10, 20);
pMat->GetDimensions(nC, nR);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(10), nC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("matrix size is not as expected", SCSIZE(20), nR);
checkMatrixElements<AllEmptyMatrix>(*pMat);
// Max and min values.
pMat = new ScMatrix(2, 2, 0.0);
pMat->PutDouble(-10, 0, 0);
pMat->PutDouble(-12, 0, 1);
pMat->PutDouble(-8, 1, 0);
pMat->PutDouble(-25, 1, 1);
CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
pMat->PutString(rPool.intern(u"Test"_ustr), 0, 0);
CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
pMat->PutBoolean(true, 0, 0);
CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
pMat = new ScMatrix(2, 2, 10.0);
pMat->PutBoolean(false, 0, 0);
pMat->PutDouble(12.5, 1, 1);
CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
// Convert matrix into a linear double array. String elements become NaN // and empty elements become 0.
pMat = new ScMatrix(3, 3);
pMat->PutDouble(2.5, 0, 0);
pMat->PutDouble(1.2, 0, 1);
pMat->PutString(rPool.intern(u"A"_ustr), 1, 1);
pMat->PutDouble(2.3, 2, 1);
pMat->PutDouble(-20, 2, 2);
// Insert the source values in A1:A2.
m_pDoc->SetString(0, 0, 0, u"=1/0"_ustr);
m_pDoc->SetValue( 0, 1, 0, 1.0);
// Create a matrix formula in B3:B4 referencing A1:A2 and doing a greater // than comparison on it's values. Error value must be propagated.
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(1, 2, 1, 3, aMark, u"=A1:A2>0"_ustr);
CPPUNIT_TEST_FIXTURE(Test, testMatrixEditable)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
m_pDoc->InsertTab(0, u"Test"_ustr);
// Values in A1:B1.
m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
// A2 is a normal formula.
m_pDoc->SetString(ScAddress(0,1,0), u"=5"_ustr);
// A3:A4 is a matrix.
ScRange aMatRange(0,2,0,0,3,0);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SetMarkArea(aMatRange);
m_pDoc->InsertMatrixFormula(0, 2, 0, 3, aMark, u"=TRANSPOSE(A1:B1)"_ustr);
// Check their values.
CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,2,0)));
CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
// Make sure A3:A4 is a matrix.
ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should be matrix origin.",
ScMatrixMode::Formula, pFC->GetMatrixFlag());
pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should be matrix reference.",
ScMatrixMode::Reference, pFC->GetMatrixFlag());
// Check to make sure A3:A4 combined is editable.
ScEditableTester aTester;
aTester.TestSelection(*m_pDoc, aMark);
CPPUNIT_ASSERT(aTester.IsEditable());
CPPUNIT_TEST_FIXTURE(Test, testSheetCopy)
{
m_pDoc->InsertTab(0, u"TestTab"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
// Insert text in A1.
m_pDoc->SetString(ScAddress(0,0,0), u"copy me"_ustr);
SCROW nRow1, nRow2; bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
// insert a note
ScAddress aAdrA1 (0,2,0); // empty cell content.
ScPostIt *pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
pNoteA1->SetText(aAdrA1, u"Hello world in A3"_ustr);
// Copy and test the result.
m_pDoc->CopyTab(0, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
CPPUNIT_ASSERT_MESSAGE("There should be note on A3 in new sheet", m_pDoc->HasNote(ScAddress(0,2,1)));
CPPUNIT_ASSERT_EQUAL(u"copy me"_ustr, m_pDoc->GetString(ScAddress(0,0,1)));
// Check the copied edit cells. const EditTextObject* pEditObj = m_pDoc->GetEditText(ScAddress(1,0,1));
CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B1.", pEditObj);
CPPUNIT_ASSERT_EQUAL(u"Edit 1"_ustr, pEditObj->GetText(0));
pEditObj = m_pDoc->GetEditText(ScAddress(1,1,1));
CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B2.", pEditObj);
CPPUNIT_ASSERT_EQUAL(u"Edit 2"_ustr, pEditObj->GetText(0));
pEditObj = m_pDoc->GetEditText(ScAddress(1,2,1));
CPPUNIT_ASSERT_MESSAGE("There should be an edit cell in B3.", pEditObj);
CPPUNIT_ASSERT_EQUAL(u"Edit 3"_ustr, pEditObj->GetText(0));
m_pDoc->DeleteTab(1);
m_pDoc->SetRowHidden(5, 10, 0, true);
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
// Copy the sheet once again.
m_pDoc->CopyTab(0, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
m_pDoc->DeleteTab(1);
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testSheetMove)
{
m_pDoc->InsertTab(0, u"TestTab1"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
SCROW nRow1, nRow2; bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
//test if inserting before another sheet works
m_pDoc->InsertTab(0, u"TestTab2"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
// Move and test the result.
m_pDoc->MoveTab(0, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("copied sheet should also have all rows visible as the original.", m_pDoc->MaxRow(), nRow2);
OUString aName;
m_pDoc->GetName(0, aName);
CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab1"_ustr, aName);
m_pDoc->SetRowHidden(5, 10, 0, true);
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
// Move the sheet once again.
m_pDoc->MoveTab(1, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 0 - 4 should be visible", SCROW(4), nRow2);
bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(5), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 5 - 10 should be hidden", SCROW(10), nRow2);
bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", SCROW(11), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 11 - maxrow should be visible", m_pDoc->MaxRow(), nRow2);
m_pDoc->GetName(0, aName);
CPPUNIT_ASSERT_EQUAL_MESSAGE( "sheets should have changed places", u"TestTab2"_ustr, aName);
m_pDoc->DeleteTab(1);
m_pDoc->DeleteTab(0);
}
// Totally empty sheet should be rightfully considered empty in all accounts.
CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 100, 100, 0));
CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
// Now, set borders in some cells...
::editeng::SvxBorderLine aLine(nullptr, 50, SvxBorderLineStyle::SOLID);
SvxBoxItem aBorderItem(ATTR_BORDER);
aBorderItem.SetLine(&aLine, SvxBoxItemLine::LEFT);
aBorderItem.SetLine(&aLine, SvxBoxItemLine::RIGHT); for (SCROW i = 0; i < 100; ++i) // Set borders from row 1 to 100.
m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
// Now the sheet is considered non-empty for printing purposes, but still // be empty in all the other cases.
CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
!m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
// Adding a real cell content should turn the block non-empty.
m_pDoc->SetString(0, 0, 0, u"Some text"_ustr);
CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
!m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
// TODO: Add more tests for normal data area calculation.
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testStreamValid)
{ /** * Make sure the sheet streams are invalidated properly.
*/
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->InsertTab(1, u"Sheet2"_ustr);
m_pDoc->InsertTab(2, u"Sheet3"_ustr);
m_pDoc->InsertTab(3, u"Sheet4"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", static_cast<SCTAB>(4), m_pDoc->GetTableCount());
// Put values into Sheet1.
m_pDoc->SetString(0, 0, 0, a1);
m_pDoc->SetString(0, 1, 0, a2);
test = m_pDoc->GetString(0, 0, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A1", test, a1);
test = m_pDoc->GetString(0, 1, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet1.A2", test, a2);
// Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
m_pDoc->SetString(0, 0, 1, u"=Sheet1.A1"_ustr);
m_pDoc->SetString(0, 1, 1, u"=Sheet1.A2"_ustr);
m_pDoc->SetString(0, 0, 2, u"=Sheet1.A1"_ustr);
m_pDoc->SetString(0, 0, 3, u"=Sheet1.A2"_ustr);
test = m_pDoc->GetString(0, 0, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A1", test, a1);
test = m_pDoc->GetString(0, 1, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet2.A2", test, a2);
test = m_pDoc->GetString(0, 0, 2);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a1);
test = m_pDoc->GetString(0, 0, 3);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected value in Sheet3.A1", test, a2);
// Set all sheet streams valid after all the initial cell values are in // place. In reality we need to have real XML streams stored in order to // claim they are valid, but we are just testing the flag values here.
m_pDoc->SetStreamValid(0, true);
m_pDoc->SetStreamValid(1, true);
m_pDoc->SetStreamValid(2, true);
m_pDoc->SetStreamValid(3, true);
CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
// Now, insert a new row at row 2 position on Sheet1. This will move cell // A2 downward but cell A1 remains unmoved.
m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 1, 2);
test = m_pDoc->GetString(0, 0, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell A1 should not have moved.", test, a1);
test = m_pDoc->GetString(0, 3, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the old cell A2 should now be at A4.", test, a2);
ScRefCellValue aCell;
aCell.assign(*m_pDoc, ScAddress(0,1,0));
CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
aCell.assign(*m_pDoc, ScAddress(0,2,0));
CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
// After the move, Sheet1, Sheet2, and Sheet4 should have their stream // invalidated, whereas Sheet3's stream should still be valid.
CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
sal_uInt32 n = pFuncMgr->getCount(); for (sal_uInt32 i = 0; i < n; ++i)
{ const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
sal_uInt32 nFuncCount = pCat->getCount(); for (sal_uInt32 j = 0; j < nFuncCount; ++j)
{ const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", OUString::createFromAscii(aTests[i].Functions[j]), pFunc->getFunctionName());
}
}
}
CPPUNIT_TEST_FIXTURE(Test, testGraphicsInGroup)
{
m_pDoc->InsertTab(0, u"TestTab"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
SCROW nRow1, nRow2; bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
m_pDoc->InitDrawLayer();
ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
SdrPage* pPage = pDrawLayer->GetPage(0);
CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
{ //Add a square
tools::Rectangle aOrigRect(2,2,100,100);
rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aOrigRect);
pPage->InsertObject(pObj.get()); const tools::Rectangle &rNewRect = pObj->GetLogicRect();
CPPUNIT_ASSERT_EQUAL_MESSAGE("must have equal position and size", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
ScDrawLayer::SetPageAnchored(*pObj);
//Use a range of rows guaranteed to include all of the square
m_pDoc->ShowRows(0, 100, 0, false);
m_pDoc->SetDrawPageSize(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
m_pDoc->ShowRows(0, 100, 0, true);
m_pDoc->SetDrawPageSize(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when page anchored", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Hiding should not change the logic rectangle", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
CPPUNIT_ASSERT_MESSAGE("Hiding should make invisible", !pObj->IsVisible());
m_pDoc->ShowRows(0, 100, 0, true);
m_pDoc->SetDrawPageSize(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should not change when cell anchored", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
CPPUNIT_ASSERT_MESSAGE("Show should make visible", pObj->IsVisible());
}
{ // Add a circle.
tools::Rectangle aOrigRect(10,10,210,210); // 200 x 200
rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
pPage->InsertObject(pObj.get()); const tools::Rectangle& rNewRect = pObj->GetLogicRect();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell anchored. Not good.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
// Insert 2 rows at the top. This should push the circle object down.
m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
m_pDoc->SetDrawPageSize(0);
// Make sure the size of the circle is still identical.
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of the circle has changed, but shouldn't!",
aOrigRect.GetSize(), rNewRect.GetSize());
// Delete 2 rows at the top. This should bring the circle object to its original position.
m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
m_pDoc->SetDrawPageSize(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Failed to move back to its original position.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
}
{ // Add a line.
basegfx::B2DPolygon aTempPoly;
Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
tools::Rectangle aOrigRect(10,200,110,300); // 100 x 100
aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
rtl::Reference<SdrPathObj> pObj = new SdrPathObj(*pDrawLayer, SdrObjKind::Line, basegfx::B2DPolyPolygon(aTempPoly));
pObj->NbcSetLogicRect(aOrigRect);
pPage->InsertObject(pObj.get()); const tools::Rectangle& rNewRect = pObj->GetLogicRect();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size differ.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size changed when cell-anchored. Not good.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
// Insert 2 rows at the top and delete them immediately.
m_pDoc->InsertRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 2);
m_pDoc->SetDrawPageSize(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size of a line object changed after row insertion and removal.", const_cast<const tools::Rectangle &>(aOrigRect), rNewRect);
sal_Int32 n = pObj->GetPointCount();
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 2 points in a line object.", static_cast<sal_Int32>(2), n);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
aStartPos, pObj->GetPoint(0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Line shape has changed.",
aEndPos, pObj->GetPoint(1));
}
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testGraphicsOnSheetMove)
{
m_pDoc->InsertTab(0, u"Tab1"_ustr);
m_pDoc->InsertTab(1, u"Tab2"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 2 sheets to begin with", static_cast<SCTAB>(2), m_pDoc->GetTableCount());
// Insert an object.
tools::Rectangle aObjRect(2,2,100,100);
rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
pPage->InsertObject(pObj.get());
ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", static_cast<size_t>(1), pPage->GetObjCount());
const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
pPage = pDrawLayer->GetPage(1);
CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", static_cast<size_t>(0), pPage->GetObjCount());
// Insert a new sheet at left-end, and make sure the object has moved to // the 2nd page.
m_pDoc->InsertTab(0, u"NewTab"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", static_cast<SCTAB>(3), m_pDoc->GetTableCount());
pPage = pDrawLayer->GetPage(0);
CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
pPage = pDrawLayer->GetPage(1);
CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
pPage = pDrawLayer->GetPage(2);
CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("3rd sheet should have no object.", size_t(0), pPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
// Now, delete the sheet that just got inserted. The object should be back // on the 1st sheet.
m_pDoc->DeleteTab(0);
pPage = pDrawLayer->GetPage(0);
CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have one object.", size_t(1), pPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Size and position of the object shouldn't change.",
aObjRect, pObj->GetLogicRect());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maStart.Tab());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(0), pData->maEnd.Tab());
// Move the 1st sheet to the last position.
m_pDoc->MoveTab(0, 1);
pPage = pDrawLayer->GetPage(0);
CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("1st sheet should have no object.", size_t(0), pPage->GetObjCount());
pPage = pDrawLayer->GetPage(1);
CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet should have one object.", size_t(1), pPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maStart.Tab());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(1), pData->maEnd.Tab());
// Copy the 2nd sheet, which has one drawing object to the last position.
m_pDoc->CopyTab(1, 2);
pPage = pDrawLayer->GetPage(2);
CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Copied sheet should have one object.", size_t(1), pPage->GetObjCount());
pObj = pPage->GetObj(0);
CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
pData = ScDrawLayer::GetObjData(pObj.get());
CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maStart.Tab());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sheet ID in cell anchor data!", SCTAB(2), pData->maEnd.Tab());
CPPUNIT_TEST_FIXTURE(Test, testToggleRefFlag)
{ /** * Test toggling relative/absolute flag of cell and cell range references. * This corresponds with hitting Shift-F4 while the cursor is on a formula * cell.
*/ // In this test, there is no need to insert formula string into a cell in // the document, as ScRefFinder does not depend on the content of the // document except for the sheet names.
{ // Excel R1C1: Selection at the end of the formula string and does not // overlap the formula string at all (inspired by fdo#39135).
OUString aFormula(u"=R1C1"_ustr);
ScAddress aPos(1, 1, 0);
ScRefFinder aFinder(aFormula, aPos, *m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
// Original
CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
// Make the column relative.
sal_Int32 n = aFormula.getLength();
aFinder.ToggleRel(n, n);
aFormula = aFinder.GetText();
CPPUNIT_ASSERT_EQUAL(u"=R1C[-1]"_ustr, aFormula);
// Make the row relative.
n = aFormula.getLength();
aFinder.ToggleRel(n, n);
aFormula = aFinder.GetText();
CPPUNIT_ASSERT_EQUAL(u"=R[-1]C1"_ustr, aFormula);
// Make both relative.
n = aFormula.getLength();
aFinder.ToggleRel(n, n);
aFormula = aFinder.GetText();
CPPUNIT_ASSERT_EQUAL(u"=R[-1]C[-1]"_ustr, aFormula);
// Back to the original.
n = aFormula.getLength();
aFinder.ToggleRel(n, n);
aFormula = aFinder.GetText();
CPPUNIT_ASSERT_EQUAL(u"=R1C1"_ustr, aFormula);
}
// Original
CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
// Set the cursor over the 'A1' part and toggle.
aFinder.ToggleRel(2, 2);
aFormula = aFinder.GetText();
CPPUNIT_ASSERT_EQUAL(u"=$A$1+4"_ustr, aFormula);
// Populate cells. for (SCROW i = 0; i < nRows; ++i) for (SCCOL j = 0; j < nCols; ++j) if (aData[i][j])
m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
// perform the query.
m_pDoc->Query(0, aParam, true);
//control output
SCROW nRow1, nRow2; bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(2), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2 & 3 should be hidden", SCROW(3), nRow2);
// Remove filtering.
rEntry.Clear();
m_pDoc->Query(0, aParam, true);
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
// Filter for non-empty cells by column C.
rEntry.bDoQuery = true;
rEntry.nField = 2;
rEntry.SetQueryByNonEmpty();
m_pDoc->Query(0, aParam, true);
// only row 3 should be hidden. The rest should be visible.
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 1 & 2 should be visible.", SCROW(1), nRow2);
bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be hidden.", SCROW(2), nRow2);
bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", SCROW(3), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 4 and down should be visible.", m_pDoc->MaxRow(), nRow2);
// Now, filter for empty cells by column C.
rEntry.SetQueryByEmpty();
m_pDoc->Query(0, aParam, true);
// Now, only row 1 and 3, and 6 and down should be visible.
bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be visible.", SCROW(0), nRow2);
bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 2 should be hidden.", SCROW(1), nRow2);
bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("row 3 should be visible.", SCROW(2), nRow2);
bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(3), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 4 & 5 should be hidden.", SCROW(4), nRow2);
bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", SCROW(5), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 6 and down should be all visible.", m_pDoc->MaxRow(), nRow2);
// Make sure the hour:minute:second format is really applied.
CPPUNIT_ASSERT_EQUAL(u"1736:39:00"_ustr, m_pDoc->GetString(ScAddress(0,1,0))); // A2
CPPUNIT_ASSERT_EQUAL(u"6360:00:00"_ustr, m_pDoc->GetString(ScAddress(0,2,0))); // A3
// Filter by the A2 value. Only A1 and A2 should be visible.
ScQueryParam aParam;
pDBData->GetQueryParam(aParam);
ScQueryEntry& rEntry = aParam.GetEntry(0);
rEntry.bDoQuery = true;
rEntry.nField = 0;
rEntry.eOp = SC_EQUAL;
rEntry.GetQueryItem().maString = m_pDoc->GetSharedStringPool().intern(u"1736:39:00"_ustr);
rEntry.GetQueryItem().meType = ScQueryEntry::ByString;
pDBData->SetQueryParam(aParam);
// perform the query.
m_pDoc->Query(0, aParam, true);
// A1:A2 should be visible while A3 should be filtered out.
CPPUNIT_ASSERT_MESSAGE("A1 should be visible.", !m_pDoc->RowFiltered(0,0));
CPPUNIT_ASSERT_MESSAGE("A2 should be visible.", !m_pDoc->RowFiltered(1,0));
CPPUNIT_ASSERT_MESSAGE("A3 should be filtered out.", m_pDoc->RowFiltered(2,0));
// Fill 1st column with 0-199, 2nd with 1-200, 3rd with "1000"-"1199", 4th with "1001-1200" // (the pairs are off by one to each other to check filtering out a value filters out // only the relevant column). for(SCROW i = 0; i < nRows; ++i)
{
m_pDoc->SetValue(0, i + 1, 0, i);
m_pDoc->SetValue(1, i + 1, 0, i+1);
m_pDoc->SetString(2, i + 1, 0, "val" + OUString::number(i+1000));
m_pDoc->SetString(3, i + 1, 0, "val" + OUString::number(i+1000+1));
}
//create the query param
ScQueryParam aParam;
pDBData->GetQueryParam(aParam);
ScQueryEntry& rEntry0 = aParam.GetEntry(0);
rEntry0.bDoQuery = true;
rEntry0.nField = 0;
rEntry0.eOp = SC_EQUAL;
rEntry0.GetQueryItems().resize(nRows);
ScQueryEntry& rEntry1 = aParam.GetEntry(1);
rEntry1.bDoQuery = true;
rEntry1.nField = 1;
rEntry1.eOp = SC_EQUAL;
rEntry1.GetQueryItems().resize(nRows);
ScQueryEntry& rEntry2 = aParam.GetEntry(2);
rEntry2.bDoQuery = true;
rEntry2.nField = 2;
rEntry2.eOp = SC_EQUAL;
rEntry2.GetQueryItems().resize(nRows);
ScQueryEntry& rEntry3 = aParam.GetEntry(3);
rEntry3.bDoQuery = true;
rEntry3.nField = 3;
rEntry3.eOp = SC_EQUAL;
rEntry3.GetQueryItems().resize(nRows); // Set up autofilter to select all values except one in each column. // This should only filter out 2nd, 3rd, 6th and 7th rows. for( int i = 0; i < nRows; ++i )
{ if(i!= 1)
rEntry0.GetQueryItems()[i].mfVal = i; if(i!= 2)
rEntry1.GetQueryItems()[i].mfVal = i + 1; if(i!= 5)
{
rEntry2.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000));
rEntry2.GetQueryItems()[i].meType = ScQueryEntry::ByString;
} if(i!= 6)
{
rEntry3.GetQueryItems()[i].maString = m_pDoc->GetSharedStringPool().intern("val" + OUString::number(i+1000+1));
rEntry3.GetQueryItems()[i].meType = ScQueryEntry::ByString;
}
} // add queryParam to database range.
pDBData->SetQueryParam(aParam);
// perform the query.
m_pDoc->Query(0, aParam, true);
// check that only rows with filtered out values are hidden, and not rows that share // a value in a different column
SCROW nRow1, nRow2;
CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden", m_pDoc->RowHidden(2, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 4 should be hidden", m_pDoc->RowHidden(3, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 5 should be visible", !m_pDoc->RowHidden(4, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 6 should be visible", !m_pDoc->RowHidden(5, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 7 should be hidden", m_pDoc->RowHidden(6, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 8 should be hidden", m_pDoc->RowHidden(7, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_MESSAGE("row 9 should be visible", !m_pDoc->RowHidden(8, 0, &nRow1, &nRow2));
// Remove filtering.
rEntry0.Clear();
rEntry1.Clear();
rEntry2.Clear();
m_pDoc->Query(0, aParam, true);
CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !m_pDoc->RowHidden(0, 0, &nRow1, &nRow2));
CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("All rows should be shown.", m_pDoc->MaxRow(), nRow2);
ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
SfxItemSet& rSet = aNewAttrs.GetItemSet();
rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
{ // First insert the string, then the format
m_pDoc->SetString(ScAddress(0,0,0), u"01:20"_ustr);
// Without the fix in place, this test would have failed with // - Expected: 01:20 // - Actual : 20:00
CPPUNIT_ASSERT_EQUAL(u"01:20"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
}
// Without the fix in place, this test would have failed with // - Expected: 10 // - Actual : 192.168.0.10
CPPUNIT_ASSERT_EQUAL(u"10"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
// Without the fix in place, this test would have failed with // - Expected: 66000:00:00 // - Actual : 464:00:00
CPPUNIT_ASSERT_EQUAL(u"66000:00:00"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
ScPatternAttr aNewAttrs(m_pDoc->getCellAttributeHelper());
SfxItemSet& rSet = aNewAttrs.GetItemSet();
rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
{ // First insert the string, then the format
m_pDoc->SetString(ScAddress(0,0,0), u"123.45"_ustr);
// Without the fix in place, this test would have failed with // - Expected: 12.3 // - Actual : 1234.5
CPPUNIT_ASSERT_EQUAL(u"12.3"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
}
// Without the fix in place, this test would have failed with // - Expected: 0.000000006 // - Actual : 6E-09
CPPUNIT_ASSERT_EQUAL(u"0.000000006"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
CPPUNIT_ASSERT_EQUAL(u"6E-10"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
// Without the fix in place, this test would have failed with // - Expected: 2021-6/1 // - Actual : 1905-07-19
CPPUNIT_ASSERT_EQUAL(u"2021-6/1"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
// Overwrite the existing date with the exact same input
m_pDoc->SetString(ScAddress(0,0,0), u"11/7/19"_ustr);
// Without the fix in place, this test would have failed with // - Expected: 2019-11-07 // - Actual : 2011-07-19
CPPUNIT_ASSERT_EQUAL(u"2019-11-07"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
// First entry is for the 'Value' field, and is greater than 5.
ScQueryEntry aEntry = aQueryParam.GetEntry(0);
CPPUNIT_ASSERT(aEntry.bDoQuery);
CPPUNIT_ASSERT_EQUAL(SCCOLROW(0), aEntry.nField);
CPPUNIT_ASSERT_EQUAL(SC_GREATER, aEntry.eOp);
// Second entry is for the 'Tag' field, and is == 'R'.
aEntry = aQueryParam.GetEntry(1);
CPPUNIT_ASSERT(aEntry.bDoQuery);
CPPUNIT_ASSERT_EQUAL(SCCOLROW(1), aEntry.nField);
CPPUNIT_ASSERT_EQUAL(SC_EQUAL, aEntry.eOp);
// perform the query.
m_pDoc->Query(0, aQueryParam, true);
// Only rows 1,8-10 should be visible. bool bFiltered = m_pDoc->RowFiltered(0, 0);
CPPUNIT_ASSERT_MESSAGE("row 1 (header row) should be visible", !bFiltered);
SCROW nRow1 = -1, nRow2 = -1;
bFiltered = m_pDoc->RowFiltered(1, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 2-7 should be filtered out.", bFiltered);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(1), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 2-7 should be filtered out.", SCROW(6), nRow2);
bFiltered = m_pDoc->RowFiltered(7, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("rows 8-10 should be visible.", !bFiltered);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(7), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("rows 8-10 should be visible.", SCROW(9), nRow2);
// perform the query.
m_pDoc->Query(0, aParam, true);
// Dates in rows 2-4 contain '2', row 5 shows 2001 only as 01, and row 6 doesn't contain it at all.
CPPUNIT_ASSERT_MESSAGE("row 2 should be visible", !m_pDoc->RowHidden(1, 0));
CPPUNIT_ASSERT_MESSAGE("row 3 should be visible", !m_pDoc->RowHidden(2, 0));
CPPUNIT_ASSERT_MESSAGE("row 4 should be visible", !m_pDoc->RowHidden(3, 0));
CPPUNIT_ASSERT_MESSAGE("row 5 should be hidden", m_pDoc->RowHidden(4, 0));
CPPUNIT_ASSERT_MESSAGE("row 6 should be hidden", m_pDoc->RowHidden(5, 0));
// Without the fix in place, this test would have failed with // - Expected: =name2 // - Actual : =name1
CPPUNIT_ASSERT_EQUAL(u"=name2"_ustr, aFormula);
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testMergedCells)
{ //test merge and unmerge //TODO: an undo/redo test for this would be a good idea
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->DoMerge(1, 1, 3, 3, 0, false);
SCCOL nEndCol = 1;
SCROW nEndRow = 1;
m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCCOL(3), nEndCol);
CPPUNIT_ASSERT_EQUAL_MESSAGE("did not merge cells", SCROW(3), nEndRow);
ScRange aRange(0,2,0,m_pDoc->MaxCol(),2,0);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SetMarkArea(aRange);
m_xDocShell->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS_BEFORE, true, true);
m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCCOL(3), nEndCol);
CPPUNIT_ASSERT_EQUAL_MESSAGE("did not increase merge area", SCROW(4), nEndRow);
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testRenameTable)
{ //test set rename table //TODO: set name1 and name2 and do an undo to check if name 1 is set now //TODO: also check if new name for table is same as another table
//test case 1 , rename table2 to sheet 1, it should return error
OUString nameToSet = u"Sheet1"_ustr;
ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
//test case 2 , simple rename to check name
nameToSet = "test1";
m_xDocShell->GetDocFunc().RenameTable(0,nameToSet,false,true);
OUString nameJustSet;
m_pDoc->GetName(0,nameJustSet);
CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
//test case 3 , rename again
OUString anOldName;
m_pDoc->GetName(0,anOldName);
nameToSet = "test2";
rDocFunc.RenameTable(0,nameToSet,false,true);
m_pDoc->GetName(0,nameJustSet);
CPPUNIT_ASSERT_EQUAL_MESSAGE("table not renamed", nameToSet, nameJustSet);
//test case 4 , check if undo works
SfxUndoAction* pUndo = new ScUndoRenameTab(*m_xDocShell,0,anOldName,nameToSet);
pUndo->Undo();
m_pDoc->GetName(0,nameJustSet);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct name is not set after undo", nameJustSet, anOldName);
pUndo->Redo();
m_pDoc->GetName(0,nameJustSet);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after redo", nameJustSet, nameToSet); delete pUndo;
m_pDoc->DeleteTab(0);
m_pDoc->DeleteTab(1);
}
CPPUNIT_TEST_FIXTURE(Test, testSetBackgroundColor)
{ //test set background color //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
Color aColor;
//test yellow
aColor=COL_YELLOW;
m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set",
aColor, m_pDoc->GetTabBgColor(0));
Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
aColor = COL_BLUE;
m_xDocShell->GetDocFunc().SetTabBgColor(0,aColor,false, true);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set the second time",
aColor, m_pDoc->GetTabBgColor(0));
//now check for undo
SfxUndoAction* pUndo = new ScUndoTabColor(*m_xDocShell, 0, aOldTabBgColor, aColor);
pUndo->Undo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aOldTabBgColor, m_pDoc->GetTabBgColor(0));
pUndo->Redo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("the correct color is not set after undo", aColor, m_pDoc->GetTabBgColor(0)); delete pUndo;
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(Test, testUpdateReference)
{ //test that formulas are correctly updated during sheet delete //TODO: add tests for relative references, updating of named ranges, ...
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->InsertTab(1, u"Sheet2"_ustr);
m_pDoc->InsertTab(2, u"Sheet3"_ustr);
m_pDoc->InsertTab(3, u"Sheet4"_ustr);
double aValue;
aValue = m_pDoc->GetValue(2,0,2);
ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2,1,2);
ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", 5, aValue);
//test deleting both sheets: one is not directly before the sheet, the other one is
m_pDoc->DeleteTab(0);
aValue = m_pDoc->GetValue(2,0,1);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2,1,1);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", 5, aValue);
m_pDoc->DeleteTab(0);
aValue = m_pDoc->GetValue(2,0,0);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2,1,0);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", 5, aValue);
//test adding two sheets
m_pDoc->InsertTab(0, u"Sheet2"_ustr);
aValue = m_pDoc->GetValue(2,0,1);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2,1,1);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", 5, aValue);
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
aValue = m_pDoc->GetValue(2,0,2);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2,1,2);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", 5, aValue);
//test new DeleteTabs/InsertTabs methods
m_pDoc->DeleteTabs(0, 2);
aValue = m_pDoc->GetValue(2, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2, 1, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", 5, aValue);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 3, aValue);
aValue = m_pDoc->GetValue(2, 1, 2);
ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", 5, aValue);
CPPUNIT_ASSERT_MESSAGE("Search And Replace should succeed", bSuccess);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 3 matching cells.", size_t(3), aMatchedRanges.size());
ScAddress aHit(0,0,0);
CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
aHit.SetRow(2);
CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
aHit.SetRow(4);
CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.Contains(ScRange(aHit)));
CPPUNIT_TEST_FIXTURE(Test, testJumpToPrecedentsDependents)
{ /** * Test to make sure correct precedent / dependent cells are obtained when * preparing to jump to them.
*/ // Precedent is another cell that the cell references, while dependent is // another cell that references it.
m_pDoc->InsertTab(0, u"Test"_ustr);
{ // C1's precedent should be A1:A2,B3.
ScAddress aC1(2, 0, 0);
ScRangeList aRange((ScRange(aC1)));
rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0, 0, 1, 0), aC1));
CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
hasRange(m_pDoc, aRefTokens, ScRange(1, 2, 0), aC1));
}
{ // C2's precedent should be A1 only.
ScAddress aC2(2, 1, 0);
ScRangeList aRange((ScRange(aC2)));
rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.", static_cast<size_t>(1), aRefTokens.size());
CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
hasRange(m_pDoc, aRefTokens, ScRange(0, 0, 0), aC2));
}
{ // A1's dependent should be C1:C2.
ScAddress aA1(0, 0, 0);
ScRangeList aRange((ScRange(aA1)));
rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
CPPUNIT_ASSERT_EQUAL_MESSAGE("C1:C2 should be the only dependent of A1.",
std::vector<ScTokenRef>::size_type(1), aRefTokens.size());
CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
hasRange(m_pDoc, aRefTokens, ScRange(2, 0, 0, 2, 1, 0), aA1));
}
// Without the fix in place, this test would have failed with // - Expected: '1 // - Actual : ''1
CPPUNIT_ASSERT_EQUAL( u"'1"_ustr, m_pDoc->GetString( 0, 0, 0 ) );
// Without the fix in place, this test would have failed with // - Expected: 10 // - Actual : 1
CPPUNIT_ASSERT_EQUAL( 10.0, m_pDoc->GetValue( 1, 0, 0 ) );
// test auto fill user data lists
m_pDoc->SetString( 0, 100, 0, u"January"_ustr );
m_pDoc->Fill( 0, 100, 0, 100, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
CPPUNIT_ASSERT_EQUAL( u"February"_ustr, aTestValue );
aTestValue = m_pDoc->GetString( 0, 102, 0 );
CPPUNIT_ASSERT_EQUAL( u"March"_ustr, aTestValue );
// test that two same user data list entries will not result in incremental fill
m_pDoc->SetString( 0, 101, 0, u"January"_ustr );
m_pDoc->Fill( 0, 100, 0, 101, nullptr, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO ); for ( SCROW i = 102; i <= 103; ++i )
{
aTestValue = m_pDoc->GetString( 0, i, 0 );
CPPUNIT_ASSERT_EQUAL( u"January"_ustr, aTestValue );
}
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// Undo should clear the area except for the top cell.
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT(pUndoMgr);
pUndoMgr->Undo();
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0))); for (SCROW i = 1; i <= 5; ++i)
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0,i,0)));
// Redo should put the serial values back in.
pUndoMgr->Redo();
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0)));
CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,4,0)));
CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
// test that filling formulas vertically up does the right thing for(SCROW nRow = 0; nRow < 10; ++nRow)
m_pDoc->SetValue(100, 100 + nRow, 0, 1);
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#89754, Without the fix in place, this test would have failed with // - Expected: 2012-10-31 // - Actual : 2012-11-01
CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 102, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 103, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"2012-10-31"_ustr, m_pDoc->GetString( 0, 104, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0, 0, 0, 0, m_pDoc->MaxRow(), 0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#58745, Without the fix in place, this test would have failed with // - Expected: 2020-01-31 // - Actual : 2019-01-11
CPPUNIT_ASSERT_EQUAL(u"2020-01-31"_ustr, m_pDoc->GetString(0, 103, 0));
CPPUNIT_ASSERT_EQUAL(u"2020-02-29"_ustr, m_pDoc->GetString(0, 104, 0));
CPPUNIT_ASSERT_EQUAL(u"2020-03-31"_ustr, m_pDoc->GetString(0, 105, 0));
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#37424: Without the fix in place, this test would have failed with // - Expected: 4.4 // - Actual : 5
CPPUNIT_ASSERT_EQUAL( u"4.4"_ustr, m_pDoc->GetString( 0, 64, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"4.5"_ustr, m_pDoc->GetString( 0, 65, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"4.6"_ustr, m_pDoc->GetString( 0, 66, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#105268: Without the fix in place, this test would have failed with // - Expected: 001-001-002 // - Actual : 001-001000
CPPUNIT_ASSERT_EQUAL( u"001-001-002"_ustr, m_pDoc->GetString( 0, 71, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"001-001-003"_ustr, m_pDoc->GetString( 0, 72, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"001-001-004"_ustr, m_pDoc->GetString( 0, 73, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#89998: Without the fix in place, this test would have failed with // - Expected: 2.00% // - Actual : 101.00%
CPPUNIT_ASSERT_EQUAL( u"2.00%"_ustr, m_pDoc->GetString( 0, 81, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"3.00%"_ustr, m_pDoc->GetString( 0, 82, 0 ) );
CPPUNIT_ASSERT_EQUAL( u"4.00%"_ustr, m_pDoc->GetString( 0, 83, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#129606: Without the fix in place, this test would have failed with // - Expected: 6 // - Actual : 6.00000000000001
CPPUNIT_ASSERT_EQUAL( u"6"_ustr, m_pDoc->GetString( 0, 50, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// tdf#151460: Without the fix in place, this test would have failed with // - Expected: 2022-10-01 20:00:00.000 // - Actual : 2022-10-01 19:59:59.999
CPPUNIT_ASSERT_EQUAL( u"2022-10-01 20:00:00.000"_ustr, m_pDoc->GetString( 0, 20, 0 ) );
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// Clear column A for a new test.
clearRange(m_pDoc, ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
m_pDoc->SetRowHidden(0, m_pDoc->MaxRow(), 0, false); // Show all rows.
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
OUString aTestVal(u"Some Text"_ustr);
// Text into cell E5.
m_pDoc->SetString(4, 3, 0, aTestVal);
// put a Note in cell E5
ScAddress rAddr(4, 3, 0);
ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
pNote->SetText(rAddr, u"Hello"_ustr);
CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
// Insert cell at D5. This should shift the string cell to right.
m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
OUString aStr = m_pDoc->GetString(5, 3, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(3, 4, 3, 4, 0));
CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(4, 3, 0));
CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(5, 3, 0));
// Delete cell D5, to shift the text cell back into D5.
m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
aStr = m_pDoc->GetString(4, 3, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have a string cell here.", aTestVal, aStr);
CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(4, 4, 4, 4, 0));
CPPUNIT_ASSERT_MESSAGE("there should be NO note", !m_pDoc->HasNote(5, 3, 0));
CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(4, 3, 0));
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
CPPUNIT_ASSERT(!m_pDoc->HasNotes());
// Check for note's presence in all tables before inserting any notes. for (SCTAB i = 0; i <= MAXTAB; ++i)
{ bool bHasNotes = m_pDoc->HasTabNotes(i);
CPPUNIT_ASSERT(!bHasNotes);
}
// Insert a new sheet to shift the current sheet to the right.
m_pDoc->InsertTab(0, u"Table2"_ustr);
CPPUNIT_ASSERT_MESSAGE("note hasn't moved", !m_pDoc->GetNote(aAddr));
aAddr.IncTab(); // Move to the next sheet.
CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
m_pDoc->DeleteTab(0);
aAddr.IncTab(-1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("note not there", pNote, m_pDoc->GetNote(aAddr));
// Insert cell at C4. This should NOT shift the note position.
bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
// Delete cell at C4. Again, this should NOT shift the note position.
m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note shouldn't have moved but it has.", pNote, m_pDoc->GetNote(aAddr));
// Now, with the note at D4, delete cell D3. This should shift the note one cell up.
m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
aAddr.IncRow(-1); // cell D3
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D4 should have shifted up to D3.", pNote, m_pDoc->GetNote(aAddr));
// Delete column C. This should shift the note one cell left.
m_pDoc->DeleteCol(0, 0, m_pDoc->MaxRow(), 0, 2, 1);
aAddr.IncCol(-1); // cell C3
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at D3 should have shifted left to C3.", pNote, m_pDoc->GetNote(aAddr));
// Insert a text where the note is.
m_pDoc->SetString(aAddr, u"Note is here."_ustr);
// Delete row 1. This should shift the note from C3 to C2.
m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), 0, 0, 1);
aAddr.IncRow(-1); // C2
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note at C3 should have shifted up to C2.", pNote, m_pDoc->GetNote(aAddr));
CPPUNIT_ASSERT_MESSAGE("there should be a note", m_pDoc->HasNote(1, 1, 0));
// test with IsBlockEmpty
CPPUNIT_ASSERT_MESSAGE("The Block should be detected as empty (no Notes)", m_pDoc->IsEmptyData(0, 0, 100, 100, 0));
CPPUNIT_ASSERT_MESSAGE("The Block should NOT be detected as empty", !m_pDoc->IsBlockEmpty(0, 0, 100, 100, 0));
// Check to make sure the notes have shifted upward.
pNote = m_pDoc->GetNote(ScAddress(1,1,0));
CPPUNIT_ASSERT_MESSAGE("B2 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,3,0));
CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
// Undo.
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT_MESSAGE("Failed to get undo manager.", pUndoMgr);
m_pDoc->CreateAllNoteCaptions(); // to make sure that all notes have their corresponding caption objects...
pUndoMgr->Undo();
pNote = m_pDoc->GetNote(ScAddress(1,1,0));
CPPUNIT_ASSERT_MESSAGE("B2 should NOT have a note.", !pNote);
pNote = m_pDoc->GetNote(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,3,0));
CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,3,0));
CPPUNIT_ASSERT_MESSAGE("B4 should NOT have a note.", !pNote);
// Undo and check the result.
pUndoMgr->Undo();
pNote = m_pDoc->GetNote(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"First Note"_ustr, pNote->GetText());
pNote = m_pDoc->GetNote(ScAddress(1,3,0));
CPPUNIT_ASSERT_MESSAGE("B4 should have a note.", pNote);
CPPUNIT_ASSERT_EQUAL(u"Second Note"_ustr, pNote->GetText());
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
ScAddress aPos(1,1,0);
ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
CPPUNIT_ASSERT_MESSAGE("Failed to insert a new cell comment.", pNote);
pNote->SetText(aPos, u"New note"_ustr);
std::unique_ptr<ScPostIt> pNote2 = m_pDoc->ReleaseNote(aPos);
CPPUNIT_ASSERT_EQUAL_MESSAGE("This note instance is expected to be identical to the original.", pNote, pNote2.get());
CPPUNIT_ASSERT_MESSAGE("The note shouldn't be here after it's been released.", !m_pDoc->HasNote(aPos));
// Modify the internal state of the note instance to make sure it's really // been released.
pNote->SetText(aPos, u"New content"_ustr);
// Re-insert the note back to the same place.
m_pDoc->SetNote(aPos, std::move(pNote2));
SdrCaptionObj* pCaption = pNote->GetOrCreateCaption(aPos);
CPPUNIT_ASSERT_MESSAGE("Failed to create a caption object.", pCaption);
CPPUNIT_ASSERT_EQUAL_MESSAGE("This caption should belong to the drawing layer of the document.",
m_pDoc->GetDrawLayer(), static_cast<ScDrawLayer*>(&pCaption->getSdrModelFromSdrObject()));
ScPostIt* pClipNote = aClipDoc.GetNote(aPos);
CPPUNIT_ASSERT_MESSAGE("Failed to copy note to the clipboard.", pClipNote);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Note on the clipboard should share the same caption object from the original.",
pCaption, pClipNote->GetCaption());
// Move B2 to B3 with note, which creates an ScUndoDragDrop, and Undo.
ScAddress aOrigPos(aPos);
ScAddress aMovePos(1,2,0);
ScPostIt* pOrigNote = m_pDoc->GetNote(aOrigPos); const SdrCaptionObj* pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos); boolconst bCut = true; // like Drag&Drop bool bRecord = true; // record Undo boolconst bPaint = false; // don't care about bool bApi = true; // API to prevent dialogs
ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc(); bool bMoveDone = rDocFunc.MoveBlock(ScRange(aOrigPos, aOrigPos), aMovePos, bCut, bRecord, bPaint, bApi);
CPPUNIT_ASSERT_MESSAGE("Cells not moved", bMoveDone);
// Verify the note move.
ScPostIt* pGoneNote = m_pDoc->GetNote(aOrigPos);
CPPUNIT_ASSERT_MESSAGE("Failed to move the note from source.", !pGoneNote);
ScPostIt* pMoveNote = m_pDoc->GetNote(aMovePos);
CPPUNIT_ASSERT_MESSAGE("Failed to move the note to destination.", pMoveNote);
// The caption object should not be identical, it was newly created upon // Drop from clipboard. // pOrigCaption is a dangling pointer. const SdrCaptionObj* pMoveCaption = pMoveNote->GetOrCreateCaption(aMovePos);
CPPUNIT_ASSERT_MESSAGE("Captions identical after move.", pOrigCaption != pMoveCaption);
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT(pUndoMgr);
pUndoMgr->Undo(); // this should not crash ... tdf#92995
// Verify the note move Undo.
pMoveNote = m_pDoc->GetNote(aMovePos);
CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move from destination.", !pMoveNote);
pOrigNote = m_pDoc->GetNote(aOrigPos);
CPPUNIT_ASSERT_MESSAGE("Failed to undo the note move to source.", pOrigNote);
// The caption object still should not be identical. // pMoveCaption is a dangling pointer.
pOrigCaption = pOrigNote->GetOrCreateCaption(aOrigPos);
CPPUNIT_ASSERT_MESSAGE("Captions identical after move undo.", pOrigCaption != pMoveCaption);
// Create a note at B4, merge B4 and B5 with ScUndoMerge, and Undo.
SfxUndoManager* pMergeUndoManager = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT(pMergeUndoManager);
pMergeUndoManager->Undo(); // this should not crash ... tdf#105667
// Undo contained the original caption object pointer which was still alive // at B4 after the merge and not cloned nor recreated during Undo.
ScPostIt* pUndoNoteB4 = m_pDoc->GetNote(aPosB4);
CPPUNIT_ASSERT_MESSAGE("No cell comment at B4 after Undo.", pUndoNoteB4); const SdrCaptionObj* pUndoCaptionB4 = pUndoNoteB4->GetCaption();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Captions not identical after Merge Undo.", pCaptionB4, pUndoCaptionB4);
// In a second document copy a note from B5 to clipboard, close the // document and then paste the note into this document.
{
ScDocShellRef xDocSh2;
getNewDocShell(xDocSh2);
ScDocument* pDoc2 = &xDocSh2->GetDocument();
pDoc2->InsertTab(0, u"OtherSheet1"_ustr);
pDoc2->InitDrawLayer(xDocSh2.get());
ScAddress aPosB5(1,4,0);
ScPostIt* pOtherNoteB5 = pDoc2->GetOrCreateNote(aPosB5);
CPPUNIT_ASSERT_MESSAGE("Failed to insert cell comment at B5.", pOtherNoteB5); const SdrCaptionObj* pOtherCaptionB5 = pOtherNoteB5->GetOrCreateCaption(aPosB5);
CPPUNIT_ASSERT_MESSAGE("No caption at B5.", pOtherCaptionB5);
// There's no ScTransferObject involved in the "fake" clipboard copy // and ScDocument dtor asking IsClipboardSource() gets no, so emulate // the part that normally is responsible for forgetting the caption // objects.
aClipDoc2.ClosingClipboardSource();
pasteFromClip( m_pDoc, ScRange(aPosB5), &aClipDoc2); // should not crash... tdf#104967
ScPostIt* pNoteB5 = m_pDoc->GetNote(aPosB5);
CPPUNIT_ASSERT_MESSAGE("Failed to paste cell comment at B5.", pNoteB5); const SdrCaptionObj* pCaptionB5 = pNoteB5->GetOrCreateCaption(aPosB5);
CPPUNIT_ASSERT_MESSAGE("No caption at pasted B5.", pCaptionB5); // Do not test if pCaptionB5 != pOtherCaptionB5 because since pDoc2 // has been closed and the caption been deleted objects *may* be // allocated at the very same memory location.
}
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
// Insert in B2 a text and cell comment.
ScAddress aPos(1,1,0);
m_pDoc->SetString(aPos, u"Text"_ustr);
ScPostIt* pNote = m_pDoc->GetOrCreateNote(aPos);
CPPUNIT_ASSERT(pNote);
pNote->SetText(aPos, u"Note1"_ustr);
// Insert in B4 a number and cell comment.
aPos.SetRow(3);
m_pDoc->SetValue(aPos, 1.1);
pNote = m_pDoc->GetOrCreateNote(aPos);
CPPUNIT_ASSERT(pNote);
pNote->SetText(aPos, u"Note2"_ustr);
// Make sure the notes are in the clipboard.
pNote = aClipDoc.GetNote(ScAddress(1,1,0));
CPPUNIT_ASSERT(pNote);
CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
// Paste to B6:B8 but only cell notes.
ScRange aDestRange(1,5,0,1,7,0);
m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::NOTE, nullptr, &aClipDoc);
// Make sure the notes are there.
pNote = m_pDoc->GetNote(ScAddress(1,5,0));
CPPUNIT_ASSERT(pNote);
CPPUNIT_ASSERT_EQUAL(u"Note1"_ustr, pNote->GetText());
// Test that GetNotesInRange includes the end of its range // and so can find the note
std::vector<sc::NoteEntry> aNotes;
m_pDoc->GetNotesInRange(ScRange(1,7,0), aNotes);
CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
// We need a drawing layer in order to create caption objects.
m_pDoc->InitDrawLayer(m_xDocShell.get());
ScAddress aAddr(2, 2, 0); // cell C3
CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in a document that doesn't have any.",
!m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
m_pDoc->GetOrCreateNote(aAddr);
CPPUNIT_ASSERT_MESSAGE("Claiming there's notes in range that doesn't have any.",
!m_pDoc->ContainsNotesInRange(ScRange(ScAddress(0, 0, 0), ScAddress(0, 1, 0))));
CPPUNIT_ASSERT_MESSAGE("Note not detected that lies on border of range.",
m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), aAddr))));
CPPUNIT_ASSERT_MESSAGE("Note not detected that lies in inner area of range.",
m_pDoc->ContainsNotesInRange((ScRange(ScAddress(0, 0, 0), ScAddress(3, 3, 0)))));
}
CPPUNIT_TEST_FIXTURE(Test, testAnchoredRotatedShape)
{
m_pDoc->InsertTab(0, u"TestTab"_ustr);
SCROW nRow1, nRow2; bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", SCROW(0), nRow1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("new sheet should have all rows visible", m_pDoc->MaxRow(), nRow2);
m_pDoc->InitDrawLayer();
ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != nullptr);
SdrPage* pPage = pDrawLayer->GetPage(0);
CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != nullptr);
m_pDoc->SetRowHeightRange(0, m_pDoc->MaxRow(), 0, o3tl::toTwips(1000, o3tl::Length::mm100));
constexpr tools::Long TOLERANCE = 30; //30 hmm for ( SCCOL nCol = 0; nCol < m_pDoc->MaxCol(); ++nCol )
m_pDoc->SetColWidth(nCol, 0, o3tl::toTwips(1000, o3tl::Length::mm100));
{ //Add a rect
tools::Rectangle aRect( 4000, 5000, 10000, 7000 );
// ensure that width and height have been adjusted accordingly
CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE );
CPPUNIT_ASSERT_DOUBLES_EQUAL( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE );
// ensure that anchor start and end addresses haven't changed
CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
}
m_pDoc->DeleteTab(0);
}
// Sheet is empty.
std::unique_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
// Sheet only has one cell.
m_pDoc->SetString(0, 0, 0, u"Only one cell"_ustr);
pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
CPPUNIT_ASSERT_EQUAL(SCROW(0), pIter->getPos());
// Setting a text width here should commit it to the column.
sal_uInt16 nTestVal = 432;
pIter->setValue(nTestVal);
CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
// Set values to row 2 through 6. for (SCROW i = 2; i <= 6; ++i)
m_pDoc->SetString(0, i, 0, u"foo"_ustr);
// Set values to row 10 through 18. for (SCROW i = 10; i <= 18; ++i)
m_pDoc->SetString(0, i, 0, u"foo"_ustr);
{ // Full range.
pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; for (constauto& rRow : aRows)
{
CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
pIter->next();
}
CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
}
{ // Specify start and end rows (6 - 16)
ScAddress aStart = aTopCell;
aStart.SetRow(6);
pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 }; for (constauto& rRow : aRows)
{
CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
pIter->next();
}
CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
}
// Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
{ // Full range again.
pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
SCROW aRows[] = { 0, 2, 18 }; for (constauto& rRow : aRows)
{
CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
pIter->next();
}
CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
}
// Delete row 2 which shifts all cells below row 2 upward. After this, we // should only have cells at rows 0 and 17.
m_pDoc->DeleteRow(0, 0, m_pDoc->MaxCol(), MAXTAB, 2, 1);
{ // Full range again.
pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, m_pDoc->MaxRow()));
SCROW aRows[] = { 0, 17 }; for (constauto& rRow : aRows)
{
CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
CPPUNIT_ASSERT_EQUAL(rRow, pIter->getPos());
pIter->next();
}
CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
}
// Formula value should have been updated.
CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
// Undo, and check the result.
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT_MESSAGE("Failed to get the undo manager.", pUndoMgr);
pUndoMgr->Undo();
ScAddress aPos(1,2,0); // B3
ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
// Make sure that B3:B4 and B7:B8 are formula cells.
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
// Make sure that B5:C6 are numeric cells.
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
// Make sure that formula cells in C3:C4 and C7:C8 are grouped. const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
// Undo and check.
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT(pUndoMgr);
pUndoMgr->Undo();
// B3:B8 should all be (ungrouped) formula cells. for (SCROW i = 2; i <= 7; ++i)
{
pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT(!pFC->IsShared());
}
// C3:C8 should be shared formula cells.
pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
// Make sure that B3:B4 and B7:B8 are formula cells.
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,2,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,3,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,6,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(ScAddress(1,7,0)));
// Make sure that B5:C6 are numeric cells.
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,4,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,5,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,4,0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(2,5,0)));
// Make sure that formula cells in C3:C4 and C7:C8 are grouped.
pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
// Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
pUndoMgr->Undo();
m_pDoc->SetValue(ScAddress(1,4,0), 10);
m_pDoc->SetValue(ScAddress(1,5,0), 11);
CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
// Insert data into B2:C6.
ScAddress aPos(1,1,0); // B2
ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
// Test the basics with real edit cells, using Column A.
SCROW nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,m_pDoc->MaxRow(),0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
nResRow = m_pDoc->GetFirstEditTextRow(ScRange(0,0,0,0,10,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no edit cells.", SCROW(-1), nResRow);
ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
rEE.SetTextCurrentDefaults(u"Test"_ustr);
m_pDoc->SetEditText(ScAddress(0,0,0), rEE.CreateTextObject()); const EditTextObject* pObj = m_pDoc->GetEditText(ScAddress(0,0,0));
CPPUNIT_ASSERT_MESSAGE("There should be an edit cell here.", pObj);
ScRange aRange(0,0,0,0,0,0);
nResRow = m_pDoc->GetFirstEditTextRow(aRange);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There is an edit cell here.", SCROW(0), nResRow);
aRange.aStart.SetRow(1);
aRange.aEnd.SetRow(1);
nResRow = m_pDoc->GetFirstEditTextRow(aRange);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
aRange.aStart.SetRow(2);
aRange.aEnd.SetRow(4);
nResRow = m_pDoc->GetFirstEditTextRow(aRange);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
aRange.aStart.SetRow(0);
aRange.aEnd.SetRow(m_pDoc->MaxRow());
nResRow = m_pDoc->GetFirstEditTextRow(aRange);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be an edit cell in specified range.", SCROW(0), nResRow);
m_pDoc->SetString(ScAddress(0,0,0), u"Test"_ustr);
m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
ScRefCellValue aCell;
aCell.assign(*m_pDoc, ScAddress(0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string cell.", CELLTYPE_STRING, aCell.getType());
aCell.assign(*m_pDoc, ScAddress(0,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
aCell.assign(*m_pDoc, ScAddress(0,2,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a numeric cell.", CELLTYPE_VALUE, aCell.getType());
aCell.assign(*m_pDoc, ScAddress(0,3,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be an empty cell.", CELLTYPE_NONE, aCell.getType());
aRange.aStart.SetRow(1);
aRange.aEnd.SetRow(1);
nResRow = m_pDoc->GetFirstEditTextRow(aRange);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be an edit cell in specified range.", SCROW(-1), nResRow);
// Test with non-edit cell but with ambiguous script type.
staticstruct aInputs
{
SCROW nRow;
SCCOL nCol; constchar* aFormula1; // Represents the formula that is input to SetFormula function. constchar* aFormula2; // Represents the formula that is actually stored in the cell.
formula::FormulaGrammar::Grammar const eGram;
// Set a numeric value to B3.
m_pDoc->SetValue(ScAddress(1,2,0), 1.0);
aState = m_pDoc->HasMultipleDataCells(aRange);
CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
// Set another numeric value to B4.
m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
aRange.aEnd.SetRow(3); // B3:B4
aState = m_pDoc->HasMultipleDataCells(aRange);
CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
// Set the query range to B4:B5. Now it should only report one cell, with // B4 being the first non-empty cell.
aRange.aStart.SetRow(3);
aRange.aEnd.SetRow(4);
aState = m_pDoc->HasMultipleDataCells(aRange);
CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
// Set the query range to A1:C3. The first non-empty cell should be B3.
aRange = ScRange(0,0,0,2,2,0);
aState = m_pDoc->HasMultipleDataCells(aRange);
CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasOneCell, aState.meState);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), aState.mnCol1);
CPPUNIT_ASSERT_EQUAL(SCROW(2), aState.mnRow1);
// Set string cells to D4 and F5, and query D3:F5. D4 should be the first // non-empty cell.
m_pDoc->SetString(ScAddress(3,3,0), u"foo"_ustr);
m_pDoc->SetString(ScAddress(5,4,0), u"bar"_ustr);
aRange = ScRange(3,2,0,5,4,0);
aState = m_pDoc->HasMultipleDataCells(aRange);
CPPUNIT_ASSERT_EQUAL(sc::MultiDataCellState::HasMultipleCells, aState.meState);
CPPUNIT_ASSERT_EQUAL(SCCOL(3), aState.mnCol1);
CPPUNIT_ASSERT_EQUAL(SCROW(3), aState.mnRow1);
pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
// diagonal down and up border in the same cell (A5)
m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
// test if both borders are applied successfully in the same cell (A5)
aPos = { 0, 4, 0 };
pPat = m_pDoc->GetPattern(aPos);
CPPUNIT_ASSERT(pPat);
pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
CPPUNIT_ASSERT_MESSAGE("Diagonal down border was expected, but not found!", pLine);
pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
CPPUNIT_ASSERT_MESSAGE("Diagonal up border was expected, but not found!", pLine);
// test if both borders are removed successfully
dDownLineItem.SetLine(nullptr);
dUpLineItem.SetLine(nullptr);
// SetLine(nullptr) should remove the lines from (A5)
m_pDoc->ApplyAttr(0, 4, 0, dDownLineItem);
m_pDoc->ApplyAttr(0, 4, 0, dUpLineItem);
pLine = pPat->GetItem(ATTR_BORDER_TLBR).GetLine();
CPPUNIT_ASSERT_MESSAGE("Diagonal down border was not expected, but is found!", !pLine);
pLine = pPat->GetItem(ATTR_BORDER_BLTR).GetLine();
CPPUNIT_ASSERT_MESSAGE("Diagonal up border was not expected, but is found!", !pLine);
// Set outside border to be on all sides, and inside borders to be only vertical. // This should result in edge borders of the spreadsheets being set, but internal // borders between cells should be only vertical, not horizontal.
::editeng::SvxBorderLine line(nullptr, 50, SvxBorderLineStyle::SOLID);
SvxBoxItem borderItem(ATTR_BORDER);
borderItem.SetLine(&line, SvxBoxItemLine::LEFT);
borderItem.SetLine(&line, SvxBoxItemLine::RIGHT);
borderItem.SetLine(&line, SvxBoxItemLine::TOP);
borderItem.SetLine(&line, SvxBoxItemLine::BOTTOM);
SvxBoxInfoItem boxInfoItem(ATTR_BORDER);
boxInfoItem.SetLine(&line, SvxBoxInfoItemLine::VERT);
CPPUNIT_TEST_FIXTURE(Test, testUndoDataAnchor)
{
m_pDoc->InsertTab(0, u"Tab1"_ustr);
CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be only 1 sheets to begin with", static_cast<SCTAB>(1), m_pDoc->GetTableCount());
// Insert an object.
tools::Rectangle aObjRect(2,1000,100,1100);
rtl::Reference<SdrObject> pObj = new SdrRectObj(*pDrawLayer, aObjRect);
pPage->InsertObject(pObj.get());
ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
// Get anchor data
ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj.get());
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
// Get non rotated anchor data
ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
// Get non rotated anchor data
pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
// Check state
ScAnchorType oldType = ScDrawLayer::GetAnchorType(*pObj);
CPPUNIT_ASSERT_EQUAL_MESSAGE( "Failed to check state SCA_CELL.", SCA_CELL, oldType);
// Get anchor data
pData = ScDrawLayer::GetObjData(pObj.get());
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
// Get non rotated anchor data
pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
// Check if data has moved to new rows
CPPUNIT_ASSERT_EQUAL(pData->maStart, aOldStart);
CPPUNIT_ASSERT_EQUAL(pData->maEnd, aOldEnd);
// Get anchor data
pData = ScDrawLayer::GetObjData(pObj.get());
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve user data for this object.", pData);
// Get non rotated anchor data
pNData = ScDrawLayer::GetNonRotatedObjData( pObj.get() );
CPPUNIT_ASSERT_MESSAGE("Failed to retrieve non rotated user data for this object.", pNData);
// Check if data has moved to new rows
CPPUNIT_ASSERT_EQUAL(pData->maStart, aNewStart);
CPPUNIT_ASSERT_EQUAL(pData->maEnd, aNewEnd);
// Protect the sheet without any options.
ScTableProtection aProtect;
aProtect.setProtected(true);
m_pDoc->SetTabProtection(0, &aProtect);
// Try to delete row 3. It should fail.
ScRange aRow3(0,2,0,m_pDoc->MaxCol(),2,0);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0); bool bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should fail.", !bDeleted);
// Protect the sheet but allow row deletion.
aProtect.setOption(ScTableProtection::DELETE_ROWS, true);
m_pDoc->SetTabProtection(0, &aProtect);
// Now we should be able to delete row 3.
bDeleted = rDocFunc.DeleteCells(aRow3, &aMark, DelCellCmd::Rows, true);
CPPUNIT_ASSERT_MESSAGE("deletion of row 3 should succeed.", bDeleted);
// But, row deletion should still fail on a protected row.
ScRange aRow10(0,9,0,m_pDoc->MaxCol(),9,0);
bDeleted = rDocFunc.DeleteCells(aRow10, &aMark, DelCellCmd::Rows, true);
CPPUNIT_ASSERT_MESSAGE("deletion of row 10 should not be allowed.", !bDeleted);
// Try inserting a new row. It should fail. bool bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should fail.", !bInserted);
bInserted = rDocFunc.InsertCells(aRow3, &aMark, INS_INSROWS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("row insertion at row 3 should succeed.", bInserted);
// Row insertion is allowed even when the rows above and below have protected flags set.
bInserted = rDocFunc.InsertCells(aRow10, &aMark, INS_INSROWS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("row insertion at row 10 should succeed.", bInserted);
}
m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
{ // Remove protected flags from columns B to E.
ScPatternAttr aAttr(m_pDoc->getCellAttributeHelper());
aAttr.GetItemSet().Put(ScProtectionAttr(false));
m_pDoc->ApplyPatternAreaTab(1, 0, 4, m_pDoc->MaxRow(), 0, aAttr);
// Protect the sheet without any options.
ScTableProtection aProtect;
aProtect.setProtected(true);
m_pDoc->SetTabProtection(0, &aProtect);
// Try to delete column C. It should fail.
ScRange aCol3(2,0,0,2,m_pDoc->MaxRow(),0);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0); bool bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should fail.", !bDeleted);
// Protect the sheet but allow column deletion.
aProtect.setOption(ScTableProtection::DELETE_COLUMNS, true);
m_pDoc->SetTabProtection(0, &aProtect);
// Now we should be able to delete column C.
bDeleted = rDocFunc.DeleteCells(aCol3, &aMark, DelCellCmd::Cols, true);
CPPUNIT_ASSERT_MESSAGE("deletion of column 3 should succeed.", bDeleted);
// But, column deletion should still fail on a protected column.
ScRange aCol10(9,0,0,9,m_pDoc->MaxRow(),0);
bDeleted = rDocFunc.DeleteCells(aCol10, &aMark, DelCellCmd::Cols, true);
CPPUNIT_ASSERT_MESSAGE("deletion of column 10 should not be allowed.", !bDeleted);
// Try inserting a new column. It should fail. bool bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should fail.", !bInserted);
bInserted = rDocFunc.InsertCells(aCol3, &aMark, INS_INSCOLS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("column insertion at column 3 should succeed.", bInserted);
// Column insertion is allowed even when the columns above and below have protected flags set.
bInserted = rDocFunc.InsertCells(aCol10, &aMark, INS_INSCOLS_AFTER, true, true);
CPPUNIT_ASSERT_MESSAGE("column insertion at column 10 should succeed.", bInserted);
}
m_pDoc->InsertTab(1, u"Matrix"_ustr); // This sheet is unprotected.
std::set<SCCOL> aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
CPPUNIT_ASSERT_MESSAGE("empty sheet should contain no formula cells.", aCols.empty());
auto equals = [](const std::set<SCCOL>& left, const std::set<SCCOL>& right)
{ return left == right;
};
// insert formula cells in columns 2, 4 and 6.
m_pDoc->SetFormula(ScAddress(2, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
m_pDoc->SetFormula(ScAddress(4, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
m_pDoc->SetFormula(ScAddress(6, 2, 0), u"=1"_ustr, m_pDoc->GetGrammar());
aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
std::set<SCCOL> aExpected = { 2, 4, 6 };
CPPUNIT_ASSERT_MESSAGE("Columns 2, 4 and 6 should contain formula cells.", equals(aExpected, aCols));
// Insert 2 columns at column A to shift everything to right by 2.
m_pDoc->InsertCol(0, 0, m_pDoc->MaxRow(), 0, 0, 2);
aExpected = { 4, 6, 8 };
aCols = m_pDoc->QueryColumnsWithFormulaCells(0);
CPPUNIT_ASSERT_MESSAGE("Columns 4, 6 and 8 should contain formula cells.", equals(aExpected, aCols));
// Check document currencies again - should be 1 entry only
aCurrencyIDs = pAccessor->getDocumentCurrencies();
CPPUNIT_ASSERT_EQUAL(size_t(1), aCurrencyIDs.size());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.