use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / excrecds.cxx
blobf9e5961424739a82896ef547b825a9d0d42140c1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <map>
23 #include <filter/msfilter/countryid.hxx>
25 #include <svl/numformat.hxx>
26 #include <sal/log.hxx>
27 #include <sax/fastattribs.hxx>
29 #include <string.h>
31 #include <global.hxx>
32 #include <document.hxx>
33 #include <dbdata.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>
40 #include <root.hxx>
42 #include <xeescher.hxx>
43 #include <xelink.hxx>
44 #include <xename.hxx>
45 #include <xlname.hxx>
46 #include <xestyle.hxx>
48 #include <xcl97rec.hxx>
49 #include <tabprotection.hxx>
50 #include <scitems.hxx>
51 #include <attrib.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
86 0x62, 0x50, 0x3f,
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 )
105 SaveCont( rStrm );
108 void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
112 //--------------------------------------------------------- class ExcEmptyRec -
114 void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
118 sal_uInt16 ExcEmptyRec::GetNum() const
120 return 0;
123 std::size_t ExcEmptyRec::GetLen() const
125 return 0;
128 //--------------------------------------------------------- class ExcDummyRec -
130 void ExcDummyRec::Save( XclExpStream& rStrm )
132 rStrm.Write( GetData(), GetLen() ); // raw write mode
135 sal_uInt16 ExcDummyRec::GetNum() const
137 return 0x0000;
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
149 return 2;
152 //--------------------------------------------------------- class ExcBof_Base -
154 ExcBof_Base::ExcBof_Base()
155 : nDocType(0)
156 , nVers(0)
157 , nRupBuild(0x096C) // copied from Excel
158 , nRupYear(0x07C9) // copied from Excel
162 //-------------------------------------------------------------- class ExcBof -
164 ExcBof::ExcBof()
166 nDocType = 0x0010;
167 nVers = 0x0500;
170 void ExcBof::SaveCont( XclExpStream& rStrm )
172 rStrm << nVers << nDocType << nRupBuild << nRupYear;
175 sal_uInt16 ExcBof::GetNum() const
177 return 0x0809;
180 std::size_t ExcBof::GetLen() const
182 return 8;
185 //------------------------------------------------------------- class ExcBofW -
187 ExcBofW::ExcBofW()
189 nDocType = 0x0005;
190 nVers = 0x0500;
193 void ExcBofW::SaveCont( XclExpStream& rStrm )
195 rStrm << nVers << nDocType << nRupBuild << nRupYear;
198 sal_uInt16 ExcBofW::GetNum() const
200 return 0x0809;
203 std::size_t ExcBofW::GetLen() const
205 return 8;
208 //-------------------------------------------------------------- class ExcEof -
210 sal_uInt16 ExcEof::GetNum() const
212 return 0x000A;
215 std::size_t ExcEof::GetLen() const
217 return 0;
220 //--------------------------------------------------------- class ExcDummy_00 -
222 std::size_t ExcDummy_00::GetLen() const
224 return nMyLen;
227 const sal_uInt8* ExcDummy_00::GetData() const
229 return pMyData;
232 //-------------------------------------------------------- class ExcDummy_04x -
234 std::size_t ExcDummy_040::GetLen() const
236 return nMyLen;
239 const sal_uInt8* ExcDummy_040::GetData() const
241 return pMyData;
244 std::size_t ExcDummy_041::GetLen() const
246 return nMyLen;
249 const sal_uInt8* ExcDummy_041::GetData() const
251 return pMyData;
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
265 return 0x0022;
268 void Exc1904::SaveXml( XclExpXmlStream& rStrm )
270 bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
272 if( bISOIEC )
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 ),
289 nTab( nTabNum )
293 ExcBundlesheetBase::ExcBundlesheetBase() :
294 m_nStrPos( STREAM_SEEK_TO_END ),
295 m_nOwnPos( STREAM_SEEK_TO_END ),
296 nGrbit( 0x0000 ),
297 nTab( SCTAB_GLOBAL )
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
311 return 0x0085;
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)
326 << nGrbit;
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
339 return nMyLen;
342 const sal_uInt8* ExcDummy_02a::GetData() const
344 return pMyData;
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 )
367 if( bFitToPages )
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
395 if (!mbSummaryBelow)
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),
426 mnTab(nTab)
430 void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
432 ScDocument& rDoc = rStrm.GetRoot().GetDoc();
433 const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
434 if ( !pTabProtect )
435 return;
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())
441 aPH = rPH;
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]) ),
450 16 );
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 ),
459 XML_password, sHash,
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());
477 if (rProts.empty())
478 return;
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),
507 mnHash(0x0000)
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)
522 rStrm << mnHash;
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 )
553 nType = nTp;
554 nOper = nOp;
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);
566 else
567 rStrm << sal_uInt32(0) << sal_uInt32(0);
570 static const char* lcl_GetOperator( sal_uInt8 nOper )
572 switch( 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);
589 else
590 return OString();
593 void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm )
595 if( IsEmpty() )
596 return;
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 ),
615 XclExpRoot( rRoot ),
616 meType(bIsEmpty ? Empty : FilterCondition),
617 nCol( nC ),
618 bIsButtonHidden( false ),
619 nFlags( 0 ),
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() )
628 return false;
630 sal_uInt16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1;
632 if( nInd == 1 )
633 nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND;
634 if( bSimple )
635 nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2;
637 aCond[ nInd ].SetCondition( nType, nOp, pText );
639 AddRecSize( aCond[ nInd ].GetTextBytes() );
641 return true;
644 bool XclExpAutofilter::HasCondition() const
646 return !aCond[0].IsEmpty();
649 bool XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry )
651 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
653 if (rItems.empty())
655 if (GetOutput() != EXC_OUTPUT_BINARY)
657 // tdf#123353 XLSX export
658 meType = BlankValue;
659 return false;
661 // XLS export
662 return true;
665 if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
667 AddMultiValueEntry(rEntry);
668 return false;
671 bool bConflict = false;
672 OUString sText;
673 const ScQueryEntry::Item& rItem = rItems[0];
674 if (!rItem.maString.isEmpty())
676 sText = rItem.maString.getString();
677 switch( rEntry.eOp )
679 case SC_CONTAINS:
680 case SC_DOES_NOT_CONTAIN:
682 sText = "*" + sText + "*";
684 break;
685 case SC_BEGINS_WITH:
686 case SC_DOES_NOT_BEGIN_WITH:
687 sText += "*";
688 break;
689 case SC_ENDS_WITH:
690 case SC_DOES_NOT_END_WITH:
691 sText = "*" + sText;
692 break;
693 default:
695 //nothing
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);
712 // other conditions
713 else
715 // top10 flags
716 sal_uInt16 nNewFlags = 0x0000;
717 switch( rEntry.eOp )
719 case SC_TOPVAL:
720 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
721 break;
722 case SC_BOTVAL:
723 nNewFlags = EXC_AFFLAG_TOP10;
724 break;
725 case SC_TOPPERC:
726 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
727 break;
728 case SC_BOTPERC:
729 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
730 break;
731 default:;
733 bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
735 bConflict = HasTop10() && bNewTop10;
736 if( !bConflict )
738 if( bNewTop10 )
740 sal_uInt32 nIndex = 0;
741 double fVal = 0.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);
749 // normal condition
750 else
752 if (GetOutput() != EXC_OUTPUT_BINARY && rEntry.eOp == SC_EQUAL)
754 AddMultiValueEntry(rEntry);
755 return false;
758 sal_uInt8 nOper = EXC_AFOPER_NONE;
760 switch( rEntry.eOp )
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;
768 case SC_CONTAINS:
769 case SC_BEGINS_WITH:
770 case SC_ENDS_WITH:
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;
776 default:;
778 bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_STRING, nOper, &sText);
782 return bConflict;
785 void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
787 meType = MultiValue;
788 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
789 for (const auto& rItem : rItems)
791 if( rItem.maString.isEmpty() )
792 bHasBlankValue = true;
793 else
794 maMultiValues.push_back(std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate));
798 void XclExpAutofilter::AddColorEntry(const ScQueryEntry& rEntry)
800 meType = ColorValue;
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())
825 return;
827 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
829 std::optional<OString> sHiddenButtonValue;
830 if (bIsButtonHidden)
831 sHiddenButtonValue = "1";
833 rWorksheet->startElement( XML_filterColumn,
834 XML_colId, OString::number(nCol),
835 XML_hiddenButton, sHiddenButtonValue
838 switch (meType)
840 case FilterCondition:
842 if( HasTop10() )
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
851 else
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
861 break;
862 case ColorValue:
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));
873 else
875 pAttrList->add(XML_cellColor, OString::number(0));
877 pAttrList->add(XML_dxfId, OString::number(GetDxfs().GetDxfByColor(color)));
878 rWorksheet->singleElement(XML_colorFilter, pAttrList);
881 break;
882 case BlankValue:
884 rWorksheet->singleElement(XML_filters, XML_blank, "1");
886 break;
887 case MultiValue:
889 if( bHasBlankValue )
890 rWorksheet->startElement(XML_filters, XML_blank, "1");
891 else
892 rWorksheet->startElement(XML_filters);
894 for (const auto& rMultiValue : maMultiValues)
896 if( !rMultiValue.second )
898 rWorksheet->singleElement(XML_filter, XML_val, rMultiValue.first);
900 else
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 };
905 sal_Int32 idx = 0;
906 for (size_t i = 0; idx >= 0 && i < 3; i++)
908 OString kw = aStr.getToken(0, '-', idx);
909 kw = kw.trim();
910 if (!kw.isEmpty())
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);
923 break;
924 // Used for constructing an empty filterColumn element for exporting the XML_hiddenButton attribute
925 case Empty: break;
927 rWorksheet->endElement( XML_filterColumn );
930 ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
931 XclExpRoot( rRoot ),
932 mbAutoFilter (false)
934 XclExpNameManager& rNameMgr = GetNameManager();
936 bool bFound = false;
937 bool bAdvanced = false;
938 const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
939 ScRange aAdvRange;
940 if (pData)
942 bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
943 bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
945 if( !bFound )
946 return;
948 ScQueryParam aParam;
949 pData->GetQueryParam( aParam );
951 ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab,
952 aParam.nCol2, aParam.nRow2, aParam.nTab );
953 aRange.PutInOrder();
954 SCCOL nColCnt = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
956 maRef = aRange;
958 // #i2394# built-in defined names must be sorted by containing sheet name
959 if (!pDefinedData)
960 rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
962 // advanced filter
963 if( bAdvanced )
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;
980 // AutoFilter
981 else
983 bool bConflict = false;
984 bool bContLoop = true;
985 bool bHasOr = false;
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;
996 if( bContLoop )
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 );
1004 if( nEntry > 0 )
1005 bHasOr |= (rEntry.eConnect == SC_OR);
1007 bConflict = (nEntry > 1) && bHasOr;
1008 if( !bConflict )
1009 bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
1010 (nFirstField != rEntry.nField);
1011 if( !bConflict )
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;
1032 break;
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();
1051 if( bConflict )
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)
1082 if (rKey.bDoSort)
1084 nField = rKey.nField;
1085 bSortAscending = rKey.bAscending;
1086 break;
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) )
1124 return true;
1125 return false;
1128 void ExcAutoFilterRecs::AddObjRecs()
1130 if( m_pFilterInfo )
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) );
1137 aAddr.IncCol();
1142 void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
1144 if( m_pFilterMode )
1145 m_pFilterMode->Save( rStrm );
1146 if( m_pFilterInfo )
1147 m_pFilterInfo->Save( rStrm );
1148 maFilterList.Save( rStrm );
1151 void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
1153 if( maFilterList.IsEmpty() && !mbAutoFilter )
1154 return;
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));
1174 else
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 ) :
1193 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() )
1208 xRec = aIt->second;
1209 xRec->AddObjRecs();
1211 return xRec;
1214 bool XclExpFilterManager::HasFilterMode( SCTAB nScTab )
1216 XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
1217 if( aIt != maFilterMap.end() )
1219 return aIt->second->HasFilterMode();
1221 return false;
1224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */