sc/Library_sc.mk | 2 sc/source/ui/docshell/docfunc.cxx | 101 -------------- sc/source/ui/inc/docfunc.hxx | 5 sc/source/ui/inc/operation/DeleteContentOperation.hxx | 39 +++++ sc/source/ui/inc/operation/Operation.hxx | 54 +++++++ sc/source/ui/operation/DeleteContentOperation.cxx | 128 ++++++++++++++++++ sc/source/ui/operation/Operation.cxx | 23 +++ 7 files changed, 258 insertions(+), 94 deletions(-)
New commits: commit fa829549cf0446eb940c0d7a96538c651513dfde Author: Tomaž Vajngerl <[email protected]> AuthorDate: Wed Jan 28 10:57:15 2026 +0900 Commit: Miklos Vajna <[email protected]> CommitDate: Tue Feb 10 14:49:25 2026 +0100 sc: Introduce Operation abstract class and DeleteContentOperation An Operation abstract class is an interface for writing operations whcih are an atomic coarse change to the document model. The Operation is needed so we can make it easier to handle and extend various operations we have, and to separate various task that are typically run in an operation. This should also reduce duplication of variosu tasks. It also makes dealing with operations easier as each operation is now confined in it's own class / file. Currently the Operation doesn't include a lot yet, but this will be added on demand when more and more operations are converted to use Oepration derived classes. This change also includes the first DeleteContentOperation which uses the code from ScDocFunc::DeleteContents. Change-Id: I023b4544041ba4b19fdba00d5c8b3559375653a6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198730 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk index cf15bce37560..cd5056f670f3 100644 --- a/sc/Library_sc.mk +++ b/sc/Library_sc.mk @@ -529,6 +529,8 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/ui/navipi/navcitem \ sc/source/ui/navipi/navipi \ sc/source/ui/navipi/scenwnd \ + sc/source/ui/operation/DeleteContentOperation \ + sc/source/ui/operation/Operation \ sc/source/ui/pagedlg/areasdlg \ sc/source/ui/pagedlg/tphfedit \ sc/source/ui/sidebar/AlignmentPropertyPanel \ diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx index 08d9c71e5ec0..45232a35df67 100644 --- a/sc/source/ui/docshell/docfunc.cxx +++ b/sc/source/ui/docshell/docfunc.cxx @@ -110,6 +110,7 @@ #include <config_features.h> #include <memory> +#include <operation/DeleteContentOperation.hxx> #include <basic/basmgr.hxx> #include <set> #include <vector> @@ -145,7 +146,7 @@ void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction) // paint row above the range (because of lines after AdjustRowHeight) -static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange ) +void ScDocFunc::PaintAbove(ScDocShell& rDocShell, const ScRange& rRange) { SCROW nRow = rRange.aStart.Row(); if ( nRow > 0 ) @@ -589,98 +590,10 @@ void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<S lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false); } -bool ScDocFunc::DeleteContents( - const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi ) +bool ScDocFunc::DeleteContents(const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi) { - ScDocShellModificator aModificator( rDocShell ); - - if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) - { - OSL_FAIL("ScDocFunc::DeleteContents without markings"); - return false; - } - - ScDocument& rDoc = rDocShell.GetDocument(); - - if (bRecord && !rDoc.IsUndoEnabled()) - bRecord = false; - - if (!CheckSheetViewProtection(sc::OperationType::DeleteContent)) - return false; - - ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, rMark); - if (!aTester.IsEditable()) - { - if (!bApi) - rDocShell.ErrorMessage(aTester.GetMessageId()); - return false; - } - - ScMarkData aMultiMark = rMark; - aMultiMark.SetMarking(false); // for MarkToMulti - - ScDocumentUniquePtr pUndoDoc; - bool bMulti = aMultiMark.IsMultiMarked(); - aMultiMark.MarkToMulti(); - const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea(); - ScRange aExtendedRange(aMarkRange); - if ( rDoc.ExtendMerge( aExtendedRange, true ) ) - bMulti = false; - - // no objects on protected tabs - bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark); - - sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted - if ( nFlags & InsertDeleteFlags::ATTRIB ) - rDocShell.UpdatePaintExt( nExtFlags, aMarkRange ); - - // order of operations: - // 1) BeginDrawUndo - // 2) Delete objects (DrawUndo will be filled) - // 3) Copy content for undo and set up undo actions - // 4) Delete content - - bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); - if (bRecord && bDrawUndo) - rDoc.BeginDrawUndo(); - - if (bObjects) - { - if (bMulti) - rDoc.DeleteObjectsInSelection( aMultiMark ); - else - rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), - aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), - aMultiMark ); - } - - // To keep track of all non-empty cells within the deleted area. - std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans; - - if ( bRecord ) - { - pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti); - pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange); - } - - rDoc.DeleteSelection( nFlags, aMultiMark ); - - // add undo action after drawing undo is complete (objects and note captions) - if( bRecord ) - { - sc::DocFuncUtil::addDeleteContentsUndo( - rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange, - std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo); - } - - if (!AdjustRowHeight( aExtendedRange, true, bApi )) - rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags ); - else if (nExtFlags & SC_PF_LINES) - lcl_PaintAbove( rDocShell, aExtendedRange ); // for lines above the range - - aModificator.SetDocumentModified(); - - return true; + sc::DeleteContentOperation aOperation(*this, rDocShell, rMark, nFlags, bRecord, bApi); + return aOperation.run(); } tools::Long ScDocShell::GetTwipWidthHint(const ScAddress& rPos) @@ -1537,7 +1450,7 @@ bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& r if (!AdjustRowHeight( aMultiRange, true, bApi )) rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags ); else if (nExtFlags & SC_PF_LINES) - lcl_PaintAbove( rDocShell, aMultiRange ); // because of lines above the range + PaintAbove( rDocShell, aMultiRange ); // because of lines above the range aModificator.SetDocumentModified(); } @@ -2897,7 +2810,7 @@ bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, { // paint only what is not done by AdjustRowHeight if (nExtFlags & SC_PF_LINES) - lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) ); + PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) ); if (nPaintFlags & PaintPartFlags::Top) rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top ); } diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx index eb3b8d4b2e5a..13b4cc1354f5 100644 --- a/sc/source/ui/inc/docfunc.hxx +++ b/sc/source/ui/inc/docfunc.hxx @@ -59,6 +59,7 @@ namespace sc class SparklineGroup; class Sparkline; enum class OperationType; + class DeleteContentOperation; } namespace tools { @@ -67,6 +68,8 @@ namespace tools class ScDocFunc { + friend class sc::DeleteContentOperation; + ScDocShell& rDocShell; static bool CheckSheetViewProtection(sc::OperationType eOperation); @@ -258,6 +261,8 @@ public: SC_DLLPUBLIC bool UngroupSparklines(ScRange const& rRange); SC_DLLPUBLIC bool ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange); + static void PaintAbove(ScDocShell& rDocShell, const ScRange& rRange); + private: void ProtectDocument(const ScDocProtection& rProtect); }; diff --git a/sc/source/ui/inc/operation/DeleteContentOperation.hxx b/sc/source/ui/inc/operation/DeleteContentOperation.hxx new file mode 100644 index 000000000000..fb245363f0dc --- /dev/null +++ b/sc/source/ui/inc/operation/DeleteContentOperation.hxx @@ -0,0 +1,39 @@ +/* -*- 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/. + */ + +#pragma once + +#include "Operation.hxx" +#include <sal/types.h> + +class ScDocShell; +class ScMarkData; +class ScDocFunc; +enum class InsertDeleteFlags : sal_Int32; + +namespace sc +{ +/** Operation which deletes content of one or multiple marked cells. */ +class DeleteContentOperation : public Operation +{ +private: + ScDocFunc& mrDocFunc; + ScDocShell& mrDocShell; + ScMarkData const& mrMark; + InsertDeleteFlags mnFlags; + + bool runImplementation() override; + +public: + DeleteContentOperation(ScDocFunc& rDocFunc, ScDocShell& rDocShell, const ScMarkData& rMark, + InsertDeleteFlags nFlags, bool bRecord, bool bApi); +}; +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/operation/Operation.hxx b/sc/source/ui/inc/operation/Operation.hxx new file mode 100644 index 000000000000..f330f1b13c9c --- /dev/null +++ b/sc/source/ui/inc/operation/Operation.hxx @@ -0,0 +1,54 @@ +/* -*- 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/. + */ + +#pragma once + +#include <SheetViewOperationsTester.hxx> + +namespace sc +{ +/** Operation is one atomic coarse change to the document model that can be run from UI or API + * + * An operation is a minimal change that is undoable and redoable. + * It should provide and handle: + * - document modification flag + * - undo/redo if enabled + * - sheet protections + * - sheet view sync and if it can be performed + * - API / UI code paths + * - invalidation + * - logging + * - other common tasks + * + * Each operation should have an OperationType. + */ +class Operation +{ +protected: + OperationType meType = OperationType::Unknown; + bool mbApi : 1 = false; + bool mbRecord : 1 = false; + +public: + Operation(OperationType eType, bool bRecord, bool bApi) + : meType(eType) + , mbApi(bApi) + , mbRecord(bRecord) + { + } + + bool run() { return runImplementation(); } + bool checkSheetViewProtection(); + + virtual bool runImplementation() = 0; +}; + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/operation/DeleteContentOperation.cxx b/sc/source/ui/operation/DeleteContentOperation.cxx new file mode 100644 index 000000000000..c1e50436017e --- /dev/null +++ b/sc/source/ui/operation/DeleteContentOperation.cxx @@ -0,0 +1,128 @@ +/* -*- 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 <operation/DeleteContentOperation.hxx> + +#include <docfuncutil.hxx> +#include <docfunc.hxx> +#include <editable.hxx> +#include <markdata.hxx> +#include <SheetViewOperationsTester.hxx> + +#include <memory> + +namespace sc +{ +DeleteContentOperation::DeleteContentOperation(ScDocFunc& rDocFunc, ScDocShell& rDocShell, + const ScMarkData& rMark, InsertDeleteFlags nFlags, + bool bRecord, bool bApi) + : Operation(OperationType::DeleteContent, bRecord, bApi) + , mrDocFunc(rDocFunc) + , mrDocShell(rDocShell) + , mrMark(rMark) + , mnFlags(nFlags) +{ +} + +bool DeleteContentOperation::runImplementation() +{ + ScDocShellModificator aModificator(mrDocShell); + + if (!mrMark.IsMarked() && !mrMark.IsMultiMarked()) + { + SAL_WARN("sc", "Operation DeleteContents without markings."); + return false; + } + + ScDocument& rDoc = mrDocShell.GetDocument(); + + if (mbRecord && !rDoc.IsUndoEnabled()) + mbRecord = false; + + if (!checkSheetViewProtection()) + return false; + + ScEditableTester aTester = ScEditableTester::CreateAndTestSelection(rDoc, mrMark); + if (!aTester.IsEditable()) + { + if (!mbApi) + mrDocShell.ErrorMessage(aTester.GetMessageId()); + return false; + } + + ScMarkData aMultiMark = mrMark; + aMultiMark.SetMarking(false); // for MarkToMulti + + ScDocumentUniquePtr pUndoDoc; + bool bMulti = aMultiMark.IsMultiMarked(); + aMultiMark.MarkToMulti(); + const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea(); + ScRange aExtendedRange(aMarkRange); + if (rDoc.ExtendMerge(aExtendedRange, true)) + bMulti = false; + + // no objects on protected tabs + bool bObjects + = (mnFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, mrMark); + + sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted + if (mnFlags & InsertDeleteFlags::ATTRIB) + mrDocShell.UpdatePaintExt(nExtFlags, aMarkRange); + + // order of operations: + // 1) BeginDrawUndo + // 2) Delete objects (DrawUndo will be filled) + // 3) Copy content for undo and set up undo actions + // 4) Delete content + + bool bDrawUndo = bObjects || (mnFlags & InsertDeleteFlags::NOTE); + if (mbRecord && bDrawUndo) + rDoc.BeginDrawUndo(); + + if (bObjects) + { + if (bMulti) + rDoc.DeleteObjectsInSelection(aMultiMark); + else + rDoc.DeleteObjectsInArea(aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), + aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMultiMark); + } + + // To keep track of all non-empty cells within the deleted area. + std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans; + + if (mbRecord) + { + pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, + mnFlags, bMulti); + pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange); + } + + rDoc.DeleteSelection(mnFlags, aMultiMark); + + // add undo action after drawing undo is complete (objects and note captions) + if (mbRecord) + { + sc::DocFuncUtil::addDeleteContentsUndo(mrDocShell.GetUndoManager(), &mrDocShell, aMultiMark, + aExtendedRange, std::move(pUndoDoc), mnFlags, + pDataSpans, bMulti, bDrawUndo); + } + + if (!mrDocFunc.AdjustRowHeight(aExtendedRange, true, mbApi)) + mrDocShell.PostPaint(aExtendedRange, PaintPartFlags::Grid, nExtFlags); + else if (nExtFlags & SC_PF_LINES) + ScDocFunc::PaintAbove(mrDocShell, aExtendedRange); // for lines above the range + + aModificator.SetDocumentModified(); + + return true; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/operation/Operation.cxx b/sc/source/ui/operation/Operation.cxx new file mode 100644 index 000000000000..d37fb3908c35 --- /dev/null +++ b/sc/source/ui/operation/Operation.cxx @@ -0,0 +1,23 @@ +/* -*- 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 <operation/Operation.hxx> +#include <SheetViewOperationsTester.hxx> +#include <docsh.hxx> + +namespace sc +{ +bool Operation::checkSheetViewProtection() +{ + sc::SheetViewOperationsTester aSheetViewTester(ScDocShell::GetViewData()); + return aSheetViewTester.check(meType); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
