sc/Library_scfilt.mk | 1 sc/inc/SheetView.hxx | 1 sc/inc/document.hxx | 2 sc/source/filter/inc/NamedSheetViewFragment.hxx | 4 sc/source/filter/inc/NamedSheetViewImporter.hxx | 33 ++++ sc/source/filter/inc/autofilterbuffer.hxx | 6 sc/source/filter/inc/worksheethelper.hxx | 8 + sc/source/filter/oox/NamedSheetViewFragment.cxx | 27 ++- sc/source/filter/oox/NamedSheetViewImporter.cxx | 166 ++++++++++++++++++++++++ sc/source/filter/oox/autofilterbuffer.cxx | 11 + sc/source/filter/oox/workbookfragment.cxx | 6 sc/source/filter/oox/worksheethelper.cxx | 27 +++ 12 files changed, 279 insertions(+), 13 deletions(-)
New commits: commit db7848d2550c8577b481678bfa9ddc8a9caeb518 Author: Tomaž Vajngerl <[email protected]> AuthorDate: Thu Mar 5 00:09:56 2026 +0900 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Mar 5 09:17:21 2026 +0100 sc: Create Sheet views in named sheet view OOXML Import Previously we only read the structure of NamedSheetView fragments from OOXML document to a temporary structure, but did nothing with the imported data yet. This change creates new sheet view and applies filters and sorting to the autofilters according to the data in the imported structures. The bulk of the work for this is done in the newly introduced NamedSheetViewImporter class. Change-Id: I4524ef797ef945969b9e5db73c43c69debe89c3e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200946 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk index c03366790058..459cf81676ae 100644 --- a/sc/Library_scfilt.mk +++ b/sc/Library_scfilt.mk @@ -173,6 +173,7 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\ sc/source/filter/oox/commentsbuffer \ sc/source/filter/oox/commentsfragment \ sc/source/filter/oox/NamedSheetViewFragment \ + sc/source/filter/oox/NamedSheetViewImporter \ sc/source/filter/oox/condformatbuffer \ sc/source/filter/oox/condformatcontext \ sc/source/filter/oox/connectionsbuffer \ diff --git a/sc/inc/SheetView.hxx b/sc/inc/SheetView.hxx index df560b121678..7c01f0a502b4 100644 --- a/sc/inc/SheetView.hxx +++ b/sc/inc/SheetView.hxx @@ -80,6 +80,7 @@ public: SheetViewID getID() const { return mnID; } OUString const& GetName() const { return maName; } + void SetName(OUString const& rName) { maName = rName; } /** A sheet view is valid if the pointer to the table is set */ bool isValid() const; diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 9809d5d03b78..a35703b427f1 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -2428,7 +2428,7 @@ public: /* Sheet View */ /** Creates a new sheet view for the table, using the sheet view table */ - std::pair<sc::SheetViewID, SCTAB> CreateNewSheetView(SCTAB nTab); + SC_DLLPUBLIC std::pair<sc::SheetViewID, SCTAB> CreateNewSheetView(SCTAB nTab); /** Return the sheet view table for the ID */ SCTAB GetSheetViewNumber(SCTAB nTab, sc::SheetViewID nID); diff --git a/sc/source/filter/inc/NamedSheetViewFragment.hxx b/sc/source/filter/inc/NamedSheetViewFragment.hxx index 5024d27d6d01..4f4ca210132c 100644 --- a/sc/source/filter/inc/NamedSheetViewFragment.hxx +++ b/sc/source/filter/inc/NamedSheetViewFragment.hxx @@ -9,7 +9,6 @@ #pragma once -#include "NamedSheetViewFragment.hxx" #include "excelhandlers.hxx" #include "autofilterbuffer.hxx" @@ -150,8 +149,9 @@ public: private: virtual oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, AttributeList const& rAttribs) override; + virtual void finalizeImport() override; }; -} // namespace oox::xls +} // namespace oox::xls::nsv /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/inc/NamedSheetViewImporter.hxx b/sc/source/filter/inc/NamedSheetViewImporter.hxx new file mode 100644 index 000000000000..f18306586171 --- /dev/null +++ b/sc/source/filter/inc/NamedSheetViewImporter.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 "workbookhelper.hxx" +#include "NamedSheetViewFragment.hxx" + +#include <vector> + +namespace oox::xls::nsv +{ +/** Imports named sheet view data into the document model. */ +class NamedSheetViewImporter final : public WorkbookHelper +{ + std::vector<NamedSheetViewData> maNamedSheetViews; + SCTAB mnTab; + +public: + explicit NamedSheetViewImporter(const WorkbookHelper& rHelper, SCTAB nTab); + void setNamedSheetViews(std::vector<NamedSheetViewData>&& rData); + void finalizeImport(); +}; + +} // namespace oox::xls::nsv + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/inc/autofilterbuffer.hxx b/sc/source/filter/inc/autofilterbuffer.hxx index fad4de53bf09..a8dbbb789e79 100644 --- a/sc/source/filter/inc/autofilterbuffer.hxx +++ b/sc/source/filter/inc/autofilterbuffer.hxx @@ -194,6 +194,9 @@ public: FilterSettingsBase& createFilterSettings() { mxSettings = std::make_shared<FilterSettingsType>( *this ); return *mxSettings; } + /** Sets column ID and filter settings from external data. */ + void setColumnData(sal_Int32 nColId, std::shared_ptr<FilterSettingsBase> xSettings); + /** Returns converted UNO API filter settings representing all filter settings of this column. */ ApiFilterSettings finalizeImport(); @@ -240,6 +243,9 @@ public: SortCondition& createSortCondition(); + /** Sets the filter range directly. */ + void setRange(const ScRange& rRange); + /** Applies the filter to the passed filter descriptor. */ void finalizeImport( const css::uno::Reference< css::sheet::XDatabaseRange >& rxDatabaseRange, sal_Int16 nSheet ); diff --git a/sc/source/filter/inc/worksheethelper.hxx b/sc/source/filter/inc/worksheethelper.hxx index 4081d248e32b..b2cc0b40743a 100644 --- a/sc/source/filter/inc/worksheethelper.hxx +++ b/sc/source/filter/inc/worksheethelper.hxx @@ -38,6 +38,8 @@ namespace com::sun::star { namespace table { class XCellRange; } } +namespace oox::xls::nsv { struct NamedSheetViewData; } + namespace oox::xls { class AutoFilterBuffer; @@ -221,6 +223,9 @@ public: ExtLst& getExtLst() const; + /** Sets the named sheet views data for this sheet. */ + void setNamedSheetViews(std::vector<nsv::NamedSheetViewData>&& rData); + /** Sets a column or row page break described in the passed struct. */ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak ); /** Inserts the hyperlink URL into the spreadsheet. */ @@ -277,6 +282,9 @@ public: /** Final import of drawing objects. Has to be called after all content has been imported */ void finalizeDrawingImport(); + /** Finalizes the import of named sheet views, by creating the sheet views from the data. */ + void finalizeNamedSheetViews(); + void setCellFormula( const ScAddress& rTokenAddress, const OUString& ); void setCellFormula( diff --git a/sc/source/filter/oox/NamedSheetViewFragment.cxx b/sc/source/filter/oox/NamedSheetViewFragment.cxx index 3b1747371a47..c839f64bec27 100644 --- a/sc/source/filter/oox/NamedSheetViewFragment.cxx +++ b/sc/source/filter/oox/NamedSheetViewFragment.cxx @@ -9,8 +9,7 @@ #include <NamedSheetViewFragment.hxx> -#include <biffhelper.hxx> -#include <richstringcontext.hxx> +#include <sal/log.hxx> #include <oox/token/namespaces.hxx> #include <oox/helper/attributelist.hxx> #include <autofiltercontext.hxx> @@ -89,6 +88,7 @@ ContextHandlerRef SortRuleContext::onCreateContext(sal_Int32 nElement, { switch (nElement) { + case XNSV_TOKEN(sortCondition): case XLS14_TOKEN(sortCondition): mrSortRuleData.maRef = rAttribs.getString(XML_ref, {}); // required mrSortRuleData.mbDescending = rAttribs.getBool(XML_descending, false); // optional @@ -156,12 +156,6 @@ ContextHandlerRef NamedSheetViewContext::onCreateContext(sal_Int32 nElement, { switch (nElement) { - case XNSV_TOKEN(namedSheetView): - { - mrNamedSheetViewData.maName = rAttribs.getString(XML_name, {}); - mrNamedSheetViewData.maID = rAttribs.getString(XML_id, {}); - return this; - } case XNSV_TOKEN(nsvFilter): { auto& rNsvFilter = mrNamedSheetViewData.maNsvFilters.emplace_back(); @@ -184,18 +178,31 @@ NamedSheetViewFragment::NamedSheetViewFragment(WorksheetHelper const& rHelper, } ContextHandlerRef NamedSheetViewFragment::onCreateContext(sal_Int32 nElement, - AttributeList const& /*rAttribs*/) + AttributeList const& rAttribs) { switch (nElement) { case XNSV_TOKEN(namedSheetViews): { - return new NamedSheetViewContext(*this, maNamedSheetViews.emplace_back()); + return this; + } + case XNSV_TOKEN(namedSheetView): + { + auto& rData = maNamedSheetViews.emplace_back(); + rData.maName = rAttribs.getString(XML_name, {}); + rData.maID = rAttribs.getString(XML_id, {}); + return new NamedSheetViewContext(*this, rData); } } return nullptr; } +void NamedSheetViewFragment::finalizeImport() +{ + if (!maNamedSheetViews.empty()) + setNamedSheetViews(std::move(maNamedSheetViews)); +} + } // namespace oox::xls::nsv /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/oox/NamedSheetViewImporter.cxx b/sc/source/filter/oox/NamedSheetViewImporter.cxx new file mode 100644 index 000000000000..7494dd5970c7 --- /dev/null +++ b/sc/source/filter/oox/NamedSheetViewImporter.cxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 <NamedSheetViewImporter.hxx> + +#include <oox/helper/propertyset.hxx> +#include <oox/token/properties.hxx> +#include <addressconverter.hxx> +#include <autofilterbuffer.hxx> +#include <document.hxx> +#include <SheetView.hxx> +#include <SheetViewManager.hxx> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <dbdocfun.hxx> +#include <sortparam.hxx> + +namespace oox::xls::nsv +{ +using namespace ::com::sun::star; + +NamedSheetViewImporter::NamedSheetViewImporter(const WorkbookHelper& rHelper, SCTAB nTab) + : WorkbookHelper(rHelper) + , mnTab(nTab) +{ +} + +void NamedSheetViewImporter::setNamedSheetViews(std::vector<NamedSheetViewData>&& rData) +{ + maNamedSheetViews = std::move(rData); +} + +void NamedSheetViewImporter::finalizeImport() +{ + if (maNamedSheetViews.empty()) + return; + + ScDocument& rDoc = getScDocument(); + + for (const auto& rViewData : maNamedSheetViews) + { + auto[nSheetViewID, nViewTab] = rDoc.CreateNewSheetView(mnTab); + if (nSheetViewID == sc::InvalidSheetViewID) + continue; + + // Set the imported name on the sheet view + auto pSheetViewManager = rDoc.GetSheetViewManager(mnTab); + if (!pSheetViewManager) + continue; + + auto pSheetView = pSheetViewManager->get(nSheetViewID); + if (pSheetView) + pSheetView->SetName(rViewData.maName); + + // Apply filters and sort for each nsvFilter + for (const auto& rNsvFilter : rViewData.maNsvFilters) + { + // Parse the filter range reference + ScRange aFilterRange; + if (rNsvFilter.maRef.isEmpty()) + continue; + + if (!getAddressConverter().convertToCellRange(aFilterRange, rNsvFilter.maRef, nViewTab, + true, true)) + continue; + + // Adjust the range to the view tab + aFilterRange.aStart.SetTab(nViewTab); + aFilterRange.aEnd.SetTab(nViewTab); + + // Build an AutoFilter from the NSV column filter data and apply it + bool bHasColumnFilters + = std::any_of(rNsvFilter.maColumnFilters.begin(), rNsvFilter.maColumnFilters.end(), + [](const auto& rCF) { return !rCF.maFilters.empty(); }); + + if (bHasColumnFilters) + { + try + { + AutoFilter aAutoFilter(*this); + aAutoFilter.setRange(aFilterRange); + + for (const auto& rColFilter : rNsvFilter.maColumnFilters) + { + for (const auto& rFilter : rColFilter.maFilters) + { + if (rFilter.mxSettings) + { + FilterColumn& rFilterColumn = aAutoFilter.createFilterColumn(); + rFilterColumn.setColumnData(rFilter.maColumnID, rFilter.mxSettings); + } + } + } + + uno::Reference<css::sheet::XDatabaseRange> xDatabaseRange + = createUnnamedDatabaseRangeObject(aFilterRange); + if (xDatabaseRange.is()) + { + PropertySet aRangeProps(xDatabaseRange); + aRangeProps.setProperty(PROP_AutoFilter, true); + aAutoFilter.finalizeImport(xDatabaseRange, nViewTab); + } + } + catch (const css::uno::Exception&) + { + SAL_WARN("sc", "Failed to apply filter to named sheet view"); + } + } + + // Apply sort rules if present + if (rNsvFilter.maSortRules) + { + const auto& rSortRules = *rNsvFilter.maSortRules; + if (!rSortRules.maRules.empty()) + { + ScSortParam aParam; + aParam.nCol1 = aFilterRange.aStart.Col(); + aParam.nRow1 = aFilterRange.aStart.Row(); + aParam.nCol2 = aFilterRange.aEnd.Col(); + aParam.nRow2 = aFilterRange.aEnd.Row(); + aParam.bHasHeader = true; + aParam.bByRow = true; + aParam.bCaseSens = rSortRules.mbCaseSensitive; + + size_t nKeyIdx = 0; + for (const auto& rSortRule : rSortRules.maRules) + { + if (nKeyIdx >= aParam.GetSortKeyCount()) + aParam.maKeyState.resize(nKeyIdx + 1); + + // Parse the sort condition reference to get the column + ScRange aSortRange; + if (!rSortRule.maRef.isEmpty() + && getAddressConverter().convertToCellRange(aSortRange, rSortRule.maRef, + nViewTab, true, true)) + { + aParam.maKeyState[nKeyIdx].bDoSort = true; + aParam.maKeyState[nKeyIdx].bAscending = !rSortRule.mbDescending; + aParam.maKeyState[nKeyIdx].nField = aSortRange.aStart.Col(); + ++nKeyIdx; + } + } + + if (nKeyIdx > 0) + { + ScDocShell* pDocShell = rDoc.GetDocumentShell(); + if (pDocShell) + { + ScDBDocFunc aFunc(*pDocShell); + aFunc.SortTab(nViewTab, aParam, false, false, true); + } + } + } + } + } + } +} + +} // namespace oox::xls::nsv + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx index dcf8da5fdc9d..fccc637ef35f 100644 --- a/sc/source/filter/oox/autofilterbuffer.cxx +++ b/sc/source/filter/oox/autofilterbuffer.cxx @@ -644,6 +644,12 @@ void FilterColumn::importFilterColumn( SequenceInputStream& rStrm ) mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON ); } +void FilterColumn::setColumnData(sal_Int32 nColId, std::shared_ptr<FilterSettingsBase> xSettings) +{ + mnColId = nColId; + mxSettings = std::move(xSettings); +} + ApiFilterSettings FilterColumn::finalizeImport() { ApiFilterSettings aSettings; @@ -706,6 +712,11 @@ void AutoFilter::importSortState( const AttributeList& rAttribs, sal_Int16 nShee AddressConverter::convertToCellRangeUnchecked(maSortRange, aRangeStr, nSheet, getScDocument()); } +void AutoFilter::setRange(const ScRange& rRange) +{ + maRange = rRange; +} + FilterColumn& AutoFilter::createFilterColumn() { FilterColumnVector::value_type xFilterColumn = std::make_shared<FilterColumn>( *this ); diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx index 8ce2f5050999..3db1ec8d2943 100644 --- a/sc/source/filter/oox/workbookfragment.cxx +++ b/sc/source/filter/oox/workbookfragment.cxx @@ -556,6 +556,12 @@ void WorkbookFragment::finalizeImport() pHelper->finalizeDrawingImport(); } + // Create sheet view tabs as late as possible. + for (WorksheetHelper* pHelper : aHelpers) + { + pHelper->finalizeNamedSheetViews(); + } + for( auto& [rxSheetGlob, rxFragment] : aSheetFragments ) { // delete fragment object and WorkbookGlobals object, will free all allocated sheet buffers diff --git a/sc/source/filter/oox/worksheethelper.cxx b/sc/source/filter/oox/worksheethelper.cxx index 6fd9bc5750ea..22c7f82f2a2b 100644 --- a/sc/source/filter/oox/worksheethelper.cxx +++ b/sc/source/filter/oox/worksheethelper.cxx @@ -72,6 +72,7 @@ #include <columnspanset.hxx> #include <dbdata.hxx> #include <cellsuno.hxx> +#include <NamedSheetViewImporter.hxx> #include <fmtuno.hxx> #include <svl/stritem.hxx> @@ -272,6 +273,12 @@ public: /** returns the ExtLst entries that need to be filled */ ExtLst& getExtLst() { return maExtLst; } + /** Sets the named sheet views data for this sheet. */ + void setNamedSheetViews(std::vector<nsv::NamedSheetViewData>&& rData) + { + maNamedSheetViewImporter.setNamedSheetViews(std::move(rData)); + } + /** Sets a column or row page break described in the passed struct. */ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak ); /** Inserts the hyperlink URL into the spreadsheet. */ @@ -318,6 +325,9 @@ public: void finalizeDrawingImport(); + /** Creates sheet views from imported named sheet view data and applies filters/sort. */ + void finalizeNamedSheetViews(); + /// Allow the threaded importer to override our progress bar impl. virtual ISegmentProgressBarRef getRowProgress() override { @@ -391,6 +401,7 @@ private: SheetViewSettings maSheetViewSett; /// View settings for this sheet. VmlDrawingPtr mxVmlDrawing; /// Collection of all VML shapes. ExtLst maExtLst; /// List of extended elements + nsv::NamedSheetViewImporter maNamedSheetViewImporter; /// Named sheet view importer. OUString maDrawingPath; /// Path to DrawingML fragment. OUString maVmlDrawingPath; /// Path to legacy VML drawing fragment. awt::Size maDrawPageSize; /// Current size of the drawing page in 1/100 mm. @@ -416,6 +427,7 @@ WorksheetGlobals::WorksheetGlobals( const WorkbookHelper& rHelper, ISegmentProgr maSheetSett( *this ), maPageSett( *this ), maSheetViewSett( *this ), + maNamedSheetViewImporter(*this, nSheet), mxProgressBar(std::move( xProgressBar )), mbFastRowProgress( false ), meSheetType( eSheetType ), @@ -992,6 +1004,11 @@ void WorksheetGlobals::finalizeWorksheetImport() lclUpdateProgressBar( mxFinalProgress, 1.0 ); } +void WorksheetGlobals::finalizeNamedSheetViews() +{ + maNamedSheetViewImporter.finalizeImport(); +} + void WorksheetGlobals::finalizeDrawingImport() { finalizeDrawings(); @@ -1527,6 +1544,11 @@ ExtLst& WorksheetHelper::getExtLst() const return mrSheetGlob.getExtLst(); } +void WorksheetHelper::setNamedSheetViews(std::vector<nsv::NamedSheetViewData>&& rData) +{ + mrSheetGlob.setNamedSheetViews(std::move(rData)); +} + void WorksheetHelper::setPageBreak( const PageBreakModel& rModel, bool bRowBreak ) { mrSheetGlob.setPageBreak( rModel, bRowBreak ); @@ -1628,6 +1650,11 @@ void WorksheetHelper::finalizeDrawingImport() mrSheetGlob.finalizeDrawingImport(); } +void WorksheetHelper::finalizeNamedSheetViews() +{ + mrSheetGlob.finalizeNamedSheetViews(); +} + void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr ) { getFormulaBuffer().setCellFormula( rTokenAddress, rTokenStr );
