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: */

Reply via email to