1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <excrecds.hxx>
23 #include <filter/msfilter/countryid.hxx>
25 #include <svl/numformat.hxx>
26 #include <sal/log.hxx>
27 #include <sax/fastattribs.hxx>
32 #include <document.hxx>
34 #include <oox/export/utils.hxx>
35 #include <oox/token/tokens.hxx>
36 #include <queryentry.hxx>
37 #include <queryparam.hxx>
38 #include <sortparam.hxx>
39 #include <userlist.hxx>
42 #include <xeescher.hxx>
46 #include <xestyle.hxx>
48 #include <xcl97rec.hxx>
49 #include <tabprotection.hxx>
50 #include <scitems.hxx>
53 using namespace ::oox
;
55 using ::com::sun::star::uno::Sequence
;
57 //--------------------------------------------------------- class ExcDummy_00 -
58 const sal_uInt8
ExcDummy_00::pMyData
[] = {
59 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
60 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
61 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
62 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
64 const std::size_t ExcDummy_00::nMyLen
= sizeof( ExcDummy_00::pMyData
);
66 //-------------------------------------------------------- class ExcDummy_04x -
67 const sal_uInt8
ExcDummy_040::pMyData
[] = {
68 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
69 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
71 const std::size_t ExcDummy_040::nMyLen
= sizeof( ExcDummy_040::pMyData
);
73 const sal_uInt8
ExcDummy_041::pMyData
[] = {
74 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
75 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
77 const std::size_t ExcDummy_041::nMyLen
= sizeof( ExcDummy_041::pMyData
);
79 //-------------------------------------------------------- class ExcDummy_02a -
80 const sal_uInt8
ExcDummy_02a::pMyData
[] = {
81 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
82 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
83 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
84 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
85 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
87 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
89 const std::size_t ExcDummy_02a::nMyLen
= sizeof( ExcDummy_02a::pMyData
);
91 //----------------------------------------------------------- class ExcRecord -
93 void ExcRecord::Save( XclExpStream
& rStrm
)
95 SetRecHeader( GetNum(), GetLen() );
96 XclExpRecord::Save( rStrm
);
99 void ExcRecord::SaveCont( XclExpStream
& /*rStrm*/ )
103 void ExcRecord::WriteBody( XclExpStream
& rStrm
)
108 void ExcRecord::SaveXml( XclExpXmlStream
& /*rStrm*/ )
112 //--------------------------------------------------------- class ExcEmptyRec -
114 void ExcEmptyRec::Save( XclExpStream
& /*rStrm*/ )
118 sal_uInt16
ExcEmptyRec::GetNum() const
123 std::size_t ExcEmptyRec::GetLen() const
128 //--------------------------------------------------------- class ExcDummyRec -
130 void ExcDummyRec::Save( XclExpStream
& rStrm
)
132 rStrm
.Write( GetData(), GetLen() ); // raw write mode
135 sal_uInt16
ExcDummyRec::GetNum() const
140 //------------------------------------------------------- class ExcBoolRecord -
142 void ExcBoolRecord::SaveCont( XclExpStream
& rStrm
)
144 rStrm
<< static_cast<sal_uInt16
>(bVal
? 0x0001 : 0x0000);
147 std::size_t ExcBoolRecord::GetLen() const
152 //--------------------------------------------------------- class ExcBof_Base -
154 ExcBof_Base::ExcBof_Base()
157 , nRupBuild(0x096C) // copied from Excel
158 , nRupYear(0x07C9) // copied from Excel
162 //-------------------------------------------------------------- class ExcBof -
170 void ExcBof::SaveCont( XclExpStream
& rStrm
)
172 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
175 sal_uInt16
ExcBof::GetNum() const
180 std::size_t ExcBof::GetLen() const
185 //------------------------------------------------------------- class ExcBofW -
193 void ExcBofW::SaveCont( XclExpStream
& rStrm
)
195 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
198 sal_uInt16
ExcBofW::GetNum() const
203 std::size_t ExcBofW::GetLen() const
208 //-------------------------------------------------------------- class ExcEof -
210 sal_uInt16
ExcEof::GetNum() const
215 std::size_t ExcEof::GetLen() const
220 //--------------------------------------------------------- class ExcDummy_00 -
222 std::size_t ExcDummy_00::GetLen() const
227 const sal_uInt8
* ExcDummy_00::GetData() const
232 //-------------------------------------------------------- class ExcDummy_04x -
234 std::size_t ExcDummy_040::GetLen() const
239 const sal_uInt8
* ExcDummy_040::GetData() const
244 std::size_t ExcDummy_041::GetLen() const
249 const sal_uInt8
* ExcDummy_041::GetData() const
254 //------------------------------------------------------------- class Exc1904 -
256 Exc1904::Exc1904( const ScDocument
& rDoc
)
258 const Date
& rDate
= rDoc
.GetFormatTable()->GetNullDate();
259 bVal
= (rDate
== Date( 1, 1, 1904 ));
260 bDateCompatibility
= (rDate
!= Date( 30, 12, 1899 ));
263 sal_uInt16
Exc1904::GetNum() const
268 void Exc1904::SaveXml( XclExpXmlStream
& rStrm
)
270 bool bISOIEC
= ( rStrm
.getVersion() == oox::core::ISOIEC_29500_2008
);
274 rStrm
.WriteAttributes(XML_dateCompatibility
, ToPsz(bDateCompatibility
));
277 if( !bISOIEC
|| bDateCompatibility
)
279 rStrm
.WriteAttributes(XML_date1904
, ToPsz(bVal
));
283 //------------------------------------------------------ class ExcBundlesheet -
285 ExcBundlesheetBase::ExcBundlesheetBase( const RootData
& rRootData
, SCTAB nTabNum
) :
286 m_nStrPos( STREAM_SEEK_TO_END
),
287 m_nOwnPos( STREAM_SEEK_TO_END
),
288 nGrbit( rRootData
.pER
->GetTabInfo().IsVisibleTab( nTabNum
) ? 0x0000 : 0x0001 ),
293 ExcBundlesheetBase::ExcBundlesheetBase() :
294 m_nStrPos( STREAM_SEEK_TO_END
),
295 m_nOwnPos( STREAM_SEEK_TO_END
),
301 void ExcBundlesheetBase::UpdateStreamPos( XclExpStream
& rStrm
)
303 rStrm
.SetSvStreamPos( m_nOwnPos
);
304 rStrm
.DisableEncryption();
305 rStrm
<< static_cast<sal_uInt32
>(m_nStrPos
);
306 rStrm
.EnableEncryption();
309 sal_uInt16
ExcBundlesheetBase::GetNum() const
314 ExcBundlesheet::ExcBundlesheet( const RootData
& rRootData
, SCTAB _nTab
) :
315 ExcBundlesheetBase( rRootData
, _nTab
)
317 OUString sTabName
= rRootData
.pER
->GetTabInfo().GetScTabName( _nTab
);
318 OSL_ENSURE( sTabName
.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
319 aName
= OUStringToOString(sTabName
, rRootData
.pER
->GetTextEncoding());
322 void ExcBundlesheet::SaveCont( XclExpStream
& rStrm
)
324 m_nOwnPos
= rStrm
.GetSvStreamPos();
325 rStrm
<< sal_uInt32(0x00000000) // dummy (stream position of the sheet)
327 rStrm
.WriteByteString(aName
); // 8 bit length, max 255 chars
330 std::size_t ExcBundlesheet::GetLen() const
332 return 7 + std::min( aName
.getLength(), sal_Int32(255) );
335 //--------------------------------------------------------- class ExcDummy_02 -
337 std::size_t ExcDummy_02a::GetLen() const
342 const sal_uInt8
* ExcDummy_02a::GetData() const
346 //--------------------------------------------------------- class ExcDummy_02 -
348 XclExpCountry::XclExpCountry( const XclExpRoot
& rRoot
) :
349 XclExpRecord( EXC_ID_COUNTRY
, 4 )
351 /* #i31530# set document country as UI country too -
352 needed for correct behaviour of number formats. */
353 mnUICountry
= mnDocCountry
= static_cast< sal_uInt16
>(
354 ::msfilter::ConvertLanguageToCountry( rRoot
.GetDocLanguage() ) );
357 void XclExpCountry::WriteBody( XclExpStream
& rStrm
)
359 rStrm
<< mnUICountry
<< mnDocCountry
;
362 // XclExpWsbool ===============================================================
364 XclExpWsbool::XclExpWsbool( bool bFitToPages
)
365 : XclExpUInt16Record( EXC_ID_WSBOOL
, EXC_WSBOOL_DEFAULTFLAGS
)
368 SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE
);
371 XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages
, SCTAB nScTab
, const Color
& rTabColor
, bool bSummaryBelow
, XclExpFilterManager
* pManager
) :
372 mnScTab(nScTab
), mpManager(pManager
), mbFitToPage(bFitToPages
), maTabColor(rTabColor
), mbSummaryBelow(bSummaryBelow
) {}
374 void XclExpXmlSheetPr::SaveXml( XclExpXmlStream
& rStrm
)
376 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
377 rWorksheet
->startElement( XML_sheetPr
,
378 // OOXTODO: XML_syncHorizontal,
379 // OOXTODO: XML_syncVertical,
380 // OOXTODO: XML_syncRef,
381 // OOXTODO: XML_transitionEvaluation,
382 // OOXTODO: XML_transitionEntry,
383 // OOXTODO: XML_published,
384 // OOXTODO: XML_codeName,
385 XML_filterMode
, mpManager
? ToPsz(mpManager
->HasFilterMode(mnScTab
)) : nullptr
386 // OOXTODO: XML_enableFormatConditionsCalculation
389 // Note : the order of child elements is significant. Don't change the order.
391 if (maTabColor
!= COL_AUTO
)
392 rWorksheet
->singleElement(XML_tabColor
, XML_rgb
, XclXmlUtils::ToOString(maTabColor
));
394 // OOXTODO: XML_outlinePr --> XML_applyStyles, XML_showOutlineSymbols, XML_summaryBelow, XML_summaryRight
396 rWorksheet
->singleElement(XML_outlinePr
, XML_summaryBelow
, "0");
398 rWorksheet
->singleElement(XML_pageSetUpPr
,
399 // OOXTODO: XML_autoPageBreaks,
400 XML_fitToPage
, ToPsz(mbFitToPage
));
402 rWorksheet
->endElement( XML_sheetPr
);
405 // XclExpWindowProtection ===============================================================
407 XclExpWindowProtection::XclExpWindowProtection(bool bValue
) :
408 XclExpBoolRecord(EXC_ID_WINDOWPROTECT
, bValue
)
412 void XclExpWindowProtection::SaveXml( XclExpXmlStream
& rStrm
)
414 rStrm
.WriteAttributes(XML_lockWindows
, ToPsz(GetBool()));
417 // XclExpDocProtection ===============================================================
419 XclExpProtection::XclExpProtection(bool bValue
) :
420 XclExpBoolRecord(EXC_ID_PROTECT
, bValue
)
424 XclExpSheetProtection::XclExpSheetProtection(bool bValue
, SCTAB nTab
) :
425 XclExpProtection( bValue
),
430 void XclExpSheetProtection::SaveXml( XclExpXmlStream
& rStrm
)
432 ScDocument
& rDoc
= rStrm
.GetRoot().GetDoc();
433 const ScTableProtection
* pTabProtect
= rDoc
.GetTabProtection(mnTab
);
437 const ScOoxPasswordHash
& rPH
= pTabProtect
->getPasswordHash();
438 // Do not write any hash attributes if there is no password.
439 ScOoxPasswordHash aPH
;
440 if (rPH
.hasPassword())
443 Sequence
<sal_Int8
> aHash
= pTabProtect
->getPasswordHash(PASSHASH_XL
);
444 std::optional
<OString
> sHash
;
445 if (aHash
.getLength() >= 2)
447 sHash
= OString::number(
448 ( static_cast<sal_uInt8
>(aHash
[0]) << 8
449 | static_cast<sal_uInt8
>(aHash
[1]) ),
452 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
453 rWorksheet
->singleElement( XML_sheetProtection
,
454 XML_algorithmName
, sax_fastparser::UseIf(aPH
.maAlgorithmName
, !aPH
.maAlgorithmName
.isEmpty()),
455 XML_hashValue
, sax_fastparser::UseIf(aPH
.maHashValue
, !aPH
.maHashValue
.isEmpty()),
456 XML_saltValue
, sax_fastparser::UseIf(aPH
.maSaltValue
, !aPH
.maSaltValue
.isEmpty()),
457 XML_spinCount
, sax_fastparser::UseIf(OString::number(aPH
.mnSpinCount
), aPH
.mnSpinCount
!= 0),
458 XML_sheet
, ToPsz( true ),
460 XML_objects
, pTabProtect
->isOptionEnabled( ScTableProtection::OBJECTS
) ? nullptr : ToPsz( true ),
461 XML_scenarios
, pTabProtect
->isOptionEnabled( ScTableProtection::SCENARIOS
) ? nullptr : ToPsz( true ),
462 XML_formatCells
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_CELLS
) ? ToPsz( false ) : nullptr,
463 XML_formatColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS
) ? ToPsz( false ) : nullptr,
464 XML_formatRows
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_ROWS
) ? ToPsz( false ) : nullptr,
465 XML_insertColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_COLUMNS
) ? ToPsz( false ) : nullptr,
466 XML_insertRows
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_ROWS
) ? ToPsz( false ) : nullptr,
467 XML_insertHyperlinks
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS
) ? ToPsz( false ) : nullptr,
468 XML_deleteColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_COLUMNS
) ? ToPsz( false ) : nullptr,
469 XML_deleteRows
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_ROWS
) ? ToPsz( false ) : nullptr,
470 XML_selectLockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS
) ? nullptr : ToPsz( true ),
471 XML_sort
, pTabProtect
->isOptionEnabled( ScTableProtection::SORT
) ? ToPsz( false ) : nullptr,
472 XML_autoFilter
, pTabProtect
->isOptionEnabled( ScTableProtection::AUTOFILTER
) ? ToPsz( false ) : nullptr,
473 XML_pivotTables
, pTabProtect
->isOptionEnabled( ScTableProtection::PIVOT_TABLES
) ? ToPsz( false ) : nullptr,
474 XML_selectUnlockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS
) ? nullptr : ToPsz( true ) );
476 const ::std::vector
<ScEnhancedProtection
>& rProts( pTabProtect
->getEnhancedProtection());
480 rWorksheet
->startElement(XML_protectedRanges
);
481 for (const auto& rProt
: rProts
)
483 if (!rProt
.maRangeList
.is())
484 continue; // Excel refuses to open if sqref is missing from a protectedRange
486 SAL_WARN_IF( rProt
.maSecurityDescriptorXML
.isEmpty() && !rProt
.maSecurityDescriptor
.empty(),
487 "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
488 rWorksheet
->singleElement( XML_protectedRange
,
489 XML_name
, sax_fastparser::UseIf(rProt
.maTitle
, !rProt
.maTitle
.isEmpty()),
490 XML_securityDescriptor
, sax_fastparser::UseIf(rProt
.maSecurityDescriptorXML
, !rProt
.maSecurityDescriptorXML
.isEmpty()),
491 /* XXX 'password' is not part of OOXML, but Excel2013
492 * writes it if loaded from BIFF, in which case
493 * 'algorithmName', 'hashValue', 'saltValue' and
494 * 'spinCount' are absent; so do we if it was present. */
495 XML_password
, sax_fastparser::UseIf(OString::number(rProt
.mnPasswordVerifier
, 16), rProt
.mnPasswordVerifier
!= 0),
496 XML_algorithmName
, sax_fastparser::UseIf(rProt
.maPasswordHash
.maAlgorithmName
, !rProt
.maPasswordHash
.maAlgorithmName
.isEmpty()),
497 XML_hashValue
, sax_fastparser::UseIf(rProt
.maPasswordHash
.maHashValue
, !rProt
.maPasswordHash
.maHashValue
.isEmpty()),
498 XML_saltValue
, sax_fastparser::UseIf(rProt
.maPasswordHash
.maSaltValue
, !rProt
.maPasswordHash
.maSaltValue
.isEmpty()),
499 XML_spinCount
, sax_fastparser::UseIf(OString::number(rProt
.maPasswordHash
.mnSpinCount
), rProt
.maPasswordHash
.mnSpinCount
!= 0),
500 XML_sqref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), *rProt
.maRangeList
).getStr());
502 rWorksheet
->endElement( XML_protectedRanges
);
505 XclExpPassHash::XclExpPassHash(const Sequence
<sal_Int8
>& aHash
) :
506 XclExpRecord(EXC_ID_PASSWORD
, 2),
509 if (aHash
.getLength() >= 2)
511 mnHash
= ((aHash
[0] << 8) & 0xFFFF);
512 mnHash
|= (aHash
[1] & 0xFF);
516 XclExpPassHash::~XclExpPassHash()
520 void XclExpPassHash::WriteBody(XclExpStream
& rStrm
)
525 XclExpFiltermode::XclExpFiltermode() :
526 XclExpEmptyRecord( EXC_ID_FILTERMODE
)
530 XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress
& rStartPos
, SCCOL nScCol
) :
531 XclExpUInt16Record( EXC_ID_AUTOFILTERINFO
, static_cast< sal_uInt16
>( nScCol
) ),
532 maStartPos( rStartPos
)
536 ExcFilterCondition::ExcFilterCondition() :
537 nType( EXC_AFTYPE_NOTUSED
),
538 nOper( EXC_AFOPER_EQUAL
)
542 ExcFilterCondition::~ExcFilterCondition()
546 std::size_t ExcFilterCondition::GetTextBytes() const
548 return pText
? (1 + pText
->GetBufferSize()) : 0;
551 void ExcFilterCondition::SetCondition( sal_uInt8 nTp
, sal_uInt8 nOp
, const OUString
* pT
)
555 pText
.reset( pT
? new XclExpString( *pT
, XclStrFlags::EightBitLength
) : nullptr);
558 void ExcFilterCondition::Save( XclExpStream
& rStrm
)
560 rStrm
<< nType
<< nOper
;
561 if (nType
== EXC_AFTYPE_STRING
)
563 OSL_ENSURE(pText
, "ExcFilterCondition::Save() -- pText is NULL!");
564 rStrm
<< sal_uInt32(0) << static_cast<sal_uInt8
>(pText
->Len()) << sal_uInt16(0) << sal_uInt8(0);
567 rStrm
<< sal_uInt32(0) << sal_uInt32(0);
570 static const char* lcl_GetOperator( sal_uInt8 nOper
)
574 case EXC_AFOPER_EQUAL
: return "equal";
575 case EXC_AFOPER_GREATER
: return "greaterThan";
576 case EXC_AFOPER_GREATEREQUAL
: return "greaterThanOrEqual";
577 case EXC_AFOPER_LESS
: return "lessThan";
578 case EXC_AFOPER_LESSEQUAL
: return "lessThanOrEqual";
579 case EXC_AFOPER_NOTEQUAL
: return "notEqual";
580 case EXC_AFOPER_NONE
:
581 default: return "**none**";
585 static OString
lcl_GetValue( sal_uInt8 nType
, const XclExpString
* pStr
)
587 if (nType
== EXC_AFTYPE_STRING
)
588 return XclXmlUtils::ToOString(*pStr
);
593 void ExcFilterCondition::SaveXml( XclExpXmlStream
& rStrm
)
598 rStrm
.GetCurrentStream()->singleElement( XML_customFilter
,
599 XML_operator
, lcl_GetOperator( nOper
),
600 XML_val
, lcl_GetValue(nType
, pText
.get()) );
603 void ExcFilterCondition::SaveText( XclExpStream
& rStrm
)
605 if( nType
== EXC_AFTYPE_STRING
)
607 OSL_ENSURE( pText
, "ExcFilterCondition::SaveText() -- pText is NULL!" );
608 pText
->WriteFlagField( rStrm
);
609 pText
->WriteBuffer( rStrm
);
613 XclExpAutofilter::XclExpAutofilter( const XclExpRoot
& rRoot
, sal_uInt16 nC
, bool bIsEmpty
) :
614 XclExpRecord( EXC_ID_AUTOFILTER
, 24 ),
616 meType(bIsEmpty
? Empty
: FilterCondition
),
618 bIsButtonHidden( false ),
620 bHasBlankValue( false )
624 bool XclExpAutofilter::AddCondition( ScQueryConnect eConn
, sal_uInt8 nType
, sal_uInt8 nOp
,
625 const OUString
* pText
, bool bSimple
)
627 if( !aCond
[ 1 ].IsEmpty() )
630 sal_uInt16 nInd
= aCond
[ 0 ].IsEmpty() ? 0 : 1;
633 nFlags
|= (eConn
== SC_OR
) ? EXC_AFFLAG_OR
: EXC_AFFLAG_AND
;
635 nFlags
|= (nInd
== 0) ? EXC_AFFLAG_SIMPLE1
: EXC_AFFLAG_SIMPLE2
;
637 aCond
[ nInd
].SetCondition( nType
, nOp
, pText
);
639 AddRecSize( aCond
[ nInd
].GetTextBytes() );
644 bool XclExpAutofilter::HasCondition() const
646 return !aCond
[0].IsEmpty();
649 bool XclExpAutofilter::AddEntry( const ScQueryEntry
& rEntry
)
651 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
655 if (GetOutput() != EXC_OUTPUT_BINARY
)
657 // tdf#123353 XLSX export
665 if (GetOutput() != EXC_OUTPUT_BINARY
&& rItems
.size() > 1)
667 AddMultiValueEntry(rEntry
);
671 bool bConflict
= false;
673 const ScQueryEntry::Item
& rItem
= rItems
[0];
674 if (!rItem
.maString
.isEmpty())
676 sText
= rItem
.maString
.getString();
680 case SC_DOES_NOT_CONTAIN
:
682 sText
= "*" + sText
+ "*";
686 case SC_DOES_NOT_BEGIN_WITH
:
690 case SC_DOES_NOT_END_WITH
:
700 // empty/nonempty fields
701 if (rEntry
.IsQueryByEmpty())
703 bConflict
= !AddCondition(rEntry
.eConnect
, EXC_AFTYPE_EMPTY
, EXC_AFOPER_NONE
, nullptr, true);
704 bHasBlankValue
= true;
706 else if(rEntry
.IsQueryByNonEmpty())
707 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_NOTEMPTY
, EXC_AFOPER_NONE
, nullptr, true );
708 else if (rEntry
.IsQueryByTextColor() || rEntry
.IsQueryByBackgroundColor())
710 AddColorEntry(rEntry
);
716 sal_uInt16 nNewFlags
= 0x0000;
720 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
);
723 nNewFlags
= EXC_AFFLAG_TOP10
;
726 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
| EXC_AFFLAG_TOP10PERC
);
729 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10PERC
);
733 bool bNewTop10
= ::get_flag( nNewFlags
, EXC_AFFLAG_TOP10
);
735 bConflict
= HasTop10() && bNewTop10
;
740 sal_uInt32 nIndex
= 0;
742 if (GetFormatter().IsNumberFormat(sText
, nIndex
, fVal
))
744 if (fVal
< 0) fVal
= 0;
745 if (fVal
>= 501) fVal
= 500;
747 nFlags
|= (nNewFlags
| static_cast<sal_uInt16
>(fVal
) << 7);
752 if (GetOutput() != EXC_OUTPUT_BINARY
&& rEntry
.eOp
== SC_EQUAL
)
754 AddMultiValueEntry(rEntry
);
758 sal_uInt8 nOper
= EXC_AFOPER_NONE
;
762 case SC_EQUAL
: nOper
= EXC_AFOPER_EQUAL
; break;
763 case SC_LESS
: nOper
= EXC_AFOPER_LESS
; break;
764 case SC_GREATER
: nOper
= EXC_AFOPER_GREATER
; break;
765 case SC_LESS_EQUAL
: nOper
= EXC_AFOPER_LESSEQUAL
; break;
766 case SC_GREATER_EQUAL
: nOper
= EXC_AFOPER_GREATEREQUAL
; break;
767 case SC_NOT_EQUAL
: nOper
= EXC_AFOPER_NOTEQUAL
; break;
771 nOper
= EXC_AFOPER_EQUAL
; break;
772 case SC_DOES_NOT_CONTAIN
:
773 case SC_DOES_NOT_BEGIN_WITH
:
774 case SC_DOES_NOT_END_WITH
:
775 nOper
= EXC_AFOPER_NOTEQUAL
; break;
778 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_STRING
, nOper
, &sText
);
785 void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry
& rEntry
)
788 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
789 for (const auto& rItem
: rItems
)
791 if( rItem
.maString
.isEmpty() )
792 bHasBlankValue
= true;
794 maMultiValues
.push_back(std::make_pair(rItem
.maString
.getString(), rItem
.meType
== ScQueryEntry::ByDate
));
798 void XclExpAutofilter::AddColorEntry(const ScQueryEntry
& rEntry
)
801 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
802 for (const auto& rItem
: rItems
)
804 maColorValues
.push_back(
805 std::make_pair(rItem
.maColor
, rItem
.meType
== ScQueryEntry::ByBackgroundColor
));
806 // Ensure that selected color(s) will be added to dxf: selection can be not in list
807 // of already added to dfx colors taken from filter range
808 if (GetDxfs().GetDxfByColor(rItem
.maColor
) == -1)
809 GetDxfs().addColor(rItem
.maColor
);
813 void XclExpAutofilter::WriteBody( XclExpStream
& rStrm
)
815 rStrm
<< nCol
<< nFlags
;
816 aCond
[ 0 ].Save( rStrm
);
817 aCond
[ 1 ].Save( rStrm
);
818 aCond
[ 0 ].SaveText( rStrm
);
819 aCond
[ 1 ].SaveText( rStrm
);
822 void XclExpAutofilter::SaveXml( XclExpXmlStream
& rStrm
)
824 if (meType
== FilterCondition
&& !HasCondition() && !HasTop10())
827 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
829 std::optional
<OString
> sHiddenButtonValue
;
831 sHiddenButtonValue
= "1";
833 rWorksheet
->startElement( XML_filterColumn
,
834 XML_colId
, OString::number(nCol
),
835 XML_hiddenButton
, sHiddenButtonValue
840 case FilterCondition
:
844 rWorksheet
->singleElement( XML_top10
,
845 XML_top
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10TOP
) ),
846 XML_percent
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10PERC
) ),
847 XML_val
, OString::number(nFlags
>> 7)
848 // OOXTODO: XML_filterVal
853 rWorksheet
->startElement(XML_customFilters
, XML_and
,
854 ToPsz((nFlags
& EXC_AFFLAG_ANDORMASK
) == EXC_AFFLAG_AND
));
855 aCond
[0].SaveXml(rStrm
);
856 aCond
[1].SaveXml(rStrm
);
857 rWorksheet
->endElement(XML_customFilters
);
859 // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
864 if (!maColorValues
.empty())
866 Color color
= maColorValues
[0].first
;
867 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
869 if (maColorValues
[0].second
) // is background color
871 pAttrList
->add(XML_cellColor
, OString::number(1));
875 pAttrList
->add(XML_cellColor
, OString::number(0));
877 pAttrList
->add(XML_dxfId
, OString::number(GetDxfs().GetDxfByColor(color
)));
878 rWorksheet
->singleElement(XML_colorFilter
, pAttrList
);
884 rWorksheet
->singleElement(XML_filters
, XML_blank
, "1");
890 rWorksheet
->startElement(XML_filters
, XML_blank
, "1");
892 rWorksheet
->startElement(XML_filters
);
894 for (const auto& rMultiValue
: maMultiValues
)
896 if( !rMultiValue
.second
)
898 rWorksheet
->singleElement(XML_filter
, XML_val
, rMultiValue
.first
);
902 OString aStr
= OUStringToOString(rMultiValue
.first
, RTL_TEXTENCODING_UTF8
);
903 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
904 sal_Int32 aDateGroup
[3] = { XML_year
, XML_month
, XML_day
};
906 for (size_t i
= 0; idx
>= 0 && i
< 3; i
++)
908 OString kw
= aStr
.getToken(0, '-', idx
);
912 pAttrList
->add(aDateGroup
[i
], kw
);
915 // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
916 // will be "day" as default, until date filter cannot handle HH:MM:SS.
917 pAttrList
->add(XML_dateTimeGrouping
, "day");
918 rWorksheet
->singleElement(XML_dateGroupItem
, pAttrList
);
921 rWorksheet
->endElement(XML_filters
);
924 // Used for constructing an empty filterColumn element for exporting the XML_hiddenButton attribute
927 rWorksheet
->endElement( XML_filterColumn
);
930 ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot
& rRoot
, SCTAB nTab
, const ScDBData
* pDefinedData
) :
934 XclExpNameManager
& rNameMgr
= GetNameManager();
937 bool bAdvanced
= false;
938 const ScDBData
* pData
= (pDefinedData
? pDefinedData
: rRoot
.GetDoc().GetAnonymousDBData(nTab
));
942 bAdvanced
= pData
->GetAdvancedQuerySource( aAdvRange
);
943 bFound
= (pData
->HasQueryParam() || pData
->HasAutoFilter() || bAdvanced
);
949 pData
->GetQueryParam( aParam
);
951 ScRange
aRange( aParam
.nCol1
, aParam
.nRow1
, aParam
.nTab
,
952 aParam
.nCol2
, aParam
.nRow2
, aParam
.nTab
);
954 SCCOL nColCnt
= aRange
.aEnd
.Col() - aRange
.aStart
.Col() + 1;
958 // #i2394# built-in defined names must be sorted by containing sheet name
960 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE
, aRange
);
965 // filter criteria, excel allows only same table
966 if( !pDefinedData
&& aAdvRange
.aStart
.Tab() == nTab
)
967 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_CRITERIA
, aAdvRange
);
969 // filter destination range, excel allows only same table
970 if( !aParam
.bInplace
)
972 ScRange
aDestRange( aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
973 aDestRange
.aEnd
.IncCol( nColCnt
- 1 );
974 if( !pDefinedData
&& aDestRange
.aStart
.Tab() == nTab
)
975 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_EXTRACT
, aDestRange
);
978 m_pFilterMode
= new XclExpFiltermode
;
983 bool bConflict
= false;
984 bool bContLoop
= true;
986 SCCOLROW nFirstField
= aParam
.GetEntry( 0 ).nField
;
987 ScDocument
& rDoc
= rRoot
.GetDoc();
988 SCROW nRow
= aRange
.aStart
.Row();
990 // create AUTOFILTER records for filtered columns
991 for( SCSIZE nEntry
= 0; !bConflict
&& bContLoop
&& (nEntry
< aParam
.GetEntryCount()); nEntry
++ )
993 const ScQueryEntry
& rEntry
= aParam
.GetEntry( nEntry
);
995 bContLoop
= rEntry
.bDoQuery
;
998 SCCOL nCol
= static_cast<SCCOL
>(rEntry
.nField
);
999 XclExpAutofilter
* pFilter
= GetByCol( nCol
- aRange
.aStart
.Col() );
1000 auto nFlag
= rDoc
.GetAttr( nCol
, nRow
, nTab
, ATTR_MERGE_FLAG
)->GetValue();
1001 bool bIsButtonHidden
= !( nFlag
& ScMF::Auto
);
1002 pFilter
->SetButtonHidden( bIsButtonHidden
);
1005 bHasOr
|= (rEntry
.eConnect
== SC_OR
);
1007 bConflict
= (nEntry
> 1) && bHasOr
;
1009 bConflict
= (nEntry
== 1) && (rEntry
.eConnect
== SC_OR
) &&
1010 (nFirstField
!= rEntry
.nField
);
1012 bConflict
= pFilter
->AddEntry( rEntry
);
1016 sal_uInt16 nColId
= 0;
1017 for ( auto nCol
= aRange
.aStart
.Col(); nCol
<= aRange
.aEnd
.Col(); nCol
++, nColId
++ )
1019 auto nFlag
= rDoc
.GetAttr( nCol
, nRow
, nTab
, ATTR_MERGE_FLAG
)->GetValue();
1020 bool bIsButtonHidden
= !( nFlag
& ScMF::Auto
);
1021 if ( bIsButtonHidden
)
1023 // Create filter column with hiddenButton=1 attribute if it doesn't exist
1024 XclExpAutofilterRef xFilter
;
1025 bool bFilterFound
= false;
1026 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
1028 xFilter
= maFilterList
.GetRecord( nPos
);
1029 if( xFilter
->GetCol() == static_cast<sal_uInt16
>(nCol
) )
1031 bFilterFound
= true;
1035 if ( !bFilterFound
)
1037 xFilter
= new XclExpAutofilter( GetRoot(), nColId
, /*bIsEmpty*/true );
1038 xFilter
->SetButtonHidden( true );
1039 maFilterList
.AppendRecord( xFilter
);
1044 // additional tests for conflicts
1045 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); !bConflict
&& (nPos
< nSize
); ++nPos
)
1047 XclExpAutofilterRef xFilter
= maFilterList
.GetRecord( nPos
);
1048 bConflict
= xFilter
->HasCondition() && xFilter
->HasTop10();
1052 maFilterList
.RemoveAllRecords();
1054 if( !maFilterList
.IsEmpty() )
1055 m_pFilterMode
= new XclExpFiltermode
;
1056 m_pFilterInfo
= new XclExpAutofilterinfo( aRange
.aStart
, nColCnt
);
1058 if (maFilterList
.IsEmpty () && !bConflict
)
1059 mbAutoFilter
= true;
1061 // get sort criteria
1063 ScSortParam aSortParam
;
1064 pData
->GetSortParam( aSortParam
);
1066 ScUserList
& rList
= ScGlobal::GetUserList();
1067 if (aSortParam
.bUserDef
&& rList
.size() > aSortParam
.nUserIndex
)
1069 // get sorted area without headers
1070 maSortRef
= ScRange(
1071 aParam
.nCol1
, aParam
.nRow1
+ (aSortParam
.bHasHeader
? 1 : 0), aParam
.nTab
,
1072 aParam
.nCol2
, aParam
.nRow2
, aParam
.nTab
);
1074 // get sorted columns with custom lists
1075 const ScUserListData
& rData
= rList
[aSortParam
.nUserIndex
];
1077 // get column index and sorting direction
1078 SCCOLROW nField
= 0;
1079 bool bSortAscending
=true;
1080 for (const auto & rKey
: aSortParam
.maKeyState
)
1084 nField
= rKey
.nField
;
1085 bSortAscending
= rKey
.bAscending
;
1090 // remember sort criteria
1091 const ScRange
aSortedColumn(
1092 nField
, aParam
.nRow1
+ (aSortParam
.bHasHeader
? 1 : 0), aParam
.nTab
,
1093 nField
, aParam
.nRow2
, aParam
.nTab
);
1094 const OUString aItemList
= rData
.GetString();
1096 maSortCustomList
.emplace_back(aSortedColumn
, aItemList
, !bSortAscending
);
1102 ExcAutoFilterRecs::~ExcAutoFilterRecs()
1106 XclExpAutofilter
* ExcAutoFilterRecs::GetByCol( SCCOL nCol
)
1108 XclExpAutofilterRef xFilter
;
1109 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
1111 xFilter
= maFilterList
.GetRecord( nPos
);
1112 if( xFilter
->GetCol() == static_cast<sal_uInt16
>(nCol
) )
1113 return xFilter
.get();
1115 xFilter
= new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16
>(nCol
) );
1116 maFilterList
.AppendRecord( xFilter
);
1117 return xFilter
.get();
1120 bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol
)
1122 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
1123 if( maFilterList
.GetRecord( nPos
)->GetCol() == static_cast<sal_uInt16
>(nCol
) )
1128 void ExcAutoFilterRecs::AddObjRecs()
1132 ScAddress
aAddr( m_pFilterInfo
->GetStartPos() );
1133 for( SCCOL nObj
= 0, nCount
= m_pFilterInfo
->GetColCount(); nObj
< nCount
; nObj
++ )
1135 std::unique_ptr
<XclObj
> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr
, IsFiltered( nObj
) ));
1136 GetObjectManager().AddObj( std::move(pObjRec
) );
1142 void ExcAutoFilterRecs::Save( XclExpStream
& rStrm
)
1145 m_pFilterMode
->Save( rStrm
);
1147 m_pFilterInfo
->Save( rStrm
);
1148 maFilterList
.Save( rStrm
);
1151 void ExcAutoFilterRecs::SaveXml( XclExpXmlStream
& rStrm
)
1153 if( maFilterList
.IsEmpty() && !mbAutoFilter
)
1156 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1157 rWorksheet
->startElement(XML_autoFilter
, XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), maRef
));
1158 // OOXTODO: XML_extLst, XML_sortState
1159 if( !maFilterList
.IsEmpty() )
1160 maFilterList
.SaveXml( rStrm
);
1162 if (!maSortCustomList
.empty())
1164 rWorksheet
->startElement(XML_sortState
, XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), maSortRef
));
1166 for (const auto & rSortCriteria
: maSortCustomList
)
1168 if (std::get
<2>(rSortCriteria
))
1169 rWorksheet
->singleElement(XML_sortCondition
,
1170 XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(),
1171 std::get
<0>(rSortCriteria
)),
1172 XML_descending
, "1",
1173 XML_customList
, std::get
<1>(rSortCriteria
));
1175 rWorksheet
->singleElement(XML_sortCondition
,
1176 XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(),
1177 std::get
<0>(rSortCriteria
)),
1178 XML_customList
, std::get
<1>(rSortCriteria
));
1181 rWorksheet
->endElement(XML_sortState
);
1184 rWorksheet
->endElement( XML_autoFilter
);
1187 bool ExcAutoFilterRecs::HasFilterMode() const
1189 return m_pFilterMode
!= nullptr;
1192 XclExpFilterManager::XclExpFilterManager( const XclExpRoot
& rRoot
) :
1197 void XclExpFilterManager::InitTabFilter( SCTAB nScTab
)
1199 maFilterMap
[ nScTab
] = new ExcAutoFilterRecs( GetRoot(), nScTab
, nullptr );
1202 XclExpRecordRef
XclExpFilterManager::CreateRecord( SCTAB nScTab
)
1204 XclExpTabFilterRef xRec
;
1205 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1206 if( aIt
!= maFilterMap
.end() )
1214 bool XclExpFilterManager::HasFilterMode( SCTAB nScTab
)
1216 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1217 if( aIt
!= maFilterMap
.end() )
1219 return aIt
->second
->HasFilterMode();
1224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */