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 <svl/zforlist.hxx>
27 #include <sal/log.hxx>
28 #include <sax/fastattribs.hxx>
33 #include <document.hxx>
35 #include <oox/export/utils.hxx>
36 #include <oox/token/tokens.hxx>
37 #include <queryentry.hxx>
38 #include <queryparam.hxx>
39 #include <sortparam.hxx>
40 #include <userlist.hxx>
43 #include <xeescher.hxx>
47 #include <xestyle.hxx>
49 #include <xcl97rec.hxx>
50 #include <tabprotection.hxx>
52 using namespace ::oox
;
54 using ::com::sun::star::uno::Sequence
;
56 //--------------------------------------------------------- class ExcDummy_00 -
57 const sal_uInt8
ExcDummy_00::pMyData
[] = {
58 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
59 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
60 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
61 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
63 const std::size_t ExcDummy_00::nMyLen
= sizeof( ExcDummy_00::pMyData
);
65 //-------------------------------------------------------- class ExcDummy_04x -
66 const sal_uInt8
ExcDummy_040::pMyData
[] = {
67 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
68 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
70 const std::size_t ExcDummy_040::nMyLen
= sizeof( ExcDummy_040::pMyData
);
72 const sal_uInt8
ExcDummy_041::pMyData
[] = {
73 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
74 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
76 const std::size_t ExcDummy_041::nMyLen
= sizeof( ExcDummy_041::pMyData
);
78 //-------------------------------------------------------- class ExcDummy_02a -
79 const sal_uInt8
ExcDummy_02a::pMyData
[] = {
80 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
81 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
82 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
83 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
84 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
86 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
88 const std::size_t ExcDummy_02a::nMyLen
= sizeof( ExcDummy_02a::pMyData
);
90 //----------------------------------------------------------- class ExcRecord -
92 void ExcRecord::Save( XclExpStream
& rStrm
)
94 SetRecHeader( GetNum(), GetLen() );
95 XclExpRecord::Save( rStrm
);
98 void ExcRecord::SaveCont( XclExpStream
& /*rStrm*/ )
102 void ExcRecord::WriteBody( XclExpStream
& rStrm
)
107 void ExcRecord::SaveXml( XclExpXmlStream
& /*rStrm*/ )
111 //--------------------------------------------------------- class ExcEmptyRec -
113 void ExcEmptyRec::Save( XclExpStream
& /*rStrm*/ )
117 sal_uInt16
ExcEmptyRec::GetNum() const
122 std::size_t ExcEmptyRec::GetLen() const
127 //--------------------------------------------------------- class ExcDummyRec -
129 void ExcDummyRec::Save( XclExpStream
& rStrm
)
131 rStrm
.Write( GetData(), GetLen() ); // raw write mode
134 sal_uInt16
ExcDummyRec::GetNum() const
139 //------------------------------------------------------- class ExcBoolRecord -
141 void ExcBoolRecord::SaveCont( XclExpStream
& rStrm
)
143 rStrm
<< static_cast<sal_uInt16
>(bVal
? 0x0001 : 0x0000);
146 std::size_t ExcBoolRecord::GetLen() const
151 //--------------------------------------------------------- class ExcBof_Base -
153 ExcBof_Base::ExcBof_Base()
156 , nRupBuild(0x096C) // copied from Excel
157 , nRupYear(0x07C9) // copied from Excel
161 //-------------------------------------------------------------- class ExcBof -
169 void ExcBof::SaveCont( XclExpStream
& rStrm
)
171 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
174 sal_uInt16
ExcBof::GetNum() const
179 std::size_t ExcBof::GetLen() const
184 //------------------------------------------------------------- class ExcBofW -
192 void ExcBofW::SaveCont( XclExpStream
& rStrm
)
194 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
197 sal_uInt16
ExcBofW::GetNum() const
202 std::size_t ExcBofW::GetLen() const
207 //-------------------------------------------------------------- class ExcEof -
209 sal_uInt16
ExcEof::GetNum() const
214 std::size_t ExcEof::GetLen() const
219 //--------------------------------------------------------- class ExcDummy_00 -
221 std::size_t ExcDummy_00::GetLen() const
226 const sal_uInt8
* ExcDummy_00::GetData() const
231 //-------------------------------------------------------- class ExcDummy_04x -
233 std::size_t ExcDummy_040::GetLen() const
238 const sal_uInt8
* ExcDummy_040::GetData() const
243 std::size_t ExcDummy_041::GetLen() const
248 const sal_uInt8
* ExcDummy_041::GetData() const
253 //------------------------------------------------------------- class Exc1904 -
255 Exc1904::Exc1904( const ScDocument
& rDoc
)
257 const Date
& rDate
= rDoc
.GetFormatTable()->GetNullDate();
258 bVal
= (rDate
== Date( 1, 1, 1904 ));
259 bDateCompatibility
= (rDate
!= Date( 30, 12, 1899 ));
262 sal_uInt16
Exc1904::GetNum() const
267 void Exc1904::SaveXml( XclExpXmlStream
& rStrm
)
269 bool bISOIEC
= ( rStrm
.getVersion() == oox::core::ISOIEC_29500_2008
);
273 rStrm
.WriteAttributes(XML_dateCompatibility
, ToPsz(bDateCompatibility
));
276 if( !bISOIEC
|| bDateCompatibility
)
278 rStrm
.WriteAttributes(XML_date1904
, ToPsz(bVal
));
282 //------------------------------------------------------ class ExcBundlesheet -
284 ExcBundlesheetBase::ExcBundlesheetBase( const RootData
& rRootData
, SCTAB nTabNum
) :
285 m_nStrPos( STREAM_SEEK_TO_END
),
286 m_nOwnPos( STREAM_SEEK_TO_END
),
287 nGrbit( rRootData
.pER
->GetTabInfo().IsVisibleTab( nTabNum
) ? 0x0000 : 0x0001 ),
292 ExcBundlesheetBase::ExcBundlesheetBase() :
293 m_nStrPos( STREAM_SEEK_TO_END
),
294 m_nOwnPos( STREAM_SEEK_TO_END
),
300 void ExcBundlesheetBase::UpdateStreamPos( XclExpStream
& rStrm
)
302 rStrm
.SetSvStreamPos( m_nOwnPos
);
303 rStrm
.DisableEncryption();
304 rStrm
<< static_cast<sal_uInt32
>(m_nStrPos
);
305 rStrm
.EnableEncryption();
308 sal_uInt16
ExcBundlesheetBase::GetNum() const
313 ExcBundlesheet::ExcBundlesheet( const RootData
& rRootData
, SCTAB _nTab
) :
314 ExcBundlesheetBase( rRootData
, _nTab
)
316 OUString sTabName
= rRootData
.pER
->GetTabInfo().GetScTabName( _nTab
);
317 OSL_ENSURE( sTabName
.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
318 aName
= OUStringToOString(sTabName
, rRootData
.pER
->GetTextEncoding());
321 void ExcBundlesheet::SaveCont( XclExpStream
& rStrm
)
323 m_nOwnPos
= rStrm
.GetSvStreamPos();
324 rStrm
<< sal_uInt32(0x00000000) // dummy (stream position of the sheet)
326 rStrm
.WriteByteString(aName
); // 8 bit length, max 255 chars
329 std::size_t ExcBundlesheet::GetLen() const
331 return 7 + std::min( aName
.getLength(), sal_Int32(255) );
334 //--------------------------------------------------------- class ExcDummy_02 -
336 std::size_t ExcDummy_02a::GetLen() const
341 const sal_uInt8
* ExcDummy_02a::GetData() const
345 //--------------------------------------------------------- class ExcDummy_02 -
347 XclExpCountry::XclExpCountry( const XclExpRoot
& rRoot
) :
348 XclExpRecord( EXC_ID_COUNTRY
, 4 )
350 /* #i31530# set document country as UI country too -
351 needed for correct behaviour of number formats. */
352 mnUICountry
= mnDocCountry
= static_cast< sal_uInt16
>(
353 ::msfilter::ConvertLanguageToCountry( rRoot
.GetDocLanguage() ) );
356 void XclExpCountry::WriteBody( XclExpStream
& rStrm
)
358 rStrm
<< mnUICountry
<< mnDocCountry
;
361 // XclExpWsbool ===============================================================
363 XclExpWsbool::XclExpWsbool( bool bFitToPages
)
364 : XclExpUInt16Record( EXC_ID_WSBOOL
, EXC_WSBOOL_DEFAULTFLAGS
)
367 SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE
);
370 XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages
, SCTAB nScTab
, const Color
& rTabColor
, XclExpFilterManager
* pManager
) :
371 mnScTab(nScTab
), mpManager(pManager
), mbFitToPage(bFitToPages
), maTabColor(rTabColor
) {}
373 void XclExpXmlSheetPr::SaveXml( XclExpXmlStream
& rStrm
)
375 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
376 rWorksheet
->startElement( XML_sheetPr
,
377 // OOXTODO: XML_syncHorizontal,
378 // OOXTODO: XML_syncVertical,
379 // OOXTODO: XML_syncRef,
380 // OOXTODO: XML_transitionEvaluation,
381 // OOXTODO: XML_transitionEntry,
382 // OOXTODO: XML_published,
383 // OOXTODO: XML_codeName,
384 XML_filterMode
, mpManager
? ToPsz(mpManager
->HasFilterMode(mnScTab
)) : nullptr
385 // OOXTODO: XML_enableFormatConditionsCalculation
388 // Note : the order of child elements is significant. Don't change the order.
390 // OOXTODO: XML_outlinePr
392 if (maTabColor
!= COL_AUTO
)
393 rWorksheet
->singleElement(XML_tabColor
, XML_rgb
, XclXmlUtils::ToOString(maTabColor
));
395 rWorksheet
->singleElement(XML_pageSetUpPr
,
396 // OOXTODO: XML_autoPageBreaks,
397 XML_fitToPage
, ToPsz(mbFitToPage
));
399 rWorksheet
->endElement( XML_sheetPr
);
402 // XclExpWindowProtection ===============================================================
404 XclExpWindowProtection::XclExpWindowProtection(bool bValue
) :
405 XclExpBoolRecord(EXC_ID_WINDOWPROTECT
, bValue
)
409 void XclExpWindowProtection::SaveXml( XclExpXmlStream
& rStrm
)
411 rStrm
.WriteAttributes(XML_lockWindows
, ToPsz(GetBool()));
414 // XclExpDocProtection ===============================================================
416 XclExpProtection::XclExpProtection(bool bValue
) :
417 XclExpBoolRecord(EXC_ID_PROTECT
, bValue
)
421 XclExpSheetProtection::XclExpSheetProtection(bool bValue
, SCTAB nTab
) :
422 XclExpProtection( bValue
),
427 void XclExpSheetProtection::SaveXml( XclExpXmlStream
& rStrm
)
429 ScDocument
& rDoc
= rStrm
.GetRoot().GetDoc();
430 const ScTableProtection
* pTabProtect
= rDoc
.GetTabProtection(mnTab
);
434 const ScOoxPasswordHash
& rPH
= pTabProtect
->getPasswordHash();
435 // Do not write any hash attributes if there is no password.
436 ScOoxPasswordHash aPH
;
437 if (rPH
.hasPassword())
440 Sequence
<sal_Int8
> aHash
= pTabProtect
->getPasswordHash(PASSHASH_XL
);
442 if (aHash
.getLength() >= 2)
444 sHash
= OString::number(
445 ( static_cast<sal_uInt8
>(aHash
[0]) << 8
446 | static_cast<sal_uInt8
>(aHash
[1]) ),
449 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
450 rWorksheet
->singleElement( XML_sheetProtection
,
451 XML_algorithmName
, aPH
.maAlgorithmName
.isEmpty() ? nullptr : aPH
.maAlgorithmName
.toUtf8().getStr(),
452 XML_hashValue
, aPH
.maHashValue
.isEmpty() ? nullptr : aPH
.maHashValue
.toUtf8().getStr(),
453 XML_saltValue
, aPH
.maSaltValue
.isEmpty() ? nullptr : aPH
.maSaltValue
.toUtf8().getStr(),
454 XML_spinCount
, aPH
.mnSpinCount
? OString::number( aPH
.mnSpinCount
).getStr() : nullptr,
455 XML_sheet
, ToPsz( true ),
456 XML_password
, sHash
.isEmpty()? nullptr : sHash
.getStr(),
457 XML_objects
, pTabProtect
->isOptionEnabled( ScTableProtection::OBJECTS
) ? nullptr : ToPsz( true ),
458 XML_scenarios
, pTabProtect
->isOptionEnabled( ScTableProtection::SCENARIOS
) ? nullptr : ToPsz( true ),
459 XML_formatCells
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_CELLS
) ? ToPsz( false ) : nullptr,
460 XML_formatColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS
) ? ToPsz( false ) : nullptr,
461 XML_formatRows
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_ROWS
) ? ToPsz( false ) : nullptr,
462 XML_insertColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_COLUMNS
) ? ToPsz( false ) : nullptr,
463 XML_insertRows
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_ROWS
) ? ToPsz( false ) : nullptr,
464 XML_insertHyperlinks
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS
) ? ToPsz( false ) : nullptr,
465 XML_deleteColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_COLUMNS
) ? ToPsz( false ) : nullptr,
466 XML_deleteRows
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_ROWS
) ? ToPsz( false ) : nullptr,
467 XML_selectLockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS
) ? nullptr : ToPsz( true ),
468 XML_sort
, pTabProtect
->isOptionEnabled( ScTableProtection::SORT
) ? ToPsz( false ) : nullptr,
469 XML_autoFilter
, pTabProtect
->isOptionEnabled( ScTableProtection::AUTOFILTER
) ? ToPsz( false ) : nullptr,
470 XML_pivotTables
, pTabProtect
->isOptionEnabled( ScTableProtection::PIVOT_TABLES
) ? ToPsz( false ) : nullptr,
471 XML_selectUnlockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS
) ? nullptr : ToPsz( true ) );
473 const ::std::vector
<ScEnhancedProtection
>& rProts( pTabProtect
->getEnhancedProtection());
477 rWorksheet
->startElement(XML_protectedRanges
);
478 for (const auto& rProt
: rProts
)
480 SAL_WARN_IF( rProt
.maSecurityDescriptorXML
.isEmpty() && !rProt
.maSecurityDescriptor
.empty(),
481 "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
482 rWorksheet
->singleElement( XML_protectedRange
,
483 XML_name
, rProt
.maTitle
.isEmpty() ? nullptr : rProt
.maTitle
.toUtf8().getStr(),
484 XML_securityDescriptor
, rProt
.maSecurityDescriptorXML
.isEmpty() ? nullptr : rProt
.maSecurityDescriptorXML
.toUtf8().getStr(),
485 /* XXX 'password' is not part of OOXML, but Excel2013
486 * writes it if loaded from BIFF, in which case
487 * 'algorithmName', 'hashValue', 'saltValue' and
488 * 'spinCount' are absent; so do we if it was present. */
489 XML_password
, rProt
.mnPasswordVerifier
? OString::number( rProt
.mnPasswordVerifier
, 16).getStr() : nullptr,
490 XML_algorithmName
, rProt
.maPasswordHash
.maAlgorithmName
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maAlgorithmName
.toUtf8().getStr(),
491 XML_hashValue
, rProt
.maPasswordHash
.maHashValue
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maHashValue
.toUtf8().getStr(),
492 XML_saltValue
, rProt
.maPasswordHash
.maSaltValue
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maSaltValue
.toUtf8().getStr(),
493 XML_spinCount
, rProt
.maPasswordHash
.mnSpinCount
? OString::number( rProt
.maPasswordHash
.mnSpinCount
).getStr() : nullptr,
494 XML_sqref
, rProt
.maRangeList
.is() ? XclXmlUtils::ToOString( rStrm
.GetRoot().GetDoc(), *rProt
.maRangeList
).getStr() : nullptr);
496 rWorksheet
->endElement( XML_protectedRanges
);
499 XclExpPassHash::XclExpPassHash(const Sequence
<sal_Int8
>& aHash
) :
500 XclExpRecord(EXC_ID_PASSWORD
, 2),
503 if (aHash
.getLength() >= 2)
505 mnHash
= ((aHash
[0] << 8) & 0xFFFF);
506 mnHash
|= (aHash
[1] & 0xFF);
510 XclExpPassHash::~XclExpPassHash()
514 void XclExpPassHash::WriteBody(XclExpStream
& rStrm
)
519 XclExpFiltermode::XclExpFiltermode() :
520 XclExpEmptyRecord( EXC_ID_FILTERMODE
)
524 XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress
& rStartPos
, SCCOL nScCol
) :
525 XclExpUInt16Record( EXC_ID_AUTOFILTERINFO
, static_cast< sal_uInt16
>( nScCol
) ),
526 maStartPos( rStartPos
)
530 ExcFilterCondition::ExcFilterCondition() :
531 nType( EXC_AFTYPE_NOTUSED
),
532 nOper( EXC_AFOPER_EQUAL
)
536 ExcFilterCondition::~ExcFilterCondition()
540 std::size_t ExcFilterCondition::GetTextBytes() const
542 return pText
? (1 + pText
->GetBufferSize()) : 0;
545 void ExcFilterCondition::SetCondition( sal_uInt8 nTp
, sal_uInt8 nOp
, const OUString
* pT
)
549 pText
.reset( pT
? new XclExpString( *pT
, XclStrFlags::EightBitLength
) : nullptr);
552 void ExcFilterCondition::Save( XclExpStream
& rStrm
)
554 rStrm
<< nType
<< nOper
;
555 if (nType
== EXC_AFTYPE_STRING
)
557 OSL_ENSURE(pText
, "ExcFilterCondition::Save() -- pText is NULL!");
558 rStrm
<< sal_uInt32(0) << static_cast<sal_uInt8
>(pText
->Len()) << sal_uInt16(0) << sal_uInt8(0);
561 rStrm
<< sal_uInt32(0) << sal_uInt32(0);
564 static const char* lcl_GetOperator( sal_uInt8 nOper
)
568 case EXC_AFOPER_EQUAL
: return "equal";
569 case EXC_AFOPER_GREATER
: return "greaterThan";
570 case EXC_AFOPER_GREATEREQUAL
: return "greaterThanOrEqual";
571 case EXC_AFOPER_LESS
: return "lessThan";
572 case EXC_AFOPER_LESSEQUAL
: return "lessThanOrEqual";
573 case EXC_AFOPER_NOTEQUAL
: return "notEqual";
574 case EXC_AFOPER_NONE
:
575 default: return "**none**";
579 static OString
lcl_GetValue( sal_uInt8 nType
, const XclExpString
* pStr
)
581 if (nType
== EXC_AFTYPE_STRING
)
582 return XclXmlUtils::ToOString(*pStr
);
587 void ExcFilterCondition::SaveXml( XclExpXmlStream
& rStrm
)
592 rStrm
.GetCurrentStream()->singleElement( XML_customFilter
,
593 XML_operator
, lcl_GetOperator( nOper
),
594 XML_val
, lcl_GetValue(nType
, pText
.get()) );
597 void ExcFilterCondition::SaveText( XclExpStream
& rStrm
)
599 if( nType
== EXC_AFTYPE_STRING
)
601 OSL_ENSURE( pText
, "ExcFilterCondition::SaveText() -- pText is NULL!" );
602 pText
->WriteFlagField( rStrm
);
603 pText
->WriteBuffer( rStrm
);
607 XclExpAutofilter::XclExpAutofilter( const XclExpRoot
& rRoot
, sal_uInt16 nC
) :
608 XclExpRecord( EXC_ID_AUTOFILTER
, 24 ),
610 meType(FilterCondition
),
613 bHasBlankValue( false )
617 bool XclExpAutofilter::AddCondition( ScQueryConnect eConn
, sal_uInt8 nType
, sal_uInt8 nOp
,
618 const OUString
* pText
, bool bSimple
)
620 if( !aCond
[ 1 ].IsEmpty() )
623 sal_uInt16 nInd
= aCond
[ 0 ].IsEmpty() ? 0 : 1;
626 nFlags
|= (eConn
== SC_OR
) ? EXC_AFFLAG_OR
: EXC_AFFLAG_AND
;
628 nFlags
|= (nInd
== 0) ? EXC_AFFLAG_SIMPLE1
: EXC_AFFLAG_SIMPLE2
;
630 aCond
[ nInd
].SetCondition( nType
, nOp
, pText
);
632 AddRecSize( aCond
[ nInd
].GetTextBytes() );
637 bool XclExpAutofilter::HasCondition() const
639 return !aCond
[0].IsEmpty();
642 bool XclExpAutofilter::AddEntry( const ScQueryEntry
& rEntry
)
644 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
648 if (GetOutput() != EXC_OUTPUT_BINARY
)
650 // tdf#123353 XLSX export
658 if (GetOutput() != EXC_OUTPUT_BINARY
&& rItems
.size() > 1)
660 AddMultiValueEntry(rEntry
);
664 bool bConflict
= false;
666 const ScQueryEntry::Item
& rItem
= rItems
[0];
667 if (!rItem
.maString
.isEmpty())
669 sText
= rItem
.maString
.getString();
673 case SC_DOES_NOT_CONTAIN
:
675 sText
= "*" + sText
+ "*";
679 case SC_DOES_NOT_BEGIN_WITH
:
683 case SC_DOES_NOT_END_WITH
:
693 // empty/nonempty fields
694 if (rEntry
.IsQueryByEmpty())
696 bConflict
= !AddCondition(rEntry
.eConnect
, EXC_AFTYPE_EMPTY
, EXC_AFOPER_NONE
, nullptr, true);
697 bHasBlankValue
= true;
699 else if(rEntry
.IsQueryByNonEmpty())
700 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_NOTEMPTY
, EXC_AFOPER_NONE
, nullptr, true );
701 else if (rEntry
.IsQueryByTextColor() || rEntry
.IsQueryByBackgroundColor())
703 AddColorEntry(rEntry
);
709 sal_uInt16 nNewFlags
= 0x0000;
713 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
);
716 nNewFlags
= EXC_AFFLAG_TOP10
;
719 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
| EXC_AFFLAG_TOP10PERC
);
722 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10PERC
);
726 bool bNewTop10
= ::get_flag( nNewFlags
, EXC_AFFLAG_TOP10
);
728 bConflict
= HasTop10() && bNewTop10
;
733 sal_uInt32 nIndex
= 0;
735 if (GetFormatter().IsNumberFormat(sText
, nIndex
, fVal
))
737 if (fVal
< 0) fVal
= 0;
738 if (fVal
>= 501) fVal
= 500;
740 nFlags
|= (nNewFlags
| static_cast<sal_uInt16
>(fVal
) << 7);
745 if (GetOutput() != EXC_OUTPUT_BINARY
&& rEntry
.eOp
== SC_EQUAL
)
747 AddMultiValueEntry(rEntry
);
751 sal_uInt8 nOper
= EXC_AFOPER_NONE
;
755 case SC_EQUAL
: nOper
= EXC_AFOPER_EQUAL
; break;
756 case SC_LESS
: nOper
= EXC_AFOPER_LESS
; break;
757 case SC_GREATER
: nOper
= EXC_AFOPER_GREATER
; break;
758 case SC_LESS_EQUAL
: nOper
= EXC_AFOPER_LESSEQUAL
; break;
759 case SC_GREATER_EQUAL
: nOper
= EXC_AFOPER_GREATEREQUAL
; break;
760 case SC_NOT_EQUAL
: nOper
= EXC_AFOPER_NOTEQUAL
; break;
764 nOper
= EXC_AFOPER_EQUAL
; break;
765 case SC_DOES_NOT_CONTAIN
:
766 case SC_DOES_NOT_BEGIN_WITH
:
767 case SC_DOES_NOT_END_WITH
:
768 nOper
= EXC_AFOPER_NOTEQUAL
; break;
771 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_STRING
, nOper
, &sText
);
778 void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry
& rEntry
)
781 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
782 for (const auto& rItem
: rItems
)
784 if( rItem
.maString
.isEmpty() )
785 bHasBlankValue
= true;
787 maMultiValues
.push_back(std::make_pair(rItem
.maString
.getString(), rItem
.meType
== ScQueryEntry::ByDate
));
791 void XclExpAutofilter::AddColorEntry(const ScQueryEntry
& rEntry
)
794 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
795 for (const auto& rItem
: rItems
)
797 maColorValues
.push_back(
798 std::make_pair(rItem
.maColor
, rItem
.meType
== ScQueryEntry::ByBackgroundColor
));
799 // Ensure that selected color(s) will be added to dxf: selection can be not in list
800 // of already added to dfx colors taken from filter range
801 if (GetDxfs().GetDxfByColor(rItem
.maColor
) == -1)
802 GetDxfs().AddColor(rItem
.maColor
);
806 void XclExpAutofilter::WriteBody( XclExpStream
& rStrm
)
808 rStrm
<< nCol
<< nFlags
;
809 aCond
[ 0 ].Save( rStrm
);
810 aCond
[ 1 ].Save( rStrm
);
811 aCond
[ 0 ].SaveText( rStrm
);
812 aCond
[ 1 ].SaveText( rStrm
);
815 void XclExpAutofilter::SaveXml( XclExpXmlStream
& rStrm
)
817 if (meType
== FilterCondition
&& !HasCondition() && !HasTop10())
820 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
822 rWorksheet
->startElement( XML_filterColumn
,
823 XML_colId
, OString::number(nCol
)
824 // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow?
825 // OOXTODO: XML_showButton
830 case FilterCondition
:
834 rWorksheet
->singleElement( XML_top10
,
835 XML_top
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10TOP
) ),
836 XML_percent
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10PERC
) ),
837 XML_val
, OString::number(nFlags
>> 7)
838 // OOXTODO: XML_filterVal
843 rWorksheet
->startElement(XML_customFilters
, XML_and
,
844 ToPsz((nFlags
& EXC_AFFLAG_ANDORMASK
) == EXC_AFFLAG_AND
));
845 aCond
[0].SaveXml(rStrm
);
846 aCond
[1].SaveXml(rStrm
);
847 rWorksheet
->endElement(XML_customFilters
);
849 // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
854 if (!maColorValues
.empty())
856 Color color
= maColorValues
[0].first
;
857 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
859 if (maColorValues
[0].second
) // is background color
861 pAttrList
->add(XML_cellColor
, OString::number(1));
865 pAttrList
->add(XML_cellColor
, OString::number(0));
867 pAttrList
->add(XML_dxfId
, OString::number(GetDxfs().GetDxfByColor(color
)));
868 rWorksheet
->singleElement(XML_colorFilter
, pAttrList
);
874 rWorksheet
->singleElement(XML_filters
, XML_blank
, "1");
880 rWorksheet
->startElement(XML_filters
, XML_blank
, "1");
882 rWorksheet
->startElement(XML_filters
);
884 for (const auto& rMultiValue
: maMultiValues
)
886 OString aStr
= OUStringToOString(rMultiValue
.first
, RTL_TEXTENCODING_UTF8
);
887 if( !rMultiValue
.second
)
889 const char* pz
= aStr
.getStr();
890 rWorksheet
->singleElement(XML_filter
, XML_val
, pz
);
894 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= sax_fastparser::FastSerializerHelper::createAttrList();
895 sal_Int32 aDateGroup
[3] = { XML_year
, XML_month
, XML_day
};
897 for (size_t i
= 0; idx
>= 0 && i
< 3; i
++)
899 OString kw
= aStr
.getToken(0, '-', idx
);
903 pAttrList
->add(aDateGroup
[i
], kw
);
906 // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
907 // will be "day" as default, until date filter cannot handle HH:MM:SS.
908 pAttrList
->add(XML_dateTimeGrouping
, "day");
909 rWorksheet
->singleElement(XML_dateGroupItem
, pAttrList
);
912 rWorksheet
->endElement(XML_filters
);
916 rWorksheet
->endElement( XML_filterColumn
);
919 ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot
& rRoot
, SCTAB nTab
, const ScDBData
* pDefinedData
) :
923 XclExpNameManager
& rNameMgr
= GetNameManager();
926 bool bAdvanced
= false;
927 const ScDBData
* pData
= (pDefinedData
? pDefinedData
: rRoot
.GetDoc().GetAnonymousDBData(nTab
));
931 bAdvanced
= pData
->GetAdvancedQuerySource( aAdvRange
);
932 bFound
= (pData
->HasQueryParam() || pData
->HasAutoFilter() || bAdvanced
);
938 pData
->GetQueryParam( aParam
);
940 ScRange
aRange( aParam
.nCol1
, aParam
.nRow1
, aParam
.nTab
,
941 aParam
.nCol2
, aParam
.nRow2
, aParam
.nTab
);
942 SCCOL nColCnt
= aParam
.nCol2
- aParam
.nCol1
+ 1;
946 // #i2394# built-in defined names must be sorted by containing sheet name
948 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE
, aRange
);
953 // filter criteria, excel allows only same table
954 if( !pDefinedData
&& aAdvRange
.aStart
.Tab() == nTab
)
955 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_CRITERIA
, aAdvRange
);
957 // filter destination range, excel allows only same table
958 if( !aParam
.bInplace
)
960 ScRange
aDestRange( aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
961 aDestRange
.aEnd
.IncCol( nColCnt
- 1 );
962 if( !pDefinedData
&& aDestRange
.aStart
.Tab() == nTab
)
963 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_EXTRACT
, aDestRange
);
966 m_pFilterMode
= new XclExpFiltermode
;
971 bool bConflict
= false;
972 bool bContLoop
= true;
974 SCCOLROW nFirstField
= aParam
.GetEntry( 0 ).nField
;
976 // create AUTOFILTER records for filtered columns
977 for( SCSIZE nEntry
= 0; !bConflict
&& bContLoop
&& (nEntry
< aParam
.GetEntryCount()); nEntry
++ )
979 const ScQueryEntry
& rEntry
= aParam
.GetEntry( nEntry
);
981 bContLoop
= rEntry
.bDoQuery
;
984 XclExpAutofilter
* pFilter
= GetByCol( static_cast<SCCOL
>(rEntry
.nField
) - aRange
.aStart
.Col() );
987 bHasOr
|= (rEntry
.eConnect
== SC_OR
);
989 bConflict
= (nEntry
> 1) && bHasOr
;
991 bConflict
= (nEntry
== 1) && (rEntry
.eConnect
== SC_OR
) &&
992 (nFirstField
!= rEntry
.nField
);
994 bConflict
= pFilter
->AddEntry( rEntry
);
998 // additional tests for conflicts
999 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); !bConflict
&& (nPos
< nSize
); ++nPos
)
1001 XclExpAutofilterRef xFilter
= maFilterList
.GetRecord( nPos
);
1002 bConflict
= xFilter
->HasCondition() && xFilter
->HasTop10();
1006 maFilterList
.RemoveAllRecords();
1008 if( !maFilterList
.IsEmpty() )
1009 m_pFilterMode
= new XclExpFiltermode
;
1010 m_pFilterInfo
= new XclExpAutofilterinfo( aRange
.aStart
, nColCnt
);
1012 if (maFilterList
.IsEmpty () && !bConflict
)
1013 mbAutoFilter
= true;
1015 // get sort criteria
1017 ScSortParam aSortParam
;
1018 pData
->GetSortParam( aSortParam
);
1020 ScUserList
* pList
= ScGlobal::GetUserList();
1021 if (aSortParam
.bUserDef
&& pList
&& pList
->size() > aSortParam
.nUserIndex
)
1023 // get sorted area without headers
1024 maSortRef
= ScRange(
1025 aParam
.nCol1
, aParam
.nRow1
+ (aSortParam
.bHasHeader
? 1 : 0), aParam
.nTab
,
1026 aParam
.nCol2
, aParam
.nRow2
, aParam
.nTab
);
1028 // get sorted columns with custom lists
1029 const ScUserListData
& rData
= (*pList
)[aSortParam
.nUserIndex
];
1031 // get column index and sorting direction
1032 SCCOLROW nField
= 0;
1033 bool bSortAscending
=true;
1034 for (const auto & rKey
: aSortParam
.maKeyState
)
1038 nField
= rKey
.nField
;
1039 bSortAscending
= rKey
.bAscending
;
1044 // remember sort criteria
1045 const ScRange
aSortedColumn(
1046 nField
, aParam
.nRow1
+ (aSortParam
.bHasHeader
? 1 : 0), aParam
.nTab
,
1047 nField
, aParam
.nRow2
, aParam
.nTab
);
1048 const OUString aItemList
= rData
.GetString();
1050 maSortCustomList
.emplace_back(aSortedColumn
, aItemList
, !bSortAscending
);
1056 ExcAutoFilterRecs::~ExcAutoFilterRecs()
1060 XclExpAutofilter
* ExcAutoFilterRecs::GetByCol( SCCOL nCol
)
1062 XclExpAutofilterRef xFilter
;
1063 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
1065 xFilter
= maFilterList
.GetRecord( nPos
);
1066 if( xFilter
->GetCol() == static_cast<sal_uInt16
>(nCol
) )
1067 return xFilter
.get();
1069 xFilter
= new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16
>(nCol
) );
1070 maFilterList
.AppendRecord( xFilter
);
1071 return xFilter
.get();
1074 bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol
)
1076 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
1077 if( maFilterList
.GetRecord( nPos
)->GetCol() == static_cast<sal_uInt16
>(nCol
) )
1082 void ExcAutoFilterRecs::AddObjRecs()
1086 ScAddress
aAddr( m_pFilterInfo
->GetStartPos() );
1087 for( SCCOL nObj
= 0, nCount
= m_pFilterInfo
->GetColCount(); nObj
< nCount
; nObj
++ )
1089 std::unique_ptr
<XclObj
> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr
, IsFiltered( nObj
) ));
1090 GetObjectManager().AddObj( std::move(pObjRec
) );
1096 void ExcAutoFilterRecs::Save( XclExpStream
& rStrm
)
1099 m_pFilterMode
->Save( rStrm
);
1101 m_pFilterInfo
->Save( rStrm
);
1102 maFilterList
.Save( rStrm
);
1105 void ExcAutoFilterRecs::SaveXml( XclExpXmlStream
& rStrm
)
1107 if( maFilterList
.IsEmpty() && !mbAutoFilter
)
1110 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1111 rWorksheet
->startElement(XML_autoFilter
, XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), maRef
));
1112 // OOXTODO: XML_extLst, XML_sortState
1113 if( !maFilterList
.IsEmpty() )
1114 maFilterList
.SaveXml( rStrm
);
1116 if (!maSortCustomList
.empty())
1118 rWorksheet
->startElement(XML_sortState
, XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), maSortRef
));
1120 for (const auto & rSortCriteria
: maSortCustomList
)
1122 if (std::get
<2>(rSortCriteria
))
1123 rWorksheet
->singleElement(XML_sortCondition
,
1124 XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(),
1125 std::get
<0>(rSortCriteria
)),
1126 XML_descending
, "1",
1127 XML_customList
, std::get
<1>(rSortCriteria
).toUtf8().getStr());
1129 rWorksheet
->singleElement(XML_sortCondition
,
1130 XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(),
1131 std::get
<0>(rSortCriteria
)),
1132 XML_customList
, std::get
<1>(rSortCriteria
).toUtf8().getStr());
1135 rWorksheet
->endElement(XML_sortState
);
1138 rWorksheet
->endElement( XML_autoFilter
);
1141 bool ExcAutoFilterRecs::HasFilterMode() const
1143 return m_pFilterMode
!= nullptr;
1146 XclExpFilterManager::XclExpFilterManager( const XclExpRoot
& rRoot
) :
1151 void XclExpFilterManager::InitTabFilter( SCTAB nScTab
)
1153 maFilterMap
[ nScTab
] = new ExcAutoFilterRecs( GetRoot(), nScTab
, nullptr );
1156 XclExpRecordRef
XclExpFilterManager::CreateRecord( SCTAB nScTab
)
1158 XclExpTabFilterRef xRec
;
1159 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1160 if( aIt
!= maFilterMap
.end() )
1168 bool XclExpFilterManager::HasFilterMode( SCTAB nScTab
)
1170 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1171 if( aIt
!= maFilterMap
.end() )
1173 return aIt
->second
->HasFilterMode();
1178 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */