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/zforlist.hxx>
26 #include <sal/log.hxx>
31 #include <document.hxx>
33 #include <oox/export/utils.hxx>
34 #include <oox/token/tokens.hxx>
35 #include <queryentry.hxx>
36 #include <queryparam.hxx>
39 #include <xeescher.hxx>
44 #include <xcl97rec.hxx>
45 #include <tabprotection.hxx>
47 using namespace ::oox
;
49 using ::com::sun::star::uno::Sequence
;
51 //--------------------------------------------------------- class ExcDummy_00 -
52 const sal_uInt8
ExcDummy_00::pMyData
[] = {
53 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
54 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
55 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
56 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
58 const std::size_t ExcDummy_00::nMyLen
= sizeof( ExcDummy_00::pMyData
);
60 //-------------------------------------------------------- class ExcDummy_04x -
61 const sal_uInt8
ExcDummy_040::pMyData
[] = {
62 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
63 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
65 const std::size_t ExcDummy_040::nMyLen
= sizeof( ExcDummy_040::pMyData
);
67 const sal_uInt8
ExcDummy_041::pMyData
[] = {
68 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
69 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
71 const std::size_t ExcDummy_041::nMyLen
= sizeof( ExcDummy_041::pMyData
);
73 //-------------------------------------------------------- class ExcDummy_02a -
74 const sal_uInt8
ExcDummy_02a::pMyData
[] = {
75 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
76 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
77 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
78 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
79 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
81 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
83 const std::size_t ExcDummy_02a::nMyLen
= sizeof( ExcDummy_02a::pMyData
);
85 //----------------------------------------------------------- class ExcRecord -
87 void ExcRecord::Save( XclExpStream
& rStrm
)
89 SetRecHeader( GetNum(), GetLen() );
90 XclExpRecord::Save( rStrm
);
93 void ExcRecord::SaveCont( XclExpStream
& /*rStrm*/ )
97 void ExcRecord::WriteBody( XclExpStream
& rStrm
)
102 void ExcRecord::SaveXml( XclExpXmlStream
& /*rStrm*/ )
106 //--------------------------------------------------------- class ExcEmptyRec -
108 void ExcEmptyRec::Save( XclExpStream
& /*rStrm*/ )
112 sal_uInt16
ExcEmptyRec::GetNum() const
117 std::size_t ExcEmptyRec::GetLen() const
122 //--------------------------------------------------------- class ExcDummyRec -
124 void ExcDummyRec::Save( XclExpStream
& rStrm
)
126 rStrm
.Write( GetData(), GetLen() ); // raw write mode
129 sal_uInt16
ExcDummyRec::GetNum() const
134 //------------------------------------------------------- class ExcBoolRecord -
136 void ExcBoolRecord::SaveCont( XclExpStream
& rStrm
)
138 rStrm
<< static_cast<sal_uInt16
>(bVal
? 0x0001 : 0x0000);
141 std::size_t ExcBoolRecord::GetLen() const
146 //--------------------------------------------------------- class ExcBof_Base -
148 ExcBof_Base::ExcBof_Base()
151 , nRupBuild(0x096C) // copied from Excel
152 , nRupYear(0x07C9) // copied from Excel
156 //-------------------------------------------------------------- class ExcBof -
164 void ExcBof::SaveCont( XclExpStream
& rStrm
)
166 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
169 sal_uInt16
ExcBof::GetNum() const
174 std::size_t ExcBof::GetLen() const
179 //------------------------------------------------------------- class ExcBofW -
187 void ExcBofW::SaveCont( XclExpStream
& rStrm
)
189 rStrm
<< nVers
<< nDocType
<< nRupBuild
<< nRupYear
;
192 sal_uInt16
ExcBofW::GetNum() const
197 std::size_t ExcBofW::GetLen() const
202 //-------------------------------------------------------------- class ExcEof -
204 sal_uInt16
ExcEof::GetNum() const
209 std::size_t ExcEof::GetLen() const
214 //--------------------------------------------------------- class ExcDummy_00 -
216 std::size_t ExcDummy_00::GetLen() const
221 const sal_uInt8
* ExcDummy_00::GetData() const
226 //-------------------------------------------------------- class ExcDummy_04x -
228 std::size_t ExcDummy_040::GetLen() const
233 const sal_uInt8
* ExcDummy_040::GetData() const
238 std::size_t ExcDummy_041::GetLen() const
243 const sal_uInt8
* ExcDummy_041::GetData() const
248 //------------------------------------------------------------- class Exc1904 -
250 Exc1904::Exc1904( const ScDocument
& rDoc
)
252 const Date
& rDate
= rDoc
.GetFormatTable()->GetNullDate();
253 bVal
= (rDate
== Date( 1, 1, 1904 ));
254 bDateCompatibility
= (rDate
!= Date( 30, 12, 1899 ));
257 sal_uInt16
Exc1904::GetNum() const
262 void Exc1904::SaveXml( XclExpXmlStream
& rStrm
)
264 bool bISOIEC
= ( rStrm
.getVersion() == oox::core::ISOIEC_29500_2008
);
268 rStrm
.WriteAttributes(XML_dateCompatibility
, ToPsz(bDateCompatibility
));
271 if( !bISOIEC
|| bDateCompatibility
)
273 rStrm
.WriteAttributes(XML_date1904
, ToPsz(bVal
));
277 //------------------------------------------------------ class ExcBundlesheet -
279 ExcBundlesheetBase::ExcBundlesheetBase( const RootData
& rRootData
, SCTAB nTabNum
) :
280 m_nStrPos( STREAM_SEEK_TO_END
),
281 m_nOwnPos( STREAM_SEEK_TO_END
),
282 nGrbit( rRootData
.pER
->GetTabInfo().IsVisibleTab( nTabNum
) ? 0x0000 : 0x0001 ),
287 ExcBundlesheetBase::ExcBundlesheetBase() :
288 m_nStrPos( STREAM_SEEK_TO_END
),
289 m_nOwnPos( STREAM_SEEK_TO_END
),
295 void ExcBundlesheetBase::UpdateStreamPos( XclExpStream
& rStrm
)
297 rStrm
.SetSvStreamPos( m_nOwnPos
);
298 rStrm
.DisableEncryption();
299 rStrm
<< static_cast<sal_uInt32
>(m_nStrPos
);
300 rStrm
.EnableEncryption();
303 sal_uInt16
ExcBundlesheetBase::GetNum() const
308 ExcBundlesheet::ExcBundlesheet( const RootData
& rRootData
, SCTAB _nTab
) :
309 ExcBundlesheetBase( rRootData
, _nTab
)
311 OUString sTabName
= rRootData
.pER
->GetTabInfo().GetScTabName( _nTab
);
312 OSL_ENSURE( sTabName
.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
313 aName
= OUStringToOString(sTabName
, rRootData
.pER
->GetTextEncoding());
316 void ExcBundlesheet::SaveCont( XclExpStream
& rStrm
)
318 m_nOwnPos
= rStrm
.GetSvStreamPos();
319 rStrm
<< sal_uInt32(0x00000000) // dummy (stream position of the sheet)
321 rStrm
.WriteByteString(aName
); // 8 bit length, max 255 chars
324 std::size_t ExcBundlesheet::GetLen() const
326 return 7 + std::min( aName
.getLength(), sal_Int32(255) );
329 //--------------------------------------------------------- class ExcDummy_02 -
331 std::size_t ExcDummy_02a::GetLen() const
336 const sal_uInt8
* ExcDummy_02a::GetData() const
340 //--------------------------------------------------------- class ExcDummy_02 -
342 XclExpCountry::XclExpCountry( const XclExpRoot
& rRoot
) :
343 XclExpRecord( EXC_ID_COUNTRY
, 4 )
345 /* #i31530# set document country as UI country too -
346 needed for correct behaviour of number formats. */
347 mnUICountry
= mnDocCountry
= static_cast< sal_uInt16
>(
348 ::msfilter::ConvertLanguageToCountry( rRoot
.GetDocLanguage() ) );
351 void XclExpCountry::WriteBody( XclExpStream
& rStrm
)
353 rStrm
<< mnUICountry
<< mnDocCountry
;
356 // XclExpWsbool ===============================================================
358 XclExpWsbool::XclExpWsbool( bool bFitToPages
)
359 : XclExpUInt16Record( EXC_ID_WSBOOL
, EXC_WSBOOL_DEFAULTFLAGS
)
362 SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE
);
365 XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages
, SCTAB nScTab
, const Color
& rTabColor
, XclExpFilterManager
* pManager
) :
366 mnScTab(nScTab
), mpManager(pManager
), mbFitToPage(bFitToPages
), maTabColor(rTabColor
) {}
368 void XclExpXmlSheetPr::SaveXml( XclExpXmlStream
& rStrm
)
370 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
371 rWorksheet
->startElement( XML_sheetPr
,
372 // OOXTODO: XML_syncHorizontal,
373 // OOXTODO: XML_syncVertical,
374 // OOXTODO: XML_syncRef,
375 // OOXTODO: XML_transitionEvaluation,
376 // OOXTODO: XML_transitionEntry,
377 // OOXTODO: XML_published,
378 // OOXTODO: XML_codeName,
379 XML_filterMode
, mpManager
? ToPsz(mpManager
->HasFilterMode(mnScTab
)) : nullptr
380 // OOXTODO: XML_enableFormatConditionsCalculation
383 // Note : the order of child elements is significant. Don't change the order.
385 // OOXTODO: XML_outlinePr
387 if (maTabColor
!= COL_AUTO
)
388 rWorksheet
->singleElement(XML_tabColor
, XML_rgb
, XclXmlUtils::ToOString(maTabColor
));
390 rWorksheet
->singleElement(XML_pageSetUpPr
,
391 // OOXTODO: XML_autoPageBreaks,
392 XML_fitToPage
, ToPsz(mbFitToPage
));
394 rWorksheet
->endElement( XML_sheetPr
);
397 // XclExpWindowProtection ===============================================================
399 XclExpWindowProtection::XclExpWindowProtection(bool bValue
) :
400 XclExpBoolRecord(EXC_ID_WINDOWPROTECT
, bValue
)
404 void XclExpWindowProtection::SaveXml( XclExpXmlStream
& rStrm
)
406 rStrm
.WriteAttributes(XML_lockWindows
, ToPsz(GetBool()));
409 // XclExpDocProtection ===============================================================
411 XclExpProtection::XclExpProtection(bool bValue
) :
412 XclExpBoolRecord(EXC_ID_PROTECT
, bValue
)
416 XclExpSheetProtection::XclExpSheetProtection(bool bValue
, SCTAB nTab
) :
417 XclExpProtection( bValue
),
422 void XclExpSheetProtection::SaveXml( XclExpXmlStream
& rStrm
)
424 ScDocument
& rDoc
= rStrm
.GetRoot().GetDoc();
425 const ScTableProtection
* pTabProtect
= rDoc
.GetTabProtection(mnTab
);
428 const ScOoxPasswordHash
& rPH
= pTabProtect
->getPasswordHash();
429 // Do not write any hash attributes if there is no password.
430 ScOoxPasswordHash aPH
;
431 if (rPH
.hasPassword())
434 Sequence
<sal_Int8
> aHash
= pTabProtect
->getPasswordHash(PASSHASH_XL
);
436 if (aHash
.getLength() >= 2)
438 sHash
= OString::number(
439 ( static_cast<sal_uInt8
>(aHash
[0]) << 8
440 | static_cast<sal_uInt8
>(aHash
[1]) ),
443 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
444 rWorksheet
->singleElement( XML_sheetProtection
,
445 XML_algorithmName
, aPH
.maAlgorithmName
.isEmpty() ? nullptr : aPH
.maAlgorithmName
.toUtf8().getStr(),
446 XML_hashValue
, aPH
.maHashValue
.isEmpty() ? nullptr : aPH
.maHashValue
.toUtf8().getStr(),
447 XML_saltValue
, aPH
.maSaltValue
.isEmpty() ? nullptr : aPH
.maSaltValue
.toUtf8().getStr(),
448 XML_spinCount
, aPH
.mnSpinCount
? OString::number( aPH
.mnSpinCount
).getStr() : nullptr,
449 XML_sheet
, ToPsz( true ),
450 XML_password
, sHash
.isEmpty()? nullptr : sHash
.getStr(),
451 XML_objects
, pTabProtect
->isOptionEnabled( ScTableProtection::OBJECTS
) ? nullptr : ToPsz( true ),
452 XML_scenarios
, pTabProtect
->isOptionEnabled( ScTableProtection::SCENARIOS
) ? nullptr : ToPsz( true ),
453 XML_formatCells
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_CELLS
) ? ToPsz( false ) : nullptr,
454 XML_formatColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS
) ? ToPsz( false ) : nullptr,
455 XML_formatRows
, pTabProtect
->isOptionEnabled( ScTableProtection::FORMAT_ROWS
) ? ToPsz( false ) : nullptr,
456 XML_insertColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_COLUMNS
) ? ToPsz( false ) : nullptr,
457 XML_insertRows
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_ROWS
) ? ToPsz( false ) : nullptr,
458 XML_insertHyperlinks
, pTabProtect
->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS
) ? ToPsz( false ) : nullptr,
459 XML_deleteColumns
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_COLUMNS
) ? ToPsz( false ) : nullptr,
460 XML_deleteRows
, pTabProtect
->isOptionEnabled( ScTableProtection::DELETE_ROWS
) ? ToPsz( false ) : nullptr,
461 XML_selectLockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS
) ? nullptr : ToPsz( true ),
462 XML_sort
, pTabProtect
->isOptionEnabled( ScTableProtection::SORT
) ? ToPsz( false ) : nullptr,
463 XML_autoFilter
, pTabProtect
->isOptionEnabled( ScTableProtection::AUTOFILTER
) ? ToPsz( false ) : nullptr,
464 XML_pivotTables
, pTabProtect
->isOptionEnabled( ScTableProtection::PIVOT_TABLES
) ? ToPsz( false ) : nullptr,
465 XML_selectUnlockedCells
, pTabProtect
->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS
) ? nullptr : ToPsz( true ) );
467 const ::std::vector
<ScEnhancedProtection
>& rProts( pTabProtect
->getEnhancedProtection());
470 rWorksheet
->startElement(XML_protectedRanges
);
471 for (const auto& rProt
: rProts
)
473 SAL_WARN_IF( rProt
.maSecurityDescriptorXML
.isEmpty() && !rProt
.maSecurityDescriptor
.empty(),
474 "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
475 rWorksheet
->singleElement( XML_protectedRange
,
476 XML_name
, rProt
.maTitle
.isEmpty() ? nullptr : rProt
.maTitle
.toUtf8().getStr(),
477 XML_securityDescriptor
, rProt
.maSecurityDescriptorXML
.isEmpty() ? nullptr : rProt
.maSecurityDescriptorXML
.toUtf8().getStr(),
478 /* XXX 'password' is not part of OOXML, but Excel2013
479 * writes it if loaded from BIFF, in which case
480 * 'algorithmName', 'hashValue', 'saltValue' and
481 * 'spinCount' are absent; so do we if it was present. */
482 XML_password
, rProt
.mnPasswordVerifier
? OString::number( rProt
.mnPasswordVerifier
, 16).getStr() : nullptr,
483 XML_algorithmName
, rProt
.maPasswordHash
.maAlgorithmName
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maAlgorithmName
.toUtf8().getStr(),
484 XML_hashValue
, rProt
.maPasswordHash
.maHashValue
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maHashValue
.toUtf8().getStr(),
485 XML_saltValue
, rProt
.maPasswordHash
.maSaltValue
.isEmpty() ? nullptr : rProt
.maPasswordHash
.maSaltValue
.toUtf8().getStr(),
486 XML_spinCount
, rProt
.maPasswordHash
.mnSpinCount
? OString::number( rProt
.maPasswordHash
.mnSpinCount
).getStr() : nullptr,
487 XML_sqref
, rProt
.maRangeList
.is() ? XclXmlUtils::ToOString( &rStrm
.GetRoot().GetDoc(), *rProt
.maRangeList
).getStr() : nullptr);
489 rWorksheet
->endElement( XML_protectedRanges
);
494 XclExpPassHash::XclExpPassHash(const Sequence
<sal_Int8
>& aHash
) :
495 XclExpRecord(EXC_ID_PASSWORD
, 2),
498 if (aHash
.getLength() >= 2)
500 mnHash
= ((aHash
[0] << 8) & 0xFFFF);
501 mnHash
|= (aHash
[1] & 0xFF);
505 XclExpPassHash::~XclExpPassHash()
509 void XclExpPassHash::WriteBody(XclExpStream
& rStrm
)
514 XclExpFiltermode::XclExpFiltermode() :
515 XclExpEmptyRecord( EXC_ID_FILTERMODE
)
519 XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress
& rStartPos
, SCCOL nScCol
) :
520 XclExpUInt16Record( EXC_ID_AUTOFILTERINFO
, static_cast< sal_uInt16
>( nScCol
) ),
521 maStartPos( rStartPos
)
525 ExcFilterCondition::ExcFilterCondition() :
526 nType( EXC_AFTYPE_NOTUSED
),
527 nOper( EXC_AFOPER_EQUAL
),
532 ExcFilterCondition::~ExcFilterCondition()
536 std::size_t ExcFilterCondition::GetTextBytes() const
538 return pText
? (1 + pText
->GetBufferSize()) : 0;
541 void ExcFilterCondition::SetCondition( sal_uInt8 nTp
, sal_uInt8 nOp
, double fV
, const OUString
* pT
)
546 pText
.reset( pT
? new XclExpString( *pT
, XclStrFlags::EightBitLength
) : nullptr);
549 void ExcFilterCondition::Save( XclExpStream
& rStrm
)
551 rStrm
<< nType
<< nOper
;
554 case EXC_AFTYPE_DOUBLE
:
557 case EXC_AFTYPE_STRING
:
558 OSL_ENSURE( pText
, "ExcFilterCondition::Save() -- pText is NULL!" );
559 rStrm
<< sal_uInt32(0) << static_cast<sal_uInt8
>(pText
->Len()) << sal_uInt16(0) << sal_uInt8(0);
561 case EXC_AFTYPE_BOOLERR
:
562 rStrm
<< sal_uInt8(0) << static_cast<sal_uInt8
>((fVal
!= 0) ? 1 : 0) << sal_uInt32(0) << sal_uInt16(0);
565 rStrm
<< sal_uInt32(0) << sal_uInt32(0);
569 static const char* lcl_GetOperator( sal_uInt8 nOper
)
573 case EXC_AFOPER_EQUAL
: return "equal";
574 case EXC_AFOPER_GREATER
: return "greaterThan";
575 case EXC_AFOPER_GREATEREQUAL
: return "greaterThanOrEqual";
576 case EXC_AFOPER_LESS
: return "lessThan";
577 case EXC_AFOPER_LESSEQUAL
: return "lessThanOrEqual";
578 case EXC_AFOPER_NOTEQUAL
: return "notEqual";
579 case EXC_AFOPER_NONE
:
580 default: return "**none**";
584 static OString
lcl_GetValue( sal_uInt8 nType
, double fVal
, const XclExpString
* pStr
)
588 case EXC_AFTYPE_STRING
: return XclXmlUtils::ToOString( *pStr
);
589 case EXC_AFTYPE_DOUBLE
: return OString::number( fVal
);
590 case EXC_AFTYPE_BOOLERR
: return OString::number( ( fVal
!= 0 ? 1 : 0 ) );
591 default: return OString();
595 void ExcFilterCondition::SaveXml( XclExpXmlStream
& rStrm
)
600 rStrm
.GetCurrentStream()->singleElement( XML_customFilter
,
601 XML_operator
, lcl_GetOperator( nOper
),
602 XML_val
, lcl_GetValue(nType
, fVal
, pText
.get()) );
605 void ExcFilterCondition::SaveText( XclExpStream
& rStrm
)
607 if( nType
== EXC_AFTYPE_STRING
)
609 OSL_ENSURE( pText
, "ExcFilterCondition::SaveText() -- pText is NULL!" );
610 pText
->WriteFlagField( rStrm
);
611 pText
->WriteBuffer( rStrm
);
615 XclExpAutofilter::XclExpAutofilter( const XclExpRoot
& rRoot
, sal_uInt16 nC
) :
616 XclExpRecord( EXC_ID_AUTOFILTER
, 24 ),
618 meType(FilterCondition
),
624 bool XclExpAutofilter::AddCondition( ScQueryConnect eConn
, sal_uInt8 nType
, sal_uInt8 nOp
,
625 double fVal
, 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
, fVal
, 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
&& rItems
.size() > 1)
657 AddMultiValueEntry(rEntry
);
661 bool bConflict
= false;
663 const ScQueryEntry::Item
& rItem
= rItems
[0];
664 if (!rItem
.maString
.isEmpty())
666 sText
= rItem
.maString
.getString();
670 case SC_DOES_NOT_CONTAIN
:
672 sText
= "*" + sText
+ "*";
676 case SC_DOES_NOT_BEGIN_WITH
:
680 case SC_DOES_NOT_END_WITH
:
690 bool bLen
= sText
.getLength() > 0;
692 // empty/nonempty fields
693 if (rEntry
.IsQueryByEmpty())
694 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_EMPTY
, EXC_AFOPER_NONE
, 0.0, nullptr, true );
695 else if(rEntry
.IsQueryByNonEmpty())
696 bConflict
= !AddCondition( rEntry
.eConnect
, EXC_AFTYPE_NOTEMPTY
, EXC_AFOPER_NONE
, 0.0, nullptr, true );
701 sal_uInt32 nIndex
= 0;
702 bool bIsNum
= !bLen
|| GetFormatter().IsNumberFormat( sText
, nIndex
, fVal
);
703 OUString
* pText
= nullptr;
708 sal_uInt16 nNewFlags
= 0x0000;
712 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
);
715 nNewFlags
= EXC_AFFLAG_TOP10
;
718 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10TOP
| EXC_AFFLAG_TOP10PERC
);
721 nNewFlags
= (EXC_AFFLAG_TOP10
| EXC_AFFLAG_TOP10PERC
);
725 bool bNewTop10
= ::get_flag( nNewFlags
, EXC_AFFLAG_TOP10
);
727 bConflict
= HasTop10() && bNewTop10
;
732 if( fVal
< 0 ) fVal
= 0;
733 if( fVal
>= 501 ) fVal
= 500;
734 nFlags
|= (nNewFlags
| static_cast<sal_uInt16
>(fVal
) << 7);
739 sal_uInt8 nType
= bIsNum
? EXC_AFTYPE_DOUBLE
: EXC_AFTYPE_STRING
;
740 sal_uInt8 nOper
= EXC_AFOPER_NONE
;
744 case SC_EQUAL
: nOper
= EXC_AFOPER_EQUAL
; break;
745 case SC_LESS
: nOper
= EXC_AFOPER_LESS
; break;
746 case SC_GREATER
: nOper
= EXC_AFOPER_GREATER
; break;
747 case SC_LESS_EQUAL
: nOper
= EXC_AFOPER_LESSEQUAL
; break;
748 case SC_GREATER_EQUAL
: nOper
= EXC_AFOPER_GREATEREQUAL
; break;
749 case SC_NOT_EQUAL
: nOper
= EXC_AFOPER_NOTEQUAL
; break;
753 nOper
= EXC_AFOPER_EQUAL
; break;
754 case SC_DOES_NOT_CONTAIN
:
755 case SC_DOES_NOT_BEGIN_WITH
:
756 case SC_DOES_NOT_END_WITH
:
757 nOper
= EXC_AFOPER_NOTEQUAL
; break;
760 bConflict
= !AddCondition( rEntry
.eConnect
, nType
, nOper
, fVal
, pText
);
767 void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry
& rEntry
)
770 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
771 for (const auto& rItem
: rItems
)
772 maMultiValues
.push_back(rItem
.maString
.getString());
775 void XclExpAutofilter::WriteBody( XclExpStream
& rStrm
)
777 rStrm
<< nCol
<< nFlags
;
778 aCond
[ 0 ].Save( rStrm
);
779 aCond
[ 1 ].Save( rStrm
);
780 aCond
[ 0 ].SaveText( rStrm
);
781 aCond
[ 1 ].SaveText( rStrm
);
784 void XclExpAutofilter::SaveXml( XclExpXmlStream
& rStrm
)
786 if (meType
== FilterCondition
&& !HasCondition())
789 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
791 rWorksheet
->startElement( XML_filterColumn
,
792 XML_colId
, OString::number(nCol
)
793 // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow?
794 // OOXTODO: XML_showButton
799 case FilterCondition
:
803 rWorksheet
->singleElement( XML_top10
,
804 XML_top
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10TOP
) ),
805 XML_percent
, ToPsz( get_flag( nFlags
, EXC_AFFLAG_TOP10PERC
) ),
806 XML_val
, OString::number((nFlags
>> 7))
807 // OOXTODO: XML_filterVal
811 rWorksheet
->startElement( XML_customFilters
,
812 XML_and
, ToPsz((nFlags
& EXC_AFFLAG_ANDORMASK
) == EXC_AFFLAG_AND
) );
813 aCond
[ 0 ].SaveXml( rStrm
);
814 aCond
[ 1 ].SaveXml( rStrm
);
815 rWorksheet
->endElement( XML_customFilters
);
816 // OOXTODO: XLM_colorFilter, XML_dynamicFilter,
817 // XML_extLst, XML_filters, XML_iconFilter, XML_top10
822 rWorksheet
->startElement(XML_filters
);
823 for (const auto& rMultiValue
: maMultiValues
)
825 OString aStr
= OUStringToOString(rMultiValue
, RTL_TEXTENCODING_UTF8
);
826 const char* pz
= aStr
.getStr();
827 rWorksheet
->singleElement(XML_filter
, XML_val
, pz
);
829 rWorksheet
->endElement(XML_filters
);
833 rWorksheet
->endElement( XML_filterColumn
);
836 ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot
& rRoot
, SCTAB nTab
, const ScDBData
* pDefinedData
) :
840 XclExpNameManager
& rNameMgr
= GetNameManager();
843 bool bAdvanced
= false;
844 const ScDBData
* pData
= (pDefinedData
? pDefinedData
: rRoot
.GetDoc().GetAnonymousDBData(nTab
));
848 bAdvanced
= pData
->GetAdvancedQuerySource( aAdvRange
);
849 bFound
= (pData
->HasQueryParam() || pData
->HasAutoFilter() || bAdvanced
);
854 pData
->GetQueryParam( aParam
);
856 ScRange
aRange( aParam
.nCol1
, aParam
.nRow1
, aParam
.nTab
,
857 aParam
.nCol2
, aParam
.nRow2
, aParam
.nTab
);
858 SCCOL nColCnt
= aParam
.nCol2
- aParam
.nCol1
+ 1;
862 // #i2394# built-in defined names must be sorted by containing sheet name
864 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE
, aRange
);
869 // filter criteria, excel allows only same table
870 if( !pDefinedData
&& aAdvRange
.aStart
.Tab() == nTab
)
871 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_CRITERIA
, aAdvRange
);
873 // filter destination range, excel allows only same table
874 if( !aParam
.bInplace
)
876 ScRange
aDestRange( aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
877 aDestRange
.aEnd
.IncCol( nColCnt
- 1 );
878 if( !pDefinedData
&& aDestRange
.aStart
.Tab() == nTab
)
879 rNameMgr
.InsertBuiltInName( EXC_BUILTIN_EXTRACT
, aDestRange
);
882 m_pFilterMode
.reset(new XclExpFiltermode
);
887 bool bConflict
= false;
888 bool bContLoop
= true;
890 SCCOLROW nFirstField
= aParam
.GetEntry( 0 ).nField
;
892 // create AUTOFILTER records for filtered columns
893 for( SCSIZE nEntry
= 0; !bConflict
&& bContLoop
&& (nEntry
< aParam
.GetEntryCount()); nEntry
++ )
895 const ScQueryEntry
& rEntry
= aParam
.GetEntry( nEntry
);
897 bContLoop
= rEntry
.bDoQuery
;
900 XclExpAutofilter
* pFilter
= GetByCol( static_cast<SCCOL
>(rEntry
.nField
) - aRange
.aStart
.Col() );
903 bHasOr
|= (rEntry
.eConnect
== SC_OR
);
905 bConflict
= (nEntry
> 1) && bHasOr
;
907 bConflict
= (nEntry
== 1) && (rEntry
.eConnect
== SC_OR
) &&
908 (nFirstField
!= rEntry
.nField
);
910 bConflict
= pFilter
->AddEntry( rEntry
);
914 // additional tests for conflicts
915 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); !bConflict
&& (nPos
< nSize
); ++nPos
)
917 XclExpAutofilterRef xFilter
= maFilterList
.GetRecord( nPos
);
918 bConflict
= xFilter
->HasCondition() && xFilter
->HasTop10();
922 maFilterList
.RemoveAllRecords();
924 if( !maFilterList
.IsEmpty() )
925 m_pFilterMode
.reset(new XclExpFiltermode
);
926 m_pFilterInfo
.reset(new XclExpAutofilterinfo( aRange
.aStart
, nColCnt
));
928 if (maFilterList
.IsEmpty () && !bConflict
)
934 ExcAutoFilterRecs::~ExcAutoFilterRecs()
938 XclExpAutofilter
* ExcAutoFilterRecs::GetByCol( SCCOL nCol
)
940 XclExpAutofilterRef xFilter
;
941 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
943 xFilter
= maFilterList
.GetRecord( nPos
);
944 if( xFilter
->GetCol() == static_cast<sal_uInt16
>(nCol
) )
945 return xFilter
.get();
947 xFilter
.reset( new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16
>(nCol
) ) );
948 maFilterList
.AppendRecord( xFilter
);
949 return xFilter
.get();
952 bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol
)
954 for( size_t nPos
= 0, nSize
= maFilterList
.GetSize(); nPos
< nSize
; ++nPos
)
955 if( maFilterList
.GetRecord( nPos
)->GetCol() == static_cast<sal_uInt16
>(nCol
) )
960 void ExcAutoFilterRecs::AddObjRecs()
964 ScAddress
aAddr( m_pFilterInfo
->GetStartPos() );
965 for( SCCOL nObj
= 0, nCount
= m_pFilterInfo
->GetColCount(); nObj
< nCount
; nObj
++ )
967 std::unique_ptr
<XclObj
> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr
, IsFiltered( nObj
) ));
968 GetObjectManager().AddObj( std::move(pObjRec
) );
974 void ExcAutoFilterRecs::Save( XclExpStream
& rStrm
)
977 m_pFilterMode
->Save( rStrm
);
979 m_pFilterInfo
->Save( rStrm
);
980 maFilterList
.Save( rStrm
);
983 void ExcAutoFilterRecs::SaveXml( XclExpXmlStream
& rStrm
)
985 if( maFilterList
.IsEmpty() && !mbAutoFilter
)
988 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
989 rWorksheet
->startElement(XML_autoFilter
, XML_ref
, XclXmlUtils::ToOString(&rStrm
.GetRoot().GetDoc(), maRef
));
990 // OOXTODO: XML_extLst, XML_sortState
991 if( !maFilterList
.IsEmpty() )
992 maFilterList
.SaveXml( rStrm
);
993 rWorksheet
->endElement( XML_autoFilter
);
996 bool ExcAutoFilterRecs::HasFilterMode() const
998 return m_pFilterMode
!= nullptr;
1001 XclExpFilterManager::XclExpFilterManager( const XclExpRoot
& rRoot
) :
1006 void XclExpFilterManager::InitTabFilter( SCTAB nScTab
)
1008 maFilterMap
[ nScTab
].reset( new ExcAutoFilterRecs( GetRoot(), nScTab
, nullptr ) );
1011 XclExpRecordRef
XclExpFilterManager::CreateRecord( SCTAB nScTab
)
1013 XclExpTabFilterRef xRec
;
1014 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1015 if( aIt
!= maFilterMap
.end() )
1023 bool XclExpFilterManager::HasFilterMode( SCTAB nScTab
)
1025 XclExpTabFilterRef xRec
;
1026 XclExpTabFilterMap::iterator aIt
= maFilterMap
.find( nScTab
);
1027 if( aIt
!= maFilterMap
.end() )
1029 return aIt
->second
->HasFilterMode();
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */