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