Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / excrecds.cxx
blob4fbe955d8b5235f4dee9ab6be0442b9ebacde31a
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/zforlist.hxx>
26 #include <sal/log.hxx>
28 #include <string.h>
30 #include <global.hxx>
31 #include <document.hxx>
32 #include <dbdata.hxx>
33 #include <oox/export/utils.hxx>
34 #include <oox/token/tokens.hxx>
35 #include <queryentry.hxx>
36 #include <queryparam.hxx>
37 #include <root.hxx>
39 #include <xeescher.hxx>
40 #include <xelink.hxx>
41 #include <xename.hxx>
42 #include <xlname.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
80 0x62, 0x50, 0x3f,
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 )
99 SaveCont( rStrm );
102 void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
106 //--------------------------------------------------------- class ExcEmptyRec -
108 void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
112 sal_uInt16 ExcEmptyRec::GetNum() const
114 return 0;
117 std::size_t ExcEmptyRec::GetLen() const
119 return 0;
122 //--------------------------------------------------------- class ExcDummyRec -
124 void ExcDummyRec::Save( XclExpStream& rStrm )
126 rStrm.Write( GetData(), GetLen() ); // raw write mode
129 sal_uInt16 ExcDummyRec::GetNum() const
131 return 0x0000;
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
143 return 2;
146 //--------------------------------------------------------- class ExcBof_Base -
148 ExcBof_Base::ExcBof_Base()
149 : nDocType(0)
150 , nVers(0)
151 , nRupBuild(0x096C) // copied from Excel
152 , nRupYear(0x07C9) // copied from Excel
156 //-------------------------------------------------------------- class ExcBof -
158 ExcBof::ExcBof()
160 nDocType = 0x0010;
161 nVers = 0x0500;
164 void ExcBof::SaveCont( XclExpStream& rStrm )
166 rStrm << nVers << nDocType << nRupBuild << nRupYear;
169 sal_uInt16 ExcBof::GetNum() const
171 return 0x0809;
174 std::size_t ExcBof::GetLen() const
176 return 8;
179 //------------------------------------------------------------- class ExcBofW -
181 ExcBofW::ExcBofW()
183 nDocType = 0x0005;
184 nVers = 0x0500;
187 void ExcBofW::SaveCont( XclExpStream& rStrm )
189 rStrm << nVers << nDocType << nRupBuild << nRupYear;
192 sal_uInt16 ExcBofW::GetNum() const
194 return 0x0809;
197 std::size_t ExcBofW::GetLen() const
199 return 8;
202 //-------------------------------------------------------------- class ExcEof -
204 sal_uInt16 ExcEof::GetNum() const
206 return 0x000A;
209 std::size_t ExcEof::GetLen() const
211 return 0;
214 //--------------------------------------------------------- class ExcDummy_00 -
216 std::size_t ExcDummy_00::GetLen() const
218 return nMyLen;
221 const sal_uInt8* ExcDummy_00::GetData() const
223 return pMyData;
226 //-------------------------------------------------------- class ExcDummy_04x -
228 std::size_t ExcDummy_040::GetLen() const
230 return nMyLen;
233 const sal_uInt8* ExcDummy_040::GetData() const
235 return pMyData;
238 std::size_t ExcDummy_041::GetLen() const
240 return nMyLen;
243 const sal_uInt8* ExcDummy_041::GetData() const
245 return pMyData;
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
259 return 0x0022;
262 void Exc1904::SaveXml( XclExpXmlStream& rStrm )
264 bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
266 if( bISOIEC )
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 ),
283 nTab( nTabNum )
287 ExcBundlesheetBase::ExcBundlesheetBase() :
288 m_nStrPos( STREAM_SEEK_TO_END ),
289 m_nOwnPos( STREAM_SEEK_TO_END ),
290 nGrbit( 0x0000 ),
291 nTab( SCTAB_GLOBAL )
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
305 return 0x0085;
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)
320 << nGrbit;
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
333 return nMyLen;
336 const sal_uInt8* ExcDummy_02a::GetData() const
338 return pMyData;
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 )
361 if( bFitToPages )
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),
418 mnTab(nTab)
422 void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
424 ScDocument& rDoc = rStrm.GetRoot().GetDoc();
425 const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
426 if ( pTabProtect )
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())
432 aPH = rPH;
434 Sequence<sal_Int8> aHash = pTabProtect->getPasswordHash(PASSHASH_XL);
435 OString sHash;
436 if (aHash.getLength() >= 2)
438 sHash = OString::number(
439 ( static_cast<sal_uInt8>(aHash[0]) << 8
440 | static_cast<sal_uInt8>(aHash[1]) ),
441 16 );
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());
468 if (!rProts.empty())
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),
496 mnHash(0x0000)
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)
511 rStrm << mnHash;
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 ),
528 fVal( 0.0 )
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 )
543 nType = nTp;
544 nOper = nOp;
545 fVal = fV;
546 pText.reset( pT ? new XclExpString( *pT, XclStrFlags::EightBitLength ) : nullptr);
549 void ExcFilterCondition::Save( XclExpStream& rStrm )
551 rStrm << nType << nOper;
552 switch( nType )
554 case EXC_AFTYPE_DOUBLE:
555 rStrm << fVal;
556 break;
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);
560 break;
561 case EXC_AFTYPE_BOOLERR:
562 rStrm << sal_uInt8(0) << static_cast<sal_uInt8>((fVal != 0) ? 1 : 0) << sal_uInt32(0) << sal_uInt16(0);
563 break;
564 default:
565 rStrm << sal_uInt32(0) << sal_uInt32(0);
569 static const char* lcl_GetOperator( sal_uInt8 nOper )
571 switch( 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 )
586 switch( nType )
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 )
597 if( IsEmpty() )
598 return;
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 ),
617 XclExpRoot( rRoot ),
618 meType(FilterCondition),
619 nCol( nC ),
620 nFlags( 0 )
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() )
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, fVal, 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();
652 if (rItems.empty())
653 return true;
655 if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
657 AddMultiValueEntry(rEntry);
658 return false;
661 bool bConflict = false;
662 OUString sText;
663 const ScQueryEntry::Item& rItem = rItems[0];
664 if (!rItem.maString.isEmpty())
666 sText = rItem.maString.getString();
667 switch( rEntry.eOp )
669 case SC_CONTAINS:
670 case SC_DOES_NOT_CONTAIN:
672 sText = "*" + sText + "*";
674 break;
675 case SC_BEGINS_WITH:
676 case SC_DOES_NOT_BEGIN_WITH:
677 sText += "*";
678 break;
679 case SC_ENDS_WITH:
680 case SC_DOES_NOT_END_WITH:
681 sText = "*" + sText;
682 break;
683 default:
685 //nothing
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 );
697 // other conditions
698 else
700 double fVal = 0.0;
701 sal_uInt32 nIndex = 0;
702 bool bIsNum = !bLen || GetFormatter().IsNumberFormat( sText, nIndex, fVal );
703 OUString* pText = nullptr;
704 if (!bIsNum)
705 pText = &sText;
707 // top10 flags
708 sal_uInt16 nNewFlags = 0x0000;
709 switch( rEntry.eOp )
711 case SC_TOPVAL:
712 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
713 break;
714 case SC_BOTVAL:
715 nNewFlags = EXC_AFFLAG_TOP10;
716 break;
717 case SC_TOPPERC:
718 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
719 break;
720 case SC_BOTPERC:
721 nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
722 break;
723 default:;
725 bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
727 bConflict = HasTop10() && bNewTop10;
728 if( !bConflict )
730 if( bNewTop10 )
732 if( fVal < 0 ) fVal = 0;
733 if( fVal >= 501 ) fVal = 500;
734 nFlags |= (nNewFlags | static_cast<sal_uInt16>(fVal) << 7);
736 // normal condition
737 else
739 sal_uInt8 nType = bIsNum ? EXC_AFTYPE_DOUBLE : EXC_AFTYPE_STRING;
740 sal_uInt8 nOper = EXC_AFOPER_NONE;
742 switch( rEntry.eOp )
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;
750 case SC_CONTAINS:
751 case SC_BEGINS_WITH:
752 case SC_ENDS_WITH:
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;
758 default:;
760 bConflict = !AddCondition( rEntry.eConnect, nType, nOper, fVal, pText );
764 return bConflict;
767 void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
769 meType = MultiValue;
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())
787 return;
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
797 switch (meType)
799 case FilterCondition:
801 if( HasTop10() )
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
819 break;
820 case MultiValue:
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);
831 break;
833 rWorksheet->endElement( XML_filterColumn );
836 ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
837 XclExpRoot( rRoot ),
838 mbAutoFilter (false)
840 XclExpNameManager& rNameMgr = GetNameManager();
842 bool bFound = false;
843 bool bAdvanced = false;
844 const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
845 ScRange aAdvRange;
846 if (pData)
848 bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
849 bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
851 if( bFound )
853 ScQueryParam aParam;
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;
860 maRef = aRange;
862 // #i2394# built-in defined names must be sorted by containing sheet name
863 if (!pDefinedData)
864 rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
866 // advanced filter
867 if( bAdvanced )
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);
884 // AutoFilter
885 else
887 bool bConflict = false;
888 bool bContLoop = true;
889 bool bHasOr = false;
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;
898 if( bContLoop )
900 XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() );
902 if( nEntry > 0 )
903 bHasOr |= (rEntry.eConnect == SC_OR);
905 bConflict = (nEntry > 1) && bHasOr;
906 if( !bConflict )
907 bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
908 (nFirstField != rEntry.nField);
909 if( !bConflict )
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();
921 if( bConflict )
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)
929 mbAutoFilter = true;
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) )
956 return true;
957 return false;
960 void ExcAutoFilterRecs::AddObjRecs()
962 if( m_pFilterInfo )
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) );
969 aAddr.IncCol();
974 void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
976 if( m_pFilterMode )
977 m_pFilterMode->Save( rStrm );
978 if( m_pFilterInfo )
979 m_pFilterInfo->Save( rStrm );
980 maFilterList.Save( rStrm );
983 void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
985 if( maFilterList.IsEmpty() && !mbAutoFilter )
986 return;
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 ) :
1002 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() )
1017 xRec = aIt->second;
1018 xRec->AddObjRecs();
1020 return xRec;
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();
1031 return false;
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */