use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xecontent.cxx
blobaad8a9cdcfdea6f3a18c2bf8bcb5ca4f75b2a0af
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 <memory>
21 #include <utility>
22 #include <xecontent.hxx>
24 #include <vector>
25 #include <algorithm>
26 #include <string_view>
28 #include <com/sun/star/frame/XModel.hpp>
29 #include <com/sun/star/sheet/XAreaLinks.hpp>
30 #include <com/sun/star/sheet/XAreaLink.hpp>
31 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <sfx2/objsh.hxx>
34 #include <tools/urlobj.hxx>
35 #include <formula/grammar.hxx>
36 #include <scitems.hxx>
37 #include <editeng/flditem.hxx>
38 #include <document.hxx>
39 #include <docsh.hxx>
40 #include <validat.hxx>
41 #include <unonames.hxx>
42 #include <convuno.hxx>
43 #include <rangenam.hxx>
44 #include <tokenarray.hxx>
45 #include <stlpool.hxx>
46 #include <patattr.hxx>
47 #include <fapihelper.hxx>
48 #include <xehelper.hxx>
49 #include <xestyle.hxx>
50 #include <xename.hxx>
51 #include <xlcontent.hxx>
52 #include <xltools.hxx>
53 #include <xeformula.hxx>
54 #include <rtl/uuid.h>
55 #include <sal/log.hxx>
56 #include <oox/export/utils.hxx>
57 #include <oox/token/namespaces.hxx>
58 #include <oox/token/relationship.hxx>
59 #include <comphelper/string.hxx>
60 #include <o3tl/string_view.hxx>
62 using namespace ::oox;
64 using ::com::sun::star::uno::Reference;
65 using ::com::sun::star::uno::UNO_QUERY;
66 using ::com::sun::star::table::CellRangeAddress;
67 using ::com::sun::star::sheet::XAreaLinks;
68 using ::com::sun::star::sheet::XAreaLink;
70 // Shared string table ========================================================
72 namespace {
74 /** A single string entry in the hash table. */
75 struct XclExpHashEntry
77 const XclExpString* mpString; /// Pointer to the string (no ownership).
78 sal_uInt32 mnSstIndex; /// The SST index of this string.
79 explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
80 mpString( pString ), mnSstIndex( nSstIndex ) {}
83 /** Function object for strict weak ordering. */
84 struct XclExpHashEntrySWO
86 bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
87 { return *rLeft.mpString < *rRight.mpString; }
92 /** Implementation of the SST export.
93 @descr Stores all passed strings in a hash table and prevents repeated
94 insertion of equal strings. */
95 class XclExpSstImpl
97 public:
98 explicit XclExpSstImpl();
100 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
101 sal_uInt32 Insert( XclExpStringRef xString );
103 /** Writes the complete SST and EXTSST records. */
104 void Save( XclExpStream& rStrm );
105 void SaveXml( XclExpXmlStream& rStrm );
107 private:
108 typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
110 std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
111 std::vector< XclExpHashVec >
112 maHashTab; /// Hashed table that manages string pointers.
113 sal_uInt32 mnTotal; /// Total count of strings (including doubles).
114 sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
117 const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
119 XclExpSstImpl::XclExpSstImpl() :
120 maHashTab( EXC_SST_HASHTABLE_SIZE ),
121 mnTotal( 0 ),
122 mnSize( 0 )
126 sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
128 OSL_ENSURE( xString, "XclExpSstImpl::Insert - empty pointer not allowed" );
129 if( !xString )
130 xString.reset( new XclExpString );
132 ++mnTotal;
133 sal_uInt32 nSstIndex = 0;
135 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
136 sal_uInt16 nHash = xString->GetHash();
137 nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
139 XclExpHashVec& rVec = maHashTab[ nHash ];
140 XclExpHashEntry aEntry( xString.get(), mnSize );
141 XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
142 if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
144 nSstIndex = mnSize;
145 maStringVector.push_back( xString );
146 rVec.insert( aIt, aEntry );
147 ++mnSize;
149 else
151 nSstIndex = aIt->mnSstIndex;
154 return nSstIndex;
157 void XclExpSstImpl::Save( XclExpStream& rStrm )
159 if( maStringVector.empty() )
160 return;
162 SvMemoryStream aExtSst( 8192 );
164 sal_uInt32 nBucket = mnSize;
165 while( nBucket > 0x0100 )
166 nBucket /= 2;
168 sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
169 sal_uInt16 nBucketIndex = 0;
171 // *** write the SST record ***
173 rStrm.StartRecord( EXC_ID_SST, 8 );
175 rStrm << mnTotal << mnSize;
176 for (auto const& elem : maStringVector)
178 if( !nBucketIndex )
180 // write bucket info before string to get correct record position
181 sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
182 sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
183 aExtSst.WriteUInt32( nStrmPos ) // stream position
184 .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
185 .WriteUInt16( 0 ); // reserved
188 rStrm << *elem;
190 if( ++nBucketIndex == nPerBucket )
191 nBucketIndex = 0;
194 rStrm.EndRecord();
196 // *** write the EXTSST record ***
198 rStrm.StartRecord( EXC_ID_EXTSST, 0 );
200 rStrm << nPerBucket;
201 rStrm.SetSliceSize( 8 ); // size of one bucket info
202 aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
203 rStrm.CopyFromStream( aExtSst );
205 rStrm.EndRecord();
208 void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
210 if( maStringVector.empty() )
211 return;
213 sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
214 u"xl/sharedStrings.xml"_ustr,
215 u"sharedStrings.xml",
216 rStrm.GetCurrentStream()->getOutputStream(),
217 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
218 oox::getRelationship(Relationship::SHAREDSTRINGS));
219 rStrm.PushStream( pSst );
221 pSst->startElement( XML_sst,
222 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
223 XML_count, OString::number(mnTotal),
224 XML_uniqueCount, OString::number(mnSize) );
226 for (auto const& elem : maStringVector)
228 pSst->startElement(XML_si);
229 elem->WriteXml( rStrm );
230 pSst->endElement( XML_si );
233 pSst->endElement( XML_sst );
235 rStrm.PopStream();
238 XclExpSst::XclExpSst() :
239 mxImpl( new XclExpSstImpl )
243 XclExpSst::~XclExpSst()
247 sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
249 return mxImpl->Insert( xString );
252 void XclExpSst::Save( XclExpStream& rStrm )
254 mxImpl->Save( rStrm );
257 void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
259 mxImpl->SaveXml( rStrm );
262 // Merged cells ===============================================================
264 XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
265 XclExpRoot( rRoot )
269 void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
271 if( GetBiff() == EXC_BIFF8 )
273 maMergedRanges.push_back( rRange );
274 maBaseXFIds.push_back( nBaseXFId );
278 sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
280 OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
281 ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
282 ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
283 for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
285 const ScRange & rScRange = rNCRanges[ i ];
286 if( rScRange.Contains( rPos ) )
287 return *aIt;
289 return EXC_XFID_NOTFOUND;
292 void XclExpMergedcells::Save( XclExpStream& rStrm )
294 if( GetBiff() != EXC_BIFF8 )
295 return;
297 XclRangeList aXclRanges;
298 GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
299 size_t nFirstRange = 0;
300 size_t nRemainingRanges = aXclRanges.size();
301 while( nRemainingRanges > 0 )
303 size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
304 rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
305 aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
306 rStrm.EndRecord();
307 nFirstRange += nRangeCount;
308 nRemainingRanges -= nRangeCount;
312 void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
314 size_t nCount = maMergedRanges.size();
315 if( !nCount )
316 return;
317 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
318 rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
319 for( size_t i = 0; i < nCount; ++i )
321 const ScRange & rRange = maMergedRanges[ i ];
322 rWorksheet->singleElement(XML_mergeCell, XML_ref,
323 XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rRange));
325 rWorksheet->endElement( XML_mergeCells );
328 // Hyperlinks =================================================================
330 XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
331 XclExpRecord( EXC_ID_HLINK ),
332 maScPos( rScPos ),
333 mxVarData( new SvMemoryStream ),
334 mnFlags( 0 )
336 const OUString& rUrl = rUrlField.GetURL();
337 const OUString& rRepr = rUrlField.GetRepresentation();
338 INetURLObject aUrlObj( rUrl );
339 const INetProtocol eProtocol = aUrlObj.GetProtocol();
340 bool bWithRepr = !rRepr.isEmpty();
341 XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
343 // description
344 if( bWithRepr )
346 XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
347 aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
348 aDescr.WriteBuffer( aXclStrm ); // NO flags
349 aXclStrm << sal_uInt16( 0 );
351 mnFlags |= EXC_HLINK_DESCR;
352 m_Repr = rRepr;
355 // file link or URL
356 if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
358 sal_uInt16 nLevel;
359 bool bRel;
360 OUString aFileName(
361 BuildFileName(nLevel, bRel, rUrl, rRoot, rRoot.GetOutput() == EXC_OUTPUT_XML_2007));
363 if( eProtocol == INetProtocol::Smb )
365 // #n382718# (and #n261623#) Convert smb notation to '\\'
366 aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
367 aFileName = aFileName.copy(4); // skip the 'smb:' part
368 aFileName = aFileName.replace('/', '\\');
371 if( !bRel )
372 mnFlags |= EXC_HLINK_ABS;
373 mnFlags |= EXC_HLINK_BODY;
375 OString aAsciiLink(OUStringToOString(aFileName,
376 rRoot.GetTextEncoding()));
377 XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
378 aXclStrm << XclTools::maGuidFileMoniker
379 << nLevel
380 << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
381 aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
382 aXclStrm << sal_uInt8( 0 )
383 << sal_uInt32( 0xDEADFFFF );
384 aXclStrm.WriteZeroBytes( 20 );
385 aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
386 << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
387 << sal_uInt16( 0x0003 );
388 aLink.WriteBuffer( aXclStrm ); // NO flags
390 if (m_Repr.isEmpty())
391 m_Repr = aFileName;
393 msTarget = XclXmlUtils::ToOUString( aLink );
395 if( bRel )
397 for( int i = 0; i < nLevel; ++i )
398 msTarget = "../" + msTarget;
400 else if (rRoot.GetOutput() != EXC_OUTPUT_XML_2007)
402 // xls expects the file:/// part appended ( or at least
403 // ms2007 does, ms2010 is more tolerant )
404 msTarget = "file:///" + msTarget;
407 else if( eProtocol != INetProtocol::NotValid )
409 XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
410 aXclStrm << XclTools::maGuidUrlMoniker
411 << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
412 aUrl.WriteBuffer( aXclStrm ); // NO flags
413 aXclStrm << sal_uInt16( 0 );
415 mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
416 if (m_Repr.isEmpty())
417 m_Repr = rUrl;
419 msTarget = XclXmlUtils::ToOUString( aUrl );
421 else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
423 OUString aTextMark( rUrl.copy( 1 ) );
425 sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
426 sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
427 // last dot is the separator, if there is no ! after it
428 if(nSepPos < nPointPos)
430 nSepPos = nPointPos;
431 aTextMark = aTextMark.replaceAt( nSepPos, 1, u"!" );
434 if (nSepPos != -1)
436 std::u16string_view aSheetName(aTextMark.subView(0, nSepPos));
438 if (aSheetName.find(' ') != std::u16string_view::npos && aSheetName[0] != '\'')
440 aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, u"'");
443 else
445 SCTAB nTab;
446 if (rRoot.GetDoc().GetTable(aTextMark, nTab))
447 aTextMark += "!A1"; // tdf#143220 link to sheet not valid without cell reference
450 mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
453 // text mark
454 if( !mxTextMark && aUrlObj.HasMark() )
455 mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
457 if( mxTextMark )
459 aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
460 mxTextMark->WriteBuffer( aXclStrm ); // NO flags
461 aXclStrm << sal_uInt16( 0 );
463 mnFlags |= EXC_HLINK_MARK;
465 OUString location = XclXmlUtils::ToOUString(*mxTextMark);
466 if (!location.isEmpty() && msTarget.endsWith(Concat2View("#" + location)))
467 msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
470 SetRecSize( 32 + mxVarData->Tell() );
473 XclExpHyperlink::~XclExpHyperlink()
477 OUString XclExpHyperlink::BuildFileName(
478 sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
480 INetURLObject aURLObject( rUrl );
481 OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
482 : aURLObject.getFSysPath(FSysStyle::Dos));
483 rnLevel = 0;
484 rbRel = rRoot.IsRelUrl();
486 if( rbRel )
488 // try to convert to relative file name
489 OUString aTmpName( aDosName );
490 aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
491 INetURLObject::EncodeMechanism::WasEncoded,
492 (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
494 if (aDosName.startsWith(INET_FILE_SCHEME))
496 // not converted to rel -> back to old, return absolute flag
497 aDosName = aTmpName;
498 rbRel = false;
500 else if (aDosName.startsWith("./"))
502 aDosName = aDosName.copy(2);
504 else
506 while (aDosName.startsWith("../"))
508 aDosName = aDosName.copy(3);
509 ++rnLevel;
513 return aDosName;
516 void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
518 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
519 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
520 rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
521 WriteEmbeddedData( rStrm );
524 void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
526 rStrm << XclTools::maGuidStdLink
527 << sal_uInt32( 2 )
528 << mnFlags;
530 mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
531 rStrm.CopyFromStream( *mxVarData );
534 void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
536 OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
537 oox::getRelationship(Relationship::HYPERLINK),
538 msTarget, true ) : OUString();
539 std::optional<OString> sTextMark;
540 if (mxTextMark)
541 sTextMark = XclXmlUtils::ToOString(*mxTextMark);
542 rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
543 XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(maScPos)),
544 FSNS( XML_r, XML_id ), sax_fastparser::UseIf(sId, !sId.isEmpty()),
545 XML_location, sTextMark,
546 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
547 XML_display, m_Repr );
550 // Label ranges ===============================================================
552 XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
553 XclExpRoot( rRoot )
555 SCTAB nScTab = GetCurrScTab();
556 // row label ranges
557 FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
558 // row labels only over 1 column (restriction of Excel97/2000/XP)
559 for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
561 ScRange & rScRange = maRowRanges[ i ];
562 if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
563 rScRange.aEnd.SetCol( rScRange.aStart.Col() );
565 // col label ranges
566 FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
569 void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
570 const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
572 for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
574 const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
575 const ScRange& rScRange = rRangePair.GetRange( 0 );
576 if( rScRange.aStart.Tab() == nScTab )
577 rScRanges.push_back( rScRange );
581 void XclExpLabelranges::Save( XclExpStream& rStrm )
583 XclExpAddressConverter& rAddrConv = GetAddressConverter();
584 XclRangeList aRowXclRanges, aColXclRanges;
585 rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
586 rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
587 if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
589 rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
590 rStrm << aRowXclRanges << aColXclRanges;
591 rStrm.EndRecord();
595 // Conditional formatting ====================================================
597 /** Represents a CF record that contains one condition of a conditional format. */
598 class XclExpCFImpl : protected XclExpRoot
600 public:
601 explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
603 /** Writes the body of the CF record. */
604 void WriteBody( XclExpStream& rStrm );
605 void SaveXml( XclExpXmlStream& rStrm );
607 private:
608 const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
609 ScAddress maOrigin; /// Top left cell of the combined range
610 XclFontData maFontData; /// Font formatting attributes.
611 XclExpCellBorder maBorder; /// Border formatting attributes.
612 XclExpCellArea maArea; /// Pattern formatting attributes.
613 XclTokenArrayRef mxTokArr1; /// Formula for first condition.
614 XclTokenArrayRef mxTokArr2; /// Formula for second condition.
615 sal_uInt32 mnFontColorId; /// Font color ID.
616 sal_uInt8 mnType; /// Type of the condition (cell/formula).
617 sal_uInt8 mnOperator; /// Comparison operator for cell type.
618 sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
619 bool mbFontUsed; /// true = Any font attribute used.
620 bool mbHeightUsed; /// true = Font height used.
621 bool mbWeightUsed; /// true = Font weight used.
622 bool mbColorUsed; /// true = Font color used.
623 bool mbUnderlUsed; /// true = Font underline type used.
624 bool mbItalicUsed; /// true = Font posture used.
625 bool mbStrikeUsed; /// true = Font strikeout used.
626 bool mbBorderUsed; /// true = Border attribute used.
627 bool mbPattUsed; /// true = Pattern attribute used.
628 bool mbFormula2;
631 XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
632 XclExpRoot( rRoot ),
633 mrFormatEntry( rFormatEntry ),
634 maOrigin( aOrigin ),
635 mnFontColorId( 0 ),
636 mnType( EXC_CF_TYPE_CELL ),
637 mnOperator( EXC_CF_CMP_NONE ),
638 mnPriority( nPriority ),
639 mbFontUsed( false ),
640 mbHeightUsed( false ),
641 mbWeightUsed( false ),
642 mbColorUsed( false ),
643 mbUnderlUsed( false ),
644 mbItalicUsed( false ),
645 mbStrikeUsed( false ),
646 mbBorderUsed( false ),
647 mbPattUsed( false ),
648 mbFormula2(false)
650 // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
651 ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
652 maOrigin.SetTab(aValidSrcPos.Tab());
654 /* Get formatting attributes here, and not in WriteBody(). This is needed to
655 correctly insert all colors into the palette. */
657 if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
659 const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
661 // font
662 mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
663 mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
664 mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
665 mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
666 mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
667 mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
668 mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
669 if( mbFontUsed )
671 vcl::Font aFont;
672 model::ComplexColor aComplexColor;
673 ScPatternAttr::fillFontOnly(aFont, rItemSet);
674 ScPatternAttr::fillColor(aComplexColor, rItemSet, ScAutoFontColorMode::Raw);
675 maFontData.FillFromVclFont(aFont, aComplexColor);
676 mnFontColorId = GetPalette().InsertColor(maFontData.maComplexColor.getFinalColor(), EXC_COLOR_CELLTEXT);
679 // border
680 mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
681 if( mbBorderUsed )
682 maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
684 // pattern
685 mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
686 if( mbPattUsed )
687 maArea.FillFromItemSet( rItemSet, GetPalette(), true );
690 // *** mode and comparison operator ***
692 switch( rFormatEntry.GetOperation() )
694 case ScConditionMode::NONE:
695 mnType = EXC_CF_TYPE_NONE;
696 break;
697 case ScConditionMode::Between:
698 mnOperator = EXC_CF_CMP_BETWEEN;
699 mbFormula2 = true;
700 break;
701 case ScConditionMode::NotBetween:
702 mnOperator = EXC_CF_CMP_NOT_BETWEEN;
703 mbFormula2 = true;
704 break;
705 case ScConditionMode::Equal:
706 mnOperator = EXC_CF_CMP_EQUAL;
707 break;
708 case ScConditionMode::NotEqual:
709 mnOperator = EXC_CF_CMP_NOT_EQUAL;
710 break;
711 case ScConditionMode::Greater:
712 mnOperator = EXC_CF_CMP_GREATER;
713 break;
714 case ScConditionMode::Less:
715 mnOperator = EXC_CF_CMP_LESS;
716 break;
717 case ScConditionMode::EqGreater:
718 mnOperator = EXC_CF_CMP_GREATER_EQUAL;
719 break;
720 case ScConditionMode::EqLess:
721 mnOperator = EXC_CF_CMP_LESS_EQUAL;
722 break;
723 case ScConditionMode::Direct:
724 mnType = EXC_CF_TYPE_FMLA;
725 break;
726 default:
727 mnType = EXC_CF_TYPE_NONE;
728 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
732 void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
735 // *** formulas ***
737 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
739 std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
740 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
742 if (mbFormula2)
744 xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
745 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
748 // *** mode and comparison operator ***
750 rStrm << mnType << mnOperator;
752 // *** formula sizes ***
754 sal_uInt16 nFmlaSize1 = mxTokArr1 ? mxTokArr1->GetSize() : 0;
755 sal_uInt16 nFmlaSize2 = mxTokArr2 ? mxTokArr2->GetSize() : 0;
756 rStrm << nFmlaSize1 << nFmlaSize2;
758 // *** formatting blocks ***
760 if( mbFontUsed || mbBorderUsed || mbPattUsed )
762 sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
764 ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
765 ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
766 ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
768 // attributes used -> set flags to 0.
769 ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
770 ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
772 rStrm << nFlags << sal_uInt16( 0 );
774 if( mbFontUsed )
776 // font height, 0xFFFFFFFF indicates unused
777 sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
778 // font style: italic and strikeout
779 sal_uInt32 nStyle = 0;
780 ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
781 ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
782 // font color, 0xFFFFFFFF indicates unused
783 sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
784 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
785 sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
786 ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
787 ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
788 // font used flag for underline -> 0 = used, 1 = default
789 sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
791 rStrm.WriteZeroBytesToRecord( 64 );
792 rStrm << nHeight
793 << nStyle
794 << maFontData.mnWeight
795 << EXC_FONTESC_NONE
796 << maFontData.mnUnderline;
797 rStrm.WriteZeroBytesToRecord( 3 );
798 rStrm << nColor
799 << sal_uInt32( 0 )
800 << nFontFlags1
801 << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
802 << nFontFlags3;
803 rStrm.WriteZeroBytesToRecord( 16 );
804 rStrm << sal_uInt16( 1 ); // must be 1
807 if( mbBorderUsed )
809 sal_uInt16 nLineStyle = 0;
810 sal_uInt32 nLineColor = 0;
811 maBorder.SetFinalColors( GetPalette() );
812 maBorder.FillToCF8( nLineStyle, nLineColor );
813 rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
816 if( mbPattUsed )
818 sal_uInt16 nPattern = 0, nColor = 0;
819 maArea.SetFinalColors( GetPalette() );
820 maArea.FillToCF8( nPattern, nColor );
821 rStrm << nPattern << nColor;
824 else
826 // no data blocks at all
827 rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
830 // *** formulas ***
832 if( mxTokArr1 )
833 mxTokArr1->WriteArray( rStrm );
834 if( mxTokArr2 )
835 mxTokArr2->WriteArray( rStrm );
838 namespace {
840 const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
842 const char *pRet = nullptr;
843 switch(eMode)
845 case ScConditionMode::Equal:
846 pRet = "equal";
847 break;
848 case ScConditionMode::Less:
849 pRet = "lessThan";
850 break;
851 case ScConditionMode::Greater:
852 pRet = "greaterThan";
853 break;
854 case ScConditionMode::EqLess:
855 pRet = "lessThanOrEqual";
856 break;
857 case ScConditionMode::EqGreater:
858 pRet = "greaterThanOrEqual";
859 break;
860 case ScConditionMode::NotEqual:
861 pRet = "notEqual";
862 break;
863 case ScConditionMode::Between:
864 bFrmla2 = true;
865 pRet = "between";
866 break;
867 case ScConditionMode::NotBetween:
868 bFrmla2 = true;
869 pRet = "notBetween";
870 break;
871 case ScConditionMode::Duplicate:
872 case ScConditionMode::NotDuplicate:
873 pRet = nullptr;
874 break;
875 case ScConditionMode::BeginsWith:
876 pRet = "beginsWith";
877 break;
878 case ScConditionMode::EndsWith:
879 pRet = "endsWith";
880 break;
881 case ScConditionMode::ContainsText:
882 pRet = "containsText";
883 break;
884 case ScConditionMode::NotContainsText:
885 pRet = "notContains";
886 break;
887 case ScConditionMode::Direct:
888 break;
889 case ScConditionMode::NONE:
890 default:
891 break;
893 return pRet;
896 const char* GetTypeString(ScConditionMode eMode)
898 switch(eMode)
900 case ScConditionMode::Direct:
901 return "expression";
902 case ScConditionMode::Top10:
903 case ScConditionMode::TopPercent:
904 case ScConditionMode::Bottom10:
905 case ScConditionMode::BottomPercent:
906 return "top10";
907 case ScConditionMode::AboveAverage:
908 case ScConditionMode::BelowAverage:
909 case ScConditionMode::AboveEqualAverage:
910 case ScConditionMode::BelowEqualAverage:
911 return "aboveAverage";
912 case ScConditionMode::NotDuplicate:
913 return "uniqueValues";
914 case ScConditionMode::Duplicate:
915 return "duplicateValues";
916 case ScConditionMode::Error:
917 return "containsErrors";
918 case ScConditionMode::NoError:
919 return "notContainsErrors";
920 case ScConditionMode::BeginsWith:
921 return "beginsWith";
922 case ScConditionMode::EndsWith:
923 return "endsWith";
924 case ScConditionMode::ContainsText:
925 return "containsText";
926 case ScConditionMode::NotContainsText:
927 return "notContainsText";
928 default:
929 return "cellIs";
933 bool IsTopBottomRule(ScConditionMode eMode)
935 switch(eMode)
937 case ScConditionMode::Top10:
938 case ScConditionMode::Bottom10:
939 case ScConditionMode::TopPercent:
940 case ScConditionMode::BottomPercent:
941 return true;
942 default:
943 break;
946 return false;
949 bool IsTextRule(ScConditionMode eMode)
951 switch(eMode)
953 case ScConditionMode::BeginsWith:
954 case ScConditionMode::EndsWith:
955 case ScConditionMode::ContainsText:
956 case ScConditionMode::NotContainsText:
957 return true;
958 default:
959 break;
962 return false;
965 bool RequiresFormula(ScConditionMode eMode)
967 if (IsTopBottomRule(eMode))
968 return false;
969 else if (IsTextRule(eMode))
970 return false;
972 switch (eMode)
974 case ScConditionMode::NoError:
975 case ScConditionMode::Error:
976 case ScConditionMode::Duplicate:
977 case ScConditionMode::NotDuplicate:
978 return false;
979 default:
980 break;
983 return true;
986 bool RequiresFixedFormula(ScConditionMode eMode)
988 switch(eMode)
990 case ScConditionMode::NoError:
991 case ScConditionMode::Error:
992 case ScConditionMode::BeginsWith:
993 case ScConditionMode::EndsWith:
994 case ScConditionMode::ContainsText:
995 case ScConditionMode::NotContainsText:
996 return true;
997 default:
998 break;
1001 return false;
1004 OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
1006 OStringBuffer aBuffer;
1007 XclXmlUtils::ToOString(aBuffer, rAddress);
1008 OString aPos = aBuffer.makeStringAndClear();
1009 switch (eMode)
1011 case ScConditionMode::Error:
1012 return OString("ISERROR(" + aPos + ")") ;
1013 case ScConditionMode::NoError:
1014 return OString("NOT(ISERROR(" + aPos + "))") ;
1015 case ScConditionMode::BeginsWith:
1016 return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1017 case ScConditionMode::EndsWith:
1018 return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1019 case ScConditionMode::ContainsText:
1020 return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText + "\"," + aPos + ")))");
1021 case ScConditionMode::NotContainsText:
1022 return OString(OString::Concat("ISERROR(SEARCH(\"") + rText + "\"," + aPos + "))");
1023 default:
1024 break;
1027 return ""_ostr;
1032 void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
1034 bool bFmla2 = false;
1035 ScConditionMode eOperation = mrFormatEntry.GetOperation();
1036 bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
1037 eOperation == ScConditionMode::AboveEqualAverage;
1038 bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
1039 eOperation == ScConditionMode::BelowEqualAverage;
1040 bool bBottom = eOperation == ScConditionMode::Bottom10
1041 || eOperation == ScConditionMode::BottomPercent;
1042 bool bPercent = eOperation == ScConditionMode::TopPercent ||
1043 eOperation == ScConditionMode::BottomPercent;
1044 OUString aRank(u"0"_ustr);
1045 if(IsTopBottomRule(eOperation))
1047 // position and formula grammar are not important
1048 // we only store a number there
1049 aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0);
1051 OString aText;
1052 if(IsTextRule(eOperation))
1054 // we need to write the text without quotes
1055 // we have to actually get the string from
1056 // the token array for that
1057 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1058 if(pTokenArray->GetLen())
1060 formula::StackVar eType = pTokenArray->FirstToken()->GetType();
1061 switch (eType)
1063 case formula::svDouble:
1065 aText = OString::number(pTokenArray->FirstToken()->GetDouble());
1066 break;
1068 default:
1070 aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
1071 break;
1077 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1078 rWorksheet->startElement( XML_cfRule,
1079 XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
1080 XML_priority, OString::number(mnPriority + 1),
1081 XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
1082 XML_aboveAverage, ToPsz10(bAboveAverage),
1083 XML_equalAverage, ToPsz10(bEqualAverage),
1084 XML_bottom, ToPsz10(bBottom),
1085 XML_percent, ToPsz10(bPercent),
1086 XML_rank, aRank,
1087 XML_text, aText,
1088 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
1090 if (RequiresFixedFormula(eOperation))
1092 rWorksheet->startElement(XML_formula);
1093 OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
1094 rWorksheet->writeEscaped(aFormula.getStr());
1095 rWorksheet->endElement( XML_formula );
1097 else if(RequiresFormula(eOperation))
1099 rWorksheet->startElement(XML_formula);
1100 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1101 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1102 pTokenArray.get()));
1103 rWorksheet->endElement( XML_formula );
1104 if (bFmla2)
1106 rWorksheet->startElement(XML_formula);
1107 std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
1108 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1109 pTokenArray2.get()));
1110 rWorksheet->endElement( XML_formula );
1113 // OOXTODO: XML_extLst
1114 rWorksheet->endElement( XML_cfRule );
1117 XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
1118 XclExpRecord( EXC_ID_CF ),
1119 XclExpRoot( rRoot ),
1120 mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
1124 XclExpCF::~XclExpCF()
1128 void XclExpCF::WriteBody( XclExpStream& rStrm )
1130 mxImpl->WriteBody( rStrm );
1133 void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
1135 mxImpl->SaveXml( rStrm );
1138 XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
1139 XclExpRecord( EXC_ID_CF ),
1140 XclExpRoot( rRoot ),
1141 mrFormatEntry(rFormatEntry),
1142 mnPriority(nPriority)
1146 XclExpDateFormat::~XclExpDateFormat()
1150 namespace {
1152 const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
1154 switch(eType)
1156 case condformat::TODAY:
1157 return "today";
1158 case condformat::YESTERDAY:
1159 return "yesterday";
1160 case condformat::TOMORROW:
1161 return "yesterday";
1162 case condformat::THISWEEK:
1163 return "thisWeek";
1164 case condformat::LASTWEEK:
1165 return "lastWeek";
1166 case condformat::NEXTWEEK:
1167 return "nextWeek";
1168 case condformat::THISMONTH:
1169 return "thisMonth";
1170 case condformat::LASTMONTH:
1171 return "lastMonth";
1172 case condformat::NEXTMONTH:
1173 return "nextMonth";
1174 case condformat::LAST7DAYS:
1175 return "last7Days";
1176 default:
1177 break;
1179 return nullptr;
1184 void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
1186 // only write the supported entries into OOXML
1187 const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
1188 if(!sTimePeriod)
1189 return;
1191 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1192 rWorksheet->startElement( XML_cfRule,
1193 XML_type, "timePeriod",
1194 XML_priority, OString::number(mnPriority + 1),
1195 XML_timePeriod, sTimePeriod,
1196 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
1197 rWorksheet->endElement( XML_cfRule);
1200 XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
1201 XclExpRoot( rRoot ),
1202 mrEntry(rEntry),
1203 maSrcPos(rAddr),
1204 mbFirst(bFirst)
1208 namespace {
1210 OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
1212 switch(rEntry.GetType())
1214 case COLORSCALE_MIN:
1215 return "min"_ostr;
1216 case COLORSCALE_MAX:
1217 return "max"_ostr;
1218 case COLORSCALE_PERCENT:
1219 return "percent"_ostr;
1220 case COLORSCALE_FORMULA:
1221 return "formula"_ostr;
1222 case COLORSCALE_AUTO:
1223 if(bFirst)
1224 return "min"_ostr;
1225 else
1226 return "max"_ostr;
1227 case COLORSCALE_PERCENTILE:
1228 return "percentile"_ostr;
1229 default:
1230 break;
1233 return "num"_ostr;
1238 void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
1240 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1242 OString aValue;
1243 if(mrEntry.GetType() == COLORSCALE_FORMULA)
1245 OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
1246 mrEntry.GetFormula());
1247 aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
1249 else
1251 aValue = OString::number( mrEntry.GetValue() );
1254 rWorksheet->startElement( XML_cfvo,
1255 XML_type, getColorScaleType(mrEntry, mbFirst),
1256 XML_val, aValue,
1257 XML_gte, sax_fastparser::UseIf("0", !mrEntry.GetGreaterThanOrEqual()));
1259 rWorksheet->endElement( XML_cfvo );
1262 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
1263 XclExpRoot( rRoot ),
1264 mrColor( rColor )
1268 XclExpColScaleCol::~XclExpColScaleCol()
1272 void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
1274 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1276 rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
1278 rWorksheet->endElement( XML_color );
1281 namespace {
1283 OString createHexStringFromDigit(sal_uInt8 nDigit)
1285 OString aString = OString::number( nDigit, 16 );
1286 if(aString.getLength() == 1)
1287 aString += OString::number(0);
1288 return aString;
1291 OString createGuidStringFromInt(sal_uInt8 nGuid[16])
1293 OStringBuffer aBuffer("{");
1294 for(size_t i = 0; i < 16; ++i)
1296 aBuffer.append(createHexStringFromDigit(nGuid[i]));
1297 if(i == 3|| i == 5 || i == 7 || i == 9 )
1298 aBuffer.append('-');
1300 aBuffer.append('}');
1301 OString aString = aBuffer.makeStringAndClear();
1302 return aString.toAsciiUpperCase();
1305 OString generateGUIDString()
1307 sal_uInt8 nGuid[16];
1308 rtl_createUuid(nGuid, nullptr, true);
1309 return createGuidStringFromInt(nGuid);
1314 XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
1315 XclExpRecord( EXC_ID_CONDFMT ),
1316 XclExpRoot( rRoot )
1318 const ScRangeList& aScRanges = rCondFormat.GetRange();
1319 GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
1320 if( maXclRanges.empty() )
1321 return;
1323 std::vector<XclExpExtCondFormatData> aExtEntries;
1324 ScAddress aOrigin = aScRanges.Combine().aStart;
1325 for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
1326 if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
1328 if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
1329 maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
1330 else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1332 const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
1333 XclExpExtCondFormatData aExtEntry;
1334 aExtEntry.nPriority = ++rIndex;
1335 aExtEntry.aGUID = generateGUIDString();
1336 aExtEntry.pEntry = &rFormat;
1337 aExtEntries.push_back(aExtEntry);
1339 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
1340 maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
1341 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
1343 const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
1344 XclExpExtCondFormatData aExtEntry;
1345 aExtEntry.nPriority = -1;
1346 aExtEntry.aGUID = generateGUIDString();
1347 aExtEntry.pEntry = &rFormat;
1348 aExtEntries.push_back(aExtEntry);
1350 maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
1352 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
1354 // don't export iconSet entries that are not in OOXML
1355 const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
1356 bool bNeedsExt = false;
1357 switch (rIconSet.GetIconSetData()->eIconSetType)
1359 case IconSet_3Smilies:
1360 case IconSet_3ColorSmilies:
1361 case IconSet_3Stars:
1362 case IconSet_3Triangles:
1363 case IconSet_5Boxes:
1365 bNeedsExt = true;
1367 break;
1368 default:
1369 break;
1372 bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
1374 if (bNeedsExt)
1376 XclExpExtCondFormatData aExtEntry;
1377 aExtEntry.nPriority = ++rIndex;
1378 aExtEntry.aGUID = generateGUIDString();
1379 aExtEntry.pEntry = &rIconSet;
1380 aExtEntries.push_back(aExtEntry);
1382 else
1383 maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
1385 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
1386 maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
1388 aScRanges.Format( msSeqRef, ScRefFlags::VALID, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
1390 if(!aExtEntries.empty() && xExtLst)
1392 XclExpExt* pParent = xExtLst->GetItem( XclExpExtDataBarType );
1393 if( !pParent )
1395 xExtLst->AddRecord( new XclExpExtCondFormat( *xExtLst ) );
1396 pParent = xExtLst->GetItem( XclExpExtDataBarType );
1398 static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ))->AddRecord(
1399 new XclExpExtConditionalFormatting( *pParent, aExtEntries, aScRanges));
1403 XclExpCondfmt::~XclExpCondfmt()
1407 bool XclExpCondfmt::IsValidForBinary() const
1409 // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
1410 // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
1412 SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
1414 return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
1417 bool XclExpCondfmt::IsValidForXml() const
1419 return !maCFList.IsEmpty() && !maXclRanges.empty();
1422 void XclExpCondfmt::Save( XclExpStream& rStrm )
1424 if( IsValidForBinary() )
1426 XclExpRecord::Save( rStrm );
1427 maCFList.Save( rStrm );
1431 void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
1433 OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
1434 OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
1436 rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
1437 << sal_uInt16( 1 )
1438 << maXclRanges.GetEnclosingRange()
1439 << maXclRanges;
1442 void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
1444 if( !IsValidForXml() )
1445 return;
1447 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1448 rWorksheet->startElement( XML_conditionalFormatting,
1449 XML_sqref, msSeqRef
1450 // OOXTODO: XML_pivot
1453 maCFList.SaveXml( rStrm );
1455 rWorksheet->endElement( XML_conditionalFormatting );
1458 XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
1459 XclExpRoot( rRoot ),
1460 mnPriority( nPriority )
1462 const ScRange & rRange = rFormat.GetRange().front();
1463 ScAddress aAddr = rRange.aStart;
1464 for(const auto& rxColorScaleEntry : rFormat)
1466 // exact position is not important, we allow only absolute refs
1468 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
1469 maCfvoList.AppendRecord( xCfvo );
1470 XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
1471 maColList.AppendRecord( xClo );
1475 void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
1477 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1479 rWorksheet->startElement( XML_cfRule,
1480 XML_type, "colorScale",
1481 XML_priority, OString::number(mnPriority + 1) );
1483 rWorksheet->startElement(XML_colorScale);
1485 maCfvoList.SaveXml(rStrm);
1486 maColList.SaveXml(rStrm);
1488 rWorksheet->endElement( XML_colorScale );
1490 rWorksheet->endElement( XML_cfRule );
1493 XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, OString aGUID):
1494 XclExpRoot( rRoot ),
1495 mrFormat( rFormat ),
1496 mnPriority( nPriority ),
1497 maGUID(std::move(aGUID))
1499 const ScRange & rRange = rFormat.GetRange().front();
1500 ScAddress aAddr = rRange.aStart;
1501 // exact position is not important, we allow only absolute refs
1502 mpCfvoLowerLimit.reset(
1503 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
1504 mpCfvoUpperLimit.reset(
1505 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
1507 mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
1510 void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
1512 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1514 rWorksheet->startElement( XML_cfRule,
1515 XML_type, "dataBar",
1516 XML_priority, OString::number(mnPriority + 1) );
1518 rWorksheet->startElement( XML_dataBar,
1519 XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
1520 XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
1521 XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
1523 mpCfvoLowerLimit->SaveXml(rStrm);
1524 mpCfvoUpperLimit->SaveXml(rStrm);
1525 mpCol->SaveXml(rStrm);
1527 rWorksheet->endElement( XML_dataBar );
1529 // extLst entries for Excel 2010 and 2013
1530 rWorksheet->startElement(XML_extLst);
1531 rWorksheet->startElement(XML_ext,
1532 FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
1533 XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
1535 rWorksheet->startElementNS( XML_x14, XML_id );
1536 rWorksheet->write(maGUID);
1537 rWorksheet->endElementNS( XML_x14, XML_id );
1539 rWorksheet->endElement( XML_ext );
1540 rWorksheet->endElement( XML_extLst );
1542 rWorksheet->endElement( XML_cfRule );
1545 XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
1546 XclExpRoot( rRoot ),
1547 mrFormat( rFormat ),
1548 mnPriority( nPriority )
1550 const ScRange & rRange = rFormat.GetRange().front();
1551 ScAddress aAddr = rRange.aStart;
1552 for (auto const& itr : rFormat)
1554 // exact position is not important, we allow only absolute refs
1556 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
1557 maCfvoList.AppendRecord( xCfvo );
1561 void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
1563 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1565 rWorksheet->startElement( XML_cfRule,
1566 XML_type, "iconSet",
1567 XML_priority, OString::number(mnPriority + 1) );
1569 OUString aIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
1570 rWorksheet->startElement( XML_iconSet,
1571 XML_iconSet, aIconSetName,
1572 XML_showValue, sax_fastparser::UseIf("0", !mrFormat.GetIconSetData()->mbShowValue),
1573 XML_reverse, sax_fastparser::UseIf("1", mrFormat.GetIconSetData()->mbReverse));
1575 maCfvoList.SaveXml( rStrm );
1577 rWorksheet->endElement( XML_iconSet );
1578 rWorksheet->endElement( XML_cfRule );
1581 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
1582 XclExpRoot( rRoot )
1584 if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
1586 sal_Int32 nIndex = 0;
1587 for( const auto& rxCondFmt : *pCondFmtList)
1589 XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
1590 if( xCondfmtRec->IsValidForXml() )
1591 maCondfmtList.AppendRecord( xCondfmtRec );
1596 void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
1598 maCondfmtList.Save( rStrm );
1601 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
1603 maCondfmtList.SaveXml( rStrm );
1606 // Validation =================================================================
1608 namespace {
1610 /** Writes a formula for the DV record. */
1611 void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
1613 sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
1614 rStrm << nFmlaSize << sal_uInt16( 0 );
1615 if( pXclTokArr )
1616 pXclTokArr->WriteArray( rStrm );
1619 /** Writes a formula for the DV record, based on a single string. */
1620 void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
1622 // fake a formula with a single tStr token
1623 rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
1624 << sal_uInt16( 0 )
1625 << EXC_TOKID_STR
1626 << rString;
1629 const char* lcl_GetValidationType( sal_uInt32 nFlags )
1631 switch( nFlags & EXC_DV_MODE_MASK )
1633 case EXC_DV_MODE_ANY: return "none";
1634 case EXC_DV_MODE_WHOLE: return "whole";
1635 case EXC_DV_MODE_DECIMAL: return "decimal";
1636 case EXC_DV_MODE_LIST: return "list";
1637 case EXC_DV_MODE_DATE: return "date";
1638 case EXC_DV_MODE_TIME: return "time";
1639 case EXC_DV_MODE_TEXTLEN: return "textLength";
1640 case EXC_DV_MODE_CUSTOM: return "custom";
1642 return nullptr;
1645 const char* lcl_GetOperatorType( sal_uInt32 nFlags )
1647 switch( nFlags & EXC_DV_COND_MASK )
1649 case EXC_DV_COND_BETWEEN: return "between";
1650 case EXC_DV_COND_NOTBETWEEN: return "notBetween";
1651 case EXC_DV_COND_EQUAL: return "equal";
1652 case EXC_DV_COND_NOTEQUAL: return "notEqual";
1653 case EXC_DV_COND_GREATER: return "greaterThan";
1654 case EXC_DV_COND_LESS: return "lessThan";
1655 case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
1656 case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
1658 return nullptr;
1661 const char* lcl_GetErrorType( sal_uInt32 nFlags )
1663 switch (nFlags & EXC_DV_ERROR_MASK)
1665 case EXC_DV_ERROR_STOP: return "stop";
1666 case EXC_DV_ERROR_WARNING: return "warning";
1667 case EXC_DV_ERROR_INFO: return "information";
1669 return nullptr;
1672 void lcl_SetValidationText(const OUString& rText, XclExpString& rValidationText)
1674 if ( !rText.isEmpty() )
1676 // maximum length allowed in Excel is 255 characters
1677 if ( rText.getLength() > 255 )
1679 OUStringBuffer aBuf( rText );
1680 rValidationText.Assign(
1681 comphelper::string::truncateToLength(aBuf, 255).makeStringAndClear() );
1683 else
1684 rValidationText.Assign( rText );
1686 else
1687 rValidationText.Assign( '\0' );
1690 } // namespace
1692 XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uInt32 nScHandle ) :
1693 XclExpRecord( EXC_ID_DV ),
1694 XclExpRoot( rRoot ),
1695 mnFlags( 0 ),
1696 mnScHandle( nScHandle )
1698 if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
1700 // prompt box - empty string represented by single NUL character
1701 OUString aTitle, aText;
1702 bool bShowPrompt = pValData->GetInput( aTitle, aText );
1703 lcl_SetValidationText(aTitle, maPromptTitle);
1704 lcl_SetValidationText(aText, maPromptText);
1706 // error box - empty string represented by single NUL character
1707 ScValidErrorStyle eScErrorStyle;
1708 bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
1709 lcl_SetValidationText(aTitle, maErrorTitle);
1710 lcl_SetValidationText(aText, maErrorText);
1712 // flags
1713 switch( pValData->GetDataMode() )
1715 case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
1716 case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
1717 case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
1718 case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
1719 case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
1720 case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
1721 case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
1722 case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
1723 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
1726 switch( pValData->GetOperation() )
1728 case ScConditionMode::NONE:
1729 case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
1730 case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
1731 case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
1732 case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
1733 case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
1734 case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
1735 case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
1736 case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
1737 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
1739 switch( eScErrorStyle )
1741 case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
1742 case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
1743 case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
1744 case SC_VALERR_MACRO:
1745 // set INFO for validity with macro call, delete title
1746 mnFlags |= EXC_DV_ERROR_INFO;
1747 maErrorTitle.Assign( '\0' ); // contains macro name
1748 break;
1749 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
1751 ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
1752 ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
1753 ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
1754 ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
1756 // formulas
1757 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
1759 // first formula
1760 std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
1761 if (xScTokArr)
1763 if( pValData->GetDataMode() == SC_VALID_LIST )
1765 OUString aString;
1766 if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
1768 bool bList = false;
1769 OUStringBuffer sListBuf;
1770 OUStringBuffer sFormulaBuf("\"");
1771 /* Formula is a list of string tokens -> build the Excel string.
1772 Data validity is BIFF8 only (important for the XclExpString object).
1773 Excel uses the NUL character as string list separator. */
1774 mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
1775 if (!aString.isEmpty())
1777 sal_Int32 nStringIx = 0;
1778 for(;;)
1780 const std::u16string_view aToken( o3tl::getToken(aString, 0, '\n', nStringIx ) );
1781 if (aToken.find(',') != std::u16string_view::npos)
1783 sListBuf.append(OUString::Concat("\"") + aToken + "\"");
1784 bList = true;
1786 else
1787 sListBuf.append(aToken);
1788 mxString1->Append( aToken );
1789 sFormulaBuf.append( aToken );
1790 if (nStringIx<0)
1791 break;
1792 sal_Unicode cUnicodeChar = 0;
1793 mxString1->Append( std::u16string_view(&cUnicodeChar, 1) );
1794 sFormulaBuf.append( ',' );
1795 sListBuf.append( ',' );
1798 ::set_flag( mnFlags, EXC_DV_STRINGLIST );
1800 // maximum length allowed in Excel is 255 characters, and don't end with an empty token
1801 // It should be 8192 but Excel doesn't accept it for unknown reason
1802 // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
1803 sal_uInt32 nLen = sFormulaBuf.getLength();
1804 if( nLen > 256 ) // 255 + beginning quote mark
1806 nLen = 256;
1807 if( sFormulaBuf[nLen - 1] == ',' )
1808 --nLen;
1809 sFormulaBuf.truncate(nLen);
1812 sFormulaBuf.append( '"' );
1813 msFormula1 = sFormulaBuf.makeStringAndClear();
1814 if (bList)
1815 msList = sListBuf.makeStringAndClear();
1816 else
1817 sListBuf.remove(0, sListBuf.getLength());
1819 else
1821 /* All other formulas in validation are stored like conditional
1822 formatting formulas (with tRefN/tAreaN tokens as value or
1823 array class). But NOT the cell references and defined names
1824 in list validation - they are stored as reference class
1825 tokens... Example:
1826 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1827 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1828 Formula compiler supports this by offering two different functions
1829 CreateDataValFormula() and CreateListValFormula(). */
1830 if(GetOutput() == EXC_OUTPUT_BINARY)
1831 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
1832 else
1833 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1834 xScTokArr.get());
1837 else
1839 // no list validation -> convert the formula
1840 if(GetOutput() == EXC_OUTPUT_BINARY)
1841 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1842 else
1843 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1844 xScTokArr.get());
1848 // second formula
1849 xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
1850 if (xScTokArr)
1852 if(GetOutput() == EXC_OUTPUT_BINARY)
1853 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1854 else
1855 msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1856 xScTokArr.get());
1859 else
1861 OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
1862 mnScHandle = SAL_MAX_UINT32;
1866 XclExpDV::~XclExpDV()
1870 void XclExpDV::InsertCellRange( const ScRange& rRange )
1872 maScRanges.Join( rRange );
1875 bool XclExpDV::Finalize()
1877 GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
1878 return (mnScHandle != SAL_MAX_UINT32) && !maXclRanges.empty();
1881 void XclExpDV::WriteBody( XclExpStream& rStrm )
1883 // flags and strings
1884 rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
1885 // condition formulas
1886 if( mxString1 )
1887 lclWriteDvFormula( rStrm, *mxString1 );
1888 else
1889 lclWriteDvFormula( rStrm, mxTokArr1.get() );
1890 lclWriteDvFormula( rStrm, mxTokArr2.get() );
1891 // cell ranges
1892 rStrm << maXclRanges;
1895 void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
1897 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1898 rWorksheet->startElement( XML_dataValidation,
1899 XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
1900 XML_error, XESTRING_TO_PSZ( maErrorText ),
1901 XML_errorStyle, lcl_GetErrorType(mnFlags),
1902 XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
1903 // OOXTODO: XML_imeMode,
1904 XML_operator, lcl_GetOperatorType( mnFlags ),
1905 XML_prompt, XESTRING_TO_PSZ( maPromptText ),
1906 XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
1907 // showDropDown should have been showNoDropDown - check oox/xlsx import for details
1908 XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
1909 XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
1910 XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
1911 XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScRanges),
1912 XML_type, lcl_GetValidationType(mnFlags) );
1913 if (!msList.isEmpty())
1915 rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
1916 FSNS(XML_xmlns, XML_x12ac),rStrm.getNamespaceURL(OOX_NS(x12ac)),
1917 FSNS(XML_xmlns, XML_mc),rStrm.getNamespaceURL(OOX_NS(mce)));
1918 rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x12ac");
1919 rWorksheet->startElement(FSNS(XML_x12ac, XML_list));
1920 rWorksheet->writeEscaped(msList);
1921 rWorksheet->endElement(FSNS(XML_x12ac, XML_list));
1922 rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
1923 rWorksheet->startElement(FSNS(XML_mc, XML_Fallback));
1924 rWorksheet->startElement(XML_formula1);
1925 rWorksheet->writeEscaped(msFormula1);
1926 rWorksheet->endElement(XML_formula1);
1927 rWorksheet->endElement(FSNS(XML_mc, XML_Fallback));
1928 rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
1930 if (msList.isEmpty() && !msFormula1.isEmpty())
1932 rWorksheet->startElement(XML_formula1);
1933 rWorksheet->writeEscaped( msFormula1 );
1934 rWorksheet->endElement( XML_formula1 );
1936 if( !msFormula2.isEmpty() )
1938 rWorksheet->startElement(XML_formula2);
1939 rWorksheet->writeEscaped( msFormula2 );
1940 rWorksheet->endElement( XML_formula2 );
1942 rWorksheet->endElement( XML_dataValidation );
1945 XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
1946 XclExpRecord( EXC_ID_DVAL, 18 ),
1947 XclExpRoot( rRoot )
1951 XclExpDval::~XclExpDval()
1955 void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uInt32 nScHandle )
1957 if( GetBiff() == EXC_BIFF8 )
1959 XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
1960 rDVRec.InsertCellRange( rRange );
1964 void XclExpDval::Save( XclExpStream& rStrm )
1966 // check all records
1967 size_t nPos = maDVList.GetSize();
1968 while( nPos )
1970 --nPos; // backwards to keep nPos valid
1971 XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
1972 if( !xDVRec->Finalize() )
1973 maDVList.RemoveRecord( nPos );
1976 // write the DVAL and the DV's
1977 if( !maDVList.IsEmpty() )
1979 XclExpRecord::Save( rStrm );
1980 maDVList.Save( rStrm );
1984 void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
1986 if( maDVList.IsEmpty() )
1987 return;
1989 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1990 rWorksheet->startElement( XML_dataValidations,
1991 XML_count, OString::number(maDVList.GetSize())
1992 // OOXTODO: XML_disablePrompts,
1993 // OOXTODO: XML_xWindow,
1994 // OOXTODO: XML_yWindow
1996 maDVList.SaveXml( rStrm );
1997 rWorksheet->endElement( XML_dataValidations );
2000 XclExpDV& XclExpDval::SearchOrCreateDv( sal_uInt32 nScHandle )
2002 // test last found record
2003 if( mxLastFoundDV && (mxLastFoundDV->GetScHandle() == nScHandle) )
2004 return *mxLastFoundDV;
2006 // binary search
2007 size_t nCurrPos = 0;
2008 if( !maDVList.IsEmpty() )
2010 size_t nFirstPos = 0;
2011 size_t nLastPos = maDVList.GetSize() - 1;
2012 bool bLoop = true;
2013 sal_uInt32 nCurrScHandle = ::std::numeric_limits< sal_uInt32 >::max();
2014 while( (nFirstPos <= nLastPos) && bLoop )
2016 nCurrPos = (nFirstPos + nLastPos) / 2;
2017 mxLastFoundDV = maDVList.GetRecord( nCurrPos );
2018 nCurrScHandle = mxLastFoundDV->GetScHandle();
2019 if( nCurrScHandle == nScHandle )
2020 bLoop = false;
2021 else if( nCurrScHandle < nScHandle )
2022 nFirstPos = nCurrPos + 1;
2023 else if( nCurrPos )
2024 nLastPos = nCurrPos - 1;
2025 else // special case for nLastPos = -1
2026 bLoop = false;
2028 if( nCurrScHandle == nScHandle )
2029 return *mxLastFoundDV;
2030 else if( nCurrScHandle < nScHandle )
2031 ++nCurrPos;
2034 // create new DV record
2035 mxLastFoundDV = new XclExpDV( *this, nScHandle );
2036 maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
2037 return *mxLastFoundDV;
2040 void XclExpDval::WriteBody( XclExpStream& rStrm )
2042 rStrm.WriteZeroBytes( 10 );
2043 rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
2046 // Web Queries ================================================================
2048 XclExpWebQuery::XclExpWebQuery(
2049 const OUString& rRangeName,
2050 const OUString& rUrl,
2051 std::u16string_view rSource,
2052 sal_Int32 nRefrSecs ) :
2053 maDestRange( rRangeName ),
2054 maUrl( rUrl ),
2055 // refresh delay time: seconds -> minutes
2056 mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
2057 mbEntireDoc( false )
2059 // comma separated list of HTML table names or indexes
2060 OUString aNewTables;
2061 OUString aAppendTable;
2062 bool bExitLoop = false;
2063 if (!rSource.empty())
2065 sal_Int32 nStringIx = 0;
2068 OUString aToken( o3tl::getToken(rSource, 0, ';', nStringIx ) );
2069 mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
2070 bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
2071 if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
2072 aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
2074 while (nStringIx>0 && !bExitLoop);
2077 if( !bExitLoop ) // neither HTML_all nor HTML_tables found
2079 if( !aNewTables.isEmpty() )
2080 mxQryTables.reset( new XclExpString( aNewTables ) );
2081 else
2082 mbEntireDoc = true;
2086 XclExpWebQuery::~XclExpWebQuery()
2090 void XclExpWebQuery::Save( XclExpStream& rStrm )
2092 OSL_ENSURE( !mbEntireDoc || !mxQryTables, "XclExpWebQuery::Save - illegal mode" );
2093 sal_uInt16 nFlags;
2095 // QSI record
2096 rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
2097 rStrm << EXC_QSI_DEFAULTFLAGS
2098 << sal_uInt16( 0x0010 )
2099 << sal_uInt16( 0x0012 )
2100 << sal_uInt32( 0x00000000 )
2101 << maDestRange;
2102 rStrm.EndRecord();
2104 // PARAMQRY record
2105 nFlags = 0;
2106 ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
2107 ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
2108 ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
2109 rStrm.StartRecord( EXC_ID_PQRY, 12 );
2110 rStrm << nFlags
2111 << sal_uInt16( 0x0000 )
2112 << sal_uInt16( 0x0001 );
2113 rStrm.WriteZeroBytes( 6 );
2114 rStrm.EndRecord();
2116 // WQSTRING record
2117 rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
2118 rStrm << maUrl;
2119 rStrm.EndRecord();
2121 // unknown record 0x0802
2122 rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
2123 rStrm << EXC_ID_0802; // repeated record id ?!?
2124 rStrm.WriteZeroBytes( 6 );
2125 rStrm << sal_uInt16( 0x0003 )
2126 << sal_uInt32( 0x00000000 )
2127 << sal_uInt16( 0x0010 )
2128 << maDestRange;
2129 rStrm.EndRecord();
2131 // WEBQRYSETTINGS record
2132 nFlags = mxQryTables ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
2133 rStrm.StartRecord( EXC_ID_WQSETT, 28 );
2134 rStrm << EXC_ID_WQSETT // repeated record id ?!?
2135 << sal_uInt16( 0x0000 )
2136 << sal_uInt16( 0x0004 )
2137 << sal_uInt16( 0x0000 )
2138 << EXC_WQSETT_DEFAULTFLAGS
2139 << nFlags;
2140 rStrm.WriteZeroBytes( 10 );
2141 rStrm << mnRefresh // refresh delay in minutes
2142 << EXC_WQSETT_FORMATFULL
2143 << sal_uInt16( 0x0000 );
2144 rStrm.EndRecord();
2146 // WEBQRYTABLES record
2147 if( mxQryTables )
2149 rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
2150 rStrm << EXC_ID_WQTABLES // repeated record id ?!?
2151 << sal_uInt16( 0x0000 )
2152 << *mxQryTables; // comma separated list of source tables
2153 rStrm.EndRecord();
2157 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
2159 SCTAB nScTab = rRoot.GetCurrScTab();
2160 ScDocShell* pShell = rRoot.GetDocShell();
2161 if( !pShell ) return;
2162 ScfPropertySet aModelProp( pShell->GetModel() );
2163 if( !aModelProp.Is() ) return;
2165 Reference< XAreaLinks > xAreaLinks;
2166 aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
2167 if( !xAreaLinks.is() ) return;
2169 for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
2171 Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
2172 if( xAreaLink.is() )
2174 CellRangeAddress aDestRange( xAreaLink->getDestArea() );
2175 if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
2177 ScfPropertySet aLinkProp( xAreaLink );
2178 OUString aFilter;
2179 if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
2180 (aFilter == EXC_WEBQRY_FILTER) )
2182 // get properties
2183 OUString /*aFilterOpt,*/ aUrl;
2184 sal_Int32 nRefresh = 0;
2186 // aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
2187 aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
2188 aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
2190 OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
2191 INetURLObject aUrlObj( aAbsDoc );
2192 OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
2193 if( aWebQueryUrl.isEmpty() )
2194 aWebQueryUrl = aAbsDoc;
2196 // find range or create a new range
2197 OUString aRangeName;
2198 ScRange aScDestRange;
2199 ScUnoConversion::FillScRange( aScDestRange, aDestRange );
2200 if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
2202 aRangeName = pRangeData->GetName();
2204 else
2206 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
2207 XclExpNameManager& rNameMgr = rRoot.GetNameManager();
2209 // create a new unique defined name containing the range
2210 XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
2211 sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
2212 aRangeName = rNameMgr.GetOrigName( nNameIdx );
2215 // create and store the web query record
2216 if( !aRangeName.isEmpty() )
2217 AppendNewRecord( new XclExpWebQuery(
2218 aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
2225 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */