sc/inc/dbdata.hxx                           |    6 -
 sc/inc/document.hxx                         |    4 
 sc/inc/globstr.hrc                          |    1 
 sc/inc/subtotalparam.hxx                    |   18 +++
 sc/inc/table.hxx                            |    2 
 sc/source/core/data/documen3.cxx            |   12 ++
 sc/source/core/data/subtotalparam.cxx       |   56 ++++++++++
 sc/source/core/data/table3.cxx              |   84 ++++++++++++++++
 sc/source/core/tool/dbdata.cxx              |  114 ++++++++++++++++++++++
 sc/source/filter/excel/xedbdata.cxx         |    3 
 sc/source/filter/oox/tablecolumnsbuffer.cxx |   14 ++
 sc/source/ui/docshell/dbdocfun.cxx          |  136 +++++++++++++++++++++++++-
 sc/source/ui/docshell/docsh5.cxx            |    2 
 sc/source/ui/inc/dbdocfun.hxx               |    5 
 sc/source/ui/inc/dbfunc.hxx                 |    3 
 sc/source/ui/inc/undodat.hxx                |   30 +++++
 sc/source/ui/undo/undodat.cxx               |  143 +++++++++++++++++++++++++++-
 sc/source/ui/view/dbfunc3.cxx               |  107 ++++++++++++++++++++
 sc/source/ui/view/gridwin.cxx               |   24 ++++
 sc/source/ui/view/tableshell.cxx            |   36 ++++++-
 20 files changed, 784 insertions(+), 16 deletions(-)

New commits:
commit f4cab688de326a106e0d3b3e213f7466280bf5a9
Author:     Balazs Varga <[email protected]>
AuthorDate: Mon Oct 27 11:14:28 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Mon Nov 17 19:02:42 2025 +0100

    Import and store Total rows information in ScSubtotal obj
    
    at ooxml import.
    
    Change-Id: I2c0d58a1c21800c987b9998e9cb59b39a872ee58
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193203
    Tested-by: Balazs Varga <[email protected]>
    Reviewed-by: Balazs Varga <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193679
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Andras Timar <[email protected]>

diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index 8f778730f050..66fabcccd472 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -277,9 +277,8 @@ public:
     SC_DLLPUBLIC void       SetAdvancedQuerySource(const ScRange* pSource);
 
     SC_DLLPUBLIC void       GetSubTotalParam(ScSubTotalParam& rSubTotalParam) 
const;
-    void        SetSubTotalParam(const ScSubTotalParam& rSubTotalParam);
-
-    void        CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const;
+    SC_DLLPUBLIC void       SetSubTotalParam(const ScSubTotalParam& 
rSubTotalParam);
+    SC_DLLPUBLIC void       CreateSubTotalParam(ScSubTotalParam& 
rSubTotalParam) const;
 
     void        GetImportParam(ScImportParam& rImportParam) const;
     void        SetImportParam(const ScImportParam& rImportParam);
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx 
b/sc/source/filter/oox/tablecolumnsbuffer.cxx
index 972b32af8dfb..81f46a43496d 100644
--- a/sc/source/filter/oox/tablecolumnsbuffer.cxx
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -22,6 +22,7 @@
 #include <sal/log.hxx>
 #include <oox/helper/attributelist.hxx>
 #include <oox/token/tokens.hxx>
+#include <subtotalparam.hxx>
 
 XmlColumnPrModel::XmlColumnPrModel() :
     mnMapId( 1 ),
@@ -130,6 +131,17 @@ bool TableColumns::finalizeImport( ScDBData* pDBData )
         }
         pDBData->SetTableColumnNames( std::move(aNames) );
         pDBData->SetTableColumnAttributes( std::move(aAttributesVector) );
+        // set subtotal parameters for columns
+        if (pDBData->HasTotals())
+        {
+            ScSubTotalParam aSubTotalParam;
+            pDBData->GetSubTotalParam(aSubTotalParam);
+            aSubTotalParam.bHasHeader = pDBData->HasHeader();
+            aSubTotalParam.bRemoveOnly = false;
+            aSubTotalParam.bReplace = false;
+            pDBData->CreateSubTotalParam(aSubTotalParam);
+            pDBData->SetSubTotalParam(aSubTotalParam);
+        }
         return true;
     }
     return false;
commit f7d6d7eaa0b1ec36f38ea1abd1248d6825510a47
Author:     Balazs Varga <[email protected]>
AuthorDate: Thu Oct 23 20:04:35 2025 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Mon Nov 17 19:02:29 2025 +0100

    Handle various Total row situation in table style
    
    Extending Table area with Total rows on and off.
    Moving Table total row when we extend table area.
    Show and hide/remove total row with Show Total Row buttom.
    Handle Undo / Redo of resized table area.
    Handle TotalsFunction (only subtotal so far - TODO) in Total row.
    Handle TotalsRowLabel (also OOXML import export) in Total row.
    Fix rendering issue after undo dbrange of table styles change.
    
    TODO: handle not only Subtotal functions
    TODO: Autofilter dropdown shows not only Table area values
    TODO: Redo, remove autofilter flags after undo/redo horizontal
    table extension (adding columns)
    
    cherry-pick from: bb9998dcbadd81a71f97921e4ee2ff1921eefee1
    
    Change-Id: Ifaa3df0693d716cf22e566244c449f85dcd91d8a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192920
    Reviewed-by: Balazs Varga <[email protected]>
    Tested-by: Balazs Varga <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193678
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Andras Timar <[email protected]>

diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index 972b06c85521..8f778730f050 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -81,6 +81,7 @@ enum class ScDBDataPortion
 // TODO: this can be merged with struct TableColumnModel
 struct TableColumnAttributes
 {
+    std::optional<OUString> maTotalsRowLabel = std::nullopt;
     std::optional<OUString> maTotalsFunction = std::nullopt;
 };
 
@@ -278,6 +279,8 @@ public:
     SC_DLLPUBLIC void       GetSubTotalParam(ScSubTotalParam& rSubTotalParam) 
const;
     void        SetSubTotalParam(const ScSubTotalParam& rSubTotalParam);
 
+    void        CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const;
+
     void        GetImportParam(ScImportParam& rImportParam) const;
     void        SetImportParam(const ScImportParam& rImportParam);
 
@@ -311,6 +314,8 @@ public:
     SC_DLLPUBLIC void SetTableStyleInfo(const ScTableStyleParam& rParams);
     SC_DLLPUBLIC const ScTableStyleParam* GetTableStyleInfo() const;
 
+    static ScSubTotalFunc GetSubTotalFuncFromString(std::u16string_view 
sFunction);
+
 private:
 
     void AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, 
SCCOL nCol1,
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 51ff4348edd9..90bbad32c232 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1212,6 +1212,10 @@ public:
 
     bool            DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
     void            RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
+    // Table SubTotals
+    bool            DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam );
+    void            RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
const ScSubTotalParam& rOldParam );
+
     bool            TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& 
rParam );
     bool            HasSubTotalCells( const ScRange& rRange );
 
diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc
index 49b203c82661..74f21a7efad7 100644
--- a/sc/inc/globstr.hrc
+++ b/sc/inc/globstr.hrc
@@ -67,6 +67,7 @@
 #define STR_UNDO_REMOVEALLOTLNS                 NC_("STR_UNDO_REMOVEALLOTLNS", 
"Clear Outline")
 #define STR_UNDO_AUTOOUTLINE                    NC_("STR_UNDO_AUTOOUTLINE", 
"AutoOutline")
 #define STR_UNDO_SUBTOTALS                      NC_("STR_UNDO_SUBTOTALS", 
"Subtotals")
+#define STR_UNDO_TABLETOTALS                    NC_("STR_UNDO_TABLETOTALS", 
"Tabletotals")
 #define STR_UNDO_SORT                           NC_("STR_UNDO_SORT", "Sort")
 #define STR_UNDO_QUERY                          NC_("STR_UNDO_QUERY", "Filter")
 #define STR_UNDO_DBDATA                         NC_("STR_UNDO_DBDATA", "Change 
Database Range")
diff --git a/sc/inc/subtotalparam.hxx b/sc/inc/subtotalparam.hxx
index df9f5ef390e3..63a9fcb4ebe5 100644
--- a/sc/inc/subtotalparam.hxx
+++ b/sc/inc/subtotalparam.hxx
@@ -32,16 +32,21 @@ struct SC_DLLPUBLIC ScSubTotalParam
     bool            bAscending:1      = true;   ///< sort ascending
     bool            bUserDef:1        = false;  ///< sort user defined
     bool            bIncludePattern:1 = false;  ///< sort formats
+    bool            bGroupedBy:1      = true;   ///< grouped by column
+    bool            bHasHeader:1      = true;   ///< has header row
 
     struct SubtotalGroup
     {
         bool  bActive    = false; ///< active groups
         SCCOL nField     = 0;     ///< associated field
         SCCOL nSubTotals = 0;     ///< number of SubTotals
+        SCCOL nSubLabels = 0;     ///< number of SubLabels
 
         using Pair = std::pair<SCCOL, ScSubTotalFunc>;
+        using LabelPair = std::pair<SCCOL, rtl::OUString>;
         // array of columns to be calculated, and associated functions
         std::unique_ptr<Pair[]> pSubTotals;
+        std::unique_ptr<LabelPair[]> pSubLabels;
 
         SubtotalGroup() = default;
         SubtotalGroup(const SubtotalGroup& r);
@@ -50,13 +55,22 @@ struct SC_DLLPUBLIC ScSubTotalParam
         bool operator==(const SubtotalGroup& r) const;
 
         void AllocSubTotals(SCCOL n);
+        void AllocSubLabels(SCCOL n);
         void SetSubtotals(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
+        void SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq);
 
+        // Totals
         std::span<Pair> subtotals() { return std::span(pSubTotals.get(), 
nSubTotals); }
         std::span<const Pair> subtotals() const { return 
std::span(pSubTotals.get(), nSubTotals); }
         SCCOL& col(SCCOL n) { return subtotals()[n].first; }
         SCCOL col(SCCOL n) const { return subtotals()[n].first; }
         ScSubTotalFunc func(SCCOL n) const { return subtotals()[n].second; }
+        // Labels
+        std::span<LabelPair> sublabels() { return std::span(pSubLabels.get(), 
nSubLabels); }
+        std::span<const LabelPair> sublabels() const { return 
std::span(pSubLabels.get(), nSubLabels); }
+        SCCOL& collabels(SCCOL n) { return sublabels()[n].first; }
+        SCCOL collabels(SCCOL n) const { return sublabels()[n].first; }
+        rtl::OUString label(SCCOL n) const { return sublabels()[n].second; }
     };
     SubtotalGroup aGroups[MAXSUBTOTAL];
 
@@ -69,6 +83,10 @@ struct SC_DLLPUBLIC ScSubTotalParam
                        const SCCOL* ptrSubTotals,
                        const ScSubTotalFunc* ptrFunctions,
                        sal_uInt16 nCount );
+    void SetSubLabels( sal_uInt16 nGroup,
+                       const SCCOL* ptrSubLabels,
+                       const rtl::OUString* ptrSubNames,
+                       sal_uInt16 nCount );
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index f3810ba98d18..591d2112e747 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -328,7 +328,9 @@ public:
 
     bool        TestRemoveSubTotals( const ScSubTotalParam& rParam );
     void        RemoveSubTotals( ScSubTotalParam& rParam );
+    void        RemoveSimpleSubTotals( ScSubTotalParam& rParam, const 
ScSubTotalParam& rOldParam );
     bool        DoSubTotals( ScSubTotalParam& rParam );
+    bool        DoSimpleSubTotals( ScSubTotalParam& rParam );
 
     const ScSheetEvents* GetSheetEvents() const              { return 
pSheetEvents.get(); }
     void        SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew );
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
index 3c95015874c0..265ab5e6b392 100644
--- a/sc/source/core/data/documen3.cxx
+++ b/sc/source/core/data/documen3.cxx
@@ -787,6 +787,18 @@ bool ScDocument::DoSubTotals( SCTAB nTab, ScSubTotalParam& 
rParam )
     return pTable && pTable->DoSubTotals(rParam);
 }
 
+void ScDocument::RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, 
const ScSubTotalParam& rOldParam )
+{
+    if (ScTable* pTable = FetchTable(nTab))
+        pTable->RemoveSimpleSubTotals( rParam, rOldParam );
+}
+
+bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+{
+    ScTable* pTable = FetchTable(nTab);
+    return pTable && pTable->DoSimpleSubTotals(rParam);
+}
+
 bool ScDocument::HasSubTotalCells( const ScRange& rRange )
 {
     ScCellIterator aIter(*this, rRange);
diff --git a/sc/source/core/data/subtotalparam.cxx 
b/sc/source/core/data/subtotalparam.cxx
index a056871be521..781480b9b02f 100644
--- a/sc/source/core/data/subtotalparam.cxx
+++ b/sc/source/core/data/subtotalparam.cxx
@@ -27,6 +27,13 @@ ScSubTotalParam::SubtotalGroup::SubtotalGroup(const 
SubtotalGroup& r)
         AllocSubTotals(r.nSubTotals);
         std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get());
     }
+
+    if (r.nSubLabels > 0)
+    {
+        assert(r.pSubLabels);
+        AllocSubLabels(r.nSubLabels);
+        std::copy_n(r.pSubLabels.get(), r.nSubLabels, pSubLabels.get());
+    }
 }
 
 ScSubTotalParam::SubtotalGroup& 
ScSubTotalParam::SubtotalGroup::operator=(const SubtotalGroup& r)
@@ -41,14 +48,23 @@ ScSubTotalParam::SubtotalGroup& 
ScSubTotalParam::SubtotalGroup::operator=(const
         std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get());
     }
 
+    AllocSubLabels(r.nSubLabels);
+    if (r.nSubLabels > 0)
+    {
+        assert(r.pSubLabels);
+        std::copy_n(r.pSubLabels.get(), r.nSubLabels, pSubLabels.get());
+    }
+
     return *this;
 }
 
 bool ScSubTotalParam::SubtotalGroup::operator==(const SubtotalGroup& r) const
 {
-    return bActive == r.bActive && nField == r.nField && nSubTotals == 
r.nSubTotals
+    return bActive == r.bActive && nField == r.nField && nSubTotals == 
r.nSubTotals && nSubLabels == r.nSubLabels
            && (!nSubTotals
-               || std::equal(pSubTotals.get(), pSubTotals.get() + nSubTotals, 
r.pSubTotals.get()));
+               || std::equal(pSubTotals.get(), pSubTotals.get() + nSubTotals, 
r.pSubTotals.get()))
+           && (!nSubLabels
+               || std::equal(pSubLabels.get(), pSubLabels.get() + nSubLabels, 
r.pSubLabels.get()));
 }
 
 void ScSubTotalParam::SubtotalGroup::AllocSubTotals(SCCOL n)
@@ -60,6 +76,15 @@ void ScSubTotalParam::SubtotalGroup::AllocSubTotals(SCCOL n)
     }
 }
 
+void ScSubTotalParam::SubtotalGroup::AllocSubLabels(SCCOL n)
+{
+    if (nSubLabels != n)
+    {
+        nSubLabels = std::max(n, SCCOL(0));
+        pSubLabels.reset(nSubLabels ? new std::pair<SCCOL, 
rtl::OUString>[nSubLabels] : nullptr);
+    }
+}
+
 void ScSubTotalParam::SubtotalGroup::SetSubtotals(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& seq)
 {
     AllocSubTotals(seq.getLength());
@@ -68,6 +93,11 @@ void ScSubTotalParam::SubtotalGroup::SetSubtotals(const 
css::uno::Sequence<css::
                           
ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(seq[i].Function)) };
 }
 
+void ScSubTotalParam::SubtotalGroup::SetSublabels(const 
css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/)
+{
+    // TODO UNO::API: SubTotalColumn has no LabelName member
+}
+
 void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup,
                                     const SCCOL* ptrSubTotals,
                                     const ScSubTotalFunc* ptrFunctions,
@@ -94,4 +124,26 @@ void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup,
         aGroups[nGroup].pSubTotals[i] = { ptrSubTotals[i], ptrFunctions[i] };
 }
 
+void ScSubTotalParam::SetSubLabels(sal_uInt16 nGroup,
+                       const SCCOL* ptrSubLabels,
+                       const rtl::OUString* ptrSubNames,
+                       sal_uInt16 nCount )
+{
+    OSL_ENSURE((nGroup <= MAXSUBTOTAL), "ScSubTotalParam::SetSubLabels(): 
nGroup > MAXSUBTOTAL!");
+    OSL_ENSURE(ptrSubLabels, "ScSubTotalParam::SetSubLabels(): ptrSubLabels == 
NULL!");
+    OSL_ENSURE(ptrSubNames, "ScSubTotalParam::SetSubLabels(): ptrSubNames == 
NULL!");
+    OSL_ENSURE((nCount > 0), "ScSubTotalParam::SetSubLabels(): nCount == 0!");
+
+    if (!(ptrSubLabels && ptrSubNames && (nCount > 0) && (nGroup <= 
MAXSUBTOTAL)))
+        return;
+
+    // 0 is interpreted as 1, otherwise decrementing the array index
+    if (nGroup != 0)
+        nGroup--;
+
+    aGroups[nGroup].AllocSubLabels(nCount);
+    for (sal_uInt16 i = 0; i < nCount; i++)
+        aGroups[nGroup].pSubLabels[i] = { ptrSubLabels[i], ptrSubNames[i] };
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 39decdf6e027..f00614448a9f 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -1918,6 +1918,20 @@ void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
     rParam.nRow2 -= aRows.size();
 }
 
+void ScTable::RemoveSimpleSubTotals( ScSubTotalParam& rParam, const 
ScSubTotalParam& rOldParam )
+{
+    const ScRange aOldRange(rOldParam.nCol1, rOldParam.nRow1, nTab, 
rOldParam.nCol2,
+                            rOldParam.nRow2, nTab);
+    SCCOL nStartCol = aOldRange.aStart.Col();
+    SCCOL nEndCol = ClampToAllocatedColumns(aOldRange.aEnd.Col());
+    SCROW nEndRow = aOldRange.aEnd.Row();
+
+    RemoveRowBreak(nEndRow + 1, false, true);
+    rDocument.DeleteRow(nStartCol, nTab, nEndCol, nTab, nEndRow, 1);
+
+    rParam.nRow2--;
+}
+
 //  Delete hard number formats (for result formulas)
 
 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
@@ -2288,6 +2302,76 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
     return bSpaceLeft;
 }
 
+bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam )
+{
+    RowEntry aRowEntry;
+    aRowEntry.nGroupNo = 0;
+    aRowEntry.nSubStartRow = rParam.nRow1 + 
static_cast<SCROW>(rParam.bHasHeader); // Header
+    aRowEntry.nFuncStart = rParam.nRow1 + 
static_cast<SCROW>(rParam.bHasHeader); // Header
+    aRowEntry.nDestRow = rParam.nRow2 + 1;
+    aRowEntry.nFuncEnd = rParam.nRow2;
+
+    bool bRet = false;
+    if (rDocument.InsertRow(rParam.nCol1, nTab, rParam.nCol2, nTab, 
aRowEntry.nDestRow, 1))
+    {
+        rParam.nRow2++;
+        DBShowRow(aRowEntry.nDestRow, true);
+        bRet = true;
+    }
+
+    // insert the labels
+    const auto& group = rParam.aGroups[aRowEntry.nGroupNo];
+    if (group.nSubLabels > 0)
+    {
+        for (SCCOL nResult = 0; nResult < group.nSubLabels; ++nResult)
+        {
+            SetString(group.collabels(nResult), aRowEntry.nDestRow, nTab, 
group.label(nResult));
+        }
+    }
+    else
+    {
+        SetString(group.nField, aRowEntry.nDestRow, nTab, u"Summary"_ustr);
+    }
+
+    // insert the formulas
+    if (group.nSubTotals > 0)
+    {
+        ScComplexRefData aRef;
+        aRef.InitFlags();
+        aRef.Ref1.SetAbsTab(nTab);
+        aRef.Ref2.SetAbsTab(nTab);
+
+        for (SCCOL nResult = 0; nResult < group.nSubTotals; ++nResult)
+        {
+            aRef.Ref1.SetAbsCol(group.col(nResult));
+            aRef.Ref1.SetAbsRow(aRowEntry.nFuncStart);
+            aRef.Ref2.SetAbsCol(group.col(nResult));
+            aRef.Ref2.SetAbsRow(aRowEntry.nFuncEnd);
+            // TODO: handle it with tablerefs: Table1[Column1]
+            ScTokenArray aArr(rDocument);
+            aArr.AddOpCode(ocSubTotal);
+            aArr.AddOpCode(ocOpen);
+            aArr.AddDouble(static_cast<double>(group.func(nResult)));
+            aArr.AddOpCode(ocSep);
+            aArr.AddDoubleReference(aRef);
+            aArr.AddOpCode(ocClose);
+            aArr.AddOpCode(ocStop);
+            ScFormulaCell* pCell = new ScFormulaCell(
+                rDocument, ScAddress(group.col(nResult), aRowEntry.nDestRow, 
nTab), aArr);
+            if (rParam.bIncludePattern)
+                pCell->SetNeedNumberFormat(true);
+
+            SetFormulaCell(group.col(nResult), aRowEntry.nDestRow, pCell);
+            if (group.col(nResult) != group.nField)
+            {
+                lcl_RemoveNumberFormat(this, group.col(nResult), 
aRowEntry.nDestRow);
+            }
+        }
+    }
+
+    return bRet;
+}
+
 void ScTable::TopTenQuery( ScQueryParam& rParam )
 {
     bool bSortCollatorInitialized = false;
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 30c2d64a1631..742a03628b30 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -697,6 +697,91 @@ void ScDBData::SetSubTotalParam(const ScSubTotalParam& 
rSubTotalParam)
     mpSubTotal.reset(new ScSubTotalParam(rSubTotalParam));
 }
 
+void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const
+{
+    rSubTotalParam.bDoSort = false;
+    rSubTotalParam.bGroupedBy = false;
+    rSubTotalParam.aGroups[0].bActive = true;
+    rSubTotalParam.aGroups[0].nField = rSubTotalParam.nCol1; // which column 
we add 'Summary'
+
+    const size_t nEntryCount = rSubTotalParam.nCol2 - rSubTotalParam.nCol1 + 
1; // col count
+    if (nEntryCount > 0)
+    {
+        // how many col we do subtotal
+        size_t nTotalsCount = std::count_if(
+            GetTableColumnAttributes().begin(), 
GetTableColumnAttributes().end(),
+            [](const TableColumnAttributes& attr) { return 
attr.maTotalsFunction.has_value(); });
+        if (nTotalsCount > 0)
+        {
+            std::unique_ptr<ScSubTotalFunc[]> pFunctions;
+            std::unique_ptr<SCCOL[]> pSubTotals;
+            pFunctions.reset(new ScSubTotalFunc[nTotalsCount]);
+            pSubTotals.reset(new SCCOL[nTotalsCount]);
+
+            for (size_t i = 0, nCheck = 0; i < nEntryCount; i++)
+            {
+                if (GetTableColumnAttributes().size() <= i)
+                {
+                    SAL_WARN("sc.core",
+                             "ScDBData::CreateSubTotalParam - column 
attributes size mismatch");
+                    break;
+                }
+                if (GetTableColumnAttributes()[i].maTotalsFunction.has_value())
+                {
+                    pSubTotals[nCheck] = rSubTotalParam.nCol1 + i;
+                    const OUString& sFuncName = 
GetTableColumnAttributes()[i].maTotalsFunction.value();
+                    //if (mpContainer && sFuncName == u"custom")
+                    //{
+                    //    // TODO: store custom formula tokenarrays somewhere
+                    //    ScFormulaCell* pFC = 
mpContainer->GetDocument().GetFormulaCell(
+                    //        ScAddress(rSubTotalParam.nCol1 + i, 
rSubTotalParam.nRow2, nTable));
+                    //    if (pFC)
+                    //    {
+                    //        std::unique_ptr<ScTokenArray> pTokenArray = 
pFC->GetCode()->Clone();
+                    //    }
+                    //}
+                    pFunctions[nCheck] = 
ScDBData::GetSubTotalFuncFromString(sFuncName);
+                    nCheck++;
+                }
+            }
+            rSubTotalParam.SetSubTotals(static_cast<sal_uInt16>(0), // group 
number
+                                        pSubTotals.get(), pFunctions.get(),
+                                        nTotalsCount); // number of array 
elements
+        }
+
+        // how many col we have totals Row Label
+        size_t nLabelsCount = std::count_if(
+            GetTableColumnAttributes().begin(), 
GetTableColumnAttributes().end(),
+            [](const TableColumnAttributes& attr) { return 
attr.maTotalsRowLabel.has_value(); });
+        if (nLabelsCount > 0)
+        {
+            std::unique_ptr<OUString[]> pLabels;
+            std::unique_ptr<SCCOL[]> pSubLabels;
+            pLabels.reset(new OUString[nLabelsCount]);
+            pSubLabels.reset(new SCCOL[nLabelsCount]);
+
+            for (size_t i = 0, nCheck = 0; i < nEntryCount; i++)
+            {
+                if (GetTableColumnAttributes().size() <= i)
+                {
+                    SAL_WARN("sc.core",
+                             "ScDBData::CreateSubTotalParam - column 
attributes size mismatch");
+                    break;
+                }
+                if (GetTableColumnAttributes()[i].maTotalsRowLabel.has_value())
+                {
+                    pSubLabels[nCheck] = rSubTotalParam.nCol1 + i;
+                    pLabels[nCheck] = 
GetTableColumnAttributes()[i].maTotalsRowLabel.value();
+                    nCheck++;
+                }
+            }
+            rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), // group 
number
+                                        pSubLabels.get(), pLabels.get(),
+                                        nLabelsCount); // number of array 
elements
+        }
+    }
+}
+
 void ScDBData::GetImportParam(ScImportParam& rImportParam) const
 {
     rImportParam = *mpImportParam;
@@ -1238,6 +1323,35 @@ const ScTableStyleParam* ScDBData::GetTableStyleInfo() 
const
     return mpTableStyles.get();
 }
 
+ScSubTotalFunc ScDBData::GetSubTotalFuncFromString(std::u16string_view 
sFunction)
+{
+    if (sFunction == u"sum")
+        return SUBTOTAL_FUNC_SUM;
+    if (sFunction == u"countNums")
+        return SUBTOTAL_FUNC_CNT;
+    if (sFunction == u"count")
+        return SUBTOTAL_FUNC_CNT2;
+    /*if (sFunction)
+        return SUBTOTAL_FUNC_PROD;*/
+    if (sFunction == u"average")
+        return SUBTOTAL_FUNC_AVE;
+    /*if (sFunction)
+        return SUBTOTAL_FUNC_MED;*/
+    if (sFunction == u"max")
+        return SUBTOTAL_FUNC_MAX;
+    if (sFunction == u"min")
+        return SUBTOTAL_FUNC_MIN;
+    if (sFunction == u"stdDev")
+        return SUBTOTAL_FUNC_STD;
+    /*if (sFunction)
+        return SUBTOTAL_FUNC_STDP;*/
+    if (sFunction == u"var")
+        return SUBTOTAL_FUNC_VAR;
+    /*if (sFunction)
+        return SUBTOTAL_FUNC_VARP;*/
+    return SUBTOTAL_FUNC_NONE;
+}
+
 namespace {
 
 class FindByTable
diff --git a/sc/source/filter/excel/xedbdata.cxx 
b/sc/source/filter/excel/xedbdata.cxx
index dd37b4030b38..97fa10cf62b4 100644
--- a/sc/source/filter/excel/xedbdata.cxx
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -261,6 +261,7 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
                     XML_id, OString::number(i+1),
                     XML_uniqueName, uniqueName,
                     XML_name, rColNames[i].toUtf8(),
+                    XML_totalsRowLabel, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsRowLabel : std::nullopt),
                     XML_totalsRowFunction, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsFunction : std::nullopt)
                     // OOXTODO: XML_dataCellStyle, ...,
                     // OOXTODO: XML_dataDxfId, ...,
@@ -269,7 +270,7 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
                     // OOXTODO: XML_queryTableFieldId, ...,
                     // OOXTODO: XML_totalsRowCellStyle, ...,
                     // OOXTODO: XML_totalsRowDxfId, ...,
-                    // OOXTODO: XML_totalsRowLabel, ...,
+                    // OOXTODO: XML_uniqueName, ...
             );
 
             if (i < rTableColumnModel.size() && 
rTableColumnModel[i].mxXmlColumnPr)
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx 
b/sc/source/filter/oox/tablecolumnsbuffer.cxx
index 0611d3690c2e..972b32af8dfb 100644
--- a/sc/source/filter/oox/tablecolumnsbuffer.cxx
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -55,6 +55,8 @@ void TableColumn::importTableColumn( const AttributeList& 
rAttribs )
     maName = rAttribs.getString( XML_name, OUString() );
     maModel.maUniqueName = rAttribs.getXString( XML_uniqueName, OUString() );
     mnDataDxfId = rAttribs.getInteger( XML_dataDxfId, -1 );
+    if ( rAttribs.hasAttribute(XML_totalsRowLabel ) )
+        maColumnAttributes.maTotalsRowLabel = rAttribs.getStringDefaulted( 
XML_totalsRowLabel );
     if ( rAttribs.hasAttribute( XML_totalsRowFunction ) )
         maColumnAttributes.maTotalsFunction = rAttribs.getStringDefaulted( 
XML_totalsRowFunction );
 }
diff --git a/sc/source/ui/docshell/dbdocfun.cxx 
b/sc/source/ui/docshell/dbdocfun.cxx
index 3b09e63ad4b8..08fa659d2ce6 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -113,7 +113,7 @@ bool ScDBDocFunc::AddDBRange( const OUString& rName, const 
ScRange& rRange )
     if (bUndo)
     {
         rDocShell.GetUndoManager()->AddUndoAction(
-                        std::make_unique<ScUndoDBData>( &rDocShell, 
std::move(pUndoColl),
+                        std::make_unique<ScUndoDBData>( &rDocShell, rName, 
std::move(pUndoColl),
                             std::make_unique<ScDBCollection>( *pDocColl ) ) );
     }
 
@@ -139,6 +139,9 @@ bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
         if (bUndo)
             pUndoColl.reset( new ScDBCollection( *pDocColl ) );
 
+        ScRange aRange;
+        iter->get()->GetArea(aRange);
+
         rDoc.PreprocessDBDataUpdate();
         rDBs.erase(iter);
         rDoc.CompileHybridFormula();
@@ -146,7 +149,7 @@ bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
         if (bUndo)
         {
             rDocShell.GetUndoManager()->AddUndoAction(
-                            std::make_unique<ScUndoDBData>( &rDocShell, 
std::move(pUndoColl),
+                            std::make_unique<ScUndoDBData>( &rDocShell, rName, 
std::move(pUndoColl),
                                 std::make_unique<ScDBCollection>( *pDocColl ) 
) );
         }
 
@@ -190,7 +193,7 @@ bool ScDBDocFunc::RenameDBRange( const OUString& rOld, 
const OUString& rNew )
             if (bUndo)
             {
                 rDocShell.GetUndoManager()->AddUndoAction(
-                                std::make_unique<ScUndoDBData>( &rDocShell, 
std::move(pUndoColl),
+                                std::make_unique<ScUndoDBData>( &rDocShell, 
rNew, std::move(pUndoColl),
                                     std::make_unique<ScDBCollection>( 
*pDocColl ) ) );
             }
             else
@@ -254,7 +257,6 @@ void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
         {
             rDoc.ApplyFlagsTab(aNewRange.aStart.Col(), aNewRange.aStart.Row(), 
aNewRange.aEnd.Col(), aNewRange.aStart.Row(), aNewRange.aStart.Tab(), 
ScMF::Auto);
         }
-
     }
 
     rDocShell.PostPaint(aOldRange, PaintPartFlags::Grid);
@@ -262,7 +264,7 @@ void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
     if (bUndo)
     {
         rDocShell.GetUndoManager()->AddUndoAction(
-                        std::make_unique<ScUndoDBData>( &rDocShell, 
std::move(pUndoColl),
+                        std::make_unique<ScUndoDBData>( &rDocShell, 
rNewData.GetName(), std::move(pUndoColl),
                             std::make_unique<ScDBCollection>( *pDocColl ) ) );
     }
 
@@ -302,7 +304,7 @@ void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& 
rNewColl, const std::ve
     if (bRecord)
     {
         rDocShell.GetUndoManager()->AddUndoAction(
-            std::make_unique<ScUndoDBData>(&rDocShell, std::move(pUndoColl),
+            std::make_unique<ScUndoDBData>(&rDocShell, OUString(), 
std::move(pUndoColl),
                 std::make_unique<ScDBCollection>(rNewColl)));
     }
 }
@@ -1235,6 +1237,128 @@ void ScDBDocFunc::DoSubTotals( SCTAB nTab, const 
ScSubTotalParam& rParam,
     aModificator.SetDocumentModified();
 }
 
+void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, 
const ScSubTotalParam& rParam,
+                                    bool bRecord, bool bApi )
+{
+    bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
+
+    ScDocument& rDoc = rDocShell.GetDocument();
+    if (bRecord && !rDoc.IsUndoEnabled())
+        bRecord = false;
+
+    ScDBData* pDBData = rDoc.GetDBAtArea(nTab, rParam.nCol1, rParam.nRow1, 
rParam.nCol2, rParam.nRow2);
+    if (!pDBData)
+    {
+        OSL_FAIL("SubTotals: no DBData");
+        return;
+    }
+
+    ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, 
nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow());
+    if (!aTester.IsEditable())
+    {
+        if (!bApi)
+            rDocShell.ErrorMessage(aTester.GetMessageId());
+        return;
+    }
+
+    if (rDoc.HasAttrib(rParam.nCol1, rParam.nRow1 + 1, nTab, rParam.nCol2, 
rParam.nRow2, nTab,
+                       HasAttrFlags::Merged | HasAttrFlags::Overlapped))
+    {
+        if (!bApi)
+            rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert 
into merged
+        return;
+    }
+
+    weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
+    ScDocShellModificator aModificator(rDocShell);
+
+    ScSubTotalParam aNewParam;
+    rNewData.GetSubTotalParam(aNewParam); // end of range is being changed
+    // ScSubTotalParam aNewParam(rParam);
+    ScDocumentUniquePtr pUndoDoc;
+    std::unique_ptr<ScRangeName> pUndoRange;
+    std::unique_ptr<ScDBCollection> pUndoDB;
+
+    if (bRecord) // secure old data
+    {
+        bool bOldFilter = bDo && rParam.bDoSort;
+
+        SCTAB nTabCount = rDoc.GetTableCount();
+        pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+        pUndoDoc->InitUndo(rDoc, nTab, nTab, false, bOldFilter);
+
+        //  secure data range - incl. filtering result
+        rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1 + 1, nTab, 
rParam.nCol2, rParam.nRow2, nTab,
+                            InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+        //  all formulas because of references
+        rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount - 
1,
+                            InsertDeleteFlags::FORMULA, false, *pUndoDoc);
+
+        //  ranges of DB and other
+        ScRangeName* pDocRange = rDoc.GetRangeName();
+        if (!pDocRange->empty())
+            pUndoRange.reset(new ScRangeName(*pDocRange));
+        ScDBCollection* pDocDB = rDoc.GetDBCollection();
+        if (!pDocDB->empty())
+            pUndoDB.reset(new ScDBCollection(*pDocDB));
+    }
+
+    if (rParam.bReplace)
+        rDoc.RemoveTableSubTotals(nTab, aNewParam, rParam);
+    bool bSuccess = true;
+    if (bDo)
+    {
+        // sort
+        if (rParam.bDoSort)
+        {
+            pDBData->SetArea(nTab, aNewParam.nCol1, aNewParam.nRow1, 
aNewParam.nCol2,
+                             aNewParam.nRow2);
+
+            //  set partial result field to before the sorting
+            //  (Duplicates are omitted, so can be called again)
+
+            ScSortParam aOldSort;
+            pDBData->GetSortParam(aOldSort);
+            ScSortParam aSortParam(aNewParam, aOldSort);
+            Sort(nTab, aSortParam, false, false, bApi);
+        }
+
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
+        rDoc.SetDrawPageSize(nTab);
+    }
+    ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
+                        nTab);
+    rDoc.SetDirty(aDirtyRange, true);
+
+    // Need to store with the new values
+    *pDBData = rNewData;
+    if (bRecord)
+    {
+        ScDBCollection* pDocDB = rDoc.GetDBCollection();
+        
rDocShell.GetUndoManager()->AddUndoAction(std::make_unique<ScUndoTableTotals>(
+            &rDocShell, nTab, rParam, aNewParam.nRow2, std::move(pUndoDoc), 
std::move(pUndoRange),
+            std::move(pUndoDB), std::make_unique<ScDBCollection>(*pDocDB)));
+    }
+
+    if (!bSuccess)
+    {
+        // "Cannot insert rows"
+        if (!bApi)
+            rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
+    }
+
+    // store
+    pDBData->SetSubTotalParam(aNewParam);
+    pDBData->SetArea(nTab, aNewParam.nCol1, aNewParam.nRow1, aNewParam.nCol2, 
aNewParam.nRow2);
+    rDoc.CompileDBFormula();
+
+    rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), 
nTab),
+                        PaintPartFlags::Grid | PaintPartFlags::Left | 
PaintPartFlags::Top
+                            | PaintPartFlags::Size);
+    aModificator.SetDocumentModified();
+}
+
 namespace {
 
 bool lcl_EmptyExcept( ScDocument& rDoc, const ScRange& rRange, const ScRange& 
rExcept )
diff --git a/sc/source/ui/docshell/docsh5.cxx b/sc/source/ui/docshell/docsh5.cxx
index aa1269aa70b0..6c8874577980 100644
--- a/sc/source/ui/docshell/docsh5.cxx
+++ b/sc/source/ui/docshell/docsh5.cxx
@@ -324,7 +324,7 @@ ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, 
ScGetDBMode eMode, ScGe
             {
                 m_pDocument->CompileHybridFormula();
 
-                GetUndoManager()->AddUndoAction( 
std::make_unique<ScUndoDBData>( this,
+                GetUndoManager()->AddUndoAction( 
std::make_unique<ScUndoDBData>( this, OUString(),
                         std::move(pUndoColl),
                         std::make_unique<ScDBCollection>( *pColl ) ) );
             }
diff --git a/sc/source/ui/inc/dbdocfun.hxx b/sc/source/ui/inc/dbdocfun.hxx
index acd95b1fd2a2..8741a05ee544 100644
--- a/sc/source/ui/inc/dbdocfun.hxx
+++ b/sc/source/ui/inc/dbdocfun.hxx
@@ -72,7 +72,10 @@ public:
                             const ScRange* pAdvSource, bool bRecord, bool bApi 
);
 
     void            DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
-                                    bool bRecord, bool bApi );
+                                 bool bRecord, bool bApi );
+
+    void            DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, 
const ScSubTotalParam& rParam,
+                                      bool bRecord, bool bApi );
 
     bool AddDBRange( const OUString& rName, const ScRange& rRange );
     bool DeleteDBRange( const OUString& rName );
diff --git a/sc/source/ui/inc/dbfunc.hxx b/sc/source/ui/inc/dbfunc.hxx
index 9d9f5481121d..826b949fc9c2 100644
--- a/sc/source/ui/inc/dbfunc.hxx
+++ b/sc/source/ui/inc/dbfunc.hxx
@@ -56,6 +56,9 @@ public:
     void            DoSubTotals( const ScSubTotalParam& rParam, bool bRecord = 
true,
                             const ScSortParam* pForceNewSort = nullptr );
 
+    void            DoTableSubTotals( const ScDBData& rNewData, const 
ScSubTotalParam& rParam,
+                                      bool bRecord = true );
+
     void            ToggleAutoFilter();
     void            HideAutoFilter();
 
diff --git a/sc/source/ui/inc/undodat.hxx b/sc/source/ui/inc/undodat.hxx
index 5060bf0c1059..3b7826ef43ae 100644
--- a/sc/source/ui/inc/undodat.hxx
+++ b/sc/source/ui/inc/undodat.hxx
@@ -205,6 +205,33 @@ private:
     std::unique_ptr<ScDBCollection> xUndoDB;
 };
 
+class ScUndoTableTotals: public ScDBFuncUndo
+{
+public:
+    ScUndoTableTotals(ScDocShell* pNewDocShell, SCTAB nNewTab,
+                      const ScSubTotalParam& rNewParam, SCROW nNewEndY,
+                      ScDocumentUniquePtr pNewUndoDoc,
+                      std::unique_ptr<ScRangeName> pNewUndoRange,
+                      std::unique_ptr<ScDBCollection> pNewUndoDB,
+                      std::unique_ptr<ScDBCollection> pNewRedoDB);
+
+    virtual void      Undo() override;
+    virtual void      Redo() override;
+    virtual void      Repeat(SfxRepeatTarget& rTarget) override;
+    virtual bool      CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+    virtual OUString GetComment() const override;
+
+private:
+    SCTAB           nTab;
+    ScSubTotalParam aParam;                         // The original passed 
parameter
+    SCROW           nNewEndRow;                     // Size of result
+    ScDocumentUniquePtr xUndoDoc;
+    std::unique_ptr<ScRangeName> xUndoRange;
+    std::unique_ptr<ScDBCollection> xUndoDB;
+    std::unique_ptr<ScDBCollection> xRedoDB;
+};
+
 class ScUndoQuery: public ScDBFuncUndo
 {
 public:
@@ -258,7 +285,7 @@ public:
 class ScUndoDBData: public ScSimpleUndo
 {
 public:
-                    ScUndoDBData( ScDocShell* pNewDocShell,
+                    ScUndoDBData( ScDocShell* pNewDocShell, const OUString& 
rName,
                             std::unique_ptr<ScDBCollection> pNewUndoColl,
                             std::unique_ptr<ScDBCollection> pNewRedoColl );
     virtual         ~ScUndoDBData() override;
@@ -271,6 +298,7 @@ public:
     virtual OUString GetComment() const override;
 
 private:
+    OUString aDBName;
     std::unique_ptr<ScDBCollection> pUndoColl;
     std::unique_ptr<ScDBCollection> pRedoColl;
 };
diff --git a/sc/source/ui/undo/undodat.cxx b/sc/source/ui/undo/undodat.cxx
index 203c64e8672b..588b755f17b5 100644
--- a/sc/source/ui/undo/undodat.cxx
+++ b/sc/source/ui/undo/undodat.cxx
@@ -747,6 +747,100 @@ bool ScUndoSubTotals::CanRepeat(SfxRepeatTarget& /* 
rTarget */) const
     return false;     // is not possible due to column numbers
 }
 
+ScUndoTableTotals::ScUndoTableTotals(ScDocShell* pNewDocShell, SCTAB nNewTab,
+                                     const ScSubTotalParam& rNewParam, SCROW 
nNewEndY,
+                                     ScDocumentUniquePtr pNewUndoDoc,
+                                     std::unique_ptr<ScRangeName> 
pNewUndoRange,
+                                     std::unique_ptr<ScDBCollection> 
pNewUndoDB,
+                                     std::unique_ptr<ScDBCollection> 
pNewRedoDB)
+    : ScDBFuncUndo(pNewDocShell, ScRange(rNewParam.nCol1, rNewParam.nRow1, 
nNewTab,
+                                         rNewParam.nCol2, rNewParam.nRow2, 
nNewTab))
+    , nTab(nNewTab)
+    , aParam(rNewParam)
+    , nNewEndRow(nNewEndY)
+    , xUndoDoc(std::move(pNewUndoDoc))
+    , xUndoRange(std::move(pNewUndoRange))
+    , xUndoDB(std::move(pNewUndoDB))
+    , xRedoDB(std::move(pNewRedoDB))
+{
+}
+
+OUString ScUndoTableTotals::GetComment() const
+{   // "Total Rows in Table Style"
+    return ScResId( STR_UNDO_TABLETOTALS );
+}
+
+void ScUndoTableTotals::Undo()
+{
+    ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+    if (!pViewShell)
+        return;
+
+    BeginUndo();
+
+    ScDocument& rDoc = pDocShell->GetDocument();
+
+    if (nNewEndRow > aParam.nRow2)
+    {
+        rDoc.DeleteRow( aParam.nCol1, nTab, aParam.nCol2, nTab, nNewEndRow, 
static_cast<SCSIZE>(1) );
+    }
+
+    rDoc.DeleteAreaTab( aParam.nCol1, aParam.nRow1+1, aParam.nCol2, 
aParam.nRow2, nTab, InsertDeleteFlags::ALL );
+
+    xUndoDoc->CopyToDocument(aParam.nCol1, aParam.nRow1 + 1, nTab, 
aParam.nCol2, aParam.nRow2, nTab,
+                                                            
InsertDeleteFlags::NONE, false, rDoc);
+    xUndoDoc->UndoToDocument(aParam.nCol1, aParam.nRow1 + 1, nTab, 
aParam.nCol2, aParam.nRow2, nTab,
+                                                            
InsertDeleteFlags::ALL, false, rDoc);
+
+    if (xUndoRange)
+        rDoc.SetRangeName(std::unique_ptr<ScRangeName>(new 
ScRangeName(*xUndoRange)));
+    if (xUndoDB)
+        rDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new 
ScDBCollection(*xUndoDB)), true);
+
+    SCTAB nVisTab = pViewShell->GetViewData().GetTabNumber();
+    if ( nVisTab != nTab )
+        pViewShell->SetTabNo( nTab );
+
+    
pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+    pDocShell->PostDataChanged();
+
+    EndUndo();
+}
+
+void ScUndoTableTotals::Redo()
+{
+    ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+    if (!pViewShell)
+        return;
+
+    BeginRedo();
+
+    SCTAB nVisTab = pViewShell->GetViewData().GetTabNumber();
+    if ( nVisTab != nTab )
+        pViewShell->SetTabNo( nTab );
+
+    const ScDBData* pDBData = nullptr;
+    if (xRedoDB)
+    {
+        if (aParam.bReplace && !aParam.bRemoveOnly)
+            pDBData = xRedoDB->GetDBAtArea(nTab, aParam.nCol1, aParam.nRow1, 
aParam.nCol2, nNewEndRow);
+        else
+            pDBData = xRedoDB->GetDBAtArea(nTab, aParam.nCol1, aParam.nRow1, 
aParam.nCol2, aParam.nRow2);
+    }
+    if (pDBData)
+        pViewShell->DoTableSubTotals(*pDBData, aParam, false);
+
+    EndRedo();
+}
+
+void ScUndoTableTotals::Repeat(SfxRepeatTarget& /* rTarget */) {
+}
+
+bool ScUndoTableTotals::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+    return false;
+}
+
 ScUndoQuery::ScUndoQuery( ScDocShell* pNewDocShell, SCTAB nNewTab, const 
ScQueryParam& rParam,
                             ScDocumentUniquePtr pNewUndoDoc, 
std::unique_ptr<ScDBCollection> pNewUndoDB,
                             const ScRange* pOld, bool bSize, const ScRange* 
pAdvSrc ) :
@@ -1012,10 +1106,11 @@ bool ScUndoAutoFilter::CanRepeat(SfxRepeatTarget& /* 
rTarget */) const
 }
 
 // change database sections (dialog)
-ScUndoDBData::ScUndoDBData( ScDocShell* pNewDocShell,
+ScUndoDBData::ScUndoDBData( ScDocShell* pNewDocShell, const OUString& aName,
                             std::unique_ptr<ScDBCollection> pNewUndoColl,
                             std::unique_ptr<ScDBCollection> pNewRedoColl ) :
     ScSimpleUndo( pNewDocShell ),
+    aDBName( aName ),
     pUndoColl( std::move(pNewUndoColl) ),
     pRedoColl( std::move(pNewRedoColl) )
 {
@@ -1043,6 +1138,29 @@ void ScUndoDBData::Undo()
     rDoc.CompileHybridFormula();
     rDoc.SetAutoCalc( bOldAutoCalc );
 
+    if (const ScDBData* pDBData
+        = 
pUndoColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aDBName)))
+    {
+        const ScTableStyleParam* pTableStyleInfo = 
pDBData->GetTableStyleInfo();
+        if (pTableStyleInfo)
+        {
+            ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+            if (pViewShell)
+            {
+                ScRange aRange;
+                pDBData->GetArea(aRange);
+                SCTAB nTab = aRange.aStart.Tab();
+                SCTAB nVisTab = pViewShell->GetViewData().GetTabNumber();
+                if (nVisTab != nTab)
+                    pViewShell->SetTabNo(nTab);
+
+                pDocShell->PostPaint(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), 
nTab,
+                                    PaintPartFlags::Grid | PaintPartFlags::Left
+                                        | PaintPartFlags::Top | 
PaintPartFlags::Size);
+            }
+        }
+    }
+
     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
 
     EndUndo();
@@ -1061,6 +1179,29 @@ void ScUndoDBData::Redo()
     rDoc.CompileHybridFormula();
     rDoc.SetAutoCalc( bOldAutoCalc );
 
+    if (const ScDBData* pDBData
+        = 
pRedoColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aDBName)))
+    {
+        const ScTableStyleParam* pTableStyleInfo = 
pDBData->GetTableStyleInfo();
+        if (pTableStyleInfo)
+        {
+            ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+            if (pViewShell)
+            {
+                ScRange aRange;
+                pDBData->GetArea(aRange);
+                SCTAB nTab = aRange.aStart.Tab();
+                SCTAB nVisTab = pViewShell->GetViewData().GetTabNumber();
+                if (nVisTab != nTab)
+                    pViewShell->SetTabNo(nTab);
+
+                pDocShell->PostPaint(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), 
nTab,
+                                    PaintPartFlags::Grid | PaintPartFlags::Left
+                                        | PaintPartFlags::Top | 
PaintPartFlags::Size);
+            }
+        }
+    }
+
     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
 
     EndRedo();
diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx
index 4db20cabb81d..8428509c8aa1 100644
--- a/sc/source/ui/view/dbfunc3.cxx
+++ b/sc/source/ui/view/dbfunc3.cxx
@@ -600,6 +600,113 @@ void ScDBFunc::DoSubTotals( const ScSubTotalParam& 
rParam, bool bRecord,
     SelectionChanged();
 }
 
+void ScDBFunc::DoTableSubTotals( const ScDBData& rNewData, const 
ScSubTotalParam& rParam, bool bRecord )
+{
+    bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
+
+    ScDocShell* pDocSh = GetViewData().GetDocShell();
+    ScDocument& rDoc = pDocSh->GetDocument();
+    SCTAB nTab = GetViewData().GetTabNumber();
+    if (bRecord && !rDoc.IsUndoEnabled())
+        bRecord = false;
+
+    ScDBData* pDBData
+        = rDoc.GetDBAtArea(nTab, rParam.nCol1, rParam.nRow1, rParam.nCol2, 
rParam.nRow2);
+    if (!pDBData)
+    {
+        OSL_FAIL("SubTotals: no DBData");
+        return;
+    }
+
+    ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, 
nTab, 0, rParam.nRow1 + 1, rDoc.MaxCol(), rDoc.MaxRow());
+    if (!aTester.IsEditable())
+    {
+        ErrorMessage(aTester.GetMessageId());
+        return;
+    }
+
+    if (rDoc.HasAttrib(rParam.nCol1, rParam.nRow1 + 1, nTab, rParam.nCol2, 
rParam.nRow2, nTab,
+                       HasAttrFlags::Merged | HasAttrFlags::Overlapped))
+    {
+        ErrorMessage(STR_MSSG_INSERTCELLS_0); // do not insert into merged
+        return;
+    }
+
+    weld::WaitObject aWait(GetViewData().GetDialogParent());
+    ScDocShellModificator aModificator(*pDocSh);
+
+    // ScSubTotalParam aNewParam(rParam);
+    ScSubTotalParam aNewParam;
+    rNewData.GetSubTotalParam(aNewParam); // change end of range
+    ScDocumentUniquePtr pUndoDoc;
+    std::unique_ptr<ScRangeName> pUndoRange;
+    std::unique_ptr<ScDBCollection> pUndoDB;
+
+    if (bRecord) // record old data
+    {
+        bool bOldFilter = bDo && rParam.bDoSort;
+        SCTAB nTabCount = rDoc.GetTableCount();
+        pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+        pUndoDoc->InitUndo(rDoc, nTab, nTab, false, bOldFilter);
+
+        // record data range - including filter results
+        rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1 + 1, nTab, 
rParam.nCol2, rParam.nRow2, nTab,
+                            InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+        // all formulas for reference
+        rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount - 
1,
+                            InsertDeleteFlags::FORMULA, false, *pUndoDoc);
+
+        // database and other ranges
+        ScRangeName* pDocRange = rDoc.GetRangeName();
+        if (!pDocRange->empty())
+            pUndoRange.reset(new ScRangeName(*pDocRange));
+        ScDBCollection* pDocDB = rDoc.GetDBCollection();
+        if (!pDocDB->empty())
+            pUndoDB.reset(new ScDBCollection(*pDocDB));
+    }
+
+    if (rParam.bReplace)
+        rDoc.RemoveTableSubTotals(nTab, aNewParam, rParam);
+    bool bSuccess = true;
+    if (bDo)
+    {
+        bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam);
+    }
+    ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, 
aNewParam.nCol2, aNewParam.nRow2,
+                        nTab);
+    rDoc.SetDirty(aDirtyRange, true);
+
+    // Need to store with the new values
+    *pDBData = rNewData;
+    if (bRecord)
+    {
+        ScDBCollection* pDocDB = rDoc.GetDBCollection();
+        
pDocSh->GetUndoManager()->AddUndoAction(std::make_unique<ScUndoTableTotals>(
+            pDocSh, nTab, rParam, aNewParam.nRow2, std::move(pUndoDoc), 
std::move(pUndoRange),
+            std::move(pUndoDB), std::make_unique<ScDBCollection>(*pDocDB)));
+    }
+
+    if (!bSuccess)
+    {
+        // "Can not insert any rows"
+        ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
+    }
+
+    // store
+    pDBData->SetSubTotalParam(aNewParam);
+    pDBData->SetArea(nTab, aNewParam.nCol1, aNewParam.nRow1, aNewParam.nCol2, 
aNewParam.nRow2);
+    rDoc.CompileDBFormula();
+
+    pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+                     PaintPartFlags::Grid | PaintPartFlags::Left | 
PaintPartFlags::Top
+                         | PaintPartFlags::Size);
+
+    aModificator.SetDocumentModified();
+
+    SelectionChanged();
+}
+
 // consolidate
 
 void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 553cd12236f4..1520d4ec8385 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -2492,7 +2492,29 @@ void ScGridWindow::MouseButtonUp( const MouseEvent& 
rMEvt )
             ScRange aNewDBRange(nStartCol, nStartRow, nTab, nFillCol, 
nFillRow, nTab);
             aNewDBRange.PutInOrder();
             aNewDBData.SetArea(nTab, aNewDBRange.aStart.Col(), 
aNewDBRange.aStart.Row(), aNewDBRange.aEnd.Col(), aNewDBRange.aEnd.Row());
-            aFunc.ModifyDBData(aNewDBData);
+            // Do subtotal if needed
+            ScRange aOldRange;
+            pDBData->GetArea(aOldRange);
+            if (aNewDBData.HasTotals() && (aOldRange.aEnd.Row() != 
aNewDBRange.aEnd.Row() || aOldRange.aStart.Row() != aNewDBRange.aStart.Row()))
+            {
+                // Subtotals
+                ScSubTotalParam aSubTotalParam;
+                pDBData->GetSubTotalParam(aSubTotalParam);
+                aSubTotalParam.bHasHeader = aNewDBData.HasHeader();
+                if (!aNewDBData.HasSubTotalParam())
+                {
+                    pDBData->CreateSubTotalParam(aSubTotalParam);
+                    aNewDBData.SetSubTotalParam(aSubTotalParam);
+                }
+                // add/replace total row
+                aSubTotalParam.bRemoveOnly = false;
+                aSubTotalParam.bReplace = true;
+                aFunc.DoTableSubTotals(aNewDBData.GetTab(), aNewDBData, 
aSubTotalParam, true, true);
+            }
+            else
+            {
+                aFunc.ModifyDBData(aNewDBData);
+            }
         }
     }
     else if (mrViewData.IsAnyFillMode())
diff --git a/sc/source/ui/view/tableshell.cxx b/sc/source/ui/view/tableshell.cxx
index c7ff2c881666..9b3769625b8d 100644
--- a/sc/source/ui/view/tableshell.cxx
+++ b/sc/source/ui/view/tableshell.cxx
@@ -23,6 +23,7 @@
 #include <document.hxx>
 #include <dbdata.hxx>
 #include <dbdocfun.hxx>
+#include <subtotalparam.hxx>
 
 #define ShellClass_ScTableShell
 #include <scslots.hxx>
@@ -91,7 +92,40 @@ void ScTableShell::ExecuteDatabaseSettings(SfxRequest& rReq)
                     aNewDBData.SetTableStyleInfo(aNewParam);
 
                     ScDBDocFunc aFunc(*rViewData.GetDocShell());
-                    aFunc.ModifyDBData(aNewDBData);
+                    // Set new area if size changed
+                    if (pDBData->HasTotals() != aNewDBData.HasTotals())
+                    {
+                        // Subtotals
+                        ScSubTotalParam aSubTotalParam;
+                        aNewDBData.GetSubTotalParam(aSubTotalParam);
+                        aSubTotalParam.bHasHeader = aNewDBData.HasHeader();
+                        if (!aNewDBData.HasSubTotalParam())
+                        {
+                            pDBData->CreateSubTotalParam(aSubTotalParam);
+                            aNewDBData.SetSubTotalParam(aSubTotalParam);
+                        }
+
+                        if (!aNewDBData.HasTotals())
+                        {
+                            // remove total row
+                            aSubTotalParam.bRemoveOnly = true;
+                            aSubTotalParam.bReplace = true;
+                            aFunc.DoTableSubTotals(aNewDBData.GetTab(), 
aNewDBData, aSubTotalParam,
+                                                   true, true);
+                        }
+                        else
+                        {
+                            // add/replace total row
+                            aSubTotalParam.bRemoveOnly = false;
+                            aSubTotalParam.bReplace = false;
+                            aFunc.DoTableSubTotals(aNewDBData.GetTab(), 
aNewDBData, aSubTotalParam,
+                                                   true, true);
+                        }
+                    }
+                    else
+                    {
+                        aFunc.ModifyDBData(aNewDBData);
+                    }
                 }
             }
             break;

Reply via email to