From 7f2a75d8d7391d74d847735c58579eeddc9cf7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Vajngerl?= Date: Sat, 14 Mar 2026 00:48:32 +0900 Subject: [PATCH] sc: Add sheet view sync for CreatePivotTableOperation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For pivot tables we operate with ScDPObject as input, which has the input and output ranges. We need to convert the ranges to the default view tab, so the pivot table is created on the default view. Sync will take pivot tables into account when copying the content to sheet views, so we don't need to do anything special here. Change-Id: Ie28fda2d04576fd689d11010401709c6c02431c8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201695 Tested-by: Jenkins CollaboraOffice Reviewed-by: Miklos Vajna Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202115 Tested-by: Tomaž Vajngerl Reviewed-by: Tomaž Vajngerl (cherry picked from commit a36ac18990c89f740b8329b8325e4abe3d3e2087) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205551 Reviewed-by: Xisco Fauli Tested-by: Xisco Fauli --- sc/CppunitTest_sc_sheetview_test.mk | 1 + sc/inc/dpobject.hxx | 2 +- sc/qa/unit/tiledrendering/SheetViewTest.cxx | 100 ++++++++++++++++++ sc/source/ui/inc/dbdocfun.hxx | 2 +- .../operation/CreatePivotTableOperation.hxx | 4 +- .../operation/CreatePivotTableOperation.cxx | 61 ++++++----- 6 files changed, 142 insertions(+), 28 deletions(-) diff --git a/sc/CppunitTest_sc_sheetview_test.mk b/sc/CppunitTest_sc_sheetview_test.mk index 80a2e54fec05..acfa9f300bd0 100644 --- a/sc/CppunitTest_sc_sheetview_test.mk +++ b/sc/CppunitTest_sc_sheetview_test.mk @@ -44,6 +44,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sc_sheetview_test, \ $(eval $(call gb_CppunitTest_use_externals,sc_sheetview_test,\ boost_headers \ libxml2 \ + mdds_headers \ )) $(eval $(call gb_CppunitTest_set_include,sc_sheetview_test,\ diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx index 03271c1fac51..8fc2ec470f35 100644 --- a/sc/inc/dpobject.hxx +++ b/sc/inc/dpobject.hxx @@ -437,7 +437,7 @@ public: SC_DLLPUBLIC ScDPObject& operator[](size_t nIndex); SC_DLLPUBLIC const ScDPObject& operator[](size_t nIndex) const; - ScDPObject* GetByName(std::u16string_view rName) const; + SC_DLLPUBLIC ScDPObject* GetByName(std::u16string_view rName) const; void DeleteOnTab( SCTAB nTab ); void UpdateReference( UpdateRefMode eUpdateRefMode, diff --git a/sc/qa/unit/tiledrendering/SheetViewTest.cxx b/sc/qa/unit/tiledrendering/SheetViewTest.cxx index 509575c4fc59..78f306396aee 100644 --- a/sc/qa/unit/tiledrendering/SheetViewTest.cxx +++ b/sc/qa/unit/tiledrendering/SheetViewTest.cxx @@ -37,9 +37,15 @@ #include #include #include +#include +#include +#include +#include #include #include +#include + using namespace css; class SheetViewTest : public ScTiledRenderingTest @@ -3724,6 +3730,100 @@ CPPUNIT_TEST_FIXTURE(SyncTest, testSync_DeleteSparklineGroup_DefaultAndSheetView } } +CPPUNIT_TEST_FIXTURE(SyncTest, testSync_CreatePivotTable_DefaultAndSheetView) +{ + // test sync to sheet view when creating a new pivot table + ScModelObj* pModelObj = createDoc("empty.ods"); + pModelObj->initializeForTiledRendering(uno::Sequence()); + ScDocShell* pDocShell = dynamic_cast(pModelObj->GetEmbeddedObject()); + ScDocument& rDocument = pDocShell->GetDocument(); + + // Set up source data: A1:B4 with headers and values + 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); + + 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(&rDocument); + ScSheetSourceDesc aSheetDesc(&rDocument); + aSheetDesc.SetSourceRange(ScRange(0, 0, nDefaultViewTab, 1, 3, nDefaultViewTab)); + aDPObject.SetSheetDesc(aSheetDesc); + aDPObject.SetOutRange(ScRange(ScAddress(4, 0, nDefaultViewTab))); + + ScDPSaveData aSaveData; + ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(u"Category"_ustr); + pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_ROW); + pDim = aSaveData.GetNewDimensionByName(u"Value"_ustr); + pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_DATA); + pDim->SetFunction(ScGeneralFunction::SUM); + aDPObject.SetSaveData(aSaveData); + + 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))); + } + + // Create pivot table from sheet view + { + switchToSheetView(); + + ScDPObject aDPObject(&rDocument); + ScSheetSourceDesc aSheetDesc(&rDocument); + aSheetDesc.SetSourceRange(ScRange(0, 0, nSheetViewTab, 1, 3, nSheetViewTab)); + aDPObject.SetSheetDesc(aSheetDesc); + aDPObject.SetOutRange(ScRange(ScAddress(7, 0, nSheetViewTab))); + + ScDPSaveData aSaveData; + ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(u"Category"_ustr); + pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_ROW); + pDim = aSaveData.GetNewDimensionByName(u"Value"_ustr); + pDim->SetOrientation(css::sheet::DataPilotFieldOrientation_DATA); + pDim->SetFunction(ScGeneralFunction::SUM); + aDPObject.SetSaveData(aSaveData); + + 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))); + } +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/dbdocfun.hxx b/sc/source/ui/inc/dbdocfun.hxx index 60d04f61715c..3057d6232a88 100644 --- a/sc/source/ui/inc/dbdocfun.hxx +++ b/sc/source/ui/inc/dbdocfun.hxx @@ -93,7 +93,7 @@ public: bool bRecord, bool bApi, bool bAllowMove = false ); bool RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi); - bool CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi); + SC_DLLPUBLIC bool CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi); bool UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi); /** diff --git a/sc/source/ui/inc/operation/CreatePivotTableOperation.hxx b/sc/source/ui/inc/operation/CreatePivotTableOperation.hxx index 43c2e307ff6c..88a9ab7b91b3 100644 --- a/sc/source/ui/inc/operation/CreatePivotTableOperation.hxx +++ b/sc/source/ui/inc/operation/CreatePivotTableOperation.hxx @@ -21,12 +21,12 @@ class CreatePivotTableOperation : public Operation { private: ScDocShell& mrDocShell; - ScDPObject const& mrDPObj; + ScDPObject const& mrDPObject; bool runImplementation() override; public: - CreatePivotTableOperation(ScDocShell& rDocShell, ScDPObject const& rDPObj, bool bRecord, + CreatePivotTableOperation(ScDocShell& rDocShell, ScDPObject const& rDPObject, bool bRecord, bool bApi); }; } // end sc namespace diff --git a/sc/source/ui/operation/CreatePivotTableOperation.cxx b/sc/source/ui/operation/CreatePivotTableOperation.cxx index 362955593600..7f91725a0e1f 100644 --- a/sc/source/ui/operation/CreatePivotTableOperation.cxx +++ b/sc/source/ui/operation/CreatePivotTableOperation.cxx @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -23,11 +24,11 @@ namespace sc { CreatePivotTableOperation::CreatePivotTableOperation(ScDocShell& rDocShell, - ScDPObject const& rDPObj, bool bRecord, + ScDPObject const& rDPObject, bool bRecord, bool bApi) : Operation(OperationType::PivotTableCreate, bRecord, bApi) , mrDocShell(rDocShell) - , mrDPObj(rDPObj) + , mrDPObject(rDPObject) { } @@ -36,55 +37,65 @@ bool CreatePivotTableOperation::runImplementation() ScDocShellModificator aModificator(mrDocShell); weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); - if (!checkSheetViewProtection()) - return false; - // At least one cell in the output range should be editable. Check in advance. ScDocument& rDoc = mrDocShell.GetDocument(); - if (!rDoc.IsImportingXML() - && !sc::pivot::isEditable(mrDocShell, ScRange(mrDPObj.GetOutRange().aStart), mbApi)) + auto aOutputStartRange = convertRange(ScRange(mrDPObject.GetOutRange().aStart)); + if (!rDoc.IsImportingXML() && !sc::pivot::isEditable(mrDocShell, aOutputStartRange, mbApi)) + { return false; + } ScDocumentUniquePtr pNewUndoDoc; if (mbRecord && !rDoc.IsUndoEnabled()) mbRecord = false; - // output range must be set at pNewObj - std::unique_ptr pDestObj(new ScDPObject(mrDPObj)); + // output range must be set at pNewObject + std::unique_ptr pDestObject(new ScDPObject(mrDPObject)); - ScDPObject& rDestObj = *pDestObj; + ScDPObject& rDestObject = *pDestObject; + + // Convert ranges from sheet view to default view + rDestObject.SetOutRange(convertRange(rDestObject.GetOutRange())); + + const ScSheetSourceDesc* pSheetDesc = rDestObject.GetSheetDesc(); + if (pSheetDesc && !pSheetDesc->HasRangeName()) + { + ScSheetSourceDesc aDesc(*pSheetDesc); + aDesc.SetSourceRange(convertRange(aDesc.GetSourceRange())); + rDestObject.SetSheetDesc(aDesc); + } // #i94570# When changing the output position in the dialog, a new table is created // with the settings from the old table, including the name. // So we have to check for duplicate names here (before inserting). - if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName())) - rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below + if (rDoc.GetDPCollection()->GetByName(rDestObject.GetName())) + rDestObject.SetName(OUString()); // ignore the invalid name, create a new name below // Synchronize groups between linked tables { const ScDPDimensionSaveData* pGroups = nullptr; - bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObj, &pGroups); + bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObject, &pGroups); if (bRefFound) { - ScDPSaveData* pSaveData = rDestObj.GetSaveData(); + ScDPSaveData* pSaveData = rDestObject.GetSaveData(); if (pSaveData) pSaveData->SetDimensionData(pGroups); } } - rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObj)); + rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObject)); - rDestObj.ReloadGroupTableData(); - rDestObj.SyncAllDimensionMembers(); - rDestObj.InvalidateData(); // before getting the new output area + rDestObject.ReloadGroupTableData(); + rDestObject.SyncAllDimensionMembers(); + rDestObject.InvalidateData(); // before getting the new output area // make sure the table has a name (not set by dialog) - if (rDestObj.GetName().isEmpty()) - rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName()); + if (rDestObject.GetName().isEmpty()) + rDestObject.SetName(rDoc.GetDPCollection()->CreateNewName()); bool bOverflow = false; - ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow); + ScRange aNewOut = rDestObject.GetNewOutputRange(bOverflow); if (bOverflow) { @@ -132,17 +143,19 @@ bool CreatePivotTableOperation::runImplementation() if (mbRecord) sc::pivot::createUndoDoc(pNewUndoDoc, rDoc, aNewOut); - rDestObj.Output(aNewOut.aStart); + rDestObject.Output(aNewOut.aStart); mrDocShell.PostPaintGridAll(); //! only necessary parts if (mbRecord) { mrDocShell.GetUndoManager()->AddUndoAction(std::make_unique( - mrDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObj, false)); + mrDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObject, false)); } + syncSheetViews(); + // notify API objects - rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName())); + rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObject.GetName())); aModificator.SetDocumentModified(); return true;