Files
odfstudio/sc/qa/unit/tiledrendering/SheetViewTest.cxx
T
Noel Grandin fcf0bcc0f9 Hold EditTextObject by value in OutlinerParaObjData, not by pointer
OutlinerParaObjData is already ref-counted data, no need
for further indirection.

Unfortunately, this sets off a larger chain of changes
than I would have preferred, but we end up storing
EditTextObject on the stack much more than before, so
that worked out well.

Change-Id: Ie7d0ccfbfeec4e6935f2ed6b8ad48aadfe1bac20
Signed-off-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/10973
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit fbf7d1bc7142d3f0e4e5f959983dea065afc5ea3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210927
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-09-14 16:27:02 +02:00

5565 lines
219 KiB
C++

/* -*- 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/.
*/
#include <sctiledrenderingtest.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/scopeguard.hxx>
#include <sfx2/lokhelper.hxx>
#include <vcl/scheduler.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <sctestviewcallback.hxx>
#include <docfunc.hxx>
#include <docsh.hxx>
#include <docuno.hxx>
#include <markdata.hxx>
#include <postit.hxx>
#include <scitems.hxx>
#include <SheetView.hxx>
#include <SheetViewManager.hxx>
#include <attrib.hxx>
#include <editeng/brushitem.hxx>
#include <i18nutil/transliteration.hxx>
#include <paramisc.hxx>
#include <cellmergeoption.hxx>
#include <rangenam.hxx>
#include <dbdocfun.hxx>
#include <dbdata.hxx>
#include <subtotalparam.hxx>
#include <drawview.hxx>
#include <drwlayer.hxx>
#include <Sparkline.hxx>
#include <SparklineAttributes.hxx>
#include <SparklineGroup.hxx>
#include <dpobject.hxx>
#include <editeng/editobj.hxx>
#include <editutil.hxx>
#include <formulacell.hxx>
#include <operation/DeleteCellsOperation.hxx>
#include <operation/InsertCellsOperation.hxx>
#include <operation/ReplacePivotTableOperation.hxx>
#include <dpshttab.hxx>
#include <dpsave.hxx>
#include <generalfunction.hxx>
#include <svx/svdorect.hxx>
#include <svx/svdpage.hxx>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
using namespace css;
class SheetViewTest : public ScTiledRenderingTest
{
protected:
static ScRange getAutoFilterArea(ScDocument& rDocument, SCTAB nTab)
{
ScDBData* pDBData = rDocument.GetAnonymousDBData(nTab);
CPPUNIT_ASSERT(pDBData);
CPPUNIT_ASSERT(pDBData->HasAutoFilter());
ScRange aRange;
pDBData->GetArea(aRange);
return aRange;
}
static OUString expectedValues(std::vector<std::u16string_view> const& rValues)
{
OUString aString;
bool bFirst = true;
for (auto const& rValue : rValues)
{
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + rValue + u"\""_ustr;
}
else
aString += u", \""_ustr + rValue + u"\""_ustr;
}
return aString;
}
static OUString getValues(ScTabViewShell* pTabView, SCCOL nCol, SCROW nStartRow, SCROW nEndRow)
{
OUString aString;
size_t nSize = nEndRow - nStartRow + 1;
bool bFirst = true;
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
{
OUString aValue = pTabView->GetCurrentString(nCol, nStartRow + nIndex);
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + aValue + u"\""_ustr;
}
else
aString += u", \""_ustr + aValue + u"\""_ustr;
}
return aString;
}
static OUString getValues(ScDocument* pDocument, SCCOL nCol, SCROW nStartRow, SCROW nEndRow,
SCTAB nTab)
{
OUString aString;
size_t nSize = nEndRow - nStartRow + 1;
bool bFirst = true;
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
{
OUString aValue = pDocument->GetString({ nCol, SCROW(nStartRow + nIndex), nTab });
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + aValue + u"\""_ustr;
}
else
aString += u", \""_ustr + aValue + u"\""_ustr;
}
return aString;
}
static OUString getTextWeight(ScDocument* pDocument, SCCOL nCol, SCROW nStartRow, SCROW nEndRow,
SCTAB nTab)
{
OUString aString;
size_t nSize = nEndRow - nStartRow + 1;
bool bFirst = true;
vcl::Font aFont;
const ScPatternAttr* pPattern;
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
{
pPattern = pDocument->GetPattern(nCol, SCROW(nStartRow + nIndex), nTab);
pPattern->fillFontOnly(aFont);
OUString aWeight;
switch (aFont.GetWeight())
{
case WEIGHT_NORMAL:
aWeight = "N";
break;
case WEIGHT_BOLD:
aWeight = "B";
break;
default:
aWeight = "?";
break;
}
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + aWeight + u"\""_ustr;
}
else
aString += u", \""_ustr + aWeight + u"\""_ustr;
}
return aString;
}
static OUString getBackgroundColor(ScDocument* pDocument, SCCOL nCol, SCROW nStartRow,
SCROW nEndRow, SCTAB nTab)
{
OUString aString;
bool bFirst = true;
for (SCROW nRow = nStartRow; nRow <= nEndRow; nRow++)
{
const ScPatternAttr* pPattern = pDocument->GetPattern(nCol, nRow, nTab);
const SvxBrushItem& rBrush = pPattern->GetItem(ATTR_BACKGROUND);
OUString aColor = rBrush.GetColor().AsRGBHexString();
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + aColor + u"\""_ustr;
}
else
aString += u", \""_ustr + aColor + u"\""_ustr;
}
return aString;
}
// Writes to string the indent state for a row range, Y = indented, N = not indented
static OUString getIndent(ScDocument* pDocument, SCCOL nCol, SCROW nStartRow, SCROW nEndRow,
SCTAB nTab)
{
OUString aString;
bool bFirst = true;
for (SCROW nRow = nStartRow; nRow <= nEndRow; nRow++)
{
const ScPatternAttr* pPattern = pDocument->GetPattern(nCol, nRow, nTab);
const ScIndentItem& rIndent = pPattern->GetItem(ATTR_INDENT);
OUString aValue = rIndent.GetValue() > 0 ? u"Y"_ustr : u"N"_ustr;
if (bFirst)
{
bFirst = false;
aString = u"\""_ustr + aValue + u"\""_ustr;
}
else
aString += u", \""_ustr + aValue + u"\""_ustr;
}
return aString;
}
void gotoCell(std::u16string_view aCellAddress)
{
dispatchCommand(
mxComponent, u".uno:GoToCell"_ustr,
comphelper::InitPropertySequence({ { "ToPoint", uno::Any(OUString(aCellAddress)) } }));
}
void createNewSheetViewInCurrentView()
{
dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {});
}
void insertSheet(OUString const& rName, sal_Int16 nIndex)
{
dispatchCommand(mxComponent, u".uno:Insert"_ustr,
comphelper::InitPropertySequence(
{ { "Name", uno::Any(rName) }, { "Index", uno::Any(nIndex) } }));
}
void removeSheetViewInCurrentView()
{
dispatchCommand(mxComponent, u".uno:RemoveSheetView"_ustr, {});
}
void sortAscendingForCell(std::u16string_view aCellAddress)
{
gotoCell(aCellAddress);
dispatchCommand(mxComponent, u".uno:SortAscending"_ustr, {});
}
void sortDescendingForCell(std::u16string_view aCellAddress)
{
gotoCell(aCellAddress);
dispatchCommand(mxComponent, u".uno:SortDescending"_ustr, {});
}
void setCellBold(std::u16string_view aCellAddress)
{
gotoCell(aCellAddress);
dispatchCommand(mxComponent, u".uno:Bold"_ustr, {});
}
void undo() { dispatchCommand(mxComponent, u".uno:Undo"_ustr, {}); }
void redo() { dispatchCommand(mxComponent, u".uno:Redo"_ustr, {}); }
};
/** Test class that contains methods commonly used for testing sync of sheet views. */
class SyncTest : public SheetViewTest
{
public:
void tearDown() override
{
moSheetView = std::nullopt;
moDefaultView = std::nullopt;
SheetViewTest::tearDown();
}
protected:
std::optional<ScTestViewCallback> moSheetView;
std::optional<ScTestViewCallback> moDefaultView;
ScTabViewShell* mpTabViewSheetView = nullptr;
ScTabViewShell* mpTabViewDefaultView = nullptr;
void setupViews()
{
moSheetView.emplace();
mpTabViewSheetView = moSheetView->getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
moDefaultView.emplace();
mpTabViewDefaultView = moDefaultView->getTabViewShell();
CPPUNIT_ASSERT(mpTabViewSheetView != mpTabViewDefaultView);
CPPUNIT_ASSERT(moSheetView->getViewID() != moDefaultView->getViewID());
}
void switchToSheetView()
{
SfxLokHelper::setView(moSheetView->getViewID());
Scheduler::ProcessEventsToIdle();
}
void switchToDefaultView()
{
SfxLokHelper::setView(moDefaultView->getViewID());
Scheduler::ProcessEventsToIdle();
}
ScDrawView* getDrawView()
{
ScTabViewShell* pTabViewShell = ScTabViewShell::GetActiveViewShell();
CPPUNIT_ASSERT(pTabViewShell);
ScDrawView* pDrawView = pTabViewShell->GetScDrawView();
CPPUNIT_ASSERT(pDrawView);
return pDrawView;
}
/** Find first pivot table on the input tab. */
static ScDPObject* findPivotTableOnTab(ScDocument& rDocument, SCTAB nTab)
{
ScDPCollection* pDPCollection = rDocument.GetDPCollection();
for (size_t i = 0; i < pDPCollection->GetCount(); ++i)
{
ScDPObject& rDPObject = (*pDPCollection)[i];
if (rDPObject.GetOutRange().aStart.Tab() == nTab)
return &rDPObject;
}
return nullptr;
}
static size_t countPivotTablesOnTab(ScDocument& rDocument, SCTAB nTab)
{
size_t nCount = 0;
ScDPCollection* pDPCollection = rDocument.GetDPCollection();
for (size_t i = 0; i < pDPCollection->GetCount(); ++i)
{
if ((*pDPCollection)[i].GetOutRange().aStart.Tab() == nTab)
++nCount;
}
return nCount;
}
static void setupPivotTableSourceData(ScDocument& rDocument)
{
rDocument.SetString(ScAddress(0, 0, 0), u"Category"_ustr);
rDocument.SetString(ScAddress(1, 0, 0), u"Value"_ustr);
rDocument.SetString(ScAddress(0, 1, 0), u"A"_ustr);
rDocument.SetString(ScAddress(0, 2, 0), u"A"_ustr);
rDocument.SetString(ScAddress(0, 3, 0), u"B"_ustr);
rDocument.SetValue(ScAddress(1, 1, 0), 10.0);
rDocument.SetValue(ScAddress(1, 2, 0), 20.0);
rDocument.SetValue(ScAddress(1, 3, 0), 30.0);
}
static ScDPObject
createCategoryValueDPObject(ScDocument& rDocument, SCTAB nTab, ScRange aOutRange,
css::sheet::DataPilotFieldOrientation eCategoryOrientation)
{
ScDPObject aDPObject(&rDocument);
ScSheetSourceDesc aSheetDesc(&rDocument);
aSheetDesc.SetSourceRange(ScRange(0, 0, nTab, 1, 3, nTab));
aDPObject.SetSheetDesc(aSheetDesc);
aDPObject.SetOutRange(aOutRange);
ScDPSaveData aSaveData;
ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(u"Category"_ustr);
pDim->SetOrientation(eCategoryOrientation);
pDim = aSaveData.GetNewDimensionByName(u"Value"_ustr);
pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_DATA);
pDim->SetFunction(ScGeneralFunction::SUM);
aDPObject.SetSaveData(aSaveData);
return aDPObject;
}
void insertDrawObject(ScDrawLayer* pDrawLayer, const tools::Rectangle& rRectangle)
{
ScDrawView* pDrawView = getDrawView();
rtl::Reference<SdrRectObj> pObject = new SdrRectObj(*pDrawLayer, rRectangle);
pDrawView->InsertObjectSafe(pObject.get(), *pDrawView->GetSdrPageView());
}
void moveDrawObject(SdrObject* pObject, const tools::Rectangle& rNewPosition)
{
tools::Rectangle aOldPosition = pObject->GetLogicRect();
Size aOffset(rNewPosition.Left() - aOldPosition.Left(),
rNewPosition.Top() - aOldPosition.Top());
ScDrawView* pDrawView = getDrawView();
pDrawView->UnmarkAllObj();
pDrawView->MarkObj(pObject, pDrawView->GetSdrPageView());
pDrawView->MoveMarkedObj(aOffset);
}
void deleteDrawObject(SdrObject* pObject)
{
ScDrawView* pDrawView = getDrawView();
pDrawView->UnmarkAllObj();
pDrawView->MarkObj(pObject, pDrawView->GetSdrPageView());
pDrawView->DeleteMarkedObj();
}
};
/** Check auto-filter sorting.
*
* Auto filter sorting should only influence the values in the
* current sheet view and not in the other views.
*/
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSheetViewAutoFilter)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Setup 2 views
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
CPPUNIT_ASSERT(pTabView1 != pTabView2);
CPPUNIT_ASSERT(aView1.getViewID() != aView2.getViewID());
// Switch to view 1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
// Check AutoFilter values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView1, 0, 1, 4));
// Switch to view 2
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
// Check auto-filter values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView2, 0, 1, 4));
// Check what sheet we currently have selected for view 1 & 2
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView2->GetViewData().GetTabNumber());
// Create a new sheet view for view 2
createNewSheetViewInCurrentView();
// Check what sheet we currently have selected for view 1 & 2
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView2->GetViewData().GetTabNumber());
// Sort AutoFilter descending
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView2->GetViewData().GetTabNumber());
// Check view 2 - sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }), getValues(pTabView2, 0, 1, 4));
// Check view 1 - unsorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView1, 0, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSyncValuesBetweenMainSheetAndSheetView)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
// Setup 2 views
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
CPPUNIT_ASSERT(pTabView1 != pTabView2);
CPPUNIT_ASSERT(aView1.getViewID() != aView2.getViewID());
// Setup data
// String in A1, and a formula in A2
ScAddress aA1(0, 0, 0);
ScAddress aA1SheetView(0, 0, 1);
ScAddress aA2(0, 1, 0);
ScAddress aA2SheetView(0, 1, 1);
typeCharsInCell(std::string("ABCD"), aA1.Col(), aA1.Row(), pTabView1, pModelObj);
typeCharsInCell(std::string("=UPPER(\"A\"&\"b\"&\"C\"&\"d\")"), aA2.Col(), aA2.Row(), pTabView1,
pModelObj);
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pDocument->GetString(aA1));
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pDocument->GetString(aA2));
// Check what the View1 and View2 sees
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row()));
// Create a sheet view in View2
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
// Change content in View1 with default view -> default view to sheet view sync
SfxLokHelper::setView(aView1.getViewID());
typeCharsInCell(std::string("XYZ"), aA1.Col(), aA1.Row(), pTabView1, pModelObj);
typeCharsInCell(std::string("=UPPER(\"x\"&\"Y\"&\"z\""), aA2.Col(), aA2.Row(), pTabView1,
pModelObj);
// Check the content is synced
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row()));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row()));
// Check the content directly in sheets
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA1));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA1SheetView));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA2));
CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA2SheetView));
// Change content in the View2 with the sheet view -> sheet view to default view sync
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
typeCharsInCell(std::string("ABC123"), aA1.Col(), aA1.Row(), pTabView2, pModelObj);
typeCharsInCell(std::string("=UPPER(\"aBc\"&\"123\""), aA2.Col(), aA2.Row(), pTabView2,
pModelObj);
// Check the content is synced
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row()));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row()));
// Check the content directly in sheets
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA1));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA1SheetView));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA2));
CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA2SheetView));
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testRemoveSheetView)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
// Setup views
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
CPPUNIT_ASSERT(pTabView1 != pTabView2);
CPPUNIT_ASSERT(aView1.getViewID() != aView2.getViewID());
// Switch to View1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
// Create a new sheet view for view 1
createNewSheetViewInCurrentView();
// Check AutoFilter values for each view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView1, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView2, 0, 1, 4));
// Switch to View2
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
// Sort AutoFilter descending
sortDescendingForCell(u"A1");
// Check values are sorted for view 2
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }), getValues(pTabView1, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }), getValues(pTabView2, 0, 1, 4));
// Sheet view must be present
auto pSheetViewManager = pDocument->GetSheetViewManager(0);
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewManager->size());
// There should be 2 tables
CPPUNIT_ASSERT_EQUAL(SCTAB(2), pDocument->GetTableCount());
// Switch to View1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
// We remove the current sheet view
removeSheetViewInCurrentView();
Scheduler::ProcessEventsToIdle();
// Sheet view was removed
CPPUNIT_ASSERT_EQUAL(size_t(0), pSheetViewManager->size());
// Only 1 table left
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pDocument->GetTableCount());
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSheetViewOperationRestrictions_DefaultViewChanged)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
// Setup views
// View 1
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// View 2
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
// View 3
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView3;
ScTabViewShell* pTabView3 = aView3.getTabViewShell();
// Create a new sheet view for view 1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
// Create a new sheet view for view 3
SfxLokHelper::setView(aView3.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
// Check AutoFilter values for each view
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView1->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView1->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView1->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView1->GetCurrentString(0, 4));
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView2->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView2->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView2->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView2->GetCurrentString(0, 4));
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView3->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView3->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView3->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView3->GetCurrentString(0, 4));
// Switch to View2
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
// Sheet view must be present
auto pSheetViewManager = pDocument->GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewManager->size());
// Sort on default view
sortDescendingForCell(u"A1");
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSheetViewOperationRestrictions_SheetViewChanged)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
// Setup views
// View 1
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// View 2
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
// View 3
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView3;
ScTabViewShell* pTabView3 = aView3.getTabViewShell();
// Create a new sheet view for view 1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
// Create a new sheet view for view 3
SfxLokHelper::setView(aView3.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
// Check AutoFilter values for each view
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView1->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView1->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView1->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView1->GetCurrentString(0, 4));
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView2->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView2->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView2->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView2->GetCurrentString(0, 4));
CPPUNIT_ASSERT_EQUAL(u"4"_ustr, pTabView3->GetCurrentString(0, 1));
CPPUNIT_ASSERT_EQUAL(u"5"_ustr, pTabView3->GetCurrentString(0, 2));
CPPUNIT_ASSERT_EQUAL(u"3"_ustr, pTabView3->GetCurrentString(0, 3));
CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView3->GetCurrentString(0, 4));
// Sheet view must be present
auto pSheetViewManager = pDocument->GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewManager->size());
// Sort on sheet view 1
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
sortDescendingForCell(u"A1");
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testCheckIfSheetViewIsSavedInDocument_ODF)
{
// Check if sheet view holder table is saved into the ODF document
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
createNewSheetViewInCurrentView();
save(TestFilter::ODS);
xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr);
CPPUNIT_ASSERT(pXmlDoc);
assertXPath(pXmlDoc, "//table:table", 1);
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testCheckIfSheetViewIsSavedInDocument_OOXML)
{
// Check if sheet view holder table is saved into the OOXML document
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
createNewSheetViewInCurrentView();
save(TestFilter::XLSX);
xmlDocUniquePtr pXmlDoc = parseExport(u"xl/workbook.xml"_ustr);
CPPUNIT_ASSERT(pXmlDoc);
assertXPath(pXmlDoc, "/x:workbook/x:sheets/x:sheet", 1);
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testRemoveTableWithSheetViews)
{
// Create a new sheet (in addition to the existing one), and create 2 sheet views of the new sheet.
// After that, delete the sheet and the 2 sheet view holder tables should also be deleted.
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument& rDocument = *pModelObj->GetDocument();
// Setup views
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// Insert a new sheet - "NewTab"
uno::Sequence<beans::PropertyValue> aArgsInsert(comphelper::InitPropertySequence(
{ { "Name", uno::Any(u"NewTab"_ustr) }, { "Index", uno::Any(sal_Int16(1)) } }));
dispatchCommand(mxComponent, u".uno:Insert"_ustr, aArgsInsert);
{
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(0), pSheetViewManager->size());
// Check we have the correct table selected
CPPUNIT_ASSERT_EQUAL(SCTAB(2), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"NewTab"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[1]);
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
}
// Create first sheet views
createNewSheetViewInCurrentView();
{
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewManager->size());
// View is now on the sheet view tab
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
auto pSheetView1 = pSheetViewManager->get(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pSheetView1->getTableNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(3), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"NewTab"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"NewTab_2"_ustr, rDocument.GetAllTableNames()[1]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[2]);
}
// Create second sheet views
createNewSheetViewInCurrentView();
{
// View is now on the latest sheet view tab
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(4), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"NewTab"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"NewTab_3"_ustr, rDocument.GetAllTableNames()[1]);
CPPUNIT_ASSERT_EQUAL(u"NewTab_2"_ustr, rDocument.GetAllTableNames()[2]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[3]);
// Sheet view must be present
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewManager->size());
auto pSheetView1 = pSheetViewManager->get(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(2), pSheetView1->getTableNumber());
auto pSheetView2 = pSheetViewManager->get(1);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pSheetView2->getTableNumber());
}
// Delete the table - index 0
uno::Sequence<beans::PropertyValue> aArgs(
comphelper::InitPropertySequence({ { "Index", uno::Any(sal_uInt16(0)) } }));
dispatchCommand(mxComponent, u".uno:Remove"_ustr, aArgs);
{
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(1), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[0]);
}
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testNewSheetViewKeepsOthersUnchanged)
{
// Regression: SC_TAB_INSERTED was broadcast twice in MakeNewSheetView,
// making every passive view shift its active tab twice and land on the
// wrong sheet.
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument& rDocument = *pModelObj->GetDocument();
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// 4 sheets is the minimum that keeps a buggy double shift of 2 -> 4
// from being clamped back to 3 by tableCount - 1.
insertSheet(u"Sheet2"_ustr, 2);
insertSheet(u"Sheet3"_ustr, 3);
insertSheet(u"Sheet4"_ustr, 4);
CPPUNIT_ASSERT_EQUAL(SCTAB(4), rDocument.GetTableCount());
pTabView1->SetTabNo(0);
// Setup View #2 on Sheet3.
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
SfxLokHelper::setView(aView2.getViewID());
Scheduler::ProcessEventsToIdle();
pTabView2->SetTabNo(2);
CPPUNIT_ASSERT_EQUAL(SCTAB(2), pTabView2->GetViewData().GetTabNumber());
// View #1 creates a new sheet view of Sheet1: one tab is inserted at
// index 1.
SfxLokHelper::setView(aView1.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_EQUAL(SCTAB(5), rDocument.GetTableCount());
// View #1 lands on its new sheet view at tab 1
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
// View #2 must follow Sheet3 from tab 2 to tab 3.
CPPUNIT_ASSERT_EQUAL(SCTAB(3), pTabView2->GetViewData().GetTabNumber());
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testRemoveSheetViewHolderTable)
{
// Delete the sheet view holder table directly
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument& rDocument = *pModelObj->GetDocument();
// Setup views
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
{
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(0), pSheetViewManager->size());
}
// Create first sheet views
createNewSheetViewInCurrentView();
{
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewManager->size());
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
auto pSheetView1 = pSheetViewManager->get(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pSheetView1->getTableNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(2), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1_2"_ustr, rDocument.GetAllTableNames()[1]);
}
// Create second sheet views
createNewSheetViewInCurrentView();
{
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(3), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1_3"_ustr, rDocument.GetAllTableNames()[1]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1_2"_ustr, rDocument.GetAllTableNames()[2]);
// Sheet view must be present
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewManager->size());
auto pSheetView1 = pSheetViewManager->get(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(2), pSheetView1->getTableNumber());
auto pSheetView2 = pSheetViewManager->get(1);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pSheetView2->getTableNumber());
}
// Unhide the sheet view holder tables (or they won't be deleted)
{
uno::Sequence<beans::PropertyValue> aArgs(
comphelper::InitPropertySequence({ { "aTableName", uno::Any(u"Hoja1_3"_ustr) } }));
dispatchCommand(mxComponent, u".uno:Show"_ustr, aArgs);
}
{
uno::Sequence<beans::PropertyValue> aArgs(
comphelper::InitPropertySequence({ { "aTableName", uno::Any(u"Hoja1_2"_ustr) } }));
dispatchCommand(mxComponent, u".uno:Show"_ustr, aArgs);
}
// Delete the table
{
uno::Sequence<beans::PropertyValue> aArgs(
comphelper::InitPropertySequence({ { "Index", uno::Any(sal_uInt16(2)) } }));
dispatchCommand(mxComponent, u".uno:Remove"_ustr, aArgs);
}
{
CPPUNIT_ASSERT_EQUAL(SCTAB(2), rDocument.GetTableCount());
CPPUNIT_ASSERT_EQUAL(u"Hoja1"_ustr, rDocument.GetAllTableNames()[0]);
CPPUNIT_ASSERT_EQUAL(u"Hoja1_2"_ustr, rDocument.GetAllTableNames()[1]);
// Sheet view must be present
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewManager->size());
auto pSheetView1 = pSheetViewManager->get(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pSheetView1->getTableNumber());
// Not deleted but the sheet view is now null
auto pSheetView2 = pSheetViewManager->get(1);
CPPUNIT_ASSERT(!pSheetView2);
}
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSyncAfterSorting_DefaultViewSort)
{
// Two related scenarios tested:
// 1. Auto-filter is sorted in the default view, then the data is changed in a sheet view.
// In this case the sheet view is unsorted and the default view is sorted, so the data
// in the default view needs to be first unsorted so the correct cell is changed.
// 2. Continuation of scenario 1, where the default view is sorted again (ascending then
// descending order). In this case the sort orders must be combined correctly, so the
// change in the sheet view would still change the correct cell in default view.
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Setup views
ScTestViewCallback aSheetView;
ScTabViewShell* pTabViewSheetView = aSheetView.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aDefaultView;
ScTabViewShell* pTabViewDefaultView = aDefaultView.getTabViewShell();
CPPUNIT_ASSERT(pTabViewSheetView != pTabViewDefaultView);
CPPUNIT_ASSERT(aSheetView.getViewID() != aDefaultView.getViewID());
// Switch to Sheet View and Create
{
SfxLokHelper::setView(aSheetView.getViewID());
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabViewSheetView->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabViewDefaultView->GetViewData().GetTabNumber());
createNewSheetViewInCurrentView();
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabViewSheetView->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabViewDefaultView->GetViewData().GetTabNumber());
}
// Switch to Default View
{
SfxLokHelper::setView(aDefaultView.getViewID());
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabViewSheetView->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabViewDefaultView->GetViewData().GetTabNumber());
// Sort AutoFilter ascending
sortAscendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabViewSheetView->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabViewDefaultView->GetViewData().GetTabNumber());
// Check values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
typeCharsInCell(std::string("9"), 0, 1, pTabViewDefaultView, pModelObj);
// Check values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"9", u"4", u"5", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"9", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
// Sort AutoFilter AGAIN descending
sortDescendingForCell(u"A1");
// Check values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"9", u"7", u"5", u"4" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"9", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
typeCharsInCell(std::string("6"), 0, 3, pTabViewDefaultView, pModelObj);
// Check values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"9", u"7", u"6", u"4" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"6", u"9", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSyncAfterSorting_SheetViewSort)
{
// Two related scenarios tested:
//
// 1. Auto-filter is sorted in the sheet view, then the data is changed in a sheet view.
// In this case the default view is unsorted and the sheet view is sorted, so the data
// in the sheet view needs to be first unsorted so we get the correct position of the
// cell that needs to be changed.
// 2. Continuation of scenario 1, where the sheet view is sorted again (ascending then
// descending order). In this case the sort orders must be combined correctly.
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Setup views
ScTestViewCallback aSheetView;
ScTabViewShell* pTabViewSheetView = aSheetView.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aDefaultView;
ScTabViewShell* pTabViewDefaultView = aDefaultView.getTabViewShell();
CPPUNIT_ASSERT(pTabViewSheetView != pTabViewDefaultView);
CPPUNIT_ASSERT(aSheetView.getViewID() != aDefaultView.getViewID());
// Switch to Sheet View and Create
{
SfxLokHelper::setView(aSheetView.getViewID());
Scheduler::ProcessEventsToIdle();
// New Sheet view
createNewSheetViewInCurrentView();
// Expect the data is same in both
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
// Sort AutoFilter
sortAscendingForCell(u"A1");
// Check values - Sheet View
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
typeCharsInCell(std::string("9"), 0, 1, pTabViewSheetView, pModelObj);
// Sheet view is automatically sorted if it was sorted before.
// So it would be { u"9", u"4", u"5", u"7" }
// and then sorted to { u"4", u"5", u"7", u"9" }
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"7", u"9" }),
getValues(pTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"9", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
// Scenario 2
// Sort AutoFilter
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"9", u"7", u"5", u"4" }),
getValues(pTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"9", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
typeCharsInCell(std::string("6"), 0, 3, pTabViewSheetView, pModelObj);
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"6", u"9", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"9", u"7", u"6", u"4" }),
getValues(pTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testSyncAfterSorting_SortInDefaultAndSheetView)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Setup views
ScTestViewCallback aSheetView;
ScTabViewShell* pTabViewSheetView = aSheetView.getTabViewShell();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aDefaultView;
ScTabViewShell* pTabViewDefaultView = aDefaultView.getTabViewShell();
CPPUNIT_ASSERT(pTabViewSheetView != pTabViewDefaultView);
CPPUNIT_ASSERT(aSheetView.getViewID() != aDefaultView.getViewID());
// Switch to Sheet View and Create
{
SfxLokHelper::setView(aSheetView.getViewID());
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
// Sort AutoFilter
sortAscendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
}
// Switch to Default view and sort
{
SfxLokHelper::setView(aDefaultView.getViewID());
Scheduler::ProcessEventsToIdle();
// Sort AutoFilter
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(pTabViewSheetView, 0, 1, 4));
}
// Switch to Sheet view and set a value
{
SfxLokHelper::setView(aSheetView.getViewID());
Scheduler::ProcessEventsToIdle();
// Change "4" to "44"
typeCharsInCell(std::string("44"), 0, 2, pTabViewSheetView, pModelObj);
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"44", u"3" }),
getValues(pTabViewDefaultView, 0, 1, 4));
// Result in { u"3", u"44", u"5", u"7" } but we resort the sheet view immediately
// so we get { u"3", u"5", u"7", u"44" }.
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"5", u"7", u"44" }),
getValues(pTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSyncAfterSorting_SortInDefaultAndSheetView_Formulas)
{
// Input is an test file that uses formulas instead of values.
// This test checks if the sorting of the document behaves the same
// for formulas as values.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter_Formulas.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
setupViews();
// Switch to Sheet View and Create
{
switchToSheetView();
createNewSheetViewInCurrentView();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7", u"1", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 6));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7", u"1", u"6" }),
getValues(mpTabViewSheetView, 0, 1, 6));
// Sort AutoFilter
sortAscendingForCell(u"A1");
// No change to sorted values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7", u"1", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 6));
// Expect sorted values for the sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"1", u"3", u"4", u"5", u"6", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 6));
}
// Switch to Default view and sort
{
switchToDefaultView();
// Sort AutoFilter
sortDescendingForCell(u"A1");
// Expect sorted values for the default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3", u"1" }),
getValues(mpTabViewDefaultView, 0, 1, 6));
// Previously sorted values - no change
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"1", u"3", u"4", u"5", u"6", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 6));
}
// Switch to Sheet view and set a value
{
switchToSheetView();
// Change "4" to "44"
typeCharsInCell(std::string("=40+4"), 0, 3, mpTabViewSheetView, pModelObj);
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"44", u"3", u"1" }),
getValues(mpTabViewDefaultView, 0, 1, 6));
// Result in { u"1", u"3", u"4", u"5", u"6", u"7" } but we resort the sheet view immediately
// so we get { u"1", u"3", u"5", u"6", u"7", u"44" }.
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"1", u"3", u"5", u"6", u"7", u"44" }),
getValues(mpTabViewSheetView, 0, 1, 6));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSyncAfterSorting_SortInDefaultView_Attributes)
{
// Instead of the number, we set the attribute
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Create new sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to Sheet view and set "bold" text attribute to all cells in the auto filter one by one
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Current font weight state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A4 in sheet view -> A5 in default view
setCellBold(u"A4");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"B", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A2 in sheet view -> A4 in default view
setCellBold(u"A2");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"N", u"B", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A3 in sheet view -> A3 in default view
setCellBold(u"A3");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A5 in sheet view -> A2 in default view
setCellBold(u"A5");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set range A4:A5, which are A2, A5 in default view
setCellBold(u"A4:A5");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"B", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSyncAfterSorting_SortInDefaultAndSheetView_Attributes)
{
// Instead of the number, we set the attribute
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to Sheet view and set "bold" text attribute to all cells in the auto filter one by one
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Current font weight state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A5 in sheet view -> A2 in default view
setCellBold(u"A5");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A4 in sheet view -> A3 in default view
setCellBold(u"A4");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A3 in sheet view -> A4 in default view
setCellBold(u"A3");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Set A2 in sheet view -> A4 in default view
setCellBold(u"A2");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DefaultView_DeleteCellOperation)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"", u"", u"" }), getValues(pDocument, 0, 1, 4, 1));
// Switch to Sheet View and Create
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Switch to Default View
{
switchToDefaultView();
gotoCell(u"A3");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
OUString aExpected = expectedValues({ u"4", u"", u"3", u"7" });
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(pDocument, 0, 1, 4, 0));
OUString aExpectedSorted = expectedValues({ u"7", u"4", u"3", u"" });
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_SheetView_DeleteCellOperation)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"", u"", u"" }), getValues(pDocument, 0, 1, 4, 1));
// Switch to Sheet View and Create
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
gotoCell(u"A4");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
OUString aExpected = expectedValues({ u"", u"5", u"3", u"7" });
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(pDocument, 0, 1, 4, 0));
OUString aExpectedSorted = expectedValues({ u"7", u"5", u"3", u"" });
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DefaultView_DeleteContentOperation)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"", u"", u"" }), getValues(pDocument, 0, 1, 4, 1));
// Switch to Sheet View and Create
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Switch to Default View
{
switchToDefaultView();
gotoCell(u"A3:A4");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
OUString aExpected = expectedValues({ u"4", u"", u"", u"7" });
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(pDocument, 0, 1, 4, 0));
OUString aExpectedSorted = expectedValues({ u"7", u"4", u"", u"" });
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_SheetView_DeleteContentOperation)
{
// Create two views, and leave the second one current.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"", u"", u"" }), getValues(pDocument, 0, 1, 4, 1));
// Switch to Sheet View and Create
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
gotoCell(u"A3:A4");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
OUString aExpected = expectedValues({ u"", u"", u"3", u"7" });
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpected, getValues(pDocument, 0, 1, 4, 0));
OUString aExpectedSorted = expectedValues({ u"7", u"3", u"", u"" });
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(aExpectedSorted, getValues(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testCreateAndDeleteSheetView)
{
// Test that creating a sheet view, deleting it, creating a new one again,
// works without an issue.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Initial values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
// Create a sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Delete the sheet view
{
switchToSheetView();
removeSheetViewInCurrentView();
}
// Create a new sheet view again
{
switchToSheetView();
createNewSheetViewInCurrentView();
typeCharsInCell(std::string("99"), 0, 1, mpTabViewSheetView, pModelObj);
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"99", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSorting_NonAutoFilterRange)
{
// When we sort outside of the auto-filtered range, we sync the changes
// to all views. When sorting auto-filtered range, then we sort only the
// current view and don't sync.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter_Extended.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument& rDocument = *pModelObj->GetDocument();
setupViews();
// Check sorting when no sheet view was created yet
{
switchToDefaultView();
// Check we have no sheet view yet
auto pSheetViewManager = rDocument.GetSheetViewManager(SCTAB(0));
CPPUNIT_ASSERT(pSheetViewManager);
CPPUNIT_ASSERT(pSheetViewManager->isEmpty());
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"22", u"11", u"33" }),
getValues(mpTabViewDefaultView, 4, 1, 4));
// Select and sort the range - no sheet views yet
sortDescendingForCell(u"E2:E5");
// Check - we expect to be sorted descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"33", u"22", u"11" }),
getValues(mpTabViewDefaultView, 4, 1, 4));
}
// Check sheet view sorting
{
switchToSheetView();
createNewSheetViewInCurrentView();
// Check initial values of the E2:E5 range
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"33", u"22", u"11" }),
getValues(mpTabViewSheetView, 4, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"33", u"22", u"11" }),
getValues(mpTabViewDefaultView, 4, 1, 4));
// Sort the range ascending
sortAscendingForCell(u"E2:E5");
// Check - we expect both views to be sorted ascending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"11", u"22", u"33", u"44" }),
getValues(mpTabViewSheetView, 4, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"11", u"22", u"33", u"44" }),
getValues(mpTabViewDefaultView, 4, 1, 4));
// Check the auto-filtered values in column B
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewSheetView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
// Select the range of an auto-filtered range and sort ascending column B
sortAscendingForCell(u"B1");
// Check - only the sheet view should be sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"ccc", u"rrr", u"sss" }),
getValues(mpTabViewSheetView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testRemoveSheetViewAndSwitchTab)
{
// Test that creating a sheet view, removing it, switching to another tab
// and switching back shows the correct data (not an empty sheet).
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument& rDocument = *pModelObj->GetDocument();
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// Insert a second sheet
uno::Sequence<beans::PropertyValue> aArgsInsert(comphelper::InitPropertySequence(
{ { "Name", uno::Any(u"Sheet2"_ustr) }, { "Index", uno::Any(sal_Int16(2)) } }));
dispatchCommand(mxComponent, u".uno:Insert"_ustr, aArgsInsert);
CPPUNIT_ASSERT_EQUAL(SCTAB(2), rDocument.GetTableCount());
// Go back to the first sheet
pTabView1->SetTabNo(0);
// Put some data in the first sheet
rDocument.SetString(ScAddress(0, 0, 0), u"ABC"_ustr);
CPPUNIT_ASSERT_EQUAL(u"ABC"_ustr, rDocument.GetString(ScAddress(0, 0, 0)));
// Create a sheet view on the first sheet
createNewSheetViewInCurrentView();
// Verify we are now on the sheet view tab
CPPUNIT_ASSERT(rDocument.GetTableSheetViewID(pTabView1->GetViewData().GetTabNumber())
!= sc::DefaultSheetViewID);
// Remove the sheet view
removeSheetViewInCurrentView();
// Should be back on the default tab (first sheet)
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
// Switch to the second sheet
pTabView1->SetTabNo(1);
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
// Switch back to the first sheet
pTabView1->SetTabNo(0);
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
// Verify the data is still visible (not an empty sheet)
CPPUNIT_ASSERT_EQUAL(u"ABC"_ustr, rDocument.GetString(ScAddress(0, 0, 0)));
}
CPPUNIT_TEST_FIXTURE(SheetViewTest, testNewViewOpensInDefaultView)
{
// Test that opening a new view while on a sheet view tab opens the new
// view on the default tab, not on the sheet view tab.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
// Setup first view
ScTestViewCallback aView1;
ScTabViewShell* pTabView1 = aView1.getTabViewShell();
// Verify we start on tab 0 (default)
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView1->GetViewData().GetTabNumber());
// Create a sheet view - view 1 should move to the sheet view tab
createNewSheetViewInCurrentView();
// Create a new view - simulates a new user or new window
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView2;
ScTabViewShell* pTabView2 = aView2.getTabViewShell();
CPPUNIT_ASSERT(pTabView1 != pTabView2);
// View 1 should be open on sheet view tab
CPPUNIT_ASSERT_EQUAL(SCTAB(1), pTabView1->GetViewData().GetTabNumber());
CPPUNIT_ASSERT(pTabView1->GetViewData().GetSheetViewID() != sc::DefaultSheetViewID);
// The view 2 should open on the default tab
CPPUNIT_ASSERT_EQUAL(SCTAB(0), pTabView2->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(sc::DefaultSheetViewID, pTabView2->GetViewData().GetSheetViewID());
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DefaultView_ClearItemsOperation)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Switch to Sheet View and Create, sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Default view: 4, 5, 3, 7
// Sheet view: 7, 5, 4, 3 (sorted)
// Set A2 and A3 bold in sheet view
{
switchToSheetView();
setCellBold(u"A2");
setCellBold(u"A3");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Switch to Default View and call ClearItems on A4:A5
{
switchToDefaultView();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(0, true);
aMark.SetMarkArea(ScRange(0, 3, 0, 0, 4, 0));
sal_uInt16 aWhich[] = { ATTR_FONT_WEIGHT, 0 };
pDocShell->GetDocFunc().ClearItems(aMark, aWhich, true);
}
// Default view: A4 is N so no change, A5 should change from B -> N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
// Sheet view: A4 -> A5 and A5 -> A2, so A2 should change from B -> N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_SheetView_ClearItemsOperation)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Switch to Sheet View and Create, sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Default view: 4, 5, 3, 7
// Sheet view: 7, 5, 4, 3 (sorted)
// Set all cells bold from sheet view
{
switchToSheetView();
setCellBold(u"A2:A5");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Call ClearItems on A3:A4 on sheet view
{
switchToSheetView();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(1, true);
aMark.SetMarkArea(ScRange(0, 2, 1, 0, 3, 1));
sal_uInt16 aWhich[] = { ATTR_FONT_WEIGHT, 0 };
pDocShell->GetDocFunc().ClearItems(aMark, aWhich, true);
}
// Sheet view: A3, A4 changed from B -> N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"N", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Default view: A3 -> A3, A4 -> A2, so A2 and A3 from B -> N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"B", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_AutoFormat_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to sheet view and apply AutoFormat
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Initial text weights
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Apply AutoFormat (format 0) to A2:A5 on default view (index 0)
pDocShell->GetDocFunc().AutoFormat(ScRange(0, 1, 0, 0, 4, 0), nullptr, 0, true);
// Values should remain unchanged after AutoFormat
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// First row is defined as header row, so we can exploit that to test resorting.
// Default view: AutoFormat applies blue to first row, gray to others
//FIXME
//CPPUNIT_ASSERT_EQUAL(expectedValues({ u"000080", u"cccccc", u"cccccc", u"cccccc" }),
// getBackgroundColor(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ffffff", u"ffffff", u"ffffff", u"ffffff" }),
getBackgroundColor(pDocument, 0, 1, 4, 0));
// Sheet view: background colors synced and re-sorted
//FIXME
//CPPUNIT_ASSERT_EQUAL(expectedValues({ u"cccccc", u"cccccc", u"cccccc", u"000080" }),
// getBackgroundColor(pDocument, 0, 1, 4, 1));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ffffff", u"ffffff", u"ffffff", u"ffffff" }),
getBackgroundColor(pDocument, 0, 1, 4, 1));
}
// AutoFormat on the sheet view should be blocked - input intersects autofilter
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
bool bResult = pDocShell->GetDocFunc().AutoFormat(
ScRange(0, 1, nSheetViewTab, 0, 4, nSheetViewTab), nullptr, 1, true);
// Returns false because the running is blocked
CPPUNIT_ASSERT(!bResult);
// Background colors should remain unchanged
//FIXME
//CPPUNIT_ASSERT_EQUAL(expectedValues({ u"000080", u"cccccc", u"cccccc", u"cccccc" }),
// getBackgroundColor(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ffffff", u"ffffff", u"ffffff", u"ffffff" }),
getBackgroundColor(pDocument, 0, 1, 4, 0));
//FIXME
//CPPUNIT_ASSERT_EQUAL(expectedValues({ u"cccccc", u"cccccc", u"cccccc", u"000080" }),
// getBackgroundColor(pDocument, 0, 1, 4, nSheetViewTab));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ffffff", u"ffffff", u"ffffff", u"ffffff" }),
getBackgroundColor(pDocument, 0, 1, 4, nSheetViewTab));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_MultipleOps_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
// Set up 2 views: for default view and for sheet view 1
setupViews();
// Add another view for sheet view 2
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aSheetView2;
int nSheetView2 = aSheetView2.getViewID();
// Set up data, which will be duplicated to default view and sheet views
// A1 = 1 - input cell
// B1 = =A1*10 - formula
// variable inputs:
// A3 = 2
// A4 = 3
// A5 = 4
pDocument->SetValue(ScAddress(0, 0, 0), 1);
pDocument->SetString(ScAddress(1, 0, 0), u"=A1*10"_ustr);
pDocument->SetValue(ScAddress(0, 2, 0), 2);
pDocument->SetValue(ScAddress(0, 3, 0), 3);
pDocument->SetValue(ScAddress(0, 4, 0), 4);
// Check value
CPPUNIT_ASSERT_EQUAL(10.0, pDocument->GetValue(ScAddress(1, 0, 0)));
// Create sheet view 1
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Create sheet view 2
{
SfxLokHelper::setView(nSheetView2);
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
}
// Perform Multiple Operations from sheet view 1
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScTabOpParam aParam;
aParam.aRefFormulaCell = ScRefAddress(1, 0, nSheetViewTab);
aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
aParam.aRefColCell = ScRefAddress(0, 0, nSheetViewTab);
aParam.meMode = ScTabOpParam::Column;
// Apply Multiple Operations on A3:B5 — fills B3:B5 with 20, 30, 40
pDocShell->GetDocFunc().TabOp(ScRange(0, 2, nSheetViewTab, 1, 4, nSheetViewTab), nullptr,
aParam, true, true);
// Verify results in default view (index 0)
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(u"20"_ustr, pDocument->GetString(ScAddress(1, 2, nDefaultViewTab)));
CPPUNIT_ASSERT_EQUAL(u"30"_ustr, pDocument->GetString(ScAddress(1, 3, nDefaultViewTab)));
CPPUNIT_ASSERT_EQUAL(u"40"_ustr, pDocument->GetString(ScAddress(1, 4, nDefaultViewTab)));
// Verify same results in sheet view 1
CPPUNIT_ASSERT_EQUAL(u"20"_ustr, pDocument->GetString(ScAddress(1, 2, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(u"30"_ustr, pDocument->GetString(ScAddress(1, 3, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(u"40"_ustr, pDocument->GetString(ScAddress(1, 4, nSheetViewTab)));
// Verify same results in sheet view 2
SCTAB nSheetView2Tab = aSheetView2.getTabViewShell()->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(u"20"_ustr, pDocument->GetString(ScAddress(1, 2, nSheetView2Tab)));
CPPUNIT_ASSERT_EQUAL(u"30"_ustr, pDocument->GetString(ScAddress(1, 3, nSheetView2Tab)));
CPPUNIT_ASSERT_EQUAL(u"40"_ustr, pDocument->GetString(ScAddress(1, 4, nSheetView2Tab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ReplaceNote_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Replace note from default view on C1
{
switchToDefaultView();
OUString aAuthor(u"Test Author"_ustr);
OUString aDate(u"2026-03-08"_ustr);
pDocShell->GetDocFunc().ReplaceNote(ScAddress(2, 0, 0), u"Default Note"_ustr, &aAuthor,
&aDate, true);
// Verify note on default view
ScPostIt* pNoteDefault = pDocument->GetNote(ScAddress(2, 0, 0));
CPPUNIT_ASSERT(pNoteDefault);
CPPUNIT_ASSERT_EQUAL(u"Default Note"_ustr, pNoteDefault->GetText());
// Verify note synced to sheet view
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScPostIt* pNoteSheet = pDocument->GetNote(ScAddress(2, 0, nSheetViewTab));
CPPUNIT_ASSERT(pNoteSheet);
CPPUNIT_ASSERT_EQUAL(u"Default Note"_ustr, pNoteSheet->GetText());
}
// Replace note from sheet view on A2
// Sheet view ascending: A2=3, A3=4, A4=5, A5=7
// Default view descending: A2=7, A3=5, A4=4, A5=3
// A2 on sheet view (value 3) maps to A5 on default view (value 3)
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
OUString aAuthor(u"Sheet Author"_ustr);
OUString aDate(u"2026-03-08"_ustr);
pDocShell->GetDocFunc().ReplaceNote(ScAddress(0, 1, nSheetViewTab), u"Sheet Note"_ustr,
&aAuthor, &aDate, true);
// Verify note on default view at A5 (value 3)
ScPostIt* pNoteDefault = pDocument->GetNote(ScAddress(0, 4, 0));
CPPUNIT_ASSERT(pNoteDefault);
CPPUNIT_ASSERT_EQUAL(u"Sheet Note"_ustr, pNoteDefault->GetText());
// Verify note on sheet view at A2 (value 3)
ScPostIt* pNoteSheet = pDocument->GetNote(ScAddress(0, 1, nSheetViewTab));
CPPUNIT_ASSERT(pNoteSheet);
CPPUNIT_ASSERT_EQUAL(u"Sheet Note"_ustr, pNoteSheet->GetText());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_MoveBlock_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Put data in column C (outside autofilter range) on default view
pDocument->SetString(ScAddress(2, 0, 0), u"Hello"_ustr);
pDocument->SetValue(ScAddress(2, 1, 0), 10.0);
pDocument->SetValue(ScAddress(2, 2, 0), 20.0);
// Move block from default view: C1:C3 -> D1:D3 (outside autofilter)
{
switchToDefaultView();
bool bMoved = pDocShell->GetDocFunc().MoveBlock(
ScRange(2, 0, 0, 2, 2, 0), ScAddress(3, 0, 0), true, true, false, true);
CPPUNIT_ASSERT(bMoved);
// Verify on default view
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(2, 0, 0)));
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(3, 0, 0)));
CPPUNIT_ASSERT_EQUAL(10.0, pDocument->GetValue(ScAddress(3, 1, 0)));
CPPUNIT_ASSERT_EQUAL(20.0, pDocument->GetValue(ScAddress(3, 2, 0)));
// Verify synced on sheet view
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(2, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(3, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(10.0, pDocument->GetValue(ScAddress(3, 1, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(20.0, pDocument->GetValue(ScAddress(3, 2, nSheetViewTab)));
}
// Move block from sheet view: D1:D3 -> E1:E3 (outside autofilter)
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
bool bMoved = pDocShell->GetDocFunc().MoveBlock(
ScRange(3, 0, nSheetViewTab, 3, 2, nSheetViewTab), ScAddress(4, 0, nSheetViewTab), true,
true, false, true);
CPPUNIT_ASSERT(bMoved);
// Verify on default view
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(3, 0, 0)));
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(4, 0, 0)));
CPPUNIT_ASSERT_EQUAL(10.0, pDocument->GetValue(ScAddress(4, 1, 0)));
CPPUNIT_ASSERT_EQUAL(20.0, pDocument->GetValue(ScAddress(4, 2, 0)));
// Verify synced on sheet view
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(3, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(4, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(10.0, pDocument->GetValue(ScAddress(4, 1, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(20.0, pDocument->GetValue(ScAddress(4, 2, nSheetViewTab)));
}
// Move block from default view inside autofilter: A2:A3 -> C2:C3
{
switchToDefaultView();
bool bMoved = pDocShell->GetDocFunc().MoveBlock(
ScRange(0, 1, 0, 0, 2, 0), ScAddress(2, 1, 0), true, true, false, true);
CPPUNIT_ASSERT(bMoved);
// Verify A2:A3 cleared and C2:C3 has values on default view
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(0, 1, 0)));
CPPUNIT_ASSERT_EQUAL(OUString(), pDocument->GetString(ScAddress(0, 2, 0)));
CPPUNIT_ASSERT_EQUAL(7.0, pDocument->GetValue(ScAddress(2, 1, 0)));
CPPUNIT_ASSERT_EQUAL(5.0, pDocument->GetValue(ScAddress(2, 2, 0)));
// Verify autofilter column on both views
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"", u"" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// Undo the move inside autofilter
{
switchToDefaultView();
undo();
// Verify autofilter values restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// Move block on the sheet view should be blocked as source intersects autofilter
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Try to move A2:A3 -> C2:C3 on sheet view tab
bool bResult = pDocShell->GetDocFunc().MoveBlock(
ScRange(0, 1, nSheetViewTab, 0, 2, nSheetViewTab), ScAddress(2, 1, nSheetViewTab), true,
true, false, true);
CPPUNIT_ASSERT(!bResult);
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_EnterMatrix_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to sheet view and enter matrix formulas
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Modify Sheet View A3 : 4 -> ={14} (matrix formula)
pDocShell->GetDocFunc().EnterMatrix(ScRange(0, 2, 1, 0, 2, 1), nullptr, nullptr,
u"={14}"_ustr, true, true, OUString(),
formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
// Default view: translated A3 to A4 : 4 -> 14
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"14", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view: A3 : 4 -> 14, and resort
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"5", u"7", u"14" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Modify Default view A2 : 7 -> ={17} (matrix formula)
pDocShell->GetDocFunc().EnterMatrix(ScRange(0, 1, 0, 0, 1, 0), nullptr, nullptr,
u"={17}"_ustr, true, true, OUString(),
formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
// Default view: A2 : 7 -> 17
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"17", u"5", u"14", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view: translated A2 to A4 : 7 -> 17, and resort
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"5", u"14", u"17" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_FillSimple_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to sheet view and fill simple
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// FillSimple A2:A3 on default view (index 0), FILL_TO_BOTTOM
// Copies A2 to A3 (value 7)
pDocShell->GetDocFunc().FillSimple(ScRange(0, 1, 0, 0, 2, 0), nullptr, FILL_TO_BOTTOM,
true);
// Default view: A3 changed from 5 -> 7
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"7", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view: synced and re-sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"7", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// Try to FillSimple on the sheet view — should be blocked because it intersects autofilter
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
bool bResult = pDocShell->GetDocFunc().FillSimple(
ScRange(0, 1, nSheetViewTab, 0, 2, nSheetViewTab), nullptr, FILL_TO_BOTTOM, true);
CPPUNIT_ASSERT(!bResult);
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"7", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"7", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_FillSeries_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to sheet view and fill series
{
switchToSheetView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// FillSeries A2:A5 on default view (index 0), FILL_TO_BOTTOM, FILL_LINEAR
// start=40, step=-10: fills 40, 30, 20, 10
pDocShell->GetDocFunc().FillSeries(ScRange(0, 1, 0, 0, 4, 0), nullptr, FILL_TO_BOTTOM,
FILL_LINEAR, FILL_DAY, 40.0, -10.0, 0.0, true);
// Default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"40", u"30", u"20", u"10" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view: synced and re-sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"10", u"20", u"30", u"40" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// Try to FillSeries on the sheet view — should be blocked because it intersects autofilter
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
bool bResult = pDocShell->GetDocFunc().FillSeries(
ScRange(0, 1, nSheetViewTab, 0, 4, nSheetViewTab), nullptr, FILL_TO_BOTTOM, FILL_LINEAR,
FILL_DAY, 100.0, 100.0, 0.0, true);
CPPUNIT_ASSERT(!bResult);
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"40", u"30", u"20", u"10" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"10", u"20", u"30", u"40" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_FillAuto_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Put data in column C (outside autofilter range) on the default view
rDocument.SetValue(ScAddress(2, 0, 0), 1.0);
rDocument.SetValue(ScAddress(2, 1, 0), 2.0);
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// FillAuto on the default view inside autofilter
{
switchToDefaultView();
// Current state default view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Current state sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// FillAuto source A2:A3 on default view (index 0), FILL_TO_BOTTOM, nCount=2
// Source: 7, 5 (step -2), fills A4:A5 with 3, 1
ScRange aFillRange(0, 1, 0, 0, 2, 0);
pDocShell->GetDocFunc().FillAuto(aFillRange, nullptr, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY,
2, 1.0, 100.0, true, true);
// Default view: 7, 5, 3, 1
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"3", u"1" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view: synced and re-sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"1", u"3", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// Try to FillAuto on the sheet view — should be blocked because it intersects autofilter
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScRange aFillRange(0, 1, nSheetViewTab, 0, 2, nSheetViewTab);
bool bResult = pDocShell->GetDocFunc().FillAuto(
aFillRange, nullptr, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, 2, 1.0, 100.0, true, true);
CPPUNIT_ASSERT(!bResult);
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"3", u"1" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"1", u"3", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
// FillAuto from sheet view outside autofilter (column C) should work
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScRange aFillRange(2, 0, nSheetViewTab, 2, 1, nSheetViewTab);
bool bResult = pDocShell->GetDocFunc().FillAuto(
aFillRange, nullptr, FILL_TO_BOTTOM, FILL_AUTO, FILL_DAY, 2, 1.0, 100.0, true, true);
CPPUNIT_ASSERT(bResult);
// Verify the returned range is on the sheet view tab,
// because the caller uses it for MarkRange on the current view
CPPUNIT_ASSERT_EQUAL(nSheetViewTab, aFillRange.aStart.Tab());
CPPUNIT_ASSERT_EQUAL(nSheetViewTab, aFillRange.aEnd.Tab());
// Verify fill result on default view: C1=1, C2=2, C3=3, C4=4
CPPUNIT_ASSERT_EQUAL(3.0, rDocument.GetValue(ScAddress(2, 2, 0)));
CPPUNIT_ASSERT_EQUAL(4.0, rDocument.GetValue(ScAddress(2, 3, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT_EQUAL(3.0, rDocument.GetValue(ScAddress(2, 2, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(4.0, rDocument.GetValue(ScAddress(2, 3, nSheetViewTab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_TransliterateText_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter_Extended.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Default view at Column B: aaa, rrr, ccc, sss
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
// Sheet view at Column B: sss, ccc, rrr, aaa
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"sss", u"ccc", u"rrr", u"aaa" }),
getValues(mpTabViewSheetView, 1, 1, 4));
// Transliterate B2:B5 to uppercase from sheet view
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(nSheetViewTab, true);
aMark.SetMarkArea(ScRange(1, 1, nSheetViewTab, 1, 4, nSheetViewTab));
pDocShell->GetDocFunc().TransliterateText(aMark, TransliterationFlags::LOWERCASE_UPPERCASE,
true);
}
// Sheet view: text is uppercased, ascending order
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"SSS", u"CCC", u"RRR", u"AAA" }),
getValues(mpTabViewSheetView, 1, 1, 4));
// Default view: synced, descending order, uppercased
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"AAA", u"RRR", u"CCC", u"SSS" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ConvertFormulaToValue_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
// Set up formulas
pDocument->SetString(ScAddress(0, 0, 0), u"=10+20"_ustr);
pDocument->SetString(ScAddress(0, 1, 0), u"=30+40"_ustr);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Verify formulas exist on both tabs
CPPUNIT_ASSERT(pDocument->GetFormulaCell(ScAddress(0, 0, 0)));
CPPUNIT_ASSERT(pDocument->GetFormulaCell(ScAddress(0, 1, 0)));
CPPUNIT_ASSERT(pDocument->GetFormulaCell(ScAddress(0, 0, nSheetViewTab)));
CPPUNIT_ASSERT(pDocument->GetFormulaCell(ScAddress(0, 1, nSheetViewTab)));
// Convert formulas to values from sheet view
{
switchToSheetView();
pDocShell->GetDocFunc().ConvertFormulaToValue(
ScRange(0, 0, nSheetViewTab, 0, 1, nSheetViewTab), false);
}
// Sheet view: formula converted, values preserved
CPPUNIT_ASSERT(!pDocument->GetFormulaCell(ScAddress(0, 0, nSheetViewTab)));
CPPUNIT_ASSERT(!pDocument->GetFormulaCell(ScAddress(0, 1, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(0, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(70.0, pDocument->GetValue(ScAddress(0, 1, nSheetViewTab)));
// Default view: synced — formulas also converted
CPPUNIT_ASSERT(!pDocument->GetFormulaCell(ScAddress(0, 0, 0)));
CPPUNIT_ASSERT(!pDocument->GetFormulaCell(ScAddress(0, 1, 0)));
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(0, 0, 0)));
CPPUNIT_ASSERT_EQUAL(70.0, pDocument->GetValue(ScAddress(0, 1, 0)));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_SetNoteText_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Default view descending: 7, 5, 4, 3
// Sheet view ascending: 3, 4, 5, 7
// Set note on A2 in sheet view
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
pDocShell->GetDocFunc().SetNoteText(ScAddress(0, 1, nSheetViewTab), u"Hello Note"_ustr,
true);
}
// Sheet view: note on A2
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScPostIt* pNoteSheet = pDocument->GetNote(ScAddress(0, 1, nSheetViewTab));
CPPUNIT_ASSERT(pNoteSheet);
CPPUNIT_ASSERT_EQUAL(u"Hello Note"_ustr, pNoteSheet->GetText());
// Default view: note synced to the cell A5
ScPostIt* pNoteDefault = pDocument->GetNote(ScAddress(0, 4, 0));
CPPUNIT_ASSERT(pNoteDefault);
CPPUNIT_ASSERT_EQUAL(u"Hello Note"_ustr, pNoteDefault->GetText());
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ChangeIndent_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Switch to Sheet view and change indent on A2:A3
// Sheet view ascending: 3, 4, 5, 7
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(nSheetViewTab, true);
aMark.SetMarkArea(ScRange(0, 1, nSheetViewTab, 0, 2, nSheetViewTab));
aMark.MarkToMulti();
pDocShell->GetDocFunc().ChangeIndent(aMark, true, true);
}
// Sheet view: A2,A3 indented, A4,A5 not
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(pDocument, 0, 1, 4, 1));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"Y", u"Y", u"N", u"N" }),
getIndent(pDocument, 0, 1, 4, 1));
// Default view: A4,A5 indented (A4 is A3, A5 is A2)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"Y", u"Y" }),
getIndent(pDocument, 0, 1, 4, 0));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testUndo_DefaultView_DeleteContent)
{
// Test undo of DeleteContents from the default view with a sheet view present.
// After undo, both views should be consistent.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Switch to Sheet View and Create, sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Default view and sheet view state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Clear content in default view
{
switchToDefaultView();
gotoCell(u"A4");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
// State after delete
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"" }),
getValues(pDocument, 0, 1, 4, 1));
// Undo from default view
{
switchToDefaultView();
undo();
}
// After undo: default should be restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
// Sheet view should also be synced back
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Redo from default view
{
switchToDefaultView();
redo();
}
// State after delete
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"" }),
getValues(pDocument, 0, 1, 4, 1));
// Undo from default view
{
switchToDefaultView();
undo();
}
// Default view and sheet view state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Clear content in sheet view
{
switchToSheetView();
gotoCell(u"A4");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
}
// State after delete
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
// Sheet view was resorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"3", u"" }),
getValues(pDocument, 0, 1, 4, 1));
// Undo from sheet view
{
switchToSheetView();
undo();
}
// Default view and sheet view state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(pDocument, 0, 1, 4, 1));
// Redo from sheet view
{
switchToSheetView();
redo();
}
// State after delete
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"", u"5", u"3", u"7" }),
getValues(pDocument, 0, 1, 4, 0));
// Sheet view was resorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"3", u"" }),
getValues(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testUndo_DefaultView_ClearItems)
{
// Test undo of ClearItems (bold removal) from the default view with a sheet view present.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Switch to Sheet View and Create, sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Set A2 and A3 bold in sheet view (values 7 and 5)
{
switchToSheetView();
setCellBold(u"A2");
setCellBold(u"A3");
}
// Default: N, B, N, B (rows with 5 and 7 are bold)
// Sheet: B, B, N, N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Clear bold on A4:A5 in default view (values 3 and 7)
{
switchToDefaultView();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(0, true);
aMark.SetMarkArea(ScRange(0, 3, 0, 0, 4, 0));
sal_uInt16 aWhich[] = { ATTR_FONT_WEIGHT, 0 };
pDocShell->GetDocFunc().ClearItems(aMark, aWhich, true);
}
// After clear: A5(7) was bold -> now N
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Undo from default view
{
switchToDefaultView();
undo();
}
// After undo: default should be restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
// Sheet view should also be synced back
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Redo from default view
{
switchToDefaultView();
redo();
}
// After redo: same as after clear
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Undo from default view
{
switchToDefaultView();
undo();
}
// Default view and sheet view state restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Clear bold on A2:A3 in sheet view
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScMarkData aMark(pDocument->GetSheetLimits());
aMark.SelectTable(nSheetViewTab, true);
aMark.SetMarkArea(ScRange(0, 1, nSheetViewTab, 0, 2, nSheetViewTab));
sal_uInt16 aWhich[] = { ATTR_FONT_WEIGHT, 0 };
pDocShell->GetDocFunc().ClearItems(aMark, aWhich, true);
}
// After clear from sheet view: all bold cleared
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Undo from sheet view
{
switchToSheetView();
undo();
}
// Default view and sheet view state restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"B", u"N", u"B" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"B", u"B", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
// Redo from sheet view
{
switchToSheetView();
redo();
}
// After redo: all bold cleared
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 0));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"N", u"N", u"N", u"N" }),
getTextWeight(pDocument, 0, 1, 4, 1));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testUndo_DefaultView_FillSeries)
{
// Test undo of FillSeries from default view with a sheet view present.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
setupViews();
// Create new sheet view and sort autofilter ascending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortAscendingForCell(u"A1");
}
// Sort autofilter descending in default view
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Current statw default view and sheet view
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// FillSeries: start=40, step=-10 -> 40, 30, 20, 10
{
switchToDefaultView();
pDocShell->GetDocFunc().FillSeries(ScRange(0, 1, 0, 0, 4, 0), nullptr, FILL_TO_BOTTOM,
FILL_LINEAR, FILL_DAY, 40.0, -10.0, 0.0, true);
}
// After fill: the default view should have the decreasing values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"40", u"30", u"20", u"10" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view has ascending sorting
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"10", u"20", u"30", u"40" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Undo from default view
{
switchToDefaultView();
undo();
}
// After undo: default and sheet view should be restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Redo from default view
{
switchToDefaultView();
redo();
}
// Values are back
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"40", u"30", u"20", u"10" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"10", u"20", u"30", u"40" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_MergeCells_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
// Set up 3 views: default view, sheet view 1, sheet view 2
setupViews();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView3;
int nView3ID = aView3.getViewID();
// Put data in cells that will be merged
pDocument->SetString(ScAddress(0, 0, 0), u"Hello"_ustr);
pDocument->SetValue(ScAddress(1, 0, 0), 42.0);
// Create sheet view 1
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Create sheet view 2
{
SfxLokHelper::setView(nView3ID);
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
}
// Merge cells A1:B2 from sheet view 1
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScCellMergeOption aMergeOption(0, 0, 1, 1);
aMergeOption.maTabs.insert(nSheetViewTab);
bool bMerged = pDocShell->GetDocFunc().MergeCells(aMergeOption, false, true, true);
CPPUNIT_ASSERT(bMerged);
// Verify on default view: A1 should have the content, merge flag set
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(0, 0, 0)));
CPPUNIT_ASSERT(pDocument->HasAttrib(0, 0, 0, 1, 1, 0,
HasAttrFlags::Merged | HasAttrFlags::Overlapped));
// Verify on sheet view 1
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(0, 0, nSheetViewTab)));
CPPUNIT_ASSERT(pDocument->HasAttrib(0, 0, nSheetViewTab, 1, 1, nSheetViewTab,
HasAttrFlags::Merged | HasAttrFlags::Overlapped));
// Verify on sheet view 2
SCTAB nSheetView2Tab = aView3.getTabViewShell()->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, pDocument->GetString(ScAddress(0, 0, nSheetView2Tab)));
CPPUNIT_ASSERT(pDocument->HasAttrib(0, 0, nSheetView2Tab, 1, 1, nSheetView2Tab,
HasAttrFlags::Merged | HasAttrFlags::Overlapped));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_InsertNameList_DefaultAndSheetView)
{
// Insert name list inserts the list of the named ranges into the sheet.
// We should sync that to all sheet views
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
// Set up 3 views: default view, sheet view 1, sheet view 2
setupViews();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView3;
int nView3ID = aView3.getViewID();
// Add a named range so InsertNameList has something to insert
ScRangeName& rGlobalNames = pDocument->GetRangeName();
ScRangeData* pRangeData = new ScRangeData(*pDocument, u"TestRange"_ustr, u"$A$10:$B$20"_ustr);
rGlobalNames.insert(std::unique_ptr<ScRangeData>(pRangeData));
// Create sheet view 1
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Create sheet view 2
{
SfxLokHelper::setView(nView3ID);
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
}
// Insert name list from sheet view 1 at A1
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
bool bInserted
= pDocShell->GetDocFunc().InsertNameList(ScAddress(0, 0, nSheetViewTab), true);
CPPUNIT_ASSERT(bInserted);
// Verify on default view: A1 should have the range name
CPPUNIT_ASSERT_EQUAL(u"TestRange"_ustr, pDocument->GetString(ScAddress(0, 0, 0)));
// Verify synced to sheet views
CPPUNIT_ASSERT_EQUAL(u"TestRange"_ustr,
pDocument->GetString(ScAddress(0, 0, nSheetViewTab)));
SCTAB nSheetView2Tab = aView3.getTabViewShell()->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(u"TestRange"_ustr,
pDocument->GetString(ScAddress(0, 0, nSheetView2Tab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_SubTotals_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument* pDocument = pModelObj->GetDocument();
// Set up 3 views: default view, sheet view 1, sheet view 2
setupViews();
SfxLokHelper::createView();
Scheduler::ProcessEventsToIdle();
ScTestViewCallback aView3;
int nView3ID = aView3.getViewID();
// Set up data with headers and values for subtotals
// Column A: Category, Column B: Values
pDocument->SetString(ScAddress(0, 0, 0), u"Category"_ustr);
pDocument->SetString(ScAddress(1, 0, 0), u"Value"_ustr);
pDocument->SetString(ScAddress(0, 1, 0), u"A"_ustr);
pDocument->SetString(ScAddress(0, 2, 0), u"A"_ustr);
pDocument->SetString(ScAddress(0, 3, 0), u"B"_ustr);
pDocument->SetValue(ScAddress(1, 1, 0), 10.0);
pDocument->SetValue(ScAddress(1, 2, 0), 20.0);
pDocument->SetValue(ScAddress(1, 3, 0), 30.0);
// Create a DB area for the data range
ScDBData* pDBData = new ScDBData(u"TestDB"_ustr, 0, 0, 0, 1, 3);
pDocument->GetDBCollection()->getNamedDBs().insert(std::unique_ptr<ScDBData>(pDBData));
// Create sheet view 1
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
// Create sheet view 2
{
SfxLokHelper::setView(nView3ID);
Scheduler::ProcessEventsToIdle();
createNewSheetViewInCurrentView();
}
// Run subtotals from sheet view 1
{
switchToSheetView();
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
ScSubTotalParam aParam;
aParam.bReplace = false;
aParam.bDoSort = false;
aParam.nCol1 = 0;
aParam.nRow1 = 0;
aParam.nCol2 = 1;
aParam.nRow2 = 3;
aParam.aGroups[0].bActive = true;
aParam.aGroups[0].nField = 0; // group by column A
SCCOL nSubCols[] = { 1 }; // subtotal on column B
ScSubTotalFunc nFunctions[] = { SUBTOTAL_FUNC_SUM };
aParam.SetSubTotals(0, nSubCols, nFunctions, 1);
ScDBDocFunc aDBDocFunc(*pDocShell);
aDBDocFunc.DoSubTotals(nSheetViewTab, aParam, true, true);
// After subtotals, new rows are inserted with subtotal results
// Row layout: Header(0), A 10(1), A 20(2), A Sum(3), B 30(4), B Sum(5), Grand Total(6)
// Verify on default view
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 3, 0)));
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 5, 0)));
CPPUNIT_ASSERT_EQUAL(60.0, pDocument->GetValue(ScAddress(1, 6, 0)));
// Verify sync to sheet view 1
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 3, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 5, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(60.0, pDocument->GetValue(ScAddress(1, 6, nSheetViewTab)));
// Verify sync to sheet view 2
SCTAB nSheetView2Tab = aView3.getTabViewShell()->GetViewData().GetTabNumber();
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 3, nSheetView2Tab)));
CPPUNIT_ASSERT_EQUAL(30.0, pDocument->GetValue(ScAddress(1, 5, nSheetView2Tab)));
CPPUNIT_ASSERT_EQUAL(60.0, pDocument->GetValue(ScAddress(1, 6, nSheetView2Tab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DrawObject_DefaultAndSheetView)
{
// Checks that draw objects are synchronized between the default view and sheet views.
// This checks inserting, moving and deleting draw objects.
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocument* pDocument = pModelObj->GetDocument();
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
switchToDefaultView();
ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
CPPUNIT_ASSERT(pDrawLayer);
SCTAB nDefaultViewTable = mpTabViewDefaultView->GetViewData().GetTabNumber();
SCTAB nSheetViewTable = mpTabViewSheetView->GetViewData().GetTabNumber();
SdrPage* pDefaultViewPage = pDrawLayer->GetPage(sal_uInt16(nDefaultViewTable));
SdrPage* pSheetViewPage = pDrawLayer->GetPage(sal_uInt16(nSheetViewTable));
CPPUNIT_ASSERT(pDefaultViewPage);
CPPUNIT_ASSERT(pSheetViewPage);
const tools::Rectangle aRectangle1(1000, 1000, 3000, 2000);
const tools::Rectangle aRectangle2(5000, 5000, 7000, 6000);
// Initially no objects on either page
CPPUNIT_ASSERT_EQUAL(size_t(0), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(size_t(0), pSheetViewPage->GetObjCount());
// Insert shape on default view
{
switchToDefaultView();
insertDrawObject(pDrawLayer, aRectangle1);
// Default view page has 1 object at correct position
CPPUNIT_ASSERT_EQUAL(size_t(1), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pDefaultViewPage->GetObj(0)->GetLogicRect());
// Sheet view page should also have 1 object at same position after sync
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pSheetViewPage->GetObj(0)->GetLogicRect());
}
// Insert shape on sheet view
{
switchToSheetView();
insertDrawObject(pDrawLayer, aRectangle2);
// Default view page has 2 objects at correct positions
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pDefaultViewPage->GetObj(1)->GetLogicRect());
// Sheet view page should also have 2 objects at same positions
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pSheetViewPage->GetObj(1)->GetLogicRect());
}
// Undo the shape insertion from sheet view
{
switchToSheetView();
undo();
// Back to 1 object on both pages, position preserved
CPPUNIT_ASSERT_EQUAL(size_t(1), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pSheetViewPage->GetObj(0)->GetLogicRect());
}
// Redo the shape insertion
{
redo();
// Back to 2 objects on both pages, positions preserved
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pDefaultViewPage->GetObj(1)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pSheetViewPage->GetObj(1)->GetLogicRect());
}
// Move the first object from the default view
const tools::Rectangle aRectangle1Moved(1500, 1300, 3500, 2300);
{
switchToDefaultView();
moveDrawObject(pDefaultViewPage->GetObj(0), aRectangle1Moved);
// Default view page: first object moved, second unchanged
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pDefaultViewPage->GetObj(1)->GetLogicRect());
// Sheet view page: same positions after sync
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2, pSheetViewPage->GetObj(1)->GetLogicRect());
}
// Move the second object from the sheet view
const tools::Rectangle aRectangle2Moved(5500, 5300, 7500, 6300);
{
switchToSheetView();
moveDrawObject(pDefaultViewPage->GetObj(1), aRectangle2Moved);
// Default view page: both objects at moved positions
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pDefaultViewPage->GetObj(1)->GetLogicRect());
// Sheet view page: same position as in default view page
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pSheetViewPage->GetObj(1)->GetLogicRect());
}
// Delete the second object from the default view
{
switchToDefaultView();
deleteDrawObject(pDefaultViewPage->GetObj(1));
// 1 object remaining on both pages
CPPUNIT_ASSERT_EQUAL(size_t(1), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
}
// Undo the delete
{
undo();
// Both objects restored on both pages
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pDefaultViewPage->GetObj(1)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pSheetViewPage->GetObj(1)->GetLogicRect());
}
// Delete the first object from the sheet view
{
switchToSheetView();
deleteDrawObject(pDefaultViewPage->GetObj(0));
// 1 object remaining on both pages
CPPUNIT_ASSERT_EQUAL(size_t(1), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(1), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
}
// Undo the delete from sheet view
{
undo();
// Both objects restored on both pages
CPPUNIT_ASSERT_EQUAL(size_t(2), pDefaultViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pDefaultViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pDefaultViewPage->GetObj(1)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(size_t(2), pSheetViewPage->GetObjCount());
CPPUNIT_ASSERT_EQUAL(aRectangle1Moved, pSheetViewPage->GetObj(0)->GetLogicRect());
CPPUNIT_ASSERT_EQUAL(aRectangle2Moved, pSheetViewPage->GetObj(1)->GetLogicRect());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_InsertSparklines_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Set up data for sparklines in A1:A3
rDocument.SetValue(ScAddress(0, 0, 0), 1.0);
rDocument.SetValue(ScAddress(0, 1, 0), 5.0);
rDocument.SetValue(ScAddress(0, 2, 0), 3.0);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Insert sparkline from default view: data A1:A3, sparkline at B1
{
switchToDefaultView();
auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
pDocShell->GetDocFunc().InsertSparklines(ScRange(0, 0, 0, 0, 2, 0),
ScRange(1, 0, 0, 1, 0, 0), pSparklineGroup);
// Verify on default view
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 0, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 0, nSheetViewTab)));
}
// Insert sparkline from sheet view: data A1:A3, sparkline at B2
{
switchToSheetView();
auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
pDocShell->GetDocFunc().InsertSparklines(ScRange(0, 0, nSheetViewTab, 0, 2, nSheetViewTab),
ScRange(1, 1, nSheetViewTab, 1, 1, nSheetViewTab),
pSparklineGroup);
// Verify on default view
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 1, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 1, nSheetViewTab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ChangeSparkline_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Set up data for sparklines
rDocument.SetValue(ScAddress(0, 0, 0), 1.0);
rDocument.SetValue(ScAddress(0, 1, 0), 5.0);
rDocument.SetValue(ScAddress(0, 2, 0), 3.0);
rDocument.SetValue(ScAddress(2, 0, 0), 10.0);
rDocument.SetValue(ScAddress(2, 1, 0), 20.0);
rDocument.SetValue(ScAddress(2, 2, 0), 30.0);
// Create a sparkline at B1 with data A1:A3
auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
rDocument.CreateSparkline(ScAddress(1, 0, 0), pSparklineGroup);
{
ScRangeList aDataRange;
aDataRange.push_back(ScRange(0, 0, 0, 0, 2, 0));
auto pSparkline = rDocument.GetSparkline(ScAddress(1, 0, 0));
pSparkline->setInputRange(aDataRange);
}
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Change sparkline data range from default view: A1:A3 -> C1:C3
{
switchToDefaultView();
auto pSparkline = rDocument.GetSparkline(ScAddress(1, 0, 0));
CPPUNIT_ASSERT(pSparkline);
ScRangeList aNewDataRange;
aNewDataRange.push_back(ScRange(2, 0, 0, 2, 2, 0));
pDocShell->GetDocFunc().ChangeSparkline(pSparkline, 0, aNewDataRange);
// Verify input range changed on default view
ScRangeList aInputRange = pSparkline->getInputRange();
CPPUNIT_ASSERT_EQUAL(size_t(1), aInputRange.size());
CPPUNIT_ASSERT_EQUAL(SCCOL(2), aInputRange[0].aStart.Col());
// Verify synced to sheet view
auto pSheetViewSparkline = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
CPPUNIT_ASSERT(pSheetViewSparkline);
ScRangeList aSheetViewInputRange = pSheetViewSparkline->getInputRange();
CPPUNIT_ASSERT_EQUAL(size_t(1), aSheetViewInputRange.size());
CPPUNIT_ASSERT_EQUAL(SCCOL(2), aSheetViewInputRange[0].aStart.Col());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DeleteSparkline_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Create sparklines at B1 and B2
auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
rDocument.CreateSparkline(ScAddress(1, 0, 0), pSparklineGroup);
rDocument.CreateSparkline(ScAddress(1, 1, 0), pSparklineGroup);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Verify sparklines exist on both views
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 0, 0)));
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 1, 0)));
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 0, nSheetViewTab)));
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 1, nSheetViewTab)));
// Delete sparkline at B1 from default view
{
switchToDefaultView();
bool bResult = pDocShell->GetDocFunc().DeleteSparkline(ScAddress(1, 0, 0));
CPPUNIT_ASSERT(bResult);
// Verify deleted on default view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 0, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 0, nSheetViewTab)));
}
// Delete sparkline at B2 from sheet view
{
switchToSheetView();
bool bResult = pDocShell->GetDocFunc().DeleteSparkline(ScAddress(1, 1, nSheetViewTab));
CPPUNIT_ASSERT(bResult);
// Verify deleted on default view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 1, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 1, nSheetViewTab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_GroupUngroupSparklines_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Create two sparklines in different groups at B1 and B2
auto pGroup1 = std::make_shared<sc::SparklineGroup>();
auto pGroup2 = std::make_shared<sc::SparklineGroup>();
rDocument.CreateSparkline(ScAddress(1, 0, 0), pGroup1);
rDocument.CreateSparkline(ScAddress(1, 1, 0), pGroup2);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Verify sparklines are in different groups initially
{
auto pSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, 0));
auto pSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, 0));
CPPUNIT_ASSERT(pSparkline1->getSparklineGroup() != pSparkline2->getSparklineGroup());
}
// Group sparklines from default view: B1:B2 into pGroup1
{
switchToDefaultView();
pDocShell->GetDocFunc().GroupSparklines(ScRange(1, 0, 0, 1, 1, 0), pGroup1);
// Verify both sparklines are in the same group on default view
auto pSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, 0));
auto pSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, 0));
CPPUNIT_ASSERT_EQUAL(pSparkline1->getSparklineGroup(), pSparkline2->getSparklineGroup());
// Verify synced to sheet view
auto pSheetViewSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
auto pSheetViewSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, nSheetViewTab));
CPPUNIT_ASSERT_EQUAL(pSheetViewSparkline1->getSparklineGroup(),
pSheetViewSparkline2->getSparklineGroup());
}
// Ungroup sparklines from default view: B1:B2
{
switchToDefaultView();
pDocShell->GetDocFunc().UngroupSparklines(ScRange(1, 0, 0, 1, 1, 0));
// Verify sparklines are in different groups on default view
auto pSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, 0));
auto pSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, 0));
CPPUNIT_ASSERT(pSparkline1->getSparklineGroup() != pSparkline2->getSparklineGroup());
// Verify synced to sheet view
auto pSheetViewSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
auto pSheetViewSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, nSheetViewTab));
CPPUNIT_ASSERT(pSheetViewSparkline1->getSparklineGroup()
!= pSheetViewSparkline2->getSparklineGroup());
}
// Group sparklines from sheet view: B1:B2 into pGroup1
{
switchToSheetView();
pDocShell->GetDocFunc().GroupSparklines(ScRange(1, 0, nSheetViewTab, 1, 1, nSheetViewTab),
pGroup1);
// Verify both sparklines are in the same group on default view
auto pSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, 0));
auto pSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, 0));
CPPUNIT_ASSERT_EQUAL(pSparkline1->getSparklineGroup(), pSparkline2->getSparklineGroup());
// Verify synced to sheet view
auto pSheetViewSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
auto pSheetViewSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, nSheetViewTab));
CPPUNIT_ASSERT_EQUAL(pSheetViewSparkline1->getSparklineGroup(),
pSheetViewSparkline2->getSparklineGroup());
}
// Ungroup sparklines from sheet view: B1:B2
{
switchToSheetView();
pDocShell->GetDocFunc().UngroupSparklines(
ScRange(1, 0, nSheetViewTab, 1, 1, nSheetViewTab));
// Verify sparklines are in different groups on default view
auto pSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, 0));
auto pSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, 0));
CPPUNIT_ASSERT(pSparkline1->getSparklineGroup() != pSparkline2->getSparklineGroup());
// Verify synced to sheet view
auto pSheetViewSparkline1 = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
auto pSheetViewSparkline2 = rDocument.GetSparkline(ScAddress(1, 1, nSheetViewTab));
CPPUNIT_ASSERT(pSheetViewSparkline1->getSparklineGroup()
!= pSheetViewSparkline2->getSparklineGroup());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ChangeSparklineGroupAttributes_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Create a sparkline group with Column type and a sparkline at B1
auto pSparklineGroup = std::make_shared<sc::SparklineGroup>();
pSparklineGroup->getAttributes().setType(sc::SparklineType::Column);
rDocument.CreateSparkline(ScAddress(1, 0, 0), pSparklineGroup);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Change sparkline group attributes from default view: Column -> Stacked
{
switchToDefaultView();
sc::SparklineAttributes aNewAttributes;
aNewAttributes.setType(sc::SparklineType::Stacked);
pDocShell->GetDocFunc().ChangeSparklineGroupAttributes(pSparklineGroup, aNewAttributes);
// Verify on default view
CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Stacked,
pSparklineGroup->getAttributes().getType());
// Verify synced to sheet view
auto pSheetViewSparkline = rDocument.GetSparkline(ScAddress(1, 0, nSheetViewTab));
CPPUNIT_ASSERT(pSheetViewSparkline);
CPPUNIT_ASSERT_EQUAL(sc::SparklineType::Stacked,
pSheetViewSparkline->getSparklineGroup()->getAttributes().getType());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DeleteSparklineGroup_DefaultAndSheetView)
{
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Create two sparkline groups with sparklines
auto pGroup1 = std::make_shared<sc::SparklineGroup>();
auto pGroup2 = std::make_shared<sc::SparklineGroup>();
rDocument.CreateSparkline(ScAddress(1, 0, 0), pGroup1);
rDocument.CreateSparkline(ScAddress(1, 1, 0), pGroup1);
rDocument.CreateSparkline(ScAddress(1, 2, 0), pGroup2);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
// Delete sparkline group 1 from default view
{
switchToDefaultView();
bool bResult = pDocShell->GetDocFunc().DeleteSparklineGroup(pGroup1, 0);
CPPUNIT_ASSERT(bResult);
// Verify sparklines from group 1 are gone on default view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 0, 0)));
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 1, 0)));
// Group 2 sparkline still exists
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 2, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 0, nSheetViewTab)));
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 1, nSheetViewTab)));
CPPUNIT_ASSERT(rDocument.HasSparkline(ScAddress(1, 2, nSheetViewTab)));
}
// Delete sparkline group 2 from sheet view
{
switchToSheetView();
bool bResult = pDocShell->GetDocFunc().DeleteSparklineGroup(pGroup2, nSheetViewTab);
CPPUNIT_ASSERT(bResult);
// Verify all sparklines are gone on default view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 2, 0)));
// Verify synced to sheet view
CPPUNIT_ASSERT(!rDocument.HasSparkline(ScAddress(1, 2, nSheetViewTab)));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_InsertCells_DefaultAndSheetView)
{
// Test sync to sheet view when inserting rows
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Put data below autofilter range so we can verify the row insert syncs
rDocument.SetString(ScAddress(0, 6, 0), u"ABC"_ustr);
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state: range A2:A5
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Insert row before row 7 in default view (outside autofilter range)
{
switchToDefaultView();
sc::InsertCellsOperation aOperation(
*pDocShell, ScRange(0, 6, nDefaultViewTab, rDocument.MaxCol(), 6, nDefaultViewTab),
nullptr, INS_INSROWS_BEFORE, true, true, false, 0);
CPPUNIT_ASSERT(aOperation.run());
// Autofilter data unchanged in both views
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// "ABC" shifted from A7 to A8, A7 is now empty - synced to both views
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 6, nDefaultViewTab)));
CPPUNIT_ASSERT_EQUAL(u"ABC"_ustr, rDocument.GetString(ScAddress(0, 7, nDefaultViewTab)));
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 6, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(u"ABC"_ustr, rDocument.GetString(ScAddress(0, 7, nSheetViewTab)));
}
// Insert row in autofilter range from default view
{
switchToDefaultView();
sc::InsertCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nDefaultViewTab, rDocument.MaxCol(), 2, nDefaultViewTab),
nullptr, INS_INSROWS_BEFORE, true, true, false, 0);
CPPUNIT_ASSERT(aOperation.run());
// Default view: inserted empty row into A3
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view re-sorted descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3", u"" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Auto-filter range should have expanded to A1:A6 on both views
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 5, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
}
// Insert row on sheet view in autofilter range — should be blocked
{
switchToSheetView();
sc::InsertCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nSheetViewTab, rDocument.MaxCol(), 2, nSheetViewTab), nullptr,
INS_INSROWS_BEFORE, true, true, false, 0);
CPPUNIT_ASSERT(!aOperation.run());
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3", u"" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Auto-filter ranges should remain unchanged
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 5, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_UndoInsertCells_SheetViewSortData)
{
// Test that undoing insert rows restores sheet view sort data
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify sheet view has sort data
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
auto& rSheetView = *pManager->iterateValidSheetViews().begin();
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
// Insert row in auto-filter range from default view
{
switchToDefaultView();
sc::InsertCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nDefaultViewTab, rDocument.MaxCol(), 2, nDefaultViewTab),
nullptr, INS_INSROWS_BEFORE, true, true, false, 0);
CPPUNIT_ASSERT(aOperation.run());
}
// Verify expanded sort range
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3", u"" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Undo the insert
{
switchToDefaultView();
undo();
}
// Verify data is back to original
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view sort data should be restored - sort range should be original 4 rows
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() != nullptr);
// Trigger sync by editing on default view
{
switchToDefaultView();
typeCharsInCell(std::string("44"), 0, 1, mpTabViewDefaultView, pModelObj);
}
// Default view: A2=44
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view should sync correctly with original sort range (descending)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"7", u"5", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_UndoOnSheetViewDoesNotSwitchTab)
{
// Test that undoing a cell edit on a sheet view doesn't switch the
// view to the default view tab.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
CPPUNIT_ASSERT(nSheetViewTab != 0);
// Type a value on the sheet view
{
switchToSheetView();
typeCharsInCell(std::string("XyZ"), 0, 1, mpTabViewSheetView, pModelObj);
}
// Verify we're still on the sheet view tab
CPPUNIT_ASSERT_EQUAL(nSheetViewTab, mpTabViewSheetView->GetViewData().GetTabNumber());
// Undo from the sheet view
{
switchToSheetView();
undo();
}
// The sheet view should still be on its original tab
CPPUNIT_ASSERT_EQUAL(nSheetViewTab, mpTabViewSheetView->GetViewData().GetTabNumber());
// Redo from the sheet view
{
switchToSheetView();
redo();
}
// Still on the sheet view tab
CPPUNIT_ASSERT_EQUAL(nSheetViewTab, mpTabViewSheetView->GetViewData().GetTabNumber());
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_AutoFilterRangeExpansionFromDefaultView)
{
// Test that expanding a range of auto-filter when typing into the cell
// directly below the auto-filter range works as expected on default view
// and is properly synced to sheet views. Also check undo and redo works as
// expected.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Sort ascending on default view
{
switchToDefaultView();
sortAscendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Initial state - default view ascending, sheet view descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify auto-filter range is A1:A5
ScDBData* pDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(pDBData);
ScRange aDBRange;
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab), aDBRange);
// Get objects for sort data checks
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
std::shared_ptr<sc::SheetView> pSheetView = rDocument.GetSheetView(nSheetViewTab);
CPPUNIT_ASSERT(pSheetView);
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
// Initial sort data
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Type a value in A6 on default view (below auto-filter range)
{
switchToDefaultView();
typeCharsInCell(std::string("6"), 0, 5, mpTabViewDefaultView, pModelObj);
}
// DB range expanded to A1:A6
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab), aDBRange);
// Default view - ascending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - descending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Sort data after expansion
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Undo the cell edit
{
switchToDefaultView();
undo();
}
// DB range contracted back to A1:A5
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab), aDBRange);
// Default view - back to ascending sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 5, nDefaultViewTab)));
// Sheet view - back to descending sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Sort data after undo
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Redo the cell edit
{
switchToDefaultView();
redo();
}
// DB range expanded to A1:A6 again
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab), aDBRange);
// Default view - ascending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - descending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Sort data after redo
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Sort the default view descending - now covers all 5 rows
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Default view - descending sorted with all 5 values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - still descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit values one at a time on the default view
// Edit A2
{
switchToDefaultView();
typeCharsInCell(std::string("77"), 0, 1, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A3
{
switchToDefaultView();
typeCharsInCell(std::string("66"), 0, 2, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A4
{
switchToDefaultView();
typeCharsInCell(std::string("55"), 0, 3, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A5
{
switchToDefaultView();
typeCharsInCell(std::string("44"), 0, 4, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A6
{
switchToDefaultView();
typeCharsInCell(std::string("33"), 0, 5, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"33" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"33" }),
getValues(mpTabViewSheetView, 0, 1, 5));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_AutoFilterRangeExpansionFromSheetView)
{
// Test that expanding a range of auto-filter when typing into the cell
// directly below the auto-filter range works as expected on sheet view
// and is properly synced to the default view. Also check undo and redo
// works as expected.
// Run the same test with different ways to set the cell value to cover
// all the paths.
using SetCellFunc = std::function<void(ScDocShell&, ScDocument&, ScAddress const&,
ScTabViewShell*, ScModelObj*)>;
struct TestCase
{
SetCellFunc maSetCell;
bool mbTextCell; // is the cell value text or number
};
auto aTestCases = std::to_array<TestCase>({
// SetNormalStringOperation
{ [this](ScDocShell&, ScDocument&, ScAddress const& rPos, ScTabViewShell* pTabView,
ScModelObj* pModel) {
typeCharsInCell(std::string("6"), rPos.Col(), rPos.Row(), pTabView, pModel);
},
false },
// SetValueOperation
{ [](ScDocShell& rDocShell, ScDocument&, ScAddress const& rPos, ScTabViewShell*,
ScModelObj*) { rDocShell.GetDocFunc().SetValueCell(rPos, 6.0, true); },
false },
// SetFormulaOperation
{ [](ScDocShell& rDocShell, ScDocument& rDocument, ScAddress const& rPos, ScTabViewShell*,
ScModelObj*) {
rDocShell.GetDocFunc().SetFormulaCell(
rPos, new ScFormulaCell(rDocument, rPos, u"=3+3"_ustr), true);
},
false },
// SetStringOperation
{ [](ScDocShell& rDocShell, ScDocument&, ScAddress const& rPos, ScTabViewShell*,
ScModelObj*) { rDocShell.GetDocFunc().SetStringCell(rPos, u"6"_ustr, true); },
true },
// SetEditTextOperation
{ [](ScDocShell& rDocShell, ScDocument& rDocument, ScAddress const& rPos, ScTabViewShell*,
ScModelObj*) {
ScFieldEditEngine& rEE = rDocument.GetEditEngine();
rEE.SetTextCurrentDefaults(u"6"_ustr);
rDocShell.GetDocFunc().SetEditCell(rPos, rEE.CreateTextObject(), true);
},
true },
});
for (auto const& rTestCase : aTestCases)
{
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
// Sort ascending on default view
{
switchToDefaultView();
sortAscendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Initial state - default view ascending, sheet view descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify auto-filter range is A1:A5
ScDBData* pDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(pDBData);
ScRange aDBRange;
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab), aDBRange);
// Get classes for sort data checks
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
std::shared_ptr<sc::SheetView> pSheetView = rDocument.GetSheetView(nSheetViewTab);
CPPUNIT_ASSERT(pSheetView);
std::shared_ptr<sc::SheetViewManager> pManager
= rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
// Initial sort data
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Set a value in A6 on sheet view (below auto-filter range)
{
switchToSheetView();
ScAddress aCellPos(0, 5, nSheetViewTab);
rTestCase.maSetCell(*pDocShell, rDocument, aCellPos, mpTabViewSheetView, pModelObj);
}
// DB range expanded to A1:A6
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab), aDBRange);
// Default view - ascending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - descending sorted with new value
// Text cells sort before numbers in descending order
if (rTestCase.mbTextCell)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"6", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
else
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Sort data valid after expansion
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Undo the cell edit
{
switchToSheetView();
undo();
}
// DB range contracted back to A1:A5
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab), aDBRange);
// Default view - back to ascending sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 5, nDefaultViewTab)));
// Sheet view - back to descending sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Sort data preserved after undo
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(4), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Redo the cell edit
{
switchToSheetView();
redo();
}
// DB range expanded to A1:A6 again
pDBData->GetArea(aDBRange);
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 5, nDefaultViewTab), aDBRange);
// Default view - ascending sorted with new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7", u"6" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - descending sorted with new value
if (rTestCase.mbTextCell)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"6", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
else
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Sort data preserved after redo
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pSheetView->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortOrder()->maSortInfo.mnLastRow);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(0), pSheetView->getSortParam()->nRow1);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pSheetView->getSortParam()->nRow2);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCROW(1), pManager->getSortOrder()->maSortInfo.mnFirstRow);
CPPUNIT_ASSERT_EQUAL(SCROW(5), pManager->getSortOrder()->maSortInfo.mnLastRow);
// Sort the default view descending - now covers all 5 rows
{
switchToDefaultView();
sortDescendingForCell(u"A1");
}
// Default view - descending sorted with all 5 values
// After re-sorting, text "6" becomes a number via typeCharsInCell overwrite
if (rTestCase.mbTextCell)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"6", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
else
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
// Sheet view - still descending
if (rTestCase.mbTextCell)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"6", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
else
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit values one at a time on the default view
// Edit A2
{
switchToDefaultView();
typeCharsInCell(std::string("77"), 0, 1, mpTabViewDefaultView, pModelObj);
}
if (rTestCase.mbTextCell)
{
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
}
else
{
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"6", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
}
// Edit A3
{
switchToDefaultView();
typeCharsInCell(std::string("66"), 0, 2, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"5", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A4
{
switchToDefaultView();
typeCharsInCell(std::string("55"), 0, 3, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"4", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A5
{
switchToDefaultView();
typeCharsInCell(std::string("44"), 0, 4, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"3" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 5));
// Edit A6
{
switchToDefaultView();
typeCharsInCell(std::string("33"), 0, 5, mpTabViewDefaultView, pModelObj);
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"33" }),
getValues(mpTabViewDefaultView, 0, 1, 5));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"77", u"66", u"55", u"44", u"33" }),
getValues(mpTabViewSheetView, 0, 1, 5));
tearDown();
setUp();
} // end for loop
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_UndoDeleteCells_SheetViewSortData)
{
// Test that undoing delete rows restores sheet view sort data
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
auto& rSheetView = *pManager->iterateValidSheetViews().begin();
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
// Delete row in auto-filter range from default view (row 3)
{
switchToDefaultView();
sc::DeleteCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nDefaultViewTab, rDocument.MaxCol(), 2, nDefaultViewTab),
nullptr, DelCellCmd::Rows, true);
CPPUNIT_ASSERT(aOperation.run());
}
// Verify shrunk sort range
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 3));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 3));
// Undo the delete
{
switchToDefaultView();
undo();
}
// Verify data is back to original
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view sort data should be restored
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() != nullptr);
// Trigger sync by editing on default view
{
switchToDefaultView();
typeCharsInCell(std::string("55"), 0, 2, mpTabViewDefaultView, pModelObj);
}
// Default view: A3=55
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"55", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view should sync correctly with original sort range (descending)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"55", u"7", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_InsertDeleteColumns_SheetViewSortData)
{
// Test that inserting/deleting columns in the middle of the auto-filter
// adjusts sheet view sort ranges and that undo/redo restores them.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter_Extended.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending on column A
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state
// Column A: default unsorted, sheet view descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Column B: follows the sort
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewSheetView, 1, 1, 4));
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
std::shared_ptr<sc::SheetView> pSheetView = rDocument.GetSheetView(nSheetViewTab);
CPPUNIT_ASSERT(pSheetView);
// Verify initial sort ranges
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortParam()->nCol2);
// Insert a column before column B (in the middle of the auto-filter)
{
switchToDefaultView();
sc::InsertCellsOperation aOperation(
*pDocShell, ScRange(1, 0, nDefaultViewTab, 1, rDocument.MaxRow(), nDefaultViewTab),
nullptr, INS_INSCOLS_BEFORE, true, true, false, 0);
CPPUNIT_ASSERT(aOperation.run());
}
// Column A unchanged, original B shifted to C
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 2, 1, 4));
// Sheet view sorted - column C has the sorted B values
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewSheetView, 2, 1, 4));
// Sort ranges should have expanded from A-C to A-D
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(3), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(3), pSheetView->getSortParam()->nCol2);
// Undo the column insert
{
switchToDefaultView();
undo();
}
// Data back in original columns
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewSheetView, 1, 1, 4));
// Sort ranges should be restored to A-C
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortParam()->nCol2);
// Redo the column insert
{
switchToDefaultView();
redo();
}
// Data shifted again - original B now in C
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 2, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewSheetView, 2, 1, 4));
// Sort ranges should be A-D again
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(3), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(3), pSheetView->getSortParam()->nCol2);
// Undo again to get back to original state for the delete test
{
switchToDefaultView();
undo();
}
// Delete column B from default view
{
switchToDefaultView();
sc::DeleteCellsOperation aOperation(
*pDocShell, ScRange(1, 0, nDefaultViewTab, 1, rDocument.MaxRow(), nDefaultViewTab),
nullptr, DelCellCmd::Cols, true);
CPPUNIT_ASSERT(aOperation.run());
}
// Column B deleted - original C now in B
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Sort ranges should have shrunk from A-C to A-B
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(1), pSheetView->getSortParam()->nCol2);
// Undo the column delete
{
switchToDefaultView();
undo();
}
// Data restored
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"ccc", u"rrr", u"sss", u"aaa" }),
getValues(mpTabViewDefaultView, 1, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"aaa", u"rrr", u"ccc", u"sss" }),
getValues(mpTabViewSheetView, 1, 1, 4));
// Sort ranges should be restored to A-C
CPPUNIT_ASSERT(pSheetView->getSortOrder() != nullptr);
CPPUNIT_ASSERT(pSheetView->getSortParam() != nullptr);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortOrder()->maSortInfo.mnFirstColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortOrder()->maSortInfo.mnLastColumn);
CPPUNIT_ASSERT_EQUAL(SCCOL(0), pSheetView->getSortParam()->nCol1);
CPPUNIT_ASSERT_EQUAL(SCCOL(2), pSheetView->getSortParam()->nCol2);
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DeleteCells_DefaultAndSheetView)
{
// Test sync to sheet view when deleting rows
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
// Put data below autofilter range so we can verify the row delete syncs
rDocument.SetString(ScAddress(0, 6, 0), u"ABC"_ustr);
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state - autofilter range A1:A5, data A2:A5
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 4, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
// Delete row 7 from default view (outside autofilter range)
{
switchToDefaultView();
sc::DeleteCellsOperation aOperation(
*pDocShell, ScRange(0, 6, nDefaultViewTab, rDocument.MaxCol(), 6, nDefaultViewTab),
nullptr, DelCellCmd::Rows, true);
CPPUNIT_ASSERT(aOperation.run());
// Autofilter data unchanged in both views
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// "ABC" was in A7 - now deleted on both views
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 6, nDefaultViewTab)));
CPPUNIT_ASSERT_EQUAL(u""_ustr, rDocument.GetString(ScAddress(0, 6, nSheetViewTab)));
// Autofilter range unchanged on both views (delete was outside)
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 4, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 4, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
}
// Delete row in autofilter range from default view (delete row 3)
{
switchToDefaultView();
sc::DeleteCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nDefaultViewTab, rDocument.MaxCol(), 2, nDefaultViewTab),
nullptr, DelCellCmd::Rows, true);
CPPUNIT_ASSERT(aOperation.run());
// Default view: row 3 deleted, range is now A2:A4
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 3));
// Sheet view re-sorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 3));
// Autofilter range should have shrunk from A1:A5 to A1:A4 on both views
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 3, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 3, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
}
// Delete row on sheet view in autofilter range - should be blocked
{
switchToSheetView();
sc::DeleteCellsOperation aOperation(
*pDocShell, ScRange(0, 2, nSheetViewTab, rDocument.MaxCol(), 2, nSheetViewTab), nullptr,
DelCellCmd::Rows, true);
CPPUNIT_ASSERT(!aOperation.run());
// Values should remain unchanged
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 3));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 3));
// Autofilter ranges should remain unchanged
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nDefaultViewTab, 0, 3, nDefaultViewTab),
getAutoFilterArea(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(ScRange(0, 0, nSheetViewTab, 0, 3, nSheetViewTab),
getAutoFilterArea(rDocument, nSheetViewTab));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_UndoSortOnSheetView)
{
// Test case:
// - Create sheet view
// - Sort descending
// - Sort ascending
// - Undo
// - Undo
// Verify SheetView has correct sort data on each undo step
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial unsorted state on both views
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Sort descending on sheet view
{
switchToSheetView();
sortDescendingForCell(u"A1");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
auto& rSheetView = *pManager->iterateValidSheetViews().begin();
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() != nullptr);
// Sort ascending on sheet view (second sort)
{
switchToSheetView();
sortAscendingForCell(u"A1");
}
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Undo ascending sort - should revert to descending
{
switchToSheetView();
undo();
}
// After first undo, sheet view should show descending sort
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// SheetView sort data should reflect descending sort
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() != nullptr);
// Undo descending sort - should revert to original unsorted
{
switchToSheetView();
undo();
}
// After second undo, sheet view data should be unsorted
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// SheetView sort data should be fully reset
CPPUNIT_ASSERT(rSheetView.getSortOrder() == nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() == nullptr);
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_UndoSortOnDefaultView)
{
// Test:
// - create sheet view with sort
// - sort default view
// - undo default view sort
// - edit on default view
// - verify sheet view sync
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Initial state: default unsorted, sheet view descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Sort ascending on default view
{
switchToDefaultView();
sortAscendingForCell(u"A1");
}
// Default view now ascending, sheet view still descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"3", u"4", u"5", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// SheetViewManager should have sort data from the default view sort
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
CPPUNIT_ASSERT(pManager->getSortOrder() != nullptr);
// Undo the default view sort
{
switchToDefaultView();
undo();
}
// Default view should be back to original order
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Default view sort data should be restored (no sort)
CPPUNIT_ASSERT(pManager->getSortOrder() == nullptr);
// Sheet view should still be descending
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Edit a cell on the default view to trigger sync - change A2
{
switchToDefaultView();
typeCharsInCell(std::string("44"), 0, 1, mpTabViewDefaultView, pModelObj);
}
// Default view: A2=44 replaces 4
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
// Sheet view should be synced and still sorted descending with the new value
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"44", u"7", u"5", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_RemoveAutoFilter_DefaultAndSheetView)
{
// Test that removing auto-filter from default view removes it from sheet view
// and resets the sort data in SheetView and SheetViewManager
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupViews();
// Create sheet view and sort descending
{
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Verify initial state
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"7", u"5", u"4", u"3" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify auto-filter exists on both views
ScDBData* pDefaultDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDBData);
CPPUNIT_ASSERT(pDefaultDBData->HasAutoFilter());
ScDBData* pSheetViewDBData = rDocument.GetAnonymousDBData(nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDBData);
CPPUNIT_ASSERT(pSheetViewDBData->HasAutoFilter());
// Verify SheetView has sort data
std::shared_ptr<sc::SheetViewManager> pManager = rDocument.GetSheetViewManager(nDefaultViewTab);
CPPUNIT_ASSERT(pManager);
CPPUNIT_ASSERT(!pManager->isEmpty());
auto& rSheetView = *pManager->iterateValidSheetViews().begin();
CPPUNIT_ASSERT(rSheetView.getSortOrder() != nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() != nullptr);
// Remove auto-filter from default view
{
switchToDefaultView();
gotoCell(u"A1");
mpTabViewDefaultView->ToggleAutoFilter();
}
// Verify auto-filter is removed from default view
pDefaultDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(!pDefaultDBData || !pDefaultDBData->HasAutoFilter());
// Verify auto-filter is removed from sheet view
pSheetViewDBData = rDocument.GetAnonymousDBData(nSheetViewTab);
CPPUNIT_ASSERT(!pSheetViewDBData || !pSheetViewDBData->HasAutoFilter());
// Verify data is same in both views (unsorted, since auto-filter is gone)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify SheetView sort data is reset
CPPUNIT_ASSERT(rSheetView.getSortOrder() == nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() == nullptr);
// Undo removing auto-filter from default view
{
switchToDefaultView();
undo();
}
// Verify auto-filter is back on both views
pDefaultDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDBData);
CPPUNIT_ASSERT(pDefaultDBData->HasAutoFilter());
pSheetViewDBData = rDocument.GetAnonymousDBData(nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDBData);
CPPUNIT_ASSERT(pSheetViewDBData->HasAutoFilter());
// Verify data is restored - both views show same data
// (sort data was reset, so sheet view is no longer sorted)
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// TODO: Sort data is not restored by undo - we expect it should be.
CPPUNIT_ASSERT(rSheetView.getSortOrder() == nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() == nullptr);
// Redo removing auto-filter
{
switchToDefaultView();
redo();
}
// Verify auto-filter is removed again from both views
pDefaultDBData = rDocument.GetAnonymousDBData(nDefaultViewTab);
CPPUNIT_ASSERT(!pDefaultDBData || !pDefaultDBData->HasAutoFilter());
pSheetViewDBData = rDocument.GetAnonymousDBData(nSheetViewTab);
CPPUNIT_ASSERT(!pSheetViewDBData || !pSheetViewDBData->HasAutoFilter());
// Verify data is unsorted on both views
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewDefaultView, 0, 1, 4));
CPPUNIT_ASSERT_EQUAL(expectedValues({ u"4", u"5", u"3", u"7" }),
getValues(mpTabViewSheetView, 0, 1, 4));
// Verify SheetView sort data is reset again
CPPUNIT_ASSERT(rSheetView.getSortOrder() == nullptr);
CPPUNIT_ASSERT(rSheetView.getSortParam() == nullptr);
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_PivotTable_DefaultAndSheetView)
{
// Test sync to sheet view when creating and updating the pivot table
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupPivotTableSourceData(rDocument);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Create pivot table from default view
{
switchToDefaultView();
ScDPObject aDPObject = createCategoryValueDPObject(
rDocument, nDefaultViewTab, ScRange(ScAddress(4, 0, nDefaultViewTab)),
css::sheet::DataPilotFieldOrientation_ROW);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.CreatePivotTable(aDPObject, true, true);
CPPUNIT_ASSERT(bResult);
// Verify pivot table output on default view - cell E1 should have content
CPPUNIT_ASSERT(!rDocument.GetString(ScAddress(4, 0, nDefaultViewTab)).isEmpty());
// Verify the pivot table output is synced to sheet view
CPPUNIT_ASSERT_EQUAL(rDocument.GetString(ScAddress(4, 0, nDefaultViewTab)),
rDocument.GetString(ScAddress(4, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(rDocument.GetString(ScAddress(4, 1, nDefaultViewTab)),
rDocument.GetString(ScAddress(4, 1, nSheetViewTab)));
}
// Update pivot table layout: change Category from ROW to COLUMN
{
switchToDefaultView();
ScDPObject* pDefaultDPObject = findPivotTableOnTab(rDocument, nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDPObject);
// Change layout: move Category to COLUMN orientation
ScDPSaveData* pSaveData = pDefaultDPObject->GetSaveData();
CPPUNIT_ASSERT(pSaveData);
ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT(pDim);
pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_COLUMN);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.UpdatePivotTable(*pDefaultDPObject, true, true);
CPPUNIT_ASSERT(bResult);
// Verify the sheet view pivot table has the updated COLUMN layout
ScDPObject* pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
ScDPSaveDimension* pSheetViewDim
= pSheetViewDPObject->GetSaveData()->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT_EQUAL(css::sheet::DataPilotFieldOrientation_COLUMN,
pSheetViewDim->GetOrientation());
// Verify both pivot tables have the same layout
CPPUNIT_ASSERT_EQUAL(*pDefaultDPObject->GetSaveData(), *pSheetViewDPObject->GetSaveData());
}
// Update pivot table layout from sheet view: change Category from COLUMN back to ROW
{
switchToSheetView();
ScDPObject* pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
// Change layout: move Category back to ROW orientation
ScDPSaveData* pSaveData = pSheetViewDPObject->GetSaveData();
CPPUNIT_ASSERT(pSaveData);
ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT(pDim);
pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_ROW);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.UpdatePivotTable(*pSheetViewDPObject, true, true);
CPPUNIT_ASSERT(bResult);
// Get the pivot table object again, as it can be recreated when we sync.
pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
// Verify the default view pivot table has the updated ROW layout
ScDPObject* pDefaultDPObject = findPivotTableOnTab(rDocument, nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDPObject);
ScDPSaveDimension* pDefaultDim
= pDefaultDPObject->GetSaveData()->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT_EQUAL(css::sheet::DataPilotFieldOrientation_ROW,
pDefaultDim->GetOrientation());
#if !defined MACOSX && !defined _WIN32
// Verify both pivot tables have the same layout
CPPUNIT_ASSERT_EQUAL(*pDefaultDPObject->GetSaveData(), *pSheetViewDPObject->GetSaveData());
#endif
}
// Create pivot table from sheet view
{
switchToSheetView();
ScDPObject aDPObject = createCategoryValueDPObject(
rDocument, nSheetViewTab, ScRange(ScAddress(7, 0, nSheetViewTab)),
css::sheet::DataPilotFieldOrientation_ROW);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.CreatePivotTable(aDPObject, true, true);
CPPUNIT_ASSERT(bResult);
// Verify pivot table created on default view - cell H1 should have content
CPPUNIT_ASSERT(!rDocument.GetString(ScAddress(7, 0, nDefaultViewTab)).isEmpty());
// Verify the pivot table output is synced to sheet view
CPPUNIT_ASSERT_EQUAL(rDocument.GetString(ScAddress(7, 0, nDefaultViewTab)),
rDocument.GetString(ScAddress(7, 0, nSheetViewTab)));
CPPUNIT_ASSERT_EQUAL(rDocument.GetString(ScAddress(7, 1, nDefaultViewTab)),
rDocument.GetString(ScAddress(7, 1, nSheetViewTab)));
}
// Remove first pivot table from default view
{
switchToDefaultView();
ScDPObject* pDefaultDPObject = findPivotTableOnTab(rDocument, nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDPObject);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.RemovePivotTable(*pDefaultDPObject, true, true);
CPPUNIT_ASSERT(bResult);
CPPUNIT_ASSERT_EQUAL(size_t(1), countPivotTablesOnTab(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(size_t(1), countPivotTablesOnTab(rDocument, nSheetViewTab));
}
// Remove second pivot table from sheet view
{
switchToSheetView();
ScDPObject* pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.RemovePivotTable(*pSheetViewDPObject, true, true);
CPPUNIT_ASSERT(bResult);
CPPUNIT_ASSERT_EQUAL(size_t(0), countPivotTablesOnTab(rDocument, nDefaultViewTab));
CPPUNIT_ASSERT_EQUAL(size_t(0), countPivotTablesOnTab(rDocument, nSheetViewTab));
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_ReplacePivotTable_DefaultAndSheetView)
{
// Test sync to sheet view when replacing a pivot table via DataPilotUpdate
ScModelObj* pModelObj = createDoc("empty.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
ScDocShell* pDocShell = dynamic_cast<ScDocShell*>(pModelObj->GetEmbeddedObject());
ScDocument& rDocument = pDocShell->GetDocument();
setupPivotTableSourceData(rDocument);
setupViews();
// Create sheet view
{
switchToSheetView();
createNewSheetViewInCurrentView();
}
SCTAB nSheetViewTab = mpTabViewSheetView->GetViewData().GetTabNumber();
SCTAB nDefaultViewTab = mpTabViewDefaultView->GetViewData().GetTabNumber();
// Create pivot table from default view
{
switchToDefaultView();
ScDPObject aDPObject = createCategoryValueDPObject(
rDocument, nDefaultViewTab, ScRange(ScAddress(4, 0, nDefaultViewTab)),
css::sheet::DataPilotFieldOrientation_ROW);
ScDBDocFunc aDBDocFunc(*pDocShell);
bool bResult = aDBDocFunc.CreatePivotTable(aDPObject, true, true);
CPPUNIT_ASSERT(bResult);
}
// Replace pivot table from default view: change Category from ROW to COLUMN
{
switchToDefaultView();
ScDPObject* pDPObject = findPivotTableOnTab(rDocument, nDefaultViewTab);
CPPUNIT_ASSERT(pDPObject);
// Build a new DPObject with modified layout
ScDPObject aNewDPObject
= createCategoryValueDPObject(rDocument, nDefaultViewTab, pDPObject->GetOutRange(),
css::sheet::DataPilotFieldOrientation_COLUMN);
sc::ReplacePivotTableOperation aOperation(*pDocShell, pDPObject, &aNewDPObject, true, true,
false);
CPPUNIT_ASSERT(aOperation.run());
// Verify the sheet view pivot table has the updated COLUMN layout
ScDPObject* pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
ScDPSaveDimension* pSheetViewDim
= pSheetViewDPObject->GetSaveData()->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT_EQUAL(css::sheet::DataPilotFieldOrientation_COLUMN,
pSheetViewDim->GetOrientation());
// Verify both pivot tables have the same layout
CPPUNIT_ASSERT_EQUAL(*pDPObject->GetSaveData(), *pSheetViewDPObject->GetSaveData());
}
// Replace pivot table from sheet view: change Category from COLUMN back to ROW
{
switchToSheetView();
ScDPObject* pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
// Build a new DPObject with modified layout
ScDPObject aNewDPObject = createCategoryValueDPObject(
rDocument, nSheetViewTab, pSheetViewDPObject->GetOutRange(),
css::sheet::DataPilotFieldOrientation_ROW);
sc::ReplacePivotTableOperation aOperation(*pDocShell, pSheetViewDPObject, &aNewDPObject,
true, true, false);
CPPUNIT_ASSERT(aOperation.run());
// Get the pivot table object again, as it can be recreated when we sync.
pSheetViewDPObject = findPivotTableOnTab(rDocument, nSheetViewTab);
CPPUNIT_ASSERT(pSheetViewDPObject);
// Verify the default view pivot table has the updated ROW layout
ScDPObject* pDefaultDPObject = findPivotTableOnTab(rDocument, nDefaultViewTab);
CPPUNIT_ASSERT(pDefaultDPObject);
ScDPSaveDimension* pDefaultDim
= pDefaultDPObject->GetSaveData()->GetDimensionByName(u"Category"_ustr);
CPPUNIT_ASSERT_EQUAL(css::sheet::DataPilotFieldOrientation_ROW,
pDefaultDim->GetOrientation());
// Verify both pivot tables have the same layout
CPPUNIT_ASSERT_EQUAL(*pDefaultDPObject->GetSaveData(), *pSheetViewDPObject->GetSaveData());
}
}
CPPUNIT_TEST_FIXTURE(SyncTest, testSync_OperationInvalidatesOtherView)
{
// Sheet view sync mutates content on the sheet view tab, so the view sitting on
// that tab must receive a tile invalidation. The reverse must also hold for the
// default view tab.
ScModelObj* pModelObj = createDoc("SheetView_AutoFilter.ods");
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
comphelper::LibreOfficeKit::setPartInInvalidation(true);
comphelper::ScopeGuard aPartInvalidationGuard(
[]() { comphelper::LibreOfficeKit::setPartInInvalidation(false); });
setupViews();
switchToSheetView();
createNewSheetViewInCurrentView();
sortDescendingForCell(u"A1");
CPPUNIT_ASSERT_EQUAL(SCTAB(1), mpTabViewSheetView->GetViewData().GetTabNumber());
CPPUNIT_ASSERT_EQUAL(SCTAB(0), mpTabViewDefaultView->GetViewData().GetTabNumber());
// Operation on the default view tab must invalidate the sheet view tab.
{
switchToDefaultView();
Scheduler::ProcessEventsToIdle();
moSheetView->ClearAllInvalids();
moDefaultView->ClearAllInvalids();
gotoCell(u"A3");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_MESSAGE("default view tab should be repainted",
moDefaultView->m_bInvalidateTiles);
CPPUNIT_ASSERT_MESSAGE("sheet view tab should be repainted",
moSheetView->m_bInvalidateTiles);
auto const& rSheetViewParts = moSheetView->m_aInvalidationsParts;
CPPUNIT_ASSERT_MESSAGE("sheet view callback should receive an invalidation",
std::ranges::find(rSheetViewParts, 1) != rSheetViewParts.end());
}
// Operation on the sheet view tab must invalidate the default view tab.
{
switchToSheetView();
Scheduler::ProcessEventsToIdle();
moSheetView->ClearAllInvalids();
moDefaultView->ClearAllInvalids();
gotoCell(u"A1");
dispatchCommand(mxComponent, u".uno:ClearContents"_ustr, {});
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT_MESSAGE("sheet view tab should be repainted",
moSheetView->m_bInvalidateTiles);
CPPUNIT_ASSERT_MESSAGE("default view tab should be repainted",
moDefaultView->m_bInvalidateTiles);
auto const& rDefaultViewParts = moDefaultView->m_aInvalidationsParts;
CPPUNIT_ASSERT_MESSAGE("default view callback should receive an invalidation",
std::ranges::find(rDefaultViewParts, 0) != rDefaultViewParts.end());
}
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */