sc/inc/dbdata.hxx | 9 - sc/inc/document.hxx | 2 sc/inc/globstr.hrc | 1 sc/inc/subtotalparam.hxx | 25 +++ sc/inc/table.hxx | 2 sc/inc/tokenarray.hxx | 1 sc/source/core/data/documen3.cxx | 16 +- sc/source/core/data/subtotalparam.cxx | 77 +++++++++-- sc/source/core/data/table3.cxx | 51 +++---- sc/source/core/tool/dbdata.cxx | 176 +++++++++++++++------------ sc/source/core/tool/token.cxx | 5 sc/source/filter/excel/xedbdata.cxx | 7 - sc/source/filter/inc/tablecolumnsbuffer.hxx | 16 +- sc/source/filter/inc/tablecolumnscontext.hxx | 1 sc/source/filter/oox/tablecolumnsbuffer.cxx | 52 ++++++- sc/source/filter/oox/tablecolumnscontext.cxx | 23 +++ sc/source/ui/docshell/dbdocfun.cxx | 22 --- sc/source/ui/view/gridwin.cxx | 8 - sc/source/ui/view/tableshell.cxx | 8 - 19 files changed, 316 insertions(+), 186 deletions(-)
New commits: commit 07fa6e32e261c4b03b4760c086304a0bc61d913e Author: Balazs Varga <[email protected]> AuthorDate: Wed Oct 29 14:54:20 2025 +0100 Commit: Andras Timar <[email protected]> CommitDate: Mon Nov 17 19:03:09 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. cherry-pick from: bca5864a3245021601991e9be8ff6401ffd812b5 Change-Id: I894c309362cfaf525ebbf47e7f49bc67b8da8495 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193205 Reviewed-by: Balazs Varga <[email protected]> Tested-by: Balazs Varga <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193681 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 66fabcccd472..0676e6d8dde5 100644 --- a/sc/inc/dbdata.hxx +++ b/sc/inc/dbdata.hxx @@ -83,6 +83,7 @@ struct TableColumnAttributes { std::optional<OUString> maTotalsRowLabel = std::nullopt; std::optional<OUString> maTotalsFunction = std::nullopt; + std::optional<OUString> maCustomFunction = std::nullopt; }; // xmlColumnPr attributes @@ -174,7 +175,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 ::std::vector< TableColumnModel > maTableColumnModel; bool mbTableColumnNamesDirty; SCSIZE nFilteredRowCount; @@ -232,8 +232,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; } SC_DLLPUBLIC void SetTableColumnModel( TableColumnModel& rModel ) { maTableColumnModel.push_back(std::move(rModel)); @@ -278,6 +276,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; @@ -317,7 +318,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 7d7caeac88b6..b6e5c9a7fbf0 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1213,7 +1213,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 ); @@ -2265,7 +2265,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..fcfc8ef04244 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,9 +59,11 @@ 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 SetSublabels(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 std::span<Pair> subtotals() { return std::span(pSubTotals.get(), nSubTotals); } @@ -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 a361d4f5ea0c..9fdd7501bfbc 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -330,7 +330,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 ); @@ -1043,7 +1043,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 244d2a95341d..9843ccebf58d 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..be9a95fc6767 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,10 +130,15 @@ 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::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 +//} void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup, const SCCOL* ptrSubTotals, @@ -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 0d00773be892..14d7226ef7c6 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -25,6 +25,7 @@ #include <unotools/charclass.hxx> #include <dbdata.hxx> +#include <compiler.hxx> #include <globalnames.hxx> #include <refupdat.hxx> #include <document.hxx> @@ -321,7 +322,6 @@ ScDBData::ScDBData( const ScDBData& rData ) : bAutoFilter (rData.bAutoFilter), bModified (rData.bModified), maTableColumnNames (rData.maTableColumnNames), - maTableColumnAttributes(rData.maTableColumnAttributes), mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty), nFilteredRowCount (rData.nFilteredRowCount) { @@ -359,7 +359,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) { @@ -417,7 +416,6 @@ ScDBData& ScDBData::operator= (const ScDBData& rData) else { maTableColumnNames = rData.maTableColumnNames; - maTableColumnAttributes = rData.maTableColumnAttributes; mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty; } @@ -697,7 +695,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; @@ -706,78 +706,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()) { - 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) { - 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()); } } @@ -881,7 +915,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; } @@ -914,7 +947,7 @@ bool ScDBData::UpdateReference(const ScDocument* pDoc, 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, @@ -925,7 +958,6 @@ bool ScDBData::UpdateReference(const ScDocument* pDoc, UpdateRefMode eUpdateRefM MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 ); // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty. maTableColumnNames = aNames; - maTableColumnAttributes.resize(aNames.size()); mbTableColumnNamesDirty = bTableColumnNamesDirty; } @@ -1009,12 +1041,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()) @@ -1026,7 +1053,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) @@ -1043,26 +1069,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) @@ -1198,7 +1220,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 97fa10cf62b4..1283082fe13c 100644 --- a/sc/source/filter/excel/xedbdata.cxx +++ b/sc/source/filter/excel/xedbdata.cxx @@ -230,7 +230,6 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry ) } const std::vector< OUString >& rColNames = rData.GetTableColumnNames(); - const std::vector< TableColumnAttributes >& rColAttributes = rData.GetTableColumnAttributes(); const std::vector< TableColumnModel >& rTableColumnModel = rData.GetTableColumnModel(); if (!rColNames.empty()) { @@ -260,9 +259,9 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry ) pTableStrm->startElement( XML_tableColumn, 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) + 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 d18d9dcab957..8abc3915afb6 100644 --- a/sc/source/filter/inc/tablecolumnsbuffer.hxx +++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx @@ -26,7 +26,6 @@ namespace oox { class AttributeList; } namespace oox { class SequenceInputStream; } -class ScDBData; namespace oox::xls { @@ -43,20 +42,29 @@ 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; /** Imports XML column properties for the xmlColumnPr element. */ void importXmlColumnPr(const AttributeList& rAttribs); /** Returns access to the table column model data. */ TableColumnModel& getModel() { return maModel; } + /** 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; TableColumnModel maModel; + + 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 81f46a43496d..0d0b6e0a75be 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> XmlColumnPrModel::XmlColumnPrModel() : @@ -57,9 +59,9 @@ void TableColumn::importTableColumn( const AttributeList& rAttribs ) maModel.maUniqueName = rAttribs.getXString( XML_uniqueName, 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*/ ) @@ -73,9 +75,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; } void TableColumn::importXmlColumnPr(const AttributeList& rAttribs) @@ -120,26 +137,37 @@ 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(); pDBData->SetTableColumnModel( rxTableColumn->getModel() ); + 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 1129bcd22f7d..e1115c41a375 100644 --- a/sc/source/filter/oox/tablecolumnscontext.cxx +++ b/sc/source/filter/oox/tablecolumnscontext.cxx @@ -20,6 +20,7 @@ #include <tablecolumnscontext.hxx> #include <tablecolumnsbuffer.hxx> +#include <oox/helper/attributelist.hxx> #include <oox/token/namespaces.hxx> namespace oox::xls { @@ -32,20 +33,38 @@ TableColumnContext::TableColumnContext( WorksheetContextBase& rParent, TableColu { } +void TableColumnContext::onCharacters( const rtl::OUString& rChars ) +{ + switch( getCurrentElement() ) + { + case XLS_TOKEN( totalsRowFormula ): + mrTableColumn.setFunc(rChars); + break; + } +} + ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) { switch (nElement) { case XLS_TOKEN(xmlColumnPr): mrTableColumn.importXmlColumnPr( rAttribs ); - break; + break; + case XLS_TOKEN(totalsRowFormula): + { + if (getCurrentElement() == XLS_TOKEN(tableColumn) && + rAttribs.getToken(XML_t, XML_normal) != XML_TOKEN_INVALID) + return this; + } + break; } 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 24724a60da33..2f7a070a772e 100644 --- a/sc/source/ui/docshell/dbdocfun.cxx +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -829,14 +829,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; @@ -902,7 +905,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) { @@ -1275,7 +1278,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; @@ -1310,7 +1312,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 2eb32f19f755..8428509c8aa1 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 1520d4ec8385..e71c3e4b3f83 100644 --- a/sc/source/ui/view/gridwin.cxx +++ b/sc/source/ui/view/gridwin.cxx @@ -2501,11 +2501,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 9b3769625b8d..bb35a0d98ba7 100644 --- a/sc/source/ui/view/tableshell.cxx +++ b/sc/source/ui/view/tableshell.cxx @@ -99,14 +99,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 bcfc5fccdf2d6d355d9369a00de04edb7333d6aa Author: Balazs Varga <[email protected]> AuthorDate: Mon Oct 27 12:20:48 2025 +0100 Commit: Andras Timar <[email protected]> CommitDate: Mon Nov 17 19:02:56 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]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193680 Reviewed-by: Andras Timar <[email protected]> Tested-by: Andras Timar <[email protected]> diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 90bbad32c232..7d7caeac88b6 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1213,7 +1213,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 74f21a7efad7..b124996be1c3 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 591d2112e747..a361d4f5ea0c 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -330,7 +330,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 265ab5e6b392..244d2a95341d 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 742a03628b30..0d00773be892 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -701,7 +701,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 08fa659d2ce6..24724a60da33 100644 --- a/sc/source/ui/docshell/dbdocfun.cxx +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -834,7 +834,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; } @@ -1309,22 +1310,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 8428509c8aa1..2eb32f19f755 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);
