sc/Library_sc.mk | 1 sc/inc/SheetView.hxx | 10 + sc/inc/SheetViewManager.hxx | 5 sc/qa/unit/tiledrendering/SheetViewTest.cxx | 146 ++++++++++++++++ sc/source/core/data/SheetViewManager.cxx | 8 sc/source/ui/docshell/SheetViewOperationsTester.cxx | 182 ++++++++++++++++++++ sc/source/ui/docshell/dbdocfun.cxx | 30 +++ sc/source/ui/docshell/docfunc.cxx | 118 ++++++++++++ sc/source/ui/inc/SheetViewOperationsTester.hxx | 84 +++++++++ sc/source/ui/inc/dbdocfun.hxx | 5 sc/source/ui/inc/docfunc.hxx | 3 sc/source/ui/inc/viewfunc.hxx | 10 - sc/source/ui/view/viewfunc.cxx | 14 + 13 files changed, 608 insertions(+), 8 deletions(-)
New commits: commit 696c5a945664432ca0bf8016f5eda8dd1e383d06 Author: Tomaž Vajngerl <[email protected]> AuthorDate: Thu Sep 25 11:47:10 2025 +0900 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Thu Oct 9 09:03:58 2025 +0200 sc: restrict ops. to sheet views and sheet view unsyncing This adds a tester for the operations which can be performed on a sheet view, and a way to unsync sheet view if the operation is performed on the default view, which can't be synced with the sheet views. When a sheet view is unsynced, an operations will not sync the result of the operation performed on the sheet view. This means any change the operation performed on the sheet view will not be seen in the default view or other sheet views. The tester has not been added to all operations yet, but only to an initial set, which will be expanded later on when more operations are tested. Change-Id: If51c507a221bf93dc4364b174886a72804d373d8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191522 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> (cherry picked from commit 4aac912da951eaf230d007f40565821aafde2d89) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191125 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk index b716acae1068..724c24b1de00 100644 --- a/sc/Library_sc.mk +++ b/sc/Library_sc.mk @@ -461,6 +461,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/ui/docshell/pagedata \ sc/source/ui/docshell/pntlock \ sc/source/ui/docshell/servobj \ + sc/source/ui/docshell/SheetViewOperationsTester \ sc/source/ui/docshell/sizedev \ sc/source/ui/docshell/tablink \ sc/source/ui/drawfunc/chartsh \ diff --git a/sc/inc/SheetView.hxx b/sc/inc/SheetView.hxx index 352a28863a17..4411f56b7b1d 100644 --- a/sc/inc/SheetView.hxx +++ b/sc/inc/SheetView.hxx @@ -23,12 +23,22 @@ class SheetView { private: ScTable* mpTable = nullptr; + bool mbSynced = true; public: SheetView(ScTable* pTable); ScTable* getTablePointer() const; SCTAB getTableNumber() const; + + /** A sheet view is valid if the pointer to the table is set */ + bool isValid() const; + + /** Mark the sheet view as unsynced (not synced). */ + void unsync() { mbSynced = false; } + + /** Is the sheet view synced with its default view. */ + bool isSynced() const { return mbSynced; } }; } diff --git a/sc/inc/SheetViewManager.hxx b/sc/inc/SheetViewManager.hxx index a2e50e716810..d74d774bdedc 100644 --- a/sc/inc/SheetViewManager.hxx +++ b/sc/inc/SheetViewManager.hxx @@ -11,6 +11,7 @@ #include "SheetViewTypes.hxx" #include "SheetView.hxx" #include "types.hxx" +#include "scdllapi.h" #include <o3tl/safeint.hxx> #include <vector> @@ -21,7 +22,7 @@ class ScTable; namespace sc { /** Manager and the holder of the sheet views for a sheet. */ -class SheetViewManager +class SC_DLLPUBLIC SheetViewManager { private: std::vector<std::shared_ptr<SheetView>> maViews; @@ -51,6 +52,8 @@ public: /** Calculate the next sheet view ID from the current ID. */ SheetViewID getNextSheetView(SheetViewID nID); + + void unsyncAllSheetViews(); }; } diff --git a/sc/qa/unit/tiledrendering/SheetViewTest.cxx b/sc/qa/unit/tiledrendering/SheetViewTest.cxx index 57384347c249..5b84c1471683 100644 --- a/sc/qa/unit/tiledrendering/SheetViewTest.cxx +++ b/sc/qa/unit/tiledrendering/SheetViewTest.cxx @@ -260,6 +260,152 @@ CPPUNIT_TEST_FIXTURE(SheetViewTest, testRemoveSheetView) 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(); + dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {}); + Scheduler::ProcessEventsToIdle(); + + // Create a new sheet view for view 3 + SfxLokHelper::setView(aView3.getViewID()); + Scheduler::ProcessEventsToIdle(); + dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {}); + Scheduler::ProcessEventsToIdle(); + + // 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->getSheetViews().size()); + + auto pSheetView1 = pSheetViewManager->get(0); + CPPUNIT_ASSERT_EQUAL(true, pSheetView1->isSynced()); + auto pSheetView2 = pSheetViewManager->get(1); + CPPUNIT_ASSERT_EQUAL(true, pSheetView2->isSynced()); + + // Sort, which will unsync sheet views + pTabView2->SetCursor(0, 0); + dispatchCommand(mxComponent, u".uno:SortDescending"_ustr, {}); + + CPPUNIT_ASSERT_EQUAL(false, pSheetView1->isSynced()); + CPPUNIT_ASSERT_EQUAL(false, pSheetView2->isSynced()); +} + +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(); + dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {}); + Scheduler::ProcessEventsToIdle(); + + // Create a new sheet view for view 3 + SfxLokHelper::setView(aView3.getViewID()); + Scheduler::ProcessEventsToIdle(); + dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {}); + Scheduler::ProcessEventsToIdle(); + + // 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->getSheetViews().size()); + + auto pSheetView1 = pSheetViewManager->get(0); + CPPUNIT_ASSERT_EQUAL(true, pSheetView1->isSynced()); + + auto pSheetView2 = pSheetViewManager->get(1); + CPPUNIT_ASSERT_EQUAL(true, pSheetView2->isSynced()); + + // Sort, which will unsync sheet views + SfxLokHelper::setView(aView1.getViewID()); + Scheduler::ProcessEventsToIdle(); + pTabView1->SetCursor(0, 0); + dispatchCommand(mxComponent, u".uno:SortDescending"_ustr, {}); + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT_EQUAL(false, pSheetView1->isSynced()); + CPPUNIT_ASSERT_EQUAL(true, pSheetView2->isSynced()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/SheetViewManager.cxx b/sc/source/core/data/SheetViewManager.cxx index b642ae2f26fc..6ddc9c72f76d 100644 --- a/sc/source/core/data/SheetViewManager.cxx +++ b/sc/source/core/data/SheetViewManager.cxx @@ -72,6 +72,14 @@ SheetViewID SheetViewManager::getNextSheetView(SheetViewID nID) return DefaultSheetViewID; } + +void SheetViewManager::unsyncAllSheetViews() +{ + for (auto const& pSheetView : maViews) + { + pSheetView->unsync(); + } +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/docshell/SheetViewOperationsTester.cxx b/sc/source/ui/docshell/SheetViewOperationsTester.cxx new file mode 100644 index 000000000000..94dc4154118d --- /dev/null +++ b/sc/source/ui/docshell/SheetViewOperationsTester.cxx @@ -0,0 +1,182 @@ +/* -*- 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 <SheetViewOperationsTester.hxx> +#include <SheetViewManager.hxx> +#include <docsh.hxx> +#include <viewdata.hxx> +#include <sal/log.hxx> + +namespace sc +{ +namespace +{ +/** Return operation as string. */ +constexpr std::string_view getOperationName(Operation eOperation) +{ + switch (eOperation) + { + case Operation::Unknown: + return "Unknown"; + case Operation::DeleteContent: + return "DeleteContent"; + case Operation::DeleteCell: + return "DeleteCell"; + case Operation::TransliterateText: + return "TransliterateText"; + case Operation::SetNormalString: + return "SetNormalString"; + case Operation::SetNoteText: + return "SetNoteText"; + case Operation::ReplaceNoteText: + return "ReplaceNoteText"; + case Operation::InsertColumnsBefore: + return "InsertColumnsBefore"; + case Operation::InsertColumnsAfter: + return "InsertColumnsAfter"; + case Operation::InsertRowsBefore: + return "InsertRowsBefore"; + case Operation::InsertRowsAfter: + return "InsertRowsAfter"; + case Operation::InsertCellsDown: + return "InsertCellsDown"; + case Operation::InsertCellsRight: + return "InsertCellsRight"; + case Operation::DeleteColumns: + return "DeleteColumns"; + case Operation::DeleteRows: + return "DeleteRows"; + case Operation::DeleteCellsLeft: + return "DeleteCellsLeft"; + case Operation::DeleteCellsUp: + return "DeleteCellsUp"; + case Operation::MoveBlock: + return "MoveBlock"; + case Operation::ClearItems: + return "ClearItems"; + case Operation::ChangeIndent: + return "ChangeIndent"; + case Operation::AutoFormat: + return "AutoFormat"; + case Operation::EnterMatrix: + return "EnterMatrix"; + case Operation::TabOperation: + return "TabOperation"; + case Operation::FillSimple: + return "FillSimple"; + case Operation::FillSeries: + return "FillSeries"; + case Operation::FillAuto: + return "FillAuto"; + case Operation::MergeCells: + return "MergeCells"; + case Operation::InsertNameList: + return "InsertNameList"; + case Operation::ConvertFormulaToValue: + return "ConvertFormulaToValue"; + case Operation::Sort: + return "Sort"; + case Operation::Query: + return "Query"; + case Operation::SubTotals: + return "SubTotals"; + case Operation::PivotTableUpdate: + return "PivotTableUpdate"; + case Operation::PivotTableRemove: + return "PivotTableRemove"; + case Operation::PivotTableCreate: + return "PivotTableCreate"; + case Operation::SparklineInsert: + return "SparklineInsert"; + case Operation::SparklineDelete: + return "SparklineDelete"; + case Operation::SparklineChange: + return "SparklineChange"; + case Operation::SparklineGroup: + return "SparklineGroup"; + case Operation::SparklineUngroup: + return "SparklineUngroup"; + case Operation::SparklineGroupDelete: + return "SparklineGroupDelete"; + case Operation::SparklineGroupChange: + return "SparklineGroupChange"; + case Operation::EnterData: + return "EnterData"; + } + return ""; +} + +/** Supported operations by sheet view. */ +constexpr bool isSupported(Operation eOperation) +{ + return eOperation == Operation::EnterData || eOperation == Operation::SetNormalString + || eOperation == Operation::Sort; +} + +/** Operations on default view that unsync all the sheet views. */ +constexpr bool doesUnsycAllSheetView(Operation eOperation) +{ + bool bOperationAllowed + = eOperation == Operation::EnterData || eOperation == Operation::SetNormalString; + + return !bOperationAllowed; +} + +/** Operations that unsync the current sheet view. */ +constexpr bool doesUnsycSheetView(Operation eOperation) { return eOperation == Operation::Sort; } + +} // end anonymous namespace + +bool SheetViewOperationsTester::check(Operation eOperation) const +{ + if (!mpViewData) + return true; + + auto& rDocument = mpViewData->GetDocument(); + SCTAB nTab = mpViewData->GetTabNumber(); + + // Never allow direct changes to the data holder sheet of the sheet view. + if (rDocument.IsSheetView(nTab)) + return false; + + sc::SheetViewID nSheetViewID = mpViewData->GetSheetViewID(); + + std::shared_ptr<sc::SheetViewManager> pSheetViewManager = rDocument.GetSheetViewManager(nTab); + + if (nSheetViewID == sc::DefaultSheetViewID && doesUnsycAllSheetView(eOperation)) + { + // Only unsync if there are sheet views. + if (!pSheetViewManager->isEmpty()) + { + pSheetViewManager->unsyncAllSheetViews(); + SAL_INFO("sc.ui", "Operation '" << getOperationName(eOperation) + << "' unsynced all sheet views for TAB " << nTab); + } + } + else + { + bool bSupported = isSupported(eOperation); + SAL_INFO_IF(!bSupported, "sc.ui", + "Operation '" << getOperationName(eOperation) + << "' not supported on sheet view '" << nSheetViewID << "'"); + + if (bSupported && doesUnsycSheetView(eOperation)) + { + std::shared_ptr<sc::SheetView> pSheetView = pSheetViewManager->get(nSheetViewID); + pSheetView->unsync(); + SAL_INFO("sc.ui", "Operation '" << getOperationName(eOperation) + << "' unsynced sheet view '" << nSheetViewID << "'"); + } + return bSupported; + } + + return true; +} +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx index 77206be9ff1e..2886f2530e15 100644 --- a/sc/source/ui/docshell/dbdocfun.cxx +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -57,11 +57,18 @@ #include <chartlis.hxx> #include <ChartTools.hxx> +#include <SheetViewOperationsTester.hxx> #include <memory> using namespace ::com::sun::star; +bool ScDBDocFunc::CheckSheetViewProtection(sc::Operation eOperation) +{ + sc::SheetViewOperationsTester aSheetViewTester(ScDocShell::GetViewData()); + return aSheetViewTester.check(eOperation); +} + bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange ) { @@ -513,6 +520,10 @@ bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam, else nStartingColToEdit++; } + + if (!CheckSheetViewProtection(sc::Operation::Sort)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock( rDoc, nTab, nStartingColToEdit, nStartingRowToEdit, aLocalParam.nCol2, aLocalParam.nRow2, true /*bNoMatrixAtAll*/); @@ -741,6 +752,9 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, return false; } + if (!CheckSheetViewProtection(sc::Operation::Query)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, aLocalParam.nCol2,aLocalParam.nRow2); @@ -1058,6 +1072,9 @@ void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam, return; } + if (!CheckSheetViewProtection(sc::Operation::SubTotals)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow()); if (!aTester.IsEditable()) { @@ -1326,6 +1343,10 @@ bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewOb ScRangeList aRanges; aRanges.push_back(pOldObj->GetOutRange()); aRanges.push_back(ScRange(pNewObj->GetOutRange().aStart)); // at least one cell in the output position must be editable. + + if (!CheckSheetViewProtection(sc::Operation::PivotTableUpdate)) + return false; + if (!isEditable(rDocShell, aRanges, bApi)) return false; @@ -1408,6 +1429,9 @@ bool ScDBDocFunc::RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool ScDocShellModificator aModificator(rDocShell); weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); + if (!CheckSheetViewProtection(sc::Operation::PivotTableRemove)) + return false; + if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi)) return false; @@ -1490,6 +1514,9 @@ bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool ScDocShellModificator aModificator(rDocShell); weld::WaitObject aWait(ScDocShell::GetActiveDialogParent()); + if (!CheckSheetViewProtection(sc::Operation::PivotTableCreate)) + return false; + // At least one cell in the output range should be editable. Check in advance. ScDocument& rDoc = rDocShell.GetDocument(); if (!rDoc.IsImportingXML() && !isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi)) @@ -1602,6 +1629,9 @@ bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi) ScDocShellModificator aModificator( rDocShell ); weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); + if (!CheckSheetViewProtection(sc::Operation::PivotTableUpdate)) + return false; + if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi, sc::EditAction::UpdatePivotTable)) return false; diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx index 36dd3d1b5a09..4e72ede58f49 100644 --- a/sc/source/ui/docshell/docfunc.cxx +++ b/sc/source/ui/docshell/docfunc.cxx @@ -106,6 +106,7 @@ #include <undo/UndoUngroupSparklines.hxx> #include <undo/UndoGroupSparklines.hxx> #include <undo/UndoEditSparkline.hxx> +#include <SheetViewOperationsTester.hxx> #include <config_features.h> #include <memory> @@ -118,6 +119,12 @@ using namespace com::sun::star; #define AUTOFORMAT_WARN_SIZE 0x10ffffUL +bool ScDocFunc::CheckSheetViewProtection(sc::Operation eOperation) +{ + sc::SheetViewOperationsTester aSheetViewTester(ScDocShell::GetViewData()); + return aSheetViewTester.check(eOperation); +} + void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction) { // #i101118# if drawing layer collects the undo actions, add it there @@ -596,6 +603,9 @@ bool ScDocFunc::DeleteContents( if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; + if (!CheckSheetViewProtection(sc::Operation::DeleteContent)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, rMark); if (!aTester.IsEditable()) { @@ -699,6 +709,9 @@ bool ScDocFunc::DeleteCell( if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; + if (!CheckSheetViewProtection(sc::Operation::DeleteCell)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark); if (!aTester.IsEditable()) { @@ -767,6 +780,9 @@ bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags if (!rDoc.IsUndoEnabled()) bRecord = false; + if (!CheckSheetViewProtection(sc::Operation::TransliterateText)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, rMark); if (!aTester.IsEditable()) { @@ -821,6 +837,10 @@ bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, con ScDocument& rDoc = rDocShell.GetDocument(); bool bUndo(rDoc.IsUndoEnabled()); + + if (!CheckSheetViewProtection(sc::Operation::SetNormalString)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row()); if (!aTester.IsEditable()) { @@ -1339,6 +1359,10 @@ void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool ScDocShellModificator aModificator( rDocShell ); ScDocument& rDoc = rDocShell.GetDocument(); + + if (!CheckSheetViewProtection(sc::Operation::SetNoteText)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row()); if (!aTester.IsEditable()) { @@ -1364,6 +1388,10 @@ void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, c { ScDocShellModificator aModificator( rDocShell ); ScDocument& rDoc = rDocShell.GetDocument(); + + if (!CheckSheetViewProtection(sc::Operation::ReplaceNoteText)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row()); if (aTester.IsEditable()) { @@ -1898,30 +1926,43 @@ bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow; ScEditableTester aTester; - + sc::Operation eOperation = sc::Operation::Unknown; switch (eCmd) { case INS_INSCOLS_BEFORE: + eOperation = sc::Operation::InsertColumnsBefore; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::InsertColumnsBefore, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark); break; case INS_INSCOLS_AFTER: + eOperation = sc::Operation::InsertColumnsBefore; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::InsertColumnsAfter, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark); break; case INS_INSROWS_BEFORE: + eOperation = sc::Operation::InsertRowsBefore; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::InsertRowsBefore, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark); break; case INS_INSROWS_AFTER: + eOperation = sc::Operation::InsertRowsAfter; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::InsertRowsAfter, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark); break; default: + if (eCmd == INS_CELLSDOWN) + eOperation = sc::Operation::InsertCellsDown; + else if (eCmd == INS_CELLSRIGHT) + eOperation = sc::Operation::InsertCellsRight; + aTester = ScEditableTester::CreateAndTestSelectedBlock( rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark); + break; } + if (!CheckSheetViewProtection(eOperation)) + return false; + if (!aTester.IsEditable()) { if (!bApi) @@ -2400,22 +2441,32 @@ bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, nEditTestEndY = rDoc.MaxRow(); ScEditableTester aTester; - + sc::Operation eOperation = sc::Operation::Unknown; switch (eCmd) { case DelCellCmd::Cols: + eOperation = sc::Operation::DeleteColumns; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::DeleteColumns, nUndoStartCol, 0, nUndoEndCol, rDoc.MaxRow(), aMark); break; case DelCellCmd::Rows: + eOperation = sc::Operation::DeleteRows; aTester = ScEditableTester::CreateAndTestBlockForAction( rDoc, sc::EditAction::DeleteRows, 0, nUndoStartRow, rDoc.MaxCol(), nUndoEndRow, aMark); break; default: + if (eCmd == DelCellCmd::CellsLeft) + eOperation = sc::Operation::DeleteCellsLeft; + else if (eCmd == DelCellCmd::CellsUp) + eOperation = sc::Operation::DeleteCellsUp; + aTester = ScEditableTester::CreateAndTestSelectedBlock( rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark); } + if (!CheckSheetViewProtection(eOperation)) + return false; + if (!aTester.IsEditable()) { if (!bApi) @@ -2970,6 +3021,9 @@ bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos, return false; } + if (!CheckSheetViewProtection(sc::Operation::MoveBlock)) + return false; + // Test for cell protection ScEditableTester aTester; @@ -4118,6 +4172,10 @@ void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, b ScDocument& rDoc = rDocShell.GetDocument(); bool bUndo (rDoc.IsUndoEnabled()); + + if (!CheckSheetViewProtection(sc::Operation::ClearItems)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, rMark); if (!aTester.IsEditable()) { @@ -4162,6 +4220,10 @@ bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bAp ScDocument& rDoc = rDocShell.GetDocument(); bool bUndo(rDoc.IsUndoEnabled()); + + if (!CheckSheetViewProtection(sc::Operation::ChangeIndent)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, rMark); if (!aTester.IsEditable()) { @@ -4251,6 +4313,10 @@ bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark, } ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat(); + + if (!CheckSheetViewProtection(sc::Operation::AutoFormat)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nStartCol, nStartRow, nEndCol, nEndRow, aMark); if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() ) { @@ -4373,6 +4439,9 @@ bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark, aMark.SelectTable( nTab, true ); } + if (!CheckSheetViewProtection(sc::Operation::EnterMatrix)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nStartCol, nStartRow, nEndCol, nEndRow, aMark); if ( aTester.IsEditable() ) { @@ -4460,6 +4529,9 @@ bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark, aMark.SelectTable( nTab, true ); } + if (!CheckSheetViewProtection(sc::Operation::TabOperation)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nStartCol, nStartRow, nEndCol, nEndRow, aMark); if ( aTester.IsEditable() ) { @@ -4604,6 +4676,9 @@ bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark, aMark.SelectTable( nTab, true ); } + if (!CheckSheetViewProtection(sc::Operation::FillSimple)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nStartCol, nStartRow, nEndCol, nEndRow, aMark); if ( aTester.IsEditable() ) { @@ -4717,6 +4792,9 @@ bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark, aMark.SelectTable( nTab, true ); } + if (!CheckSheetViewProtection(sc::Operation::FillSeries)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nStartCol, nStartRow, nEndCol, nEndRow, aMark); if ( aTester.IsEditable() ) { @@ -4885,6 +4963,9 @@ bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir e break; } + if (!CheckSheetViewProtection(sc::Operation::FillAuto)) + return false; + // Test for cell protection //! Source range can be protected !!! //! but can't contain matrix fragments !!! @@ -4985,8 +5066,12 @@ bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bo if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; + if (!CheckSheetViewProtection(sc::Operation::MergeCells)) + return false; + for (const auto& rTab : rOption.maTabs) { + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow); if (!aTester.IsEditable()) { @@ -5424,6 +5509,9 @@ bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi ) SCCOL nEndCol = nStartCol + 1; SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1; + if (!CheckSheetViewProtection(sc::Operation::InsertNameList)) + return false; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow); if (aTester.IsEditable()) { @@ -5738,6 +5826,9 @@ void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction if (!rDoc.IsUndoEnabled()) bRecord = false; + if (!CheckSheetViewProtection(sc::Operation::ConvertFormulaToValue)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestRange(rDoc, rRange, sc::EditAction::Unknown); if (!aTester.IsEditable()) { @@ -5780,6 +5871,9 @@ void ScDocFunc::EndListAction() bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange, const std::shared_ptr<sc::SparklineGroup>& pSparklineGroup) { + if (!CheckSheetViewProtection(sc::Operation::SparklineInsert)) + return false; + std::vector<sc::SparklineData> aSparklineDataVector; if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col()) @@ -5872,6 +5966,10 @@ bool ScDocFunc::DeleteSparkline(ScAddress const& rAddress) if (!rDocument.HasSparkline(rAddress)) return false; + if (!CheckSheetViewProtection(sc::Operation::SparklineDelete)) + return false; + + auto pUndoDeleteSparkline = std::make_unique<sc::UndoDeleteSparkline>(rDocShell, rAddress); // delete sparkline by "redoing" pUndoDeleteSparkline->Redo(); @@ -5890,6 +5988,9 @@ bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& if (!rDocument.HasTable(nTab)) return false; + if (!CheckSheetViewProtection(sc::Operation::SparklineGroupDelete)) + return false; + auto pUndo = std::make_unique<sc::UndoDeleteSparklineGroup>(rDocShell, pSparklineGroup, nTab); // delete sparkline group by "redoing" pUndo->Redo(); @@ -5900,6 +6001,10 @@ bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup, sc::SparklineAttributes const& rNewAttributes) { + if (!CheckSheetViewProtection(sc::Operation::SparklineGroupChange)) + return false; + + auto pUndo = std::make_unique<sc::UndoEditSparklneGroup>(rDocShell, pExistingSparklineGroup, rNewAttributes); // change sparkline group attributes by "redoing" pUndo->Redo(); @@ -5909,6 +6014,9 @@ bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGrou bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup) { + if (!CheckSheetViewProtection(sc::Operation::SparklineGroup)) + return false; + auto pUndo = std::make_unique<sc::UndoGroupSparklines>(rDocShell, rRange, rpGroup); // group sparklines by "redoing" pUndo->Redo(); @@ -5918,6 +6026,9 @@ bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::Spark bool ScDocFunc::UngroupSparklines(ScRange const& rRange) { + if (!CheckSheetViewProtection(sc::Operation::SparklineUngroup)) + return false; + auto pUndo = std::make_unique<sc::UndoUngroupSparklines>(rDocShell, rRange); // ungroup sparklines by "redoing" pUndo->Redo(); @@ -5927,6 +6038,9 @@ bool ScDocFunc::UngroupSparklines(ScRange const& rRange) bool ScDocFunc::ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange) { + if (!CheckSheetViewProtection(sc::Operation::SparklineChange)) + return false; + auto pUndo = std::make_unique<sc::UndoEditSparkline>(rDocShell, rpSparkline, nTab, rDataRange); // change sparkline by "redoing" pUndo->Redo(); diff --git a/sc/source/ui/inc/SheetViewOperationsTester.hxx b/sc/source/ui/inc/SheetViewOperationsTester.hxx new file mode 100644 index 000000000000..21a1ac00ac34 --- /dev/null +++ b/sc/source/ui/inc/SheetViewOperationsTester.hxx @@ -0,0 +1,84 @@ +/* -*- 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 <string_view> + +#pragma once + +class ScViewData; + +namespace sc +{ +/** Operations that can be performed on a sheet. */ +enum class Operation +{ + Unknown, + DeleteContent, + DeleteCell, + TransliterateText, + SetNormalString, + SetNoteText, + ReplaceNoteText, + InsertColumnsBefore, + InsertColumnsAfter, + InsertRowsBefore, + InsertRowsAfter, + InsertCellsDown, + InsertCellsRight, + DeleteColumns, + DeleteRows, + DeleteCellsLeft, + DeleteCellsUp, + MoveBlock, + ClearItems, + ChangeIndent, + AutoFormat, + EnterMatrix, + TabOperation, + FillSimple, + FillSeries, + FillAuto, + MergeCells, + InsertNameList, + ConvertFormulaToValue, + Sort, + Query, + SubTotals, + PivotTableUpdate, + PivotTableRemove, + PivotTableCreate, + SparklineInsert, + SparklineDelete, + SparklineChange, + SparklineGroup, + SparklineUngroup, + SparklineGroupDelete, + SparklineGroupChange, + EnterData, +}; + +/** Tester for operations on sheet views and default views + * + * Tests if an operation can be performed in a sheet view or the operation on the default view unsyncs the sheet view(s). + */ +class SheetViewOperationsTester +{ + ScViewData* mpViewData; + +public: + SheetViewOperationsTester(ScViewData* pViewData) + : mpViewData(pViewData) + { + } + + bool check(Operation eOperation) const; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/dbdocfun.hxx b/sc/source/ui/inc/dbdocfun.hxx index 8fdd5f7f1002..7ada3b2ad0aa 100644 --- a/sc/source/ui/inc/dbdocfun.hxx +++ b/sc/source/ui/inc/dbdocfun.hxx @@ -39,6 +39,10 @@ namespace com::sun::star::beans { struct PropertyValue; } namespace svx { class ODataAccessDescriptor; } +namespace sc +{ + enum class Operation; +} class ScDBDocFunc { @@ -46,6 +50,7 @@ friend class ScDBFunc; private: ScDocShell& rDocShell; + static bool CheckSheetViewProtection(sc::Operation eOperation); public: ScDBDocFunc( ScDocShell& rDocSh ): rDocShell(rDocSh) {} diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx index 7a41b8d51636..e308884c9b97 100644 --- a/sc/source/ui/inc/docfunc.hxx +++ b/sc/source/ui/inc/docfunc.hxx @@ -48,6 +48,7 @@ class ScConditionalFormat; class ScConditionalFormatList; class ScUndoRemoveMerge; class ScRangeName; +class ScViewData; enum class TransliterationFlags; enum class CreateNameFlags; @@ -57,6 +58,7 @@ namespace sc class SparklineAttributes; class SparklineGroup; class Sparkline; + enum class Operation; } namespace tools { @@ -66,6 +68,7 @@ namespace tools class ScDocFunc { ScDocShell& rDocShell; + static bool CheckSheetViewProtection(sc::Operation eOperation); protected: bool AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi ); diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx index a497d47968ae..47ab774a017e 100644 --- a/sc/source/ui/inc/viewfunc.hxx +++ b/sc/source/ui/inc/viewfunc.hxx @@ -50,10 +50,10 @@ enum class CreateNameFlags; namespace editeng { class SvxBorderLine; } namespace com::sun::star::embed { class XEmbeddedObject; } -namespace sc { - -struct ColRowSpan; - +namespace sc +{ + struct ColRowSpan; + enum class Operation; } namespace com::sun::star::datatransfer { class XTransferable; } @@ -398,6 +398,8 @@ private: void MarkAndJumpToRanges(const ScRangeList& rRanges); void CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount ); + + bool CheckSheetViewProtection(sc::Operation eOperation); }; extern bool bPasteIsMove; diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx index e608f2be9cb3..0088b890f213 100644 --- a/sc/source/ui/view/viewfunc.cxx +++ b/sc/source/ui/view/viewfunc.cxx @@ -78,6 +78,7 @@ #include <stringutil.hxx> #include <SparklineList.hxx> #include <SheetViewManager.hxx> +#include <SheetViewOperationsTester.hxx> #include <memory> @@ -134,9 +135,14 @@ ScViewFunc::~ScViewFunc() { } -namespace +bool ScViewFunc::CheckSheetViewProtection(sc::Operation eOperation) { + sc::SheetViewOperationsTester aSheetViewTester(&GetViewData()); + return aSheetViewTester.check(eOperation); +} +namespace +{ struct FormulaProcessingContext { std::shared_ptr<ScAddress> aPos; @@ -783,6 +789,9 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, ScDocFunc &rFunc = GetViewData().GetDocFunc(); std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(GetViewData().GetDocShell()); + if (!CheckSheetViewProtection(sc::Operation::EnterData)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestSelectedBlock(rDoc, nCol, nRow, nCol, nRow, aMark); if (!aTester.IsEditable()) { @@ -892,6 +901,9 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, ScDocShellModificator aModificator( rDocSh ); + if (!CheckSheetViewProtection(sc::Operation::EnterData)) + return; + ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, nTab, nCol, nRow, nCol, nRow); if (aTester.IsEditable()) {
