diff options
Diffstat (limited to 'sc/source/filter/excel/excrecds.cxx')
-rw-r--r-- | sc/source/filter/excel/excrecds.cxx | 1177 |
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: */ |