sc/inc/dbdata.hxx | 9 - sc/inc/document.hxx | 2 sc/inc/globstr.hrc | 1 sc/inc/subtotalparam.hxx | 23 +++ sc/inc/table.hxx | 2 sc/inc/tokenarray.hxx | 1 sc/source/core/data/documen3.cxx | 16 +- sc/source/core/data/subtotalparam.cxx | 69 +++++++++- sc/source/core/data/table3.cxx | 51 +++---- sc/source/core/tool/dbdata.cxx | 177 ++++++++++++++------------- sc/source/core/tool/token.cxx | 5 sc/source/filter/excel/xedbdata.cxx | 7 - sc/source/filter/inc/tablecolumnsbuffer.hxx | 15 +- sc/source/filter/inc/tablecolumnscontext.hxx | 1 sc/source/filter/oox/tablecolumnsbuffer.cxx | 52 ++++++- sc/source/filter/oox/tablecolumnscontext.cxx | 22 ++- sc/source/ui/docshell/dbdocfun.cxx | 24 --- sc/source/ui/view/gridwin.cxx | 8 - sc/source/ui/view/tableshell.cxx | 8 - 19 files changed, 308 insertions(+), 185 deletions(-)
New commits: commit bca5864a3245021601991e9be8ff6401ffd812b5 Author: Balazs Varga <[email protected]> AuthorDate: Wed Oct 29 14:54:20 2025 +0100 Commit: Balazs Varga <[email protected]> CommitDate: Fri Oct 31 16:28:26 2025 +0100 Handle custom functions in Total row of table styles Also rework and better (more dynamic) handling of Total row. Also fix the additional ooxml import problems around Total row. Change-Id: I894c309362cfaf525ebbf47e7f49bc67b8da8495 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193205 Reviewed-by: Balazs Varga <[email protected]> Tested-by: Balazs Varga <[email protected]> diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx index 656b4d1d61d5..7d44abcb0b41 100644 --- a/sc/inc/dbdata.hxx +++ b/sc/inc/dbdata.hxx @@ -82,6 +82,7 @@ struct TableColumnAttributes { std::optional<OUString> maTotalsRowLabel = std::nullopt; std::optional<OUString> maTotalsFunction = std::nullopt; + std::optional<OUString> maCustomFunction = std::nullopt; }; /** Container base class to provide selected access for ScDBData. */ @@ -150,7 +151,6 @@ private: bool bModified; ///< is set/cleared for/by(?) UpdateReference ::std::vector< OUString > maTableColumnNames; ///< names of table columns - ::std::vector< TableColumnAttributes > maTableColumnAttributes; ///< attributes of table columns bool mbTableColumnNamesDirty; SCSIZE nFilteredRowCount; @@ -205,8 +205,6 @@ public: void EndTableColumnNamesListener(); SC_DLLPUBLIC void SetTableColumnNames( ::std::vector< OUString >&& rNames ); SC_DLLPUBLIC const ::std::vector< OUString >& GetTableColumnNames() const { return maTableColumnNames; } - SC_DLLPUBLIC void SetTableColumnAttributes( ::std::vector< TableColumnAttributes >&& rAttributes ); - SC_DLLPUBLIC const ::std::vector< TableColumnAttributes >& GetTableColumnAttributes() const { return maTableColumnAttributes; } bool AreTableColumnNamesDirty() const { return mbTableColumnNamesDirty; } /** Refresh/update the column names with the header row's cell contents. */ @@ -246,6 +244,9 @@ public: SC_DLLPUBLIC void GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const; SC_DLLPUBLIC void SetSubTotalParam(const ScSubTotalParam& rSubTotalParam); + SC_DLLPUBLIC void ImportSubTotalParam(ScSubTotalParam& rSubTotalParam, + const std::vector<TableColumnAttributes>& rAttributesVector, + formula::FormulaGrammar::Grammar eGrammar) const; SC_DLLPUBLIC void CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const; void GetImportParam(ScImportParam& rImportParam) const; @@ -285,7 +286,7 @@ public: private: - void AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1, + void AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1, SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 ); void InvalidateTableColumnNames( bool bSwapToEmptyNames ); }; diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 322a4a740e06..0b5e24b4879c 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1187,7 +1187,7 @@ public: bool DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); void RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); // Table SubTotals - bool DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, sal_uInt16 nIndex ); + bool DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); void RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, const ScSubTotalParam& rOldParam ); bool TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& rParam ); @@ -2241,7 +2241,7 @@ public: void Reorder( const sc::ReorderParam& rParam ); void PrepareQuery( SCTAB nTab, ScQueryParam& rQueryParam ); - SCSIZE Query( SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub ); + SCSIZE Query( SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub, bool bKeepTotals ); SC_DLLPUBLIC bool CreateQueryParam( const ScRange& rRange, ScQueryParam& rQueryParam ); OUString GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab); diff --git a/sc/inc/subtotalparam.hxx b/sc/inc/subtotalparam.hxx index 63a9fcb4ebe5..3909db212bef 100644 --- a/sc/inc/subtotalparam.hxx +++ b/sc/inc/subtotalparam.hxx @@ -10,6 +10,7 @@ #pragma once #include "global.hxx" +#include <tokenarray.hxx> #include <memory> #include <span> @@ -40,13 +41,16 @@ struct SC_DLLPUBLIC ScSubTotalParam bool bActive = false; ///< active groups SCCOL nField = 0; ///< associated field SCCOL nSubTotals = 0; ///< number of SubTotals + SCCOL nCustFuncs = 0; ///< number of Custom Functions SCCOL nSubLabels = 0; ///< number of SubLabels using Pair = std::pair<SCCOL, ScSubTotalFunc>; + using FuncPair = std::pair<SCCOL, std::unique_ptr<ScTokenArray>>; 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; + std::unique_ptr<FuncPair[]> pCustFuncs; // custom functions + std::unique_ptr<LabelPair[]> pSubLabels; // Total labels SubtotalGroup() = default; SubtotalGroup(const SubtotalGroup& r); @@ -55,8 +59,10 @@ struct SC_DLLPUBLIC ScSubTotalParam bool operator==(const SubtotalGroup& r) const; void AllocSubTotals(SCCOL n); + void AllocCustFuncs(SCCOL n); void AllocSubLabels(SCCOL n); void SetSubtotals(const css::uno::Sequence<css::sheet::SubTotalColumn>& seq); + void SetCustFuncs(const css::uno::Sequence<css::sheet::SubTotalColumn>& seq); void SetSublabels(const css::uno::Sequence<css::sheet::SubTotalColumn>& seq); // Totals @@ -65,6 +71,12 @@ struct SC_DLLPUBLIC ScSubTotalParam 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; } + // Total Functions + std::span<FuncPair> custfuncs() { return std::span(pCustFuncs.get(), nCustFuncs); } + std::span<const FuncPair> custfuncs() const { return std::span(pCustFuncs.get(), nCustFuncs); } + SCCOL& colcust(SCCOL n) { return custfuncs()[n].first; } + SCCOL colcust(SCCOL n) const { return custfuncs()[n].first; } + ScTokenArray* custToken(SCCOL n) const { return custfuncs()[n].second.get(); } // Labels std::span<LabelPair> sublabels() { return std::span(pSubLabels.get(), nSubLabels); } std::span<const LabelPair> sublabels() const { return std::span(pSubLabels.get(), nSubLabels); } @@ -77,15 +89,20 @@ struct SC_DLLPUBLIC ScSubTotalParam ScSubTotalParam() = default; ScSubTotalParam(const ScSubTotalParam&) = default; + ScSubTotalParam(ScSubTotalParam&&) noexcept = default; + ScSubTotalParam& operator=(ScSubTotalParam&&) noexcept = default; + ScSubTotalParam& operator=(const ScSubTotalParam&) = default; inline bool operator==(const ScSubTotalParam&) const = default; void SetSubTotals( sal_uInt16 nGroup, const SCCOL* ptrSubTotals, const ScSubTotalFunc* ptrFunctions, sal_uInt16 nCount ); + void SetCustFuncs( sal_uInt16 nGroup, + std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>>& rColFuncs, + sal_uInt16 nCount ); void SetSubLabels( sal_uInt16 nGroup, - const SCCOL* ptrSubLabels, - const rtl::OUString* ptrSubNames, + std::vector<std::pair<SCCOL, rtl::OUString>>& rColLabels, sal_uInt16 nCount ); }; diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 3a433eb66b75..6a178c2e8258 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -322,7 +322,7 @@ public: void RemoveSubTotals( ScSubTotalParam& rParam ); void RemoveSimpleSubTotals( ScSubTotalParam& rParam, const ScSubTotalParam& rOldParam ); bool DoSubTotals( ScSubTotalParam& rParam ); - bool DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex ); + bool DoSimpleSubTotals( ScSubTotalParam& rParam ); const ScSheetEvents* GetSheetEvents() const { return pSheetEvents.get(); } void SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew ); @@ -1027,7 +1027,7 @@ public: // For ValidQuery() see ScQueryEvalutor class. void TopTenQuery( ScQueryParam& ); void PrepareQuery( ScQueryParam& rQueryParam ); - SCSIZE Query(const ScQueryParam& rQueryParam, bool bKeepSub); + SCSIZE Query(const ScQueryParam& rQueryParam, bool bKeepSub, bool bKeepTotals); bool CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam); void GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering = false); diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx index 519ad1cc720a..f35f63ff5077 100644 --- a/sc/source/core/data/documen3.cxx +++ b/sc/source/core/data/documen3.cxx @@ -793,10 +793,10 @@ void ScDocument::RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, cons pTable->RemoveSimpleSubTotals( rParam, rOldParam ); } -bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, sal_uInt16 nIndex ) +bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) { ScTable* pTable = FetchTable(nTab); - return pTable && pTable->DoSimpleSubTotals(rParam, nIndex); + return pTable && pTable->DoSimpleSubTotals(rParam); } bool ScDocument::HasSubTotalCells( const ScRange& rRange ) @@ -1483,10 +1483,10 @@ void ScDocument::PrepareQuery( SCTAB nTab, ScQueryParam& rQueryParam ) } } -SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub) +SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub, bool bKeepTotals) { if (ScTable* pTable = FetchTable(nTab)) - return pTable->Query(rQueryParam, bKeepSub); + return pTable->Query(rQueryParam, bKeepSub, bKeepTotals); OSL_FAIL("missing tab"); return 0; diff --git a/sc/source/core/data/subtotalparam.cxx b/sc/source/core/data/subtotalparam.cxx index 781480b9b02f..5246b1925de1 100644 --- a/sc/source/core/data/subtotalparam.cxx +++ b/sc/source/core/data/subtotalparam.cxx @@ -28,6 +28,19 @@ ScSubTotalParam::SubtotalGroup::SubtotalGroup(const SubtotalGroup& r) std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get()); } + if (r.nCustFuncs > 0) + { + assert(r.pCustFuncs); + AllocCustFuncs(r.nCustFuncs); + for (SCCOL i = 0; i < r.nCustFuncs; ++i) + { + if (r.pCustFuncs[i].second) + pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, r.pCustFuncs[i].second->Clone()); + else + pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, nullptr); + } + } + if (r.nSubLabels > 0) { assert(r.pSubLabels); @@ -48,6 +61,19 @@ ScSubTotalParam::SubtotalGroup& ScSubTotalParam::SubtotalGroup::operator=(const std::copy_n(r.pSubTotals.get(), r.nSubTotals, pSubTotals.get()); } + AllocCustFuncs(r.nCustFuncs); + if (r.nCustFuncs > 0) + { + assert(r.pCustFuncs); + for (SCCOL i = 0; i < r.nCustFuncs; ++i) + { + if (r.pCustFuncs[i].second) + pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, r.pCustFuncs[i].second->Clone()); + else + pCustFuncs[i] = std::make_pair(r.pCustFuncs[i].first, nullptr); + } + } + AllocSubLabels(r.nSubLabels); if (r.nSubLabels > 0) { @@ -63,6 +89,8 @@ bool ScSubTotalParam::SubtotalGroup::operator==(const SubtotalGroup& r) const return bActive == r.bActive && nField == r.nField && nSubTotals == r.nSubTotals && nSubLabels == r.nSubLabels && (!nSubTotals || std::equal(pSubTotals.get(), pSubTotals.get() + nSubTotals, r.pSubTotals.get())) + && (!nCustFuncs + || std::equal(pCustFuncs.get(), pCustFuncs.get() + nCustFuncs, r.pCustFuncs.get())) && (!nSubLabels || std::equal(pSubLabels.get(), pSubLabels.get() + nSubLabels, r.pSubLabels.get())); } @@ -76,6 +104,15 @@ void ScSubTotalParam::SubtotalGroup::AllocSubTotals(SCCOL n) } } +void ScSubTotalParam::SubtotalGroup::AllocCustFuncs(SCCOL n) +{ + if (nCustFuncs != n) + { + nCustFuncs = std::max(n, SCCOL(0)); + pCustFuncs.reset(nCustFuncs ? new std::pair<SCCOL, std::unique_ptr<ScTokenArray>>[nCustFuncs] : nullptr); + } +} + void ScSubTotalParam::SubtotalGroup::AllocSubLabels(SCCOL n) { if (nSubLabels != n) @@ -93,6 +130,11 @@ void ScSubTotalParam::SubtotalGroup::SetSubtotals(const css::uno::Sequence<css:: ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(seq[i].Function)) }; } +void ScSubTotalParam::SubtotalGroup::SetCustFuncs(const css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/) +{ + // TODO UNO::API: SubTotalColumn has no token array member +} + void ScSubTotalParam::SubtotalGroup::SetSublabels(const css::uno::Sequence<css::sheet::SubTotalColumn>& /*seq*/) { // TODO UNO::API: SubTotalColumn has no LabelName member @@ -124,26 +166,33 @@ void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup, aGroups[nGroup].pSubTotals[i] = { ptrSubTotals[i], ptrFunctions[i] }; } +void ScSubTotalParam::SetCustFuncs(sal_uInt16 nGroup, + std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>>& rColFuncs, + sal_uInt16 nCount ) +{ + OSL_ENSURE((nGroup <= MAXSUBTOTAL), "ScSubTotalParam::SetCustFuncs(): nGroup > MAXSUBTOTAL!"); + OSL_ENSURE((nCount > 0), "ScSubTotalParam::SetCustFuncs(): nCount == 0!"); + if (!(nCount > 0 && nGroup <= MAXSUBTOTAL)) + return; + + aGroups[nGroup].AllocCustFuncs(nCount); + for (sal_uInt16 i = 0; i < nCount; i++) + aGroups[nGroup].pCustFuncs[i] = std::make_pair(rColFuncs[i].first, std::move(rColFuncs[i].second)); +} + void ScSubTotalParam::SetSubLabels(sal_uInt16 nGroup, - const SCCOL* ptrSubLabels, - const rtl::OUString* ptrSubNames, + std::vector<std::pair<SCCOL, rtl::OUString>>& rColLabels, 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))) + if (!(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] }; + aGroups[nGroup].pSubLabels[i] = std::make_pair(rColLabels[i].first, std::move(rColLabels[i].second)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index ea9ddcffdc02..be8931c3ab1e 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -2302,7 +2302,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) return bSpaceLeft; } -bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex ) +bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam ) { RowEntry aRowEntry; aRowEntry.nGroupNo = 0; @@ -2330,38 +2330,27 @@ bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex ) } else { - SetString(group.nField, aRowEntry.nDestRow, nTab, ScResId(STR_TABLE_TOTAL)); + SetString(rParam.nCol1, aRowEntry.nDestRow, nTab, ScResId(STR_TABLE_TOTAL)); } // insert the formulas - if (group.nSubTotals > 0) + if (group.nCustFuncs > 0) { - for (SCCOL nResult = 0; nResult < group.nSubTotals; ++nResult) + for (SCCOL nResult = 0; nResult < group.nCustFuncs; ++nResult) { - ScTokenArray aArr(rDocument); - aArr.AddOpCode(ocSubTotal); - aArr.AddOpCode(ocOpen); - aArr.AddDouble(static_cast<double>(group.func(nResult))); - aArr.AddOpCode(ocSep); - // Table refs structure - aArr.AddTableRef(nIndex); - aArr.AddOpCode(ocTableRefOpen); - ScSingleRefData aSingleRef; - aSingleRef.InitAddress(group.col(nResult), aRowEntry.nFuncStart - 1, nTab); - aArr.AddSingleReference(aSingleRef); - aArr.AddOpCode(ocTableRefClose); - // Table refs structure end - 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) + if (ScTokenArray* pArray = group.custToken(nResult)) { - lcl_RemoveNumberFormat(this, group.col(nResult), aRowEntry.nDestRow); + ScFormulaCell* pCell = new ScFormulaCell( + rDocument, ScAddress(group.colcust(nResult), aRowEntry.nDestRow, nTab), + *pArray); + if (rParam.bIncludePattern) + pCell->SetNeedNumberFormat(true); + + SetFormulaCell(group.colcust(nResult), aRowEntry.nDestRow, pCell); + if (group.colcust(nResult) != group.nField) + { + lcl_RemoveNumberFormat(this, group.colcust(nResult), aRowEntry.nDestRow); + } } } } @@ -2616,7 +2605,7 @@ void ScTable::PrepareQuery( ScQueryParam& rQueryParam ) lcl_PrepareQuery(&rDocument, this, rQueryParam, false); } -SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub) +SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub, bool bKeepTotals) { ScQueryParam aParam( rParamOrg ); typedef std::unordered_set<OUString> StrSetType; @@ -2649,6 +2638,11 @@ SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub) { bool bResult; // Filter result bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos); + // Keep Totals row (last) even if we have no any cell formula! + if (!bValid && bKeepTotals && j == nRealRow2) + { + bValid = true; + } if (!bValid && bKeepSub) // Keep subtotals { for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++) diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx index d08320579e81..a1a0f30ccc1e 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -319,7 +319,6 @@ ScDBData::ScDBData( const ScDBData& rData ) : bAutoFilter (rData.bAutoFilter), bModified (rData.bModified), maTableColumnNames (rData.maTableColumnNames), - maTableColumnAttributes(rData.maTableColumnAttributes), mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty), nFilteredRowCount (rData.nFilteredRowCount) { @@ -356,7 +355,6 @@ ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) : bAutoFilter (rData.bAutoFilter), bModified (rData.bModified), maTableColumnNames (rData.maTableColumnNames), - maTableColumnAttributes(rData.maTableColumnAttributes), mbTableColumnNamesDirty (rData.mbTableColumnNamesDirty), nFilteredRowCount (rData.nFilteredRowCount) { @@ -413,7 +411,6 @@ ScDBData& ScDBData::operator= (const ScDBData& rData) else { maTableColumnNames = rData.maTableColumnNames; - maTableColumnAttributes = rData.maTableColumnAttributes; mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty; } @@ -693,7 +690,9 @@ void ScDBData::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam) mpSubTotal.reset(new ScSubTotalParam(rSubTotalParam)); } -void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const +void ScDBData::ImportSubTotalParam(ScSubTotalParam& rSubTotalParam, + const std::vector<TableColumnAttributes>& rAttributesVector, + formula::FormulaGrammar::Grammar eGrammar) const { rSubTotalParam.bDoSort = false; rSubTotalParam.bGroupedBy = false; @@ -702,80 +701,112 @@ void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const 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) + SCCOL nCol = rSubTotalParam.nCol1; + std::vector<std::pair<SCCOL, OUString>> vColLabels; + std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>> vColFuncs; + for (const auto& rxTableColumn : rAttributesVector) { - 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 (rxTableColumn.maTotalsRowLabel.has_value()) + { + OUString aStr = rxTableColumn.maTotalsRowLabel.value(); + if (!aStr.isEmpty()) + vColLabels.push_back(std::make_pair(nCol, std::move(aStr))); + } + else if (mpContainer && rxTableColumn.maTotalsFunction.has_value()) { - OSL_ENSURE(nCheck <= nTotalsCount, "Range error"); - if (GetTableColumnAttributes().size() <= i) + ScDocument& rDoc = mpContainer->GetDocument(); + const OUString& sFuncName = rxTableColumn.maTotalsFunction.value(); + if (sFuncName == u"custom") { - SAL_WARN("sc.core", - "ScDBData::CreateSubTotalParam - column attributes size mismatch"); - break; + if (rxTableColumn.maCustomFunction.has_value()) + { + SCROW nLastRow = rSubTotalParam.nRow2; + if (!HasTotals()) + nLastRow++; + + ScAddress aPos(nCol, nLastRow, nTable); + ScCompiler aComp(rDoc, aPos, eGrammar, true, + false); + std::unique_ptr<ScTokenArray> pArr + = aComp.CompileString(rxTableColumn.maCustomFunction.value()); + if (pArr) + { + vColFuncs.push_back(std::make_pair(nCol, std::move(pArr))); + } + } } - if (GetTableColumnAttributes()[i].maTotalsFunction.has_value()) + else { - 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++; + ScSubTotalFunc eSubType = GetSubTotalFuncFromString(sFuncName); + if (eSubType != SUBTOTAL_FUNC_NONE) + { + std::unique_ptr<ScTokenArray> pArr(new ScTokenArray(rDoc)); + pArr->AddOpCode(ocSubTotal); + pArr->AddOpCode(ocOpen); + pArr->AddDouble(static_cast<double>(eSubType)); + pArr->AddOpCode(ocSep); + // Table refs structure + pArr->AddTableRef(GetIndex()); + pArr->AddOpCode(ocTableRefOpen); + ScSingleRefData aSingleRef; + aSingleRef.InitAddress(nCol, rSubTotalParam.nRow1, nTable); + pArr->AddSingleReference(aSingleRef); + pArr->AddOpCode(ocTableRefClose); + // Table refs structure end + pArr->AddOpCode(ocClose); + pArr->AddOpCode(ocStop); + // Store + vColFuncs.push_back(std::make_pair(nCol, std::move(pArr))); + } } } - rSubTotalParam.SetSubTotals(static_cast<sal_uInt16>(0), // group number - pSubTotals.get(), pFunctions.get(), - nTotalsCount); // number of array elements + nCol++; } + rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), vColLabels, vColLabels.size()); + rSubTotalParam.SetCustFuncs(static_cast<sal_uInt16>(0), vColFuncs, vColFuncs.size()); + } +} - // 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]); +void ScDBData::CreateSubTotalParam(ScSubTotalParam& rSubTotalParam) const +{ + rSubTotalParam.bDoSort = false; + rSubTotalParam.bGroupedBy = false; + rSubTotalParam.aGroups[0].nField = rSubTotalParam.nCol1; // which column we add 'Summary' - for (size_t i = 0, nCheck = 0; i < nEntryCount; i++) + const size_t nEntryCount = rSubTotalParam.nCol2 - rSubTotalParam.nCol1 + 1; // col count + if (nEntryCount > 0 && mpContainer) + { + ScDocument& rDoc = mpContainer->GetDocument(); + ScHorizontalCellIterator aIter(rDoc, nTable, rSubTotalParam.nCol1, rSubTotalParam.nRow2, + rSubTotalParam.nCol2, + rSubTotalParam.nRow2); // Total row only + ScRefCellValue* pCell; + SCCOL nCol = rSubTotalParam.nCol1 - 1; + SCROW nRow; + std::vector<std::pair<SCCOL, OUString>> vColLabels; + std::vector<std::pair<SCCOL, std::unique_ptr<ScTokenArray>>> vColFuncs; + while ((pCell = aIter.GetNext(nCol, nRow)) != nullptr) + { + if (pCell->getType() != CELLTYPE_FORMULA) { - OSL_ENSURE(nCheck <= nLabelsCount, "Range error"); - if (GetTableColumnAttributes().size() <= i) - { - SAL_WARN("sc.core", - "ScDBData::CreateSubTotalParam - column attributes size mismatch"); - break; - } - if (GetTableColumnAttributes()[i].maTotalsRowLabel.has_value()) + OUString aStr = pCell->getString(rDoc); + if (!aStr.isEmpty()) + vColLabels.push_back(std::make_pair(nCol, std::move(aStr))); + } + else + { + if (ScFormulaCell* pFC = pCell->getFormula()) { - pSubLabels[nCheck] = rSubTotalParam.nCol1 + i; - pLabels[nCheck] = GetTableColumnAttributes()[i].maTotalsRowLabel.value(); - nCheck++; + std::unique_ptr<ScTokenArray> pTokens = pFC->GetCode()->Clone(); + if (pTokens) + { + vColFuncs.push_back(std::make_pair(nCol, std::move(pTokens))); + } } } - rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), // group number - pSubLabels.get(), pLabels.get(), - nLabelsCount); // number of array elements } + rSubTotalParam.SetSubLabels(static_cast<sal_uInt16>(0), vColLabels, vColLabels.size()); + rSubTotalParam.SetCustFuncs(static_cast<sal_uInt16>(0), vColFuncs, vColFuncs.size()); } } @@ -879,7 +910,6 @@ void ScDBData::UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos) aRange.aEnd.Row()); // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty. maTableColumnNames = aNames; - maTableColumnAttributes.resize(aNames.size()); mbTableColumnNamesDirty = bTableColumnNamesDirty; } @@ -912,7 +942,7 @@ bool ScDBData::UpdateReference(const ScDocument& rDoc, UpdateRefMode eUpdateRefM if (bDoUpdate && eRet != UR_INVALID) { // MoveTo() invalidates column names via SetArea(); adjust, remember and set new. - AdjustTableColumnAttributes( eUpdateRefMode, nDx, nCol1, nOldCol1, nOldCol2, theCol1, theCol2); + AdjustTableColumnNames( eUpdateRefMode, nDx, nCol1, nOldCol1, nOldCol2, theCol1, theCol2); ::std::vector<OUString> aNames( maTableColumnNames); bool bTableColumnNamesDirty = mbTableColumnNamesDirty; // tdf#48025, tdf#141946: update the column index of the filter criteria, @@ -923,7 +953,6 @@ bool ScDBData::UpdateReference(const ScDocument& rDoc, UpdateRefMode eUpdateRefM MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 ); // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty. maTableColumnNames = aNames; - maTableColumnAttributes.resize(aNames.size()); mbTableColumnNamesDirty = bTableColumnNamesDirty; } @@ -1007,12 +1036,7 @@ void ScDBData::SetTableColumnNames( ::std::vector< OUString >&& rNames ) mbTableColumnNamesDirty = false; } -void ScDBData::SetTableColumnAttributes( ::std::vector< TableColumnAttributes >&& rAttributes ) -{ - maTableColumnAttributes = std::move(rAttributes); -} - -void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1, +void ScDBData::AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1, SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 ) { if (maTableColumnNames.empty()) @@ -1024,7 +1048,6 @@ void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL return; // not moved or entirely moved, nothing to do ::std::vector<OUString> aNewNames; - ::std::vector<TableColumnAttributes> aNewAttributes; if (eUpdateRefMode == URM_INSDEL) { if (nDx > 0) @@ -1041,26 +1064,22 @@ void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL if (nDx > 0) n += nDx; aNewNames.resize(n); - aNewAttributes.resize(n); // Copy head. for (size_t i = 0; i < nHead; ++i) { aNewNames[i] = maTableColumnNames[i]; - aNewAttributes[i] = maTableColumnAttributes[i]; } // Copy tail, inserted middle range, if any, stays empty. for (size_t i = n - nTail, j = maTableColumnNames.size() - nTail; i < n; ++i, ++j) { aNewNames[i] = maTableColumnNames[j]; - aNewAttributes[i] = maTableColumnAttributes[j]; } } } // else empty aNewNames invalidates names/offsets SAL_WARN_IF( !maTableColumnNames.empty() && aNewNames.empty(), - "sc.core", "ScDBData::AdjustTableColumnAttributes - invalidating column attributes/offsets"); + "sc.core", "ScDBData::AdjustTableColumnNames - invalidating column names/offsets"); aNewNames.swap( maTableColumnNames); - aNewAttributes.swap(maTableColumnAttributes); if (maTableColumnNames.empty()) mbTableColumnNamesDirty = true; if (mbTableColumnNamesDirty) @@ -1196,7 +1215,6 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc ) } aNewNames.swap( maTableColumnNames); - maTableColumnAttributes.resize(maTableColumnNames.size()); mbTableColumnNamesDirty = false; } diff --git a/sc/source/filter/excel/xedbdata.cxx b/sc/source/filter/excel/xedbdata.cxx index 8f374f6c769a..ecc715b1acbd 100644 --- a/sc/source/filter/excel/xedbdata.cxx +++ b/sc/source/filter/excel/xedbdata.cxx @@ -222,7 +222,6 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry ) } const std::vector< OUString >& rColNames = rData.GetTableColumnNames(); - const std::vector< TableColumnAttributes >& rColAttributes = rData.GetTableColumnAttributes(); if (!rColNames.empty()) { pTableStrm->startElement(XML_tableColumns, @@ -238,9 +237,9 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry ) pTableStrm->singleElement( XML_tableColumn, XML_id, OString::number(i+1), - 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) + 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, ..., // OOXTODO: XML_headerRowCellStyle, ..., diff --git a/sc/source/filter/inc/tablecolumnsbuffer.hxx b/sc/source/filter/inc/tablecolumnsbuffer.hxx index efcfd979b219..8c2e53e0f737 100644 --- a/sc/source/filter/inc/tablecolumnsbuffer.hxx +++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx @@ -20,7 +20,6 @@ #pragma once #include <oox/helper/refvector.hxx> -#include <dbdata.hxx> #include "workbookhelper.hxx" namespace oox { class AttributeList; } @@ -41,14 +40,22 @@ public: void importTableColumn( SequenceInputStream& rStrm ); /** Gets the name of this column. */ const OUString& getName() const; - /** Gets the attributes of this column. */ - const TableColumnAttributes& getColumnAttributes() const; + /** Gets the Total Row Label of this column. */ + const std::optional<OUString>& getColumnRowLabel() const; + /** Gets the Subtotal function of this column. */ + const std::optional<OUString>& getColumnSubTotal() const; + /** Gets the Custom function of this column. */ + const std::optional<OUString>& getColumnFunction() const; + /** Sets the function of this column. */ + void setFunc( const OUString& rChars ); private: OUString maName; sal_Int32 mnId; sal_Int32 mnDataDxfId; - TableColumnAttributes maColumnAttributes; + std::optional<OUString> maRowLabel = std::nullopt; + std::optional<OUString> maSubTotal = std::nullopt; + std::optional<OUString> maFunction = std::nullopt; }; class TableColumns : public WorkbookHelper diff --git a/sc/source/filter/inc/tablecolumnscontext.hxx b/sc/source/filter/inc/tablecolumnscontext.hxx index 1f5a5ba046b5..21d123713f9d 100644 --- a/sc/source/filter/inc/tablecolumnscontext.hxx +++ b/sc/source/filter/inc/tablecolumnscontext.hxx @@ -33,6 +33,7 @@ public: protected: virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override; + virtual void onCharacters( const rtl::OUString& rChars ) override; virtual void onStartElement( const AttributeList& rAttribs ) override; virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override; diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx b/sc/source/filter/oox/tablecolumnsbuffer.cxx index 3e42a7825ad3..447ec0c976a2 100644 --- a/sc/source/filter/oox/tablecolumnsbuffer.cxx +++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx @@ -20,8 +20,10 @@ #include <tablecolumnsbuffer.hxx> #include <sal/log.hxx> +#include <formula/grammar.hxx> #include <oox/helper/attributelist.hxx> #include <oox/token/tokens.hxx> +#include <dbdata.hxx> #include <subtotalparam.hxx> namespace oox::xls { @@ -39,9 +41,9 @@ void TableColumn::importTableColumn( const AttributeList& rAttribs ) maName = rAttribs.getString( XML_name, OUString() ); mnDataDxfId = rAttribs.getInteger( XML_dataDxfId, -1 ); if ( rAttribs.hasAttribute(XML_totalsRowLabel ) ) - maColumnAttributes.maTotalsRowLabel = rAttribs.getStringDefaulted( XML_totalsRowLabel ); + maRowLabel = rAttribs.getStringDefaulted(XML_totalsRowLabel); if ( rAttribs.hasAttribute( XML_totalsRowFunction ) ) - maColumnAttributes.maTotalsFunction = rAttribs.getStringDefaulted( XML_totalsRowFunction ); + maSubTotal = rAttribs.getStringDefaulted(XML_totalsRowFunction); } void TableColumn::importTableColumn( SequenceInputStream& /*rStrm*/ ) @@ -55,9 +57,24 @@ const OUString& TableColumn::getName() const return maName; } -const TableColumnAttributes& TableColumn::getColumnAttributes() const +const std::optional<OUString>& TableColumn::getColumnRowLabel() const { - return maColumnAttributes; + return maRowLabel; +} + +const std::optional<OUString>& TableColumn::getColumnSubTotal() const +{ + return maSubTotal; +} + +const std::optional<OUString>& TableColumn::getColumnFunction() const +{ + return maFunction; +} + +void TableColumn::setFunc( const OUString& rChars ) +{ + maFunction = rChars; } TableColumns::TableColumns( const WorkbookHelper& rHelper ) : @@ -92,25 +109,36 @@ bool TableColumns::finalizeImport( ScDBData* pDBData ) { /* TODO: use svl::SharedString for names */ ::std::vector< OUString > aNames( maTableColumnVector.size()); - ::std::vector< TableColumnAttributes > aAttributesVector( maTableColumnVector.size() ); + ::std::vector< TableColumnAttributes > aAttributes( maTableColumnVector.size() ); size_t i = 0; + bool hasAnySetValue = false; for (const auto& rxTableColumn : maTableColumnVector) { aNames[i] = rxTableColumn->getName(); - aAttributesVector[i] = rxTableColumn->getColumnAttributes(); + aAttributes[i].maTotalsRowLabel = rxTableColumn->getColumnRowLabel(); + aAttributes[i].maTotalsFunction = rxTableColumn->getColumnSubTotal(); + aAttributes[i].maCustomFunction = rxTableColumn->getColumnFunction(); + + if (!hasAnySetValue + && (aAttributes[i].maTotalsRowLabel.has_value() + || aAttributes[i].maTotalsFunction.has_value() + || aAttributes[i].maCustomFunction.has_value())) + { + hasAnySetValue = true; + } + ++i; } pDBData->SetTableColumnNames( std::move(aNames) ); - pDBData->SetTableColumnAttributes( std::move(aAttributesVector) ); - // set subtotal parameters for columns - if (pDBData->HasTotals()) + + // Import subtotal parameters for columns + if (hasAnySetValue && !pDBData->HasTotals()) { ScSubTotalParam aSubTotalParam; pDBData->GetSubTotalParam(aSubTotalParam); aSubTotalParam.bHasHeader = pDBData->HasHeader(); - aSubTotalParam.bRemoveOnly = false; - aSubTotalParam.bReplace = false; - pDBData->CreateSubTotalParam(aSubTotalParam); + pDBData->ImportSubTotalParam(aSubTotalParam, aAttributes, + formula::FormulaGrammar::GRAM_OOXML); pDBData->SetSubTotalParam(aSubTotalParam); } return true; diff --git a/sc/source/filter/oox/tablecolumnscontext.cxx b/sc/source/filter/oox/tablecolumnscontext.cxx index 270f544bd503..c21fb7ea3629 100644 --- a/sc/source/filter/oox/tablecolumnscontext.cxx +++ b/sc/source/filter/oox/tablecolumnscontext.cxx @@ -32,15 +32,31 @@ TableColumnContext::TableColumnContext( WorksheetContextBase& rParent, TableColu { } -ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) +void TableColumnContext::onCharacters( const rtl::OUString& rChars ) { - /* no known child elements */ + switch( getCurrentElement() ) + { + case XLS_TOKEN( totalsRowFormula ): + mrTableColumn.setFunc(rChars); + break; + } +} + +ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + if ((getCurrentElement() == XLS_TOKEN(tableColumn)) + && (nElement == XLS_TOKEN(totalsRowFormula))) + { + if (rAttribs.getToken(XML_t, XML_normal) != XML_TOKEN_INVALID) + return this; + } return nullptr; } void TableColumnContext::onStartElement( const AttributeList& rAttribs ) { - mrTableColumn.importTableColumn( rAttribs ); + if (getCurrentElement() == XLS_TOKEN(tableColumn)) + mrTableColumn.importTableColumn( rAttribs ); } ContextHandlerRef TableColumnContext::onCreateRecordContext( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ ) diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx index c85b6fac5d72..ba5b99e6cd2e 100644 --- a/sc/source/ui/docshell/dbdocfun.cxx +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -813,14 +813,17 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); bool bKeepSub = false; // repeat existing partial results? + bool bKeepTotals = false; if (rQueryParam.GetEntry(0).bDoQuery) // not at cancellation { ScSubTotalParam aSubTotalParam; pDBData->GetSubTotalParam( aSubTotalParam ); // partial results exist? - if ((aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly) - || (pDBData->HasTotals() && pDBData->GetTableStyleInfo())) + if ((aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)) bKeepSub = true; + + if (pDBData->HasTotals() && pDBData->GetTableStyleInfo()) + bKeepTotals = true; } ScDocumentUniquePtr pUndoDoc; @@ -886,7 +889,7 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, } // execute filtering on the document - SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub ); + SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub, bKeepTotals ); pDBData->CalcSaveFilteredCount( nCount ); if (bCopy) { @@ -1256,7 +1259,6 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, const 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; @@ -1291,7 +1293,7 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, const bool bSuccess = true; if (bDo) { - bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex()); + bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam); rDoc.SetDrawPageSize(nTab); } ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx index c78a8b40f05b..b4e94dd62341 100644 --- a/sc/source/ui/view/dbfunc3.cxx +++ b/sc/source/ui/view/dbfunc3.cxx @@ -671,7 +671,7 @@ void ScDBFunc::DoTableSubTotals( const ScDBData& rNewData, const ScSubTotalParam bool bSuccess = true; if (bDo) { - bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex()); + bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam); } ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab); diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx index 4104663e4b5f..1917a5e9b0af 100644 --- a/sc/source/ui/view/gridwin.cxx +++ b/sc/source/ui/view/gridwin.cxx @@ -2484,11 +2484,9 @@ void ScGridWindow::MouseButtonUp( const MouseEvent& rMEvt ) ScSubTotalParam aSubTotalParam; pDBData->GetSubTotalParam(aSubTotalParam); aSubTotalParam.bHasHeader = aNewDBData.HasHeader(); - if (!aNewDBData.HasSubTotalParam()) - { - pDBData->CreateSubTotalParam(aSubTotalParam); - aNewDBData.SetSubTotalParam(aSubTotalParam); - } + // store current subtotal settings + pDBData->CreateSubTotalParam(aSubTotalParam); + aNewDBData.SetSubTotalParam(aSubTotalParam); // add/replace total row aSubTotalParam.bRemoveOnly = false; aSubTotalParam.bReplace = true; diff --git a/sc/source/ui/view/tableshell.cxx b/sc/source/ui/view/tableshell.cxx index 2a2909cc7f64..df51311df8f2 100644 --- a/sc/source/ui/view/tableshell.cxx +++ b/sc/source/ui/view/tableshell.cxx @@ -98,14 +98,12 @@ void ScTableShell::ExecuteDatabaseSettings(SfxRequest& rReq) ScSubTotalParam aSubTotalParam; aNewDBData.GetSubTotalParam(aSubTotalParam); aSubTotalParam.bHasHeader = aNewDBData.HasHeader(); - if (!aNewDBData.HasSubTotalParam()) - { - pDBData->CreateSubTotalParam(aSubTotalParam); - aNewDBData.SetSubTotalParam(aSubTotalParam); - } if (!aNewDBData.HasTotals()) { + // store current subtotal settings + pDBData->CreateSubTotalParam(aSubTotalParam); + aNewDBData.SetSubTotalParam(aSubTotalParam); // remove total row aSubTotalParam.bRemoveOnly = true; aSubTotalParam.bReplace = true; commit 5c9631f7bcd0f632396895159f31e5ff0426ae6b Author: Balazs Varga <[email protected]> AuthorDate: Mon Oct 27 12:20:48 2025 +0100 Commit: Balazs Varga <[email protected]> CommitDate: Fri Oct 31 16:28:14 2025 +0100 Do not extend DbDataRange automatically for TableStyles when we have Total Rows. Also do not set bActive for groups in SubtotalParam otherwise the original Subtotal will run. Use TableRefs[ColumnName] for Total functions. Change-Id: Id95aa3ab58a7de7c7640a8e7d724df089fb66065 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193204 Tested-by: Balazs Varga <[email protected]> Reviewed-by: Balazs Varga <[email protected]> diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index aae6db64a30a..322a4a740e06 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1187,7 +1187,7 @@ public: bool DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); void RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); // Table SubTotals - bool DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam ); + bool DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, sal_uInt16 nIndex ); void RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, const ScSubTotalParam& rOldParam ); bool TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& rParam ); diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc index 7885c5acf443..7e837dacd788 100644 --- a/sc/inc/globstr.hrc +++ b/sc/inc/globstr.hrc @@ -208,6 +208,7 @@ #define STR_TABLE_GRAND_STDDEV NC_("STR_TABLE_GRAND_STDDEV", "Grand StdDev") #define STR_TABLE_GRAND_SUM NC_("STR_TABLE_GRAND_SUM", "Grand Sum") #define STR_TABLE_GRAND_VAR NC_("STR_TABLE_GRAND_VAR", "Grand Var") +#define STR_TABLE_TOTAL NC_("STR_TABLE_TOTAL", "Summary") #define STR_NOCHARTATCURSOR NC_("STR_NOCHARTATCURSOR", "No chart found at this position.") #define STR_PIVOT_NOTFOUND NC_("STR_PIVOT_NOTFOUND", "No pivot table found at this position.") #define STR_EMPTYDATA NC_("STR_EMPTYDATA", "(empty)") diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index cf3d7d9428c5..3a433eb66b75 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -322,7 +322,7 @@ public: void RemoveSubTotals( ScSubTotalParam& rParam ); void RemoveSimpleSubTotals( ScSubTotalParam& rParam, const ScSubTotalParam& rOldParam ); bool DoSubTotals( ScSubTotalParam& rParam ); - bool DoSimpleSubTotals( ScSubTotalParam& rParam ); + bool DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex ); const ScSheetEvents* GetSheetEvents() const { return pSheetEvents.get(); } void SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew ); diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx index 90495d9446ba..08938553ea4f 100644 --- a/sc/inc/tokenarray.hxx +++ b/sc/inc/tokenarray.hxx @@ -114,6 +114,7 @@ public: SC_DLLPUBLIC formula::FormulaToken* AddDoubleReference( const ScComplexRefData& rRef ); SC_DLLPUBLIC void AddRangeName( sal_uInt16 n, sal_Int16 nSheet ); formula::FormulaToken* AddDBRange( sal_uInt16 n ); + SC_DLLPUBLIC formula::FormulaToken* AddTableRef( sal_uInt16 n ); SC_DLLPUBLIC formula::FormulaToken* AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName ); SC_DLLPUBLIC void AddExternalSingleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName, const ScSingleRefData& rRef ); SC_DLLPUBLIC formula::FormulaToken* AddExternalDoubleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName, const ScComplexRefData& rRef ); diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx index 179713f2055b..519ad1cc720a 100644 --- a/sc/source/core/data/documen3.cxx +++ b/sc/source/core/data/documen3.cxx @@ -793,10 +793,10 @@ void ScDocument::RemoveTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, cons pTable->RemoveSimpleSubTotals( rParam, rOldParam ); } -bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) +bool ScDocument::DoTableSubTotals( SCTAB nTab, ScSubTotalParam& rParam, sal_uInt16 nIndex ) { ScTable* pTable = FetchTable(nTab); - return pTable && pTable->DoSimpleSubTotals(rParam); + return pTable && pTable->DoSimpleSubTotals(rParam, nIndex); } bool ScDocument::HasSubTotalCells( const ScRange& rRange ) @@ -1580,9 +1580,12 @@ void ScDocument::GetFilterEntries( ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA); //!?? if (!pDBData) return; - - pDBData->ExtendBackColorArea(*this); - pDBData->ExtendDataArea(*this); + // Do not extand DBArea automatically in case of Table Styles with Total row + if (!pDBData->HasTotals() || !pDBData->GetTableStyleInfo()) + { + pDBData->ExtendBackColorArea(*this); + pDBData->ExtendDataArea(*this); + } SCTAB nAreaTab; SCCOL nStartCol; SCROW nStartRow; @@ -1592,6 +1595,9 @@ void ScDocument::GetFilterEntries( if (pDBData->HasHeader()) ++nStartRow; + // For Table Styles area, exclude total row + if (pDBData->HasTotals() && pDBData->GetTableStyleInfo()) + --nEndRow; ScQueryParam aParam; pDBData->GetQueryParam( aParam ); diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index f00614448a9f..ea9ddcffdc02 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -2302,7 +2302,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) return bSpaceLeft; } -bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam ) +bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam, sal_uInt16 nIndex ) { RowEntry aRowEntry; aRowEntry.nGroupNo = 0; @@ -2330,30 +2330,27 @@ bool ScTable::DoSimpleSubTotals( ScSubTotalParam& rParam ) } else { - SetString(group.nField, aRowEntry.nDestRow, nTab, u"Summary"_ustr); + SetString(group.nField, aRowEntry.nDestRow, nTab, ScResId(STR_TABLE_TOTAL)); } // 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); + // Table refs structure + aArr.AddTableRef(nIndex); + aArr.AddOpCode(ocTableRefOpen); + ScSingleRefData aSingleRef; + aSingleRef.InitAddress(group.col(nResult), aRowEntry.nFuncStart - 1, nTab); + aArr.AddSingleReference(aSingleRef); + aArr.AddOpCode(ocTableRefClose); + // Table refs structure end aArr.AddOpCode(ocClose); aArr.AddOpCode(ocStop); ScFormulaCell* pCell = new ScFormulaCell( diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx index ae2d7161696d..d08320579e81 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -697,7 +697,6 @@ 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 diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index d2d57372a91b..eb9ba6c1db50 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -2355,6 +2355,11 @@ FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n ) return Add( new FormulaIndexToken( ocDBArea, n)); } +FormulaToken* ScTokenArray::AddTableRef( sal_uInt16 n ) +{ + return Add( new ScTableRefToken(n, ScTableRefToken::TABLE)); +} + FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName ) { return Add( new ScExternalNameToken(nFileId, rName) ); diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx index 06b987d432c2..c85b6fac5d72 100644 --- a/sc/source/ui/docshell/dbdocfun.cxx +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -818,7 +818,8 @@ bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, ScSubTotalParam aSubTotalParam; pDBData->GetSubTotalParam( aSubTotalParam ); // partial results exist? - if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly) + if ((aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly) + || (pDBData->HasTotals() && pDBData->GetTableStyleInfo())) bKeepSub = true; } @@ -1290,22 +1291,7 @@ void ScDBDocFunc::DoTableSubTotals( SCTAB nTab, const ScDBData& rNewData, const 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); + bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex()); rDoc.SetDrawPageSize(nTab); } ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx index b4e94dd62341..c78a8b40f05b 100644 --- a/sc/source/ui/view/dbfunc3.cxx +++ b/sc/source/ui/view/dbfunc3.cxx @@ -671,7 +671,7 @@ void ScDBFunc::DoTableSubTotals( const ScDBData& rNewData, const ScSubTotalParam bool bSuccess = true; if (bDo) { - bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam); + bSuccess = rDoc.DoTableSubTotals(nTab, aNewParam, rNewData.GetIndex()); } ScRange aDirtyRange(aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab);
