external/liborcus/UnpackedTarball_liborcus.mk | 1 external/liborcus/autofilter-crash.patch | 34 +++ sc/Library_scfilt.mk | 1 sc/inc/queryentry.hxx | 1 sc/source/core/tool/queryentry.cxx | 9 sc/source/filter/inc/orcus_autofilter.hxx | 111 +++++++++ sc/source/filter/inc/orcusinterface.hxx | 9 sc/source/filter/orcus/autofilter.cxx | 291 ++++++++++++++++++++++++++ sc/source/filter/orcus/interface.cxx | 17 + 9 files changed, 467 insertions(+), 7 deletions(-)
New commits: commit 08c9e64c0bedc05767dbcdfb879d389e0c413d85 Author: Kohei Yoshida <ko...@libreoffice.org> AuthorDate: Thu Mar 6 22:27:20 2025 -0500 Commit: Kohei Yoshida <ko...@libreoffice.org> CommitDate: Sat Mar 8 03:40:19 2025 +0100 Import autofilter via orcus interface This should address tdf#154311. Change-Id: I84401ad7a9982eaa868e68d7ae612debf09ecf9e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182606 Tested-by: Jenkins Reviewed-by: Kohei Yoshida <ko...@libreoffice.org> diff --git a/external/liborcus/UnpackedTarball_liborcus.mk b/external/liborcus/UnpackedTarball_liborcus.mk index b866f59c6ac9..24412db28496 100644 --- a/external/liborcus/UnpackedTarball_liborcus.mk +++ b/external/liborcus/UnpackedTarball_liborcus.mk @@ -20,6 +20,7 @@ $(eval $(call gb_UnpackedTarball_add_patches,liborcus,\ external/liborcus/libtool.patch.0 \ external/liborcus/styles-element-rules.patch \ external/liborcus/enum-labels.patch \ + external/liborcus/autofilter-crash.patch \ )) ifeq ($(OS),WNT) diff --git a/external/liborcus/autofilter-crash.patch b/external/liborcus/autofilter-crash.patch new file mode 100644 index 000000000000..d434cc94356e --- /dev/null +++ b/external/liborcus/autofilter-crash.patch @@ -0,0 +1,34 @@ +From 2ff46664c61f2cce545ac7ca972d6e9c6ffa270d Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida <kohei.yosh...@gmail.com> +Date: Thu, 27 Feb 2025 21:39:52 -0500 +Subject: [PATCH] Filter node stack may be empty when no filtering is applied + +But when the stack is not empty, then the assert condition still +stands. This is related to #207. +--- + src/liborcus/xls_xml_auto_filter_context.cpp | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/liborcus/xls_xml_auto_filter_context.cpp b/src/liborcus/xls_xml_auto_filter_context.cpp +index 509c0b25..34a9210c 100644 +--- a/src/liborcus/xls_xml_auto_filter_context.cpp ++++ b/src/liborcus/xls_xml_auto_filter_context.cpp +@@ -193,9 +193,12 @@ void xls_xml_auto_filter_context::end_auto_filter() + if (!mp_auto_filter) + return; + +- assert(m_filter_node_stack.size() == 1u); // root node +- m_filter_node_stack.back()->commit(); +- m_filter_node_stack.pop_back(); ++ if (!m_filter_node_stack.empty()) ++ { ++ assert(m_filter_node_stack.size() == 1u); // root node ++ m_filter_node_stack.back()->commit(); ++ m_filter_node_stack.pop_back(); ++ } + mp_auto_filter->commit(); + } + +-- +2.34.1 + diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk index e4936656676e..d060e3880ae7 100644 --- a/sc/Library_scfilt.mk +++ b/sc/Library_scfilt.mk @@ -234,6 +234,7 @@ $(eval $(call gb_Library_use_externals,scfilt,\ )) $(eval $(call gb_Library_add_exception_objects,scfilt,\ + sc/source/filter/orcus/autofilter \ sc/source/filter/orcus/interface \ sc/source/filter/orcus/orcusfiltersimpl \ sc/source/filter/orcus/xmlcontext \ diff --git a/sc/inc/queryentry.hxx b/sc/inc/queryentry.hxx index 9798b83df785..ad130549cb0f 100644 --- a/sc/inc/queryentry.hxx +++ b/sc/inc/queryentry.hxx @@ -66,6 +66,7 @@ struct SC_DLLPUBLIC ScQueryEntry final ScQueryEntry(); ScQueryEntry(const ScQueryEntry& r); + ScQueryEntry(ScQueryEntry&& r) noexcept; ~ScQueryEntry(); /// creates pSearchParam and pSearchText if necessary diff --git a/sc/source/core/tool/queryentry.cxx b/sc/source/core/tool/queryentry.cxx index d66382d2e2b8..a39dabdcf559 100644 --- a/sc/source/core/tool/queryentry.cxx +++ b/sc/source/core/tool/queryentry.cxx @@ -56,6 +56,15 @@ ScQueryEntry::ScQueryEntry(const ScQueryEntry& r) : { } +ScQueryEntry::ScQueryEntry(ScQueryEntry&& r) noexcept : + bDoQuery(r.bDoQuery), + nField(r.nField), + eOp(r.eOp), + eConnect(r.eConnect), + maQueryItems(std::move(r.maQueryItems)) +{ +} + ScQueryEntry::~ScQueryEntry() { } diff --git a/sc/source/filter/inc/orcus_autofilter.hxx b/sc/source/filter/inc/orcus_autofilter.hxx new file mode 100644 index 000000000000..22be6a29db4d --- /dev/null +++ b/sc/source/filter/inc/orcus_autofilter.hxx @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <address.hxx> +#include <documentimport.hxx> +#include <queryentry.hxx> + +#include <orcus/spreadsheet/import_interface_auto_filter.hpp> + +#include <vector> +#include <functional> + +class ScOrcusGlobalSettings; + +class ScOrcusAutoFilterMultiValues + : public orcus::spreadsheet::iface::import_auto_filter_multi_values +{ +public: + using commitFuncType = std::function<void(ScQueryEntry)>; + + ScOrcusAutoFilterMultiValues(ScDocument& rDoc, const ScOrcusGlobalSettings& rGS); + + void add_value(std::string_view value) override; + + void commit() override; + + void reset(commitFuncType func); + +private: + ScDocument& mrDoc; + const ScOrcusGlobalSettings& mrGlobalSettings; + + ScQueryEntry maEntry; + commitFuncType maCommitFunc; +}; + +class ScOrcusAutoFilterNode : public orcus::spreadsheet::iface::import_auto_filter_node +{ +public: + using commitFuncType = std::function<void(std::vector<ScQueryEntry>, bool)>; + + ScOrcusAutoFilterNode(ScDocument& rDoc, const ScOrcusGlobalSettings& rGS); + + virtual void append_item(orcus::spreadsheet::col_t field, + orcus::spreadsheet::auto_filter_op_t op, double value) override; + + virtual void append_item(orcus::spreadsheet::col_t field, + orcus::spreadsheet::auto_filter_op_t op, std::string_view value, + bool regex) override; + + virtual void append_item(orcus::spreadsheet::col_t field, + orcus::spreadsheet::auto_filter_op_t op) override; + + virtual orcus::spreadsheet::iface::import_auto_filter_node* + start_node(orcus::spreadsheet::auto_filter_node_op_t op) override; + + virtual orcus::spreadsheet::iface::import_auto_filter_multi_values* + start_multi_values(orcus::spreadsheet::col_t field) override; + + virtual void commit() override; + + void reset(SCCOL nStartCol, ScQueryConnect eConn, commitFuncType func); + +private: + ScDocument& mrDoc; + const ScOrcusGlobalSettings& mrGlobalSettings; + ScOrcusAutoFilterMultiValues maMultiValues; + std::unique_ptr<ScOrcusAutoFilterNode> mxChild; + + SCCOL mnStartCol = -1; + ScQueryConnect meConn; + bool mbHasRegex = false; + + std::vector<ScQueryEntry> maEntries; + commitFuncType maCommitFunc; +}; + +class ScOrcusAutoFilter : public orcus::spreadsheet::iface::import_auto_filter +{ +public: + ScOrcusAutoFilter(ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab); + ScOrcusAutoFilter(const ScOrcusAutoFilter&) = delete; + ~ScOrcusAutoFilter(); + + virtual orcus::spreadsheet::iface::import_auto_filter_node* + start_node(orcus::spreadsheet::auto_filter_node_op_t op) override; + + virtual void commit() override; + + void reset(const orcus::spreadsheet::range_t& range); + +private: + ScDocumentImport& mrDoc; + + ScOrcusAutoFilterNode maNode; + std::unique_ptr<ScDBData> mxData; + + const SCTAB mnTab; + SCCOL mnCol1; + SCCOL mnCol2; + SCROW mnRow1; + SCROW mnRow2; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/inc/orcusinterface.hxx b/sc/source/filter/inc/orcusinterface.hxx index d9c7e2eacacc..1ff420a358cd 100644 --- a/sc/source/filter/inc/orcusinterface.hxx +++ b/sc/source/filter/inc/orcusinterface.hxx @@ -18,6 +18,7 @@ #include <editeng/svxenum.hxx> #include <editeng/editobj.hxx> +#include "orcus_autofilter.hxx" #include "sharedformulagroups.hxx" #include <conditio.hxx> @@ -366,7 +367,7 @@ class ScOrcusSheet : public orcus::spreadsheet::iface::import_sheet friend class ScOrcusArrayFormula; ScDocumentImport& mrDoc; - SCTAB mnTab; + const SCTAB mnTab; ScOrcusFactory& mrFactory; ScOrcusStyles& mrStyles; sc::SharedFormulaGroups maFormulaGroups; @@ -376,6 +377,7 @@ class ScOrcusSheet : public orcus::spreadsheet::iface::import_sheet ScOrcusNamedExpression maNamedExpressions; ScOrcusFormula maFormula; ScOrcusArrayFormula maArrayFormula; + ScOrcusAutoFilter maAutoFilter; int mnCellCount; @@ -384,7 +386,7 @@ class ScOrcusSheet : public orcus::spreadsheet::iface::import_sheet ScDocumentImport& getDoc(); public: - ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory); + ScOrcusSheet(ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab, ScOrcusFactory& rFactory); virtual orcus::spreadsheet::iface::import_sheet_properties* get_sheet_properties() override; virtual orcus::spreadsheet::iface::import_conditional_format* get_conditional_format() override; @@ -411,6 +413,9 @@ public: virtual void fill_down_cells(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, orcus::spreadsheet::row_t range_size) override; + virtual orcus::spreadsheet::iface::import_auto_filter* + start_auto_filter(const orcus::spreadsheet::range_t& range) override; + SCTAB getIndex() const { return mnTab; } const sc::SharedFormulaGroups& getSharedFormulaGroups() const; diff --git a/sc/source/filter/orcus/autofilter.cxx b/sc/source/filter/orcus/autofilter.cxx new file mode 100644 index 000000000000..344dbcdc0b06 --- /dev/null +++ b/sc/source/filter/orcus/autofilter.cxx @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <orcus_autofilter.hxx> +#include <orcusinterface.hxx> +#include <dbdata.hxx> +#include <globalnames.hxx> +#include <queryparam.hxx> + +#include <svl/sharedstringpool.hxx> + +#include <memory> + +namespace os = orcus::spreadsheet; + +namespace +{ +ScQueryConnect toQueryConnect(os::auto_filter_node_op_t op) +{ + switch (op) + { + case os::auto_filter_node_op_t::op_and: + return SC_AND; + case os::auto_filter_node_op_t::op_or: + return SC_OR; + case os::auto_filter_node_op_t::unspecified:; + } + + throw std::runtime_error("filter node operator type is not specified"); +} + +void setQueryOp(ScQueryEntry& rEntry, os::auto_filter_op_t op) +{ + switch (op) + { + case os::auto_filter_op_t::empty: + rEntry.bDoQuery = true; + rEntry.SetQueryByEmpty(); + break; + case os::auto_filter_op_t::not_empty: + rEntry.bDoQuery = true; + rEntry.SetQueryByNonEmpty(); + break; + case os::auto_filter_op_t::equal: + rEntry.bDoQuery = true; + rEntry.eOp = SC_EQUAL; + break; + case os::auto_filter_op_t::not_equal: + rEntry.bDoQuery = true; + rEntry.eOp = SC_NOT_EQUAL; + break; + case os::auto_filter_op_t::contain: + rEntry.bDoQuery = true; + rEntry.eOp = SC_CONTAINS; + break; + case os::auto_filter_op_t::not_contain: + rEntry.bDoQuery = true; + rEntry.eOp = SC_DOES_NOT_CONTAIN; + break; + case os::auto_filter_op_t::begin_with: + rEntry.bDoQuery = true; + rEntry.eOp = SC_BEGINS_WITH; + break; + case os::auto_filter_op_t::not_begin_with: + rEntry.bDoQuery = true; + rEntry.eOp = SC_DOES_NOT_BEGIN_WITH; + break; + case os::auto_filter_op_t::end_with: + rEntry.bDoQuery = true; + rEntry.eOp = SC_ENDS_WITH; + break; + case os::auto_filter_op_t::not_end_with: + rEntry.bDoQuery = true; + rEntry.eOp = SC_DOES_NOT_END_WITH; + break; + case os::auto_filter_op_t::greater: + rEntry.bDoQuery = true; + rEntry.eOp = SC_GREATER; + break; + case os::auto_filter_op_t::greater_equal: + rEntry.bDoQuery = true; + rEntry.eOp = SC_GREATER_EQUAL; + break; + case os::auto_filter_op_t::less: + rEntry.bDoQuery = true; + rEntry.eOp = SC_LESS; + break; + case os::auto_filter_op_t::less_equal: + rEntry.bDoQuery = true; + rEntry.eOp = SC_LESS_EQUAL; + break; + case os::auto_filter_op_t::top: + rEntry.bDoQuery = true; + rEntry.eOp = SC_TOPVAL; + break; + case os::auto_filter_op_t::bottom: + rEntry.bDoQuery = true; + rEntry.eOp = SC_BOTVAL; + break; + case os::auto_filter_op_t::top_percent: + rEntry.bDoQuery = true; + rEntry.eOp = SC_TOPPERC; + break; + case os::auto_filter_op_t::bottom_percent: + rEntry.bDoQuery = true; + rEntry.eOp = SC_BOTPERC; + break; + case os::auto_filter_op_t::top_percent_range: + case os::auto_filter_op_t::bottom_percent_range: + case os::auto_filter_op_t::unspecified: + rEntry.bDoQuery = false; + break; + } +} +} + +ScOrcusAutoFilterMultiValues::ScOrcusAutoFilterMultiValues(ScDocument& rDoc, + const ScOrcusGlobalSettings& rGS) + : mrDoc(rDoc) + , mrGlobalSettings(rGS) +{ +} + +void ScOrcusAutoFilterMultiValues::add_value(std::string_view value) +{ + OUString aStr(value.data(), value.size(), mrGlobalSettings.getTextEncoding()); + + ScQueryEntry::Item aItem; + aItem.maString = mrDoc.GetSharedStringPool().intern(aStr); + aItem.meType = ScQueryEntry::ByString; + + maEntry.GetQueryItems().push_back(aItem); +} + +void ScOrcusAutoFilterMultiValues::commit() { maCommitFunc(std::move(maEntry)); } + +void ScOrcusAutoFilterMultiValues::reset(commitFuncType func) +{ + maEntry.Clear(); + maEntry.bDoQuery = true; + maCommitFunc = std::move(func); +} + +ScOrcusAutoFilterNode::ScOrcusAutoFilterNode(ScDocument& rDoc, const ScOrcusGlobalSettings& rGS) + : mrDoc(rDoc) + , mrGlobalSettings(rGS) + , maMultiValues(rDoc, rGS) +{ +} + +void ScOrcusAutoFilterNode::append_item(os::col_t field, os::auto_filter_op_t op, double value) +{ + ScQueryEntry aEntry; + aEntry.nField = mnStartCol + field; + aEntry.eConnect = meConn; + setQueryOp(aEntry, op); + aEntry.GetQueryItem().meType = ScQueryEntry::ByValue; + aEntry.GetQueryItem().mfVal = value; + + maEntries.push_back(aEntry); +} + +void ScOrcusAutoFilterNode::append_item(os::col_t field, os::auto_filter_op_t op, + std::string_view value, bool regex) +{ + ScQueryEntry aEntry; + aEntry.nField = mnStartCol + field; + aEntry.eConnect = meConn; + setQueryOp(aEntry, op); + aEntry.GetQueryItem().meType = ScQueryEntry::ByString; + + OUString aStr(value.data(), value.size(), mrGlobalSettings.getTextEncoding()); + aEntry.GetQueryItem().maString = mrDoc.GetSharedStringPool().intern(aStr); + + maEntries.push_back(aEntry); + + if (regex) + mbHasRegex = true; +} + +void ScOrcusAutoFilterNode::append_item(os::col_t field, os::auto_filter_op_t op) +{ + ScQueryEntry aEntry; + aEntry.nField = mnStartCol + field; + aEntry.eConnect = meConn; + setQueryOp(aEntry, op); + + maEntries.push_back(aEntry); +} + +os::iface::import_auto_filter_node* ScOrcusAutoFilterNode::start_node(os::auto_filter_node_op_t op) +{ + auto func = [this](std::vector<ScQueryEntry> aEntries, bool bHasRegex) { + if (aEntries.empty()) + return; + + aEntries[0].eConnect = meConn; + + for (auto& rEntry : aEntries) + maEntries.push_back(std::move(rEntry)); + + if (bHasRegex) + mbHasRegex = true; + }; + + mxChild = std::make_unique<ScOrcusAutoFilterNode>(mrDoc, mrGlobalSettings); + mxChild->reset(mnStartCol, toQueryConnect(op), std::move(func)); + return mxChild.get(); +} + +os::iface::import_auto_filter_multi_values* +ScOrcusAutoFilterNode::start_multi_values(os::col_t field) +{ + auto func = [this, field](ScQueryEntry aEntry) { + aEntry.nField = mnStartCol + field; + maEntries.push_back(std::move(aEntry)); + }; + + maMultiValues.reset(std::move(func)); + return &maMultiValues; +} + +void ScOrcusAutoFilterNode::commit() { maCommitFunc(std::move(maEntries), mbHasRegex); } + +void ScOrcusAutoFilterNode::reset(SCCOL nStartCol, ScQueryConnect eConn, commitFuncType func) +{ + mnStartCol = nStartCol; + meConn = eConn; + + mxChild.reset(); + maEntries.clear(); + maCommitFunc = std::move(func); +} + +ScOrcusAutoFilter::ScOrcusAutoFilter(ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, + SCTAB nTab) + : mrDoc(rDoc) + , maNode(rDoc.getDoc(), rGS) + , mnTab(nTab) +{ +} + +ScOrcusAutoFilter::~ScOrcusAutoFilter() = default; + +os::iface::import_auto_filter_node* ScOrcusAutoFilter::start_node(os::auto_filter_node_op_t op) +{ + ScDBData& rData = *mxData; + auto func = [&rData](std::vector<ScQueryEntry> aEntries, bool bHasRegex) { + ScQueryParam aParam; + + if (bHasRegex) + aParam.eSearchType = utl::SearchParam::SearchType::Regexp; + + for (auto& rEntry : aEntries) + aParam.AppendEntry() = std::move(rEntry); + + rData.SetQueryParam(aParam); + }; + + maNode.reset(mnCol1, toQueryConnect(op), std::move(func)); + return &maNode; +} + +void ScOrcusAutoFilter::commit() +{ + auto& rDoc = mrDoc.getDoc(); + rDoc.SetAnonymousDBData(mnTab, std::move(mxData)); + + for (SCCOL nCol = mnCol1; nCol <= mnCol2; ++nCol) + { + auto nFlag = rDoc.GetAttr(nCol, mnRow1, mnTab, ATTR_MERGE_FLAG)->GetValue(); + rDoc.ApplyAttr(nCol, mnRow1, mnTab, ScMergeFlagAttr(nFlag | ScMF::Auto)); + } +} + +void ScOrcusAutoFilter::reset(const os::range_t& range) +{ + mnCol1 = range.first.column; + mnCol2 = range.last.column; + mnRow1 = range.first.row; + mnRow2 = range.last.row; + + mxData = std::make_unique<ScDBData>(STR_DB_LOCAL_NONAME, mnTab, mnCol1, mnRow1, mnCol2, mnRow2); + mxData->SetAutoFilter(true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/orcus/interface.cxx b/sc/source/filter/orcus/interface.cxx index e64a6134c674..aaa8b8e0281b 100644 --- a/sc/source/filter/orcus/interface.cxx +++ b/sc/source/filter/orcus/interface.cxx @@ -329,7 +329,7 @@ orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::append_sheet( // The calc document initializes with one sheet already present. assert(maDoc.getSheetCount() == 1); maDoc.setSheetName(0, aTabName); - maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, 0, *this)); + maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, maGlobalSettings, 0, *this)); return maSheets.back().get(); } @@ -337,7 +337,7 @@ orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::append_sheet( return nullptr; SCTAB nTab = maDoc.getSheetCount() - 1; - maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this)); + maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, maGlobalSettings, nTab, *this)); return maSheets.back().get(); } @@ -373,7 +373,7 @@ orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(std::string_v return it->get(); // Create a new orcus sheet instance for this. - maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this)); + maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, maGlobalSettings, nTab, *this)); return maSheets.back().get(); } @@ -389,7 +389,7 @@ orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(orcus::spread return it->get(); // Create a new orcus sheet instance for this. - maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this)); + maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, maGlobalSettings, nTab, *this)); return maSheets.back().get(); } @@ -963,7 +963,7 @@ void ScOrcusConditionalFormat::commit_format() mpCurrentFormat.reset(new ScConditionalFormat(0, &mrDoc)); } -ScOrcusSheet::ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory) : +ScOrcusSheet::ScOrcusSheet(ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab, ScOrcusFactory& rFactory) : mrDoc(rDoc), mnTab(nTab), mrFactory(rFactory), @@ -973,6 +973,7 @@ ScOrcusSheet::ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& r maNamedExpressions(rDoc, rFactory.getGlobalSettings(), nTab), maFormula(*this), maArrayFormula(*this), + maAutoFilter(rDoc, rGS, nTab), mnCellCount(0) { } @@ -1301,6 +1302,12 @@ void ScOrcusSheet::fill_down_cells(os::row_t row, os::col_t col, os::row_t range cellInserted(); } +os::iface::import_auto_filter* ScOrcusSheet::start_auto_filter(const os::range_t& range) +{ + maAutoFilter.reset(range); + return &maAutoFilter; +} + const sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups() const { return maFormulaGroups;