summaryrefslogtreecommitdiffstats
path: root/sc/source/filter/excel/excrecds.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/filter/excel/excrecds.cxx')
-rw-r--r--sc/source/filter/excel/excrecds.cxx1177
1 files changed, 1177 insertions, 0 deletions
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
new file mode 100644
index 000000000..b175445bc
--- /dev/null
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -0,0 +1,1177 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excrecds.hxx>
+
+#include <map>
+#include <filter/msfilter/countryid.hxx>
+
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <string.h>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+#include <sortparam.hxx>
+#include <userlist.hxx>
+#include <root.hxx>
+
+#include <xeescher.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xlname.hxx>
+#include <xestyle.hxx>
+
+#include <xcl97rec.hxx>
+#include <tabprotection.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Sequence;
+
+//--------------------------------------------------------- class ExcDummy_00 -
+const sal_uInt8 ExcDummy_00::pMyData[] = {
+ 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+};
+const std::size_t ExcDummy_00::nMyLen = sizeof( ExcDummy_00::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_04x -
+const sal_uInt8 ExcDummy_040::pMyData[] = {
+ 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
+ 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
+};
+const std::size_t ExcDummy_040::nMyLen = sizeof( ExcDummy_040::pMyData );
+
+const sal_uInt8 ExcDummy_041::pMyData[] = {
+ 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
+ 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
+};
+const std::size_t ExcDummy_041::nMyLen = sizeof( ExcDummy_041::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_02a -
+const sal_uInt8 ExcDummy_02a::pMyData[] = {
+ 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
+ 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
+ 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
+ 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
+ 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
+ 0x62, 0x50, 0x3f,
+ 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
+};
+const std::size_t ExcDummy_02a::nMyLen = sizeof( ExcDummy_02a::pMyData );
+
+//----------------------------------------------------------- class ExcRecord -
+
+void ExcRecord::Save( XclExpStream& rStrm )
+{
+ SetRecHeader( GetNum(), GetLen() );
+ XclExpRecord::Save( rStrm );
+}
+
+void ExcRecord::SaveCont( XclExpStream& /*rStrm*/ )
+{
+}
+
+void ExcRecord::WriteBody( XclExpStream& rStrm )
+{
+ SaveCont( rStrm );
+}
+
+void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+sal_uInt16 ExcEmptyRec::GetNum() const
+{
+ return 0;
+}
+
+std::size_t ExcEmptyRec::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+void ExcDummyRec::Save( XclExpStream& rStrm )
+{
+ rStrm.Write( GetData(), GetLen() ); // raw write mode
+}
+
+sal_uInt16 ExcDummyRec::GetNum() const
+{
+ return 0x0000;
+}
+
+//------------------------------------------------------- class ExcBoolRecord -
+
+void ExcBoolRecord::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(bVal ? 0x0001 : 0x0000);
+}
+
+std::size_t ExcBoolRecord::GetLen() const
+{
+ return 2;
+}
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+ExcBof_Base::ExcBof_Base()
+ : nDocType(0)
+ , nVers(0)
+ , nRupBuild(0x096C) // copied from Excel
+ , nRupYear(0x07C9) // copied from Excel
+{
+}
+
+//-------------------------------------------------------------- class ExcBof -
+
+ExcBof::ExcBof()
+{
+ nDocType = 0x0010;
+ nVers = 0x0500;
+}
+
+void ExcBof::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBof::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof::GetLen() const
+{
+ return 8;
+}
+
+//------------------------------------------------------------- class ExcBofW -
+
+ExcBofW::ExcBofW()
+{
+ nDocType = 0x0005;
+ nVers = 0x0500;
+}
+
+void ExcBofW::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBofW::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBofW::GetLen() const
+{
+ return 8;
+}
+
+//-------------------------------------------------------------- class ExcEof -
+
+sal_uInt16 ExcEof::GetNum() const
+{
+ return 0x000A;
+}
+
+std::size_t ExcEof::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummy_00 -
+
+std::size_t ExcDummy_00::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_00::GetData() const
+{
+ return pMyData;
+}
+
+//-------------------------------------------------------- class ExcDummy_04x -
+
+std::size_t ExcDummy_040::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_040::GetData() const
+{
+ return pMyData;
+}
+
+std::size_t ExcDummy_041::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_041::GetData() const
+{
+ return pMyData;
+}
+
+//------------------------------------------------------------- class Exc1904 -
+
+Exc1904::Exc1904( const ScDocument& rDoc )
+{
+ const Date& rDate = rDoc.GetFormatTable()->GetNullDate();
+ bVal = (rDate == Date( 1, 1, 1904 ));
+ bDateCompatibility = (rDate != Date( 30, 12, 1899 ));
+}
+
+sal_uInt16 Exc1904::GetNum() const
+{
+ return 0x0022;
+}
+
+void Exc1904::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
+
+ if( bISOIEC )
+ {
+ rStrm.WriteAttributes(XML_dateCompatibility, ToPsz(bDateCompatibility));
+ }
+
+ if( !bISOIEC || bDateCompatibility )
+ {
+ rStrm.WriteAttributes(XML_date1904, ToPsz(bVal));
+ }
+}
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+ExcBundlesheetBase::ExcBundlesheetBase( const RootData& rRootData, SCTAB nTabNum ) :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( rRootData.pER->GetTabInfo().IsVisibleTab( nTabNum ) ? 0x0000 : 0x0001 ),
+ nTab( nTabNum )
+{
+}
+
+ExcBundlesheetBase::ExcBundlesheetBase() :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( 0x0000 ),
+ nTab( SCTAB_GLOBAL )
+{
+}
+
+void ExcBundlesheetBase::UpdateStreamPos( XclExpStream& rStrm )
+{
+ rStrm.SetSvStreamPos( m_nOwnPos );
+ rStrm.DisableEncryption();
+ rStrm << static_cast<sal_uInt32>(m_nStrPos);
+ rStrm.EnableEncryption();
+}
+
+sal_uInt16 ExcBundlesheetBase::GetNum() const
+{
+ return 0x0085;
+}
+
+ExcBundlesheet::ExcBundlesheet( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, _nTab )
+{
+ OUString sTabName = rRootData.pER->GetTabInfo().GetScTabName( _nTab );
+ OSL_ENSURE( sTabName.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
+ aName = OUStringToOString(sTabName, rRootData.pER->GetTextEncoding());
+}
+
+void ExcBundlesheet::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ rStrm << sal_uInt32(0x00000000) // dummy (stream position of the sheet)
+ << nGrbit;
+ rStrm.WriteByteString(aName); // 8 bit length, max 255 chars
+}
+
+std::size_t ExcBundlesheet::GetLen() const
+{
+ return 7 + std::min( aName.getLength(), sal_Int32(255) );
+}
+
+//--------------------------------------------------------- class ExcDummy_02 -
+
+std::size_t ExcDummy_02a::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_02a::GetData() const
+{
+ return pMyData;
+}
+//--------------------------------------------------------- class ExcDummy_02 -
+
+XclExpCountry::XclExpCountry( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_COUNTRY, 4 )
+{
+ /* #i31530# set document country as UI country too -
+ needed for correct behaviour of number formats. */
+ mnUICountry = mnDocCountry = static_cast< sal_uInt16 >(
+ ::msfilter::ConvertLanguageToCountry( rRoot.GetDocLanguage() ) );
+}
+
+void XclExpCountry::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnUICountry << mnDocCountry;
+}
+
+// XclExpWsbool ===============================================================
+
+XclExpWsbool::XclExpWsbool( bool bFitToPages )
+ : XclExpUInt16Record( EXC_ID_WSBOOL, EXC_WSBOOL_DEFAULTFLAGS )
+{
+ if( bFitToPages )
+ SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE );
+}
+
+XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ) :
+ mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor) {}
+
+void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetPr,
+ // OOXTODO: XML_syncHorizontal,
+ // OOXTODO: XML_syncVertical,
+ // OOXTODO: XML_syncRef,
+ // OOXTODO: XML_transitionEvaluation,
+ // OOXTODO: XML_transitionEntry,
+ // OOXTODO: XML_published,
+ // OOXTODO: XML_codeName,
+ XML_filterMode, mpManager ? ToPsz(mpManager->HasFilterMode(mnScTab)) : nullptr
+ // OOXTODO: XML_enableFormatConditionsCalculation
+ );
+
+ // Note : the order of child elements is significant. Don't change the order.
+
+ // OOXTODO: XML_outlinePr
+
+ if (maTabColor != COL_AUTO)
+ rWorksheet->singleElement(XML_tabColor, XML_rgb, XclXmlUtils::ToOString(maTabColor));
+
+ rWorksheet->singleElement(XML_pageSetUpPr,
+ // OOXTODO: XML_autoPageBreaks,
+ XML_fitToPage, ToPsz(mbFitToPage));
+
+ rWorksheet->endElement( XML_sheetPr );
+}
+
+// XclExpWindowProtection ===============================================================
+
+XclExpWindowProtection::XclExpWindowProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_WINDOWPROTECT, bValue)
+{
+}
+
+void XclExpWindowProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_lockWindows, ToPsz(GetBool()));
+}
+
+// XclExpDocProtection ===============================================================
+
+XclExpProtection::XclExpProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_PROTECT, bValue)
+{
+}
+
+XclExpSheetProtection::XclExpSheetProtection(bool bValue, SCTAB nTab ) :
+ XclExpProtection( bValue),
+ mnTab(nTab)
+{
+}
+
+void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
+ if ( !pTabProtect )
+ return;
+
+ const ScOoxPasswordHash& rPH = pTabProtect->getPasswordHash();
+ // Do not write any hash attributes if there is no password.
+ ScOoxPasswordHash aPH;
+ if (rPH.hasPassword())
+ aPH = rPH;
+
+ Sequence<sal_Int8> aHash = pTabProtect->getPasswordHash(PASSHASH_XL);
+ OString sHash;
+ if (aHash.getLength() >= 2)
+ {
+ sHash = OString::number(
+ ( static_cast<sal_uInt8>(aHash[0]) << 8
+ | static_cast<sal_uInt8>(aHash[1]) ),
+ 16 );
+ }
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_sheetProtection,
+ XML_algorithmName, aPH.maAlgorithmName.isEmpty() ? nullptr : aPH.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, aPH.maHashValue.isEmpty() ? nullptr : aPH.maHashValue.toUtf8().getStr(),
+ XML_saltValue, aPH.maSaltValue.isEmpty() ? nullptr : aPH.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, aPH.mnSpinCount ? OString::number( aPH.mnSpinCount).getStr() : nullptr,
+ XML_sheet, ToPsz( true ),
+ XML_password, sHash.isEmpty()? nullptr : sHash.getStr(),
+ XML_objects, pTabProtect->isOptionEnabled( ScTableProtection::OBJECTS ) ? nullptr : ToPsz( true ),
+ XML_scenarios, pTabProtect->isOptionEnabled( ScTableProtection::SCENARIOS ) ? nullptr : ToPsz( true ),
+ XML_formatCells, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_CELLS ) ? ToPsz( false ) : nullptr,
+ XML_formatColumns, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_formatRows, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertColumns, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_insertRows, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertHyperlinks, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS ) ? ToPsz( false ) : nullptr,
+ XML_deleteColumns, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_deleteRows, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_selectLockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS ) ? nullptr : ToPsz( true ),
+ XML_sort, pTabProtect->isOptionEnabled( ScTableProtection::SORT ) ? ToPsz( false ) : nullptr,
+ XML_autoFilter, pTabProtect->isOptionEnabled( ScTableProtection::AUTOFILTER ) ? ToPsz( false ) : nullptr,
+ XML_pivotTables, pTabProtect->isOptionEnabled( ScTableProtection::PIVOT_TABLES ) ? ToPsz( false ) : nullptr,
+ XML_selectUnlockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS ) ? nullptr : ToPsz( true ) );
+
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ if (rProts.empty())
+ return;
+
+ rWorksheet->startElement(XML_protectedRanges);
+ for (const auto& rProt : rProts)
+ {
+ SAL_WARN_IF( rProt.maSecurityDescriptorXML.isEmpty() && !rProt.maSecurityDescriptor.empty(),
+ "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
+ rWorksheet->singleElement( XML_protectedRange,
+ XML_name, rProt.maTitle.isEmpty() ? nullptr : rProt.maTitle.toUtf8().getStr(),
+ XML_securityDescriptor, rProt.maSecurityDescriptorXML.isEmpty() ? nullptr : rProt.maSecurityDescriptorXML.toUtf8().getStr(),
+ /* XXX 'password' is not part of OOXML, but Excel2013
+ * writes it if loaded from BIFF, in which case
+ * 'algorithmName', 'hashValue', 'saltValue' and
+ * 'spinCount' are absent; so do we if it was present. */
+ XML_password, rProt.mnPasswordVerifier ? OString::number( rProt.mnPasswordVerifier, 16).getStr() : nullptr,
+ XML_algorithmName, rProt.maPasswordHash.maAlgorithmName.isEmpty() ? nullptr : rProt.maPasswordHash.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, rProt.maPasswordHash.maHashValue.isEmpty() ? nullptr : rProt.maPasswordHash.maHashValue.toUtf8().getStr(),
+ XML_saltValue, rProt.maPasswordHash.maSaltValue.isEmpty() ? nullptr : rProt.maPasswordHash.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, rProt.maPasswordHash.mnSpinCount ? OString::number( rProt.maPasswordHash.mnSpinCount).getStr() : nullptr,
+ XML_sqref, rProt.maRangeList.is() ? XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), *rProt.maRangeList).getStr() : nullptr);
+ }
+ rWorksheet->endElement( XML_protectedRanges);
+}
+
+XclExpPassHash::XclExpPassHash(const Sequence<sal_Int8>& aHash) :
+ XclExpRecord(EXC_ID_PASSWORD, 2),
+ mnHash(0x0000)
+{
+ if (aHash.getLength() >= 2)
+ {
+ mnHash = ((aHash[0] << 8) & 0xFFFF);
+ mnHash |= (aHash[1] & 0xFF);
+ }
+}
+
+XclExpPassHash::~XclExpPassHash()
+{
+}
+
+void XclExpPassHash::WriteBody(XclExpStream& rStrm)
+{
+ rStrm << mnHash;
+}
+
+XclExpFiltermode::XclExpFiltermode() :
+ XclExpEmptyRecord( EXC_ID_FILTERMODE )
+{
+}
+
+XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol ) :
+ XclExpUInt16Record( EXC_ID_AUTOFILTERINFO, static_cast< sal_uInt16 >( nScCol ) ),
+ maStartPos( rStartPos )
+{
+}
+
+ExcFilterCondition::ExcFilterCondition() :
+ nType( EXC_AFTYPE_NOTUSED ),
+ nOper( EXC_AFOPER_EQUAL )
+{
+}
+
+ExcFilterCondition::~ExcFilterCondition()
+{
+}
+
+std::size_t ExcFilterCondition::GetTextBytes() const
+{
+ return pText ? (1 + pText->GetBufferSize()) : 0;
+}
+
+void ExcFilterCondition::SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT )
+{
+ nType = nTp;
+ nOper = nOp;
+ pText.reset( pT ? new XclExpString( *pT, XclStrFlags::EightBitLength ) : nullptr);
+}
+
+void ExcFilterCondition::Save( XclExpStream& rStrm )
+{
+ rStrm << nType << nOper;
+ if (nType == EXC_AFTYPE_STRING)
+ {
+ OSL_ENSURE(pText, "ExcFilterCondition::Save() -- pText is NULL!");
+ rStrm << sal_uInt32(0) << static_cast<sal_uInt8>(pText->Len()) << sal_uInt16(0) << sal_uInt8(0);
+ }
+ else
+ rStrm << sal_uInt32(0) << sal_uInt32(0);
+}
+
+static const char* lcl_GetOperator( sal_uInt8 nOper )
+{
+ switch( nOper )
+ {
+ case EXC_AFOPER_EQUAL: return "equal";
+ case EXC_AFOPER_GREATER: return "greaterThan";
+ case EXC_AFOPER_GREATEREQUAL: return "greaterThanOrEqual";
+ case EXC_AFOPER_LESS: return "lessThan";
+ case EXC_AFOPER_LESSEQUAL: return "lessThanOrEqual";
+ case EXC_AFOPER_NOTEQUAL: return "notEqual";
+ case EXC_AFOPER_NONE:
+ default: return "**none**";
+ }
+}
+
+static OString lcl_GetValue( sal_uInt8 nType, const XclExpString* pStr )
+{
+ if (nType == EXC_AFTYPE_STRING)
+ return XclXmlUtils::ToOString(*pStr);
+ else
+ return OString();
+}
+
+void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( IsEmpty() )
+ return;
+
+ rStrm.GetCurrentStream()->singleElement( XML_customFilter,
+ XML_operator, lcl_GetOperator( nOper ),
+ XML_val, lcl_GetValue(nType, pText.get()) );
+}
+
+void ExcFilterCondition::SaveText( XclExpStream& rStrm )
+{
+ if( nType == EXC_AFTYPE_STRING )
+ {
+ OSL_ENSURE( pText, "ExcFilterCondition::SaveText() -- pText is NULL!" );
+ pText->WriteFlagField( rStrm );
+ pText->WriteBuffer( rStrm );
+ }
+}
+
+XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC ) :
+ XclExpRecord( EXC_ID_AUTOFILTER, 24 ),
+ XclExpRoot( rRoot ),
+ meType(FilterCondition),
+ nCol( nC ),
+ nFlags( 0 ),
+ bHasBlankValue( false )
+{
+}
+
+bool XclExpAutofilter::AddCondition( ScQueryConnect eConn, sal_uInt8 nType, sal_uInt8 nOp,
+ const OUString* pText, bool bSimple )
+{
+ if( !aCond[ 1 ].IsEmpty() )
+ return false;
+
+ sal_uInt16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1;
+
+ if( nInd == 1 )
+ nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND;
+ if( bSimple )
+ nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2;
+
+ aCond[ nInd ].SetCondition( nType, nOp, pText );
+
+ AddRecSize( aCond[ nInd ].GetTextBytes() );
+
+ return true;
+}
+
+bool XclExpAutofilter::HasCondition() const
+{
+ return !aCond[0].IsEmpty();
+}
+
+bool XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry )
+{
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+
+ if (rItems.empty())
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY)
+ {
+ // tdf#123353 XLSX export
+ meType = BlankValue;
+ return false;
+ }
+ // XLS export
+ return true;
+ }
+
+ if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ bool bConflict = false;
+ OUString sText;
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (!rItem.maString.isEmpty())
+ {
+ sText = rItem.maString.getString();
+ switch( rEntry.eOp )
+ {
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ {
+ sText = "*" + sText + "*";
+ }
+ break;
+ case SC_BEGINS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ sText += "*";
+ break;
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_END_WITH:
+ sText = "*" + sText;
+ break;
+ default:
+ {
+ //nothing
+ }
+ }
+ }
+
+ // empty/nonempty fields
+ if (rEntry.IsQueryByEmpty())
+ {
+ bConflict = !AddCondition(rEntry.eConnect, EXC_AFTYPE_EMPTY, EXC_AFOPER_NONE, nullptr, true);
+ bHasBlankValue = true;
+ }
+ else if(rEntry.IsQueryByNonEmpty())
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_NOTEMPTY, EXC_AFOPER_NONE, nullptr, true );
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ AddColorEntry(rEntry);
+ }
+ // other conditions
+ else
+ {
+ // top10 flags
+ sal_uInt16 nNewFlags = 0x0000;
+ switch( rEntry.eOp )
+ {
+ case SC_TOPVAL:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
+ break;
+ case SC_BOTVAL:
+ nNewFlags = EXC_AFFLAG_TOP10;
+ break;
+ case SC_TOPPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
+ break;
+ case SC_BOTPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
+ break;
+ default:;
+ }
+ bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
+
+ bConflict = HasTop10() && bNewTop10;
+ if( !bConflict )
+ {
+ if( bNewTop10 )
+ {
+ sal_uInt32 nIndex = 0;
+ double fVal = 0.0;
+ if (GetFormatter().IsNumberFormat(sText, nIndex, fVal))
+ {
+ if (fVal < 0) fVal = 0;
+ if (fVal >= 501) fVal = 500;
+ }
+ nFlags |= (nNewFlags | static_cast<sal_uInt16>(fVal) << 7);
+ }
+ // normal condition
+ else
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY && rEntry.eOp == SC_EQUAL)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ sal_uInt8 nOper = EXC_AFOPER_NONE;
+
+ switch( rEntry.eOp )
+ {
+ case SC_EQUAL: nOper = EXC_AFOPER_EQUAL; break;
+ case SC_LESS: nOper = EXC_AFOPER_LESS; break;
+ case SC_GREATER: nOper = EXC_AFOPER_GREATER; break;
+ case SC_LESS_EQUAL: nOper = EXC_AFOPER_LESSEQUAL; break;
+ case SC_GREATER_EQUAL: nOper = EXC_AFOPER_GREATEREQUAL; break;
+ case SC_NOT_EQUAL: nOper = EXC_AFOPER_NOTEQUAL; break;
+ case SC_CONTAINS:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ nOper = EXC_AFOPER_EQUAL; break;
+ case SC_DOES_NOT_CONTAIN:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ nOper = EXC_AFOPER_NOTEQUAL; break;
+ default:;
+ }
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_STRING, nOper, &sText);
+ }
+ }
+ }
+ return bConflict;
+}
+
+void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
+{
+ meType = MultiValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ if( rItem.maString.isEmpty() )
+ bHasBlankValue = true;
+ else
+ maMultiValues.push_back(std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate));
+ }
+}
+
+void XclExpAutofilter::AddColorEntry(const ScQueryEntry& rEntry)
+{
+ meType = ColorValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ maColorValues.push_back(
+ std::make_pair(rItem.maColor, rItem.meType == ScQueryEntry::ByBackgroundColor));
+ // Ensure that selected color(s) will be added to dxf: selection can be not in list
+ // of already added to dfx colors taken from filter range
+ if (GetDxfs().GetDxfByColor(rItem.maColor) == -1)
+ GetDxfs().AddColor(rItem.maColor);
+ }
+}
+
+void XclExpAutofilter::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << nCol << nFlags;
+ aCond[ 0 ].Save( rStrm );
+ aCond[ 1 ].Save( rStrm );
+ aCond[ 0 ].SaveText( rStrm );
+ aCond[ 1 ].SaveText( rStrm );
+}
+
+void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (meType == FilterCondition && !HasCondition() && !HasTop10())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_filterColumn,
+ XML_colId, OString::number(nCol)
+ // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow?
+ // OOXTODO: XML_showButton
+ );
+
+ switch (meType)
+ {
+ case FilterCondition:
+ {
+ if( HasTop10() )
+ {
+ rWorksheet->singleElement( XML_top10,
+ XML_top, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10TOP ) ),
+ XML_percent, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10PERC ) ),
+ XML_val, OString::number(nFlags >> 7)
+ // OOXTODO: XML_filterVal
+ );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_customFilters, XML_and,
+ ToPsz((nFlags & EXC_AFFLAG_ANDORMASK) == EXC_AFFLAG_AND));
+ aCond[0].SaveXml(rStrm);
+ aCond[1].SaveXml(rStrm);
+ rWorksheet->endElement(XML_customFilters);
+ }
+ // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
+ }
+ break;
+ case ColorValue:
+ {
+ if (!maColorValues.empty())
+ {
+ Color color = maColorValues[0].first;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ if (maColorValues[0].second) // is background color
+ {
+ pAttrList->add(XML_cellColor, OString::number(1));
+ }
+ else
+ {
+ pAttrList->add(XML_cellColor, OString::number(0));
+ }
+ pAttrList->add(XML_dxfId, OString::number(GetDxfs().GetDxfByColor(color)));
+ rWorksheet->singleElement(XML_colorFilter, pAttrList);
+ }
+ }
+ break;
+ case BlankValue:
+ {
+ rWorksheet->singleElement(XML_filters, XML_blank, "1");
+ }
+ break;
+ case MultiValue:
+ {
+ if( bHasBlankValue )
+ rWorksheet->startElement(XML_filters, XML_blank, "1");
+ else
+ rWorksheet->startElement(XML_filters);
+
+ for (const auto& rMultiValue : maMultiValues)
+ {
+ OString aStr = OUStringToOString(rMultiValue.first, RTL_TEXTENCODING_UTF8);
+ if( !rMultiValue.second )
+ {
+ const char* pz = aStr.getStr();
+ rWorksheet->singleElement(XML_filter, XML_val, pz);
+ }
+ else
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ sal_Int32 aDateGroup[3] = { XML_year, XML_month, XML_day };
+ sal_Int32 idx = 0;
+ for (size_t i = 0; idx >= 0 && i < 3; i++)
+ {
+ OString kw = aStr.getToken(0, '-', idx);
+ kw = kw.trim();
+ if (!kw.isEmpty())
+ {
+ pAttrList->add(aDateGroup[i], kw);
+ }
+ }
+ // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
+ // will be "day" as default, until date filter cannot handle HH:MM:SS.
+ pAttrList->add(XML_dateTimeGrouping, "day");
+ rWorksheet->singleElement(XML_dateGroupItem, pAttrList);
+ }
+ }
+ rWorksheet->endElement(XML_filters);
+ }
+ break;
+ }
+ rWorksheet->endElement( XML_filterColumn );
+}
+
+ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
+ XclExpRoot( rRoot ),
+ mbAutoFilter (false)
+{
+ XclExpNameManager& rNameMgr = GetNameManager();
+
+ bool bFound = false;
+ bool bAdvanced = false;
+ const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
+ ScRange aAdvRange;
+ if (pData)
+ {
+ bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
+ bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
+ }
+ if( !bFound )
+ return;
+
+ ScQueryParam aParam;
+ pData->GetQueryParam( aParam );
+
+ ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+ SCCOL nColCnt = aParam.nCol2 - aParam.nCol1 + 1;
+
+ maRef = aRange;
+
+ // #i2394# built-in defined names must be sorted by containing sheet name
+ if (!pDefinedData)
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
+
+ // advanced filter
+ if( bAdvanced )
+ {
+ // filter criteria, excel allows only same table
+ if( !pDefinedData && aAdvRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_CRITERIA, aAdvRange );
+
+ // filter destination range, excel allows only same table
+ if( !aParam.bInplace )
+ {
+ ScRange aDestRange( aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
+ aDestRange.aEnd.IncCol( nColCnt - 1 );
+ if( !pDefinedData && aDestRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_EXTRACT, aDestRange );
+ }
+
+ m_pFilterMode = new XclExpFiltermode;
+ }
+ // AutoFilter
+ else
+ {
+ bool bConflict = false;
+ bool bContLoop = true;
+ bool bHasOr = false;
+ SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField;
+
+ // create AUTOFILTER records for filtered columns
+ for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ )
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry( nEntry );
+
+ bContLoop = rEntry.bDoQuery;
+ if( bContLoop )
+ {
+ XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() );
+
+ if( nEntry > 0 )
+ bHasOr |= (rEntry.eConnect == SC_OR);
+
+ bConflict = (nEntry > 1) && bHasOr;
+ if( !bConflict )
+ bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
+ (nFirstField != rEntry.nField);
+ if( !bConflict )
+ bConflict = pFilter->AddEntry( rEntry );
+ }
+ }
+
+ // additional tests for conflicts
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos )
+ {
+ XclExpAutofilterRef xFilter = maFilterList.GetRecord( nPos );
+ bConflict = xFilter->HasCondition() && xFilter->HasTop10();
+ }
+
+ if( bConflict )
+ maFilterList.RemoveAllRecords();
+
+ if( !maFilterList.IsEmpty() )
+ m_pFilterMode = new XclExpFiltermode;
+ m_pFilterInfo = new XclExpAutofilterinfo( aRange.aStart, nColCnt );
+
+ if (maFilterList.IsEmpty () && !bConflict)
+ mbAutoFilter = true;
+
+ // get sort criteria
+ {
+ ScSortParam aSortParam;
+ pData->GetSortParam( aSortParam );
+
+ ScUserList* pList = ScGlobal::GetUserList();
+ if (aSortParam.bUserDef && pList && pList->size() > aSortParam.nUserIndex)
+ {
+ // get sorted area without headers
+ maSortRef = ScRange(
+ aParam.nCol1, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+
+ // get sorted columns with custom lists
+ const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
+
+ // get column index and sorting direction
+ SCCOLROW nField = 0;
+ bool bSortAscending=true;
+ for (const auto & rKey : aSortParam.maKeyState)
+ {
+ if (rKey.bDoSort)
+ {
+ nField = rKey.nField;
+ bSortAscending = rKey.bAscending;
+ break;
+ }
+ }
+
+ // remember sort criteria
+ const ScRange aSortedColumn(
+ nField, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ nField, aParam.nRow2, aParam.nTab );
+ const OUString aItemList = rData.GetString();
+
+ maSortCustomList.emplace_back(aSortedColumn, aItemList, !bSortAscending);
+ }
+ }
+ }
+}
+
+ExcAutoFilterRecs::~ExcAutoFilterRecs()
+{
+}
+
+XclExpAutofilter* ExcAutoFilterRecs::GetByCol( SCCOL nCol )
+{
+ XclExpAutofilterRef xFilter;
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ {
+ xFilter = maFilterList.GetRecord( nPos );
+ if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return xFilter.get();
+ }
+ xFilter = new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16>(nCol) );
+ maFilterList.AppendRecord( xFilter );
+ return xFilter.get();
+}
+
+bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol )
+{
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ if( maFilterList.GetRecord( nPos )->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return true;
+ return false;
+}
+
+void ExcAutoFilterRecs::AddObjRecs()
+{
+ if( m_pFilterInfo )
+ {
+ ScAddress aAddr( m_pFilterInfo->GetStartPos() );
+ for( SCCOL nObj = 0, nCount = m_pFilterInfo->GetColCount(); nObj < nCount; nObj++ )
+ {
+ std::unique_ptr<XclObj> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr, IsFiltered( nObj ) ));
+ GetObjectManager().AddObj( std::move(pObjRec) );
+ aAddr.IncCol();
+ }
+ }
+}
+
+void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
+{
+ if( m_pFilterMode )
+ m_pFilterMode->Save( rStrm );
+ if( m_pFilterInfo )
+ m_pFilterInfo->Save( rStrm );
+ maFilterList.Save( rStrm );
+}
+
+void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFilterList.IsEmpty() && !mbAutoFilter )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_autoFilter, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRef));
+ // OOXTODO: XML_extLst, XML_sortState
+ if( !maFilterList.IsEmpty() )
+ maFilterList.SaveXml( rStrm );
+
+ if (!maSortCustomList.empty())
+ {
+ rWorksheet->startElement(XML_sortState, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSortRef));
+
+ for (const auto & rSortCriteria : maSortCustomList)
+ {
+ if (std::get<2>(rSortCriteria))
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_descending, "1",
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ else
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ }
+
+ rWorksheet->endElement(XML_sortState);
+ }
+
+ rWorksheet->endElement( XML_autoFilter );
+}
+
+bool ExcAutoFilterRecs::HasFilterMode() const
+{
+ return m_pFilterMode != nullptr;
+}
+
+XclExpFilterManager::XclExpFilterManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpFilterManager::InitTabFilter( SCTAB nScTab )
+{
+ maFilterMap[ nScTab ] = new ExcAutoFilterRecs( GetRoot(), nScTab, nullptr );
+}
+
+XclExpRecordRef XclExpFilterManager::CreateRecord( SCTAB nScTab )
+{
+ XclExpTabFilterRef xRec;
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ xRec = aIt->second;
+ xRec->AddObjRecs();
+ }
+ return xRec;
+}
+
+bool XclExpFilterManager::HasFilterMode( SCTAB nScTab )
+{
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ return aIt->second->HasFilterMode();
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */