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;
